NodeMCU timer stops unexpectedly - timer

I have a NodeMCU Lua application that uses two timers. Each timer invokes a function that causes an HTTP request to be made to a local server.
After a few iterations, one of the timers stops, and the other timer continues. The number of iterations before the timer stops seems to be random. I have run the test script many times and the point at which the timer stops is never the same. Note: it is not always the same timer that halts.
Here is some test code that reliably demonstrates this problem:
ctr1=0
ctr2=0
local function doCmdChk()
ctr1 = ctr1 + 1
http.get( "http://192.168.2.38/ICmd.py?i=" .. ctr1 , nil,
function(rspCode, payload)
tmr.start(1)
end)
end
local function sendData()
ctr2 = ctr2 + 1
local msgBdy = '{"s":"' .. ctr2 .. '","i":"test23", "d":"heap='..node.heap()..'"}'
http.post("http://192.168.2.38/DeviceScan.py", "Content-Type: text/json\r\n", msgBdy,
function(rspCode, payload)
tmr.start(2)
end)
end
--mainline start:
tmr.alarm(1, 3000, tmr.ALARM_SEMI, doCmdChk)
tmr.alarm(2, 5000, tmr.ALARM_SEMI, sendData)
My application doesn't fire off the HTTP requests as quickly as the test code, but when the application runs for several hours the same result eventually occurs (i.e. one of the timers stops running). Reducing the time between HTTP requests makes the error occur sooner.
Has anyone encountered this issue? Does anyone have any ideas as to how to troubleshoot this issue? (not being able to reliably send continuous HTTP requests is a show stopper for this application).

The solution is to set up flags so that only one http request is outstanding at any given time. Here is the previous test script that includes the flags:
ctr1=0
ctr2=0
sendFlag=true
local function doCmdChk()
if sendFlag then
sendFlag=false
ctr1 = ctr1 + 1
http.get( "http://192.168.2.38/ICmd.py?i=" .. ctr1 , nil,
function(rspCode, payload)
sendFlag=true
tmr.start(1)
end)
else
tmr.alarm(3, 1000, tmr.ALARM_SINGLE, doCmdChk)
end
end
local function sendData()
if sendFlag then
sendFlag=false
ctr2 = ctr2 + 1
local msgBdy = '{"s":"' .. ctr2 .. '","i":"test23", "d":"heap='..node.heap()..'"}'
http.post("http://192.168.2.38/DeviceScan.py", "Content-Type: text/json\r\n", msgBdy,
function(rspCode, payload)
sendFlag=true
tmr.start(2)
end)
else
tmr.alarm(3, 1000, tmr.ALARM_SINGLE, sendData)
end
end
--mainline start:
tmr.alarm(1, 3000, tmr.ALARM_SEMI, doCmdChk)
tmr.alarm(2, 5000, tmr.ALARM_SEMI, sendData)
I ran this script for several hours and both http send functions continued to work as expected.
I tried the node.task.post() option, test script as follows:
ctr1=0
ctr2=0
local function doCmdChk()
ctr1 = ctr1 + 1
http.get( "http://192.168.2.38/ICmd.py?i=" .. ctr1 , nil,
function(rspCode, payload)
sendFlag=true
tmr.start(1)
end)
end
local function sendData()
ctr2 = ctr2 + 1
local msgBdy = '{"s":"' .. ctr2 .. '","i":"test23", "d":"heap='..node.heap()..'"}'
http.post("http://192.168.2.38/DeviceScan.py", "Content-Type: text/json\r\n", msgBdy,
function(rspCode, payload)
sendFlag=true
tmr.start(2)
end)
end
--mainline start:
tmr.alarm(1, 3000, tmr.ALARM_SEMI, function() node.task.post(node.task.MEDIUM_PRIORITY, doCmdChk) end)
tmr.alarm(2, 5000, tmr.ALARM_SEMI, function() node.task.post(node.task.HIGH_PRIORITY, sendData) end)
But after a couple of hours of running one of the http callbacks was not invoked, so there must have been a collision.

I don't know much about NodeMCU, but according to the reference manual the http.post and http.get callback functions are invoked when a response is received. Hence timers are only restarted when a response is received. Any chance there is a delay or maybe you never get a response?
Restarting a timer after some third party stuff has responded would add a variable delay and therefor should not be very precise. I would not expect it to be as accurate as some test code.
For debugging I suggest you print the actual delays between invoked callbacks or the delay between post/get and response.

Related

using usocket seems to halt the loop (micropython)

