Tag Archives: LED matrix

LED Matrix Clocks

I developed a couple of clocks using a couple of red 8×8 LED Matrix Modules – the 2D MATRIX CLOCK and the TIME & TEMP MATRIX CLOCK

The 2D MATRIX CLOCK, time is displayed by scrolling from left to right, or, under software control, from top to bottom. Each digit of the time successively slides in from the right side and when centered, stops momentarily and brightens slightly. It then scrolls to the left while the next digit in the display scrolls into view. The cycle repeats with a short delay between successive displays.

 

[codesyntax lang=”php” title=”2D Matrix Clock – Arduino Source Code” blockstate=”collapsed”]

//*****************************************************************************************//
//                                      MATRIX CLOCK
//                                 Adrian Jones, March 2014
//
//    - allows left-to-right and top-to-bottom scrolling
//
//*****************************************************************************************//
#include "ascii.h"
#include <Wire.h>             // I2C-WIRE library
#include <RTClib.h>           // RTC-Library

// define max7219 registers and control pins
#define max7219_reg_noop        0x00
#define max7219_reg_digit0      0x01
#define max7219_reg_digit1      0x02
#define max7219_reg_digit2      0x03
#define max7219_reg_digit3      0x04
#define max7219_reg_digit4      0x05
#define max7219_reg_digit5      0x06
#define max7219_reg_digit6      0x07
#define max7219_reg_digit7      0x08
#define max7219_reg_decodeMode  0x09
#define max7219_reg_intensity   0x0a
#define max7219_reg_scanLimit   0x0b
#define max7219_reg_shutdown    0x0c
#define max7219_reg_displayTest 0x0f
#define dataIn  5             // DIN
#define load    6             // CS 
#define clock   7             // CLK

// define RTC operation
RTC_DS1307 RTC;               // Tiny RTC (DS1307) module (SDA - A4, SCL - A5)

// rotary encoder, switch and LED control
#define enc_PinA   2          // encoder A to pin 2  (interrupt 0)
#define enc_PinB   4          // encoder B to pin 4
#define enc_Switch 3          // encoder switch to pin 3 (interrupt 1)
#define mode_Pin   8          // mode LED pin
#define min_Pin    9          // minute LED pin

unsigned char enc_A, enc_B, enc_A_prev=0;

static boolean rotating  = false;
static boolean clockwise = false;
static boolean updateFlag= false;
static int mode = 0;          // 0 - nothing, 1 - hour set, 2 - min set

// define display strings
#define max_array_size 100
char ac[max_array_size] = {};
byte rc[8] = {};
String display_message = "";
int arraylen;


// operational parameters
#define delay_line 100       // ms between line shifts
#define delay_char 400       // ms between characters
#define delay_mess 500       // ms between messages
#define cblanks 1            // number of blank lines between characters
#define eblanks 0            // number of additional blank lines (above 8) at the end of the message

// display features
static boolean top2bottom  = false;      // display direction (top to bottom, or right to left
static boolean hour24      = false;      // 24 hour display?
static boolean charHI      = true;       // highlight whole character

static boolean doSerial = true;           // serial output?


//*****************************************************************************************//
//                                      Initial Setup
//*****************************************************************************************//

void setup () {
  Wire.begin();
  
  Serial.begin(57600);
  if(doSerial) Serial.print("MATRIX Clock - Adrian Jones, Mar. 2014");
  
  // 8x8 LED matrix control pins
  pinMode(dataIn, OUTPUT);
  pinMode(clock,  OUTPUT);
  pinMode(load,   OUTPUT);
  initMatrix();                            // initialize LED matrix

  // LED pins
  pinMode(mode_Pin, OUTPUT);               // mode pin
  digitalWrite(mode_Pin, 1);
  pinMode(min_Pin, OUTPUT);                // minute pin
  digitalWrite(min_Pin, 1);
  
  // encoder control
  pinMode(enc_PinA, INPUT_PULLUP);  digitalWrite(enc_PinA,   HIGH);      // rotary encoder pin A
  pinMode(enc_PinB, INPUT_PULLUP);  digitalWrite(enc_PinB,   HIGH);      // rotary encoder pin B
  pinMode(enc_Switch, INPUT_PULLUP);digitalWrite(enc_Switch, HIGH);      // encoder switch
  
  attachInterrupt(0, rotEncoder, CHANGE);    // time setting
  attachInterrupt(1, swEncoder,  CHANGE);    // mins / hours

  // RTC  
  RTC.begin();
  if (! RTC.isrunning()) {   
    RTC.adjust(DateTime(__DATE__, __TIME__));
    if(doSerial) Serial.println("  (RTC reset)");
  } else {
    if(doSerial) Serial.println("  (RTC running)");
  }
  
}  

