I2C Hardware Controller


I have designed and built a hardware controller which connects to the Raspberry Pi via I2C (2 wires), interrupt (1 wire) and power (2 wires). It provides up to 30 rotary encoders, 50 switches and 64 potentiometer inputs. (Can be simplified to provide 8 potentiometer inputs.) The interrupt signal asserts when there are unread value changes. Rotary encoders provide relative offset from previous read and scaled rotation increments, i.e. rotate fast to increment in larger steps. There are some snags to resolve (particularly around the potentiometer multiplexing) but it seems to work quite well. It uses a STM32F103 microcontroller (actually a Maple Mini but could be adapted to Blue Pill or similar).

I have written a zyncoder library to interface with the I2C HWC and changes to incorporate with the Zynthian web configuration. (Pull requests pending…) There is an occasional issue with one of the buttons that I need to resolve (could be construction or possibly GPI conflict) but it is working quite well providing the four rotary encoder and push switches required for Zynthian.

The I2C HWC off loads processing from the Raspberry Pi, doing all the scanning and measuring. The RPi just needs to monitor for a single interrupt signal then read which controller has changed and what its new value is. The interrupt line remains asserted until all value changes have been read.

Currently the quantity of each type of controller may be limited at compile time but I have a task to add a feature to configure the device via I2C messaging. The GitHub project code is mostly complete but the schematics and PCB are still being worked on.


Cheap PCM5102a board working

Very nice, looks low cost too!



