It took a while but I’ve finally managed to get the firmware of my new Rainbowduino V3 controllers to a state that I can use them as part of my 512 RGB LED coffee table project. I’ve learnt a lot of new things about interrupt handling of AVR micro controllers that I didn’t want to learn at all and I went trough quite some frustrating weeks where I was very close to just throw those controllers into the trash. But luckily I’ve managed to get the firmware flying at the end. If you want to find out what obstacles I had to fight with, how it all works at the end and where you can find the Rainbowduino V3 firmware including the matching Java API than feel free to continue reading this blog post 🙂
The first firmware I’ve tried for my new Rainbowduino V3 controllers was the reference firmware offered by the manufacturer (seeedstudio.com) of the controller. The firmware works quite nice but it targets for standalone usage of the controller without any functionality to stream 8×8 RGB frames to the controller via an I2C link or the build-in USB connector. Since I’ve already used the neorainbowduino firmware for my Rainbowduino V2 controllers that were used for the first test assembly of my coffee table I was also familiar with sending those frames with the help of an Arduino Uno or Teensy controller via an I2C-link to the Rainbowduino controllers. Sadly the neorainbowduino firmware can’t be used for V3 controllers since seeedstudio.com has change the controllers hardware quite a lot. Also I did know from my initial tests that the I2C-link approach is limited to max. four Rainbowduino controllers per Teensy controller since otherwise those controllers will act as the bottleneck preventing the Java program from sending RGB frames at ~20fps. An Arduino Uno due to his even worse serial performance isn’t able to drive more than two Rainbowduino controllers at ~20fps. Therefore it was quite obvious for me to focus on the build-in USB connector of the Rainbowduino V3 controller since this would solve several problems for my use case:
- Direct serial connection to every Rainbowduino V3 controller without having another controller like an Arduino Uno or Teensy in-line that will act as a bottleneck.
- No need for an external power supply since each controller can be powered directly via the USB connection.
- No additional wiring needed for the I2C-links or being able to upload the controllers firmware since both can be done via a single USB cable.
Actually there’s one drawback of using an USB connection compared to to an I2C-link: You won’t be able to directly daisy-chain those controllers any more since the USB plug will be in the way. Luckily this isn’t a problem for my project since I do use some self-build 34x34cm LED panels so that I have enough space for an USB plug attached to each of my controllers.
While searching a bit for Rainbowduino V3 firmwares that claim to be able to send RGB frames via an USB connection I only found the rainbowdash firmware which offers an operation mode called DirectMode that is able to send RGB frames via serial to the controllers. Sadly this only works up to a baudrate of 19200 until the firmware suffers from data corruption / data loss due to conflicting interrupts: the internal Arduino interrupt that is used to receive incoming serial data and the interrupt of the rewritten seeedstudio.com firmware used to drive the LEDs. Conclusion: I own eight new controllers but there wasn’t any firmware available to stream 24bit RGB frames with 8 bit per color channel to those Rainbowduino V3 controllers via the build-in USB connector. Therefore I’ve decided to start my own firmware trying to combine the features of the neorainbowduino and the original seeedstudio.com firmware. That was end of March – or in other words – five month ago.. I had no fucking clue in what kind of trouble I’ll put myself into..
So.. It’s mid-September in the meanwhile, I’ve learned things I didn’t want to learn, I’ve fought with issues I couldn’t imagine five month ago and I was quite frustrated sometimes.. but I’ve managed to get my Rainbowduino V3 firmware including the matching Java API up and running! I’ve called it rainbowduino-v3-streaming-firmware since it’s only feature is to receive RGB frames via a serial connection of the build-in USB connector. You can find it at https://code.google.com/p/rainbowduino-v3-streaming-firmware/ if you want to use it for your own projects.
How does it work?
The firmware expects four frame fragments to be send to the controller to be able to show one 8×8 24bit RGB frame. Splitting up the frame into four fragments is done to avoid running into data corruption / data loss as soon as the internal buffer of the Rainbowduino V3 controller runs full. It took a while to understand that the recent Arduino 1.0 update has reduced to buffer size from 128byte to 64byte although most of the blog and forum posts you’ll find in the net are talking about a 128byte serial buffer – but that’s a different story. Each frame fragment gets extended with a header and some checksums to be able to implement a reliable serial protocol including some proper error handling. Whenever a frame fragment was received an ACK reply message is send via the serial connection to the Java API. The firmware itself just holds two frames in memory: the one that is currently shown by the LEDs and the next one that is currently being received via serial. As soon as a full frame has been transferred the frame buffers do switch. Everything was held rather simple since the firmware should just accomplish a single job: Receiving 8×8 RGB frames and drive the LEDs with it.
The most painful problem I had to solve were those two conflicting interrupts I’ve mentioned earlier. In the beginning I could either decide to receive all frame fragments in a reliable way but suffering from ghosting / flickering effects at the same time since the timing while driving those RGB LEDs wasn’t reliable any more. This was caused by the serial interrupt stopping the LED update routine whenever serial data was received. The other option was to keep the timing of the LED update routine evenly but dealing with data corruption / data loss whenever the controller was busy with pushing color data to those LEDs. This problem kept me busy for several month and I’ve tried multiple things to get around it including some frustrating moments when you have to revert all of your code changes since your new idea on how to solve this problem didn’t turned out to work as expected.
At the end the solution to address this interrupt problem was to simply re-enable the global interrupts of the AVR micro controller inside of the just running LED update interrupt call. First this sounds like a stupid idea to allow a single threaded micro controller to again call interrupts although the currently running interrupt callback isn’t done yet – this kind of smells like an interrupt calling itself over and over again which will brick your controller. In my case there’s only the internal serial interrupt that is able to interrupt the LED update routine to be able to handle incoming serial data. This combined with the fact that the runtime behaviour of the LED update routine is very predictable since it only pushes one of the frame buffers to the driver ICs prevents the controller to run into interrupt loops. It took a while to figure out the best working timing and interrupt configuration but afterwards the LED update routine is only interrupted very shortly whenever the controller has to handle incoming serial data. The time between the next call of the update routine is still enough to parse the incoming serial data and store it in the frame buffer. Since the LED update routine is triggered every ~1250µs the controller also doesn’t have enough time to neither receive a full frame fragment nor parsing it completely so that there’s no risk to run into problems with the 64byte build-in serial buffer that would otherwise result in data corruption and data loss.
After fixing the interrupt problem the firmware is now able to run at up to 35 frames per second. Starting from ~38fps the timing between the Java API and the firmware gets wonky which results in lots of frame (fragments) being dropped by the firmware. Nevertheless 35fps should be more than enough for most usages. Also keep in mind that the internal refresh rate of the LEDs is close to ~100fps so that even if you stream frames with only 35fps to the controller the actual frame is pushed almost three times to the LEDs by the driver ICs. Therefore I’m currently not able to see any flickering at all while showing frames on the Rainbowduino V3 controllers. Another advantage is that this approach scales pretty good while using multiple Rainbowduino V3 controllers at the same time since even a single USB connection has more than enough speed to drive multiple controllers. I my case I even use all eight controllers directly attached to a single USB hub without any noticeable latencies of the USB connection. To not create any new bottlenecks in the Java API the RainbowduinoV3.java class was implemented in a thread-safe way so that more than one thread can drive multiple controllers at the same time.
Also I’ve tried to keep the Java third-party library dependencies down to a minimum so that you’re not forced to use bigger frameworks like Processing just to be able to send RGB frames to a Rainbowduino V3 controller. It only depends on the RXTX, Apache Commons Lang 3, and the Simple Logging Facade libraries. If you need more technical information on how to use my rainbowduino-v3-streaming-firmware than please consult the technical documentation available on the project homepage. I’ve also provided two simple examples on how to use the Java API of the firmware so that you should be able to get your controller running without writing a single line of code. Those examples are also able to drive more than one Rainbowduino V3 controller at the same time.
For my 512 RGB LED coffee table project this means that I can focus again on finishing the remaining hardware tasks of the table itself so that I finally will able to use it as a coffee table in the near future! I’ll keep you informed as soon as this happens 🙂