APA102 pixel poi using Teensy3

Here is a build example for using apa102 led strips with a Teensy 3.1 as an example of making some relatively cheap pixel poi that are capable of displaying images or patterns. The images are displayed by loading pixel data held in arrays stored in flash memory, and patterns are made mathematically / through code routines. If you are using DIY pixel poi that are using 24 bit leds, You can convert your images to a hex array for upload, here:
Pixel Poi Image converter

teensy 3 pixel poi using strips of apa102 leds
teensy 3 pixel poi using strips of apa102 leds

The attached code is written to accept any strip length, and uses the FastLED library. The poi are rechargeable via the single Teensy micro usb port, so no opening up to change batteries. A red light glows gentle through the plastic indicating charging.

This is the APA102 Pixel poi / graphic poi with Teensy 3
This is the APA102 Pixel poi / graphic poi with Teensy 3

pixel poi baton using teensy 3 and apa102 led strip
pixel poi baton using teensy 3 and apa102 led strip
120 led graphic poi / pixel poi

The finished pov / pixel / graphic poi are 185g each, and 51cm long.

Before going further, it is important to note that my requirements for pov poi may well be very different to yours. When designing pixel poi you need to consider: image pixel density, weight of final poi/baton, battery longevity, routine code size, length of baton / poi, brightness of leds. I decided to go for lightweight, short, fast spin potential, simple, all relating to space inside the tube housing. Therefore, I am not using a 5v level shifter for these leds, but you can easily fit one (I am saving the remaining space for a flash memory add-on). The trade off is that the battery will only last roughly half an hour when using images with lots of white in them. You will have to consider these issues when building your own version.

Led_reel_and_polycarb

BOM
APA102 pixel strips (4x60leds @ 144leds/m)
Polycarbonate clear tube (2x50cm)
Polycarbonate clear strip (2x50cm)
3.7v Lipo battery (2200mAh 14500 cell)
Pesky products Lipo charger
Polymorph
Teensy 3.1
2x switches
Various odds of wire
Sticky backed plastic
poi finger loops/handles

Total cost for these is less than £200 for the pair. That’s pretty good value.

The build:
The APA102 leds were chosen as they are the first affordable pixels to come on a high density strip, that also have a suitably high refresh rate. Previous experiments with led strips such as the ws281* did not perform well, gave a speckled image at best, and forced a very slow rotation speed to be used to get effect at all. The APA102 were ordered from China as they were so very much cheaper than buying them from a US or European supplier. Adafruit are more expensive and sell them under the ‘dotstar’ name, I don’t know why.

The leds come on a reel with joins in the continuous strip every 50cm. This may important for you, as where they are soldered together the gap between the pixels is ever so slightly greater than the rest of the pixels. This does not make much difference in general, but was a factor in me deciding to cut the strips at this point rather than have the solder point somewhere in the middle of the poi.

solder_point

The Polycarbonate tube and strip were ordered with the Teensy in mind. The internal diameter (22mm) is such that the Teensy 3.1 and the 14500 (AA size) 3.7v lipo battery can fit inside together. Polycarbonate is good material to use in that it is strong, can take a knock or two, does not react badly to strong glues, can be drilled and cut without shattering or splintering, and is reasonably lightweight. It is also readily available from mail order services that cut to size and spec. I went a such a tight squeeze so that I could keep the tube as small as possible = as light as possible.

polycarbonate clear tube and internal strip for apa102 fixing
polycarbonate clear tube and internal strip for apa102 fixing

apa102 on internal mounting strip
The led strips were fixed to the polycarb strip that goes inside the tube. These strips come with sticky backing, so it was a simple peel and stick process. The second side of leds was matched up so that the leds were directly opposite each other. This way you can get away with just have two rows of leds that give a good coverage to the viewer. This gets mounted inside the polycarb tube, but not until the wiring is connected.

opposing leds

Next, assemble and test the lipo charger on the Teensy 3 with the battery. This charger is the one made by onehorse (add link). it allows you to embed the teensy in the casing and still charge it via the same micro usb port that you upload sketches with. It makes life really easy. I tried direct mounting to the teensy +ve, -ve and usb pins and I tried a different configuration using wires to have the charger remote from the board. This was to test out space requirements. In the end I went with directly mounting to the teensy. Board soldering it the board, I soldered on two wires for the battery terminals, and a wire to use a switch between the battery +ve and Vin.

lipo charger

The setup was tested before connecting to the led strips, and then tested again with the led strips connected.
Click image for larger version.

t3_lipo_batt_leds

I then used polymorph to fashion a spinning eye and make a cap for the poi baton case. This was then secured using a couple of screws that i’d made holes for in the case. These screws go through the holes in the case and fix in to the polymorph providing a secure fixing for the spinning loops.
Click image for larger version.

lipo_glow

Software:
The software uses FastLED to drive the APA102. At the moment this setup uses non spi pins, and there is further work to do related to using these and using the other spi pins to provide additional flash memory. The sketch attached is just an example of how to access and display the image arrays, you can easily add patterns, and also a button in hardware to switch between performance sets.

There are two parts to displaying an image. Firstly the image is stored in a ‘const’ array, and then this is accessed by the routine.

here is the code with the array elements reduced so that it can fit in this post. The full array is in the attachment. You will see below that the the void PoiSonic routines are consecutively numbered. This is due to the java programme I use to output image arrays in the correct values, It automatically produced the .ino file to upload to the Teensy. You can call the array whatever you and and change the code accordingly.

The key is the for loop:

for (int x=0;x0;z--){
leds[z-1]=array[x+((j-z)*f)];}
FastLED.show();

This loop works through the led pixel values held in the const array in memory. As the software I use to make the array put the pixel data top to bottom order, and the leds on the strip are in bottom to top order, the for loop does a switch to put the pixels in reverse order when it grabs the data from the array.

The below is a code example, you need to fill in the array with your colour values

/*
*This sketch outputs images to persistence of vision led strips
*It uses FastLed to drive APA102 leds, sending colour values from
*arrays held in flash memory (designated by 'const'). You need to
*set the number of slices you have made your image into,
*e.g. bmp image of 60 pixels high by 150 wide
* would give 60 num_leds and
* 150 slices (number of slices you have made your image into)
*/

#include "FastLED.h"

#define NUM_LEDS 60 //number of leds in strip length on one side
#define DATA_PIN 2//7 = second hardware spi data
#define CLOCK_PIN 3//14 = second hardware spi clock
CRGB leds[NUM_LEDS];
int numberOfSlices = 150;

void setup() {

delay(200);
FastLED.addLeds(leds, NUM_LEDS);
}

const unsigned int array1[] = { 0x000100, 0x000100, 0x000100, 0x000100, 0x000100, 0x000100, /*etc .. add your values here*/}; //end of array

void loop() {

PoiSonic(8000,array1);//holly
}

void PoiSonic(unsigned long time, const unsigned int array[]){
unsigned long currentTime = millis();
while (millis()< currentTime + (time)) { int f= numberOfSlices; int z; //a counter int j=NUM_LEDS; for (int x=0;x0;z--){
leds[z-1]=array[x+((j-z)*f)];}
FastLED.show();
delayMicroseconds(40); //may need to increase / decrease depending on spin rate
}
delayMicroseconds(1000); //may need to increase / decrease depending on spin rate
}
}