nodemcu with Lua and 8266 tmr.stop - lua

Note: This is a copy of a question asked here
Hi
I am completely new to EPS8266 and Lua (but not to programming - my first CPU was an 8080...)
Using a nodemcu HUZZA from adafruit
Anyway I am testing some timer stuff and running into this:
tmr.alarm(0, 500, 1, function()
print("I'm here")
tmr.stop(0)
end)
Without the stop, the loop keeps printing, with it the tmr.stop(0) stops. ... so far so good.
But if I want to start the timer again like:
tmr.alarm(0, 500, 1, function()
print("I'm here")
tmr.stop(0)
-- do some stuff
tmr.start(0)
end)
I get an error: PANIC: unprotected error in call to Lua API...
The documentation says that the tmr is still registered when stop is called.
A call to tmr.state(0) does the same. Only tmr.stop(0) seems to works as expected.
Thanks for your thoughts.

The documentation says to no longer use static timers
Static timers are deprecated and will be removed later. Use the OO API initiated with tmr.create().
If you want complete control over when the functions in the timer callback are executed you need a ALARM_SEMI instance upon which you call start whenever needed. It'll fire exactly once for every time you call start on it.
local mytimer = tmr.create()
mytimer:register(500, tmr.ALARM_SEMI, function() print("I'm here") end)
-- do stuff here
-- then whenever needed trigger the timer
mytimer:start()
Note that mytimer is not unregistered and not garbage collected.

Based on the documentation, you need to use tmr.ALARM_SEMI as your alarm mode.
ALARM_SEMI is described by the documentation as:
tmr.ALARM_SEMI manually repeating alarm (call tmr.start() to
restart)
tmr.ALARM_SEMI is equal to 2. Based on that, this should work:
tmr.alarm(0, 500, 2, function()
print("I'm here")
tmr.stop(0)
-- do some stuff
tmr.start(0)
end)

Related

lua: init.lua:15: attempt to call method 'alarm' (a nil value)

