Tuesday, 13 May 2014

HammerPong #4: LED Strip Refresh Rate Headaches

So onwards and upwards (and back down the other side) .... Time to have a go at making the LED strips that will provide the display surface for the game. I'd already decided I wanted each game strip to be made up of three 5 metre, 150 LED strings mounted side by side and I thought that it might be nice to stagger the centre strip, placing each of its LEDs midway between the pairs of LEDs on either side. I thought that this might help give an illusion of denser pixels and also make it easier to animate the chevron style shapes I've been thinking about for the game.

I did some eBay surfing looking for suitable construction materials and found an adhesive backed roll of 5mm thick, 75mm wide, solid neoprene rubber tape.. This seemed perfect for mounting the 3 LED strips side by side on the sticky side with the remaining exposed adhesive and LED strips with a layer of clear sticky tape. After a bit more searching I decided to try some signmakers masking tape (100mm wide low-tack adhesive paper tape) instead since I thought this would give a nice diffusion of the LED colours.



Sticking down the LED strips went really well. I didn't remove their backing tape since there wasn't much point, they stuck down fine with the backing still in place and it keeps me a few options open if it all went wrong. Must confess I did get in a bit of an angry mess with the paper tape, which was frustratingly difficult to lay down on top of the adhesive in one go and was all too easy to crease or tear. I did end up with some joins, which I wasn't too happy about, but after applying a layer of clear sticky tape to the whole strip it didn't look so bad after all, and once I fired up one of the strips the effect was actually pretty good!



I applied themultiplexing approach I described before, using a modified version of the Adafruit Neopixel Library running on an Arduino Uno. I had replaced my original Toshiba 4514 multiplexer chip with a higher speed Texas Instruments CD74HC4514EN and it seemed to work fine driving the 3 strips together. I had fun trying some nice particle system sketches before having a go at some graphics for the Hammer Pong game.

I tried animating a “puck” shooting along the strip, which all seemed to work fine.... except I was a little bit disappointed with the maximum speed I was getting. I could not see any problems in the code, so I got out the calculator:

150 pixels per strip
x 24 bits per pixel
x 3 strips
x ~1.25us per bit
= ~13.5ms to refresh all three strips
= ~74 frames per second

Hmm....

OK, for video, 74fps would be pretty good! However moving the puck one pixel at a time means the fastest it can tun the length of the 150 pixel strip is about 2 seconds. It gets even worse if we add in the other strip the maximum frame update rate would be halved, and the distance doubled.. That would mean 8 seconds for the puck to reach the opposing player if it moved 1 pixel distance per frame. A bit slow. Hmmmmmm...

Yeah of course this is easily solved by making the puck image move more than one pixel between frames, which would be the usual way of doing things. The problem is that the LEDs are very bright - just like I want them to be - and persistence of vision effects make position jumps between the frames really obvious - you see the image frozen in several locations along the strip, spoiling the sense of fluid movement. I really want single pixel per frame motion to make it look smooth, dang!

So what to do about it? Well the WS2812 protocol sets some base restrictions: The data rate is fixed at 800kbps, so we cannot update faster than 1.25us per bit. Also we have to refresh the entire strip at once due to the serial nature of the load operation (we can't just load pixels that have changed and leave the others). So updating a 150 LED strip will always take a minimum of 4.5ms and there is nothing we can do about that (other than cutting the strip into smaller lengths and addressing them separately maybe... but I don't want to go there!).

But, we do have the possibility of loading the data to all the strips in parallel - so there is no specific reason why we can't load all 6 strips in the same 4.5ms cycle. So, what could stop us doing this?

Well firstly we will need to render all the data into a memory buffer before the strip update (at this data rate we will not have the time to render images on the fly) . Yikes.. thats going to be a lot of memory (by microcontroller standards).. 6 strips x 150 pixels x 3 bytes per pixel = 2700bytes... already more than the 2k RAM on the Atmega328 microcontroller (sad face)

We could reduce this using a lookup table (“palette”) of colour values and storing the palette index for each pixel instead of the 24 bit RGB colour. Lets say we have an 8 bit palette index (up to 256 colours) with perhaps 64 colours actually defined in that 8 bit colour-space.

6 strips x 150 pixels x 1 byte per pixel = 900bytes
plus palette; 64 colours x 3 bits per colour = 192bytes
=1092 bytes total

