Aurora on 64x64 display, SmartMatrix 3.0 Beta

I finally got a 64x64 display assembled and working! After testing with the FeatureDemo and AnimatedGIFs examples, I immediately set out to get Aurora working. Aurora uses a lot of RAM, and combined with the quadrupled buffers required for 64x64, it just wouldn’t fit. So I started a new branch for 64x64 and started hacking away, disabling features.

I have the Menu, Settings, Patterns, and Audio Patterns working. Animations are not disabled, but do not work yet. The buffer used to decode the GIFs is just too big after being quadrupled to handle 64x64 images. I’m trying to rework it to just decode directly to the background layer’s buffer, but I think that might cause problems with some GIF frame disposal methods.

Anyway, it’s up on GitHub if anyone want to try it out: GitHub - pixelmatix/aurora at sm3.0-64x64

Here’s some video of it in action. It really doesn’t do it justice, it’s so much more amazing in person. :smile:

This is great! How many patterns are actually working as intended? I found the FastLED examples in SmartMatrix don’t function properly at 64x64, the noise function just wasn’t intended to handle values >32. I’m guessing some of the more complicated patterns (e.g. NoiseSmearing) would have a slow framerate.

I started trying to solve the RAM problem for 64x64 GIFs a while ago. The sm3.0 branch of AnimatedGIFs has a ~256byte LZW decoding buffer instead of width*height size. The sm3.0-giflayer branch has the option to USE_EXTERNAL_BUFFERS for LZW decoding, to save even more RAM, but it really complicates the sketch. I’d like to create a custom Layer class for decoding GIFs that can hide the complexity from the user, but I’m not actively working on this anymore.

Wow, looks great! Now you’re tempting me to go out and buy a bigger display! Do you have a frame that works with the 64x64 matrix or are they just bare at the moment? Was hard to tell in the dark on the videos. I assume you’re using 2 chained 64x32 units like they sell on Adafruit? What do you use to mount them together?

As far as the memory issues go, have you tried dynamically allocating the buffers for each pattern instead of using static arrays? This actually occurred to me some time back when I was looking at the code, but it wasn’t a pressing issue until now. I know new and delete aren’t allowed in embedded C++ but malloc/free should work. Since you can’t use constructors, you’d just need an init/free method on each pattern class that you call from the menu when they’re switched out. I know memory fragmentation can be a concern when using malloc on an embedded device but I think if you statically allocate anything that is shared between the patterns and are very meticulous about freeing everything you malloc, fragmentation could be avoided. I haven’t really looked at the gif code but I am assuming it could use dynamic allocation as well, so when I say pattern I really mean any display function that’s switched in and out.

Another alternative could be to statically allocate a buffer that’s shared between each pattern. Make it big enough for the most demanding pattern and the patterns that don’t need enough can just ignore whatever they don’t need. If certain patterns need multiple smaller buffers, or buffers of different types, just declare pointers to the desired offset within the array and cast it to the appropriate type.

I have 11 Patterns and 33 Audio Patterns working. I have not made it through all of the Audio Patterns yet, some are still not scaling properly.

I’ll try to take a look at the noise and other FastLED examples. I had to disable all of the patterns, audio patterns, and effects that uses large arrays, such as noise, noise streaming, fire, flock, attract, etc.

I haven’t looked at the LZW code, but I do think it might be possible to get rid of at least one of these buffers in GIFParseFunctions.cpp:

// Buffer image data is decoded into
byte imageData[WIDTH * HEIGHT];

// Backup image data buffer for saving portions of image disposal method == 3
byte imageDataBU[WIDTH * HEIGHT];

I think I should be able to just write directly to the background buffer. I think for DISPOSAL_NONE I can just clear the display before writing the next frame. For DISPOSAL_LEAVE I won’t clear the display, just write the next frame. DISPOSAL_BACKGROUND just fills the screen with a background color before writing the next frame. I don’t think DISPOSAL_RESTORE will work properly without a buffer.

Thanks! I used four 32x32 matrices. It should work with two 64x32 matrices though. I don’t have it in a frame yet, the video is just the bare display with no diffusion. I may try the Ikea RIBBA, although Louis says it’s deeper than the MCS shadow box, so it’ll take some work to get the right depth/diffusion. Right now I just have them mounted on a piece of 1/8" hardboard from Home Depot. I’ll try to post some pictures soon.

I’ve been meaning to try dynamic allocation for the patterns. I don’t have any experience with it in C/C++. Arduino/Teensyduino has been my first real foray into it. I have a lot of experience with C# and Java.

I’ve already combined the easy stuff, where each pattern had its own buffers, such as heat maps, boid arrays, etc.

If you have time, it’d really help for me to see even a simple example of dynamically allocating patterns. I would greatly appreciate the help.

I would avoid using malloc with free, as that can result in problems and hard to troubleshoot bugs. I suggest allocating a large buffer that is used by only one pattern at a time. That may take some annoying pointer wrangling to divide up the large buffer among the objects each pattern needs when it starts, but should result in a more stable program.

I’ll try to work up an example using the Life pattern when I get a chance. I would definitely suggest reading up on pointers and arrays in C. They are really just different syntaxes for the same thing, and once you grasp that, a lot of the low-level memory management stuff will start to make a lot more sense. That will be important to understand regardless of whether you are using malloc/free or using pointers to reference different parts of a shared buffer.

Chapter 5 of K&R is probably the best explanation you’ll ever read, directly from the guys who invented C. Chapter 6 is also well worth a read, especially section 6.8 on unions and 6.9 on bit fields. Unions allow you to treat the same piece of memory as several different types, and that could actually be one way to make it easier to deal with a shared buffer. Bit fields can be really helpful for reducing memory consumption when you only need to store a few bits of data. That is the trick I used to save space in the Life pattern’s Cell class.

I feel a bit guilty for linking to a pirated copy of that book. IMO it’s one of the best computer science texts of all time and belongs on every serious C programmer’s shelf. If you end up finding it useful, please ease my conscience and buy a copy :).

Hello how exactly do i install this on my teensy? i Have a 64x64 led display and would like to use it.

Do you already have the Teensy connected to the display? Are you already using a SmartMatrix shield? Have you already gotten the ‘regular’ Aurora working? If yes to all of these, then you can try the Aurora 64x64 branch.