Overview

Pac Man is my spirit animal.

Okay, not really. But as a child of the 80’s, video arcade games were a huge cultural phenomenon of my formative years.

Tiny affordable microcontrollers are a cultural phenomenon of today’s generation.

This project bridges the generations to create an animated LED necklace or charm that you can customize to create a retro-style personal “totem video game creature.” It’s a small project, good for electronics novices and group workshops. And you’ll have something eye-catching to wear and show off afterward.
This guide was written for the Trinket Mini and Gemma v2 boards. It has been updated to also support the Trinket M0 and Gemma M0 using CircuitPython. We recommend the Trinket M0 or Gemma M0 as they are easier to use and are more compatible with modern computers!

Tools Needed

This is a soldering project, albeit a small one. You will need the common soldering paraphernalia of a soldering iron, solder, wire (20 to 26 gauge, either stranded or solid) and tools for cutting and stripping wire.

Parts needed

Can I use a “small” (1.2 inch) LED matrix instead of the “mini” (0.8 inch) version?

Sure can! Just be super extra careful to follow the assembly directions in the LED Backpack Guide and install the matrix the right way on the board. This is a common mistake! The “mini” matrix is recommended for this project because it’s less troublesome for beginners.

Can I use other color matrices?

Yes! You can use any color of our 8x8 matrices: red, green, blue, white...

Can I use the bi-color matrix backpack?

Yes it will work but you will have to adjust the bitmaps and code to handle the extra rows (it appears like a 16x8 matrix to the driver chip), we don't have a tutorial for that but mechanically, the wiring is the same.

Wiring and Soldering

Here’s the schematic when using a Gemma board. Simplicity itself! The battery isn’t shown here…it simply plugs into the JST connector:
This diagram uses the Gemma v2 but you can also use the Gemma M0. The only change to be aware of for the Gemma M0 is that the optional replay button is touch sensitive on the Gemma M0. This means that no physical button is necessary and any conductive materials can be used to replace the animation
A couple extra steps are required if using a Trinket board (3.3V or 5V doesn’t make a difference, the connections are the same):
Since there’s no JST battery connector on the Trinket, we’ll need to make one using the battery extension cable. Cut it a couple inches from the socket end (where the battery plugs in), separate the red and black wires about halfway, and strip about 1/4" of insulation from the ends.

The red and black wires each need to connect to two places in the circuit: red goes to BAT+ on Trinket and + on the backpack, while black goes to GND and –. These connections require a split inline splice. Even a regular 1-to-1 inline splice can be tricky for a novice, 2-way is an extra challenge. It’s a little easier with narrow-gauge wire (e.g. 26 ga.).
An example of a three-way inline splice (this is from another project — we’ll be doing just a 2-way splice, but the technique is the same):

Line up the 2 wires on one side and twist them together. Slide heat-shrink tube as far down as possible. Then bring in the power wire from the opposite direction and twist around the others. Solder and heat-shrink the connection.

Work slowly and methodically, remember to slide the heat-shrink tube on FIRST before joining the wires, and use proper soldering technique: heat the wires and apply the solder there; don’t move a glop of solder from the iron to the connection.

Arduino Code

The Arduino code presented below works well on Gemma v2 and Trinket Mini. But if you have an M0 board you must use the CircuitPython code on the next page of this guide, no Arduino IDE required!

If this is your first time using Trinket or Gemma, work through the Introducing Trinket or Introducing Gemma guide first; you need to customize some settings in the Arduino IDE. Once you have it up and running (test the “blink” sketch), then continue…

In the Arduino IDE, create a new sketch (File→New), then copy and paste the following code (click the “copy code” link at the top right, switch to the Arduino IDE and select Edit→Paste).

The program is fairly small but uses some advanced techniques, so don’t be alarmed if a lot of it is unfamiliar. The important stuff you’ll actually be editing is on the next page.

// Trinket/Gemma + LED matrix backpack jewelry.  Plays animated
// sequence on LED matrix.  Press reset button to display again,
// or add optional momentary button between pin #1 and +V.
// THERE IS NO ANIMATION DATA IN THIS SOURCE FILE, you should
// rarely need to change anything here.  EDIT anim.h INSTEAD.

