nRF24L01 – 2.4 GHz Funkmodule

Über den Beitrag

Der nRF24L01 bzw. nRF24L01+ ist ein leistungsfähiger Transceiver (Transmitter und Receiver) für das 2.4 GHz ISM Frequenzband, den ihr mithilfe von Arduino Boards oder anderen Mikrocontrollern und der Bibliothek RF24 programmieren könnt. Die Bibliothek hat gute Beispielsketche, die aber insbesondere für Anfänger unter Umständen etwas schwer verdaulich sind. Ich habe diesen Beitrag geschrieben, um den Einstieg – hoffentlich – etwas zu erleichtern.

Das kommt auf euch zu:

Technische Eigenschaften der nRF24L01 Module

Die nRFL01 Serie stammt von der Firma Nordic Semiconductors. Ein Datenblatt für den nRF24L01(+) gibt es auf der Herstellerseite nicht oder nicht mehr. Als Dokumentation scheint lediglich eine vorläufige Produktspezifikation im Umlauf zu sein, zum Beispiel hier auf GitHub.

Die meisten von euch werden den nRFL24L01 nicht als IC, sondern als Modul einsetzen. Davon gibt es viele verschiedene Ausführungen. Besonders weit verbreitet sind diese Modelle:

Zwei nRF24L01 Module
Zwei nRF24L01 Module

Das obere, große Modul mit SMA Antenne wird meistens als „nRF24L01 + PA + LNA“ angeboten, wobei PA für Power Amplifier und LNA für Low Noise Amplifier steht. Es soll eine Reichweite von bis zu 800 Metern erreichen. Dafür verbraucht es in der Spitze stolze 115 Milliampere im Sende- und bis zu 45 Milliampere im Empfangsbetrieb.

Die „nRF24L01 + PA + LNA“ Module sind empfindlich gegenüber elektromagnetischer Strahlung. Wenn sie nicht funktionieren, dann versucht sie abzuschirmen. Ideal wäre ein Metallgehäuse. Es geht aber auch mit einer Schicht Alufolie, nur müsst ihr dann vorher eine Schicht Isoliermaterial auflegen, um Kurzschlüsse zu vermeiden. Siehe auch hier. Bei mir haben die Teile tatsächlich erst nach der Abschirmung funktioniert. Ich hätte sie schon beinahe entsorgt, bis ich den Hinweis mit der Abschirmung gefunden habe.

Die kleinen Module sind unproblematischer. Sie brauchen maximal um die 14 Milliampere. Dafür liegt ihre Reichweite bei 100 Metern (Verkäuferangabe!). Dazu weiter unten mehr. Eine Kombination der verschiedenen Module funktioniert auch.

Weitere technische Daten

  • Betriebsspannung: 1.9 – 3.6 Volt
  • Spannungstoleranz der I/O Pins: 5 Volt
  • Kanäle: 125 (2.400 – 2.525 GHz)
  • Datenrate: 250 kbit/s, 1 Mbit/s, 2 Mbit/s
  • Max. Ausgangsleistung: 0 dBm
  • Standby Stromverbrauch: 26 µA
  • FIFO (First In, First OUT) Puffer: 3 x 32 Bit
  • Kommunikation: per SPI

Pinout / Anschlüsse

Die nRF24L01 Module haben 8 Pins:

  • MISO/MOSI/SCK: SPI Anschlüsse
  • GND/VCC: Spannungsversorgung
  • CE: Chip Enable
  • CSN: Chip Select
  • IRQ: Interrupt Pin

Die Anordnung der Pins und die fehlende Beschriftung sind etwas unkomfortabel für Versuchsschaltungen. Wenn ihr es etwas bequemer haben wollt, dann besorgt ihr euch für wenig Geld Adapterplatinen. Diese besitzen, neben vernünftig beschrifteten Ausgängen, noch einen AMS1117-3.3 Volt Spannungsregler und Kondensatoren zur Spannungsstabilisierung. Zum Betrieb sind damit mindestens 4.6 Volt notwendig.

Falls ihr – so wie ich – die Bauteile lieber fest ins Breadboard stecken möchtet, könnt ihr alternativ einen ESP8266-ESP01 Adapter missbrauchen. Ihr müsst dann aber den Kondensator (ESD) entfernen und die Ausgänge umbenennen.

nRF24L01 Adapter – links: Selbstbau, rechts: kommerziell erhältlicher Adapter

Vorbereitungen

Anschluss an den Mikrocontroller

Für die Beispielsketche kam die folgende Schaltung zum Einsatz:

Schaltung für das nRF24L01 Modul
Schaltung für das nRF24L01 Modul

Als Kondensator kam ein 10 µF Elko zum Einsatz. Er kann aber durchaus noch größer ausfallen.

Lediglich für einen Sketch habe ich zusätzlich noch den Interruptpin IRQ mit dem Arduino Pin 2 verbunden.

Falls ihr ein „nRF24L01 + PA + LNA“ Modul verwendet, dann wird es auf der Transmitterseite mit der Stromversorgung über den 3.3 Volt Pin eng. Der Arduino UNO und der Nano liefern nur 50 Milliampere. Für den Testbetrieb über kurze Distanz könnt ihr die Sendeleistung herunterregeln, ansonsten solltet ihr eine potentere Stromquelle wählen.

Installation der RF24 Bibliothek

Ihr findet die RF24 Bibliothek über die Bibliotheksverwaltung der Arduino IDE. Sucht dort nach RF24:

RF24 Bibliothek
RF24 Bibliothek

Alternativ ladet ihr die Bibliothek hier von GitHub herunter.

Hinweise zu den Sketchen

Die RF24 Bibliothek verfügt über eine Reihe von Beispielsketchen, die die Funktionen des nRF24L01 veranschaulichen. Allerdings können die Beispiele auf den ersten Blick ein wenig verwirrend sein, da die Autoren den Transmitter- und den Receiverteil jeweils in einem Sketch zusammengefasst haben. Erst nach dem Programmstart wird dem jeweiligen Modul seine Rolle über den seriellen Monitor zugeordnet. Das ist eigentlich ziemlich cool, aber, wie zuvor erwähnt, macht es den Einstieg etwas schwerer. Ich habe deshalb für den Transmitter und den Receiver jeweils separate Sketche verfasst.

Einige meiner Beispielsketche erzeugen sowohl auf der Transmitter-, als auch auf der Receiverseite Ausgaben auf dem seriellen Monitor. Man kann natürlich zwischen den Ports hin- und herwechseln, einfacher ist es jedoch zwei Instanzen der Arduino IDE zu erzeugen. Das heißt, ihr ruft die Arduino IDE zweimal auf, ordnet jeder einen eigenen Port zu und könnt so gleichzeitig zwei serielle Monitore öffnen.

Minimalsketch

Im ersten Beispiel kommen zwei nRF24L01 Module zum Einsatz. Der eine übernimmt die Rolle als Transmitter, der andere dient als Receiver. Wenn der Receiver eine Nachricht erhält, schickt er eine Empfangsbestätigung (Acknowledgement) an den Transmitter zurück. Im ersten Beispiel merken wir nichts davon, ich wollte diesen Aspekt aber nicht unterschlagen.

Ein nRF24L01 Transmitter-Receiver Paar
Ein nRF24L01 Transmitter-Receiver Paar

Minimum Transmittersketch

Wir beginnen mit dem Transmittersketch:

#include <SPI.h>
#include <RF24.h>

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "1RF24"; // address / identifier

