|
nRF24L01 |
Raspberry Pi |
|---|---|
|
GND |
GND |
|
VCC |
3,3V |
|
CE |
GPIO22 |
|
CSN |
GPIO8 (CE0) |
|
SCK |
GPIO11 (SCK) |
|
MOSI |
GPIO10 (MOSI) |
|
MISO |
GPIO9 (MISO) |
|
IRQ |
GPIO24 |

Abbildung 2: Das Pinout des nRF24L01-Moduls. Die IRQ-Leitung benötigen Sie nicht unbedingt. Quelle: Martin Mohr

Abbildung 3: Der Versuchsaufbau mit je einem Raspberry Pi 3 und 4, an denen die beiden nRF24L01-Module hängen. Quelle: Martin Mohr
Nachdem Sie alles zusammengebaut haben, müssen Sie die SPI-Schnittstelle auf den beiden Mini-Rechnern noch mit dem Kommando sudo raspi-config aktivieren. Dazu verwenden Sie den Menüpunkt 3**INTERFACING OPTIONS | I4**SPI enable.
Erster Test
Um sich unnötiges Abtippen zu sparen, laden Sie das Beispielprogramm direkt von der Github-Seite der Bibliothek auf die beiden RasPis herunter (Listing 2, erste Zeile). Danach starten Sie das Programm jeweils in der Konsole (zweite Zeile).
Listing 2
Beispielprogramm und Scanner
$ wget https://github.com/nRF24/pyRF24/raw/main/examples/getting_started.py $ python getting_started.py $ wget https://github.com/nRF24/pyRF24/raw/main/examples/scanner.py
Nach dem Start fragt das Programm, welches radio Sie verwenden möchten. Abhängig von Ihrer Eingabe vergibt es die Adresse für den Raspberry Pi. Hier ist es lediglich wichtig, dass Sie nicht auf beiden Mini-Rechnern dieselbe Adresse vergeben. Wählen Sie deshalb auf einem RasPi als Adresse 0 und auf dem anderen 1. Als Nächstes müssen Sie sich entscheiden, ob Sie den jeweiligen Raspberry Pi als Sender (T) oder als Empfänger (R) betreiben wollen. Auch hier müssen Sie darauf achten, nicht beiden Geräten dieselbe Rolle zuzuweisen.
Haben Sie alles erledigt, beginnen die beiden RasPis zu kommunizieren, wie man auf Abbildung 4 sehen kann. Spannend ist dabei die Tatsache, dass die Datenübertragung scheinbar fehlerbehaftet ist. Beim Empfänger kommen nur ungefähr dieselben Werte an, die gesendet wurden. Im nächsten Abschnitt klären wir, was hier genau passiert und welche Möglichkeiten es gibt, einen solchen Fehler zu verhindern.
Problem Fließkomma
Um genau zu klären, was hier schiefgelaufen ist, müssen wir uns mit der internen Arbeitsweise von CPUs beschäftigen. Der erste wichtige Aspekt ist die Tatsache, dass Mikroprozessoren digital arbeiten, also nur mit Nullen und Einsen. Damit lassen sich ganzzahlige Werte darstellen, mit denen eine CPU dann arbeitet. Dabei hängt der mögliche Wertebereich von der Anzahl der zur Speicherung verfügbaren Bits ab.
Nun gibt es neben den Ganzzahlen noch Gleitkommazahlen. Wir beabsichtigen hier gar nicht tiefer in die mathematischen Grundlagen dahinter einzusteigen. Für uns ist nur wichtig, dass Gleitkommazahlen nicht auf dieselbe Weise im Speicher des Rechners abgelegt werden wie Ganzzahlen. Hier kommt ein Verfahren zum Einsatz, bei dem eine Mantisse mit einem zugehörigen Exponenten zur Darstellung eine Zahl dient. Diese Art der Speicherung ist sehr geschickt und flexibel, birgt allerdings auch ein gewisses Fehlerpotenzial. Falls Sie tiefer in das Thema einsteigen möchten, raten wir zur Lektüre des entsprechenden Wikipedia-Artikels [6].
Aber wodurch genau entsteht bei unserer Datenübertragung der Fehler? Auch hier gilt es, ein wenig auszuholen. Python verwendet intern 64 Bit (8 Byte) zur Speicherung von Gleitkommazahlen. Zur Funkübertragung muss das Beispielprogramm die Zahlen in ein Array von Bytes umwandeln. Das erledigt im Beispielprogramm das Kommando struct.pack("<f", payload[0]). Und da haben wir auch schon unser Problem: Der Befehl wandelt einen 8-Byte-Wert in ein 4-Byte-Array um. Das klappt aber nur auf Kosten der Genauigkeit. Beim Empfangen erfolgt die Umwandlung noch einmal in die andere Richtung. Sie ahnen es schon: Das Problem wird dadurch nicht aufgehoben, sondern verstärkt sich noch. Es treten zwei Mal Rundungsfehler bei den übertragenen Daten auf, was die verfälschten Werte verursacht.
Da drängt sich die Frage auf, wie sich dieser Fehler beheben lässt. Die naheliegende Idee wäre, einfach 8 Byte anstelle von 4 Byte zu übertragen. Nach einem intensiven Blick auf den Sourcecode erscheint das als gar keine so schwierige Aufgabe. An allen Stellen im Code, in denen die pack Methode mit dem Parameter "<f" zum Einsatz kommt, schreiben Sie einfach ein d anstelle von f. Damit veranlassen wir pack, mit einem Double-Wert zu arbeiten, und siehe da: Es klappt, wie es soll (Abbildung 5). Manchmal ist die dynamische Typisierung von Python eher ein Fluch als ein Segen.

Abbildung 5: Nach der Angabe des richtigen Parameters für pack verläuft die Datenübertragung fehlerfrei. Quelle: Martin Mohr
An dieser Stelle möchten wir Ihnen nicht vorenthalten, dass es noch eine zweite Möglichkeit gibt, solche Rundungsfehler zu verhindern: Sie übermitteln die zu übertragenden Werte als Ganzzahlen mit einem dazugehörigen Faktor. Auf diese Weise vermeiden Sie ungewollte Rundungsfehler. Dieses Verfahren wird recht oft für die Datenübertragung im industriellen Umfeld eingesetzt. Dort müssen oft sehr heterogene Systeme interagieren, die schon auf Hardwareebene unterschiedliche Darstellungen für Daten verwenden.
Scanner
Die Fehlersuche bei Funkverbindungen kann sehr zeitraubend sein. Schließlich bekommt man nur schwer heraus, welche Signale von der Antenne abgestrahlt werden. Hier kann uns das Programm scanner.py aus den Beispielen der Bibliothek gute Dienste erweisen. Sie richten es mit dem Befehl aus der letzten Zeile von Listing 2 auf dem Raspberry Pi ein.
Das Programm scannt alle 125 Kanäle und zählt für jeden die eingehenden Übertragungen (Abbildung 6). Auf diese Weise erhalten Sie einen groben Anhaltspunkt, auf welchen Kanälen das Funkmodul Daten empfängt. Das eröffnet außerdem die Möglichkeit, für Ihr Projekt einen Kanal auszuwählen, auf dem noch nicht allzu viele Übertragungen stattfinden. So können Sie Kollisionen vermeiden.