//*****************************************************************************************//
//                                      Main Loop
//*****************************************************************************************//

void loop () {

    DateTime now = RTC.now();   // 
    show_time_and_date(now);                    // display time
    display_message = createMessage(now);
    arraylen = initDisplayString(display_message);
    
    if(updateFlag) {
      show_time_and_date(now);
      updateFlag = false;  
    }

    while(rotating) {
      delay(1);                                   // debounce
      adjTime(now, clockwise); 
      show_time_and_date( RTC.now() );
      display_message = createMessage(now);
      arraylen = initDisplayString(display_message);
      delay(1);
      rotating = false;       // Reset the interrupt flag back to false
    }
    delay(5);

    for (int i = 0; i < (arraylen-7); i++) {                       // loops through message array, advancing one byte at a time
      for (int j = 1; j < 9; j++) { maxSingle(j,ac[i+8-j]);  }     // row 1 gets ac[i+8], row 2 gets ac[i+7] etc... row 8 gets ac[i+0]
      if(i%(8+cblanks) == 0) {                                     // when there is a complete character on the display...
          if(charHI) maxSingle(max7219_reg_intensity, 0x01);       // ... increase brightness and temporary halt
          newDelay(delay_char);
      } else {                                                     // normal brightness
          maxSingle(max7219_reg_intensity, 0x00);
          newDelay(delay_line);
      }
    }
    if(mode == 0) newDelay(delay_mess);
}

// ********************************************************************************** //
//                                      INTERRUPT ROUTINES
// ********************************************************************************** //

// function rotEncoder(): ISR called when encoder rotated
void rotEncoder(){
  delay(1);
  enc_A = digitalRead(enc_PinA); 
  enc_B = digitalRead(enc_PinB);
  if(!enc_A && enc_A_prev){      // change of state
    clockwise = (!enc_A && enc_B)? true : false;
    if(mode != 0) rotating = true;
  } 
  enc_A_prev = enc_A;     // Store value of A for next time    
}

// function swEncoder(): ISR called when encoder button pushed
void swEncoder(){
  delay(1);
  if(digitalRead (enc_Switch) != LOW) return;  // if switch depressed
  delay(1);                                    // debounce  
  if(digitalRead (enc_Switch) != LOW) return;  // if switch still depressed
  mode++; mode = mode % 3;                     // increment mode
  digitalWrite(mode_Pin, !(mode == 1));        // hour adjust LED
  digitalWrite(min_Pin,  !(mode == 2));        // minute adjust LED
  updateFlag = true;
}

// ********************************************************************************** //
//                                      OPERATION ROUTINES
// ********************************************************************************** //

// function newDelay
void newDelay (int dly) {
  for (int z=1; z< dly; z++) {
    delay(1);
    if(rotating || updateFlag) break;
  }
}



// function initMatrix()  :   initialization of the MAX7219 registers
void initMatrix() {
      maxSingle(max7219_reg_scanLimit, 0x07);       // all 8 columns being used
      maxSingle(max7219_reg_decodeMode, 0x00);      // set to LED matrix (not 7 seg. digit)
      maxSingle(max7219_reg_shutdown, 0x01);        // not in shutdown mode
      maxSingle(max7219_reg_displayTest, 0x00);     // not in display test
      for (int e=1; e<=8; e++) {maxSingle(e,0); }   // clear LED registers (turn all LEDs off)
      maxSingle(max7219_reg_intensity, 0x00);       // set intensity. Range: 0x00 to 0x0f 
}

// function adjTime(): increments/decrements (based encoder direction) hours/mins (depending on mode)
void adjTime(DateTime now, boolean dir) {
  if(mode == 1) {    // adjust hours
    int adj_hrs = now.hour();
    if(dir) {        // increment
       if(++adj_hrs >= 25) adj_hrs = 1;
    } else {         // decrement
      if(adj_hrs == 0) adj_hrs = 24;
      if(--adj_hrs <= 0) adj_hrs = 24;
    }
    RTC.adjust(DateTime(now.year(), now.month(), now.day(), adj_hrs, now.minute(), now.second() ));
  } 
  
  if(mode == 2) {    // adjust minutes
    int adj_mins = now.minute();
    if(dir) {   
      if(++adj_mins >= 60) adj_mins = 0; 
    } else {
      if(--adj_mins < 0) adj_mins = 59; 
    }
    RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), adj_mins, now.second() ));
  }
}