void setup() {
  radio.begin();
  radio.openWritingPipe(address); // set the address
  radio.stopListening(); // set as transmitter
}
void loop() {
  const char text[] = "Hi Receiver"; // max. 32 bytes
  radio.write(&text, sizeof(text));
  
  delay(2000);
}

 

Erklärungen zum Transmittersketch – Pipes und Adressen

Achtung – jetzt wird es vielleicht etwas verwirrend: Die Datenübertragung der nRF24L01 Module erfolgt über sogenannte Pipes (= Rohr / Leitung). Dabei handelt es sich um eine Art Kommunikationskanal. Allerdings sind die Pipes nicht zu verwechseln mit den „Channels“, zu denen wir noch kommen. Der nRF24L01 hat eine Pipe zum Schreiben (bzw. Senden). Zum Lesen (bzw. Empfangen) könnt ihr bis zu sechs Pipes einrichten.

Damit zwei nRF24L01 Module miteinander kommunizieren können, muss die Pipe des Transmitters („writing pipe“) und die Pipe – bzw. eine der Pipes – des Empfängers („reading pipe“) dieselbe Adresse haben. Diese besteht aus einer Kombination von 5 Bytes. Die Bezeichnung „Adresse“ ist vielleicht etwas irreführend, ich wollte aber von der Nomenklatur der Bibliothek bzw. des Datenblattes nicht abweichen. Ihr könnt euch die Adresse auch einfach als Kennung (Identifier) vorstellen. Nehmt das erst einmal so hin. Später wird es – hoffentlich – klarer.

Weitere Erklärungen zum Transmittersketch

Ihr bindet zunächst die notwendigen Bibliotheken ein und erzeugt dann mit RF24 radio(7, 8) ein RF24 Objekt mit dem Namen „radio“. Dabei legt ihr auch die CE und CSN Pins fest. Zusätzlich könntet ihr die SPI Taktrate übergeben – schaut dazu hier in die Dokumentation der Klasse. 

Mit radio.begin() wird das nRF24L01 Modul initialisiert. Mit openWritingPipe(address) öffnet ihr den Schreibkanal und legt die Adresse fest. Die Funktion stopListening() bringt das Modul in den Transmittermodus.

Eure Nachrichten versendet ihr in Häppchen von maximal 32 Byte. Ihr definiert sie als Character Array und übergebt sie der Funktion write(). Um Arbeitsspeicher zu sparen, übergebt ihr das Character Array als Referenz. Dazu stellt ihr dem Variablennamen den Adressoperator & voran.

Minimum Receiversketch

Kommen wir zum Receiversketch:

#include <SPI.h>
#include "RF24.h"

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "1RF24"; // address / identifier

void setup() {
  Serial.begin(115200);
  radio.begin();
  radio.openReadingPipe(0,address); // set the address for pipe 0
  radio.startListening(); // set as receiver
}

void loop() {
  if(radio.available()){
    char text[33] = {0}; 
    radio.read(&text, sizeof(text)-1);
    Serial.println(text);
  }
}

Die wesentlichen Unterschiede zum Transmittersketch sind:

  • openReadingPipe(0, address) öffnet den Lesekanal 0 und legt die Adresse fest. Da es mehr als einen Lesekanal gibt, müsst ihr ihn spezifizieren. Die möglichen Werte sind 0 bis 5. Aus Gründen, die erst später klar werden, könntet ihr in diesem Beispiel nur 0 oder 1 wählen.
  • startListening() bringt den nRF24L01 in den Receivermodus.
  • available() prüft, ob eine Nachricht empfangen wurde.
  • read() liest die Nachricht. Ihr müsst die Variable übergeben, in der die Nachricht gespeichert wird und die Anzahl der zu lesenden Bytes.
    • Die Variable text ist ein Character Array. Character Arrays enden mit dem unsichtbaren Nullzeichen '\0'. Deshalb hat text eine Länge von 33 und nicht 32.

Die Ausgabe des Receiversketches ist keine große Überraschung:

nRF24L01 - Output minimum_receiver_sketch.ino
Output minimum_receiver_sketch.ino

Erweiterte Sketche

Die folgenden zwei Sketche bewirken im Prinzip dasselbe wie die Minimalsketche. Nachrichten werden vom Transmitter zum Receiver gesendet und dort ausgegeben. Die Sketche sind aber um diverse Funktionen erweitert. Einige dieser Funktionen ändern die Standardeinstellungen, andere bieten mehr Sicherheit.

Erweiterter Transmittersketch

Hier zunächst der Transmittersketch:

#include <SPI.h>
#include <RF24.h>

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "1RF24"; // address / identifier

void setup() {
  Serial.begin(115200);
  if(!radio.begin()){
    Serial.println("nRF24L01 module not connected!");
    while(1){}
  }
  else 
    Serial.println("nRF24L01 module connected!");
  
  /* Set the data rate:
   * RF24_250KBPS: 250 kbit per second
   * RF24_1MBPS:   1 megabit per second (default)
   * RF24_2MBPS:   2 megabit per second
   */
  radio.setDataRate(RF24_2MBPS);

  /* Set the power amplifier level rate:
   * RF24_PA_MIN:   -18 dBm
   * RF24_PA_LOW:   -12 dBm
   * RF24_PA_HIGH:   -6 dBm
   * RF24_PA_MAX:     0 dBm (default)
   */
  radio.setPALevel(RF24_PA_LOW); // sufficient for tests side by side

  /* Set the channel x with x = 0...125 => 2400 MHz + x MHz 
   * Default: 76 => Frequency = 2476 MHz
   * use getChannel to query the channel
   */
  radio.setChannel(0);
  
  radio.openWritingPipe(address); // set the address
  radio.stopListening(); // set as transmitter

  /* You can choose if acknowlegdements shall be requested (true = default) or not (false) */
  radio.setAutoAck(true);
 
  /* with this you are able to choose if an acknowledgement is requested for 
   * INDIVIDUAL messages.
   */
  radio.enableDynamicAck(); 

  /* setRetries(byte delay, byte count) sets the number of retries until the message is
   * successfully sent. 
   * Delay time = 250 µs + delay * 250 µs. Default delay = 5 => 1500 µs. Max delay = 15.
   * Count: number of retries. Default = Max = 15. 
   */
  radio.setRetries(5,15);

  /* The default payload size is 32. You can set a fixed payload size which must be the
   * same on both the transmitter (TX) and receiver (RX)side. Alternatively, you can use 
   * dynamic payloads, which need to be enabled on RX and TX. 
   */
  //radio.setPayloadSize(11);
  radio.enableDynamicPayloads();
}
void loop() {
  const char text[] = "Hi Receiver";
  Serial.println(sizeof(text));  
  if(radio.write(&text, sizeof(text)-1, 0)){ // 0: acknowledgement request, 1: no ack request
    Serial.println("Message successfully sent");
  }
  delay(2000);
}

 

