Przejdź do treści
ESP32 od zera: Wi-Fi, BLE i MicroPython
Podgląd za darmo

MQTT: pomiary do brokera i sterowanie z komputera

Cel

Wyślesz pomiary z ESP32 do brokera MQTT, odczytasz je na komputerze i sterujesz diodą, wysyłając wiadomość z komputera. Poznasz tematy, retain i „ostatnią wolę” (Last Will). Wybierzesz broker: publiczny testowy albo własny na Raspberry Pi.

Potrzebne części

  • układ z lekcji 3–5 (dioda na GPIO4, potencjometr na GPIO1, plik siec.py na płytce)
  • komputer z terminalem (albo program MQTT Explorer), opcjonalnie Raspberry Pi

6.1. Jak działa MQTT

W HTTP (lekcja 5) klient pyta serwer i dostaje odpowiedź. W MQTT wszyscy łączą się z jednym pośrednikiem, brokerem:

  • publikujący wysyła wiadomość na temat (topic), np. dom/salon/temperatura;
  • subskrybujący zapisuje się na tematy i broker przekazuje mu każdą nową wiadomość;
  • ESP32 nie musi znać adresu komputera ani telefonu. Wszyscy znają tylko broker.

Schemat MQTT: ESP32 publikuje pomiary na temat .../pomiar do brokera, komputer subskrybuje ten temat; komputer publikuje na .../dioda/set, a ESP32 subskrybuje i włącza diodę

Rys. 6.1. Broker pośredniczy w obie strony. Nikt nie łączy się bezpośrednio z ESP32.

Tematy mają strukturę katalogów oddzielonych /. Przy subskrypcji działają symbole wieloznaczne: + zastępuje jeden poziom (dom/+/temperatura), a # wszystkie poziomy do końca (dom/#).

Dwie opcje, których użyjemy:

  • retain: broker zapamiętuje ostatnią wiadomość z tematu i od razu wysyła ją każdemu nowemu subskrybentowi (np. aktualny stan diody);
  • Last Will: wiadomość, którą broker opublikuje sam, jeśli ESP32 zniknie bez pożegnania (np. offline po odcięciu zasilania).

6.2. Wybór brokera

A. Publiczny broker testowy test.mosquitto.org (port 1883). Działa od razu, bez konta.

🔒 Publiczny broker widzi każdy. Każdy może czytać i wysyłać wiadomości na Twoje tematy, a ruch na porcie 1883 nie jest szyfrowany. Wysyłaj tam tylko dane testowe: żadnych haseł, adresów, lokalizacji ani danych, które mówią, kiedy jesteś w domu. Broker bywa przeciążony i nie gwarantuje działania.

Żeby Twoje wiadomości nie mieszały się z cudzymi, w temacie jest numer Twojej płytki (machine.unique_id()), np. wirelab/kurs-esp32/a1b2c3d4e5f6/pomiar.

B. Własny broker Mosquitto na Raspberry Pi w sieci domowej: dane nie wychodzą z domu, a dostęp jest na hasło. Na Raspberry Pi (Raspberry Pi OS):

bash
$ sudo apt install mosquitto mosquitto-clients
$ sudo mosquitto_passwd -c /etc/mosquitto/passwd esp32
$ sudo chown mosquitto:mosquitto /etc/mosquitto/passwd
$ sudo chmod 600 /etc/mosquitto/passwd
$ sudo nano /etc/mosquitto/conf.d/lan.conf

Treść pliku lan.conf:

tekst
listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd
bash
$ sudo systemctl restart mosquitto
$ hostname -I

Mosquitto od wersji 2.0 bez listener przyjmuje połączenia tylko z samego Raspberry Pi, a bez hasła odrzuca klientów. Ostatnie polecenie pokazuje adres IP Raspberry Pi. Dopisz do siec.py na płytce (Thonny, jak w lekcji 5) trzy linie:

Python
MQTT_HOST = "192.168.1.20"     # adres IP Raspberry Pi
MQTT_USER = "esp32"
MQTT_HASLO = "haslo-z-mosquitto_passwd"

Programy w tej lekcji same sprawdzają, czy te stałe są w siec.py. Jeśli ich nie ma, łączą się z test.mosquitto.org.

🔒 Nie przekierowuj portu 1883 na routerze. Broker bez szyfrowania ma działać tylko w sieci domowej.

6.3. Podgląd na komputerze

Klient wiersza poleceń Mosquitto: w Fedorze sudo dnf install mosquitto, w Debianie i Ubuntu sudo apt install mosquitto-clients, na Windows i macOS instalator z mosquitto.org. Graficzna alternatywa: MQTT Explorer.

Polecenie do podglądu wypisze Ci program z 6.4 (z Twoim tematem). Ogólnie wygląda tak:

bash
$ mosquitto_sub -h test.mosquitto.org -t 'wirelab/kurs-esp32/TWOJE_ID/#' -v

Dla własnego brokera dodaj -h ADRES_RPI -u esp32 -P haslo.

6.4. Publikowanie pomiarów

MicroPython
import json, time, esp32, machine
from machine import ADC, Pin
from umqtt.simple import MQTTClient
import siec

siec.polacz()
ID = machine.unique_id().hex()
BROKER = getattr(siec, "MQTT_HOST", "test.mosquitto.org")
TEMAT = f"wirelab/kurs-esp32/{ID}/pomiar"

klient = MQTTClient("esp32-" + ID, BROKER, keepalive=60,
                    user=getattr(siec, "MQTT_USER", None),
                    password=getattr(siec, "MQTT_HASLO", None))
