Teensy 3 WS2811 POV (144led/m)

Status
Not open for further replies.

mortonkopf

Well-known member
Hello all,

I have been working on a POV baton using Teensy 3 and OctoWS2811. I currently have a set of POV batons using LPD6803 leds and Teensy 2++, but these had to be soldered individually, and my soldering was not too good. Anyhow, I wanted to get a higher density, and the 144led/m strips looked like a good idea. I have used the Octows2811 lib for scrolling text displays, but thought that it might be fast enough for a POV - and it is (great stuff Paul).

I bought a couple of strips of the144led per meter ws2811 led strips from china, but the build quality was terrible, and in the end i had to do a fair bit of cutting of the strip and resoldering. anyhow, here is pic of first attempt, hope you like it. Like Neep (we must get in touch), I think that POV toys are fun, and seem to be spending too much time thinking about them. I think that the teensy 3 and octo lib are the way to go. How about a thread on Led twirly things?

Happy to share code if anyone wants it. Just working on figuring out the sd code from ZTiK.nl (your work is also much appreciated).

Also, Neep, I have some maths code patterns that you might like. Let me know.

mortonkopf

teensy3_1.jpgholly.jpg
 
Last edited:
I have a question about how the ws2811 colour is reset using octows2811. There appears to be a 'gap' in the colour when refreshing. Is this because the 50us low switches the ws2811 led off momentarily, or perhaps something in my code. When using the ldp6803, the colour could remain until a new colour was set, so no gaps, just a continuous flow, as it were. A pic below shows this. These were driven with Fast_SPI lib.

Any pointers appreciated, as the continuous changes looks smoother to my mind.

kind regards
mortonkopf
centre.jpg
 
Last edited:
The gaps are a function of the slow PWM rate on the WS281X chips (about 400 Hz). Fine for animation on a static thing, good enough for light painting, not so good for POV. For the latter I recommend...well, almost anything else (e.g. LPD8806), downside being the coarser pixel density. Tradeoffs!
 
@pburgess. thanks for the info. Is a shame that this is so, but there you go. Now on the search for high density strips without this issue.

I have tested these strips for slower refresh work for a light painting staff, and they work reasonably well.
regards
 
Dear Mortonkopf,

i'm working on a project similar to yours and i can see that you got a great result so far.

Can you please share the code and picture of your project or schematic ?

Thanks in advance.
 
@e3d. I have not made a schematic for this project, but it simply follows the set up of two other wiring schemes, one for the SD Card and one for the WS2811 led strip. The SD card pins are described in posts on this forum, and also included in the code below.

This forum post by ZTiK.nl describes the sd card use: http://forum.pjrc.com/threads/16758-Teensy-3-MicroSD-guide

For the wiring of the leds, look at the Octows2811 page on the pjrc site http://www.pjrc.com/teensy/td_libs_OctoWS2811.html for a good description of the set up, better than me going through it here. I use 4 x AAA rechargeable batteries that run the Teensy3, Leds, and the sd card a the same time. I found that I needed to use a common GND pin for the Leds and the Teensy.

the code is available at: http://orchardelica.com/wp/displaying-led-graphic-poi-bmp-from-sd-card
 
Thanks Mortonkopf,

I Just added slight modification on your code to try to work it out on 16 LEDs NeoPixel strip using Arduino 1.0.5 environment & Teensduino loader v.1.17

however, i'm unable to compile the code due to the following error message, what could be the problem ?

Please note that i'm using Teensy 2.0

----error message-----

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.5 (Mac OS X), Board: "Teensy 2.0"
pov_2_0:18: error: expected constructor, destructor, or type conversion before 'int'
pov_2_0.ino: In function 'void bmpDraw(char*, long unsigned int)':
pov_2_0:96: error: initializer fails to determine size of '__c'


-----Code------

//#include <OctoWS2811.h>// ensure you are on PIN 2 + PINS 15 and 16 bridge
#include <Adafruit_NeoPixel.h>
#include <SdFat.h>
#include <SdFatUtil.h>

//OCtoWS2811 library from the good folk at PJRC http://forum.pjrc.com/forum.php
//SDfat library from this brilliant person http://code.google.com/p/sdfatlib/
// most of the code for the buffer workings come from ZTik.nl (many thanks)

//for SD card wiring
//MOSI goes to pin 11
//MISO goes to pin 12
//SCLK goes to pin 13
//SS goes to pin 10

//fixed values for octoWS2811
const int ledsPerStrip = 16;
DMAMEM int displayMemory[ledsPerStrip*6];
int drawingMemory[ledsPerStrip*6];
//const int config = WS2811_GRB | WS2811_800kHz;
//OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);
Adafruit_NeoPixel leds = Adafruit_NeoPixel(16, 6, NEO_GRB + NEO_KHZ800);

//for SD_Fat
SdFile myFile;
#define SD_CS SS // Set the chip select line to whatever you use (10 doesnt conflict with the library)
SdFat sd; // set filesystem
SdFile bmpFile; // set filesystem

//for image processing
#define BUF_SIZE 180//10240
#define BUFFPIXEL 60//20

// because the BMP is read from the bottom first, and we are displaying this using a POV wand,
// the BMP needs to be saved on its side to display correctly when the POV is waved, that is
// turned by 90 degrees, then saved as a 24bit bmp to the SD card

// BMP needs to be NUM_LEDS high when saving a version to the BMP.

// In the SD card, place 24 bit color BMP files (be sure they are 24-bit!)


ArduinoOutStream cout(Serial);
// store error strings in flash to save RAM
#define error(s) sd.errorHalt_P(PSTR(s))


