Project Brightsaber: Realtime Graphics inside a Weapon Blade
More than a few people have mused upon the idea of installing lights inside the
weapon of a combat robot. Similar “Persistance of Vision” setups are
available for bikes and car wheels, but of course, doing it with a robot is kicking
it up a notch! I've thought for some time that it would be fun to see what could be
done with easily available materials, but never had the time to get around to it -- until now!
in mind, and based on suggestions on the forums and a little Googling, I
settled upon the SpokePOV kit as
the basis for my experiments. The SpokePOV provides a display created by a line of 30 LEDs, and by
sensing the presence of a magnet once per rotation, it can determine how fast it is rotating, and
turn the LEDs on and off as it rotates in order to create the illusion of a stable image.
6/26/06 - Initial Build and Test
first step was to get a SpokePOV. I chose to get a Blue LED kit with extra memory, plus a programming dongle
and magnet, which set me back the princely sum of $59.79. Soldering together the kit was pretty
straightforward, though I only installed one line of LEDs since I don't need to illuminate both
sides of the blade.
I then hacked together a stubby holder out of a strip of UMHW that in a previous life was
one of Totally Offensive's self-righting hoops, and mounted it on a TWM-18 gearbox. Some bolts and
washers served to balance the blade... more or less... okay, mostly less. And my apologies - as you
can see, my workshop is a total sty; I'll have to spend a few hours cleaning it up as soon as
this project is done... or maybe the one after...
The whole contraption was clamped into the vise, wired up to a speed controller and radio, the
magnet was mounted on a rod just outside of the blade radius, and my son and I bravely retired to the hallway for the initial spinup.
As you can see, with the throttle set fairly low (maybe about 200 RPM), an image is visible
(in this case, a Biohazard symbol). My camera didn't have a shutter speed low enough to capture
a full revolution. To the eye, it looks quite nice and stable, though.
One minor problem - when the center battery is strapped down tightly, some sort of a short develops
that causes it to heat up (but just the center battery). I haven't been able to track this down, but the
SpokePOV can be run with an external battery pack, and in fact I'll have to do this in an actual blade to keep the height down. So I'm going to try that as a workaround.
06/28/06 - Logomania
some serious handholding by
the long-suffering creator of the SpokePOV, I got to the point where I could modify
both the firmware that runs in the device, and the drawing application that lets you
create and download images.
The original version of this app would take an image bitmap and map it onto the
radial pixels of the SpokePOV. However, if we want to display changing text on the device,
it's far easier to just map pixels 1-to-1, so that the image is in effect wrapped around the
I added the ability to load in a bitmap without any scaling or mapping to the
SpokePOV.py python app, and tested it with a simple logo.
Note: all the software I'm creating/modifying for this project
can be downloaded using the link at the bottom of this page. Enjoy!
06/30/06 - Issues of Character
next step in getting the SpokePOV to
display messages is generating a character set. I wrote a quick RealBasic app that
lets you pick a font, position it, and generates a 3K (768x32) bitmap of the regular
ascii characters (32-127). Each character in it is actually 14x14, with a 1 pixel
boundary, and in the bitmap, they are shifted down one pixel to accomodate the
fact that the SpokePOV only has 30 leds. This means that the characters still fit
into a 16x16 block of bits in the EEPROM -- and are aligned on byte boundaries --
but will display on the SpokePOV without being clipped.
I also wrote a new python app, SpokeChar.py, that lets me upload the character
set to the first 3 1k buffers in the EEPROM. As you can see from the image, the
standard SpokePOV firmware displays the first 32 characters quite nicely (they
actually look better to the eye than they do to the camera!)
The rest of the afternoon was spent expanding SpokeChar to provide a facility
for loading, saving, editing and uploading messages for the as-yet-unwritten
new firmware to display. Up to 63 16-character lines can be stuffed into the final
1K buffer, though I may need to use some of this for some translation tables.
07/01/06 - A Message of Great Importance
got started bright and early on Saturday,
reading and heavily annotating Limor's original SpokePOV firmware in order to
better understand it. It's been a while since I've done any serious low-level
programming, though “when I was a lad, the bytes only had 6 bits!”
After a while, it all started to come back to me, and there were several
really elegant touches in the code for which Limor should be justly proud.
With that done, it was only about an hour's work to create a new version
of the firmware (CharPOVfirmware1_01) that would display an arbitrary message embedded directly in
the firmware. The inaugural message, “Hands off my tools!”, is a
message to certain people who ought to know better (but in case you're wondering,
nobody that I know!).
Because the SpokePOV is mounted very close to the axis of rotation,
the top line of the display is a little squished together. In the final blade
implementation, it'll be further out, and this will be less of a problem.
07/02/06 - Bar Wars
Quite a bit of time wasted today running down blind alleys as I tried to
get the SpokePOV to scroll through multiline messages. It proved impossible
to store them in the external EEPROM (reading was just too slow, and the serial
bus needs to be dedicated to clocking out the pixels), and that
discouraged me from trying the internal one. Since the ATMEL has only a pittance
of RAM, storing the message in variables (even so called "const" variables) would
limit me to about 4 lines. Then I wondered if I could
store the text as part of the program - and this proved to be a huge can of
worms, demonstrating that my C-skills weren't so much rusty as downright
corroded. Finally, some research at AVRFreaks
turned up the PROGMEM libraries, which did the trick. Then another hour was
wasted tracking down a vexing bug that stopped the SpokePOV from displaying
ANYTHING - which turned out to be a broken lead on the Hall Effect sensor!
But finally, after much wailing and gnashing of teeth, a stripped down
version of the firmware (CharPOVfirmware1_02) that displays up to 16 lines of
messages emerged. In order to get things to fit, I had to cut out all the EEPROM
upload/download code, so if
you want to change character sets, you need to install the standard SpokePOV
Here is a short video ( Quicktime | YouTube )
of the successful test run. About the best decision I made all day was the choice of
what to make it say. If you choose the Quicktime version, you'll need
Quicktime 7 (or later)
in order to play it as it uses the H.264 codec. In order to get things to look
reasonable, I had to use a huge amount of motion blur (temporally averaging adjacent
frames) because of the interaction between the camera frame rate and the SpokePOV
rotation rate. So the fade effect as the lines change is an artifact of that
processing, not what the Mk.1 eyeball will see!
07/03/06 - Smooth, Baby, Smooth!
After mucking around most of the day working on boring stuff (ie: the stuff that allows
me to pay the mortgage), I finally got a chance to sit down and play. The project for the
day was to implement smooth scrolling of the messages, so they would appear to crawl,
pixel by pixel, from edge to axis.
I had though this would involve a lot of bit-shifting in the ATMEL, but it turned
out there was a more elegant solution.
The way the LEDs are updated on the SpokePOV is pretty simple. 4 bytes (32 bits)
are sent out over the serial bus that connects the ATMEL to the EEPROM. A chain of shift
registers monitor this link, and each bit that passes over it is moved into the shift
register chain. It's like a bucket brigade; each new bit causes the old bits to move 1
space down the line, with the last one taking the big plunge into the bit bucket. Then
when all 32 bits have been moved, another data line is toggled to move them from the shift registers
to the outputs that turn on the LEDs.
OK, so far so good. You send out 8 bits of data 4 times, and latch them, and the
LEDs do what you want. In order to do scrolling, I need to take 48 bits of data (parts of
3 lines could be visible at any one time), and then shift them to get the 32 bits I need
to send out over the serial link.
I finally realized that, as Mr. Spock would say, “His pattern indicates
8-bit thinking.” I was so used to thinking, “Serial port... send out a byte...”
that it took a while to recognise that on a chip like the ATMEL, it's quite possible
to send less -- or more -- than a byte at a time.
Once I understood that, the solution was simple: send out the first 32 bits as normal,
then send out from 0-15 extra bits to scroll the first line off the display and move the
visible parts of the third line onto it, and then just latch what happens to be in the shift
registers at that point onto the LEDs.
30 minutes after I realized this, the updated firmware (CharPOVfirmware1_03) was up
and running. As you can see from this video
( Quicktime | YouTube )
it worked like a charm. The only problem is that it worked like a charm -- the very first time!
As any programmer will tell you, when your code works the first time, something is probably
(Note: because of the temporal averaging done to the video, the scrolling looks blurrier
than it appears to the eye. There's also a glitch early in the video which appears to be
a tape dropout)
It is important to mention that in terms of execution speed, doing the bit shifting
inside the ATMEL would have been faster than sending the bits out over the serial bus. However,
this solution is likely to be more space-efficient. Once most of the major features of the
new firmware are up and running, I'll almost certainly revisit this code and try to get it
running faster, probably by doing the bit shifting in the ATMEL and interleaving it and the
Oh yes, I also found a minor mistake in the font generation software; it was shifting
the characters (in order to avoid the blank pixels) the wrong way. If you look carefully at
the original video from 07/02, you can see that the descenders on the outer line of text are
clipped. The new version of SpokePOVFontMaker fixes this, and the fonts in the distribution
have all been modified.
07/04/06 - Cutting Edge Design
4th of July being a holiday,
I didn't do any programming on the BrightSaber. Instead, I spent the day coming up with
the initial design for the BrightSaber Demo blade.
Since I don't have a huge amount of time, and since this blade is not intended to
be used in combat, I decided on a quick-and-dirty approach using a stack of waterjet-cut
0.125" 6061 Aluminum plates.
The plates bolt together to form a blade with hollows in the interior that hold the
SpokePOVs and batteries. One of the interior plates has channels in it for the wiring, both
for batteries and to permit communication between the SpokePOVs at some time in the future.
I have modelled it at the top of the stack for clarity, but it will probably be one of the
If you look carefully, you can see that most of the bolt holes (both for securing the
SpokePOVs and clamping the blade) do not pass through the top plate. This lets me assemble all
the other plates seperately from the top plate (using flat head cap screws), and then bolt on
the top plate much more quickly.
My current thinking is that I'll mount the SpokePOVs using some rubber grommets for
shock mounting, with a few small through-bolts from the top plate serving to help prevent
Underneath the blade are a couple of access ports that let me get to the SpokePOV
progamming connector. I'll also use some Futaba connectors as power switches and run them
to the access ports.
07/05/06 - Dynamic Display of Digital Data
Today was spent mucking around trying to get dynamic display working - things like a constantly
updating RPM. To keep things simple, I started out with something totally basic - the
The dynamic display module I created for the firmware has a few simple limitations
that make it manageable. It can display different types of data, but only one dynamic
data element can be displayed at any time (ie: one per 2 lines if scrolling by lines, or
one every 3 lines if scrolling smoothly).
The actual dynamic display code came together very quickly, and it worked fine... except
when smooth scrolling was turned on. Then the code was too big, so I had to put it on a diet
(and found a few brainfarts, like using the % operator, which, since the ATMEL doesn't have
multiplication and division instructions, means a library is automatically added to the code
to do the operation - slow and costly in program space).
I finally got the program to fit, and it worked fine... except when smooth scrolling
was turned on! !&^#&*@*@!! When both dynamic data and smooth scrolling were enabled, the display
would barf bigtime; some of the characters would display, some would be garbage.
After many hours of headbanging, sacrificing coffee to the programming Gods, and
exercising my remarkable vocabulary of invective in several languages, I finally realized that
the problem was RAM space. The ATMEL has a pittance of RAM, and it's shared by variables, the
stack, and so on. When both dynamic data and smooth scrolling were enabled, things wrapped
around and the ATMEL went goofy.
I was able to reduce the memory requirements by moving more stuff into PROGMEM variables
and making all the interrupt routines non-interruptible, which meant that the pixel output
routine did not need to make local copies of some variables. And so now it works -- barely!
Here is a short video
( Quicktime | YouTube )
showing both smooth scrolling and dynamic display. It does jitter a little bit more than I
would like at certain speeds, and that is something that will have to be addressed if at all
possible. It may be related to the fact that the timers cannot interrupt each other now.
With both dynamic display and smooth scrolling on, I can get the blade up to a bit over
400RPM before it stops working. With less going on, the blade can go a bit faster. I'm not
quite sure what's going on here; the display basically freezes with a few LEDs on (and they
sometimes change). Slowing down the blade permits it to recover.
Oh well, enough for one day; code's in CharPOVfirmware1_04...
07/07/06 - Rev it up, Baby!
Today I tackled the problem of displaying RPMs. The big issue here is converting from
rotation time (measured in CPU/256 clock units) into RPM -- the problem being that the
shorter the rotation time, the higher the RPM. This meant division, not only to convert
to RPM but also to convert to digits for display -- and since the ATMEL doesn't have
a division instruction, this means the compiler will link in a library, which will
use up space I don't have. So I had to find another way.
After some deliberation, and several pages of notes, I came up with a nifty
little trick that uses a table of partial sums (stored in PROGMEM) to directly compute
the RPM in ASCII. No divisions are required, just bit shifts and subtracts. The one
concession I had to make is that it actually computes RPM/10, which is enough for my
purposes (it could easily be modified to compute RPM*1 if the expected top RPM is
( Quicktime | YouTube )
shows an uncalibrated RPM display (my guess based on an 8mhz clock). Analysis of the
video showed that when it was displaying 600 RPM the actual speed was 566 RPM; a quick
change to the table of partial sums was all that was needed to fix that.
To make calibration easier, I changed the character set generation software so that
the final character (ASCII 127, \0x7F) has all bits on, and used that for the top line.
Note that the video indicates that the full 360 degrees are not being used in the display,
and I'll have to figure out what's going on there.
At present, the RPM code is not space-optimized, and can't be used in conjunction
with scrolling. I have hopes of fixing that, but I'd kill (well, maim, anyway)
for a faster ATMEL with more program space! You can find the current code in CharPOVfirmware1_05.
07/08/06 - Crunching Code
Sunday is supposedly a day of rest, but there is no rest for the wicked, so I
whiled away the hours optimizing the code in the hopes of freeing up a byte or two.
The big breakthrough came when I realized that multiple-bit shifts on the ATMEL,
in particular word-length (16 bit) shifts, are horribly space-inefficient because the
ATMEL only has single byte, single bit shift instructions. Once I noticed this, I figured out
that if I was particularly baroque about the way the character sets were stored in memory,
I could avoid almost all the shifting. A quick rewrite of the character upload python
program (now SpokeCharInterleaved.py) later, I'd saved over 150 bytes of code! Then a few
tricks involving operating only on the top or bottom byte of a word saved another 20 or 30
bytes. Finally, I simplified the reset button code (now press to sleep, press again to wake
and reset) eliminated another 20 or 30 bytes. I was on a roll!
CharPOVfirmware1_06 includes all of these fixes, plus an extra feature: you now
specify lines by using an indexing array into the line text data, which means that repeated
lines (such as all blank lines) only cost you one byte of program space. And a cute hack
with this feature and the array that positions the dynamic data made the code even simpler,
saving even more program space. Finally, I fixed the bug in 1.05 that was not displaying
the last character in each line.
The net result is that the firmware can now display both RPM and the REV counter while
smooth scrolling, and still has 150 bytes of space left - which I'll use to implement the
clock - when I have time for it!
Don't hit me for that last joke, I can't help myself...
07/15/06 - Plate Mail
and somewhat perplexed UPS guy delivered yet another great package to me on Thursday -- the
waterjet-cut plates for the BrightSaber blade. After drilling out the holes to the correct size
(waterjet cutting tends to be a little undersized) and doing some countersinking (with a hand-drill,
which is why the countersinks look like crap; fortunately, nobody will ever see them... ummm....) and
tapping, the plates were stacked up and bolted together. BTW, I also realized the blade plates exhibit
chirality (aka “handedness”), but only after I'd countersunk the wrong side of one of the
plates. Not a fatal error, since I could have hidden it in the stack, but in any case I had cut
extra plates just in case.
At this point, I realized something else -- since the programming port connector and
EEPROM chip on the SpokePOVs could project down into the access hole slot, the blade could have
fewer laminations and thus be considerably thinner - 0.75" instead of 1.00".
The battery and SpokePOV compartments were then lined with 0.100" adhesive-backed foam
(courtesy of the local Wal-Mart's craft department, great stuff). The holes were punched
with a leather-punch (a very handy tool, btw, made by Tandy, better known as Radio Shack -- their
original business was leather supply). I also used it, and a handy circular coaxial-cable part, to punch out some foam grommets
to insulate a couple of the 6-32 mounting bolts that pass over or close to PCB traces. Oh yeah, all
the bigger bolts are 10-32's.
Then, after chopping off the tangs of the SpokePOV PCBs, I installed the blades and
battery packs. Due to a screwup on my part, the second SpokePOV (Red LEDs, it'll be used to make
background images for the text) won't be working until I get a replacement part from Limor, so
it was only installed for reasons of weight balance.
Still lots of work to do, but no matter what, the BrightSaber will debut at the
2006 Robot Fighting League National Championships, August 10-11 in Minneapolis,
MN. Admission is free, so come down and see it in action (or see it explode, either way, it'll be
fun to watch)
08/13/06 - Debut at Nationals
Brightsaber made its public debut at
the 2006 RFL Nationals, riding on top of the latest version of my spinner bot,
Totally Offensive. The bot had some teething pains at the
event, but the BrightSaber performed quite well.
My thanks to Fuzzy Mauldin for taking the combination long-exposure flash photograph
of the blade in action.
Here is a video ( Quicktime | YouTube ) of the
blade thanking everyone who helped me on the blade and the robot, and one that demonstrates the
RPM, Rev Count and time ( Quicktime | YouTube )
Next Steps - and Unanswered Questions
The next step in the project will be to design more advanced hardware, in the hopes of getting the
max RPM up. I'd also like to put a bluetooth receiver on the blade! Finally, it would be cool to build
a version that actually fits inside a combat-capable blade.