# Read and decode .led file

I'm working on a flexible LED screen persisting of 20 meters of digital RGB LED strip with WS2811 IC for each LED.

I want to control this using Arduino, which isn't a problem using FastSPI. My problem is, that I want to create different LED programs (for instance using LedEdit 2013) which can then be exported to *.led files.

This in itself isn't a problem, as I have already generated these files. The problem is how to read and decode the *.led file? From what I have been able to read around the internet (searching around for couple hours now) the files consist of some sort of header followed by base64 encoded information about the x and y position of LEDs that should be turned on and which color they should be (RGB 0-255 value I suppose). I am not entirely sure how exactly I am going to decode this signal and get something actually useful out of it?

If I can just get some useful information out, I can surely program my way out if implementing it with the FastSPI library for Arduino.

EDIT:

I have made two simple example files that could perhaps be easier to look at for you guys.

Both are made up op a simple 10 x 10 led setup, both only one scene; one all black and one all white.

• Does the example led file you've posted just show a pattern for the first frame and blank for the other 19 by chance? – PeterJ May 29 '13 at 6:07
• @PeterJ I accidently changed the example file on my server. I have changed the post above to now have two example files: one of a black LED scene and one of a white LED scene. What I have come to conclude so far: Each line has exactly 1024 characters; First line has 512 "random stuff" and 512 characters of what seems to be scene information; When the scene is uniform (white or black) the information in the file seems to be uniform colour information to be sent out. Still I have trouble making sense of this as binary code or decimal code - even using base64 decoding... – Simon May 29 '13 at 20:19
• There, now I want some black LEDs! Oh, wait, I do have some black light emitting diodes, the UV ones. – Anindo Ghosh May 30 '13 at 4:35
• It looks like straight binary data to me, I just gave it a good crack and started writing an answer about it being a bitstream because the 0/1 values are determined by varying pulse widths (it's not normal SPI) but a few things didn't add up there either when I looked at the bit patterns. I wouldn't have a chance for a few days anyway but if you don't get an answer ping me and I'll write up an answer with a different way to go about it without using LedEdit (although it will be untested C pseudocode). – PeterJ May 30 '13 at 11:12
• Anindo Ghosh: what?? @PeterJ: yesterday it started to make a little sense to me, but then suddenly it didn't any more. I was thinking if it was a bitstream of 0/1 that I was looking at, but then again to get white I would imagine everything being just 1's and to get black just 0's, which is not the case. Also it seems that the characters used in the file is different each time I make a test export. Yesterday I made a simple RGBWB scene (one scene of each) which gave an interesting pattern, but still nothing useful. – Simon May 30 '13 at 16:01

I also had problems interpreting the structure of the .LED file however another method would be to create your own bitmaps. I created a LED image using Microsoft Paint, under File | Properties I set the width and height both to 10 pixels followed by a File | Save As and selecting 24-bit Bitmap as the type. If you zoom in to maximum it's easy to select pixels individually and Linux and Mac OSX have plenty of applications that can write Windows bitmaps if you're using a different O/S.

Then I created the following Visual C++ command line application to read the bitmap and create a constant array definition. It writes the code to stdout so for example you can invoke it using bitmapconvert test.bmp > test.h. Note that it makes various assumptions regarding the bitmap size and bit depth so in no way is 'production code' so at some stage you may want to look further into the bitmap file format.

#include "stdafx.h"
#include <stdio.h>
#include <stdint.h>

#define BITMAP_WIDTH 10
#define BITMAP_HEIGHT 10

#pragma pack (1)
struct PIXEL_DEF {
uint8_t B, G, R;
} pixels[BITMAP_HEIGHT][BITMAP_WIDTH];

{
uint32_t data_offset, curx = 0, cury = 0;

fseek(bmp, data_offset, SEEK_SET); // Move to start of bits
{
curx++;
if (curx >= BITMAP_WIDTH)
{
// Rows padded to 4 bytes, so we may need to skip some data
fseek(bmp, 32 - curx * sizeof(PIXEL_DEF) % 32, SEEK_CUR);
curx = 0;
cury++;
}
}
fclose(bmp);
}

// Following assumes top-left to bottom-right order, you may need to change
void dump_bitmap()
{
int x, y;

printf("const uint8_t bitmap_data[] = {\n");
for (y=0; y < BITMAP_HEIGHT; y++)
{
for (x=0; x < BITMAP_WIDTH; x++)
{
if (x == 0)
printf("\t");
else
printf(",");
printf("0x%02X,0x%02X,0x%02X", pixels[y][x].R, pixels[y][x].G, pixels[y][x].B);
}
if (y < BITMAP_HEIGHT)
printf(",");
printf("\n");
}
printf("};\n");
}

int _tmain(int argc, TCHAR* argv[])
{
FILE *bmp;

if (argc != 2)
printf("Usage: BitMapConvert infile.bmp");
else {
if (bmp = _wfopen(argv[1], _T("rb")))
{
dump_bitmap();
}
}
return 0;
}


The 10 x 10 24-bitmap I used for testing has the following pattern.

The output generated from the above program has been included at the start of following pseudocode that is my interpretation from the datasheet of how data should be sent to the device. The following delays are fow low speed mode, use half the values for high speed (although reset can remain the same). Pin 7 is connected to VDD for low speed, and left disconnected for high-speed so you'll need to check how your hardware is configured. If possible low speed will be easier.

const uint8_t bitmap_data[] = {
0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,
0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0xFF,
0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,
};

void send_zero()
{
set_DI_high();
delay_us(0.5);
set_DI_low();
delay_us(2);
}

void send_one()
{
set_DI_high();
delay_us(1.2);
set_DI_low();
delay_us(1.3);
}

void send_reset()
{
set_DI_low();
delay_us(50);
set_DI_high();
}

void send_image()
{
uint8_t i;

send_reset();
for (i=0; i < sizeof(bitmap_data); i++)
{
if (bitmap_data[i] & 0x80) send_one() else send_zero();
if (bitmap_data[i] & 0x40) send_one() else send_zero();
if (bitmap_data[i] & 0x20) send_one() else send_zero();
if (bitmap_data[i] & 0x10) send_one() else send_zero();
if (bitmap_data[i] & 0x08) send_one() else send_zero();
if (bitmap_data[i] & 0x04) send_one() else send_zero();
if (bitmap_data[i] & 0x02) send_one() else send_zero();
if (bitmap_data[i] & 0x01) send_one() else send_zero();
}
}


As you can see the protocol is not SPI and one challenge you will have getting the timing right within the +/- 150nS specified by the datasheet which is about 1 cycle at 8MHz. Rather than using any form of delay_us you'll probably have to use nop instructions and take into account timing delays caused by setting the ports and function calls etc. Sometimes examining the assembler output of the compiler can be useful in that regard and a scope is always useful to verify it. The datasheet appears to show the same 150nS value for both low and high speed modes. It may be in low speed mode it's a little less critical in practice.

Having said that I just looked at the FastSPI library you mentioned and it does mention support for that device. It appears to use the SPI channel in a non-standard way (not that there's any problem with that) to help alleviate the timing constraints. So a good starting point would be try using that in combination with the above bitmap table.

• It is perhaps not the perfect solution, but it has given me some ideas of a different approach using images rather than using standard software like LedEdit to make the files. Thank you very much for you time and help! – Simon Jun 18 '13 at 22:30