idfxx 1.0.0
Modern C++23 components for ESP-IDF
Loading...
Searching...
No Matches
timer.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Chris Leishman
3
4#pragma once
5
20#include <idfxx/error>
21
22#include <algorithm>
23#include <chrono>
24#include <esp_timer.h>
25#include <functional>
26#include <string>
27
28namespace idfxx {
29
41class timer {
42public:
50 struct clock {
51 using rep = int64_t;
52 using period = std::micro;
53 using duration = std::chrono::microseconds;
54 using time_point = std::chrono::time_point<clock>;
55 static constexpr bool is_steady = true;
56
62 };
63
73 enum class dispatch_method : int {
75#if CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD || __DOXYGEN__
77#endif
78 };
79
83 struct config {
84 std::string_view name = "";
86 bool skip_unhandled_events = false;
87 };
88
89#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
100 [[nodiscard]] explicit timer(const config& cfg, std::move_only_function<void()> callback);
101#endif
102
115 [[nodiscard]] static result<timer> make(config cfg, std::move_only_function<void()> callback);
116
129 [[nodiscard]] static result<timer> make(config cfg, void (*callback)(void*), void* arg);
130
131#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
156 template<typename Rep, typename Period>
158 config cfg,
159 const std::chrono::duration<Rep, Period>& timeout,
160 std::move_only_function<void()> callback
161 ) {
162 return unwrap(try_start_once(std::move(cfg), timeout, std::move(callback)));
163 }
164
183 template<typename Rep, typename Period>
184 [[nodiscard]] static timer
185 start_once(config cfg, const std::chrono::duration<Rep, Period>& timeout, void (*callback)(void*), void* arg) {
186 return unwrap(try_start_once(std::move(cfg), timeout, callback, arg));
187 }
188
211 [[nodiscard]] static timer
212 start_once(config cfg, clock::time_point time, std::move_only_function<void()> callback) {
213 return unwrap(try_start_once(std::move(cfg), time, std::move(callback)));
214 }
215
233 [[nodiscard]] static timer start_once(config cfg, clock::time_point time, void (*callback)(void*), void* arg) {
234 return unwrap(try_start_once(std::move(cfg), time, callback, arg));
235 }
236
261 template<typename Rep, typename Period>
263 config cfg,
264 const std::chrono::duration<Rep, Period>& interval,
265 std::move_only_function<void()> callback
266 ) {
267 return unwrap(try_start_periodic(std::move(cfg), interval, std::move(callback)));
268 }
269
288 template<typename Rep, typename Period>
289 [[nodiscard]] static timer
290 start_periodic(config cfg, const std::chrono::duration<Rep, Period>& interval, void (*callback)(void*), void* arg) {
291 return unwrap(try_start_periodic(std::move(cfg), interval, callback, arg));
292 }
293
294#endif
295
315 template<typename Rep, typename Period>
317 config cfg,
318 const std::chrono::duration<Rep, Period>& timeout,
319 std::move_only_function<void()> callback
320 ) {
321 auto t = make(std::move(cfg), std::move(callback));
322 if (!t) {
323 return t;
324 }
325 auto r = t->try_start_once(timeout);
326 if (!r) {
327 return error(r.error());
328 }
329 return t;
330 }
331
351 template<typename Rep, typename Period>
352 [[nodiscard]] static result<timer>
353 try_start_once(config cfg, const std::chrono::duration<Rep, Period>& timeout, void (*callback)(void*), void* arg) {
354 auto t = make(std::move(cfg), callback, arg);
355 if (!t) {
356 return t;
357 }
358 auto r = t->try_start_once(timeout);
359 if (!r) {
360 return error(r.error());
361 }
362 return t;
363 }
364
383 [[nodiscard]] static result<timer>
384 try_start_once(config cfg, clock::time_point time, std::move_only_function<void()> callback) {
385 auto t = make(std::move(cfg), std::move(callback));
386 if (!t) {
387 return t;
388 }
389 auto r = t->try_start_once(time);
390 if (!r) {
391 return error(r.error());
392 }
393 return t;
394 }
395
414 [[nodiscard]] static result<timer>
415 try_start_once(config cfg, clock::time_point time, void (*callback)(void*), void* arg) {
416 auto t = make(std::move(cfg), callback, arg);
417 if (!t) {
418 return t;
419 }
420 auto r = t->try_start_once(time);
421 if (!r) {
422 return error(r.error());
423 }
424 return t;
425 }
426
446 template<typename Rep, typename Period>
448 config cfg,
449 const std::chrono::duration<Rep, Period>& interval,
450 std::move_only_function<void()> callback
451 ) {
452 auto t = make(std::move(cfg), std::move(callback));
453 if (!t) {
454 return t;
455 }
456 auto r = t->try_start_periodic(interval);
457 if (!r) {
458 return error(r.error());
459 }
460 return t;
461 }
462
482 template<typename Rep, typename Period>
484 config cfg,
485 const std::chrono::duration<Rep, Period>& interval,
486 void (*callback)(void*),
487 void* arg
488 ) {
489 auto t = make(std::move(cfg), callback, arg);
490 if (!t) {
491 return t;
492 }
493 auto r = t->try_start_periodic(interval);
494 if (!r) {
495 return error(r.error());
496 }
497 return t;
498 }
499
507
508 timer(const timer&) = delete;
509 timer& operator=(const timer&) = delete;
510 timer(timer&& other) noexcept;
511 timer& operator=(timer&& other) noexcept;
512
518
523 [[nodiscard]] const std::string& name() const noexcept { return _name; }
524
525#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
535 template<typename Rep, typename Period>
536 void start_once(const std::chrono::duration<Rep, Period>& timeout) {
538 }
539
559
569 template<typename Rep, typename Period>
570 void start_periodic(const std::chrono::duration<Rep, Period>& interval) {
572 }
573#endif
574
587 template<typename Rep, typename Period>
588 [[nodiscard]] result<void> try_start_once(const std::chrono::duration<Rep, Period>& timeout) {
589 if (_handle == nullptr) {
591 }
592 return wrap(esp_timer_start_once(_handle, to_us(timeout)));
593 }
594
606 if (_handle == nullptr) {
608 }
609 auto timeout_us = std::max(int64_t{0}, (time - clock::now()).count());
610 return wrap(esp_timer_start_once(_handle, static_cast<uint64_t>(timeout_us)));
611 }
612
632
645 template<typename Rep, typename Period>
646 [[nodiscard]] result<void> try_start_periodic(const std::chrono::duration<Rep, Period>& interval) {
647 if (_handle == nullptr) {
649 }
650 return wrap(esp_timer_start_periodic(_handle, to_us(interval)));
651 }
652
670
671#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
681 template<typename Rep, typename Period>
682 void restart(const std::chrono::duration<Rep, Period>& timeout) {
684 }
685#endif
686
699 template<typename Rep, typename Period>
700 [[nodiscard]] result<void> try_restart(const std::chrono::duration<Rep, Period>& timeout) {
701 if (_handle == nullptr) {
703 }
704 auto err = esp_timer_restart(_handle, to_us(timeout));
705 if (err == ESP_ERR_INVALID_STATE) {
706 return wrap(esp_timer_start_once(_handle, to_us(timeout)));
707 }
708 return wrap(err);
709 }
710
728
729#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
736 void stop() { unwrap(try_stop()); }
737#endif
738
746 if (_handle == nullptr) {
748 }
749 return wrap(esp_timer_stop(_handle));
750 }
751
763
769 if (_handle == nullptr) {
770 return false;
771 }
772 return esp_timer_is_active(_handle);
773 }
774
784 [[nodiscard]] std::chrono::microseconds period() const noexcept {
785 if (_handle == nullptr) {
786 return std::chrono::microseconds{0};
787 }
790 return std::chrono::microseconds{static_cast<int64_t>(period_us)};
791 }
792
803 if (_handle == nullptr) {
804 return clock::time_point::max();
805 }
807 if (esp_timer_get_expiry_time(_handle, &expiry) != ESP_OK) {
808 return clock::time_point::max();
809 }
810 if (expiry > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
811 return clock::time_point::max();
812 }
813 return clock::time_point{clock::duration{static_cast<int64_t>(expiry)}};
814 }
815
827
828private:
830 struct context;
832
833 explicit timer(esp_timer_handle_t handle, std::string name, context* ctx = nullptr);
834
835 void _stop_and_delete() noexcept;
836
838 static constexpr int64_t to_us(const std::chrono::duration<Rep, Period>& d) {
839 return std::chrono::duration_cast<std::chrono::microseconds>(d).count();
840 }
841
842 static void trampoline(void* arg);
843
844 esp_timer_handle_t _handle = nullptr;
845 std::string _name;
846 context* _context = nullptr;
847};
848
// end of idfxx_timer
850
851} // namespace idfxx
Task lifecycle management.
Definition task.hpp:47
High-resolution timer with microsecond precision.
Definition timer.hpp:41
timer(const config &cfg, std::move_only_function< void()> callback)
Creates a timer with a std::move_only_function callback.
const std::string & name() const noexcept
Returns the timer name.
Definition timer.hpp:523
esp_err_t try_stop_isr()
Stops the timer (ISR-compatible).
static clock::time_point next_alarm()
Returns the time of the next scheduled timer event.
Definition timer.hpp:824
result< void > try_start_once(const std::chrono::duration< Rep, Period > &timeout)
Starts the timer as a one-shot timer.
Definition timer.hpp:588
std::chrono::microseconds period() const noexcept
Returns the period of a periodic timer.
Definition timer.hpp:784
void start_once(clock::time_point time)
Starts the timer as a one-shot timer at an absolute time.
Definition timer.hpp:558
static timer start_once(config cfg, clock::time_point time, void(*callback)(void *), void *arg)
Creates and starts a one-shot timer at an absolute time with a raw function pointer callback.
Definition timer.hpp:233
static result< timer > try_start_once(config cfg, clock::time_point time, std::move_only_function< void()> callback)
Creates and starts a one-shot timer at an absolute time with a std::move_only_function callback.
Definition timer.hpp:384
timer(const timer &)=delete
static result< timer > make(config cfg, void(*callback)(void *), void *arg)
Creates a timer with a raw function pointer callback.
clock::time_point expiry_time() const noexcept
Returns the absolute expiry time for a one-shot timer.
Definition timer.hpp:802
static timer start_once(config cfg, clock::time_point time, std::move_only_function< void()> callback)
Creates and starts a one-shot timer at an absolute time with a std::move_only_function callback.
Definition timer.hpp:212
result< void > try_start_once(clock::time_point time)
Starts the timer as a one-shot timer at an absolute time.
Definition timer.hpp:605
timer & operator=(timer &&other) noexcept
esp_err_t try_restart_isr(uint64_t timeout_us)
Restarts the timer with a new timeout (ISR-compatible).
static result< timer > try_start_once(config cfg, clock::time_point time, void(*callback)(void *), void *arg)
Creates and starts a one-shot timer at an absolute time with a raw function pointer callback.
Definition timer.hpp:415
esp_err_t try_start_periodic_isr(uint64_t interval_us)
Starts the timer as a periodic timer (ISR-compatible).
timer(timer &&other) noexcept
esp_err_t try_start_once_isr(uint64_t timeout_us)
Starts the timer as a one-shot timer (ISR-compatible).
void start_periodic(const std::chrono::duration< Rep, Period > &interval)
Starts the timer as a periodic timer.
Definition timer.hpp:570
static result< timer > try_start_once(config cfg, const std::chrono::duration< Rep, Period > &timeout, std::move_only_function< void()> callback)
Creates and starts a one-shot timer with a std::move_only_function callback.
Definition timer.hpp:316
static result< timer > make(config cfg, std::move_only_function< void()> callback)
Creates a timer with a std::move_only_function callback.
static result< timer > try_start_periodic(config cfg, const std::chrono::duration< Rep, Period > &interval, std::move_only_function< void()> callback)
Creates and starts a periodic timer with a std::move_only_function callback.
Definition timer.hpp:447
void start_once(const std::chrono::duration< Rep, Period > &timeout)
Starts the timer as a one-shot timer.
Definition timer.hpp:536
esp_timer_handle_t idf_handle() const noexcept
Returns the underlying ESP-IDF timer handle.
Definition timer.hpp:517
static timer start_periodic(config cfg, const std::chrono::duration< Rep, Period > &interval, void(*callback)(void *), void *arg)
Creates and starts a periodic timer with a raw function pointer callback.
Definition timer.hpp:290
result< void > try_stop()
Stops the timer.
Definition timer.hpp:745
bool is_active() const noexcept
Checks if the timer is currently running.
Definition timer.hpp:768
void stop()
Stops the timer.
Definition timer.hpp:736
void restart(const std::chrono::duration< Rep, Period > &timeout)
Restarts the timer with a new timeout.
Definition timer.hpp:682
static result< timer > try_start_once(config cfg, const std::chrono::duration< Rep, Period > &timeout, void(*callback)(void *), void *arg)
Creates and starts a one-shot timer with a raw function pointer callback.
Definition timer.hpp:353
timer & operator=(const timer &)=delete
static timer start_once(config cfg, const std::chrono::duration< Rep, Period > &timeout, void(*callback)(void *), void *arg)
Creates and starts a one-shot timer with a raw function pointer callback.
Definition timer.hpp:185
result< void > try_start_periodic(const std::chrono::duration< Rep, Period > &interval)
Starts the timer as a periodic timer.
Definition timer.hpp:646
result< void > try_restart(const std::chrono::duration< Rep, Period > &timeout)
Restarts the timer with a new timeout.
Definition timer.hpp:700
static result< timer > try_start_periodic(config cfg, const std::chrono::duration< Rep, Period > &interval, void(*callback)(void *), void *arg)
Creates and starts a periodic timer with a raw function pointer callback.
Definition timer.hpp:483
~timer()
Destroys the timer.
dispatch_method
Callback dispatch type.
Definition timer.hpp:73
@ task
Callback runs in high-priority timer task (default)
static timer start_periodic(config cfg, const std::chrono::duration< Rep, Period > &interval, std::move_only_function< void()> callback)
Creates and starts a periodic timer with a std::move_only_function callback.
Definition timer.hpp:262
static timer start_once(config cfg, const std::chrono::duration< Rep, Period > &timeout, std::move_only_function< void()> callback)
Creates and starts a one-shot timer with a std::move_only_function callback.
Definition timer.hpp:157
int esp_err_t
Definition error.hpp:35
constexpr std::unexpected< std::error_code > error(E e) noexcept
Creates an unexpected error from an error code enum.
Definition error.hpp:187
T unwrap(result< T > result)
Throws a std::system_error if the result is an error.
Definition error.hpp:307
@ invalid_state
Invalid state.
@ timeout
Operation timed out.
std::expected< T, std::error_code > result
result type wrapping a value or error code.
Definition error.hpp:120
result< void > wrap(esp_err_t e)
Wraps an esp_err_t into a result<void>.
Definition error.hpp:286
Monotonic clock based on boot time.
Definition timer.hpp:50
std::micro period
Definition timer.hpp:52
static constexpr bool is_steady
Definition timer.hpp:55
std::chrono::time_point< clock > time_point
Definition timer.hpp:54
static time_point now() noexcept
Returns the current time.
Definition timer.hpp:61
std::chrono::microseconds duration
Definition timer.hpp:53
Timer configuration parameters.
Definition timer.hpp:83
bool skip_unhandled_events
Skip events if callback busy.
Definition timer.hpp:86
enum dispatch_method dispatch
Callback dispatch type.
Definition timer.hpp:85
std::string_view name
Timer name for debugging.
Definition timer.hpp:84