ESP32/ESP8266 MicroPython Web Server – Control Outputs

Learn how to build a web server to control the ESP32 or ESP8266 outputs using MicroPython framework. As an example we’ll build a web server with ON and OFF buttons to control the on-board LED of the ESP32/ESP8266. We’ll use sockets and the Python socket API.

esp32 web server with micropython on smartphone

Prerequisites

To program the ESP32 and ESP8266 with MicroPython, we use uPyCraft IDE as a programming environment. Follow the next tutorials to install uPyCraft IDE and flash MicroPython firmware on your board:

If this is your first time dealing with MicroPython you may find these next tutorials useful:

Parts required

For this tutorial you need an ESP32 or ESP8266 board:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Preparing the Files

Connect the ESP32 or ESP8266 board to your computer. Open uPyCraft IDE, and go to Tools > Serial and select the serial port.

uPyCraft IDE selecting serial port

You should see the files on the ESP32/ESP8266 board on the device folder. By default, when you burn MicroPython firmware, a boot.py file is created.

For this project you’ll need a boot.py file and a main.py file. The boot.py file has the code that only needs to run once on boot. This includes importing libraries, network credentials, instantiating pins, connecting to your network, and other configurations.

The main.py file will contain the code that runs the web server to serve files and perform tasks based on the requests received by the client.

Creating the main.py file on your board

1. Press the “New file” button to create a new file.

2. Press the “Save file” button to save the file in your computer.

3. A new window opens, name your file main.py and save it in your computer:

4. After that, you should see the following in your uPyCraft IDE (the boot.py file in your device and a new tab with the main.py file):

5. Click the “Download and run” button to upload the file to your ESP board:

6. The device directory should now load the main.py file. Your ESP has the file main.py stored.

uPycraft IDE creating main.py file

boot.py

Copy the following code to the ESP32/ESP8266 boot.py file.

# Complete project details at https://RandomNerdTutorials.com

try:
  import usocket as socket
except:
  import socket

from machine import Pin
import network

import esp
esp.osdebug(None)

import gc
gc.collect()

ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'

station = network.WLAN(network.STA_IF)

station.active(True)
station.connect(ssid, password)

while station.isconnected() == False:
  pass

print('Connection successful')
print(station.ifconfig())

led = Pin(2, Pin.OUT)

View raw code

As mentioned previously, we create our web server using sockets and the Python socket API. The official documentation imports the socket library as follows:

try:
  import usocket as socket
except:
  import socket

We need to import the Pin class from the machine module to be able to interact with the GPIOs.

from machine import Pin

After importing the socket library, we need to import the network library. The network library allows us to connect the ESP32 or ESP8266 to a Wi-Fi network.

import network

The following lines turn off vendor OS debugging messages:

import esp
esp.osdebug(None)

Then, we run a garbage collector:

import gc
gc.collect()

A garbage collector is a form of automatic memory management. This is a way to reclaim memory occupied by objects that are no longer in used by the program. This is useful to save space in the flash memory.

The following variables hold your network credentials:

ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'replace_with_your_password'

You should replace the words highlighted in red with your network SSID and password, so that the ESP is able to connect to your router.

Then, set the ESP32 or ESP8266 as a Wi-Fi station:

station = network.WLAN(network.STA_IF)

After that, activate the station:

station.active(True)

Finally, the ESP32/ESP8266 connects to your router using the SSID and password defined earlier:

station.connect(ssid, password)

The following statement ensures that the code doesn’t proceed while the ESP is not connected to your network.

while station.isconnected() == False:
  pass

After a successful connection, print network interface parameters like the ESP32/ESP8266 IP address – use the ifconfig() method on the station object.

print('Connection successful')
print(station.ifconfig())

Create a Pin object called led that is an output, that refers to the ESP32/ESP8266 GPIO2:

led = Pin(2, Pin.OUT)

main.py

Copy the following code to the ESP32/ESP8266 main.py file.

# Complete project details at https://RandomNerdTutorials.com