I'm trying to code a simple program for a ESP32 board.
My main program is fairly simple and it has to run on a loop.
On the side, the device also needs to be able to respond to HTTP requests with a very simple response.
This is my attempt (a rework of https://randomnerdtutorials.com/micropython-esp32-esp8266-bme280-web-server/):
try:
import usocket as socket
except:
import socket
from micropython import const
import time
REFRESH_DELAY = const(60000) #millisecondi
def do_connect():
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('connecting to network...')
wlan.config(dhcp_hostname=HOST)
wlan.connect('SSID', 'PSWD')
while not wlan.isconnected():
pass
print('network config:', wlan.ifconfig())
import json
import esp
esp.osdebug(None)
import gc
gc.collect()
do_connect()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, SENSOR_SCKT_PORT))
s.listen(5)
prevRun = 0
i = 0
while True:
print("iteration #"+str(i))
i += 1
# run every 60 seconds
curRun = int(round(time.time() * 1000))
if curRun - prevRun >= REFRESH_DELAY:
prevRun = curRun
# MAIN PROGRAM
# ......
# whole bunch of code
# ....
# run continuously:
try:
if gc.mem_free() < 102000:
gc.collect()
conn, addr = s.accept()
conn.settimeout(3.0)
print('Got a connection from %s' % str(addr))
request = conn.recv(1024)
conn.settimeout(None)
request = str(request)
#print('Content = %s' % request)
measurements = 'some json stuff'
conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.send(measurements)
conn.close()
except OSError as e:
conn.close()
print('Connection closed')
what happens is I only get the iteration #0, and then the while True loop halts.
If I ping this server with a HTTP request, I get a correct response, AND the loop advances to iteration #1 and #2 (no idea why it thinks I pinged it with 2 requests).
So it seems that socket.listen(5) is halting the while loop.
Is there any way to avoid this?
Any other solution?
I don't think that threading is an option here.
The problem is that s.accept() is a blocking call...it won't return until it receives a connection. This is why it pauses your loop.
The easiest solution is probably to check whether or not a connection is waiting before calling s.accept(); you can do this using either select.select or select.poll. I prefer the select.poll API, which would end up looking something like this:
import esp
import gc
import json
import machine
import network
import select
import socket
import time
from micropython import const
HOST = '0.0.0.0'
SENSOR_SCKT_PORT = const(1234)
REFRESH_DELAY = const(60000) # milliseconds
def wait_for_connection():
print('waiting for connection...')
wlan = network.WLAN(network.STA_IF)
while not wlan.isconnected():
machine.idle()
print('...connected. network config:', wlan.ifconfig())
esp.osdebug(None)
gc.collect()
wait_for_connection()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, SENSOR_SCKT_PORT))
s.listen(5)
poll = select.poll()
poll.register(s, select.POLLIN)
prevRun = 0
i = 0
while True:
print("iteration #"+str(i))
i += 1
# run every 60 seconds
curRun = int(round(time.time() * 1000))
if curRun - prevRun >= REFRESH_DELAY:
prevRun = curRun
# MAIN PROGRAM
# ......
# whole bunch of code
# ....
# run continuously:
try:
if gc.mem_free() < 102000:
gc.collect()
events = poll.poll(100)
if events:
conn, addr = s.accept()
conn.settimeout(3.0)
print('Got a connection from %s' % str(addr))
request = conn.recv(1024)
conn.settimeout(None)
request = str(request)
# print('Content = %s' % request)
measurements = 'some json stuff'
conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.send(measurements)
conn.close()
except OSError:
conn.close()
print('Connection closed')
You'll note that I've taken a few liberties with your code to get it running on my device and to appease my sense of style; primarily, I've excised most of your do_connect method and put all the imports at the top of the file.
The only real changes are:
We create a select.poll() object:
poll = select.poll()
We ask it to monitor the s variable for POLLIN events:
poll.register(s, select.POLLIN)
We check if any connections are pending before attempting to handle a connection:
events = poll.poll(100)
if events:
conn, addr = s.accept()
conn.settimeout(3.0)
[...]
With these changes in place, running your code and making a request looks something like this:
iteration #0
iteration #1
iteration #2
iteration #3
iteration #4
iteration #5
iteration #6
Got a connection from ('192.168.1.169', 54392)
iteration #7
iteration #8
iteration #9
iteration #10
Note that as written here, your loop will iterate at least once every 100ms (and you can control that by changing the timeout on our call to poll.poll()).
Note: the above was tested on an esp8266 device (A Wemos D1 clone) running MicroPython v1.13-268-gf7aafc062).

knife.timer on Lua syntax - not behaving as expected

