Christmas card (arduino, max7221, 8x8 matrix led)

This is a Christmas card with an arduino, max7221 and a 8x8 matrix LED.

My first version was a 64pixels attiny greating card:
I collected some patterns and used a 5x7 font
see video here: 64pixels christmas card with an attiny and 8x8 matrix led - YouTube

For arduino I used a max7221 with the library LedControl.h
http://www.arduino.cc/playground/Main/LedControl

...it's the same code sequence in this arduino video :slight_smile:
see video: 64pixels christmas card with arduino - YouTube

Here is the font and the code for arduino:

font.h

#include <avr/io.h>
#include <avr/pgmspace.h>

#ifndef FONT_H_
#define FONT_H_

#define MAX_CHARS 59
#define CHAR_OFFSET 0x20

const uint8_t font[] PROGMEM = {
  // 5 chars bitmap, stop bit is bit.7
0x00,0x00,0x00,0x00,0x80,      // 0x20, 32, ' 
0x00,0x30,0x7D,0x30,0x80,      // 0x21, 33, !
0x70,0x60,0x00,0x70,0xE0,      // 0x22, 34, "
0x12,0x3F,0x12,0x3F,0x92,      // 0x23, 35, #
0x12,0x6A,0x2B,0xA4,0x80,      // 0x24, 36, $
0x63,0x64,0x08,0x13,0xE3,      // 0x25, 37, %
0x36,0x49,0x35,0x02,0x85,      // 0x26, 38, &
0x00,0x70,0x60,0x80,0x80,      // 0x27, 39, '
0x00,0x3E,0x41,0x80,0x80,      // 0x28, 40, (
0x00,0x41,0x3E,0x80,0x80,      // 0x29, 41, )
0x08,0x3E,0x1C,0x3E,0x88,      // 0x2A, 42, *
0x08,0x08,0x3E,0x08,0x88,      // 0x2B, 43, +
0x00,0x03,0x03,0x80,0x80,      // 0x2C, 44, ,
0x08,0x08,0x08,0x08,0x88,      // 0x2D, 45, -
0x00,0x03,0x03,0x80,0x80,      // 0x2E, 46, .
0x02,0x04,0x08,0x10,0xA0,      // 0x2F, 47, /
0x3E,0x45,0x49,0x51,0xBE,      // 0x30, 48, 0
0x00,0x21,0x7F,0x81,0x80,      // 0x31, 49, 1
0x23,0x45,0x49,0x49,0xB1,      // 0x32, 50, 2
0x22,0x49,0x49,0x49,0xB6,      // 0x33, 51, 3
0xC,0x14,0x24,0x7F,0x84,      // 0x34, 52, 4
0x7A,0x49,0x49,0x49,0xC6,      // 0x35, 53, 5
0x1E,0x29,0x49,0x49,0x86,      // 0x36, 54, 6
0x40,0x47,0x48,0x50,0xE0,      // 0x37, 55, 7
0x36,0x49,0x49,0x49,0xB6,      // 0x38, 56, 8
0x30,0x49,0x49,0x4A,0xBC,      // 0x39, 57, 9
0x00,0x1B,0x1B,0x80,0x80,      // 0x3A, 58, :
0x00,0x1B,0x1B,0x80,0x80,      // 0x3B, 59, ;
0x08,0x14,0x22,0xC1,0x80,      // 0x3C, 60, <
0x12,0x12,0x12,0x12,0x92,      // 0x3D, 61, =
0x00,0x41,0x22,0x14,0x88,      // 0x3E, 62, >
0x20,0x40,0x4D,0x48,0xB0,      // 0x3F, 63, ?
0x3E,0x41,0x5D,0x55,0xBC,      // 0x40, 64, @
0x3F,0x44,0x44,0x44,0xBF,      // 0x41, 65, A
0x7F,0x49,0x49,0x49,0xB6,      // 0x42, 66, B
0x3E,0x41,0x41,0x41,0xA2,      // 0x43, 67, C
0x7F,0x41,0x41,0x41,0xBE,      // 0x44, 68, D
0x7F,0x49,0x49,0x49,0xC1,      // 0x45, 69, E
0x7F,0x48,0x48,0x48,0xC0,      // 0x46, 70, F
0x3E,0x41,0x49,0x49,0xAF,      // 0x47, 71, G
0x7F,0x08,0x08,0x08,0xFF,      // 0x48, 72, H
0x00,0x41,0x7F,0xC1,0x80,      // 0x49, 73, I
0x06,0x01,0x01,0x01,0xFE,      // 0x4A, 74, J
0x7F,0x08,0x14,0x22,0xC1,      // 0x4B, 75, K
0x7F,0x01,0x01,0x01,0x81,      // 0x4C, 76, L
0x7F,0x20,0x10,0x20,0xFF,      // 0x4D, 77, M
0x7F,0x20,0x10,0x08,0xFF,      // 0x4E, 78, N
0x3E,0x41,0x41,0x41,0xBE,      // 0x4F, 79, O
0x7F,0x48,0x48,0x48,0xB0,      // 0x50, 80, P
0x3E,0x41,0x45,0x42,0xBD,      // 0x51, 81, Q
0x7F,0x48,0x48,0x4C,0xB3,      // 0x52, 82, R
0x32,0x49,0x49,0x49,0xA6,      // 0x53, 83, S
0x40,0x40,0x7F,0x40,0xC0,      // 0x54, 84, T
0x7E,0x01,0x01,0x01,0xFE,      // 0x55, 85, U
0x7C,0x02,0x01,0x02,0xFC,      // 0x56, 86, V
0x7E,0x01,0x1E,0x01,0xFE,      // 0x57, 87, W
0x63,0x14,0x08,0x14,0xE3,      // 0x58, 88, X
0x70,0x08,0x07,0x08,0xF0,      // 0x59, 89, Y
0x47,0x49,0x51,0xE1,0x80,      // 0x5A, 90, Z
};

