Matrix clock

In this tutorial we build a matrix clock with a Arduino (or Nano), 8 max7219 8x8 led matrix modules, a ds3231 rtc module and 3 momentary tactile buttons.



The youtube video here above is filmed with a rearlier version of the script. This version has a extra mode to adjust the brightness of the leds.

Rightclick to see full picture The wooden box is a old winebox which is painted with non reflecting black paint. I used a spray can but it works also with a roller.

In the library (be sure the library files copies to the libraries folder under a folder named LedControlMS), i maked some adjustments to the fonts.

Rightclick to see full picture The first step is to fix the matrix modules all together, you can use a small piece of wood. The first matrix module we connect the in going pins (bottom near max7219 chip) to the pin5,6 & 7 according to the script. The upper pins (out going) we connect to the in going pins of the second module and so we daisy chained all the modules.


Rightclick to see full picture For time keeping we use a DS3131 module which has a i2c bus and we connect in this case to the a4 (SDA) & a5 (SCL) pins of the Arduino. Don't forget VCC (5v) & GND.
On the photo you will probably recognize a DS1307, those will work also, but i experienced that de DS1307 is not very accurate. I recommend to use the DS3231.
Click here for more about i2c.

Rightclick to see full picture The buttons we connect to pin 2 (menu), pin 3 (up) and pin 4(down). The menu button in this case is the red button.
Each button you connect one pin to vcc and the other pin goes to the data (in) pins (2,3 and 4) and also with a 6K8 resister to the ground.

The empty hole in the box was a leftover when using a network shield in the earlier versions.
Rightclick to see full picture Rightclick to see full picture







Matrix_Clock_v6.ino

//
//******** Matrix clock for Arduino v6 **********
//******** Author: RenÚ Wennekes
//
//We always have to include the library
#include <Wire.h>
#include "LedControlMS.h"
#include "RTClib.h" // Credit: Adafruit

RTC_DS1307 RTC;

/*
Now we need a LedControl to work with.
***** These pin numbers will probably not work with your hardware *****
pin 7 is connected to the DataIn (DIN)
pin 6 is connected to the CLK (CLK)
pin 5 is connected to LOAD (CS)
We have 8 modules of MAX72XX.
*/
#define NBR_MTX 8 //Number of displays
LedControl lc=LedControl(7,6,5, NBR_MTX);


//buttons
const int menubuttonPin = 2; // the number of the menubutton pin
const int upbuttonPin = 3; // the number of the upbutton pin
const int downbuttonPin = 4; // the number of the downbutton pin
int menubuttonState = 0; // variable for reading the menubutton status
int upbuttonState = 0; // variable for reading the upbutton status
int downbuttonState = 0; // variable for reading the downbutton status
int mode = 0; // 0=display time, 1=display date, 2=set hour, 3=set minutes, 4=reset seconds, 5=set date, 6=set month, 7=set year
// 0=display time, 1=display date, 2=set year, 3=set month, 4=set date, 5=set hour, 6=set minutes, 7=reset seconds

int bright = 3; //brightness

String ch;
char ch0,ch1,ch2,ch3,ch4,ch5,ch6,ch7;


void setup() {
Serial.begin (9600);


// initialize the pushbutton pin as an input:
pinMode(upbuttonPin, INPUT);
pinMode(downbuttonPin, INPUT);
pinMode(menubuttonPin, INPUT);


// Instantiate the RTC
Wire.begin();
RTC.begin();


// Check if the RTC is running.
if (! RTC.isrunning()) {
Serial.println("RTC is NOT running");
}


/*
The MAX72XX is in power-saving mode on startup,
we have to do a wakeup call
*/
for (int i=0; i< NBR_MTX; i++){
lc.shutdown(i,false);
/* Set the brightness to a low value */
lc.setIntensity(i,bright);
/* and clear the display */
lc.clearDisplay(i);
}
} //End of setup


void day_forward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
epoch = epoch + 86400;
RTC.adjust(epoch);
now = RTC.now();
}

void day_backward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
epoch = epoch - 86400;
RTC.adjust(epoch);
now = RTC.now();
}

