Uygulamalı Eğitimler

MiniMQTT ile CircuitPython'da MQTT

Adafruit MiniMQTT ile CircuitPython kartlarında publish ve subscribe: settings.toml'da sırlar, ESP32-S2/S3 ve Pico W'de TLS, callback'ler, yeniden bağlanma.

Güncellendi · 8 dk okuma

MiniMQTT ile CircuitPython'da MQTT

Adafruit MiniMQTT (adafruit_minimqtt), CircuitPython'un MQTT istemcisidir. ESP32-S2, ESP32-S3 ve Raspberry Pi Pico W gibi Wi-Fi'lı kartlarda çalışır, kartın yerleşik TLS yığınını kullanır ve paho-mqtt kullandıysanız size tanıdık gelecek bir callback stili izler. Bu eğitimde kütüphaneyi kuruyor, güvenli şekilde bağlanıyor, 10 saniyede bir ölçüm publish ediyor, komutlara tepki veriyor ve Wi-Fi kopmalarını atlatıyoruz.

Örnekler, Adafruit bundle'ındaki güncel bir MiniMQTT sürümüyle CircuitPython 9.x'i hedefler. CircuitPython her kaydettiğinizde code.py'yi yeniden yüklediği için hızlı deneme yapabilirsiniz; sahaya çıkan bir cihazın neden gerçek bir hata yönetimi döngüsüne ihtiyaç duyduğunu da göreceksiniz.

Gerekenler

ParçaNotlar
Wi-Fi'lı CircuitPython kartıESP32-S2/S3 Feather veya QT Py, Pico W ya da bir AirLift kartı
CircuitPython 9.xcircuitpython.org'dan tam olarak kartınıza uygun sürümü yükleyin
adafruit_minimqtt kütüphanesiBağımlılıklarıyla birlikte, circup ile kurulur
Bir MQTT brokerÖrnekler broker.connectmq.com (8883 TLS) kullanır

Kütüphaneyi, doğru .mpy dosyalarını ve bağımlılıkları CIRCUITPY sürücüsüne kopyalayan circup ile kurun:

MiniMQTT kurulumu
pip install circup
circup install adafruit_minimqtt
circup list          # kurulu sürümleri gösterir, eskileri işaretler

adafruit_minimqtt klasörünü bundle'dan lib/ içine elle de kopyalayabilirsiniz; circup, bundle'ı CircuitPython sürümünüze göre seçtiği için daha az hataya yol açar.

1. settings.toml'da gizli bilgiler

CircuitPython, settings.toml dosyasını sürücünün kök dizininden okur. İki CIRCUITPY_WIFI_* anahtarı ayrıca kartın açılışta Wi-Fi'ye otomatik bağlanmasını sağlar.

settings.toml
CIRCUITPY_WIFI_SSID = "your-ssid"
CIRCUITPY_WIFI_PASSWORD = "your-wifi-password"
MQTT_BROKER = "broker.connectmq.com"
MQTT_PORT = 8883
MQTT_USER = "username"
MQTT_PASS = "password"

Değerleri os.getenv() ile okuyun. Kimlik bilgilerini burada tutmak, code.py'yi hiçbir şey sızdırmadan paylaşmanıza veya commit etmenize olanak tanır.

2. Eksiksiz bir publisher ve subscriber

code.py
import os, time
import microcontroller, ssl, socketpool, wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT

if not wifi.radio.connected:
    wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"),
                       os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print("Wi-Fi:", wifi.radio.ipv4_address)

DEVICE = "cpy-" + microcontroller.cpu.uid.hex()
BASE = f"testmqtt/cpy/{DEVICE}"

def on_connect(client, userdata, flags, rc):
    print("bağlandı, rc =", rc)
    client.subscribe(f"{BASE}/cmd", qos=1)

def on_disconnect(client, userdata, rc):
    print("bağlantı koptu, rc =", rc)

def on_message(client, topic, message):
    print(f"{topic}: {message}")
    if message == "ping":
        client.publish(f"{BASE}/pong", str(time.monotonic()))

pool = socketpool.SocketPool(wifi.radio)
mqtt = MQTT.MQTT(
    broker=os.getenv("MQTT_BROKER"),
    port=os.getenv("MQTT_PORT"),
    username=os.getenv("MQTT_USER"),
    password=os.getenv("MQTT_PASS"),
    client_id=DEVICE,
    is_ssl=True,
    keep_alive=60,
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)
mqtt.on_connect = on_connect
mqtt.on_disconnect = on_disconnect
mqtt.on_message = on_message
mqtt.will_set(f"{BASE}/status", "offline", retain=True, qos=1)