#endif /*FONT_H_*/

a very strict forum: no code uploads, only 9500 Byte message length

lets try
here the first part of LCDMatrix.pde 1/2

//We always have to include the library
#include "LedControl.h"
#include "font.h"
#include <avr/pgmspace.h>

/*
 Now we need a LedControl to work with.
 ***** These pin numbers will probably not work with your hardware *****
 pin 12 is connected to the DataIn 
 pin 11 is connected to the CLK 
 pin 10 is connected to LOAD 
 We have only a single MAX72XX.
 */
LedControl lc=LedControl(12,11,10,1);

void setup() {
  /*
   The MAX72XX is in power-saving mode on startup,
   we have to do a wakeup call
   */
  lc.shutdown(0,false);
  /* Set the brightness to a medium values */
  lc.setIntensity(0,8);
  /* and clear the display */
  lc.clearDisplay(0);
}

// Change these values to adjust scroll speeds and animation iterations
#define ANIMATION_SCROLL_SPEED 200  // how fast to scroll the animations
#define ANIMATION_SPEED 300              // how fast to change to next picture
#define TEXT_SCROLL_SPEED 120      // how fast to scrill the text (wait)
#define REPEAT_ANIMATION 2        // how often to repeat the animation if in cycling mode
#define REPEAT_TEXT 1              // how often to repeat the text if in cycling mode

// How to add a new message:
// * add the new message (only upper case, see font.h)
// * adjust MAX_MESSAGES
// * add the new message to messages
// NOTE: messages may not be longer than 255 chars.
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1193587488
// Since 0012, PGM_P is broken. Fortunately there's a workaround 

char message_00[] PROGMEM = " ! MERRY CHRISTMAS AND A HAPPY NEW YEAR ! ";
char message_01[] PROGMEM = " COUNTDOWN...";
char message_02[] PROGMEM = " 5  4  3  2  1 ... BOOM!! ";

#define MAX_MESSAGES 3
//PGM_P PROGMEM messages[] = {
PROGMEM const char *messages[] = {  
    message_00
    ,message_01
    ,message_02
}; 

//#define MAX_ANIMATIONS 3

const uint8_t sprite_xy[][8] PROGMEM = {
      { 0x06, 0x1E, 0x7E, 0xFF, 0x7E, 0x1E, 0x06, 0x00}, //0  wood1
      { 0x26, 0x9E, 0x7E, 0xFF, 0x7E, 0x9E, 0x06, 0x08}, //1  wood1
      { 0x07, 0x1F, 0x37, 0x30, 0x3F, 0x3F, 0x1F, 0x0F}, //2 Weinachtsmann
      { 0x0E, 0x3E, 0x6E, 0x60, 0x7F, 0x7F, 0x3F, 0x1F}, //3 Weinachtsmann
      { 0x21, 0x22, 0x44, 0x78, 0xF0, 0x4B, 0x45, 0x40}, //4  mov_man
      { 0x13, 0x24, 0x44, 0x78, 0xF0, 0x48, 0x47, 0x81}, //5  mov_man
      { 0x3C, 0x42, 0x81, 0xA1, 0x89, 0x95, 0xA5, 0x42}, //6 packman
      { 0x3C, 0x42, 0x81, 0xA1, 0x89, 0x89, 0x89, 0x76}, //7 packman
      { 0x00, 0x00, 0x18, 0x3C, 0x1E, 0x3C, 0x18, 0x00}, //8  Heart1
      { 0x38, 0x7C, 0x7E, 0x3F, 0x3F, 0x7E, 0x7C, 0x38}, //9  Heart2
};

