ESP-LiveControl  1.99.1
HTTP server, AJAX API backend and Vue.js web application implementing self-contained, zero-install WiFi remote control of hardware modules attached to the Espressif ESP32 SoC
multi_timer.hpp
Go to the documentation of this file.
1 
7 #ifndef MULTI_TIMER_HPP__
8 #define MULTI_TIMER_HPP__
9 
10 //#include <functional> // Has std::invoke but is only available from C++17..
11 //#include <type_traits> // C++20 version has the std::type_identity_t built in
12 #include "freertos/semphr.h"
13 #include <Ticker.h>
14 
15 // Allows calling a ordinary (non-static) class member function by inserting a
16 // lambda function taking the instance pointer as an argument..
17 // .. which because of creating a bound function should even perform better
18 // than indirecting a member function pointer call?
19 #define TICKER_MEMBER_CALL(NON_STATIC_MEMBER_FN) \
20  [](decltype(this) self){self->NON_STATIC_MEMBER_FN();}, this
21 #define TICKER_MEMBER_CALL_WITH_COUNT(NON_STATIC_MEMBER_FN) \
22  [](decltype(this) self, uint32_t i){self->NON_STATIC_MEMBER_FN(i);}, this
23 
34 class MultiTimer : private Ticker
35 {
36 public:
38  // std::type_identity_t is part of C++20 but current toolchain uses C++11.
39  // The following block is backporting the feature and will be obsolete soon.
40  // When this is the case, include <type_traits> and use std::type_indentity_t
41  template<typename T>
42  struct type_identity {using type = T;};
43  template<class T>
44  using type_identity_t = typename type_identity<T>::type;
46 
47  using callback_with_arg_and_count_t = void (*)(void*, uint32_t);
48 
49  MultiTimer() {
50  // When starting the timer with first_tick_nodelay=true, the first
51  // invocation of the timer callback is from the application task.
52  // Because any successive call is made from the high-priority esp_timer
53  // task, we need a mutex to prevent the timer task to proceed before
54  // the very first callback invocation has finished...
55  _reentry_mutex = xSemaphoreCreateMutex();
56  assert(_reentry_mutex);
57  }
58 
59 
122  template <typename TArg>
123  esp_err_t attach_static_ms(uint32_t milliseconds,
124  uint32_t total_repeat_count,
125  void (*callback)(type_identity_t<TArg>, uint32_t),
126  TArg arg,
127  bool first_tick_nodelay=false) {
128  static_assert(sizeof(TArg) <= sizeof(uint32_t),
129  "attach_ms() callback argument size must be <= 4 bytes");
130  _interval_ms = milliseconds;
131  _repeat_count_requested = total_repeat_count;
132  _callback = reinterpret_cast<callback_t>(callback);
133  _orig_arg = (uint32_t)arg;
134  _first_tick_nodelay = first_tick_nodelay;
135  _cb_lambda = [](void *_this){
136  auto self = static_cast<decltype(this)>(_this);
137  self->_mutex_take();
138  auto repeat_count = ++self->_repeat_count;
139  if (repeat_count < self->_repeat_count_requested) {
140  esp_timer_start_once(self->_timer, self->_interval_ms*1000ull);
141  } else {
142  self->_repeat_count = 0;
143  }
144  auto cb = reinterpret_cast<callback_with_arg_and_count_t>(self->_callback);
145  auto arg = (TArg)(self->_orig_arg);
146  cb(arg, repeat_count);
147  self->_mutex_give();
148  };
149  return _attach_ms((uint32_t)this);
150  }
151 
152  template <typename TArg>
153  esp_err_t attach_static_ms(uint32_t milliseconds,
154  uint32_t total_repeat_count,
155  void (*callback)(type_identity_t<TArg>),
156  TArg arg,
157  bool first_tick_nodelay=false) {
158  static_assert(sizeof(TArg) <= sizeof(uint32_t),
159  "attach_ms() callback argument size must be <= 4 bytes");
160  _interval_ms = milliseconds;
161  _repeat_count_requested = total_repeat_count;
162  _callback = reinterpret_cast<callback_t>(callback);
163  _orig_arg = (uint32_t)arg;
164  _first_tick_nodelay = first_tick_nodelay;
165  _cb_lambda = [](void *_this){
166  auto self = static_cast<decltype(this)>(_this);
167  self->_mutex_take();
168  auto repeat_count = ++self->_repeat_count;
169  if (repeat_count < self->_repeat_count_requested) {
170  esp_timer_start_once(self->_timer, self->_interval_ms*1000ull);
171  } else {
172  self->_repeat_count = 0;
173  }
174  auto cb = reinterpret_cast<callback_with_arg_t>(self->_callback);
175  auto arg = (TArg)(self->_orig_arg);
176  cb(arg);
177  self->_mutex_give();
178  };
179  return _attach_ms((uint32_t)this);
180  }
181 
182  esp_err_t attach_static_ms(uint32_t milliseconds,
183  uint32_t total_repeat_count,
184  callback_t callback,
185  bool first_tick_nodelay=false) {
186  _interval_ms = milliseconds;
187  _repeat_count_requested = total_repeat_count;
188  _callback = reinterpret_cast<callback_t>(callback);
189  _first_tick_nodelay = first_tick_nodelay;
190  _cb_lambda = [](void *_this){
191  auto self = static_cast<decltype(this)>(_this);
192  self->_mutex_take();
193  auto repeat_count = ++self->_repeat_count;
194  if (repeat_count < self->_repeat_count_requested) {
195  esp_timer_start_once(self->_timer, self->_interval_ms*1000ull);
196  } else {
197  self->_repeat_count = 0;
198  }
199  self->_callback();
200  self->_mutex_give();
201  };
202  return _attach_ms((uint32_t)this);
203  }
204 
208  void start() {
209  if (_first_tick_nodelay && _cb_lambda) {
210  _cb_lambda(this);
211  } else {
212  esp_timer_start_once(_timer, _interval_ms*1000ull);
213  }
214  }
215  void start(uint32_t interval_ms) {
216  _interval_ms = interval_ms;
217  if (_first_tick_nodelay && _cb_lambda) {
218  _cb_lambda(this);
219  } else {
220  esp_timer_start_once(_timer, _interval_ms*1000ull);
221  }
222  }
223 
224  void stop() {
225  esp_timer_stop(_timer);
226  _repeat_count = 0;
227  }
228  esp_err_t stop_return_errors() {
229  auto errors = esp_timer_stop(_timer);
230  _repeat_count = 0;
231  return errors;
232  }
233 
234  void pause() {
235  esp_timer_stop(_timer);
236  }
237  esp_err_t pause_return_errors() {
238  return esp_timer_stop(_timer);
239  }
240 
241  void resume() {
242  esp_timer_start_once(_timer, _interval_ms*1000ull);
243  }
244  esp_err_t resume_return_errors() {
245  return esp_timer_start_once(_timer, _interval_ms*1000ull);
246  }
247 
248  // The only other functions we make available again in this class
249  using Ticker::detach;
250  using Ticker::active;
251 
252 protected:
253  using Ticker::_timer;
254  uint32_t _interval_ms;
255  uint32_t _repeat_count_requested{1};
256  uint32_t _repeat_count{0};
257  // Original callback cast into a callback_t
258  callback_t _callback;
259  uint32_t _orig_arg{0};
260  // Callback encapsulated into lambda function with repeat counter etc.
261  callback_with_arg_t _cb_lambda{nullptr};
262  bool _first_tick_nodelay{false};
263  SemaphoreHandle_t _reentry_mutex = NULL;
264 
265  esp_err_t _attach_ms(uint32_t arg) {
266  esp_timer_create_args_t _timerConfig;
267  _timerConfig.callback = _cb_lambda;
268  _timerConfig.arg = reinterpret_cast<void*>(arg);
269  _timerConfig.dispatch_method = ESP_TIMER_TASK;
270  _timerConfig.name = "MultiTimer";
271  if (_timer) {
272  esp_timer_stop(_timer);
273  esp_timer_delete(_timer);
274  }
275  return esp_timer_create(&_timerConfig, &_timer);
276  }
277 
278  inline void _mutex_take() {
279  xSemaphoreTake(_reentry_mutex, portMAX_DELAY);
280  }
281 
282  inline void _mutex_give() {
283  xSemaphoreGive(_reentry_mutex);
284  }
285 };
286 
287 
302 template<typename TClass>
304 {
305 public:
306  using mem_func_ptr_t = void (TClass::*)();
307  using mem_func_ptr_with_count_t = void (TClass::*)(uint32_t);
308 
324  esp_err_t attach_mem_func_ptr_ms(uint32_t milliseconds,
325  uint32_t total_repeat_count,
326  mem_func_ptr_t mem_func_ptr,
327  TClass *inst,
328  bool first_tick_nodelay=false) {
329  _interval_ms = milliseconds;
330  _repeat_count_requested = total_repeat_count;
331  _mem_func_ptr = mem_func_ptr;
332  _orig_arg = reinterpret_cast<uint32_t>(inst);
333  _first_tick_nodelay = first_tick_nodelay;
334  _cb_lambda = [](void *_this){
335  auto self = static_cast<decltype(this)>(_this);
336  self->_mutex_take();
337  auto repeat_count = ++self->_repeat_count;
338  if (repeat_count < self->_repeat_count_requested) {
339  esp_timer_start_once(self->_timer, self->_interval_ms*1000ull);
340  } else {
341  self->_repeat_count = 0;
342  }
343  auto inst = reinterpret_cast<TClass*>(self->_orig_arg);
344  //std::invoke(self->_mem_func_ptr, *inst);
345  auto mfp = self->_mem_func_ptr;
346  (inst->*mfp)();
347  self->_mutex_give();
348  };
349  return _attach_ms((uint32_t)this);
350  }
351 
352  esp_err_t attach_mem_func_ptr_ms(uint32_t milliseconds,
353  uint32_t total_repeat_count,
354  mem_func_ptr_with_count_t mem_func_ptr,
355  TClass *inst,
356  bool first_tick_nodelay=false) {
357  _interval_ms = milliseconds;
358  _repeat_count_requested = total_repeat_count;
359  _mem_func_ptr = reinterpret_cast<mem_func_ptr_t>(mem_func_ptr);
360  //_mem_func_ptr_with_count = mem_func_ptr;
361  _orig_arg = reinterpret_cast<uint32_t>(inst);
362  _first_tick_nodelay = first_tick_nodelay;
363  _cb_lambda = [](void *_this){
364  auto self = static_cast<decltype(this)>(_this);
365  self->_mutex_take();
366  auto repeat_count = ++self->_repeat_count;
367  if (repeat_count < self->_repeat_count_requested) {
368  esp_timer_start_once(self->_timer, self->_interval_ms*1000ull);
369  } else {
370  self->_repeat_count = 0;
371  }
372  auto inst = reinterpret_cast<TClass*>(self->_orig_arg);
373  //std::invoke(self->_mem_func_ptr, *inst, self->_repeat_count);
374  auto mfp = reinterpret_cast<mem_func_ptr_with_count_t>(self->_mem_func_ptr);
375  //auto mfp = self->_mem_func_ptr_with_count;
376  (inst->*mfp)(repeat_count);
377  self->_mutex_give();
378  };
379  return _attach_ms((uint32_t)this);
380  }
381 
382 protected:
383  mem_func_ptr_t _mem_func_ptr;
384  //mem_func_ptr_with_count_t _mem_func_ptr_with_count;
385 };
386 
387 
388 #endif
Ticker timer derivative allowing for a fixed number of repeated calls.
Definition: multi_timer.hpp:35
void start()
Definition: multi_timer.hpp:208
esp_err_t attach_static_ms(uint32_t milliseconds, uint32_t total_repeat_count, void(*callback)(type_identity_t< TArg >, uint32_t), TArg arg, bool first_tick_nodelay=false)
Attach a free function, static member function or a non-capturing lambda function to the timer.
Definition: multi_timer.hpp:123
Same as MultiTimer but allows a pointer to a non-static member function as a callback.
Definition: multi_timer.hpp:304
esp_err_t attach_mem_func_ptr_ms(uint32_t milliseconds, uint32_t total_repeat_count, mem_func_ptr_t mem_func_ptr, TClass *inst, bool first_tick_nodelay=false)
Same as attach_static_ms() but taking a pointer to a non-static member function as a callback,...
Definition: multi_timer.hpp:324
Definition: multi_timer.hpp:42