// function rotChar(char): for character char, transposes bits 90 deg. (top - bottom ==> left - right)
// and stores results in rc[0] - rc[7].
byte rotChar(char inLetter) {
   int ind = int(inLetter) - 0x20;
   for (int col = 0; col < 8; col++) {
     byte mask = 0x01 << (7-col);
     for (int row = 0; row < 8 ; row++) { bitWrite(rc[col], 7-row,(alphanum[ind][row] & mask)); }
   }
}  


// function show_time_and_date:  print out time string & bytes
void show_time_and_date(DateTime datetime){
  if(doSerial) {
    int minutes = datetime.minute();
    int hours   = datetime.hour(); if(hours==0) hours=24;
    int seconds = datetime.second();
    char delim = '/'; char dend = ' ';
    String te = "Current date/time: ";
    te = te + datetime.year() + delim + datetime.month() + delim + datetime.day() + dend;

    Serial.print(te);
    if(hours<10) Serial.print(0); Serial.print(hours,DEC);
    Serial.print(":");
    if(minutes<10) Serial.print(0); Serial.print(minutes,DEC);
    Serial.print(":");
    if(seconds < 10) Serial.print(0); Serial.print(seconds,DEC);
    Serial.println("");
  } 
}


String createMessage(DateTime datetime) {
  String new_mess = " ";
  int hr = datetime.hour()%24;    if(hr == 0) hr = 24;
  int mn = datetime.minute();
  if(mode == 0) {                  // Normal mode
    if(hour24) { 
      new_mess += hr;
    } else {
      new_mess += (hr > 12)? hr - 12 : hr;
    }
    new_mess += ':';
    if(mn < 10) new_mess += '0'; new_mess += mn;
    if(!hour24) new_mess += (hr > 12)? "~" : "^";
  }
  
  if(mode == 1) {                  // Adjusting hours
    new_mess += hr;
  }  
  
  if(mode == 2) {                 // Adjusting minutes
    if(mn < 10) new_mess += '0'; new_mess += mn;
  }
  return new_mess;
}


// function initDisplayString()  :   creates array of message string with blanks between characters and at end
int initDisplayString(String message) {
  int x = 0;
  for (int y = 0; y < message.length(); y++ ){
    char thisCh = message.charAt(y);
    int ind = int(thisCh) - 0x20;
    if(!top2bottom) rotChar(thisCh);
    for (int row = 0; row < 8 + cblanks; row++) {
      if (row <= 7) {
        ac[x] = (top2bottom)? alphanum[ind][7-row] : rc[row];
      } else {
         ac[x] = 0;
      }
      x++;
    }
  }

  for(int y = 0; y < 7+eblanks; y++) {ac[x] = 0; x++; }    // end blanks
  return x;
}


// function maxSingle()  :   loads data into register 
void maxSingle(byte reg, byte col) {      
  digitalWrite(load, LOW);       // begin    
  putByte(reg);                    // specify register
  putByte(col);                    // ((data & 0x01) * 256) + data >> 1); // put data  
  digitalWrite(load,HIGH);
}


// function putByte()  :   loads data to matrix, MSB to LSB 
void putByte(byte data) {
  byte i = 8;
  byte mask;
  while(i > 0) {                 // MSB to LSB
    mask = 0x01 << (i - 1);      // create bitmask
    digitalWrite(clock, LOW);   // tick
    if (data & mask){            // choose bit
      digitalWrite(dataIn, HIGH);// send 1
    } else {
      digitalWrite(dataIn, LOW); // send 0
    }
    digitalWrite(clock, HIGH);   // tock
    --i;                         // move to lesser bit
   }
}
     

      
     

[/codesyntax]

[codesyntax lang=”php” title=”2D Matric Clock – Arduino Include File (ascii.h)” blockstate=”collapsed”]

