LED Matrix Clock Program Includes and  Defines

/* LED Matrix Clock. is in a wood cabinet and has a 8x40 red dot-matrix display.

   It is powered by an attached wall power supply.

   It is programmed by a 6-pin connector with the GND(black) pin nearest the edge.

   It has the following components:

   1. A UNO-R3 compatible Arduino with a micro-SD socket.

   2. A DS3231 RTC with CR2032 battery backup.

   3. Five modules with 8x8 LED matrices driven by MAX7219s and daisy-chained.

   4. A Pololu IR LIDAR distance measuring sensor for mode selection.

   5. A CDS photocell for auto-dimming..

   6. Two push-buttons for setting adjustments.

   7. A micro SD drive SPI CS = 4

   8. An Ethernet interface SPI CS = 10

*/

#include <GyverMAX7219.h>                   //Only library found with a getRotation function

#include <ds3231.h>#include <Wire.h>

#include <SD.h>


#define AM_W 5  // 5 матрицы (40 точки)

#define AM_H 1  // 1 матрицы (8 точек)

#define PC_PORT A0

#define UP_BUTTON 5

#define DOWN_BUTTON 6

#define LIDAR_PORT 3

#define PULSE_PORT 2

#define LIDAR_TIMEOUT 20000

#define MAX_DISTANCES 4

#define VIEW_TIME 2

#define SS 4

#include <Wire.h>

#include <SD.h>


#define AM_W 5  // 5 матрицы (40 точки)

#define AM_H 1  // 1 матрицы (8 точек)

#define PC_PORT A0

#define UP_BUTTON 5

#define DOWN_BUTTON 6

#define LIDAR_PORT 3

#define PULSE_PORT 2

#define LIDAR_TIMEOUT 20000

#define MAX_DISTANCES 4

#define VIEW_TIME 2

#define SS 4

LED Matrix Clock Program Global Variables

MAX7219 < 5, 1, 9 > mtrx;                 //5 modules wide, 1 high, CS = 9


ts t; //ts is a struct findable in ds3231.h


char dow[22] = "SunMonTueWedThuFriSat";

char mchar[37] = "JanFebMarAprMayJunJlyAugSepOctNovDec";

const int threshold[6] = {16, 160, 418, 607, 785, 1024};          //for the photocell

const int zones[MAX_DISTANCES] = {45, 25, 12, 7}; //for getDistance

const char *filenames[] = { "/Commands.txt", "/Displays.txt", "/Intro.txt", "/Info.txt", "Stored.txt", "MoveAway.txt"};

bool sdInit;

unsigned int sysMode;

bool changed = false;


struct adjust_t {

  int labelInx;

  int upLimit;

  int lowLimit;

};

uint8_t loopCount = 39;                      //For timeouts

adjust_t adjVals[] = {  {5, 23, 0}, {8, 59, 0}};

LED Matrix Clock Program Transmit Functions

char txBuf[31];

const char preamble[] = "14L1776t,G,";


void transmitDT(void) {

  uint8_t checkCount = 0;

  sprintf(txBuf, "%s%02u,%02u,%02u,%02u,", preamble, t.mon, t.mday, t.hour, t.min);

  for (int i = 11; i < 22; i++) {

    checkCount += txBuf[i];

  }

  sprintf((txBuf + 23), "%02X", checkCount);

  txBuf[25] = 0;

  digitalWrite(PULSE_PORT, HIGH);

  delay(20);

  digitalWrite(PULSE_PORT, LOW);

  delay(10);

  Serial.println(txBuf);

}

void checkSD(int fnInx) {

  File myFile;

  char item = '~';

  short unsigned int hourVal;

  short unsigned int minuteVal;

  char cmdVal;

  char nameVal[7];

  char actionVal[7];

  int inx;

  if (sdInit) {

    myFile = SD.open(filenames[fnInx]);

  }

  else {

    sdInit = SD.begin(SS);

    Serial.print(fnInx);

    Serial.println(" failed");

    mtrx.println("failed");

    mtrx.update();

    delay(1000);

  }

  if (myFile) {

    while ((myFile.available()) && (item > 3)) {

      inx = 0;

      do {

        item = myFile.read();

        if (item >= ' ') {

          txBuf[inx++] = item;

        }

      } while ((item != '\n') && (item > 3));

      txBuf[inx] = 0;

      sscanf(txBuf, "%2hu %2hu %c %s %s", &hourVal, &minuteVal, &cmdVal, nameVal, actionVal);

      if ((hourVal == t.hour) && (minuteVal == t.min)) {

        sprintf(txBuf, "%s", preamble);

        if (fnInx == 1) {

          txBuf[7] = 'O';

          txBuf[8] = ',';

          txBuf[9] = cmdVal;

          txBuf[10] = 0;

        }

        else {

          txBuf[7] = cmdVal;

          txBuf[8] = 0;

        }

        digitalWrite(PULSE_PORT, HIGH);

        delay(20);

        digitalWrite(PULSE_PORT, LOW);

        delay(10);

        Serial.println(txBuf);

        delay(330);

      }

    }

  }

  myFile.close();

  delay(60);

}

