From 0a4c88a36d02e9c89d39d0fe1add6aedccfe934a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erwan=20Queff=C3=A9lec?= Date: Wed, 20 Jun 2018 22:18:14 +0200 Subject: [PATCH] Use SDK NTP and better RTC l MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Erwan Queffélec --- platformio.ini | 3 +- src/core/CoolBoard.cpp | 220 +++++++++++------------ src/core/CoolBoard.h | 10 +- src/core/CoolBoardLed.cpp | 1 + src/core/CoolBoardLed.h | 2 +- src/core/CoolTime.cpp | 356 ++++---------------------------------- src/core/CoolTime.h | 49 ++---- src/core/CoolWifi.cpp | 56 +++--- src/core/CoolWifi.h | 10 +- 9 files changed, 184 insertions(+), 523 deletions(-) diff --git a/platformio.ini b/platformio.ini index 4698588d..385023d8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,11 +20,10 @@ platform = espressif8266@1.7.0 lib_deps = ArduinoJson@5.13.1 NeoPixelBus@2.2.9 - Time@1.5 SparkFun BME280@1.2.0 DallasTemperature@3.8.0 OneWire@2.3.4 - https://github.com/LaCoolCo/DS1337RTC + https://github.com/cyberp/DS1337 https://github.com/practicalarduino/SHT1x build_flags = !echo "-DCOOL_FW_VERSION="\'\"$(git describe --tags --always)\"\' "-Wl,-Tesp8266.flash.4m2m.ld -DVTABLES_IN_FLASH -D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY" diff --git a/src/core/CoolBoard.cpp b/src/core/CoolBoard.cpp index d7be68e1..5b2cb3ba 100644 --- a/src/core/CoolBoard.cpp +++ b/src/core/CoolBoard.cpp @@ -46,16 +46,9 @@ void CoolBoard::begin() { pinMode(BOOTSTRAP_PIN, INPUT); digitalWrite(ENABLE_I2C_PIN, HIGH); delay(100); - - DEBUG_LOG("Start RTC configuration..."); - this->coolTime.config(); - this->coolTime.offGrid(); - delay(100); - this->coolBoardSensors.config(); this->coolBoardSensors.begin(); delay(100); - this->coolBoardActuator.config(); this->coolBoardActuator.begin(); delay(100); @@ -63,15 +56,12 @@ void CoolBoard::begin() { this->coolBoardLed.printConf(); this->coolBoardSensors.printConf(); this->coolBoardActuator.printConf(); - this->coolTime.printConf(); - if (this->jetpackActive) { this->jetPack.config(); this->jetPack.begin(); this->jetPack.printConf(); delay(100); } - if (this->ireneActive) { this->irene3000.config(); this->irene3000.begin(); @@ -79,13 +69,12 @@ void CoolBoard::begin() { this->irene3000.printConf(); delay(100); } - if (this->externalSensorsActive) { this->externalSensors->config(); this->externalSensors->begin(); delay(100); } - this->coolTime.begin(); + this->mqttsConfig(); delay(100); SPIFFS.end(); } @@ -99,83 +88,86 @@ void CoolBoard::loop() { INFO_LOG("Connecting..."); this->connect(); } - INFO_LOG("Updating RTC..."); - this->coolTime.update(); - if (!SPIFFS.exists("/configSent.flag")) { - sendAllConfig(); - File f; - if (!(f = SPIFFS.open("/configSent.flag", "w"))) { - ERROR_LOG("Can't create file configSent.flag in SPIFFS"); - } else { - f.close(); + INFO_LOG("Synchronizing RTC..."); + bool rtcSynced = this->coolTime.sync(); + if (!rtcSynced) { + this->clockProblem(); + } else { + if (!SPIFFS.exists("/configSent.flag")) { + this->sendAllConfig(); + File f; + if (!(f = SPIFFS.open("/configSent.flag", "w"))) { + ERROR_LOG("Can't create file configSent.flag in SPIFFS"); + } else { + f.close(); + } + } + DynamicJsonBuffer jsonBuffer; + JsonObject &root = jsonBuffer.createObject(); + JsonObject &state = root.createNestedObject("state"); + JsonObject &reported = state.createNestedObject("reported"); + INFO_LOG("Collecting board and sensor data..."); + this->readPublicIP(reported); + this->readBoardData(reported); + this->readSensors(reported); + INFO_LOG("Setting actuators and reporting their state..."); + this->handleActuators(reported); + delay(50); + if (this->shouldLog()) { + INFO_LOG("Sending log over MQTT..."); + String data; + root.printTo(data); + this->mqttLog(data); + this->previousLogTime = millis(); + } + + if (digitalRead(BOOTSTRAP_PIN) == LOW) { + INFO_LOG("Bootstrap is in LOAD position, starting AP for further " + "configuration..."); + this->coolWifi->startAccessPoint(this->coolBoardLed); + delay(500); + } + INFO_LOG("Listening to update messages..."); + this->mqttListen(); + if (CoolFileSystem::hasSavedLogs()) { + INFO_LOG("Sending saved messages..."); + this->sendSavedMessages(); } - } - DynamicJsonBuffer jsonBuffer; - JsonObject &root = jsonBuffer.createObject(); - JsonObject &state = root.createNestedObject("state"); - JsonObject &reported = state.createNestedObject("reported"); - INFO_LOG("Collecting board and sensor data..."); - this->readPublicIP(reported); - this->readBoardData(reported); - this->readSensors(reported); - INFO_LOG("Setting actuators and reporting their state..."); - this->handleActuators(reported); - delay(50); - if (this->shouldLog()) { - INFO_LOG("Sending log over MQTT..."); - String data; - root.printTo(data); - this->mqttLog(data); - this->previousLogTime = millis(); - } - this->startAP(); - INFO_LOG("Listening to saved messages..."); - this->mqttListen(); - if (CoolFileSystem::hasSavedLogs()) { - INFO_LOG("Sending saved messages..."); - this->sendSavedMessages(); } SPIFFS.end(); - if (this->sleepActive && !this->shouldLog()) { + if (this->sleepActive && (!this->shouldLog() || !rtcSynced)) { this->sleep(this->secondsToNextLog()); } } bool CoolBoard::isConnected() { - if (this->coolWifi->state() != WL_CONNECTED) { - WARN_LOG("Wifi disconnected"); + if (WiFi.status() != WL_CONNECTED) { return false; } - if (this->coolPubSubClient->state() != 0) { - WARN_LOG("MQTT disconnected"); + if (this->coolPubSubClient->state() != MQTT_CONNECTED) { return false; } return true; } -int CoolBoard::connect() { +void CoolBoard::connect() { if (this->coolWifi->wifiCount > 0) { this->coolBoardLed.write(BLUE); this->coolWifi->connect(); } else { INFO_LOG("No configured Wifi access point, launching configuration portal"); - this->coolWifi->disconnect(); - delay(200); - this->coolBoardLed.write(FUCHSIA); - this->coolWifi->connectAP(); + this->coolWifi->startAccessPoint(this->coolBoardLed); } delay(100); - if (this->coolWifi->state() == WL_CONNECTED) { + if (WiFi.status() == WL_CONNECTED) { + this->coolTime.begin(); delay(100); - this->coolBoardLed.blink(GREEN, 5); - if (this->mqttConnect() != 0) { - this->mqttProblem(); - } + this->mqttConnect(); delay(100); - } else { - this->coolBoardLed.blink(RED, 10); } - return (this->coolPubSubClient->state()); + if (!this->isConnected()) { + this->networkProblem(); + } } void CoolBoard::sendSavedMessages() { @@ -189,8 +181,8 @@ void CoolBoard::sendSavedMessages() { CoolFileSystem::deleteSavedLog(savedLogNumber); this->messageSent(); } else { - ERROR_LOG("MQTT publish failed, data stay on SPIFFS"); - this->mqttProblem(); + this->networkProblem(); + ERROR_LOG("MQTT publish failed, kept log on SPIFFS"); break; } } @@ -198,16 +190,16 @@ void CoolBoard::sendSavedMessages() { void CoolBoard::handleActuators(JsonObject &reported) { if (this->manual == 0) { + Date date = this->coolTime.rtc.getDate(); + INFO_LOG("Actuators configuration: automatic"); - tmElements_t tm; - tm = this->coolTime.getTimeDate(); if (this->jetpackActive) { - DEBUG_LOG("Collecting Jetpack actuators data..."); - this->jetPack.doAction(reported, int(tm.Hour), int(tm.Minute)); + DEBUG_LOG("Updating and recording Jetpack state..."); + this->jetPack.doAction(reported, date.getHour(), date.getMinutes()); } - DEBUG_LOG("Collecting onboard actuator data..."); - if (this->coolBoardActuator.doAction(reported, int(tm.Hour), - int(tm.Minute))) { + DEBUG_LOG("Updating and recording onboard actuator state..."); + if (this->coolBoardActuator.doAction(reported, date.getHour(), + date.getMinutes())) { this->coolBoardActuator.write(1); reported["ActB"] = 1; } else { @@ -261,7 +253,6 @@ bool CoolBoard::config() { config.set(json, "sleepActive", this->sleepActive); config.set(json, "manual", this->manual); config.set(json, "mqttServer", this->mqttServer); - this->mqttsConfig(); INFO_LOG("Main configuration loaded"); return (true); } @@ -304,7 +295,7 @@ void CoolBoard::update(const char *answer) { firmwareJson.printTo(otaUpdateConfig); otaUpdateConfig.close(); DEBUG_JSON("Saved OTA HTTPS update configuration to: ", firmwareJson); - INFO_LOG("New firmaware update received, your board will now reboot " + INFO_LOG("New firmware update received, your board will now reboot " "to apply..."); delay(100); ESP.restart(); @@ -379,10 +370,10 @@ void CoolBoard::readSensors(JsonObject &reported) { } void CoolBoard::readBoardData(JsonObject &reported) { - reported["timestamp"] = this->coolTime.getESDate(); + reported["timestamp"] = this->coolTime.getIso8601DateTime(); reported["mac"] = this->mqttId; reported["firmwareVersion"] = COOL_FW_VERSION; - if (this->isConnected()) { + if (WiFi.status() == WL_CONNECTED) { reported["wifiSignal"] = WiFi.RSSI(); } } @@ -430,37 +421,33 @@ void CoolBoard::sendConfig(const char *moduleName, const char *filePath) { } void CoolBoard::readPublicIP(JsonObject &reported) { - if (this->isConnected()) { - reported["publicIp"] = this->coolWifi->getExternalIP(); + if (WiFi.status() == WL_CONNECTED) { + String ip; + if (this->coolWifi->getPublicIp(ip)) { + DEBUG_VAR("Public IP address:", ip); + reported["publicIp"] = ip; + } } } -void CoolBoard::startAP() { - if (digitalRead(BOOTSTRAP_PIN) == LOW) { - INFO_LOG("Bootstrap is in LOAD position, starting AP for further " - "configuration..."); - this->coolWifi->disconnect(); - delay(200); - this->coolBoardLed.write(FUCHSIA); - this->coolWifi->connectAP(); - yield(); - if (this->coolWifi->state() == WL_CONNECTED) { - this->coolBoardLed.blink(GREEN, 5); - } else { - this->coolBoardLed.blink(RED, 10); - } - delay(500); +void CoolBoard::networkProblem() { + WARN_LOG("Network unreachable"); + CoolWifi::printStatus(WiFi.status()); + this->printMqttState(this->coolPubSubClient->state()); + for (int i = 0; i < 8; i++) { + this->coolBoardLed.blink(ORANGE, 0.2); } } -void CoolBoard::mqttProblem() { - this->coolBoardLed.blink(RED, 0.2); - delay(200); - this->coolBoardLed.blink(RED, 0.2); - delay(200); +void CoolBoard::clockProblem() { + ERROR_LOG("NTP failed and RTC has stopped, not doing anything!"); + for (int i = 0; i < 8; i++) { + this->coolBoardLed.blink(RED, 0.2); + } } void CoolBoard::spiffsProblem() { + CRITICAL_LOG("Filesystem failed, please contact support!"); this->coolBoardLed.write(RED); while (true) { yield(); @@ -506,7 +493,7 @@ void CoolBoard::printMqttState(int state) { } } -int CoolBoard::mqttConnect() { +void CoolBoard::mqttConnect() { int i = 0; INFO_LOG("MQTT connecting..."); @@ -522,22 +509,20 @@ int CoolBoard::mqttConnect() { delay(5); i++; } - int state = this->coolPubSubClient->state(); - this->printMqttState(state); - return (state); } void CoolBoard::mqttLog(String data) { - DEBUG_VAR("Message to publish:", data); - DEBUG_VAR("Message size:", data.length()); bool messageSent = false; + + DEBUG_VAR("Message to log:", data); + DEBUG_VAR("Message size:", data.length()); if (this->isConnected()) { messageSent = this->mqttPublish(data); } - if (!this->isConnected() || !messageSent) { - ERROR_LOG("MQTT publish failed, data saved on SPIFFS"); + if (!messageSent) { CoolFileSystem::saveLogToFile(data.c_str()); - this->mqttProblem(); + this->networkProblem(); + WARN_LOG("Log not sent, saved on SPIFFS"); } else { INFO_LOG("MQTT publish successful"); this->messageSent(); @@ -583,13 +568,13 @@ void CoolBoard::mqttsConvert(String cert) { } } -bool CoolBoard::mqttsConfig() { +void CoolBoard::mqttsConfig() { if (SPIFFS.exists("/certificate.bin") && SPIFFS.exists("/privateKey.bin")) { - DEBUG_LOG("Loading Certificate"); + DEBUG_LOG("Loading X509 certificate"); this->mqttsConvert("/certificate.bin"); - DEBUG_LOG("Loading privateKey"); + DEBUG_LOG("Loading X509 private key"); this->mqttsConvert("/privateKey.bin"); - DEBUG_LOG("Setting wifiClient"); + DEBUG_LOG("Configuring MQTT"); this->coolPubSubClient->setClient(*this->wifiClientSecure); this->coolPubSubClient->setServer(this->mqttServer.c_str(), 8883); this->coolPubSubClient->setCallback( @@ -602,15 +587,13 @@ bool CoolBoard::mqttsConfig() { String(F("$aws/things/")) + this->mqttId + String(F("/shadow/update")); this->mqttInTopic = String(F("$aws/things/")) + this->mqttId + String(F("/shadow/update/delta")); - return (true); } else { - this->spiffsProblem(); ERROR_LOG("Certificate & Key binaries not found"); DEBUG_VAR("/certificate.bin exist return: ", SPIFFS.exists("/certificate.bin")); DEBUG_VAR("/privateKey.bin exist return: ", SPIFFS.exists("/privateKey.bin")); - return (false); + this->spiffsProblem(); } } @@ -636,20 +619,21 @@ void CoolBoard::tryFirmwareUpdate() { INFO_LOG("Failed to prepare firmware update (missing configuration)"); } } else { - ERROR_LOG("Failed to read firmware update configuration"); + INFO_LOG("No firmware update pending"); } } void CoolBoard::updateFirmware(String firmwareVersion, String firmwareUrl, String firmwareUrlFingerprint) { - this->coolBoardLed.write(BROWN); + this->coolBoardLed.write(ORANGE); delete this->coolPubSubClient; delete this->externalSensors; delete this->wifiClientSecure; SPIFFS.remove("/otaUpdateConfig.json"); SPIFFS.end(); delay(100); - if (this->coolWifi->connect() == WL_CONNECTED) { + this->coolWifi->connect(); + if (WiFi.status() == WL_CONNECTED) { Serial.flush(); Serial.setDebugOutput(true); delete this->coolWifi; diff --git a/src/core/CoolBoard.h b/src/core/CoolBoard.h index cf5766cc..2bd36945 100644 --- a/src/core/CoolBoard.h +++ b/src/core/CoolBoard.h @@ -51,7 +51,7 @@ class CoolBoard { bool config(); void update(const char *answer); void loop(); - int connect(); + void connect(); bool isConnected(); unsigned long getLogInterval(); void printConf(); @@ -63,18 +63,18 @@ class CoolBoard { void sendAllConfig(); void sendConfig(const char *moduleName, const char *filePath); void readPublicIP(JsonObject &reported); - void startAP(); - void mqttProblem(); + void clockProblem(); + void networkProblem(); void spiffsProblem(); void messageSent(); unsigned long secondsToNextLog(); bool shouldLog(); void printMqttState(int state); - int mqttConnect(); + void mqttConnect(); bool mqttPublish(String data); bool mqttListen(); void mqttCallback(char *topic, byte *payload, unsigned int length); - bool mqttsConfig(); + void mqttsConfig(); static int b64decode(String b64Text, uint8_t *output); void mqttsConvert(String cert); void updateFirmware(String firmwareVersion, String firmwareUrl, String firmwareUrlFingerprint); diff --git a/src/core/CoolBoardLed.cpp b/src/core/CoolBoardLed.cpp index 9c0fdac6..1b423844 100644 --- a/src/core/CoolBoardLed.cpp +++ b/src/core/CoolBoardLed.cpp @@ -65,6 +65,7 @@ void CoolBoardLed::blink(uint8_t r, uint8_t g, uint8_t b, float t) { delay(t * 1000); this->neoPixelLed.SetPixelColor(0, RgbColor(0, 0, 0)); this->neoPixelLed.Show(); + delay(t * 1000); } } diff --git a/src/core/CoolBoardLed.h b/src/core/CoolBoardLed.h index ab8acd0b..24f093b6 100644 --- a/src/core/CoolBoardLed.h +++ b/src/core/CoolBoardLed.h @@ -36,7 +36,7 @@ #define YELLOW 30, 30, 0 #define BLUE 0, 0, 50 #define FUCHSIA 30, 0, 30 -#define BROWN 50, 25, 0 +#define ORANGE 50, 25, 0 class CoolBoardLed { diff --git a/src/core/CoolTime.cpp b/src/core/CoolTime.cpp index 9a1c91aa..dd00caa9 100644 --- a/src/core/CoolTime.cpp +++ b/src/core/CoolTime.cpp @@ -29,339 +29,57 @@ #include "CoolLog.h" #include "CoolTime.h" -void CoolTime::begin() { Udp.begin(localPort); } +bool CoolTime::ntpSync = false; -void CoolTime::offGrid() { - if (compileTime && !NTP) { - char posMarker = 0; - for (int i = 0; i <= sizeof(__TIMESTAMP__); i++) { - if (__TIMESTAMP__[i] == ':') { - posMarker = i; - break; - } - } - char monthAbbr[4] = {__TIMESTAMP__[4], __TIMESTAMP__[5], __TIMESTAMP__[6], - 0}; - char tempDay[3] = {__TIMESTAMP__[8], __TIMESTAMP__[9], 0}; - int Day = atoi(&tempDay[0]); - char tempHour[3] = {__TIMESTAMP__[posMarker - 2], - __TIMESTAMP__[posMarker - 1], 0}; - int Hour = atoi(&tempHour[0]); - char tempMinute[3] = {__TIMESTAMP__[posMarker + 1], - __TIMESTAMP__[posMarker + 2], 0}; - int Minute = atoi(&tempMinute[0]); - char tempSecond[3] = {__TIMESTAMP__[posMarker + 4], - __TIMESTAMP__[posMarker + 5], 0}; - int Second = atoi(&tempSecond[0]); - char tempYear[3] = {__TIMESTAMP__[posMarker + 9], - __TIMESTAMP__[posMarker + 10], 0}; - int Year = atoi(&tempYear[0]); - int Month; - - if (strstr(monthAbbr, "Jan")) { - Month = 1; - } - if (strstr(monthAbbr, "Feb")) { - Month = 2; - } - if (strstr(monthAbbr, "Mar")) { - Month = 3; - } - if (strstr(monthAbbr, "Apr")) { - Month = 4; - } - if (strstr(monthAbbr, "May")) { - Month = 5; - } - if (strstr(monthAbbr, "Jun")) { - Month = 6; - } - if (strstr(monthAbbr, "Jul")) { - Month = 7; - } - if (strstr(monthAbbr, "Aug")) { - Month = 8; - } - if (strstr(monthAbbr, "Sep")) { - Month = 9; - } - if (strstr(monthAbbr, "Oct")) { - Month = 10; - } - if (strstr(monthAbbr, "Nov")) { - Month = 11; - } - if (strstr(monthAbbr, "Dec")) { - Month = 12; - } - setDateTime(y2kYearToTm(Year), Month, Day, Hour, Minute, Second); - unsigned long instantTime = RTC.get(CLOCK_ADDRESS); - this->timeSync = instantTime; - this->compileTime = false; - this->config(false); - DEBUG_VAR("RTC set from:", __TIMESTAMP__); - DEBUG_VAR("Seconds since UNIX Epoch:", instantTime); - } -} - -void CoolTime::update() { - if (this->NTP && WiFi.status() == WL_CONNECTED) { - if (!this->isServerSelected()) { - this->selectTimeServer(); - this->printConf(); - } - if (!(this->isTimeSync())) { - int repeats = 0; - - DEBUG_LOG("Waiting for sync"); - this->timeSync = this->getNtpTime(); - while (!this->timeSync) { - delay(1000); - this->timeSync = this->getNtpTime(); - - if (repeats >= 4) { - selectTimeServer(); - delay(500); - this->timeSync = this->getNtpTime(); - break; - } - repeats++; - } - breakTime(this->timeSync, this->tmSet); - this->rtc.set(makeTime(this->tmSet), CLOCK_ADDRESS); - } - this->config(true); - } -} - -void CoolTime::setDateTime(int year, int month, int day, int hour, int minutes, - int seconds) { - tmElements_t tm; - - tm.Second = seconds; - tm.Minute = minutes; - tm.Hour = hour; - tm.Day = day; - tm.Month = month; - tm.Year = year; - this->rtc.set(makeTime(tm), CLOCK_ADDRESS); - DEBUG_VAR("Time set to:", this->getESDate()); -} - -tmElements_t CoolTime::getTimeDate() { - tmElements_t tm; - - // FIXME: experimental: dummy call to prevent slow RTC - rtc.get(CLOCK_ADDRESS); - delay(50); - time_t timeDate = this->rtc.get(CLOCK_ADDRESS); - breakTime(timeDate, tm); - return (tm); -} - -String CoolTime::getESDate() { - tmElements_t tm = this->getTimeDate(); - - // output format: "yyyy-mm-ddTHH:MM:ssZ" - String elasticSearchString = - String(tm.Year + 1970) + "-" + this->formatDigits(tm.Month) + "-"; - elasticSearchString += - this->formatDigits(tm.Day) + "T" + this->formatDigits(tm.Hour) + ":"; - elasticSearchString += - this->formatDigits(tm.Minute) + ":" + this->formatDigits(tm.Second) + "Z"; - return (elasticSearchString); -} - -unsigned long CoolTime::getLastSyncTime() { - DEBUG_VAR("Last RTC sync time:", this->timeSync); - return (this->timeSync); +void timeSet(void) { + CoolTime::ntpSync = true; + DEBUG_LOG("Received response from NTP server"); } -bool CoolTime::isTimeSync(unsigned long seconds) { - // FIXME: experimental: dummy call to prevent slow RTC - RTC.get(CLOCK_ADDRESS); - - unsigned long instantTime = RTC.get(CLOCK_ADDRESS); - - DEBUG_VAR("Current RTC time:", instantTime); - DEBUG_VAR("Time since last sync:", instantTime - this->timeSync); - if ((instantTime - this->timeSync) > (seconds)) { - WARN_LOG("RTC is not synchronised"); - return (false); - } - DEBUG_LOG("RTC is synchronised"); - return (true); +void CoolTime::printStatus() { + INFO_VAR("RTC ISO8601 timestamp:", this->getIso8601DateTime()); + DEBUG_VAR("RTC UNIX timestamp:", this->rtc.getTimestamp()); } -time_t CoolTime::getNtpTime() { - IPAddress timeServerIp; - - WiFi.hostByName(this->TIME_SERVER_LIST[this->timeServerIdx], - timeServerIp); - if (timeServerIp[0] == 0 && timeServerIp[1] == 0 && timeServerIp[2] == 0 && - timeServerIp[3] == 0) { - WARN_VAR("No IP address for timeserver", - this->TIME_SERVER_LIST[this->timeServerIdx]); - WARN_LOG("Will run NTP benchmark later on"); - return 0; - } else { - DEBUG_VAR("Sending NTP request to:", timeServerIp); - - while (Udp.parsePacket() > 0) - ; // discard any previously received packets - sendNTPpacket(timeServerIp); - uint32_t beginWait = millis(); - - while (millis() - beginWait < TIMEOUT) { - int size = Udp.parsePacket(); - if (size >= NTP_PACKET_SIZE) { - unsigned long secsSince1900; - unsigned long unixTime; - DEBUG_LOG("Received response from NTP server"); - Udp.read(packetBuffer, NTP_PACKET_SIZE); - // convert four bytes starting at location 40 to a long integer - secsSince1900 = (unsigned long)packetBuffer[40] << 24; - secsSince1900 |= (unsigned long)packetBuffer[41] << 16; - secsSince1900 |= (unsigned long)packetBuffer[42] << 8; - secsSince1900 |= (unsigned long)packetBuffer[43]; - unixTime = secsSince1900 - 2208988800UL; - DEBUG_VAR("Received UNIX time:", unixTime); - return unixTime; - } - } +void CoolTime::begin() { + this->printStatus(); + if (this->rtc.hasStopped()) { + WARN_LOG("RTC has stopped, need to resync"); } - ERROR_LOG("No response from NTP server"); - return 0; + settimeofday_cb(timeSet); + configTime(0, 0, "0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org"); } -void CoolTime::sendNTPpacket(IPAddress &address) { - memset(packetBuffer, 0, NTP_PACKET_SIZE); - // Initialize values needed to form NTP request - // (see URL above for details on the packets) - packetBuffer[0] = 0b11100011; // LI, Version, Mode - packetBuffer[1] = 0; // Stratum, or type of clock - packetBuffer[2] = 6; // Polling Interval - packetBuffer[3] = 0xEC; // Peer Clock Precision - // 8 bytes of zero for Root Delay & Root Dispersion - packetBuffer[12] = 49; - packetBuffer[13] = 0x4E; - packetBuffer[14] = 49; - packetBuffer[15] = 52; - // all NTP fields have been given values, now - // you can send a packet requesting a timestamp: - Udp.beginPacket(address, 123); // NTP requests are to port 123 - Udp.write(packetBuffer, NTP_PACKET_SIZE); - Udp.endPacket(); -} +bool CoolTime::sync() { + uint32_t waitUntil = millis() + NTP_TIMEOUT_MS; -bool CoolTime::config(bool overwrite) { - CoolConfig config("/rtcConfig.json"); + INFO_LOG("Waiting for NTP..."); - if (!config.readFileAsJson()) { - ERROR_LOG("Failed to parse RTC configuration"); - return (false); + while (!CoolTime::ntpSync && millis() < waitUntil) { + delay(100); } - JsonObject &json = config.get(); - DEBUG_JSON("RTC configuration JSON:", json); - config.set(json, "timePool", this->timeServerIdx, overwrite); - config.set(json, "timeSync", this->timeSync, overwrite); - config.set(json, "NTP", this->NTP, overwrite); - config.set(json, "compileTime", this->compileTime, overwrite); - INFO_LOG("RTC configuration loaded"); - if (overwrite) { - if (!config.writeJsonToFile()) { - ERROR_LOG("Failed to save RTC configuration"); - return (false); - } - } - return (true); -} - -bool CoolTime::isServerSelected() const { - return (this->timeServerIdx >= 0 && this->timeServerIdx < SERVERCOUNT); -} - -void CoolTime::printConf() { - INFO_LOG("RTC configuration"); - String timeServer; - if (this->isServerSelected()) { - timeServer = this->TIME_SERVER_LIST[this->timeServerIdx]; + if (CoolTime::ntpSync) { + this->rtc.setDateTime(time(nullptr)); + this->rtc.clearOSF(); + INFO_LOG("RTC was synchronized with NTP"); + this->printStatus(); + return true; } else { - timeServer = "NONE"; - } - INFO_VAR(" Local port =", this->localPort); - INFO_VAR(" NTP enabled =", this->NTP); - INFO_VAR(" Use compilation date =", this->compileTime); - INFO_VAR(" Selected time server =", timeServer); - INFO_VAR(" RTC timestamp =", this->timeSync); -} - -String CoolTime::formatDigits(int digits) { - if (digits < 10) { - return (String("0") + String(digits)); + if (rtc.hasStopped()) { + return false; + } else { + WARN_LOG("NTP failed, falling back to RTC"); + return true; + } } - return (String(digits)); } -bool CoolTime::selectTimeServer() { - INFO_LOG("Performing NTP server benchmark..."); - - uint32_t latency[SERVERCOUNT]; - bool timeout[SERVERCOUNT]; - - for (int i = 0; i < SERVERCOUNT; i++) { - latency[i] = UINT32_MAX; - timeout[i] = false; - } - - for (int j = 1; j <= NTP_OVERSAMPLE; j++) { - for (int i = 0; i < SERVERCOUNT; i++) { - while (Udp.parsePacket() > 0) { - ; // discard any previously received packets - } - IPAddress timeServerIp; - const char *timeServer = TIME_SERVER_LIST[i]; - WiFi.hostByName(timeServer, timeServerIp); - if (timeServerIp[0] == 0 && timeServerIp[1] == 0 && - timeServerIp[2] == 0 && timeServerIp[3] == 0) { - WARN_VAR("Could not get IP of NTP server:", timeServer); - timeout[i] = true; - } else { - DEBUG_VAR("Sending NTP request to:", timeServer); - sendNTPpacket(timeServerIp); - - uint32_t beginWait = millis(); +String CoolTime::getIso8601DateTime() { + char timestamp[] = "YYYY-MM-DDTHH:MM:SSZ"; + Date d = this->rtc.getDate(); - while ((millis() - beginWait) < (TIMEOUT + 200)) { - int size = Udp.parsePacket(); - if (size >= NTP_PACKET_SIZE) { - latency[i] += (millis() - beginWait); - DEBUG_VAR("Received response from NTP server:", timeServer); - break; - } - if ((millis() - beginWait) >= TIMEOUT) { - timeout[i] = true; - WARN_VAR("Hit timeout for NTP server:", timeServer); - break; - } - } - } - } - } - - unsigned long minLatency = UINT32_MAX; - for (int i = 0; i < SERVERCOUNT; i++) { - if (!timeout[i] && (latency[i] < minLatency)) { - minLatency = latency[i]; - this->timeServerIdx = i; - } - } - INFO_VAR("NTP minimum latency:", minLatency / NTP_OVERSAMPLE); - if (this->isServerSelected()) { - INFO_VAR("NTP latency test finished, fastest is:", TIME_SERVER_LIST[this->timeServerIdx]); - return true; - } else { - ERROR_LOG("NTP latency test finished, no suitable server found!"); - return false; - } + sprintf(timestamp, "%04d-%02d-%02dT%02d:%02d:%02dZ", YEAR_2K + d.getYear(), + d.getMonth(), d.getDay(), d.getHour(), d.getMinutes(), + d.getSeconds()); + return String(timestamp); } \ No newline at end of file diff --git a/src/core/CoolTime.h b/src/core/CoolTime.h index ee2c909f..e1935d29 100644 --- a/src/core/CoolTime.h +++ b/src/core/CoolTime.h @@ -26,53 +26,24 @@ #include +#include #include -#include +#include #include -#include -#include - -#define NTP_PACKET_SIZE 48 -#define SERVERCOUNT 6 -#define NTP_OVERSAMPLE 3 -#define TIMEOUT 2000 -#define SECONDS_IN_WEEK 604800 +#define NTP_TIMEOUT_MS 15000 +#define YEAR_2K 2000 class CoolTime { - public: void begin(); - void offGrid(); - void update(); - bool config(bool overwrite = false); - void printConf(); + bool sync(); + void printStatus(); void setDateTime(int year, int month, int day, int hour, int minutes, int seconds); - tmElements_t getTimeDate(); - String getESDate(); - unsigned long getLastSyncTime(); - bool isTimeSync(unsigned long seconds = SECONDS_IN_WEEK); - time_t getNtpTime(); - void sendNTPpacket(IPAddress &address); - String formatDigits(int digits); - bool selectTimeServer(); - bool isServerSelected() const; - -private: - unsigned long timeSync = 0; - int8_t timeServerIdx = -1; - const char *TIME_SERVER_LIST[SERVERCOUNT] = { - "africa.pool.ntp.org", "asia.pool.ntp.org", - "europe.pool.ntp.org", "north-america.pool.ntp.org", - "oceania.pool.ntp.org", "south-america.pool.ntp.org"}; - bool NTP = true; - bool compileTime = false; - WiFiUDP Udp; - unsigned int localPort = 0; - byte packetBuffer[NTP_PACKET_SIZE]; - tmElements_t tmSet; - DS1337RTC rtc; + String getIso8601DateTime(); + DS1337 rtc; + static bool ntpSync; }; -#endif +#endif \ No newline at end of file diff --git a/src/core/CoolWifi.cpp b/src/core/CoolWifi.cpp index 71acd93d..ee88a9c5 100644 --- a/src/core/CoolWifi.cpp +++ b/src/core/CoolWifi.cpp @@ -32,20 +32,21 @@ #include #define MAX_WIFI_NETWORKS 10 +#define WIFI_CONNECT_TIMEOUT_DECISECONDS 300 -wl_status_t CoolWifi::state() { return (WiFi.status()); } - -wl_status_t CoolWifi::disconnect() { - WiFi.disconnect(); - DEBUG_VAR("Wifi status:", WiFi.status()); - return (WiFi.status()); -} - -wl_status_t CoolWifi::connect() { +void CoolWifi::connect() { this->config(); INFO_LOG("Wifi connecting..."); - this->connectWifiMulti(); - return (WiFi.status()); + int i = 0; + DEBUG_VAR("Entry time to Wifi connection attempt:", millis()); + while ((this->wifiMulti.run() != WL_CONNECTED) && + (i < WIFI_CONNECT_TIMEOUT_DECISECONDS)) { + i++; + delay(100); + } + DEBUG_VAR("Exit time from Wifi connection attempt:", millis()); + + printStatus(WiFi.status()); } void CoolWifi::printStatus(wl_status_t status) { @@ -77,22 +78,11 @@ void CoolWifi::printStatus(wl_status_t status) { } } -wl_status_t CoolWifi::connectWifiMulti() { - int i = 0; - - DEBUG_VAR("Entry time to Wifi connection attempt:", millis()); - while ((this->wifiMulti.run() != WL_CONNECTED) && (i < 300)) { - i++; - delay(100); - } - DEBUG_VAR("Exit time from Wifi connection attempt:", millis()); - - wl_status_t status = WiFi.status(); - printStatus(status); - return (status); -} - -wl_status_t CoolWifi::connectAP() { +void CoolWifi::startAccessPoint(CoolBoardLed &led) { + WiFi.disconnect(); + delay(200); + led.write(FUCHSIA); + delay(500); WiFiManager wifiManager; String tempMAC = WiFi.macAddress(); @@ -106,13 +96,15 @@ wl_status_t CoolWifi::connectAP() { wl_status_t status = WiFi.status(); if (status == WL_CONNECTED) { + led.blink(GREEN, 5); INFO_VAR("Wifi network selected:", WiFi.SSID()); this->addWifi(WiFi.SSID(), WiFi.psk()); } else { + led.blink(RED, 10); ERROR_LOG("No Wifi network was configured."); } printStatus(status); - return (status); + yield(); } bool CoolWifi::config() { @@ -172,17 +164,15 @@ bool CoolWifi::addWifi(String ssid, String pass) { return (true); } -String CoolWifi::getExternalIP() { +bool CoolWifi::getPublicIp(String &ip) { HTTPClient http; - String ip; http.begin("http://api.ipify.org/"); if (http.GET() == HTTP_CODE_OK) { ip = http.getString(); - DEBUG_VAR("Public IP address:", ip); + return (true); } - // return only the IP in the string - return (ip); + return (false); } void CoolWifi::printConf(String ssidList[]) { diff --git a/src/core/CoolWifi.h b/src/core/CoolWifi.h index b27befb5..d941dec0 100644 --- a/src/core/CoolWifi.h +++ b/src/core/CoolWifi.h @@ -26,6 +26,7 @@ #include #include +#include "CoolBoardLed.h" class CoolWifi { @@ -33,12 +34,9 @@ class CoolWifi { ESP8266WiFiMulti wifiMulti; static void printStatus(wl_status_t status); bool config(); - wl_status_t connect(); - wl_status_t connectWifiMulti(); - wl_status_t connectAP(); - wl_status_t state(); - wl_status_t disconnect(); - String getExternalIP(); + void connect(); + void startAccessPoint(CoolBoardLed &led); + bool getPublicIp(String &ip); uint8_t wifiCount = 0; private: bool addWifi(String ssid, String pass);