Add files via upload

This commit is contained in:
hdrlux
2023-07-24 10:38:11 +02:00
committed by GitHub
parent b84ddd26be
commit db4934d1bc
16 changed files with 1048 additions and 0 deletions

152
Font_data.h Normal file
View File

@@ -0,0 +1,152 @@
/*
Font_data.h
MIT License
Copyright (c) 2023 hdrlux
This is the data file for custom fonts, as alternative for the sysfont defined in MD_MAX72xx_font.cpp
'fontClock' only has chars 32..126, saving a few bytes of memory
The first number of every line is the width of the char, the number of columns
The following numbers are the columns, each byte for 8 LED on/off
*/
#ifndef Font_data_H
#define Font_data_H
//fontClock is adapted sysfont for better time/date display in Dutch language
MD_MAX72XX::fontType_t fontClock[] PROGMEM =
{
'F', 2, 0, 0, 0, 126, 8,
0, // 0
0, // 1
0, // 2
0, // 3
0, // 4
0, // 5
0, // 6
0, // 7
0, // 8
0, // 9
0, // 10
0, // 11
0, // 12
0, // 13
0, // 14
0, // 15
0, // 16
0, // 17
0, // 18
0, // 19
0, // 20
0, // 21
0, // 22
0, // 23
0, // 24
0, // 25
0, // 26
0, // 27
0, // 28
0, // 29
0, // 30
0, // 31
2, 0, 0, // 32 - 'Space'
2, 0, 95, // 33 - '!'
3, 7, 0, 7, // 34 - '"'
5, 20, 127, 20, 127, 20, // 35 - '#'
5, 68, 74, 255, 74, 50, // 36 - '$'
5, 99, 19, 8, 100, 99, // 37 - '%'
5, 54, 73, 73, 54, 72, // 38 - '&'
1, 7, // 39 - '''
3, 62, 65, 65, // 40 - '('
3, 65, 65, 62, // 41 - ')'
//5, 8, 42, 28, 42, 8, // 42 - '*'
3, 2, 5, 2, // 42 - '°' Degree symbol, replacing * char for easy of use'
5, 8, 8, 62, 8, 8, // 43 - '+'
2, 96, 224, // 44 - ','
2, 8, 8, // 45 - '-'
1, 64, // 46 - '.' width 1
5, 96, 16, 8, 4, 3, // 47 - '/'
5, 62, 81, 73, 69, 62, // 48 - '0'
5, 0, 4, 2, 127, 0, // 49 - '1' - width 5
5, 113, 73, 73, 73, 70, // 50 - '2'
5, 65, 73, 73, 73, 54, // 51 - '3'
5, 15, 8, 8, 8, 127, // 52 - '4'
5, 79, 73, 73, 73, 49, // 53 - '5'
5, 62, 73, 73, 73, 48, // 54 - '6'
5, 1, 1, 121, 5, 3, // 55 - european style '7'
5, 54, 73, 73, 73, 54, // 56 - '8'
5, 6, 73, 73, 73, 62, // 57 - '9'
1, 36, // 58 - colon ':' - width 1
2, 108, 236, // 59 - ';'
3, 8, 20, 34, // 60 - '<'
4, 20, 20, 20, 20, // 61 - '='
3, 34, 20, 8, // 62 - '>'
5, 1, 89, 9, 9, 6, // 63 - '?'
5, 62, 65, 93, 89, 78, // 64 - '@'
5, 126, 9, 9, 9, 126, // 65 - 'A'
5, 127, 73, 73, 73, 54, // 66 - 'B'
5, 62, 65, 65, 65, 65, // 67 - 'C'
5, 127, 65, 65, 65, 62, // 68 - 'D'
5, 127, 73, 73, 73, 65, // 69 - 'E'
5, 127, 9, 9, 9, 1, // 70 - 'F'
5, 62, 65, 65, 73, 121, // 71 - 'G'
5, 127, 8, 8, 8, 127, // 72 - 'H'
3, 65, 127, 65, // 73 - 'I'
5, 48, 65, 65, 65, 63, // 74 - 'J'
5, 127, 8, 20, 34, 65, // 75 - 'K'
5, 127, 64, 64, 64, 64, // 76 - 'L'
5, 127, 2, 12, 2, 127, // 77 - 'M'
5, 127, 4, 8, 16, 127, // 78 - 'N'
5, 62, 65, 65, 65, 62, // 79 - 'O'
5, 127, 9, 9, 9, 6, // 80 - 'P'
5, 62, 65, 65, 97, 126, // 81 - 'Q'
5, 127, 9, 25, 41, 70, // 82 - 'R'
5, 70, 73, 73, 73, 49, // 83 - 'S'
5, 1, 1, 127, 1, 1, // 84 - 'T'
5, 63, 64, 64, 64, 63, // 85 - 'U'
5, 31, 32, 64, 32, 31, // 86 - 'V'
5, 63, 64, 56, 64, 63, // 87 - 'W'
5, 99, 20, 8, 20, 99, // 88 - 'X'
5, 3, 4, 120, 4, 3, // 89 - 'Y'
5, 97, 81, 73, 69, 67, // 90 - 'Z'
3, 127, 65, 65, // 91 - '['
5, 3, 4, 8, 16, 96, // 92 - '\'
3, 65, 65, 127, // 93 - ']'
5, 4, 2, 1, 2, 4, // 94 - '^'
4, 128, 128, 128, 128, // 95 - '_'
3, 1, 2, 4, // 96 - '`'
4, 56, 68, 68, 124, // 97 - 'a'
4, 127, 68, 68, 56, // 98 - 'b'
4, 56, 68, 68, 68, // 99 - 'c'
4, 56, 68, 68, 127, // 100 - 'd'
4, 56, 84, 84, 88, // 101 - 'e'
4, 4, 126, 5, 1, // 102 - 'f'
4, 24, 164, 164, 124, // 103 - 'g'
4, 127, 4, 4, 120, // 104 - 'h'
3, 0, 125, 0, // 105 - 'i' width 3
3, 132, 133, 124, // 106 - 'j'
4, 127, 16, 40, 68, // 107 - 'k'
3, 127, 0, 0, // 108 - 'l' width 3
5, 124, 4, 120, 4, 120, // 109 - 'm'
4, 124, 4, 4, 120, // 110 - 'n'
4, 56, 68, 68, 56, // 111 - 'o'
4, 252, 36, 36, 24, // 112 - 'p'
4, 24, 36, 36, 252, // 113 - 'q'
4, 124, 4, 4, 8, // 114 - 'r'
4, 88, 84, 84, 52, // 115 - 's'
4, 4, 127, 68, 0, // 116 - 't' width 4
4, 60, 64, 64, 124, // 117 - 'u'
4, 28, 32, 64, 124, // 118 - 'v'
5, 60, 64, 48, 64, 60, // 119 - 'w'
4, 108, 16, 16, 108, // 120 - 'x'
5, 125, 0, 132, 133, 124, // 121 - 'ij' dutch 'y'
4, 100, 84, 84, 76, // 122 - 'z'
4, 8, 54, 65, 65, // 123 - '{'
1, 127, // 124 - '|'
4, 65, 65, 54, 8, // 125 - '}'
4, 2, 1, 2, 1, // 126 - '~'
};
// option: add multiple fonts definitions
#endif // Font_data_H