LED Matrix Clock  RTC Adjustment

int adjustRTC(int place, int inVal) {         //place is 0 for hour, 1 for minute

  if (digitalRead(UP_BUTTON) == 0) {

    if (inVal >= adjVals[place].upLimit) {

      inVal = adjVals[place].lowLimit;

    }

    else {

      inVal++;

    }

    changed = true;

  }

  if (digitalRead(DOWN_BUTTON) == 0) {

    if (inVal <= adjVals[place].lowLimit) {

      inVal = adjVals[place].upLimit;

    }

    else {

      inVal--;

    }

    changed = true;

  }

  mtrx.setCursor(0, 0);

  if (place == 0) {

    mtrx.print("Hour");

  }

  if (place == 1) {

    mtrx.print("Min ");

  }

  mtrx.setCursor(29, 0);

  mtrx.print(inVal);

  mtrx.dot(loopCount, 7);   //Visual indication of time to take more action, or store changes

  mtrx.update();

  while ((digitalRead(UP_BUTTON) == 0) || (digitalRead(DOWN_BUTTON) == 0)) {

    delay(1);

  }

  return  inVal;

}

LED Matrix Clock Program Utility Functions

int getDistance(void) {      //Returns distance in centimeters or 50 on timeout

  int retVal = 50;

  int16_t count;

  count = pulseIn(LIDAR_PORT, HIGH, LIDAR_TIMEOUT);

  if (count > 0) {

    retVal = 3 * (count - 1000) / 40;

  }

  return retVal;

}


int getBright(void) {

  int bv = 0;

  int pcVal = analogRead(PC_PORT);

  do {

  } while (pcVal > threshold[bv++]);

  return --bv;

}

LED Matrix Clock Program Display Functions

void dispTime(void) {

  char amPm = 'A';

  uint8_t hrShort = t.hour;

  if (t.hour > 12) {

    hrShort -= 12;

    amPm = 'P';

  }

  if (hrShort < 10) {

    mtrx.print(' ');

  }

  mtrx.print(hrShort);

  mtrx.setCursor(11, 0);

  mtrx.print(':');

  mtrx.setCursor(15, 0);

  mtrx.print(t.min);

  mtrx.print("  ");

  mtrx.setCursor(29, 0);

  mtrx.print(amPm);

  mtrx.print('M');

}

void dispDay(void) {

  mtrx.print(dow[(t.wday - 1) * 3]);

  mtrx.print(dow[((t.wday - 1) * 3) + 1]);

  mtrx.print(dow[((t.wday - 1) * 3) + 2]);

  mtrx.setCursor(23, 0);

  mtrx.print(mchar[(t.mon - 1) * 3]);

  mtrx.print(mchar[((t.mon - 1) * 3) + 1]);

  mtrx.print(mchar[((t.mon - 1) * 3) + 2]);

}


void dispMY(void) {

  mtrx.print(t.mday);

  mtrx.setCursor(17, 0);

  mtrx.print(t.year);

}


void dispSDmsg(int fnInx, int viewTime) {

  File myFile;

  char item = '~';

  int cnt = 0;

  char lineBuf[8];

  if (sdInit) {

    myFile = SD.open(filenames[fnInx]);

    mtrx.clear();

  }

  else {

    sdInit = SD.begin(SS);

    Serial.print(fnInx);

    Serial.println(" failed");

    mtrx.println("failed");

    mtrx.update();

    delay(1000);

  }

  if (myFile) {

    while ((myFile.available()) && (item > 3)) {

      for (cnt = 0; cnt < 7; cnt++) {

        lineBuf[cnt] = ' ';

      }

      cnt = 0;

      mtrx.setCursor(0, 0);

      do {

        item = myFile.read();

        if ((item >= ' ') && (cnt < 8)) {

          lineBuf[cnt++] = item;

        }

      } while ((item != '\n') && (item > 3));

      if (cnt > 0) {

        mtrx.print(lineBuf);

      }

      mtrx.update();

      delay(viewTime);

    }

    myFile.close();

  }

}

LED Matrix Clock Program Setup