Yes it is low cost. The Maple Mini cost approx. £3 from ebay and it requires a signal diode (1N4148) for each switch / encoder pin so that is about 12 x £0.01 for this implementation with just 4 encoders with push switches. It could be done for even less by using a Blue Pill type board which cost about £1.60 from ebay but that is not yet tested (see issue #9). I also used a 40 pin DIL socket to allow me to remove the uController. I see you can get ZIF sockets on ebay for as little as £1 or 10 normal sockets for the same price. This implementation does cost a little more (£1 - £2) than the MCP23017 used by Zynthian’s all in one board but it has the advantage of offloading effort from the RPi and reduces the RPI GPI usage (only one interrupt).



I can’t wait to take a look to this :wink:



The zyncoder library code has a lot of conditional compilation (#ifdef) which makes it tricky to extend. I was worried that my changes might break the existing encoders which I cannot test so I created a copy of the library to implement the I2C interface then added conditional complication to CMakeLists.txt, i.e. compile I2C version of libzyncoder.so if ZYNTHIAN_WIRING_LAYOUT=I2C_HWC. I added I2C_HWC to the webconf options. This works but there is too much duplication of code. I would recommend extracting the low-level hardware interface (switches and encoders) from that file to allow a more modular compilation approach (with fewer preprocessor directives). I will review my code to see if it is fit to submit as a PR so that you have early visibility of this but we may wish to consider how best to integrate this.



Pull requests: webconf #57, ui #75, zyncoder #9 submitted.



Hi @riban!

Your PR are merged! Congratulations for your nice build and thanks a lot for your contribution!
Your approach is really interesting. As you can imagine, i’ve thought about following this path in the past. Your contribution takes the subject to my mind again … jeje!

The reason for not following this path (aka, using a separated uc for the encoders/switches) until now has been making the building process as easy as possible. I want that build a zynthian be accessible to people with little skills on electronics. Including a uc implies 2 separated firmwares.

Anyway, i’m open to following this path some day, specially if we could “upload” the uc’s firmaware from the RBPi. Perhaps we could use the serial port for that. What do you think?




Thanks for merging - I can stop pointing my Zynth at my fork and hence stop the headache of merges and rebases and… (I don’t get on with git!).

We could use some clever switching arrangement with the serial port but I worry it adds undue complexity to the circuit, i.e. switching serial between uController and MIDI. It might just be simpler to use a USB port though that might break some mechanical builds, i.e. connecting the externally presented USB ports to the internal uController. We could go down the road of using the USB as the means of communication instead of I2C (like the project on your github) but I am not sure this is the most robust method.

If it is just a one-time flash of firmware then we could make it a build step to plug the RPi USB to the uController USB and if any firmware updates are required then a similar temporary connection would be required. I think I prefer this option. The updater could use a closed-loop test, i.e. update firmware then check I2C functionality. (I am working on enhanced I2C messaging so firmware version could be one of the extended parameters available for such a test.)

I built my prototype with the Maple Mini USB exposed to allow firmware update or indeed it can be used to power the RPi (though I want to avoid that). So plugging a loop cable between USB connections for firmware update is not that difficult and may be an acceptable step.



I don’t think the two firmware thing will be a big issue. I used to develop twin board projects, with different people working on the two firmwares. It was a PITA to keep releases synchronized. We just let each team release its own firmware whenever whey needed, and each board had its own port for firmware uploader. No one has never complained.

(I’m going toward a TRIPLE board, with my Roland DZy… go figure. If only I could put my hands on some dream.fr high end keyboard ASICs… )



The problem is not development, but people building zynthian boxes.

One of my goals is making the build accessible to almost everyone, specially musicians with little technical skills. Introducing a second board with firmware to upload using some external tool is not going to simplify things for them …

Anyway, i’m thinking about using RBPi serial port for uploading firmware automatically, so users doesn’t have to know about it …




True. But musician with little tech skill will just buy the kit, or at least the second board pre programmed, like they probably do with the Zynthian main PCBs… moreover the firmware for a panel controller will be (i hope) quite simple, and seldom updated.

Anyway, for Arduino-like boards, the AVR programming tool is command line and available under linux. The only thing I don’t know is if it could works with the internal rPI serial port, because IIRC the internal port is TX/RX only, whereas to put Arduinos in update mode, other signals are needed.

When I had to program an Arduino from a rPI, I had to connect the two via USB.



This is possible but may need some changes to the design to free up the micro controller serial ports and would need a robust bootloader firmware on the uC. I will see what the options are. (I still prefer the idea of linking an external cable for firmware updates but appreciate your aspiration to hide this from users - my current work project is a software delivery to users who don’t know what a mouse is!!!)

1 Like


…AKA the majority of italian government office employees…



Another option would be to upload firmware via I2C. Maybe I could wrote code to accept a new firmware via I2C and flash it to the uC. I don’t know how slow that would be and how complex the firmware flasher would be but it is possible. I will do some calculation to see if it is worth investigating further.

1 Like


@jofemodo If we were to use serial port to flash firmware to the STM32, we should interrupt the serial feed to the MIDI output to avoid junk being sent to attached MIDI devices. Is this desirable? Is it worth adding additional hardware to gate the Serial Tx line? (It’s not difficult but adds to the complexity.)

I have looked at the figures and it looks like flashing via I2C could be done but requires code to be written into the STM32 to support this. I will do some research into that.

Another option could be to feed the serial port to the STM32 and use MIDI SYSEX to update the firmware but this would require similar code as I2C update and run slower so may not be a good solution.

Yet another option may be to use spare GPIO to bit-bang another serial port. Firmware updates would only occur when the Zynth was not being played so the overhead to add a temporary bit-banging serial port should be fine.

The advantage of using USB or serial to flash the STM32 is that we can use existing upload mechanisms without having to write our own.



I have added issue #10 to my HWC github project requesting the ability to update firmware via I2C but I suggest we put this on the back burner for a while whilst I make HWC more robust. I think we need it to be solid before it is considered for use as Zynthian’s primary controller and some use cases may influence how best it be integrated and what features might benefit both projects. I have a Blue Pill type board on order which may prove a suitable device (cheaper but without booloader so may be more initial effort to get working). We would also want to consider any extra effort and dependencies that this approach might have on production of the kit. I guess you won’t want extra effort flashing the device during production so would ideally like to see it get flashed by the OS on first boot. All fun and games for the coming months. For now I want to use Zynth more to see how it works for me and what improvements may be required.

(Whilst typing yet another longish post it occurred to me that…) Another option may be for the HWC to use MIDI in and out rather than I2C, capable of sending control data to Zynth (using recent CUIA enhancement). This would allow HWC to be a standalone MIDI controller if required or integrated with Zynth. The Zynth MIDI input would either need to go through HWC
being merged by HWC (not ideal) or have another MIDI input to RPi but we don’t have a spare hardware serial input on the RPi so this may raise more issues than it solves. Lots of ideas - please contribute solutions (or more problems) :slight_smile: . Now I must get back to work…



The original Nord Modular did a GUI over MIDI which was the most impressive piece of tunneling over MIDI I’ve seen. From a cabling point of view you are into two cables plus power minimum.
Ethernet is probably a better way of connecting blocks of zynths together When you use POIP you are down to one cat cable.
Do we see the I2C just not extending beyond an enclosure…?
If we put a zynth presence at every i2c location even if it’s nothing more than a zero with no audio attached to drive it all.

I think we can adopt a MIDI grammar that we transfer around our closed MIDI network that can control & test the GUI Interface. If we can run the tests from a midi file stored goodness knows where then so much the better. The only really important issue is keeping message latency down and we can carry far more MIDI traffic over ethernet than we can over two MIDI lines.

1 Like


I2C only works over relatively short distances, e.g. 1m at 100kBaud. I wouldn’t want to extend the raw I2C outside the enclosure. I think for short external interconnects (up to 5m) we should use USB. For longer interconnects a transmission line should be used, e.g. Ethernet, RS422/R485, etc. As there is already an Ethernet connector on the RPi then it makes sense to use that but let’s be clear what problems we want to solve. The HWC is designed to provide a local controller for an I2C master, in this case a RPi running Zynthian. It would make sense to me to focus on that combination and use Zynthian’s features to extend this control if required, e.g. extra (to the 4 main rotary encoders) controllers mapping to MIDI / OSC controllers being transported via MIDI / OSC / MIDINet, etc.

I saw that work has been started on a USB based controller (also using a STM32). I wanted to piggyback on the internal I2C interface to avoid the need for external connectors. If desirable, I could implement a USB interface on HWC to allow it to go on space walks outside the safe confines of the Zynth enclosure.

1 Like


With the addition of an internal I2C interface, would it be possible to build a single encoder Zynth with this? https://www.tindie.com/products/Saimon/i2c-navkey-7-functions-joypad-on-the-i2c-bus/ The up/down/left/right keys could be used to select the encoder and then the rotate and push functions would work accordingly for the selected controller. In a different mode, it could also be used as an x-y controller.

Adding an I2C interface may add a bunch of exciting options to our beloved Zynth!



The current design of the UI actually depends far more heavily on the four buttons than the encoders. The SELECT encoder is used for most operations. I think the other three are only used in the CONTROLLER screen (@jofemodo please confirm). Therefore this input device may better be configured as the four switches + SELECT encoder. This may provide a simple controller and its footprint may be small enough to just squeeze on a 1U panel (though it would be tight). The approach to signalling is similar to our HWC (I2C + interrupt) so it could be added as an input device but the UI would need some changes to accommodate it.

I note this device is mid-priced (more expensive than encoders) but expensive to ship worldwide and currently out of stock. It looks interesting though.

BTW the I2C interface already existed as it drives the GPIO extender of the current kit.