void month_forward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
unsigned long newMonth = now.month();
switch (newMonth) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
epoch = epoch + 86400*31;
break;
case 2:
epoch = epoch + 86400*28;
break;
case 4: case 6: case 9: case 11:
epoch = epoch + 86400*31;
break;
}
RTC.adjust(epoch);
now = RTC.now();
}

void month_backward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
unsigned long newMonth = now.month();
switch (newMonth) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
epoch = epoch - 86400*31;
break;
case 2:
epoch = epoch - 86400*28;
break;
case 4: case 6: case 9: case 11:
epoch = epoch - 86400*31;
break;
}
RTC.adjust(epoch);
now = RTC.now();
}
void year_forward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
unsigned long newMonth = now.month();
unsigned long newYear = now.year();
epoch = epoch + 86400*365;
if ( (newYear % 4) == 0) {
epoch = epoch + 86400*1;
}
RTC.adjust(epoch);
now = RTC.now();
}

void year_backward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
unsigned long newMonth = now.month();
unsigned long newYear = now.year()-1;
epoch = epoch - 86400*365;
if ( (newYear % 4) == 0) {
epoch = epoch - 86400*1;
}
RTC.adjust(epoch);
now = RTC.now();
}

void hour_forward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
epoch = epoch + 3600;
RTC.adjust(epoch);
now = RTC.now();
}

void hour_backward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
epoch = epoch - 3600;
RTC.adjust(epoch);
now = RTC.now();
}

void minute_forward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
epoch = epoch + 60;
RTC.adjust(epoch);
now = RTC.now();
}

void minute_backward(){
DateTime now = RTC.now();
unsigned long epoch = now.unixtime();
epoch = epoch - 60;
RTC.adjust(epoch);
now = RTC.now();
}

void second_reset(){
DateTime now = RTC.now();
unsigned long newSecond = now.second();
unsigned long epoch = now.unixtime();
epoch = epoch - newSecond;
RTC.adjust(epoch);
now = RTC.now();
}


