Become a subscriber (Free)

Join 29,000 other subscribers to receive subscriber sale discounts and other free resources.
Don't worry -- youre-mail address is totally secure. I promise to use it only to send you MicroZine.

ESP8266 Webserver Output Control

This page is about creating an ESP8266 webserver to control outputs. The method is demonstrated by using the built in LED as a controlled output.

This code uses a Lolin nodeMCU module (the built in LED is on pin 4) but you could also use any ESP8266 based unit. It uses new NodeMCU firmware lua 5.1.4 and SDK 2.2.1. flashed into the ESP8266.

The previous examples show simpler bits of code while this one shows a more useful framework.

Anyway the original code is split into 3 files:

  1. credentials.lua
  2. init.lua
  3. application.lua

ESP8266 Webserver Operation

The credentials.lua file has only two lines - these contain your ssid and password.
The init.lua file starts the web interface (this is also started from re-boot).
The application file contains your html application code.

Webserver Application

ESP8266 webserver html buttons control LEDs

The basic idea is that you write your html code in appication.lua and that init.lua does the WiFi startup / monitoring / re-start (with output to the serial terminal). Once the wifi is up and an IP address has been assigned, it runs your html application code. So you can experiment with code in application.lua and leave everything else alone (and know that it will work!).

Note: To check the basic operation comment out dofile("application.lua"). This will let you see the connection from start, to IP assignment, to 3 sec delay and full start.

The file init.lua is the boot up start file that Lua always runs if it is present. In that file is a three second delay (see below) which allows you to stop the ESP module send the command stopWiFi() e.g. if you need to change some code. Otherwise the ESP module will keep re-booting and you can't get access to re-program it. It's even worse if you have no delay as you will then need to re-flash the ESP module.

LED Blink Status

If you don't have a serial terminal attached to the ESP8266 Webserber then there is no way to figure out what is going on so I have also added a further file that shows you what's going on by flashing the built in LED at different rates - explained below.

  • Idle and waiting for connection to router : Relaxed flashing: Ok.

  • Connected to router waiting for IP : A bit more upbeat flashing: Ok.

  • Received the IP now waiting 3s to start : Triple flash urgent delay: Warning.

  • Error, restarting IP connection : Double flash longer delay: Error.

  • LED off after going through the above : Everything is running smoothly.

On my setup the WiFi router is a long way away so it sometimes gets a re-connection request when it misses a beacon alert. The LED flash lets you see the ESP module restart action.

The added file is:

  • LEDBlinkn.lua

I also modified init.lua to use this LED flash code placing the calls to LEDBlinkn at strategic places in that code. This means you have some feedback as to the status of the ESP8266 Webserver even with no serial connection.

ESP8266 Webserver Lua Code Operation

ESP8266 Webserver Credentials

Once you have put in your credentials, you don't need to keep re-entering them for different html applications because the file is stored in the internal file system of the ESP8266.

It won't need re-updating (if you don't format the file system) and any code you write could use that file. Its a very good way to use the internal file system so you don't need to repeat code for different WiFi applications. The code is adapted from code on github: here.

This file is trivial but useful containing 2 lines:

SSID = "ssid" PASSWORD = "password"

Save the above code as credentials.lua (change to your ssid and password).

ESP8266 Webserver Application

This is where "you" put your application code. in the example below the first set of functions performs the blink operation and the bottom one generates the html page.

-- ESP8266 webserver -- This allows control of the builtin LED - On/Off/Blink/Blink Off. -- local outpin=4 local state = false function init() gpio.mode(outpin,gpio.OUTPUT) gpio.write(outpin,gpio.HIGH) -- LED is pulled so HIGH = off blinkOFF() end function togLED() if state==false then gpio.write(outpin,gpio.HIGH) else gpio.write(outpin,gpio.LOW) end state = not state;
end function blkinkON() if mytimer~=nil then return end -- Timer already on. mytimer = tmr.create() mytimer:alarm(200, tmr.ALARM_AUTO, function() togLED() end) end function blinkOFF() if mytimer==nil then return end -- Timer already off. mytimer:unregister() mytimer=nil end srv=net.createServer(net.TCP) init() srv:listen(80,function(conn) conn:on("receive",function(conn,payload) print(payload) -- View the received data, function controlLED() control = string.sub(payload,fnd[2]+1) -- Data is at end already. if control == "ON" then gpio.write(outpin,gpio.LOW); blinkOFF() return end if control == "OFF" then gpio.write(outpin,gpio.HIGH); blinkOFF() return end if control == "Blink" then blkinkON() return end if control == "Blinkoff" then blinkOFF() return end end --get control data from payload fnd = {string.find(payload,"ledbi=")} if #fnd ~= 0 then controlLED() end -- Is there data in payload? - Take action if so. conn:send('<!DOCTYPE HTML>\n') conn:send('<html>\n') conn:send('<head><meta http-equiv="content-type" content="text/html; charset=UTF-8">\n') -- Scale the viewport to fit the device. conn:send('<meta name="viewport" content="width=device-width, initial-scale=1">') -- Title conn:send('<title>ESP8266 Wifi LED Control</title>\n') -- CSS style definition for submit buttons conn:send('<style>\n') conn:send('input[type="submit"] {\n') conn:send('color:#050; width:70px; padding:10px;\n') conn:send('font: bold 84% "trebuchet ms",helvetica,sans-serif;\n') conn:send('background-color:lightgreen;\n') conn:send('border:1px solid; border-radius: 12px;\n') conn:send('transition-duration: 0.4s;\n') conn:send('}\n') conn:send('input[type="submit"]:hover {\n') conn:send('background-color:lightblue;\n') conn:send('color: white;\n') conn:send('}') conn:send('</style></head>\n') -- HTML body Page content. conn:send('<body>') conn:send('<h1>Control of nodeMCU<br>(ESP8266-E12) Built in LED.</h1>\n') conn:send('<p>The built in LED for NodeMCU V3 is on D4</p>\n') -- HTML Form (POST type) and buttons. conn:send('<form action="" method="POST">\n') conn:send('<input type="submit" name="ledbi" value="ON" > Turn Built in LED on<br><br>\n') conn:send('<input type="submit" name="ledbi" value="OFF"> Turn Built in LED off<br><br>\n') conn:send('<input type="submit" name="ledbi" value="Blink"> Blink LED<br><br>\n') conn:send('<input type="submit" name="ledbi" value="Blinkoff"> Stop LED Blink</form>\n') conn:send('</body></html>\n') conn:on("sent",function(conn) conn:close() end)
end) end)