69
GPS_RTC_Clock.cpp Normal file
View File

@@ -0,0 +1,69 @@
/*
GPS_RTC_Clock.cpp
MIT License
Copyright (c) 2023 hdrlux
*/
#include <Arduino.h> // for all the 'standard' Arduino stuff
#include "GPS_RTC_Clock.h"
#include "RTC_com.h"
#include "GPS_com.h"
#include "LocalDateTime.h"
bool NewSec = false;
bool NewMin = false;
bool NewHour = false;
time_t Loc_t = 0;
byte last_min = -1;
byte last_5min = -1;
byte last_hour = -1;
bool ResetMCU = true;
void GPS_RTC_Clock_setup() {
RTC_LED_setup();
RTC_setup();
GPS_setup();
TZ_setup();
}
void GPS_RTC_Clock_loop() {
GPS_loop();
RTC_loop();
}
void Sec_Flip(time_t t) { // called from RTC_com
Loc_t = TZ_Sec(t); // convert UTC to Local Time
NewSec = true; // set "Ready to print"
if (ResetMCU) { // set init time
ResetMCU = false;
last_5min = minute(Loc_t) / 5;
last_hour = hour(Loc_t);
}
if (last_min != minute(Loc_t)) { // Check new minute
MinFlip();
}
}
void MinFlip() {
last_min = minute(Loc_t);
if (last_5min != last_min / 5) { // Check new 5-min
Min5Flip();
}
if (last_hour != hour(Loc_t)) { // Check new hour
HourFlip();
}
NewMin = true; // set "Ready to print"
}
void Min5Flip() {
last_5min = last_min / 5;
GPS_ON(); // try sync RTC-GPS
}
void HourFlip() { // do something on the flip of the RTC hour [UTC time]
last_hour = hour(Loc_t);
NewHour = true; // set "Ready to print"
}
//End

58
GPS_RTC_Clock.h Normal file
View File

