esp_garden git · master
Distributed ESP sensor and actuator system (garden)
C++ 70.7% Markdown 18% C 10.7%git clone https://git.christianimmanuel.de/embedded/esp_garden.gitwget https://git.christianimmanuel.de/embedded/esp_garden/archive/esp_garden.tar.gzUpdate timing stuff
client_sensor/ClientSensor.cpp | 129 ++++++++++++++++++++++++---------------- client_sensor/ClientSensor.h | 7 ++- client_sensor/client_sensor.ino | 9 ++- globals.cpp | 4 +- globals.h | 1 + 5 files changed, 91 insertions(+), 59 deletions(-) diff --git a/client_sensor/ClientSensor.cpp b/client_sensor/ClientSensor.cpp index 4e1d088..22870c8 100644 --- a/client_sensor/ClientSensor.cpp +++ b/client_sensor/ClientSensor.cpp @@ -47,18 +47,19 @@ void init_WiFi() { uint8_t config_request_attempts = 0; -void printSensorConfig(const SensorConfig_s& cfg) { +void print_SensorConfig(const SensorConfig_s& cfg) { DBG_PRINTF("SensorConfig for '%s'\n", cfg.sensor_name); DBG_PRINTF(" is_active: %s\n", cfg.is_active ? "true" : "false"); DBG_PRINTF(" pin: %d\n", cfg.pin); DBG_PRINTF(" min_moisture: %d\n", cfg.min_moisture); DBG_PRINTF(" notification_delay_ms: %d\n", cfg.notification_delay_ms); DBG_PRINTF(" notification_name: %s\n", cfg.notification_name); - DBG_PRINTF(" pump_mac: %s\n", (char*)cfg.pump_mac); + DBG_PRINTF(" pump_mac: "); printMac(cfg.pump_mac); DBG_PRINTF(" delay_ms: %d\n", cfg.delay_ms); DBG_PRINTF(" use_time: %s\n", cfg.use_time ? "true" : "false"); DBG_PRINTF(" time_count: %u\n", cfg.time_count); - DBG_PRINTF(" last_sensor_read: %llu\n", cfg.last_sensor_read); + DBG_PRINTF(" last_sensor_read: %lld\n", cfg.last_sensor_read); + DBG_PRINTF(" next_sensor_read: %lld\n", cfg.next_sensor_read); if (cfg.time_count > 0) { DBG_PRINT(" times: "); @@ -323,12 +324,13 @@ bool send_AndWaitAck(const uint8_t* peer, const char* message, uint32_t timeout_ // ---------------------------------------------------------------------------- // Time handling // ---------------------------------------------------------------------------- -uint64_t get_CurrentUnixMs() { - time_t now_sec; - time(&now_sec); - struct timeval tv; - gettimeofday(&tv, nullptr); - return (uint64_t)tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL; + +int64_t get_CurrentUnix() { + return (int64_t)time(nullptr); +} + +int64_t get_CurrentUnixMs() { + return get_CurrentUnix() * 1000; } bool request_TimeSync(uint32_t timeout_ms) { @@ -348,7 +350,8 @@ void setup_Timezone() { tzset(); } -void get_LocalTime(time_t unix_time, int *hour, int *minute) { +void get_LocalTime(int *hour, int *minute) { + time_t unix_time = get_CurrentUnix(); struct tm tm_time; localtime_r(&unix_time, &tm_time); *hour = tm_time.tm_hour; @@ -356,46 +359,81 @@ void get_LocalTime(time_t unix_time, int *hour, int *minute) { } uint16_t get_CurrentMinutes() { - uint64_t t = get_CurrentUnixMs(); - if (t == 0) return 0; - time_t sec = t / 1000; int hour, minute; - get_LocalTime(sec, &hour, &minute); + get_LocalTime(&hour, &minute); return hour * 60 + minute; } // ---------------------------------------------------------------------------- // Deep sleep scheduler // ---------------------------------------------------------------------------- -void sleep_UntilNextSend() { - uint64_t ms_now = get_CurrentUnixMs(); - uint32_t min_delay_ms = UINT32_MAX; +int64_t getAndSet_NextSensorReadFor(const char* sensor_name) { + int64_t unix_now = get_CurrentUnix(); + uint16_t min_current = get_CurrentMinutes(); for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) { - SensorConfig_s & p = sensor_configs[i]; + SensorConfig_s &p = sensor_configs[i]; if (!p.is_active) continue; + if (strcmp(p.sensor_name, sensor_name) != 0) continue; + if (p.next_sensor_read > unix_now) + return p.next_sensor_read; + + int64_t next_time = INT64_MAX; - if (p.use_time) { - uint16_t min_current = get_CurrentMinutes(); + if (p.use_time && p.time_count > 0) { + uint32_t best_delta_min = 24 * 60; for (size_t j = 0; j < p.time_count; j++) { - int16_t min_delta = (int16_t)p.times[j] - (int16_t)min_current; - if (min_delta <= 0) min_delta += 24 * 60; // wrap to next day - uint32_t delta_ms = min_delta * 60 * 1000; - if (delta_ms < min_delay_ms) min_delay_ms = delta_ms; + int16_t delta = (int16_t)p.times[j] - (int16_t)min_current; + if (delta <= 0) delta += 24 * 60; + if ((uint32_t)delta < best_delta_min) + best_delta_min = delta; } - } else { - uint64_t last_read = p.last_sensor_read * 1000ULL; - uint32_t delta_ms = (p.delay_ms > (ms_now - last_read)) - ? (p.delay_ms - (ms_now - last_read)) - : 0; - if (delta_ms < min_delay_ms) min_delay_ms = delta_ms; + next_time = unix_now + (int64_t)best_delta_min * 60; + } else if (p.delay_ms > 0) { + next_time = p.last_sensor_read + (p.delay_ms / 1000); + } + + if (next_time != INT64_MAX) { + p.next_sensor_read = next_time; + return next_time; + } + } + + return -1; +} + +int64_t getAndSet_NextSensorReadsUnixTime() { + int64_t unix_now = get_CurrentUnix(); + int64_t unix_next = INT64_MAX; + unsigned long start = millis(); + + for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) { + SensorConfig_s &p = sensor_configs[i]; + if (!p.is_active) continue; + + int64_t next_time = getAndSet_NextSensorReadFor(p.sensor_name); + if (next_time > 0) { + // optional: compensate for function execution time + next_time -= (int64_t)((millis() - start) / 1000); + + if (next_time < unix_next) + unix_next = next_time; } } - if (min_delay_ms == UINT32_MAX) return; + return (unix_next == INT64_MAX) ? -1 : unix_next; +} + +void sleep_UntilNextSend(int64_t next_read) { + int64_t now_sec = get_CurrentUnix(); + + int64_t delta_sec = next_read - now_sec; + if (delta_sec <= 0) delta_sec = 0; + uint32_t min_delay_ms = delta_sec > UINT32_MAX / 1000 + ? UINT32_MAX + : (uint32_t)(delta_sec * 1000); - // i just need the next sensor read from here, the sleep part should get handled somewhere else as other esp need the next sensor read, sleep is just for that one. // Prevent sleeping if next send is within 1 minute if (min_delay_ms < 60 * 1000) { @@ -410,7 +448,7 @@ void sleep_UntilNextSend() { ? (min_delay_ms - prep_time_ms) : 0; - DBG_PRINTF("Sleeping for %u ms (prep margin: %u ms)\n", + DBG_PRINTF("Sleeping for %u ms (setup margin: %u ms)\n", sleep_time_ms, prep_time_ms); DBG_PRINTF("######################\n\n"); @@ -418,6 +456,7 @@ void sleep_UntilNextSend() { esp_deep_sleep_start(); } + // ---------------------------------------------------------------------------- // Request sensor config from server via ESP-NOW // ---------------------------------------------------------------------------- @@ -551,6 +590,8 @@ bool request_SensorConfig(const char* sensor_name) { } } + cfg->next_sensor_read = 0; + getAndSet_NextSensorReadFor(cfg->sensor_name); config_request_attempts = 0; return true; } @@ -620,35 +661,19 @@ void get_AllSensorConfigs() { // ---------------------------------------------------------------------------- bool is_TimeToReadSensor(size_t config_id) { - uint64_t ms_now = get_CurrentUnixMs(); - SensorConfig_s& p = sensor_configs[config_id]; DBG_PRINTF("check is_TimeToReadSensor: %s\n", p.sensor_name); - printSensorConfig(p); - if (!p.is_active) { + if (!p.is_active) { DBG_PRINTLN("Sensor is inactive!"); return false; } - if (p.use_time) { - uint16_t min_current = get_CurrentMinutes(); - for (size_t j = 0; j < p.time_count; j++) { - if (min_current == p.times[j] && - ms_now - (p.last_sensor_read * 1000ULL) > 60000) { - return true; - break; - } - } - } else { - //if (ms_now - last_sent_array[config_id] >= p.delay_ms) is_time_to_read = true; - if (ms_now - (p.last_sensor_read * 1000ULL) >= p.delay_ms) - return true; - } - return false; + return (p.next_sensor_read <= get_CurrentUnix()); } + void send_DataToPeer(size_t config_id, uint32_t value) { char message[64]; diff --git a/client_sensor/ClientSensor.h b/client_sensor/ClientSensor.h index 5d8aa45..b37918f 100644 --- a/client_sensor/ClientSensor.h +++ b/client_sensor/ClientSensor.h @@ -22,6 +22,7 @@ void init_WiFi(); // ---------------------------------------------------------------------------- // Peers // ---------------------------------------------------------------------------- +void print_SensorConfig(const SensorConfig_s& cfg); // ---------------------------------------------------------------------------- // Config to flash @@ -41,14 +42,16 @@ void init_EspNow(); // Time handling // ---------------------------------------------------------------------------- -uint64_t get_CurrentUnixMs(); +int64_t get_CurrentUnix(); +int64_t get_CurrentUnixMs(); bool request_TimeSync(uint32_t timeout_ms = 3000); void setup_Timezone(); // ---------------------------------------------------------------------------- // Deep sleep scheduler // ---------------------------------------------------------------------------- -void sleep_UntilNextSend(); +int64_t getAndSet_NextSensorReadsUnixTime(); +void sleep_UntilNextSend(int64_t next_read); // ---------------------------------------------------------------------------- // Request sensor config from server via ESP-NOW diff --git a/client_sensor/client_sensor.ino b/client_sensor/client_sensor.ino index 6e5194d..e126ab2 100644 --- a/client_sensor/client_sensor.ino +++ b/client_sensor/client_sensor.ino @@ -41,8 +41,9 @@ void setup() { // ---------------------------------------------------------------------------- void loop() { - DBG_PRINTF("## Unix time: %lld\n", (long long)get_CurrentUnixMs() / 1000); + DBG_PRINTF("## Unix time: %lld\n", get_CurrentUnix()); for (size_t config_id = 0; config_id < SENSOR_CONFIG_COUNT; config_id++) { + print_SensorConfig(sensor_configs[config_id]); if ( is_TimeToReadSensor(config_id) ) { /* * read sensor value @@ -52,7 +53,7 @@ void loop() { uint32_t value_to_send = fake_value_to_send; // set last read time - sensor_configs[config_id].last_sensor_read = (long long)get_CurrentUnixMs() / 1000; + sensor_configs[config_id].last_sensor_read = get_CurrentUnix(); // send to server and configured peer (e.g. pump) send_DataToPeer(config_id, value_to_send); @@ -70,5 +71,7 @@ void loop() { */ - sleep_UntilNextSend(); + int64_t next_read = getAndSet_NextSensorReadsUnixTime(); + + sleep_UntilNextSend(next_read); } diff --git a/globals.cpp b/globals.cpp index 11c7af4..e658ac8 100644 --- a/globals.cpp +++ b/globals.cpp @@ -22,8 +22,8 @@ constexpr uint8_t PEER_SERVER[] = {0x58,0xcf,0x79,0x1e,0xed,0x18}; constexpr uint8_t PEER_PUMP[] = {0x51,0xc1,0x79,0x1e,0xed,0x18}; RTC_DATA_ATTR SensorConfig_s sensor_configs[] = { - {"moisture_1", true, 11, 10, 0, "pump_1", {0}, 90000, true, {465, 466}, 2, 0}, - {"moisture_2", true, 11, 10, 0, "pump_2", {0}, 120000, false, {0}, 0, 0} + {"moisture_1", true, 11, 10, 0, "pump_1", {0}, 90000, true, {465, 466}, 2, 0, 0}, + {"moisture_2", true, 11, 10, 0, "pump_2", {0}, 120000, false, {0}, 0, 0, 0} }; uint8_t config_request_attempts_max = 3; diff --git a/globals.h b/globals.h index d0b4985..c3f787d 100644 --- a/globals.h +++ b/globals.h @@ -66,6 +66,7 @@ struct SensorConfig_s { uint16_t times[8]; uint8_t time_count; int64_t last_sensor_read; // Unix time + int64_t next_sensor_read; // Unix time }; struct SensorEntry {