0

I am facing some troubles with my NodeMCU, running a simple server in access point mode, programmed in LUA using ESPlorer.

Here is the LUA script :

local SSID = "NodeMCU"
local SSID_PASSWORD = "12345678"

function connect (conn)
   print ("Hello connect")
   conn:on ("receive",
       function (cn, req_data)
           print(req_data)
           print("Available memory :")
           print(node.heap())
           --local query_data = get_http_req (req_data)
           local query_data = {}

           cn:send("HTTP/1.1 200 OK\n\n", 
           function()
               cn:close()
               --collectgarbage()
           end)
      end)
end

-- Configure the ESP as a station (client)
wifi.setmode (wifi.SOFTAP)

cfg={}
cfg.ssid=SSID
cfg.pwd=SSID_PASSWORD
wifi.ap.config(cfg)

cfg={}
cfg.ip="192.168.1.1";
cfg.netmask="255.255.255.0";
cfg.gateway="192.168.1.1";
wifi.ap.setip(cfg)

print("Set up UART config")
uart.setup(1, 921600, 8, uart.PARITY_NONE, uart.STOPBITS_1, 1)

-- Create the httpd server
svr = net.createServer (net.TCP, 30)

-- Server listening on port 80, call connect function if a request is received
svr:listen (80, connect)

Once this program is running on NodeMCU, I connect my PC in WiFi to the NodeMCU, and I send it some http POST requests with this piece of Java code :

public void sendPost(RGBWPixel[][] LedMatrix) throws Exception {

    HttpURLConnection con = (HttpURLConnection) obj.openConnection();

    System.out.println("Sending post");
    // add reuqest header
    con.setRequestMethod("POST");
    con.setRequestProperty("matrixValues", new String(convertTo1DCharArray(LedMatrix)));
    con.setDoOutput(true);


    DataOutputStream wr = new DataOutputStream(con.getOutputStream());
    wr.flush();
    wr.close();

    con.getResponseCode();

}

obj is the URLconnection corresponding to the NodeMCU IP adress. The String corresponding to matrixValues always has a 2050 length.

Please notice that I reduced the LUA srcipt to the minimal functions that make the problem happend. More exactly, it happens when I had the cn:send() part, but I don't know if it is possible to receive and process the request without sending a response, because the request is not sent when I don't run the con.getResponseCode() from the Java program. I am a beginner with http, so I don't understand all the protocols yet.

Here is how the output looks like from the NodeMCU side, in ESPlorer :

> dofile("init.lua");
Set up UART config
> Hello connect
POST / HTTP/1.1
matrixValues: 
Available memory :
38088

Available memory :
37032
Hello connect
POST / HTTP/1.1
matrixValues: 
Available memory :
37688

Available memory :
36664
Hello connect
POST / HTTP/1.1
matrixValues: 
Available memory :
37440

Available memory :
36264

And after a few dozens of iterations, this happend and the NodeMCU restarts :

Hello connect
POST / HTTP/1.1
matrixValues: 
Available memory :
4680

Available memory :
3600
E:M 1584
PANIC: unprotected error in call to Lua API (init.lua:19: out of memory)

ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x40100000, len 26704, room 16 
tail 0
chksum 0x0c
load 0x3ffe8000, len 2184, room 8 
tail 0
chksum 0x9a
load 0x3ffe8888, len 136, room 8 
tail 0
chksum 0x44
csum 0x44

Line 19 corresponds to the line of cn:send(). I guess I made something wrong declaring some variables, functions or callback funtions in the LUA script, that stack until there is no more memory... Also, I don't understand why there is 2 calls to the conn:on callback funtion (where node.heap() is printed) for only 1 "Hello connect". It is like a second void http request is always sent...

Thank you very much for your precious time and your potential help, if you came to the end of this post!

Paul

  • The `receive` event is fired for every network frame! Hence, if the data sent to the device exceeds 1460 bytes (derived from Ethernet frame size) it will fire more than once. Your data size is 2050. So, you have 2 `receive` events for each `connect` event. – Egor Skriptunoff Apr 29 '18 at 09:47

2 Answers2

0

sck:send(data, fn) is equivalent to sck:send(data); sck:on("sent", fn).
A closure passed to :on() must not reference the object directly (explanations).
Use first argument of the callback function instead of referencing the upvalue.

cn:send(
   "HTTP/1.1 200 OK\n\n", 
   function(s)  -- s here has the same value as cn
      s:close()
      --collectgarbage()
   end
)
Egor Skriptunoff
  • 23,359
  • 2
  • 34
  • 64
  • Egor, do you have an idea how we could prevent the constant inflow of these kind of questions? I stopped counting how many times I gave more or less the same answer. – Marcel Stör Apr 29 '18 at 17:50
  • @MarcelStör - Is it possible to move `luaL_unref(...cb_*_ref)` from `net_delete()` to `net_close()`? – Egor Skriptunoff Apr 29 '18 at 20:20
  • Thank you for your help, I am sorry if this question has been answered a lot of times, but it is quite hard to find previous answers when you don't know the exact nature of the problem. =/ However, I tried with your code (and with shorter requests to simplify the problem) but there is still a memory leak. If I understand well, "conn", "cn" and "s" all refer to the same object in my script. Maybe a problem with a callback at an other level? – Paul Breugnot Apr 30 '18 at 08:21
  • There's a perfect example (template) of how to do that in our docs at https://nodemcu.readthedocs.io/en/latest/en/modules/net/#example_6. As far as the presumed leak goes I'm quite certain it's not a leak. Even with your original code you can see in the output that free heap bounces back from time to time. That's the GC clearing dead memory (e.g. old sockets). That's why you can still run out of memory if the MCU has to serve lots of requests in quick succession. The GC isn't fast enough freeing memory. – Marcel Stör Apr 30 '18 at 18:59
0

Send the header:

Connection: close

With both requests and responses. The default for modern http servers and client apps is keep-alive. When this header is present the other side will explicitly close the connection when all data has been sent. When the connection closes, memory is freed.

Mark McGinty
  • 756
  • 7
  • 13