def web_page():
  if led.value() == 1:
    gpio_state="ON"
  else:
    gpio_state="OFF"
  
  html = """<html><head> <title>ESP Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
  h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none; 
  border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
  .button2{background-color: #4286f4;}</style></head><body> <h1>ESP Web Server</h1> 
  <p>GPIO state: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
  <p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""
  return html

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)

while True:
  conn, addr = s.accept()
  print('Got a connection from %s' % str(addr))
  request = conn.recv(1024)
  request = str(request)
  print('Content = %s' % request)
  led_on = request.find('/?led=on')
  led_off = request.find('/?led=off')
  if led_on == 6:
    print('LED ON')
    led.value(1)
  if led_off == 6:
    print('LED OFF')
    led.value(0)
  response = web_page()
  conn.send('HTTP/1.1 200 OK\n')
  conn.send('Content-Type: text/html\n')
  conn.send('Connection: close\n\n')
  conn.sendall(response)
  conn.close()

View raw code

The script starts by creating a function called web_page(). This function returns a variable called html that contains the HTML text to build the web page.

def web_page():

The web page displays the current GPIO state. So, before generating the HTML text, we need to check the LED state. We save its state on the gpio_state variable:

if led.value() == 1:
  gpio_state="ON"
else:
  gpio_state="OFF"

After that, the gpio_state variable is incorporated into the HTML text using “+” signs to concatenate strings.

html = """<html><head> <title>ESP Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none; 
border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
.button2{background-color: #4286f4;}</style></head><body> <h1>ESP Web Server</h1> 
<p>GPIO state: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
<p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""

Creating a socket server

After creating the HTML to build the web page, we need to create a listening socket to listen for incoming requests and send the HTML text in response. For a better understanding, the following figure shows a diagram on how to create sockets for server-client interaction:

python socket server and client

Create a socket using socket.socket(), and specify the socket type. We create a new socket object called s with the given address family, and socket type. This is a STREAM TCP socket:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Next, bind the socket to an address (network interface and port number) using the bind() method. The bind() method accepts a tupple variable with the ip address, and port number:

s.bind(('', 80))

In our example, we are passing an empty string ‘ ‘ as an IP address and port 80. In this case, the empty string refers to the localhost IP address (this means the ESP32 or ESP8266 IP address).

The next line enables the server to accept connections; it makes a “listening” socket. The argument specifies the maximum number of queued connections. The maximum is 5.

s.listen(5)

In the while loop is where we listen for requests and send responses. When a client connects, the server calls the accept() method to accept the connection. When a client connects, it saves a new socket object to accept and send data on the conn variable, and saves the client address to connect to the server on the addr variable.

conn, addr = s.accept()

Then, print the address of the client saved on the addr variable.

print('Got a connection from %s' % str(addr))

The data is exchanged between the client and server using the send() and recv() methods.

The following line gets the request received on the newly created socket and saves it in the request variable.

request = conn.recv(1024)

The recv() method receives the data from the client socket (remember that we’ve created a new socket object on the conn variable). The argument of the recv() method specifies the maximum data that can be received at once.

The next line simply prints the content of the request:

print('Content = %s' % str(request))

Then, create a variable called response that contains the HTML text returned by the web_page() function:

response = web_page()

Finally, send the response to the socket client using the send() and sendall() methods:

conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.sendall(response)

In the end, close the created socket.

conn.close()

Testing the Web Server

Upload the main.py and boot.py files to the ESP32/ESP8266. Your device folder should contain two files: boot.py and main.py.

After uploading the files, press the ESP EN/RST on-board button.

esp32 enable button

After a few seconds, it should establish a connection with your router and print the IP address on the Shell.

Open your browser, and type your ESP IP address you’ve just found. You should see the web server page as shown below.

esp32 web server control outputs

When you press the ON button, you make a request on the ESP IP address followed by /?led=on. The ESP32/ESP8266 on-board LED turns on, and the GPIO state is updated on the page.

Note: some ESP8266 on-board LEDs turn on the LED with an OFF command, and turn off the LED with the ON command.

web server on smartphone and esp32

When you press the OFF button, you make a request on the ESP IP address followed by /?led=off. The LED turns off, and the GPIO state is updated.

Note: to keep this tutorial simple, we’re controlling the on-board LED that corresponds to GPIO 2. You can control any other GPIO with any other output (a relay, for example) using the same method. Also, you can modify the code to control multiple GPIOs or change the HTML text to create a different web page.

Wrapping Up

This tutorial showed you how to build a simple web server with MicroPython firmware to control the ESP32/ESP8266 GPIOs using sockets and the Python socket library. If you’re looking for a web server tutorial with Arduino IDE, you can check the following resources:

If you’re looking for more projects with the ESP32 and ESP8266 boards, you can take a look at the following:

We hope you’ve found this article about how to build a web server with MicroPython useful. To learn more about MicroPython, take a look at our eBook: MicroPython Programming with ESP32 and ESP8266.


Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »

Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our weekly newsletter!

34 thoughts on “ESP32/ESP8266 MicroPython Web Server – Control Outputs”

  1. Hello Sara,

    this explanation is pretty detailed. IMHO it is a good compromise between explaining it all from scratch and “drop a short textline”

    I want to make some suggestions how to improve the explanation.
    Add “ESP32” to word “server” and the words “your smartphone, tablet or PC” to the word “client” at the grafic which shows the steps what server and client are doing.

    The result a webpage that can be clicked to switch on/off a LED is impressing. To achieve this a lot of code-technologies have to be used. I don’t want to suggest explain all
    – server-client,
    – TCP/IP,
    – sockets,
    – HTML
    all from scratch. That would inflate the explanation much too much.

    But there are a few places where a small effort gives pretty much more insight into using all these coding-technologies.

    I mean find a way that the parts of the html-code that defines the buttons and the text on the buttons is very easy to find and easy to modify.

    Another way could be using some kind of a website-editor, if there exists one that is easy to use. By “easy to use” I mean a software that is very very similar to an office-software or at least very very intuitive to use.
    So that one hour of learning is enough to create a website-variation of your demo that has three buttons and a slider.

    Then showing how the html-code-output of this software is implemented into the python-code by copy and paste.

    best regards

    Stefan

    Reply
    • Hi Stefan.
      Thank you for your suggestions. We’ll take those into account when writing new posts about this subject.
      Also, I’ll try to improve this article when I have the chance.
      Thank you.
      Regards,
      Sara 🙂

      Reply
  2. Hi Rui,
    I have Linux Mint 19 based on ubuntu … uPyCraft IDE doesn’t work … Is there another way or an improvement pf uPyCraft IDE ?
    Thank you

    Reply
    • Hi Bernard.
      uPyCraft should work on your system if you compile it yourself: github.com/DFRobot/uPyCraft_src
      If you compile it, it should work.
      Note: you don’t need to use uPycraft IDE, you can program program the board with a serial connection using REPL or webREPL.
      Regards,
      Sara 🙂

      Reply
  3. Dear, I’have a lot of problem to use this app with Safari or Google Chrome for iPhone. If I use Google Chrome for mac I don’t have any problem, but when I use Safari I can’t open the page. I see the serial, and I can read the connection by computer to ESP32, but the browser don’t open the contents. Also on Android and Google Chrome all works.

    Reply
    • Hi Giovanni.
      I’m sorry to hear that.
      I actually didn’t test the web server on Safari or iPhone. I’ll try to figure out what happens.
      Meanwhile, if you find what causes the problem, just let us know.
      Regards,
      Sara 🙂

      Reply
  4. good article as usual 🙂
    My question though is why or when would you use python instead of the Arduino ide? What are the benefits etc.
    Also I agree with Stefan on the editor HTML thing might be helpful.

    Reply
    • Hi Bob.
      I think both are good ways to program the ESP32.
      MicroPython is much simpler to program, it supports a REPL (Read-Evaluate-Print Loop). The REPL allows you to connect to a board and execute code quickly without the need to compile or upload code.
      It also gives you a better overview of the files stored on the ESP32. Uploading code to the ESP32 is much faster with MicroPython.
      In terms of libraries for sensors and modules, at the moment there is more support for Arduino IDE.
      Regards,
      Sara 🙂

      Reply
  5. Works perfect on my old laptop ( Ubuntu ) using Chromium or Firefox . Doesn’t work on my IPhone 5S ( tried Safari and Chromium ) . EFM ( electronics F…… magic) ..
    Thanks anyway , great tutorial , step by step explained , Great Job !
    Bob

    Reply
  6. I made a printscreen with the result of “request = conn.recv(1024)” . First logon is the laptop , 2nd one is the Phone . I can mail it if you want it . Thanks again , keep up the good job !
    Bob

    Reply
  7. Did Some research and tried different browsers on my. IPhone .
    Puffin browser replied : Error 102
    (net:: ERR_CONNECTION_REFUSED) when loading URL http://(IP-adress)
    Hope this is a clue ….
    Thanks a lot and keep up the good job !
    Bob

    Reply
  8. I think I followed the steps correctly but when I try to run it I get error messages:

    main.py:4: undefined name ‘led’
    main.py:18: undefined name ‘socket’
    main.py:18: undefined name ‘socket’
    main.py:18: undefined name ‘socket’
    main.py:32: undefined name ‘led’
    main.py:35: undefined name ‘led’

    Any suggestions what I’m doing wrong?

    Thank you.

    Reply
  9. Found a working script for the IPhone , out of the box from the official micropython site :
    https://docs.micropython.org/en/latest/esp8266/tutorial/network_tcp.html#simple-http-server
    I modified boot.py a little ( removed double imports ) , works perfect 🙂
    these lines ( “arduino style”) make the difference I think …
    line = cl_file.readline()
    if not line or line == b’\r\n’:
    Anyway , thanks a lot for helping people to become decent programmers 🙂
    Love you guys , lots of greetings from Belgium ,
    Bob

    Reply
    • Hi Jaroslav.
      In the while loop, after receiving a request, we need to check if the request contains the ‘/?led=on’ or ‘/?led=on’ expressions. For that, we can apply the find() method on the request variable. the find() method returns the lowest index of the substring we are looking for.

      Because the substrings we are looking for are always on index 6, we can add an if statement to detect the content of the request. If the led_on variable is equal to 6, we know we’ve received a request on the /?led=on URL and we turn the LED on.
      If the led_off variable is equal to 6, we’ve received a request on the /?led=off URL and we turn the LED off.

      I hope my explanation is clear.
      Regards,
      Sara 🙂

      Reply
  10. Hi
    Many thanks for your work
    Following this article works fine. But there is tow questions
    The program stops at “conn, addr = s.accept()” waiting for a request, and it can wait for ever.
    Is it possible just to check if there is one and if there is not continue the loop and the check again the next loop. If this is possible you can use the micro controller for other task in between the requests as in the Arduino env
    When connecting with Chrome, on PC or phone, the connection are busy for ever if you do not close the browser
    The server hangs printing Got a connection from (‘192.168.1.193’, 54536) and you can not use any other computer or browser untill it is closed, not just the tab but the whole browser.
    Connecting the a Raspberry pi works fine.
    As it works on RPi it seems to be the PC/phone or Chrome that is the problem. Is there something to do about this. It happen that you forget to close the window and then is it impossible to aces the serve from an other place

    Reply
    • Hi Hans.
      You are right.
      But I don’t think it is something to do with the code. Because when we first implemented this, it worked fine supporting several clients at the same time.
      It seems that google Chrome opens two connections, leaving the second one opened. So we are not able to listen requests from other clients while that connection is opened.
      I don’t know why this is happening, but we’re working to find a solution.
      If you know a workaround for this, please share with us.
      Thank you for your comment.
      Regards,
      Sara

      Reply
  11. As usual, this was a great tutorial. Took me a couple of times to get it to come together. I have a Heltec wifi 32 with oled and wanted the oled to tell me what ip I was connected to. Has anyone else complained that the Upycraft ide crashes windows 10?

    Reply

Leave a Reply to Henk Oegema Cancel reply

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.