void loop(){
upbuttonState = digitalRead(upbuttonPin);
downbuttonState = digitalRead(downbuttonPin);
menubuttonState = digitalRead(menubuttonPin);
if (menubuttonState == HIGH) {
mode++;
if (mode == 9) {
mode =0;
}
}
//screen = mode;
Serial.print(mode);
Serial.print(" ");

// Get the current time
DateTime now = RTC.now();

unsigned long epoch = now.unixtime();
Serial.println(epoch);


unsigned long newHour = now.hour();
unsigned long newMinute = now.minute();
unsigned long newSecond = now.second();
unsigned long newYear = now.year();
unsigned long newMonth = now.month();
unsigned long newDay = now.day();

int hour1 = (newHour%10);
int hour10 = (newHour/10);
int minute1 = (newMinute%10);
int minute10 = (newMinute/10);
int second1 = (newSecond%10);
int second10 = (newSecond/10);

int year1 = (newYear%10);
int year10 = ((newYear/10)%10);
int year100 = ((newYear/100)%10);
int year1000 = (newYear/1000);
int month1 = (newMonth%10);
int month10 = (newMonth/10);
int day1 = (newDay%10);
int day10 = (newDay/10);


switch (mode) {
case 0:
//Display (12-1-2015 changed from left to right yo right to left for better display)
lc.displayChar(7, second1);
lc.displayChar(6, second10);
lc.displayChar(5, 11); //Display colon on digit 5
lc.displayChar(4, minute1);
lc.displayChar(3, minute10);
lc.displayChar(2, 11); //Display colon on digit 2
lc.displayChar(1, hour1);
lc.displayChar(0, hour10);
break;
case 1:
//Display date
lc.displayChar(0, day10);
lc.displayChar(1, day1);
lc.displayChar(2, month10);
lc.displayChar(3, month1);
lc.displayChar(4, year1000);
lc.displayChar(5, year100);
lc.displayChar(6, year10);
lc.displayChar(7, year1);
break;
case 5: //Set hour
//Display (12-1-2015 changed from left to right yo right to left for better display)
//lc.displayChar(7, second1);
lc.clearDisplay(7);
//lc.displayChar(6, second10);
lc.clearDisplay(6);
lc.displayChar(5, 11); //Display colon on digit 5
//lc.displayChar(4, minute1);
lc.clearDisplay(4);
//lc.displayChar(3, minute10);
lc.clearDisplay(3);
lc.displayChar(2, 11); //Display colon on digit 2
lc.displayChar(1, hour1);
lc.displayChar(0, hour10);
break;
case 6: //Set minutes
//Display (12-1-2015 changed from left to right yo right to left for better display)
lc.clearDisplay(7);
lc.clearDisplay(6);
//lc.displayChar(7, second1);
//lc.displayChar(6, second10);
lc.displayChar(5, 11); //Display colon on digit 5
lc.displayChar(4, minute1);
lc.displayChar(3, minute10);
lc.displayChar(2, 11); //Display colon on digit 2
lc.clearDisplay(1);
lc.clearDisplay(0);
//lc.displayChar(1, hour1);
//lc.displayChar(0, hour10);
break;
case 7: //Reset seconds
//Display (12-1-2015 changed from left to right yo right to left for better display)
lc.displayChar(7, second1);
lc.displayChar(6, second10);
lc.displayChar(5, 11); //Display colon on digit 5
lc.clearDisplay(4);
lc.clearDisplay(3);
//lc.displayChar(4, minute1);
//lc.displayChar(3, minute10);
lc.displayChar(2, 11); //Display colon on digit 2
lc.clearDisplay(1);
lc.clearDisplay(0);
//lc.displayChar(1, hour1);
//lc.displayChar(0, hour10);
break;
case 4: //Set date
ch= "date";
ch0= ch[0];
ch1= ch[1];
ch2= ch[2];
ch3= ch[3];
lc.displayChar(0, lc.getCharArrayPosition(ch0));
lc.displayChar(1, lc.getCharArrayPosition(ch1));
lc.displayChar(2, lc.getCharArrayPosition(ch2));
lc.displayChar(3, lc.getCharArrayPosition(ch3));
lc.clearDisplay(4);
lc.clearDisplay(5);
lc.displayChar(6, day10);
lc.displayChar(7, day1);
//lc.clearDisplay(2);
//lc.clearDisplay(3);
//lc.clearDisplay(4);
//lc.clearDisplay(5);
//lc.clearDisplay(6);
//lc.clearDisplay(7);
//lc.displayChar(2, month10);
//lc.displayChar(3, month1);
//lc.displayChar(4, year1000);
//lc.displayChar(5, year100);
//lc.displayChar(6, year10);
//lc.displayChar(7, year1);
break;
case 3: //Set month
ch= "month";
ch0= ch[0];
ch1= ch[1];
ch2= ch[2];
ch3= ch[3];
ch4= ch[4];
lc.displayChar(0, lc.getCharArrayPosition(ch0));
lc.displayChar(1, lc.getCharArrayPosition(ch1));
lc.displayChar(2, lc.getCharArrayPosition(ch2));
lc.displayChar(3, lc.getCharArrayPosition(ch3));
lc.displayChar(4, lc.getCharArrayPosition(ch4));
lc.clearDisplay(5);
lc.displayChar(6, month10);
lc.displayChar(7, month1);
break;
case 2: //Set year
ch= "year";
ch0= ch[0];
ch1= ch[1];
ch2= ch[2];
ch3= ch[3];
lc.displayChar(0, lc.getCharArrayPosition(ch0));
lc.displayChar(1, lc.getCharArrayPosition(ch1));
lc.displayChar(2, lc.getCharArrayPosition(ch2));
lc.displayChar(3, lc.getCharArrayPosition(ch3));
lc.displayChar(4, year1000);
lc.displayChar(5, year100);
lc.displayChar(6, year10);
lc.displayChar(7, year1);
break;
case 8: //Set brightness
int bright1 = (bright%10);
int bright10 = (bright/10);
ch= "bright";
ch0= ch[0];
ch1= ch[1];
ch2= ch[2];
ch3= ch[3];
ch4= ch[4];
ch5= ch[5];
lc.displayChar(0, lc.getCharArrayPosition(ch0));
lc.displayChar(1, lc.getCharArrayPosition(ch1));
lc.displayChar(2, lc.getCharArrayPosition(ch2));
lc.displayChar(3, lc.getCharArrayPosition(ch3));
lc.displayChar(4, lc.getCharArrayPosition(ch4));
lc.displayChar(5, lc.getCharArrayPosition(ch5));
lc.displayChar(6, bright10);
lc.displayChar(7, bright1);
break;
}

upbuttonState = digitalRead(upbuttonPin);
downbuttonState = digitalRead(downbuttonPin);


//adjusting the time
if (mode == 5 && upbuttonState == HIGH) {
hour_forward();
}
if (mode == 5 && downbuttonState == HIGH) {
hour_backward();
}
if (mode == 6 && upbuttonState == HIGH) {
minute_forward();
}
if (mode == 6 && downbuttonState == HIGH) {
minute_backward();
}
if (mode == 7 && downbuttonState == HIGH) {
second_reset();
}
if (mode == 4 && upbuttonState == HIGH) {
day_forward();
}
if (mode == 4 && downbuttonState == HIGH) {
day_backward();
}
if (mode == 3 && upbuttonState == HIGH) {
month_forward();
}
if (mode == 3 && downbuttonState == HIGH) {
month_backward();
}
if (mode == 2 && upbuttonState == HIGH) {
year_forward();
}
if (mode == 2 && downbuttonState == HIGH) {
year_backward();
}
if (mode == 8 && upbuttonState == HIGH && bright<15) {
bright++;
for (int i=0; i< NBR_MTX; i++){
lc.setIntensity(i,bright);
}
}
if (mode == 8 && downbuttonState == HIGH && bright>0) {
bright--;
for (int i=0; i< NBR_MTX; i++){
lc.setIntensity(i,bright);
}
}

} //End of loop

