Berry 'tasmota.defer()' (#22976)

This commit is contained in:
s-hadinger 2025-02-09 17:56:38 +01:00 committed by GitHub
parent 5a6b219971
commit ff5b4956c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 2049 additions and 1903 deletions

View File

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Formatter `%_U` for `ext_snprintf_P()` to print uint64_t variable as decimal equivalent to `%llu` - Formatter `%_U` for `ext_snprintf_P()` to print uint64_t variable as decimal equivalent to `%llu`
- Support for RC-switch decoding of 64-bit received data - Support for RC-switch decoding of 64-bit received data
- Berry `tasmota.defer()`
### Breaking Changed ### Breaking Changed

View File

@ -86,6 +86,7 @@ class be_class_tasmota (scope: global, name: Tasmota) {
_fl, var // list of active fast-loop object (faster than drivers) _fl, var // list of active fast-loop object (faster than drivers)
_rules, var // list of active rules _rules, var // list of active rules
_timers, var // list of active timers _timers, var // list of active timers
_defer, var // list of deferred functions to be called at next millisecond
_crons, var // list of active crons _crons, var // list of active crons
_ccmd, var // list of active Tasmota commands implemented in Berry _ccmd, var // list of active Tasmota commands implemented in Berry
_drivers, var // list of active drivers _drivers, var // list of active drivers
@ -173,6 +174,8 @@ class be_class_tasmota (scope: global, name: Tasmota) {
exec_rules, closure(class_Tasmota_exec_rules_closure) exec_rules, closure(class_Tasmota_exec_rules_closure)
exec_tele, closure(class_Tasmota_exec_tele_closure) exec_tele, closure(class_Tasmota_exec_tele_closure)
set_timer, closure(class_Tasmota_set_timer_closure) set_timer, closure(class_Tasmota_set_timer_closure)
run_timers, closure(class_Tasmota_run_timers_closure)
defer, closure(class_Tasmota_defer_closure)
run_deferred, closure(class_Tasmota_run_deferred_closure) run_deferred, closure(class_Tasmota_run_deferred_closure)
remove_timer, closure(class_Tasmota_remove_timer_closure) remove_timer, closure(class_Tasmota_remove_timer_closure)
add_cmd, closure(class_Tasmota_add_cmd_closure) add_cmd, closure(class_Tasmota_add_cmd_closure)

View File

@ -9,6 +9,7 @@ class Tasmota
var _fl # list of fast_loop registered closures var _fl # list of fast_loop registered closures
var _rules var _rules
var _timers # holds both timers and cron var _timers # holds both timers and cron
var _defer # holds functions to be called at next millisecond
var _crons var _crons
var _ccmd var _ccmd
var _drivers var _drivers
@ -273,13 +274,39 @@ class Tasmota
def set_timer(delay,f,id) def set_timer(delay,f,id)
self.check_not_method(f) self.check_not_method(f)
if self._timers == nil if self._timers == nil
self._timers=[] self._timers = []
end end
self._timers.push(Trigger(self.millis(delay),f,id)) self._timers.push(Trigger(self.millis(delay),f,id))
end end
# run every 50ms tick # special version to push a function that will be called immediately after
def defer(f)
if self._defer == nil
self._defer = []
end
self._defer.push(f)
tasmota.global.deferred_ready = 1
end
# run any immediate function
def run_deferred() def run_deferred()
if self._defer
var sz = size(self._defer) # make sure to run only those present at first, and not those inserted in between
while sz > 0
var f = self._defer[0]
self._defer.remove(0)
sz -= 1
f()
end
if size(self._defer) == 0
tasmota.global.deferred_ready = 0
end
end
end
# run every 50ms tick
def run_timers()
self.run_deferred() # run immediate functions first
if self._timers if self._timers
var i=0 var i=0
while i < self._timers.size() while i < self._timers.size()
@ -699,7 +726,7 @@ class Tasmota
def event(event_type, cmd, idx, payload, raw) def event(event_type, cmd, idx, payload, raw)
import introspect import introspect
if event_type=='every_50ms' if event_type=='every_50ms'
self.run_deferred() self.run_timers()
end #- first run deferred events -# end #- first run deferred events -#
if event_type=='every_250ms' if event_type=='every_250ms'

View File

@ -383,6 +383,7 @@ struct TasmotaGlobal_t {
#endif // PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED #endif // PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED
#ifdef USE_BERRY #ifdef USE_BERRY
bool berry_deferred_ready = false; // is there an deferred Berry function to be called at next millisecond
bool berry_fast_loop_enabled = false; // is Berry fast loop enabled, i.e. control is passed at each loop iteration bool berry_fast_loop_enabled = false; // is Berry fast loop enabled, i.e. control is passed at each loop iteration
#endif // USE_BERRY #endif // USE_BERRY
} TasmotaGlobal = { 0 }; } TasmotaGlobal = { 0 };

View File

@ -33,10 +33,11 @@ extern "C" {
extern const be_ctypes_structure_t be_tasmota_global_struct = { extern const be_ctypes_structure_t be_tasmota_global_struct = {
sizeof(TasmotaGlobal), /* size in bytes */ sizeof(TasmotaGlobal), /* size in bytes */
11, /* number of elements */ 12, /* number of elements */
nullptr, nullptr,
(const be_ctypes_structure_item_t[11]) { (const be_ctypes_structure_item_t[12]) {
// Warning: fields below need to be in alphabetical order // Warning: fields below need to be in alphabetical order
{ "deferred_ready", offsetof(TasmotaGlobal_t, berry_deferred_ready), 0, 0, ctypes_u8, 0 },
{ "devices_present", offsetof(TasmotaGlobal_t, devices_present), 0, 0, ctypes_u8, 0 }, { "devices_present", offsetof(TasmotaGlobal_t, devices_present), 0, 0, ctypes_u8, 0 },
{ "energy_driver", offsetof(TasmotaGlobal_t, energy_driver), 0, 0, ctypes_u8, 0 }, { "energy_driver", offsetof(TasmotaGlobal_t, energy_driver), 0, 0, ctypes_u8, 0 },
{ "fast_loop_enabled", offsetof(TasmotaGlobal_t, berry_fast_loop_enabled), 0, 0, ctypes_u8, 0 }, { "fast_loop_enabled", offsetof(TasmotaGlobal_t, berry_fast_loop_enabled), 0, 0, ctypes_u8, 0 },

View File

@ -176,6 +176,26 @@ int32_t callBerryEventDispatcher(const char *type, const char *cmd, int32_t idx,
return ret; return ret;
} }
// simple wrapper to call `tasmota.<method_name>()`
static void callBerryTasmotaFunc(const char * method_name) {
bvm *vm = berry.vm;
if (be_getglobal(vm, "tasmota")) {
if (be_getmethod(vm, -1, method_name)) {
be_pushvalue(vm, -2); // add instance as first arg
BrTimeoutStart();
int32_t ret = be_pcall(vm, 1);
if (ret != 0) {
be_error_pop_all(berry.vm); // clear Berry stack
}
BrTimeoutReset();
be_pop(vm, 1);
}
be_pop(vm, 1); // remove method
}
be_pop(vm, 1); // remove instance object
be_pop(vm, be_top(vm)); // clean
}
// Simplified version of event loop. Just call `tasmota.fast_loop()` // Simplified version of event loop. Just call `tasmota.fast_loop()`
// `every_5ms` is a flag to wait at least 5ms between calss to `tasmota.fast_loop()` // `every_5ms` is a flag to wait at least 5ms between calss to `tasmota.fast_loop()`
void callBerryFastLoop(bool every_5ms) { void callBerryFastLoop(bool every_5ms) {
@ -191,21 +211,13 @@ void callBerryFastLoop(bool every_5ms) {
fast_loop_last_call = now; fast_loop_last_call = now;
// TODO - can we make this dereferencing once for all? // TODO - can we make this dereferencing once for all?
if (be_getglobal(vm, "tasmota")) { callBerryTasmotaFunc("fast_loop");
if (be_getmethod(vm, -1, "fast_loop")) { }
be_pushvalue(vm, -2); // add instance as first arg
BrTimeoutStart(); // call `tasmota.run_immediate()`
int32_t ret = be_pcall(vm, 1); void callBerryRunDeferred(void) {
if (ret != 0) { if (nullptr == berry.vm) { return; }
be_error_pop_all(berry.vm); // clear Berry stack callBerryTasmotaFunc("run_deferred");
}
BrTimeoutReset();
be_pop(vm, 1);
}
be_pop(vm, 1); // remove method
}
be_pop(vm, 1); // remove instance object
be_pop(vm, be_top(vm)); // clean
} }
/*********************************************************************************************\ /*********************************************************************************************\
@ -888,6 +900,9 @@ bool Xdrv52(uint32_t function)
switch (function) { switch (function) {
case FUNC_SLEEP_LOOP: case FUNC_SLEEP_LOOP:
if (TasmotaGlobal.berry_deferred_ready) { // there are immediate functions registered, call them first
callBerryRunDeferred(); // call `tasmota.run_immediate()`
}
if (TasmotaGlobal.berry_fast_loop_enabled) { // call only if enabled at global level if (TasmotaGlobal.berry_fast_loop_enabled) { // call only if enabled at global level
callBerryFastLoop(true); // call `tasmota.fast_loop()` optimized for minimal performance impact callBerryFastLoop(true); // call `tasmota.fast_loop()` optimized for minimal performance impact
} }
@ -914,6 +929,9 @@ bool Xdrv52(uint32_t function)
} }
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
} }
if (TasmotaGlobal.berry_deferred_ready) { // there are immediate functions registered, call them first
callBerryRunDeferred(); // call `tasmota.run_immediate()`
}
if (TasmotaGlobal.berry_fast_loop_enabled) { // call only if enabled at global level if (TasmotaGlobal.berry_fast_loop_enabled) { // call only if enabled at global level
callBerryFastLoop(false); // call `tasmota.fast_loop()` optimized for minimal performance impact callBerryFastLoop(false); // call `tasmota.fast_loop()` optimized for minimal performance impact
} }