3

With NodeMCU, we can easily create timer function in esp8266 chip. However, I wonder what will happen if codes inside a timer execute over the timer interval I set?

Please see the code below. If I set a timer with 2 seconds interval, and "Something to do" inside this timer executes over 2 seconds, then what will happen?

tmr.alarm(0, 2000, 1, function ()
    --Something to do
end)

a) Will "Something to do" be terminated once the interval reaches 2 seconds?

b) Or "Something to do" will continue execute until finish, and the next "Something to do" will be delayed?

c) Or the each round of this timer will wait for "Something to do" to finish regardless of the 2-seconds-interval? (the interval is automatically expanded)

d) or else?

Marcel Stör
  • 22,695
  • 19
  • 92
  • 198
Peter
  • 45
  • 3

2 Answers2

0

The function does not behave as you might think. Usually when you are required to provide a callback function, the callback is executed when an event occurs; which is what happens here. The callback function is executed after the timer expires.

The documentation for tmr.alarm() says that the function is a combination of tmr.register() and tmr.start(). The tmr.register() documentation says

Configures a timer and registers the callback function to call on expiry.

So, your answer is that "Something to do" will run, until it's finished, 2 seconds after the tmr.alarm() function is called.

tmr.alarm() (and tmr.register() which it is based upon) can take a mode parameter. I'll describe their behavior and how each is affected by the execution time of the callback function.

  • tmr.ALARM_SINGLE: Run the callback function only once n seconds after the call to tmr.alarm() finishes. Completely independent of the execution time of the callback function.
  • tmr.ALARM_AUTO: Run the callback function repeated every n seconds after the call to tmr.alarm() finishes. It is important to note that the next interval starts as soon as the previous finishes, regardless of the execution time of the callback. So if the callback take 0.5s to finish executing and the timer is 2s, then the next call will occur 1.5s after the callback function finishes.
  • tmr.ALARM_SEMI: Run the callback function n seconds after the call to tmr.alarm() finishes. Unlike tmr.ALARM_AUTO the next interval is not automatically started, but will only start after you call tmr.start(); which you should probably do within the callback function. This means you can set the timer to be dependent on the execution time of the callback function. If the timer is 2s and you restart the timer at the end of your callback function, then the next time the callback will run will be 2s from then.

As you might be able to tell, you don't want the execution time of the callback function to be greater than the timer period, callbacks will keep stacking on top of each other, never finishing. The callbacks should be simple and quick to execute, perhaps scheduling additional work as another task.

ktb
  • 1,498
  • 10
  • 27
  • You mean that "Something to do" will execute once every timer round's end, not beginning? – Peter Jun 02 '17 at 05:54
  • Yes, exactly. The timer runs for n seconds, then it executes the code. You can set the alarm to be one-shot, or repeat every n seconds based on the `mode` parameter. – ktb Jun 02 '17 at 06:00
  • So "Something to do" will actually be executed independently, not inside the timer, and basically won't affect timer regardless of execution time? When the next interval ends, timer will fire another thread of "Something to do". Is my understanding right? – Peter Jun 02 '17 at 07:11
  • I don't understand what you mean when you say "inside the timer". I think you are applying your understanding of what a timer is to this perhaps poorly named library. What this function does is "n seconds from now, run this function", "this" being the "Something to do". The execution time of the callback function will not have an affect on when the timer triggers, it will always be n seconds after the call to `tmr.alarm()`. And *if* you set the timer so that it repeats, the function will be called again depending upon the mode you set it to. See my edits for more info on the modes. – ktb Jun 02 '17 at 12:54
0

I believe there's a misunderstanding of what type of firmware NodeMCU is or what kind of programming model it requires.

The NodeMCU programming model is similar to that of Node.js, only in Lua. It is asynchronous and event-driven. Many functions, therefore, have parameters for callback functions.

Source: NodeMCU README, "Programming Model"

The Lua libraries provide a set of functions for declaring application functions (written in Lua) as callbacks (which are stored in the Lua registry) to associate application tasks with specific hardware and timer events. These are non-preemptive at an applications level* The Lua libraries work in consort with the SDK to queue pending events and invoke any registered Lua callback routines, which then run to completion uninterrupted.

Source: NodeMCU Lua developer FAQ

For full explanations see the "event tasking system" chapter at https://nodemcu.readthedocs.io/en/latest/en/lua-developer-faq/#so-how-does-the-sdk-event-tasking-system-work-in-lua

You're saying

and "Something to do" inside this timer executes over 2 seconds

but the truth is that it will never run for 2s. In fact, any task that runs for more than a few milliseconds uninterrupted may cause the Wifi and TCP stacks to fail. If you write code that violates this principle then the watchdog might reset your device any time. Events that your code triggers are simply added to a queue and executed in sequential order.

Thus, the correct answer is b) in most cases.

Marcel Stör
  • 22,695
  • 19
  • 92
  • 198
  • Thank you for your detailed reply. What if "Something to do" does not require using Wifi and TCP stacks? Let me suppose "Something to do" is turn on the LED for 3 seconds, will it actually light up for 3 seconds? – Peter Jun 02 '17 at 05:43
  • More misconceptions I believe...You don't need any _processing_ power to keep a LED turned on for 3s. The device needs a few nanoseconds to turn the GPIO pin from high to low (or vice versa) and then it's idle again until you tell it to reverse the operation. A simple blink example is e.g. here: https://roboindia.com/tutorials/esp8266-led-blinking-lua – Marcel Stör Jun 02 '17 at 14:38