Dazu einige Erklärungen:

  • begin() kennt ihr schon. Aber hier prüfen wir mit dem Rückgabewert (true / false), ob das nRF24L01 Modul verbunden ist. 
  • Mit setDataRate() legt ihr die Datenübertragungsrate fest. Es stehen drei Optionen zur Wahl: 250 kbit/s, 1 Mbit/s oder 2 Mbit/s. Zu beachten: Transmitter und Receiver müssen dieselbe Einstellung haben!
  • setPALevel() bestimmt die Verstärkungsstufe. Ihr könnt -18, -12, -6 oder 0 dBm einstellen. Je größer der Wert, desto größer die Reichweite. Dasselbe gilt aber auch für den Strombedarf.
  • Die Funktion setChannel(channel) eröffnet euch die Möglichkeit, die Sendefrequenz zu ändern. Die Frequenz ist 2400 MHz + channel * 1 MHz mit channel = 0 bis 125. Sender und Empfänger müssen auf dieselbe Frequenz eingestellt sein. Voreinstellung ist 76.
  • Mit setAutoAck(true/false) legt ihr fest, ob der Transmitter eine Empfangsbestätigung anfordern soll oder nicht. Voreinstellung ist „true“.
  • enableDynamicAck() ermöglicht euch, für jeden write() Befehl individuell festzulegen, ob eine Empfangsbestätigung geschickt werden soll oder nicht.
  • Wird eine Empfangsbestätigung angefordert, aber sie kommt nicht, sendet der nRF24L01 die Nachricht erneut. Die Zahl der Wiederholungen (count) und die Zeit zwischen den Wiederholungen (delay) steuert ihr mit setRetries(delay, count). Dabei ist delay eine Zahl zwischen 0 und 15 und die resultierende Verzögerung ist 250 µs + delay * 250 µs. Die Voreinstellung ist setRetries(5, 15).
  • setPayloadSize(size) legt die Länge (size) der Nachricht (die/der Payload) fest. Voreingestellt ist size = 32. Ihr müsst auf der Sender- und Empfängerseite denselben Wert einstellen.
  • Alternativ haltet ihr die Länge der Nachricht mit enableDynamicPayloads() variabel.
  • write() kennt ihr schon, aber:
    • Mit dem Parameter sizeof(text)-1 übergeben wir das Character Array ohne den abschließendes Nullzeichen. Auf der Receiverseite können wir ihn wieder anhängen. Ihr könnt den Nullstring natürlich auch mit übergeben – aber wozu?
    • Wir übergeben einen dritten Parameter, der steuert, ob eine Empfangsbestätigung erfolgen soll. 0 ist mit, 1 ist ohne Empfangsbestätigung. Genau andersherum, als man es erwarten würde.
    • Wir nutzen den Rückgabewert von write(), um zu prüfen, ob die Datenübertragung erfolgreich abgeschlossen wurde. Das funktioniert natürlich nur, wenn auch eine Empfangsbestätigung angefordert wird.

Erweiteter Receiversketch

Und hier der Receiver Sketch:

#include <SPI.h>
#include "RF24.h"

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "1RF24"; // address / identifier
void setup() {
  Serial.begin(115200);
  if(!radio.begin()){
    Serial.println("nRF24L01 module not connected!");
    while(1){}
  }
  else 
    Serial.println("nRF24L01 module connected!");

  /* Set the data rate:
   * RF24_250KBPS: 250 kbit per second
   * RF24_1MBPS:   1 megabit per second
   * RF24_2MBPS:   2 megabit per second
   */
  radio.setDataRate(RF24_2MBPS);

  /* Set the power amplifier level rate:
   * RF24_PA_MIN:   -18 dBm
   * RF24_PA_LOW:   -12 dBm
   * RF24_PA_HIGH:   -6 dBm
   * RF24_PA_MAX:     0 dBm (default)
   */
  radio.setPALevel(RF24_PA_LOW); // sufficient for tests side by side 
   
   /* Set the channel x with x = 0...125 => 2400 MHz + x MHz 
   * Default: 76 => Frequency = 2476 MHz
   * use getChannel to query the channel
   */
  radio.setChannel(0);
  
  radio.openReadingPipe(0,address); // set the address
  radio.startListening(); // set as receiver

  /* The default payload size is 32. You can set a fixed payload size which 
   * must be the same on both the transmitter (TX) and receiver (RX)side. 
   * Alternatively, you can use dynamic payloads, which need to be enabled 
   * on RX and TX. 
   */
  //radio.setPayloadSize(11);
  radio.enableDynamicPayloads();
}

void loop() {
  if(radio.available()){
    byte len = radio.getDynamicPayloadSize();
    Serial.println(len); //just for information
    char text[len+1] = {0}; 
    radio.read(&text, len);
    Serial.println(text);
  }
}

 

Zum Receiversketch gibt es weniger zu sagen:

  • Die Einstellungen für die Payload müssen mit denen des Transmitters übereinstimmen.
  • getDynamicPayloadSize() fragt die Größe der Payload in Bytes ab.
  • Da wir das Character Array ohne Nullzeichen übergeben haben, fügen wir ihn durch die Definition char text[len+1] = {0} wieder hinzu.

Und hier die Ausgabe. Wie erwartet wurden 11 Bytes empfangen:

Output receiver_extended.ino
Output receiver_extended.ino

Der nRF24L01 als MultiCeiver™

Als Receiver kann der nRF24L01 gleichzeitig auf sechs Transmitter „hören“. Allerdings gilt die Einschränkung, dass immer nur eine Nachricht zu einem gegebenen Zeitpunkt erhalten werden kann. Hier kommen die Wiederholungen ins Spiel.

Ein Receiver ("Multiceiver") für 6 Transmitter
Ein Receiver („Multiceiver“) für 6 Transmitter

Wir beginnen diesmal mit der Receiverseite. Der nRF24L01 nutzt hier seine sechs Pipes. Jede der Pipes bekommt eine individuelle Reading Pipe Adresse, die mit der Writing Pipe Adresse des korrespondierenden Transmitters übereinstimmen muss. 

Jetzt wird es noch einmal etwas verwirrend: Nur die Adresse der Pipe 0 besteht aus individuellen 5 Bytes. Bei den Adressen der Pipes 1 bis 5 unterscheidet sich nur das Byte 0. Diese Adressen „teilen“ sich die Bytes 1 bis 4 der Adresse von Pipe 1. Das Schema lautet also:

  • Adresse 0 ist „abcde“.
  • Adresse 1 bis 5 ist: „fghij“, „kghij“, „lghij“, „mghij“, „nghij“.
  • Dabei stehen a bis n für jedes druckbare Zeichen.

Und dieses System begründet auch, warum ihr beim minimum_receiver_sketch nur die Reading Pipe 0 oder 1 wählen konntet (ihr erinnert euch?): Wenn die Adresse der Pipe 1 nicht definiert ist, sind es die Bytes 1 – 4 der Adressen der Pipes 2 – 5 auch nicht.

Hier nun der Receiversketch:

#include <SPI.h>
#include "RF24.h"

RF24 radio(7, 8); // (CE, CSN)

const byte address[][6] = {"0Base", "1RF24", "2RF24", "3RF24", "4RF24", "5RF24"};

void setup() {
  Serial.begin(115200);
  radio.begin();
  radio.setPALevel(RF24_PA_LOW); // sufficient for tests side by side 

  for(int i=0; i<6; i++){
    radio.openReadingPipe(i, address[i]);
  }
  radio.startListening(); // set as receiver
}

void loop() {
  byte pipe; 
  if(radio.available(&pipe)){
    char receivedText[33] = {0}; 
    radio.read(&receivedText, sizeof(receivedText));
    Serial.print("Received on pipe ");
    Serial.print(pipe);
    Serial.print(": ");
    Serial.println(receivedText);
  }
}

Neu ist bei diesem Sketch, dass der Funktion available() die Variable pipe (als Referenz) übergeben wird. Das erlaubt uns zu prüfen, über welche Pipe die Nachricht eingetroffen ist. Alle anderen Funktionen wurden schon besprochen.

Und nun zum Transmittersketch für das Modul 0. Um ihn für die anderen fünf Transmittermodule anzupassen, müsst ihr lediglich in Zeile 8 die Transmitter ID tx_id abändern.

#include <SPI.h>
#include <RF24.h>

RF24 radio(7, 8); // (CE, CSN)

