I am trying to just make a connection from the ESP8266 and a test server like www.httpbin.org to get some data. I keep having problems in the connection.
I am using a very simple code that (excluding the obvious connection to the local WiFi) looks like:
net.dns.setdnsserver("8.8.8.8",1)
net.dns.resolve("httpbin.org", function(server_ip)
if (server_ip == nil) then print("DNS fail!") else
print(server_ip)
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload)
print('\nReceived\n')
print(payload)
end )
conn:on("connection", function(conn, payload)
print('\nConnected')
conn:send("HTTP/1.1\r\n"
.."Host: httpbin.org/get\r\n"
.."Connection: keep-alive\r\n"
.."Accept: */*\r\n\r\n")
end)
conn:connect(80, server_ip)
end
end)
I get as result that the print(server_ip) returns
userdata: 3fff1b80
And the connection hangs (I never get any message printed). What am I doing wrong? Thanks.
You were nearly there. Try this:
net.dns.setdnsserver("8.8.8.8", 1)
net.dns.resolve("httpbin.org", function(sk, server_ip)
if (server_ip == nil) then print("DNS fail!") else
print(server_ip)
conn = net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload)
print("Received: " .. payload)
end)
conn:on("connection", function(conn, payload)
print("Connected")
conn:send("GET /get HTTP/1.1\r\n"
.. "Host: httpbin.org\r\n"
.. "Connection: keep-alive\r\n"
.. "Accept: */*\r\n\r\n")
end)
conn:connect(80, server_ip)
end
end)
I changed two things:
as per the docs the callback for net.dns.resolve has 2 parameters (socket and IP address)
the HTTP headers were wrong:
host really is just the host
but the HTTP verb and the path were missing, it's GET /get HTTP/1.1
Of course you may just as well strip this down to:
conn = net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload)
print("Received: " .. payload)
end)
conn:on("connection", function(conn, payload)
print("Connected")
conn:send("GET /get HTTP/1.1\r\n"
.. "Host: httpbin.org\r\n"
.. "Connection: keep-alive\r\n"
.. "Accept: */*\r\n\r\n")
end)
conn:connect(80, "httpbin.org")
Related
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.
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.
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.
here is my lua script ,running on esp8266 with nodemcu firmware,I am keeping a function check_wifi() to check that my esp module is connected ,then I am publishing topic "on" , and setting two event handler for "connect" and for "message",but my handler for
function check_wifi()
local ip = wifi.sta.getip()
if(ip==nil) then
print("Connecting...")
else
tmr.stop(0)
print("Connected to AP!")
print(ip)
m:connect("test.mosquitto.org", 1883, 0, function(conn) print("connected") end)
m:publish("on","1",0,0,0)
m:on("connect",function(m)
m:subscribe("on",0,function(m) print(m);print("subcribed") end)
end )
m:on("message", function(conn, topic, data)
print("in on")
print(topic .. ":" )
if data ~= nil then
print(data)
end
end)
end
end
local SSID = "Crystalrock2"
local SSID_PASSWORD = "nest2abc123"
gpio.mode(4,gpio.OUTPUT)
gpio.write(4,gpio.LOW)
-- configure ESP as a station
wifi.setmode(wifi.STATION)
wifi.sta.config(SSID,SSID_PASSWORD)
wifi.sta.connect()
m = mqtt.Client("dee15", 120, "test", "password")
m:on("message", function(conn, topic, data)
print("in on")
print(topic .. ":" )
if data ~= nil then
print(data)
end
end)
m:on("connect",function(m)
m:subscribe("on",0,function(m) print(m);print("subcribed") end)
end )
m:connect("test.mosquitto.org", 1883, 0, function(conn) print("connected") end)
tmr.alarm(0,2000,1,check_wifi)
here what I am getting:
output:Connecting...
Connecting...
Connected to AP!
192.168.0.100
userdata: 3fff8678
subcribed
why m:on("message" ..)is not triggering and printing message received
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.