Skip to content

Commit

Permalink
Prevent 50 day overflow in various(not all) timing related places.
Browse files Browse the repository at this point in the history
This requires a fallback implementation for the ESP8266.
  • Loading branch information
ToMe25 committed Sep 15, 2024
1 parent 03ac904 commit c109c35
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 35 deletions.
6 changes: 3 additions & 3 deletions lib/fallback_log/include/fallback_log.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* The MIT license can be found in the project root and at https://opensource.org/licenses/MIT.
*/

#ifndef SRC_FALLBACK_LOG_H_
#define SRC_FALLBACK_LOG_H_
#ifndef LIB_FALLBACK_TIMER_INCLUDE_FALLBACK_LOG_H_
#define LIB_FALLBACK_TIMER_INCLUDE_FALLBACK_LOG_H_

#include <cstdio>
#include <utils.h>
Expand Down Expand Up @@ -77,4 +77,4 @@
#endif
#endif

#endif /* SRC_FALLBACK_LOG_H_ */
#endif /* LIB_FALLBACK_TIMER_INCLUDE_FALLBACK_LOG_H_ */
5 changes: 5 additions & 0 deletions lib/fallback_timer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Fallback Timer
This tiny library contains an ESP8266 NonOS definition of the ESP RTOS `esp_timer_get_time` function.
This fallback version only works on the ESP8266 NonOS SDK.

**Warning**: This implementation only works if its called more than once every hour.
43 changes: 43 additions & 0 deletions lib/fallback_timer/include/fallback_timer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* fallback_timer.h
*
* This file contains the header definition for an ESP8266 NonOS fallback definition of the ESP RTOS esp_timer_get_time function.
*
* This fallback relies on being called more than once each hour to function.
*
* Created on: Sep 15, 2024
*
* Copyright (C) 2023 ToMe25.
* This project is licensed under the MIT License.
* The MIT license can be found in the project root and at https://opensource.org/licenses/MIT.
*/

#ifndef LIB_FALLBACK_TIMER_INCLUDE_FALLBACK_TIMER_H_
#define LIB_FALLBACK_TIMER_INCLUDE_FALLBACK_TIMER_H_

// Platform IO seems to ignore the platform specification in a local library.json.
#ifdef ESP8266

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* Returns the time in microseconds since the ESP was started.
*
* Needs to be called at least once each hour to function.
*
* @return The time in microseconds since the ESP first started.
*/
int64_t esp_timer_get_time();


#ifdef __cplusplus
}
#endif

#endif /* ESP8266 */

#endif /* LIB_FALLBACK_TIMER_INCLUDE_FALLBACK_TIMER_H_ */
7 changes: 7 additions & 0 deletions lib/fallback_timer/library.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "FallbackTimer",
"description": "A simple library containing a fallback definition of esp_timer_get_time.",
"version": "1.0.0",
"license": "MIT",
"platforms": [ "espressif8266" ]
}
40 changes: 40 additions & 0 deletions lib/fallback_timer/src/fallback_timer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* fallback_timer.h
*
* This file contains the implementation for an ESP8266 NonOS fallback definition of the ESP RTOS esp_timer_get_time function.
*
* This fallback relies on being called more than once each hour to function.
*
* Created on: Sep 15, 2024
*
* Copyright (C) 2023 ToMe25.
* This project is licensed under the MIT License.
* The MIT license can be found in the project root and at https://opensource.org/licenses/MIT.
*/

// Platform IO seems to ignore the platform specification in a local library.json.
#ifdef ESP8266

#include "fallback_timer.h"
#include <user_interface.h>

/**
* The current time collected when esp_timer_get_time was last called.
*/
uint32_t last_time = 0;

/**
* The number of times the current system time overflowed already.
*/
uint32_t time_overflows = 0;

int64_t esp_timer_get_time() {
const uint32_t now = system_get_time();
if (now < last_time) {
time_overflows++;
}
last_time = now;
return (((int64_t) time_overflows) << 32) + now;
}

