nodemcu lua dofile - how to invoke another lua file? - lua

I have a working init.lua on my nodemcu esp8266:
-- load credentials, 'SSID' and 'PASSWORD' declared and initialize in there
dofile("credentials.lua")
function startup()
if file.open("init.lua") == nil then
print("init.lua deleted or renamed")
else
print("Running")
file.close("init.lua")
-- the actual application is stored in 'application.lua'
-- dofile("application.lua")
end
end
print("Connecting to WiFi access point...")
wifi.setmode(wifi.STATION)
wifi.sta.config(SSID, PASSWORD)
-- wifi.sta.connect() not necessary because config() uses auto-connect=true by default
tmr.create():alarm(1000, tmr.ALARM_AUTO, function(cb_timer)
if wifi.sta.getip() == nil then
print("Waiting for IP address...")
else
cb_timer:unregister()
print("WiFi connection established, IP address: " .. wifi.sta.getip())
print("You have 3 seconds to abort")
print("Waiting...")
tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup)
end
end)
It runs without errors and the wireless connection is established.
Now I have written a second bme280_mqtt.lua that I want to run automatically:
alt=45 -- altitude of the measurement place
bme280.init(3, 4)
P, T = bme280.baro()
-- convert measure air pressure to sea level pressure
QNH = bme280.qfe2qnh(P, alt)
ldk=string.format("Ld=%d.%03d ", QNH/1000, QNH%1000)
H, T = bme280.humi()
if T<0 then
temp=string.format("T=-%d.%02d°C ", -T/100, -T%100)
else
temp=string.format("T=%d.%02d°C ", T/100, T%100)
end
luftf=string.format("Lf=%d%% ", H/1000, H%1000)
D = bme280.dewpoint(H, T)
if D<0 then
taupu=string.format("Tp=-%d.%02d°C ", -D/100, -D%100)
else
taupu=string.format("Tp=%d.%02d°C ", D/100, D%100)
end
m = mqtt.Client("wetterstation", 120)
m:connect("192.168.1.116", 1883)
m:publish("wetterstation",temp .. taupu .. luftf .. ldk,0,0)
node.dsleep(10*1000000)
Called by hand in ESPlorer via Send to ESP Button everything works fine.
But with
dofile(bme280_mqtt.lua)
I get:
dofile('bme280_mqtt.lua')
bme280_mqtt.lua:25: not connected
stack traceback:
[C]: in function 'publish'
bme280_mqtt.lua:25: in main chunk
[C]: in function 'dofile'
stdin:1: in main chunk
What is the mistake here? And how do I call the bme280_mqtt.lua from init.lua correctly?
Kind regards

how do I call the bme280_mqtt.lua from init.lua correctly?
You do invoke it correctly.
bme280_mqtt.lua:25: not connected
Means that there's an error on/from line 25 from bme280_mqtt.lua.
I didn't count the lines but the problem is right here
m:connect("192.168.1.116", 1883)
m:publish("wetterstation",temp .. taupu .. luftf .. ldk,0,0)
You can only publish once the connection to the broker was established. Look at the example at http://nodemcu.readthedocs.io/en/latest/en/modules/mqtt/#example. You can either use the callback function in the connect function to publish or register an on-connect event handler before you call connect like so:
m:on("connect", function(client)
-- publish here
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)

ESP8266 Tcp-to-uart with reprogramming ability

