Walkie-Talkie auf Basis von ESP-NOW
über
Stellen Sie sich vor, Ihr drahtloses Projekt benötigt sowohl schnelle Reaktionszeiten als auch eine große Reichweite? WLAN und Bluetooth sind für solche Anwendungen ungeeignet. Vielleicht ist ESP-NOW eine gute Alternative? Verbindungen werden fast sofort hergestellt, und es sind Reichweiten von mehreren hundert Metern möglich. In diesem Artikel probieren wir es in einer einfachen drahtlosen Intercom-Anwendung vulgo Walkie-Talkie aus.
Der ESP32 von Espressif wird meist wegen seiner WLAN- und Bluetooth-Fähigkeiten verwendet. Wi-Fi und Bluetooth sind zwar großartige Protokolle für alle Arten von drahtlosen Anwendungen, aber sie haben doch ihre Grenzen.
Ein Nachteil von Wi-Fi ist die Zeit, die benötigt wird, um eine Verbindung herzustellen. Außerdem ist bei Wi-Fi keine direkte Kommunikation (Peer-to-Peer, Bild 1) zwischen Geräten möglich. Es ist immer ein Router beteiligt. Aus diesem Grund eignet sich Wi-Fi nicht wirklich für einfache Fernbedienungen mit geringer Latenzzeit, zum Beispiel, um ein Garagentor zu öffnen oder eine Lampe ein- und auszuschalten. Solche Aufgaben erfordern nämlich eine sofortige Reaktion. Um diesen Makel zu umgehen, sind WLAN-Anwendungen in der Regel ständig eingeschaltet und verbunden. Doch dabei benötigen sie viel Energie, selbst wenn sie im Leerlauf sind.
Bluetooth hingegen zeichnet sich durch einen schnellen Verbindungsaufbau und Peer-to-Peer-Kommunikation aus. Es wäre damit hervorragend für Fernbedienungen mit geringer Latenz geeignet, wenn da nicht die geringe Reichweite wäre: Bluetooth funktioniert nur, wenn die kommunizierenden Geräte nicht weiter als etwa zehn Meter voneinander entfernt sind. Es gibt zwar Bluetooth für größere Entfernungen, aber es ist noch nicht weit verbreitet.
Die Lösung: ESP-NOW
Das drahtlose Protokoll ESP-NOW von Espressif ist eine Lösung für Situationen, in denen sowohl schnelle Reaktionszeiten als auch eine große Reichweite erforderlich sind und das gleiche Frequenzband wie bei Wi-Fi oder Bluetooth verwendet werden soll.
Das Protokoll vereint die Vorteile von Wi-Fi und Bluetooth. ESP-NOW zielt damit auf die Hausautomatisierung und das intelligente Haus ab. Da es One-to-Many- und Many-to-Many-Topologien ermöglicht (Bild 2), benötigt es keine Router, Gateways oder gar eine Cloud.
ESP-NOW nutzt dafür keine ausgefallenen Verbindungs- oder High-Level-Kommunikationsprotokolle. Die Adressierung basiert auf der Ethernet-MAC-Adresse des Knotens, und es ist nur ein Pairing-Schritt erforderlich, damit die Knoten miteinander kommunizieren können. Außerdem wird nicht garantiert, dass die Datenpakete in der richtigen Reihenfolge ankommen. Für einfache Fernsteuerungsanwendungen ist das alles aber kein Problem.
Die Datenrate von ESP-NOW beträgt standardmäßig 1 Mbit/s (konfigurierbar), und ein Datenpaket kann eine Nutzlast von bis zu 250 Byte haben. Zusammen mit den Header- und Prüfsummenbytes und so weiter ergibt dies eine maximale Paketgröße von 255 Byte.
Bauen wir ein Walkie-Talkie!
Mein Ziel war es, eine Art Walkie-Talkie oder eine Gegensprechanlage auf der Grundlage von ESP-NOW zu bauen. Ein kurzer Blick auf die Spezifikationen des ESP32 zeigt, dass er über alles verfügt, was man dafür braucht: einen Analog-Digital-Wandler (ADC), einen Digital-Analog-Wandler (DAC), viel Rechenleistung und natürlich den ganzen Funkkram. In der Praxis sieht es allerdings nicht ganz so rosig aus.
Der 12-Bit breite ADC erweist sich als ziemlich langsam, ich habe eine maximale Abtastrate von etwa 20 kHz gemessen. Irgendwo im Internet wurde erwähnt, dass seine analoge Bandbreite nur 6 kHz beträgt. Der DAC ist acht Bit breit (aber es gibt zwei davon), was die mögliche Audioqualität noch mehr einschränkt.
Ein Walkie-Talkie kann jedoch mit diesen Zahlen auskommen, wenn das Audiosignal auf die Standard-Telefonie-Bandbreite von 3,5 kHz gestutzt wird. Bei einer Abtastrate von 8 kHz ergibt sich eine Datenrate von (8.000/250) × 255 × 8 = 65.280 bit/s (zur Erinnerung: die maximale Payload beträgt 250 Byte). Dies liegt zwar weit unter der Standardrate von 1 Mbit/s, so dass mit diesen Spezifikationen keine High-Fidelity-Audioqualität möglich ist, aber das ist ohnehin nicht unser Ziel: Für uns ist die Verständlichkeit wichtiger.
Die Schaltung
Der Einfachheit zuliebe habe ich einen bandbegrenzten Ein-Transistor-Kondensatormikrofon-Vorverstärker als Audioeingang verwendet und einen klassischen, auf dem LM386 basierenden Verstärker als Audioausgang hinzugefügt (Bild 3). Die Eingangsbandbreite wird am unteren Ende durch die etwas unterdimensionierten C1 und C5, am oberen Ende wird durch die Tiefpassfilter R4/C2 und R5/C3 begrenzt. Ähnliche Tiefpassfilter werden am Ausgang des DACs eingesetzt. Das Signal an der „heißen“ Seite von P1 sollte nicht größer als 400 mVSS sein.
Als ESP32-Modul habe ich mich für das ESP32-PICO-KIT entschieden. Es gibt viele andere Module, aber nicht alle stellen die DAC-Ausgänge an GPIO25 und GPIO26 zur Verfügung. Außerdem brauchen wir einen ADC-Eingang. Ich habe dafür GPIO32 (ADC1, Kanal 4) verwendet. Der Testpunkt TP1 an GPIO26 (der zweite DAC-Ausgang) ist als Monitorausgang für das Mikrofonsignal vorgesehen. Ein Drucktaster an GPIO33 bietet Push-to-Talk-Funktionalität (PTT), und die LED an GPIO27 ist die bei den meisten Mikrocontroller-Schaltungen übliche Multifunktions-LED.
Die Stromversorgung ist in einen analogen und einen digitalen Teil aufgeteilt ist. Der Grund dafür ist nicht, dass schnelle digitale Schaltgeräusche nicht in den Audioeingang eingekoppelt werden, sondern die Vermeidung eines bestimmten Klickgeräuschs am Ausgang. Offenbar erzeugt eine auf dem ESP32 laufende Aufgabe periodische Stromstöße, die hörbar werden können, wenn die Schaltung nicht sorgfältig aufgebaut ist. Der beste Weg dazu ist die Verwendung von zwei separaten Stromversorgungen (Bild 4). Das ESP32-Modul muss in dieser Beziehung wie ein Bauteil behandelt werden, das eine Stromversorgung benötigt (wie der LM386), und nicht wie ein Modul, das auch den Rest der Schaltung mit Strom versorgen kann. Denken Sie daran, dass der LM386 einen Versorgungsspannungsbereich von 4...12 V hat.
C10 ist optional und wird nur in einigen seltenen Fällen bei frühen ESP32-Modulen benötigt, die nicht richtig booten, wenn sie nicht an einen Computer angeschlossen sind (oder ähnliche Probleme haben). Zufälligerweise hatte ich noch einige dieser frühen Module, und so habe ich C10 in meinen Entwurf aufgenommen.
Die Software
Das Programm für das Walkie-Talkie basiert auf dem ESPNow_Basic_Master-Beispiel aus dem Arduino-ESP32-Boards-Paket von Espressif. Nachdem ich es an meine Bedürfnisse angepasst hatte, fügte ich Audio-Sampling und -Wiedergabe hinzu. Es gibt ein paar Dinge, die Sie vielleicht über das Programm wissen möchten und sollten.
Audio-Sampling und Audio-Wiedergabe werden von einem Timer-Interrupt gesteuert, der mit 8 kHz läuft. Für das Sampling setzt die Interrupt-Service-Routine (ISR) des Sample-Rate-Timers nur ein Flag, um zu signalisieren, dass ein neues Sample erfasst werden soll. Die Funktion loop() fragt dieses Flag ab und führt die erforderlichen Maßnahmen durch. Dies liegt daran, dass der ADC nicht innerhalb einer ISR gelesen werden sollte, wenn die von Espressif bereitgestellte ADC-API verwendet wird. Die hier verwendete Funktion adc1_get_raw() ruft alle möglichen anderen Funktionen auf, die Dinge tun können, über die Sie keine Kontrolle haben. Da die ESP32-Software in einer Multitasking-Umgebung läuft, ist es wichtig, die Thread-Sicherheit zu gewährleisten. Wenn Sie Arduino für die ESP32-Programmierung verwenden, wird vieles davon automatisch erledigt, aber wenn Sie vorhaben, mein Programm auf ESP-IDF zu portieren, müssen Sie vielleicht etwas vorsichtiger sein.
Die Audiowiedergabe ist einfach, da die Sample-Rate-Timer-ISR ein Sample in den DAC schreibt, just wenn eines verfügbar ist. Ist dies nicht der Fall, wird der DAC-Ausgang auf die Hälfte der ESP32-Versorgung, also 1,65 V eingestellt. Das einzige Beachtenswerte ist ein so genannter Ping-Pong-Puffer, der den digitalen Audioempfang optimiert (Bild 5). Ein solcher Puffer besteht eigentlich aus zwei Puffern, von denen einer gefüllt wird, während der andere gelesen wird. Dadurch kann es zu Überschneidungen kommen. Theoretisch sollte dies zwar nicht passieren, da Sender und Empfänger die gleiche Abtastrate und Timing-Logik verwenden, in der Realität ist dies jedoch aufgrund von Timing-Toleranzen manchmal der Fall. Ein Ping-Pong- oder Doppelpuffer hilft, störende Knackser bei der Wiedergabe zu vermeiden. Beachten Sie, dass ein ungeordneter Empfang von Datenpaketen nicht behandelt wird.
Pairing
Die Walkie-Talkie-Firmware ist ein Master-Slave-System. Der Master arbeitet im Wi-Fi-Station-Modus (STA), während ein Slave im Access-Point-Modus (AP) ist. Der Master stellt unmittelbar eine Verbindung zu einem Slave her, wenn er einen erkennt, und kann sofort mit dem Senden von Daten beginnen. Wenn der Master eine Verbindung zum Slave herstellt, wird jedoch nicht gleichzeitig der Slave mit dem Master verbunden. Der Slave kann keine Daten an den Master senden, und ein Zwei-Wege-Betrieb ist nicht möglich (zumindest ist es mir nicht gelungen; wenn Sie es besser wissen, lassen Sie es mich bitte wissen).
Eine Möglichkeit, den Slave mit dem Master zu verbinden, ist die Verwendung von Datenempfangs-Callbacks. Wenn Daten empfangen werden, wird die Adresse des Absenders zusammen mit den Daten an diese Funktion übergeben. Sobald der Slave also etwas empfängt, kann er sich mit dem Absender der Daten verbinden. Dazu habe ich dieselben Funktionen und Verfahren wie der Master verwendet, um sich mit dem Slave zu verbinden. Es gibt jedoch eine Raffiniertheit, die nicht sehr gut (wenn überhaupt) dokumentiert ist: Der Slave muss sein Wi-Fi-Schnittstellenfeld auf ESP_IF_WIFI_AP setzen, sonst funktioniert er nicht. Dieses Feld ist standardmäßig auf ESP_IF_WIFI_STA voreingestellt, wie es vom Master benötigt wird, so dass das Programm es im Normalfall nicht explizit setzen muss. Daher erscheint das Feld nirgends in den Beispielprogrammen, so dass der Benutzer nichts von seiner Existenz weiß.
Push-to-Talk
Wenn ESP-NOW kontinuierlich sendet, wird die MCU ziemlich schnell heiß. In der Walkie-Talkie-Anwendung gibt es aber keinen Grund, ununterbrochen zu streamen, und so habe ich eine Push-to-Talk-Taste (auch bekannt als PTT) hinzugefügt (S1). Drücken Sie diese Taste und halten Sie sie gedrückt, während Sie sprechen. Wenn der Sender mit dem Empfänger gepaart ist, leuchtet die LED auf. Auf der Empfängerseite leuchtet die LED ebenfalls auf und zeigt damit an, dass ein Anruf eingeht. Um Rückkopplungen zu vermeiden, wird der Audioausgang auf der Senderseite stummgeschaltet, wenn die PTT-Taste gedrückt wird. Obwohl die Kommunikation im Prinzip vollduplex ist, sollten die beiden Gesprächspartner dennoch nicht gleichzeitig sprechen. Dies ist eine gute Gelegenheit, „Roger“ und „Over“ in Ihre Sätze einzubauen.
Ein Programm für alle Fälle
Das Programm besteht aus einer Arduino-.ino-Datei (Sketch). Außer dem Espressif-ESP32-Board-Paket werden keine weiteren Bibliotheken benötigt. Das Walkie-Talkie benötigt ein Master- und ein Slave-Gerät. Um das Programm für das Master-Gerät zu kompilieren, kommentieren Sie Zeile 12 aus, in der NODE_TYPE_SLAVE steht. Für das Slave-Gerät muss dieses Makro definiert werden. Sie können auch einige andere Einstellungen ändern, wenn Sie möchten. So ist es auch möglich, ohne die Unterstützung des Audioeingangs (AUDIO_SOURCE) und/oder -ausgangs (AUDIO_SINK) zu kompilieren. Dies ist praktisch für die Fehlersuche oder für eine Anwendung, die nur eine einseitige Kommunikation benötigt. Der Quellcode kann von heruntergeladen werden.
Höhere Wiedergabetreue?
Es sollte nicht allzu kompliziert sein, hochwertige Audiodaten über ESP-NOW zu übertragen, wenn man statt des einfachen Mikrofonverstärkers und des im ESP32 eingebauten ADC und DAC auf I2S umsteigt. Dies macht die Schaltung und das Programm etwas komplexer, würde aber - zumindest theoretisch - das Streaming von 16-Bit-Audiodaten mit einer Abtastrate von 48 kHz ermöglichen. Allerdings muss der möglicherweise nicht ordnungsgemäße Empfang von Paketen richtig gehandhabt werden. Aber hey, wurde Bluetooth nicht genau dafür entwickelt?
Reichweitentest
Um zu sehen, ob ESP-NOW eine Kommunikation über große Entfernungen ermöglicht, habe ich ein einfaches Programm geschrieben, das einmal pro Sekunde eine Ping-Nachricht an den Slave sendet. Der Slave war nichts weiter als ein ESP32-PICO-KIT mit einer an GPIO27 angeschlossenen LED, die über eine USB-Powerbank mit Strom versorgt wurde. Jedes Mal, wenn ein Ping empfangen wird, blinkt die LED kurz auf (100 ms).
Mit dem Sender, der im Freien in 1 m Höhe über dem Boden platziert wurde, erreichte ich eine Sichtverbindung (LOS) von etwa 150 m. Ab dieser Entfernung wurde der Empfang unregelmäßig, und der Slave musste höher, etwa 2 m über dem Boden gehalten werden. Diese Situation kann wahrscheinlich durch eine sorgfältigere Positionierung der beiden Gegenstellen verbessert werden.
Dieser Artikel (230496-02) wird in der Gastausgabe von Espressif im Elektor Mag im Dezember 2023 erscheinen.
Haben Sie Fragen oder Kommentare?
Haben Sie technische Fragen oder Kommentare zu diesem Artikel? Schicken Sie eine E-Mail an den Autor unter clemens.valens@elektor.com oder kontaktieren Sie Elektor unter redaktion@elektor.de.
Diskussion (6 Kommentare)