ESP8266 NodeMCU Run from heap memory
I am trying to switch an LED using an ESP8266-01 by sending a POST from my laptop (using node.js)
I now have a memory problem because whenever I send a POST request, the memory used in ESP increases and the heap memory decreases and it crashes (restarts) when there is no memory.
any thoughts?
Here is my code on the ESP side (main.lua):
gpio.mode(3, gpio.OUTPUT)
srv=net.createServer(net.TCP,28800)
print("Server created... \n")
local pinState=0
srv:listen(80,function(conn)
conn:on("receive", function(conn,request)
local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
if(method == nil)then
_, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
end
local message={}
print("Method:"..method);
if(method == "POST")then
if(pinState==0)then
gpio.write(3,gpio.HIGH)
pinState=1
print("LED ON")
message[#message + 1] = "HTTP/1.1 200 OK\r\n"
message[#message + 1] = "Content-Type: text/html\r\n\r\n"
message[#message + 1] = "POST request successfully received\r\n"
elseif(pinState==1)then
gpio.write(3,gpio.LOW)
pinState=0
print("LED OFF")
message[#message + 1] = "HTTP/1.1 200 OK\r\n"
message[#message + 1] = "Content-Type: text/html\r\n\r\n"
message[#message + 1] = "POST request successfully received\r\n"
end
elseif(method == "GET")then
message[#message + 1] = "HTTP/1.1 200 OK\r\n"
message[#message + 1] = "Content-Type: text/html\r\n\r\n"
message[#message + 1] = "LED STATE="..tostring(pinState).."\r\n"
end
local function send()
if #message > 0 then
conn:send(table.remove(message, 1))
else
conn:close()
end
end
conn:on("sent", send)
send()
local message={}
local _, _, method, path, vars= {}
local heapSize=node.heap()
if heapSize<1000 then
node.restart()
end
collectgarbage()
print("Memory Used:"..collectgarbage("count"))
print("Heap Available:"..heapSize)
end)
end)
On node.js:
var request = require('request');
// Configure request
var options = {
url: 'http://192.168.1.91',//ESP IP address
method: 'POST'
}
// Start the request
request(options, function (error, response, body)
{
if(!error)
{
return console.log('Server responded with:',body);
}
if(error)
{
return console.error('ERROR:', error);
}
})
my init.lua just connects to wifi.
Thank you for your help!
Rey
source to share
An improved version of your code is this:
gpio.mode(3, gpio.OUTPUT)
srv = net.createServer(net.TCP, 28800)
print("Server created... \n")
local pinState = 0
srv:listen(80, function(conn)
conn:on("receive", function(sck, request)
local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
if (method == nil) then
_, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
end
local message = {}
message[#message + 1] = "HTTP/1.1 200 OK\r\n"
message[#message + 1] = "Content-Type: text/html\r\n\r\n"
print("Method:" .. method);
if (method == "POST") then
message[#message + 1] = "POST request successfully received\r\n"
if (pinState == 0) then
gpio.write(3, gpio.HIGH)
pinState = 1
print("LED ON")
elseif (pinState == 1) then
gpio.write(3, gpio.LOW)
pinState = 0
print("LED OFF")
end
elseif (method == "GET") then
message[#message + 1] = "LED STATE=" .. tostring(pinState) .. "\r\n"
end
local function send(sk)
if #message > 0 then
sk:send(table.remove(message, 1))
else
sk:close()
message = nil
print("Heap Available:" .. node.heap())
end
end
sck:on("sent", send)
send(sck)
end)
end)
I removed some duplicate padding code message
and I also remove the "reset" and GC code at the end (no longer relevant). The real problem was the closed upvalues to the callback function.
Each of your callback functions should use its own copy of the passed socket instance, rather by referencing one of the callback functions.
- Line 5
srv:listen(80, function(conn)
shows the socket variable in the callbackconn
. - On line 6 there is another callback function that receives the socket, this time called
sck
. This function should be referred to assck
(sck:on()
andsend(sck)
). -
socket:on("sent")
the callback itself gets an instance / socket. Your original functionsend()
did not use this, it didconn
. So I addedsk
and only use this insend()
.
source to share
Your callback must have one parameter, connection. And you have to configure your sent sender handler at the same level as when you receive it - the conn that is sent to receive is not necessarily the same connection passed to srv: listen.
Finally, redundant copies of string literals are a waste of memory (although this probably won't leak it.)
source to share
So the solution from Marcel worked.
Here's another solution to the problem:
print("Starting main.lua... \n")
gpio.mode(3, gpio.OUTPUT)
srv=net.createServer(net.TCP,28800)
print("Server created... \n")
srv:listen(80,function(conn)
conn:on("receive", function(conn,request)
local _,_,method,path= string.find(request, "([A-Z]+) (.+)?(.+) HTTP")
local _,_,key,light_level = string.find(request, "(%a+)%s*:%s*(%d+)")
if(method == nil)then
_,_,method,path = string.find(request, "([A-Z]+) (.+) HTTP")
end
local duty=light_level*1023/100
pwm.setup(3, 500, duty)
local message={}
print("Level:"..light_level)
if(method == "POST")then --light_level was sent from node.js as the header of the request
if(duty>0)then
pwm.start(3)
message = {"HTTP/1.0 200 OK\r\n Content-Type: text/html\r\n\r\n"}
message[#message + 1] = (light_level/100)
elseif(duty==0)then
pwm.stop(3)
message = {"HTTP/1.0 200 OK\r\n Content-Type: text/html\r\n\r\n"}
message[#message + 1] = 0
end
elseif(method == "GET")then
message[#message + 1] = "HTTP/1.1 200 OK\r\n Content-Type: text/html\r\n\r\n"
message[#message + 1] = "LED STATE="..tostring(pinState).."\r\n"
end
local function send()
if #message > 0 then
conn:send(table.remove(message, 1))
else
conn:close()
conn = nil
collectgarbage()
end
end
conn:on("sent", send)
send()
local message = nil
local _,_,method,path = nil
local _,_,key,light_level = nil
local duty=nil
--for debugging
local heapSize=node.heap()
if heapSize<2000 then
node.restart()
end
print("Memory Used:"..collectgarbage("count"))
print("Heap Available:"..heapSize)
local heapSize=nil
--debugging end
end)
end)
source to share