I'm using this example from nodemcu repo:
uart.setup(0,9600,8,0,1,0)
sv=net.createServer(net.TCP, 60)
global_c = nil
sv:listen(9999, function(c)
if global_c~=nil then
global_c:close()
end
global_c=c
c:on("receive",function(sck,pl) uart.write(0,pl) end)
end)
uart.on("data",4, function(data)
if global_c~=nil then
global_c:send(data)
end
end, 0)
But, since I'm using uart module, I'm no longer able to communicate with my chip via LuaLoader and can not upload updated init.lua files. Instead, I have to put chip into flash-upload mode, then flash initial nodemcu firmware and then my updated init.lua. Too much steps.
How can I retain ability to communicate via LuaLoader? I've tried something like this:
uart.on('data', '\n', handleUartResponse, 0)
...
...
function handleUartResponse(response)
if response == 'flash\n' then
g_flash = true
toggleOutput(true)
uart.write(0, 'flash mode')
elseif response == 'endflash\n' then
g_flash = false
uart.write(0, 'normal mode')
toggleOutput(false)
elseif g_flash then
node.input(response)
else
if g_conn ~= nil then
g_conn:send(response, function(sock)
closeConnection(sock)
g_conn = nil
end)
end
end
end
function toggleOutput(turnOn)
if turnOn then
node.output(nil, 1)
else
node.output(silent, 0)
end
end
It prints flash mode and normal mode in another serial terminal, but it doesn't work in LuaLoader. I think the problem is in uart setup, maybe it should not be \n, but other condition, I don't know what.
Got it! Not sure if it is the best option, since I'm new to lua, but it works.
function handleNormalMode(response)
if response == 'flash\r' then -- magic code to enter interpreter mode
toggleFlash(true)
else -- tcp-to-uart
if g_conn ~= nil then
g_conn:send(response, function(sock)
closeConnection(sock)
g_conn = nil
end)
end
end
end
function ignore(x)
end
function uartSetup(echo)
uart.setup(0, 115200, 8, 0, 1, echo)
end
function toggleFlash(turnOn)
if turnOn then
uart.on('data') -- unregister old callback
uartSetup(1) -- re-configure uart
uart.on('data', 0, ignore, 1) -- this allows lua interpreter to work
node.output(nil) -- turn on lua output to uart
uart.write(0, 'flash mode') -- notify user
else
node.output(ignore, 0) -- turn off lua output to uart
uart.on('data') -- unregister old callback
uartSetup(0) -- re-configure uart
uart.on('data', '\r', handleNormalMode, 0) -- turn on tcp-to-uart
uart.write(0, 'normal mode') -- notify user
end
end
I'm calling toggleFlash(false) at the beginning of script. Then, typing flash\r enters lua interpreter mode and to switch back I'm just typing toggleFlash(false) and it works! That simplifies updating init.lua every time.

Asterisk Hangup Handler using lua

