/*
* Use of MAX72XX, DS1307 and DTH22 components to
* print some information on the display.
*
* for more examples:
* https://github.com/MajicDesigns/MD_Parola/tree/main/examples
* https://github.com/MajicDesigns/MD_MAX72XX/tree/main/examples
*/
#define USE_WOKWI 1
// Header file includes
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <DHT.h>
#include <SPI.h>
#include <Wire.h>
#include "Font_Data.h"
// 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
#if USE_WOKWI
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#else
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#endif
#define MAX_CLK_ZONES 2
#define ZONE_SIZE 8
#define MAX_DEVICES (MAX_CLK_ZONES * ZONE_SIZE)
#define ZONE_UPPER 1
#define ZONE_LOWER 0
//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
//#define MAX_DEVICES 8 // Define the number of displays connected
#define CLK_PIN 13 // CLK or SCK
#define DATA_PIN 11 // DATA or MOSI
#define CS_PIN 10 // CS or SS
#define SPEED_TIME 75 // Speed of the transition
#define PAUSE_TIME 0
#define MAX_MESG 50
// These are for the clock
#define DS1307_ADDRESS 0x68
// Hardware adaptation parameters for scrolling
bool invertUpperZone = false;
// These are for the temperature
#define DHTPIN 2
#define TIMEDHT 2000
#if USE_WOKWI
#define DHTTYPE DHT22
#else
#define DHTTYPE DHT11
#endif
bool FlagStartComplete = false;
// Global variables
uint8_t wday, mday, month, year;
uint8_t hours, minutes, seconds;
char szMesgL[MAX_MESG + 1] = "";
char szMesgH[MAX_MESG + 1] = "";
float humidity, celsius, fahrenheit;
//7, 24, 24, 128, 192, 64, 64, 64, // 170 - '* upper'
//7, 0, 0, 15, 31, 16, 16, 16, // 42 - '* lower
//7, 24, 24, 192, 192, 64, 64, 64, // 164 - '$ upper'
//7, 0, 0, 31, 31, 2, 2, 2, // 36 - '$ lower'
uint8_t degCupper[] = { 7, 24, 24, 128, 192, 64, 64, 64 }; // Deg C
uint8_t degClower[] = { 7, 0, 0, 15, 31, 16, 16, 16 }; // Deg C
uint8_t degFupper[] = { 7, 24, 24, 192, 192, 64, 64, 64 }; // Deg F
uint8_t degFlower[] = { 7, 0, 0, 31, 31, 2, 2, 2 }; // Deg F
uint8_t clear = 0x00;
uint32_t timerDHT = TIMEDHT;
DHT dht(DHTPIN, DHTTYPE);
// Hardware SPI connection
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
void beginDS1307()
{
// Read the values (date and time) of the DS1307 module
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(clear);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 0x07);
seconds = bcdToDec(Wire.read());
minutes = bcdToDec(Wire.read());
hours = bcdToDec(Wire.read() & 0xff);
wday = bcdToDec(Wire.read());
mday = bcdToDec(Wire.read());
month = bcdToDec(Wire.read());
year = bcdToDec(Wire.read());
}
uint8_t decToBcd(uint8_t value)
{
return ((value / 10 * 16) + (value % 10));
}
uint8_t bcdToDec(uint8_t value)
{
return ((value / 16 * 10) + (value % 16));
}
// Code for reading clock time
void getTime(char *psz, bool f = true)
{
sprintf(psz, "%02d%c%02d", hours, (f ? ':' : ' '), minutes);
}
// Code for reading clock date
void getDate(char *psz)
{
char szBuf[10];
sprintf(psz, "%d %s %04d", mday , mon2str(month, szBuf, sizeof(szBuf) - 1), (year + 2000));
}
// Code for get Temperature
void getTemperature()
{
// Wait for a time between measurements
if ((millis() - timerDHT) > TIMEDHT) {
// Update the timer
timerDHT = millis();
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
humidity = dht.readHumidity();
// Read temperature as Celsius (the default)
celsius = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
fahrenheit = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again)
if (isnan(humidity) || isnan(celsius) || isnan(fahrenheit)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
}
}
// Get a label from PROGMEM into a char array
char *mon2str(uint8_t mon, char *psz, uint8_t len)
{
static const __FlashStringHelper* str[] =
{
F("Jan"), F("Feb"), F("Mar"), F("Apr"),
F("May"), F("Jun"), F("Jul"), F("Aug"),
F("Sep"), F("Oct"), F("Nov"), F("Dec")
};
strncpy_P(psz, (const char PROGMEM *)str[mon - 1], len);
psz[len] = '\0';
return (psz);
}
char *dow2str(uint8_t code, char *psz, uint8_t len)
{
static const __FlashStringHelper* str[] =
{
F("Sunday"), F("Monday"), F("Tuesday"),
F("Wednesday"), F("Thursday"), F("Friday"),
F("Saturday")
};
strncpy_P(psz, (const char PROGMEM *)str[code - 1], len);
psz[len] = '\0';
return (psz);
}
void createHString(char *pH, char *pL)
{
for (; *pL != '\0'; pL++)
*pH++ = *pL | 0x80; // offset character
*pH = '\0'; // terminate the string
}
void setup(void)
{
Serial.begin(9600);
Wire.begin();
invertUpperZone = (HARDWARE_TYPE == MD_MAX72XX::GENERIC_HW || HARDWARE_TYPE == MD_MAX72XX::PAROLA_HW);
// initialise the LED display
P.begin(MAX_CLK_ZONES);
// Set up zones for 2 halves of the display
P.setZone(ZONE_LOWER, 0, ZONE_SIZE - 1);
P.setZone(ZONE_UPPER, ZONE_SIZE, MAX_DEVICES - 1);
P.setFont(numeric7SegDouble);
P.setCharSpacing(P.getCharSpacing() * 2); // double height --> double spacing
if (invertUpperZone)
{
P.setZoneEffect(ZONE_UPPER, true, PA_FLIP_UD);
P.setZoneEffect(ZONE_UPPER, true, PA_FLIP_LR);
P.displayZoneText(ZONE_LOWER, szMesgL, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(ZONE_UPPER, szMesgH, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
}
else
{
P.displayZoneText(ZONE_LOWER, szMesgL, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(ZONE_UPPER, szMesgH, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
}
P.addChar(170, degCupper);
P.addChar(42, degClower);
P.addChar(164, degFupper);
P.addChar(36, degFlower);
dht.begin();
}
void loop(void)
{
static uint32_t lastTime = 0; // Memory (ms)
static uint8_t display = 0; // Current display mode
static bool flasher = false; // Seconds passing flasher
beginDS1307();
getTemperature();
P.displayAnimate();
if (P.getZoneStatus(ZONE_LOWER) && P.getZoneStatus(ZONE_UPPER))
{
#if USE_WOKWI
switch (display)
{
case 0: // Temperature deg Celsius
P.setPause(ZONE_LOWER, 1000);
P.setTextEffect(ZONE_LOWER, PA_SCROLL_LEFT, PA_SCROLL_UP);
P.setPause(ZONE_UPPER, 1000);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_RIGHT, PA_SCROLL_UP);
display++;
dtostrf(celsius, 3, 1, szMesgL);
strcat(szMesgL, "*");
createHString(szMesgH, szMesgL);
break;
case 1: // Temperature deg Fahrenheit
P.setTextEffect(ZONE_LOWER, PA_SCROLL_UP, PA_SCROLL_DOWN);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_UP, PA_SCROLL_DOWN);
display++;
dtostrf(fahrenheit, 3, 1, szMesgL);
strcat(szMesgL, "$");
createHString(szMesgH, szMesgL);
break;
case 2: // Humidity
P.setTextEffect(ZONE_LOWER, PA_SCROLL_DOWN, PA_SCROLL_LEFT);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_DOWN, PA_SCROLL_RIGHT);
display++;
dtostrf(humidity, 3, 1, szMesgL);
strcat(szMesgL, "%");
createHString(szMesgH, szMesgL);
break;
case 3: // Clock
if(!FlagStartComplete){
P.setTextEffect(ZONE_LOWER, PA_PRINT, PA_NO_EFFECT);
P.setTextEffect(ZONE_UPPER, PA_PRINT, PA_NO_EFFECT);
P.setPause(ZONE_LOWER, 0);
P.setPause(ZONE_UPPER, 0);
FlagStartComplete = true;
}
if ((millis() - lastTime) >= 1000)
{
lastTime = millis();
getTime(szMesgL, flasher);
createHString(szMesgH, szMesgL);
flasher = !flasher;
}
if ((seconds == 00) && (seconds <= 30)) {
display++;
P.setTextEffect(ZONE_LOWER, PA_PRINT, PA_NO_EFFECT);
P.setTextEffect(ZONE_UPPER, PA_PRINT, PA_NO_EFFECT);
FlagStartComplete = false;
}
break;
case 4: // Day of week
P.setTextEffect(ZONE_LOWER, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
display++;
dow2str(wday, szMesgL, MAX_MESG);
createHString(szMesgH, szMesgL);
break;
default: // Calendar
P.setTextEffect(ZONE_LOWER, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_RIGHT, PA_SCROLL_RIGHT);
display = 0;
getDate(szMesgL);
createHString(szMesgH, szMesgL);
break;
}
#else
switch (display)
{
case 0: // Temperature deg Celsius
P.setPause(ZONE_LOWER, 1000);
P.setTextEffect(ZONE_LOWER, PA_SCROLL_LEFT, PA_SCROLL_UP);
P.setPause(ZONE_UPPER, 1000);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_LEFT, PA_SCROLL_UP);
display++;
dtostrf(celsius, 3, 1, szMesgL);
strcat(szMesgL, "*");
createHString(szMesgH, szMesgL);
break;
case 1: // Temperature deg Fahrenheit
P.setTextEffect(ZONE_LOWER, PA_SCROLL_UP, PA_SCROLL_DOWN);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_UP, PA_SCROLL_DOWN);
display++;
dtostrf(fahrenheit, 3, 1, szMesgL);
strcat(szMesgL, "$");
createHString(szMesgH, szMesgL);
break;
case 2: // Humidity
P.setTextEffect(ZONE_LOWER, PA_SCROLL_DOWN, PA_SCROLL_LEFT);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_DOWN, PA_SCROLL_LEFT);
display++;
dtostrf(humidity, 3, 1, szMesgL);
strcat(szMesgL, "%");
createHString(szMesgH, szMesgL);
break;
case 3: // Clock
if(!FlagStartComplete){
P.setTextEffect(ZONE_LOWER, PA_PRINT, PA_NO_EFFECT);
P.setTextEffect(ZONE_UPPER, PA_PRINT, PA_NO_EFFECT);
P.setPause(ZONE_LOWER, 0);
P.setPause(ZONE_UPPER, 0);
FlagStartComplete = true;
}
if ((millis() - lastTime) >= 1000)
{
lastTime = millis();
getTime(szMesgL, flasher);
createHString(szMesgH, szMesgL);
flasher = !flasher;
}
if ((seconds == 00) && (seconds <= 30)) {
display++;
P.setTextEffect(ZONE_LOWER, PA_PRINT, PA_NO_EFFECT);
P.setTextEffect(ZONE_UPPER, PA_PRINT, PA_NO_EFFECT);
FlagStartComplete = false;
}
break;
case 4: // Day of week
P.setTextEffect(ZONE_LOWER, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
dow2str(wday, szMesgL, MAX_MESG);
createHString(szMesgH, szMesgL);
break;
default: // Calendar
P.setTextEffect(ZONE_LOWER, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
P.setTextEffect(ZONE_UPPER, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display = 0;
getDate(szMesgL);
createHString(szMesgH, szMesgL);
break;
}
#endif
P.displayReset();
// synchronise the start
P.synchZoneStart();
}
}