LED Matrix animation editor

Getting tired of having to manipulate my animation in code for every little change, I got the idea of a tool that could help me in this.

I have some future plans for implementation, but any suggestions are welcome.

Download: http://www.bleq.nl/arduino/downloads/ArduinoFrameAnimator.jar
The jar should be runnable if you have java setup correctly. If you're having trouble starting it, please reply to this thread and I'll try to help you getting it running.

The tool generates a .h file that looks like this:

int animationFrames = 2;

uint8_t animation[][8][8] = {
  {
    { 0x1, 0x2, 0x3, 0x2, 0x1, 0x0, 0x1, 0x2 },
    { 0x0, 0x1, 0x2, 0x3, 0x2, 0x1, 0x0, 0x1 },
    { 0x1, 0x0, 0x1, 0x2, 0x3, 0x2, 0x1, 0x0 },
    { 0x2, 0x1, 0x0, 0x1, 0x2, 0x3, 0x2, 0x1 },
    { 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3, 0x2 },
    { 0x2, 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3 },
    { 0x1, 0x2, 0x3, 0x2, 0x1, 0x0, 0x1, 0x2 },
    { 0x0, 0x1, 0x2, 0x3, 0x2, 0x1, 0x0, 0x1 }
  },
  {
    { 0x2, 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3 },
    { 0x1, 0x2, 0x3, 0x2, 0x1, 0x0, 0x1, 0x2 },
    { 0x0, 0x1, 0x2, 0x3, 0x2, 0x1, 0x0, 0x1 },
    { 0x1, 0x0, 0x1, 0x2, 0x3, 0x2, 0x1, 0x0 },
    { 0x2, 0x1, 0x0, 0x1, 0x2, 0x3, 0x2, 0x1 },
    { 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3, 0x2 },
    { 0x2, 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3 },
    { 0x1, 0x2, 0x3, 0x2, 0x1, 0x0, 0x1, 0x2 }
  }
};

You can also find the Sketch I'm using this with over here:
http://www.bleq.nl/arduino/downloads/LedMatrixShiftedPwmExample.zip

For this Sketch I have my Arduino wired like this:

As you can see I've chosen to use two shift registers, one for each side of the matrix. This mixes the sourcing and sinking of currents on the registers.
It's not really according to specs, because of the limits on the 74HC595. I draw and sink more current than advised by the documentation, but so far it's still running...

Neat idea. I'll play with it for sure!

so I've had a go at it, my hardware is different than yours so it's not an exact fit but it's quite useful none the less.

Suggestions:

  1. I'm using a sparkfun matrix so unlit cells are white on black rather than black.(actually I see they're grey but whiter would be closer to reality)
  2. It would be helpful to be able to copy a cell - just another button up top that would duplicate the selected cell similar to insert.
  3. It would be good to be able to draw by dragging the mouse i.e. I wouldn't have to click each cell.
  4. A question - where does the time delay from the gui go? I don't see it in the animation.h file and your sketch seems to have a "50" built in.
  5. This is over-the-top but I ask because maybe it's not hard: I don't have pwm but I do have rgb. How hard would it be to let me (say) hold down the r key for red or g for green or something similar.
    6)when you save or export it filters the list for(e.g.) .led or .h but it doesn't append the suffix which i would expect it to do.

As a further note, this is good just for drawing too. I have a cute little house on my display.

Thanks again and please post any updates or animation extravaganzas. Maybe we could have an 8x8 film festival with side scrolling subtitles.

Thanks Bill for your replies.

Some of the suggestions you mention are already on my (mental) list for improvement.

A reaction to your suggestions:

  1. Mine are quite white as well, so good suggestions.
  2. My idea was to be able to drag-n-drop frames to order them, or copy them by pressing Ctrl
  3. Definitely on the list, quite annoying to click after a while. My idea is to have some sort of a palette on the left where you can choose a color from.
  4. The delay is, unfortunately, not really used yet. At first I wanted to include it in the data set using a more complex structure, but that would make it harder to adapt code to use it. My new idea is to add an array in the .h-file with the time per frame.
  5. RGB support is on my list too. I haven't got all the hardware to wire up my own RGB matrix yet, but I want the app to support it by the time I get my hardware wired up.
  6. I'll look into this, there's gotta be a standard solution for this.

