4

I have made a 24 x 15 LED matrix using an Arduino, shift registers and TLC5940s.

The Arduino Uno has a measly 32 KB of memory, so the graphics are not stored to arrays beforehand. Rather, I write algorithms to generate artistic animations using math equations.

Example code for a rainbow sine wave is:

for (int iterations = 0; iterations < times; iterations++)
{
    val += PI/500;

    for (int col = 0; col < NUM_COLS; col++)
    {
        digitalWrite(layerLatchPin, LOW);
        shiftOut(layerDataPin, layerClockPin, MSBFIRST, colMasks[col] >> 16 );
        shiftOut(layerDataPin, layerClockPin, MSBFIRST, colMasks[col] >> 8 );
        shiftOut(layerDataPin, layerClockPin, MSBFIRST, colMasks[col] );
        digitalWrite(layerLatchPin, HIGH);

        Tlc.clear();
        int rainbow1 = 7 + 7*sin(2*PI*col/NUM_COLS_TOTAL + val);
        setRainbowSinkValue(rainbow1, k);
        Tlc.update();
    }
}

Where the setRainbowSinkValue sets one of the LEDS from 1 to 15 to a certain colour, and val shifts the wave to the right every iteration.

So I'm looking for simple graphics routines like this, in order to get cool animations, without having to store everything in arrays, as 15 x 24 x RGB quickly uses up all 32 KB of RAM.

I will try get an Arduino Mega, but let's assume this isn't an option for now.

How can I do it?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
djb
  • 1,635
  • 3
  • 26
  • 49

4 Answers4

2

There are many effects you can get if you start to overlay simple functions like sin or cos. This guy creates the "plasma" effect which I think is always a cool thing to watch :)

Another way is to use noise functions to calculate the color of your pixels. You get a lot of examples if you google for "Arduino Perlin noise" (depending on your Arduino model you might not be able to get high framerates because Perlin noise requires some CPU power).

Philipp Horn
  • 568
  • 1
  • 5
  • 12
  • I see someone posted some Perlin noise Arduino code. I'll read through your link and try it out. Thanks! – djb Apr 04 '13 at 14:41
2

I've been working on similar graphics style projects with the Arduino and have considered a variety of strategies to deal with the limited. Personally I find algorithmic animations rather banal and generic unless they are combined with other things or directed in some way.

At any rate, the two approaches I have been working on:

  • defining a custom format to pack the data as bits and then using bitshifting to unpack it

  • storing simple SVG graphics in PROGMEM and then using sprite techniques to move them around the screen (with screen wrap around etc.). By using Boolean operations to merge multiple graphics together it's possible to get animated layer effects and build up complexity/variety.

I only use single color LEDs so things are simpler conceptually and datawise.

spring
  • 18,009
  • 15
  • 80
  • 160
  • Interesting; so drawing SVG to LEDs... any hints on how to do that? – djb Apr 06 '13 at 15:59
  • @djb: SVG is just an XML format storing a path's points or primitives (rects, circles, etc). With (very) simple shapes, I store the data points (x/y) and use line routines (Bresenham, DDA) to interpolate between them - to do sprite animation: pull shape data from PROGMEM, turn on LEDs based on interpolating that data, wait a bit, shift shape data by some x/y, redraw at new position. The gist is to store only the simple shape data - not every frame of an animation - and do the animation through code. – spring Apr 06 '13 at 19:26
1

A good question but you're probably not going to find anything due to the nature of the platform.

You have the general idea to use algorithms to generate effects, so you should go ahead and write more crazy functions.

You could package your functions and make them available to everyone.

Also, if you allow it, use the serial port to communicate with a host that has more resources and can supply endless streams of patterns.

Using a transmitter and receiver will also work for connecting to another computer.

zaf
  • 22,776
  • 12
  • 65
  • 95
  • I run it on a car battery in the desert, so unfortunately I can't use a computer. But yeah, I'll post the code somewhere if I come up with anything especially good. – djb Apr 04 '13 at 13:56
  • indeed, Afrika Burn :) it's like burning man but 1/10 the size – djb Apr 04 '13 at 14:35
  • Holy crap! Wish I could go! – zaf Apr 04 '13 at 17:51
1

I will answer related questions but not exactly the question you asked because I am not a graphics expert....

First of all, don't forget PROGMEM, which allows you to store data in flash memory. There is a lot more flash than SRAM, and the usual thing to do, in fact, is to store extra data in flash.

Secondly, there are compression techniques that are available that will reduce your memory consumption. And these "compression" techniques are natural to the kinds of tasks you are doing anyway, so the word "compression" is a bit misleading.

First of all, we observe that because human perception of light intensity is exponential (shameless link to my own answer on this topic), depending on how exactly you use the LED drivers, you need not store the exact intensity. It looks like you are using only 8 bits of intensity on the TLC5940, not full 12. For 8 bits of LED driver intensity, you only have 8 or 9 different intensity values (because the intensity you tell the LED driver to use is 2^perceptible_intensity). 8 different values can be stored in only three bits. Storing three bit chunks in bytes can be a bit of a pain, but you can still treat each "pixel" in your array as a uint16_t, but store the entire color information. So you reduce your memory consumption to 2/3 of what it was. Furthermore, you can just palettize your image: each pixel is a byte (uint8_t), and indexes a place in a palette, which could be three bytes if you'd like. The palette need not be large, and, in fact, you don't have to have a palette at all, which just means having a palette in code: your code knows how to transform a byte into a set of intensities. Then you generate the actual intensity values that the TLC5940 needs right before you shift them out.

Community
  • 1
  • 1
angelatlarge
  • 4,086
  • 2
  • 19
  • 36
  • 1
    When I compile, it says "Binary sketch size: 14,234 bytes (of a 32,256 byte maximum)" for example. So, isn't it already using 32KB flash memory? SRAM is only 2KB. Thanks for compression advice; I'm not sure I understand how to compress 24 bit colour into 16 bits. I use 3 unsigned chars to hold R/G/B; so instead it indexes a palette? Could you expand on this? For the rainbow code's intensity, k is just incremented, and goes into a sin() func to get a value between 1-256, which is shifted up to between 1-4096. Don't think I will save much space on intensity value compression. – djb Apr 05 '13 at 11:50
  • ah ok, so a palette like described in Philipp's link, using HSBtoRGB... just 256 colours or so, that don't have discontinuities. – djb Apr 05 '13 at 12:54
  • No, you are using 15K (out of 32K), so you have more than half of your flash free. – angelatlarge Apr 05 '13 at 16:10
  • Is there any way to load PROGMEM in a non-static/non-const fashion? I'd like to initialize a PROGMEM int[360][3] with palette colours, but if I try to say palette[i][j] = 0, it says I can't use read-only memory. – djb Apr 06 '13 at 14:28
  • No, unfortunately not: `PROGMEM` is _almost exclusively_ read-only. It is possible to write to `PROGMEM`, but only a page at a time. – angelatlarge Apr 07 '13 at 06:17