void setup()
{
// Serial.begin(9600); //for bug testing and speed checking using serial
// while(!Serial){};
// delay(100);
progmemPrint(PSTR("Initializing SD card..."));
if (!sd.begin(SD_CS, SPI_FULL_SPEED)) {
progmemPrintln(PSTR("failed!"));
return;
}
progmemPrintln(PSTR("OK!"));

//begin for Octows2811
leds.begin();
delay(200);
}

void loop()
{
bmpDraw("star.bmp",2000);//filename and time in ms to keep displaying

}

void bmpDraw(char* filename, unsigned long time) {
unsigned long currentTime = millis();

//so that the image continues to be displayed again for a set time
while (millis()< currentTime + (time)) {

SdFile bmpFile;
int bmpWidth, bmpHeight; // W+H in pixels
uint8_t bmpDepth; // Bit depth (currently must be 24)
uint32_t bmpImageoffset; // Start of image data in file
uint32_t rowSize; // Not always = bmpWidth; may have padding
uint8_t sdbuffer[3*BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
/*uint16_t*/uint32_t povbuffer[BUFFPIXEL]; // pixel out buffer (16-bit per pixel)//////mg/////this needs to be 24bit per pixel////////
uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
boolean goodBmp = false; // Set to true on valid header parse
boolean flip = true; // BMP is stored bottom-to-top
int w, h, row, col;
uint8_t r, g, b;
uint32_t pos = 0, startTime = millis();
uint8_t povidx = 0;
boolean first = true;


// Open requested file on SD card
bmpFile.open(filename, O_READ);
progmemPrint(PSTR(filename));
// Parse BMP header
if(read16(bmpFile) == 0x4D42) { // BMP signature
progmemPrint(PSTR("File size: "));
Serial.println(read32(bmpFile));
(void)read32(bmpFile); // Read & ignore creator bytes
bmpImageoffset = read32(bmpFile); // Start of image data
progmemPrint(PSTR("Image Offset: "));
Serial.println(bmpImageoffset, DEC);
// Read DIB header
progmemPrint(PSTR("Header size: "));
Serial.println(read32(bmpFile));
bmpWidth = read32(bmpFile);
bmpHeight = read32(bmpFile);
if(read16(bmpFile) == 1) { // # planes -- must be '1'
bmpDepth = read16(bmpFile); // bits per pixel
progmemPrint(PSTR("Bit Depth: ")); //Serial.println(bmpDepth);
if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

goodBmp = true; // Supported BMP format -- proceed!

// BMP rows are padded (if needed) to 4-byte boundary
rowSize = (bmpWidth * 3 + 3) & ~3;

// If bmpHeight is negative, image is in top-down order.
// This is not canon but has been observed in the wild.
if(bmpHeight < 0) {
bmpHeight = -bmpHeight;
flip = false;
}

w = bmpWidth;
h = bmpHeight;

for (row=0; row<h; row++) {
if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
else // Bitmap is stored top-to-bottom
pos = bmpImageoffset + row * rowSize;
if(bmpFile.curPosition() != pos) { // Need seek?
bmpFile.seekSet(pos);
buffidx = sizeof(sdbuffer); // Force buffer reload
}

for (col=0; col<w; col++) { // For each column...
// read more pixel data
if (buffidx >= sizeof(sdbuffer)) {
povidx = 0;
bmpFile.read(sdbuffer, sizeof(sdbuffer));
buffidx = 0; // Set index to beginning
}
// set pixel
b = sdbuffer[buffidx++];
g = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];

povbuffer[povidx++] = Color(b,r,g);//octo colour, but you might need to change this
}
for(int i=0;i<ledsPerStrip;i++){
leds.setPixelColor(i, povbuffer); }

leds.show();
// delay(1);// change the delay time depending effect required
delayMicroseconds(220);
} // end scanline

// progmemPrint(PSTR("Loaded in ")); //for bug testing using serial output
// Serial.print(millis() - startTime);
// Serial.println(" ms");
} // end goodBmp
}
}
// Serial.println(read16(bmpFile), HEX);
}
bmpFile.close();
}

//------- HELPER FUNCTION------//
// Create a 24 bit color value from R,G,B ////these strips are BRG, but you might need to change this
unsigned int Color(byte b/*r*/, byte g, byte /*b*/r)
{
//Take the lowest 8 bits of each value and append them end to end
return( ((unsigned int)g & 0xFF )<<16 | ((unsigned int)r & 0xFF)<<8 | (unsigned int)b & 0xFF);
}
//end of helper function

// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(SdFile& f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}

uint32_t read32(SdFile& f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}

// Copy string from flash to serial port
// Source string MUST be inside a PSTR() declaration!
void progmemPrint(const char *str) {
char c;
while(c = pgm_read_byte(str++)) Serial.print(c);
}

// Same as above, with trailing newline
void progmemPrintln(const char *str) {
progmemPrint(str);
Serial.println();
}
 
Last edited:
@e3d, THis was written for the octows2811 lib, but should be easily transferred over to another lib. You probably need to comment out the line:
//DMAMEM int displayMemory[ledsPerStrip*6]; or, as it says that there is an issue in the draw method, it might be to do with the progmemPrint calls. but I don't have a teensy 2 to test this at the moment.

You will also need to change the calls to show() the pixels and to set up the pixel driver. for example, octows2811 lib uses leds.begin() whereas it looks like neopixel lib uses strip.begin(); and strip.show(); you will need to change this in setup.

Also, octows2811 lib uses leds.setPixel to set the pixel colour, whereas it looks as though neopixel lib uses strip.setPixelColor(); I think that you may well need to go through the sketch to firstly initialise the strip in setup with strip.begin, and chagnes the other calls to set the pixels.

I have not used the neopixel library so don't know where to look. The fastspi lib may also be a useful alternative.
 
Last edited:
Status
Not open for further replies.
Back
Top