@@ -0,0 +1,58 @@
/*
GPS_RTC_Clock.h
MIT License
Copyright (c) 2023 hdrlux
main local Lib with Clock control, creating 'long term ±0.1 sec accurate Arduino clock'
tested hardware:
-Arduino Nano 5V ATmega328P
-see local *.h files for other hardware
main functions:
- Sync RTC with GPS in UTC using interrupt
- LED blinking in sync with GPS PPS, to check accuracy
- LED ON if RTC synced in last hours
- Convert to Local Date/Time
- Setting flag for printing new time to display, using interrupt
*/
/***** Notice *******************************************
!! GPS & USB use the same serial port !!
you will have to disconnect the Arduino RX pin 0 from
the GPS TX pin, to upload a new sketch over USB
->remove GPS PCB or Nano PCB from main PCB/breadboard
********************************************************/
#ifndef GPS_RTC_Clock_H
#define GPS_RTC_Clock_H
#include "ISOWeekNumber.h" // pass to main ino
#include "Local_names.h" // pass to main ino
#include "RTC_com.h"
extern bool NewSec; // pass to main ino = second ready to print
extern bool NewMin; // pass to main ino = minute ready to print
extern bool NewHour; // pass to main ino = Hour ready to print
extern time_t Loc_t; // pass to main ino = Local timestamp
// call from main ino, GPS_RTC_Clock.h
void GPS_RTC_Clock_setup();
// call from main ino, GPS_RTC_Clock.h
void GPS_RTC_Clock_loop();
// process sec from RTC
void Sec_Flip(time_t t);
// do something on reboot & the flip of the RTC minute [timezone independent]
void MinFlip();
// do something every 5 minutes
void Min5Flip();
// do something every hour, on the hour
void HourFlip();
#endif // GPS_RTC_Clock_H

96
GPS_RTC_Clock_33.ino Normal file
View File

@@ -0,0 +1,96 @@
/* GPS_RTC_Clock
MIT License
Copyright (c) 2023 hdrlux
https://www.instructables.com/01-Sec-Accurate-RTC-GPS-Wall-Clock-Arduino-96x8-LE/
https://github.com/hdrlux/GPS_RTC_Clock
*/
#include "GPS_RTC_Clock.h"
#include "LED_96x8_matrix.h"
char TempBuf[5] = "99.9"; // demo value
char HumiBuf[3] = "99"; // demo value
void setup() { // the setup function runs once when you press reset or power the board
GPS_RTC_Clock_setup(); // first in setup
Matrix_setup(); // LED display
Serial.begin(9600); // = 9600, must be same as GPS for debug
Serial.println(); // flush serial
Serial.println("-Arduino Reboot-"); // debug
}
void loop() { // the loop function runs over and over again forever
GPS_RTC_Clock_loop(); // first in loop
PrintSec();
PrintHour();
}
void PrintSec() { // print time if new second
if (NewSec) { //
NewSec = false; // remove flag, do only once every sec
char startmarker = '<';
char endmarker = '>';
Serial.print(startmarker); // for remote display via RS485
Serial.print(Loc_t); // for remote display via RS485
Serial.println(endmarker); // for remote display via RS485
char TimeBuf[9]; // time string buffer, max n-1 char
snprintf(TimeBuf, sizeof(TimeBuf), "%.2u:%.2u:%.2u", // https://cplusplus.com/reference/cstdio/printf/
hour(Loc_t), minute(Loc_t), second(Loc_t)); // time 24h format
Serial.print("Time: "); // debug
Serial.println(TimeBuf); // debug
Print_time_zone(TimeBuf); // print to LED Matrix
PrintMin();
}
}
void PrintMin() {
char TextBuf[12]; // date string buffer, max n-1 char
if (!DIP_Scroll()) { // print date only or 'scrolling' text
snprintf(TextBuf, sizeof(TextBuf), "%s %.2u %s",
dayShortStrLoc(weekday(Loc_t)), day(Loc_t), monthShortStrLoc(month(Loc_t))); // weekday, day, month
} else { // print 'scrolling'
/*** HowTo ******************************************************************
print different info every few seconds, because of limited display space
define the amount of different text lines, must be at least 1
define the amount of seconds each textline is displayed, must be at least 1
define each text line, this may be duplicates of other text lines
*****************************************************************************/
byte text_lines = 3; // amount of different text lines printed
byte text_timer = 5; // amount of seconds to next text
byte text_counter(((Loc_t / text_timer) % text_lines) + 1); // range = 1..n
if (text_counter == 1) {
snprintf(TextBuf, sizeof(TextBuf), "%.2u%s%u",
day(Loc_t), monthShortStrLoc(month(Loc_t)), year(Loc_t)); // day, month, year
} else if (text_counter == 2) {
snprintf(TextBuf, sizeof(TextBuf), "%s W%.2u",
dayShortStrLoc(weekday(Loc_t)), ISOWeekNumber(Loc_t)); // weekday, week
} else if (text_counter == 3) {
snprintf(TextBuf, sizeof(TextBuf), "%s*C %s%%", TempBuf, HumiBuf); // * = ° degrees char in fontClock
} else {
snprintf(TextBuf, sizeof(TextBuf), " Err ");
}
}
Print_date_zone(TextBuf); // print to LED Matrix
if (NewMin) { // print date if new minute
NewMin = false; // remove flag, do only once every min
char DateBuf[21]; // date string buffer, max n-1 char
// long date version
snprintf(DateBuf, sizeof(DateBuf), "%s %.2u-%s-%u W%.2u",
dayShortStrLoc(weekday(Loc_t)), day(Loc_t), monthShortStrLoc(month(Loc_t)), year(Loc_t), ISOWeekNumber(Loc_t));
Serial.print("Date long: "); // debug
Serial.println(DateBuf); // debug
}
}
void PrintHour() { // do if new hour
if (NewHour) { //
NewHour = false; // remove flag, do only once every hour
Serial.println("bring out the Cuckoo ;-)"); // debug
}
}
//End

