diff --git a/CHANGELOG.md b/CHANGELOG.md index a2f1187..fcd042c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,13 @@ The format follows Keep a Changelog and the project adheres to Semantic Versioni ## [Unreleased] ### Changed - Removed the library-provided inline `timer` instance. Sketches should now declare their own `ESPTimer` objects (global, static, or as class members) before calling `init()`, enabling multiple independent timer managers. -- Added `clearTimeout(id)` for explicit timeout cancellation and kept `clearTimer(id)` as a backward-compatible alias. +- Standardized teardown around `deinit()` + `isInitialized()` and removed the `clearTimer(id)` alias in favor of `clearTimeout(id)`. - Added `ESPTimerConfig::usePSRAMBuffers` and routed timer-owned persistent/transient vectors through `ESPBufferManager` with safe fallback to default heap. - Migrated timer lane task creation/lifecycle back to native FreeRTOS task handling (`xTaskCreatePinnedToCore`/`vTaskDelete`). ### Fixed - Ensured per-second and per-minute countdown timers emit their final tick by rounding up remaining time. +- Added lifecycle test coverage for pre-init `deinit()`, repeated `deinit()`, and `init -> deinit -> init` reinitialization. ### Documentation - Added an MIT license badge and cross-links to other ESPToolKit libraries in the README. diff --git a/README.md b/README.md index f3e5326..83d98b6 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Include the umbrella header, create an `ESPTimer` instance, and call `init` once #include ESPTimer timer; +volatile bool shouldShutdownTimers = false; void setup() { Serial.begin(115200); @@ -48,9 +49,18 @@ void setup() { timer.pauseInterval(intervalId); timer.resumeInterval(intervalId); + + timer.setTimeout([](){ + shouldShutdownTimers = true; + }, 30000); } -void loop() {} +void loop() { + if (shouldShutdownTimers && timer.isInitialized()) { + timer.deinit(); + shouldShutdownTimers = false; + } +} ``` Explore `examples/Basic/Basic.ino` for a complete sketch that demonstrates all timer types. @@ -64,6 +74,8 @@ Explore `examples/Basic/Basic.ino` for a complete sketch that demonstrates all t ## API Reference - `void init(const ESPTimerConfig& cfg = {})` – allocate mutexes and spawn each timer worker with the provided stack/priority/core settings. +- `void deinit()` – idempotently stop all timer workers, clear active timers/counters, and free runtime resources. +- `bool isInitialized() const` – `true` when timer workers and synchronization primitives are active. - Scheduling helpers - `uint32_t setTimeout(std::function cb, uint32_t delayMs)` - `uint32_t setInterval(std::function cb, uint32_t periodMs)` @@ -72,7 +84,6 @@ Explore `examples/Basic/Basic.ino` for a complete sketch that demonstrates all t - `uint32_t setMinCounter(std::function cb, uint32_t totalMs)` - Control helpers: `pause*`, `resume*`, `toggleRunStatus*`, `clear*`, `ESPTimerStatus getStatus(id)`. - Timeout-specific clear: `clearTimeout(id)`. - - Backward-compatible alias: `clearTimer(id)` (same behavior as `clearTimeout(id)`). `ESPTimerConfig` knobs (per task type): - Stack sizes (`stackSizeTimeout`, `stackSizeInterval`, `stackSizeSec`, `stackSizeMs`, `stackSizeMin`). diff --git a/examples/Basic/Basic.ino b/examples/Basic/Basic.ino index 2456e6b..c73c6f2 100644 --- a/examples/Basic/Basic.ino +++ b/examples/Basic/Basic.ino @@ -3,6 +3,7 @@ #include ESPTimer timer; // Create your own instance +volatile bool shouldDeinit = false; void setup() { Serial.begin(115200); @@ -59,8 +60,19 @@ void setup() { auto s = timer.getStatus(t1); Serial.printf("Timeout status: %d\n", static_cast(s)); }, 2000); + + // Request teardown from loop context (not from timer worker tasks). + timer.setTimeout([]() { + shouldDeinit = true; + }, 15000); } void loop() { + if (shouldDeinit && timer.isInitialized()) { + timer.deinit(); + shouldDeinit = false; + Serial.println("Timer runtime deinitialized"); + } + // App code does other things; timers run in background FreeRTOS tasks } diff --git a/examples/PauseResume/PauseResume.ino b/examples/PauseResume/PauseResume.ino index bf2eb5b..f2ad3da 100644 --- a/examples/PauseResume/PauseResume.ino +++ b/examples/PauseResume/PauseResume.ino @@ -3,6 +3,7 @@ #include ESPTimer timer; +volatile bool shouldDeinit = false; uint32_t intervalId; @@ -39,6 +40,16 @@ void setup() { auto s = timer.getStatus(intervalId); Serial.printf("Status now: %d (Invalid=0 if removed)\n", (int)s); }, 10000); + + timer.setTimeout([]() { + shouldDeinit = true; + }, 12000); } -void loop() {} +void loop() { + if (shouldDeinit && timer.isInitialized()) { + timer.deinit(); + shouldDeinit = false; + Serial.println("ESPTimer deinitialized"); + } +} diff --git a/src/esp_timer/timer.cpp b/src/esp_timer/timer.cpp index a35d618..48137ef 100644 --- a/src/esp_timer/timer.cpp +++ b/src/esp_timer/timer.cpp @@ -370,7 +370,6 @@ bool ESPTimer::toggleRunStatusMinCounter(uint32_t id) { } bool ESPTimer::clearTimeout(uint32_t id) { return clearItem(Type::Timeout, id); } -bool ESPTimer::clearTimer(uint32_t id) { return clearTimeout(id); } bool ESPTimer::clearInterval(uint32_t id) { return clearItem(Type::Interval, id); } bool ESPTimer::clearSecCounter(uint32_t id) { return clearItem(Type::Sec, id); } bool ESPTimer::clearMsCounter(uint32_t id) { return clearItem(Type::Ms, id); } diff --git a/src/esp_timer/timer.h b/src/esp_timer/timer.h index 29d2779..0f2122f 100644 --- a/src/esp_timer/timer.h +++ b/src/esp_timer/timer.h @@ -52,7 +52,7 @@ class ESPTimer { void init(const ESPTimerConfig& cfg = ESPTimerConfig()); void deinit(); - bool initialized() const { return initialized_; } + bool isInitialized() const { return initialized_; } // Scheduling uint32_t setTimeout(std::function cb, uint32_t delayMs); @@ -84,8 +84,6 @@ class ESPTimer { // Clear (stop and remove) timers; returns true on success bool clearTimeout(uint32_t id); - // Backward-compatible alias for clearTimeout - bool clearTimer(uint32_t id); bool clearInterval(uint32_t id); bool clearSecCounter(uint32_t id); bool clearMsCounter(uint32_t id); diff --git a/test/test_basic/test_main.cpp b/test/test_basic/test_main.cpp index 893e120..3c3a9bd 100644 --- a/test/test_basic/test_main.cpp +++ b/test/test_basic/test_main.cpp @@ -2,12 +2,12 @@ #include #include -ESPTimer timer; - void test_api_compiles() { + ESPTimer timer; ESPTimerConfig cfg; cfg.usePSRAMBuffers = true; timer.init(cfg); + TEST_ASSERT_TRUE(timer.isInitialized()); auto id1 = timer.setTimeout([]() {}, 1000); auto id2 = timer.setInterval([]() {}, 20); @@ -34,16 +34,54 @@ void test_api_compiles() { TEST_ASSERT_TRUE(running); TEST_ASSERT_TRUE(timer.clearTimeout(id1)); - TEST_ASSERT_TRUE(timer.clearTimer(id6)); + TEST_ASSERT_TRUE(timer.clearTimeout(id6)); // Clear should return true once TEST_ASSERT_TRUE(timer.clearInterval(id2)); + + timer.deinit(); + TEST_ASSERT_FALSE(timer.isInitialized()); +} + +void test_deinit_pre_init_is_safe_and_idempotent() { + ESPTimer timer; + + TEST_ASSERT_FALSE(timer.isInitialized()); + timer.deinit(); + TEST_ASSERT_FALSE(timer.isInitialized()); + timer.deinit(); + TEST_ASSERT_FALSE(timer.isInitialized()); +} + +void test_reinit_lifecycle() { + ESPTimer timer; + + timer.init(); + TEST_ASSERT_TRUE(timer.isInitialized()); + auto firstId = timer.setTimeout([]() {}, 5); + TEST_ASSERT_TRUE(firstId > 0); + + timer.deinit(); + TEST_ASSERT_FALSE(timer.isInitialized()); + timer.deinit(); + TEST_ASSERT_FALSE(timer.isInitialized()); + + timer.init(); + TEST_ASSERT_TRUE(timer.isInitialized()); + auto secondId = timer.setInterval([]() {}, 5); + TEST_ASSERT_TRUE(secondId > 0); + TEST_ASSERT_TRUE(timer.clearInterval(secondId)); + + timer.deinit(); + TEST_ASSERT_FALSE(timer.isInitialized()); } void setup() { delay(2000); UNITY_BEGIN(); RUN_TEST(test_api_compiles); + RUN_TEST(test_deinit_pre_init_is_safe_and_idempotent); + RUN_TEST(test_reinit_lifecycle); UNITY_END(); }