LedControlMS.cpp

/*
* LedControl.cpp - A library for controling Leds with a MAX7219/MAX7221
* Copyright (c) 2007 Eberhard Fahle
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* This permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/


#include "LedControlMS.h"

//the opcodes for the MAX7221 and MAX7219
#define OP_NOOP 0
#define OP_DIGIT0 1
#define OP_DIGIT1 2
#define OP_DIGIT2 3
#define OP_DIGIT3 4
#define OP_DIGIT4 5
#define OP_DIGIT5 6
#define OP_DIGIT6 7
#define OP_DIGIT7 8
#define OP_DECODEMODE 9
#define OP_INTENSITY 10
#define OP_SCANLIMIT 11
#define OP_SHUTDOWN 12
#define OP_DISPLAYTEST 15

LedControl::LedControl(int dataPin, int clkPin, int csPin, int numDevices) {
LEDCONTROL_SPI_MOSI=dataPin;
LEDCONTROL_SPI_CLK=clkPin;
LEDCONTROL_SPI_CS=csPin;
if(numDevices<=0 || numDevices>8 )
numDevices=8;
maxDevices=numDevices;
pinMode(LEDCONTROL_SPI_MOSI,OUTPUT);
pinMode(LEDCONTROL_SPI_CLK,OUTPUT);
pinMode(LEDCONTROL_SPI_CS,OUTPUT);
digitalWrite(LEDCONTROL_SPI_CS,HIGH);
LEDCONTROL_SPI_MOSI=dataPin;
for(int i=0;i<64;i++)
status[i]=0x00;
for(int i=0;i<maxDevices;i++) {
spiTransfer(i,OP_DISPLAYTEST,0);
//scanlimit is set to max on startup
setScanLimit(i,7);
//decode is done in source
spiTransfer(i,OP_DECODEMODE,0);
clearDisplay(i);
//we go into shutdown-mode on startup
shutdown(i,true);
}
}

int LedControl::getDeviceCount() {
return maxDevices;
}

void LedControl::shutdown(int addr, bool b) {
if(addr<0 || addr>=maxDevices)
return;
if(b)
spiTransfer(addr, OP_SHUTDOWN,0);
else
spiTransfer(addr, OP_SHUTDOWN,1);
}

void LedControl::setScanLimit(int addr, int limit) {
if(addr<0 || addr>=maxDevices)
return;
if(limit>=0 || limit<8)
spiTransfer(addr, OP_SCANLIMIT,limit);
}

void LedControl::setIntensity(int addr, int intensity) {
if(addr<0 || addr>=maxDevices)
return;
if(intensity>=0 || intensity<16)
spiTransfer(addr, OP_INTENSITY,intensity);

}

void LedControl::clearDisplay(int addr) {
int offset;

if(addr<0 || addr>=maxDevices)
return;
offset=addr*8;
for(int i=0;i<8;i++) {
status[offset+i]=0;
spiTransfer(addr, i+1,status[offset+i]);
}
}

void LedControl::clearAll() {
for (int i=0;i<maxDevices;i++) clearDisplay(i);
}

void LedControl::setLed(int addr, int row, int column, boolean state) {
int offset;
byte val=0x00;

if(addr<0 || addr>=maxDevices)
return;
if(row<0 || row>7 || column<0 || column>7)
return;
offset=addr*8;
val=B10000000 >> column;
if(state)
status[offset+row]=status[offset+row]|val;
else {
val=~val;
status[offset+row]=status[offset+row]&val;
}
spiTransfer(addr, row+1,status[offset+row]);
}

void LedControl::setRow(int addr, int row, byte value) {
int offset;
if(addr<0 || addr>=maxDevices)
return;
if(row<0 || row>7)
return;
offset=addr*8;
status[offset+row]=value;
spiTransfer(addr, row+1,status[offset+row]);
}

void LedControl::setColumn(int addr, int col, byte value) {
byte val;

if(addr<0 || addr>=maxDevices)
return;
if(col<0 || col>7)
return;
for(int row=0;row<8;row++) {
val=value >> (7-row);
val=val & 0x01;
setLed(addr,row,col,val);
}
}

void LedControl::setDigit(int addr, int digit, byte value, boolean dp) {
int offset;
byte v;

if(addr<0 || addr>=maxDevices)
return;
if(digit<0 || digit>7 || value>15)
return;
offset=addr*8;
v=charTable[value];
if(dp)
v|=B10000000;
status[offset+digit]=v;
spiTransfer(addr, digit+1,v);

}

void LedControl::setChar(int addr, int digit, char value, boolean dp) {
int offset;
byte index,v;

if(addr<0 || addr>=maxDevices)
return;
if(digit<0 || digit>7)
return;
offset=addr*8;
index=(byte)value;
if(index >127) {
//nothing define we use the space char
value=32;
}
v=charTable[index];
if(dp)
v|=B10000000;
status[offset+digit]=v;
spiTransfer(addr, digit+1,v);
}

void LedControl::spiTransfer(int addr, volatile byte opcode, volatile byte data) {
//Create an array with the data to shift out
int offset=addr*2;
int maxbytes=maxDevices*2;

for(int i=0;i<maxbytes;i++)
spidata[i]=(byte)0;
//put our device data into the array
spidata[offset+1]=opcode;
spidata[offset]=data;
//enable the line
digitalWrite(LEDCONTROL_SPI_CS,LOW);
//Now shift out the data
for(int i=maxbytes;i>0;i--)
shiftOut(LEDCONTROL_SPI_MOSI,LEDCONTROL_SPI_CLK,MSBFIRST,spidata[i-1]);
//latch the data onto the display
digitalWrite(LEDCONTROL_SPI_CS,HIGH);
}

int LedControl::getCharArrayPosition(char input){
if ((input==' ')||(input=='+')) return 10;
if (input==':') return 11;
if (input=='-') return 12;
if (input=='.') return 13;
if ((input =='(')) return 14; //replace by '˝'
if ((input >='0')&&(input <='9')) return (input-'0');
if ((input >='A')&&(input <='Z')) return (input-'A' + 15);
if ((input >='a')&&(input <='z')) return (input-'a' + 15);
//return 13;
return 10;
}

void LedControl::writeString(int mtx, char * displayString) {
while ( displayString[0]!=0) {
char c =displayString[0];
int pos= getCharArrayPosition( c);
displayChar(mtx,pos);
delay(300);
clearAll();
displayString++;
}
}


void LedControl::displayChar(int matrix, int charIndex) {
for (int i=0; i<8;i++) {
//setRow(matrix,i, alphabetBitmap[charIndex][i]);
setColumn(matrix,i, alphabetBitmap[charIndex][7-i]);
}
}


void LedControl::displaySmallchar(int position, int charIndex) {
if(position>=0 && position<58) {

int beginrow = position;
int endrow = position + 6;
int matrixbegin = (beginrow / 8);
int matrixend = (endrow / 8);
int matrixbeginrow = (beginrow%8); // begin position of the first matrix
int matrixendrow = (endrow%8); //endposition
if (matrixbeginrow > 2 || matrixbegin < matrixend) { //spans over 2 matrixes

for (int i=matrixbeginrow; i<8;i++) { //First matrix to 8th row
setRow(matrixbegin,i, alphabetSmallBitmap[charIndex][i-matrixbeginrow]);
}
for (int i=0; i<matrixendrow;i++) { //Second matrix from 0th row
setRow(matrixend,i, alphabetSmallBitmap[charIndex][i+(8-matrixbeginrow)]);
}

}
else {
//note matrixbeginrow = 0, 1 or2
for (int i=+matrixbeginrow; i<matrixbeginrow+8;i++) {
setRow(matrixbegin,i, alphabetSmallBitmap[charIndex][i-matrixbeginrow]);
}
}


}



}

LedControlMS.h

/*
* LedControl.h - A library for controling Leds with a MAX7219/MAX7221
* Copyright (c) 2007 Eberhard Fahle
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* This permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef LedControl_h
#define LedControl_h

#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

/*
* Segments to be switched on for characters and digits on
* 7-Segment Displays
*/
const static byte charTable[128] = {
B01111110,B00110000,B01101101,B01111001,B00110011,B01011011,B01011111,B01110000,
B01111111,B01111011,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B10000000,B00000001,B10000000,B00000000,
B01111110,B00110000,B01101101,B01111001,B00110011,B01011011,B01011111,B01110000,
B01111111,B01111011,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111,B00000000,
B00110111,B00000000,B00000000,B00000000,B00001110,B00000000,B00000000,B00000000,
B01100111,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00001000,
B00000000,B01110111,B00011111,B00001101,B00111101,B01001111,B01000111,B00000000,
B00110111,B00000000,B00000000,B00000000,B00001110,B00000000,B00000000,B00000000,
B01100111,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000
};


