Captive portal goes live

A project log for IuT voltmeter for a breadboard

Measure several voltages on a breadboard and display results on a smartphone for less than $10 for parts

alexandernAlexanderN 05/16/2019 at 13:110 Comments

It was a while since I put the previous project entry. Nevertheless it is better late than never, isn't it?


In order to operate the device using the previous version of the firmware, one needed to connect to the access point IuT_voltemeter (no password required) with their smartphone/tablet/laptop, and entering the IP address of the web server, namely, in a web browser. The new revision allows enetering any valid web address , e.g. (Please be warned that typing any word will probably not do the trick as the browser will try to ask non-accesible search engine for entries).

The starting point for the captive portal was the CaptiveIntraWeb project developed by Andy Reischle 4 years ago. Since then the NodeMCU Lua firmware evolved to the point that this older code did not work at all, and required fixing.

Fortunately the required amends were not too difficult to debug. The amended firmware makes use of newer net.udpsocket Lua module. There is a sure possibility to take advantage of the net.dns Lua module but I decided to amend a working code rather than try to develop a new one from scratch not to getting too deep into the DNS protocol (I lightly used the following two sources that worked very well for my needs: DNS Query Message Format and DNS Response Message Format).

It was necessary to amend the Lua code that was placed into the file dns-liar.lua, and call this code from the IuT_00.lua as follows (I put this call at the very end of the file)


 In turn, the dns-liar1.lua contained the following code where I left all the acknowledgements

-- dns-liar1.lua
--Thanks to Thomas Shaddack for optimizations - 20150707 ARe
local i1,i2,i3,i4=dns_ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
-- constants required for a proper DNS reply
--% code in this section was modified to amend the deprecated APIs
--% see for details 
udpSocket:on("receive", function(s, data, port, ip)
    --print(string.format("received '%s' from %s:%d", data, ip, port)) -- DEBUG
    --s:send(port, ip, "echo: " .. data) -- from the docs example replaced below
    s:send(port, ip, dns_tr..dns_str1..dns_q..dns_str2..dns_strIP)    
--old callback code - deprecated
--  print(dns_pl)
--  print(dns_tr..dns_str1..dns_q..dns_str2..dns_strIP)
--  svr:send(dns_tr..dns_str1..dns_q..dns_str2..dns_strIP)
--  collectgarbage("collect")
udpSocket:listen(53) -- DNS requests are sent to the gateway IP : 53
function decodedns(dns_pl)
  local a=string.len(dns_pl)
  dns_tr = string.sub(dns_pl, 1, 2) -- get the first 2B of the request -> request ID
  local bte=""
  local i=13
  local bte2=""
  while bte2 ~= "0" do
    bte = string.byte(dns_pl,i)
    bte2 = string.format("%x", bte )
    dns_q = dns_q .. string.char(bte)
  --print(string.format("DNS request: '%s' for %s",dns_tr,dns_q)) -- DEBUG
print("DNS Server is now listening. Free Heap:", node.heap())


Bonus youtube link: ESP32 NodeMCU Lua Firmware - Config, build and flash | How to from Alija Bobija