Electronics - BrixIT Bloghttps://blog.brixit.nl/tag/electronics/page/1Mon, 01 May 2023 16:46:41 -000060Can't get blinky on the BL602https://blog.brixit.nl/blinky-on-the-bl602/74ElectronicsMartijn BraamMon, 01 May 2023 16:46:41 -0000<p>One of the boards I have in my parts box is the PINE64 BL602 evaluation board. The "PineCone". It's a competitor to WiFi enabled microcontrollers like the Espressif products.</p> <p>I put it aside since when I received it there was not really any software to go along with it and I don't have anywhere near the experience to bring up a board like this.</p> <h2>Getting blinky running 3 years laters</h2> <p>This product has been out for 3 years now so lets see if it has formed a community.</p> <p>So if you are not familiar with blinky, it's the Hello World! of the microcontroller world. The goal is have a led connected to one pin of the board and toggle that pin on and off every second to make a blinking led.</p> <p>Let's check the website first. The official page for this from PINE64 itself is <a href="https://pine64.com/product/pinecone-bl602-evaluation-board/">https://pine64.com/product/pinecone-bl602-evaluation-board/</a>. Since the only official thing is the webshop which doesn't list any documentation or links to progress further on how to use this thing I consider it a dead end. Let's move on to the pine64.org side.</p> <p>The community side is the wiki page at <a href="https://wiki.pine64.org/wiki/PineCone">https://wiki.pine64.org/wiki/PineCone</a>. This has the specs again and a block diagram. It also has the datasheets for the components on the board and the schematics. Not anything I actually need to get started using this product.</p> <p>Let's compare to the Raspberry Pi Pico, a competitor that's doing quite well. When you search for this you land on the official product page <a href="https://www.raspberrypi.com/products/raspberry-pi-pico/">https://www.raspberrypi.com/products/raspberry-pi-pico/</a> that has the shop link, the specifications but more importantly it's a hub linking to the "Getting started" guide and the documentation for users.</p> <p>So lets scroll down on the PineCone wiki page since it's my only hope for docs. Thee quarters down this long wiki page is a list of flashing utilities. I don't have anything to flash yet but I look forward to figuring which of the 6 listed flashers fits my personality best /s.</p> <p>The rest of this page is links to random github repositories and blog articles. The top link being the link to the SDK for this board. It links to <a href="https://github.com/pine64/bl_iot_sdk">https://github.com/pine64/bl_iot_sdk</a> and is labeled as "compilers, linkers, and all the code to build on Windows, Linux (x86_64), and MacOS". The last activity here was 2 years ago. This seems mostly focussed on the reverse engineering effort but lets have a look.</p> <h2>bl_iot_sdk</h2> <p>This repository has a README with again more links to a lot of different places. One of these is linking to <a href="https://pine64.github.io/bl602-docs/">https://pine64.github.io/bl602-docs/</a> which is a sphinx generated documentation site. This is several pages too deep from the official pine64 website but I forgive that since it has the magical words I've been looking for:</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1682954202/image.png" class="kg-image"></figure> <p>So I'm going down this path, it almost seems familiar to the way the Pi Pico sdk is used. Clone the repository and create an environment variable pointing to the checkout folder.</p> <p>Next it tells me how to compile the example apps, but I don't want to compile the example apps, those probably work and they are in-tree so those probaby Just Work(tm) without setting up the scaffolding you should do for your own projects. I just want to build a main.c that does blinky. This part of the documentation sadly stops after compiling and flashing the built-in apps.</p> <p>I have continued browsing this documentation for a bit but it does not show at all how to make a minimal blinky example and build it. The rest of the documentation is mainly using the advanced features, various flashing systems and debugging systems and image formats.</p> <p>So to compare this with the Pi Pico documentation: The first thing I find when I read the documentation of the SDK is:</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1682954861/image.png" class="kg-image"><figcaption>Blinky in the Pi Pico docs</figcaption></figure> <p>Chapter 1.2 is the first chapter with actual documentation and it starts with all the things I want for getting started with yet another microcontroller board:</p> <ul><li>A very simple .c file that implements blinky without using any advanced features of the hardware.</li> <li>The output of <code>tree</code> showing exactly which folder structure I should have and where the files go</li> <li>The CMakeLists.txt that builds this application</li> </ul> <p>The BL602 documentation has shown me none of the above yet. At this point I give up and just open 20 tabs in my browser with all the linked pages from the wiki.</p> <p>All these links are random flashing utilities, porting new programming languages and porting new frameworks. Also a lot of them showing how to flash the example apps to the thing.</p> <p>PEOPLE DON'T BUY MICROCONTROLLERS TO FLASH EXISTING EXAMPLES</p> <h2>Getting ANYTHING to run</h2> <p>So there is no documentation on how to make blinky for this platform whatsoever. I give up and figure it out myself from the sdk example apps.</p> <p>There's no blinky in the examples, the simplest application I could find was the helloworld one that outputs some text over uart. This uses FreeRTOS to get that working so there's a complete lack of basic single .c file demo applications for this.</p> <h2>More issues</h2> <p>This SDK has multiple things I don't like. The most important one is that it ships prebuilt compilers in the source tree. This is not a proper SDK, this is some Android-level build mess.</p> <p>The whole point of using nice RISC-V boards is that this is a standard architecture, all Linux distributions already ship a maintained RISC-V toolchain so why would I ever use some random precompiled binaries shipped by a Chinese vendor.</p> <p>Why is this using Makefiles to build the thing instead of cmake which the Raspberry Pi foundation have already proven you can integrate way neater. This is just throwing hardware over the wall and linking to the chip vendor documentation, hoping the community can match a proper made tutorial, proper documentation and make a new SDK that's not a mess.</p> <p>I guess I just dump this thing back in the parts bin and wait some years. Quite sad since this seems to be some really nice hardware and a great alternative to the existing microcontroller platforms.</p> Sensors and PCB designhttps://blog.brixit.nl/sensors-and-pcb-design/69ElectronicsMartijn BraamThu, 02 Mar 2023 20:09:12 -0000<p>I do a lot of software development, I do enough of it to be comfortable with the process. I have worked on enough projects and have made enough pieces of software that it's quite easy to quickly spin up a new project to fix a specific issue I'm having.</p> <p>Hardware design is a whole other can of worms. I have played with things like the Arduino and later the STM32 chips, ESP8266 and RP2040. These things are quite neat from a programmers perspective. You write your code in c++ and the included toolchain figures out all the hard parts and flashes the board. Hardware design is also quite simple since it's mostly putting together different hardware modules and breakout boards. Only a basic understanding of the hardware busses is required to get stuff up and running.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677722030/PXL_20230302_015255483.jpg" class="kg-image"></figure> <p>Projects usually look like the picture above. No resistors, capacitors or other components are needed. Just a breadboard (also optional), jumper wires and the IO modules you need to make your idea work.</p> <p>Power is dealt with by the microcontroller board, the modules already include the pull-up resistors and power regulators required and the programmer is also included already.</p> <p>The software side is also simplified and abstracted to the point where it's harder to get your average javascript project working than the embedded projects. You'd never have to open the 440 page atmega 328p manual to write the firmware. You don't even need to know about the register map for GPIO pins since things like digitalWrite(pin, state) exist.</p> <p>It's easy to get a complete prototype running like this but then what?</p> <h2>Putting things in production</h2> <p>By far the easiest way to put the project in production is just shoving the breadboard in a project box and declaring it done. It works but it's not <i>neat</i>. This works if you only need one and it's for yourself.</p> <p>When you need 5? 10? 30? of the same thing then breadboarding it will become a lot less practical. In some cases it's easy enough to just stick the modules on a piece of protoboard and solder it together. When that doesn't scale another option is putting the modules on a custom PCB.</p> <p>This is exactly what I did for a project. This is the first PCB I have ever designed and put into production:</p> <figure class="kg-card kg-gallery-card"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1677723864/breadboarding.jpg" class="kg-image" width="600" height="450"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1677723765/PXL_20230302_020751553.jpg" class="kg-image" width="600" height="450"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1677723741/PXL_20220416_093229174_1.jpg" class="kg-image" width="600" height="450"></div></div></div></figure> <p> It's a PCB that <i>only</i> has pinheaders and connectors on it. This board would have a nodeMCU module and an off-the-shelf rs485 module on it. The board itself is incredibly simple but it did give me a chance to run through the complete process of going from a schematic to an actual physical product. Since it has no actual components on it it has removed a lot of worry if the board will work at all.</p> <p>In this case the board was designed because I needed 15 of this design and this makes a lot more reliable and easy to maintain with the possibility to have neat screw terminals on it for the external connections.</p> <h2>Optimizing the design more</h2> <p>The above design was simple enough to not worry about optimizing it, but another project I wanted to take on is replacing my various sensor nodes with a neater custom design.</p> <figure class="kg-card kg-gallery-card"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1677724089/PXL_20210630_124752069.jpg" class="kg-image" width="600" height="450"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1677724099/PXL_20230302_021534355.jpg" class="kg-image" width="600" height="423"></div></div></div></figure> <p>These boards have been put together around various ESP8266 dev boards, most of them are nodeMCU v3 boards, some are off-the-shelf boards that I receive data from with an rtl-sdr.</p> <p>My plan is to make the one board to rule them all. A custom designed board that makes it easy to connect my existing sensors and allows a bit of extension. The design is based around an ESP-12F module since all my existing sensor code is already written for the various ESP8266 boards. It's also in stock at LCSC so that makes it a lot easier to get fabricated.</p> <p>The design goals for this custom board is:</p> <ul><li>On-board programmer for development</li> <li>Have the ESP module hooked up so it can deep sleep properly</li> <li>Have a built in temperature sensor. Most of my external sensor boards are already temperature/humidity sensors so it makes sense to just include it since it&#x27;s a cheap chip.</li> <li>Have a screw terminal for hooking up onewire devices</li> <li>Expose a silkscreen marked SPI and I2C bus</li> <li>Be able to be powered with an USB Type-C cable or a single lithium cell.</li> <li>Have a built-in charger for the lithium cell.</li> <li>The board should be able to be assembled by <a href="https://jlcpcb.com/">JLCPCB</a> and <a href="https://aisler.net/">Aisler</a></li> </ul> <p>For implementing this I mostly looked at the nodeMCU v3 schematic as reference and also made the basic layout of the PCB similar to that board. Initially I wanted to add the very popular DS18B20 onewire temperature sensor on-board but that got replaced with the SHT30 module that also includes a humidity sensor and connects to the I2C bus instead.</p> <p>For the programming and USB part I included the CP2102 USB-to-serial converter chip that was also included on the nodeMCU board. It is probably overkill for this application though since I'm not using any of the functionality of that chip beside the RX/TX pins. I dropped the auto-reset circuit since I was worried it would interfere with the deep sleep reset circuit that I found more important.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677725133/20230301_0022.jpg" class="kg-image"></figure> <p>One simple change from the nodeMCU design is that I swapped the USB Micro-B connector for a Type-C connector which requires a bit more PCB routing to deal with the reversable plug and two extra resistors to make it up to USB spec. The Type-C spec requires a 5.1kΩ resistor to ground on both CC pins (individually, don't cheap out and connect both CC pins to the same resistor like on the Raspberry Pi)</p> <p>Most of this design work is simply reading the datasheets for the components and following the recommendations there. Some datasheets even have bits of recommended PCB layout in it to make the PCB layout easier.</p> <p>Since all the complicated ESP8266 circuitry is already handled on the SoM I used and the USB-to-serial converter is laid out, the rest is simple connectors. Except... there's power stuff to deal with.</p> <p>Most of the difficulty with this design is figuring out the power design. If the battery connection was not included it would've been relatively straightforward. Just have a power regulator that converts the 5V from the USB connector to the 3.3V for the rest of the board. This is exactly what the nodeMCU board does.</p> <p>In my case the regulator has to deal with power coming from either USB or a single Lithium bringing the input voltage range from 3.4-ish to 5V. There's also a charger chip included to charge the connected cell and a bit of circuitry to switch between the battery and USB power.</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677725740/blockdiagram-mqttboard.png" class="kg-image"><figcaption>The block diagram for the sensor board</figcaption></figure> <p>The diagram above shows the final power design. It has the MCP1700 3.3V regulator to replace the LM1117 regulator from the nodeMCU design, the MCP part has a way lower quiescent current which helps with the battery life. The MCP73832 is a single cell li-ion/li-po charger that requires very few external components. In this design it's programmed to charge at 400mA which is not super fast but it should last some months on a single charge anyway.</p> <p>The magic part is this circuit for the power switching:</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677726058/image.png" class="kg-image"></figure> <p>This receives 5V from the USB connector or the battery power (or both) and feeds them into the regulator to generate the 3.3v rail. The MOSFET will disconnect the battery power when 5V is supplied and the diode makes sure that the power of the battery won't ever flow back into the 5V part of the schematic.</p> <p>Since there's also a single analog input on the ESP8266 I thought it would be a good idea to use it to read the battery level. This is yet again a decision that added a bunch more complexity. The theory for this is pretty simple: you add a resistor divider to bring the 0V-4.3V of the battery to the 0V-1V range of the analog input and then you'd read the battery voltage with a bit of code. The issue of this is that the resistor divider will not only do the voltage scaling for the ADC but will also be a path for the battery to leak power.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677726538/image.png" class="kg-image"></figure> <p>This is the part that's added to fix that. With a few extra parts the resistor divider can be completely disconnected from the battery stopping the power leakage. The software on the ESP will have to first enable the measuring circuit by toggleing a gpio, reading the value with the ADC and then disableing the gpio again. Since there's already 5.1kΩ resistors on the board I'm using another one here together with a 100kΩ. This re-use of resistor values is done since some manufacturers charge extra per distinct part model used. This brings the voltage range of the battery down to about 0-0.2v. This seemed fine at the time since the ESP has a 10-bit ADC.</p> <p>With using only a fifth of the ADC range the ADC resolution is already brought down to slightly less than 8 bits. But batteries usually don't go down to zero volts. The normal voltage range is 3-4.2V giving a voltage difference of 0.06V after the voltage divider making the result slightly less than 6 bits of resolution. This does not account for ADC noise in the system yet. The result is that the final battery level graph is a bit choppy but at least it functions.</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677727433/image.png" class="kg-image"></figure> <p>The last thing left is connectors. I added a footprint for some screw terminals for the OneWire connection. This footprint is basically the same as a pinheader except it has a fancy outline. For the battery I added a JST-PH connector because it seemed the most common one for batteries.</p> <p>The I2C and SPI bus don't have standardized connectors though. But, through a bit of searching I found the <a href="https://digilent.com/reference/_media/reference/pmod/pmod-interface-specification-1_2_0.pdf">pmod spec</a>. This is an open specification made by Digilent to connect modules to their FPGA development boards. It's perfect for this usecase since the interface is just pinheaders with a defined pinout and it defines the power supply as 3.3V which I already have on the board. Some of the sensors I want are even available as pmod modules.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677760255/20230301_0027.jpg" class="kg-image"><figcaption>The two pmod headers on the board</figcaption></figure> <h2>Getting the boards made</h2> <p>After laying out the PCB another complicated process begins. Getting it actually fabricated and assembled. The board has been designed that it can be hand-soldered if needed. No BGA parts are used and the <code>_handsolder</code> variants of the footprints are used for the common parts, which are slightly larger.</p> <p>To not have vendor lock-in the board is designed to be assembled by both JLCPCB and Aisler. JLCPCB is the very cheap option in this case and Aisler is quite neat since it's actually in The Netherlands. The Aisler design rules are less forgiving than JLCPCB so I used those for the board. It mostly mean that I can't use the absolutely tiny vias that JLCPCB can drill.</p> <p>For the assembly service metadata has been added to the schematic. For JLCPCB a <code>LCSC</code> column was added to the part data that contains the part code for ordering from LCSC. For Aisler the parts come from the other big parts warehouses instead like Mouser. For that the <code>MPN</code> column is added that contains the manufacturer part number.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677761073/image.png" class="kg-image"></figure> <p>With this data and the selected footprints the board can be assembled. I left out all the part numberes for the through-hole parts since those are either more expensive or impossible to assemble and also pretty easy to solder manually if required on that specific board.</p> <p>To actually get it made Aisler has a nice plugin for the PCB editor in Kicad: <a href="https://github.com/AislerHQ/PushForKiCad">Aisler Push</a>. With this it's just a single button and the Kicad file will be sent off to Aisler where their servers will generate the necessary fabrication files from it. From there it's using the Aisler website to fix up the MPN matching with actual parts from various suppliers and pressing order.</p> <p>For JLCPCB the process is more complicated. The fabrication files have to be generated manually. There's <a href="https://support.jlcpcb.com/article/194-how-to-generate-gerber-and-drill-files-in-kicad-6">a tutorial</a> for going through the various steps of generating the ~10 files you need and then those can be zipped up and uploaded to the website. Since the LCSC part codes are completely unique the assembly step of this process Just Works(tm) without having to adjust anything on the website.</p> <p>If Aisler had an option to specify the exact part match in the schematic metadata instead it would probably be the easiest option in this case so I hope that gets figured out in the future. Two weeks later I had the board :)</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677762386/20230302_0002.jpg" class="kg-image"></figure> <h2>The Firmware</h2> <p>For the firmware of this board I just re-used the one I had previously written for one of the nodeMCU based sensor boards and extended it for the sleep features. It's pretty basic firmware that does the standard WiFi connection things for the ESP module and connects to my MQTT server to push the sensor readings in a loop.</p> <p>For the battery operated boards that wouldn't be enough though. With that firmware the battery would run out in hours. There's a great series of blog posts from <a href="https://www.bakke.online/index.php/2017/05/22/reducing-wifi-power-consumption-on-esp8266-part-3/">Oppoverbakke </a>that go into great detail on how to optimize deep sleep on the ESP module. Especially the last post on <a href="https://www.bakke.online/index.php/2017/06/24/esp8266-wifi-power-reduction-avoiding-network-scan/">avoiding WiFi scanning</a> is very helpful for reducing power use.</p> <p>To avoid as many delays as possible the WiFi accesspoint mac address and channel are saved into the memory of the RTC inside the module which is the only part of the chip left powered in the deepest sleep mode. Then with that information the module will save around one second of time being awake and connecting. Another optimisation is using a static ip address instead of DHCP to save another second.</p> <p>I don't like having static ip addresses in my network. I have static DHCP leases for everything instead. This is why I extended the RTC connection code to do a DHCP request the first time and then save the received DHCP data in the RTC memory together with a counter to re-do DHCP after some time. This means that my board is still a fully DHCP compliant device without making a DHCP request every time it wakes up, this is what the lease time is for after all. Thanks to this I only have to power down the board when I need to change the IP address instead of reflashing it or building in some configuration web interface.</p> <p>During deep sleep the whole board uses 19.1µA with a half charged battery in my tests and the transmission is down to to a bit less than a second at 70mA, but the power use of the transmissions varies quite a lot.</p> <p>I had one module in production outdoors quickly to test. It sleeps for one minute in deep sleep and doesn't have any other optimisations. Looking at the data from that module it looks like it would last for about two weeks, even with the sub-zero temperatures during the night.</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677763269/image.png" class="kg-image"></figure> <p>The battery life for the optimized version has yet to be seen. I have calculated it to be around 3 months but it would take a while to have conclusive results on that. This board uses a "3500mAh" 18650 cell which I assume in the calculations is actually 2000mAh.</p> <h2>Next revisions</h2> <p>During testing and programming I found out I had made a few mistakes with the board design. The most problematic one is that I put zener diodes on the UART lines between the USB-to-serial converter and the ESP module. This was to prevent power from the serial lines to flow into the CP2102 module and powering up that chip partially.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677763683/image.png" class="kg-image"></figure> <p>This did not work. When I got the boards I spend a bit of time figuring out why the programmer couldn't connect. The voltage drop through the diodes I picked is probably too much for the serial communication to work. To make things worse the diodes are completely unnecessary for this since the uart lines won't leak power in deep sleep anyway. Luckily this is easily fixable on my boards by making a solder bridge across the diodes.</p> <p>Another issue on the board is that the battery charger chip gets very hot and is too close to the temperature sensor. I also forgot to add the thermal relief specified in the layout suggestions of the part so while charging that chip is practically constantly at 80°C making the value of the temperature sensor raise by around 5°C. Since this only affects the board when the battery is charging it's not a critical fault.</p> <p>For cost optimization I skipped battery protection on the board. For my uses I just ordered a protected 18650 cell so it should be fine. But since I'm making a second revision board to fix these issues anyway I decided to include a battery protection chip this time around.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1677764022/image.png" class="kg-image"></figure> <p>The protection chip sits between the ground terminal of the battery and the actual board ground and disconnects the battery pack in case of overcurrent, short circuits, overcharge or undercharge.</p> <p>Another small change I did is add an extra pinheader to the board to access the UART lines directly, since pinheaders are basically free anyway.</p> <h2>You should make your own boards</h2> <p>Making boards is fun. I learned a lot during the process and now have some important skills to use in my other projects. Can you really call yourself a full stack programmer if you haven't designed the hardware yourself? :D</p> Reverse engineering the BMD camera HDMI controlhttps://blog.brixit.nl/reverse-engineering-the-bmd-camera-hdmi-control/613a2683aee9f6453ef1c02aElectronicsMartijn BraamThu, 09 Sep 2021 21:32:01 -0000<p>I have a Blackmagic Design 4k camera here (the bmpcc4k) and it has an interesting feature where you can control camera settings when hooking the hdmi cable from the camera to a BMD Atem mini series video switcher. It allows controlling a few settings that are not accessible in the camera settings itself like the values of the primary color corrector.</p> <p>This is the exact same thing that their more expensive cameras for studios can do by hooking up a SDI (video over coax) cable to it and hooking it into a more expensive ATEM switcher.</p> <p>The camera also has Bluetooth control which allows you to change some basic settings like resolution, audio settings and lens control. I have been able to use this but unfortunately I ran into a hardware defect where the touchscreen on the camera just stops working. At this point I did not have bluetooth switched on so I cannot even change my camera settings through that anymore. While this is a defect in the camera looking at the forums BMD did not really want to help with getting this fixed unless I pay <i>a lot</i> to get this fixed.</p> <p>The alternative is figuring out how the HDMI control works, figuring out how much more is possible over that protocol and try to enable bluetooth through that.</p> <h2>Guesses</h2> <p>So the camera is controlled over the same HDMI connection that's used to feed the signal to the atem switcher. This works even with the cheapest cables I have and cable manufacturers love reducing the amount of wires in their cables. This doesn't leave a lot of option where that data can be. All the high speed data pairs in HDMI are directional and are going in the camera->switcher direction so it's not using anything complicated inside the HDMI frame to get data across. Looking at some pinouts this basically only leaves the CEC and DDC stuff.</p> <p>DDC is the display data channel, it's an i2c bus that's used to query the information about the monitor that the cable is plugged in to. This exact same system is present in VGA, DVI, Displayport and HDMI. I guess the standard was good enough that it was just copied over to every newer one :)</p> <h2>Sniffing the DDC traffic</h2> <p>Messing with the DDC bus is usually pretty simple. On linux you can load the <code>i2c-dev</code> module and that will expose the DDC connections on all video outputs as <code>/dev/i2c-*</code> nodes. If I have my computer hooked into the HDMI input of an ATEM switcher I can dump the edid data over the bus using i2cdump for example:</p> <div class="highlight"><pre><span></span><span class="gp"># </span>Bus <span class="m">1</span> is my HDMI connector, 0x50 is the i2c address <span class="k">for</span> the EDID eeprom <span class="gp">$ </span>i2cdump -y <span class="m">1</span> 0x50 <span class="go">No size specified (using byte-data access)</span> <span class="go"> 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef</span> <span class="go">00: 00 ff ff ff ff ff ff 00 23 01 00 00 01 00 00 00 ........#?..?...</span> <span class="go">10: 26 10 01 03 80 47 28 96 0a da ff a3 58 4a a2 29 &amp;????G(???.?XJ?)</span> <span class="go">20: 17 49 4b 00 00 00 01 01 01 01 01 01 01 01 01 01 ?IK...??????????</span> <span class="go">30: 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c ???????:??q8-@X,</span> <span class="go">40: 45 00 c4 8e 21 00 00 1e 02 3a 80 d0 72 38 2d 40 E.??!..??:??r8-@</span> <span class="go">50: 58 2c 45 00 c4 8e 21 00 00 1e 00 00 00 fc 00 42 X,E.??!..?...?.B</span> <span class="go">60: 4d 44 20 48 44 4d 49 0a 20 20 20 20 00 00 00 fd MD HDMI? ...?</span> <span class="go">70: 00 32 3c 1f 44 08 00 0a 20 20 20 20 20 20 01 f7 .2&lt;?D?.? ??</span> <span class="go">80: 02 03 1f 54 49 85 84 94 93 a0 a1 a2 9f 90 23 09 ???TI?????????#?</span> <span class="go">90: 04 07 83 01 00 00 68 03 0c 00 10 00 00 1e 00 01 ????..h??.?..?.?</span> <span class="go">a0: 1d 00 72 51 d0 1e 20 6e 28 55 00 c4 8e 21 00 00 ?.rQ?? n(U.??!..</span> <span class="go">b0: 1e 01 1d 00 bc 52 d0 1e 20 b8 28 55 40 c4 8e 21 ???.?R?? ?(U@??!</span> <span class="go">c0: 00 00 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 ..?.............</span> <span class="go">d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................</span> <span class="go">e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................</span> <span class="go">f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f2 ...............?</span> </pre></div> <p>Doing the same for the camera is a lot harder though. Since the direction for that is reversed the computer has to be i2c slave on the hdmi connector instead which is not something that's just easily doable.</p> <p>The other option is just splicing into the HDMI cable and finding the right wires. So that's exactly what I did.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072506/PXL_20210909_151933077.jpg" class="kg-image"><figcaption>The answer for &quot;how to get the shielding off like this&quot; is: carefully</figcaption></figure> <p>After removing a part of the outer isolation and shielding from an HDMI cable it looks like there are a bunch of high speed differential pairs with individual shielding. Those are not interesting for the data I'm looking for, they will be the clock pairs and a pair for red, green and blue pixels (or YUV in some modes). Besides those pairs there are 4 wires left over and a ground wire.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072506/PXL_20210909_154213804.jpg" class="kg-image"></figure> <p>DDC needs two wires to work, SDA and SCL for data and clock, and it needs the ground wire. The other two wires might be the +5V wire that was originally intended to power the EDID chip when the monitor is off. And a wire for hotplug detection of the connector. Since I don't care that much for the signal integrity of the cable, and these wires carry slow signals. I just decided to cut them and splice in some leftover resistor leads to heave points to attach probes to.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072506/PXL_20210909_161413183.jpg" class="kg-image"></figure> <p>Then I just connected probes to these wires and hooked it up to a CWAV USBee AX 8 channel logic analyzer. With that connected and the wire colors correctly labeled I ran a 10 second dump in the sigrok pulseview software and plugged in the camera.</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072506/image.png" class="kg-image"></figure> <p>This was not what I expected. None of this looks like i2c data since there would be a clock and data pair. After looking at the HDMI pinouts some more I realized my mistake. There's 5 differential shielded pairs in my cable, one of them is used for the DDC data pair. But then what is happening on D4?</p> <p>One of the other busses in the HDMI cable is CEC, the Consume Electronics Control signalling. This is used to send commands from your TV to a set-top box or DVD player. Instead of being a two wire bus this is a single wire bidirectional signalling system that follows the AV.link standard. Let's label that wire and see what Pulseview can decode from it.</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072507/image-1.png" class="kg-image"></figure> <p>Here is the view with the CEC decoder added and zoomed in on the initial little blip of data on the CEC wire. So what's happening here?</p> <p>The bottom row of the decoder shows the actual highest level decoding of the bits, in this case it's 2x the same message. CEC uses 4 bits for addressing and the first byte of a packet contains the sender and receiver address. The blue "EO..." bit in this screenshot is the End Of Message byte which is true in this message indicating there's no more data for this frame. A frame with only the addresses is a PING command as indicated by the decoder. This is a message send by the device on power-up to check if a device at address 1 (Recording_1) already exist on the bus. If such a device exists it will pull the CEC line down directly after the EOM bit. In this case that didn't happen which results in the NACK bit at the end of the message.</p> <p>So what's happening here is that either the camera or the switcher is checking the CEC bus to see if the address it wants to use already exists and not getting a ping response 2 times.</p> <p>So the next message:</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072507/image-2.png" class="kg-image"></figure> <p>So here device 1 is sending a broadcast (destination 0xF) and it contains 3 more bytes of data. This means the second byte (0x84) is the opcode. In this case it's REPORT_PHYSICAL_ADDRESS and the last 2 bytes are the arguments for the command. There seems to be 2 addressing systems in CEC, the 4 bit address that's used for routing packets and a physical address, and this is the device at CEC address 1 broadcasting that it's physical address is 0x20 which decodes to address 2.0.0.0. The camera is connected to port 2 of the atem, if I move it to port 4 it becomes 4.0.0.0.</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072507/image-3.png" class="kg-image"></figure> <p>The the next command is a vendor command, so here the guessing starts again. the 0xA0 opcode that means the next 3 bytes are the vendor ID and the rest is the vendor specific command.</p> <p>The 0x7c2e0d that's received after that is the mac OID for Blackmagic Design, so we're getting somewhere at least with this. Then the next bytes are 0x01 and 0x01. Now the hard part is figuring out what that means.</p> <h2>BMD vendor commands</h2> <p>So I found that by connecting a raspberry pi to the atem switcher I can monitor the CEC traffic it gets. Sadly it doesn't get a copy of all the CEC traffic happening in thee switcher as the standard says it should do. It does get CEC broadcast data instead.</p> <p>From a quick look at some atem features that causes broadcasts to all devices, the first byte of the vendor data is a command ID and the rest is the argument. Command 0x03 is used to start/stop recording on all attached hardware where the first argument is 0x01 to start and 0x00 to stop.</p> <p>Command 0x02 is the tally status command with the bytes following it being a bitfield for the tally. From a 4 port atem mini:</p> <pre><code>The program/preview controlled by the hardware panel: Program 1, Preview 1: 0x03 0x00 Program 2, Preview 2: 0x30 0x00 Program 3, Preview 3: 0x00 0x03 Program 4, Preview 4: 0x00 0x30 Seperate program and preview tally: Program 1, Preview 3: 0x01 0x02</code></pre> <p>So the data for this command is 2 bits per input, 2 bits wasted. The whole thing is little endian.</p> <p>So my protocol docs so far:</p> <table> <thead> <tr> <th>cec vendor command</th> <th>command</th> <th>args</th> <th>notes</th> </tr> </thead> <tbody> <tr> <td>0x1f 0xa0 0x7c2e0d</td> <td>0x01</td> <td>1 byte</td> <td>unknown</td> </tr> <tr> <td>0x1f 0xa0 0x7c2e0d</td> <td>0x02</td> <td>4 bits per channel</td> <td>Tally status<br>bit 0 is preview<br>bit 1 is program<br>bit 2 and 3 are reserved</td> </tr> <tr> <td>0x1f 0xa0 0x7c2e0d</td> <td>0x03</td> <td>1 byte</td> <td>Trigger record<br>0x00 = stop<br>0x01 = start</td> </tr> </tbody> </table> <p>Interestingly, from sniffing the HDMI cable it looks like the color shading commands for the camera don't use the cec vendor command but they use just random (as far as I can tel) CEC opcodes. I have not been able to find just a list of CEC opcodes and their arguments so I can't really verify this. The CEC decoder in PulseView also doesn't seem to support all opcodes.</p> <h2>Camera control commands</h2> <table> <thead> <tr> <th>addr</th> <th>opcode</th> <th>args</th> <th>notes</th> </tr> </thead> <tbody> <tr> <td>0x01</td> <td>0x19</td> <td>?</td> <td>Lift W</td> </tr> <tr> <td>0x01</td> <td>0x19</td> <td>?</td> <td>Lift W</td> </tr> <tr> <td>0x01</td> <td>0x1e</td> <td>?</td> <td>Saturation</td> </tr> <tr> <td>0x01</td> <td>0x1d</td> <td>?</td> <td>Hue</td> </tr> <tr> <td>0x01</td> <td>0x20</td> <td>?</td> <td>Contrast</td> </tr> <tr> <td>0x01</td> <td>0x42</td> <td>u16</td> <td>Aperture</td> </tr> <tr> <td>0x01</td> <td>0x46</td> <td>i16</td> <td>Zoom</td> </tr> <tr> <td>0x01</td> <td>0x52</td> <td>i8</td> <td>Gain, signed byte<br>0xf4 = -12dB</td> </tr> <tr> <td>0x01</td> <td>0x54</td> <td>u8</td> <td>White balance<br>0x32 = 2500k, 50k per step</td> </tr> <tr> <td>0x01</td> <td>0x55</td> <td>u8</td> <td>White balance tint<br>Unknown format</td> </tr> <tr> <td>0x01</td> <td>0x57</td> <td>i8</td> <td>Shutter</td> </tr> </tbody> </table> <p>So this is a list of a few of the full CEC packets sent when shading a camera, some of the signed values are a bit weird, becoming 2 bytes when they are negative and 1 byte when they are positive. Here's the contrast message for example:</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072507/image-4.png" class="kg-image"></figure> <p>Most of these controls don't have a neat numeric value to compare against in the ATEM Software Control software so to get the exact encoding it would be required to implement that in <a href="https://openswitcher.org/">OpenSwitcher </a>first.</p> <p>Also to figure out the commands that are not implemented yet in ATEMSC I need to find a way to inject CEC commands between the camera and the atem. There's some USB solutions that might work, it's also possible with an arduino and a HDMI breakout. In either case I need to wait for parts so that will be a part 2 of this series.</p> <p>With the protocol decoded it would be possible to do a few neat things, like make a cheap (and I mean really really cheap) injector to put between the network and the HDMI cable of the camera to remotely shade one of these cameras when you're not on the ATEM mini hardware but on the older generation of hardware instead.</p> Making a backcover extension for the PinePhonehttps://blog.brixit.nl/making-a-backcover-extension-for-the-pinephone/5f22edfb7725960d859ca2d7PhonesMartijn BraamFri, 31 Jul 2020 10:16:41 -0000<p>So one of the features of the PinePhone that hasn't seen much (any?) use is the pogo pin expansion header on the back. It's 6 pogo pins that can be used to make backcovers that extend the phone functionality.</p> <p>These pogo pins give external access to battery power, usb power, a dedicated i2c bus and an interrupt pin. This is enough to make a wide range of custom attachments. The official announced attachments are a battery extension backcover (that uses the two power pins) and a keyboard backcover (that uses the i2c bus).</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072496/DSC0011-cen.jpg" class="kg-image"></figure> <h2>Making your own extension</h2> <p>Since this is an open platform, it's quite easy to make your own extension that connects to the PinePhone pogo pins. In my case I'm going to attach an <a href="https://www.melexis.com/en/product/MLX90640/Far-Infrared-Thermal-Sensor-Array">MLX90640 far-infrared thermal array</a>, which is a fancy way of saying thermal camera. It's a module that can be get relatively cheap, but it's low resolution. The sensor only has 32x24 pixels, to get a good image that has to be upscaled and combined with a visible light camera (which conveniently is built into the PinePhone) to create a nice thermal image.</p> <p>To make the physical interface, I melted 6 holes through a PinePhone backcover and put tiny nails in them that match up with the pogo pins.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072497/DSC0016.jpg" class="kg-image"><figcaption>I didn&#x27;t have 6 nails of the same size ofcourse</figcaption></figure> <p>I also made a <a href="https://www.youtube.com/watch?v=lFsQpd0bLTY">video showing me doing this</a>. Then I used some double sided tape to stick the thermal camera module to the backcover and soldered wires between the sensor and the nails. The pinout for the pogo pins is this:</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072497/rect46533.png" class="kg-image"></figure> <p>Connecting up an i2c device should be relatively straightforward, it's important to note that the i2c lines are pulled up by the phone to 3v3.</p> <p>The VBUS pin is powered by USB, I used this one for the sensor, it only has power when something is plugged into USB, and it's 5V. </p> <p>The second power pin is VBAT, which connects to the battery voltage.</p> <h2>What can you do with this?</h2> <p>Everything! The sky is the limit! except bandwith and power constraints ofcourse...</p> <p>The maximum speed of the i2c bus is 400 kHz, which is enough for a lot of applications, but don't expect to add ethernet or hdmi to this. Since i2c is a bus you can add multiple things at the same time to these pins. I just added the single thermal camera but you can just add a second i2c device to the same pins in parallel.</p> <p>If you need another bus than i2c you can probably get a bridge chip for it or add a small microcontroller to bridge another protocol. Basically any microcontroller has support for i2c and can most likely connect to whatever you want, like use gpio on the microcontroller to read from a keyboard matrix, or add some IR leds and sensors to add IrDA. </p> <h2>The software side</h2> <p>There are two ways to use your newly attached gadget. The first is i2cdev. It isn't super efficient but it gives you access to the i2c hardware directly from userspace. The only thing that's needed is <code>modprobe i2cdev</code>to make /dev/i2c* appear. The bus on the pogopins on the PinePhone is twi2, so <code>/dev/i2c-2</code> should be the right bus.</p> <p>Here's a simple python example for reading a byte from and attached i2c chip:</p> <div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">smbus2</span> <span class="kn">import</span> <span class="n">SMBus</span> <span class="c1"># Connect to /dev/i2c-2</span> <span class="n">bus</span> <span class="o">=</span> <span class="n">SMBus</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># Read register 0x42 from the i2c device at address 0x33</span> <span class="n">bus</span><span class="o">.</span><span class="n">write_byte_data</span><span class="p">(</span><span class="mh">0x33</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x42</span><span class="p">)</span> <span class="n">value</span> <span class="o">=</span> <span class="n">bus</span><span class="o">.</span><span class="n">read_byte_data</span><span class="p">(</span><span class="mh">0x33</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> </pre></div> <p>The second method is letting a kernel module deal with the hardware. This can be accomplished by recompiling the dtb/kernel with your hardware in it, which isn't very portable. Or by using device tree overlays.</p> <p>Here's an example of the dtbo for an TI HDC1000 humidity and temperature sensor:</p> <div class="highlight"><pre><span></span><span class="c1">// Definitions for dhc1000 module</span> <span class="cp">/dts-v1/</span><span class="p">;</span><span class="w"></span> <span class="cp">/plugin/</span><span class="p">;</span><span class="w"></span> <span class="nf">/</span><span class="cm"> </span><span class="p">{</span><span class="w"></span> <span class="nf">fragment</span><span class="o">@</span><span class="mi">0</span><span class="cm"> </span><span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="c1">// Target the pogo pin i2c bus</span> <span class="w"> </span><span class="n">target</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&lt;&amp;</span><span class="na">i2c2</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> <span class="w"> </span><span class="nf">__overlay__</span><span class="cm"> </span><span class="p">{</span><span class="w"></span> <span class="w"> </span> <span class="w"> </span><span class="c1">// Define the hdc100x device on this i2c bus</span> <span class="w"> </span><span class="nf">hdc100x</span><span class="o">@</span><span class="mi">40</span><span class="cm"> </span><span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="kr">compatible</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&quot;ti,hdc1000&quot;</span><span class="p">;</span><span class="w"></span> <span class="w"> </span><span class="kr">reg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&lt;</span><span class="mh">0x40</span><span class="o">&gt;</span><span class="p">;</span><span class="w"></span> <span class="w"> </span><span class="p">};</span><span class="w"></span> <span class="w"> </span> <span class="w"> </span><span class="p">};</span><span class="w"></span> <span class="p">};</span><span class="w"></span> </pre></div> <p>The loading of this device tree overlay is more complicated. It needs to be compiled first, which needs the sources of the kernel used on the PinePhone so it knows what &i2c2 means. Then the compiled dtbo file needs to be put somewhere in sysfs to make it load at runtime, or needs to be added to u-boot to be loaded at boot time. This is all a bit out of scope for this article (meaning I haven't tried it yet)</p> <p>After the dtbo is loaded, the new device should just show up in the kernel like it was always part of the phone, in this example it's an iio sensor so it shows up in /sys/bus/iio/device:$number/$parameter, so you can just query the humidity by reading some file.</p> <h2>Connecting a thermal camera</h2> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072497/DSC0015.jpg" class="kg-image"></figure> <p>One of the things I have had here in my parts bin is the MLX90640 thermal imaging sensor. I thought it would make for a great demo of what's possible with the pogo pins. Connecting it was trivial since it only needs power and i2c on the breakout board I have and this breakout board also has a voltage regular so I can power it by anything from 3 to 6 volts, perfect for the PinePhone.</p> <p>For the software side I chose the i2cdev method and used python to get data from the sensor. Only after a while I found out that there's already a kernel module available in the mainline kernel that exposes this sensor as a regular v4l2 webcam.</p> <p>The first python lib I tried was the Adafruit circuitpython one, I didn't manage to get this to work since it expects if you're running it on Linux that it's must be a raspberry pi. Then I found a module from Seeed studio which seemed easier but still didn't give me any data from the sensor after messing with it for a while. It turns out the seeed and adafruit code is very similar and probably copied from eachother and had the interfacing code replaced.</p> <p>Since both libraries weren't working I wrote a quick 'n dirty python script based on the registermap in the datasheet of the sensor and managed to get the raw sensor data from that. This code doesn't read the factory calibration data yet from the sensors eeeprom so there's quite a lot of variance in the pixels. The raw data from the pixels is also 16 bit signed data, which I just pick the 8 most significant bits from to make a simple grayscale png as output. Since somewhere around room temperature these raw values go negative there's some pure white pixels instead of useful data.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072497/pixels.png" class="kg-image"><figcaption>Left: raw pixels, top right: properly interpolated and upscaled, bottom right: more familiar color map applied</figcaption></figure> <p>The raw data from the sensor looks a bit underwhelming, it's mainly messed up by not applying calibration which should get rid of the checkerboard pattern and the data format which should get rid of the white pixels. </p> <p>The sensor actually responds with 2 extra rows of "pixels", this data is the extra data needed to get proper measurements from this like the global gain of the chip (since it does auto exposure) and the ambient chip temperature. With that data the absolute temperature for every pixel can be calculated.</p> <p>For getting real nice "FLIR-like" output from this it should also be combined with the image captured from the way higher resolution normal camera in the PinePhone itself.</p> <h2>Conclusion</h2> <p>Is this demo practical? not really, the camera works but since it's quite bulky the phone can't be put in your pocket anymore. This can probably be solved by using a 3d printed backcover instead that properly integrates the camera and keeps it aligned with the visible light camera. This also needs a lot more software work to make it a nice experience.</p> <p>It does provide a great demo of the extendability though and I can't wait to see what people will do with this feature.</p> E-paper status displayhttps://blog.brixit.nl/epaper/5ea5931a6cd0491609d8f934ElectronicsMartijn BraamMon, 27 Apr 2020 18:00:00 -0000<p>There's a project called LCD Smartie that converted a character LCD into a small status monitor for your computer. It can show system status, the info on what's playing in Winamp. E-mail headlines.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072493/4x20_hd44780_bars2.jpg" class="kg-image"><figcaption>LCD Smartie example (from lcdsmartie.sourceforge.net)</figcaption></figure> <p>I wanted to have something similar, but connected to my fileserver to show some stats, but I didn't want to have yet another permanent light on my desk, it's already a chrismas lightshow of blue, green and orange power and status lights for various devices.</p> <p>The nice solution for this would be using an e-paper display instead of an LCD. After some searching I found the <a href="https://www.waveshare.com/6inch-e-paper-hat.htm">6" display kit with raspberry pi hat</a> from Waveshare.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072493/epaper-out-of-the-box.jpg" class="kg-image"><figcaption>The kit with an image uploaded through the Windows software</figcaption></figure> <p>The nice thing about this specific board is that it also has a microUSB port for connecting it to a computer instead of a raspberry pi. Sadly there isn't really a lot of information available about this interface, it's designed to be driven by spi, i2c or i80 and the docs reflect that. The USB port seems to be mainly for updating firmware. </p> <p>Waveshare has a Windows tool available that can communicate with the display controller over USB and upload firmware, it can also send images to it with their clunky interface. I want to control it with Linux though so I have to write something new.</p> <p>The USB interface is pretty weird. The display shows up as a mass storage device with no medium inserted (like an SD card reader with no card inserted). Somehow their software can communicate with this. So the first step is <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procmon">sysinternals procmon</a>. This is basically equivelent to running a strace in Windows. </p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072493/tcon-demo.PNG" class="kg-image"></figure> <p>This shows it's opening the virtual mass storage device from the display controller (F: in my case) and then using IOCTL_SCSI_PASS_THROUGH_DIRECT to actually control the display. </p> <p>This is the same communication protocol used by tools like smartmontools to communicate with harddisk firmware to get disk information. So the upside is that it should be possible to control this display without messing with kernel modules. The downside is that it will require root to control the display.</p> <p>With this information I was finally able to find a datasheet that had some details on the USB communication protocol called "<a href="https://www.waveshare.com/w/upload/c/c9/IT8951_USB_ProgrammingGuide_v.0.4_20161114.pdf">IT8951 USB Programming Guide</a>" This contains some details on how the protocol works and some demo code for making these calls in Windows. Armed with this information I started writing a C program that should draw to the display.</p> <p>The drawing procedure is pretty simple. You send a command to get some general information like the resolution and the address of the framebuffer. Then you use the load image command to send pixels to a specific part of the framebuffer, then you use the display command to actually trigger the display refresh. This is the part that makes the display flicker and show the new content.</p> <p>It was all pretty straightforward except that it didn't work. My display just didn't start flickering to update the contents. After scanning through the datasheet for a while I gave up and went back to reverse engineering the Windows tool. This time I used Wireshark to make a dump of all the USB traffic since procmon didn't show me the actual data in the syscalls. </p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072494/wireshark.png" class="kg-image"></figure> <p>This did show some interesting things. Mainly that I was missing a single byte in the display update command (0x94) causing the display controller to hang until it was reset. Another interesting thing I noticed is that for uploading a full-screen image (800x600) it was sending the image in strips of 800x76 pixels using seperate load-image commands and then sending a single display-image command to update the whole screen. After implementing this I was able to update my e-paper over USB from Linux!</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072494/IMG_20200426_131543--1-.jpg" class="kg-image"></figure> <p>All this is available as a simple small tool: <a href="https://git.sr.ht/~martijnbraam/it8951">it8951</a> (named after the controller used). It takes a device name, x+y coordinates and the image width,height to update on the screen and then takes raw image data on stdin to display. The nice thing about this controller is that you don't have to care at all about low level image formatting, just send a 8-bit grayscale image to it and it will be converted in the right format to the display.</p> <p>This display supports multiple modes (selected by the -m argument of my tool). Mode 0 is used to completely blank a region. It will flicker a few times and then make the region perfectly white.</p> <p>The next one is Mode 2, it will display a 16 level grayscale image based on the 256 level input image you gave it, it will not do any dithering for this. Updating the screen in this mode will cause it to flicker that region once. This is called G16 mode.</p> <p>The other nice mode is Mode 4, it is called A2 mode and will update the screen without flickering. It is very fast but it has a downside, it only renders pure black and white. </p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072494/DSC0112.jpg" class="kg-image"><figcaption>The date is drawn in G16 mode and the time in A2 mode</figcaption></figure> <p>In the example above the date is drawn in G16 mode, since it's not updated often anyway and the time is drawn in A2 mode so it can update every minute without having an annoying flicker. You can see that the date has smoother edges since it can be anti-aliased using the 16 grayscale levels, The time has crispy edges since it's only pure black. Also the A2 move leaves some ghosting behind as you can see.</p> <h1>Drawing something useful</h1> <p>Now I have the display working with a tool I can pipe data into I made a python program that uses PIL to generate a nice status display. It generates a full screen background image that has the dates and the borders on them and draws that every day at midnight with a fresh date. It also uses this moment to fully clear the display in mode 0 to get rid of ghosting artifacts. Then it will update the time every minute in A2 mode in the center. It will then check all the data feeds and if one changed it will update only that specific region in G16 mode, which is distracting, but at that point it will also notify you that there's new data. When the python script is restarted it will update all the regions one by one, which looks very satisfying.</p> <iframe width="100%" height="360" src="https://www.youtube.com/embed/uReT5aMYeug?feature=oembed"> <p>The next step is making a nice wooden frame around it so it can stand on my desk.</p> Harley Benton (Thomann) Power Attenuator test and teardownhttps://blog.brixit.nl/harley-benton-thomann-power-attenuator-test-and-teardown/5e91c26e2cc6a25f176027a0ElectronicsMartijn BraamFri, 26 Feb 2016 23:00:00 -0000<p>Thomann just started shipping their new <a href="https://www.thomann.de/gb/harley_benton_power_attenuator.htm">Harley Benton Power Attenuator</a>. It can handle an input power of<br>80 watts (it contains an 100 watt L-pad, so they added some margin). It is a simple device containing<br>only passive components. It has a control for cabinet volume and line-out volume but they are not fully<br>seperated. The line-out level is a signel tapped of after the main cabinet volume knob so you can't<br>turn the cabinet completely off and use the line out for recording / headset usage.</p> <figure class="kg-card kg-gallery-card"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072477/att1.jpg" class="kg-image" width="600" height="400"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072478/att2.jpg" class="kg-image" width="600" height="400"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072478/att3.jpg" class="kg-image" width="600" height="400"></div></div></div></figure> <h2>Measurement</h2> <p>To measure the performance of the attenuator I connected it to a USB DAC/ADC and ran a frequency sweep with the<br>tool QLoud. QLoud is a tool normally used to measure the frequency response of a speaker using a measurement microphone.</p> <p>I connected the line out signal of the DAC to the 16 Ohm speaker input of the attenuator and connected the<br>speaker output of the attenuator to the line in of the USB DAC. I also did a reference measurement by<br>connecting the input and output of my DAC/ADC directly together. The last set of measurements is connecting<br>the line signal to the input of my guitar tube amp (a Defender 5H 15 watt all-tube amp) and connecting the<br>line-out of the attenuator to the USB ADC.</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072478/measurements.png" class="kg-image"></figure> <h2>Taking it apart</h2> <p>I'm also curious what is inside a fully passive device for ~100 euro so I took it apart. The case is<br>very easy to open, 4 screws on the front and 4 on the back and you can just lift the side and top part off.</p> <p>The answer to my question: there is not a lot in it.</p> <p>The main part of the attenuator is the 100 watt L-pad (The huge potentiometer behind the cabinet volume knob).<br>The signal from the amp is put almost directly accross the two outer pins of the potmeter so it will dissipate<br>most of the energy. The other large part of the circuit is a bunch of 50 watt resistors (The resistors with<br>orange cooling fins). They are there to match the impedance for the amp. The signal coming from the L-pad pot<br>and the small potmeter for line-out is put straight into the line-out and speaker-out jack on the PCB. The rest<br>of the circuit on the PCB is for the MicMod circuit that is supposed to simulate a cabinet and provide a balanced<br>microphone signal. I haven't tested the MicMod output yet.</p> <figure class="kg-card kg-gallery-card"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072478/open1.jpg" class="kg-image" width="600" height="400"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072478/open2.jpg" class="kg-image" width="600" height="400"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072479/open3.jpg" class="kg-image" width="600" height="400"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072479/open4.jpg" class="kg-image" width="600" height="400"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072479/open5.jpg" class="kg-image" width="600" height="400"></div></div></div></figure> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072479/schematic.jpg" class="kg-image"><figcaption>Yeah, I&#x27;m not so great with CAD software</figcaption></figure> Modding a Behringer VT911https://blog.brixit.nl/modding-a-behringer-vt911/5e91c8692cc6a25f1760282bElectronicsMartijn BraamSun, 03 May 2015 22:00:00 -0000<p>A few weeks ago I replaced my Line6 Podxt live multieffect pedal with a guitar amp, cabinet and 2 pedals.<br>The amp is a Kustom Defender 5H head and I use a Kustom 1x12 cab with it.</p> <p>To replace the multieffect pedal I bought a Behringer VT911 overdrive pedal and a<br>Digitech Digital Reverb pedal. I chose for the VT911 because it contains a tube for the distortion and it<br>only cost me € 14,90 at <a href="http://www.thomann.de/gb/behringer_vt911_vintage_tube_overdrive.htm">Thomann</a>.</p> <p>The pedal sounds fine. Not great but it's a great pedal for the price. I searched on the internet<br>for easy mods to do on the pedal to improve the sound quality and found various suggestions on a few forums.</p> <figure class="kg-card kg-gallery-card"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072484/mod1.jpg" class="kg-image" width="600" height="397"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072484/mod2.jpg" class="kg-image" width="600" height="397"></div><div class="kg-gallery-image"><img src="https://blog.brixit.nl/image/w600//static/files/blog.brixit.nl/1670072484/mod3.jpg" class="kg-image" width="600" height="397"></div></div></div></figure> <p>The first thing I did was adding a resistor and capacitor to the tube board to give the tube some extra<br>headroom. One of the major problems I had was that it doesn't really have a setting to only distort a little.<br>In the 0-1 range on the overdrive the pedal is usable but is very quiet even with the volume all the way to 10.<br>On the 1-10 range the pedal distorts so much that I wouldn't call this a overdrive pedal anymore.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072484/mod4.jpg" class="kg-image"></figure> <p>The mod on the tube board increases the headroom so the tube distorts later and has a lot less compression<br>but the drive knob doesn't have a very large usable range. On the high distortion range the pedal also<br>starts to sound very muddy. The second mod adds another resistor and capacitor parallel to the drive potmeter<br>to change it's range and frequency response.</p> <figure class="kg-card kg-image-card"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072485/mod5.jpg" class="kg-image"></figure> <p>Here is a list of the exact values used in the mod:</p> <pre><code>Tube board: 4.7 nF capacitor between pin 1 and 7 on the tube (marked 472) 470 ohm resistor between pin 7 and ground (yellow purple black black brown) Main board: 1.0 nF capacitor connected to the outer pins of the drive pot (marked 102) 100 Kohm resistor parallel to the same pot (brown black black orange brown) </code></pre> <p>I've recorded a sample before the mod and after the mod. Judge for yourself:</p> <p></p> Bus Pirate V3 and LM75 temperature sensorshttps://blog.brixit.nl/bus-pirate-v3-and-lm75-temperature-sensors/5e91ce0c2cc6a25f176028e8ElectronicsMartijn BraamSun, 20 Jul 2014 22:00:00 -0000<p>This is a tiny reference for reading the temperature with an LM75a temperature sensor and a Bus Pirate v3 on a Linux computer.</p> <h2>What is a Bus Pirate</h2> <p>The <a href="http://dangerousprototypes.com/docs/Bus_Pirate">Bus Pirate</a> is a "hacker multi-tool" developed by Dangerous Prototypes. The tool is basically an interface between your computer and a lot of standard communication busses like: spi, i2c, 1-wire, JTAG, uart, midi and ps2-keyboard. It also has a frequency counter (1Hz - 40Mhz) and generator (1kHz - 4Mhz), a low speed logic analyzer (10Hz - 1Mhz) and a lot more. You can read more about this on the <a href="http://dangerousprototypes.com/docs/Features_overview">features page</a></p> <h2>What is an LM75</h2> <p>The LM75 is a small temperature measurement chip mostly used inside computers. The Linux tool lm-sensors has built-in support for LM75 temperature sensors connected to an i2c bus supported by Linux. This makes it possible to connect the LM75 temperature sensor to a usb to i2c bridge and connect environment temperature sensors to Linux and use it to control internal and external fans.</p> <p>The <a href="http://www.ti.com/product/lm75a">product page</a> for this chip lists the accuracy as 2°c and the accurate range as -25°c - 100°c. Whats unlisted is that the precision of the chip is 0.125°c so if you calibrate the chip with a good thermometer you can measure the temperature in way smaller steps.</p> <h2>Connecting the sensor</h2> <p>The sensor is in a small package so I used a breakout board for easier connecting.</p> <p>My connections:</p> <pre><code>Bus Pirate | LM75 | Name ------------------------------------------- red | VCC | Bus Pirate +3.3V source green | VCC | Bus Pirate pull-up voltage brown | GND | Ground grey | SDA | i2c data line purple | SCL | i2c clock line </code></pre> <p>The exact pins on your LM75 depend on the package. Check the <a href="http://www.nxp.com/documents/data_sheet/LM75A.pdf">datasheet</a></p> <h2>Firing up the Bus Pirate</h2> <p>After you connect the Bus Pirate to the computer there should be a new ttyUSB device available. (check this with the dmesg command). The easiest way to use the Bus Pirate is to connect to it with screen. To do this run the following command:</p> <div class="highlight"><pre><span></span><span class="gp">$ </span>screen /dev/ttyUSB0 <span class="m">115200</span> <span class="gp"># </span><span class="k">if</span> you get a permission issue try: <span class="gp">$ </span>sudo screen /dev/ttyUSB0 <span class="m">115200</span> </pre></div> <p>After connecting press enter and the Bus Pirate prompt should appear <code>(HiZ>)</code>. This prompt means its currently in HiZ mode. To switch the mode press <code>m</code> and press enter. Then enter <code>4</code> (i2c). Then it asks for the speed. The LM75a can operate on any i2c speed up to 400Khz so choose any.</p> <p>After switching to i2c mode the prompt is changed to <code>I2C></code> now you need to enable the power supply and the pull-up resistors.</p> <pre><code>I2C&gt;W Power supplies ON I2C&gt;P Pull-up resistors ON I2C&gt; </code></pre> <p>Now all the communications are initialized. The next command is dependend on the address of your LM75 chip. The address is set with the 3 address pins. if all address pins are grounded the address is 0x48. The Bus Pirate has a handy macro for finding all i2c slaves on the connected bus. Enter (1), something like this should appear:</p> <pre><code>I2C&gt;(1) Searching I2C address space. Found devices at: 0x92(0x49 W) 0x93(0x49 R) </code></pre> <p>This means that my LM75 is on the address 0x49 and it lists the send and receive address. To get the current temperature enter the following command:</p> <pre><code>I2C&gt;[0x93 rr] I2C START BIT WRITE: 0x93 ACK READ: 0x12 READ: ACK 0x20 NACK I2C STOP BIT I2C&gt; </code></pre> <p>This makes it write 0x93 to the bus (The read address of the LM75) and read 2 bytes. The two received bytes are 0x12 and 0x20. The most significant byte is sent first so that makes for a temperature of 0x1220 or 4622 in decimal. To get the real temperature divide this number by 255 and round to the nearest 0.125. This makes the read temperature 18.125°c Automatic reading</p> <p>The Bus Pirate has a nice python api for automating communication called <code>pyBusPirateLite</code>. To read the temperature from my sensor I modified the thermometer.pyscript and created lm75.py.</p> <div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env python</span> <span class="kn">import</span> <span class="nn">sys</span> <span class="kn">from</span> <span class="nn">pyBusPirateLite.I2Chigh</span> <span class="kn">import</span> <span class="o">*</span> <span class="k">class</span> <span class="nc">LM75</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">i2c</span><span class="p">,</span> <span class="n">address</span><span class="o">=</span><span class="mh">0x48</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">i2c</span> <span class="o">=</span> <span class="n">i2c</span><span class="p">;</span> <span class="bp">self</span><span class="o">.</span><span class="n">address</span> <span class="o">=</span> <span class="n">address</span><span class="p">;</span> <span class="k">def</span> <span class="nf">get_temp_reg</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">reg</span><span class="p">):</span> <span class="n">temp</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">i2c</span><span class="o">.</span><span class="n">get_word</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">address</span><span class="p">,</span> <span class="n">reg</span><span class="p">);</span> <span class="k">if</span> <span class="n">temp</span> <span class="o">&lt;</span> <span class="mi">32768</span><span class="p">:</span> <span class="k">return</span> <span class="n">temp</span><span class="o">/</span><span class="mf">256.0</span><span class="p">;</span> <span class="k">else</span><span class="p">:</span> <span class="k">return</span> <span class="p">(</span><span class="n">temp</span><span class="o">-</span><span class="mi">65536</span><span class="p">)</span><span class="o">/</span><span class="mf">256.0</span><span class="p">;</span> <span class="k">def</span> <span class="nf">get_temp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_temp_reg</span><span class="p">(</span><span class="mh">0x00</span><span class="p">);</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="c1"># Serial timeout five seconds for debugging mistakes in I2C class</span> <span class="n">i2c</span> <span class="o">=</span> <span class="n">I2Chigh</span><span class="p">(</span><span class="s2">&quot;/dev/ttyUSB0&quot;</span><span class="p">,</span> <span class="mi">115200</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="k">except</span> <span class="ne">Exception</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span> <span class="nb">print</span> <span class="s2">&quot;Error&quot;</span><span class="p">,</span><span class="n">e</span> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">()</span> <span class="n">i2c</span><span class="o">.</span><span class="n">BBmode</span><span class="p">()</span> <span class="n">i2c</span><span class="o">.</span><span class="n">enter_I2C</span><span class="p">()</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">i2c</span><span class="o">.</span><span class="n">cfg_pins</span><span class="p">(</span><span class="n">I2CPins</span><span class="o">.</span><span class="n">POWER</span> <span class="o">|</span> <span class="n">I2CPins</span><span class="o">.</span><span class="n">PULLUPS</span><span class="p">):</span> <span class="nb">print</span> <span class="s2">&quot;Failed to set I2C peripherals.&quot;</span> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">()</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">i2c</span><span class="o">.</span><span class="n">set_speed</span><span class="p">(</span><span class="n">I2CSpeed</span><span class="o">.</span><span class="n">_100KHZ</span><span class="p">):</span> <span class="nb">print</span> <span class="s2">&quot;Failed to set I2C Speed.&quot;</span> <span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">()</span> <span class="n">i2c</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="mf">0.2</span><span class="p">)</span> <span class="n">lm75</span> <span class="o">=</span> <span class="n">LM75</span><span class="p">(</span><span class="n">i2c</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">base</span><span class="o">=</span><span class="mi">16</span><span class="p">))</span> <span class="nb">print</span> <span class="n">lm75</span><span class="o">.</span><span class="n">get_temp</span><span class="p">()</span> </pre></div> <p>Run this command with the address of your LM75 sensor and you should get the current temperature</p> <div class="highlight"><pre><span></span><span class="gp"># </span>Read from LM75 on address 0x49 <span class="gp">$ </span>./lm75.py <span class="m">49</span> <span class="go">18.125</span> </pre></div> Reverse-engineering a Logitech speaker sethttps://blog.brixit.nl/reverse-engineering-a-logitech-speaker-set/5e91ceae2cc6a25f176028f1ElectronicsMartijn BraamSat, 19 Apr 2014 22:00:00 -0000<p>Last week I received a broken Logitech 2.1 speakers set. The Logitech z-340 specificaly. The power light turned on but no sound and the volume knob produced a crackling sound when turning so the amp in the subwoofer is still working. I've tried disassembling the subwoofer but it's mostly glued together. So I first reverse engineered the speaker unit. It contains a small circuit board with the controls for the subwoofer. The left speaker and the subwoofer are connected with a 10 pin cable with din connectors. The speaker unit also contains a preamp for the headphones output</p> <figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.brixit.nl/image/w1000//static/files/blog.brixit.nl/1670072492/schematic-1.jpg" class="kg-image"><figcaption>The reverse-engineered schematic</figcaption></figure> <p>After some testing and probing on the circuit board I found out that the volume potentiometer is broken. I don't have spare stereo potentiometers lying around so I soldered two wires across it. Now the speakerset always run on full volume and I change the volume on my laptop.</p> <p>After "fixing" the speakerset I found out that it sounds like complete crap. It has a very strong bass and a lot of treble but all the mid-tones are gone so I don't think I will be having this speakerset for long.</p>