Die Hauptklasse SDProtocols stellt die Schnittstelle zur Protokollverarbeitung bereit.
link:../../sd_protocols/sd_protocols.py[role=include]PySignalduino ist als Bibliothek konzipiert, die beispielsweise in MQTT-Bridges oder Home-Automation-Skripten verwendet werden kann. Sie übernimmt die Erkennung und Dekodierung der Rohdaten.
Für Debugging-Zwecke können Sie eine eigene Callback-Funktion registrieren:
link:../../sd_protocols/sd_protocols.py[role=include]PySignalduino bietet eine integrierte MQTT-Integration über die Klasse MqttPublisher. Diese ermöglicht das Veröffentlichen dekodierter Nachrichten an einen MQTT-Broker und das Empfangen von Befehlen über MQTT-Topics.
Die MQTT-Verbindung wird automatisch initialisiert, wenn die Umgebungsvariable MQTT_HOST gesetzt ist. Folgende Umgebungsvariablen können konfiguriert werden:
-
MQTT_HOST– Hostname oder IP-Adresse des MQTT-Brokers (Standard:localhost) -
MQTT_PORT– Port des Brokers (Standard:1883) -
MQTT_TOPIC– Basis-Topic für alle Nachrichten (Standard:signalduino) -
MQTT_USERNAME– Optionaler Benutzername für Authentifizierung -
MQTT_PASSWORD– Optionales Passwort für Authentifizierung -
MQTT_COMPRESSION_ENABLED– Boolescher Wert (0/1) zur Aktivierung der Payload-Kompression (Standard: 0)
Der MqttPublisher wird innerhalb des SignalduinoController verwendet und stellt eine asynchrone Context-Manager-Schnittstelle bereit:
link:../../main.py[role=include]-
{topic}/messages– JSON‑kodierte dekodierte Nachrichten (DecodedMessage) -
{topic}/commands/#– Topic für eingehende Befehle (Wildcard-Subscription) -
{topic}/responses– Antworten auf GET-Befehle, inkl.get/cc1101/frequency. -
{topic}/errors– Fehlerantworten. -
{topic}/status– Heartbeat‑ und Statusmeldungen (optional)
Der Publisher sendet regelmäßig einen Heartbeat („online“) unter {topic}/status, solange die Verbindung besteht.
Bei Verbindungsabbruch wird „offline“ gepublished.
link:../../tests/test_mqtt.py[role=include]PySignalduino stellt eine umfangreiche Befehls-API zur Steuerung des SIGNALDuino-Firmware-Geräts bereit. Die Klasse SignalduinoCommands kapselt alle verfügbaren seriellen Befehle und bietet eine asynchrone Schnittstelle.
Die folgenden Befehle werden unterstützt (Auswahl):
-
Systembefehle:
-
get_version()– Firmware-Version abfragen (V) -
get_help()– Hilfe anzeigen (?) -
get_free_ram()– Freien RAM abfragen ® -
get_uptime()– Uptime in Sekunden (t) -
ping()– Ping-Gerät (P) -
get_cc1101_status()– CC1101-Status (s) -
disable_receiver()– Empfänger deaktivieren (XQ) -
enable_receiver()– Empfänger aktivieren (XE) -
factory_reset()– Werkseinstellungen wiederherstellen (e) -
Konfigurationsbefehle:
-
get_config()– Konfiguration lesen (CG) -
set_decoder_state(decoder, enabled)– Decoder aktivieren/deaktivieren (C<CMD><FLAG>) -
set_manchester_min_bit_length(length)– MC Min Bit Length setzen (CSmcmbl=) -
set_message_type_enabled(message_type, enabled)– Nachrichtentyp aktivieren/deaktivieren (C<FLAG><TYPE>) -
get_ccconf()– CC1101-Konfiguration abfragen (C0DnF) -
get_ccpatable()– CC1101 PA Table abfragen (C3E) -
read_cc1101_register(register)– CC1101-Register lesen (C<reg>) -
write_register(register, value)– EEPROM/CC1101-Register schreiben (W<reg><val>) -
read_eeprom(address)– EEPROM-Byte lesen (r<addr>) -
set_patable(value)– PA Table schreiben (x<val>) -
set_bwidth(value)– Bandbreite setzen (C10<val>) -
set_rampl(value)– Rampenlänge setzen (W1D<val>) -
set_sens(value)– Empfindlichkeit setzen (W1F<val>) -
Sendebefehle:
-
send_combined(params)– Kombinierten Sendebefehl (SC…) -
send_manchester(params)– Manchester senden (SM…) -
send_raw(params)– Rohdaten senden (SR…) -
send_xfsk(params)– xFSK senden (SN…) -
send_message(message)– Vorkodierte Nachricht senden
Befehle, die die Hardware-Konfiguration ändern (z.
B. write_register, set_patable), werden in der Regel im EEPROM des SIGNALDuino persistent gespeichert.
Die Persistenz wird durch die Firmware gewährleistet; PySignalduino sendet lediglich die entsprechenden Kommandos.
Wenn MQTT aktiviert ist, können Befehle über das Topic {base_topic}/commands/{command} gesendet werden. Die Basis für Antworten ist {base_topic}/responses (Erfolg) oder {base_topic}/errors (Fehler). Das base_topic ist standardmäßig signalduino/v1.
Die optionale req_id kann im Request-Payload gesendet werden und wird unverändert in die Response übernommen. Sie dient zur Korrelation von asynchronen Anfragen und Antworten.
Dieser Befehl fragt die aktuell im CC1101-Transceiver eingestellte Funkfrequenz ab.
1. Request Topic und Payload (Senden)
-
Topic:
signalduino/v1/commands/get/cc1101/frequency(ersetzesignalduino/v1durch dein konfiguriertes{base_topic}) -
Payload: Muss eine
req_idzur Korrelation der Antwort enthalten. Ist keinereq_idim Payload enthalten, wird automatisch der Wert"NO_REQ_ID"verwendet.
{
"req_id": "client-12345-freq-req-A"
}
2. Response Topic und Payload (Empfangen)
-
Erfolgs-Topic:
signalduino/v1/responses -
Fehler-Topic:
signalduino/v1/errors
Erfolgreiche Antwort (auf `signalduino/v1/responses\`):
{
"command": "get/cc1101/frequency",
"success": true,
"req_id": "client-12345-freq-req-A",
"payload": {
"frequency_mhz": 433.920
}
}Fehlerhafte Antwort (auf `signalduino/v1/errors\`):
{
"command": "get/cc1101/frequency",
"success": false,
"req_id": "client-12345-freq-req-A",
"error": "Hardware nicht initialisiert"
}Beispiel mit mosquitto_pub und mosquitto_sub (angenommen base_topic ist signalduino/v1):
# Zum Senden des Requests
mosquitto_pub -h localhost -t "signalduino/v1/commands/get/cc1101/frequency" -m '{"req_id": "test-123"}'
# Zum Empfangen der Antwort
mosquitto_sub -h localhost -t "signalduino/v1/responses"Alle anderen allgemeinen Befehle folgen ebenfalls diesem Schema, wobei {command} den Pfad nach /commands/ darstellt.
Beispiel mit mosquitto_pub:
# Sende Request für System-Version
mosquitto_pub -h localhost -t "signalduino/v1/commands/get/system/version" -m '{"req_id": "test-version"}'
# Antwort empfängst du auf signalduino/v1/responseslink:../../tests/test_controller.py[role=include]link:../../main.py[role=include]Die folgenden Klassen und Schnittstellen sind für die Integration besonders relevant:
Die Klasse signalduino.mqtt.MqttPublisher bietet eine asynchrone Context-Manager-Schnittstelle zur Kommunikation mit einem MQTT-Broker.
-
Methoden:
-
async publish(message: DecodedMessage)– Veröffentlicht eine dekodierte Nachricht unter{topic}/messages -
async publish_simple(subtopic: str, payload: str, retain: bool = False)– Veröffentlicht eine einfache Zeichenkette unter{topic}/{subtopic} -
async is_connected() → bool– Prüft, ob die Verbindung zum Broker besteht -
register_command_callback(callback: Callable)– Registriert einen asynchronen Callback für eingehende Befehle -
Context-Manager:
async with MqttPublisher() as publisher:
Die Klasse signalduino.commands.SignalduinoCommands kapselt alle seriellen Befehle für die SIGNALDuino-Firmware.
-
Initialisierung: Erfordert eine asynchrone Sendefunktion (wird normalerweise vom
SignalduinoControllerbereitgestellt) -
Alle Methoden sind asynchron (
async def) und geben entwederstr(Antwort) zurück oderNone(keine Antwort erwartet) -
Umfang: Systembefehle, Konfiguration, Senden von Nachrichten (siehe Abschnitt „Command Interface“)
Sowohl SignalduinoController als auch MqttPublisher und die Transportklassen (TcpTransport, SerialTransport) implementieren das asynchrone Context-Manager-Protokoll (aenter/aexit). Dies gewährleistet eine sichere Ressourcenverwaltung (Verbindungsauf‑/abbau, Hintergrundtasks).
Beispiel für verschachtelte Context-Manager:
link:../../main.py[role=include]-
SignalduinoController– Zentrale Steuerungsklasse, koordiniert Transport, Parser, MQTT und Befehle -
TcpTransport,SerialTransport– Asynchrone Transportimplementierungen für TCP bzw. serielle Verbindungen -
DecodedMessage,RawFrame– Datentypen für dekodierte Nachrichten und Rohframes
Eine vollständige API-Dokumentation kann mit pydoc oder mittels Sphinx generiert werden.
Dieser Abschnitt beschreibt häufige Probleme und deren Lösungen.
-
Keine Verbindung zum Broker: Stellen Sie sicher, dass die Umgebungsvariablen
MQTT_HOSTundMQTT_PORTkorrekt gesetzt sind. Der Broker muss erreichbar sein und keine Authentifizierung erfordern (oder Benutzername/Passwort müssen gesetzt sein). -
Verbindung bricht ab: Überprüfen Sie die Netzwerkverbindung und Broker-Konfiguration. Der MQTT-Client (
aiomqtt) versucht automatisch, die Verbindung wiederherzustellen. Falls die Verbindung dauerhaft abbricht, prüfen Sie Firewall-Einstellungen und Broker-Logs. -
MQTT-Nachrichten werden nicht empfangen: Stellen Sie sicher, dass das Topic
{topic}/commands/#abonniert ist. Der Command-Listener startet automatisch, wenn MQTT aktiviert ist. Überprüfen Sie die Log-Ausgabe auf Fehler.
-
RuntimeError: no running event loop: Tritt auf, wenn asyncio-Funktionen außerhalb eines laufenden Event-Loops aufgerufen werden. Stellen Sie sicher, dass Ihr Code innerhalb einer asyncio-Coroutine läuft undasyncio.run()verwendet wird. Verwenden Sieasync withfür Context-Manager. -
Tasks hängen oder werden nicht abgebrochen: Alle Hintergrundtasks sollten auf das
_stop_eventreagieren. Bei manuell erstellten Tasks müssen Sieasyncio.CancelledErrorabfangen und Ressourcen freigeben. -
Deadlocks in Queues: Wenn eine Queue voll ist und kein Consumer mehr liest, kann
await queue.put()blockieren. Stellen Sie sicher, dass die Consumer-Tasks laufen und die Queue nicht überfüllt wird. Verwenden Sieasyncio.wait_formit Timeout.
-
Keine Antwort auf Befehle: Überprüfen Sie die serielle oder TCP-Verbindung. Stellen Sie sicher, dass das Gerät eingeschaltet ist und die korrekte Baudrate (115200) verwendet wird. Testen Sie mit einem Terminal-Programm, ob das Gerät auf
V(Version) antwortet. -
Timeout-Errors: Die Standard-Timeout für Befehle beträgt 2 Sekunden. Bei langsamen Verbindungen kann dies erhöht werden. Falls Timeouts trotzdem auftreten, könnte die Verbindung instabil sein.
-
Parser erkennt keine Protokolle: Überprüfen Sie, ob die Rohdaten im erwarteten Format ankommen (z.B.
+MU;…). Stellen Sie sicher, dass die Protokolldefinitionen (protocols.json) geladen werden und das Protokoll aktiviert ist.
Aktivieren Sie Debug-Logging, um detaillierte Informationen zu erhalten:
link:../../main.py[role=include]Die Log-Ausgabe zeigt den Status von Transport, Parser und MQTT.
-
Entwicklungsstatus: Da PySignalduino noch in aktiver Entwicklung ist, können sich Verhalten und API zwischen Commits ändern. Bei unerwartetem Verhalten prüfen Sie bitte die aktuelle Codebasis und melden Sie Issues auf GitHub.
-
aiomqtt-Versionen: Verwenden Sieaiomqtt>=2.0.0. Ältere Versionen können Inkompatibilitäten aufweisen. -
Windows und asyncio: Unter Windows kann es bei seriellen Verbindungen zu Problemen mit asyncio kommen. Verwenden Sie
asyncio.ProactorEventLoopoder weichen Sie auf TCP-Transport aus. -
Memory Leaks: Bei langem Betrieb können asyncio-Tasks Speicher verbrauchen. Stellen Sie sicher, dass abgeschlossene Tasks garbage-collected werden. Verwenden Sie
asyncio.create_taskmit Referenzen, um Tasks später abbrechen zu können.
Bei weiteren Problemen öffnen Sie bitte ein Issue auf GitHub mit den relevanten Logs und Konfigurationsdetails.