// Program to demonstrate the MD_Parola library
// - Optional use of DS1307 module for time and DHT11 sensor for temp and humidity
// - DHT11 library (DHT11) found at https://github.com/winlinvip/SimpleDHT
// - Optional use of DS1307 module for time
// - DS1307 library (MD_DS1307) found at https://github.com/MajicDesigns/DS1307
// NOTE: MD_MAX72xx library must be installed and configured for the LED
// matrix type being used. Refer documentation included in the MD_MAX72xx
// library or see this link:
// https://github.com/MajicDesigns/MD_Parola
// https://github.com/MajicDesigns/MD_MAX72XX
//https://www.youtube.com/channel/UCCC8DuqicBtP3A_aC53HYDQ/videos
// Use the DS1307 clock module
#define USE_DS1307 0
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include "Font_Data.h"
#include <MD_DS1307.h>
#include <Wire.h>
// Use the DHT11 temp and humidity sensor
#include <SimpleDHT.h>
int pinDHT11 = 2;
SimpleDHT11 dht11(pinDHT11);
float h = 0;
float t = 0;
float f = 0;
int16_t speed;
const uint8_t SPEED_DEADBAND = 5;
static bool flasher = false; // seconds passing flasher
const uint8_t SPEED_IN = A15;
volatile boolean buttonA = false;
volatile boolean buttonB = false;
volatile boolean buttonC = false;
volatile boolean buttonD = false;
int StateOfbuttonA = 0;
int StateOfbuttonB = 0;
int StateOfbuttonC = 0;
int StateOfbuttonD = 0;
int NewStateOfbuttonA = 0;
int NewStateOfbuttonB = 0;
int NewStateOfbuttonC = 0;
int NewStateOfbuttonD = 0;
uint8_t scrollSpeed = 25; // default frame delay value
int Mode = 0;
int contrast = 0;
// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 16
#define CLK_PIN 13
#define DATA_PIN 11
#define CS_PIN 10
// HARDWARE SPI
//MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, 16);
// SOFTWARE SPI
MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
#define SPEED_TIME 80
#define PAUSE_TIME 10
#define MAX_MESG 20
// Global variables
char szTime[9]; // mm:ss\0
char szMesg[MAX_MESG+1] = "";
char szMes[MAX_MESG+1] = "";
char szMeD[MAX_MESG+1] = "";
char szMeM[MAX_MESG+1] = "";
char szMeY[MAX_MESG+1] = "";
char szsecond[4]; // ss
uint8_t degC[] = { 6, 3, 3, 56, 68, 68, 68 }; // Deg C
uint8_t degF[] = { 6, 3, 3, 124, 20, 20, 4 }; // Deg F
char *mon2str(uint8_t mon, char *psz, uint8_t len)
// Get a label from PROGMEM into a char array
{
static const char str[][4] PROGMEM =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
*psz = '\0';
mon--;
if (mon < 12)
{
strncpy_P(psz, str[mon], len);
psz[len] = '\0';
}
return(psz);
}
char *dow2str(uint8_t code, char *psz, uint8_t len)
{
static const char str[][10] PROGMEM =
{
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
*psz = '\0';
code--;
if (code < 7)
{
strncpy_P(psz, str[code], len);
psz[len] = '\0';
}
return(psz);
}
void getTime(char *psz, bool f = true)
// Code for reading clock time
{
RTC.readTime();
sprintf(psz, "%02d%c%02d", RTC.h, (f ? ':' : ' '), RTC.m);
}
void getsecond(char *psz)
// Code for reading clock date
{
sprintf(psz, "%02d", RTC.s);
}
void getDate(char *psz)
// Code for reading clock date
{
char szBuf[10];
sprintf(psz, "%0d %s %04d", RTC.dd, mon2str(RTC.mm, szBuf, sizeof(szBuf)-1), RTC.yyyy);
}
void getTemperature()
{
// read without samples.
byte temperature = 0;
byte humidity = 0;
int err = SimpleDHTErrSuccess;
if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
delay(100);
return;
}
h = (int)humidity;
t = (int)temperature;
// Read temperature as Fahrenheit
f = (1.8 * DHT.temperature)+32;
}
void getHour(char *psz, bool f = true)
// Code for reading clock time
{
if (flasher) {
sprintf(psz, "@@%c%02d", (f ? ':' : ' '), RTC.m);
}
else {
sprintf(psz, "%02d%c%02d", RTC.h, (f ? ':' : ' '), RTC.m);
}
}
void getMinute(char *psz, bool f = true)
// Code for reading clock time
{
if (flasher) {
sprintf(psz, "%02d%c%@@", RTC.h, (f ? ':' : ' '));
}
else {
sprintf(psz, "%02d%c%02d", RTC.h, (f ? ':' : ' '), RTC.m);
}
}
void getSec(char *psz)
// Code for reading clock date
{
if (flasher) {
sprintf(psz, " ");
}
else {
sprintf(psz, "%02d", RTC.s);
}
}
void getDow(char *psz)
// Code for reading clock date
{
if (Mode == 4 ) {
if (flasher) {
sprintf(psz, " ");
}
else {
dow2str(RTC.dow, szMes, MAX_MESG);
}
}
else {
dow2str(RTC.dow, szMes, MAX_MESG);
}
}
void getDay(char *psz)
// Code for reading clock date
{
if (Mode == 5 ) {
if (flasher) {
sprintf(psz, " ");
}
else {
sprintf(psz, "%02d", RTC.dd);
}
}
else {
sprintf(psz, "%02d", RTC.dd);
}
}
void getMon(char *psz)
// Code for reading clock date
{
if (Mode == 6 ) {
if (flasher) {
sprintf(psz, " ");
}
else {
char szBuf[4];
sprintf(psz, "%s", mon2str(RTC.mm, szBuf, sizeof(szBuf)-1));
}
}
else {
char szBuf[4];
sprintf(psz, "%s", mon2str(RTC.mm, szBuf, sizeof(szBuf)-1));
}
}
void getYer(char *psz)
// Code for reading clock date
{
if (Mode == 7 ) {
if (flasher) {
sprintf(psz, " ");
}
else {
sprintf(psz, "%04d", RTC.yyyy);
}
}
else {
sprintf(psz, "%04d", RTC.yyyy);
}
}
void getmodea()
{
Mode = 0;
// initialise the LED display
P.begin(4);
// Set up zones for 4 halves of the display
// Each zone gets a different font, making up the top
// and bottom half of each letter
P.setZone(0, 0, 1);
P.setZone(1, 2, 7);
P.setZone(2, 10, 15);
P.setZone(3, 8, 9);
P.setFont(0, NULL);
P.setFont(1, BigFontLower);
P.setFont(2, BigFontUpper);
P.setFont(3, NULL);
P.displayZoneText(0, szsecond, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(1, szTime, PA_LEFT, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(2, szTime, PA_LEFT, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(3, szMesg, PA_CENTER, scrollSpeed, PAUSE_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}
void getmodeb()
{
// initialise the LED display
P.begin(4);
// Set up zones for 4 halves of the display
// Each zone gets a different font, making up the top
// and bottom half of each letter
P.setZone(0, 0, 2);
P.setZone(1, 3, 5);
P.setZone(2, 6, 7);
P.setZone(3, 8, 15);
P.setFont(0, NULL);
P.setFont(1, NULL);
P.setFont(2, NULL);
P.setFont(3, NULL);
P.displayZoneText(3, szMes, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(0, szMeY, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(2, szMeD, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(1, szMeM, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
}
void getmodec()
{
Mode = 8;
P.begin(2);
// Set up zones for 4 halves of the display
// Each zone gets a different font, making up the top
// and bottom half of each letter
P.setZone(0, 0, 7);
P.setZone(1, 8, 15);
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
}
void setup(void)
{
pinMode(SPEED_IN, INPUT);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(6, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
RTC.control(DS1307_CLOCK_HALT, DS1307_OFF);
RTC.control(DS1307_12H, DS1307_OFF);
RTC.readTime();
getmodea();
P.addChar('^', degC);
P.addChar('_', degF);
getTime(szTime);
}
void loop(void)
{
P.setIntensity(contrast);
NewStateOfbuttonA = digitalRead(7);
NewStateOfbuttonB = digitalRead(6);
NewStateOfbuttonC = digitalRead(5);
NewStateOfbuttonD = digitalRead(4);
buttonAisPressed();
buttonBisPressed();
buttonCisPressed();
buttonDisPressed();
SPEEDischanged();
if (buttonA) {
if (Mode == 0 ) {
buttonA = false;
contrast++;
if (contrast >= 51 ) {
contrast = 50;
}
}
else if (Mode == 1 ) {
buttonA = false;
RTC.h++;
if (RTC.h >= 24 ) {
RTC.h = 0;
}
RTC.writeTime();
}
else if (Mode == 2 ) {
buttonA = false;
RTC.m++;
if (RTC.m >= 60 ) {
RTC.m = 0;
}
RTC.writeTime();
}
else if (Mode == 3 ) {
buttonA = false;
RTC.s = 0;
RTC.writeTime();
}
else if (Mode == 4 ) {
buttonA = false;
RTC.dow++;
if (RTC.dow >= 8 ) {
RTC.dow = 1;
}
RTC.writeTime();
}
else if (Mode == 5 ) {
buttonA = false;
RTC.dd++;
if (RTC.dd >= 32 ) {
RTC.dd = 1;
}
RTC.writeTime();
}
else if (Mode == 6 ) {
buttonA = false;
RTC.mm++;
if (RTC.mm >= 13 ) {
RTC.mm = 1;
}
RTC.writeTime();
}
else if (Mode == 7 ) {
buttonA = false;
RTC.yyyy++;
if (RTC.yyyy >= 2035 ) {
RTC.yyyy = 2015;
}
RTC.writeTime();
}
}
if (buttonB) {
buttonB = false;
Mode++;
if (Mode >= 8 ) {
getmodea();
}
else if (Mode >= 4 ) {
getmodeb();
}
}
if (buttonD) {
buttonD = false;
if (Mode == 0 ) {
getmodec();
}
else {
getmodea();
}
}
if (buttonC) {
if (Mode == 0 ) {
buttonC = false;
contrast--;
if (contrast <= 0 ) {
contrast = 0;
}
}
else if (Mode == 1 ) {
buttonC = false;
RTC.h--;
if (RTC.h <= 0 ) {
RTC.h = 23;
}
RTC.writeTime();
}
else if (Mode == 2 ) {
buttonC = false;
RTC.m--;
if (RTC.m <= 0 ) {
RTC.m = 59;
}
RTC.writeTime();
}
else if (Mode == 3 ) {
buttonC = false;
RTC.s = 0;
RTC.writeTime();
}
else if (Mode == 4 ) {
buttonC = false;
RTC.dow--;
if (RTC.dow <= 0 ) {
RTC.dow = 7;
}
RTC.writeTime();
}
else if (Mode == 5 ) {
buttonC = false;
RTC.dd--;
if (RTC.dd <= 0 ) {
RTC.dd = 31;
}
RTC.writeTime();
}
else if (Mode == 6 ) {
buttonC = false;
RTC.mm--;
if (RTC.mm <= 0 ) {
RTC.mm = 12;
}
RTC.writeTime();
}
else if (Mode == 7 ) {
buttonC = false;
RTC.yyyy--;
if (RTC.yyyy <= 2010 ) {
RTC.yyyy = 2025;
}
RTC.writeTime();
}
}
static uint32_t lastTime = 0; // millis() memory
static uint8_t display = 0; // current display mode
static uint8_t cycle = 0;
P.displayAnimate();
if (Mode == 0 ) {
if (P.getZoneStatus(3))
{
switch (display)
{
case 0: // day of week
P.setTextEffect(3, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
dow2str(RTC.dow, szMesg, MAX_MESG);
break;
case 1: // Calendar
P.setTextEffect(3, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
getDate(szMesg);
break;
case 2: // Temperature deg °C
P.setTextEffect(3, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
getTemperature();
dtostrf(t, 3, 1, szMesg);
strcat(szMesg, "^");
break;
case 3: // Temperature deg °F
P.setTextEffect(3, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
dtostrf(f, 3, 1, szMesg);
strcat(szMesg, "_");
break;
case 4: // Relative Humidity
P.setTextEffect(3, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display = 0;
dtostrf(h, 3, 0, szMesg);
strcat(szMesg, "% RH");
break;
}
P.displayReset(3);
}
//adjust the time string if we have to
if (millis() - lastTime >= 1000)
{
lastTime = millis();
getsecond(szsecond);
getTime(szTime, flasher);
flasher = !flasher;
P.displayReset(0);
P.displayReset(1);
P.displayReset(2);
}
}
else if (Mode == 1 ) {
if (millis() - lastTime >= 250)
{
RTC.readTime();
lastTime = millis();
getsecond(szsecond);
getHour(szTime, flasher);
flasher = !flasher;
P.displayReset(0);
P.displayReset(1);
P.displayReset(2);
}
}
else if (Mode == 2 ) {
if (millis() - lastTime >= 250)
{
RTC.readTime();
lastTime = millis();
getsecond(szsecond);
getMinute(szTime, flasher);
flasher = !flasher;
P.displayReset(0);
P.displayReset(1);
P.displayReset(2);
}
}
else if (Mode == 3 ) {
if (millis() - lastTime >= 250)
{
RTC.readTime();
lastTime = millis();
getSec(szsecond);
flasher = !flasher;
P.displayReset(0);
P.displayReset(1);
P.displayReset(2);
}
}
else if (Mode == 4 ) {
if (millis() - lastTime >= 250)
{
RTC.readTime();
lastTime = millis();
getDow(szMes);
getYer(szMeY);
getMon(szMeM);
getDay(szMeD);
flasher = !flasher;
P.displayReset(0);
P.displayReset(1);
P.displayReset(2);
P.displayReset(3);
}
}
else if (Mode == 5 ) {
if (millis() - lastTime >= 250)
{
RTC.readTime();
lastTime = millis();
getYer(szMeY);
getMon(szMeM);
getDay(szMeD);
getDow(szMes);
flasher = !flasher;
P.displayReset(0);
P.displayReset(1);
P.displayReset(2);
P.displayReset(3);
}
}
else if (Mode == 6 ) {
if (millis() - lastTime >= 250)
{
RTC.readTime();
lastTime = millis();
getYer(szMeY);
getMon(szMeM);
getDay(szMeD);
getDow(szMes);
flasher = !flasher;
P.displayReset(0);
P.displayReset(1);
P.displayReset(2);
P.displayReset(3);
}
}
else if (Mode == 7 ) {
if (millis() - lastTime >= 250)
{
RTC.readTime();
lastTime = millis();
getYer(szMeY);
getMon(szMeM);
getDay(szMeD);
getDow(szMes);
flasher = !flasher;
P.displayReset(0);
P.displayReset(1);
P.displayReset(2);
P.displayReset(3);
}
}
else if (Mode == 8 ) {
if (P.getZoneStatus(0) && P.getZoneStatus(1))
{
switch (cycle)
{
case 0: // day of week
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
P.displayZoneText(0, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_UP, PA_SCROLL_DOWN);
P.displayZoneText(1, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_DOWN, PA_SCROLL_UP);
cycle++;
getTime(szMesg);
break;
case 1: // day of week
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
P.displayZoneText(0, szMesg, PA_RIGHT, scrollSpeed, PAUSE_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
P.displayZoneText(1, szMesg, PA_LEFT, scrollSpeed, PAUSE_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
cycle++;
dow2str(RTC.dow, szMesg, MAX_MESG);
break;
case 2: // day of week
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
P.displayZoneText(0, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_UP, PA_SCROLL_DOWN);
P.displayZoneText(1, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_DOWN, PA_SCROLL_UP);
cycle++;
getTime(szMesg);
break;
case 3: // Calendar
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
P.displayZoneText(0, szMesg, PA_RIGHT, scrollSpeed, PAUSE_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
P.displayZoneText(1, szMesg, PA_LEFT, scrollSpeed, PAUSE_TIME, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
cycle++;
getDate(szMesg);
break;
case 4: // day of week
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
P.displayZoneText(0, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_UP, PA_SCROLL_DOWN);
P.displayZoneText(1, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_DOWN, PA_SCROLL_UP);
cycle++;
getTime(szMesg);
break;
case 5: // Temperature deg °C
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
P.displayZoneText(0, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_UP, PA_SCROLL_UP);
P.displayZoneText(1, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_DOWN, PA_SCROLL_DOWN);
cycle++;
getTemperature();
dtostrf(t, 3, 1, szMesg);
strcat(szMesg, "$");
break;
case 6: // Temperature deg °F
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
P.displayZoneText(0, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_DOWN, PA_SCROLL_DOWN);
P.displayZoneText(1, szMesg, PA_CENTER, scrollSpeed, 3000, PA_SCROLL_UP, PA_SCROLL_UP);
cycle++;
dtostrf(f, 3, 1, szMesg);
strcat(szMesg, "&");
break;
case 7: // Temperature deg RH
P.setFont(0, BigFontLower);
P.setFont(1, BigFontUpper);
P.displayZoneText(0, szMesg, PA_CENTER, scrollSpeed, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
P.displayZoneText(1, szMesg, PA_CENTER, scrollSpeed, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
cycle = 0;
dtostrf(h, 3, 1, szMesg);
strcat(szMesg, "% RH");
break;
}
P.displayReset(0);
P.displayReset(1);
}
}
}
void SPEEDischanged()
{
int16_t speed = map(analogRead(SPEED_IN), 0, 1023, 0, 200);
if ((speed >= ((int16_t)P.getSpeed() + SPEED_DEADBAND)) ||
(speed <= ((int16_t)P.getSpeed() - SPEED_DEADBAND)))
{
P.setSpeed(speed);
scrollSpeed = speed;
}
}
void buttonAisPressed()
{
if (NewStateOfbuttonA != StateOfbuttonA)
{
if (NewStateOfbuttonA == 0)
{
buttonA=true;
}
delay(50);
}
StateOfbuttonA = NewStateOfbuttonA;
}
void buttonBisPressed()
{
if (NewStateOfbuttonB != StateOfbuttonB)
{
if (NewStateOfbuttonB == 0) {
buttonB=true;
}
delay(50);
}
StateOfbuttonB = NewStateOfbuttonB;
}
void buttonCisPressed()
{
if (NewStateOfbuttonC != StateOfbuttonC)
{
if (NewStateOfbuttonC == 0) {
buttonC=true;
}
delay(50);
}
StateOfbuttonC = NewStateOfbuttonC;
}
void buttonDisPressed()
{
if (NewStateOfbuttonD != StateOfbuttonD)
{
if (NewStateOfbuttonD == 0) {
buttonD=true;
}
delay(50);
}
StateOfbuttonD = NewStateOfbuttonD;
}