mqtt.connect()
mqtt.publish(f"{BASE}/status", "online", retain=True, qos=1)

last = 0
while True:
    mqtt.loop(timeout=1)                  # gelen paketleri okur, zamanı gelince PINGREQ gönderir
    if time.monotonic() - last >= 10:
        temp = microcontroller.cpu.temperature
        mqtt.publish(f"{BASE}/temperature", f"{temp:.1f}")
        last = time.monotonic()

Önemli satırların yaptığı iş:

  • socket_pool ve ssl_context, MiniMQTT'yi kartın ağ yığınına bağlar. ssl.create_default_context(), CircuitPython firmware'ine gömülü kök sertifikaları kullanır; böylece sertifikası genel bir CA'dan gelen bir broker ek dosya olmadan doğrulanır.
  • loop(timeout=1) sık sık çalışmalıdır. Gelen mesajları işler, on_message'ı çağırır ve keep-alive ping'lerini zamanında gönderir. Timeout, istemcinin socket_timeout değerinden (varsayılan 1 saniye) küçük olamaz; aksi halde MiniMQTT hata fırlatır.
  • on_connect içinde abone olmak, ilki ve her yeniden bağlanma dahil her başarılı bağlantının aboneliği geri getirmesini sağlar. Bu önemlidir, çünkü broker.connectmq.com gibi paylaşımlı test uç noktaları bağlantılar arasında session saklamaz.
  • microcontroller.cpu.uid kalıcı ve benzersiz bir client ID verir. Geçici testler için rastgele ID tercih ederseniz client ID oluşturucu geçerli ID'ler üretir.
  • microcontroller.cpu.temperature demo için pratik bir değerdir; bazı kartlarda None döndürür. Gerçek ölçümler için yerine gerçek bir sensör sürücüsü (BME280, SHT4x…) koyun.

Topic bazlı yönlendirme

Bir kart birden fazla topic'e abone olduğunda topic bazlı callback'ler kodu düzenli tutar:

Topic bazlı callback'ler
def on_led(client, topic, message):
    led.value = message == "on"

mqtt.add_topic_callback(f"{BASE}/led", on_led)
mqtt.subscribe(f"{BASE}/led")

Bu topic'teki mesajlar on_led'e gider; geri kalan her şey genel on_message'a ulaşmaya devam eder.

3. Tarayıcıdan test edin

code.py'yi kaydedin ve seri konsolu açın (Mu, Thonny veya screen /dev/tty.usbmodem* 115200). IP adresini, ardından bağlandı, rc = 0 satırını görmelisiniz.

Şimdi online MQTT istemcisini açın, aynı broker'a bağlanın ve testmqtt/cpy/# topic'ine abone olun. Retained online durumunu ve her 10 saniyede bir sıcaklık değerini göreceksiniz. testmqtt/cpy/<device>/cmd topic'ine ping publish edin; bir saniye içinde pong geri gelir.

CircuitPython kartınızı tarayıcıda izleyin

Last Will'i test etmek için kartın gücünü kesin: yaklaşık bir buçuk keep-alive süresi sonra broker, durum topic'ine offline publish eder.

4. Bağlantı kopmalarını atlatmak

Geliştirme sırasında bir istisna code.py'yi durdurur ve siz düzeltirsiniz. Bir dolabın içindeki cihazda ise yakalanmamış bir istisna, biri gücü kesip yeniden verene kadar cihazın sessiz kalması demektir. Döngüyü sarmalayın:

code.py — dayanıklı ana döngü
def ensure_wifi():
    if not wifi.radio.connected:
        print("Wi-Fi yeniden bağlanıyor")
        wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"),
                           os.getenv("CIRCUITPY_WIFI_PASSWORD"))

delay = 1
last = 0
while True:
    try:
        mqtt.loop(timeout=1)
        if time.monotonic() - last >= 10:
            mqtt.publish(f"{BASE}/temperature", f"{microcontroller.cpu.temperature:.1f}")
            last = time.monotonic()
        delay = 1
    except (OSError, RuntimeError, ConnectionError, MQTT.MMQTTException) as e:
        print("hata:", e, "- yeniden deneme", delay, "sn sonra")
        time.sleep(delay)
        delay = min(delay * 2, 60)
        try:
            ensure_wifi()
            mqtt.reconnect()              # on_connect yeniden çalışır ve tekrar abone olur
            mqtt.publish(f"{BASE}/status", "online", retain=True, qos=1)
        except Exception as e2:
            print("yeniden bağlanma başarısız:", e2)
            if delay >= 60:
                microcontroller.reset()   # tekrarlanan hatalardan sonra son çare