#endif /* ESP8266 */
23 changes: 14 additions & 9 deletions src/AsyncTrackingFallbackWebHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#endif
#include <utils.h>
#include <fallback_log.h>
#ifdef ESP8266
#include <fallback_timer.h>
#endif

web::AsyncTrackingFallbackWebHandler::AsyncTrackingFallbackWebHandler(
const String &uri, HTTPFallbackRequestHandler fallback) :
Expand All @@ -27,11 +30,13 @@ web::AsyncTrackingFallbackWebHandler::~AsyncTrackingFallbackWebHandler() {

}

web::HTTPRequestHandler web::AsyncTrackingFallbackWebHandler::_getHandler(const WebRequestMethod method) const {
web::HTTPRequestHandler web::AsyncTrackingFallbackWebHandler::_getHandler(
const WebRequestMethod method) const {
return _handlers[utils::get_msb((WebRequestMethodComposite) method)];
}

void web::AsyncTrackingFallbackWebHandler::setHandler(const WebRequestMethodComposite methods, HTTPRequestHandler handler) {
void web::AsyncTrackingFallbackWebHandler::setHandler(
const WebRequestMethodComposite methods, HTTPRequestHandler handler) {
for (size_t i = 0; i < _handlers.size(); i++) {
if (methods & (1 << i)) {
_handlers[i] = handler;
Expand Down Expand Up @@ -66,7 +71,7 @@ bool web::AsyncTrackingFallbackWebHandler::canHandle(AsyncWebServerRequest *requ
}

void web::AsyncTrackingFallbackWebHandler::handleRequest(AsyncWebServerRequest *request) {
const size_t start = micros();
const uint64_t start = (uint64_t) esp_timer_get_time();
ResponseData response(request->beginResponse(500), 0, 500);
HTTPRequestHandler handler = _getHandler((WebRequestMethod) request->method());
if (handler) {
Expand All @@ -76,19 +81,19 @@ void web::AsyncTrackingFallbackWebHandler::handleRequest(AsyncWebServerRequest *
delete response.response;
response = _fallbackHandler(getHandledMethods(), request);
} else {
log_w("The handler for uri \"%s\" didn't have a handler for request type %s, and didn't have a fallback handler.",
log_w(
"The handler for uri \"%s\" didn't have a handler for request type %s, and didn't have a fallback handler.",
_uri.c_str(), request->methodToString());
}
#if ENABLE_PROMETHEUS_PUSH == 1 || ENABLE_PROMETHEUS_SCRAPE_SUPPORT == 1
prom::http_requests_total[request->url()][ {
(WebRequestMethod) request->method(), response.status_code }]++;
#endif
const size_t mid = micros();
const uint64_t mid = (uint64_t) esp_timer_get_time();
request->send(response.response);
const size_t end = micros();
log_d("Handling a request to \"%s\" took %luus + %luus.",
request->url().c_str(), (long unsigned int ) (mid - start),
(long unsigned int ) (end - mid));
const uint64_t end = (uint64_t) esp_timer_get_time();
log_d("Handling a request to \"%s\" took %lluus + %lluus.",
request->url().c_str(), (mid - start), (end - mid));
}

bool web::AsyncTrackingFallbackWebHandler::isRequestHandlerTrivial() {
Expand Down
7 changes: 4 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
#if ENABLE_ARDUINO_OTA == 1
#include <ArduinoOTA.h>
#endif
#if defined(ESP8266)
#if ESP8266
#include <dhcpserver.h>
#include <fallback_timer.h>
#endif
#include <fallback_log.h>

Expand Down Expand Up @@ -229,7 +230,7 @@ void onWiFiEvent(WiFiEvent_t id) {
#endif

void loop() {
const uint64_t start = millis();
const uint64_t start = (uint64_t) esp_timer_get_time() / 1000;

if (loop_iterations % 4 == 0) {
if (sensors::SENSOR_HANDLER.getTimeSinceMeasurement() == -1
Expand Down Expand Up @@ -301,7 +302,7 @@ void loop() {
mqtt::loop();

loop_iterations++;
const uint64_t end = millis();
const uint64_t end = (uint64_t) esp_timer_get_time() / 1000;
delay(max(0, 500 - int16_t(end - start)));
}

Expand Down
6 changes: 3 additions & 3 deletions src/prometheus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

#include "prometheus.h"
#if ENABLE_DEEP_SLEEP_MODE == 1
#if ENABLE_DEEP_SLEEP_MODE == 1 || ENABLE_PROMETHEUS_PUSH == 1
#include "main.h"
#endif
#include "sensor_handler.h"
Expand Down Expand Up @@ -378,7 +378,7 @@ void prom::pushMetrics() {
}

#if ENABLE_DEEP_SLEEP_MODE != 1
uint64_t now = millis();
const uint64_t now = (uint64_t) esp_timer_get_time() / 1000;
if (now - last_push >= PROMETHEUS_PUSH_INTERVAL * 1000) {
#endif
tcpClient = new AsyncClient();
Expand Down Expand Up @@ -423,7 +423,7 @@ void prom::pushMetrics() {
uint32_t code = atoi(status_code);
if (code == 200) {
#if ENABLE_DEEP_SLEEP_MODE != 1
uint64_t now = millis();
const uint64_t now = (uint64_t) esp_timer_get_time() / 1000;

if (now - last_push >= (PROMETHEUS_PUSH_INTERVAL + 10) * 1000) {
log_i("Successfully pushed again after %lums.", now - last_push);
Expand Down
11 changes: 7 additions & 4 deletions src/sensor_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#elif SENSOR_TYPE == SENSOR_TYPE_DALLAS
#include "sensors/DallasHandler.h"
#endif
#ifdef ESP8266
#include <fallback_timer.h>
#endif

namespace sensors {

Expand Down Expand Up @@ -43,13 +46,13 @@ const std::string SensorHandler::getLastHumidityString() {
}

int64_t SensorHandler::getTimeSinceMeasurement() {
uint32_t now = millis();
const uint64_t now = (uint64_t) esp_timer_get_time() / 1000;
if (_last_finished_request == -1) {
return -1;
} else if (_last_finished_request < 0) {
log_d("Invalid time of last measurement: %lldms.", _last_finished_request);
return -1;
} else if (_last_finished_request > now) {
} else if ((uint64_t) _last_finished_request > now) {
log_d("Invalid time since last measurement: %lldms.", now - _last_finished_request);
return -1;
}
Expand All @@ -58,13 +61,13 @@ int64_t SensorHandler::getTimeSinceMeasurement() {
}

int64_t SensorHandler::getTimeSinceValidMeasurement() {
uint32_t now = millis();
const uint64_t now = (uint64_t) esp_timer_get_time() / 1000;
if (_last_valid_request == -1) {
return -1;
} else if (_last_valid_request < 0) {
log_d("Invalid time of last valid measurement: %lldms.", _last_valid_request);
return -1;
} else if (_last_valid_request > now) {
} else if ((uint64_t) _last_valid_request > now) {
log_d("Invalid time since last valid measurement: %lldms.", now - _last_valid_request);
return -1;
}
Expand Down
11 changes: 7 additions & 4 deletions src/sensors/DHTHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

#include "sensors/DHTHandler.h"
#include <fallback_log.h>
#ifdef ESP8266
#include <fallback_timer.h>
#endif

namespace sensors {

Expand All @@ -27,8 +30,8 @@ bool DHTHandler::begin() {
}

bool DHTHandler::requestMeasurement() {
const uint32_t now = millis();
if (_last_request == -1 || now - (uint32_t) _last_request >= MIN_INTERVAL) {
const uint64_t now = (uint64_t) esp_timer_get_time() / 1000;
if (_last_request == -1 || now - (uint64_t) _last_request >= MIN_INTERVAL) {
_last_request = now;
if (!_dht.read(false)) {
_temperature = _humidity = NAN;
Expand All @@ -52,8 +55,8 @@ bool DHTHandler::requestMeasurement() {
return true;
} else {
log_i("Attempted to read sensor data before minimum delay.");
log_d("Min delay: %ums, Time since measurement: %ums",
(uint32_t) MIN_INTERVAL, (now - (uint32_t) _last_request));
log_d("Min delay: %hums, Time since measurement: %llums",
MIN_INTERVAL, (now - (uint64_t) _last_request));
return false;
}
}
Expand Down
19 changes: 12 additions & 7 deletions src/sensors/DallasHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

#include "sensors/DallasHandler.h"
#include <fallback_log.h>
#ifdef ESP8266
#include <fallback_timer.h>
#endif

namespace sensors {

Expand Down Expand Up @@ -40,12 +43,12 @@ bool DallasHandler::begin() {
}

bool DallasHandler::requestMeasurement() {
uint32_t now = millis();
if (_last_request == -1 || now - (uint32_t) _last_request >= MIN_INTERVAL) {
uint64_t now = (uint64_t) esp_timer_get_time() / 1000;
if (_last_request == -1 || now - (uint64_t) _last_request >= MIN_INTERVAL) {
// Read previous measurements, if they weren't read yet.
if (_last_request > _last_finished_request) {
getTemperature();
now = millis();
now = (uint64_t) esp_timer_get_time() / 1000;
}

_last_request = now;
Expand All @@ -66,8 +69,8 @@ bool DallasHandler::requestMeasurement() {
return true;
} else {
log_i("Attempted to read sensor data before minimum delay.");
log_d("Min delay: %ums, Time since measurement: %ums",
(uint32_t) MIN_INTERVAL, (now - (uint32_t) _last_request));
log_d("Min delay: %hums, Time since measurement: %llums", MIN_INTERVAL,
(now - (uint64_t) _last_request));
return false;
}
}
Expand All @@ -77,8 +80,9 @@ bool DallasHandler::supportsTemperature() const {
}

float DallasHandler::getTemperature() {
const uint64_t now = (uint64_t) esp_timer_get_time() / 1000;
if (_last_request > _last_finished_request
&& millis() - (uint32_t) _last_request >= MIN_INTERVAL) {
&& now - (uint64_t) _last_request >= MIN_INTERVAL) {
DeviceAddress address;
if (!_sensors.getAddress(address, SENSOR_INDEX)) {
log_w("Failed to get address for sensor %u.", SENSOR_INDEX);
Expand Down Expand Up @@ -116,8 +120,9 @@ float DallasHandler::getTemperature() {
}

float DallasHandler::getLastTemperature() {
const uint64_t now = (uint64_t) esp_timer_get_time() / 1000;
if (_last_request > _last_finished_request
&& millis() - _last_request >= MIN_INTERVAL) {
&& now - (uint64_t) _last_request >= MIN_INTERVAL) {
getTemperature();
}
return _last_valid_temperature;
Expand Down
6 changes: 4 additions & 2 deletions src/webhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#include <ESP8266mDNS.h>
#endif
#include <fallback_log.h>
#if ENABLE_TIMINGS_API == 1 && defined(ESP8266)
#include <fallback_timer.h>
#endif
#endif /* ENABLE_WEB_SERVER == 1 */

#if ENABLE_WEB_SERVER == 1
Expand Down Expand Up @@ -80,7 +83,7 @@ void web::setup() {
#if ENABLE_TIMINGS_API == 1
registerRequestHandler("/timings/since_startup_ms", HTTP_GET,
[](AsyncWebServerRequest *request) -> ResponseData {
const String str = String(millis());
const String str = String(esp_timer_get_time() / 1000);
AsyncWebServerResponse *response = request->beginResponse(200,
"text/plain", str.c_str());
response->addHeader("Cache-Control", CACHE_CONTROL_NOCACHE);
Expand Down Expand Up @@ -110,7 +113,6 @@ void web::setup() {
registerStaticHandler("/timings/info", "text/plain",
"This directory contains various timing informations.\n"
"A list of these endpoints is currently not available.\n"
"The precision of these timings may not be ideal because the millis function returns an unsigned 32 bit interger that wraps after ~50 days.\n"
"All endpoints return values in milliseconds.");

registerRedirect("/timings", "/timings/info");
Expand Down

0 comments on commit c109c35

Please sign in to comment.