119
GPS_com.cpp Normal file
View File

@@ -0,0 +1,119 @@
/*
GPS_com.cpp
MIT License
Copyright (c) 2023 hdrlux
*/
#include <Arduino.h> // for all the 'standard' Arduino stuff
#include <NMEAGPS.h> // https://github.com/SlashDevin/NeoGPS
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time
#include "GPS_com.h"
#include "RTC_com.h" // to set the RTC
// add the static modifier to limit visibility of these variables to just this file
static byte GPS_PPS_PIN = 2; // Pin 2 = NANO INT0, GPS PPS interrupt signal on this Pin
static bool gps_on; // flag for GPS ON/OFF
static int pulse_count = 0;
static time_t gps_seconds_t = 0; // GPS time
static byte gps_sats = 0;
volatile bool GPS_sec = false; // flag for GPS-PPS
// set interrupt flag
void gps_interrupt() {
GPS_sec = true;
}
NMEAGPS gps; // This parses the GPS characters
gps_fix fix; // This holds on to the latest values
//#include <GPSport.h> // use only for soft-serial
#define gpsPort Serial
#define GPS_PORT_NAME "Serial"
#define DEBUG_PORT Serial
/***** Notice ********************************************
Edit file \Arduino\libraries\NeoGPS\src\NMEAGPS_cfg.h
UnComment line //#define NMEAGPS_PARSE_ZDA
only process the NMEA sentences GGA, RMC en ZDA
*********************************************************/
/****** Notice *******************************************
Edit file \Arduino\libraries\NeoGPS\src\NeoTime.h
change both instances of the const name 'DAYS_PER_WEEK'
to something else, they conflict with TimeLib.h
*********************************************************/
// Compile check processing for both RMC & ZDA message [both contain date & time]
// Less dependent on GPS chip config
#ifndef NMEAGPS_PARSE_RMC
#error You must define NMEAGPS_PARSE_RMC in NMEAGPS_cfg.h!
#endif
#ifndef NMEAGPS_PARSE_ZDA
#error You must define NMEAGPS_PARSE_ZDA in NMEAGPS_cfg.h!
#endif
#ifndef NMEAGPS_PARSE_GGA
#error You must define NMEAGPS_PARSE_GGA in NMEAGPS_cfg.h!
#endif
const long POSIX_Y2K_offset = 946684800; // = 10957 days = 30 years, NeoTime (GPS) & TimeLib (RTC) library use different Epoch year, 2000 & 1970
const long offset_28y = 883612800; // = 10227 days = 28 years, simple test for 2038 bug
const long offset_26w = 15724800; // = 182 days = 26 weeks, simple test for timezone
void GPS_setup() {
pinMode(GPS_PPS_PIN, INPUT_PULLUP); // enable pullup on interrupt pin
attachInterrupt(digitalPinToInterrupt(GPS_PPS_PIN), gps_interrupt, RISING); // 100ms HIGH at start of second
GPS_sec = false;
gpsPort.begin(9600); // set PC to same baudrate for debug messages
GPS_ON();
}
void GPS_loop() {
if (GPS_sec) { // do after GPS PPS interrupt
GPS_sec = false; // clear flag
GPS_PPS();
}
GPS_read_seconds(); // continue reading buffer
}
void GPS_PPS() { // do something on the flip of the GPS second
if (gps_on) { // do only when needed
pulse_count += 1;
if (pulse_count > 2) { // skip first PPS-Pulses, to make shure time is from satellite
if (gps_seconds_t != 0) { // do only if value is set
// gps_seconds_t += offset_28y; // debug & testing only!
// gps_seconds_t += offset_26w; // debug & testing only!
SetRTC(gps_seconds_t); // sync RTC with GPS
GPS_OFF();
}
}
}
}
void GPS_read_seconds() {
while (gps.available(gpsPort)) {
fix = gps.read();
if (fix.valid.time && fix.valid.date) {
gps_seconds_t = fix.dateTime + POSIX_Y2K_offset; // convert for different epoch year
}
if (fix.valid.satellites) {
gps_sats = fix.satellites;
}
}
}
void GPS_ON() {
if (!gps_on) { // only if NOT on
gps_on = true;
gps_seconds_t = 0; // make shure GPS serial is alive before setting
pulse_count = 0;
Serial.println("GPS: ON"); // debug
}
}
void GPS_OFF() {
if (gps_on) { // only if NOT off
gps_on = false;
Serial.println("GPS: OFF"); // debug
}
}
//End

49
GPS_com.h Normal file
View File