i am trying to fix a piece of code i did find online. (yeah i know....)
But in case you guys can help me out wihth this error it would be just amazing:
Error: lua: init.lua:15: attempt to call method 'alarm' (a nil value)
Code (from here: https://github.com/Christoph-D/esp8266-wakelight)
dofile("globals.lc")
wifi.setmode(wifi.STATION)
wifi.sta.config(WIFI_SSID, WIFI_PASSWORD)
wifi.sta.sethostname(MY_HOSTNAME)
if WIFI_STATIC_IP then
wifi.sta.setip({ip = WIFI_STATIC_IP, netmask = WIFI_NETMASK, gateway = WIFI_GATEWAY})
end
wifi.sta.connect()
-- Initialize the LED_PIN to the reset state.
gpio.mode(LED_PIN, gpio.OUTPUT)
gpio.write(LED_PIN, gpio.LOW)
tmr.alarm(
MAIN_TIMER_ID, 2000, tmr.ALARM_AUTO, function ()
if wifi.sta.getip() then
tmr.unregister(MAIN_TIMER_ID)
print("Config done, IP is " .. wifi.sta.getip())
dofile("ledserver.lc")
end
end)
What can i do there? Whats wrong?
Cheers and thank you!!!
It is all in the manual. You just have to read it.
There is an example of how to use the alarm method of timer objects.
if not tmr.create():alarm(5000, tmr.ALARM_SINGLE, function()
print("hey there")
end)
then
print("whoopsie")
end
You attempted to call tmr.alarm but it is tobj:alarm. The manual does not mention tmr.alarm. This function was removed from NodeMCU in January 2019.
You're using code you found online that is basedn on an older NodeMCU version. It is using functions that are deprecated by now.
See https://github.com/nodemcu/nodemcu-firmware/pull/2603#issuecomment-453235401
and
https://github.com/nodemcu/nodemcu-firmware/compare/5b22e1f9aee77095ab99dd6240ebd9dddd1cc5a0..c6444ecb6088d20e95197d808d8303c8093faab5
So you have to create a timer object first befvor you can use any of its methods. alarm is not a method of the tmr module anymore.
Edit
First you have to create a timer object https://nodemcu.readthedocs.io/en/latest/modules/tmr/#tobjcreate
local tObj = tmr.create()
Then you have to register a callback and start the timer. There is a convenience function alarm that does both for us.
And when we do not need our timer anymore we have to free the resources by calling
tObj:unregister()
Try something like
-- create a timer object
local tObj = tmr.create()
-- register an alarm
tObj:alarm(2000, tmr.ALARM_AUTO, function ()
if wifi.sta.getip() then
tObj:unregister()
print("Config done, IP is " .. wifi.sta.getip())
dofile("ledserver.lc")
end
end)

Attempt to index global 'websocket'

I'm pretty new to the ESP8266. I'm trying to add the WebSockets to the Lua code, but everytime I try to use the WebSocket looking at the documentation, the device throws error as attempt to index global websocket (a nil value). I'm not really sure if there is something to be imported, can anyone please help me with this.
function connectToSocket()
print ("Connect to socket called, OK.")
local ws_client = websocket.createClient()
end
wifi.setphymode(wifi.PHYMODE_N)
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","PWD")
wifi.sta.eventMonReg(wifi.STA_IDLE, function() print("IDLE") end)
wifi.sta.eventMonReg(wifi.STA_CONNECTING, function() print("CONNECTING...") end)
wifi.sta.eventMonReg(wifi.STA_WRONGPWD, function() print("WRONG PASSWORD!!!") end)
wifi.sta.eventMonReg(wifi.STA_APNOTFOUND, function() print("NO SUCH SSID FOUND") end)
wifi.sta.eventMonReg(wifi.STA_FAIL, function() print("FAILED TO CONNECT") end)
wifi.sta.eventMonReg(wifi.STA_GOTIP, function()
print("GOT IP "..wifi.sta.getip())
connectToSocket()
end)
wifi.sta.eventMonStart()
wifi.sta.connect()
I see three problems with the above code.
The main issue appears to be that your firmware is missing the websocket module. Uncomment https://github.com/nodemcu/nodemcu-firmware/blob/master/app/include/user_modules.h#L75 if you happen to build it manually.
Furthermore, all event handlers need to be registered before the respective events have a chance to be fired. I see you intend to do exactly that. However, by default wifi.sta.config uses auto connect=true in which case the WiFi registration process starts before the event monitor is started.
Lastly, the signature for wifi.sta.config changed a few months ago (see docs for details). Now you'd have to say wifi.sta.config{"SSID","PWD"} thereby passing a Lua table.

With Lua/NodeMCU, how can I wait until >1 mqtt publish calls have been made before running a block of code?

My application involves a battery-powered ESP8266 running NodeMCU for the purpose of updating sensor values periodically over MQTT.
To save battery life, I want to call dsleep() as soon as I'm done with my work. That work might involve more than 1 call to mqqt.Client.publish(). This brings us to the problem I'm facing.
I'm a Lua newbie, but as I understand, the right way to run some code after publish() has finished is to give it a PUBACK callback:
m = mqtt.Client(...)
m.publish("/my/topic", "some message", 1, 0, callback_func)
And in a simple case like above, that works great - even though the actual sending of the MQTT message is async with regard to the publish() call (see a good discussion of this here), callback_func() in the above example will only be called when publish() is done.
But when I have more than 1 call to publish() and want my callback to a) be called after they're all complete, and b) only be called once, I'm stuck.
The naive approach to this would be to put the callback (which is optional) only on the Nth publish() call:
m = mqtt.Client(...)
m.publish("/my/topic", "some message", 1, 0)
m.publish("/another/topic", "unrelated message", 1, 0, callback_func)
But this will not do what's expected. As documented:
NOTE: When calling publish() more than once, the last callback function defined will be called for ALL publish commands.
So in the above example, callback_func() ends up getting called twice (once for each successful publish().
I could combine the multiple publish() calls into a single call, but that feels like an ugly hack, and would have other adverse implications. If my two messages are conceptually distinct, this approach would push logic to separate them into the subscriber - yuck. And if they needed to go to different topics, this would be even worse. There must be a better way.
I thought perhaps mqqt.Client.close() would wait for my different publish() calls to finish, but it doesn't.
I'm out of ideas, and hoping someone with more Lua and/or NodeMCU+mqqt experience can give me a nudge in the right direction.
Here's my actual code, if it helps:
-- prior to this, we've gotten on the wifi network and acquired an IP
dofile("temp.lua") -- provides get_temp()
m = mqtt.Client("clientid", 120, "8266test", "password")
function mainloop(client)
print("connected - at top of loop")
m:publish("uptime",tmr.time(),1,0, function(client) print("sent uptime") end)
temp, humi = get_temp()
if (temp ~= nil) then
print(string.format("temp: %d", temp))
print(string.format("humi: %d", humi))
m:publish("temp",temp,1,0)
m:publish("humi",humi,1,0, function(client) -- note: this callback will be used for all publish() calls
rtctime.dsleep(SLEEP_USEC)
end)
end
end
m:on("connect", mainloop)
m:on("offline", function(client) is_connected = false print ("offline") end)
m:connect(MQQT_SVR, 1883, 0, mainloop,
function(client, reason) print("failed reason: "..reason) end)
Option 1:
publish all data at once, then go to sleep.
Option 2:
split your callback into two parts. the first part checks if you are done, the second part goes to sleep if you are done.
Of course you can solve this differently, count how many are left, count how many you have sent, send and remove items from a list until the list is empty,...
Of course there are more options but these are simple and sufficient.
Edit: example as requested
local totalItemCount = 5
function publishCallback()
itemsPublished = (itemsPublished or 0) + 1
print("item published")
if itemsPublished == totalItemCount then
print("I'm done, good night!")
end
end
for i = 1, totalItemCount do
publishCallback()
end
item published
item published
item published
item published
item published
I'm done, good night!

Lua making game loop and other functions

So like i have mainloop and many other functions but i cant call them because of there is infinitive loop how can i make them so i can call the functions.
local socket = require("socket")
local function sleep(sec)
socket.select(nil, nil, sec)
end
coroutine.wrap(function()
while true do
sleep(1)
end
end)()
print("bob") -- like here
like in code it doesn't print bob because there is loop is way avoid that i tried using corountines but they didnt work
Normally I would suggest using non-blocking commands, but I'm not certain what socket.select is doing. It appears you are misusing it as a blocking timer.

Computercraft lua change value later

ok, Im almost completely new to lua and computercraft but I have alot of creativity. I'm trying to write code that will reprint a variable every second. here is what I have so far:
display = "Loading..."
While true do
sleep(1)
term.clear()
term.setCursorPos(1,1)
print (display)
end
sleep(3)
display = "hello"
I want to use it to render a 2d game, the "display" variable would change often and thus why i want it to be updated every second.
It does indeed refresh every second when I run the code but for some reason I cant seem to get the "display" variable to change after 3 seconds to test it.
What am I doing wrong?
while true is an infinite loop. The script never reaches sleep(3).
Perhaps you want to replace while true with for i=1,3.
I am not experienced in Lua, but this might be a solution: answer on SO
In the UI thread, run:
while ((status=lua_resume(L_coroutine, 0)) == LUA_YIELD) {
semaphore_wait(); /* whatever the appropriate C# call is */
}
"Wait for response" should look something like:
while not results[my_result] do
coroutine.yield()
end
The "incoming message" function should look like the following in Lua:
results[cur_result]=parsed_message

Resources