I am so close to getting this working 100%
Everything works but the matrix when scrolling the date is all messed up. The correct details are obtained from the NTP as I can serial print them to check.
I know this is my user error, but I can find what I have done wrong
The date scrolls something like " Mda 26 e 2023
it should be "Monday 26 june 2023"
I also have to work on the flashing dots between the hh:mm as they flash like every 3 seconds and stay on longer than expected
Any help most welcome
#ifdef ESP8266
#include <ESP8266WiFi.h> // Built-in for ESP8266
#else
#include <WiFi.h> // Built-in for ESP32
#endif
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
//#include <SPI.h> // use these for Arduino
//#include <time.h> // use these for Arduino /* time_t, struct tm, time, localtime, strftime */
#include "myconfig.h"
#include "Font_Data.h"
#include <DHT.h> // for temp & humid readings
#define SPEED_TIME 75
#define PAUSE_TIME0 0
#define PAUSE_TIME 2000
#define PAUSE_TIME1 0
#define MAX_MESG 30 // has to be 1 longer than your longest message
// These are for the temperature
#define DHT_PIN 2 // D4
#define DHT_TYPE DHT11 // we are using the blue one. DHT22 white one would be better
#define TIME_DHT 28000 // time between readings its a slow sensor. not so much in this code
// but to fast a reading and it will fail make this time very slightly less than the scrolling interval
// to show the temp & humid this prevents the sensor heating itself by reading it too fast. this made
// a massive differance in the offset required
// DHT11 temp +/- temp 5%, humid +/- 5% DHT22 +/- temp 2%, humid 2%. both repeatability +/- 1%.
// Dont use these is you want to send a rocket into space
// more reading here https://www.kandrsmith.org/RJS/Misc/Hygrometers/calib_dht22_dht11_sht71.html
int buzzer = 5; //D1 ***** Dont use D3/gpio0 as it grounds during booting and the buzzer will sound
float humidity, celsius, fahrenheit; // to store decimal numbers like 66.7
// char need to store info for the matrix
char hm_Char[10];
char yearChar[5], dayChar[30], weekdayChar[20], fullChar[30], dateChar[6];
char hour_Char[3], min_Char[3], sec_Char[3];
char szTime[7]; // hh:mm
char szMesg[MAX_MESG + 1] = "";
char Temp[4];
char Humid[4];
//const char *psz;
byte hour_Int, min_Int, sec_Int, dd_Int;
byte humidityalarm=66;
// bytes can store 0 to 255, an int can store -32,768 to 32,767 so bytes take up less memory
String TimeFormat_str, Date_str; // to select M or I for 24hrs or 12hr clock
// parola effects
textEffect_t scrollEffect = PA_SCROLL_LEFT;
textPosition_t scrollAlign = PA_LEFT;
uint8_t degC[] = { 6, 3, 3, 56, 68, 68, 68 }; // Deg C font as this is not in our font files
uint8_t degF[] = { 6, 3, 3, 124, 20, 20, 4 }; // Deg F font as this is not in our font files
uint32_t timerDHT = TIME_DHT;
//********* USER SETTINGS - CHANGE YOUR SETTING BELOW **********
String Time_display = "M"; // M for metric 24hrs clock, I for imperial 12hrs clock
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // See video number 45 you have a choice of 4 settings
#define MAX_DEVICES 4 // As we have a 4x1 matrix
#define MAX_NUMBER_OF_ZONES 1
//Connections to a Wemos D1 Mini
const uint8_t CLK_PIN = 14; // or SCK D5
const uint8_t DATA_PIN = 13; // or MOSI D7
const uint8_t CS_PIN = 15; // or SS D8
//MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); // I have called my instance "P"
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
byte Time_dim = 20; // what time to auto dim the display 24 hrs clock
byte Time_bright = 7; // what time to auto dim the display 24 hr clock
byte Max_bright = 3; // max brightness of the maxtrix
byte Min_bright = 0; // minimum brightness of the matrix 0-15
// *************** temp humid sensor crude offsets ***************************//
// as the DHT11 is not very accurate and inside a tube its effected by the heat from the matrix and esp8266
// this is very crude but works once the clock has been on for a while and up to normal temp. The temp
// change if you have been uploading code to the chip. also going to drill some vent holes in the top and
// bottom of the case ********* adjust to your offset or comment out ******
float Ctemp_Offset = 0.6;
float Ftemp_Offset = 32.72; // adjust to your offset or comment out (1°C × 9/5) + 32 = 33.8°F
float Humid_Offset = 4.0; // humid offset
// ********** Change to your WiFi credentials in the myconfig.h file, select your time zone and adjust your NTS **********
const char *ssid = WIFI_SSID; // SSID of local network
const char *password = WIFI_PW; // Password on network
const char *Timezone = "CET-1CEST,M3.5.0,M10.5.0/3"; // Rome/italy see link below for more short codes -1
const char *NTP_Server_1 = "1.uk.pool.ntp.org";
const char *NTP_Server_2 = "2.uk.pool.ntp.org";
/* Useful links
Zones https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
NTP world servers https://www.ntppool.org/en/
World times https://www.timeanddate.com/time/zones/
https://cplusplus.com/reference/ctime/strftime/
*/
//Messages to display
// char *Message(uint8_t code, char *psz, uint8_t len) {
// static const __FlashStringHelper *str[] = {
// F("note 1"),
// F("note 2"), // just short so we can show effects with all text on the matrix
// F("Message 3 this is a long 100 character message, just adjust the char above ******************** 100"), // 100
// };
// https://www.lettercount.com/
// strncpy_P(psz, (const char PROGMEM *)str[code - 1], len); //Copy characters from string to program memory
// psz[len] = '\0';
// return (psz);
// }
//********* END OF USER SETTINGS**********/
DHT dht(DHT_PIN, DHT_TYPE); // set up the dht
//Get Temperature & humidity
void getTemperature() { // only read if our TIME_DHT has past.
// Wait for a time between measurements
if ((millis() - timerDHT) > TIME_DHT) {
// Update the timer
Serial.println("reading temp");
Serial.println(dht.readTemperature());
Serial.println((millis() - timerDHT));
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();
humidity = humidity - Humid_Offset;
if (humidity >= 100) {
humidity = 99; // so that the matrix does not overflow due to the font size of 4x7
}
// Read temperature as Celsius (the default)
celsius = dht.readTemperature();
celsius = celsius - Ctemp_Offset;
// celsius=celsius+90; no need to check temp is greater than 100C as it will fit on the matrix at font size 4x7
// Read temperature as Fahrenheit (is Fahrenheit = true)
fahrenheit = dht.readTemperature(false);
fahrenheit = fahrenheit - Ftemp_Offset;
// 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;
}
}
}
void getTime(char *psz, bool f = true) // get the time and pull out the parts we need hours, minutes, seconds
{
min_Int = atoi(min_Char); // convert char to int
hour_Int = atoi(hour_Char);
sec_Int = atoi(sec_Char);
// Serial.println(hour_Int); // to check reading etc if you wish
// Serial.println(hour_Char);
// Serial.println(min_Int);
// Serial.println(min_Char);
// Serial.println(sec_Int);
// Serial.println(sec_Char);
// Serial.println("");
//combine text and variables into a string for output to the Serial Monitor/matrix
sprintf(psz, "%02d%c%02d", hour_Int, (f ? ':' : ' '), min_Int); // add the seperating two dots between the Hours and minutes
// d or i for signed decimal integer, u – unsigned decimal integer, c for Character s a string of characters
}
// set up messages
// void getMessage1(char *psz) {
// char szBuf1[33]; // must be at least 1 greater than the message length
// sprintf(psz, "%s", Message(1, szBuf1, sizeof(szBuf1) - 1));
// }
// void getMessage2(char *psz) {
// char szBuf2[33]; // must be at least 1 greater than the message length
// sprintf(psz, "%s", Message(2, szBuf2, sizeof(szBuf2) - 1));
// }
// void getMessage3(char *psz) {
// char szBuf3[101]; // Length of message plus 1
// sprintf(psz, "%s", Message(3, szBuf3, sizeof(szBuf3) - 1));
// }
// void shortDate(char *psz) { // Formats date as: Fri Mar 17 11:28:33 2023
// sprintf(psz, "%s", dayChar);
// //sprintf(psz, "%02d%c%02d", hour_Int,':', minInt);
// }
// void LongDate(char *psz) { // Formats date as: Friday March 17 11:28 2023
// sprintf(psz, "%s", fullChar);
// //sprintf(psz, "%02d%c%02d", hour_Int,':', minInt);
// Serial.println(fullChar);
// }
// https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm
void setup() {
Serial.begin(115200);
StartWiFi();
digitalWrite(buzzer, HIGH); // so the buzzer is off during boot
configTime(0, 0, NTP_Server_1, NTP_Server_2);
setenv("TZ", Timezone, 1);
TimeFormat_str = Time_display; // using the string above to select the type of clock 24 hrs or 12hr
UpdateLocalTime(TimeFormat_str);
pinMode(buzzer, OUTPUT); // set buzzer pin as output
P.begin(0); // start Parola
P.displayClear(0);
P.setInvert(false);
P.setZone(0, 0, 3); // for HH:MM zone 0 0,1,3 when not using PA_FLIP_UD ect below
P.setFont(0, nullptr);
P.setIntensity(Max_bright); // brightness of matrix
P.displayZoneText(0, szMesg, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
//P.displayZoneText(0, fullChar, PA_LEFT, SPEED_TIME, PAUSE_TIME1, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
// P.displayZoneText(0, Temp, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
// P.displayZoneText(0, Humid, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
//P.displayZoneText(1, szTime, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
// we dont need this now but here how to turn the matrix 180 degrees
// you also would have to change the text direction in the effects below
// P.setZone(0, 0, 3);
// P.setZoneEffect(0, true, PA_FLIP_UD); // will make it upside down but reversed text
// P.setZoneEffect(0, true, PA_FLIP_LR); // make the digits the not mirrored
// P.setZoneEffect(1, true, PA_FLIP_UD);
// P.setZoneEffect(1, true, PA_FLIP_LR);
P.addChar('$', degC); // where ever we put $ the ⁰C will appear with the superscript like o
P.addChar('&', degF); // where ever we put $ the ⁰F will appear with the superscript like o
dht.begin(); // start the temp/humid sensor
//delay(1000);
}
void loop() {
getTemperature(); // get temp/humid reading
UpdateLocalTime(TimeFormat_str); // update time
// Serial.println(hour_Int);
// Serial.println(hour_Char);
// Serial.println(minInt);
// Serial.println(min_Char);
// Serial.println(sec_Int);
// Serial.println(sec_Char);
// Serial.println("");
if (hour_Int >= Time_dim || hour_Int <= Time_bright) P.setIntensity(Min_bright); // based using 24hr clock to save code dim at 20:00 bright at 07:00
else {
P.setIntensity(Max_bright); // matrix brightness
}
static uint32_t lastTime = 0; // Memory (ms)
static uint8_t display = 0; // Current display mode
static bool flasher = false; // Seconds passing flasher
P.displayAnimate();
if (P.getZoneStatus(0)) {
switch (display) {
case 0: // HH:MM format like 13:55 in a new font
//P.setPause(0, 2000);
P.setFont(0, F4x7straight);
P.displayZoneText(0, szMesg, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT); // otherwise you dont see the hour all change at say 19:59 to 20:00
if (millis() - lastTime >= 1000) {
lastTime = millis();
getTime(szMesg, flasher);
flasher = !flasher;
}
// time delay to show the time for a while
if (sec_Int == 15 && sec_Int <= 45) { // wait for both seconds to have equal 15 and 45 so this "time"
// delay can vary a bit the first time around. dont use 00 and 30
display++;
//P.setTextEffect(0, PA_PRINT, PA_SCROLL_LEFT);
//P.displayZoneText(0, szTime, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
}
break;
case 1: // scroll short date, Wed Mar 15 11:11:23 2023
P.setFont(0, F4x7straight);
Serial.println(dayChar); // to check that we have what we want to show
P.displayZoneText(0, dayChar, PA_LEFT, SPEED_TIME, PAUSE_TIME1, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
break;
case 2: // Temperature deg Celsius
P.setFont(0, F4x7straight);
dtostrf(celsius, 3, 1, szMesg); // turns a float into a string, float, min width, num digits after dec, where to store it
strcat(szMesg, "$"); // appends the string to the char
display++;
P.displayZoneText(0, szMesg, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_SCROLL_DOWN, PA_SCROLL_UP_RIGHT);
break;
case 3: // Relative Humidity
P.setFont(0, F4x7straight);
dtostrf(humidity, 3, 1, szMesg);
strcat(szMesg, "%rh");
if (humidity >= humidityalarm) { // just an example how to use the buzzer
digitalWrite(buzzer, LOW);
//delay(100); // should not effect time or anomations if it does use millis
}
digitalWrite(buzzer, HIGH); // buzzer off
display = 0; // as its our last case statement
P.displayZoneText(0, szMesg, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_SCROLL_DOWN, PA_SCROLL_UP_RIGHT);
break;
}
P.displayReset(0); // zone 0 update the display
}
}
//#########################################################################################
void UpdateLocalTime(String Format) {
time_t now; // = time(nullptr);
time(&now);
//Serial.println(time(&now));
//Unix time or Epoch or Posix time, time since 00:00:00 UTC 1st jan 1970 minus leap seconds
// an example as of 28 feb 2023 at 16:19 1677597570
//See http://www.cplusplus.com/reference/ctime/strftime/
if (TimeFormat_str == "M") {
strftime(hm_Char, 10, "%H:%M", localtime(&now)); // hours and minutes NO flashing dots 14:06
} else {
strftime(hm_Char, 10, "%I:%M", localtime(&now)); // Formats hour as: 02:06 , 12hrs clock
}
strftime(hour_Char, 4, "%H", localtime(&now)); // hours xx
strftime(min_Char, 4, "%M", localtime(&now)); // minutes xx
strftime(sec_Char, 4, "%S", localtime(&now)); // seconds xx
strftime(yearChar, 5, "%G", localtime(&now)); // year xxxx
strftime(weekdayChar, 14, "%A", localtime(&now)); // day of the week like Thursday
strftime(dayChar, 30, "%c", localtime(&now)); // Formats date as: Thu Aug 23 14:55:02 2001
strftime(fullChar, 30, "%A %d %B %G", localtime(&now)); // format the date how we like it
// convert char to int NOTE based on 24hr time so we can select when to dim the matrix
min_Int = atoi(min_Char);
hour_Int = atoi(hour_Char);
sec_Int = atoi(sec_Char);
// Serial.println(hour_Int);
// Serial.println(hour_Char);
// Serial.println(minInt);
// Serial.println(min_Char);
// Serial.println(sec_Int);
// Serial.println(sec_Char);
// Serial.println("");
}
//#########################################################################################
void StartWiFi() {
/* Set the ESP to be a WiFi-client, otherwise by default, it acts as ss both a client and an access-point
and can cause network-issues with other WiFi-devices on your WiFi-network. */
WiFi.mode(WIFI_STA);
Serial.print(F("\r\nConnecting to SSID: "));
Serial.println(String(ssid));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println(" ");
Serial.print("WiFi connected to address: ");
Serial.print(WiFi.localIP());
Serial.println(" ");
}