ESP8266 NodeMCU Running Out of Heap Memory

时光怂恿深爱的人放手 提交于 2019-11-26 09:58:13

问题


I am trying to toggle an LED using ESP8266-01 by sending POST from my laptop (using node.js)

I now have a memory issue because whenever I send POST request, the memory used in the ESP increases, and heap memory decreases, and it crashes (restart) when theres no memory left.

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 the node.js:

var request = require(\'request\');
// Configure request
var options = {
    url: \'http://192.168.1.91\',//ESP\'s 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 is just connecting to Wifi.

Thanks for your help!

Rey


回答1:


There was a problem in the NodeMCU docs with the socket:send example you seem to have based your implementation on. We discussed it and I fixed it.

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 duplicated code wrt populating message and I also remove the "resetting" and GC code at the end (no longer relevant). The real issue though was with closed upvalues in the callback functions.

Each of your callback functions should use its own copy of the passed socket instance rather referencing the one of a wrapping callback function.

  • On line 5 srv:listen(80, function(conn) the socket variable in the callback is conn.
  • On line 6 there's another callback function which receives a socket, this time called sck. It should be referenced within that function as sck (sck:on() and send(sck)).
  • The socket:on("sent") callback itself receives a/the socket instance. Your original send() function didn't not use that though and used conn instead. So, I added sk and use this one exclusively within send().



回答2:


Your on sent callback should accept one parameter, a connection. And you should setup the on sent handler at the same level as on receive -- conn that's passed to on receive is not necessarily the same conn passed to srv:listen.

Lastly, redundant copies of string literals is a waste of memory, (though that likely won't cause it to leak.)




回答3:


So the solution from Marcel worked.

Here is just another option to solve 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)


来源:https://stackoverflow.com/questions/37315548/esp8266-nodemcu-running-out-of-heap-memory

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!