I'm building a bell controller for a factory that rings for 2 seconds at shift changes and break times. Using a M5Stack DinMeter (StampS3 with RTC, Encoder) with a Mini 3A Relay Unit.
I don't often code in C, so I'm looking for any suggestions to improve my test code - I'm especially interested in the use of string arrays (or not) and identifying if the current time is in the predefined array list... I understand that strings can cause memory issues, but as I am not manipulating them I am hoping what I have is OK.
The RTC is set using internet NTP servers for accuracy over WiFi. I will eventually modify the code to synchronize at least once a day - I think that should be sufficient.
I plan on creating a GUI using the encoder button later, but for now I have defined the bell times in a string array. There are a few serial print calls for debugging that will be removed later. Because the RTC does not seem to provide access to it's IRQ signal, I'm using a while() loop to monitor for changes to the time seconds both to trigger the relay as close to the exact time as possible and also to minimize wasteful refreshing of the time display on the screen.
If you see anything horrible lol or know a better way to accomplish something I'd be grateful for any advice.
EDIT - I live in the East Coast US, so I will have to include code to accommodate DST time changes... Working on that now.
Thanks!
/**
* @file Shift_Bell
* @author TomKatt
* @brief Shift Bell Test
* @version 0.1
* @date 2025-03-21
*
*
* @Hardwares: M5DinMeter
* @Platform Version: Arduino M5Stack Board Manager v2.1.1
* @Dependent Library:
* M5GFX: https://github.com/m5stack/M5GFX
* M5Unified: https://github.com/m5stack/M5Unified
*/
#if defined(ARDUINO)
#define WIFI_SSID "*******"
#define WIFI_PASSWORD "*******"
#define NTP_TIMEZONE "UTC+4"
#define NTP_SERVER1 "0.pool.ntp.org"
#define NTP_SERVER2 "1.pool.ntp.org"
#define NTP_SERVER3 "2.pool.ntp.org"
#include <WiFi.h>
// Different versions of the framework have different SNTP header file names and
// availability.
#if __has_include(<esp_sntp.h>)
#include <esp_sntp.h>
#define SNTP_ENABLED 1
#elif __has_include(<sntp.h>)
#include <sntp.h>
#define SNTP_ENABLED 1
#endif
#endif
#ifndef SNTP_ENABLED
#define SNTP_ENABLED 0
#endif
#include <M5DinMeter.h>
bool Relay = false; // Relay Status
bool NewMinute = false; // New Minute Flag
int LastSecond; // Last Second Placeholder
const int switchPin = 2; // Relay GPIO Pin
char time_string[10]; // String Array of Shift Times
// Shift Bell Time Triggers
String shift_times[9] = {"05:00:00", "05:10:00",
"06:00:00", "06:10:00",
"09:00:00", "09:10:00",
"12:00:00", "12:40:00",
"19:15:00" };
void setup(void) {
DinMeter.begin();
DinMeter.Display.setRotation(1);
DinMeter.Display.setTextColor(GREEN, BLACK);
DinMeter.Display.setTextDatum(middle_center);
DinMeter.Display.setTextFont(&fonts::Orbitron_Light_32);
DinMeter.Display.setTextSize(1);
DinMeter.Display.setBrightness(32);
Serial.begin(115200);
pinMode(switchPin, OUTPUT); // Set pin 2 to output mode for Relay control
if (!DinMeter.Rtc.isEnabled()) {
Serial.println("RTC not found.");
DinMeter.Display.println("RTC not found.");
for (;;) {
vTaskDelay(500);
}
}
DinMeter.Display.setCursor(0, 10);
Serial.println("RTC found.");
DinMeter.Display.print("BOOTING...\r\n");
// It is recommended to set UTC for the RTC and ESP32 internal clocks.
/* /// setup RTC ( direct setting )
// YYYY MM DD hh mm ss
DinMeter.Rtc.setDateTime( { { 2021, 12, 31 }, { 12, 34, 56 } } );
//*/
/// setup RTC ( NTP auto setting )
DinMeter.Display.print("WiFi");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
DinMeter.Display.print(".");
delay(1000);
}
Serial.println("\r\n WiFi Connected.");
DinMeter.Display.print("ON\r\n");
configTzTime(NTP_TIMEZONE, NTP_SERVER1, NTP_SERVER2, NTP_SERVER3);
#if SNTP_ENABLED
DinMeter.Display.print("NTP");
while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) {
Serial.print('.');
DinMeter.Display.print(".");
delay(1000);
}
#else
delay(1600);
struct tm timeInfo;
while (!getLocalTime(&timeInfo, 1000)) {
Serial.print('.');
};
#endif
Serial.println("\r\n NTP Connected.");
DinMeter.Display.print("ON\r\n");
time_t t = time(nullptr) + 1; // Advance one second.
while (t > time(nullptr))
; /// Synchronization in seconds
DinMeter.Rtc.setDateTime(gmtime(&t));
delay(1000);
DinMeter.Display.clear();
auto tm = localtime(&t); // for local timezone.
LastSecond = tm->tm_sec;
}
void loop(void) {
// MAIN LOOP
static constexpr const char* const wd[7] = {"Sun", "Mon", "Tue", "Wed",
"Thr", "Fri", "Sat"};
auto t = time(nullptr);
auto tm = localtime(&t); // for local timezone.
while (LastSecond == tm->tm_sec) {
// loop until next second
t = time(nullptr);
tm = localtime(&t); // for local timezone.
}
LastSecond = tm->tm_sec;
DinMeter.Display.setCursor(20, 35);
DinMeter.Display.printf("%02d/%02d/%02d", tm->tm_mon + 1, tm->tm_mday, (tm->tm_year + 1900) % 100);
DinMeter.Display.setCursor(35, 80);
DinMeter.Display.printf("%02d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
strftime(time_string, 10, "%T", tm);
Serial.println(time_string);
if(std::find(std::begin(shift_times), std::end(shift_times), time_string) != std::end(shift_times)) {
// Relay ON
digitalWrite(switchPin, HIGH);
Relay = true;
Serial.println("BELL ON");
}
if((tm->tm_sec>1) && (Relay)) {
// Relay OFF after 2 seconds
digitalWrite(switchPin, LOW);
Relay = false;
Serial.println("BELL OFF");
}
}