@@ -0,0 +1,49 @@
/*
GPS_com.h
MIT License
Copyright (c) 2023 hdrlux
all GPS time functions, tested hardware:
- Ublox Neo M8N
- External L1-antenna
- set Pin: PPS Interrupt in file GPS_com.cpp [default: 2 = NANO INT0]
*/
/***** Notice ********************************************
Edit file \Arduino\libraries\NeoGPS\src\NMEAGPS_cfg.h
UnComment line //#define NMEAGPS_PARSE_ZDA
only process the NMEA sentences GGA, RMC en ZDA
*********************************************************/
/****** Notice *******************************************
Edit file \Arduino\libraries\NeoGPS\src\NeoTime.h
change both instances of the const 'DAYS_PER_WEEK'
to something else, they conflict with TimeLib.h
*********************************************************/
#ifndef GPS_com_H
#define GPS_com_H
// handle interrupt
void gps_interrupt();
// setup hardware & interrupt from PseudoPPS
void GPS_setup();
// main GPS loop, GPS_com
void GPS_loop();
// do something on the flip of the GPS second
void GPS_PPS();
// get time & amount of satts in FIX
void GPS_read_seconds();
// turn GPS on
void GPS_ON();
// no hardware OFF, just ignore
void GPS_OFF();
#endif // GPS_com_H

35
ISOWeekNumber.cpp Normal file
View File

@@ -0,0 +1,35 @@
/*
ISOWeekNumber.cpp
MIT License
Copyright (c) 2023 hdrlux
*/
#include <Arduino.h> // for all the 'standard' Arduino stuff
#include "ISOWeekNumber.h"
byte ISOdayOfWeek(long days) { // TimeLib.h deviates from ISO8601 with Sunday = 1
// ONLY valid for Epoch year 1970
return (((days + 3) % DAYS_PER_WEEK) + 1); // Mon=1..Sun=7
}
byte ISOWeekNumber(time_t t) { // uses TimeLib.h - valid until 2099
// weeknumber ONLY valid for Mon=1..Sun=7 datelibrary
// calculations valid for ANY Epoch year, only relative dates
long NowDay = elapsedDays(t); // days_since_epoch: now
long CloseThu = NowDay - ISOdayOfWeek(NowDay) + 4; // days_since_epoch: closest Thursday from now
int ISOYear = year(CloseThu * SECS_PER_DAY); // Year for closest Thursday, may be different in first or last week
tmElements_t tm;
tm.Hour = 0;
tm.Minute = 0;
tm.Second = 0;
tm.Day = 4; // 4th day according ISO8601
tm.Month = 1; // Jan
tm.Year = CalendarYrToTm(ISOYear);
time_t JanFour = makeTime(tm); // date: Year-Jan-4
long JanFourDay = elapsedDays(JanFour); // days_since_epoch: Year-Jan-4
int ISOYearDay = NowDay - JanFourDay + ISOdayOfWeek(JanFourDay); // Day number in the ISOYear, range [1..371]
return (((ISOYearDay - 1) / 7) + 1); // Convert Day number in the ISOYear to ISOWeek number, range [1..53]
}
//End

22
ISOWeekNumber.h Normal file
View File

@@ -0,0 +1,22 @@
/*
ISOWeekNumber.h
MIT License
Copyright (c) 2023 hdrlux
function:
- provides weeknumber from Unix time, according ISO8601
*/
#ifndef ISOWeekNumber_H
#define ISOWeekNumber_H
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time valid until year 2099, no 2038 bug !
// ISO8601 compliant, 1 = Monday
byte ISOdayOfWeek(long days);
// ISO8601 compliant, ISOWeek number, range [1..53]
byte ISOWeekNumber(time_t t);
#endif // ISOWeekNumber_H

146
LED_96x8_matrix.cpp Normal file
View File

