Forráskód Böngészése

Merge pull request #75 from marcoskirsch/master

Merge master to dev
Marcos 7 éve
szülő
commit
189e4a5c56

+ 7 - 5
Makefile

@@ -5,7 +5,9 @@
 NODEMCU-UPLOADER=../nodemcu-uploader/nodemcu-uploader.py
 NODEMCU-UPLOADER=../nodemcu-uploader/nodemcu-uploader.py
 # Serial port
 # Serial port
 PORT=/dev/cu.SLAB_USBtoUART
 PORT=/dev/cu.SLAB_USBtoUART
-SPEED=9600
+SPEED=115200
+
+NODEMCU-COMMAND=$(NODEMCU-UPLOADER) -b $(SPEED) --start_baud $(SPEED) -p $(PORT) upload
 
 
 ######################################################################
 ######################################################################
 # End of user config
 # End of user config
@@ -33,17 +35,17 @@ usage:
 
 
 # Upload one files only
 # Upload one files only
 upload:
 upload:
-	@python $(NODEMCU-UPLOADER) -b $(SPEED) -p $(PORT) upload $(FILE)
+	@python $(NODEMCU-COMMAND) $(FILE)
 
 
 # Upload HTTP files only
 # Upload HTTP files only
 upload_http: $(HTTP_FILES)
 upload_http: $(HTTP_FILES)
-	@python $(NODEMCU-UPLOADER) -b $(SPEED) -p $(PORT) upload $(foreach f, $^, $(f))
+	@python $(NODEMCU-COMMAND) $(foreach f, $^, $(f))
 
 
 # Upload httpserver lua files (init and server module)
 # Upload httpserver lua files (init and server module)
 upload_server: $(LUA_FILES)
 upload_server: $(LUA_FILES)
-	@python $(NODEMCU-UPLOADER) -b $(SPEED) -p $(PORT) upload $(foreach f, $^, $(f))
+	@python $(NODEMCU-COMMAND) $(foreach f, $^, $(f))
 
 
 # Upload all
 # Upload all
 upload_all: $(LUA_FILES) $(HTTP_FILES)
 upload_all: $(LUA_FILES) $(HTTP_FILES)
-	@python $(NODEMCU-UPLOADER) -b $(SPEED) -p $(PORT) upload $(foreach f, $^, $(f))
+	@python $(NODEMCU-COMMAND) $(foreach f, $^, $(f))
 
 

+ 3 - 2
README.md

@@ -65,12 +65,13 @@ Let the abuse begin.
 
 
 4. How to use HTTP Basic Authentication.
 4. How to use HTTP Basic Authentication.
 
 
-   Enable and configure HTTP Basic Authentication by editing "httpserver-conf.lua" file.
+   Modify variables in configuration file httpserver-conf.lua in order to enable and to configure usernames/passwords.
+   See comments in that file for more details.
 
 
    When enabled, HTTP Basic Authentication is global to every file served by the server.
    When enabled, HTTP Basic Authentication is global to every file served by the server.
 
 
    Remember that HTTP Basic Authentication is a very basic authentication protocol, and should not be
    Remember that HTTP Basic Authentication is a very basic authentication protocol, and should not be
-   considered secure if the server is not using encryption, as your username and password travel
+   considered as secure since the server is not using encryption. Username and passwords travel
    in plain text.
    in plain text.
 
 
 ## How to use server-side scripting using your own Lua scripts
 ## How to use server-side scripting using your own Lua scripts

+ 16 - 12
http/cars.html

@@ -1,17 +1,21 @@
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <html lang="en">
 <html lang="en">
