The Arduino package manager has a library called NTPClient, which provides, as you might guess, an NTP client. Using this with the system clock lets you keep reasonable time without a battery-backed RTC.
Once you know what time it is, scheduling events becomes trivial. This isn't a fully working example, but should give you the idea. The docs have more information. The NTPClient also works quite well with the AceTime library for time zone and other management.
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ESP8266WiFi.h>
#include <AceTime.h>
#include <ctime>
using namespace ace_time;
using namespace ace_time::clock;
using namespace std;
WiFiUDP ntpUDP;
const long utcOffsetInSeconds = 0;
// Get UTC from NTP - let AceTime figure out the local time
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds, 3600);
//3600s/h - sync time once per hour, this is sufficient.
// don't hammer pool.ntp.org!
static BasicZoneProcessor estProcessor;
static SystemClockLoop systemClock(nullptr /*reference*/, nullptr /*backup*/);
const char* ssid = // your SSID;
const char* password = // your wifi password;
void setup() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("Connecting to wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.println('.');
delay(500);
}
systemClock.setup();
timeClient.begin();
}
int clockUpdateCounter = 50000;
void loop(){
// Update the system clock from the NTP client periodically
if (++clockUpdateCounter > 50000){ // can be more elegant than this... depends on your loop speed, can actually use the real time for this too, I'm just making this simple because I don't have a lot of time...
clockUpdateCounter = 0;
timeClient.update();
// doesn't matter how often you call timeClient.update().
// you can put this in the main loop() if you want.
// We supplied 3600s as the refresh interval in the constructor
// and internally .update() checks whether enough time has passed
// before making a request to the NTP server itself
auto estTz = TimeZone::forZoneInfo(&zonedb::kZoneAmerica_Toronto, &estProcessor);
auto estTime = ZonedDateTime::forUnixSeconds(timeClient.getEpochTime(), estTz);
// using the system clock is optional, but has a few
// conveniences. Here we just make sure that the systemClock remains
// sync'd with the NTP server. Doesn't matter how often you do this
// but usually around once an hour is plenty
systemClock.setNow(estTime.toEpochSeconds());
}
systemClock.loop();
delay(30);
}
The above keeps the system clock in sync with the NTP server. You can then use the system clock to fetch the time and use it for all types of timing purposes. eg :
// get Time
acetime_t nowT = systemClock.getNow();
auto estTz = TimeZone::forZoneInfo(&zonedb::kZoneAmerica_Toronto, &estProcessor);
auto nowTime = ZonedDateTime::forEpochSeconds(nowT, estTz);
Checking the time is fast, so you can do it inside loop() and only trigger the action when whatever time has elapsed.
This is a particularly nice solution for database integration since you can record a real timestamp of the actual date and time when logging your data to the database.
Answer from J... on Stack Overflowc++ - Esp8266 Timer instead of delay - Stack Overflow
Fractional Microsecond Delay;
ESP8266 delay/milliseconds makes LED strip buggy
esp8266 problem no delay time possible in my programm
Videos
Hey all, possess any of you managed to get fractional microsecond delay on the ESP8266/ESP32? I need to delay about 5.33 microseconds between setting a pin high and then back to low. I understand that delayMicroseconds mightbe close, but wanted to see if there might be something with a bit more resolution. Thanks!
I'm trying to build a serial command line interface for an ESP8266. Here's some of my code:
String ReadUntilEnter(String currentInput) {
if (Serial.available() > 0) {
char inputChar = Serial.read();
// On newline, stop reading chars and return the string
if ((int) inputChar == 13) {
Serial.print("\r\n");
return currentInput;
}
// If the character is not a newline, add it to the string and keep listening
else {
Serial.print(inputChar);
currentInput += inputChar;
return ReadUntilEnter(currentInput);
}
}
// Wait for serial to become available
else {
delay(100);
return ReadUntilEnter(currentInput);
}
}
void loop() {
Serial.print("Console# ");
while (Serial.available() == 0) {}
String command = ReadUntilEnter("");
// handle command
}
Essentially, I want the users to be greeted with a prompt that says:
Console#
The user can then type in a command:
Console# hack cia
The issue I am encountering happens when the user has typed one or more characters inside the prompt, but has not yet hit enter. The function is holding up the thread while waiting for the next character. The watchdog does not like this and will reset the ESP if someone takes too long to type a command. It works decently well with the 100 ms delay but if you take a long time, it will be halted by the watchdog. I have tried using the yield() function in place of the 100 ms delay, but the ESP would crash once it hit the yield statement - not a watchdog stop but a full crash with a stack trace and everything. Can someone please give me some advice? I've looked at tons of forum threads on the topic of the watchdog but I can't get it to work
Thanks so much
I guess your problem is a stack overflow, because of too many recursions.
You should try doing it with loops. Something like
while (Serial.available() == 0) {
delay(100);
}
And
do {
....
} while (inputChar != 13);
I prefer this approach:
if (Serial.available() != 0) { // if there is a character in the buffer, c = Serial.read(); // get it. if (c == '\r') c = '\n';
nextChar(c); // feed it to the parser. } // end if (Serial.available()) // ** // if you have something else to do, do it here. // **
No need for yield() or delay().
[Edit] apologies for formatting; I can never seem to get md to work.
I bought a cheap ESP-01/relay module I picked up to ultimately wire up my garage door. Everything works as advertised, but I'm getting a low/ground pulse on GPIO0 on startup that's triggering the relay momentarily. I have spent way too much time googling this issue to no avail. I've found others with same issue, but haven't found any fixes. I don't think there's anything in the software that will prevent it, and I've tried a 10k pullup on GPIO0 without success. Should I try a lower resistance resistor?
Ultimately I'll probably just buy a better ESP8266 for this project, but just for the sake of learning how to use a timer/delay circuit, I thought I'd post here for any advice/recommendations. Keep in mind I'm a bit of a novice with all this stuff.
Edit: Recommendations in here I tried:
2200uf/35v across power and ground. Still pulses
Tried using GPIO2 instead of 0. Still pulses
Tried a delay of 5 seconds right off the bat in the setup function. Still pulses
Swapped order of digitalWrite and pinMode. Still pulses
Appreciate all the suggestions!
Bonus video: https://www.youtube.com/watch?v=jnZMach5tH8
Probably need a capacitor across the power and ground at the device to fill in the surge when it starts ip. Maybe a 220uf or bigger.
Just add a delay(200) to the top of your setup()function. The esp8266 of all variants use that pin during boot, and having any GPIO on that pin can during that cycle can cause problems either with your system, or with booting.