Kalıp her platformda aynıdır: önce ağ katmanını, sonra MQTT'yi düzeltin; bir filonun yeniden başlayan bir broker'ı ezmemesi için üstel backoff uygulayın ve son güvenlik ağı olarak donanım reset'ini elde tutun. MQTT yeniden bağlanma en iyi uygulamaları mantığını daha ayrıntılı açıklıyor.

5. TLS ve sertifika notları

  • Genel CA sertifikaları doğrudan çalışır. CircuitPython bir kök sertifika paketiyle gelir ve ssl.create_default_context() bunu kullanır.
  • Kendinden imzalı veya özel CA mı? Açıkça yükleyin: ctx = ssl.create_default_context(); ctx.load_verify_locations(cadata=CA_PEM); burada CA_PEM, kök sertifikanızın PEM metnidir.
  • Bellek sınırlıdır. TLS büyükçe bir buffer ister. PSRAM'i olmayan kartlarda code.py içinde MQTT'ye erken bağlanın ve el sıkışmadan önce büyük bellek ayırmalarından kaçının.
  • 1883'te şifresiz TCP, is_ssl=False ve port=1883 ile çalışır ama şifreyi düz metin gönderir. Yalnızca masa üstü test ağında kullanın.

El sıkışma başarısız olursa önce bilgisayarınızdan bir TLS bağlantı testi yaparak broker tarafını eleyin.

Karşılaşacağınız hatalar

MesajSebep
ConnectionError: No network with that ssidYanlış SSID veya yalnızca 5 GHz yayın yapan ağ
ConnectionError: Authentication failureYanlış Wi-Fi şifresi
MMQTTException: Connection Refused - Incorrect username/passwordBroker kimlik bilgilerini reddetti (CONNACK 4)
MMQTTException: Connection Refused - UnauthorizedBağlanma izni yok (CONNACK 5)
OSError: [Errno 116] ETIMEDOUTPort engelli, host yanlış veya broker'a ulaşılamıyor
MemoryErrorTLS veya büyük bir payload için yetersiz heap

CONNACK kodları standart MQTT 3.1.1 dönüş kodlarıdır; reason code aracı hepsini listeler. Daha ileri giden bir sensör projesi için (birden fazla sensör, JSON payload'lar, bir dashboard) MQTT hava istasyonu yazısına bakın. Platformlar arasında seçim yapıyorsanız MicroPython umqtt eğitimi aynı görevleri daha alt seviye bir istemciyle gösteriyor.

MiniMQTT, CircuitPython kartlarına birkaç düzine satırda TLS destekli eksiksiz bir MQTT istemcisi kazandırır. Gizli bilgileri settings.toml'a koyun, on_connect içinde abone olun, loop()'u sürekli çağırın ve hepsini bir yeniden bağlanma döngüsüne sarın: bir hobi kartı, aylarca çalışır halde bırakabileceğiniz bir cihaza dönüşür.

Sıkça sorulan sorular

Adafruit MiniMQTT hangi kartlarda çalışır?

Ağ yığını olan her CircuitPython kartında: ESP32-S2, ESP32-S3 ve Raspberry Pi Pico W gibi yerleşik Wi-Fi'lı kartlarda ve esp32spi kütüphanesi üzerinden bir ESP32 AirLift yardımcı işlemcisi kullanan kartlarda. MQTT kodu aynıdır; yalnızca socket pool kurulumu farklıdır.

MiniMQTT MQTT 5 veya QoS 2 destekliyor mu?

Hayır. MiniMQTT, MQTT 3.1.1 konuşur ve QoS 0 ile QoS 1'i destekler. MQTT 5 destekleyen broker'lar 3.1.1 istemcilerini de kabul eder, yani onlarla çalışır; ancak karttan MQTT 5 özelliklerini kullanamazsınız.

MiniMQTT mesajlarım neden bytes yerine str olarak geliyor?

MiniMQTT varsayılan olarak payload'ları UTF-8 olarak çözer ve on_message'a bir str iletir. CBOR veya görüntü gibi ikili payload'lar için istemciyi use_binary_mode=True ile oluşturun; böylece bytes alırsınız.