void setup() {

  Serial.begin(2400);

  delay(100);

  pinMode(UP_BUTTON, INPUT_PULLUP);

  pinMode(DOWN_BUTTON, INPUT_PULLUP);

  pinMode(10, OUTPUT);                        //Ethernet CS, disable it:cannot use

  pinMode(PULSE_PORT, OUTPUT);

  digitalWrite(PULSE_PORT, LOW);

  digitalWrite(10, HIGH);

  pinMode(SS, OUTPUT);              // Wired CS pin for SD

  digitalWrite(SS, HIGH);

  while (!Serial) {

    ; // wait for serial port to connect. Needed for native USB port only

  }

  mtrx.begin();

  mtrx.setRotation(3);                        //The modules come with pins horizontal,canot be butted that way

  mtrx.setBright(3);                          //Scale of 0 to 5. Current varies 170 - 260 MA

  Wire.begin();                               //start i2c (required for connection)

  DS3231_init(DS3231_INTCN); //register the ds3231 (DS3231_INTCN is the default address of ds3231

  //, this is set by macro for no performance loss)


  DS3231_get(&t);

  if ((t.year < 2024) || (t.year > 2033)) {   //RTC initial startup

    //sec,min,hour,mday,mon,year,wday

    ts s = {0, 14, 19, 7, 1, 2024, 1};

    DS3231_set(s);

  }

  // Secure Digital Drive

  sdInit = SD.begin(SS);

  delay(100);

  sysMode = 0;

  dispSDmsg(2, 400);

}

LED Matrix Clock Program  Loop 

Because of the small display character size, multiple screens (sysMode) are needed.

There being no room on the front for a manual control, a LIDAR IR distance-measuring module selects the screens,

Because the operators hands must be close when adjusting the RTC, timing was also used.

void loop() {

  static int subCount = 0;

  int brightInx;

  static int adjVal;

  int distance = getDistance();                         //in centimeters

  subCount++;

  if (subCount >= VIEW_TIME) {

    subCount = 0;

    if (loopCount > 0) {

      loopCount --;

    }

  }

  DS3231_get(&t);

  if (t.sec == 5) {

    transmitDT();

    delay(960);

  }

  if (t.sec == 17) {

    checkSD(1);

  }

  if (t.sec == 10) {

    checkSD(0);

  }

  brightInx = getBright();

  mtrx.setBright(brightInx);

  mtrx.clear();

  mtrx.setCursor(0, 0);

  switch (sysMode) {

    case 0:                             //Hours,minutes

      dispTime();

      if (distance <= zones[1]) {

        sysMode = 1;

      }

      break;

    case 1:                             //Day of week, month both 3-character abbreviations

      dispDay();

      if (distance <= zones[2]) {

        sysMode = 2;

      }

      if (distance >= zones[0]) {

        sysMode = 0;

      }

      break;

    case 2:                             //Date in month and full year

      dispMY();

      if (distance <= zones[3]) {

        sysMode = 3;

        dispSDmsg(3, 400);             // Information message

        mtrx.clear();

        loopCount = 39;

      }

      if (distance >= zones[1]) {

        sysMode = 1;

      }

      break;

    case 3:

      mtrx.print("Press  ");

      for (int i = 31; i < 39; i += 2) {

        mtrx.dot(i, 5);

      }

      mtrx.dot(loopCount, 7);

      mtrx.update();

      if (digitalRead(UP_BUTTON) == 0) {

        sysMode = 4;

        adjVal = t.hour;

        changed = false;

        loopCount = 39;

      }

      if (digitalRead(DOWN_BUTTON) == 0) {

        sysMode = 5;

        adjVal = t.min;

        changed = false;

        loopCount = 39;

      }

      while ((digitalRead(UP_BUTTON) == 0) || (digitalRead(DOWN_BUTTON) == 0)) {

        delay(100);

      }

      if (loopCount == 0) {

        mtrx.clear();

        loopCount = 42;

        sysMode = 10;

      }

      break;

    case 4:                            //Hour adjustment, no carry in or out

      adjVal = adjustRTC(0, adjVal);

      delay(200);

      if (loopCount == 0)  {

        if (changed) {

          t.min = adjVal;

          DS3231_set(t);

          dispSDmsg(4, 400);

        }

        sysMode = 10;

        loopCount = 42;

      }

      break;

    case 5:                               //Minute adjustment, no carry in or out

      adjVal = adjustRTC(1, adjVal);

      delay(200);

      if (loopCount == 0)  {

        if (changed) {

          t.min = adjVal;

          DS3231_set(t);

          dispSDmsg(4, 400);

        }

        sysMode = 10;

        loopCount = 42;

      }

      break;

    case 10:

    default:

      if (distance > zones[1]) {

        sysMode = 0;

      }

      if ((!changed) && (loopCount == 40)) {

        dispSDmsg(5, 200);

      }

      if (loopCount == 0) {

        loopCount = 39;       ;

        mtrx.clear();

      }

      if (loopCount < 40) {

        mtrx.dot(loopCount, 7);

        mtrx.update();

      }

      break;

  }

  mtrx.update();

  delay(80);

}