const byte address[][6] = {"0Base", "1RF24", "2RF24", "3RF24", "4RF24", "5RF24"};

byte tx_id = 0; // max. 6 TX: 0...5
                        
void setup() {
  Serial.begin(115200);
  radio.begin();
  radio.setPALevel(RF24_PA_LOW); // sufficient for tests side by side 

  radio.openWritingPipe(address[tx_id]); // set the address
  radio.stopListening(); 
  radio.setRetries(((tx_id * 3) % 12) + 3, 15);
}
void loop() {
  char text[32] = {0};
  strcpy(text, "Message from TX ");
  char buf[2] ={0};
  itoa(tx_id, buf, 10);
  strcat(text, buf);
  Serial.println(text);
  
  if(radio.write(&text, sizeof(text))){
    Serial.println("Message successfully sent");
  }
  delay(3000);
}

Noch ein paar Anmerkungen. Die eigentümliche Konstruktion für das Retry-Delay in Zeile 17 soll sicherstellen, dass die Wiederholungen von den Modulen in unterschiedlichen Abständen gesendet werden. Dadurch werden Kollisionen vermieden (genauer gesagt: wiederholte Kollisionen).

Der größte Teil des Codes in loop() dient der Zusammensetzung der Nachricht:

  • strcpy(text, "Message from TX ") kopiert „Message from TX “ in text.
  • itoa(tx_id, buf, 10)  kopiert den Integerwert tx_id als Zeichenkette in das Character Array buf unter Verwendung des Dezimalsystems.
  • strcat(text, buf) hängt buf an das Ende von text.

Wenn ihr Floats versenden wollt, dann könntet ihr die Funktion dtostrf() verwenden:

  • dtostrf(float_value, min_width, num_digits_after_decimal, target). Dabei ist min_width die Mindestbreite (>=4), num_digits_after_decimal ist die Anzahl der Stellen hinter dem Komma und target ist das Character Array, in das ihr euren Wert schreibt.
nRF24L01 - Output multiceiver_receiver.ino
Output multiceiver_receiver.ino

Anmerkung zum MultiCeiver Beispielsketch der Bibliothek

Wenn ihr euch den Beispielsketch MulticeiverDemo.ino der RF24 Bibliothek anschaut, dann werdet ihr dort vielleicht über die Adress-Definition stolpern:

uint64_t address[6] = { 0x7878787878LL,
                        0xB3B4B5B6F1LL,
                        0xB3B4B5B6CDLL,
                        0xB3B4B5B6A3LL,
                        0xB3B4B5B60FLL,
                        0xB3B4B5B605LL };

Die Adressen werden in diesem Sketch als Array von 6 Ganzzahlen mit einer Größe von je 8 Byte definiert. Das nimmt etwas mehr Platz ein als die sechs Arrays von je 6 Byte. Das ist aber noch nicht der Punkt. Vielmehr könnte die Frage aufkommen, warum sich bei den Adressen 1 bis 5 das rechts stehende Byte unterscheidet und nicht das links stehende. Die Antwort ist, dass das Byte 0 einer Ganzzahl ganz rechts steht, wohingegen sich das 0te Element eines Arrays links befindet.

Die Rolle des nRF24L01 wechseln

Die Rolle des nRF24L01 als Transmitter oder Receiver könnt ihr während des laufenden Programmes wechseln. Der folgende Sketch macht aus dem nRF24L01 alle zwei Sekunden einen Transmitter, der eine Nachricht versendet. Wenn die Nachricht erfolgreich gesendet wurde, gibt es eine entsprechende Erfolgsmeldung und der Transmitter wird zum Receiver. Wenn der nRF24L01 als Receiver eine Nachricht erhalten hat, dann wird diese ausgegeben und er wird wieder zum Transmitter.

Das bedeutet, dass das Modul, welches mit diesem Sketch läuft, den Takt angibt. Deshalb habe ich den Sketch „leading_transceiver.ino“ genannt:

#include <SPI.h>
#include <RF24.h>

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "_no_1"; // address / identifier

void setup() {
  Serial.begin(115200);
  radio.begin();
  radio.setPALevel(RF24_PA_LOW); // sufficient for tests side by side 

  radio.openWritingPipe(address); // set the address
  radio.openReadingPipe(1,address);
}
void loop() {
  unsigned long int sendingPeriod = 2000;
  static unsigned long int lastSend = 0;
  const char sendText[] = "Hi Follower";

  if((millis()-lastSend)>sendingPeriod){
    radio.stopListening(); // set as transmitter
    lastSend = millis();
    if(radio.write(&sendText, sizeof(sendText))){
      Serial.println("Message successfully sent");
      radio.startListening();
    }
  }
  if(radio.available()){
    char receivedText[33] = {0}; 
    radio.read(&receivedText, sizeof(receivedText));
    Serial.print("Message received: ");
    Serial.println(receivedText);
    radio.stopListening();
  }
}

 

Das Modul auf der anderen Seite startet als Receiver. Empfängt es eine Nachricht, wird sie ausgegeben und das Modul wird zum Transmitter. Als Transmitter sendet es ein Nachricht zurück und wird wieder zum Receiver.

Wenn es dumm läuft, könnte der Fall eintreten, dass beide Module bis in alle Ewigkeit aufeinander warten, ohne dass etwas passiert. Dafür habe ich die Zeilen 18 – 20 eingefügt. Einmal pro Sekunde wird das Modul daran erinnert, dass es die Receiverrolle übernehmen soll.

#include <SPI.h>
#include "RF24.h"

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "_no_1"; // address / identifier
void setup() {
  Serial.begin(115200);
  radio.begin();
  radio.setPALevel(RF24_PA_LOW); // sufficient for tests side by side 
   
  radio.openReadingPipe(0,address); // set the address
  radio.openWritingPipe(address);
  radio.startListening(); // set as receiver
}

void loop() {
  if((millis()%1000) == 0){
    radio.startListening();  // gentle reminder to listen
  }
  if(radio.available()){
    char receivedText[33] = {0}; 
    radio.read(&receivedText, sizeof(receivedText));
    Serial.print("Message received: ");
    Serial.println(receivedText);
    
    radio.stopListening();
    const char sendText[] = "Hi Leader";
    if(radio.write(&sendText, sizeof(sendText))){
      Serial.println("Message successfully sent");
      radio.startListening();
    }
  }
}

 

Erweiterte Acknowledgements

Im vorherigen Beispiel war das eine Modul vorwiegend Transmitter und das andere vorwiegend Receiver. Der Receiver sollte nur dann eine Nachricht zurücksenden, wenn er zuvor eine Nachricht vom Transmitter erhalten hatte. Für diese Konstellation gibt es eine einfachere Lösung, und zwar könnt ihr Daten mit dem Acknowledgement versenden. Huckepack, sozusagen. Der Vorteil ist, dass ihr die Rolle der Module nicht wechseln müsst.

Die Funktion, die dieses Feature aktiviert, ist enableAckPayload(). Sie muss sowohl auf der Transmitter-, als auch auf der Receiverseite aufgerufen werden. Ob eine Antwort des Receivers vorliegt, prüft ihr mit available(). Ist das der Fall, dann könnt ihr die Nachricht wie gewohnt mit read() lesen.

#include <SPI.h>
#include <RF24.h>

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "_no_1"; // address / identifier

