ESPHome, Tasmota and the Arduino framework are the usual ways to program an ESP32 for the home, but there is another option that suits people who already know Python: MicroPython. It runs a Python interpreter directly on the microcontroller. You type commands into an interactive prompt (the REPL), see results immediately and copy .py files to the board like files on a USB stick. This guide flashes MicroPython onto a classic ESP32 board, reads a DHT22 sensor and publishes the readings to an MQTT broker with a Last Will, using only tools and APIs from the official documentation.
What you need
- An ESP32 development board with at least 4 MiB of flash (most WROOM-based boards).
- A USB cable that carries data, not only power.
- A DHT22 (AM2302) sensor. MicroPython has a built-in driver for it, which keeps this first project simple. For better accuracy, see our comparison of temperature and humidity sensors.
- An MQTT broker on your network, for example Mosquitto.
- Python 3 on your computer.
Install the tools
Two command-line tools do all the work:
pip install esptool mpremote
esptool is Espressif's flashing tool. mpremote is the official MicroPython tool for the REPL, files and packages.
A note on esptool versions: since version 5, the command is esptool (not esptool.py) and command names use hyphens, for example write-flash instead of write_flash. Espressif's migration guide says the old names still work with a deprecation warning but will be removed in the next major release. The MicroPython download page still shows the old form; the commands below use the new one.
Download and flash the firmware
Open the MicroPython download page for ESP32 / WROOM (ESP32_GENERIC) and download the latest .bin file from the first section. The page lists variants for boards with SPIRAM (WROVER), single-core chips, 2 MiB D2WD chips and boards that need over-the-air updates. If you are not sure, the generic variant is the right choice for most boards. Other chips (ESP32-S2, -S3, -C3, -C6) have their own download pages.
Connect the board and erase the flash. This is required the first time you install MicroPython:
esptool erase-flash
esptool usually finds the serial port by itself. If it does not, or if several boards are connected, add --port, for example --port /dev/ttyUSB0 on Linux or --port COM4 on Windows.
Then write the firmware. For the classic ESP32 the download page specifies the address 0x1000:
esptool --baud 460800 write-flash 0x1000 ESP32_GENERIC-DATE-VERSION.bin
Replace the file name with the one you downloaded. If flashing fails partway through, the MicroPython page recommends removing --baud 460800 to flash at the slower default speed. If esptool cannot connect, the MicroPython ESP32 tutorial describes entering download mode by hand: hold the BOOT (or IO0) button, press and release reset, then release BOOT.
Say hello in the REPL
Run mpremote without arguments. It connects to the first USB serial device it finds and opens the REPL (the serial speed is 115200 baud if you use another terminal program). Press Enter and you should see the >>> prompt:
>>> import sys
>>> sys.implementation
Exit mpremote with Ctrl+] or Ctrl+X.
Connect to Wi-Fi
The MicroPython quick reference for the ESP32 gives this pattern for joining a network:
import network
wlan = network.WLAN()
wlan.active(True)
if not wlan.isconnected():
wlan.connect('ssid', 'key')
while not wlan.isconnected():
pass
print('network config:', wlan.ipconfig('addr4'))
According to the quick reference, after connect() the ESP32 by default keeps retrying forever, even when the password is wrong or no access point is in range.
Read the DHT22
Wire the sensor: VCC to 3.3 V, GND to GND and DATA to GPIO4. Modules on a small board usually include a pull-up resistor on DATA; a bare sensor needs one between DATA and 3.3 V. Then, in the REPL:
import dht, machine
d = dht.DHT22(machine.Pin(4))
d.measure()
d.temperature(), d.humidity()
The quick reference shows the same calls. Keep at least 2 seconds between measurements; that is the sensor's sampling period.
Install the MQTT client
The MQTT client umqtt.simple lives in micropython-lib. Install it from your computer with:
mpremote mip install umqtt.simple
mpremote downloads the package and copies it to the board over USB, so the board does not need internet access for this step.
Two limitations are worth knowing, both visible in the library source: umqtt.simple does not reconnect automatically, and it does not support publishing with QoS 2. QoS 0 and 1 are enough for sensors. For automatic reconnection there is a separate umqtt.robust package; the example below handles failures by restarting the board instead.
The complete program
MicroPython runs boot.py and then main.py when the board starts, so put the program in main.py. Adjust the network name, password, broker address and MQTT account:
import time, machine, network, dht
from umqtt.simple import MQTTClient
WIFI_SSID = 'your-network'
WIFI_KEY = 'your-wifi-password'
BROKER = '192.168.1.10'
MQTT_USER = 'sensor'
MQTT_PASSWORD = 'mqtt-password'
BASE = b'home/livingroom/esp32'
def wifi():
wlan = network.WLAN()
wlan.active(True)
if not wlan.isconnected():
wlan.connect(WIFI_SSID, WIFI_KEY)
for _ in range(30):
if wlan.isconnected():
break
time.sleep(1)
if not wlan.isconnected():
raise OSError('no wifi')
def main():
wifi()
sensor = dht.DHT22(machine.Pin(4))
client = MQTTClient('esp32-livingroom', BROKER, user=MQTT_USER,
password=MQTT_PASSWORD, keepalive=120)
client.set_last_will(BASE + b'/status', b'offline', retain=True)
client.connect()
client.publish(BASE + b'/status', b'online', retain=True)
while True:
sensor.measure()
client.publish(BASE + b'/temperature', str(sensor.temperature()).encode(), retain=True)
client.publish(BASE + b'/humidity', str(sensor.humidity()).encode(), retain=True)
time.sleep(60)
try:
main()
except Exception as e:
print('error:', e)
time.sleep(10)
machine.reset()
What the program does:
- Last Will. Before connecting, the client registers a retained
offlinemessage on the status topic. If the board loses power or Wi-Fi, the broker publishes it after the keep-alive time runs out. Right after connecting, the program publishes a retainedonline. - Retained readings. A dashboard that starts later gets the latest temperature and humidity immediately.
- Keep-alive of 120 seconds with a reading every 60 seconds, so regular publishes keep the connection alive without extra pings.
- Simple error handling. Any failure (Wi-Fi, broker, sensor) ends in a restart after 10 seconds. That is crude but reliable for a sensor that has no other job.
Copy the file to the board and reset it:
mpremote cp main.py :main.py
mpremote reset
Watch the messages on your computer:
mosquitto_sub -h 192.168.1.10 -u USER -P 'PASSWORD' -t 'home/livingroom/esp32/#' -v
Unplug the board and, within about three minutes (one and a half times the keep-alive), the status changes to offline.
Where to go next
- Store the Wi-Fi and MQTT passwords in a separate file on the board instead of in
main.py, so you can share the code without them. - Use
mpremote mount .while developing: the board sees your local directory as/remoteand you avoid copying files after each change. - Use
machine.deepsleep()for battery power. The board then restarts after each sleep and runsmain.pyfrom the beginning, so the program structure changes. - If you later want a configuration file instead of code, the same board can run ESPHome or Tasmota. MicroPython is the better fit when you want full control in a language you already know.