char alphanum[][8] ={{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},    // blank  Hex 20  Dec 32
                     {0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x10},    // ! 33
                     {0x00,0x28,0x28,0x28,0x00,0x00,0x00,0x00},    // " 34
                     {0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00},    // # 35
                     {0x10,0x38,0x50,0x38,0x14,0x54,0x38,0x10},    // $
                     {0x41,0xA2,0x44,0x08,0x10,0x22,0x45,0x82},    // %
                     {0x38,0x44,0x44,0x38,0x50,0x4A,0x44,0x3A},    // &
                     {0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00},    // '
                     {0x30,0x40,0x80,0x80,0x80,0x80,0x40,0x30},    // (  40
                     {0xC0,0x20,0x10,0x10,0x10,0x10,0x20,0xC0},    // )
                     {0x28,0x10,0xAA,0x54,0xAA,0x10,0x28,0x00},    // *
                     {0x00,0x10,0x10,0x10,0xFE,0x10,0x10,0x10},    // +
                     {0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x10},    // ,
                     {0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00},    // -
                     {0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18},    // .
                     {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80},    // /
                     {0x7E,0xC1,0xA1,0x91,0x89,0x85,0x83,0x7E},    // 0
                     {0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x7C},    // 1
                     {0x38,0x44,0x82,0x04,0x18,0x20,0x40,0xFE},    // 2   50
                     {0x7C,0x82,0x02,0x3C,0x02,0x02,0x82,0x7C},    // 3
                     {0x08,0x18,0x28,0x48,0xFE,0x08,0x08,0x08},    // 4
                     {0xFE,0x80,0xF8,0x04,0x02,0x82,0x44,0x38},    // 5
                     {0x38,0x44,0x80,0xB8,0xC4,0x82,0x44,0x38},    // 6
                     {0xFE,0x02,0x04,0x08,0x10,0x20,0x20,0x20},    // 7
                     {0x7C,0x82,0x82,0x7C,0x82,0x82,0x82,0x7C},    // 8
                     {0x7C,0x82,0x82,0x7E,0x02,0x82,0x44,0x38},    // 9
                     {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x00},    // :
                     {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x30},    // ;
                     {0x00,0x10,0x20,0x40,0x80,0x40,0x20,0x10},    // <  60
                     {0x00,0x00,0x00,0x7E,0x00,0x7E,0x00,0x00},    // =
                     {0x00,0x80,0x40,0x20,0x10,0x20,0x40,0x80},    // >
                     {0x70,0x88,0x88,0x10,0x20,0x20,0x00,0x20},    // ?
                     {0x7E,0x81,0x99,0xA1,0xA1,0x9E,0x80,0x7E},    // @
                     {0x3C,0x42,0x81,0x81,0xFF,0x81,0x81,0x81},    // A
                     {0xFC,0x82,0x81,0xFE,0x81,0x81,0x82,0xFC},    // B
                     {0x3C,0x42,0x81,0x80,0x80,0x81,0x42,0x3C},    // C
                     {0xFC,0x82,0x81,0x81,0x81,0x81,0x82,0xFC},    // D
                     {0xFE,0x80,0x80,0xFC,0x80,0x80,0x80,0xFE},    // E
                     {0xFE,0x80,0x80,0xFC,0x80,0x80,0x80,0x80},    // F  70
                     {0x3C,0x42,0x81,0x80,0x87,0x81,0x42,0x3C},    // G
                     {0x81,0x81,0x81,0xFF,0x81,0x81,0x81,0x81},    // H
                     {0xFE,0x10,0x10,0x10,0x10,0x10,0x10,0xFE},    // I
                     {0xFF,0x08,0x08,0x08,0x08,0x88,0x88,0x70},    // J
                     {0x88,0x90,0xA0,0xC0,0xA0,0x90,0x88,0x84},    // K
                     {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xFE},    // L
                     {0x81,0xC3,0xA5,0x99,0x81,0x81,0x81,0x81},    // M
                     {0x81,0xC1,0xA1,0x91,0x89,0x85,0x83,0x81},    // N
                     {0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C},    // O
                     {0xFC,0x82,0x81,0x82,0xFC,0x80,0x80,0x80},    // P  80
                     {0x3C,0x42,0x81,0x81,0x81,0x85,0x42,0x3D},    // Q
                     {0xFC,0x82,0x81,0x82,0xFC,0x84,0x82,0x81},    // R
                     {0x3C,0x42,0x81,0x40,0x3E,0x81,0x42,0x3C},    // S
                     {0xFE,0x10,0x10,0x10,0x10,0x10,0x10,0x10},    // T
                     {0x82,0x82,0x82,0x82,0x82,0x82,0x44,0x38},    // U
                     {0x82,0x82,0x82,0x82,0x82,0x44,0x28,0x10},    // V
                     {0x81,0x81,0x81,0x81,0x99,0xA5,0xC3,0x81},    // W
                     {0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81},    // X
                     {0x82,0x44,0x28,0x10,0x10,0x10,0x10,0x10},    // Y
                     {0xFF,0x02,0x04,0x08,0x10,0x20,0x40,0xFF},    // Z  90
                     {0xE0,0x80,0x80,0x80,0x80,0x80,0x80,0xE0},    // [ 
                     {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01},    // 
                     {0x07,0x01,0x01,0x01,0x01,0x01,0x01,0x07},    // ]
                     {0xE0,0xA0,0xE0,0xA0,0xAA,0x15,0x15,0x11},    // am (coded as '^'
                     {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E},    // _
                     {0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00},    // '
                     {0x00,0x00,0x38,0x04,0x3C,0x44,0x48,0x34},    // a
                     {0x00,0x40,0x40,0x40,0x78,0x44,0x44,0x38},    // b
                     {0x00,0x00,0x18,0x24,0x40,0x40,0x24,0x18},    // c
                     {0x00,0x04,0x04,0x04,0x3C,0x44,0x44,0x38},    // d  100
                     {0x00,0x00,0x38,0x44,0x7C,0x40,0x44,0x38},    // e
                     {0x00,0x18,0x20,0x20,0x78,0x20,0x20,0x20},    // f
                     {0x00,0x38,0x44,0x44,0x38,0x04,0x44,0x38},    // g
                     {0x00,0x40,0x40,0x40,0x78,0x44,0x44,0x44},    // h
                     {0x00,0x00,0x40,0x00,0x40,0x40,0x40,0x40},    // i
                     {0x00,0x08,0x00,0x08,0x08,0x08,0x48,0x30},    // j
                     {0x00,0x40,0x40,0x48,0x50,0x60,0x50,0x48},    // k 
                     {0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x20},    // l
                     {0x00,0x00,0x00,0x28,0x54,0x44,0x44,0x44},    // m
                     {0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44},    // n  110
                     {0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x38},    // o 
                     {0x00,0x00,0x70,0x48,0x48,0x70,0x40,0x40},    // p
                     {0x00,0x00,0x30,0x48,0x48,0x38,0x08,0x08},    // q
                     {0x00,0x00,0x00,0x30,0x48,0x40,0x40,0x40},    // r
                     {0x00,0x30,0x48,0x40,0x30,0x08,0x48,0x30},    // s
                     {0x00,0x20,0x70,0x20,0x20,0x20,0x28,0x10},    // t
                     {0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x38},    // u
                     {0x00,0x00,0x44,0x44,0x44,0x44,0x28,0x10},    // v
                     {0x00,0x00,0x82,0x82,0x82,0x92,0x54,0x28},    // w
                     {0x00,0x00,0x84,0x48,0x30,0x30,0x48,0x84},    // x  120
                     {0x00,0x48,0x48,0x48,0x38,0x08,0x48,0x30},    // y 
                     {0x00,0x00,0x00,0x7C,0x08,0x10,0x20,0x7C},    // z
                     {0x00,0x30,0x40,0x40,0x80,0x40,0x40,0x30},    // {
                     {0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20},    // |
                     {0x00,0x60,0x10,0x10,0x08,0x10,0x10,0x60},    // }
                     {0xE0,0xA0,0xE0,0x80,0x8A,0x15,0x15,0x11}     // pm codes as '~'  Hex 7E, Dec 126
                   };

[/codesyntax]

 

The TIME & TEMP MATRIX CLOCK is a display unit that shows time, date and temperature on a LED matrix display. The time and temperature are displayed successively on the matrix display and immediately after a new minute, the day of the week, date, month and time are displayed.

 

Read more…

MIDI Foot Controller

The MIDI FOOT CONTROLLER is a simple foot pedal interface to allows keyboard players to rapidly change between preset MIDI settings.

MidiFootBoard-p16-switch MIDI Foot Controller

The unit provides 6 stomp switches, 4 of which are used to cycle through 3 sets (S1, S2, S3) of three MIDI presets. Each preset controls the program for up to 4 MIDI channels (MIDI channel 1 – 4). The remaining two switches allow the user to increment and decrement programs, from the current set and preset (MIDI channel 1) to allow for finer granularity of sound control.

Read more…