void setup() {
  Serial.begin(115200);
  radio.begin();
  
  radio.openWritingPipe(address); // set the address for writing
  radio.openReadingPipe(1, address);  // set the address for reading
  radio.stopListening(); // set as transmitter
  radio.setPALevel(RF24_PA_LOW); // sufficient for tests side by side 

  radio.enableDynamicPayloads();
  radio.enableAckPayload();
}
void loop() {
  unsigned long int sendingPeriod = 2000;
  static unsigned long int lastSend = 0;
  const char text[] = "Hi Receiver";

  if((millis()-lastSend)>sendingPeriod){
    lastSend = millis();
    if(radio.write(&text, sizeof(text))){ 
      Serial.println("Message successfully sent");
    }
  }
  if(radio.available()){
    Serial.print("Received: ");
    byte len= radio.getDynamicPayloadSize();
    char ackPayload[len] = {0};
    radio.read(&ackPayload, len);
    Serial.println(ackPayload);
  }
}

 

Und so sieht die Receiverseite aus:

#include <SPI.h>
#include "RF24.h"

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "_no_1"; // address / identifier

void setup() {
  Serial.begin(115200);
  radio.begin();
  
  radio.openReadingPipe(0,address); // set the address for writing
  radio.openWritingPipe(address); // set the address for reading
  radio.startListening(); // set as receiver
  radio.setPALevel(RF24_PA_LOW); // sufficient for tests side by side
 
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  char ackPayload[] = "First Answer";
  radio.writeAckPayload(0, &ackPayload, sizeof(ackPayload));
}

void loop() {
  static float counter = 0.0;
  if(radio.available()){
    byte len = radio.getDynamicPayloadSize();
    char text[len+1] = {0}; 
    radio.read(&text, sizeof(text));
    Serial.println(text);
    
    //Acknowledgement Payload:
    char ackPayload[16];
    strcpy(ackPayload, "counter ");
    char number[12] = {0};
    dtostrf(counter, 4, 2, number);
    Serial.println(number);
    strcat(ackPayload, number);
    Serial.println(ackPayload);
    counter += 1.0;
    radio.writeAckPayload(0, &ackPayload, sizeof(ackPayload));
  }
}

 

Die Antwort an den Transmitter schreibt ihr mit writeAckPayload(pipe, text, size) in den FIFO. Sie wird nach Erhalt der Nachricht sofort gesendet. D.h. ihr müsst die Antwort formulieren, bevor ihr die Nachricht bekommt. Wenn die Antwort zum Beispiel den Messwert eines Sensors beinhaltet, dann ist dieser nicht ganz aktuell. Entweder ihr lebt damit, oder:

  • Ihr schreibt regelmäßig ein Update in den FIFO.
  • Der Transmitter sendet seine Nachricht zweimal kurz hintereinander und ihr verwerft jeweils die erste Antwort.
  • Ihr geht doch zur Methode zurück, bei der ihr die Rollen wechselt.

Damit es nicht so langweilig ist und immer dieselbe Receiverantwort kommt, sendet der Receiver in diesem Beispiel einen Zählerstand zurück. Hier kommt auch dtostrf() zum Einsatz.

Das ist die Ausgabe der Transmitterseite:

Output transmitter_ack_payloads.ino
Output transmitter_ack_payloads.ino

Interrupts des nRF24L01 nutzen

Der nRF24L01 kann drei Ereignisse durch einen Interrupt melden:

  • Daten wurden versendet.
  • Die Datenübertragung ist schiefgegangen.
  • Es liegen Daten zum Abruf aus dem FIFO bereit.

Um die Interrupts ein- oder auszuschalten, verwendet ihr die Funktion maskIRQ(data_sent, data_fail, data_ready). Eine „0“ für data_send, data_fail oder data_ready aktiviert den Interrupt, eine „1“ maskiert ihn. 

Der IRQ Pin ist LOW-aktiv. Das heißt, ihr solltet den Interrupt am Arduino auf FALLING einstellen.

Falls ihr mehrere der drei Interruptauslöser aktiviert habt, könnt ihr mit whatHappened(data_sent, data_fail, data_ready) herausfinden, welcher den letzten Interrupt verursacht hat. Der „schuldige“ Parameter hat den Wert 1.

Der folgende Sketch nutzt den Data Ready Interrupt, um anzuzeigen, dass Daten empfangen wurden. Entsprechend kann auf die Abfrage if radio.available() verzichtet werden. Dieses spezielle Beispiel bringt keinen wirklichen Vorteil, sondern dient nur der Anschauung. Eine sinnvollere Nutzung des Data Ready Interrupts wäre beispielsweise, den Mikrocontroller aus dem Schlaf zu wecken.

#include <SPI.h>
#include "RF24.h"
#define IRQ_PIN 2 
volatile bool event = false;

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "_no_1"; // address / identifier

void setup() {
  Serial.begin(115200);
  radio.begin();
  radio.openReadingPipe(0,address); // set the address
  radio.startListening(); // set as receiver
  pinMode(IRQ_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(IRQ_PIN), interruptHandler, FALLING);
  // let IRQ pin only trigger on "data ready" event in RX mode
  radio.maskIRQ(1, 1, 0);  // args = "data_sent", "data_fail", "data_ready"
}

void loop() {
  if(event){
    //if(radio.available()){
      char text[33] = {0}; 
      radio.read(&text, sizeof(text)-1);
      Serial.println(text);
    //}
    event = false;
  }
}

void interruptHandler(){
  event = true;
  bool tx_ds, tx_df, rx_dr;                 // declare variables for IRQ masks
  radio.whatHappened(tx_ds, tx_df, rx_dr);  // get values for IRQ masks

  Serial.print("Data_sent: ");
  Serial.print(tx_ds);  // print "data sent" mask state
  Serial.print(", Data_fail: ");
  Serial.print(tx_df);  // print "data fail" mask state
  Serial.print(", Data_ready: ");
  Serial.println(rx_dr);  // print "data ready" mask state
}

 

Und dies ist die Ausgabe:

Output receiver_IRQ.ino

Weitere Funktionen

Ich habe in meinen Beispielsketchen noch nicht alle Funktionen abgedeckt. Auf ein paar der fehlenden Funktionen möchte ich noch aufmerksam machen, da ich sie für besonders sinnvoll halte. Ich gehe aber nur oberflächlich auf sie ein.

  • writeFast() ähnelt write(). write() schreibt die Nachricht in den FIFO und wartet, bis sie versendet ist. writeFast() hingegen wartet nicht, sondern weitere Nachrichten können in den FIFO Puffer geschrieben werden, bis alle drei FIFOs voll sind. Erst dann blockiert das Programm, bis wieder FIFO-Kapazität frei ist. Bei großen Datenmengen gibt das einen gewissen Geschwindigkeitsvorteil.
  • powerDown() versetzt euren nRF24L01 in den Tiefschlaf, in welchem er nur 0.9 µA Strom verbraucht. powerUp() ist das Gegenstück dazu.
  • flush_rx() und flush_tx() löschen den FIFO des Receivers bzw. des Transmitters.
  • printDetails(), printPrettyDetails() und sprintfPrettyDetails() sind nützlich für das Debugging. Die Funktionen zeigen Status und Einstellungen des nRF24L01 an.

Weitere Informationen zu diesen und weiteren Funktionen findet ihr in der Klassendokumentation oder in den Beispielsketchen der Bibliothek.

Indoor-Reichweitentest

Ich habe einen Reichweitentest bei mir im Haus durchgeführt. Ohne Barriere mag man die maximalen Reichweiten vielleicht erreichen, drinnen geht das wegen der Wände natürlich nicht. Auch stört unter Umständen Fremdstrahlung wie das WLAN.