const static byte alphabetBitmap[41][8]={
{0b00111100,
0b01100110,
0b01000110,
0b01001010,
0b01010010,
0b01100010,
0b01100110,
0b00111100}, //0

{0b00011000,
0b00111000,
0b01111000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b01111110}, //1

{0b00111100,
0b01100110,
0b01000010,
0b00000110,
0b00011100,
0b00110000,
0b01100010,
0b01111110},//2

{0b00111100,
0b01100110,
0b01000010,
0b00001100,
0b00001100,
0b01000010,
0b01100110,
0b00111100},//3

{0b00001100,
0b00011100,
0b00110100,
0b01100100,
0b01111110,
0b00000100,
0b00000100,
0b00000100},//4

{0b01111110,
0b01100010,
0b01100000,
0b00111100,
0b00000110,
0b01000010,
0b01100110,
0b00111100},//5

{0b00111100,
0b01100110,
0b01100000,
0b01111100,
0b01100110,
0b01000010,
0b01100110,
0b00111100},//6

{0b01111110,
0b00000110,
0b00000110,
0b00001100,
0b00011000,
0b00011000,
0b00011000,
0b00011000},//7

{0b00111100,
0b01100110,
0b01000010,
0b00111100,
0b00111100,
0b01000010,
0b01100110,
0b00111100},//8

{0b00111100,
0b01100110,
0b01000010,
0b01100110,
0b00111110,
0b00000110,
0b01100110,
0b00111100},//9

{0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000},// blank space

{0b00000000,
0b00011000,
0b00011000,
0b00000000,
0b00000000,
0b00011000,
0b00011000,
0b00000000},// :

{0b00000000,
0b00000000,
0b00000000,
0b01111110,
0b01111110,
0b00000000,
0b00000000,
0b00000000},// -

{0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00011000,
0b00011000},// .

{0b00000000,
0b00000000,
0b00000000,
0b00011000,
0b00011000,
0b00000000,
0b00000000,
0b00000000},//Đ

{0b00111100,
0b01100110,
0b01000010,
0b01111110,
0b01111110,
0b01000010,
0b01000010,
0b01000010},//A

{0b01111100,
0b01000110,
0b01000010,
0b01111100,
0b01111100,
0b01000010,
0b01000110,
0b01111100,},//B

{0b00111100,
0b01100110,
0b01000010,
0b01000000,
0b01000000,
0b01000010,
0b01100110,
0b00111100,},//C

{0b01111100,
0b01000110,
0b01000010,
0b01000010,
0b01000010,
0b01000010,
0b01000110,
0b01111100,},//D

{0b01111110,
0b01000000,
0b01000000,
0b01111000,
0b01111000,
0b01000000,
0b01000000,
0b01111110,},//E

{0b01111110,
0b01000000,
0b01000000,
0b01111000,
0b01111000,
0b01000000,
0b01000000,
0b01000000,},//F

{0b00111100,
0b01100110,
0b01000010,
0b01000000,
0b01001110,
0b01000010,
0b01100110,
0b00111100,},//G

{0b01000010,
0b01000010,
0b01000010,
0b01111110,
0b01111110,
0b01000010,
0b01000010,
0b01000010,},//H

{0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,},//I

{0b00000110,
0b00000110,
0b00000110,
0b00000110,
0b00000110,
0b01000110,
0b01101100,
0b00111100,},//J

{0b01000110,
0b01001100,
0b01011000,
0b01110000,
0b01110000,
0b01011000,
0b01001100,
0b01000110,},//K

{0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01000000,
0b01111110,},//L

{0b01000010,
0b01100110,
0b01011010,
0b01011010,
0b01000010,
0b01000010,
0b01000010,
0b01000010,},//M

{0b01000010,
0b01100010,
0b01010010,
0b01010010,
0b01001010,
0b01001010,
0b01000110,
0b01000010,},//N

{0b00111100,
0b01100110,
0b01000010,
0b01000010,
0b01000010,
0b01000010,
0b01100110,
0b00111100,},//O

{0b00111100,
0b01100110,
0b01000010,
0b01100110,
0b01111100,
0b01000000,
0b01000000,
0b01000000,},//P

{0b00111100,
0b01100110,
0b01000010,
0b01000010,
0b01011010,
0b01001110,
0b01100110,
0b00111100,},//Q

{0b00111100,
0b01100110,
0b01000010,
0b01100110,
0b01111100,
0b01011000,
0b01001100,
0b01000110,},//R

{0b00111100,
0b01100110,
0b01000000,
0b00111000,
0b00011100,
0b00000010,
0b01100110,
0b00111100,},//S

{0b01111110,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,
0b00011000,},//T

{0b01000010,
0b01000010,
0b01000010,
0b01000010,
0b01000010,
0b01000010,
0b01100110,
0b00111100,},//U

{0b01000010,
0b01000010,
0b01000010,
0b00100100,
0b00100100,
0b00100100,
0b00011000,
0b00011000,},//V

{0b01000010,
0b01000010,
0b01000010,
0b01000010,
0b01011010,
0b01011010,
0b01011010,
0b00111100,},//W

{0b01000010,
0b01000010,
0b00100100,
0b00011000,
0b0001000,
0b00100100,
0b01000010,
0b01000010,},//X

{0b01000010,
0b01000010,
0b01000010,
0b00100110,
0b00011110,
0b00000010,
0b01000110,
0b00111100,},//Y

{0b01111110,
0b00000010,
0b00000100,
0b00001000,
0b00010000,
0b00100000,
0b01000000,
0b01111110,}//Z


};

