This circuit is based on a PIC16F628A microcontroller which sends serial data through a pair of STP16C596 constant current LED drivers that in turn drive 32 LED pairs. Here is a schematic diagram of the circuit. One kilobyte of image data is stored in the program memory area of the microcontroller and is read by way of a lookup table. The firmware uses four interrupt routines to do its job:
  1. An interrupt to provide the time interval between radial raster lines
  2. One to increment a counter for timing the wheel rotation interval
  3. One to reset all counters and update the raster interval value every time the hall effect sensor is triggered
  4. And finally, an interrupt that shuts down all LEDs when the battery voltage gets too low.
In fact, after the initial startup routine, virtually every part of the firmware’s execution runs inside an interrupt routine.

Timing values for the radial raster line interval are retrieved from a lookup table that exists in the microcontroller’s program space. Data for the lookup table is generated with a Qbasic program, although you only need to run this program if you want to experiment with different timing values from the ones I’ve come up with.


This Spoke POV has the following features:
  • Support for In Circuit Serial Programming (ICSP)
  • 32 LEDs on each side of each circuit board (64 LEDs per board, 192 LEDs total)
  • Displays a 1 kilobyte image (32 LEDs x 256 radial "raster lines")
  • All LEDs can be driven with 20mA at 100% duty indefinitely. This produces a very bright image.
  • The firmware shuts the circuit down automatically when the voltage gets too low to prevent damage to rechargeable battery packs.
  • This project can be constructed entirely from "through-hole" soldered components
  • Fits most bike wheels that are 26" and larger


The following flowcharts illustrate the logic in Spoke POV’s various firmware routines. Both program and image data exist within the program memory space of the PIC 16F628A microcontroller. This means that there is no mechanism for uploading image data at runtime. The chip must be reprogrammed if you want to display a different image.
Main program flowchart

Main program flowchart:

This is the main program loop that decides what interrupt routine to run. After the initial startup instructions, the first thing it does is loop endlessly until an interrupt happens. The rest of the code runs entirely within interrupt servicing routines. (The pink, green, blue and purple circles)

TIMER1 rollover flowchart:

TIMER1 rollover flowchart

TIMER1 is used to measure the time elapsed during one complete wheel revolution. The only problem was that even when the TIMER1 prescaler was set to its maximum value, it still wasn’t long enough to measure one revolution at low speed (walking speed). Of course a persistent image would never be visible at such a speed, but I wanted to strive for a fairly wide operating range. In order to overcome the TIMER1 limitation, I decided to increment a counter so that TIMER1 could do two complete cycles, which takes about one second. (One wheel revolution per second is a reasonably slow riding pace.) After two complete cycles of TIMER1, this "postscaler" counter stops incrementing.

Hall effect trigger flowchart:

Hall effect trigger flowchart

The halleffecttrigger routine is where we determine the timebase for the radial "raster lines". The least significant bit of the TIMER1 "postscaler" is combined with the seven most significant bits from the high byte of TIMER1. The resulting number is used to reference a timebase value in a lookup table. After that, TIMER1, the TIMER1 postscaler and the raster line counter are reset and then we return to the main routine.

Since we can only know the duration of one wheel revolution after it has happened, the timing of the LED raster output is always lagging behind slightly. If the rider is moving at a constant speed, this is not a problem, however if the rider changes speed, the image will become slightly distorted. In practice, this distortion isn’t really a big deal.

TIMER0 rollover flowchart:

TIMER0 rollover flowchart

The TIMER0 rollover routine is responsible for displaying radial raster lines at a rate dictated by the timebase value acquired from the hall effect trigger routine. Whenever TIMER0 overflows, this routine calls the outputimage routine to display one radial raster line of the image. A counter is used to keep track of which raster line is to be displayed next. Once the last raster line has been displayed, the counter stops incrementing. Finally, before returning to the main program, this routine resets TIMER0 to the timebase value.

Outputimage flowchart:

Outputimage flowchart

This routine fetches image data from a lookup table. To avoid the problems associated with trying to access a lookup table that crosses memory page boundaries, I’ve chosen an image size that occupies exactly four consecutive memory pages and then interleaved the image data across them, with each raster line composed of one byte from each page. That way, one simply has to load PCLATH with the appropriate page number prior to fetching each byte. After fetching each byte of image data, this routine calls the writebyte routine which sends the data serially out to the shift registers.

Writebyte flowchart:

Writebyte flowchart