#define BRIGHTNESS   12 // 0=min, 15=max
#define I2C_ADDR   0x70 // Edit if backpack A0/A1 jumpers set

#include <Wire.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include "anim.h"       // Animation data is located here

static const uint8_t PROGMEM reorder[] = { // Column-reordering table
    0x00,0x40,0x20,0x60,0x10,0x50,0x30,0x70,0x08,0x48,0x28,0x68,0x18,0x58,0x38,0x78,
    0x04,0x44,0x24,0x64,0x14,0x54,0x34,0x74,0x0c,0x4c,0x2c,0x6c,0x1c,0x5c,0x3c,0x7c,
    0x02,0x42,0x22,0x62,0x12,0x52,0x32,0x72,0x0a,0x4a,0x2a,0x6a,0x1a,0x5a,0x3a,0x7a,
    0x06,0x46,0x26,0x66,0x16,0x56,0x36,0x76,0x0e,0x4e,0x2e,0x6e,0x1e,0x5e,0x3e,0x7e,
    0x01,0x41,0x21,0x61,0x11,0x51,0x31,0x71,0x09,0x49,0x29,0x69,0x19,0x59,0x39,0x79,
    0x05,0x45,0x25,0x65,0x15,0x55,0x35,0x75,0x0d,0x4d,0x2d,0x6d,0x1d,0x5d,0x3d,0x7d,
    0x03,0x43,0x23,0x63,0x13,0x53,0x33,0x73,0x0b,0x4b,0x2b,0x6b,0x1b,0x5b,0x3b,0x7b,
    0x07,0x47,0x27,0x67,0x17,0x57,0x37,0x77,0x0f,0x4f,0x2f,0x6f,0x1f,0x5f,0x3f,0x7f,
    0x80,0xc0,0xa0,0xe0,0x90,0xd0,0xb0,0xf0,0x88,0xc8,0xa8,0xe8,0x98,0xd8,0xb8,0xf8,
    0x84,0xc4,0xa4,0xe4,0x94,0xd4,0xb4,0xf4,0x8c,0xcc,0xac,0xec,0x9c,0xdc,0xbc,0xfc,
    0x82,0xc2,0xa2,0xe2,0x92,0xd2,0xb2,0xf2,0x8a,0xca,0xaa,0xea,0x9a,0xda,0xba,0xfa,
    0x86,0xc6,0xa6,0xe6,0x96,0xd6,0xb6,0xf6,0x8e,0xce,0xae,0xee,0x9e,0xde,0xbe,0xfe,
    0x81,0xc1,0xa1,0xe1,0x91,0xd1,0xb1,0xf1,0x89,0xc9,0xa9,0xe9,0x99,0xd9,0xb9,0xf9,
    0x85,0xc5,0xa5,0xe5,0x95,0xd5,0xb5,0xf5,0x8d,0xcd,0xad,0xed,0x9d,0xdd,0xbd,0xfd,
    0x83,0xc3,0xa3,0xe3,0x93,0xd3,0xb3,0xf3,0x8b,0xcb,0xab,0xeb,0x9b,0xdb,0xbb,0xfb,
    0x87,0xc7,0xa7,0xe7,0x97,0xd7,0xb7,0xf7,0x8f,0xcf,0xaf,0xef,0x9f,0xdf,0xbf,0xff };

void ledCmd(uint8_t x) { // Issue command to LED backback driver
  Wire.beginTransmission(I2C_ADDR);
  Wire.write(x);
  Wire.endTransmission();
}

void clear(void) { // Clear display buffer
  Wire.beginTransmission(I2C_ADDR);
  for(uint8_t i=0; i<17; i++) Wire.write(0);
  Wire.endTransmission();
}

void setup() {
  power_timer1_disable();    // Disable unused peripherals
  power_adc_disable();       // to save power
  PCMSK |= _BV(PCINT1);      // Set change mask for pin 1
  Wire.begin();              // I2C init
  clear();                   // Blank display
  ledCmd(0x21);              // Turn on oscillator
  ledCmd(0xE0 | BRIGHTNESS); // Set brightness
  ledCmd(0x81);              // Display on, no blink
}

uint8_t rep = REPS;