Ich habe für den Test den maximalen PA-Level gewählt und die niedrigste Datenübertragungsrate. Sowohl auf der Transmitter-, als auch auf der Receiverseiter kamen jeweils zwei Spannungsversorgungen zum Einsatz. Der Arduino Nano wurde mit einer 9 Volt Blockbatterie über VIN versorgt. Das nRF24L01 Modul habe ich mit einem Lithium-Ionen-Akku betrieben. Letzteres ist nicht generell zu empfehlen, da der Akku frisch geladen bis 4.2 Volt liefert. Meine Akkus waren schon etwas entladen. Mit ~3.8 Volt lagen sie ein wenig über der Spezifikationsgrenze von 3.6 Volt. Für kurze Zeit ist das vertretbar. Der Spannungsversorgung des nRF24L01 habe ich noch einen fetten Elko mit 470 µF spendiert.

Stellvertretend ist hier der Receiversketch:

#include <SPI.h>
#include "RF24.h"
const int ledPin = 6;

RF24 radio(7, 8); // (CE, CSN)

const byte address[6] = "_no_1"; // address / identifier

void setup() {
  pinMode(ledPin, OUTPUT);
  radio.begin();
  radio.openReadingPipe(0,address); // set the address
  radio.startListening(); // set as receiver
  radio.setPALevel(RF24_PA_MAX);
  radio.setDataRate(RF24_250KBPS);
}

void loop() {
  if(radio.available()){
    char text[33] = {0}; 
    radio.read(&text, sizeof(text)-1);
    String textString = String(text);
    if(textString == "Hi Receiver"){
      digitalWrite(ledPin, HIGH);
      delay(300);
      digitalWrite(ledPin, LOW);
    }
  }
}

Den Transmitter habe ich in einer Ecke des Hauses positioniert. Er sendete alle zwei Sekunden die Nachricht „Hi Receiver“. Bei korrekter Übertragung blinkte die LED auf der Receiverseite kurz auf. So bin ich dann mit dem Receiver durchs Haus gelaufen und habe geprüft, wo die LED blinkte und wo nicht (was meine Familie kopfschüttelnd zur Kenntnis nahm).

Ergebnis für das Standardmodul

Im Nebenraum war der Empfang problemlos. Noch einen Raum weiter gab es nur Empfang in der Nähe der Tür. Wie groß die Reichweite in Innenräumen ist, hängt natürlich in hohem Maße von der Bausubstanz des Hauses ab. Ein besseres Maß ist vielleicht das folgende: Die Reichweite war in etwa vergleichbar mit der Reichweite des 2.4 GHz WLAN Netzes meines Routers (Fritz!Box 7590). Dieses dringt auch nicht durch das ganze Haus und muss durch einen Repeater unterstützt werden. Wenn ihr also quer durch eure Wohnung oder euer Haus funken wollt, dann geht nicht unbedingt davon aus, dass das mit den Standardmodulen funktioniert.

Ergebnis für das „nRF24L01 + PA + LNA“ Modul

Wie eingangs erwähnt, musste ich die „nRF24L01 + PA + LNA“ Module erst einmal mit Alufolie abschirmen, damit sie überhaupt funktionierten (ja, hier nützen Aluhüte etwas 😉 ). Dann aber war die Reichweite sehr viel besser als die der Standardmodule. Probleme gab es erst beim Senden aus dem Keller zum Dachboden.