Save the above code as application.lua

The application code is run after the webserver is started in init.lua. It has the following functions:

- Initialises the port to drive the built in LED.
- Toggle the state of the LED ( LED on is output low ).
blinkOFF() - Turn off and unregister the blink timer.
- Decodes the data within the payload to control the led.

The line:


...creates a TCP Transfer Control Protocol server.

...sets the server to listen on port 80 for html data:


Passes parameters 'conn' & 'payload' to the inline function when the ESP8266 receives a data on port 80

The inline function is activated which checks if the payload has the string 'ledbi=' within - if it does, then the function controlLED is used to control the LED. Data used, is the data following the string 'ledbi=' which is available following this string to the end of the payload.

The rest of this function serves up the HTML page that the browser can display

The line

   <meta name="viewport" content="width=device-width, initial-scale=1">

.. allows a browser or mobile to show a reasonable sized page i.e. it scales to the viewport.

The CSS style after the comment

   -- CSS style definition for submit buttons used to style the submit button for size a and rounded corners and lightgreen background.

The section commeted as:

   -- HTML Form (POST type) and buttons.

Is where the buttons are defined in a <form> section and where the values to be sent on each button press are specified.

ESP8266 Webserver LEDBlinkn

local LEDpin = 4 local LEDState = false local n = 0 local _nStart = 0 local _togDelay local _interval local blinkTimer = tmr.create() local blinkInterval = tmr.create() gpio.mode(4,gpio.OUTPUT) gpio.write(LEDpin,gpio.HIGH) -- high is off for builtin LED. -- Blink n times with delay between on-off then wait interval - repeat function LEDBlinkN(numTog,togDelay,interval) -- Get round not having statics if blinkTimer:state() ~= nil then LEDBlinkStop() end-- Stop if was previously started. _nStart = numTog _togDelay = togDelay _interval = interval doInterval() end function LEDBlinkStop() if blinkTimer:state() ~= nil then blinkTimer:unregister() blinkInterval:unregister() end gpio.write(LEDpin,gpio.HIGH) LEDState = false n = 0 end function toggleLEDbuiltin() if blinkTimer:state() ~= nil and n <=0 then blinkTimer:unregister() return end if (LEDstate) then gpio.write(LEDpin,gpio.LOW) else gpio.write(LEDpin,gpio.HIGH) -- high is off for builtin LED. n = n -1 end LEDstate = not LEDstate end -- Fast toggle function LEDBlinkNStart() n = _nStart -- Initialise the toggle number blinkTimer:alarm(_togDelay, tmr.ALARM_AUTO, function () toggleLEDbuiltin() end) end -- Interval timer function doInterval() LEDBlinkNStart() -- Start with the led toggle before the interval blinkInterval:alarm(_interval, tmr.ALARM_AUTO, function () LEDBlinkNStart() end) end --test --LEDBlinkN_setNumBlinks(50,4) --LEDBlinkN(3,50,1500)

Save the above code as LEDBlinkn.lua

The LEDBlinkn code has the following functions:

togDelay, interval)
- The user code to stat a blink. numTog is the number of initial on/off flashes. togDelay defines the on and off period. interval defines the time until re-starting the sequence i.e. multiple flash followed by interval delay. the operation continues until stopped by LEDBlinkStop() or is reset to a different sequence  using LEDBlinkN().
- Stop the blink aciton.
toggleLEDbuiltin() - Toggle the built in LED.
- This is the fast flash timer - code flashes the LED n times.
- This timer times the interval calling LEDBlinkNStart after the delay. It also calls LEDBlinkNStartat the beginning so you get something displayed at the start of a sequence.