I am trying to Add a hangup handler in my dial plan to catch unanswered calls from dial with multiple extensions. But unfortunately I my hangup handlers are not executing at all.
Below is my extensions.lua
CONSOLE = "Console/dsp" -- Console interface for demo
IAXINFO = "guest" -- IAXtel username/password
TRUNK = "SIP"
GSMTRUNK = "Dongle/dongle0/"
PRITRUNK = "DAHDI/g0/"
TRUNKMSD = 1
local inspect = require ('inspect')
--from here onwards our logic is there Rajesh
function dialtomobind(number)
app.Dial(GSMTRUNK..number, 50,'egb(add-hangup-handler^'..number..'^1)')
local dialstatus = channel['DIALSTATUS']:get()
app.NoOp('DIAL STATUS:'..dialstatus)
app.Hangup(16)
end
function add_hangup_handler(context, extension)
channel['hangup_handler_push']='hangup-handlers,s,1'
app.NoOp('pre-dial handler, Adding Hangup Handler'..channel['hangup_handler_push']:get())
app.Return()
end
function h_handler(context, extension)
app.NoOp('After Hangup:'..extension)
--app.DumpChan(3)
end
function directdialing(c,e)
app.Answer(5)
app.Playback('tt-monkeys')
dialtomobind(string.sub(e, 4))
app.Hangup(16)
end
function hangup_handler_1(c,e)
--channel['hangup_handler_push']
app.NoOp('Hangup Handler 1 Executed')
app.Return()
end
extensions = {
['from-internal-sip']={
['h']=h_handler;
['_500XXXXXXXXXX']=directdialing;
};
['dongle_incoming']={
['_.']=gsm_dongle_handler;
};
['add-hangup-handler']={
['_.']=add_hangup_handler;
};
['hangup-handlers']={
['_.']=hangup_handler_1;
};
}
ASTERISK CLI OUT PUT:
-- Executing [5008884882772#from-internal-sip:1] Answer("SIP/8884882772-00000047", "5")
> 0x7f14c80072a0 -- Probation passed - setting RTP source address to 192.168.2.30:54032
[Aug 29 18:24:17] NOTICE[26470][C-000000c5]: res_rtp_asterisk.c:4478 ast_rtp_read: Unknown RTP codec 95 received from '192.168.2.30:54032'
-- Executing [5008884882772#from-internal-sip:1] Playback("SIP/8884882772-00000047", "tt-monkeys")
-- <SIP/8884882772-00000047> Playing 'tt-monkeys.gsm' (language 'en')
-- Executing [5008884882772#from-internal-sip:1] Dial("SIP/8884882772-00000047", "Dongle/dongle0/8884882772,50,egb(add-hangup-handler^8884882772^1)")
-- Dongle/dongle0-010000000e Internal Gosub(add-hangup-handler,8884882772,1) start
-- Executing [8884882772#add-hangup-handler:1] NoOp("Dongle/dongle0-010000000e", "pre-dial handler, Adding Hangup Handlerhangup-handlers,s,1")
-- Executing [8884882772#add-hangup-handler:1] Return("Dongle/dongle0-010000000e", "")
== Spawn extension (dongle_incoming, 5008884882772, 1) exited non-zero on 'Dongle/dongle0-010000000e'
-- Dongle/dongle0-010000000e Internal Gosub(add-hangup-handler,8884882772,1) complete GOSUB_RETVAL=
-- Called Dongle/dongle0/8884882772
-- Dongle/dongle0-010000000e is making progress passing it to SIP/8884882772-00000047
== Spawn extension (from-internal-sip, 5008884882772, 1) exited non-zero on 'SIP/8884882772-00000047'
-- Executing [h#from-internal-sip:1] NoOp("SIP/8884882772-00000047", "After Hangup:h")
Hangup handler is dialplan handler.
So if you want use it, start OTHER lua script at submited context/extension on hangup.
Extension hangup-handlers,s,1 have be valid and exist.
You have use
app.Set('CHANNEL(hangup_handler)=hangup-handlers,s,1');
becuase it is not variable, it is function.
hangup_handler_push is not at all a channel variable. So Can't access using below methods.
channel['hangup_handler_push']='hangup-handlers,s,1'
or
channel['hangup_handler_push']='hangup-handlers,s,1'
To setup a hangup handler we have to use dial plan function CHANNEL(https://wiki.asterisk.org/wiki/display/AST/Hangup+Handlers). So below methods will be working.
channel.CHANNEL('hangup_handler_push'):set('hangup-handlers,s,1')
As Asterisk Set Application Can be used to set a channel Variables as well as can call a asterisk Functions also. So below code also working.
app.Set('CHANNEL(hangup_handler)=hangup-handlers,s,1');
Reference:
Dial Plan Functions In Lua
https://wiki.asterisk.org/wiki/pages/viewpage.action?pageId=16548029

NodeMCU webserver closing connection after first send?

I have an small web server running on my ESP-12 with nodemcu firmware:
sv=net.createServer(net.TCP,10)
sv:listen(80,function(c)
c:on("receive", function(c, pl)
if(string.find(pl,"GET / ")) then
print("Asking for index")
c:send("Line 1")
c:send("Line 2")
c:send("Line 3")
c:close()
end
end)
c:on("sent",function(conn)
print("sended something...")
end)
end)
It seems my connection is getting closed after the first send, in my browser I only see the "line 1" text, line 2 a 3 does not appear, and in my serial console im just seeing the "sended something" text one time, even commenting the close statement and letting the connection to timeout does not change the behavior. What am I missing here?
I don't think that you can use send multiple times. Whenever I use one of my ESP8266 as a server I use a buffer variable :
sv=net.createServer(net.TCP,10)
-- 'c' -> connection, 'pl' -> payload
sv:listen(80,function(c)
c:on("receive", function(c, pl)
if(string.find(pl,"GET / ")) then
print("Asking for index")
local buffer = ""
buffer = buffer.."Line 1"
buffer = buffer.."Line 2"
buffer = buffer.."Line 3"
c:send(buffer)
c:close()
end
end)
c:on("sent",function(c)
print("sended something...")
end)
end)
EDIT: After reading the docs again, send can take another argument with a callback function, it can maybe be used to have multiple send command. Never tried it though :(.
EDIT 2: If you have a really long string to send, it's better to use table.concat
The net.socket:send() documentation provides a nice example which I repeat here.
srv = net.createServer(net.TCP)
function receiver(sck, data)
local response = {}
-- if you're sending back HTML over HTTP you'll want something like this instead
-- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"}
response[#response + 1] = "lots of data"
response[#response + 1] = "even more data"
response[#response + 1] = "e.g. content read from a file"
-- sends and removes the first element from the 'response' table
local function send(localSocket)
if #response > 0 then
localSocket:send(table.remove(response, 1))
else
localSocket:close()
response = nil
end
end
-- triggers the send() function again once the first chunk of data was sent
sck:on("sent", send)
send(sck)
end
srv:listen(80, function(conn)
conn:on("receive", receiver)
end)

nodemcu how to cut down the time required to acquire ip address

With NodeMCU in station mode with the following snippet of code in init.lua it still takes on average about 6 iterations of the loop before an IP address is reported (or IP stack ready state is achieved)
wifi.sta.disconnect()
--settings.lua
SSID = "xxxx"
APPWD = "yyyy"
cfg =
{
ip="192.168.0.85",
netmask="255.255.255.0",
gateway="192.168.0.1"
}
wifi.sta.setip(cfg)
wifi.sta.config(SSID,APPWD)
wifi.sta.autoconnect(1)
-- wait for WIFI ----
function checkWIFI()
print("Waiting for WIFI...")
ipAddr = wifi.sta.getip()
if ( ( ipAddr ~= nil ) and ( ipAddr ~= "0.0.0.0" ) )then
print("IP Address: " ..ipAddr)
else
-- schedule try again
tmr.alarm( 0 , 1000 , 0 , checkWIFI)
end
end
tmr.alarm( 0 , 1000 , 0 , checkWIFI)
Tried with and without static IP configuration, seems no different
Is this normal?
Is there a way to make faster?
Am I just doing it wrong?
The following is my practical experience, which may be out of date now. I need to retest these...
I do a similar thing and it works well. However, while WiFi is not available quickly after a "reset", it is available very quickly on wakeup from deep sleep.
After first use (settings are saved automatically) I later simply do this
lua
wifi.sta.setip(cfg)
wifi.sta.status() -- this used to speed things up
I also set wifi.sta.autoconnect(0) to avoid dhcp delays.
BTW, to check for a connection it is best to wait for wifi.sta.status() == 5.
HTH
Based on #Eyal's answer, I was able to get an IP address typically in 415-470ms from deep sleep (based on the timer), or 3.25 seconds from cold power on.
NOTE: There is no DNS resolution with this solution
Save your network config to flash
wifi.sta.clearconfig()
wifi.setmode(wifi.STATION)
station_cfg={}
station_cfg.ssid="MyWiFiNetwork"
station_cfg.pwd="MyWiFiPassword"
station_cfg.save=true
station_cfg.auto=false
wifi.sta.config(station_cfg)
On every boot
cfg =
{
ip="192.168.0.99",
netmask="255.255.255.0",
gateway="192.168.0.1"
}
wifi.sta.setip(cfg)
wifi.sta.connect()
Test it with
function checkWIFI()
ipAddr = wifi.sta.getip()
if ( wifi.sta.status() == wifi.STA_GOTIP )then
--LED On
gpio.write(4, gpio.LOW)
gpio.mode(4, gpio.OUTPUT)
print("Time " .. tmr.now()/1000 .. "ms")
print("IP Address: " ..ipAddr)
else
-- schedule try again
tmr.create():alarm(50, tmr.ALARM_SINGLE, checkWIFI)
end
print("WiFi Status " .. wifi.sta.status())
end
tmr.create():alarm(50, tmr.ALARM_SINGLE, checkWIFI)

Resources