37 thoughts on “nRF24L01 – 2.4 GHz Funkmodule

  1. Hallo Wolfgang,

    bei mir werden am seriellen Monitor nur Quadratzeichen ausgegeben.
    Nur zu Beginn kam einmal „Hi Receiver“ gefogt von Quadratzeichen.
    Was mache ich verkehrt?

    Gruß
    Marco

    1. Hallo Marco,

      normalerweise sind komische Zeichen ein Indikator dafür, dass schlicht die Baudrate im Sketch nicht mit der im seriellen Monitor übereinstimmt. Falls das nicht das Problem ist, dann wüsste ich gerne welche Module du einsetzt, welche Beispielsketche hast du genommen und hast du sie modifiziert, und was für Arduino Boards verwendest du?

      VG, Wolfgang

      1. Hallo Wolfgang,

        ich verwende den „Minimum Transmitter Sketch“ auf einem Arudino Nano V3.0 und den „Minimum Receiver Sketch“ auf einem Arduino Uno. Also die ersten beiden Programme auf dieser Seite. Es werden auch nicht nur Quadrate sondern auch ab und an andere Sonderzeichen ausgegeben, alles in einer Zeile. Die Sketche sind unverändert.

        Gruß
        Marco

        1. Hi Marco, ich schaue mir das noch einmal an, heißt ich baue es noch einmal nach. Allerdings wird das erst was in der zweiten Wochenhälfte. Welche Module nutzt du, die kleinen oder die großen? Die großen sind ein bisschen zickig – siehe letztes Kapitel.
          VG, Wolfgang

          1. Hallo Wolfgang,
            super Tip mit der Baudrate, ich habe sie im Seriellen Monitor eingestellt, jetzt funktioniert es. Vielen Dank.
            Mein Ziel ist es eine Temeratur oder Drehzahl zu messen und per Funk zu übertragen und auszuwerten.
            Gruß
            Marco

      2. Hallo Wolfgang,
        ich verwende den „Minimum Transmitter Sketch“ auf einem Arudino Nano V3.0 und den „Minimum Receiver Sketch“ auf einem Arduino Uno. Also die ersten beiden Programme auf dieser Seite. Es werden auch nicht nur Quadrate sondern auch ab und an andere Sonderzeichen ausgegeben, alles in einer Zeile. Die Sketche sind unverändert.
        Gruß
        Marco

      3. Hallo Wolfgang,
        super Tip mit der Baudrate, ich habe sie im Seriellen Monitor eingestellt, jetzt funktioniert es. Vielen Dank.
        Mein Ziel ist es eine Temeratur oder Drehzahl zu messen und per Funk zu übertragen und auszuwerten.
        Gruß
        Marco

  2. Hallo Wolfgang,
    ich danke dir für diese sehr gute Erklärung und Informationen.
    So etwas habe ich gesucht, speziell wegen den Pipes.

    Bei der Erklärung als MultiCeiver scheint aber meines Erachtens ein Fehler zu sein.
    Dort steht als zweiter Punkt bzgl. den Adressen: fghij“, „kghij“, „lghij“, „mghij“, „nhij“.
    Müsste die letzte „Adresse“ nicht „nghij“ heißen?

    viele Grüße
    Helmut

    1. Hallo,

      richtig, da fehlte natürlich ein „g“. Vielen Dank für den Hinweis!

      VG, Wolfgang

  3. Hallo Wolfgang,

    die Beiträge hier sind für mich als Anfänger echt hilfreich. Allerdings habe ich jetzt mit der Funkerei so meine Probleme:

    Nach dem Hochladen der beiden erweiterten Sketche auf je einen Arduino Uno kommt in beiden Fällen die Meldung „nRF24L01 module not connected!“. Ich habe zwar keinen Kondensator eingebaut, aber die Sendeleistung auf radio.setPALevel(RF24_PA_MIN) begrenzt, was für side-by-side-Betrieb ohne Kondensator ausreichen soll.

    Die Verkabelung ist bei beiden identisch:
    CSN = /, CE = 8, MOSI = 11, MISO = 12, SCK = 13, VCC = 3,3V, GND.

    Was mache ich falsch?

    Viele Grüße aus Hagen
    Michael

    1. Hallo Michael,
      ich denke nicht, dass der fehlende Kondensator das Problem ist. Die Meldung kommt ja ganz zu Beginn, wenn versucht wird, die Module mit radio.begin() zu initialisieren. Da „verhandeln“ erst einmal Arduino und Funkmodul, aber noch nicht die Funkmodule miteinander.
      Manchmal sind es ganz profane Dinge wie ein kaputtes Steckkabel, aber da der Fehler auf beiden Seiten auftritt, wäre das schon ein komischer Zufall.
      Bei den Anschlüssen kann man sich recht leicht vertun, weil die Pins nicht gekennzeichnet sind, aber ich schätze, dass du das wahrscheinlich auch schon x-fach geprüft hast.
      Ich hatte mal ein geteiltes Breadboard, also eines wo die lange + und – Leiste nicht durchgehend ist und das war mir nicht aufgefallen. Hat mich Stunden gekostet…
      Die Sketche hast du unverändert gelassen? Wenn du magst, kannst du mir mal ein Foto von deiner Schaltung per mail (wolfgang.ewald@wolles-elektronikkiste.de) schicken. Vielleicht fällt mir ja irgendetwas auf.
      Das wird schon!
      VG, Wolfgang

      1. Hallo Wolfgang,

        danke für die Antwort und erstmal frohe Pfingsten!

        Wegen der nicht gekennzeichneten Pins habe ich bei AZDelivery (von dort beziehe ich auch die Funkmodule und die „Arduinos“) entsprechende Adapter (https://www.az-delivery.de/products/adapter-fur-nrf24l01) gekauft, die sind beschriftet und haben auch eine Kontroll-LED, die fröhlich leuchtet. Also steht schon mal die Stromversorgung. Die Sketche habe ich unverändert gelassen.

        Ich habe jetzt testweise den Transmittersketch auf einen MEGA 2560 geladen (CE = 7, CSN = 8, MISO = 50, MOSI = 51, SCK = 52): dasselbe Ergebnis.

        Ein Foto vom Aufbau reiche ich gleich nach. Wahrscheinlich mache ich irgendwas falsch…

        Viele Grüße aus Hagen

        Michael

        1. Hallo Michael,

          ich denke, jetzt weiß ich es. Wenn du den Adapter nutzt, dann musst du den 5 Volt Ausgang verwenden. Der Adapter hat einen AMS1117-3.3V Spannungsregler. Um 3.3 Volt zu erreichen, muss man ihn mit mindestens 4.6 Volt betreiben, um auf der sicheren Seite zu sein. Wenn das Funkmodul direkt anschließt, dann darfst du nicht mehr als 3.6 Volt verwenden. Das steht im Prinzip auch im Beitragstext, aber vielleicht sollte ich das noch ein wenig stärker betonen.

          VG, Wolfgang

          1. Hallo Wolfgang,

            genau so ist es, das hatte ich übersehen! Vielen Dank für den Hinweis! Jetzt klappt’s!

            Eine Frage noch: Der Kondensator soll möglichst nahe am Funkmodul platziert werden. Irgendwo habe ich den Hinweis gefunden, dass dort jemand den Kondensator direkt an die Stromversorgung des Moduls/des Adapters gelötet hat. Ist das sinnvoll?

            Viele Grüße aus Hagen

            Michael

  4. Hallo Wolfgang,

    nun bin ich doch gezwungen, mich mit den nRF24L01 zu beschäftigen, da ich auf drei Fernbedienungen lauschen muss, deren Adressen nicht geändert werden können.

    Habe ich das richtig verstanden, das ich „radio.openReadingPipe“ nicht drei völlig unterschiedliche Adressen übergeben kann?

    Drei mal „radio.begin()“ auf ein nRF24L01 führt sicher zu Kollisionen?!
    Oder drei nRF24L01 für je eine Adresse? (Was ich jedoch für Stromverschwendung halte)

    Wie kann man mit einem nRF24L01 gleichzeitig drei völlig unterschiedliche Adressen lesen?

    Gruß
    Robert

    1. Hallo Robert, ja, ein nRF24L01 Modul kann mit mehreren anderen Modulen kommunizieren bzw. „lauschen“ wie du es ausdrückst. Du müsstest den Sketch multiceiver_receiver.ino in diesem Abschnitt: https://wolles-elektronikkiste.de/nrf24l01-2-4-ghz-funkmodule#Multiceiver für dich anpassen.
      Und wie du dort siehst, wird Radio.begin() nur einmal aufgerufen, dafür musst du aber radio.openReadingPipe(i, address[i]); für jeden Transmitter aufrufen.
      Viel Erfolg! VG, Wolfgang

      1. Hallo Wolfgang,

        ich kann also „gleichzeitig“ auf diesen „unterschiedlichen“ Adressen: 0xB7641C6408, 0xEF28812B08, 0x60E6925809 lauschen?
        Obwohl Du doch oben erklärt hast, das sich die Adressen der Pipes 1-5 Byte 1-4 teilen?
        Ich habe schon ein wenig rum probiert, kann aber nur auf einer Adresse lesen.
        Und wie bereits geschrieben, sind die Adressen der Fernbedienungen in Stein gemeißelt.

        Gruß
        Robert

        1. Ja, das hatte ich wohl nicht bedacht, dass deine Adressen komplett unterschiedlich und unabänderlich sind. Dann wird es schwieriger. Wenn mir noch etwas einfällt, dann melde ich mich.

        2. Nach etwas Nachdenken bin ich zu dem Schluss gekommen, dass ein Modul mit drei Tasks nicht funktioniert. Und da das ziemlicher Blödsinn war, habe ich das herausgesucht aus meine. Kommentar.
          Bleibt wahrscheinlich nur die Variante mit drei Empfängermodulen zu arbeiten. Nicht wirklich schön…

          1. Hallo Wolfgang,

            vielen Dank für deine Einschätzung.
            Sehr komisch das die Module nicht mehrere verschiedene Adressen aus dem Funkverkehr herausfiltern können. Aber es schein wohl Technisch bedingt zu sein.
            Kennst Du vielleicht Alternative Module, die auch auf 2,4Ghz senden und empfangen können?

            Gruß
            Robert

            1. Leider nicht wirklich, außer den ESP32 Modulen, die über ESP-NOW kommunizieren. Aber ich glaube, das nützt dir nicht so viel. Tut mir leid.

  5. Hallo Wolfgang,
    ich habe mich gefragt ob man auch mehrere Receiver an einen Transmitter schalten kann? Außerdem wollte ich noch fragen ob sowohl Receiver also auch Transmitter einen Arduino benötigen. Das du weißt wofür:
    Ich möchte mit einem Transmitter mehrere Receiver ansteuern, welche nur ein Licht im Richtigen Zeitpunkt anmachen.
    Ist das möglich?
    (P.s. nachher soll das per App gesteuert werden.)
    Freundliche Grüße
    Sven

    1. Hallo Sven,
      ich habe es nicht probiert, also kann ich es nicht mit Bestimmtheit sagen. Was wahrscheinlich nicht geht, ist eine Nachricht gleichzeitig an zwei verschiedene Receiver zu senden für die man an einfach dieselbe Adresse verwendet. Das könnte Probleme mit den Acknowledgments geben, die zurückgesendet werden. Was gehen müsste, ist die zwei Receiver über unterschiedliche „Writing Pipes“ nacheinander anzusprechen. Also: radio.openWritingPipe(address1); dann Nachricht senden, dann radio.openWritingPipe(address2); und dann Nachricht an den anderen Receiver senden. Aber wie gesagt, probiert habe ich das nicht. Ich denke, du musst wohl mal ein bisschen herumprobieren
      Zu der anderen Frage: Ohne einen ansteuernden Mikrocontroller (Arduino) ist der nRF24L01 ziemlich wertlos.
      VG, Wolfgang

  6. Hallo Wolfgang,

    ich muss jetzt, nach gut 2 Wochen erfolglosen Versuchen, dir eine Frage stellen. Hast du jemals versucht, einen NRF24L01+ in Minimalbeschaltung an einem 328P standalone, interner Takt, 1MHz zum laufen zu bekommen?
    Ich habe alles versucht!!! Schaltung mehrfach kontrolliert, Compilerinfos in Boards angepasst, SPI Frequenz runter auf 250kB/s.
    Weder auf meiner PCB noch auf einem via SPI Programmer geflashtem Nano läuft das Modul.
    Hast du soetwas schon erlebt?

    Gruß Sven

    1. Hi Sven,

      zwei Wochen probieren – das ist hart. Ich habe die Module bisher nicht bei niedrigeren Taktraten ausprobiert und kann nur spekulieren. Du schreibst, du hast die SPI Taktrate heruntergesetzt, was Sinn macht. Hast du auch die Übertragungsrate der Module mit setDataRate() heruntergesetzt? Ich weiß nicht, ob das notwendig ist oder etwas bringt, aber vielleicht muss sonst irgendwer auf irgendwen zu lange warten.

      Ansonsten versuche ich bei Problemen alles auf einen Stand zurückzubringen, der funktioniert. Und dann füge ich Stück für Stück Änderungen ein, bis es nicht mehr geht. Oder andersherum, ich ändere, bis es wieder geht. Wenn du alles so lässt, wie es ist und nur die Taktfrequenz des Atmega328P erhöhst, geht es dann? Anders ausgedrückt: Ist es sicher nur die Taktfrequenz?

      Mehr fällt mir so spontan nicht ein.

      VG, Wolfgang

    2. Hallo Sven,
      da mich das nicht in Ruhe gelassen hat, habe jetzt mal zwei nRF24L01 Module mit LGT8F328P MiniEVB Boards angesteuert, weil man da recht bequem die Taktrate herunterregeln kann. Bei 1 MHz Taktrate funktionierte die Übertragung, auch ohne das Herunterregeln der SPI Übertragungsrate oder Herunterregeln der Übertragungsrate. Ich habe die Sketche transmitter_extended.ino und receiver_extended.ino dafür ausprobiert. Das Einzige, das ich ändern musste, war die Baudrate für die Anzeige auf dem seriellen Monitor. Unter 8 MHz funktionierte die Baurate 115200 nicht mehr.
      Wenn du möchtest, dann kannst du mir mal deine Programme und vielleicht Fotos von deiner Schaltung schicken (wolfgang.ewald@wolles-elektronikkiste.de). Vielleicht fällt mir dann noch etwas ein.
      VG, Wolfgang

  7. Hallo Wolfgang
    SUPER Blog hier – gratuliere.

    Deshalb ein kurzer Hinweis: Die RF24-Lib hat bei Verwendung eines ATtiny84 einen Fehler in der Piun-Belegung. (Hat mich ein paar graue Haare gekostet und mich an meiner Programmierfähigkeit zweifeln lassen).
    Wer also ungewöhnliche Prozessoren wie die Tinys verwenden möchte, sollte sich in die Lib reinhacken und die Pin-Belegung prüfen.

    Die ATtiny sind hier nicht erwähnt aber eignen sich super für stromsparende Applikationen (ohne WIFI). Ich mag die Dinger. Bin allerdings inzwischen auf LoRa-Funk umgestiegen wegen Reichweite, vor allem outdoor.

    1. Vielen Dank für den Hinweis. Das kann ich leider selbst nicht ändern, da die RF24-Lib nicht von mir stammt. Ich könnte aber – falls du das selbst nicht machen möchtest – das auf GitHub den Autoren als Issue melden und so anderen graue Haare und Selbstzweifel ersparen. Kannst mir noch mal mitteilen, welche Pins da nicht passen? Ich bin auch ein ATtiny Fan – man muss nicht immer mit Kanonen auf Spatzen schießen!

      VG, Wolfgang

  8. Hallo Wolfgang
    Kannst Du mir helfen? Möchte in 2 Wohnungen Türklingel und Türöffner mit nRF24L01 bauen.
    Hab mir schon ein Prog. „Gezimmert“ das Funktioniert. Welche Addresse(n) muss ich ändern damit sich die Beiden nicht gegenseitig beeinflussen?

    Rruß
    Alois

    1. Hallo Alois,
      zwei Möglichkeiten:
      1) Die Adresse (im Programm: „address“) anders benennen, als bei dem Paar, dass du schon im Einsatz hast. Dann setzt du die Adresse im Programm für das Transmittermodul in openWritingPipe(address); ein und dann entsprechend im Receivermodulprogramm: openReadingPipe(0,address);.
      2) Du stellst bei dem neuen Paar einen anderen Kanal mit setChannel ein. Dann solltest du dir theoretisch die Adressänderung sparen können.
      VG, Wolfgang

  9. Hallo Wolfgang,
    vielen Dank für diesen sehr schönen Beitrag! Obwohl ich die nRF24-Module schon in mehreren Projekten verwendet habe, muss ich zugeben, mich noch nicht mit allen möglichen Konfigurationen die der nRF24 in Zusammenarbeit mit der Library von TMRh20 bietet auseinandergesetzt zu haben.
    Das Interessante an diesem Modul ist die Kombination aus geringem Schlafstrom, hohe Geschwindigkeit beim Aufwachen, Senden, und wieder schlafen gehen, sowie der moderate Strombedarf beim senden, so daß man Projekte mit einer Knopfzelle über Jahre zum laufen bekommt.
    https://www.arduinoforum.de/arduino-Thread-SensEgg1-Temperatursensor-im-%C3%9C-Ei-ATtiny84-nRF24-NTC
    https://www.arduinoforum.de/arduino-Thread-SensEgg3-FunkSensor-im-%C3%9C-Ei-ATmega168PA-nRF24-BME280-NTC
    https://www.arduinoforum.de/arduino-Thread-Door-Sens-Low-Power-Funk-ReedSensor

    Ich hoffe Du hast nichts gegen die Lings meiner Projekte, ansonsten schmeiße die einfach raus!

    Gruß André

    1. Hallo André,

      vielen Dank für deine Einschätzung und die Links, die ich gerne teile!

      VG, Wolfgang

  10. Hallo Wolfgang

    wie immer sehr ausführlich und umfangreich erklärt – vielen Dank.

    Ich frage mich allerdings, welche Vorteile habe ich mit diesen Modulen gegenüber ESP-NOW?
    Link: https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/

    Beides sendet auf 2,4GHz, die Sendeleistung ist in etwa gleich, mit dem Unterschied, daß ich mit ESP-NOW kein zusätzliches Funkmodul benötige und mehr als sechs Kommunikationspartner einrichten kann.

    Zudem Frage ich mich auch noch, ob das 2,4GHz Netz mit WiFi und Bluetooth – die ja in den Haushalten immer mehr werden – nicht langsam zu voll wird.
    Wäre es nicht sinnvoller wieder auf 433MHz oder 868MHz zu gehen?

    Gruß
    Robert

    1. Hallo Robert,

      danke für den Kommentar und die Frage. Nur, weil ich über bestimmte Bauteile berichte, heißt das nicht unbedingt, dass ich ein großer Freund von ihnen bin oder sage, dass es nichts Besseres gäbe. Ich selbst bin eher ein Fan von 433 MHz Modulen, insbesondere dem HC-12 Modul. ESP-NOW ist in der Tat interessant, vielleicht berichte ich darüber auch noch einmal.

      VG, Wolfgang

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert