HTTP GET in LUA/NodeMCU - lua

I'm trying to send a HTTP GET each time I press a button :
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","PWD")
function loop()
if wifi.sta.status() == 5 then
-- Stop the loop
tmr.stop(0)
else
print("Connecting...")
end
end
tmr.alarm(0, 100, 1, function() loop() end)
print(wifi.sta.getip())
outpin_led = 1
inpin_button = 3
gpio.mode(outpin_led,gpio.OUTPUT)
gpio.mode(inpin_button,gpio.INPUT)
light_on = false
function light()
if not light_on then
-- turn the light on
gpio.write(outpin_led,gpio.HIGH)
light_on = true
http.get("https://google.com", function(clt, data)
print(data)
end)
else
-- turn the light off
gpio.write(outpin_led,gpio.LOW)
light_on = false
end
end
gpio.trig(inpin_button,"down",light)
The line containing http.get is throwing this error message :
> PANIC: unprotected error in call to Lua API (stdin:6: attempt to index global 'http' (a nil value))
I made sure my NodeMCU build contained the http module by compiling it through http://nodemcu-build.com/
Any idea ?
Thanks.

As Marcel Stör pointed out, it was indeed an issue during the flashing of the firmware.
Thanks a lot for your reply and for your work on http://nodemcu-build.com/.

Related

LUA using function variable outside of it

I am trying to edit a script in LUA but I couldn't get access of a local defined in a function
LUA Code
function getSafeMoney()
local SafeMoney = nil
QBCore.Functions.ExecuteSql(false, 'SELECT * FROM `moneysafes` WHERE `safe` = "mechanic"', function(result)
SafeMoney = json.decode(json.encode(result[1])).money;
end)
return SafeMoney
end
print(getSafeMoney())
Result :
nil
here's the sql function as sysdevs asked
QBCore.Functions.ExecuteSql = function(wait, query, cb)
local rtndata = {}
local waiting = true
exports['ghmattimysql']:execute(query, {}, function(data)
if cb ~= nil and wait == false then
cb(data)
end
rtndata = data
waiting = false
end)
if wait then
while waiting do
Citizen.Wait(5)
end
if cb ~= nil and wait == true then
cb(rtndata)
end
end
return rtndata
end
Your second function is not holding the main function I assume, I am bad at lua aswell, but I think that changing
QBCore.Functions.ExecuteSql(false, ............
to
QBCore.Functions.ExecuteSql(true, ............
This will probably fix your problem, still your code is ambiguous and needs concentration if you could provide more information I might be able to help more

Tarantool fiber behavior with fiber.yield() and fiber.testcancel()

I ran into an unexpected behavior while building Tarantool app based on fibers.
Simple reproducer of my code looks like this:
local log = require('log')
local fiber = require('fiber')
box.cfg{}
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
fiber.yield()
end
return 0
end
local wrapfunc = function()
local ok, resp = pcall(func)
log.info(ok)
log.info(resp)
end
for _ = 1, 100 do
local myfiber = fiber.create(wrapfunc)
fiber.sleep(0.02)
fiber.kill(myfiber)
end
and it prints to log false, fiber is cancelled. Moreover, if I use the following func:
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
pcall(fiber.yield)
end
return 0
end
it prints to log true, 1, and if I use
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
if pcall(fiber.yield) ~= true then
return 2
end
end
return 0
end
it prints to log true, 2.
I expected that after yielding from running myfiber, if control returns to the external fiber and it calls fiber.kill(myfiber), the next time control returns to the cancelled myfiber we will be in the end of cycle iteration and on the next iteration code will successfully return 1. However, the work of func ends with throwing error fiber is cancelled, not with return. So how the real life cycle of yielding fiber works?
Actually, there is no unexpected behaviour here. I believe it mostly documentation issue. Let me explain. I've simplified your example a bit:
#!/usr/bin/env tarantool
local fiber = require('fiber')
local f1 = function() fiber.yield() end
local f2 = function() pcall(fiber.yield) end
local func = function(fn)
fn()
if not pcall(fiber.testcancel) then
return 'fiber.testcancel() failed'
end
end
local fiber1 = fiber.create(function() print(pcall(func, f1)) end)
fiber.kill(fiber1)
local fiber2 = fiber.create(function() print(pcall(func, f2)) end)
fiber.kill(fiber2)
The output would be:
false fiber is cancelled
true fiber.testcancel() failed
When you call fiber.kill, fiber.yield() or fiber.sleep() just raises an error, so your fiber is not able to reach fiber.testcancel and just dies. When you do pcall(fiber.yield), you basically suppress this error and proceed. Then fiber.testcancel checks its fiber status and reraise an exception. But this is a silly example.
Now, with bigger chunks of code, when lots of function invocations involved, you usually want to catch those errors during yield, do some finalisation work and call fiber.testcancel() to promote error upwards (imagine multiple checks of this kind in different parts of big stacktrace). I believe it is the basic use case, fiber.testcancel was introduced for, besides discussions if its design is usable or not.
P.s. And yes, occasional exceptions of such yield calls are not documented. At least I could not find anything in the fiber page

Web request event driven Lua code

What i have seen and currently implemented in my Lua scripts running on my ESP8266 WiFi module, are polling the server page every few seconds and than checking whether the value has changed or not. If value has changed than take some action.
Here's my code
tmr.alarm(1,10000, 1, function()
if (wifi.sta.getip() == nil) then
print("IP unavaiable, Waiting...")
else
tmr.stop(1)
tmr.alarm(1,10000, 1, function()
event_driven_func()
end)
end
end)
function event_driven_func()
print("Inside event_driven_func function"..node.heap());
conn = nil
conn=net.createConnection(net.TCP,0)
conn:on("receive", function(conn, payload)
-- local buf = "";
startRead = false
Data = ""
print("payload : "..payload)
for i = 1, #payload do
c = payload:sub(i,i)
if (c=="{") then
startRead=true
end
if (startRead) then
Data=Data..c
end
if (c=="}") then
startRead=false
print("json string detected Do something now ...");
end
end
conn:close()
collectgarbage("collect");
end)
conn:connect(80,"my_server.co.in")
conn:on("connection", function(conn, payload)
conn:send("GET /xyz.php HTTP/1.0\r\nHost: my_server.co.in\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n")
end)
end
But this is highly inefficient.
How to implement or better a pseduo-code, a event driven code where whenever the value changes there's an interrupt to the code and then event_driven_func executes ?
Or any better efficient way.
Application example :
Say i've number of youtube subscribers showing on a server page, whenever the subscribers change it should make a "POST" web request to one of my json page xyz.php (this will be done using IFTTT ) and then a event will execute the function in Lua code so that value will be reflected.

Yet another module memory loss issue

This problem is driving me nuts. Its consumed way too much time and I could do with some help, please!
Basically, Im downloading weather data from my server using Get(). This works well. All I want to do is display it on a 16x2 i2c lcd screen, however I am plagued with 'out of memory' issues.
I have tried several modules provided by the community and all seem to suffer the same problems, so Im convinced the problem lays with my code / calling convention.
Im currently using module lcd.lua, a slight variation on Vladimir Dronnikov's offering. and calling it in the prescribed manor
<pre>
i2c.setup(0, 3, 4, i2c.SLOW)
lcd = dofile("lcd.lua")()
</pre>
the calling lua script is liberally sprinkled with print statements:
<pre>
lcd.put(lcd:locate(0, 0),"Mast Temp: ")
</pre>
My file structure looks like this
<pre>
Init.lua -- opportunity to prevent script execution. Calls wifiConnect
wifiConnect.lua -- connects to wifi and calls dofile(DetServerData())
GetServerData.lua -- Fetches data and displays on lcd module.
</pre>
When the wifiConnect calls dofile against GetServerData it will usually fall over with 'not enough memory' sometimes the script will run 2 or 3 times before encountering 'out of memory' errors.
I have however had it run 8000-9000 times before falling over. (only one or twice.)
If I allow wifiConnect to run GetFileData it will stop with
<pre>
PANIC: unprotected error in call to Lua API (not enough memory)
</pre>
loading GetServerData seems to consume 13816 bytes on the heap.. leaving 18800. surely enough to run.
I have tried require(getServerData), slightly different numbers, same result.
GetServerData is as follows
-- 0 DS18B20_temp_C
-- 1 WindSpeed_kmph
-- 2 WindBearing;
-- 3 WindDir
-- 4 BMP_temp
-- 5 BMP_pressure
-- BMP_altitude --------------REMOVED
-- 6 DHT22_temperature
-- 7 DHT22_humidity
-- DH22_dewPoint--------------REMOVED
-- DHT22_heatIndex--------------REMOVED
local WDPin = 1
local LCDscreenPin = 2
local LCDmaxScreen = 4
local cycleCount=0
local WDogLED = false
local dataTable={} ; i=1
local connClosed = true
local LCDpageID = 1
function GetData()
if connClosed == true then
local conn=net.createConnection(net.TCP, 0)
print("Open connection...")
WatchDog()
if conn == nil then
print("Connection error")
else
conn:connect(80,'31.220.16.114')
connClosed = false
tmr.alarm(4,3000,0,ResetConn)
conn:send("GET /test.txt? HTTP/1.1\r\n")
conn:send("Host: theoldschool.esy.es\r\n")
conn:send("Accept: */*\r\n")
conn:send("User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n")
conn:send("\r\n")
conn:on("disconnection", function(conn)
connClosed = true
WatchDog()
print("Disconnected...")
end) -- on:"disconection"
conn:on("receive",function(conn,payload)
cleanData = extractWeatherData(payload)
DbugGarbageCollect()
fillDataTable(cleanData,'\r\n')
for k,v in pairs(dataTable) do print(k,v) end
cycleCount = cycleCount +1
print(cycleCount)
end)-- on recieve
end-- if conn == nil
end -- if connClosed
end--GetData
function ResetConn()
print("Stuck")
connClosed = true
end -- ResetConn
function fillDataTable(inputstr, sep)
local i=0
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
dataTable[i] = str
i = i + 1
end--for
end--splitCmd
function stripMarkers(str, chrs)
local s = str:gsub("["..chrs:gsub("%W","%%%1").."]", '')
return s
end
function extractWeatherData (payload)
local startChar = string.find(payload, '%')
local s = string.sub(payload, startChar,endChar)
s = stripMarkers(s, "")
return s
end -- extractWeatherData
function WatchDog()
if WDogLED == false then
WDogLED = true
gpio.write(WDPin, gpio.HIGH)
else
WDogLED = false
gpio.write(WDPin, gpio.LOW)
end --if
end--WatchDog
function DbugGarbageCollect()
local before = collectgarbage("count")
collectgarbage()
print(before - collectgarbage("count"))
print(node.heap())
end --DbugGarbageCollect
function LCDdisplay()
lcd.clear()
if LCDpageID == 1 then
lcd.put(lcd:locate(0, 0),"Mast Temp: ")
lcd.put(lcd:locate(1, 0),"Shade Tmp: ")
elseif LCDpageID == 2 then
lcd.put(lcd:locate(0, 0),"Wind Dir: ")
lcd.put(lcd:locate(1,0),"Wind Knts: ")
elseif LCDpageID == 3 then
lcd.put(lcd:locate(0, 0),"Pressure: ")
lcd.put(lcd:locate(1, 0),"BMP Temp: ")
elseif LCDpageID == 4 then
lcd.put(lcd:locate(0, 0),"DHT Temp: ")
lcd.put(lcd:locate(1, 0),"Humidity: ")
else
lcd.put(lcd:locate(0, 0),"SCREEN ERROR 1")
end --if
--updateLCDDisplay()
collectgarbage()
end --LCDdisplay
function updateLCDDisplay()
if LCDpageID == 1 then
lcd.put(lcd:locate(0, 11),dataTable[0])
-- LCDScreenOP.lcdprint(dataTable[0],1,11)
-- LCDScreenOP.lcdprint(dataTable[7],2,11)
elseif LCDpageID == 2 then
--LCDScreenOP.lcdprint(dataTable[2],1,10)
-- LCDScreenOP.lcdprint(dataTable[3],1,14)
-- LCDScreenOP.lcdprint(dataTable[1],2,11)
elseif LCDpageID == 3 then
-- LCDScreenOP.lcdprint(dataTable[5],1,10)
-- LCDScreenOP.lcdprint(dataTable[4],2,10)
elseif LCDpageID == 4 then
--LCDScreenOP.lcdprint(dataTable[6],1,10)
-- LCDScreenOP.lcdprint(dataTable[7],2,10)
else
-- LCDScreenOP.cls()
-- LCDScreenOP.cursor(0)
-- LCDScreenOP.lcdprint("U/D ERROR",1,0)
end --if
-- package.loaded.LCDScreenOP = nil
DbugGarbageCollect()
end -- updateDisplay
function LCDScreenChange(level)
LCDpageID = LCDpageID + 1
if LCDpageID == LCDmaxScreen + 1 then LCDpageID = 1 end
LCDdisplay()
end-- buttonpress
--============================ CODE ==============================
i2c.setup(0, 3, 4, i2c.SLOW)
lcd = dofile("lcd.lua")()
print ("here")
gpio.mode(WDPin, gpio.OUTPUT)
gpio.write(WDPin, gpio.HIGH)
gpio.mode(LCDscreenPin, gpio.INPUT, gpio.PULLUP)
DbugGarbageCollect()
gpio.trig(LCDscreenPin, "down",LCDScreenChange)
tmr.alarm(2,1500,1,GetData)
and a screen grab of the esplorer window looks like this when loading GetServerData and allowing it to run
<pre>
abort = true
startup aborted
=node.heap()
>**32616**
file.remove("GetServerData.lua");
file.open("GetServerData.lua","w+");
w = file.writeline
-- Above code in here !!
file.close();
dofile("GetServerData.lua");
>not enough memory
dofile('GetServerData.lua')
not enough memory
=node.heap()
>**18800**
dofile('GetServerData.lua')
>not enough memory
</pre>
ANY help would be gratefully received and any other info that may help will be happily supplied
Many Thanks
Philip
That's quite a lot of code to comb through. It'd be a more helpful if you provided a Minimal, Complete, and Verifiable example which will reproduce the problem (-> Minimal).
A quick glance revealed 2.5 issues but there are likely some more.
Closed upvalues
conn:on("receive",function(conn,payload) leaks because the callback parameter shouldn't be conn but something else instead. See https://github.com/nodemcu/nodemcu-firmware/issues/194#issuecomment-184817667 or https://stackoverflow.com/a/37379426/131929
Sending is asynchronous
conn:send shouldn't be called in quick succession because each of those calls is asynchronous and thus the invocation order is not guaranteed. See the net.socket:send documentation for details and a nice example.
Consider http module
Using http.get may help you reduce the complexity in your code.

The wifi.sta module connects if a loop is running?

Im trying to detect when the module actually connects to my wifi AP, since .connect does not have a callback im doing something simple like this:
wifi.sta.config("SSID","password")
wifi.sta.connect()
tmr.delay(1000000)
i = 0
while(wifi.sta.status() ~= 5 and i < 10) do
print("Waiting")
print(wifi.sta.status())
i = i + 1
tmr.delay(1000000)
end
But the output of .sta.status() is always 1 inside the loop. When it finish, if I send the command =wifi.sta.status() manually from the IDE it tells me 5. Why?
If you use a recent dev firmware you can do something really event based :
wifi.setmode(wifi.STATION)
wifi.sta.config(SSID, PASSWORD)
function Success()
tmr.stop(0)
if (SERIAL_PRINT) then
print("IP: " .. wifi.sta.getip())
end
wifi.sta.eventMonStop()
wifi.sta.eventMonReg(wifi.STA_GOTIP, "unreg")
dofile("mainProgram.lua")
end
function Failure()
if (SERIAL_PRINT) then
print("Unable to connect")
end
wifi.sta.eventMonStop()
wifi.sta.eventMonReg(wifi.STA_GOTIP, "unreg")
return 0
end
tmr.alarm(0,30000,0, function() Failure() end)
wifi.sta.connect()
wifi.sta.eventMonReg(wifi.STA_GOTIP, function() Success() end)
wifi.sta.eventMonStart()
EDIT: Please have a look to the documentation for a list of all events. If you wish to use this code, you'll have to handle the failure more cleanly.
Using tmr.delay doesnot let run the event loop, you should use a timer callback.
Then the code could be something like :
wifi.sta.config("SSID","password")
wifi.sta.connect()
i=0
tmr.alarm(1, 1000, 1, function()
if (wifi.sta.status() ~= 5 and i < 10) then
print("Status:"..wifi.sta.status())
i = i + 1
else
tmr.stop(1)
if (wifi.sta.status() == 5) then
print("IP:"..wifi.sta.getip())
else
print("Status:"..wifi.sta.status())
end
end
end)

Resources