Left here for archival purposes.

User avatar
By radu022003
#16615 Hello, i don't know yet if is a bug or not but maybe we can clarify it here.
I wrote a LUA program for periodically read temperature with ds18b20 and read a LDR connected to adc pin.
Module is in station mode and as server on port 8080 listening for connections.

My router is a Netgear R7000 with DD-WRT on it.
Module is doing it's job good, but from time to time it disconnects from wifi and is reconnecting again after 1hour. The longest connection time was around 23hours.

I haven't managed to understand why is disconnecting and what can i do to prevent this behavior or at least try to reconnect imediatelly, not after 1 hour.
I made a test and if i restart wifi on router, esp is connecting itself right after wifi becomes active.

Can someone tell me how can i make esp module's connection more stable?


init.lua:
Code: Select allwifi.setmode(wifi.STATION)
 wifi.sta.config("AP_name","password")
 wifi.sta.connect()
 tmr.delay(1000000)
 print("ESP8266 mode is: " .. wifi.getmode())
 print("The module MAC address is: " .. wifi.sta.getmac())
 --print("Config done, IP is "..ip)
 --Startup file--
NextFile="server.lua"
 l = file.list();
    for k,v in pairs(l) do
    --  print("name:"..k..", size:"..v)
         if k == NextFile then
         print("Wait 5 seconds please")
         tmr.alarm(0, 5000, 0, function() dofile(NextFile) end)
         print("Started file ".. NextFile)
         else
       --  do nothing
         end
    end
print("End of startup")


server.lua
Code: Select all   require('ds18b20')
   gpio4 = 1
   ds18b20.setup(gpio4)
   
   print("server.lua started")
   tmr.alarm(0, 1000, 1, function()
               tmr.wdclr()
               if wifi.sta.status() < 5 then
                  node.restart()
               end
               end )
   pin=7
   gpio.mode(pin,gpio.OUTPUT)
   srv=net.createServer(net.TCP,50)
    srv:listen(8080,function(conn)
      conn:on("receive",function(conn,payload)
        print(payload)
      if payload == "[status]" then
         temp = ds18b20.read()
         temp1 = temp%10000
         temp = temp/10000
         light = adc.read(0)
         conn:send("["..temp.."."..temp1..","..light.."]")
      end
      
      if payload == "heat_on" then
         gpio.write(pin,gpio.HIGH)
         conn:send(gpio.read(pin))
      elseif payload == "heat_off" then
         gpio.write(pin,gpio.LOW)
         conn:send(gpio.read(pin))
      end
      end)
    end)
   package.loaded["ds18b20"]=nil
User avatar
By TerryE
#16640 @radu022003, I address your specific Q at the end of this post. However, if you don't mind here are some comments on your general approach to coding for the ESP8266 to work with its RAM constraints. OK, I realise that there is no right way to code. The only real tests are (i) does it work? and (ii) is it maintainable? If the answer to both is yes then please ignore. :) However, I thought that you and other readers might like a different perspective on how to approach this. I hope that you find it useful.

The background to this is given in some of my recent posts on how to optimise lua for RAM footprint. nodeMCU lua + ESP SDK is event driven and none-preemptive. The tmr.delay does nothing here apart from stop anything getting in for 1sec. You use a delay if you want to toggle an I/O pin for X uSec, but not for this. Consider something like
Code: Select allwifi.setmode(wifi.STATION)
wifi.sta.config("AP_name","password")
wifi.sta.connect()
-- put any other debug logic in here if you want to prit out the config
local server = require "server"
if not server then return
tmr.alarm(0, 1000, 1, function()
  -- poll until station connected
  if wifi.sta.status() == 5 then
    print("ESP8266 mode is: " .. wifi.getmode())
    print("The module MAC address is: " .. wifi.sta.getmac())
    print("Config done, IP is "..ip)
    print("End of startup")
    tmr.alarm(0, 1000, 1, server)
  end
end)

  • Here the timer is used to yield control to the wifi and to poll/wait for GOT_IP.
  • You need the if not server to prevent a PANIC loop if you've got a compile error in server.lua.
  • The print can go before the alarm statement or after -- it doesn't matter. The alarm won't fire until the routine returns anyway.
  • The same alarm is then repurposed to execute the server. Once this has been done all references to the init.lua have been removed so these chunks will be GCed before the server fires. This will free this memory before server executes and requires the ds18b20 module
If you think about the server module, you only need to start to do anything with the thermometers after the 8080 connection has been made and fired the listener callback, so you you've got various strategies for saving RAM here, e.g.
  • You could defer the require 'ds18b20' until the first time through the connection or even the receive and this would allow the GC to collect the top level intialisation chunks
  • Alternatively you could do ds18b20.addrs() at the top level to select the thermometer you want and then set
    Code: Select allds18b20.addrs, ds18b20.read = nil, nil

    to GC these chunks as the first is a one-time call and the second it totally redundant since simple takes a lot of line to do a return readNumber(addr, unit)
  • Sorry, but do you have an integer binary with a patched ds18b20 module? My version returns a temp in °C if the second parameter is omitted. Even so the simplest and the best way to format strings is to use the function that was designed for this, e.g.
    Code: Select allconn:on("receive",function(conn,payload)
      print(payload)
      local resp, temp
      if payload == "[status]" then
         temp = ds18b20.readNumber(my_ds18b20)
         resp = ("[%.3f,%u]"):format(temp,adc.read(0))
      else
         gpio.write(pin,(payload == "heat_off") and gpio.HIGH or gpio.LOW)
         resp = gpio.read(pin))
      end
      conn:send(resp)
    end)
As to your comment that the device disconnects then why not just use the on() call designed to handle this situation, and death and rebirth is a good tactic in these circumstances:
Code: Select allconn:on('disconnect', function() node.restart() end)


PS. port 8080 is a bad choice , IMO since this is often used as an alternative to port 80 for HTTP protocols. Why not port 8266? It seems appropriate, no? 8-)
User avatar
By radu022003
#16659 First thing, thank you for very detailed answer. I will go forward and use your advices. I also wanted to use the :on(disconnect) but I think that is referring to clients connected to the server and not disconnecting from wifi network. Because my raspberry pi is the one who is the client for this server and once at 40 seconds, it is connecting, asking the temp and adc and then disconnects. I don't think is a good ideea to restart esp module once per 40 seconds. I choosed 8080 because it's only in local network, but any other would work for me.
The problem is that when esp stops accepting connections is nothing I can do for 40 min or 1 hour, just hard reboot, which is not an option.
I also thought about RAM memory waste, but the same script can run flawlessly for 12 hours and suddenly stops work.
I will implement your suggestions for sure, but my feeling is that this behavior will not disappear.
Some times is stays connected for only 6 minutes, then "dead" for another hour and after that again reconnects. If in 12 hours is not running out of memory, why it can run out of memory in 6 min sometimes?
Do you think that maybe overheating of the module can lead to such behavior?
Or maybe power supply? Because I supply it from 5V/2A and a AP1117 which is 3.3V regulator.

Thank you.