Do programowania ESP32 w domu zwykle używa się ESPHome, Tasmoty albo środowiska Arduino. Jest jednak jeszcze jedna możliwość, wygodna dla osób znających Pythona: MicroPython. To interpreter Pythona działający bezpośrednio na mikrokontrolerze. Polecenia wpisujesz w interaktywnej konsoli (REPL), wyniki widzisz od razu, a pliki .py kopiujesz na płytkę jak na pendrive’a. W tym poradniku wgrasz MicroPythona na klasyczną płytkę ESP32, odczytasz czujnik DHT22 i wyślesz odczyty do brokera MQTT razem z ostatnią wolą. Wszystkie narzędzia i funkcje pochodzą z oficjalnej dokumentacji.

Czego potrzebujesz

  • Płytki rozwojowej ESP32 z co najmniej 4 MiB pamięci flash (większość płytek z modułem WROOM).
  • Kabla USB do transmisji danych, a nie tylko do ładowania.
  • Czujnika DHT22 (AM2302). MicroPython ma do niego wbudowany sterownik, dzięki czemu pierwszy projekt jest prostszy. Jeśli zależy ci na dokładności, zajrzyj do naszego porównania czujników temperatury i wilgotności.
  • Brokera MQTT w sieci lokalnej, na przykład Mosquitto.
  • Pythona 3 na komputerze.

Zainstaluj narzędzia

Do wszystkiego wystarczą dwa narzędzia wiersza poleceń:

pip install esptool mpremote

esptool to narzędzie Espressif do wgrywania oprogramowania. mpremote to oficjalne narzędzie MicroPythona do obsługi konsoli REPL, plików i pakietów.

Uwaga na wersje esptool: od wersji 5 polecenie nazywa się esptool (a nie esptool.py), a w nazwach poleceń zamiast podkreślników są łączniki, na przykład write-flash zamiast write_flash. Według przewodnika migracji Espressif stare nazwy nadal działają z ostrzeżeniem, ale znikną w następnej wersji głównej. Na stronie pobierania MicroPythona wciąż widnieje stara forma; poniższe polecenia używają nowej.

Pobierz i wgraj oprogramowanie

Otwórz stronę pobierania MicroPythona dla ESP32 / WROOM (ESP32_GENERIC) i pobierz najnowszy plik .bin z pierwszej sekcji. Na stronie są też warianty dla płytek z pamięcią SPIRAM (WROVER), układów jednordzeniowych, układów D2WD z 2 MiB pamięci flash i płytek wymagających aktualizacji przez sieć (OTA). Jeśli nie masz pewności, dla większości płytek właściwy będzie wariant podstawowy. Inne układy (ESP32-S2, -S3, -C3, -C6) mają osobne strony pobierania.

Podłącz płytkę i wyczyść pamięć flash. Przy pierwszej instalacji MicroPythona jest to konieczne:

esptool erase-flash

esptool zwykle sam znajduje port szeregowy. Jeśli go nie znajdzie albo podłączonych jest kilka płytek, dodaj --port, na przykład --port /dev/ttyUSB0 w Linuksie lub --port COM4 w Windows.

Następnie wgraj oprogramowanie. Dla klasycznego ESP32 na stronie pobierania podano adres 0x1000:

esptool --baud 460800 write-flash 0x1000 ESP32_GENERIC-DATA-WERSJA.bin

Zamień nazwę pliku na pobraną. Jeśli wgrywanie przerwie się w połowie, na stronie MicroPythona zalecono usunięcie --baud 460800, żeby wgrać oprogramowanie z wolniejszą, domyślną prędkością. Jeśli esptool w ogóle nie może nawiązać połączenia, w samouczku MicroPythona dla ESP32 opisano ręczne przejście w tryb pobierania: przytrzymaj przycisk BOOT (lub IO0), naciśnij i puść przycisk reset, a potem puść BOOT.

Przywitaj się w konsoli REPL

Uruchom mpremote bez argumentów. Narzędzie łączy się z pierwszym znalezionym urządzeniem szeregowym USB i otwiera konsolę REPL (jeśli używasz innego programu terminalowego, prędkość to 115200 bodów). Naciśnij Enter, a powinien pojawić się znak zachęty >>>:

>>> import sys
>>> sys.implementation

Z mpremote wychodzi się skrótem Ctrl+] lub Ctrl+X.

Połącz się z Wi-Fi