void loop() {

  for(int i=0; i<sizeof(anim); i) { // For each frame...
    Wire.beginTransmission(I2C_ADDR);
    Wire.write(0);                  // Start address
    for(uint8_t j=0; j<8; j++) {    // 8 rows...
      Wire.write(pgm_read_byte(&reorder[pgm_read_byte(&anim[i++])]));
      Wire.write(0);
    }
    Wire.endTransmission();
    delay(pgm_read_byte(&anim[i++]) * 10);
  }

  if(!--rep) {             // If last cycle...
    ledCmd(0x20);          // LED matrix in standby mode
    GIMSK = _BV(PCIE);     // Enable pin change interrupt
    power_all_disable();   // All peripherals off
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sei();                 // Keep interrupts disabled
    sleep_mode();          // Power down CPU (pin 1 will wake)
    // Execution resumes here on wake.
    GIMSK = 0;             // Disable pin change interrupt
    rep   = REPS;          // Reset animation counter
    power_timer0_enable(); // Re-enable timer
    power_usi_enable();    // Re-enable USI
    Wire.begin();          // Re-init I2C
    clear();               // Blank display
    ledCmd(0x21);          // Re-enable matrix
  }
}

ISR(PCINT0_vect) {} // Button tap

We’re not done yet!

Now we'll create the graphics for some alien creatures. You can skip ahead if you just want to finish the pendant, or read on for the geeky code details…

Normally when using these matrices we recommend using the Adafruit LED Backpack Library. Because library installation is often a trouble spot, this code minimizes extra library use and instead needs to do a few things “raw,” and it’s a bit intimidating as a result. So it’s okay just to copy and paste the code and proceed to the next step if you prefer.

