Since my oldest son has been spending more and more time getting better and better at soldering things onto other things and driving them with little micro-controllers, I decided to give it a try as well.
Overall the assembly was a breeze. I am absolutely abysmal with a soldering iron, so I was grateful for the extra space on the Gemma. It’s still a mess but it fired up easily after loading a test “blinker” program onto the chip, and aside from the LED Matrix being attached at a slight incline (again, I’m bad at soldering) we had colorful little dots moving about in no time.
One aspect that I failed to note when I chose the Gemma was how little on-board memory it has. While not a problem for a standard small LED matrix, the BiColor is somewhat wasted on the Gemma since it doesn’t have enough space to load the full AdaFruit graphic library. So everything needs to be done at a low-level from a graphics standpoint. Not a big deal I suppose, and I welcome the chance to learn how. But I feel that I’m missing out on some cool features since it won’t work directly with the library that was build for it.
This project was based on the AdaFruit Wearable Animated Pendant project, but we replaced the standard LED matrix with the BiColor variant and backpack.
Parts List to Make Your Own
1. AdaFruit Arduino Gemma microcontroller.
2. Adafruit Bicolor LED Square Pixel Matrix.
3. Lithium Ion Polymer Battery – 3.7v 150mAh.
4. Adafruit Micro Lipo – USB LiIon/LiPoly charger.
The original source code can be found HERE. But some modifications had to be made to support the BiColor Matrix. Below is the modified source to get the animated Space Invaders to appear:
SpaceInvaders-Gemma-Color.ino
// 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 2 // 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 };
*/
static const uint8_t PROGMEM reorder[] = {
0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,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(pgm_read_byte(&reorder[pgm_read_byte(&anim2[i])]));
i++;
}
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
anim.h
// 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,
};
const uint8_t PROGMEM anim2[] = {
// 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.
B00010000, // This is the first frame for alien #1
B00000000, // If you squint you can kind of see the
B00100000, // image in the 0’s and 1’s.
B00000000,
B00000100,
B00100000,
B00000100,
B00000000,
// 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
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
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,
};