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.pyna 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.

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.
offlinepo 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):
$ 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:
listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd
$ 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:
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:
$ 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
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.
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.simplego 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: -2lub-202przyconnect(): zła nazwa brokera, brak internetu albo DNS.MQTTException: 5przyconnect(): broker odrzucił login (złe hasło lub brakMQTT_USER/MQTT_HASLOwsiec.pyprzy własnym brokerze).ECONNRESETpo minucie: brakping()lubcheck_msg()dłużej niż keepalive. Broker uznał klienta za martwego.- Dwie płytki z tym samym
client_id: broker rozłącza starszą. Stądunique_id()w nazwie. mosquitto_subnic nie pokazuje: literówka w temacie (wielkość liter ma znaczenie) albo apostrofy pominięte w terminalu przy#.- Raspberry Pi:
Connection refused: braklistener 1883w konfiguracji (Mosquitto 2 słucha wtedy tylko lokalnie) albo zapora systemowa.
Zadania
- Dodaj do publikowanych danych moc sygnału Wi-Fi (
wlan.status("rssi");siec.polacz()zwraca obiektwlan). - Rozszerz program z 6.5 o temat
.../rgb/setz treścią np.255,0,40, która ustawia kolor diody RGB z lekcji 3. - Postaw własny broker na Raspberry Pi, dopisz dane do
siec.pyi sprawdź, że programy z 6.4 i 6.5 działają bez zmian. - Usuń zapamiętaną (retain) wiadomość:
mosquitto_pub -t '<temat>' -r -nwysył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.