The code starts by disabling the chip’s Timer1 and analog-to-digital converter to save a little power and extend battery life; they’re not used by this program. Then it initializes the HT16K33 matrix driver chip the Arduino “Wire” library for the I²C protocol, clearing the image memory, setting the brightness and enabling the display (brightness is set with a #define near the top of the code…lower numbers are dimmer, but improve battery life).

The program then loops one or more times, reading animation frames from flash memory (we’ll explain that on the next page), issuing the bitmap data to the matrix driver and displaying each image for a short period. The big table lookup is because the matrix columns aren’t wired in-order on the Backpack board; this re-orders the bits in memory to match the column order.

At the end of the sequence, both the LED matrix driver and the CPU are put into a low-power state to help preserve battery life. A pin-change interrupt is enabled on pin #1 that will wake the CPU from sleep and restart the animation. This button is optional; you can use the onboard reset button as well (though it will have a brief delay as the bootloader starts).

Animation data goes in a separate file. This way it can be edited or replaced without having to rummage through the code; it’s a bit less intimidating.

At the right side of the editor window, click the triangle, select “New Tab” and type anim.h as the filename. Then copy and paste this next chunk of code:

// Animation data for Trinket/Gemma + LED matrix backpack jewelry.
// Edit this file to change the animation; it's unlikely you'll need
// to edit the source code.

#define REPS 3 // Number of times to repeat the animation loop (1-255)

const uint8_t PROGMEM anim[] = {

  // Animation bitmaps.  Each frame of animation MUST contain
  // 8 lines of graphics data (there is no error checking for
  // length).  Each line should be prefixed with the letter 'B',
  // followed by exactly 8 binary digits (0 or 1), no more,
  // no less (again, no error checking).  '0' represents an
  // 'off' pixel, '1' an 'on' pixel.  End line with a comma.
  B00011000, // This is the first frame for alien #1
  B00111100, // If you squint you can kind of see the
  B01111110, // image in the 0's and 1's.
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B10100101,
  // The 9th line (required) is the time to display this frame,
  // in 1/100ths of a second (e.g. 100 = 1 sec, 25 = 1/4 sec,
  // etc.).  Range is 0 (no delay) to 255 (2.55 seconds).  If
  // longer delays are needed, make duplicate frames.
  25, // 0.25 seconds

  B00011000, // This is the second frame for alien #1
  B00111100,
  B01111110,
  B11011011,
  B11111111,
  B00100100,
  B01011010,
  B01000010,
  25, // 0.25 second delay

  // Frames 3 & 4 for alien #1 are duplicates of frames 1 & 2.
  // Rather than list them 'the tall way' again, the lines are merged here...
  B00011000, B00111100, B01111110, B11011011, B11111111, B00100100, B01011010, B10100101, 25,
  B00011000, B00111100, B01111110, B11011011, B11111111, B00100100, B01011010, B01000010, 25,

  B00000000, // First frame for alien #2
  B00111100,
  B01111110,
  B11011011,
  B11011011,
  B01111110,
  B00100100,
  B11000011,
  25, // 0.25 second delay

  B00111100, // Second frame for alien #2
  B01111110,
  B11011011,
  B11011011,
  B01111110,
  B00100100,
  B00100100,
  B00100100,
  25,

  // Frames 3 & 4 for alien #2 are duplicates of frames 1 & 2
  B00000000, B00111100, B01111110, B11011011, B11011011, B01111110, B00100100, B11000011, 25,
  B00111100, B01111110, B11011011, B11011011, B01111110, B00100100, B00100100, B00100100, 25,

  B00100100, // First frame for alien #3
  B00100100,
  B01111110,
  B11011011,
  B11111111,
  B11111111,
  B10100101,
  B00100100,
  25,

  B00100100, // Second frame for alien #3
  B10100101,
  B11111111,
  B11011011,
  B11111111,
  B01111110,
  B00100100,
  B01000010,
  25,

  // Frames are duplicated as with prior aliens
  B00100100, B00100100, B01111110, B11011011, B11111111, B11111111, B10100101, B00100100, 25,
  B00100100, B10100101, B11111111, B11011011, B11111111, B01111110, B00100100, B01000010, 25,

  B00111100, // First frame for alien #4
  B01111110,
  B00110011,
  B01111110,
  B00111100,
  B00000000,
  B00001000,
  B00000000,
  12, // ~1/8 second delay

  B00111100, // Second frame for alien #4
  B01111110,
  B10011001,
  B01111110,
  B00111100,
  B00000000,
  B00001000,
  B00001000,
  12,

  B00111100, // Third frame for alien #4 (NOT a repeat of frame 1)
  B01111110,
  B11001100,
  B01111110,
  B00111100,
  B00000000,
  B00000000,
  B00001000,
  12,

  B00111100, // Fourth frame for alien #4 (NOT a repeat of frame 2)
  B01111110,
  B01100110,
  B01111110,
  B00111100,
  B00000000,
  B00000000,
  B00000000,
  12,

  // Frames 5-8 are duplicates of 1-4, lines merged for brevity
  B00111100, B01111110, B00110011, B01111110, B00111100, B00000000, B00001000, B00000000, 12,
  B00111100, B01111110, B10011001, B01111110, B00111100, B00000000, B00001000, B00001000, 12,
  B00111100, B01111110, B11001100, B01111110, B00111100, B00000000, B00000000, B00001000, 12,
  B00111100, B01111110, B01100110, B01111110, B00111100, B00000000, B00000000, B00000000, 12,
};

From the Tools→Board menu, select Adafruit Trinket 8 MHz or Adafruit Gemma as appropriate. Connect the USB cable between the computer and board, press the reset button, then click the upload button (right arrow icon) in the Arduino IDE. In a moment you should get a light show from the LEDs. (If it doesn’t, check your wiring against the schematics. If the code refuses to compile, most likely the anim.h file is mis-named.

You’ll see an animation sequence of four Space Invaders-style aliens that repeats three times, then shuts off. To see it again, tap the reset button. (If the USB cable is still connected, there’s a long delay before it starts again — this is normal, the delay is much shorter when running off the battery or using the optional replay button.)

To change the animation in the future, you only need to edit or replace the contents of anim.h; it’s rare that you’ll need to edit the main source code. Ambitious programmers could write a program to convert an animated GIF into a replacement anim.h file, but for now it’s necessary to edit this file manually.

There are 9 lines for each frame of animation; 8 of these are bitmap data, and the 9th line is the delay time. Each bitmap line consists of the letter ‘B’ followed by 8 binary digits (0 or 1), where ‘0’ (zero, not uppercase ‘o’) represents an “off” pixel, ‘1’ (one) an “on” pixel, and ends with a comma. The delay is given in 1/100ths of a second; 100 = 1 second, 25 = 1/4 second and so forth. The delay range is from 0 to 255; if you need longer delays, make duplicate frames.

You can almost see the bitmap image in the text:

You don’t have to represent every row on its own line like this, but it makes the image much easier to visualize. You can see a few places in the file above where we’ve smooshed all 9 lines together to save space in the vertical direction. These frames are copies of others; we already know what they look like.

After editing, press reset on the board and upload as you did before.

If the program refuses to compile after editing anim.h, it’s most likely one of the following:

  • Missing comma at end of line
  • Missing B (upper case) at start of line
  • Too many or too few digits on a line, or characters other than 0 (zero) and 1 (one)
  • Spaces between characters

There’s enough room in the chip for about 320 frames of animation; anything less is fine, of course.

CircuitPython Code

GEMMA M0 boards can run CircuitPython — a different approach to programming compared to Arduino sketches. In fact, CircuitPython comes factory pre-loaded on GEMMA M0. If you’ve overwritten it with an Arduino sketch, or just want to learn the basics of setting up and using CircuitPython, this is explained in the Adafruit GEMMA M0 guide.

These directions are specific to the Gemma M0 and Trinket M0 boards. The original GEMMA with an 8-bit AVR microcontroller doesn’t run CircuitPython…for those boards, use the Arduino sketch on the “Arduino code” page of this guide.

Below is CircuitPython code that works similarly (though not the same) as the Arduino sketch shown on a prior page. To use this, plug the GEMMA M0 into USB…it should show up on your computer as a small flash drive…then edit the file “main.py” with your text editor of choice. Select and copy the code below and paste it into that file, entirely replacing its contents (don’t mix it in with lingering bits of old code). When you save the file, the code should start running almost immediately (if not, see notes at the bottom of this page).

If GEMMA M0 doesn’t show up as a drive, follow the GEMMA M0 guide link above to prepare the board for CircuitPython.

This code requires two additional libraries be installed:

  1. adafruit_circuitpython_ht16k33
  2. adafruit_bus_device

If you’ve just reloaded the board with CircuitPython, create the “lib” directory and then download the Adafruit CircuitPython Bundle.

import time

import adafruit_ht16k33.matrix
import board
import busio as io
import touchio

touch = touchio.TouchIn(board.D1)

i2c = io.I2C(board.SCL, board.SDA)
matrix = adafruit_ht16k33.matrix.Matrix8x8(i2c)

# pixels initializers
x_pix = y_pix = 8
x = y = 0
matrix.fill(0)
matrix.show()

# seconds to pause between frames
frame_delay = [.25, .25, .25, .25, .25, .25, .25, .25, .25, .25, .25, .25,
               .125, .125, .125, .125, .125, .125, .125, .125, .125]

# counter for animation frames
frame_count = 0

# animation bitmaps
animation = [
    # frame 0 - alien #1 frame 1
    [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0],
     [0, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 1, 0, 1, 1, 0, 1, 0], [1, 0, 1, 0, 0, 1, 0, 1]],
    # frame 1 - alien #1 frame 2
    [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0],
     [0, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 1, 0, 1, 1, 0, 1, 0], [0, 1, 0, 0, 0, 0, 1, 0]],
    # frame 2 - alien #1 frame 1 (duplicate frame 1)
    [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0],
     [0, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 1, 0, 1, 1, 0, 1, 0], [1, 0, 1, 0, 0, 1, 0, 1]],
    # frame 3 - alien #1 frame 2 (duplicate frame 2)
    [[0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0],
     [0, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 1, 0, 1, 1, 0, 1, 0], [0, 1, 0, 0, 0, 0, 1, 0]],
    # frame 4 - alien #2 frame 1
    [[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0],
     [0, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 0, 1, 1], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 0, 0, 1, 0, 0], [1, 1, 0, 0, 0, 0, 1, 1]],
    # frame 5 - alien #2 frame 2
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [1, 1, 0, 1, 1, 0, 1, 1], [1, 1, 0, 1, 1, 0, 1, 1],
     [0, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 0, 1, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 1, 0, 0]],
    # frame 6 - alien #2 frame 1 (duplicate frame 5)
    [[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0],
     [0, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 0, 1, 1], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 0, 0, 1, 0, 0], [1, 1, 0, 0, 0, 0, 1, 1]],
    # frame 7 - alien #2 frame 2 (duplicate frame 6)
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [1, 1, 0, 1, 1, 0, 1, 1], [1, 1, 0, 1, 1, 0, 1, 1],
     [0, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 0, 1, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 1, 0, 0]],
    # frame 8 - alien #3 first frame
    [[0, 0, 1, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1],
     [1, 0, 1, 0, 0, 1, 0, 1], [0, 0, 1, 0, 0, 1, 0, 0]],
    # frame 9 - alien #3 second frame
    [[0, 0, 1, 0, 0, 1, 0, 0], [1, 0, 1, 0, 0, 1, 0, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 1, 0]],
    # frame 10 - alien #3 first frame (duplicate frame 9)
    [[0, 0, 1, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 1, 0, 0],
     [0, 1, 1, 1, 1, 1, 1, 0], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1],
     [1, 0, 1, 0, 0, 1, 0, 1], [0, 0, 1, 0, 0, 1, 0, 0]],
    # frame 11 - alien #3 second frame (duplicate frame 10)
    [[0, 0, 1, 0, 0, 1, 0, 0], [1, 0, 1, 0, 0, 1, 0, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 0, 1, 1, 0, 1, 1],
     [1, 1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 1, 0]],
    # frame 12 - alien #4 first frame
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 0, 0, 1, 1], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]],
    # frame 13 - alien #4 second frame
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [1, 0, 0, 1, 1, 0, 0, 1], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0]],
    # frame 14 - alien #4 third frame (not a repeat)
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [1, 1, 0, 0, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0]],
    # frame 15 - alien #4 fourth frame (not a repeat)
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 1, 1, 0, 0, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]],
    # frame 16 - alien #4 first frame (duplicate frame 12)
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 0, 0, 1, 1], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]],
    # frame 17 - alien #4 second frame (duplicate frame 13)
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [1, 0, 0, 1, 1, 0, 0, 1], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0]],
    # frame 18 - alien #4 second frame (duplicate frame 14)
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [1, 1, 0, 0, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0]],
    # frame 19 - alien #4 second frame (duplicate frame 15)
    [[0, 0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 1, 1, 0, 0, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 0],
     [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]]
]