Thanks again for your suggestions.

Version 0.2 (16-02-2010)

  • Implemented whish for saving with extension if none given in dialog.
  • Restructured the FrameOrderEditor, only code change, no visual improvement
  • Drag-n-drop frames to sort them
  • Drag-n-drop+Ctrl to copy frames
  • Palette with colors
  • Draw by drag, not just click
  • Timedelay actually used, exported to .h file in animationDelays[]
  • About screen

http://www.bleq.nl/arduino/downloads/ArduinoFrameAnimator.jar
That is a link to the current version. Explicit download:
http://www.bleq.nl/arduino/downloads/ArduinoFrameAnimator_0.2.jar

I think this is neat, although I don't plan on using it anytime soon. I do have a couple of suggestions (both are probably overkill, though):

  1. Can you implement an RLE (run-length encoding) compression scheme on the bits; you would have to change your tool and the sketch, but this should make the memory footprint smaller.

  2. On a similar note (and possibly more effective?) can you do intra/inter-frame diffs between frames; that is, if an LED is on in two adjacent frames, don't repeat, just leave it on? In a way, it is RLE on a per-frame basis.

Like I said, these are both overkill for the project, and the data and code would be more daunting (and not as easy to follow); but on a RAM starved system like the standard Arduino, every little byte (and bit) counts...

:slight_smile:

Thanks cr0sh, for you reply and suggestions.

I am planning on improving the memory usage, you are quite right about every byte counting.

Currently it's using 1 byte per pixel.
With the 4 intensity levels I'm using, only 2 bits per pixel would be sufficient.

I'll definitely look into more efficient storage, I might look into RLE. Haven't heard of it before, but after looking at it on wikipedia I'm not sure how much it'll improve in this case. But it's worth a try.

Well, giving it some thought, it really doesn't make much sense when there are only 64-128 bits involved (works much better on larger resolutions). The inter-frame differential compression idea might work better; encode the frame differences and decode them, instead of sending each complete frame (similar to mpeg encoding). Even so, I am not sure if it would save you much or not...

coolstuff :slight_smile:

any chance of a version with matrix size option? like 4x4 5x5 etc..
and are you limited to just them 4 levels of light in the pallet?

Thanks.

I don't have matrices other than 8x8 myself. But if you're interested in support for other sizes, I'll add it to the wishlist.

The 4 levels in the pallet could also be configurable. I have chosen for only 4 because I didn't notice much difference between the other levels and it could save quite some space in memory.

If I'm right the intensity of higher levels is marginal.
My code, for example, has a maximum of 16 PWM levels.
So if you use level 1, the LED is on 1/16 of the time.
On level 2, the LED is on 2/16 of the time, that's double the time and so double the perceived intensity.
Level 3, turns the LED on for 3/16 of the time, that's already only 1.5 times the intensity of level 2.

If you continue this calculation, you'll find the level 16 is only 1.067 times brighter than 15.

But I'll add it to the wishlist, mainly because I just found out the difference between level 16 and level 3 would still be a factor 5.3, which I didn't realize before.

After another evening of hacking and typing I can't resist releasing my latest additions before going to bed (GMT+1 here).

Version 0.3 (17-02-2010)

  • Minor gui updates
  • New data format (16 bytes/frame instead of 64)
  • Multiple matrix sizes
  • Sroll to added frame in frame order editor
  • Drag pixels in frame, for easier movement animations

http://www.bleq.nl/arduino/downloads/ArduinoFrameAnimator.jar (linked to latest version)
http://www.bleq.nl/arduino/downloads/ArduinoFrameAnimator_0.3.jar (explicitly download 0.3)

this is nice to make wavetables with, with a little bit of editing it could be made to do that even better. draw a waveform and get 128 or 256 values.
maybe even with some smoothing and so on.

That is a nice looking app. Can you clarify it for me?

It seems that this "animates" things by creating individual frames on a development PC, generating source code for a series of constants in an Arduino sketch.

That is in comparison to generating code in the loop() function that changes a starting frame "on the fly" while the sketch is running on an Arduino.

Both methods will give the same visual result, of course. I just want to understand it more completely.

Thanks

kool thanks for adding the option for different size matrix :3

Another update to the tool.

