Created
August 29, 2025 07:42
-
-
Save alatalo/cdd7e4c48bdc5a3fbb875d33d4a6d590 to your computer and use it in GitHub Desktop.
Impulse NTP master clock for ESP32 + L298N and mechanical slave clocks (alternate polarity each pulse)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Impulse NTP master clock for ESP32 + L298N (alternate polarity each pulse) | |
// Original by Wawa https://forum.arduino.cc/t/running-special-clock-with-motor-controller/1159828/106 | |
// Refactored by Ville Alatalo https://github.com/alatalo 08/2025 | |
// + minute-impulses | |
// + ENA for toggling coil | |
// + Finnish NTP + TZ | |
const char* WIFI_SSID = "Internet of Shit"; // WiFi SSID | |
const char* WIFI_PASS = ""; // WiFi password | |
const char* MY_TZ = "EET-2EEST,M3.5.0/3,M10.5.0/4"; // Helsinki, Finland | |
const char* MY_NTP_SERVER = "fi.pool.ntp.org"; // NTP pool, Finland | |
#include <WiFi.h> | |
#include "esp_sntp.h" | |
time_t now; | |
tm tm; | |
const int in1 = 25; // L298N IN1 | |
const int in2 = 26; // L298N IN2 | |
const int ena = 27; // L298N ENA (gate on-time) | |
const uint16_t pulseTime = 120; // coil on time (milliseconds) | |
const int STEP_SEC = 60; // clock steps (seconds) | |
const uint32_t MIN_INTERVAL_MS = 500; // when catching up, don't go faster than ~2 Hz | |
const uint32_t MAX_INTERVAL_MS = 120000; // cap long waits, mainly for safety | |
unsigned long prevMillis = 0; | |
unsigned long toggleTime = 0; | |
unsigned long intervalMs = 60000; | |
time_t ticktock = 0; // scheduled epoch-second for next pulse | |
bool ntpFlag = false; | |
bool toggle = false; | |
bool DST = false, prevDST = false; | |
void timeSyncCallback(struct timeval *tv) { | |
Serial.println(F("Time sync")); | |
ntpFlag = true; | |
} | |
void setup() { | |
pinMode(in1, OUTPUT); | |
pinMode(in2, OUTPUT); | |
pinMode(ena, OUTPUT); | |
digitalWrite(in1, LOW); | |
digitalWrite(in2, LOW); | |
digitalWrite(ena, LOW); | |
Serial.begin(115200); | |
Serial.println(F("\nESP32 NTP clock")); | |
sntp_set_time_sync_notification_cb(timeSyncCallback); | |
configTime(0, 0, MY_NTP_SERVER); | |
setenv("TZ", MY_TZ, 1); | |
tzset(); | |
WiFi.persistent(false); | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(WIFI_SSID, WIFI_PASS); | |
while (WiFi.status() != WL_CONNECTED) delay(200); | |
Serial.println(F("Connected, waiting for time sync")); | |
while (!ntpFlag) yield(); | |
time(&now); | |
// Align next tick to the *next* minute boundary (or half-minute if STEP_SEC=30) | |
ticktock = (now - (now % STEP_SEC)) + STEP_SEC; | |
localtime_r(&now, &tm); | |
DST = tm.tm_isdst; | |
prevDST = DST; | |
Serial.println(DST ? F("Summer time") : F("Winter time")); | |
// First wait interval until the boundary | |
intervalMs = (unsigned long)((ticktock - now) * 1000L); | |
intervalMs = constrain(intervalMs, MIN_INTERVAL_MS, MAX_INTERVAL_MS); | |
prevMillis = millis(); | |
} | |
void loop() { | |
time(&now); | |
localtime_r(&now, &tm); | |
// Handle DST change: fast-forward (to summer) or slow down/pause (to winter) | |
DST = tm.tm_isdst; | |
if (DST != prevDST) { | |
if (DST) { | |
// Summer time: advance 1 hour -> send extra pulses quickly | |
ticktock -= 3600; | |
Serial.println(F("DST -> Summer: fast-forwarding")); | |
} else { | |
// Winter time: fall back 1 hour -> pause progression | |
ticktock += 3600; | |
Serial.println(F("DST -> Winter: pausing an hour")); | |
} | |
prevDST = DST; | |
} | |
// Fire a pulse when due | |
if (millis() - prevMillis >= intervalMs) { | |
prevMillis = millis(); | |
printf("%02u:%02u:%02u\n", tm.tm_hour, tm.tm_min, tm.tm_sec); | |
// schedule next target at +STEP_SEC | |
ticktock += STEP_SEC; | |
// compute corrected wait to next boundary | |
long dSec = (long)ticktock - (long)now; | |
intervalMs = (unsigned long)(dSec * 1000L); | |
intervalMs = constrain(intervalMs, MIN_INTERVAL_MS, MAX_INTERVAL_MS); | |
// alternate polarity and gate coil on for pulseTime | |
toggle = !toggle; | |
digitalWrite(in1, toggle); | |
digitalWrite(in2, !toggle); | |
digitalWrite(ena, HIGH); // coil on | |
Serial.println(F("Coil on")); | |
toggleTime = millis(); | |
} | |
// Turn coil off after pulseTime | |
if (digitalRead(ena) == HIGH && (millis() - toggleTime > pulseTime)) { | |
digitalWrite(ena, LOW); // coil off | |
Serial.println(F("Coil off")); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment