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.gzAdd pump
client_pump/client_pump.ino | 244 ++++++++++++++++++++++++++++++++++++++++ client_sensor/client_sensor.ino | 13 ++- globals.cpp | 2 + globals.h | 2 + 4 files changed, 258 insertions(+), 3 deletions(-) diff --git a/client_pump/client_pump.ino b/client_pump/client_pump.ino new file mode 100644 index 0000000..c175419 --- /dev/null +++ b/client_pump/client_pump.ino @@ -0,0 +1,244 @@ +#include "globals.h" +#include "secrets.h" + +#include <Arduino.h> +#include <WiFi.h> +#include <esp_wifi.h> +#include <esp_now.h> +#include <esp_sleep.h> +#include <sys/time.h> + + +#include "esp_system.h" +extern "C" esp_err_t esp_base_mac_addr_set(const uint8_t mac[6]); + +long long next_message_unix = 0; +bool go_to_sleep = false; + + +enum PumpSatatus_e { + INACTIVE, + START, + RUNNING, + FINISHED +}; + +struct PumpConfig_s { + char pump_name[32]; + int pumping_delay_sec; + int pumping_duration; + long long next_message_unix; + long long pumping_started_at_unix; + PumpSatatus_e pumping_status; +}; + +#define PUMP_COUNT 2 +PumpConfig_s pump_configs[PUMP_COUNT] = { + {"pump_1", 0, 0, 0, 0, INACTIVE}, + {"pump_2", 0, 0, 0, 0, INACTIVE}, +}; + +// ---------------------------------------------------------------------------- +// Time handling +// ---------------------------------------------------------------------------- + +int64_t get_CurrentUnix() { + return (int64_t)time(nullptr); +} + +void setup_Timezone() { + setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1); + tzset(); +} + +void set_LocalTime(long long unix_now) { + struct timeval tv; + tv.tv_sec = unix_now; // seconds since epoch + tv.tv_usec = 0; // microseconds + + settimeofday(&tv, nullptr); // set system time +} + +// ---------------------------------------------------------------------------- +// WiFi handling +// ---------------------------------------------------------------------------- +int32_t get_WiFiChannel() { + DBG_PRINTLN(ssid); + int32_t n = WiFi.scanNetworks(); + if (n <= 0) return 0; + for (uint8_t i = 0; i < n; i++) { + if (!strcmp(ssid, WiFi.SSID(i).c_str())) return WiFi.channel(i); + } + return 0; +} + +void init_WiFi() { + WiFi.mode(WIFI_MODE_STA); + int32_t channel = get_WiFiChannel(); + if (channel > 0) { + esp_wifi_set_promiscuous(true); + esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); + esp_wifi_set_promiscuous(false); + } +} + + +// ---------------------------------------------------------------------------- +// ESP-NOW handling +// ---------------------------------------------------------------------------- + +void on_DataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) { + if (!info || !data || len <= 0) return; + if (memcmp(info->src_addr, PEER_SENSOR, 6) != 0) return; + + PumpConfig_s pump_config; + char msg[64]; + long long unix_now = 0; + memcpy(msg, data, len); + msg[len] = '\0'; + + DBG_PRINTF("Received from server: %s\n", msg); + + if (strncmp(msg, "p:", 2) == 0) { + + int n = sscanf(msg, "p:%lld:%31[^:]:%d:%d:%lld", + &unix_now, + pump_config.pump_name, + &pump_config.pumping_delay_sec, + &pump_config.pumping_duration, + &pump_config.next_message_unix); + + if (n == 5) { + for (size_t i = 0; i < PUMP_COUNT; i++) { + if (strcmp(pump_configs[i].pump_name, pump_config.pump_name) != 0) + continue; + + memcpy(&pump_configs[i], &pump_config, sizeof(PumpConfig_s)); + next_message_unix = pump_config.next_message_unix; + + DBG_PRINTF("unix_now: %lld\n", unix_now); + DBG_PRINTF("pump_name: %s\n", pump_configs[i].pump_name); + DBG_PRINTF("pumping_delay_sec: %d\n", pump_configs[i].pumping_delay_sec); + DBG_PRINTF("pumping_duration: %d\n", pump_configs[i].pumping_duration); + DBG_PRINTF("next_message_unix: %lld\n", pump_configs[i].next_message_unix); + + set_LocalTime(unix_now); + + if (pump_configs[i].pumping_duration > 0) { + pump_configs[i].pumping_status = START; + } else { + go_to_sleep = true; + } + break; + } + } else { + DBG_PRINTLN("Failed to parse message"); + } + } +} + + +void init_EspNow() { + if (esp_now_init() != ESP_OK) { + DBG_PRINTLN("ESP NOW init failed"); + while (true); + } + esp_now_register_recv_cb(on_DataRecv); +} + + +// ---------------------------------------------------------------------------- +// Deep sleep scheduler +// ---------------------------------------------------------------------------- + +void sleep_UntilNextSend(int64_t next_read) { + go_to_sleep = false; + + 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); + + + // Prevent sleeping if next send is within g_min_sleep_time + if (min_delay_ms < g_min_sleep_time) { + DBG_PRINTF("Delaying for %u ms (no deep sleep)\n", min_delay_ms); + DBG_PRINTF("######################\n\n"); + delay(min_delay_ms); + return; + } + + uint32_t prep_time_ms = g_setup_duration + g_wakeup_earlier_pump; + uint32_t sleep_time_ms = (min_delay_ms > prep_time_ms) + ? (min_delay_ms - prep_time_ms) + : 0; + + DBG_PRINTF("Sleeping for %u ms (setup margin: %u ms)\n", + sleep_time_ms, prep_time_ms); + DBG_PRINTF("######################\n\n"); + + esp_sleep_enable_timer_wakeup((uint64_t)sleep_time_ms * 1000ULL); + esp_deep_sleep_start(); +} + + + + +void start_Pump(PumpConfig_s& pump_data) { + pump_data.pumping_started_at_unix = get_CurrentUnix(); + pump_data.pumping_status = RUNNING; + DBG_PRINTF("Starting Pump %s\n", pump_data.pump_name); + // whatever we have to do to make the pump running +} + +void stop_Pump(PumpConfig_s& pump_data) { + pump_data.pumping_status = INACTIVE; + DBG_PRINTF("Stopped Pump %s\n", pump_data.pump_name); + // whatever we have to do to stop the pump +} + +void setup() { + uint32_t setup_start = millis(); + go_to_sleep = false; + + DBG_BEGIN(); + + esp_base_mac_addr_set(PEER_PUMP); + + setup_Timezone(); + init_WiFi(); + init_EspNow(); + + g_setup_duration = millis() - setup_start; + DBG_PRINTF("Setup duration: %u ms\n", g_setup_duration); +} + + +void loop() { + bool pumping = false; + for (size_t i = 0; i < PUMP_COUNT; i++) { + PumpConfig_s& cfg = pump_configs[i]; + + if (START == cfg.pumping_status) { + + start_Pump(cfg); + pumping = true; + + } else + if (RUNNING == cfg.pumping_status) { + + long long stop_time = cfg.pumping_started_at_unix + cfg.pumping_duration; + long long now = get_CurrentUnix(); + if (stop_time <= now) + stop_Pump(cfg); + else pumping = true; + + } + } + if (go_to_sleep && !pumping) + sleep_UntilNextSend(next_message_unix); +} diff --git a/client_sensor/client_sensor.ino b/client_sensor/client_sensor.ino index d2269d6..a7d0c1a 100644 --- a/client_sensor/client_sensor.ino +++ b/client_sensor/client_sensor.ino @@ -1,5 +1,8 @@ #include "ClientSensor.h" +#include "esp_system.h" +extern "C" esp_err_t esp_base_mac_addr_set(const uint8_t mac[6]); + // this is going to be a sensor read value! // RTC_DATA_ATTR is not required than! @@ -12,6 +15,9 @@ void setup() { uint32_t setup_start = millis(); DBG_BEGIN(); + + esp_base_mac_addr_set(PEER_SENSOR); + setup_Timezone(); init_WiFi(); init_EspNow(); @@ -43,7 +49,7 @@ void setup() { void loop() { DBG_PRINTF("## Unix time: %lld\n", get_CurrentUnix()); for (size_t config_id = 0; config_id < SENSOR_CONFIG_COUNT; config_id++) { - SensorConfig_s &peer = sensor_configs[config_id]; + SensorConfig_s& peer = sensor_configs[config_id]; DBG_PRINTF("LOOP checking config: %s\n", peer.sensor_name); print_SensorConfig(peer); if ( is_TimeToReadSensor(config_id) ) { @@ -60,8 +66,9 @@ void loop() { peer.next_sensor_read = get_NextSensorReadFor(sensor_configs[config_id].sensor_name); - // send to server and configured peer (e.g. pump) - send_DataToPeer(config_id, value_to_send); + // set to true if value says we should pump + bool shall_pump = fake_value_to_send % 2; + send_DataToPumpAndServer(config_id, value_to_send, shall_pump); fake_value_to_send++; } print_SensorConfig(peer); diff --git a/globals.cpp b/globals.cpp index b97506e..e3abcde 100644 --- a/globals.cpp +++ b/globals.cpp @@ -3,6 +3,7 @@ uint32_t g_setup_duration = 0; uint32_t g_wakeup_earlier_server = 20000; uint32_t g_wakeup_earlier_sensor = 2000; +uint32_t g_wakeup_earlier_pump = 2000; SensorEntry g_buffer[BUFFER_SIZE]; uint8_t g_buffer_count = 0; @@ -20,6 +21,7 @@ const uint32_t g_min_sleep_time = 60000; RTC_DATA_ATTR bool all_init_configs_received = false; constexpr uint8_t PEER_SERVER[6] = {0xc2,0xde,0x67,0x2a,0x1e,0x29}; +constexpr uint8_t PEER_SENSOR[6] = {0xc2,0xc2,0x80,0x2f,0xfe,0x28}; constexpr uint8_t PEER_PUMP[6] = {0xc2,0xc1,0x79,0x1e,0xed,0x18}; SensorConfig_s sensor_configs[] = { diff --git a/globals.h b/globals.h index 5816a53..de85a5c 100644 --- a/globals.h +++ b/globals.h @@ -38,6 +38,7 @@ extern uint32_t g_setup_duration; // getting set automaticall in setup so we know how long it takes extern uint32_t g_wakeup_earlier_server; // some safety space if setup takes longere (maybe 20sec safety?) extern uint32_t g_wakeup_earlier_sensor; // some safety space if setup takes longere (maybe 20sec safety?) +extern uint32_t g_wakeup_earlier_pump; // some safety space if setup takes longere (maybe 20sec safety?) struct SensorConfigServer_s { char sensor_name[32]; @@ -99,6 +100,7 @@ extern bool g_received_ok_from_server; extern const uint32_t g_min_sleep_time; extern RTC_DATA_ATTR bool all_init_configs_received; extern const uint8_t PEER_SERVER[]; +extern const uint8_t PEER_SENSOR[]; extern const uint8_t PEER_PUMP[]; extern RTC_DATA_ATTR SensorConfig_s sensor_configs[]; extern uint8_t config_request_attempts_max;