#
# run until we are out of animation frames
# use Gemma's built-in reset button or switch to restart
#
# populate matrix
while True:

    if frame_count < len(animation):
        for x in range(x_pix):
            for y in range(y_pix):
                matrix.pixel(x, y, animation[frame_count][x][y])

        # next animation frame
        frame_count += 1

        # show animation
        matrix.show()

        # pause for effect
        time.sleep(frame_delay[frame_count])

    else:

        matrix.fill(0)
        matrix.show()
        time.sleep(.1)

        # A0/D1 pin has been touched
        # reset animation
        if touch.value:
            frame_count = 0
Due to pin differences on the Gemma M0 versus the Gemma v2 we are using D1 as the optional touch based reset. The onboard Gemma M0 switch and reset button will also reset the animation.

Finishing Up

Connect the LiPo battery to make sure that everything runs, then stack the components and fold the wires around so that nothing is protruding. If using the onboard reset button, make sure it’s on the back side where you can reach it, not blocked by the LED backpack or battery. Other than your wires, there should be no conductive parts touching between the microcontroller board and LED backpack.

The layers can be held together with a couple small pieces of foam tape, dabs of hot-melt adhesive, epoxy, etc. If you added a replay button you can encase the whole thing in a plastic bubble (such as toy vending machines dispense) with just the button protruding.

The mounting holes on the LED backpack can be used for attaching a lanyard. Plastic lace from a craft store is one option. Do not use a metal chain for this…it’s conductive and could cause an electrical short as the pendant shifts around. Another option is a pin back…we have a magnetic type in the shop, or you can find the regular variety at most sewing or craft shops…again, be careful with this bridging electrical contacts.
The little battery should last all day, depending how often you activate it. To recharge, unplug the LiPo cell from the Gemma (or JST plug if using a Trinket) and connect it to the Micro Lipo USB charger. The battery does not charge when the microcontroller is connected to USB.
This guide was first published on Oct 01, 2013. It was last updated on Oct 01, 2013.