A typical example is


This creates 3 on/off flashes with period 50 * 2 * 3 = 300ms but also starts the interval timer for a 550ms delay - this is the total time ofthe sequence so numTog  * togDelay must be smaller than interval - you could update the code to print an erro if this is not the case.

This gives a blip of 3 fast flashes followed by the delay and is used to warn of the 3 second timeout.

For an even M:S use:


On for 100ns and Off for 100 ms, total interval time 200ms.

ESP8266 Webserver Init

I modified init.lua slightly to give you a function to stop the webserver after power up (there's a 3 second window to allow this - and no obvious way to stop it).[ you can use wifi.sta.disconnect() as well ]

--nodemcu_test_startup -- load credentials, 'SSID' and 'PASSWORD' declared and initialize in there dofile("credentials.lua") dofile("LEDBlinkn.lua") function startup() if"init.lua") == nil then print("init.lua deleted or renamed") else print("Running") file.close("init.lua") LEDBlinkStop() file.close("LEDBlinkn.lua") -- the actual application is stored in 'application.lua' dofile("application.lua") end end -- Define WiFi station event callbacks wifi_connect_event = function(T) print("Connection to AP("..T.SSID..") established!") print("Waiting for IP address...") if disconnect_ct ~= nil then disconnect_ct = nil end LEDBlinkN(1,100,200) -- faster blinks end wifi_got_ip_event = function(T) -- Note: Having an IP address does not mean there is internet access! -- Internet connectivity can be determined with net.dns.resolve(). print("Wifi connection is ready! IP address is: "..T.IP) print("Startup will resume momentarily, you have 3 seconds to abort.") print("Waiting...") startUpTimer = tmr.create() -- JFM mod to allow abort startUpTimer:alarm(3000, tmr.ALARM_SINGLE, startup) LEDBlinkN(3,50,550) -- fast warning 3, delay end function stopWiFi() -- JFM LEDBlinkStop() startUpTimer:stop() startUpTimer:unregister() end wifi_disconnect_event = function(T) LEDBlinkN(2,100,700) -- double flash with delay = error if T.reason == wifi.eventmon.reason.ASSOC_LEAVE then --the station has disassociated from a previously connected AP return end -- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration. local total_tries = 75 print("\nWiFi connection to AP("..T.SSID..") has failed!") --There are many possible disconnect reasons, the following iterates through --the list and returns the string corresponding to the disconnect reason. for key,val in pairs(wifi.eventmon.reason) do if val == T.reason then print("Disconnect reason: "..val.."("..key..")") break end end if disconnect_ct == nil then disconnect_ct = 1 else disconnect_ct = disconnect_ct + 1 end if disconnect_ct < total_tries then print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")") else wifi.sta.disconnect() print("Aborting connection to AP!") disconnect_ct = nil end end -- Register WiFi Station event callbacks wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, wifi_connect_event) wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, wifi_got_ip_event) wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, wifi_disconnect_event) LEDBlinkN(1,200,400) -- Idle print("Connecting to WiFi access point...") wifi.setmode(wifi.STATION) wifi.sta.config({ssid=SSID, pwd=PASSWORD}) -- wifi.sta.connect() not necessary because config() uses auto-connect=true by default

Save the above code as init.lua

The above code is a very good example of event driven programming (source here)

The LEDBlinkN() functions display WiFI connection status using the builtin LED. In addition you can use the function stopWiFi() during the 3 second delay period to halt init.lua. You can also erase the init.lua file from the ESP8266 to do the same (using the ESPlorer interface) except blinking LEDs will keep blinking.

The other useful thing about the above code is that it will tell you why a connection failed so you get a lot of information to the serial terminal from it.

<<< Back to Lua Examples

New! Comments

Have your say about what you just read! Leave me a comment in the box below.

Privacy Policy | Contact | About Me

Site Map | Terms of Use

Visit our Facebook Page:
To Visit Click Here

Recent Articles

  1. ACS758 secrets...

    Using the ACS758 to measure very high current

    Read more

  2. DigitalWrite and equivalent fast macros. Speed up your code!

    Find out how digitalWrite() works...Now use 17x Faster macros!

    Read more

  3. ACS712 secrets...

    Using the ACS712 to measure high current

    Read more

  4. MAX6675 Simple Arduino Tutorial for measuring up to 1024°C

    Showing you the easiest way to setup your MAX6675 and Type-K thermocouple with an error table showing you how to get the best results

    Read more

  5. Secrets of Arduino millis: How it works and how to use it.

    How to use Arduino millis() to make delays, one-shots and simple schedulers plus simple analysis of arduino millis() code; Find out how it works.

    Read more

  6. INA219: A voltage and current sensing chip using I2C

    INA219: How to use the INA210 for maximum accuracy in current and power measurements

    Read more

Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's


- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan


"Awesome site,
very, very easy and nice
to navigate!"

- Matt

Learn Microcontrollers

"Interested in

Sign up for The
Free 7 day guide:


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."

- Dave


"Your site is a great
and perfect work.

- Suresh


"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.

- Anon

Back to Top