This is much more doable - and we can save more memory by avoiding storing the palette in RAM.. e.g. by using PROGMEM data stored in the much more spacious 32k FLASH. However the next problem is processing speed...

To be honest I have never had to be so concerned about performance at this level before, ever. But when we are talking about bit-banging at 800kHz every CPU cycle counts. For an ATMEGA328 running at 16Mhz each clock cycle is 62.5ns. Now that *is* pretty fast, but we have to bang these bits pretty fast too. Reading the assembly language code for the Neopixel library really shows how careful the timing of this stuff needs to be.

However, if we can update a single strip by writing LOW and HIGH byte values to an 8 bit port register at this data rate, there is really no reason we cannot update 6 strips (or even 8 - one for each port bit) at the same time ithout breaking a sweat.. same number of bits to load, right? The extra overhead will be preparing the next port byte value, where we'll need to load data from 6 different memory addresses (one per strip) instead of just one - and might need an palette lookup for each one too.

Reading from this really useful blog post (http://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/) we do have up to 9us of idle time to play with between bits. This might make it all possible on an ATMEGA328 with some tight assembly language code, but you know what, maybe its time to look at something with a bit more grunt. Like an ARM board....


I've ordered an Arduino Due as a start. I may yet try to get it working on the Uno, but the comparatively huge amount of memory and much faster CPU speed of the Due does cure a few headaches. Just need to wait for it to arrive now - watch this space.....


Saturday, 10 May 2014

HammerPong #3: 7-Segment Display Made Out Of Fairground Lights



This is the third post in my diary of building my "Hammer Pong" game. 

After a visit with the kids to a local fun fair a couple of weeks ago, I thought some fairground-style lights would look good to jazz up the fascia of my game! I started looking on eBay for used ones, but eventually
decided to buy some new ones.

Loads of LED stuff :o)
At first I was thinking of using a row of individual lights to mark the score, but then I got this crazy idea of making a giant 2 digit, seven segment display!

First of all I needed some kind of board to attach the display to for a proof of concept. My neighbour was throwing out a big sheet of cardboard from a packing case, so I was happy to take if off his hands.

These lights use LED modules in place of filament bulbs, which give a nice clean white light behind the coloured plastic lenses. The LED modules run on 24V and are rated for 1.2W which means they only need 50mA current, which isn't too challenging to switch. 
Bits of a light
I decided to use 3 light units for each segment of my display, so for each segment I'd be switching 150mA at 24V. For this I went for TIP120 Darlington power transistors as I had a few of them lying about and I know them pretty well. 

I wanted to keep my options open for PWM fading and thought that if I used FETs I might need additional FET driver ICs to PWM switch the FETs. Anyway, a bit of experimenting on breadboard shows that a TIP120 could switch and PWM fade a set of 3 lights, wired in parallel, without breaking a sweat. For that test I connected the base of the TIP120 to a PWM pin of an Arduino Uno, via a 1k Ohm resistor. The TIP120 switches on the low side, so my lights were wired to the +24V line and their GND line was connected to the TIP120 collector, with the emitter connected to the GND of the 24V supply. Applying a voltage to the base makes the lights come on.

Now, I don't want to have to use an Arduino pin for every one of my display segments, so I decided to use a 74HC595 shift register to control the lights for the seven segment display. One nice feature of the 595 is the Output Enable pin. Usually I hard wire this to ground to keep the outputs enabled, but here I have it connected to a PWM pin on the Arduino. I thought this would let me fade the brightness of the entire 7 segment display and it seems to work superbly - just need to remember it is an active low signal, so analogWrite(0) is full on and analogWrite(255) is full off.

Each output of the 74HC595 is connected via a 1K resistor to a TIP120. Add a bit of code to load the shift register, define the numeric digits are we're ready to roll with a 7 segment display test!
Shift register/TIP120 driver board

The lights use a screw-on base which forces some sharp terminals through the insulation of the cabling. This makes it nice and quick to wire up without soldering. I used a common 24V supply rail and wired together the ground connections of the three lights on each segment, then connected to the appropriate TIP120 collector terminal. A quick Arduino sketch and I have digits counting, with a bit of a fade in and out around a change of digit.
Quick'n'dirty build

I think I made my digit a bit tall for it's width, but the visual effect seems to work. Now I just need to make sure I have enough space on the fascia of my game for 2 of these - they are quite big! I hope they aren't too big to read properly from a playing distance... hmmmm.... only one way to find out.
In action