-<head>
-   <meta http-equiv="content-type" content="text/html; charset=utf-8">
-   <title>Nice cars</title>
-</head>
-<body>
-<h1>Nice cars!</h1>
-<p>This page loads "large" images of fancy cars. It is meant to serve as a stress test for nodemcu-httpserver.</p>
-<figure><img src="cars-ferrari.jpg" /><figcaption>Ferrari</figcaption></figure>
-<figure><img src="cars-lambo.jpg" /><figcaption>Lamborghini</figcaption></figure>
-<figure><img src="cars-mas.jpg" /><figcaption>Maserati</figcaption></figure>
-<figure><img src="cars-porsche.jpg" /><figcaption>Porsche</figcaption></figure>
-</body>
+   <head>
+      <meta http-equiv="content-type" content="text/html; charset=utf-8">
+      <title>Nice cars</title>
+   </head>
+   <body>
+      <h1>Nice cars!</h1>
+      <p>
+         This page loads "large" images of fancy cars. It is meant to serve as a stress test for nodemcu-httpserver.<br>
+         It works with three embedded images of cars, but the server crashes with four. Edit this file and try it yourself.<br>
+         Whoever manages to modify nodemcu-httpserver to load all four images without crashing wins a prize!
+      </p>
+      <figure><img src="cars-ferrari.jpg" /><figcaption>Ferrari</figcaption></figure>
+      <figure><img src="cars-lambo.jpg" /><figcaption>Lamborghini</figcaption></figure>
+      <figure><img src="cars-mas.jpg" /><figcaption>Maserati</figcaption></figure>
+      <figure><img src="cars-porsche.jpg" /><figcaption>Porsche</figcaption></figure>
+   </body>
 </html>
 </html>
 
 
 
 

+ 7 - 3
http/file_list.lua

@@ -7,12 +7,16 @@ return function (connection, req, args)
    <h1>Server File Listing</h1>
    <h1>Server File Listing</h1>
    ]===])
    ]===])
 
 
-   local remaining, used, total=file.fsinfo()
+   local remaining, used, total = file.fsinfo()
    connection:send("<b>Total size: </b> " .. total .. " bytes<br/>\n" ..
    connection:send("<b>Total size: </b> " .. total .. " bytes<br/>\n" ..
                    "<b>In Use: </b> " .. used .. " bytes<br/>\n" ..
                    "<b>In Use: </b> " .. used .. " bytes<br/>\n" ..
-                   "<b>Free: </b> " .. remaining .. " bytes<br/>\n" ..
-                   "<p>\n<b>Files:</b><br/>\n<ul>\n")
+                   "<b>Free: </b> " .. remaining .. " bytes<br/>\n")
 
 
+   local flashAddress, flashSize = file.fscfg ()
+   connection:send("<b>Flash Address: </b> " .. flashAddress .. " bytes<br/>\n" ..
+                   "<b>Flash Size: </b> " .. flashSize .. " bytes<br/>\n")
+
+   connection:send("<p>\n<b>Files:</b><br/>\n<ul>\n")
    for name, size in pairs(file.list()) do
    for name, size in pairs(file.list()) do
       local isHttpFile = string.match(name, "(http/)") ~= nil
       local isHttpFile = string.match(name, "(http/)") ~= nil
       if isHttpFile then
       if isHttpFile then

+ 1 - 1
http/index.html

@@ -20,7 +20,7 @@
    <h3>Serve me some pages!</h3>
    <h3>Serve me some pages!</h3>
    <ul>
    <ul>
       <li><a href="index.html">Index</a>: This page (static)</li>
       <li><a href="index.html">Index</a>: This page (static)</li>
