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!

With that 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 Limor, 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 circular display.

I added the ability to load in a bitmap without any scaling or mapping to the 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,, 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 software.

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 horribly wrong!

(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 serial communications.

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 central plates.

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 lateral motion.

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 rotation count.

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 < 999).

This video ( 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 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

My long-suffering
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.

Finally, I mounted the blade (sans top) onto the actual weapon drive, a Satcon S28-150 MagMotor that drives a Team Whyachi TWM-3RS Weapon Gearbox. Boy is my workshop still messy! With that done, I set the camera on timer mode, ran out of the room and hid, and spun up the blade. Success!

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.


All the added software I've developed, plus the Solidworks blade part files, can be found here. Please read the README file as it contains both documentation on the software, and lots of notes about sticking points I found and worked around when installing Python on Windows XP. There is also a file documenting how to get the AVR programming environment running under Mac OSX.