If anyone is familiar with knife.timer on Lua, could you please look at my code and tell me what I have out of order?
I'm hoping to do two things:
have a countdown timer that ticks down every second and
have a timer that after six seconds begins blinking my characters for 3 more seconds before changing state.
With the following code, my countdown timer starts at 9 but gets well into the negative tens. My characters begin to blink after what feels like 4 seconds, and continue blinking for a few seconds after changing state.
I have Timer:update(dt) in main, so I'm not sure why the timing is off. And I thought the finish would not call the change state function until the characters' 16 iterations of blinking were done.
function PlayerPilotState:update(dt)
self.player.currentAnimation:update(dt)
Timer.every(1, function()
self.timer = self.timer - 1
end)
Timer.after(6, function()
Timer.every(0.2, function()
self.player.blinking = not self.player.blinking
self.player.otherPlayer.blinking = not self.player.otherPlayer.blinking
end):finish(function()
self.player:changeState('falling')
self.player.otherPlayer:changeState('falling')
end):limit(16)
end)
end
Thanks!
With the basic countdown timer, you never specify when it should stop. Try using :limit(9) or self.timer = math.max(0,self.timer - 1)
2.Have you timed it properly (it's hard to feel how much time has passed), since you use Timer.after. The :finish() function happens inside the :after(), and after the :every(), which could cause things to be weird. I suggest adding the :limit before the :finish().
Timer.after(6, function()
Timer.every(0.2, function()
self.player.blinking = not self.player.blinking
self.player.otherPlayer.blinking = not self.player.otherPlayer.blinking
end):limit(16):finish(function()
self.player:changeState('falling')
self.player.otherPlayer:changeState('falling')
end)
end)
Both of the above comments were extremely useful. It turns out that putting timers under update is a bad idea because they either are calling new timers or refreshing strangely. What I ended up with, and works perfectly is:
function PlayerPilotState:enter(params)
Timer.every(1, function()
self.timer = self.timer - 1
end)
Timer.after(6, function()
Timer.every(0.2, function()
self.player.blinking = not self.player.blinking
self.player.otherPlayer.blinking = not self.player.otherPlayer.blinking
end)
:limit(15)
:finish(function()
self.player.blinking = false
self.player.otherPlayer.blinking = false
self.player:changeState('falling')
self.player.otherPlayer:changeState('falling')
end)
end)
end
A follow up question I would have, is how to do this without using an enter function? I guess any function call that called only once (with maybe a boolean flag to call it) would work. Thanks!

how to set coreSize parameter in Hystrix?

I'm working on a spring boot project about electronic contract recently.And There has an interface of raiseContract().Considering that the traffic of this interface will be large in the future,My Leader let me use Hystrix to defender it.And I did not use it before.I am learning it and trying to use it on the interface.I use ThreadPool Isolation Strategy and I don't konw how to set
the parameter of coreSize reasonable in ThreadPoolProperties.In other words,I want to know what should I follow to set it.
I did a lot of research,but I did not get the answer.All of Answer is about the meaning of coreSize,maxQueueSize etc.
Here is my code:
#HystrixCommand(
groupKey = "contractGroup",
commandKey = "raiseContract",
fallbackMethod = "raiseContractFallback",
threadPoolProperties = {
#HystrixProperty(name = "coreSize", value = "20"),
#HystrixProperty(name = "maxQueueSize", value = "150"),
#HystrixProperty(name = "queueSizeRejectionThreshold", value = "100")},
commandProperties = {
#HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "15000"),
#HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
#HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
#HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000"),
#HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "20")
})
As you are already aware of, there are 3 main threadPool configurations:
coreSize: Number of threads that will be maintained in the pool
maxSize: Defines how much extra threads are allowed in case need arises.
maxQueueSize: Queue size of the tasks
Now lets start with an example. Assume there is a service using hystrix, call it HystrixService, for which coreSize = maxSize = n and maxQueueSize = -1 (default case). This means at a time at most 'n' tasks will be executed. Any extra task that is coming will be rejected (fallback will be executed).
So, in ideal scenario, you have to ensure that this HystrixService doesn't reject any request coming to it. You need to know at max how many requests can there be on HystrixService. So if throughput on HystrixService is 10 Requests per second, then max concurrent requests on HystrixService can be 10. Now suppose latency of HystrixService is 2 sec, then by the time it responds to first 10 requests, 10 more requests will come. i.e. total requests = 2 * 10 = 20. So coreSize in this case should be 20.
This is same as mentioned in hystrix documentation,
coreSize = Peak Request per sec × P99 latency + some breathing room
Now, you can keep maxSize and queueSize a bit high, so that it doesn't reject requests, in case there are sudden throughput spikes on your service.

Why does Gatling stop simulation when any scenario exists and doesn't wait until the end?

Let's say I have this configuration
val scn = (name: String) => scenario(name)
.forever() {
.exec(request)
}
setUp(
scn("scn1").inject(atOnceUsers(1))
.throttle(
jumpToRps(1), holdFor(10 seconds)
),
scn("scn2").inject(atOnceUsers(1))
.throttle(jumpToRps(1), holdFor(20 seconds))
).protocols(http.baseURLs(url))
I would expect to run the whole simulation for 20 seconds - until all is finished. What actually happens is that the simulation is stopped after 10 seconds, right after the first scenario finishes.
---- Requests ------------------------------------------------------------------
> Global (OK=20 KO=0 )
> scn1 / HTTP Request (OK=10 KO=0 )
> scn2 / HTTP Request (OK=10 KO=0 )
---- scn1 ----------------------------------------------------------------------
[--------------------------------------------------------------------------] 0%
waiting: 0 / active: 1 / done:0
---- scn2 ----------------------------------------------------------------------
[--------------------------------------------------------------------------] 0%
waiting: 0 / active: 1 / done:0
================================================================================
Simulation foo.Bar completed in 10 seconds
To overcome this in general, I need to configure all scenarios that ends earlier then the final one to wait with zero throttle.
setUp(
scn.inject(atOnceUsers(1))
.throttle(
jumpToRps(1), holdFor(10 seconds),
jumpToRps(0), holdFor(10 seconds) // <-- added wait
),
scn.inject(atOnceUsers(1))
.throttle(jumpToRps(1), holdFor(20 seconds))
).protocols(http.baseURLs(url))
Is this expected behavior? What other options do I have to make my simulation run until all scenarios are finished or until maxDuration?
Possible explanation could be that Feeder loops on data and when there is no more data it exists. In this case call "circular" on your feeder so that it goes back to the top of the sequence once the end is reached

Creating a timer using Lua

I would like to create a timer using Lua, in a way that I could specify a callback function to be triggered after X seconds have passed.
What would be the best way to achieve this? ( I need to download some data from a webserver that will be parsed once or twice an hour )
Cheers.
If milisecond accuracy is not needed, you could just go for a coroutine solution, which you resume periodically, like at the end of your main loop, Like this:
require 'socket' -- for having a sleep function ( could also use os.execute(sleep 10))
timer = function (time)
local init = os.time()
local diff=os.difftime(os.time(),init)
while diff<time do
coroutine.yield(diff)
diff=os.difftime(os.time(),init)
end
print( 'Timer timed out at '..time..' seconds!')
end
co=coroutine.create(timer)
coroutine.resume(co,30) -- timer starts here!
while coroutine.status(co)~="dead" do
print("time passed",select(2,coroutine.resume(co)))
print('',coroutine.status(co))
socket.sleep(5)
end
This uses the sleep function in LuaSocket, you could use any other of the alternatives suggested on the Lua-users Wiki
Try lalarm, here:
http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/
Example (based on src/test.lua):
-- alarm([secs,[func]])
alarm(1, function() print(2) end); print(1)
Output:
1
2
If it's acceptable for you, you can try LuaNode. The following code sets a timer:
setInterval(function()
console.log("I run once a minute")
end, 60000)
process:loop()
use Script.SetTimer(interval, callbackFunction)
After reading this thread and others I decided to go with Luv lib. Here is my solution:
uv = require('luv') --luarocks install luv
function set_timeout(timeout, callback)
local timer = uv.new_timer()
local function ontimeout()
uv.timer_stop(timer)
uv.close(timer)
callback()
end
uv.timer_start(timer, timeout, 0, ontimeout)
return timer
end
set_timeout(1000, function() print('ok') end) -- time in ms
uv.run() --it will hold at this point until every timer have finished
On my Debian I've install lua-lgi packet to get access to the GObject based libraries.
The following code show you an usage demonstrating that you can use few asynchronuous callbacks:
local lgi = require 'lgi'
local GLib = lgi.GLib
-- Get the main loop object that handles all the events
local main_loop = GLib.MainLoop()
cnt = 0
function tictac()
cnt = cnt + 1
print("tic")
-- This callback will be called until the condition is true
return cnt < 10
end
-- Call tictac function every 2 senconds
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, tictac)
-- You can also use an anonymous function like that
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1,
function()
print( "There have been ", cnt, "tic")
-- This callback will never stop
return true
end)
-- Once everything is setup, you can start the main loop
main_loop:run()
-- Next instructions will be still interpreted
print("Main loop is running")
You can find more documentation about LGI here

Resources