klient.connect()
print("Połączono z brokerem", BROKER)
print(f"Podgląd: mosquitto_sub -h {BROKER} -t '{TEMAT}' -v")

pot = ADC(Pin(1), atten=ADC.ATTN_11DB)
try:
    for n in range(30):
        dane = {
            "n": n,
            "napiecie": round(pot.read_uv() / 1_000_000, 3),
            "temp_ukladu": esp32.mcu_temperature(),
        }
        klient.publish(TEMAT, json.dumps(dane))
        print("wysłano", dane)
        time.sleep(5)
finally:
    klient.disconnect()

Pomiary w formacie JSON łatwo potem odczyta każdy program: Home Assistant, Node-RED czy skrypt w Pythonie.

6.5. Sterowanie diodą z komputera

Teraz ESP32 subskrybuje temat .../dioda/set, a stan diody publikuje z flagą retain. Last Will ustawia status offline, gdy płytka zniknie.

MicroPython
import time, machine
from machine import Pin
from umqtt.simple import MQTTClient
import siec

siec.polacz()
ID = machine.unique_id().hex()
BROKER = getattr(siec, "MQTT_HOST", "test.mosquitto.org")
BAZA = f"wirelab/kurs-esp32/{ID}"
dioda = Pin(4, Pin.OUT)


def wiadomosc(temat, tresc):
    print("odebrano:", temat, tresc)
    if tresc in (b"1", b"on"):
        dioda.on()
    elif tresc in (b"0", b"off"):
        dioda.off()
    klient.publish(BAZA + "/dioda", str(dioda.value()), retain=True)


klient = MQTTClient("esp32-" + ID, BROKER, keepalive=60,
                    user=getattr(siec, "MQTT_USER", None),
                    password=getattr(siec, "MQTT_HASLO", None))
klient.set_callback(wiadomosc)
klient.set_last_will(BAZA + "/status", "offline", retain=True)
klient.connect()
klient.publish(BAZA + "/status", "online", retain=True)
klient.subscribe(BAZA + "/dioda/set")
print("Wyślij z komputera:")
print(f"  mosquitto_pub -h {BROKER} -t '{BAZA}/dioda/set' -m 1")

ostatni_ping = time.ticks_ms()
try:
    while True:
        klient.check_msg()                  # sprawdza wiadomości, nie blokuje
        if time.ticks_diff(time.ticks_ms(), ostatni_ping) > 30_000:
            klient.ping()                   # utrzymuje połączenie (keepalive 60 s)
            ostatni_ping = time.ticks_ms()
        time.sleep_ms(50)
finally:
    klient.publish(BAZA + "/status", "offline", retain=True)
    klient.disconnect()
    dioda.off()

Na komputerze w jednym terminalu uruchom mosquitto_sub z tematem .../#, a w drugim mosquitto_pub z -m 1 i -m 0. Dioda się przełącza, a w podglądzie widać nowy stan. Odłącz płytkę od USB i poczekaj ok. 1,5 minuty: broker sam opublikuje offline (Last Will), bo przestał dostawać sygnał życia.

6.6. QoS w skrócie

  • QoS 0 (domyślnie): „wyślij i zapomnij”. Wiadomość może zginąć, np. przy zerwanym Wi-Fi.
  • QoS 1: broker potwierdza odbiór, nadawca ponawia do skutku. Wiadomość może dojść dwa razy.
  • QoS 2 (dokładnie raz): umqtt.simple go nie obsługuje.

Do pomiarów wysyłanych co kilka sekund QoS 0 wystarcza. QoS 1 włączysz tak: klient.publish(temat, dane, qos=1).

Typowe błędy

  • OSError: -2 lub -202 przy connect(): zła nazwa brokera, brak internetu albo DNS.
  • MQTTException: 5 przy connect(): broker odrzucił login (złe hasło lub brak MQTT_USER/MQTT_HASLO w siec.py przy własnym brokerze).
  • ECONNRESET po minucie: brak ping() lub check_msg() dłużej niż keepalive. Broker uznał klienta za martwego.
  • Dwie płytki z tym samym client_id: broker rozłącza starszą. Stąd unique_id() w nazwie.
  • mosquitto_sub nic nie pokazuje: literówka w temacie (wielkość liter ma znaczenie) albo apostrofy pominięte w terminalu przy #.
  • Raspberry Pi: Connection refused: brak listener 1883 w konfiguracji (Mosquitto 2 słucha wtedy tylko lokalnie) albo zapora systemowa.

Zadania

  1. Dodaj do publikowanych danych moc sygnału Wi-Fi (wlan.status("rssi"); siec.polacz() zwraca obiekt wlan).
  2. Rozszerz program z 6.5 o temat .../rgb/set z treścią np. 255,0,40, która ustawia kolor diody RGB z lekcji 3.
  3. Postaw własny broker na Raspberry Pi, dopisz dane do siec.py i sprawdź, że programy z 6.4 i 6.5 działają bez zmian.
  4. Usuń zapamiętaną (retain) wiadomość: mosquitto_pub -t '<temat>' -r -n wysyła pustą wiadomość z retain.

Podsumowanie

MQTT łączy urządzenia przez brokera: publikujesz na temat, a subskrybenci dostają wiadomość. umqtt.simple jest wbudowany w MicroPython dla ESP32: connect(), publish(), subscribe() z set_callback() i regularne check_msg() oraz ping(). retain przechowuje ostatni stan, a Last Will ogłasza zniknięcie urządzenia. Publiczny broker jest tylko do testów i widzi go każdy; do prawdziwych danych użyj własnego brokera z hasłem w sieci domowej.

Napisz