//These are the small bitmaps (5x7), 90 degrees counterclockwise!!!!
const static byte alphabetSmallBitmap[11][6]={
{0b01111100,
0b10100010,
0b10010010,
0b10001010,
0b01111100,
0b00000000}, //0

{0b10001000,
0b10000100,
0b11111110,
0b10000000,
0b10000000,
0b00000000}, //1

{0b11000100,
0b10100010,
0b10010010,
0b10001010,
0b10000100,
0b00000000},//2

{0b01000100,
0b10000010,
0b10010010,
0b10010010,
0b01101100,
0b00000000},//3

{0b00011110,
0b00010000,
0b00010000,
0b11111110,
0b00010000,
0b00000000},//4

{0b10001110,
0b10001010,
0b10001010,
0b01010010,
0b00100010,
0b00000000},//5

{0b01111100,
0b10010010,
0b10010010,
0b10010010,
0b01100100,
0b00000000},//6

{0b00000010,
0b00000010,
0b11110010,
0b00001010,
0b00000110,
0b00000000},//7

{0b01101100,
0b10010010,
0b10010010,
0b10010010,
0b01101100,
0b00000000},//8

{0b01001100,
0b10010010,
0b10010010,
0b10010010,
0b01101100,
0b00000000}//9

};


class LedControl {
private :
/* The array for shifting the data to the devices */
byte spidata[16];
/* Send out a single command to the device */
void spiTransfer(int addr, byte opcode, byte data);

/* We keep track of the led-status for all 8 devices in this array */
byte status[64];
/* Data is shifted out of this pin*/
int LEDCONTROL_SPI_MOSI;
/* The clock is signaled on this pin */
int LEDCONTROL_SPI_CLK;
/* This one is driven LOW for chip selectzion */
int LEDCONTROL_SPI_CS;
/* The maximum number of devices we use */
int maxDevices;


public:
/*
* Create a new controler
* Params :
* dataPin pin on the Arduino where data gets shifted out
* clockPin pin for the clock
* csPin pin for selecting the device
* numDevices maximum number of devices that can be controled
*/
LedControl(int dataPin, int clkPin, int csPin, int numDevices=1);

/*
* Gets the number of devices attached to this LedControl.
* Returns :
* int the number of devices on this LedControl
*/
int getDeviceCount();

/*
* Set the shutdown (power saving) mode for the device
* Params :
* addr The address of the display to control
* status If true the device goes into power-down mode. Set to false
* for normal operation.
*/
void shutdown(int addr, bool status);

/*
* Set the number of digits (or rows) to be displayed.
* See datasheet for sideeffects of the scanlimit on the brightness
* of the display.
* Params :
* addr address of the display to control
* limit number of digits to be displayed (1..8)
*/
void setScanLimit(int addr, int limit);

/*
* Set the brightness of the display.
* Params:
* addr the address of the display to control
* intensity the brightness of the display. (0..15)
*/
void setIntensity(int addr, int intensity);

/*
* Switch all Leds on the display off.
* Params:
* addr address of the display to control
*/
void clearDisplay(int addr);

void clearAll();

/*
* Set the status of a single Led.
* Params :
* addr address of the display
* row the row of the Led (0..7)
* col the column of the Led (0..7)
* state If true the led is switched on,
* if false it is switched off
*/
void setLed(int addr, int row, int col, boolean state);

/*
* Set all 8 Led's in a row to a new state
* Params:
* addr address of the display
* row row which is to be set (0..7)
* value each bit set to 1 will light up the
* corresponding Led.
*/
void setRow(int addr, int row, byte value);

/*
* Set all 8 Led's in a column to a new state
* Params:
* addr address of the display
* col column which is to be set (0..7)
* value each bit set to 1 will light up the
* corresponding Led.
*/
void setColumn(int addr, int col, byte value);

/*
* Display a hexadecimal digit on a 7-Segment Display
* Params:
* addr address of the display
* digit the position of the digit on the display (0..7)
* value the value to be displayed. (0x00..0x0F)
* dp sets the decimal point.
*/
void setDigit(int addr, int digit, byte value, boolean dp);

/*
* Display a character on a 7-Segment display.
* There are only a few characters that make sense here :
* '0','1','2','3','4','5','6','7','8','9','0',
* 'A','b','c','d','E','F','H','L','P',
* '.','-','_',' '
* Params:
* addr address of the display
* digit the position of the character on the display (0..7)
* value the character to be displayed.
* dp sets the decimal point.
*/
void setChar(int addr, int digit, char value, boolean dp);

//Returns the array number in the alphabetBitmap array
int getCharArrayPosition(char c);

void writeString(int mtx, char * displayString);
void displayChar(int matrix, int charIndex);

/*
* Display small character 5x7 defined in a 6x8 bitmap
* Position is starting row from 0 to 58
* Copyright (2014) RenÚ Wennekes
* Last entry 2-1-2-2014
*/
void displaySmallchar(int position, int charIndex);
};

#endif //LedControl.h