@@ -0,0 +1,146 @@
/*
LED_96x8_matrix.cpp
MIT License
Copyright (c) 2023 hdrlux
*/
#include <Arduino.h> // for all the 'standard' Arduino stuff
#include <MD_MAX72xx.h> // https://github.com/MajicDesigns/MD_MAX72XX
#include "LED_96x8_matrix.h"
#include "Font_data.h"
#include "RTC_com.h" // for Sync_Err
// Define the number of devices we have in the chain and the hardware interface
#define MAX_DEVICES 12 // total amount of 8x8 LED segments
#define LEFT_DEVICES 5 // amount of 8x8 LED segments in left zone
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // check if you get strange pixel print
#define CLK_PIN 13 // or SCK [is also onboard LED on Nano]
#define DATA_PIN 11 // or MOSI
#define CS_PIN 10 // or SS
#define INIT_BRIGHT 0 // inital intensity
#define DIP1 6 // Scroll DIP switch
#define DIP2 7 // Bright DIP switch
// SPI hardware interface
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Text parameters
#define CHAR_SPACING 1 // pixels between characters
void Matrix_setup() {
mx.begin();
mx.setFont(fontClock); // custom font in Font_data.h
delay(100); // wait init
Bright_time_zone(INIT_BRIGHT);
Bright_date_zone(INIT_BRIGHT);
pinMode(DIP1, INPUT_PULLUP);
pinMode(DIP2, INPUT_PULLUP);
}
void Bright_date_zone(byte value) { // set range 0..15
mx.control(0, MAX_DEVICES - LEFT_DEVICES - 1, MD_MAX72XX::INTENSITY, value);
}
void Bright_time_zone(byte value) { // set range 0..15
mx.control(MAX_DEVICES - LEFT_DEVICES, MAX_DEVICES - 1, MD_MAX72XX::INTENSITY, value);
}
void DIP_Bright() {
if (digitalRead(DIP2) == LOW) {
Bright_time_zone(3);
Bright_date_zone(3);
} else {
Bright_time_zone(INIT_BRIGHT);
Bright_date_zone(INIT_BRIGHT);
}
}
bool DIP_Scroll() {
if (digitalRead(DIP1) == LOW) {
return true;
}
return false;
}
void Print_date_zone(char *my_text) {
char TextBuf[13]; // string buffer, max n-1 char
char err_char;
if (SyncErr) {
err_char = '!'; // sync warning on display
} else {
err_char = ' '; // space
}
snprintf(TextBuf, sizeof(TextBuf), "%c%s", err_char, my_text); // add char before text, 'between' zones
printText(0, MAX_DEVICES - LEFT_DEVICES - 1, TextBuf); // right zone, from input side
}
void Print_time_zone(char *my_text) { // assuming max 8 chars
DIP_Bright();
printText(MAX_DEVICES - LEFT_DEVICES, MAX_DEVICES - 1, my_text); // left zone
}
void printText(uint8_t modStart, uint8_t modEnd, char *pMsg)
/***** HowTo **********************************************
Simple text printing, uses minimal memory
Print the text chars to the LED matrix modules specified.
Message area is padded with blank columns after printing.
**********************************************************/
{
uint8_t state = 0;
uint8_t curLen;
uint16_t showLen;
uint8_t cBuf[8];
int16_t col = ((modEnd + 1) * COL_SIZE) - 1;
mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
do // finite state machine to print the characters in the space available
{
switch (state) {
case 0: // Load the next character from the font table
// if we reached end of message, reset the message pointer
if (*pMsg == '\0') {
showLen = col - (modEnd * COL_SIZE); // padding characters
state = 2;
break;
}
// retrieve the next character form the font file
showLen = mx.getChar(*pMsg++, sizeof(cBuf) / sizeof(cBuf[0]), cBuf);
curLen = 0;
state++;
// !! deliberately fall through to next state to start displaying
case 1: // display the next part of the character
mx.setColumn(col--, cBuf[curLen++]);
// done with font character, now display the space between chars
if (curLen == showLen) {
showLen = CHAR_SPACING;
state = 2;
}
break;
case 2: // initialize state for displaying empty columns
curLen = 0;
state++;
// fall through
case 3: // display inter-character spacing or end of message padding (blank columns)
mx.setColumn(col--, 0);
curLen++;
if (curLen == showLen)
state = 0;
break;
default:
col = -1; // this definitely ends the do loop
}
} while (col >= (modStart * COL_SIZE));
mx.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}
//End

42
LED_96x8_matrix.h Normal file
View File

@@ -0,0 +1,42 @@
/*
LED_96x8_matrix.h
MIT License
Copyright (c) 2023 hdrlux
all LED Display functions, specific hardware:
- MAX7219 with 8x8 LED segment 32x32mm
- unit FC16 = 4 segments on one PCB
- 3 units in series = chain of 12 segments, making a 96x8 display
- if NOT wired correct, ALL LED's are ON in HIGH brightness, using 9W power -> use 10W 5V/2A power source for project safety
- normal operation uses 0,5W for LED display + 0,5W for control board
*/
#ifndef LED_96x8_matrix_H
#define LED_96x8_matrix_H
// main display setup
void Matrix_setup();
// intensity right
void Bright_date_zone(byte value);
// intensity left
void Bright_time_zone(byte value);
// Check DIP switch for brightness control
void DIP_Bright();
// Check DIP switch for text scrolling
bool DIP_Scroll();
// print right
void Print_date_zone(char *my_text);
// print left
void Print_time_zone(char *my_text);
// print chars to specific segment range, NO animations
void printText(uint8_t modStart, uint8_t modEnd, char *pMsg);
#endif // LED_96x8_matrix_H

74
LocalDateTime.cpp Normal file
View File