The writebyte routine sends serial image data to the shift registers. It does this by copying the least significant bit of an image byte, rotating the bits in the image byte one place to the right, then repeating the process for all remaining bits. After a bit is copied, the shift register clock line must be pulsed so that the data in the shift register is "shifted" over by one place, making room for the next bit.

LowBattery flowchart:

LowBattery flowchart

The LowBattery routine checks to see if the supply voltage is lower than a predetermined reference voltage. If so, it switches off all LEDs by way of the TurnOffLeds routine and then enters sleep mode. This prevents the POV board from "locking up" when the battery voltage gets too low for the microcontroller and it also prevents the battery pack from being over-discharged which could result in damage.

By default, the reference voltage is set to 3.4375 volts, which should be used with 8.4 volt battery packs. If you plan to use a 7.2 volt pack, then line 149 of the firmware must be modified. For more information, refer to step 4 of the Loading an image section. For information on selecting the right battery voltage, refer to the Batteries section.

TurnOffLeds flowchart:

TurnOffLeds flowchart

The TurnOffLeds routine is called once after the device is switched on, and then again by the LowBattery routine when the battery voltage drops below a predefined threshold. It simply sends a sequence of 0x00 bytes to the shift registers so that no image is displayed.

Shift registers

If one were to drive each LED pair with a dedicated output line, the microcontroller would have to have a very large number of output lines. A far more elegant solution is to use constant current LED drivers that accept serial data input. One such device is the ST 16C596 16 bit constant current LED sink driver which can drive sixteen LEDs and allows multiple devices to be cascaded together. It also has a seperate storage register that allows one set of data to be displayed while the next set is being loaded. In this circuit, four lines are used to control the LED output: serial data input (SDI), clock (CLK), latch enable (/LE) and serial data output (SDO). Each pulse of the clock line causes the data to be "shifted" over by one place and each pulse of the latch enable line causes the LED outputs to reflect the contents of the shift register. Note that in the illustration, the storage register is represented as a bank of switches, but it is in fact a bank of D-type flip-flops that can store eight bits while another eight are being loaded in the shift register.

Shift register illustration

The Qbasic programs

In addition to the microcontroller firmware, two Qbasic programs are required for setting the timing values and converting image data so they can be incorporated in the firmware.
Qbasic source code for Povslope


This program simply creates the timebase lookup table. The table produced by this program is linear, so the only parameters one needs to be concerned with are slope and offset. Note: the timing data supplied in the sample firmware is reasonably accurate so you should only use POVSLOPE.BAS if you plan to experiment with different timing values.


Memory page mapping for image data
POVIMAGE.BAS is used to convert a raster image into radial data in the form of a series of "RETLW B’xxxxxxxx’" commands that can be copied and pasted directly into the POV assembly code. The image data is read one pixel at a time as a series of 32 concentric rings. Each group of 8 rings ends up occupying one memory page.

Because of the limitations of Qbasic and the fact that I didn’t want to bother figuring out how to read TIFF files, I’ve made it read headerless RAW files. The images must be 700x700 pixels, 8 bits per pixel, with the pixels being either pure black (0x00) or pure white (0xff). Such a file can be created with Photoshop. When you’ve finished creating the image, the final file size should be exactly 490000 bytes.

To keep this device from lighting up when the bike is stationary, the last raster line is always set to zero (off). Because the firmware stops incrementing the raster line counter when it reaches the last line in the image, having all LEDs off in that line will cause them to remain that way until the next hall effect trigger.

Qbasic source code for Povimage

Possible improvements

I’d love to hear from anyone who has ideas or suggestions on how this circuit could be improved. My email address is on the Contact page.

Here are some improvements that I’m considering:
  • Use an LED driver that supports PWM brightness control for displaying images with varying shades of colour.
  • Use RGB LEDs in conjunction with PWM brightness control for a full colour Spoke POV!
  • Have the microcontroller access image data from a serial flash RAM chip for greater data storage and the ability to upload new images without having to re-program the microcontroller.
  • Use an accelerometer instead of a hall effect sensor. This would eliminate the need for an external magnet, which would in turn eliminate the need to re-write the image data if the magnet is relocated to a different part of the bicycle frame.
  • A simple and reliable way to supply power to the POV boards from outside of the bicycle wheel, thereby allowing the use of larger and cheaper batteries. The two methods I’ve considered are a slip ring and and an inductive coupling.
  • The ability to upload image data on the fly via some sort of wireless technology such as Bluetooth.