nodemcu-firmware: Hardware timer (platform_hw_timer_*) causing freeze
I am building a module to radio control Somfy blinds via 433Mhz RF transmitter. A sequence of 0 and 1 with different delays needs to be transmitted as illustrated on the chart:
There are implementations for Arduino using delay functions but on ESP8266 I need to go asynchronous. That is why I have decided to use the hw_timer to achieve precise timing in the order of microseconds.
Actual behavior
I am indeed able to control blinds but after sending few commands the ESP8266 freezes and then goes restart triggered by hw watchdog (rst cause: 4, boot mode:(3,0)
).
Test code
The minimum code to reproduce the issue is
modules\somfy.c
#include "os_type.h"
#include "osapi.h"
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "hw_timer.h"
#include "user_interface.h"
#define SYMBOL 640 // symbol width in microseconds
#define TIMER_OWNER ((os_param_t) 's')
static uint32_t delay[10] = {9415, 89565, 4*SYMBOL, 4*SYMBOL, 4*SYMBOL, 4550, SYMBOL, SYMBOL, SYMBOL, 30415}; // 143 ms total
static uint8_t signalindex;
static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
(void) p;
//NODE_DBG("%d\n", signalindex);
signalindex++;
if (signalindex<10) {
platform_hw_timer_arm_us(TIMER_OWNER, delay[signalindex-1]);
} else {
platform_hw_timer_close(TIMER_OWNER);
}
}
static int somfy_lua_sendcommand(lua_State* L) {
if (!platform_hw_timer_init(TIMER_OWNER, NMI_SOURCE, TRUE)) {
// Failed to init the timer
luaL_error(L, "Unable to initialize timer");
}
NODE_DBG("Triggering hw timer (%d)\n", system_get_time());
platform_hw_timer_set_func(TIMER_OWNER, sendCommand, 0);
signalindex=0;
sendCommand(0);
return 0;
}
static const LUA_REG_TYPE somfy_map[] = {
{ LSTRKEY( "sendcommand" ), LFUNCVAL(somfy_lua_sendcommand)},
{ LNILKEY, LNILVAL}
};
NODEMCU_MODULE(SOMFY, "somfy", somfy_map, NULL);
and the Lua code to trigger it
tmr.alarm(0, 250, tmr.ALARM_AUTO, function() somfy.sendcommand() end)
The Lua code freezes and crashes in less than 1 minute. The sendcommand
calls platform_hw_timer_close()
in less than 150ms so there should be no issue that the function is called when the timer is still running.
Am I doing something which is not compliant with specifications?
NodeMCU version
I am using dev
branch, version up to 56b4a3e
commit.
Modules included: file, gpio, net, node, tmr, uart, wifi
.
Hardware
ESP-01. No need to connect the RF transmitter to reproduce the issue.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 19 (13 by maintainers)
I have merged
gpio.serout
andgpio.seroutasync
as suggested. I like the idea. It’s backwards compatible and does not increase the code size much while both versions are still available. I have added one feature - ifcallback
parameter is numeric thegpio
goes async but no callback is triggered.@oyooyo small modification and
gpio.serout
behaves the way you describe. The modification is that the last time ISR is called the GPIO state is not inverted.@devsaurus I have fixed also the way the callback was triggered. The way it was implemented was funny and kind of a workaround. I did not know about this straightforward way.
Latest commit.
@vsky279 regarding your question / implementation of the finished callback: I never saw your approach before to arm a timer from ISR for callbacks. Can’t assess whether it’s ok in interrupt context. For the pcm module I used
task_post_*()
as mentioned in the Extension Developer FAQ. Examples can be found in gpio and pcm.@oyooyo I’m not sure whether we should drop the synchronous
gpio.serout()
completely. The hw_timer based asynchronous solution comes with constraints regarding the step time. IIRC Espressif specifies a minimum reload time of ~50 µs. We might end up with two flavors targeted for different use cases:Am I missing something here?
Both flavors could be further collapsed into
Setting or omitting a callback would switch between the asynchronous and synchronous variant respectively. (repeat_num could be made a mandatory parameter for simplicity)
What do others think?
I thought the same when I saw this issue. While I have no personal interest in a somfy module, I’m very interested in a proper, asynchronous version of gpio.serout() using interrupts instead of busy-waiting. So I would very much welcome if the code made its way into a function like
gpio.seroutasync()
or so that could then be used by multiple one-way communication protocols (for example using 433MHz or IR transmitters).Lukas, kudos for sticking to our new issue template 😃