@@ -0,0 +1,74 @@
/*
LocalDateTime.cpp
MIT License
Copyright (c) 2023 hdrlux
*/
#include <Arduino.h> // for all the 'standard' Arduino stuff
#include <Timezone_Generic.h> // https://github.com/khoih-prog/Timezone_Generic [only this one, DO NOT install all dependencies !!]
#include "LocalDateTime.h"
/*** HowTo ****************************************************************
TimeChangeRule myRule = {abbrev, week, dow, month, hour, offset};
abbrev: is a character string abbreviation for the time zone,
it must be no longer than five characters.
week: is the week of the month that the rule starts.
dow: is the day of the week that the rule starts.
hour: is the hour in local time that the rule starts (0-23).
offset: is the UTC offset in minutes for the time zone being defined.
For convenience, the following symbolic names can be used:
week: First, Second, Third, Fourth, Last
dow: Sun, Mon, Tue, Wed, Thu, Fri, Sat
month: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
**************************************************************************/
/*** Instruction ******************************************************************
Select ONE set of TimeChangeRules below, also for non DST zones
Selection NOT possible during runtime, only at compiletime
The timezones are defined by the offset, in minutes, from UTC.
This means the start of a new minute, and the seconds, are timezone independent !
**********************************************************************************/
// Eastern US time zone [Ottawa, New York, Miami, Nassau]
//TimeChangeRule mySTD = { "EST", First, Sun, Nov, 2, -300 }; // change to UTC - 5 hours
//TimeChangeRule myDST = { "EDT", Second, Sun, Mar, 2, -240 }; // change to UTC - 4 hours
// Western European Time Zone [London = GMT/BST, Dublin = GMT/IST, Lisboa = WET/WEST, Canarias]
//TimeChangeRule mySTD = { "WET ", Last, Sun, Oct, 2, 0 }; // change to UTC
//TimeChangeRule myDST = { "WEST", Last, Sun, Mar, 1, 60 }; // change to UTC + 1 hours
// Central European Time Zone, directive 2000/84/EC [Bruxelles, Paris, Madrid, Roma, Beograd, Warszawa, Stockholm, Berlin]
// All 3 European timezones change at the same time: 01:00 UTC
TimeChangeRule mySTD = { "CET ", Last, Sun, Oct, 3, 60 }; // change to UTC + 1 hours
TimeChangeRule myDST = { "CEST", Last, Sun, Mar, 2, 120 }; // change to UTC + 2 hours
// Eastern European Time Zone [Helsinki, Rīga, Kyiv, Αθήνα]
//TimeChangeRule mySTD = { "EET ", Last, Sun, Oct, 4, 120 }; // change to UTC + 2 hours
//TimeChangeRule myDST = { "EEST", Last, Sun, Mar, 3, 180 }; // change to UTC + 3 hours
// Australia Eastern Time Zone [Canberra, Brisbane, Sydney, Melbourne, Hobart]
//TimeChangeRule mySTD = { "AEST", First, Sun, Apr, 3, 600 }; // change to UTC + 10 hours
//TimeChangeRule myDST = { "AEDT", First, Sun, Oct, 2, 660 }; // change to UTC + 11 hours
// Москва Standard Time (MSK, does not observe DST)
//TimeChangeRule mySTD = {"MSK ", Last, Sun, Oct, 1, 180}; // allways UTC + 3 hours
//TimeChangeRule myDST = {"MSK ", Last, Sun, Mar, 1, 180}; // allways UTC + 3 hours
// Universal Time Coordinated [UTC]
//TimeChangeRule mySTD = {"UTC ", Last, Sun, Oct, 1, 0};
//TimeChangeRule myDST = {"UTC ", Last, Sun, Mar, 1, 0};
Timezone* myTZ;
void TZ_setup() {
myTZ = new Timezone(myDST, mySTD);
}
time_t TZ_Sec(time_t t) { // convert UTC to Local-Time
TimeChangeRule* tcr; // pointer to the time change rule, use to get TZ abbrev
return (myTZ->toLocal(t, &tcr));
}
//End

25
LocalDateTime.h Normal file
View File

@@ -0,0 +1,25 @@
/*
LocalDateTime.h
MIT License
Copyright (c) 2023 hdrlux
function:
- Convert UTC timestamp to Local timestamp, including DST detection [=winter/summer time]
usage:
- uncomment ONE timezone in file LocalDateTime.cpp
*/
#ifndef LocalDateTime_H
#define LocalDateTime_H
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time valid until year 2099, no 2038 bug !
// main TZ setup, GPS_RTC_Clock
void TZ_setup();
// convert UTC to Local-Time
time_t TZ_Sec(time_t t);
#endif // LocalDateTime_H

37
Local_names.cpp Normal file
View File

@@ -0,0 +1,37 @@
/*
Local_names.cpp
MIT License
Copyright (c) 2023 hdrlux
*/
#include <Arduino.h> // for all the 'standard' Arduino stuff
#include "Local_names.h"
#define dt_MAX_STRING_LEN 9
#define dt_SHORT_STR_LEN 3
static char buffer_m[dt_MAX_STRING_LEN + 1];
static char buffer_d[dt_MAX_STRING_LEN + 1];
//const char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec"; // Translate to local Language
//const char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThuFriSat"; // Translate to local Language
const char monthShortNames_Loc[] PROGMEM = "ErrJanFebMrtAprMeiJunJulAugSepOktNovDec"; // Dutch
const char dayShortNames_Loc[] PROGMEM = "ErrZonMaaDinWoeDonVryZat"; // Dutch
char* monthShortStrLoc(uint8_t month) {
for (int i = 0; i < dt_SHORT_STR_LEN; i++)
buffer_m[i] = pgm_read_byte(&(monthShortNames_Loc[i + (month * dt_SHORT_STR_LEN)]));
buffer_m[dt_SHORT_STR_LEN] = 0;
return buffer_m;
}
char* dayShortStrLoc(uint8_t day) {
uint8_t index = day * dt_SHORT_STR_LEN;
for (int i = 0; i < dt_SHORT_STR_LEN; i++)
buffer_d[i] = pgm_read_byte(&(dayShortNames_Loc[index + i]));
buffer_d[dt_SHORT_STR_LEN] = 0;
return buffer_d;
}
//End

