telnet_fifosock.lua 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. --[[ A telnet server T. Ellison, May 2018
  2. This version is more complex than the simple Lua example previously provided in our
  3. distro. The main reason is that a single Lua command can produce a LOT of output,
  4. and the server has to work within four constraints:
  5. - The SDK rules are that you can only issue one send per task invocation, so any
  6. overflow must be buffered, and the buffer emptied using an on:sent cb
  7. - Since the interpeter invokes a node.output cb per field, you have a double
  8. whammy that these fields are typically small, so using a simple array FIFO
  9. would rapidly exhaust RAM.
  10. - For network efficiency, you want to aggregate any FIFO buffered into sensible
  11. sized packet, say 1024 bytes, but you also need to handle the case when larger
  12. string span multiple packets. However, you must flush the buffer if necessary.
  13. - The overall buffering strategy needs to be reasonably memory efficient and avoid
  14. hitting the GC too hard, so where practical avoid aggregating small strings to
  15. more than 256 chars (as NodeMCU handles <256 using stack buffers), and avoid
  16. serial aggregation such as buf = buf .. str as this hammers the GC.
  17. So this server adopts a simple buffering scheme using a two level FIFO. The node.output
  18. cb adds cb records to the 1st level FIFO until the #recs is > 32 or the total size
  19. would exceed 256 bytes. Once over this threashold, the contents of the FIFO are
  20. concatenated into a 2nd level FIFO entry of upto 256 bytes, and the 1st level FIFO
  21. cleared down to any residue.
  22. ]]
  23. --luacheck: no unused args
  24. local node, tmr, wifi, uwrite = node, tmr, wifi, uart.write
  25. local function telnet_listener(socket)
  26. local queueLine = (require "fifosock").wrap(socket)
  27. local function receiveLine(s, line)
  28. node.input(line)
  29. end
  30. local function disconnect(s)
  31. socket:on("disconnection", nil)
  32. socket:on("reconnection", nil)
  33. socket:on("connection", nil)
  34. socket:on("receive", nil)
  35. socket:on("sent", nil)
  36. node.output(nil)
  37. end
  38. socket:on("receive", receiveLine)
  39. socket:on("disconnection", disconnect)
  40. node.output(queueLine, 0)
  41. print(("Welcome to NodeMCU world (%d mem free, %s)"):format(node.heap(), wifi.sta.getip()))
  42. end
  43. local listenerSocket
  44. return {
  45. open = function(this, ssid, pwd, port)
  46. if ssid then
  47. wifi.setmode(wifi.STATION, false)
  48. wifi.sta.config { ssid = ssid, pwd = pwd, save = false }
  49. end
  50. local t = tmr.create()
  51. t:alarm(500, tmr.ALARM_AUTO, function()
  52. if (wifi.sta.status() == wifi.STA_GOTIP) then
  53. t:unregister()
  54. t=nil
  55. print(("Telnet server started (%d mem free, %s)"):format(node.heap(), wifi.sta.getip()))
  56. net.createServer(net.TCP, 180):listen(port or 23, telnet_listener)
  57. else
  58. uwrite(0,".")
  59. end
  60. end)
  61. end,
  62. close = function(this)
  63. if listenerSocket then
  64. listenerSocket:close()
  65. package.loaded.telnet = nil
  66. listenerSocket = nil
  67. collectgarbage()
  68. end
  69. end,
  70. }