W skróconym opisie MicroPythona dla ESP32 podano taki wzorzec łączenia z siecią:

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'))

Według tego opisu po wywołaniu connect() ESP32 domyślnie bez końca ponawia próby połączenia, nawet przy złym haśle albo braku punktu dostępowego w zasięgu.

Odczytaj DHT22

Podłącz czujnik: VCC do 3,3 V, GND do GND, a DATA do GPIO4. Moduły na małej płytce zwykle mają już rezystor podciągający na linii DATA; sam czujnik wymaga dodania takiego rezystora między DATA a 3,3 V. Następnie w konsoli REPL:

import dht, machine
d = dht.DHT22(machine.Pin(4))
d.measure()
d.temperature(), d.humidity()

Te same wywołania znajdziesz w skróconym opisie. Między pomiarami zachowaj co najmniej 2 sekundy, bo taki jest okres pomiarowy czujnika.

Zainstaluj klienta MQTT

Klient MQTT umqtt.simple jest częścią micropython-lib. Zainstalujesz go z komputera poleceniem:

mpremote mip install umqtt.simple

mpremote pobiera pakiet i kopiuje go na płytkę przez USB, więc na tym etapie płytka nie potrzebuje dostępu do internetu.

Warto znać dwa ograniczenia, oba widoczne w kodzie źródłowym biblioteki: umqtt.simple nie łączy się ponownie automatycznie i nie obsługuje publikowania z QoS 2. Dla czujników QoS 0 i 1 w zupełności wystarczą. Do automatycznego ponawiania połączenia służy osobny pakiet umqtt.robust; w przykładzie poniżej błędy kończą się po prostu ponownym uruchomieniem płytki.

Cały program

Po starcie płytki MicroPython wykonuje najpierw boot.py, a potem main.py, więc program umieść w main.py. Uzupełnij nazwę sieci, hasło, adres brokera i konto MQTT:

import time, machine, network, dht
from umqtt.simple import MQTTClient

WIFI_SSID = 'nazwa-sieci'
WIFI_KEY = 'haslo-wifi'
BROKER = '192.168.1.10'
MQTT_USER = 'sensor'
MQTT_PASSWORD = 'haslo-mqtt'
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()

Co robi program:

  • Ostatnia wola. Przed połączeniem klient rejestruje w temacie stanu zachowywaną wiadomość offline. Jeśli płytka straci zasilanie albo Wi-Fi, broker opublikuje ją po upływie czasu podtrzymania. Zaraz po połączeniu program publikuje zachowywane online.
  • Zachowywane odczyty. Panel uruchomiony później od razu dostaje ostatnią temperaturę i wilgotność.
  • Czas podtrzymania 120 sekund przy odczycie co 60 sekund, więc regularne publikacje podtrzymują połączenie bez dodatkowych pakietów kontrolnych.
  • Prosta obsługa błędów. Każda awaria (Wi-Fi, broker, czujnik) kończy się ponownym uruchomieniem po 10 sekundach. To rozwiązanie toporne, ale niezawodne w czujniku, który nie ma innych zadań.

Skopiuj plik na płytkę i uruchom ją ponownie:

mpremote cp main.py :main.py
mpremote reset

Obserwuj wiadomości na komputerze:

mosquitto_sub -h 192.168.1.10 -u UŻYTKOWNIK -P 'HASŁO' -t 'home/livingroom/esp32/#' -v

Odłącz płytkę od zasilania, a w ciągu około trzech minut (półtora czasu podtrzymania) stan zmieni się na offline.

Co dalej

  • Przechowuj hasła do Wi-Fi i MQTT w osobnym pliku na płytce zamiast w main.py, żeby móc udostępniać kod bez nich.
  • Podczas pracy nad kodem używaj mpremote mount .: lokalny katalog jest wtedy widoczny na płytce jako /remote i nie trzeba kopiować plików po każdej zmianie.
  • Przy zasilaniu bateryjnym użyj machine.deepsleep(). Płytka uruchamia się wtedy od nowa po każdym uśpieniu i wykonuje main.py od początku, więc program trzeba zbudować inaczej.
  • Jeśli później wolisz plik konfiguracyjny zamiast kodu, na tej samej płytce można uruchomić ESPHome albo Tasmotę. MicroPython sprawdza się lepiej, gdy zależy ci na pełnej kontroli w znanym już języku.