20
Local_names.h Normal file
View File

@@ -0,0 +1,20 @@
/*
Local_names.h
MIT License
Copyright (c) 2023 hdrlux
Convert to short local weekday & month names
Workaround because DateStrings.cpp in TimeLib is english only
*/
#ifndef Local_names_H
#define Local_names_H
// Local short month
char* monthShortStrLoc(uint8_t month);
// Local short weekday
char* dayShortStrLoc(uint8_t day);
#endif // Local_names_H

70
RTC_com.cpp Normal file
View File

@@ -0,0 +1,70 @@
/*
RTC_com.cpp
MIT License
Copyright (c) 2023 hdrlux
*/
#include <Arduino.h> // for all the 'standard' Arduino stuff
#include <DS3232RTC.h> // https://github.com/JChristensen/DS3232RTC
#include <TimeLib.h> // https://github.com/PaulStoffregen/Time [valid until year 2099, no 2038 bug]
#include "RTC_com.h"
// add the static modifier to limit visibility of these variables to just this file
static int ledPin_PPS = A6; // Pin for RTC PseudoPPS LED [analog PIN used as digital PIN]
static int ledPin_Sync = A7; // Pin for valid RTC Sync
static byte RTC_1HZ_PIN = 3; // Pin 3 = NANO INT1, RTC provides a 1Hz interrupt signal on this Pin
static time_t last_sync_t = 0; // last sync
static byte sync_err_hours = 4; // set to 4 for DS3231M, may be set to 8 for DS3231SN. Warning if no GPS sync after n hours, possible 0.1 sec error.
bool SyncErr = true;
#include <DS3232RTC.h>
DS3232RTC myRTC;
volatile bool RTC_sec = false; // flag for PseudoPPS
static unsigned long RTCMillis; // blinking timer
void rtc_interrupt() {
RTCMillis = millis();
RTC_sec = true;
}
void RTC_LED_setup() {
pinMode(ledPin_PPS, OUTPUT);
pinMode(ledPin_Sync, OUTPUT);
digitalWrite(ledPin_PPS, LOW); // LED off
digitalWrite(ledPin_Sync, LOW); // LED off
}
void RTC_setup() {
pinMode(RTC_1HZ_PIN, INPUT_PULLUP); // enable pullup on interrupt pin (RTC SQW pin is open drain)
attachInterrupt(digitalPinToInterrupt(RTC_1HZ_PIN), rtc_interrupt, FALLING); // HIGH 500ms after start of second
myRTC.begin();
myRTC.squareWave(DS3232RTC::SQWAVE_1_HZ); // 1 Hz square wave
RTC_sec = false;
}
void RTC_loop() {
if (RTC_sec) { // do after RTC PseudoPPS interrupt, without interrupt NO time/date to display
RTC_sec = false; // clear flag
digitalWrite(ledPin_PPS, LOW); // LED off
time_t now_t = myRTC.get();
if ((last_sync_t + (sync_err_hours * 3600)) < now_t) {
SyncErr = true;
digitalWrite(ledPin_Sync, LOW); // LED off
}
Sec_Flip(now_t); // pass current datetime from RTC in UTC
}
if (millis() > (RTCMillis + 100)) { // do 100ms after PseudoPPS interrupt
digitalWrite(ledPin_PPS, HIGH); // LED on
}
}
void SetRTC(time_t t) {
myRTC.set(t + 1); // the GPS NMEA output is 1 second behind at PPS !!
last_sync_t = t;
SyncErr = false;
digitalWrite(ledPin_Sync, HIGH); // LED on
Serial.println("RTC set by GPS"); // debug
}
//End

34
RTC_com.h Normal file
View File

@@ -0,0 +1,34 @@
/*
RTC_com.h
MIT License
Copyright (c) 2023 hdrlux
all RTC functions, tested hardware:
- DS3231SN via I2C, has only 2-digit years, the Epoch is 2000-01-01. Valid until year 2099
- set Pin: PseudoPPS Interrupt in file RTC_com.cpp [default: 3 = NANO INT1]
- set Pin: PseudoPPS LED in file RTC_com.cpp [default: A3]
*/
#ifndef RTC_com_H
#define RTC_com_H
extern bool SyncErr; // RTC not synced in last n hours
#include "GPS_RTC_Clock.h" // for passing new second
// handle interrupt
void rtc_interrupt();
// setup the pin as output
void RTC_LED_setup();
// setup hardware & interrupt from PseudoPPS
void RTC_setup();
// main RTC loop, RTC_com
void RTC_loop();
// sync RTC from GPS
void SetRTC(time_t t);
#endif // RTC_com_H