/* * 8x40 LED Matrix Clock Code version 2 * 8x40 LED Matrix Clock using DS3231 RTC module, IR recevier and Arduino * Matrix uses 74HC595 Shift Registers to control the LEDs * Source: https://www.youtube.com/channel/UCAE_BN64jkkjfGJLSs21upg * Created by: uElectroPro * Date: 31-Mar-2019 * Last Update: 16-JAN-2021 */ #include #include #define clock1 2 #define data1 3 #define latch 4 #define clock2 5 #define data2 6 #define mode 0xFF22DD // mode #define select 0xFF02FD // arrow #define inc 0xFFC23D // eq #define ok 0xFFB04F // 0 const int A[6] = {B00000000, B01111110, B10001000, B10001000, B10001000, B01111110 }; // ALPHABETS const int B[6] = {B00000000, B11111110, B10010010, B10010010, B10010010, B01101100 }; const int C[6] = {B00000000, B01111100, B10000010, B10000010, B10000010, B01000100 }; const int D[6] = {B00000000, B11111110, B10000010, B10000010, B10000010, B01111100 }; const int E[6] = {B00000000, B11111110, B10010010, B10010010, B10010010, B10000010 }; const int F[6] = {B00000000, B11111110, B10010000, B10010000, B10010000, B10000000 }; const int G[6] = {B00000000, B01111100, B10000010, B10000010, B10001010, B01001100 }; const int H[6] = {B00000000, B11111110, B00010000, B00010000, B00010000, B11111110 }; const int I[6] = {B00000000, B00000000, B10000010, B11111110, B10000010, B00000000 }; const int J[6] = {B00000000, B00000100, B00000010, B10000010, B11111100, B10000000 }; const int K[6] = {B00000000, B11111110, B00010000, B00101000, B01000100, B10000010 }; const int L[6] = {B00000000, B11111110, B00000010, B00000010, B00000010, B00000010 }; const int M[6] = {B00000000, B11111110, B01000000, B00110000, B01000000, B11111110 }; const int N[6] = {B00000000, B11111110, B00100000, B00010000, B00001000, B11111110 }; const int O[7] = {B00000000, B01111100, B10000010, B10000010, B10000010, B01111100 }; const int P[6] = {B00000000, B11111110, B10010000, B10010000, B10010000, B01100000 }; const int Q[6] = {B00000000, B01111100, B10000010, B10001010, B10000100, B01111010 }; const int R[6] = {B00000000, B11111110, B10010000, B10011000, B10010100, B01100010 }; const int S[6] = {B00000000, B01100010, B10010010, B10010010, B10010010, B10001100 }; const int T[6] = {B00000000, B10000000, B10000000, B11111110, B10000000, B10000000 }; const int U[6] = {B00000000, B11111100, B00000010, B00000010, B00000010, B11111100 }; const int V[6] = {B00000000, B11111000, B00000100, B00000010, B00000100, B11111000 }; const int W[6] = {B00000000, B11111110, B00000100, B00011000, B00000100, B11111110 }; const int X[6] = {B00000000, B11000110, B00101000, B00010000, B00101000, B11000110 }; const int Y[6] = {B00000000, B11100000, B00010000, B00001110, B00010000, B11100000 }; const int Z[6] = {B00000000, B10000110, B10001010, B10010010, B10100010, B11000010 }; const int space[6] = {B00000000, B00000000, B00000000, B00000000, B00000000, B00000000 }; // SPACE ' ' const int degree[6] = {B00000000, B00000000, B11100000, B10100000, B11100000, B00000000 }; // degree'`' const int num[10][6] = { {B00000000, B01111100, B10000010, B10000010, B10000010, B01111100}, // 0 to 9 {B00000000, B00000000, B01000010, B11111110, B00000010, B00000000}, {B00000000, B01000110, B10001010, B10010010, B10100010, B01000010}, {B00000000, B01000100, B10000010, B10010010, B10010010, B01101100}, {B00000000, B00011000, B00101000, B01001000, B11111110, B00001000}, {B00000000, B11100100, B10010010, B10010010, B10010010, B10001100}, {B00000000, B01101100, B10010010, B10010010, B10010010, B00001100}, {B00000000, B10000000, B10001110, B10010000, B10100000, B11000000}, {B00000000, B01101100, B10010010, B10010010, B10010010, B01101100}, {B00000000, B01100000, B10010010, B10010010, B10010010, B01101100} }; const int smallNum[10][4] = { {B00000000, B00111110, B00100010, B00111110}, // 0 to 9 {B00000000, B00010010, B00111110, B00000010}, {B00000000, B00101110, B00101010, B00111010}, {B00000000, B00101010, B00101010, B00111110}, {B00000000, B00111000, B00001000, B00111110}, {B00000000, B00111010, B00101010, B00101110}, {B00000000, B00111110, B00101010, B00101110}, {B00000000, B00100000, B00100000, B00111110}, {B00000000, B00111110, B00101010, B00111110}, {B00000000, B00111010, B00101010, B00111110} }; const int deg[] = { B01000000, B00111110, B00100010, B00100010 }; // `C const int colon[2] = { B00000000, B00101000 }; // : const int sp = B00000000; // blank column byte disp[40] = {0}; // display char text[40]; // variable hours : minutes : seconds // myTime[] [0][1] [2][3] [4][5] int myTime[] = {1,2,0,0,0,0}; int myDate[] = {0,1,0,1,2,0,1,9}; int digitNum = 0, dowNum = 0, setupState = 0; // "setupState" is used to select (1)time, (2)date, or (3)DOW, bool colonState = 0, setupMode = 0, blnk = 0, locked = 0; // if locked=0 text (or Menu) will be displayed for current "setupState", if locked=1, we can change time, date, or dow int SEC = 0, preSec = 0, timeout = 0; // timeout, is delay before the selected digit starts blinking, when its value is changed int k=0, ch=0, index=0; unsigned long mil, halfSec, lastTime = 0, ms = 0; unsigned long irVal = 0; const int RECV_PIN = 9; // IR receiver pin (was 7) IRrecv irrecv(RECV_PIN); decode_results results; DS3231 rtc(SDA,SCL); Time t; char *d, *DOW; int temp; char months[12][3] = { "JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC" }; void setup(){ irrecv.enableIRIn(); // Start the receiver irrecv.blink13(true); pinMode(latch, OUTPUT); pinMode(clock1, OUTPUT); pinMode(clock2, OUTPUT); pinMode(data1, OUTPUT); pinMode(data2, OUTPUT); Serial.begin(57600); rtc.begin(); // The following lines can be uncommented to set the date and time // rtc.setDOW(SUNDAY); // Set Day-of-Week to SUNDAY // rtc.setTime(12, 0, 0); // Set the time to 12:00:00 (24hr format) // rtc.setDate(1, 1, 2019); // Set the date to 1st January, 2019 } void loop(){ irVal = 0; if(irrecv.decode(&results)){ irVal = results.value; Serial.println(irVal, HEX); irrecv.resume(); // Receive the next value } t = rtc.getTime(); d = rtc.getDateStr(); SEC = t.sec; /**********************\ Update Variables \**********************/ if(preSec!=SEC){ preSec = SEC; if(!setupMode){ // stop updating variables when setup mode in on myTime[4] = SEC/10; // seconds myTime[5] = SEC%10; // myTime[2] = (t.min)/10; // minutes myTime[3] = (t.min)%10; // int h = t.hour; if(h>12){ // converting 24 hr to 12 hr format h-=12; } if(h == 0){ // hour will be 12 (AM) when h = 0 myTime[0] = 1; myTime[1] = 2; } else{ myTime[0] = h/10; // hours myTime[1] = h%10; // } myDate[0] = d[0]-'0'; // day myDate[1] = d[1]-'0'; // myDate[2] = d[3]-'0'; // month myDate[3] = d[4]-'0'; // myDate[4] = d[6]-'0'; // year myDate[5] = d[7]-'0'; // myDate[6] = d[8]-'0'; // myDate[7] = d[9]-'0'; // colonState = !colonState; // change colon state to blink colon every second } /***************\ UPDATE DISPLAY (Time, Date & DOW) \***************/ // Set positions for all digits on matrix or // arrange all the digits on matrix at specific position // if(SEC<11 || SEC>15) for(int g=0;g<40;g++){ // TIME if(g<5){ disp[g] = num[myTime[0]][g+1]; // hour } else if(g>=5 && g<11){ disp[g] = num[myTime[1]][g-5]; // } else if(g>=13 && g<19){ disp[g] = num[myTime[2]][g-13]; // min } else if(g>=19 && g<25){ disp[g] = num[myTime[3]][g-19]; // } else if(g>=27 && g<31){ disp[g] = smallNum[myTime[4]][g-27]; // sec } else if(g>=31 && g<35){ disp[g] = smallNum[myTime[5]][g-31]; // } else if(g==12){ disp[g] = colon[colonState]; // blink colons } else{ disp[g] = sp; // blank column space } } /***************\Day of Week & DATE\***************/ if(SEC==0 || SEC==10){ DOW = rtc.getDOWStr(); int n=0; for(int i = 0; i<40; i++){ if(i<10-n){ if(DOW[i]>='A' & DOW[i]<='Z' || DOW[i]>='a' & DOW[i]<='z') text[i] = DOW[i]; else if(DOW[i]=='\0'){ text[i] = ' '; n = 9-i; } } else if(i==10-n){ text[i] = d[0]; text[i+1] = d[1]; text[i+2] = ' '; i = 12-n; } else if(i==13-n){ int x = (myDate[2]*10 + myDate[3]) - 1; text[i] = months[x][0]; text[i+1] = months[x][1]; text[i+2] = months[x][2]; text[i+3] = ' '; i = 16-n; } else if(i==17-n){ text[i] = d[6]; text[i+1] = d[7]; text[i+2] = d[8]; text[i+3] = d[9]; text[i+4] = ' '; i = 21-n; } else if(i==22-n){ text[i] = 'T'; text[i+1] = 'E'; text[i+2] = 'M'; text[i+3] = 'P'; text[i+4] = ' '; text[i+5] = temp/10+'0'; text[i+6] = temp%10+'0'; text[i+7] = '`'; text[i+8] = 'C'; i = 30-n; } else { text[i] = ' '; text[37] = NULL; } } } // Serial.println(text); /**********************\ Temperature \**********************/ if(!setupMode) if(SEC>=46 && SEC<49){ // time (in seconds) for temperature to display temp = rtc.getTemp(); for(int i=27;i<40;i++){ if(i<31){ disp[i] = smallNum[temp/10][i-27]; } else if(i>=31 && i<35){ disp[i] = smallNum[temp%10][i-31]; } else if(i>=35 && i<39){ disp[i] = deg[i-35]; } else{ disp[i] = sp; } } } if(SEC==11 && !setupMode){ ScrollText(text, 90); // scroll speed } } // updating variables end /**********************\ Other Operations \**********************/ if(millis()-ms > 100){ // 100 in milliseconds ms = millis(); if(timeout > 0){ // timeout is used for small delay befor the selected digit start blinking when we change its value blnk = 0; // or selected digit will not blink while blnk = 0 timeout++; if(timeout>6) timeout = 0; } } if(SEC==10) if(millis()-mil>=125){ mil = millis(); for(int j=0; j<40; j++){ disp[j] = disp[j]<=122){ // mil = millis(); // for(int j=0; j<40; j++){ // disp[j] = disp[j]< 500){ lastTime = millis(); blnk = !blnk; // blnk variable, change its value (0 or 1) every half a second, which is used to blink the selected digit while updating time/date } } /**********************\ Blink Selected Digit \**********************/ // While updating Time & Date, this part of code will display time & date and blinks the selected digit if(setupMode){ /***************\ TIME \***************/ if(setupState==1 && locked) // Blinks the selected digit for Time for(int g=0; g<40; g++){ if(g<5){ if(digitNum==1 && blnk) disp[g] = sp; // blank space else disp[g] = num[myTime[0]][g+1]; // hour } else if(g>=5 && g<11){ if(digitNum==1 && blnk) disp[g] = sp; // blank space else disp[g] = num[myTime[1]][g-5]; // hour } else if(g>=13 && g<19){ if(digitNum==2 && blnk) disp[g] = sp; // blank space else disp[g] = num[myTime[2]][g-13]; // min } else if(g>=19 && g<25){ if(digitNum==3 && blnk) disp[g] = sp; // blank space else disp[g] = num[myTime[3]][g-19]; // min } else if(g>=27 && g<31){ if(digitNum==4 && blnk) disp[g] = sp; // blank space else disp[g] = smallNum[myTime[4]][g-27]; // sec } else if(g>=31 && g<35){ if(digitNum==5 && blnk) disp[g] = sp; // blank space else disp[g] = smallNum[myTime[5]][g-31]; // sec } else if(g==12){ disp[g] = colon[1]; // colons on } else{ disp[g] = sp; // blank column space } } /***************\ DATE \***************/ if(setupState==2 && locked) // Blinks the selected digit for Date for(int g=0;g<40;g++){ if(g<5){ if(digitNum==0 && blnk) disp[g] = sp; // blank space else disp[g] = num[myDate[0]][g+1]; // day } else if(g>=5 && g<11){ if(digitNum==1 && blnk) disp[g] = sp; // blank space else disp[g] = num[myDate[1]][g-5]; // day } else if(g>=13 && g<19){ if(digitNum==3 && blnk) disp[g] = sp; // blank space else disp[g] = num[myDate[2]][g-13]; // month } else if(g>=19 && g<25){ if(digitNum==3 && blnk) disp[g] = sp; // blank space else disp[g] = num[myDate[3]][g-19]; // month } else if(g>=27 && g<33){ if(digitNum==4 && blnk) disp[g] = sp; // blank space else disp[g] = num[myDate[6]][g-27]; // year } else if(g>=33 && g<39){ if(digitNum==5 && blnk) disp[g] = sp; // blank space else disp[g] = num[myDate[7]][g-33]; // year } else if(g==12 || g==26){ disp[g] = B00000010; // dot } else{ disp[g] = sp; // blank column space } } /***************\ DOW \***************/ // Display Day of Week while updating if(setupState==3 && locked) if(dowNum==0) DisplayText(" MON "); else if(dowNum==1) DisplayText(" TUE "); else if(dowNum==2) DisplayText(" WED "); else if(dowNum==3) DisplayText(" THU "); else if(dowNum==4) DisplayText(" FRI "); else if(dowNum==5) DisplayText(" SAT "); else if(dowNum==6) DisplayText(" SUN "); } // setup mode end /**********************\ Set Time, Date & DOW \**********************/ /***************\ MODE \***************/ if(irVal == mode){ setupMode = !setupMode; // setupMode = 1 enable, setupMode = 0 disable if(setupMode == 1){ // get current time and store it in variables(in 24hr format) myTime[0] = t.hour/10; myTime[1] = t.hour%10; // myTime[2] = t.min/10; // myTime[3] = t.min%10; // myTime[4] = t.sec/10; // myTime[5] = t.sec%10; dowNum = t.dow - 1; } setupState = 0; digitNum = 0; blnk = 0; locked = 0; } if(setupMode){ // if setup mode is 1, then we can change time, date and day of week (DOW) /***************\ Menu \***************/ if(!locked) if(setupState==0) DisplayText(" SETUP "); // display text else if(setupState==1) DisplayText(" TIME "); else if(setupState==2) DisplayText(" DATE "); else if(setupState==3) DisplayText(" DOW "); /***************\ SET \***************/ if(irVal == ok){ // ok, Set time, date or dow int a,b,c; if(locked) if(setupState==1){ a = myTime[0]*10 + myTime[1]; // hour b = myTime[2]*10 + myTime[3]; // min c = myTime[4]*10 + myTime[5]; // sec rtc.setTime(a,b,c); // set time } else if(setupState==2){ a = myDate[0]*10 + myDate[1]; // day b = myDate[2]*10 + myDate[3]; // month c = myDate[6]*10 + myDate[7] + 2000; // year rtc.setDate(a,b,c); // set date } else if(setupState==3){ if(dowNum==0) rtc.setDOW(MONDAY); else if(dowNum==1) rtc.setDOW(TUESDAY); else if(dowNum==2) rtc.setDOW(WEDNESDAY); else if(dowNum==3) rtc.setDOW(THURSDAY); else if(dowNum==4) rtc.setDOW(FRIDAY); else if(dowNum==5) rtc.setDOW(SATURDAY); else if(dowNum==6) rtc.setDOW(SUNDAY); } if(setupState > 0) locked = !locked; digitNum = 0; if(setupState==1 && digitNum==0) // setupState = 1 is for time digitNum = 1; } /***************\ SELECT \***************/ if(irVal == select){ // select digit to change its value if(!locked){ setupState++; // select (1)time, (2)date or (3)DOW if(setupState==4) setupState = 0; } else if(locked){ if(setupState==1 || setupState==2){ digitNum++; // select digit (0 to 5) if(digitNum==6) digitNum = 0; } if(setupState==1 && digitNum==0) // setupState = 1 is for time digitNum = 1; if(setupState==2 && digitNum==2) // setupState = 2 is for date digitNum = 3; } } /***************\ INCREASE \***************/ if(irVal == inc){ // increase digit value if(locked){ if(setupState==1){ // --> Time myTime[digitNum]++; if(myTime[0]==3) myTime[0] = 0; if(myTime[1]==10) myTime[1] = 0; if(myTime[2]==6) myTime[2] = 0; if(myTime[3]==10) myTime[3] = 0; if(myTime[4]==6) myTime[4] = 0; if(myTime[5]==10) myTime[5] = 0; if(myTime[1]%10 == 0 && digitNum==1){ myTime[0]++; } if(myTime[0]==2 && myTime[1]==4){ myTime[0] = 0; myTime[1] = 0; } } else if(setupState==2){ // --> Date if(digitNum>3) // increament of '2' because next two variables(4,5) which is 20 of 2019 don't need to be change and display, myDate[digitNum+2]++; // and we jump over to the next two variables(6,7) else myDate[digitNum]++; if(myDate[0]==4) myDate[0] = 0; if(myDate[1]==10) myDate[1] = 0; if(myDate[2]==2) myDate[2] = 0; if(myDate[3]==10) myDate[3] = 0; if(myDate[6]==10) myDate[6] = 0; if(myDate[7]==10) myDate[7] = 0; if(myDate[3]%10 == 0 && digitNum==3){ myDate[2]++; } if(myDate[2]==1 && myDate[3]==3){ myDate[2] = 0; myDate[3] = 1; } } else if(setupState==3){ // --> DOW dowNum++; if(dowNum==7) dowNum = 0; } timeout = 1; } } } // setup mode end /**********************\ Refresh Display \**********************/ RefreshDisplay(disp); // Serial.println(millis()); // to check how much time it takes to refresh display } void RefreshDisplay(byte Disp[]){ // here first we convert bytes from columns to rows (Disp[40] to disp1[8][5]) then refresh the display, // because it takes minimum time to refresh display row wise than column wise int matrices = 5; int columns = 40; byte disp1[8][matrices]; // 8 rows, 5 matrix int col = 0; byte a = 0; for(int row=0; row<8; row++) for(int matrix=0; matrix < matrices; matrix++) for(int i=0; i<8; i++){ bitWrite(disp1[row][matrix], i, bitRead(Disp[col], row)); // Reading (left to right(0 to 7)) each row bit from column(0 to 39) and write them into differnt array (disp1). col++; if(col==columns) col = 0; } // Refresh Display row wise for(int row=0; row<8; row++){ a = 0; bitSet(a, row); digitalWrite(latch, LOW); shiftOut(data2,clock2,LSBFIRST,~a); // control the rows for(int i=4;i>=0;i--) shiftOut(data1,clock1,MSBFIRST,disp1[row][i]); // shiftout main display disp1 digitalWrite(latch, HIGH); } } void ScrollText(char str[], int sp){ clearDisplay(); while(1){ if(millis()-mil>sp){ mil = millis(); for(int g=0; g<39; g++) disp[g] = disp[g+1]; if(str[ch]>='a' & str[ch]<='z'){ array(str[ch]-32, 39, index); } else { array(str[ch], 39, index); } index++; if(index==6){ ch++; index=0; } if(str[ch]=='\0'){ ch = 0; index = 0; break; } } RefreshDisplay(disp); } } void clearDisplay(){ for(int i=0;i<40;i++) disp[i] = 0; RefreshDisplay(disp); } void DisplayText(char ch[]){ // Due to matrix size(40 columns) only 6 characters can be display, each character consist of six columns int c = 0; for(int g=0; g<40; g++){ if(ch[c]>='A' && ch[c]<='Z' || ch[c]==' '){ array(ch[c], g, g%6); } else disp[g] = B00000000; if(g%6==0 && g!=0) c++; } } void array(char alp, int j, int i){ // uncomment the letters, from here and start of the program, you want to use. if(alp=='A') // A disp[j] = A[i]; else if(alp=='B') // B disp[j] = B[i]; else if(alp=='C') // C disp[j] = C[i]; else if(alp=='D') // D disp[j] = D[i]; else if(alp=='E') // E disp[j] = E[i]; else if(alp=='F') // F disp[j] = F[i]; else if(alp=='G') // G disp[j] = G[i]; else if(alp=='H') // H disp[j] = H[i]; else if(alp=='I') // I disp[j] = I[i]; else if(alp=='J') // J disp[j] = J[i]; else if(alp=='K') // K disp[j] = K[i]; else if(alp=='L') // L disp[j] = L[i]; else if(alp=='M') // M disp[j] = M[i]; else if(alp=='N') // N disp[j] = N[i]; else if(alp=='O') // O disp[j] = O[i]; else if(alp=='P') // P disp[j] = P[i]; else if(alp=='Q') // Q disp[j] = Q[i]; else if(alp=='R') // R disp[j] = R[i]; else if(alp=='S') // S disp[j] = S[i]; else if(alp=='T') // T disp[j] = T[i]; else if(alp=='U') // U disp[j] = U[i]; else if(alp=='V') // V disp[j] = V[i]; else if(alp=='W') // W disp[j] = W[i]; else if(alp=='X') // X disp[j] = X[i]; else if(alp=='Y') // Y disp[j] = Y[i]; else if(alp=='Z') // Z disp[j] = Z[i]; else if(alp=='0') // 0 disp[j] = num[0][i]; else if(alp=='1') // 1 disp[j] = num[1][i]; else if(alp=='2') // 2 disp[j] = num[2][i]; else if(alp=='3') // 3 disp[j] = num[3][i]; else if(alp=='4') // 4 disp[j] = num[4][i]; else if(alp=='5') // 5 disp[j] = num[5][i]; else if(alp=='6') // 6 disp[j] = num[6][i]; else if(alp=='7') // 7 disp[j] = num[7][i]; else if(alp=='8') // 8 disp[j] = num[8][i]; else if(alp=='9') // 9 disp[j] = num[9][i]; else if(alp==' ') // space disp[j] = space[i]; else if(alp=='`') // '`' disp[j] = degree[i]; // else if(alp==NULL){ // NULL // ch = 0; // index = 0; // } }