From 930b97ba67f6772f0067614ee2089bb8756e3587 Mon Sep 17 00:00:00 2001 From: wybran Date: Sat, 6 Dec 2025 09:42:40 +0100 Subject: [PATCH 01/19] feat: implement RTC and sleep manager functionality; refactor existing sleep manager code --- app/boards/nucleo_wl55jc.overlay | 7 +- app/prj.conf | 12 +- app/src/Application.cpp | 8 +- app/src/Application.hpp | 3 +- app/src/main.cpp | 4 +- app/src/peripherals/peripheral.hpp | 13 +- app/src/peripherals/rtc/rtc_peripheral.cpp | 105 +++++++++++++ app/src/peripherals/rtc/rtc_peripheral.hpp | 32 ++++ app/src/peripherals/sleep/sleep_manager.cpp | 57 ++++++++ app/src/peripherals/sleep/sleep_manager.hpp | 31 ++++ .../peripherals/sleep/sleep_manager_base.hpp | 32 ++++ .../peripherals/sleep/sleep_manager_esp.cpp | 92 ++++++++++++ .../peripherals/sleep/sleep_manager_esp.hpp | 30 ++++ .../peripherals/sleep/sleep_manager_stm.cpp | 138 ++++++++++++++++++ .../peripherals/sleep/sleep_manager_stm.hpp | 39 +++++ app/src/utils/sleep-manager.cpp | 93 ------------ app/src/utils/sleep-manager.hpp | 43 ------ 17 files changed, 583 insertions(+), 156 deletions(-) create mode 100644 app/src/peripherals/rtc/rtc_peripheral.cpp create mode 100644 app/src/peripherals/rtc/rtc_peripheral.hpp create mode 100644 app/src/peripherals/sleep/sleep_manager.cpp create mode 100644 app/src/peripherals/sleep/sleep_manager.hpp create mode 100644 app/src/peripherals/sleep/sleep_manager_base.hpp create mode 100644 app/src/peripherals/sleep/sleep_manager_esp.cpp create mode 100644 app/src/peripherals/sleep/sleep_manager_esp.hpp create mode 100644 app/src/peripherals/sleep/sleep_manager_stm.cpp create mode 100644 app/src/peripherals/sleep/sleep_manager_stm.hpp delete mode 100644 app/src/utils/sleep-manager.cpp delete mode 100644 app/src/utils/sleep-manager.hpp diff --git a/app/boards/nucleo_wl55jc.overlay b/app/boards/nucleo_wl55jc.overlay index 78486d8..7d68315 100644 --- a/app/boards/nucleo_wl55jc.overlay +++ b/app/boards/nucleo_wl55jc.overlay @@ -3,7 +3,8 @@ / { aliases { bme280-i2c = &i2c2; - }; + wkup-src = &user_button_1; + }; }; &i2c2 { @@ -18,3 +19,7 @@ status = "okay"; }; }; + +&pwr { + status = "okay"; +}; diff --git a/app/prj.conf b/app/prj.conf index 30ae509..452bd86 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -9,15 +9,15 @@ CONFIG_NEWLIB_LIBC=y CONFIG_NANOPB=y CONFIG_REBOOT=y +CONFIG_POWEROFF=y +CONFIG_STM32_WKUP_PINS=y CONFIG_I2C=y CONFIG_GPIO=y CONFIG_SPI=y -CONFIG_BQ274XX_PM=y - -CONFIG_PM_DEVICE=y -CONFIG_POWEROFF=y +CONFIG_UART_USE_RUNTIME_CONFIGURE=y +CONFIG_UART_ASYNC_API=y CONFIG_SENSOR=y CONFIG_SENSOR_ASYNC_API=y @@ -27,6 +27,7 @@ CONFIG_MAIN_STACK_SIZE=2048 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_LOG=y CONFIG_PRINTK=y +CONFIG_LOG_MODE_IMMEDIATE=y # Random number generator required for several LoRaWAN services CONFIG_ENTROPY_GENERATOR=y @@ -40,6 +41,9 @@ CONFIG_LORAWAN_NVM_SETTINGS=y CONFIG_LORAWAN_SERVICES=y CONFIG_LORAWAN_LOG_LEVEL_DBG=y +CONFIG_RTC=y +CONFIG_RTC_ALARM=y + CONFIG_NVS=y CONFIG_SETTINGS=y CONFIG_SETTINGS_RUNTIME=y diff --git a/app/src/Application.cpp b/app/src/Application.cpp index 178d363..c383118 100644 --- a/app/src/Application.cpp +++ b/app/src/Application.cpp @@ -5,7 +5,6 @@ #include "buzzverse/packet.pb.h" #include "peripherals/lorawan_handler/lorawan_handler.hpp" -#include "utils/sleep-manager.hpp" LOG_MODULE_REGISTER(application, CONFIG_APP_LOG_LEVEL); @@ -26,12 +25,9 @@ bool Application::init() { return false; } -#if defined(CONFIG_SOC_ESP32S3) if (m_sleep_manager && m_sleep_manager->is_ready()) { - esp_sleep_wakeup_cause_t cause = m_sleep_manager->get_wakeup_cause(); - LOG_INF("ESP32S3 Wakeup cause: %d", cause); + LOG_INF("Wakeup cause: %d", static_cast(m_sleep_manager->get_wakeup_cause())); } -#endif LOG_INF("Application core initialization complete."); return true; } @@ -117,7 +113,7 @@ void Application::enter_low_power_mode(int sleep_duration_ms) { if (!m_sleep_manager || !m_sleep_manager->is_ready()) { LOG_WRN("SleepManager not available/ready. Defaulting to k_sleep for %d ms.", sleep_duration_ms); - k_sleep(K_MSEC(sleep_duration_ms)); + k_msleep(sleep_duration_ms); sys_reboot(SYS_REBOOT_COLD); } diff --git a/app/src/Application.hpp b/app/src/Application.hpp index 2902b0f..07f6be4 100644 --- a/app/src/Application.hpp +++ b/app/src/Application.hpp @@ -1,14 +1,15 @@ #ifndef APPLICATION_HPP #define APPLICATION_HPP +#include #include "buzzverse/packet.pb.h" #include "sensor.hpp" +#include "peripherals/sleep/sleep_manager.hpp" // Number of supported sensor types used in the sensors array #define NUMBER_OF_SENSORS 1 class LoRaWANHandler; -class SleepManager; class Application { public: diff --git a/app/src/main.cpp b/app/src/main.cpp index bbc00dc..a7a3a13 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -6,10 +6,10 @@ #include "Application.hpp" #include "peripherals/lorawan_handler/lorawan_handler.hpp" +#include "peripherals/sleep/sleep_manager.hpp" #include "sensors/bme280/bme280.hpp" #include "sensors/bq27441/bq27441.hpp" #include "utils/banner.hpp" -#include "utils/sleep-manager.hpp" LOG_MODULE_REGISTER(main_entry, LOG_LEVEL_DBG); @@ -62,7 +62,7 @@ int main(void) { #else LOG_INF("Device sleep not enabled. Entering polling loop."); while (true) { - k_sleep(K_MSEC(APP_SLEEP_DURATION_MS)); + k_msleep(APP_SLEEP_DURATION_MS); app.run_cycle(); } #endif diff --git a/app/src/peripherals/peripheral.hpp b/app/src/peripherals/peripheral.hpp index 17c0c73..4731cc4 100644 --- a/app/src/peripherals/peripheral.hpp +++ b/app/src/peripherals/peripheral.hpp @@ -16,12 +16,13 @@ class Peripheral { * @brief Generic peripheral status codes */ enum class Status { - OK = 0, /**< Operation successful */ - INIT_ERR = -1, /**< Initialization failed */ - NOT_READY = -2, /**< Peripheral is not ready */ - ERROR_INVALID_PARAM = -3, /**< Invalid parameter provided */ - ERROR_NOT_INITIALIZED = -4, /**< Initialization failed */ - ERROR_HW_CONFIG_FAILED = -5, /**< Hardware configuration failed */ + OK = 0, /**< Operation successful */ + INIT_ERR = -1, /**< Initialization failed */ + NOT_READY = -2, /**< Peripheral is not ready */ + ERROR_INVALID_PARAM = -3, /**< Invalid parameter provided */ + ERROR_NOT_INITIALIZED = -4, /**< Initialization failed */ + ERROR_HW_CONFIG_FAILED = -5, /**< Hardware configuration failed */ + ERROR_ALREADY_INITIALIZED = -6 /**< Peripheral already initialized */ }; /** diff --git a/app/src/peripherals/rtc/rtc_peripheral.cpp b/app/src/peripherals/rtc/rtc_peripheral.cpp new file mode 100644 index 0000000..4a3df4c --- /dev/null +++ b/app/src/peripherals/rtc/rtc_peripheral.cpp @@ -0,0 +1,105 @@ +#include "rtc_peripheral.hpp" + +LOG_MODULE_REGISTER(rtc_periph, LOG_LEVEL_INF); + +RtcPeripheral::RtcPeripheral() : rtc_dev(DEVICE_DT_GET(DT_NODELABEL(rtc))), initialized(false) {} + +Peripheral::Status RtcPeripheral::init() { + if (initialized) { + return Peripheral::Status::ERROR_ALREADY_INITIALIZED; + } + + if (!device_is_ready(rtc_dev)) { + LOG_ERR("rtc not ready"); + return Peripheral::Status::NOT_READY; + } + + if (!ensure_time_valid()) { + LOG_WRN("rtc no valid time, init default"); + if (!set_default_time()) { + LOG_ERR("rtc time set failed"); + return Peripheral::Status::ERROR_HW_CONFIG_FAILED; + } + } + + initialized = true; + return Peripheral::Status::OK; +} + +bool RtcPeripheral::is_ready() const { + return initialized && device_is_ready(rtc_dev); +} + +etl::string RtcPeripheral::get_name() const { + return etl::string("RtcPeripheral"); +} + +bool RtcPeripheral::get_time(rtc_time& out_time) const { + return rtc_get_time(rtc_dev, &out_time) == 0; +} + +bool RtcPeripheral::ensure_time_valid() { + rtc_time tmp; + return rtc_get_time(rtc_dev, &tmp) == 0; +} + +bool RtcPeripheral::set_alarm(const rtc_time& t) { + uint16_t supported = 0; + if (rtc_alarm_get_supported_fields(rtc_dev, 0, &supported) != 0) { + return false; + } + + uint16_t mask = 0; + if (supported & RTC_ALARM_TIME_MASK_SECOND) mask |= RTC_ALARM_TIME_MASK_SECOND; + if (supported & RTC_ALARM_TIME_MASK_MINUTE) mask |= RTC_ALARM_TIME_MASK_MINUTE; + if (supported & RTC_ALARM_TIME_MASK_HOUR) mask |= RTC_ALARM_TIME_MASK_HOUR; + if (supported & RTC_ALARM_TIME_MASK_MONTHDAY) mask |= RTC_ALARM_TIME_MASK_MONTHDAY; + if (supported & RTC_ALARM_TIME_MASK_MONTH) mask |= RTC_ALARM_TIME_MASK_MONTH; + if (supported & RTC_ALARM_TIME_MASK_YEAR) mask |= RTC_ALARM_TIME_MASK_YEAR; + + (void)rtc_alarm_is_pending(rtc_dev, 0); + + return rtc_alarm_set_time(rtc_dev, 0, mask, &t) == 0; +} + +int64_t RtcPeripheral::to_epoch(const rtc_time& t) const { + struct rtc_time tmp = t; + struct tm tm_now = *rtc_time_to_tm(&tmp); + return timeutil_timegm64(&tm_now); +} + +bool RtcPeripheral::epoch_to_rtc_time(int64_t epoch, rtc_time& out) const { + time_t epoch32 = static_cast(epoch); + + struct tm tm_alarm; + if (gmtime_r(&epoch32, &tm_alarm) == nullptr) { + return false; + } + + out.tm_sec = tm_alarm.tm_sec; + out.tm_min = tm_alarm.tm_min; + out.tm_hour = tm_alarm.tm_hour; + out.tm_mday = tm_alarm.tm_mday; + out.tm_mon = tm_alarm.tm_mon; + out.tm_year = tm_alarm.tm_year; + out.tm_wday = tm_alarm.tm_wday; + out.tm_yday = tm_alarm.tm_yday; + out.tm_isdst = -1; + + return true; +} + +bool RtcPeripheral::set_default_time() { + rtc_time def{}; + def.tm_sec = 0; + def.tm_min = 0; + def.tm_hour = 0; + def.tm_mday = 1; + def.tm_mon = 0; + def.tm_year = 2025 - 1900; + def.tm_wday = 0; + def.tm_yday = 0; + def.tm_isdst = 0; + + return rtc_set_time(rtc_dev, &def) == 0; +} \ No newline at end of file diff --git a/app/src/peripherals/rtc/rtc_peripheral.hpp b/app/src/peripherals/rtc/rtc_peripheral.hpp new file mode 100644 index 0000000..8847c2f --- /dev/null +++ b/app/src/peripherals/rtc/rtc_peripheral.hpp @@ -0,0 +1,32 @@ +#ifndef RTC_PERIPHERAL_HPP +#define RTC_PERIPHERAL_HPP + +#include +#include +#include +#include + +#include "peripheral.hpp" + +class RtcPeripheral : public Peripheral { + public: + RtcPeripheral(); + + Status init() override; + bool is_ready() const override; + etl::string get_name() const override; + + bool get_time(rtc_time& out_time) const; + bool ensure_time_valid(); + bool set_alarm(const rtc_time& t); + int64_t to_epoch(const rtc_time& t) const; + bool epoch_to_rtc_time(int64_t epoch, rtc_time& out) const; + + private: + bool set_default_time(); + + const device* rtc_dev; + bool initialized; +}; + +#endif \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager.cpp b/app/src/peripherals/sleep/sleep_manager.cpp new file mode 100644 index 0000000..5ea7288 --- /dev/null +++ b/app/src/peripherals/sleep/sleep_manager.cpp @@ -0,0 +1,57 @@ +#include "sleep_manager.hpp" + +#if defined(CONFIG_SOC_STM32WL55XX) + #include "sleep_manager_stm.hpp" +#elif defined(CONFIG_SOC_ESP32S3) + #include "sleep_manager_esp.hpp" +#endif + +SleepManager::SleepManager() { +#if defined(CONFIG_SOC_STM32WL55XX) + impl = etl::unique_ptr(new SleepManagerStm()); +#elif defined(CONFIG_SOC_ESP32S3) + impl = etl::unique_ptr(new SleepManagerEsp()); +#else + impl = nullptr; +#endif +} + +Peripheral::Status SleepManager::init() { + if (!impl) { + return Peripheral::Status::INIT_ERR; + } + return impl->init(); +} + +bool SleepManager::is_ready() const { + return impl && impl->is_ready(); +} + +etl::string SleepManager::get_name() const { + return impl ? impl->get_name() : etl::string("SleepManagerNone"); +} + +void SleepManager::enter_sleep(SleepMode mode) { + if (impl) { + impl->enter_sleep(mode); + } +} + +void SleepManager::set_sleep_duration(int ms) { + if (impl) { + impl->set_sleep_duration(ms); + } +} + +void SleepManager::timed_sleep() { + if (impl) { + impl->timed_sleep(); + } +} + +SleepManager::WakeCause SleepManager::get_wakeup_cause() const { + if (!impl) { + return SleepManagerBase::WakeCause::UNKNOWN; + } + return impl->get_wakeup_cause(); +} \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager.hpp b/app/src/peripherals/sleep/sleep_manager.hpp new file mode 100644 index 0000000..cece028 --- /dev/null +++ b/app/src/peripherals/sleep/sleep_manager.hpp @@ -0,0 +1,31 @@ +#ifndef SLEEP_MANAGER_HPP +#define SLEEP_MANAGER_HPP + +#include +#include + +#include "peripheral.hpp" +#include "sleep_manager_base.hpp" + +class SleepManager : public Peripheral { + public: + using SleepMode = SleepManagerBase::SleepMode; + using WakeCause = SleepManagerBase::WakeCause; + + SleepManager(); + + Peripheral::Status init() override; + bool is_ready() const override; + etl::string get_name() const override; + + void enter_sleep(SleepMode mode); + void set_sleep_duration(int ms); + void timed_sleep(); + + WakeCause get_wakeup_cause() const; + + private: + etl::unique_ptr impl; +}; + +#endif \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager_base.hpp b/app/src/peripherals/sleep/sleep_manager_base.hpp new file mode 100644 index 0000000..2fd03f2 --- /dev/null +++ b/app/src/peripherals/sleep/sleep_manager_base.hpp @@ -0,0 +1,32 @@ +#ifndef SLEEP_MANAGER_BASE_HPP +#define SLEEP_MANAGER_BASE_HPP + +#include + +#include "peripheral.hpp" + +constexpr size_t SLEEP_MANAGER_NAME_SIZE = 32; +constexpr int DEFAULT_SLEEP_DURATION_MS = 5000; + +class SleepManagerBase : public Peripheral { + public: + enum class SleepMode { LIGHT_SLEEP, DEEP_SLEEP }; + + enum class WakeCause { UNKNOWN = 0, RTC_ALARM, WAKEUP_PIN, TIMER, POWER_ON_RESET }; + + virtual ~SleepManagerBase() = default; + + virtual Peripheral::Status init() = 0; + virtual bool is_ready() const = 0; + virtual etl::string get_name() const = 0; + + virtual void enter_sleep(SleepMode mode) = 0; + virtual void set_sleep_duration(int duration_ms) = 0; + virtual void timed_sleep() = 0; + + virtual WakeCause get_wakeup_cause() const { + return WakeCause::UNKNOWN; + } +}; + +#endif \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager_esp.cpp b/app/src/peripherals/sleep/sleep_manager_esp.cpp new file mode 100644 index 0000000..75fa4f6 --- /dev/null +++ b/app/src/peripherals/sleep/sleep_manager_esp.cpp @@ -0,0 +1,92 @@ +#include "sleep_manager_esp.hpp" + +#include +#include + +#ifdef CONFIG_SOC_ESP32S3 + #include + #include +#endif + +LOG_MODULE_REGISTER(sleep_mgr_esp, LOG_LEVEL_INF); + +Peripheral::Status SleepManagerEsp::init() { + initialized = true; + return Peripheral::Status::OK; +} + +bool SleepManagerEsp::is_ready() const { + return initialized; +} + +etl::string SleepManagerEsp::get_name() const { + return etl::string("SleepManagerEsp"); +} + +void SleepManagerEsp::enter_sleep(SleepMode mode) { + if (!initialized) { + LOG_ERR("sleep_mgr not initialized"); + return; + } + +#ifdef CONFIG_SOC_ESP32S3 + if (mode == SleepMode::LIGHT_SLEEP) { + k_msleep(sleep_duration_ms); + return; + } + + const uint64_t us = static_cast(sleep_duration_ms) * 1000ULL; + esp_err_t rc = esp_sleep_enable_timer_wakeup(us); + if (rc != ESP_OK) { + LOG_WRN("timer_wakeup setup failed (%d), fallback msleep", (int)rc); + k_msleep(sleep_duration_ms); + return; + } + + k_msleep(50); + esp_deep_sleep_start(); +#else + k_msleep(sleep_duration_ms); +#endif +} + +void SleepManagerEsp::timed_sleep() { + if (!initialized) { + LOG_ERR("sleep_mgr not initialized"); + return; + } + k_msleep(sleep_duration_ms); +} + +void SleepManagerEsp::set_sleep_duration(int duration_ms) { + if (duration_ms <= 0) { + LOG_WRN("invalid sleep timeout %d", duration_ms); + return; + } + sleep_duration_ms = duration_ms; +} + +SleepManagerBase::WakeCause SleepManagerEsp::get_wakeup_cause() const { + if (!initialized) { + return WakeCause::UNKNOWN; + } + +#ifdef CONFIG_SOC_ESP32S3 + esp_sleep_source_t src = esp_sleep_get_wakeup_cause(); + switch (src) { + case ESP_SLEEP_WAKEUP_TIMER: + return WakeCause::TIMER; + + case ESP_SLEEP_WAKEUP_GPIO: + return WakeCause::WAKEUP_PIN; + + case ESP_SLEEP_WAKEUP_UNDEFINED: + return WakeCause::POWER_ON_RESET; + + default: + return WakeCause::UNKNOWN; + } +#else + return WakeCause::UNKNOWN; +#endif +} \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager_esp.hpp b/app/src/peripherals/sleep/sleep_manager_esp.hpp new file mode 100644 index 0000000..8d0fb11 --- /dev/null +++ b/app/src/peripherals/sleep/sleep_manager_esp.hpp @@ -0,0 +1,30 @@ +#ifndef SLEEP_MANAGER_ESP_HPP +#define SLEEP_MANAGER_ESP_HPP + +#include "sleep_manager_base.hpp" + +#ifdef CONFIG_SOC_ESP32S3 + #include + #include +#endif + +class SleepManagerEsp final : public SleepManagerBase { + public: + SleepManagerEsp() = default; + + Peripheral::Status init() override; + bool is_ready() const override; + etl::string get_name() const override; + + void enter_sleep(SleepMode mode) override; + void set_sleep_duration(int duration_ms) override; + void timed_sleep() override; + + WakeCause get_wakeup_cause() const override; + + private: + bool initialized = false; + int sleep_duration_ms = DEFAULT_SLEEP_DURATION_MS; +}; + +#endif \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp new file mode 100644 index 0000000..1a8d5de --- /dev/null +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -0,0 +1,138 @@ +#include "sleep_manager_stm.hpp" + +LOG_MODULE_REGISTER(sleep_mgr_stm, LOG_LEVEL_INF); + +SleepManagerStm::SleepManagerStm() + : initialized(false), + sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS), + wake_cause(WakeCause::POWER_ON_RESET) +#ifdef CONFIG_SOC_STM32WL55XX + , + wkup_gpio(GPIO_DT_SPEC_GET(DT_ALIAS(wkup_src), gpios)), + rtc() +#endif +{ +} + +Peripheral::Status SleepManagerStm::init() { +#ifdef CONFIG_SOC_STM32WL55XX + if (!device_is_ready(wkup_gpio.port)) { + LOG_ERR("wkup gpio not ready"); + return Peripheral::Status::NOT_READY; + } + + const int pin_rc = gpio_pin_configure_dt(&wkup_gpio, STM32_GPIO_WKUP); + if (pin_rc != 0) { + LOG_ERR("wkup gpio cfg failed (%d)", pin_rc); + return Peripheral::Status::ERROR_HW_CONFIG_FAILED; + } + + const Peripheral::Status rtc_rc = rtc.init(); + if (rtc_rc != Peripheral::Status::OK && rtc_rc != Peripheral::Status::ERROR_ALREADY_INITIALIZED) { + LOG_ERR("rtc init failed (%d)", static_cast(rtc_rc)); + return rtc_rc; + } + + if (!rtc.is_ready()) { + LOG_ERR("rtc not ready after init"); + return Peripheral::Status::NOT_READY; + } + + wake_cause = WakeCause::POWER_ON_RESET; +#endif + + initialized = true; + return Peripheral::Status::OK; +} + +bool SleepManagerStm::is_ready() const { +#ifdef CONFIG_SOC_STM32WL55XX + return initialized && rtc.is_ready(); +#else + return initialized; +#endif +} + +etl::string SleepManagerStm::get_name() const { + return etl::string("SleepManagerStm"); +} + +void SleepManagerStm::enter_sleep(SleepMode mode) { + ARG_UNUSED(mode); + + if (!initialized) { + LOG_ERR("sleep_mgr not initialized"); + return; + } + +#ifdef CONFIG_SOC_STM32WL55XX + if (!rtc.is_ready()) { + LOG_ERR("rtc unavailable"); + k_msleep(sleep_timeout_ms); + wake_cause = WakeCause::TIMER; + return; + } + + if (!rtc.ensure_time_valid()) { + LOG_WRN("rtc time invalid, sleep without alarm"); + wake_cause = WakeCause::WAKEUP_PIN; + sys_poweroff(); + return; + } + + rtc_time now; + if (!rtc.get_time(now)) { + LOG_WRN("rtc read failed, sleep without alarm"); + wake_cause = WakeCause::WAKEUP_PIN; + sys_poweroff(); + return; + } + + const int64_t epoch_now = rtc.to_epoch(now); + const int64_t epoch_alarm = epoch_now + (sleep_timeout_ms / 1000); + + rtc_time alarm_time; + if (!rtc.epoch_to_rtc_time(epoch_alarm, alarm_time)) { + LOG_WRN("rtc convert failed, sleep without alarm"); + wake_cause = WakeCause::WAKEUP_PIN; + sys_poweroff(); + return; + } + + if (!rtc.set_alarm(alarm_time)) { + LOG_WRN("rtc alarm set failed, sleep without alarm"); + wake_cause = WakeCause::WAKEUP_PIN; + sys_poweroff(); + return; + } + + LOG_INF("sleep %d ms (rtc alarm armed)", sleep_timeout_ms); + wake_cause = WakeCause::RTC_ALARM; + sys_poweroff(); +#else + k_msleep(sleep_timeout_ms); + wake_cause = WakeCause::TIMER; +#endif +} + +void SleepManagerStm::set_sleep_duration(int duration_ms) { + if (duration_ms <= 0) { + LOG_WRN("invalid sleep timeout %d", duration_ms); + return; + } + sleep_timeout_ms = duration_ms; +} + +void SleepManagerStm::timed_sleep() { + if (!initialized) { + LOG_ERR("sleep_mgr not initialized"); + return; + } + + k_msleep(sleep_timeout_ms); + wake_cause = WakeCause::TIMER; +} + +SleepManagerBase::WakeCause SleepManagerStm::get_wakeup_cause() const { + return wake_cause; +} \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager_stm.hpp b/app/src/peripherals/sleep/sleep_manager_stm.hpp new file mode 100644 index 0000000..4e709ae --- /dev/null +++ b/app/src/peripherals/sleep/sleep_manager_stm.hpp @@ -0,0 +1,39 @@ +#ifndef SLEEP_MANAGER_STM_HPP +#define SLEEP_MANAGER_STM_HPP + +#include +#include +#include +#include +#include +#include + +#include "peripherals/rtc/rtc_peripheral.hpp" +#include "sleep_manager_base.hpp" + +class SleepManagerStm : public SleepManagerBase { + public: + SleepManagerStm(); + + Peripheral::Status init() override; + bool is_ready() const override; + etl::string get_name() const override; + + void enter_sleep(SleepMode mode) override; + void set_sleep_duration(int duration_ms) override; + void timed_sleep() override; + + WakeCause get_wakeup_cause() const override; + + private: + bool initialized; + int sleep_timeout_ms; + WakeCause wake_cause; + +#ifdef CONFIG_SOC_STM32WL55XX + gpio_dt_spec wkup_gpio; + RtcPeripheral rtc; +#endif +}; + +#endif \ No newline at end of file diff --git a/app/src/utils/sleep-manager.cpp b/app/src/utils/sleep-manager.cpp deleted file mode 100644 index 8eddac8..0000000 --- a/app/src/utils/sleep-manager.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "sleep-manager.hpp" - -#include -#include - -#ifdef CONFIG_SOC_ESP32S3 - #include -#endif - -LOG_MODULE_REGISTER(sleep_manager, LOG_LEVEL_DBG); - -SleepManager::SleepManager() {} - -Peripheral::Status SleepManager::init() { - initialized = true; - LOG_INF("SleepManager initialized."); - return Status::OK; -} - -bool SleepManager::is_ready() const { - return initialized; -} - -etl::string SleepManager::get_name() const { - return "SleepManager"; -} - -void SleepManager::enter_sleep(SleepMode mode) { - if (!initialized) { - LOG_WRN("Sleep Manager not initialized, cannot sleep."); - return; - } - -#ifdef CONFIG_SOC_SERIES_STM32 - LOG_WRN("STM32 Sleep not implemented yet."); - timed_sleep(); -#elif CONFIG_SOC_ESP32S3 - - if (mode == SleepMode::LIGHT_SLEEP) { - LOG_INF("Entering light sleep (using k_sleep)..."); - k_sleep(K_MSEC(DEFAULT_SLEEP_DURATION_MS)); - - } else if (mode == SleepMode::DEEP_SLEEP) { - LOG_INF("Configuring wake-up sources for TIMED deep sleep..."); - - // --- Enable Timer Wakeup --- - uint64_t sleep_time_us = (uint64_t)sleep_duration_ms * 1000ULL; - LOG_INF("Enabling timer wakeup for %llu us (%d ms)", sleep_time_us, sleep_duration_ms); - esp_err_t err = esp_sleep_enable_timer_wakeup(sleep_time_us); - if (err != ESP_OK) { - LOG_ERR("Failed to enable timer wakeup (err %d)", err); - LOG_WRN("Falling back to k_sleep instead of deep sleep."); - k_sleep(K_MSEC(sleep_duration_ms)); - return; - } - - LOG_INF("Entering ESP32 deep sleep mode (timer wake)..."); - k_sleep(K_MSEC(50)); // Allow logs to propagate before deep sleep - - esp_deep_sleep_start(); - - LOG_ERR("Should not reach here after deep sleep start!"); - } -#else - LOG_WRN("Deep sleep not implemented for this SOC."); - timed_sleep(); -#endif -} - -void SleepManager::timed_sleep() { - if (!initialized) { - LOG_WRN("Sleep Manager not initialized, cannot sleep."); - return; - } - LOG_INF("Entering timed sleep (k_sleep) for %dms...", sleep_duration_ms); - k_sleep(K_MSEC(sleep_duration_ms)); -} - -void SleepManager::set_sleep_duration(int duration_ms) { - if (duration_ms > 0) { - sleep_duration_ms = duration_ms; - LOG_INF("Sleep duration set to %d ms", sleep_duration_ms); - } else { - LOG_WRN("Invalid sleep duration: %d ms. Keeping previous value: %d ms", duration_ms, - sleep_duration_ms); - } -} - -#ifdef CONFIG_SOC_ESP32S3 -esp_sleep_wakeup_cause_t SleepManager::get_wakeup_cause() { - return esp_sleep_get_wakeup_cause(); -} -#endif \ No newline at end of file diff --git a/app/src/utils/sleep-manager.hpp b/app/src/utils/sleep-manager.hpp deleted file mode 100644 index 01212e0..0000000 --- a/app/src/utils/sleep-manager.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef SLEEPMANAGER_HPP -#define SLEEPMANAGER_HPP - -#include -#include -#include -#include - -#ifdef CONFIG_SOC_ESP32S3 - #include -#endif - -#include "peripheral.hpp" - -constexpr size_t SLEEP_MANAGER_NAME_SIZE = 32; -constexpr int DEFAULT_SLEEP_DURATION_MS = 5000; - -class SleepManager : public Peripheral { - public: - enum class SleepMode { LIGHT_SLEEP, DEEP_SLEEP }; - - SleepManager(); - ~SleepManager() override = default; - - Status init() override; - bool is_ready() const override; - etl::string get_name() const override; - - void enter_sleep(SleepMode mode); - void set_sleep_duration(int duration_ms); - void timed_sleep(); - -#ifdef CONFIG_SOC_ESP32S3 - esp_sleep_wakeup_cause_t get_wakeup_cause(); -#endif - - private: - // --- Member Variables --- - bool initialized = false; - int sleep_duration_ms = DEFAULT_SLEEP_DURATION_MS; -}; - -#endif // SLEEPMANAGER_HPP \ No newline at end of file From 4fec1dda8cff2262941676beefae3c14ee315754 Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 9 Mar 2026 18:54:09 +0100 Subject: [PATCH 02/19] fix: update zephyr revision to v4.3.0 and refine cmsis name in west.yml --- west.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/west.yml b/west.yml index d972f5f..7a0dde1 100644 --- a/west.yml +++ b/west.yml @@ -8,12 +8,12 @@ manifest: projects: - name: zephyr url: https://github.com/zephyrproject-rtos/zephyr - revision: v4.0.0 + revision: v4.3.0 import: name-allowlist: - hal_espressif - hal_stm32 - - cmsis + - cmsis_6 - loramac-node - mcuboot - nanopb From ae214eb0d783e163e8fd7045e59deda8ff5be43b Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 9 Mar 2026 18:56:41 +0100 Subject: [PATCH 03/19] feat: enhance sleep manager and RTC integration; add alarm handler and button ISR --- app/boards/nucleo_wl55jc.overlay | 14 ++++++- app/prj.conf | 8 +++- app/src/peripherals/rtc/rtc_peripheral.cpp | 6 +++ .../peripherals/sleep/sleep_manager_stm.cpp | 38 +++++++++---------- .../peripherals/sleep/sleep_manager_stm.hpp | 4 +- 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/app/boards/nucleo_wl55jc.overlay b/app/boards/nucleo_wl55jc.overlay index 7d68315..8a89f41 100644 --- a/app/boards/nucleo_wl55jc.overlay +++ b/app/boards/nucleo_wl55jc.overlay @@ -3,8 +3,18 @@ / { aliases { bme280-i2c = &i2c2; - wkup-src = &user_button_1; - }; + wkup-src = &user_input_pc4; + }; + + gpio_keys { + compatible = "gpio-keys"; + + user_input_pc4: pc4_input { + label = "PC4_INPUT"; + gpios = <&gpioc 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; + zephyr,code = ; + }; + }; }; &i2c2 { diff --git a/app/prj.conf b/app/prj.conf index 452bd86..f9476d7 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -8,6 +8,7 @@ CONFIG_GLIBCXX_LIBCPP=y CONFIG_NEWLIB_LIBC=y CONFIG_NANOPB=y +CONFIG_RESET=y CONFIG_REBOOT=y CONFIG_POWEROFF=y CONFIG_STM32_WKUP_PINS=y @@ -16,8 +17,10 @@ CONFIG_I2C=y CONFIG_GPIO=y CONFIG_SPI=y -CONFIG_UART_USE_RUNTIME_CONFIGURE=y -CONFIG_UART_ASYNC_API=y +CONFIG_PM=y +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y +CONFIG_PM_DEVICE_SYSTEM_MANAGED=y CONFIG_SENSOR=y CONFIG_SENSOR_ASYNC_API=y @@ -25,6 +28,7 @@ CONFIG_SENSOR_ASYNC_API=y # General Zephyr settings CONFIG_MAIN_STACK_SIZE=2048 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + CONFIG_LOG=y CONFIG_PRINTK=y CONFIG_LOG_MODE_IMMEDIATE=y diff --git a/app/src/peripherals/rtc/rtc_peripheral.cpp b/app/src/peripherals/rtc/rtc_peripheral.cpp index 4a3df4c..d24797b 100644 --- a/app/src/peripherals/rtc/rtc_peripheral.cpp +++ b/app/src/peripherals/rtc/rtc_peripheral.cpp @@ -4,6 +4,10 @@ LOG_MODULE_REGISTER(rtc_periph, LOG_LEVEL_INF); RtcPeripheral::RtcPeripheral() : rtc_dev(DEVICE_DT_GET(DT_NODELABEL(rtc))), initialized(false) {} +static void rtc_alarm_handler(const struct device* dev, uint16_t id, void* user_data) { + printk("RTC ALARM FIRED! id=%u\n", id); +} + Peripheral::Status RtcPeripheral::init() { if (initialized) { return Peripheral::Status::ERROR_ALREADY_INITIALIZED; @@ -22,6 +26,8 @@ Peripheral::Status RtcPeripheral::init() { } } + rtc_alarm_set_callback(rtc_dev, 0, rtc_alarm_handler, nullptr); + initialized = true; return Peripheral::Status::OK; } diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 1a8d5de..ed41e3f 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -4,8 +4,7 @@ LOG_MODULE_REGISTER(sleep_mgr_stm, LOG_LEVEL_INF); SleepManagerStm::SleepManagerStm() : initialized(false), - sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS), - wake_cause(WakeCause::POWER_ON_RESET) + sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS) #ifdef CONFIG_SOC_STM32WL55XX , wkup_gpio(GPIO_DT_SPEC_GET(DT_ALIAS(wkup_src), gpios)), @@ -14,6 +13,15 @@ SleepManagerStm::SleepManagerStm() { } +static struct gpio_callback btn_cb_data; + +int count = 0; + +void button_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { + count++; + printk("Button pressed! Total count: %d\n", count); +} + Peripheral::Status SleepManagerStm::init() { #ifdef CONFIG_SOC_STM32WL55XX if (!device_is_ready(wkup_gpio.port)) { @@ -21,12 +29,17 @@ Peripheral::Status SleepManagerStm::init() { return Peripheral::Status::NOT_READY; } - const int pin_rc = gpio_pin_configure_dt(&wkup_gpio, STM32_GPIO_WKUP); + const int pin_rc = gpio_pin_configure_dt(&wkup_gpio, GPIO_INPUT); if (pin_rc != 0) { LOG_ERR("wkup gpio cfg failed (%d)", pin_rc); return Peripheral::Status::ERROR_HW_CONFIG_FAILED; } + gpio_pin_interrupt_configure_dt(&wkup_gpio, GPIO_INT_EDGE_TO_ACTIVE); + + gpio_init_callback(&btn_cb_data, button_isr, BIT(wkup_gpio.pin)); + gpio_add_callback(wkup_gpio.port, &btn_cb_data); + const Peripheral::Status rtc_rc = rtc.init(); if (rtc_rc != Peripheral::Status::OK && rtc_rc != Peripheral::Status::ERROR_ALREADY_INITIALIZED) { LOG_ERR("rtc init failed (%d)", static_cast(rtc_rc)); @@ -38,7 +51,6 @@ Peripheral::Status SleepManagerStm::init() { return Peripheral::Status::NOT_READY; } - wake_cause = WakeCause::POWER_ON_RESET; #endif initialized = true; @@ -69,13 +81,11 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { if (!rtc.is_ready()) { LOG_ERR("rtc unavailable"); k_msleep(sleep_timeout_ms); - wake_cause = WakeCause::TIMER; return; } if (!rtc.ensure_time_valid()) { LOG_WRN("rtc time invalid, sleep without alarm"); - wake_cause = WakeCause::WAKEUP_PIN; sys_poweroff(); return; } @@ -83,7 +93,6 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { rtc_time now; if (!rtc.get_time(now)) { LOG_WRN("rtc read failed, sleep without alarm"); - wake_cause = WakeCause::WAKEUP_PIN; sys_poweroff(); return; } @@ -94,24 +103,20 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { rtc_time alarm_time; if (!rtc.epoch_to_rtc_time(epoch_alarm, alarm_time)) { LOG_WRN("rtc convert failed, sleep without alarm"); - wake_cause = WakeCause::WAKEUP_PIN; sys_poweroff(); return; } if (!rtc.set_alarm(alarm_time)) { LOG_WRN("rtc alarm set failed, sleep without alarm"); - wake_cause = WakeCause::WAKEUP_PIN; - sys_poweroff(); return; } - LOG_INF("sleep %d ms (rtc alarm armed)", sleep_timeout_ms); - wake_cause = WakeCause::RTC_ALARM; - sys_poweroff(); + //sys_poweroff(); + k_sleep(K_FOREVER); + #else k_msleep(sleep_timeout_ms); - wake_cause = WakeCause::TIMER; #endif } @@ -130,9 +135,4 @@ void SleepManagerStm::timed_sleep() { } k_msleep(sleep_timeout_ms); - wake_cause = WakeCause::TIMER; } - -SleepManagerBase::WakeCause SleepManagerStm::get_wakeup_cause() const { - return wake_cause; -} \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager_stm.hpp b/app/src/peripherals/sleep/sleep_manager_stm.hpp index 4e709ae..a9ad237 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.hpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "peripherals/rtc/rtc_peripheral.hpp" #include "sleep_manager_base.hpp" @@ -23,12 +24,9 @@ class SleepManagerStm : public SleepManagerBase { void set_sleep_duration(int duration_ms) override; void timed_sleep() override; - WakeCause get_wakeup_cause() const override; - private: bool initialized; int sleep_timeout_ms; - WakeCause wake_cause; #ifdef CONFIG_SOC_STM32WL55XX gpio_dt_spec wkup_gpio; From db713821aca0a7965bd168996d88547ea17dcbf3 Mon Sep 17 00:00:00 2001 From: Olaf Bykowski Date: Mon, 9 Mar 2026 19:14:47 +0100 Subject: [PATCH 04/19] feat: update configuration for power management and ADC support --- app/prj.conf | 4 +++- west.yml | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/prj.conf b/app/prj.conf index 2e94ce6..2620251 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -16,7 +16,9 @@ CONFIG_SPI=y CONFIG_BQ274XX_PM=y +CONFIG_PM=y CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y CONFIG_POWEROFF=y CONFIG_SENSOR=y @@ -50,4 +52,4 @@ CONFIG_BOOTLOADER_MCUBOOT=y # ADC config CONFIG_ADC=y -CONFIG_CBPRINTF_FP_SUPPORT=y \ No newline at end of file +CONFIG_CBPRINTF_FP_SUPPORT=y diff --git a/west.yml b/west.yml index d972f5f..1ed6b37 100644 --- a/west.yml +++ b/west.yml @@ -8,12 +8,12 @@ manifest: projects: - name: zephyr url: https://github.com/zephyrproject-rtos/zephyr - revision: v4.0.0 + revision: a143581 import: name-allowlist: - hal_espressif - hal_stm32 - - cmsis + - cmsis_6 - loramac-node - mcuboot - nanopb From 7dba4a1a1c6d93590fc6838573c8f8d1b19bf1ad Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 16 Mar 2026 19:07:00 +0100 Subject: [PATCH 05/19] feat: enhance sleep management and GPIO wakeup sources; refactor Application and LoRaWANHandler --- app/boards/nucleo_wl55jc.overlay | 8 +- app/prj.conf | 3 +- app/src/Application.cpp | 100 ++++++++---------- app/src/Application.hpp | 8 +- app/src/main.cpp | 23 ++-- .../lorawan_handler/lorawan_handler.cpp | 6 +- .../lorawan_handler/lorawan_handler.hpp | 3 +- app/src/peripherals/rtc/rtc_peripheral.cpp | 11 +- app/src/peripherals/sleep/sleep_manager.cpp | 57 ---------- app/src/peripherals/sleep/sleep_manager.hpp | 41 ++++--- .../peripherals/sleep/sleep_manager_stm.cpp | 78 +++++++++----- .../peripherals/sleep/sleep_manager_stm.hpp | 5 +- app/src/sensors/bme280/bme280.cpp | 60 +++++------ 13 files changed, 193 insertions(+), 210 deletions(-) delete mode 100644 app/src/peripherals/sleep/sleep_manager.cpp diff --git a/app/boards/nucleo_wl55jc.overlay b/app/boards/nucleo_wl55jc.overlay index 3affed8..3dd3907 100644 --- a/app/boards/nucleo_wl55jc.overlay +++ b/app/boards/nucleo_wl55jc.overlay @@ -3,7 +3,8 @@ / { aliases { bme280-i2c = &i2c2; - wkup-src = &user_input_pc4; + wkup-src-1 = &user_input_pc4; + wkup-src-2 = &user_button_1; }; gpio_keys { @@ -23,11 +24,12 @@ }; &i2c2 { - pinctrl-0 = <&i2c2_scl_pa12 &i2c2_sda_pa11>; - pinctrl-names = "default"; status = "okay"; clock-frequency = ; + pinctrl-0 = <&i2c2_scl_pa12 &i2c2_sda_pa11>; + pinctrl-names = "default"; + bme280@76 { compatible = "bosch,bme280"; reg = <0x76>; diff --git a/app/prj.conf b/app/prj.conf index 4ab03fd..cc636c0 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -14,6 +14,8 @@ CONFIG_POWEROFF=y CONFIG_STM32_WKUP_PINS=y CONFIG_I2C=y +CONFIG_I2C_STM32_BUS_RECOVERY=y + CONFIG_GPIO=y CONFIG_SPI=y @@ -22,7 +24,6 @@ CONFIG_BQ274XX_PM=y CONFIG_PM=y CONFIG_PM_DEVICE=y CONFIG_PM_DEVICE_RUNTIME=y -CONFIG_POWEROFF=y CONFIG_SENSOR=y CONFIG_SENSOR_ASYNC_API=y diff --git a/app/src/Application.cpp b/app/src/Application.cpp index c383118..3409387 100644 --- a/app/src/Application.cpp +++ b/app/src/Application.cpp @@ -8,14 +8,9 @@ LOG_MODULE_REGISTER(application, CONFIG_APP_LOG_LEVEL); -Application::Application( - etl::array, NUMBER_OF_SENSORS>& sensors, - LoRaWANHandler& lorawan, - etl::unique_ptr sleep_manager) - : - m_sensors(sensors), - m_lorawan(lorawan), - m_sleep_manager(etl::move(sleep_manager)) {} +Application::Application(etl::array, NUMBER_OF_SENSORS>& sensors, + LoRaWANHandler& lorawan, SleepManager* sleep_manager) + : m_sensors(sensors), m_lorawan(lorawan), m_sleep_manager(sleep_manager) {} bool Application::init() { LOG_INF("Application core initializing..."); @@ -36,19 +31,19 @@ bool Application::initialize_peripherals() { LOG_DBG("Initializing all application peripherals..."); bool all_essential_ready = true; - // Initialize sensors - for(auto& sensor: m_sensors) { - if(sensor) { - LOG_DBG("Initializing: %s", sensor->get_name().c_str()); - if (sensor->init() != Peripheral::Status::OK) { - LOG_ERR("%s initialization failed.", sensor->get_name().c_str()); - } else { - LOG_INF("%s initialized.", sensor->get_name().c_str()); - } - } - } - - // Initialize the LoRaWAN manager + // Initialize sensors + for (auto& sensor : m_sensors) { + if (sensor) { + LOG_DBG("Initializing: %s", sensor->get_name().c_str()); + if (sensor->init() != Peripheral::Status::OK) { + LOG_ERR("%s initialization failed.", sensor->get_name().c_str()); + } else { + LOG_INF("%s initialized.", sensor->get_name().c_str()); + } + } + } + + // Initialize the LoRaWAN manager LOG_DBG("Initializing: %s", m_lorawan.get_name().c_str()); if (m_lorawan.init() != Peripheral::Status::OK) { LOG_ERR("%s initialization failed.", m_lorawan.get_name().c_str()); @@ -72,40 +67,41 @@ bool Application::initialize_peripherals() { } void Application::generate_init_failure_report(buzzverse_v1_Packet& packet) { - LOG_INF("Generating initialization failure report..."); + LOG_INF("Generating initialization failure report..."); - packet.which_data = buzzverse_v1_Packet_status_tag; - auto& status_msg = packet.data.status; + packet.which_data = buzzverse_v1_Packet_status_tag; + auto& status_msg = packet.data.status; - for(const auto& sensor: m_sensors) { - if(sensor) - sensor->get_status(status_msg); - } + for (const auto& sensor : m_sensors) { + if (sensor) sensor->get_status(status_msg); + } - if (m_lorawan.is_ready()) { - status_msg.lorawan_status = buzzverse_v1_Status_ComponentState_NORMAL; - } else { - status_msg.lorawan_status = buzzverse_v1_Status_ComponentState_INITIALIZATION_FAILED; - } + if (m_lorawan.is_ready()) { + status_msg.lorawan_status = buzzverse_v1_Status_ComponentState_NORMAL; + } else { + status_msg.lorawan_status = buzzverse_v1_Status_ComponentState_INITIALIZATION_FAILED; + } } void Application::run_cycle() { - LOG_INF("--- Starting Application Cycle ---"); - - for(auto& sensor: m_sensors) { - if (sensor && sensor->is_ready()) { - buzzverse_v1_Packet packet; - if (sensor->get_packet(packet) == Sensor::Status::OK) { - send_lora_packet(packet); - } else { - LOG_ERR("Failed to get %s packet", sensor->get_name().c_str()); - } - } else { - LOG_ERR("%s not ready for reading.", sensor->get_name().c_str()); - } - } - - LOG_INF("--- Application Cycle Complete ---"); + k_msleep(50); + + LOG_INF("--- Starting Application Cycle ---"); + + for(auto& sensor: m_sensors) { + if (sensor && sensor->is_ready()) { + buzzverse_v1_Packet packet; + if (sensor->get_packet(packet) == Sensor::Status::OK) { + send_lora_packet(packet); + } else { + LOG_ERR("Failed to get %s packet", sensor->get_name().c_str()); + } + } else { + LOG_ERR("%s not ready for reading.", sensor->get_name().c_str()); + } + } + + LOG_INF("--- Application Cycle Complete ---"); } void Application::enter_low_power_mode(int sleep_duration_ms) { @@ -114,17 +110,13 @@ void Application::enter_low_power_mode(int sleep_duration_ms) { LOG_WRN("SleepManager not available/ready. Defaulting to k_sleep for %d ms.", sleep_duration_ms); k_msleep(sleep_duration_ms); - sys_reboot(SYS_REBOOT_COLD); + return; } LOG_INF("Preparing system for deep sleep (duration: %d ms)...", sleep_duration_ms); m_sleep_manager->set_sleep_duration(sleep_duration_ms); m_sleep_manager->enter_sleep(SleepManager::SleepMode::DEEP_SLEEP); - - // This code should not be reached on platforms where deep sleep causes a reset. - LOG_ERR("!!! CRITICAL: Execution continued after deep sleep call. Deep sleep failed. !!!"); - sys_reboot(SYS_REBOOT_COLD); } void Application::send_lora_packet(const buzzverse_v1_Packet& packet) { diff --git a/app/src/Application.hpp b/app/src/Application.hpp index 96e93f6..23320e4 100644 --- a/app/src/Application.hpp +++ b/app/src/Application.hpp @@ -30,9 +30,9 @@ class Application { * @param sleep_manager Pointer to the SleepManager. Can be nullptr if sleep is disabled. */ Application( - etl::array, NUMBER_OF_SENSORS>& sensors, - LoRaWANHandler& lorawan, - etl::unique_ptr sleep_manager); + etl::array, NUMBER_OF_SENSORS>& sensors, + LoRaWANHandler& lorawan, + SleepManager* sleep_manager); ~Application() = default; // Main phases of the application @@ -64,7 +64,7 @@ class Application { etl::array, NUMBER_OF_SENSORS>& m_sensors; LoRaWANHandler& m_lorawan; - etl::unique_ptr m_sleep_manager; + SleepManager* m_sleep_manager; }; #endif // APPLICATION_HPP diff --git a/app/src/main.cpp b/app/src/main.cpp index 2b99858..f250d9a 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -24,6 +24,7 @@ LOG_MODULE_REGISTER(main_entry, LOG_LEVEL_DBG); static const struct adc_dt_spec soil_sensor_adc_spec = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0); +K_SEM_DEFINE(wakeup_sem, 0, 1); int main(void) { printk("%s\n", APP_ASCII_BANNER); @@ -34,7 +35,7 @@ int main(void) { // Array of available sensors etl::array, NUMBER_OF_SENSORS> sensors { - etl::unique_ptr(etl::move(&bme280)), + etl::unique_ptr(etl::move(&bme280)), #ifdef CONFIG_ENABLE_ANALOG etl::unique_ptr(etl::move(&analog)), #endif @@ -43,13 +44,13 @@ int main(void) { BQ27441 bq27441(DEVICE_DT_GET_ANY(ti_bq274xx)); LoRaWANHandler lorawan(bq27441); - etl::unique_ptr p_sleep_manager(nullptr); #ifdef CONFIG_ENABLE_DEVICE_SLEEP - p_sleep_manager = etl::unique_ptr(new SleepManager); + SleepManager sleep_manager; + Application app(sensors, lorawan, &sleep_manager); +#else + Application app(sensors, lorawan, nullptr); #endif - Application app(sensors, lorawan, etl::move(p_sleep_manager)); - if (!app.init()) { LOG_ERR("Critical application initialization failed!"); @@ -65,18 +66,16 @@ int main(void) { sys_reboot(SYS_REBOOT_COLD); } - app.run_cycle(); + while (true) { + app.run_cycle(); #ifdef CONFIG_ENABLE_DEVICE_SLEEP - app.enter_low_power_mode(APP_SLEEP_DURATION_MS); - LOG_WRN("Execution continued after enter_low_power_mode - this is unexpected for deep sleep."); + app.enter_low_power_mode(APP_SLEEP_DURATION_MS); #else - LOG_INF("Device sleep not enabled. Entering polling loop."); - while (true) { + LOG_INF("Device sleep not enabled. Entering polling loop."); k_msleep(APP_SLEEP_DURATION_MS); - app.run_cycle(); - } #endif + } return 0; // Should not be reached } diff --git a/app/src/peripherals/lorawan_handler/lorawan_handler.cpp b/app/src/peripherals/lorawan_handler/lorawan_handler.cpp index 2564cde..b31ec83 100644 --- a/app/src/peripherals/lorawan_handler/lorawan_handler.cpp +++ b/app/src/peripherals/lorawan_handler/lorawan_handler.cpp @@ -9,10 +9,10 @@ LOG_MODULE_REGISTER(lorawan_handler, LOG_LEVEL_DBG); -etl::unique_ptr LoRaWANHandler::battery_sensor(nullptr); +Sensor* LoRaWANHandler::battery_sensor = nullptr; -LoRaWANHandler::LoRaWANHandler(Sensor& battery_sensor) { - LoRaWANHandler::battery_sensor = etl::unique_ptr(etl::move(&battery_sensor)); +LoRaWANHandler::LoRaWANHandler(Sensor& battery_sensor_ref) { + LoRaWANHandler::battery_sensor = &battery_sensor_ref; #if defined(CONFIG_LORAWAN_JOIN_OTAA) const char* dev_eui_str = CONFIG_LORAWAN_DEV_EUI; diff --git a/app/src/peripherals/lorawan_handler/lorawan_handler.hpp b/app/src/peripherals/lorawan_handler/lorawan_handler.hpp index 4dcb168..c3d5c47 100644 --- a/app/src/peripherals/lorawan_handler/lorawan_handler.hpp +++ b/app/src/peripherals/lorawan_handler/lorawan_handler.hpp @@ -2,7 +2,6 @@ #define LORAWAN_HANDLER_HPP #include -#include #include #include "buzzverse/bq27441.pb.h" @@ -55,7 +54,7 @@ class LoRaWANHandler : public Peripheral { etl::array app_skey; etl::array nwk_skey; - static etl::unique_ptr battery_sensor; + static Sensor* battery_sensor; static uint8_t battery_level_callback(); }; diff --git a/app/src/peripherals/rtc/rtc_peripheral.cpp b/app/src/peripherals/rtc/rtc_peripheral.cpp index d24797b..1c568e7 100644 --- a/app/src/peripherals/rtc/rtc_peripheral.cpp +++ b/app/src/peripherals/rtc/rtc_peripheral.cpp @@ -2,10 +2,13 @@ LOG_MODULE_REGISTER(rtc_periph, LOG_LEVEL_INF); +extern struct k_sem wakeup_sem; + RtcPeripheral::RtcPeripheral() : rtc_dev(DEVICE_DT_GET(DT_NODELABEL(rtc))), initialized(false) {} static void rtc_alarm_handler(const struct device* dev, uint16_t id, void* user_data) { printk("RTC ALARM FIRED! id=%u\n", id); + k_sem_give(&wakeup_sem); } Peripheral::Status RtcPeripheral::init() { @@ -96,16 +99,20 @@ bool RtcPeripheral::epoch_to_rtc_time(int64_t epoch, rtc_time& out) const { } bool RtcPeripheral::set_default_time() { +#ifndef CONFIG_DEFAULT_RTC_YEAR +#define CONFIG_DEFAULT_RTC_YEAR 2025 +#endif + rtc_time def{}; def.tm_sec = 0; def.tm_min = 0; def.tm_hour = 0; def.tm_mday = 1; def.tm_mon = 0; - def.tm_year = 2025 - 1900; + def.tm_year = CONFIG_DEFAULT_RTC_YEAR - 1900; def.tm_wday = 0; def.tm_yday = 0; def.tm_isdst = 0; return rtc_set_time(rtc_dev, &def) == 0; -} \ No newline at end of file +} diff --git a/app/src/peripherals/sleep/sleep_manager.cpp b/app/src/peripherals/sleep/sleep_manager.cpp deleted file mode 100644 index 5ea7288..0000000 --- a/app/src/peripherals/sleep/sleep_manager.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "sleep_manager.hpp" - -#if defined(CONFIG_SOC_STM32WL55XX) - #include "sleep_manager_stm.hpp" -#elif defined(CONFIG_SOC_ESP32S3) - #include "sleep_manager_esp.hpp" -#endif - -SleepManager::SleepManager() { -#if defined(CONFIG_SOC_STM32WL55XX) - impl = etl::unique_ptr(new SleepManagerStm()); -#elif defined(CONFIG_SOC_ESP32S3) - impl = etl::unique_ptr(new SleepManagerEsp()); -#else - impl = nullptr; -#endif -} - -Peripheral::Status SleepManager::init() { - if (!impl) { - return Peripheral::Status::INIT_ERR; - } - return impl->init(); -} - -bool SleepManager::is_ready() const { - return impl && impl->is_ready(); -} - -etl::string SleepManager::get_name() const { - return impl ? impl->get_name() : etl::string("SleepManagerNone"); -} - -void SleepManager::enter_sleep(SleepMode mode) { - if (impl) { - impl->enter_sleep(mode); - } -} - -void SleepManager::set_sleep_duration(int ms) { - if (impl) { - impl->set_sleep_duration(ms); - } -} - -void SleepManager::timed_sleep() { - if (impl) { - impl->timed_sleep(); - } -} - -SleepManager::WakeCause SleepManager::get_wakeup_cause() const { - if (!impl) { - return SleepManagerBase::WakeCause::UNKNOWN; - } - return impl->get_wakeup_cause(); -} \ No newline at end of file diff --git a/app/src/peripherals/sleep/sleep_manager.hpp b/app/src/peripherals/sleep/sleep_manager.hpp index cece028..cfc8cef 100644 --- a/app/src/peripherals/sleep/sleep_manager.hpp +++ b/app/src/peripherals/sleep/sleep_manager.hpp @@ -1,31 +1,48 @@ #ifndef SLEEP_MANAGER_HPP #define SLEEP_MANAGER_HPP -#include #include - #include "peripheral.hpp" #include "sleep_manager_base.hpp" +#if defined(CONFIG_SOC_STM32WL55XX) + #include "sleep_manager_stm.hpp" + typedef SleepManagerStm SleepManagerImpl; +#elif defined(CONFIG_SOC_ESP32S3) + #include "sleep_manager_esp.hpp" + typedef SleepManagerEsp SleepManagerImpl; +#else + class SleepManagerDummy : public SleepManagerBase { + public: + Peripheral::Status init() override { return Peripheral::Status::OK; } + bool is_ready() const override { return true; } + etl::string get_name() const override { return "Dummy"; } + void enter_sleep(SleepMode mode) override {} + void set_sleep_duration(int duration_ms) override {} + void timed_sleep() override {} + }; + typedef SleepManagerDummy SleepManagerImpl; +#endif + class SleepManager : public Peripheral { public: using SleepMode = SleepManagerBase::SleepMode; using WakeCause = SleepManagerBase::WakeCause; - SleepManager(); + SleepManager() = default; - Peripheral::Status init() override; - bool is_ready() const override; - etl::string get_name() const override; + Peripheral::Status init() override { return impl.init(); } + bool is_ready() const override { return impl.is_ready(); } + etl::string get_name() const override { return impl.get_name(); } - void enter_sleep(SleepMode mode); - void set_sleep_duration(int ms); - void timed_sleep(); + void enter_sleep(SleepMode mode) { impl.enter_sleep(mode); } + void set_sleep_duration(int ms) { impl.set_sleep_duration(ms); } + void timed_sleep() { impl.timed_sleep(); } - WakeCause get_wakeup_cause() const; + WakeCause get_wakeup_cause() const { return impl.get_wakeup_cause(); } private: - etl::unique_ptr impl; + SleepManagerImpl impl; }; -#endif \ No newline at end of file +#endif diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index ed41e3f..979d011 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -2,43 +2,68 @@ LOG_MODULE_REGISTER(sleep_mgr_stm, LOG_LEVEL_INF); +extern struct k_sem wakeup_sem; + +namespace { + struct gpio_callback wkup_cb_data_1; + struct gpio_callback wkup_cb_data_2; + + volatile uint32_t wkup_count = 0; + volatile uint32_t last_wkup_time = 0; + + void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { + uint32_t current_time = k_uptime_get_32(); + + // Debouncing (250ms) + if (current_time - last_wkup_time > 250) { + last_wkup_time = current_time; + wkup_count++; + printk("Wakeup trigger! Total count: %u\n", wkup_count); + k_sem_give(&wakeup_sem); + } + } +} + SleepManagerStm::SleepManagerStm() : initialized(false), sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS) #ifdef CONFIG_SOC_STM32WL55XX , - wkup_gpio(GPIO_DT_SPEC_GET(DT_ALIAS(wkup_src), gpios)), + wkup_gpio_1(GPIO_DT_SPEC_GET(DT_ALIAS(wkup_src_1), gpios)), + wkup_gpio_2(GPIO_DT_SPEC_GET(DT_ALIAS(wkup_src_2), gpios)), rtc() #endif { } -static struct gpio_callback btn_cb_data; - -int count = 0; - -void button_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { - count++; - printk("Button pressed! Total count: %d\n", count); -} - Peripheral::Status SleepManagerStm::init() { #ifdef CONFIG_SOC_STM32WL55XX - if (!device_is_ready(wkup_gpio.port)) { - LOG_ERR("wkup gpio not ready"); - return Peripheral::Status::NOT_READY; - } - const int pin_rc = gpio_pin_configure_dt(&wkup_gpio, GPIO_INPUT); - if (pin_rc != 0) { - LOG_ERR("wkup gpio cfg failed (%d)", pin_rc); - return Peripheral::Status::ERROR_HW_CONFIG_FAILED; + if (!device_is_ready(wkup_gpio_1.port)) { + LOG_ERR("wkup gpio 1 not ready"); + } else { + if (gpio_pin_configure_dt(&wkup_gpio_1, GPIO_INPUT) != 0) { + LOG_ERR("wkup gpio 1 cfg failed"); + } else { + gpio_pin_interrupt_configure_dt(&wkup_gpio_1, GPIO_INT_EDGE_TO_ACTIVE); + gpio_init_callback(&wkup_cb_data_1, wkup_isr, BIT(wkup_gpio_1.pin)); + gpio_add_callback(wkup_gpio_1.port, &wkup_cb_data_1); + LOG_INF("Wakeup source 1 initialized."); + } } - gpio_pin_interrupt_configure_dt(&wkup_gpio, GPIO_INT_EDGE_TO_ACTIVE); - - gpio_init_callback(&btn_cb_data, button_isr, BIT(wkup_gpio.pin)); - gpio_add_callback(wkup_gpio.port, &btn_cb_data); + if (!device_is_ready(wkup_gpio_2.port)) { + LOG_ERR("wkup gpio 2 not ready"); + } else { + if (gpio_pin_configure_dt(&wkup_gpio_2, GPIO_INPUT) != 0) { + LOG_ERR("wkup gpio 2 cfg failed"); + } else { + gpio_pin_interrupt_configure_dt(&wkup_gpio_2, GPIO_INT_EDGE_TO_ACTIVE); + gpio_init_callback(&wkup_cb_data_2, wkup_isr, BIT(wkup_gpio_2.pin)); + gpio_add_callback(wkup_gpio_2.port, &wkup_cb_data_2); + LOG_INF("Wakeup source 2 initialized."); + } + } const Peripheral::Status rtc_rc = rtc.init(); if (rtc_rc != Peripheral::Status::OK && rtc_rc != Peripheral::Status::ERROR_ALREADY_INITIALIZED) { @@ -86,14 +111,14 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { if (!rtc.ensure_time_valid()) { LOG_WRN("rtc time invalid, sleep without alarm"); - sys_poweroff(); + k_sem_take(&wakeup_sem, K_FOREVER); return; } rtc_time now; if (!rtc.get_time(now)) { LOG_WRN("rtc read failed, sleep without alarm"); - sys_poweroff(); + k_sem_take(&wakeup_sem, K_FOREVER); return; } @@ -103,7 +128,7 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { rtc_time alarm_time; if (!rtc.epoch_to_rtc_time(epoch_alarm, alarm_time)) { LOG_WRN("rtc convert failed, sleep without alarm"); - sys_poweroff(); + k_sem_take(&wakeup_sem, K_FOREVER); return; } @@ -112,8 +137,7 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { return; } - //sys_poweroff(); - k_sleep(K_FOREVER); + k_sem_take(&wakeup_sem, K_FOREVER); #else k_msleep(sleep_timeout_ms); diff --git a/app/src/peripherals/sleep/sleep_manager_stm.hpp b/app/src/peripherals/sleep/sleep_manager_stm.hpp index a9ad237..efba291 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.hpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.hpp @@ -29,9 +29,10 @@ class SleepManagerStm : public SleepManagerBase { int sleep_timeout_ms; #ifdef CONFIG_SOC_STM32WL55XX - gpio_dt_spec wkup_gpio; + gpio_dt_spec wkup_gpio_1; + gpio_dt_spec wkup_gpio_2; RtcPeripheral rtc; #endif }; -#endif \ No newline at end of file +#endif diff --git a/app/src/sensors/bme280/bme280.cpp b/app/src/sensors/bme280/bme280.cpp index 309aa57..0b84307 100644 --- a/app/src/sensors/bme280/bme280.cpp +++ b/app/src/sensors/bme280/bme280.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "buzzverse/bme280.pb.h" @@ -14,7 +15,7 @@ using Status = Sensor::Status; Peripheral::Status BME280::init() { if (!device_is_ready(bme280_dev)) { LOG_WRN("BME280 device not ready"); - status = buzzverse_v1_Status_ComponentState_INITIALIZATION_FAILED; + status = buzzverse_v1_Status_ComponentState_INITIALIZATION_FAILED; return Peripheral::Status::NOT_READY; } @@ -27,8 +28,11 @@ Peripheral::Status BME280::init() { Status BME280::read_data(buzzverse_v1_BME280Data& data) const { struct sensor_value temp, press, humidity; + pm_device_runtime_get(bme280_dev); + if (sensor_sample_fetch(bme280_dev) != 0) { LOG_ERR("Failed to fetch BME280 data"); + pm_device_runtime_put(bme280_dev); return Status::READ_ERR; } @@ -36,18 +40,18 @@ Status BME280::read_data(buzzverse_v1_BME280Data& data) const { sensor_channel_get(bme280_dev, SENSOR_CHAN_PRESS, &press); sensor_channel_get(bme280_dev, SENSOR_CHAN_HUMIDITY, &humidity); - // Convert temperature to whole degrees (-128 to 127) + pm_device_runtime_put(bme280_dev); + data.temperature = static_cast(temp.val1); - double total_pressure_kPa = static_cast(press.val1) + static_cast(press.val2) / 1000000.0; + double total_pressure_kPa = + static_cast(press.val1) + static_cast(press.val2) / 1000000.0; double pressure_hPa = total_pressure_kPa * 10.0; int32_t rounded_pressure_hPa = static_cast(round(pressure_hPa)); - // Convert pressure as difference from 1000 hPa (-128 to 127) data.pressure = static_cast(rounded_pressure_hPa - 1000); - // Convert humidity to whole percentage (0-100%) data.humidity = static_cast(humidity.val1); LOG_INF("Temperature: %d C", data.temperature); @@ -58,40 +62,34 @@ Status BME280::read_data(buzzverse_v1_BME280Data& data) const { } Status BME280::get_packet(buzzverse_v1_Packet& packet) const { - buzzverse_v1_BME280Data bme_data = buzzverse_v1_BME280Data_init_zero; + buzzverse_v1_BME280Data bme_data = buzzverse_v1_BME280Data_init_zero; - if(read_data(bme_data) != Sensor::Status::OK) { - return Status::READ_ERR; - } + if (read_data(bme_data) != Sensor::Status::OK) { + return Status::READ_ERR; + } - if (!validate_data(bme_data)) { - LOG_WRN("No valid BME280 data to construct an application packet."); - return Status::READ_ERR; - } + if (!validate_data(bme_data)) { + LOG_WRN("No valid BME280 data to construct an application packet."); + return Status::READ_ERR; + } - packet = buzzverse_v1_Packet_init_default; - packet.which_data = buzzverse_v1_Packet_bme280_tag; - packet.data.bme280 = bme_data; - LOG_DBG("Packet constructed with BME280 data."); + packet = buzzverse_v1_Packet_init_default; + packet.which_data = buzzverse_v1_Packet_bme280_tag; + packet.data.bme280 = bme_data; + LOG_DBG("Packet constructed with BME280 data."); - return Status::OK; + return Status::OK; } void BME280::get_status(buzzverse_v1_Status& status_message) const { - status_message.bme280_status = status; + status_message.bme280_status = status; } bool BME280::validate_data(buzzverse_v1_BME280Data& data) const { - if( - (data.temperature >= -40 && data.temperature <= 85) && - // We return the difference from 1000 hPa, and the BME operating range for pressure is <300, 1100> hPa. - // Note that the pressure value is saved as a 8-bit signed integer, - // so in reality we don't expect values outside <-128, 127> = <872, 1127> hPa. - // This function however stays true to the operating ranges according to the BME280 datasheet. - (data.pressure >= -700 && data.pressure <= 100) && - (data.humidity >= 0 && data.humidity <= 100) - ) - return true; - else - return false; + if ((data.temperature >= -40 && data.temperature <= 85) && + (data.pressure >= -700 && data.pressure <= 100) && + (data.humidity >= 0 && data.humidity <= 100)) + return true; + else + return false; } From 5e3e8a7dd8557073cb6cb66951388fcf89b5c80e Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 16 Mar 2026 19:30:05 +0100 Subject: [PATCH 06/19] feat: implement wakeup count management in sleep manager; enhance application cycle reporting --- app/src/Application.cpp | 44 ++++++++----- app/src/peripherals/sleep/sleep_manager.hpp | 63 +++++++++++++------ .../peripherals/sleep/sleep_manager_base.hpp | 2 + .../peripherals/sleep/sleep_manager_stm.cpp | 7 ++- .../peripherals/sleep/sleep_manager_stm.hpp | 2 + 5 files changed, 83 insertions(+), 35 deletions(-) diff --git a/app/src/Application.cpp b/app/src/Application.cpp index 3409387..34d038f 100644 --- a/app/src/Application.cpp +++ b/app/src/Application.cpp @@ -84,24 +84,38 @@ void Application::generate_init_failure_report(buzzverse_v1_Packet& packet) { } void Application::run_cycle() { - k_msleep(50); - - LOG_INF("--- Starting Application Cycle ---"); + k_msleep(50); + LOG_INF("--- Starting Application Cycle ---"); + + if (m_sleep_manager) { + uint32_t count = m_sleep_manager->get_and_clear_wakeup_count(); + + if (count > 0) { + LOG_INF("Detected %u wakeup events. Sending report...", count); + + buzzverse_v1_Packet packet = buzzverse_v1_Packet_init_default; + packet.which_data = buzzverse_v1_Packet_status_tag; + + for (auto& sensor : m_sensors) { + if (sensor) sensor->get_status(packet.data.status); + } + + send_lora_packet(packet); + } + } - for(auto& sensor: m_sensors) { - if (sensor && sensor->is_ready()) { - buzzverse_v1_Packet packet; - if (sensor->get_packet(packet) == Sensor::Status::OK) { - send_lora_packet(packet); - } else { - LOG_ERR("Failed to get %s packet", sensor->get_name().c_str()); - } - } else { - LOG_ERR("%s not ready for reading.", sensor->get_name().c_str()); + for (auto& sensor : m_sensors) { + if (sensor && sensor->is_ready()) { + buzzverse_v1_Packet packet; + if (sensor->get_packet(packet) == Sensor::Status::OK) { + send_lora_packet(packet); + } else { + LOG_ERR("Failed to get %s packet", sensor->get_name().c_str()); + } + } } - } - LOG_INF("--- Application Cycle Complete ---"); + LOG_INF("--- Application Cycle Complete ---"); } void Application::enter_low_power_mode(int sleep_duration_ms) { diff --git a/app/src/peripherals/sleep/sleep_manager.hpp b/app/src/peripherals/sleep/sleep_manager.hpp index cfc8cef..0542603 100644 --- a/app/src/peripherals/sleep/sleep_manager.hpp +++ b/app/src/peripherals/sleep/sleep_manager.hpp @@ -2,26 +2,33 @@ #define SLEEP_MANAGER_HPP #include + #include "peripheral.hpp" #include "sleep_manager_base.hpp" #if defined(CONFIG_SOC_STM32WL55XX) #include "sleep_manager_stm.hpp" - typedef SleepManagerStm SleepManagerImpl; +typedef SleepManagerStm SleepManagerImpl; #elif defined(CONFIG_SOC_ESP32S3) #include "sleep_manager_esp.hpp" - typedef SleepManagerEsp SleepManagerImpl; +typedef SleepManagerEsp SleepManagerImpl; #else - class SleepManagerDummy : public SleepManagerBase { - public: - Peripheral::Status init() override { return Peripheral::Status::OK; } - bool is_ready() const override { return true; } - etl::string get_name() const override { return "Dummy"; } - void enter_sleep(SleepMode mode) override {} - void set_sleep_duration(int duration_ms) override {} - void timed_sleep() override {} - }; - typedef SleepManagerDummy SleepManagerImpl; +class SleepManagerDummy : public SleepManagerBase { + public: + Peripheral::Status init() override { + return Peripheral::Status::OK; + } + bool is_ready() const override { + return true; + } + etl::string get_name() const override { + return "Dummy"; + } + void enter_sleep(SleepMode mode) override {} + void set_sleep_duration(int duration_ms) override {} + void timed_sleep() override {} +}; +typedef SleepManagerDummy SleepManagerImpl; #endif class SleepManager : public Peripheral { @@ -31,15 +38,33 @@ class SleepManager : public Peripheral { SleepManager() = default; - Peripheral::Status init() override { return impl.init(); } - bool is_ready() const override { return impl.is_ready(); } - etl::string get_name() const override { return impl.get_name(); } + Peripheral::Status init() override { + return impl.init(); + } + bool is_ready() const override { + return impl.is_ready(); + } + etl::string get_name() const override { + return impl.get_name(); + } + + uint32_t get_and_clear_wakeup_count() { + return impl.get_and_clear_wakeup_count(); + } - void enter_sleep(SleepMode mode) { impl.enter_sleep(mode); } - void set_sleep_duration(int ms) { impl.set_sleep_duration(ms); } - void timed_sleep() { impl.timed_sleep(); } + void enter_sleep(SleepMode mode) { + impl.enter_sleep(mode); + } + void set_sleep_duration(int ms) { + impl.set_sleep_duration(ms); + } + void timed_sleep() { + impl.timed_sleep(); + } - WakeCause get_wakeup_cause() const { return impl.get_wakeup_cause(); } + WakeCause get_wakeup_cause() const { + return impl.get_wakeup_cause(); + } private: SleepManagerImpl impl; diff --git a/app/src/peripherals/sleep/sleep_manager_base.hpp b/app/src/peripherals/sleep/sleep_manager_base.hpp index 2fd03f2..89e6df8 100644 --- a/app/src/peripherals/sleep/sleep_manager_base.hpp +++ b/app/src/peripherals/sleep/sleep_manager_base.hpp @@ -20,6 +20,8 @@ class SleepManagerBase : public Peripheral { virtual bool is_ready() const = 0; virtual etl::string get_name() const = 0; + virtual uint32_t get_and_clear_wakeup_count() { return 0; } + virtual void enter_sleep(SleepMode mode) = 0; virtual void set_sleep_duration(int duration_ms) = 0; virtual void timed_sleep() = 0; diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 979d011..6308531 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -19,7 +19,6 @@ namespace { last_wkup_time = current_time; wkup_count++; printk("Wakeup trigger! Total count: %u\n", wkup_count); - k_sem_give(&wakeup_sem); } } } @@ -36,6 +35,12 @@ SleepManagerStm::SleepManagerStm() { } +uint32_t SleepManagerStm::get_and_clear_wakeup_count() { + uint32_t current_count = wkup_count; + wkup_count = 0; + return current_count; +} + Peripheral::Status SleepManagerStm::init() { #ifdef CONFIG_SOC_STM32WL55XX diff --git a/app/src/peripherals/sleep/sleep_manager_stm.hpp b/app/src/peripherals/sleep/sleep_manager_stm.hpp index efba291..6085d48 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.hpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.hpp @@ -20,6 +20,8 @@ class SleepManagerStm : public SleepManagerBase { bool is_ready() const override; etl::string get_name() const override; + uint32_t get_and_clear_wakeup_count() override; + void enter_sleep(SleepMode mode) override; void set_sleep_duration(int duration_ms) override; void timed_sleep() override; From b77063c251a80e04df449f9f874ca5b3cad64d0b Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 16 Mar 2026 20:02:48 +0100 Subject: [PATCH 07/19] feat: update packet data to include wakeup event count in run_cycle --- app/protobufs | 2 +- app/src/Application.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/protobufs b/app/protobufs index 2ad0cf8..d2578b5 160000 --- a/app/protobufs +++ b/app/protobufs @@ -1 +1 @@ -Subproject commit 2ad0cf8d53c186a9fe53f264de15780f4ca91b4b +Subproject commit d2578b556f4a0ae0dbb673023ffc6b7c6896ec5a diff --git a/app/src/Application.cpp b/app/src/Application.cpp index 34d038f..680756b 100644 --- a/app/src/Application.cpp +++ b/app/src/Application.cpp @@ -94,7 +94,8 @@ void Application::run_cycle() { LOG_INF("Detected %u wakeup events. Sending report...", count); buzzverse_v1_Packet packet = buzzverse_v1_Packet_init_default; - packet.which_data = buzzverse_v1_Packet_status_tag; + packet.which_data = buzzverse_v1_Packet_counter_tag; + packet.data.counter.counter = count; for (auto& sensor : m_sensors) { if (sensor) sensor->get_status(packet.data.status); From 6b20778e042b4d13d681d69c3390670bb41de443 Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 16 Mar 2026 20:38:34 +0100 Subject: [PATCH 08/19] feat: optimize run_cycle by removing redundant sensor status retrieval --- app/src/Application.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/Application.cpp b/app/src/Application.cpp index 680756b..778a4c1 100644 --- a/app/src/Application.cpp +++ b/app/src/Application.cpp @@ -96,10 +96,6 @@ void Application::run_cycle() { buzzverse_v1_Packet packet = buzzverse_v1_Packet_init_default; packet.which_data = buzzverse_v1_Packet_counter_tag; packet.data.counter.counter = count; - - for (auto& sensor : m_sensors) { - if (sensor) sensor->get_status(packet.data.status); - } send_lora_packet(packet); } @@ -107,7 +103,7 @@ void Application::run_cycle() { for (auto& sensor : m_sensors) { if (sensor && sensor->is_ready()) { - buzzverse_v1_Packet packet; + buzzverse_v1_Packet packet = buzzverse_v1_Packet_init_default; if (sensor->get_packet(packet) == Sensor::Status::OK) { send_lora_packet(packet); } else { From f463f43a741036ed8509aa031ed53e04fcc56701 Mon Sep 17 00:00:00 2001 From: jacek-kow <124920545+jacek-kow@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:09:32 +0100 Subject: [PATCH 09/19] refactor: changed naked battery sensor pointer to unique_ptr --- app/src/peripherals/lorawan_handler/lorawan_handler.cpp | 6 +++--- app/src/peripherals/lorawan_handler/lorawan_handler.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/peripherals/lorawan_handler/lorawan_handler.cpp b/app/src/peripherals/lorawan_handler/lorawan_handler.cpp index b31ec83..2564cde 100644 --- a/app/src/peripherals/lorawan_handler/lorawan_handler.cpp +++ b/app/src/peripherals/lorawan_handler/lorawan_handler.cpp @@ -9,10 +9,10 @@ LOG_MODULE_REGISTER(lorawan_handler, LOG_LEVEL_DBG); -Sensor* LoRaWANHandler::battery_sensor = nullptr; +etl::unique_ptr LoRaWANHandler::battery_sensor(nullptr); -LoRaWANHandler::LoRaWANHandler(Sensor& battery_sensor_ref) { - LoRaWANHandler::battery_sensor = &battery_sensor_ref; +LoRaWANHandler::LoRaWANHandler(Sensor& battery_sensor) { + LoRaWANHandler::battery_sensor = etl::unique_ptr(etl::move(&battery_sensor)); #if defined(CONFIG_LORAWAN_JOIN_OTAA) const char* dev_eui_str = CONFIG_LORAWAN_DEV_EUI; diff --git a/app/src/peripherals/lorawan_handler/lorawan_handler.hpp b/app/src/peripherals/lorawan_handler/lorawan_handler.hpp index c3d5c47..5dca59c 100644 --- a/app/src/peripherals/lorawan_handler/lorawan_handler.hpp +++ b/app/src/peripherals/lorawan_handler/lorawan_handler.hpp @@ -54,7 +54,7 @@ class LoRaWANHandler : public Peripheral { etl::array app_skey; etl::array nwk_skey; - static Sensor* battery_sensor; + static etl::unique_ptr battery_sensor; static uint8_t battery_level_callback(); }; From f016792e09117465b43c52ca2228ed8342c0d2cf Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 23 Mar 2026 17:01:35 +0100 Subject: [PATCH 10/19] feat: refactor sleep manager to support multiple wakeup sources and enhance GPIO handling --- app/boards/nucleo_wl55jc.overlay | 14 +- .../peripherals/sleep/sleep_manager_stm.cpp | 142 ++++++------------ .../peripherals/sleep/sleep_manager_stm.hpp | 14 +- 3 files changed, 60 insertions(+), 110 deletions(-) diff --git a/app/boards/nucleo_wl55jc.overlay b/app/boards/nucleo_wl55jc.overlay index 3dd3907..277c53e 100644 --- a/app/boards/nucleo_wl55jc.overlay +++ b/app/boards/nucleo_wl55jc.overlay @@ -3,23 +3,13 @@ / { aliases { bme280-i2c = &i2c2; - wkup-src-1 = &user_input_pc4; - wkup-src-2 = &user_button_1; - }; - - gpio_keys { - compatible = "gpio-keys"; - - user_input_pc4: pc4_input { - label = "PC4_INPUT"; - gpios = <&gpioc 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>; - zephyr,code = ; - }; }; zephyr,user { io-channels = <&adc1 6>; io-channels-names = "a0"; + wakeup-gpios = <&gpioc 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>, + <&gpioa 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; }; }; diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 6308531..9d27c9c 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -2,91 +2,82 @@ LOG_MODULE_REGISTER(sleep_mgr_stm, LOG_LEVEL_INF); -extern struct k_sem wakeup_sem; - namespace { - struct gpio_callback wkup_cb_data_1; - struct gpio_callback wkup_cb_data_2; - volatile uint32_t wkup_count = 0; volatile uint32_t last_wkup_time = 0; void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { uint32_t current_time = k_uptime_get_32(); - // Debouncing (250ms) if (current_time - last_wkup_time > 250) { last_wkup_time = current_time; wkup_count++; - printk("Wakeup trigger! Total count: %u\n", wkup_count); } } } +extern struct k_sem wakeup_sem; + +#define WAKEUP_PIN_CFG(node_id, prop, idx) \ + GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx), + SleepManagerStm::SleepManagerStm() : initialized(false), - sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS) + sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS) { #ifdef CONFIG_SOC_STM32WL55XX - , - wkup_gpio_1(GPIO_DT_SPEC_GET(DT_ALIAS(wkup_src_1), gpios)), - wkup_gpio_2(GPIO_DT_SPEC_GET(DT_ALIAS(wkup_src_2), gpios)), - rtc() + static const struct gpio_dt_spec specs[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), wakeup_gpios, WAKEUP_PIN_CFG) + }; + + for (size_t i = 0; i < ARRAY_SIZE(specs); i++) { + if (!wakeup_pins.full()) { + WakeupPin pin; + pin.spec = specs[i]; + wakeup_pins.push_back(pin); + } else { + LOG_WRN("Max wakeup pins reached, skipping some."); + break; + } + } #endif -{ -} - -uint32_t SleepManagerStm::get_and_clear_wakeup_count() { - uint32_t current_count = wkup_count; - wkup_count = 0; - return current_count; } Peripheral::Status SleepManagerStm::init() { #ifdef CONFIG_SOC_STM32WL55XX - - if (!device_is_ready(wkup_gpio_1.port)) { - LOG_ERR("wkup gpio 1 not ready"); - } else { - if (gpio_pin_configure_dt(&wkup_gpio_1, GPIO_INPUT) != 0) { - LOG_ERR("wkup gpio 1 cfg failed"); - } else { - gpio_pin_interrupt_configure_dt(&wkup_gpio_1, GPIO_INT_EDGE_TO_ACTIVE); - gpio_init_callback(&wkup_cb_data_1, wkup_isr, BIT(wkup_gpio_1.pin)); - gpio_add_callback(wkup_gpio_1.port, &wkup_cb_data_1); - LOG_INF("Wakeup source 1 initialized."); + for (auto& pin : wakeup_pins) { + if (!device_is_ready(pin.spec.port)) { + LOG_ERR("GPIO port %s not ready", pin.spec.port->name); + continue; } - } - if (!device_is_ready(wkup_gpio_2.port)) { - LOG_ERR("wkup gpio 2 not ready"); - } else { - if (gpio_pin_configure_dt(&wkup_gpio_2, GPIO_INPUT) != 0) { - LOG_ERR("wkup gpio 2 cfg failed"); - } else { - gpio_pin_interrupt_configure_dt(&wkup_gpio_2, GPIO_INT_EDGE_TO_ACTIVE); - gpio_init_callback(&wkup_cb_data_2, wkup_isr, BIT(wkup_gpio_2.pin)); - gpio_add_callback(wkup_gpio_2.port, &wkup_cb_data_2); - LOG_INF("Wakeup source 2 initialized."); + if (gpio_pin_configure_dt(&pin.spec, GPIO_INPUT) != 0) { + LOG_ERR("Pin %d cfg failed", pin.spec.pin); + continue; } + + gpio_pin_interrupt_configure_dt(&pin.spec, GPIO_INT_EDGE_TO_ACTIVE); + gpio_init_callback(&pin.cb_data, wkup_isr, BIT(pin.spec.pin)); + gpio_add_callback(pin.spec.port, &pin.cb_data); + + LOG_INF("Wakeup source initialized: Pin %d on %s", pin.spec.pin, pin.spec.port->name); } const Peripheral::Status rtc_rc = rtc.init(); if (rtc_rc != Peripheral::Status::OK && rtc_rc != Peripheral::Status::ERROR_ALREADY_INITIALIZED) { - LOG_ERR("rtc init failed (%d)", static_cast(rtc_rc)); return rtc_rc; } - - if (!rtc.is_ready()) { - LOG_ERR("rtc not ready after init"); - return Peripheral::Status::NOT_READY; - } - #endif initialized = true; return Peripheral::Status::OK; } +uint32_t SleepManagerStm::get_and_clear_wakeup_count() { + uint32_t current_count = wkup_count; + wkup_count = 0; + return current_count; +} + bool SleepManagerStm::is_ready() const { #ifdef CONFIG_SOC_STM32WL55XX return initialized && rtc.is_ready(); @@ -101,67 +92,32 @@ etl::string SleepManagerStm::get_name() const { void SleepManagerStm::enter_sleep(SleepMode mode) { ARG_UNUSED(mode); - - if (!initialized) { - LOG_ERR("sleep_mgr not initialized"); - return; - } + if (!initialized) return; #ifdef CONFIG_SOC_STM32WL55XX - if (!rtc.is_ready()) { - LOG_ERR("rtc unavailable"); - k_msleep(sleep_timeout_ms); - return; - } - - if (!rtc.ensure_time_valid()) { - LOG_WRN("rtc time invalid, sleep without alarm"); + if (!rtc.is_ready() || !rtc.ensure_time_valid()) { k_sem_take(&wakeup_sem, K_FOREVER); return; } rtc_time now; - if (!rtc.get_time(now)) { - LOG_WRN("rtc read failed, sleep without alarm"); - k_sem_take(&wakeup_sem, K_FOREVER); - return; - } - - const int64_t epoch_now = rtc.to_epoch(now); - const int64_t epoch_alarm = epoch_now + (sleep_timeout_ms / 1000); - - rtc_time alarm_time; - if (!rtc.epoch_to_rtc_time(epoch_alarm, alarm_time)) { - LOG_WRN("rtc convert failed, sleep without alarm"); - k_sem_take(&wakeup_sem, K_FOREVER); - return; - } - - if (!rtc.set_alarm(alarm_time)) { - LOG_WRN("rtc alarm set failed, sleep without alarm"); - return; + if (rtc.get_time(now)) { + int64_t epoch_alarm = rtc.to_epoch(now) + (sleep_timeout_ms / 1000); + rtc_time alarm_time; + if (rtc.epoch_to_rtc_time(epoch_alarm, alarm_time)) { + rtc.set_alarm(alarm_time); + } } - k_sem_take(&wakeup_sem, K_FOREVER); - #else k_msleep(sleep_timeout_ms); #endif } void SleepManagerStm::set_sleep_duration(int duration_ms) { - if (duration_ms <= 0) { - LOG_WRN("invalid sleep timeout %d", duration_ms); - return; - } - sleep_timeout_ms = duration_ms; + if (duration_ms > 0) sleep_timeout_ms = duration_ms; } void SleepManagerStm::timed_sleep() { - if (!initialized) { - LOG_ERR("sleep_mgr not initialized"); - return; - } - - k_msleep(sleep_timeout_ms); + if (initialized) k_msleep(sleep_timeout_ms); } diff --git a/app/src/peripherals/sleep/sleep_manager_stm.hpp b/app/src/peripherals/sleep/sleep_manager_stm.hpp index 6085d48..b17eece 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.hpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.hpp @@ -2,16 +2,19 @@ #define SLEEP_MANAGER_STM_HPP #include -#include #include #include #include -#include -#include +#include #include "peripherals/rtc/rtc_peripheral.hpp" #include "sleep_manager_base.hpp" +struct WakeupPin { + struct gpio_dt_spec spec; + struct gpio_callback cb_data; +}; + class SleepManagerStm : public SleepManagerBase { public: SleepManagerStm(); @@ -27,12 +30,13 @@ class SleepManagerStm : public SleepManagerBase { void timed_sleep() override; private: + static constexpr size_t MAX_WAKEUP_PINS = 4; + etl::vector wakeup_pins; + bool initialized; int sleep_timeout_ms; #ifdef CONFIG_SOC_STM32WL55XX - gpio_dt_spec wkup_gpio_1; - gpio_dt_spec wkup_gpio_2; RtcPeripheral rtc; #endif }; From 52fd5f535c702ac6d84bba509487259f689846c4 Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 23 Mar 2026 17:09:02 +0100 Subject: [PATCH 11/19] refactor: clean up formatting and improve logging in sleep manager initialization --- .../peripherals/sleep/sleep_manager_stm.cpp | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 9d27c9c..65ca5d9 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -3,31 +3,29 @@ LOG_MODULE_REGISTER(sleep_mgr_stm, LOG_LEVEL_INF); namespace { - volatile uint32_t wkup_count = 0; - volatile uint32_t last_wkup_time = 0; - - void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { - uint32_t current_time = k_uptime_get_32(); - - if (current_time - last_wkup_time > 250) { - last_wkup_time = current_time; - wkup_count++; - } +volatile uint32_t wkup_count = 0; +volatile uint32_t last_wkup_time = 0; + +void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { + uint32_t current_time = k_uptime_get_32(); + + if (current_time - last_wkup_time > 250) { + last_wkup_time = current_time; + wkup_count++; } } +} // namespace extern struct k_sem wakeup_sem; -#define WAKEUP_PIN_CFG(node_id, prop, idx) \ - GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx), +#define WAKEUP_PIN_CFG(node_id, prop, idx) GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx), SleepManagerStm::SleepManagerStm() - : initialized(false), - sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS) { + : initialized(false), sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS) { #ifdef CONFIG_SOC_STM32WL55XX + #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), wakeup_gpios) static const struct gpio_dt_spec specs[] = { - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), wakeup_gpios, WAKEUP_PIN_CFG) - }; + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), wakeup_gpios, WAKEUP_PIN_CFG)}; for (size_t i = 0; i < ARRAY_SIZE(specs); i++) { if (!wakeup_pins.full()) { @@ -35,15 +33,22 @@ SleepManagerStm::SleepManagerStm() pin.spec = specs[i]; wakeup_pins.push_back(pin); } else { - LOG_WRN("Max wakeup pins reached, skipping some."); + LOG_WRN("Max wakeup pins reached."); break; } } + #else + LOG_WRN("No wakeup-gpios defined in zephyr,user node."); + #endif #endif } Peripheral::Status SleepManagerStm::init() { #ifdef CONFIG_SOC_STM32WL55XX + if (wakeup_pins.empty()) { + LOG_INF("No wakeup pins to configure."); + } + for (auto& pin : wakeup_pins) { if (!device_is_ready(pin.spec.port)) { LOG_ERR("GPIO port %s not ready", pin.spec.port->name); @@ -58,7 +63,7 @@ Peripheral::Status SleepManagerStm::init() { gpio_pin_interrupt_configure_dt(&pin.spec, GPIO_INT_EDGE_TO_ACTIVE); gpio_init_callback(&pin.cb_data, wkup_isr, BIT(pin.spec.pin)); gpio_add_callback(pin.spec.port, &pin.cb_data); - + LOG_INF("Wakeup source initialized: Pin %d on %s", pin.spec.pin, pin.spec.port->name); } From 82272f5d16628b2fbaada98d194814d3815bd4a3 Mon Sep 17 00:00:00 2001 From: Olaf Hubert Bykowski Date: Mon, 23 Mar 2026 17:11:40 +0100 Subject: [PATCH 12/19] feat: update sleep manager to support additional STM32WLE5XX configuration --- app/src/peripherals/sleep/sleep_manager_stm.cpp | 8 ++++---- app/src/peripherals/sleep/sleep_manager_stm.hpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 65ca5d9..6d6269c 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -22,7 +22,7 @@ extern struct k_sem wakeup_sem; SleepManagerStm::SleepManagerStm() : initialized(false), sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS) { -#ifdef CONFIG_SOC_STM32WL55XX +#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), wakeup_gpios) static const struct gpio_dt_spec specs[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), wakeup_gpios, WAKEUP_PIN_CFG)}; @@ -44,7 +44,7 @@ SleepManagerStm::SleepManagerStm() } Peripheral::Status SleepManagerStm::init() { -#ifdef CONFIG_SOC_STM32WL55XX +#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) if (wakeup_pins.empty()) { LOG_INF("No wakeup pins to configure."); } @@ -84,7 +84,7 @@ uint32_t SleepManagerStm::get_and_clear_wakeup_count() { } bool SleepManagerStm::is_ready() const { -#ifdef CONFIG_SOC_STM32WL55XX +#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) return initialized && rtc.is_ready(); #else return initialized; @@ -99,7 +99,7 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { ARG_UNUSED(mode); if (!initialized) return; -#ifdef CONFIG_SOC_STM32WL55XX +#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) if (!rtc.is_ready() || !rtc.ensure_time_valid()) { k_sem_take(&wakeup_sem, K_FOREVER); return; diff --git a/app/src/peripherals/sleep/sleep_manager_stm.hpp b/app/src/peripherals/sleep/sleep_manager_stm.hpp index b17eece..25c5734 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.hpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.hpp @@ -1,11 +1,11 @@ #ifndef SLEEP_MANAGER_STM_HPP #define SLEEP_MANAGER_STM_HPP +#include #include #include #include #include -#include #include "peripherals/rtc/rtc_peripheral.hpp" #include "sleep_manager_base.hpp" @@ -24,7 +24,7 @@ class SleepManagerStm : public SleepManagerBase { etl::string get_name() const override; uint32_t get_and_clear_wakeup_count() override; - + void enter_sleep(SleepMode mode) override; void set_sleep_duration(int duration_ms) override; void timed_sleep() override; @@ -32,11 +32,11 @@ class SleepManagerStm : public SleepManagerBase { private: static constexpr size_t MAX_WAKEUP_PINS = 4; etl::vector wakeup_pins; - + bool initialized; int sleep_timeout_ms; -#ifdef CONFIG_SOC_STM32WL55XX +#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) RtcPeripheral rtc; #endif }; From 97080f7382ab9320def4ef8f766dcfb9446ec0e3 Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 23 Mar 2026 17:27:03 +0100 Subject: [PATCH 13/19] feat: add GPIO wakeup debounce configuration and update STM32 series checks --- app/Kconfig | 7 +++++++ app/src/peripherals/sleep/sleep_manager.hpp | 2 +- app/src/peripherals/sleep/sleep_manager_stm.cpp | 12 ++++++------ app/src/peripherals/sleep/sleep_manager_stm.hpp | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/Kconfig b/app/Kconfig index fcfc09d..255e66a 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -81,6 +81,13 @@ config SLEEP_TIME_MS help Duration for which the device will sleep before waking up. This value is in milliseconds. +config GPIO_WAKEUP_DEBOUNCE_MS + int "GPIO Wakeup Debounce Time (ms)" + default 1000 + help + Time in milliseconds to ignore subsequent transitions on wakeup pins + to prevent multiple triggers from switch bouncing. + config ENABLE_ANALOG bool "Enable Analog Sensor" default n diff --git a/app/src/peripherals/sleep/sleep_manager.hpp b/app/src/peripherals/sleep/sleep_manager.hpp index 0542603..dbbe474 100644 --- a/app/src/peripherals/sleep/sleep_manager.hpp +++ b/app/src/peripherals/sleep/sleep_manager.hpp @@ -6,7 +6,7 @@ #include "peripheral.hpp" #include "sleep_manager_base.hpp" -#if defined(CONFIG_SOC_STM32WL55XX) +#if defined(CONFIG_SOC_SERIES_STM32WLX) #include "sleep_manager_stm.hpp" typedef SleepManagerStm SleepManagerImpl; #elif defined(CONFIG_SOC_ESP32S3) diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 6d6269c..410d70e 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -9,12 +9,12 @@ volatile uint32_t last_wkup_time = 0; void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { uint32_t current_time = k_uptime_get_32(); - if (current_time - last_wkup_time > 250) { + if (current_time - last_wkup_time > CONFIG_GPIO_WAKEUP_DEBOUNCE_MS) { last_wkup_time = current_time; wkup_count++; } } -} // namespace +} extern struct k_sem wakeup_sem; @@ -22,7 +22,7 @@ extern struct k_sem wakeup_sem; SleepManagerStm::SleepManagerStm() : initialized(false), sleep_timeout_ms(DEFAULT_SLEEP_DURATION_MS) { -#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) +#if defined(CONFIG_SOC_SERIES_STM32WLX) #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), wakeup_gpios) static const struct gpio_dt_spec specs[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), wakeup_gpios, WAKEUP_PIN_CFG)}; @@ -44,7 +44,7 @@ SleepManagerStm::SleepManagerStm() } Peripheral::Status SleepManagerStm::init() { -#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) +#if defined(CONFIG_SOC_SERIES_STM32WLX) if (wakeup_pins.empty()) { LOG_INF("No wakeup pins to configure."); } @@ -84,7 +84,7 @@ uint32_t SleepManagerStm::get_and_clear_wakeup_count() { } bool SleepManagerStm::is_ready() const { -#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) +#if defined(CONFIG_SOC_SERIES_STM32WLX) return initialized && rtc.is_ready(); #else return initialized; @@ -99,7 +99,7 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { ARG_UNUSED(mode); if (!initialized) return; -#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) +#if defined(CONFIG_SOC_SERIES_STM32WLX) if (!rtc.is_ready() || !rtc.ensure_time_valid()) { k_sem_take(&wakeup_sem, K_FOREVER); return; diff --git a/app/src/peripherals/sleep/sleep_manager_stm.hpp b/app/src/peripherals/sleep/sleep_manager_stm.hpp index 25c5734..26a6358 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.hpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.hpp @@ -36,7 +36,7 @@ class SleepManagerStm : public SleepManagerBase { bool initialized; int sleep_timeout_ms; -#if defined(CONFIG_SOC_STM32WL55XX) || defined(CONFIG_SOC_STM32WLE5XX) +#if defined(CONFIG_SOC_SERIES_STM32WLX) RtcPeripheral rtc; #endif }; From 9169e89eebc86117a114e7997e6624d62b8be71c Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 23 Mar 2026 17:34:07 +0100 Subject: [PATCH 14/19] feat: add logging for wakeup ISR to track GPIO pin triggers --- app/src/peripherals/sleep/sleep_manager_stm.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 410d70e..00885a1 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -9,6 +9,8 @@ volatile uint32_t last_wkup_time = 0; void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { uint32_t current_time = k_uptime_get_32(); + LOG_INF("Wakeup ISR triggered on device %s, pins 0x%08x", dev->name, pins); + if (current_time - last_wkup_time > CONFIG_GPIO_WAKEUP_DEBOUNCE_MS) { last_wkup_time = current_time; wkup_count++; From bf8d56e6b6f237ca2b3f180b93c1c5581f7b6f99 Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 23 Mar 2026 17:47:49 +0100 Subject: [PATCH 15/19] feat: remove unnecessary device runtime management in BME280 sensor read_data --- app/src/sensors/bme280/bme280.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/src/sensors/bme280/bme280.cpp b/app/src/sensors/bme280/bme280.cpp index 0b84307..677af5d 100644 --- a/app/src/sensors/bme280/bme280.cpp +++ b/app/src/sensors/bme280/bme280.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "buzzverse/bme280.pb.h" @@ -28,11 +27,8 @@ Peripheral::Status BME280::init() { Status BME280::read_data(buzzverse_v1_BME280Data& data) const { struct sensor_value temp, press, humidity; - pm_device_runtime_get(bme280_dev); - if (sensor_sample_fetch(bme280_dev) != 0) { LOG_ERR("Failed to fetch BME280 data"); - pm_device_runtime_put(bme280_dev); return Status::READ_ERR; } @@ -40,8 +36,6 @@ Status BME280::read_data(buzzverse_v1_BME280Data& data) const { sensor_channel_get(bme280_dev, SENSOR_CHAN_PRESS, &press); sensor_channel_get(bme280_dev, SENSOR_CHAN_HUMIDITY, &humidity); - pm_device_runtime_put(bme280_dev); - data.temperature = static_cast(temp.val1); double total_pressure_kPa = From 00444f6bb57924268fd7a94eccd6d6ee56c0d5e9 Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 23 Mar 2026 18:08:20 +0100 Subject: [PATCH 16/19] feat: enhance peripheral status codes and improve logging in RTC peripheral initialization --- app/src/peripherals/peripheral.hpp | 13 ++--- app/src/peripherals/rtc/rtc_peripheral.cpp | 9 ++- .../peripherals/sleep/sleep_manager_stm.cpp | 2 + app/src/sensors/bme280/bme280.cpp | 56 +++++++++++-------- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/app/src/peripherals/peripheral.hpp b/app/src/peripherals/peripheral.hpp index 4731cc4..17c0c73 100644 --- a/app/src/peripherals/peripheral.hpp +++ b/app/src/peripherals/peripheral.hpp @@ -16,13 +16,12 @@ class Peripheral { * @brief Generic peripheral status codes */ enum class Status { - OK = 0, /**< Operation successful */ - INIT_ERR = -1, /**< Initialization failed */ - NOT_READY = -2, /**< Peripheral is not ready */ - ERROR_INVALID_PARAM = -3, /**< Invalid parameter provided */ - ERROR_NOT_INITIALIZED = -4, /**< Initialization failed */ - ERROR_HW_CONFIG_FAILED = -5, /**< Hardware configuration failed */ - ERROR_ALREADY_INITIALIZED = -6 /**< Peripheral already initialized */ + OK = 0, /**< Operation successful */ + INIT_ERR = -1, /**< Initialization failed */ + NOT_READY = -2, /**< Peripheral is not ready */ + ERROR_INVALID_PARAM = -3, /**< Invalid parameter provided */ + ERROR_NOT_INITIALIZED = -4, /**< Initialization failed */ + ERROR_HW_CONFIG_FAILED = -5, /**< Hardware configuration failed */ }; /** diff --git a/app/src/peripherals/rtc/rtc_peripheral.cpp b/app/src/peripherals/rtc/rtc_peripheral.cpp index 1c568e7..549d060 100644 --- a/app/src/peripherals/rtc/rtc_peripheral.cpp +++ b/app/src/peripherals/rtc/rtc_peripheral.cpp @@ -7,7 +7,6 @@ extern struct k_sem wakeup_sem; RtcPeripheral::RtcPeripheral() : rtc_dev(DEVICE_DT_GET(DT_NODELABEL(rtc))), initialized(false) {} static void rtc_alarm_handler(const struct device* dev, uint16_t id, void* user_data) { - printk("RTC ALARM FIRED! id=%u\n", id); k_sem_give(&wakeup_sem); } @@ -29,7 +28,11 @@ Peripheral::Status RtcPeripheral::init() { } } - rtc_alarm_set_callback(rtc_dev, 0, rtc_alarm_handler, nullptr); + int rc = rtc_alarm_set_callback(rtc_dev, 0, rtc_alarm_handler, nullptr); + if (rc != 0) { + LOG_ERR("rtc alarm callback registration failed (err %d)", rc); + return Peripheral::Status::ERROR_HW_CONFIG_FAILED; + } initialized = true; return Peripheral::Status::OK; @@ -100,7 +103,7 @@ bool RtcPeripheral::epoch_to_rtc_time(int64_t epoch, rtc_time& out) const { bool RtcPeripheral::set_default_time() { #ifndef CONFIG_DEFAULT_RTC_YEAR -#define CONFIG_DEFAULT_RTC_YEAR 2025 + #define CONFIG_DEFAULT_RTC_YEAR 2025 #endif rtc_time def{}; diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 00885a1..41f426f 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -14,6 +14,8 @@ void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) if (current_time - last_wkup_time > CONFIG_GPIO_WAKEUP_DEBOUNCE_MS) { last_wkup_time = current_time; wkup_count++; + + LOG_INF("Wakeup event count incremented: %u", wkup_count); } } } diff --git a/app/src/sensors/bme280/bme280.cpp b/app/src/sensors/bme280/bme280.cpp index 677af5d..2bb715f 100644 --- a/app/src/sensors/bme280/bme280.cpp +++ b/app/src/sensors/bme280/bme280.cpp @@ -14,7 +14,7 @@ using Status = Sensor::Status; Peripheral::Status BME280::init() { if (!device_is_ready(bme280_dev)) { LOG_WRN("BME280 device not ready"); - status = buzzverse_v1_Status_ComponentState_INITIALIZATION_FAILED; + status = buzzverse_v1_Status_ComponentState_INITIALIZATION_FAILED; return Peripheral::Status::NOT_READY; } @@ -36,16 +36,18 @@ Status BME280::read_data(buzzverse_v1_BME280Data& data) const { sensor_channel_get(bme280_dev, SENSOR_CHAN_PRESS, &press); sensor_channel_get(bme280_dev, SENSOR_CHAN_HUMIDITY, &humidity); + // Convert temperature to whole degrees (-128 to 127) data.temperature = static_cast(temp.val1); - double total_pressure_kPa = - static_cast(press.val1) + static_cast(press.val2) / 1000000.0; + double total_pressure_kPa = static_cast(press.val1) + static_cast(press.val2) / 1000000.0; double pressure_hPa = total_pressure_kPa * 10.0; int32_t rounded_pressure_hPa = static_cast(round(pressure_hPa)); + // Convert pressure as difference from 1000 hPa (-128 to 127) data.pressure = static_cast(rounded_pressure_hPa - 1000); + // Convert humidity to whole percentage (0-100%) data.humidity = static_cast(humidity.val1); LOG_INF("Temperature: %d C", data.temperature); @@ -56,34 +58,40 @@ Status BME280::read_data(buzzverse_v1_BME280Data& data) const { } Status BME280::get_packet(buzzverse_v1_Packet& packet) const { - buzzverse_v1_BME280Data bme_data = buzzverse_v1_BME280Data_init_zero; + buzzverse_v1_BME280Data bme_data = buzzverse_v1_BME280Data_init_zero; - if (read_data(bme_data) != Sensor::Status::OK) { - return Status::READ_ERR; - } + if(read_data(bme_data) != Sensor::Status::OK) { + return Status::READ_ERR; + } - if (!validate_data(bme_data)) { - LOG_WRN("No valid BME280 data to construct an application packet."); - return Status::READ_ERR; - } + if (!validate_data(bme_data)) { + LOG_WRN("No valid BME280 data to construct an application packet."); + return Status::READ_ERR; + } - packet = buzzverse_v1_Packet_init_default; - packet.which_data = buzzverse_v1_Packet_bme280_tag; - packet.data.bme280 = bme_data; - LOG_DBG("Packet constructed with BME280 data."); + packet = buzzverse_v1_Packet_init_default; + packet.which_data = buzzverse_v1_Packet_bme280_tag; + packet.data.bme280 = bme_data; + LOG_DBG("Packet constructed with BME280 data."); - return Status::OK; + return Status::OK; } void BME280::get_status(buzzverse_v1_Status& status_message) const { - status_message.bme280_status = status; + status_message.bme280_status = status; } bool BME280::validate_data(buzzverse_v1_BME280Data& data) const { - if ((data.temperature >= -40 && data.temperature <= 85) && - (data.pressure >= -700 && data.pressure <= 100) && - (data.humidity >= 0 && data.humidity <= 100)) - return true; - else - return false; -} + if( + (data.temperature >= -40 && data.temperature <= 85) && + // We return the difference from 1000 hPa, and the BME operating range for pressure is <300, 1100> hPa. + // Note that the pressure value is saved as a 8-bit signed integer, + // so in reality we don't expect values outside <-128, 127> = <872, 1127> hPa. + // This function however stays true to the operating ranges according to the BME280 datasheet. + (data.pressure >= -700 && data.pressure <= 100) && + (data.humidity >= 0 && data.humidity <= 100) + ) + return true; + else + return false; +} \ No newline at end of file From 1dd6b1027794d2c43b92cffe91a70f214b9127e8 Mon Sep 17 00:00:00 2001 From: wybran Date: Mon, 23 Mar 2026 18:23:59 +0100 Subject: [PATCH 17/19] feat: add ERROR_ALREADY_INITIALIZED status code to Peripheral class --- app/src/peripherals/peripheral.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/peripherals/peripheral.hpp b/app/src/peripherals/peripheral.hpp index 17c0c73..3026239 100644 --- a/app/src/peripherals/peripheral.hpp +++ b/app/src/peripherals/peripheral.hpp @@ -22,6 +22,7 @@ class Peripheral { ERROR_INVALID_PARAM = -3, /**< Invalid parameter provided */ ERROR_NOT_INITIALIZED = -4, /**< Initialization failed */ ERROR_HW_CONFIG_FAILED = -5, /**< Hardware configuration failed */ + ERROR_ALREADY_INITIALIZED = -6 /**< Peripheral is already initialized */ }; /** From b3f50f5a54e989010824a6d24bd3e3271f63901c Mon Sep 17 00:00:00 2001 From: Olaf Hubert Bykowski Date: Thu, 26 Mar 2026 16:45:57 +0100 Subject: [PATCH 18/19] feat: refactor wakeup ISR to use atomic operations and improve debounce handling --- .../peripherals/sleep/sleep_manager_stm.cpp | 73 ++++++++++++------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/app/src/peripherals/sleep/sleep_manager_stm.cpp b/app/src/peripherals/sleep/sleep_manager_stm.cpp index 41f426f..e894e9a 100644 --- a/app/src/peripherals/sleep/sleep_manager_stm.cpp +++ b/app/src/peripherals/sleep/sleep_manager_stm.cpp @@ -1,26 +1,38 @@ #include "sleep_manager_stm.hpp" -LOG_MODULE_REGISTER(sleep_mgr_stm, LOG_LEVEL_INF); - -namespace { -volatile uint32_t wkup_count = 0; -volatile uint32_t last_wkup_time = 0; +#include +#include -void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { - uint32_t current_time = k_uptime_get_32(); +LOG_MODULE_REGISTER(sleep_mgr_stm, LOG_LEVEL_INF); - LOG_INF("Wakeup ISR triggered on device %s, pins 0x%08x", dev->name, pins); +/* Use the real global semaphore defined elsewhere */ +extern struct k_sem wakeup_sem; - if (current_time - last_wkup_time > CONFIG_GPIO_WAKEUP_DEBOUNCE_MS) { - last_wkup_time = current_time; - wkup_count++; +namespace { - LOG_INF("Wakeup event count incremented: %u", wkup_count); +/* + * ISR/thread shared state: + * - Use atomics (volatile is NOT sufficient for concurrency). + * - Keep ISR extremely small: no LOG_* calls here. + */ +static atomic_t wkup_count; +static atomic_t last_wkup_time; + +static void wkup_isr(const struct device* dev, struct gpio_callback* cb, uint32_t pins) { + ARG_UNUSED(dev); + ARG_UNUSED(cb); + ARG_UNUSED(pins); + + const uint32_t now = k_uptime_get_32(); + const uint32_t last = (uint32_t)atomic_get(&last_wkup_time); + + if ((uint32_t)(now - last) > CONFIG_GPIO_WAKEUP_DEBOUNCE_MS) { + atomic_set(&last_wkup_time, now); + atomic_inc(&wkup_count); } } -} -extern struct k_sem wakeup_sem; +} // namespace #define WAKEUP_PIN_CFG(node_id, prop, idx) GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx), @@ -64,9 +76,10 @@ Peripheral::Status SleepManagerStm::init() { continue; } - gpio_pin_interrupt_configure_dt(&pin.spec, GPIO_INT_EDGE_TO_ACTIVE); + (void)gpio_pin_interrupt_configure_dt(&pin.spec, GPIO_INT_EDGE_TO_ACTIVE); + gpio_init_callback(&pin.cb_data, wkup_isr, BIT(pin.spec.pin)); - gpio_add_callback(pin.spec.port, &pin.cb_data); + (void)gpio_add_callback(pin.spec.port, &pin.cb_data); LOG_INF("Wakeup source initialized: Pin %d on %s", pin.spec.pin, pin.spec.port->name); } @@ -82,9 +95,8 @@ Peripheral::Status SleepManagerStm::init() { } uint32_t SleepManagerStm::get_and_clear_wakeup_count() { - uint32_t current_count = wkup_count; - wkup_count = 0; - return current_count; + /* atomically get-and-clear */ + return (uint32_t)atomic_set(&wkup_count, 0); } bool SleepManagerStm::is_ready() const { @@ -101,11 +113,17 @@ etl::string SleepManagerStm::get_name() const { void SleepManagerStm::enter_sleep(SleepMode mode) { ARG_UNUSED(mode); - if (!initialized) return; + if (!initialized) { + return; + } #if defined(CONFIG_SOC_SERIES_STM32WLX) if (!rtc.is_ready() || !rtc.ensure_time_valid()) { - k_sem_take(&wakeup_sem, K_FOREVER); + /* + * Wait until something wakes us (wkup_isr gives wakeup_sem). + * This is thread context; safe to block here. + */ + (void)k_sem_take(&wakeup_sem, K_FOREVER); return; } @@ -117,16 +135,21 @@ void SleepManagerStm::enter_sleep(SleepMode mode) { rtc.set_alarm(alarm_time); } } - k_sem_take(&wakeup_sem, K_FOREVER); + + (void)k_sem_take(&wakeup_sem, K_FOREVER); #else k_msleep(sleep_timeout_ms); #endif } void SleepManagerStm::set_sleep_duration(int duration_ms) { - if (duration_ms > 0) sleep_timeout_ms = duration_ms; + if (duration_ms > 0) { + sleep_timeout_ms = duration_ms; + } } void SleepManagerStm::timed_sleep() { - if (initialized) k_msleep(sleep_timeout_ms); -} + if (initialized) { + k_msleep(sleep_timeout_ms); + } +} \ No newline at end of file From 191999ff52002a7eb17d6eb974a5371c3a803b26 Mon Sep 17 00:00:00 2001 From: Olaf Hubert Bykowski Date: Thu, 26 Mar 2026 17:17:39 +0100 Subject: [PATCH 19/19] feat: update prj.conf for improved configuration and logging settings --- app/prj.conf | 85 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/app/prj.conf b/app/prj.conf index cc636c0..5229519 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -1,63 +1,84 @@ -# Copyright (c) 2021 Nordic Semiconductor ASA -# SPDX-License-Identifier: Apache-2.0 -# -# This file contains selected Kconfig options for the application. - +# --- Language / libc --- CONFIG_CPP=y CONFIG_GLIBCXX_LIBCPP=y CONFIG_NEWLIB_LIBC=y + +# --- App deps --- CONFIG_NANOPB=y +# --- Reset / power --- CONFIG_RESET=y CONFIG_REBOOT=y CONFIG_POWEROFF=y + +# STM32 wakeup pins support CONFIG_STM32_WKUP_PINS=y +# --- Buses / peripherals --- +CONFIG_GPIO=y CONFIG_I2C=y CONFIG_I2C_STM32_BUS_RECOVERY=y - -CONFIG_GPIO=y CONFIG_SPI=y +CONFIG_ADC=y -CONFIG_BQ274XX_PM=y - -CONFIG_PM=y -CONFIG_PM_DEVICE=y -CONFIG_PM_DEVICE_RUNTIME=y - +# --- Sensors --- CONFIG_SENSOR=y CONFIG_SENSOR_ASYNC_API=y -# General Zephyr settings -CONFIG_MAIN_STACK_SIZE=2048 -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +# --- Stacks --- +CONFIG_MAIN_STACK_SIZE=8192 +CONFIG_IDLE_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 -CONFIG_LOG=y +# --- Logging / console --- +# CONFIG_LOG=y +# CONFIG_LOG_PRINTK=n CONFIG_PRINTK=y +CONFIG_SERIAL=y -# Random number generator required for several LoRaWAN services -CONFIG_ENTROPY_GENERATOR=y +CONFIG_LOG=y +CONFIG_LOG_BACKEND_UART=y -# LoRaWAN application layer -CONFIG_LORA=y -CONFIG_LORAWAN=y -CONFIG_LORAWAN_NVM_SETTINGS=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y -# LoRaWAN services required for FUOTA -CONFIG_LORAWAN_SERVICES=y -CONFIG_LORAWAN_LOG_LEVEL_DBG=y +# Helpful crash info +CONFIG_FAULT_DUMP=2 +CONFIG_EXCEPTION_STACK_TRACE=y + +# --- RNG --- +CONFIG_ENTROPY_GENERATOR=y +# --- RTC / alarm --- CONFIG_RTC=y CONFIG_RTC_ALARM=y +# --- Flash / settings --- +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y CONFIG_NVS=y CONFIG_SETTINGS=y CONFIG_SETTINGS_RUNTIME=y -CONFIG_FLASH=y -CONFIG_FLASH_MAP=y -CONFIG_BOOTLOADER_MCUBOOT=y +# --- LoRa / LoRaWAN --- +CONFIG_LORA=y +CONFIG_LORAWAN=y +CONFIG_LORAWAN_NVM_SETTINGS=y + +# LoRaWAN services (FUOTA etc.) +CONFIG_LORAWAN_SERVICES=y +CONFIG_LORAWAN_LOG_LEVEL_DBG=y -# ADC config -CONFIG_ADC=y -CONFIG_CBPRINTF_FP_SUPPORT=y +CONFIG_PM=n +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y +CONFIG_PM_DEVICE_SYSTEM_MANAGED=y + +# --- Bootloader --- +CONFIG_BOOTLOADER_MCUBOOT=n + +CONFIG_STACK_SENTINEL=y +CONFIG_THREAD_NAME=y +CONFIG_THREAD_MONITOR=y +CONFIG_INIT_STACKS=y +CONFIG_STACK_CANARIES=y \ No newline at end of file