const uint8_t stern1[][8] PROGMEM = {
      { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, // Star
      { 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, 0x00, 0x00}, 
      { 0x00, 0x18, 0x24, 0x42, 0x42, 0x24, 0x18, 0x00}, 
      { 0x18, 0x24, 0x42, 0x81, 0x81, 0x42, 0x24, 0x18}, 
      { 0x24, 0x42, 0x81, 0x00, 0x00, 0x81, 0x42, 0x24}, 
      { 0x42, 0x81, 0x00, 0x00, 0x00, 0x00, 0x81, 0x42}, 
      { 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81}, 
      { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 
};

//next 2 patterns are posted here  http://www.mikroe.com/forum/viewtopic.php?t=11194
const uint8_t stern2[][8] PROGMEM = {
      { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, // rot. Star
      { 0x00, 0x00, 0x24, 0x18, 0x18, 0x24, 0x00, 0x00},
      { 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00},
      { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81},
      { 0x40, 0x21, 0x12, 0x1C, 0x38, 0x48, 0x84, 0x02},
      { 0x20, 0x10, 0x09, 0x3A, 0x5C, 0x90, 0x08, 0x04},
      { 0x10, 0x08, 0x08, 0x79, 0x9E, 0x10, 0x10, 0x08},
      { 0x08, 0x08, 0x08, 0xF8, 0x1F, 0x10, 0x10, 0x10},
      { 0x04, 0x08, 0x88, 0x78, 0x1E, 0x11, 0x10, 0x20},
      { 0x02, 0x84, 0x48, 0x38, 0x1C, 0x12, 0x21, 0x40},
      { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81},
      { 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00},
      { 0x00, 0x00, 0x24, 0x18, 0x18, 0x24, 0x00, 0x00},
      { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00},
};

const uint8_t dancer1[][8] PROGMEM = {
      { 0x21, 0x63, 0x44, 0x78, 0xF8, 0x44, 0xC3, 0x81},  // dancing man
      { 0x81, 0xC3, 0x44, 0xF8, 0x78, 0x44, 0x63, 0x21},  
      { 0x21, 0x63, 0x44, 0x78, 0xF8, 0x44, 0xC3, 0x81},  
      { 0x81, 0xC3, 0x44, 0xF8, 0x78, 0x44, 0x63, 0x21},  
      { 0x21, 0x63, 0x44, 0x78, 0xF8, 0x44, 0xC3, 0x81}, 
      { 0x81, 0xC3, 0x44, 0xF8, 0x78, 0x44, 0x63, 0x21}, 
      { 0x21, 0x63, 0x44, 0x78, 0xF8, 0x44, 0xC3, 0x81},  
      { 0x81, 0xC3, 0x44, 0xF8, 0x78, 0x44, 0x63, 0x21},  
      { 0x81, 0xC3, 0x44, 0xF8, 0x7E, 0x41, 0x61, 0x21},  
      { 0x21, 0x61, 0x41, 0xFE, 0xFE, 0x41, 0x61, 0x21},  
      { 0x21, 0x61, 0x41, 0x7E, 0xF8, 0x44, 0xC3, 0x81},  
      { 0x21, 0x61, 0x41, 0xFE, 0xFE, 0x41, 0x61, 0x21},  
      { 0x81, 0xC3, 0x44, 0xF8, 0x7E, 0x41, 0x61, 0x21},  
      { 0x21, 0x61, 0x41, 0xFE, 0xFE, 0x41, 0x61, 0x21},  
      { 0x81, 0xC3, 0x44, 0xF8, 0xF8, 0x44, 0xC3, 0x81},  
      { 0x21, 0x61, 0x41, 0xFE, 0xFE, 0x41, 0x61, 0x21}, 
      { 0x81, 0xC3, 0x44, 0xF8, 0xF8, 0x44, 0xC3, 0x81},  
      { 0x81, 0xC3, 0x44, 0x78, 0xF8, 0x44, 0xC3, 0x81}, 
      { 0x81, 0xC3, 0x44, 0xF8, 0x78, 0x44, 0xC3, 0x81}, 
      { 0x81, 0xC3, 0x44, 0x78, 0xF8, 0x44, 0xC3, 0x81},  
      { 0x81, 0xC3, 0x44, 0xF8, 0x78, 0x44, 0xC3, 0x81},  
      { 0x41, 0x43, 0x44, 0xF8, 0x78, 0x44, 0x63, 0x21},  
      { 0x21, 0x63, 0x44, 0x78, 0xF8, 0x44, 0x43, 0x41},  
      { 0x41, 0x43, 0x44, 0xF8, 0x78, 0x44, 0x63, 0x21},  
      { 0x21, 0x63, 0x44, 0x78, 0xF8, 0x44, 0x43, 0x41},  
      { 0x40, 0x45, 0x4B, 0xF0, 0x78, 0x44, 0x22, 0x21},  
      { 0x81, 0x47, 0x48, 0xF0, 0x78, 0x44, 0x24, 0x13},  
      { 0x40, 0x46, 0xFB, 0x78, 0x44, 0x22, 0x21, 0x00}, 
      { 0x47, 0xF8, 0x7E, 0x41, 0x40, 0x40, 0x00, 0x00},  
      { 0xF8, 0x7C, 0x43, 0xC1, 0x00, 0x00, 0x00, 0x00},  
      { 0xFC, 0x42, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00},  
      { 0x47, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},  
      { 0x20, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00},  
      { 0x20, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},  
      { 0x20, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}, 
      { 0x20, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},  
};

const uint8_t rocket[][8] PROGMEM = {
      { 0x07, 0x0F, 0x0F, 0x0E, 0x00, 0x00, 0x00, 0x00}, // rocket
      { 0x05, 0x0E, 0x1F, 0x3E, 0x3C, 0x38, 0x00, 0x00}, // rocket
      { 0x01, 0x0A, 0x1C, 0x3E, 0x7C, 0x78, 0x70, 0x00}, // rocket
      { 0x11, 0x42, 0x2C, 0x9C, 0x39, 0x70, 0xE2, 0xC8}, // rocket
      { 0x24, 0x00, 0x50, 0x02, 0xA0, 0x04, 0x50, 0x01}, // rocket 
      { 0x42, 0x20, 0x8A, 0x01, 0x42, 0x10, 0x85, 0x28}, // rocket 
};

uint8_t screen_mem[8];                  // screen memory
uint8_t active_row=1;                  // active row
uint8_t message_ptr = 0;                 // points to the active char in the message
uint8_t message_displayed = 0;           // how often has the message been displayed?
uint8_t active_char = 0;                 // stores the active char
uint8_t message_length = 0;              // stores the length of the active message
uint8_t char_ptr = 0;                    // points to the active col in the char
uint8_t char_length = 0;                 // stores the length of the active char

and now the second part of LCDMatrix.pde 2/2

/*
 * copy screen_mem[] to LED Matrix 8x8
 */
void writeArduinoOnMatrix() {
  /* here is the data for the characters */
  int i;
  for (i=0; i<8; i++)
    lc.setColumn(0,i,screen_mem[i]);
}

/*
 * show_char
 * Displays the actual message. 
 * Scrolls the screen to the left and draws new pixels on the right.
 * char stop bit is bit.7
 */
void show_char(const prog_uint8_t string[]) {
  uint8_t i;
  uint8_t b;

  // shift the screen to the left
  for (i = 0; i < 7; i++) {
    screen_mem[i] = screen_mem[i+1]; 
  }
  // advance a char if needed
  if (char_length == 0x80) {

    char_length =0;      //reset stop bit

    //read next char from progmem
    memcpy_P(&active_char,&string[message_ptr],1);
    //this is an alternativ PROGMEM access:
    //active_char =  pgm_read_byte_near(string + message_ptr);
    message_ptr++;

    //string stop byte 0x00 
    if (active_char == 0) {
      message_ptr = 0;
      message_displayed++;
      char_length =0x80; // immediately read next char
    }

    active_char -= CHAR_OFFSET;
    char_ptr = 0;

    // this makes the space between two chars
    screen_mem[7]=0;
    return; 
  }

  // read pixels for current column of char
  b = pgm_read_byte(&font[active_char * 5 + char_ptr]);
  char_ptr++;
  //char_length= (b & 0x01);
  //b = (b >> 1);
  char_length= (b & 0x80);
  b = (b & 0x7F);
  // write pixels into screen memory
  screen_mem[7] =b;
  Serial.print((uint16_t)active_char); Serial.print(" / "); Serial.println(char_ptr, HEX); 
}


/*
 * copy_to_buffer
 * Copies the given sprite from PROGMEM to 8x8 LED RAM.
 */
void copy_to_buffer(const prog_uint8_t sprite[8]) {
  memcpy_P(screen_mem, sprite, 8);
}


/*
 * scroll_animation
 * Uses sprite_1 and sprite_2 to draw a simple animation.
 * blink=1 : change sprite_1/2 every step  // blink=0 : blink only in the middle
 * scroll_speed: factor time for next step
 */
void scroll_animation(const prog_uint8_t sprite_1[8], const prog_uint8_t sprite_2[8], uint8_t blink) {
  uint8_t i,j;
  int8_t x,z;

  for (i = 0; i < REPEAT_ANIMATION; i++) {
    //you can scroll from right with (x = -8; x < 8; x++)
    for (x = 8; x > -9; x--) {
      //move sprite 1
      for (j = 0; j < 8; j++) {
        screen_mem[j] = 0x00;
        z = x+j;
        if ((z>=0) && (z<8)) {
          if (blink & x) {
            memcpy_P(&screen_mem[j],&sprite_2[z],1);
          }
          else {
            memcpy_P(&screen_mem[j],&sprite_1[z],1);
          }
        }
      }
      writeArduinoOnMatrix();
      delay(ANIMATION_SCROLL_SPEED);

      if (x==0) {
        //in the middle shift betwenn sprite_1 and sprite_2
        for (j=0; j<4;j++) {
          copy_to_buffer(sprite_2);
          writeArduinoOnMatrix(); 
          delay(ANIMATION_SPEED);
          copy_to_buffer(sprite_1);
          writeArduinoOnMatrix(); 
          delay(ANIMATION_SPEED);
        }
      }
    }      
  }
}

/*
 * step_animation
 * Uses sprite_1 to draw a simple animation.
 * size of array
 */
void step_animation(const prog_uint8_t sprite_1[][8], uint8_t size) {
  uint8_t i;
  for (i = 0; i < size; i++) {
    copy_to_buffer(sprite_1[i]); //array: step pointer by 8
    writeArduinoOnMatrix(); 
    delay(ANIMATION_SPEED);
  }      
}

void loop() {

  int8_t i,j,k;

  while (1) {
  
    for (i = 0; i < 8; i+=2) {
      scroll_animation(sprite_xy[i], sprite_xy[(i+1)], 1);
      writeArduinoOnMatrix(); 
      delay(750);
    }

    //3   heart
    for (i = 0; i < REPEAT_ANIMATION*3; i++) {
      copy_to_buffer(sprite_xy[8]);
      writeArduinoOnMatrix(); 
      delay(750);
      copy_to_buffer(sprite_xy[9]);        
      writeArduinoOnMatrix(); 
      delay(750);
    }

    //4
    for (k = 0; k < REPEAT_ANIMATION; k++) {
      step_animation(stern1, 8);
      for (i = 7; i >= 0; i--) {
        copy_to_buffer(stern1[i]);
        writeArduinoOnMatrix(); 
        delay(ANIMATION_SPEED);
      }      
      for (j = 0; j < REPEAT_ANIMATION; j++) {
        step_animation(stern2, 14);
      }
    }


    //5
    for (i = 0; i < REPEAT_ANIMATION; i++) {
      step_animation(dancer1, 36);      
    }      

    //6
    for (i = 0; i < MAX_MESSAGES; i++) {
      message_displayed = 0;
      while (message_displayed < REPEAT_TEXT) {
        show_char((uint8_t*)pgm_read_word(&(messages[i])));  //show_char((prog_uint8_t*)pgm_read_word(&(messages[i])));
        writeArduinoOnMatrix(); 
        delay(TEXT_SCROLL_SPEED);
      }
    }

    for (i = 0; i < REPEAT_ANIMATION*4; i++) {
      step_animation(rocket, 6);
    }

  }

}

Great project!