Monday, 10 July 2017

arduino - 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.


Black: http://www.codesmith.dk/black.led and White: http://www.codesmith.dk/white.led



Answer



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
#include

#define BITMAP_WIDTH 10
#define BITMAP_HEIGHT 10

#pragma pack (1)
struct PIXEL_DEF {

uint8_t B, G, R;
} pixels[BITMAP_HEIGHT][BITMAP_WIDTH];

void read_bitmap_data(FILE *bmp)
{
uint32_t data_offset, curx = 0, cury = 0;

fseek(bmp, 10, SEEK_SET); // Skip to offset to bitmap bits
fread(&data_offset, sizeof(data_offset), 1, bmp);
fseek(bmp, data_offset, SEEK_SET); // Move to start of bits

while (fread(&pixels[cury][curx], sizeof(PIXEL_DEF), 1, bmp))
{
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")))
{
read_bitmap_data(bmp);
dump_bitmap();
}
}

return 0;
}

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


Test 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.


No comments:

Post a Comment

arduino - Can I use TI&#39;s cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...