#pragma once #include #include #ifndef USER_BTN_PRESSED #define USER_BTN_PRESSED LOW #endif #if defined(ESP_PLATFORM) #include #include #include #include #include "driver/rtc_io.h" class ESP32Board : public mesh::MainBoard { protected: uint8_t startup_reason; bool inhibit_sleep = false; public: void begin() { // for future use, sub-classes SHOULD call this from their begin() startup_reason = BD_STARTUP_NORMAL; #ifdef ESP32_CPU_FREQ setCpuFrequencyMhz(ESP32_CPU_FREQ); #endif #ifdef PIN_VBAT_READ // battery read support pinMode(PIN_VBAT_READ, INPUT); adcAttachPin(PIN_VBAT_READ); #endif #ifdef P_LORA_TX_LED pinMode(P_LORA_TX_LED, OUTPUT); digitalWrite(P_LORA_TX_LED, LOW); #endif #if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) #if PIN_BOARD_SDA >= 0 && PIN_BOARD_SCL >= 0 Wire.begin(PIN_BOARD_SDA, PIN_BOARD_SCL); #endif #else Wire.begin(); #endif } // Temperature from ESP32 MCU float getMCUTemperature() override { uint32_t raw = 0; // To get and average the temperature so it is more accurate, especially in low temperature for (int i = 0; i < 4; i++) { raw += temperatureRead(); } return raw / 4; } void enterLightSleep(uint32_t secs) { #if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(P_LORA_DIO_1) // Supported ESP32 variants if (rtc_gpio_is_valid_gpio((gpio_num_t)P_LORA_DIO_1)) { // Only enter sleep mode if P_LORA_DIO_1 is RTC pin esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // To wake up when receiving a LoRa packet if (secs > 0) { esp_sleep_enable_timer_wakeup(secs * 1000000); // To wake up every hour to do periodically jobs } esp_light_sleep_start(); // CPU enters light sleep } #endif } void sleep(uint32_t secs) override { if (!inhibit_sleep) { enterLightSleep(secs); // To wake up after "secs" seconds or when receiving a LoRa packet } } uint8_t getStartupReason() const override { return startup_reason; } #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on } void onAfterTransmit() override { digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #elif defined(P_LORA_TX_NEOPIXEL_LED) #define NEOPIXEL_BRIGHTNESS 64 // white brightness (max 255) void onBeforeTransmit() override { neopixelWrite(P_LORA_TX_NEOPIXEL_LED, NEOPIXEL_BRIGHTNESS, NEOPIXEL_BRIGHTNESS, NEOPIXEL_BRIGHTNESS); // turn TX neopixel on (White) } void onAfterTransmit() override { neopixelWrite(P_LORA_TX_NEOPIXEL_LED, 0, 0, 0); // turn TX neopixel off } #endif uint16_t getBattMilliVolts() override { #ifdef PIN_VBAT_READ analogReadResolution(12); uint32_t raw = 0; for (int i = 0; i < 4; i++) { raw += analogReadMilliVolts(PIN_VBAT_READ); } raw = raw / 4; return (2 * raw); #else return 0; // not supported #endif } const char* getManufacturerName() const override { return "Generic ESP32"; } void reboot() override { esp_restart(); } bool startOTAUpdate(const char* id, char reply[]) override; void setInhibitSleep(bool inhibit) { inhibit_sleep = inhibit; } }; class ESP32RTCClock : public mesh::RTCClock { Preferences _prefs; bool _prefs_ready = false; uint32_t _last_persisted_time = 0; unsigned long _last_persist_ms = 0; static constexpr uint32_t kDefaultEpoch = 1715770351; // 15 May 2024, 8:50pm static constexpr unsigned long kPersistIntervalMs = 15UL * 60UL * 1000UL; void applyTime(uint32_t time) { struct timeval tv; tv.tv_sec = time; tv.tv_usec = 0; settimeofday(&tv, NULL); } void persistCurrentTime(bool force) { if (!_prefs_ready) { return; } const unsigned long now_ms = millis(); const uint32_t now = getCurrentTime(); if (!force) { if (now == 0 || now == _last_persisted_time) { return; } if ((now_ms - _last_persist_ms) < kPersistIntervalMs) { return; } } _prefs.putULong("epoch", now); _last_persisted_time = now; _last_persist_ms = now_ms; } public: ESP32RTCClock() { } void begin() { _prefs_ready = _prefs.begin("mesh-clock", false); const uint32_t persisted_epoch = _prefs_ready ? _prefs.getULong("epoch", 0) : 0; esp_reset_reason_t reason = esp_reset_reason(); if (persisted_epoch > kDefaultEpoch) { applyTime(persisted_epoch); _last_persisted_time = persisted_epoch; _last_persist_ms = millis(); } else if (reason == ESP_RST_POWERON) { // start with some date/time in the recent past applyTime(kDefaultEpoch); } } uint32_t getCurrentTime() override { time_t _now; time(&_now); return _now; } void setCurrentTime(uint32_t time) override { applyTime(time); persistCurrentTime(true); } void tick() override { persistCurrentTime(false); } }; #endif