I am building a shift bell controller for our facility that rings a bell for 3 seconds at eight different times throughout the day. This schedule is fixed (don't need ability to modify or edit) and runs the same each day of the week, so all I need to do is identify the hour and minute to energize a relay for the bell.
This code uses NTP time synchronization and automatically calculates local time and accounts for DST (based on some NTP examples I found). The one-second loop and time schedule logic is what I've come up with so far. It will become apparent that C++ is not my preferred coding language lol (but I'm working on that).
The two primary elements in question here are is the shift_times string array and the use of std::find to compare the current time (formatted into a working string) against the shift_times string array - if the current time matches one of the defined times, the relay is energized and I set a flag (Relay) to monitor that. Because each trigger only needs to ring the bell for 3 seconds, I don't check the hour and only see if the seconds > 2 if the Relay flag is set to de-energize the relay output.
This is the closest thing I could come up with in C++ to match the logic flow in my head - I'm sure there are better ways to accomplish this task. Also, I understand that strings can be quirky in C so they need to be handled carefully - I'm not sure if I am doing that or creating a memory issue down the road lol.
PS - I am using an M5Stack DinMeter board... Just disregard all the board specific stuff for display etc.
EDIT - I am using the one second looping interval so that the scheduled events fire as close to exact time as possible (employees like having the shop schedule match their phones lol). I also have an lcd display with M/D/Y + H:M:S
#include <WiFi.h>
#include <time.h>
#include <M5DinMeter.h>
const int switchPin = 2; // Relay GPIO pin
const char* ssid = "wifi_ssid";
const char* password = "wifi_pw";
const char* NTP_SERVER = "us.pool.ntp.org";
const char* TZ_INFO = "EST5EDT"; // enter your time zone (https://remotemonitoringsystems.ca/time-zone-abbreviations.php)
tm timeinfo;
time_t now;
long unsigned lastNTPtime;
unsigned long lastEntryTime;
// Shift Bell Time Triggers Schedule
String shift_times[8] = {"10:54", "10:55", "10:56", "10:57", "12:00", "12:40", "14:30", "15:30"};
void setup() {
DinMeter.begin();
DinMeter.Display.setRotation(1);
DinMeter.Display.setTextColor(GREEN, BLACK);
DinMeter.Display.setTextDatum(MC_DATUM);
DinMeter.Display.setTextFont(&fonts::FreeMono24pt7b);
DinMeter.Display.setTextSize(1);
DinMeter.Display.setBrightness(32);
DinMeter.Display.clear();
M5.Display.drawString("WiFi...",M5.Display.width() / 2, M5.Display.height() /2);
pinMode(switchPin, OUTPUT); // Set pin to output mode for Relay control
Serial.begin(115200);
Serial.println("\n\nNTP Time Test\n");
WiFi.begin(ssid, password);
int counter = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(200);
if (++counter > 100) ESP.restart();
Serial.print ( "." );
}
Serial.println("\n\nWiFi connected\n\n");
configTime(0, 0, NTP_SERVER);
// See https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv for Timezone codes for your region
setenv("TZ", TZ_INFO, 1);
if (getNTPtime(10)) { // wait up to 10sec to sync
} else {
Serial.println("Time not set");
ESP.restart();
}
DinMeter.Display.clear();
lastNTPtime = time(&now);
lastEntryTime = millis();
}
void loop() {
static int LastSecond;
static tm LastTime;
static bool Relay; // Relay Status
char TimeTxt[6];
while (LastTime.tm_sec == timeinfo.tm_sec) {
// loop until next second
time(&now);
localtime_r(&now, &timeinfo);
}
if (LastTime.tm_min != timeinfo.tm_min) {
strftime (TimeTxt, 6, "%H:%M", &timeinfo);
if(std::find(std::begin(shift_times), std::end(shift_times), TimeTxt) != std::end(shift_times)){
// Relay ON
digitalWrite(switchPin, HIGH);
Relay = true;
Serial.println("BELL ON");
}
}
if ((timeinfo.tm_sec > 2) && (Relay)) {
// Relay OFF after 3 seconds
digitalWrite(switchPin, LOW);
Relay = false;
Serial.println("BELL OFF");
}
LastTime = timeinfo;
showTime(LastTime); // FOO update display with localtime(&now)
}
bool getNTPtime(int sec) {
{
uint32_t start = millis();
do {
time(&now);
localtime_r(&now, &timeinfo);
Serial.print(".");
delay(10);
} while (((millis() - start) <= (1000 * sec)) && (timeinfo.tm_year < (2016 - 1900)));
if (timeinfo.tm_year <= (2016 - 1900)) return false; // the NTP call was not successful
}
return true;
}
// Update Display
void showTime(tm localTime) {
char time_output[9];
char date_output[9];
strftime(time_output, 9, "%H:%M:%S", localtime(&now));
strftime(date_output, 9, "%m/%d/%y", localtime(&now));
M5.Display.drawString(date_output,M5.Display.width() / 2,33);
M5.Display.drawString(time_output,M5.Display.width() / 2,102);
}