You can see the basic idea on the right there - you have a string of individually addressable LED pixels and you use them to display a bitmap image, one column at a time whilst you take a long-exposure photograph. The result is an image that seems to hang in the air. You can just see the white stick in my hand which has a string of 144 APA102 LED pixels..
What's an LED pixel? it's actually 3 LEDs, a red one, a green one and a blue one plus a clever chip which can control the brightness of each one independently, based on a digital data stream. This usually works something like this; each LED can have one of 256 brightness levels, i.e. brightness is represented by an 8-bit number, so the controller chip needs 24 bits of information to set the pixel's colour. The chip has two sets of registers, one to store the colour being displayed and a second to temporarily store incoming data. Once the chip has received its 24 bits, it simply passes any more data on to the next chip, until data stops being sent. When data stops being sent, the incoming data is copied to the display registers and the pixel changes colour. That way, all the pixels in a string can change colour at the same time.
A variety of LED pixels are available on the market, some easier to drive than others. The very inexpensive ones (e.g. WS2812) need data to be sent at a very precise rate with no interruptions and so are very difficult to drive. The APA102, which I've used here is much easier to drive - data can be sent at at any speed from a few KHz up to several MHz. The data stream is deemed to have finished if nothing is sent for about half a millisecond, which is quite a long time for a 32-bit microcomputer clocked at around 1GHz.
The box, and what's inside
So, how are these pixels being controlled? Over on the right you can see the bottom end of the light-painting stick. The yellow box contains a battery, a Raspberry Pi Zero (would have used a Pi Zero W, but they weren't around yet when I built this), a WiFi adapter and a circuit board that shuts off the power when the Pi has been shutdown. There are also two LEDs and two small button switches. The white button switches on the power and the red one shuts down the Pi which then switches off the power. There's a red LED that lights up when the Pi is shutting down and a green one which flashes when it's booted up and ready to go. There is a third switch on the handle, which triggers the image display. Press and release that, and an image is displayed once, keep it pressed and the image will display repeatedly.
In this picture you can see a few of the LED pixels. APA102 combines the three LEDs and the controller into one 5x5mm chip, so the pixels can be packed quite closely. There is a version in a 2x2mm package, but they're a bit harder to find, plus, at that density it gets quite expensive producing a decent sized pixel string. You can also see the battery, comprising three 18650 lithium ion cells connected in series.
Here's a close-up of the circuit board. Raspberry Pi uses 3.3V logic levels but the APA102 needs 5V logic levels. The little black chip on the right of this bit of stripboard provides the necessary level shifting.
In the centre of the board are two of the
MP1584 DC-DC converters I mentioned in my previous post about the PiFI. These step down the battery voltage from the 12.6V of the fully charged lithium cells to 5V. One of them supplies the Pi Zero and the level shifter, the other powers the LED string. I used two of them to spread the load a bit and to isolate the Pi's power supply from voltage fluctuations due to the LED string changing brightness. 144 pixels can cause quite a large current drain at 30mA per pixel. Fortunately, full white on all the pixels doesn't happen often, or I'd be worried about the survivability of the converter.
Circuit description
The components on the left of the stripboard control the switching off and on, the schematic is shown here. Here's how that works. S1 is the white button - press that and Q1 will be biased into conduction which will pull the gate of M1, a p-channel MOSFET, down below the supply rail voltage making it conduct. This will supply power to the external circuit (our Pi Zero) and also maintain the base-emitter voltage of Q1 so that the circuit latches on.
Pressing S2 sends a signal to one of the Pi's GPIO pins. That pin is monitored by a python script running in the background, when it goes high, the script sends another GPIO pin, connected to the "shutdown now" input high then it issues a shutdown command. When the "shutdown now" input goes high, that momentarily increases the base voltage of Q1, which is already conducting so this doesn't make any difference now. C3 will now discharge through R4 and the base of Q1, returning the base voltage to normal. When the Pi has finished shutting down, all its GPIO pins will go low, allowing C3 to pull the base voltage of Q1 low enough to shut it off. When Q1 shuts off, the voltage at the gate of M1 will rise to the point where M1 will shut off, disconnecting the Pi from the battery and preventing the circuit from latching on again - in other words, the Pi will have switched itself off.
Software
The Pi Zero is running Raspbian Jessie Lite. It is configured to act as a WiFi access point using hostapd with dnsmasq performing the twin duties of DHCP server and DNS. There is also a Samba share. You can't connect to the internet this way, and that was never my intention. This simply provides a way to upload pictures to display and to change which one will be displayed. The Samba share allows file management from a mobile device, WiFi connection allows a command line via SSH.
Two python scripts are run at startup - one runs in the background and checks for the shutdown button being pressed, the other handles the displaying of images.
Displaying images
The program for displaying images is a python script which will only ever display one image - an image called "showthis.png". The program loads this when it starts, then waits for the trigger button to be pressed before displaying it one column at a time on the pixel string. Once it has been displayed it will be re-loaded. This provides a simple way to change the image to be displayed - rename or copy the one you want to display to "showthis.png" and press the trigger once to 'flush out' the old image, and load the new one.
Generating Images
Not a lot of point having this if you have nothing to display. I use a vector drawing program such as Inkscape on Windows or Linux machines, Draw+ or Vector on RISCOS systems and then either screen grab the bit I want or export the drawing as a bitmap. In either case, the result is a bitmap image that can then be resized so that it is 144 pixels high (because that's how many pixels are in the LED string) and uploaded to the Pi Zero via WiFi.