Thursday, 8 May 2014

HammerPong #2: Reading Player Inputs

The next step in the build of my Hammer Pong game was to read the user input - when they whack something with a big ol' hammer.. I wanted to give the game some kind of “velocity sensitivity” so that different levels of force would trigger different behaviours in the game (but maybe a very hard hit would not always be to player's advantage - perhaps if the opponent manages to return a fast shot it comes back at ridiculous speed:)

Since I can see this part of the build taking a lot of punishment, I wanted to avoid anything with moving parts that were likely to break with prolonged use. My idea was to use a piezo disk as a contact pickup, and design in some way to detect the force of a hit (tell the difference between different volume levels). For robustness and safety reasons I thought using a foam hammer with a foam block as the target. Burying the piezo disk deep in the block should protect it.

I'd seen the EVA foam blocks that are sold by gym shops as exercise aids and thought they would be good to try, being pre-shaped and finished, tough and quite dense with a small amount of “give” to them.

I had originally anticipated getting some kind of off-the-shelf toy hammers, made out of foam or rubber, but I could not find anything that looks very suitable. Most of them were very soft light foam, too small, or inflatable, so I decided to try and make my own using EVA foam blocks. For the hammer handle I bought some fibreglass broom handles (thinking they would be lighter and safer than wood, but still hold up to some abuse).

I still need to give the construction of the hammers some thought, but for my tests I used an EVA foam “yoga block” as the head and a length of broom handle. I used a standard holesaw, intended for timber, to bore a hole into the side of the block (I fully expected it to rip the foam to pieces, but it actually cut the cylindrical hole perfectly!). Again still experimenting I used generous amounts of cyanoacrylate superglue (set off with activator spray) to join the handle to the head. Superglue is brittle, so I thought it might crack with use, but I tried very hard to break it by hammering things or shear it by twisting the handle... suprisingly the glue join held up to all the abuse I gave it so there is definite potential.
Hammer Prototype

I wrapped the piezo in duct tape and buried it in the middle of an EVA foam cylinder. The only downside is that the “slap” of the two foam surfaces hitting each other is very loud.. maybe too loud. I might experiment with covering the target with fabric later on..
Foam Cylinder With Piezo

So on to the electronics... I know from experience that a piezo disk can throw out a pretty high voltage spike if you hit it hard. Since I wanted to be able to get a measure of how hard the target was hit, I had to use an analog measurement of the piezo output voltage. But, now the microcontroller I am using runs at 5V power, any input voltage higher than 5V would be clipped at 5V on my analog input (I could use a higher analog reference voltage, but lets keep it simple) reducing the range of input levels I can tell apart so middling hits and hard hits would be indistinguishable.

So, I need some kind of attenuation (scaling down) of the input voltage level, for which I used a simple 10k trim pot. I also thought some low-pass filtering would help separate the main impact impulse from other noise. Something else I noticed with an oscilloscope was that the negative voltage peak coming from the struck piezo was substantially higher than the preceding positive peak (not sure why that should be..). 

Anyway, all of the factors led me to using an op-amp to make an active lowpass filter (which also inverts and buffers the input). The trimpot gives a variable attenuation of the raw piezo output to bring it within the range of the opamp input. The opamp output can then be fed directly to an analog input pin on the microcontroller where it can be measured.



While I am planning to use an ATMega328 for the main processor on this project, I decided to offload the input monitoring to a second MCU, I think this is particularly important given the main processor could be blocked for a short time updating the LED strips and might possibly miss input pulses. I used a PIC16F1825 for my little helper as I am familiar with this chip and have a ton of them.

Circuit On Stripboard
My PIC code monitors two analog inputs (one for each hammer target) in parallel. When an input reading exceeds a trigger threshold, the PIC samples the input over a short period, storing the maximum value (the idea being to get the peak value of the trigger pulse). This information is then transmitted as a serial message and the PIC waits holds off for period of time to allow the sound to fade before going back to listening. See the code here https://github.com/hotchk155/HammerPong/blob/master/InputListener/InputListener.c

I used MIDI for the serial protocol. Some people might say I use MIDI for everything, but I love it for a lot of sensible reasons :)

