I want to turn on and off a led from a web server using NodeMCU but every time I compile the same error occurs. I am in a beginner level so I need some help. I believe this error has something to do with the function listen(). Maybe because I changed my DNS, the port 80 can't be used.
Error:
dofile("ConectarRedeWireless.lua");
192.168.137.57 255.255.255.0 192.168.137.1
ConectarRedeWireless.lua:13: address in use
stack traceback:
[C]: in function 'listen'
ConectarRedeWireless.lua:13: in main chunk
[C]: in function 'dofile'
stdin:1: in main chunk
Code:
-- Conexao na rede Wifi
wifi.setmode(wifi.STATION)
wifi.sta.config("goncalo","936674888")
print(wifi.sta.getip())
-- Definicoes do pino do led
led1 = 1
gpio.mode(led1, gpio.OUTPUT)
-- Definicoes do Web Server
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
conn:on("receive", function(client,request)
local buf = "";
local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
if(method == nil)then
_, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
end
local _GET = {}
if (vars ~= nil)then
for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
_GET[k] = v
end
end
buf = buf.."<h1><u>FILIPEFLOP</u></h1>";
buf = buf.."<h2><i>ESP8266 Web Server</i></h2>";
buf = buf.."<p><button><b>LED 1 LIG</b></button> <br/><br/><button><b>LED 1 DES</b></button></p>";
local _on,_off = "",""
if(_GET.pin == "LIGA1")then
gpio.write(led1, gpio.HIGH);
elseif(_GET.pin == "DESLIGA1")then
gpio.write(led1, gpio.LOW);
end
client:send(buf);
client:close();
collectgarbage();
end)
end)
First of all I suggest you use a proper init sequence in which you wait for your device to be fully connected to the WiFi until you set up the server. We keep a template in our documentation.
srv:listen(80,function(conn) can only be called once for any given port because you can't have multiple server sockets listening on the same port. That's why you get the
address in use
error. Our net.server:listen() documentation shows how to check whether srv is available before you listen. You can extend that by running srv:close() first before you listen again. Something like this:
sv = net.createServer(net.TCP, 30)
if sv then
sv:close()
sv:listen(80, function(conn)
-- do something
end)
end
Btw, where did you get that code sample from? I've seen this several times here on Stack Overflow but it's got more flaws that should be corrected.
Related
I wanted it to teleport to a private server but it wont teleport and it doesn't show any errors.
Here's the code:
local TeleportService = game:GetService("TeleportService")
local Players = {}
local GamePlayers = game:GetService("Players")
local IsTeleporting = false
local PlayersAllowed = script.Parent.Lobby.Teleporter.MaxPlayers
local function Teleport()
if #Players > 0 then
local TeleportPlayers = {}
for i = 1, #Players do
local I = i
if game.Players:FindFirstChild(Players[i]) then
table.insert(TeleportPlayers, GamePlayers:FindFirstChild(Players[i]))
TransitionEvent:FireClient(GamePlayers:FindFirstChild(Players[i]))
else
table.remove(Players, i)
end
end
wait(0.5)
IsTeleporting = true
pcall(function()
TeleportService:TeleportPartyAsync(TeleportID, TeleportPlayers)
end)
wait(0.5)
IsTeleporting = false
end
end
Any help would be appreciated!
it doesn't show any errors.
pcall(function()
print("function called")
error("an error occurred!")
print("this will not be reached")
end)
This will print "function called". Nothing else. No error shown.
From the Lua 5.4 Reference Manual:
pcall (f [, arg1, ···])
Calls the function f with the given arguments in protected mode. This
means that any error inside f is not propagated; instead, pcall
catches the error and returns a status code. Its first result is the
status code (a boolean), which is true if the call succeeds without
errors. In such case, pcall also returns all results from the call,
after this first result. In case of any error, pcall returns false
plus the error object. Note that errors caught by pcall do not call a
message handler.
Check pcall's return values to see if the function ran successfully.
Compare your code to Roblox's example:
local Players = game:GetService("Players")
local TeleportService = game:GetService("TeleportService")
local placeId = 0 -- replace
local playerList = Players:GetPlayers()
local success, result = pcall(function()
return TeleportService:TeleportPartyAsync(placeId, playerList)
end)
if success then
local jobId = result
print("Players teleported to "..jobId)
else
warn(result)
end
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.
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/.
I'm quite new to Lua. I have tried to solve the following problem on my own but I couldn't figure it out. Here is my issue:
I'm running a web server on my NodeMCU devkit v0.9. I can easily access the web server on my own computer, but if I try to access the server over my iPhone or using the requests module in Python, I always get the message The network connection was lost. I'm using the following firmware: nodemcu_float_0.9.6-dev_20150704.
print(wifi.sta.getip())
led1 = 3
led2 = 4
gpio.mode(led1, gpio.OUTPUT)
gpio.mode(led2, gpio.OUTPUT)
srv=net.createServer(net.TCP,30)
srv:listen(80,function(conn)
conn:on("receive", function(client,request)
local buf = "";
local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
if(method == nil)then
_, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
end
local _GET = {}
if (vars ~= nil)then
for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
_GET[k] = v
end
end
buf = buf.."<h1> ESP8266 Web Server</h1>";
buf = buf.."<p>GPIO0 <button>SWITCH LIGHTS</button></p>";
buf = buf.."<p>"..gpio.read(led1).."</p>";
local _on,_off = "",""
if(_GET.pin == "led1")then
if(gpio.read(led1) == 1) then
gpio.write(led1, gpio.LOW);
else
gpio.write(led1, gpio.HIGH);
end
else end
client:send(buf);
client:close();
end)
end)
I was able to solve this problem by added some information to the beginning of the html buffer. Replace:local buf='' with local buf="HTTP/1.1 200 OK\r\nContent-type: text/html\r\nConnection: close\r\n\r\n"
I guess desktop browsers are just more forgiving in this respect.
I have a completely working esp chip that connects to wifi and creates a server. When I send it an OTA command, it runs a function that downloads a file using a socket connection.
This is the upgrader.lua that I am using:
--------------------------------------
-- Upgrader module for NODEMCU
-- LICENCE: http://opensource.org/licenses/MIT
-- cloudzhou<wuyunzhou#espressif.com> - Heavily modified by aschmois
--------------------------------------
--[[
update('file.lua', 'http://IP.ADRESS/path/file.lua')
]]--
local header = ''
local isTruncated = false
local function save(filename, response)
if isTruncated then
file.write(response)
return
end
header = header..response
local i, j = string.find(header, '\r\n\r\n')
if i == nil or j == nil then
return
end
prefixBody = string.sub(header, j+1, -1)
file.write(prefixBody)
header = ''
isTruncated = true
return
end
----
function update(filename, url, cn)
local tmpError = nil
local running = true
local error = nil
local success = false
print("Downloading from: " .. url)
local ip, port, path = string.gmatch(url, 'http://([0-9.]+):?([0-9]*)(/.*)')()
if ip == nil then
return false
end
if port == nil or port == '' then
port = 80
end
port = port + 0
if path == nil or path == '' then
path = '/'
end
print("-- Detailed Connection Info --")
print("IP: ".. ip)
print("Port: ".. port)
print("Path: ".. path)
print("-- END --")
local function timeout()
error = tmpError
file.remove(filename)
conn:close()
running = false
end
conn = net.createConnection(net.TCP, false)
conn:on('connection', function(sck, response)
tmr.stop(1)
file.open(filename, 'w')
conn:send('GET '..path..' HTTP/1.0\r\nHost: '..ip..'\r\n'..'Connection: close\r\nAccept: */*\r\n\r\n')
tmpError = "READ TIMEOUT"
tmr.alarm(1, 10000, 0, timeout)
end)
conn:on('receive', function(sck, response)
tmr.stop(1)
tmpError = "READ(2) TIMEOUT"
tmr.alarm(1, 10000, 0, timeout)
print(response)
save(filename, response)
end)
conn:on('disconnection', function(sck, response)
tmr.stop(1)
local function reset()
local list = file.list()
for k,v in pairs(list) do
if(filename == k) then
if(v == 0) then
success = false
file.close()
file.remove(filename)
else
file.close()
success = true
end
end
end
print(header)
header = ''
isTruncated = false
if(success) then
print(filename..' saved')
else
print("Could not download `".. filename.."`")
end
running = false
end
tmr.alarm(0, 2000, 0, reset)
end)
conn:connect(port, ip)
tmpError = "CONN TIMEOUT"
tmr.alarm(1, 10000, 0, timeout)
tmr.alarm(2, 1000, 1, function()
if(running == false) then
tmr.stop(2)
local buf = ''
if(success) then
buf = buf.."HTTP/1.1 200 OK\r\nServer: WiFi Relay\r\nContent-Type: text/plain\r\n\r\n"
buf = buf.."1"
else
buf = buf.."HTTP/1.1 500\r\nServer: WiFi Relay\r\nContent-Type: text/plain\r\n\r\n"
buf = buf.."0"
buf = buf.."\n"
if(error ~= nil) then
buf = buf..error
else
buf = buf.."UNKNOWN ERROR"
end
end
cn:send(buf)
cn:close()
end
end)
return true
end
As a test I am sending it: filename = rz.lua and url = http://192.168.1.132/rz.lua. The cn variable is the connection to send back information to the client.
The esp chip prints:
Downloading from: http://192.168.1.132/rz.lua
-- Detailed Connection Info --
IP: 192.168.1.132
Ò_ÇRöfJSúfÊÃjêÐÿ (junk reset data)
The problem seems to be connected with the conn:send() command. If it's inside the on connect function it resets. If it's outside, I will get a read timeout (since on read is never called). I really have no idea what else to do.
This is the ESP firmware info:
NodeMCU custom build by frightanic.com
branch: master
commit: 93421f2702fb02ce169f82f96be7f2a8865511e1
SSL: false
modules: node,file,gpio,wifi,net,tmr,uart
You are resetting. The "junk" is a BootROM message at the wrong baud rate.
Don't do a send followed by a close in the same callback. Use an on('sent', ... ) to trigger the close. So the 21 line body of your alarm 2 callback would be better written:
local response = "HTTP/1.1 200 OK\r\nServer: WiFi Relay\r\nContent-Type: text/plain\r\n\r\n%s"
cn:send(response:format(success and "1" or ("0\n\r" .. (error or "UNKNOWN ERROR")))
cn:on('sent', function(cn) cn:close() end)
On that note your 27 line disconnect callback would be better written:
tmr.stop(1)
tmr.alarm(0, 2000, 0, function()
local len = file.list()(filename)
success = len and len > 0
file.close()
if not success then file.remove(filename)
file.flush()
end)
Note that it's always wise to flush the SPIFFS after writing or removing files.
You use a standard pattern, so why not encapsulate it:
local conn = net.createConnection(net.TCP, false)
local function setTimeout(reason)
-- tmr.stop(1) -- not needed is the next line resets the alarm
tmr.alarm(1, 10000, 0, function ()
-- you don't need tmpError as reason is a local and bound as an upval
error, running = reason, false
file.remove(filename) file.flush()
return conn:close()
end)
end
I could go on but I leave this to you. With a little thought, your code would be a third of the size and more readable.
I can't be sure but the problem seems to have been a memory error (weird since there was no panic) so this is what I did to fix it:
local request = table.concat({"GET ", path,
" / HTTP/1.1\r\n",
"Host: ", ip, "\r\n",
"Connection: close\r\n",
"Accept: */*\r\n",
"User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)",
"\r\n\r\n"})
conn = net.createConnection(net.TCP, false)
conn:on('connection', function(sck, response)
tmr.stop(1)
tmpError = "READ TIMEOUT"
tmr.alarm(1, 10000, 0, timeout)
conn:send(request)
end)
I created the request using the table.concat method and using a table instead of one big string. Hopefully this will help those in need.