Close

I'd write you a shorter letter if I only had the time.

A project log for Holiday Blinkenlights in 1K for about a buck

Creating interesting graphics on a strand of RGB LEDs using an ATtiny13, with just 64 bytes of RAM and just 1K of flash.

mark-vandewetteringMark VandeWettering 12/03/2016 at 21:390 Comments

When you are writing prose, brevity can be difficult. You have to think very carefully about how to express your ideas concisely and achieve maximum impact. Often, it's simply easier to dump the contents of your brain, and just keep writing and writing, and telling stories and engaging in diversions and pretty soon you realize that you are just continuing to ramble, not just about how to do LED programming compactly, but on the meaning of Christmas, about how you wore your blinking Santa hat, and this one woman with a full grocery cart and a screaming child and a tired look on her face was behind you when you were just picking up a carton of milk, and yet managed a smile, and about how you immediately allowed her to cut in front of you because hey, that's the way it is during the holidays, and I showed her pictures of my grand daughter, and now as I think about it I have two grand daughters and I miss them especially now that it's nearing Christmas time...

You get the idea.

The fact is that the most direct path to getting a basic Christmas LED blinker is to stand on the shoulders of giants, and just use the hardware and software that others have used before you. The most obvious and least error prone way would probably be to buy a real Arduino from the official suppliers or from Adafruit for around $25, download the standard Arduino development environment, then use Adafruit's awesome NeoPixel library and tutorials. Indeed, that's just what I did. As my first program I decided to do something simple. I'd write a program that would display a repeating pattern of eight colors along the entire length of 240 LEDs that I had, and that a few times a second it would shift the LEDs, and then redisplay the pattern. It didn't take me very long (I spent more time finding a spare Arduino and the appropriate USB cable). Here's the resulting code:

#include <Adafruit_NeoPixel.h>

const int PIN = 5 ;
const int NLED = 240 ;

// Create an interface to the Adafruit library, which is 
// very cool and very convenient...

Adafruit_NeoPixel strip = Adafruit_NeoPixel(240, PIN, NEO_GRB + NEO_KHZ800);

void
setup()
{
    strip.begin() ;
    strip.show() ;
}

#define LVAL 	(0)
#define HVAL	(64)

int colortab[] = {
    LVAL, LVAL, LVAL,		// BLACK
    HVAL, LVAL, LVAL,		// RED
    LVAL, HVAL, LVAL,		// GREEN
    LVAL, LVAL, HVAL,		// BLUE
    HVAL, HVAL, LVAL,		// YELLOW
    LVAL, HVAL, HVAL,		// CYAN
    HVAL, LVAL, HVAL,		// MAGENTA
    HVAL, HVAL, HVAL,		// WHITE
} ;


void
loop()
{
    int start = 0 ;
    int offset, i ;

    for (;;) {
	offset = start ;
	for (i=0; i<NLED; i++) {
	    strip.setPixelColor(i, colortab[offset], colortab[offset+1], colortab[offset+2]) ;
	    offset += 3 ;
	    if (offset >= 24)
		offset = 0 ;
	}
	strip.show() ;
	delay(100) ;

	start += 3 ;
	if (start >= 24) 
	    start = 0 ;
    }
}

Yeah, it's not commented, because it seems pretty obvious to me. If it's not to you, then go read Adafruit's tutorial some more. Basically the colortab array contains R, G, B values for 8 different colors, and the main loop starts by copying entries from that table into the strip using the simple strip.setPixelColor interface. Most of the logic involves handling the wrap around and replication of color values. When all the LEDs have been updated, then it calls the strip.show() function to tell them all to update. It then updates, delays for 100 milliseconds, and then repeats.

It works. It works just fine. If you want, you could skip onto using code like this, or modifying it to do different patterns, timing, etc.

But...

Here is the thing: it's on a $25 Arduino Uno board, which is physically large, and fairly expensive.

First, let's address the size. One of the great things about the Arduino is that there are a variety of boards which are compatible with one another. The same code could conceivably be run on those boards too. For instance, Adafruit sells the Metro Mini for a mere $12.50, which is tiny (just 0.7" x 1.7") and is in most respects a completely compatible replica of the larger and more expensive Uno board. These are great. They include the necessary interface to use USB to power them and communicate with them for programming. If you are really trying to pinch pennies, you can get imported boards from China, like these from Banggood, which cost just $10.20 for five of them. Yes, just $2 each. These don't include the USB interface, but you can buy one separately for about $5.

Adafruit also stocks a series of cheap boards based upon a smaller cousin of the ATmega328 chip that powers the Uno. The Adafruit Trinket uses an ATtiny85, which costs just $7, and has just 8K of flash and only 512 bytes of RAM memory (compared to the 32K of flash and the 2K of RAM in the standard Arduino.) These boards are not entirely compatible with the Uno, but you can program them using the same and IDE and if you are careful, you can use many libraries on them as well.

Whether you choose to support some of the terrific vendors like Adafruit or Sparkfun who provide a lot of awesome libraries and support, or scrimp a few pennies and roll the dice with super chip modules from China (I do both) you could use the code above to drive these LEDs.

But...

There are a few issues with the code. First of all, it's not all that tiny. Sure, it fits pretty easily into Arduino. When it compiles, it reports the following:

Program:    2568 bytes (7.8% Full)
(.text + .data + .bootloader)

Data:         88 bytes (4.3% Full)
(.data + .bss + .noinit)

We have lots and lots of space! Only 7.8% full!

But there is a hidden issue. This does not report the memory that is allocated by the NeoPixel library at run time. Adafruit's library is awesome and flexible, because it allows you to update each pixel in any order you like. You can even read back the values that you stored there, which is nice when you try to fade them smoothly to black over time. But that means that you need 3 bytes of storage for each LED in the system. Since my 4m strand has 240 LEDs, that means that I need 3 * 240 bytes (about 720 bytes) or memory to store those values. That's over 1/3 of the total RAM available, which again, doesn't sound too bad. But perhaps you need that RAM for something else. Or perhaps you want to use one of those Adafruit Trinket's, which only has 512 bytes of memory. What can you do?

Well, that's what this project is all about. It is about being clever about making cool blinky light displays that use very little memory. You could use the Trinket or perhaps just a lowly ATtiny13A which costs just $1.25 and has only 1K of flash and just 64 bytes of ram to make cool, blinking displays. That you can use to make a Santa hat. I am going to show how I used the awesome info (and code!) I gleaned from @joshlevine about how to drive these RGB strips to make the same display that took just 2568 bytes of flash and 720+ bytes of RAM to run in less that 300 bytes of flash and maybe only four bytes of RAM. I'll then do a couple of additional tricks to show how patterns which may not appear to be so simple can still be done on tiny processors.

Feel free to leave comments and discussion, I'll try to answer them as I go along.

Discussions