- It is very simple to implement, with most messages being only 3 bytes long
- It has a nice easy synchronisation scheme (bit 7 set means start of new message - simple as..) that stops your sender and receiver getting out of step
- It is perfect for this kind of thing (transmitting simple “note” events with a velocity) - it is pretty much what it is designed for.
- The best bit... if you have some MIDI gear and utilities then you already have all the test tools you need! I can test my input reader before I have the main game program written by simply playing notes through a synthesizer. When I test the game I can fake input using a MIDI keyboard. And I already have a bunch of MIDI cables I can used to connect things together.

Next to get some big flashing lights working!

Monday, 5 May 2014

Hammer Pong #1 - Multiplexing WS2812B addressable LED strips

My 1-D Pong game was accepted to be part of an electronic art exhibition this summer.. Yay!! 1-D Pong was a project of mine from a couple of years back, which uses a 5M addressable LED string as the display surface for a game based on the classic PONG. Since the LED string is one dimensional, you can't "miss the ball" in the conventional sense so the gameplay is all about timing; you press a footswitch at exactly the right moment to return the ball. Despite being so basic it works pretty well as a game, and the pace accelerates with each return to keep it challenging.

As this will be my first gallery installation I decided I really wanted to improve and build on the original (which is, to be honest, getting a bit tatty) and do something a bit more ambitious. In speaking with the curator a few weeks back I learned that the venue has a 7 metre high ceiling, and that the exhibition would have a fairground theme. Wouldn't it be great to orient the 5m strip vertically up the wall, I thought. That got me thinking about those old "high striker" / "test your strength" fairground side shows.. you know the ones with the big hammer to whack a puck up a tube and ring a bell? Well.. combine that with the 1-D Pong and Hammer Pong was born!

Two players will whack it out against each other, hitting their foam mallets against foam block "triggers" to shoot a pulse of light up a vertical LED strip, where it will cross over and return down the opponents strip. The opponent needs to hit their block at just the right time to fire the pulse back as it reaches the base of the strip. Miss the timing and you lose.

A simple concept, but technically a little bit more challenging build than the original 1-D Pong. I will be building it over the next few weeks and will post my progress on this blog, which might be of interest to anyone who wants to know about the build after it is complete. So let's get started...

Challenge number one is that I need to drive multiple LED strips. My original 1-D Pong project was created a couple of years ago when addressable LED strips were new (and rather more expensive) and my strip used an obscure controller called a yds600 for which I had to write my own support library. Due to the way the strip required the SPI communications clock to keep "ticking" after the data was latched, to keep its PWM going, those strips would be hard to drive in multiple.

However, these days most strips use the WS2812 controller - I find these things amazing; the controller chips is actually inside the LED!!

Just think about that for a moment... a single WS2812 LED is a tiny package about 5mm square,  containing not just red, blue and green LED elements but a tiny silicon chip with a serial data controller, internal oscillator, three 8 bit PWM channels and current management circuitry. They can be had for less than 20 cents each but are more usually bought in flexible strings (usually 30 or 60 LEDs to a metre) where the LED data in and data out pins are chained together as a giant shift register so a single data pin on your microcontroller can drive them all. I think that's amazing!

Also, these days it is easy to find ready made code to drive the strips. Adafruit sell WS2812 LED assemblies under their NeoPixel brand and their Arduino library code seems pretty well written, so I have been working with that. One potentially nice point about the Neopixel library code is that it "bit-bangs" the data (the output pin is explicitly toggled on and off by code rather than using a built in serial hardware peripheral on the AVR) so the strip is not confined to specific output pins.

This means that two strips could be connected to different output pins using the NeoPixel library. However I was already getting a bit more ambitious than that... when I bought my strips I bought a job lot of ten x 5m WS2812B strips with 30LEDs/m so I could get the price down to just over US$30 a strip including shipping from China. This means I have quite a few strips to play with :)... so why not make each of my 2 players "tubes" actually made up of 3 LED strips side by side, giving each a 3x150 pixel matrix to allow some simple animations and effects, and increase the brightness... Oooh!
Ten 5 metre addressable LED strips
To run the total 6 strips needed I could use 6 output pins and 6 instances of the NeoPixel library object, or I could potentially chain the strips together and use a single pin by treating them as a single 900 (150 x 6) pixel strip. However, I think this would mean the library keeping an image of the 3 byte colour data for each of the 900 pixels in memory at the same time, which would not even fit in the Atmega328's 2k of RAM!