Version 0.4 (28-02-2010)

  • Select part of frame (for dragging)
  • Cancel in Size Selector window is working now
  • Added support for RGB matrix
  • Hotkeys for insert, delete, next and previous frame (INSERT, DELETE, ALT+Right, ALT+Left)
  • Added app logo (taskbar, alt-tab window)
  • Added context menu to copy/move a tile when dragged (drag frame with right mouse)
  • Rotate and flip animation
  • Remember used folder (open/save/export)
  • Live connection (over TCP, port 4321)

More information:
https://www.bleq.nl/arduino/
And in the readme:
https://www.bleq.nl/arduino/downloads/ArduinoFrameAnimator_Readme.txt

@TBAr: it is indeed a tool that creates frames that are stored on the Arduino and then played back. You could of course, like you said, also write code that generates the frames. It'll depend on your wishes which one suits best.

very nice. will play with it today.

hi, im new to all this stuff so go easy dude. ive got a 328 board running a direct drive 8x8 matrix. Would you be able to change your code to allow for that instead of the serial one you have. if that makes any sense and you can, would you mind sharing that code. thanks jay

if you have your matrix working drawing simple patterns you can probably adapt his code. I did that because my hardware was different.

start by just looking at the definitions of the patterns.

For those who're struggling with the more complex code, here is a simpler version.
I haven't spend much time writing this, therefore it's not optimized.
But, it'll give help you get started.

#include <TimerOne.h>

#include "animation.h"

// Pin numbers of the rows of the matrix
int rows[] = { 19, 18, 17, 16, 15, 14, 13, 12 };
#define ROW_ON HIGH
#define ROW_OFF LOW

// Pin numbers of the columns of the matrix
int cols[] = {  9,  8,  7,  6,  5,  4,  3,  2 };
#define COL_ON LOW
#define COL_OFF HIGH

// Number of PWM levels, with 2 bits/pixel it's 4, with 4 bits/pixel it's 16
#define PWM_LEVELS 4
int timerDelay = 1400; // The time the iProcess gets to handle it's stuff
byte pwmCounter = 0; // A timer used for for PWM effect
byte brightnesses[64]; // The current image to display

// This method is activated by Timer1 and displays the image, one row at a time
void iProcess() {
  for(int r=0; r<8; ++r) {
    for(int c=0; c<8; ++c) {
      // Turn on and off the columns
      if(brightnesses[r * 8 +c] > pwmCounter) {
        digitalWrite(cols[c], COL_ON);
      } else {
        digitalWrite(cols[c], COL_OFF);
      }
    }
    
    // Show the current row
    digitalWrite(rows[r], ROW_ON);
    delayMicroseconds(40);
    digitalWrite(rows[r], ROW_OFF);

    if(++pwmCounter > PWM_LEVELS) pwmCounter = 0;
    
    // Turn off all columns (this is where it currently goes wrong)
    // The first column will be on much longer than the last... therefore you get dimmer pixels to the right
    for(int c=0; c<8; ++c) {
      digitalWrite(cols[c], COL_OFF);
    }
  }
}

void setup() {
  // Set rows and columns to output and off
  for(int r=0; r<8; r++) {
    pinMode(rows[r], OUTPUT);
    digitalWrite(rows[r], ROW_OFF);
  }
  for(int c=0; c<8; c++) {
    pinMode(cols[c], OUTPUT);
    digitalWrite(cols[c], COL_OFF);
  }
  
  // Initialize the timer
  Timer1.initialize(timerDelay);
  Timer1.attachInterrupt(iProcess);
}

// A number indicating when to advance to the next frame
unsigned long nextImage = 0;
// A counter to know what frame we're showing
int animationIndex = -1;

void loop() {
  if(millis() > nextImage) {
    if(++animationIndex >= animationFrames) animationIndex = 0;
    nextImage = millis() + animationDelays[animationIndex];
    // Copy the pixels from the frame to the array used by iProcess
    for(int i=0; i<(8*8); ++i) {
      brightnesses[i] = (animation[animationIndex][i/4] >> (i%4*2)) & B00000011;
    }
  }
}

Very cool. I wish I had PC programming skills to make something like this to support my Arduino driven 5x5x5 LED cube. I have a 32K byte eeprom on it for displaying predefined scripts but it sure is time consuming building the scripts manually. :cry:

Lefty