-      <li><a href="cars.html">Nice cars</a>: Stress test, loads 4 "large" images. Makes the chip panic and restart :( (static)</li>
+      <li><a href="cars.html">Nice cars</a>: Stress test, loads several "large" images. Makes the chip panic and restart :( (static)</li>
       <li><a href="zipped.html">Zipped</a>: File is actually saved as zipped.html.gz. A compressed file! (static but gzipped)</li>
       <li><a href="zipped.html">Zipped</a>: File is actually saved as zipped.html.gz. A compressed file! (static but gzipped)</li>
       <li><a href="zipped.html.gz">Zipped</a>: Same exact file as served above. Server is smart enough to treat the .gz extension correctly (static but gzipped)</li>
       <li><a href="zipped.html.gz">Zipped</a>: Same exact file as served above. Server is smart enough to treat the .gz extension correctly (static but gzipped)</li>
       <li><a href="args.lua">Arguments</a>: Parses arguments passed in the URL and prints them. (Lua)</li>
       <li><a href="args.lua">Arguments</a>: Parses arguments passed in the URL and prints them. (Lua)</li>

+ 16 - 6
httpserver-b64decode.lua

@@ -1,33 +1,44 @@
 #!/usr/local/bin/lua
 #!/usr/local/bin/lua
 -- httpserver-b64decode.lua
 -- httpserver-b64decode.lua
 -- Part of nodemcu-httpserver, contains b64 decoding used for HTTP Basic Authentication.
 -- Part of nodemcu-httpserver, contains b64 decoding used for HTTP Basic Authentication.
+-- Modified to use an exponentiation by multiplication method for only applicable for unsigned integers.
 -- Based on http://lua-users.org/wiki/BaseSixtyFour by Alex Kloss
 -- Based on http://lua-users.org/wiki/BaseSixtyFour by Alex Kloss
 -- compatible with lua 5.1
 -- compatible with lua 5.1
 -- http://www.it-rfc.de
 -- http://www.it-rfc.de
 -- Author: Marcos Kirsch
 -- Author: Marcos Kirsch
 
 
+local function uipow(a, b)
+    local ret = 1
+    if b >= 0 then
+        for i = 1, b do
+            ret = ret * a
+        end
+    end
+    return ret
+end
+
 -- bitshift functions (<<, >> equivalent)
 -- bitshift functions (<<, >> equivalent)
 -- shift left
 -- shift left
 local function lsh(value,shift)
 local function lsh(value,shift)
-   return (value*(2^shift)) % 256
+   return (value*(uipow(2, shift))) % 256
 end
 end
 
 
 -- shift right
 -- shift right
 local function rsh(value,shift)
 local function rsh(value,shift)
    -- Lua builds with no floating point don't define math.
    -- Lua builds with no floating point don't define math.
-   if math then return math.floor(value/2^shift) % 256 end
-   return (value/2^shift) % 256
+   if math then return math.floor(value/uipow(2, shift)) % 256 end
+   return (value/uipow(2, shift)) % 256
 end
 end
 
 
 -- return single bit (for OR)
 -- return single bit (for OR)
 local function bit(x,b)
 local function bit(x,b)
-   return (x % 2^b - x % 2^(b-1) > 0)
+   return (x % uipow(2, b) - x % uipow(2, (b-1)) > 0)
 end
 end
 
 
 -- logic OR for number values
 -- logic OR for number values
 local function lor(x,y)
 local function lor(x,y)
    result = 0
    result = 0
-   for p=1,8 do result = result + (((bit(x,p) or bit(y,p)) == true) and 2^(p-1) or 0) end
+   for p=1,8 do result = result + (((bit(x,p) or bit(y,p)) == true) and uipow(2, (p-1)) or 0) end
    return result
    return result
 end
 end
 
 
@@ -62,4 +73,3 @@ return function(data)
    end
    end
    return result
    return result
 end
 end
-

+ 14 - 3
httpserver-basicauth.lua

@@ -4,6 +4,16 @@
 
 
 basicAuth = {}
 basicAuth = {}
 
 
+-- Returns true if the user/password match one of the users/passwords in httpserver-conf.lua.
+-- Returns false otherwise.
+function loginIsValid(user, pwd, users)
+   if user == nil then return false end
+   if pwd == nil then return false end
+   if users[user] == nil then return false end
+   if users[user] ~= pwd then return false end
+   return true
+end
+
 -- Parse basic auth http header.
 -- Parse basic auth http header.
 -- Returns the username if header contains valid credentials,
 -- Returns the username if header contains valid credentials,
 -- nil otherwise.
 -- nil otherwise.
@@ -15,12 +25,13 @@ function basicAuth.authenticate(header)
    end
    end
    local credentials = dofile("httpserver-b64decode.lc")(credentials_enc)
    local credentials = dofile("httpserver-b64decode.lc")(credentials_enc)
    local user, pwd = credentials:match("^(.*):(.*)$")
    local user, pwd = credentials:match("^(.*):(.*)$")
-   if user ~= conf.auth.user or pwd ~= conf.auth.password then
+   if loginIsValid(user, pwd, conf.auth.users) then
+      print("httpserver-basicauth: User \"" .. user .. "\": Authenticated.")
+      return user
+   else
       print("httpserver-basicauth: User \"" .. user .. "\": Access denied.")
       print("httpserver-basicauth: User \"" .. user .. "\": Access denied.")
       return nil
       return nil
    end
    end
-   print("httpserver-basicauth: User \"" .. user .. "\": Authenticated.")
-   return user
 end
 end
 
 
 function basicAuth.authErrorHeader()
 function basicAuth.authErrorHeader()

+ 7 - 5
httpserver-conf.lua

@@ -4,12 +4,14 @@
 
 
 local conf = {}
 local conf = {}
 
 
--- Basic Authentication Conf
+-- Configure Basic HTTP Authentication.
 local auth = {}
 local auth = {}
+-- Set to true if you want to enable.
 auth.enabled = false
 auth.enabled = false
-auth.realm = "nodemcu-httpserver" -- displayed in the login dialog users get
-auth.user = "user"
-auth.password = "password" -- PLEASE change this
-conf.auth = auth
+-- Displayed in the login dialog users see before authenticating.
+auth.realm = "nodemcu-httpserver"
+-- Add users and passwords to this table. Do not leave this unchanged if you enable authentication!
+auth.users = {user1 = "password1", user2 = "password2", user3 = "password3"}
 
 
+conf.auth = auth
 return conf
 return conf

+ 59 - 67
httpserver-connection.lua

@@ -1,67 +1,59 @@
--- httpserver-connection
--- Part of nodemcu-httpserver, provides a buffered connection object that can handle multiple
--- consecutive send() calls, and buffers small payloads to send once they get big.
--- For this to work, it must be used from a coroutine and owner is responsible for the final
--- flush() and for closing the connection.
--- Author: Philip Gladstone, Marcos Kirsch
-
-BufferedConnection = {}
-
--- parameter is the nodemcu-firmware connection
-function BufferedConnection:new(connection)
-   local newInstance = {}
-   newInstance.connection = connection
-   newInstance.size = 0
-   newInstance.data = {}
-
-   function newInstance:flush()
-      if self.size > 0 then
-         self.connection:send(table.concat(self.data, ""))
-         self.data = {}
-         self.size = 0
-         return true
-      end
-      return false
-   end
-
-   function newInstance:send(payload)
-      local flushthreshold = 1400
-
-      local newsize = self.size + payload:len()
-      while newsize > flushthreshold do
-          --STEP1: cut out piece from payload to complete threshold bytes in table
-          local piecesize = flushthreshold - self.size
-          local piece = payload:sub(1, piecesize)
-          payload = payload:sub(piecesize + 1, -1)
-          --STEP2: insert piece into table
-          table.insert(self.data, piece)
-          self.size = self.size + piecesize --size should be same as flushthreshold
-          --STEP3: flush entire table
-          if self:flush() then
-              coroutine.yield()
-          end
-          --at this point, size should be 0, because the table was just flushed
-          newsize = self.size + payload:len()
-      end
-            
-      --at this point, whatever is left in payload should be <= flushthreshold
-      local plen = payload:len()
-      if plen == flushthreshold then
-          --case 1: what is left in payload is exactly flushthreshold bytes (boundary case), so flush it
-          table.insert(self.data, payload)
-          self.size = self.size + plen
-          if self:flush() then
-              coroutine.yield()
-          end
-      elseif payload:len() then
-          --case 2: what is left in payload is less than flushthreshold, so just leave it in the table
-          table.insert(self.data, payload)
-          self.size = self.size + plen
-      --else, case 3: nothing left in payload, so do nothing
-      end
-   end
-
-   return newInstance
-end
-
-return BufferedConnection
+-- httpserver-connection
+-- Part of nodemcu-httpserver, provides a buffered connection object that can handle multiple
+-- consecutive send() calls, and buffers small payloads to send once they get big.
+-- For this to work, it must be used from a coroutine and owner is responsible for the final
+-- flush() and for closing the connection.
+-- Author: Philip Gladstone, Marcos Kirsch
+
+BufferedConnection = {}
+
+-- parameter is the nodemcu-firmware connection
+function BufferedConnection:new(connection)
+   local newInstance = {}
+   newInstance.connection = connection
+   newInstance.size = 0
+   newInstance.data = {}
+
+   -- Returns true if there was any data to be sent.
+   function newInstance:flush()
+      if self.size > 0 then
+         self.connection:send(table.concat(self.data, ""))
+         self.data = {}
+         self.size = 0
+         return true
+      end
+      return false
+   end
+
+   function newInstance:send(payload)
+      local flushThreshold = 1400
+      local newSize = self.size + payload:len()
+      while newSize >= flushThreshold do
+         --STEP1: cut out piece from payload to complete threshold bytes in table
+         local pieceSize = flushThreshold - self.size
+         local piece = payload:sub(1, pieceSize)
+         payload = payload:sub(pieceSize + 1, -1)
+         --STEP2: insert piece into table
+         table.insert(self.data, piece)
+         piece = nil
+         self.size = self.size + pieceSize --size should be same as flushThreshold
+         --STEP3: flush entire table
+         if self:flush() then
+            coroutine.yield()
+         end
+         --at this point, size should be 0, because the table was just flushed
+         newSize = self.size + payload:len()
+      end
+
+      --at this point, whatever is left in payload should be < flushThreshold
+      if payload:len() ~= 0 then
+         --leave remaining data in the table
+         table.insert(self.data, payload)
+         self.size = self.size + payload:len()
+      end
+   end
+   return newInstance
+
+end
+
+return BufferedConnection

+ 4 - 3
httpserver-header.lua

@@ -13,15 +13,16 @@ return function(connection, code, extension, isGzipped)
 
 
    local function getMimeType(ext)
    local function getMimeType(ext)
       -- A few MIME types. Keep list short. If you need something that is missing, let's add it.
       -- A few MIME types. Keep list short. If you need something that is missing, let's add it.
-      local mt = { css = "text/css", gif = "image/gif", html = "text/html", ico = "image/x-icon", jpeg = "image/jpeg", jpg = "image/jpeg", js = "application/javascript", json = "application/json", png = "image/png", xml = "text/xml" }
+      local mt = {css = "text/css", gif = "image/gif", html = "text/html", ico = "image/x-icon", jpeg = "image/jpeg", 
+         jpg = "image/jpeg", js = "application/javascript", json = "application/json", png = "image/png", xml = "text/xml"}
       if mt[ext] then return mt[ext] else return "text/plain" end
       if mt[ext] then return mt[ext] else return "text/plain" end
    end
    end
 
 
    local mimeType = getMimeType(extension)
    local mimeType = getMimeType(extension)
 
 
-   connection:send("HTTP/1.0 " .. code .. " " .. getHTTPStatusString(code) .. "\r\nServer: nodemcu-httpserver\r\nContent-Type: " .. mimeType .. "\r\nCache-Control: private, no-store\r\n")
+   connection:send("HTTP/1.0 " .. code .. " " .. getHTTPStatusString(code) .. "\r\nServer: nodemcu-httpserver\r\nContent-Type: " .. mimeType .. "\r\n")
    if isGzipped then
    if isGzipped then
-      connection:send("Cache-Control: max-age=2592000\r\nContent-Encoding: gzip\r\n")
+      connection:send("Cache-Control: private, max-age=2592000\r\nContent-Encoding: gzip\r\n")
    end
    end
    connection:send("Connection: close\r\n\r\n")
    connection:send("Connection: close\r\n\r\n")
 end
 end

+ 13 - 20
httpserver-static.lua

@@ -3,32 +3,25 @@
 -- Author: Marcos Kirsch
 -- Author: Marcos Kirsch
 
 
 return function (connection, req, args)
 return function (connection, req, args)
-   --print("Begin sending:", args.file)
-   --print("node.heap(): ", node.heap())
    dofile("httpserver-header.lc")(connection, 200, args.ext, args.isGzipped)
    dofile("httpserver-header.lc")(connection, 200, args.ext, args.isGzipped)
    -- Send file in little chunks
    -- Send file in little chunks
-   local continue = true
-   local size = file.list()[args.file]
-   local bytesSent = 0
+   local bytesRemaining = file.list()[args.file]
    -- Chunks larger than 1024 don't work.
    -- Chunks larger than 1024 don't work.
    -- https://github.com/nodemcu/nodemcu-firmware/issues/1075
    -- https://github.com/nodemcu/nodemcu-firmware/issues/1075
    local chunkSize = 1024
    local chunkSize = 1024
-   while continue do
-      collectgarbage()
-
-      -- NodeMCU file API lets you open 1 file at a time.
-      -- So we need to open, seek, close each time in order
-      -- to support multiple simultaneous clients.
-      file.open(args.file)
-      file.seek("set", bytesSent)
-      local chunk = file.read(chunkSize)
-      file.close()
-
+   local fileHandle = file.open(args.file)
+   while bytesRemaining > 0 do
+      local bytesToRead = 0
+      if bytesRemaining > chunkSize then bytesToRead = chunkSize else bytesToRead = bytesRemaining end
+      local chunk = fileHandle:read(bytesToRead)
       connection:send(chunk)
       connection:send(chunk)
-      bytesSent = bytesSent + #chunk
+      bytesRemaining = bytesRemaining - #chunk
+      --print(args.file .. ": Sent "..#chunk.. " bytes, " .. bytesRemaining .. " to go.")
       chunk = nil
       chunk = nil
-      --print("Sent: " .. bytesSent .. " of " .. size)
-      if bytesSent == size then continue = false end
+      collectgarbage()
    end
    end
-   --print("Finished sending: ", args.file)
+   print("Finished sending: ", args.file)
+   fileHandle:close()
+   fileHandle = nil
+   collectgarbage()
 end
 end

+ 0 - 5
httpserver.lua

@@ -155,11 +155,6 @@ return function (port)
 
 
       end
       end
    )
    )
-   -- false and nil evaluate as false
-   local ip = wifi.sta.getip()
-   if not ip then ip = wifi.ap.getip() end
-   if not ip then ip = "unknown IP" end
-   print("nodemcu-httpserver running at http://" .. ip .. ":" ..  port)
    return s
    return s
 
 
 end
 end

+ 44 - 28
init.lua

@@ -5,7 +5,7 @@ local wifiConfig = {}
 -- wifi.STATION         -- station: join a WiFi network
 -- wifi.STATION         -- station: join a WiFi network
 -- wifi.SOFTAP          -- access point: create a WiFi network
 -- wifi.SOFTAP          -- access point: create a WiFi network
 -- wifi.wifi.STATIONAP  -- both station and access point
 -- wifi.wifi.STATIONAP  -- both station and access point
-wifiConfig.mode = wifi.STATIONAP  -- both station and access point
+wifiConfig.mode = wifi.STATION
 
 
 wifiConfig.accessPointConfig = {}
 wifiConfig.accessPointConfig = {}
 wifiConfig.accessPointConfig.ssid = "ESP-"..node.chipid()   -- Name of the SSID you want to create
 wifiConfig.accessPointConfig.ssid = "ESP-"..node.chipid()   -- Name of the SSID you want to create
@@ -23,7 +23,7 @@ wifiConfig.stationPointConfig.pwd =  ""                -- Password for the WiFi
 -- Tell the chip to connect to the access point
 -- Tell the chip to connect to the access point
 
 
 wifi.setmode(wifiConfig.mode)
 wifi.setmode(wifiConfig.mode)
-print('set (mode='..wifi.getmode()..')')
+--print('set (mode='..wifi.getmode()..')')
 
 
 if (wifiConfig.mode == wifi.SOFTAP) or (wifiConfig.mode == wifi.STATIONAP) then
 if (wifiConfig.mode == wifi.SOFTAP) or (wifiConfig.mode == wifi.STATIONAP) then
     print('AP MAC: ',wifi.ap.getmac())
     print('AP MAC: ',wifi.ap.getmac())
@@ -44,7 +44,7 @@ collectgarbage()
 -- End WiFi configuration
 -- End WiFi configuration
 
 
 -- Compile server code and remove original .lua files.
 -- Compile server code and remove original .lua files.
--- This only happens the first time afer the .lua files are uploaded.
+-- This only happens the first time after server .lua files are uploaded.
 
 
 local compileAndRemoveIfNeeded = function(f)
 local compileAndRemoveIfNeeded = function(f)
    if file.open(f) then
    if file.open(f) then
@@ -74,32 +74,48 @@ serverFiles = nil
 collectgarbage()
 collectgarbage()
 
 
 -- Connect to the WiFi access point.
 -- Connect to the WiFi access point.
--- Once the device is connected, you may start the HTTP server.
-
-if (wifi.getmode() == wifi.STATION) or (wifi.getmode() == wifi.STATIONAP) then
-    local joinCounter = 0
-    local joinMaxAttempts = 5
-    tmr.alarm(0, 3000, 1, function()
-       local ip = wifi.sta.getip()
-       if ip == nil and joinCounter < joinMaxAttempts then
-          print('Connecting to WiFi Access Point ...')
-          joinCounter = joinCounter +1
-       else
-          if joinCounter == joinMaxAttempts then
-             print('Failed to connect to WiFi Access Point.')
-          else
-             print('IP: ',ip)
-          end
-          tmr.stop(0)
-          joinCounter = nil
-          joinMaxAttempts = nil
-          collectgarbage()
-       end
-    end)
+-- Once the device is connected, start the HTTP server.
+
+startServer = function(ip, hostname)
+   local serverPort = 80
+   if (dofile("httpserver.lc")(serverPort)) then
+      print("nodemcu-httpserver running at:")
+      print("   http://" .. ip .. ":" ..  serverPort)
+      if (mdns) then
+         mdns.register(hostname, { description="A tiny server", service="http", port=serverPort, location='Earth' })
+         print ('   http://' .. hostname .. '.local.:' .. serverPort)
+      end
+   end
 end
 end
 
 
--- Uncomment to automatically start the server in port 80
-if (not not wifi.sta.getip()) or (not not wifi.ap.getip()) then
-    --dofile("httpserver.lc")(80)
+if (wifi.getmode() == wifi.STATION) or (wifi.getmode() == wifi.STATIONAP) then
+   local joinCounter = 0
+   local joinMaxAttempts = 5
+   tmr.alarm(0, 3000, 1, function()
+      local ip = wifi.sta.getip()
+      if (not ip) then ip = wifi.ap.getip() end
+      if ip == nil and joinCounter < joinMaxAttempts then
+         print('Connecting to WiFi Access Point ...')
+         joinCounter = joinCounter + 1
+      else
+         if joinCounter == joinMaxAttempts then
+            print('Failed to connect to WiFi Access Point.')
+         else
+            print("IP = " .. ip)
+            if (not not wifi.sta.getip()) or (not not wifi.ap.getip()) then
+               -- Second parameter is for mDNS (aka Zeroconf aka Bonjour) registration.
+               -- If the mdns module is compiled in the firmware, advertise the server with this name.
+               -- If no mdns is compiled, then parameter is ignored.
+               startServer(ip, "nodemcu")
+            end
+         end
+         tmr.stop(0)
+         joinCounter = nil
+         joinMaxAttempts = nil
+         collectgarbage()
+      end
+   end)
+else
+   startServer()
 end
 end