So... I need to be a bit creative. Firstly there isn't really a need in my game to keep an image of the display contents in memory.. All the required display content can be recalculated at every frame then dumped to the strips and latched into them. There is no need for me to refer back to the display content afterwards (e.g. collision detection is not relevant).

Also the WS2812 data protocol is very timing critical, so the transmit code needs to be tight and run without interruption (I am pleased to see Adafruit coded the important parts in assembly language in their library). This means that it is not possible to send data to multiple strips in parallel, you must send the full set of data to each strip in turn (unless they are chained together and a single send can address all strips, but this needs the full 900 pixel content to be buffered for send, so is a no-no)

So, I decided I need a way to update one strip at a time, making sure I required only a single 150 pixel buffer in memory. I guess two options are possible
  • Dynamically reconfigure the NeoPixel library to direct the data from a single library object and 150 pixel buffer to 6 different digital output pins in turn, re-rendering image into the data buffer for each new strip. This would need to use 6 output pins. It may also need some changes to the library code to allow the output pin to be efficiently reconfigured on the fly. 
  • Use a digital multiplexer IC (I have some TC4514BP's available, which should fit the bill) to connect a single output pin to each of the 6 strips in turn, re-rendering image into the data buffer for each new strip. This would need to use 4 output pins (one for the data and 3 for the selection between strips). I'd need to make sure the multiplexer chip would not skew the output pulses (which for the WS2812 are timing critical). I thought this approach could work without changes to the library code, but read on...

I went for the second option... some simple tests on a breadboard showed it worked, but I did need to modify the NeoPixel library code to allow the data output to be inverted (so that when it would usually output HIGH it output a LOW and vice-versa). I'll explain the reason...

The 4514 digital multiplexer has 16 outputs (more than I need, but I have a load of these chips lying about so..). You have 4 Address pins which you use to select which of the 16 outputs will be set to a HIGH value (all the other 15 show LOW).

There is also an Enable pin which must be held low for the selected output to show HIGH. If the Enable pin is HIGH then all of the 16 outputs are LOW. This allows the Enable pin to be used to toggle the selected output pin between LOW and HIGH, BUT the logic is inverted. If you want the selected output to be HIGH then Enable must be LOW and vice-versa. Therefore if we want the output from the NeoPixel library to drive the Enable pin we either need some kind of Inverter chip, or we can hack the library code to enable inverting of the logic. I went for the second option :)

TC4514BP 1-of-16 Digital Multiplexer

Some simple tests on breadboard and it seems to be working fine. I was concerned about the gate lag on the multiplexer impacting the time-critical WS2812 protocol (ICs have a small but possibly significant "propagation delay" between changing an input and seeing the output change) however as long as the delay is symmetrical and rising and falling edges are delayed by a similar amount of time then it should not be a problem for my application. The data sheet shows this is within the WS2812 +/-150ns tolerance.

Another good thing about this multiplexer is that unselected outputs are driven LOW rather than floated. This means that the WS2812 "Latch" command (pulling the data line low for an extended period of time) can take place after we have deselected the strip and moved on to the next one, making things ever so slightly faster.

Simple test with 2 strips

In case you want to use the same approach, here is the change I made to the NeoPixel library. First off I added a flag field activeLow (I wanted to make it switchable in case I needed to toggle during trouble shooting)
class Adafruit_NeoPixel {
:
 private:
#ifdef __AVR__
uint8_t
    activeLow;       // Set to 1 to use ACTIVE LOW output pulses    
#endif
};

I initialise the flag to 1
Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint8_t p, uint8_t t) : numLEDs(n), numBytes(n * 3), pin(p), pixels(NULL)
  ,type(t)
#ifdef __AVR__
  ,port(portOutputRegister(digitalPinToPort(p))),
   pinMask(digitalPinToBitMask(p)),
   activeLow(1)
#endif
The library implementation uses conditional compilation directives for the many Arduino boards, so you need to look through and find several places where the bit masks lo and hi are defined. This is a pre-calculation of values used later to toggle specific bits in the port registers. hi is usually set to the current port value with the target bit set and lo to the value with the bit cleared. We simply calculate these in the opposite sense if activeLow is needed
 if(activeLow)
        {
          lo = PORTD |  pinMask;
          hi = PORTD & ~pinMask;
        }
        else
        {
          
          hi = PORTD |  pinMask;
          lo = PORTD & ~pinMask;
        }