Introduction

The LED Matrix Shades use three main approaches to generating patterns on the front LED array:

Bitmap Frames

Before attempting to modify LED Matrix Shades code, you should go through the Programming Instructions to make sure that everything is set up correctly.

In the current implementation of the LED Matrix Shades code, bitmap frames are stored in flash memory as an array of bytes. They are stored in binary notation to make them a little easier to read and edit manually. The default AS1130Glasses sketch contains graphic frames associated with the beatingHearts() function, which cycles through three sizes of heart shapes to create a pulsing heart animation. The code for the bitmap frames is copied below:

// Full-frame bitmap graphics

char Graphics[3][24] PROGMEM = {
{0b00000000, 0b00000000, 0b00000000, 0b00001000, 0b00011100, 0b00111000, 0b00011100, 0b00001000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00001000, 0b00011100, 0b00111000, 0b00011100, 0b00001000, 0b00000000, 0b00000000, 0b00000000},
{0b00000000, 0b00000000, 0b00001100, 0b00011110, 0b00111110, 0b01111100, 0b00111110, 0b00011110, 0b00001100, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00001100, 0b00011110, 0b00111110, 0b01111100, 0b00111110, 0b00011110, 0b00001100, 0b00000000, 0b00000000},
{0b00000000, 0b00001110, 0b00011111, 0b00111111, 0b01111111, 0b11111110, 0b01111111, 0b00111111, 0b00011111, 0b00001110, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00001110, 0b00011111, 0b00111111, 0b01111111, 0b11111110, 0b01111111, 0b00111111, 0b00011111, 0b00001110, 0b00000000}
};

The data structure is a two dimensional array of bytes, where each row is 24 bytes long (the LED Matrix Shades are 24×8 pixels in size). The leftmost byte corresponds to the leftmost column on the LED array, when looking at the LED Matrix Shades from the front. Within each byte, the leftmost bit corresponds to the bottom row, and the rightmost bit corresponds to the top row. The image below should clarify how the bit positions map to active LEDs:

More graphics frames can be added to the existing Graphics array by following the same format as the existing rows (don't forget the comma after every row except the last one). We are working on an application to make the process easier, but for now it is entirely possible to create a bitmap frame by drawing the pattern out on graph paper or a spreadsheet, and manually setting bits.

Displaying the bitmap frames uses some premade functions to manage the LED array and handle process of sending bitmap frames to the LED array buffer. Here is the full beatingHearts() function, located in glassespatterns.h:

byte currentHeartFrame = 0;
byte heartLoopCount = 0;
void beatingHearts() {
  
  if (!patternInit) {
    switchDrawType(0,0);
    patternInit = true;
  }
  
  heartLoopCount++;
  if (heartLoopCount > 50) {
    heartLoopCount = 0;
    
    if (currentHeartFrame < 3) {
      loadGraphicsFrame(currentHeartFrame);
    } else {
      loadGraphicsFrame(5 - currentHeartFrame);
    }
    
    currentHeartFrame++;
    if (currentHeartFrame > 5) currentHeartFrame = 0;
    
    writeBitFrame(0,0);
  }
}

We'll go through the function and describe the meaning of each section.

byte currentHeartFrame = 0;
byte heartLoopCount = 0;
void beatingHearts() {

The two byte variables are global variables used to keep track of the current state of the animation (currentHeartFrame), and to count up a delay to slow the animation to the desired speed (heartLoopCount). The function is declared void with no parameters.

  if (!patternInit) {
    switchDrawType(0,0);
    patternInit = true;
  }

Most patterns require some type of startup task. The patternInit global variable is set to false whenever the pattern changes (either manually or automatically). In this case, the only required task is to run switchDrawType(0,0);, which selects framebuffer 0, and disables PWM since this will be a bit frame rather than a PWM frame. Then patternInit is set to true so that this section of code will not be accessed the next time the function is called.

  heartLoopCount++;
  if (heartLoopCount > 50) {
    heartLoopCount = 0;

Every time the function is called, heartLoopCount is incremented. The rest of the code in the function will not be run until it reaches 50; this implements a delay between animated frames so that they display at the desired speed. Once the counter reaches 50, it is set back to 0 to start counting up again.

    if (currentHeartFrame < 3) {
      loadGraphicsFrame(currentHeartFrame);
    } else {
      loadGraphicsFrame(5 - currentHeartFrame);
    }

Since there are only three graphics frames for this pattern, the code needs to choose some frames in reverse order while the currentHeartFrame variable counts up. In this case, while the counter is 0, 1, or 2, that is the graphics frame to be loaded. If the counter is at 3, then 5 - 3 = 2 will be selected; when the counter is at 4, then 5 - 4 = 1 will be loaded.

    currentHeartFrame++;
    if (currentHeartFrame > 5) currentHeartFrame = 0;
    
    writeBitFrame(0,0);
  }
}

The currentHeartFrame variable will be incremented each time this portion of the function is run. The maximum value is 5, then the counter will be reset to 0. The end result is the following sequence of graphics frames loaded: 0, 1, 2, 2, 1, 0, 0, 1, 2, 2, 1, 0 repeating indefinitely. Once the correct bitmap has been loaded, then the writeBitFrame(0,0) command will set frame 0 on the controller chips to the contents of buffer 0.

The function above is designed to display an animation. If all you need is to display one frame, then the function can be much simpler:

void displayFrameZero() {
  
  if (!patternInit) {
    switchDrawType(0,0);
    patternInit = true;
  }
  
  loadGraphicsFrame(0);
  writeBitFrame(0,0);
}