1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-10 14:42:08 +03:00
david gauchard 9a2ed274f3 polledTimeout: add option to use CPU count instead of millis() (#5870)
* polledTimeout: add option to use CPU count instead of millis()

* use more "using" alias

* more c++/clear code, using typename (thanks @devyte)

* rename class name to include unit, introduce timeMax() and check it with assert()

* remove useless defines

* improve api readability, add micro-second unit

* update example

* mock: emulate getCycleCount, add/fix polledTimeout CI test

* + nano-seconds, assert -> message, comments, host test

* allow 0 for timeout (enables immediate timeout, fix division by 0)

* typo, set member instead of local variable

* unify error message

* slight change on checkExpired() allows "never expired"
also removed printed message, add YieldAndDelay, simplify calculations

* remove traces of debug.h/cpp in this PR

* include missing <limits> header

* back to original expired test, introduce boolean _neverExpires, fix reset(), getTimeout() is invalid

* fix expiredOneShot with _timeout==0 check

* reenable getTimeout()

* expose checkExpired with unit conversion

* fix timing comments, move critical code to iram

* add member ::neverExpires and use it where relevant

* improve clarity

* remove exposed checkExpired(), adapt LEAmDNS with equivalent

* add API ::resetToNeverExpires(), use it in LEAmDNS

* remove offending constness from ::flagged() LEAmDNS (due do API fix in PolledTimeout)

* simplify "Fast" base classes

* minor variable rename

* Fix examples

* compliance with good c++ manners

* minor changes for consistency

* add missing const

* expired() and bool() moved to iram

* constexpr compensation computing

* add/update comments

* move neverExpires and alwaysExpired
2019-04-05 10:50:53 -03:00

281 lines
8.4 KiB
C++

/*
ESP8266 mDNS responder clock
This example demonstrates two features of the LEA MDNSResponder:
1. The host and service domain negotiation process that ensures
the uniqueness of the finally choosen host and service domain name.
2. The dynamic MDNS service TXT feature
A 'clock' service in announced via the MDNS responder and the current
time is set as a TXT item (eg. 'curtime=Mon Oct 15 19:54:35 2018').
The time value is updated every second!
The ESP is initially announced to clients as 'esp8266.local', if this host domain
is already used in the local network, another host domain is negociated. Keep an
eye to the serial output to learn the final host domain for the clock service.
The service itself is is announced as 'host domain'._espclk._tcp.local.
As the service uses port 80, a very simple HTTP server is installed also to deliver
a small web page containing a greeting and the current time (not updated).
The web server code is taken nearly 1:1 from the 'mDNS_Web_Server.ino' example.
Point your browser to 'host domain'.local to see this web page.
Instructions:
- Update WiFi SSID and password as necessary.
- Flash the sketch to the ESP8266 board
- Install host software:
- For Linux, install Avahi (http://avahi.org/).
- For Windows, install Bonjour (http://www.apple.com/support/bonjour/).
- For Mac OSX and iOS support is built in through Bonjour already.
- Use a MDNS/Bonjour browser like 'Discovery' to find the clock service in your local
network and see the current time updates.
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <time.h>
/*
Include the MDNSResponder (the library needs to be included also)
As LEA MDNSResponder is experimantal in the ESP8266 environment currently, the
legacy MDNSResponder is defaulted in th include file.
There are two ways to access LEA MDNSResponder:
1. Prepend every declaration and call to global declarations or functions with the namespace, like:
'LEAmDNS::MDNSResponder::hMDNSService hMDNSService;'
This way is used in the example. But be careful, if the namespace declaration is missing
somewhere, the call might go to the legacy implementation...
2. Open 'ESP8266mDNS.h' and set LEAmDNS to default.
*/
#include <ESP8266mDNS.h>
#include <PolledTimeout.h>
/*
Global defines and vars
*/
#define TIMEZONE_OFFSET 1 // CET
#define DST_OFFSET 1 // CEST
#define UPDATE_CYCLE (1 * 1000) // every second
#define SERVICE_PORT 80 // HTTP port
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
char* pcHostDomain = 0; // Negociated host domain
bool bHostDomainConfirmed = false; // Flags the confirmation of the host domain
MDNSResponder::hMDNSService hMDNSService = 0; // The handle of the clock service in the MDNS responder
// HTTP server at port 'SERVICE_PORT' will respond to HTTP requests
ESP8266WebServer server(SERVICE_PORT);
/*
getTimeString
*/
const char* getTimeString(void) {
static char acTimeString[32];
time_t now = time(nullptr);
ctime_r(&now, acTimeString);
size_t stLength;
while (((stLength = strlen(acTimeString))) &&
('\n' == acTimeString[stLength - 1])) {
acTimeString[stLength - 1] = 0; // Remove trailing line break...
}
return acTimeString;
}
/*
setClock
Set time via NTP
*/
void setClock(void) {
configTime((TIMEZONE_OFFSET * 3600), (DST_OFFSET * 3600), "pool.ntp.org", "time.nist.gov", "time.windows.com");
Serial.print("Waiting for NTP time sync: ");
time_t now = time(nullptr); // Secs since 01.01.1970 (when uninitalized starts with (8 * 3600 = 28800)
while (now < 8 * 3600 * 2) { // Wait for realistic value
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println("");
Serial.printf("Current time: %s\n", getTimeString());
}
/*
setStationHostname
*/
bool setStationHostname(const char* p_pcHostname) {
if (p_pcHostname) {
WiFi.hostname(p_pcHostname);
Serial.printf("setDeviceHostname: Station hostname is set to '%s'\n", p_pcHostname);
}
return true;
}
/*
MDNSDynamicServiceTxtCallback
Add a dynamic MDNS TXT item 'ct' to the clock service.
The callback function is called every time, the TXT items for the clock service
are needed.
This can be triggered by calling MDNS.announce().
*/
void MDNSDynamicServiceTxtCallback(const MDNSResponder::hMDNSService p_hService) {
Serial.println("MDNSDynamicServiceTxtCallback");
if (hMDNSService == p_hService) {
Serial.printf("Updating curtime TXT item to: %s\n", getTimeString());
MDNS.addDynamicServiceTxt(p_hService, "curtime", getTimeString());
}
}
/*
MDNSProbeResultCallback
Probe result callback for the host domain.
If the domain is free, the host domain is set and the clock service is
added.
If the domain is already used, a new name is created and the probing is
restarted via p_pMDNSResponder->setHostname().
*/
void hostProbeResult(String p_pcDomainName, bool p_bProbeResult) {
Serial.println("MDNSProbeResultCallback");
Serial.printf("MDNSProbeResultCallback: Host domain '%s.local' is %s\n", p_pcDomainName.c_str(), (p_bProbeResult ? "free" : "already USED!"));
if (true == p_bProbeResult) {
// Set station hostname
setStationHostname(pcHostDomain);
if (!bHostDomainConfirmed) {
// Hostname free -> setup clock service
bHostDomainConfirmed = true;
if (!hMDNSService) {
// Add a 'clock.tcp' service to port 'SERVICE_PORT', using the host domain as instance domain
hMDNSService = MDNS.addService(0, "espclk", "tcp", SERVICE_PORT);
if (hMDNSService) {
// Add a simple static MDNS service TXT item
MDNS.addServiceTxt(hMDNSService, "port#", SERVICE_PORT);
// Set the callback function for dynamic service TXTs
MDNS.setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallback);
}
}
}
} else {
// Change hostname, use '-' as divider between base name and index
if (MDNSResponder::indexDomain(pcHostDomain, "-", 0)) {
MDNS.setHostname(pcHostDomain);
} else {
Serial.println("MDNSProbeResultCallback: FAILED to update hostname!");
}
}
}
/*
handleHTTPClient
*/
void handleHTTPRequest() {
Serial.println("");
Serial.println("HTTP Request");
// Get current time
time_t now = time(nullptr);;
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
String s;
s = "<!DOCTYPE HTML>\r\n<html>Hello from ";
s += WiFi.hostname() + " at " + WiFi.localIP().toString();
// Simple addition of the current time
s += "\r\nCurrent time is: ";
s += getTimeString();
// done :-)
s += "</html>\r\n\r\n";
Serial.println("Sending 200");
server.send(200, "text/html", s);
}
/*
setup
*/
void setup(void) {
Serial.begin(115200);
// Connect to WiFi network
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Sync clock
setClock();
// Setup MDNS responder
MDNS.setHostProbeResultCallback(hostProbeResult);
// Init the (currently empty) host domain string with 'esp8266'
if ((!MDNSResponder::indexDomain(pcHostDomain, 0, "esp8266")) ||
(!MDNS.begin(pcHostDomain))) {
Serial.println("Error setting up MDNS responder!");
while (1) { // STOP
delay(1000);
}
}
Serial.println("MDNS responder started");
// Setup HTTP server
server.on("/", handleHTTPRequest);
server.begin();
Serial.println("HTTP server started");
}
/*
loop
*/
void loop(void) {
// Check if a request has come in
server.handleClient();
// Allow MDNS processing
MDNS.update();
static esp8266::polledTimeout::periodicMs timeout(UPDATE_CYCLE);
if (timeout.expired()) {
if (hMDNSService) {
// Just trigger a new MDNS announcement, this will lead to a call to
// 'MDNSDynamicServiceTxtCallback', which will update the time TXT item
MDNS.announce();
}
}
}