Every four years, the Chaos Computer Club runs the CCCamp. My daughter calls this “Afrikaburn for nerds”, which isn’t that far from the truth. Several thousand (5000?) hackers went out to a remote camp site in Germany to set up a village, complete with power, high-quality Internet and a mobile phone network. I was lucky enough to get a ticket (thanks Milliways/McFly!) and of course had to have a project to take with me.
This year’s Defcon had a really cool hardhat competition. Essentially the idea was to construct something cool that fits in/around/on a hardhat, there were a number of competitors with outrageous ideas like AndrewMohawk’s Blunicorn. It’s an fun idea that solves a lot of the problems with wearable electronics – solid structure, lots of space for electronics/batteries and a large area for displays.
My original idea was to fit 16 small screens all around the hard-hat, each of which would have independently eyeball animations, controlled by an STM32F4 quadcopter flight controller board. This turned out to be a little ambitious for the processing power available, so the hat landed up with 4 screens in a row at the front, and another screen centered on the ‘forehead’.
Why so many screens?
Why not! But also because the BSidesCapeTown 2019 badge uses an ST7789 TFT screen, and I have a lot of them left over from the prototyping/destroying/burning/crying phase. Adafruit has a library for them, so my thinking was “how hard could it be to adapt this to drive multiple screens?”. Famous last words of course.
They’re driven using SPI (Serial Peripheral Interface). How does SPI work? It’s usually a clock line (CLK), one or more data lines (Dx), and a Chip Select (CS) line. The CLK and Dx lines are connected to all devices on the ‘bus’, and one CS line is used per device. When the CS line for a particular device is active, it will accept commands, otherwise it ignores them. Easy! Unfortunately the CS line on these screens is hard-wired to active, probably to save space – the screen needs two extra lines: reset (RST) and data/command mode (DC).
How can we drive multiple screens without the CS line?
One option is multiple SPI ports on the micro-controller, but unfortunately only two were available. So what about sharing the CLK and DC lines, with only the data lines driven separately per screen? It turns out that this approach works for at least 6 screens, perhaps more. Unfortunately this isn’t a standard configuration for any of the available driver libraries, so I decided to write a bit-banged version. In hindsight I realised that it’s quite possible that Quad-SPI (in 8-bit mode) may work with up to 8 screens (one bit per screen) …
I have an STM32F446 nucleo board available which was hooked up to all of the screens via this insane tangle of wires.
Making it work
The first step for any project like this is to get hold of the datasheet, and use this in conjunction with other libraries. After three days of trying every combination of initialisation routines, and getting nothing but a grey screen with a periodic short flash of lighter grey, I eventually stumbled on a Github Issue for the Arduino TFT_eSPI library, discussing SPI modes. It turns out that the screen uses SPI mode 3. I switched to that, and suddenly the screen was displaying my test pattern!
At this point it seemed like the project could work, so a friend of mine got me a R30/$2 (!!) red construction helmet. We cut holes for the screens, and used copious amounts of double-sided tape to attach the screens to the helmet. Ready for Prod!
CCCamp
The helmet with all its sketchy wiring and screens had to go in my carry-on bag, and no-one blinked an eye. I spent at least a few hours every day working to get the displays working fully independently. Two difficult bugs merged into one frustrating bug that appeared to be unsolvable. The first was color mapping, which turned out to be a misunderstanding about how the screen’s 15-bit colour space fits into a 16-bit number (the LSB is dropped, not the MSB!), and the second is the STM32F4’s handling of out-of-memory issues – it appears to be functioning, but the pointers are mangled. The solution was a 16-bit screen buffer, with a 3-bit colour LUT (Look Up Table) per screen.
During a late night session in the Soldering village, the helmet was covered in googly eyes, and the screen’s eyes converted from spooky to googly. A day later I had it all ‘working’, slowly, with frequent random pupillary wandering out of the ‘eyeball’.
Next step, the forehead screen needed to be more CYBER. I copied some text drawing code from a previous badge and I attempted to scale the fonts from a 26×26 screen to a 240×240 screen. Apparently mixing text rendering code with lot of good beer and whisky isn’t a great idea, and I couldn’t get text to display properly on the yellow screen. I called it quits on the Friday night, and started to enjoy the other parts of the conference.
A little bit of extra work got me some CYBERS!
On the way home the hat had to be disassembled so that it could fit into my bag(s). It all compressed down to this pile of wires, screens and the hard hat.
After an hour of trying to put it back together, I decided on a far more effective approach to the wiring – use pin headers on the breadboard and hook everything up neatly.
Lessons learned
- It’s never as easy as it should (could?) be
- Pay attention to the small details
- SPI Mode 3!
- Compiler warnings (pointer types and such)
- Linker warnings (Memory usage warnings!)
- A pile of wires and breadboards in a hat works surprisingly well
- Tape down the wires and breadboards once you’re sure it all works
- Use pin headers on a breadboard where possible!
- Awesome things can be made in a surprisingly short time
The (super sketchy) code can be found in the ‘multiple-screens’ branch of the Monero Defcon 26 badge repo.
The hat will be revived in time for BSides Cape Town. Come say Hi!