As the title says... Chat on...

User avatar
By magnayn
#33950 I am trying to use an ESP-01 to read lines from a serial port, split it up into sections and send each section over MQTT. (The source here is an emontx board, for interest's sake).

I have learned from experience that if I don't re-connect mqtt, then it just eventually times out and stops working. My code is below, the first iteration (I've omitted wifi and list implementations for brevity):

Code: Select allfunction initMQTT()
    m = mqtt.Client("ESP3", 120, "user", "pass")
    listItems = List.new()

    -- 9600 for a test 
   uart.setup( 0, 38400, 8, 0, 1, 0);

   uart.on("data", "\n", function(data)
        processMQ(data) 
   end, 0)

end

function sendAll(conn)
  local rv = List.popleft(listItems)
 
  if rv == nil then   
     m:close()
  else
     m:publish(rv[1], rv[2], 0, 0, sendAll);
  end
end

function decim(y)

     x = tonumber(y)
     if x == nil then return nil end
     
     l = string.len(x)
     a = string.sub(x,0,l-2)
     b = string.sub(x,l-1);

     return ""..a.."."..b
end

function processMQ(data)
     if (data=='') then return end
         
     pos = 0
     idx = 0

    for st,sp in function() return string.find(data," ",pos,true) end do
     
         item = string.sub(data,pos,st-1);
       
         List.pushright(listItems, {"/emontx/current/"..idx,tonumber(item)});
           
         idx = idx + 1
         pos = sp + 1
    end

     item = string.sub(data,pos)

     List.pushright(listItems, {"/emontx/voltage",decim(item)});
         
     pushAll()

end

function pushAll()
     m:connect("192.168.0.16", 1883, 0, function(conn)
           sendAll(conn)
     end)
end

initMQTT()


This *almost* works - except if two lines come in quick succession, I get failures that the mqtt is already connected. So I re-wire it to this:

Code: Select allfunction sendAll()
  local rv = List.popleft(listItems)
 
  if rv == nil then   
     m:close()
     amPushing=0
  else
     m:publish(rv[1], rv[2], 0, 0, sendAll);
  end
end

function pushAll()
     if amPushing == 0 then
       amPushing = 1
     m:connect("192.168.0.16", 1883, 0, function(conn)
           sendAll(conn)
     end)

   end
end



This gets much further, but now occasionally still bombs with

Code: Select allPANIC: unprotected error in call to Lua API (wtaf.lua:117: already connected)


What I *assume* is happening is the m:close() must return immediately, but the connection is still deemed to be open until some time later (perhaps some kind of MQTT 'bye' from the server).

All these callbacks, even though (I assume) they're non-preempted, seems a very tortuous solution compared with just having m:close() be synchronous. I'm not sure (new to LUA) how to make access to a shared resource (mqtt) 'safe' and what primitives I should be using (yield?) to achieve this.