Reputation: 471
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<[email protected]> - 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
Upvotes: 0
Views: 1014
Reputation: 10888
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.
Upvotes: 2
Reputation: 471
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.
Upvotes: 0