Nrf24l01+ Daten Senden, Empfangen und zurück senden

Moin Leute,

Ich will mir momentan eine Fernbedienung (Sender) mit einem Arduino Nano, und einen Empfänger mit einem Arduino Uno oder Mega oder was auch immer Bauen.

Eine One-way-Verbindung ist ja absolut kein Problem.

Allerdings will ich jetzt Daten vom Sender (Poti 0-1023) an den Empfänger übermitteln, der setzt das dann in Bewegung (Servos, DC-Motoren) um, und dann soll er noch Daten (irgendwann mal Drehzahl, und Servowinkel) zurück Senden, dass ich genau weiss, dass die Verbindung auch funktioniert.

Ich habe mir letztens ein “Pingpong” Sketch geschrieben, der auch funktioniert.

Ping

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

RF24 radio(8, 7);

static const uint64_t pipes[6] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL};

//byte ClientNummer = 1; // Mögliche Werte: 1-6
int poti1Val = 1;
int poti2Val;
int beep = 3;
LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup() {
  lcd.begin();
  pinMode(beep, OUTPUT);
  Serial.begin(9600);
  radio.begin();
  delay(20);
  radio.setChannel(110);                // Funkkanal - Mögliche Werte: 0 - 127
  radio.setAutoAck(0);
  radio.setRetries(15, 0);
  radio.setPALevel(RF24_PA_LOW);     // Sendestärke darf die gesetzlichen Vorgaben des jeweiligen Landes nicht überschreiten!
  // RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm

  radio.openWritingPipe(pipes[0]);
  radio.openReadingPipe(1, pipes[1]);

  radio.startListening();
  lcd.backlight();
  delay(20);
}


void loop()
{
  transmit();
  recieve();
}

void transmit()
{
  long message[8] = {int(poti1Val)};
  radio.stopListening();
  radio.write(&message, sizeof(message));
  Serial.print("Sent: ");
  Serial.println(poti1Val);
  radio.startListening();
  delay(50);
}

void recieve()
{
  if (radio.available()) {
    long got_message[8] = {0, 0, 0, 0, 0, 0, 0, 0};

    bool done = false;
    while (!done) {
      done = radio.read(&got_message, sizeof(got_message));
    }
    poti2Val = got_message[0];
    if (poti2Val == 1)
    {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Connection ok");
      delay(100);
      digitalWrite(beep, HIGH);
      delay(100);
      digitalWrite(beep, LOW);
      delay(100);
      Serial.print("Recieved: ");
      Serial.println(poti2Val);
      poti2Val = 0;

    }
  }
  else {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("No connection");
    digitalWrite(beep, HIGH);
    delay(1000);
    digitalWrite(beep, LOW);

  }
}

Pong

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

RF24 radio(8, 7);

static const uint64_t pipes[6] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL};

int poti1Val;
int poti2Val = 1;

void setup() {
  radio.begin();
  delay(20);
  radio.setChannel(110);                // Funkkanal - Mögliche Werte: 0 - 127
  radio.setAutoAck(0);
  radio.setPALevel(RF24_PA_LOW);     // Sendestärke darf die gesetzlichen Vorgaben des jeweiligen Landes nicht überschreiten!
  // RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm

  radio.openWritingPipe(pipes[1]);
  radio.openReadingPipe(1, pipes[0]);


  radio.startListening();
  delay(20);
  Serial.begin(9600);
}


void loop()
{
  recieve();
}

void recieve()
{
  if (radio.available()) {
    long got_message[8] = {0, 0, 0, 0, 0, 0, 0, 0};

    bool done = false;
    while (!done) {
      done = radio.read(&got_message, sizeof(got_message));
    }
    poti1Val = got_message[0];
    Serial.print("Recieved: ");
    Serial.println(poti1Val);
    if (poti1Val == 1)
    {
      transmit();
    }
  }
}

void transmit()
{
  long message[8] = {int(poti2Val)};
  radio.stopListening();
  radio.write(&message, sizeof(message));
  Serial.print("Sent beep: ");
  Serial.println(poti2Val);
  radio.startListening();
}

Allerdings wenn ich das jetzt auf die Fernbedienung übertragen will, funktioniert es garnicht mehr.

Ich glaube, und denke, es liegt natürlich am “radio.stop/startListening” und die Zeiten dazwischen.

Also wenn ich jetzt was hin sende, fängt er sofort an zu hören, aber da kommt noch nichts, weil der Empfänger evtl mit 1ms verzögerung zurück sendet, und der Sender dann wieder bei “stopListening” ist.

Ich habe keine ahnung, wie ich die 2 Arduinos abstimmen kann, dass der Sender auf die Antwort vom Empfänger wartet, aber die Steuerung dennoch reibungslos funktionieren soll.

Ich glaube bei meinem Pingpong ist das eher zufall, dass es funktioniert.

Ich habe im Netz schon diverse sachen gelesen, hab auch bezüglich writeAckPayload() sachen gelesen.

Allerdings verstehe ich es nicht.

Ich hoffe mir kann hier jemand helfen.

Der Sensor darf nicht ständig senden, dazwischen sollten schon genügend große Pausen sein, in denen etwas empfangen werden kann. Einmal pro Sekunde sollte als Startwert reichen.

Die Pause darf natürlich nicht mit delay() implementiert werden, das blockiert sämtliche anderen Aktivitäten. Siehe BlinkWithoutDelay und ähnliche SeveralThingsAtATime Beispiele, wie eine nicht-blockierende Pause implementiert werden kann.

Moin, jo, das weiss ich.

Ich nutze auch "BlinkWithoutDelay" im anderen Sketch. Aber mir bringt es ja nichts, wenn die Fernbedienung nur jede Sekunde einen Steuerbefehl sendet. Und wenn die 2 Arduinos dann nicht Synchron laufen, also, dass der Empfänger gerade im Empfangsmodus ist, und der Sender auch? Dann funktioniert es wiederum auch nicht.

Die Fernbedienung sendet, wenn eine Taste gedrückt wird, sonst kann sie ständig auf Empfang bleiben. Sie kann auch gezielt Meßwerte abrufen, dann wartet der Sensor die ganze Zeit nur auf solche Kommandos, und sendet auch nur dann eine Antwort.

Nun, die Fernbedienung ist mit Potis ausgestattet, also Joysticks. Und deren Status wird permanent abgefragt und zum Empfänger (Ein RC-Boot) gesendet. Das Boot muss permanent Steuerbefehle erhalten, sonst fährt es ja nicht richtig.

Sender code

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

//RF24 radio(48, 49); // für Arduino Mega
RF24 radio(8, 7); // für Arduino Nano/Uno/etc

static const uint64_t pipes[6] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL};

int poti1Val;
int poti2Val = 0;
int poti1 = 1;
int poti2 = 0;
int motorrpm;
int servowinkel;
int beep = 3;
int pong;
int sent = 0;

#define code 1337

void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.setChannel(50);                // Funkkanal - Mögliche Werte: 0 - 127
  radio.setAutoAck(0);
  radio.setRetries(15, 0);
  radio.setPALevel(RF24_PA_MIN);
  // Sendestärke darf die gesetzlichen Vorgaben des jeweiligen Landes nicht überschreiten!
  // RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm
  pinMode(beep, OUTPUT);
  radio.openWritingPipe(pipes[0]);
  radio.openWritingPipe(pipes[1]);
  radio.openWritingPipe(pipes[2]);
  radio.openReadingPipe(1, pipes[1]);
  radio.openReadingPipe(2, pipes[2]);
  radio.openReadingPipe(3, pipes[3]);
}


void loop()
{
  poti1Val = analogRead(poti1);
  poti2Val = analogRead(poti2);
  poti1Val = map(poti1Val, 0, 1023, 170, 10);
  poti2Val = map(poti2Val, 0, 1023, 255, 0);
  long message[8] = {int(poti1Val), int(poti2Val), (code)};
  if (sent == 0)
  {
    radio.stopListening();
    radio.write(&message, sizeof(message));
    Serial.println("Sent");
    sent = 1;
  }
  if (sent == 1)
  {
    radio.startListening();
    Serial.println("Start listening");
    delay(10);
    if (radio.available()) {
      long got_message[8] = {0, 0, 0, 0, 0, 0, 0, 0};
      bool done = false;
      while (!done) {
        done = radio.read(&got_message, sizeof(got_message));
      }
      pong = got_message[0];
      Serial.println(pong);
      if (pong = 69)
      {
        Serial.println("Recieved");
      }
      sent = 0;
    }
  }
//  Serial.println(poti1Val);
//  Serial.println(poti2Val);
//  Serial.println(code);
}

Empfänger

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

RF24 radio(8, 7);

static const uint64_t pipes[6] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL};

int poti1Val;
int poti2Val;
int motor = 3;
Servo myservo1;
int motorrpm;
int servowinkel;
int code;

#define pong 69

void setup()
{
  radio.begin();
  radio.setChannel(50);                // Funkkanal - Mögliche Werte: 0 - 127
  radio.setAutoAck(0);
  radio.setPALevel(RF24_PA_MIN);     // Sendestärke darf die gesetzlichen Vorgaben des jeweiligen Landes nicht überschreiten!
  // RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm
  radio.openWritingPipe(pipes[1]);
  radio.openWritingPipe(pipes[2]);
  radio.openWritingPipe(pipes[3]);
  radio.openReadingPipe(1, pipes[0]);
  radio.openReadingPipe(2, pipes[1]);
  radio.openReadingPipe(3, pipes[2]);
  pinMode(motor, OUTPUT);
  myservo1.attach(10);
  Serial.begin(9600);
  radio.startListening();
}

void loop()
{
  if (radio.available()) {
    long got_message[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    bool done = false;
    while (!done) {
      done = radio.read(&got_message, sizeof(got_message));
    }
    poti1Val = got_message[0];
    poti2Val = got_message[1];
    code = got_message[2];
    if (code == 1337)
    {
      analogWrite(motor, poti2Val);
      myservo1.write(poti1Val);
      Serial.println(poti1Val);
      Serial.println(poti2Val);
      Serial.println(code);
      Serial.println("Sent");
      long message[8] = {(pong)};
      delay(1);
      radio.stopListening();
      radio.write(&message, sizeof(message));
      radio.startListening();
    }
  }
}

So sieht mein code momentan aus.

Jetzt Sendet der Sender einmal die Werte der Analog eingänge, wenn der Empfänger es empfängt, schickt er eine Bestätigung zurück. Wenn der Sender diese erhält, darf er erst erneut senden.

Wenn ich das ganz ohne delays benutze, kommt rein garnichts zurück.
Mit ein paar Millisekunden delay dazwischen, läuft es ca. 20 Programmzyklen durch, und bleib dann bei

    radio.startListening();
    Serial.println("Start listening");
    delay(10);

…hängen.

Keine ahnung wie ich das löse?

Dein Code ist so furchtbar und konzeptlos zusammengebastelt, so kann das nicht funktionieren .-( Versuche erst mal zu verstehen, was da momentan abläuft, und mache es dann so, wie es eigentlich funktionieren soll.

Dein Sender sendet ständig Daten, so kann das nicht funktionieren. Dazu hatte ich ja schon vorgeschlagen, erst mal nur alle Sekunde zu senden, und das Programm so überhaupt zum Laufen zu bekommen. Dann kannst Du dazu übergehen, öfters zu senden.

Hallo,

ich hab mir mal Gedanken gemacht.

Eine lösung habe ich damit erzielt:

Sender

// Funkfernbedienung, Sender, 2-Wege Kommunikation

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

// Identifikationscode für Reciever

#define code 1337

// CE, CSN Pins definieren

//RF24 radio(48, 49); // für Arduino Mega
RF24 radio(8, 7); // für Arduino Nano/Uno/etc

static const uint64_t pipes[6] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL};

// I²C Adresse für LCD, Spalten, Zeilen

LiquidCrystal_I2C lcd(0x27, 20, 4);

// Messwerte

int poti1Val;
int poti2Val;
int poti1 = 1;
int poti2 = 0;

// Sonstige

int pong;
int sent = 0;
const long interval = 10000;
unsigned long previousMillis = 0;
int motorrpm;
int servowinkel;

// Pins

int beep = 3; // Speaker Pin

// Setup

void setup() {

  // Seriellen Monitor starten (Baudrate 9600)

  Serial.begin(9600);

  // LCD-Setup

  lcd.begin();
  lcd.backlight();
  lcd.clear();

  // RF-24 Setup

  radio.begin();
  radio.setChannel(50);                // Funkkanal - Mögliche Werte: 0 - 127
  radio.setAutoAck(0);
  radio.setRetries(15, 0);
  radio.setPALevel(RF24_PA_MIN);
  // RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm

  // Pipes öffnen

  radio.openWritingPipe(pipes[0]);
  radio.openWritingPipe(pipes[1]);
  radio.openWritingPipe(pipes[2]);
  radio.openReadingPipe(1, pipes[1]);
  radio.openReadingPipe(2, pipes[2]);
  radio.openReadingPipe(3, pipes[3]);

  // Pinmodes definieren

  pinMode(beep, OUTPUT);

}

// Main loop

void loop()
{
  messwerte(); // Messwerte auslesen
  if (sent == 0)
  {
    transmit(); // Daten übermitteln
  }
  if (sent == 1)
  {
    recieve(); // Daten empfangen und auswerten
  }
  if (sent == 1 && pong != 69)
  {
    transmit(); // Daten übermitteln
  }
  //  Serial.println(poti1Val);
  //  Serial.println(poti2Val);
  //  Serial.println(code);
}

// Messwerte auslesen und mappen

void messwerte()
{
  poti1Val = analogRead(poti1);
  poti2Val = analogRead(poti2);
  poti1Val = map(poti1Val, 0, 1023, 170, 10);
  poti2Val = map(poti2Val, 0, 1023, 255, 0);
}

// Inhalte per Funk übertragen

void transmit()
{
  long message[8] = {int(poti1Val), int(poti2Val), (code)};
  radio.stopListening();
  radio.write(&message, sizeof(message));
  //  Serial.println("Sent");
  sent = 1;
}

// Inhalte per Funk empfangen

void recieve()
{
  radio.startListening();
  //  Serial.println("Start listening");
  delay(10);
  if (radio.available()) {
    long got_message[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    bool done = false;
    while (!done) {
      done = radio.read(&got_message, sizeof(got_message));
    }
    motorrpm = got_message[0];
    servowinkel = got_message[1];
    pong = got_message[2];
    //   Serial.println(motorrpm);
    //   Serial.println(servowinkel);
    //   Serial.println(pong);
    lcdPrint();
    if (pong = 69)
    {
      //     Serial.println("Recieved");
      pong = 0;
    }
    sent = 0;
  }
  if (!radio.available())    {
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("No data");
    }
  }
}

// Inhalte auf dem LCD ausgeben

void lcdPrint()
{
  lcd.setCursor(0, 0);
  if (motorrpm < 100 && motorrpm > 9)
  {
    lcd.print("Motor: ");
    lcd.print("0");
    lcd.print(motorrpm);
  }
  if (motorrpm < 10)
  {
    lcd.print("Motor: ");
    lcd.print("00");
    lcd.print(motorrpm);
  }
  if (motorrpm > 99)
  {
    lcd.print("Motor: ");
    lcd.print("100");
  }
  lcd.print(" %  ");
  lcd.setCursor(0, 1);
  if (servowinkel < 100 && servowinkel > 9)
  {
    lcd.print("Servo: ");
    lcd.print("0");
    lcd.print(servowinkel);
  }
  if (servowinkel < 10)
  {
    lcd.print("Servo: ");
    lcd.print("00");
    lcd.print(servowinkel);
  }
  if (servowinkel > 99)
  {
    lcd.print("Servo: ");
    lcd.print(servowinkel);
  }
  lcd.print(" Grad");
}

Empfänger

// Funkfernbedienung, Empfänger, 2-Wege Kommunikation

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

// Identifikationscode für Transmitter

#define pong 69

// CE, CSN Pins definieren

//RF24 radio(48, 49); // für Arduino Mega
RF24 radio(8, 7); // für Arduino Nano/Uno/etc

static const uint64_t pipes[6] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL};

// Messwerte

int poti1Val;
int poti2Val;

// Pins

int motor = 3;
Servo myservo1;

// Sonstige

int motorrpm;
int servowinkel;

// Code Variable

int code;

// Main loop

void setup()
{

  // Seriellen Monitor starten (Baudrate 9600)

  // Serial.begin(9600);

  // RF-24 Setup

  radio.begin();
  radio.setChannel(50); // Mögliche Kanäle 0-127
  radio.setAutoAck(0);
  radio.setPALevel(RF24_PA_MIN);
  // RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_MED=-6dBM, and RF24_PA_HIGH=0dBm

  // Pipes öffnen

  radio.openWritingPipe(pipes[1]);
  radio.openWritingPipe(pipes[2]);
  radio.openWritingPipe(pipes[3]);
  radio.openReadingPipe(1, pipes[0]);
  radio.openReadingPipe(2, pipes[1]);
  radio.openReadingPipe(3, pipes[2]);

  // Pinmodes definieren

  pinMode(motor, OUTPUT);
  myservo1.attach(10);

  // RF-24 Zuhören

  radio.startListening();
}

// Main loop

void loop()
{
  recieve(); // Daten empfangen und auswerten
}

// Inhalte per Funk empfangen

void recieve()
{
  if (radio.available()) {
    long got_message[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    bool done = false;
    while (!done) {
      done = radio.read(&got_message, sizeof(got_message));
    }
    poti1Val = got_message[0];
    poti2Val = got_message[1];
    code = got_message[2];
    if (code == 1337)
    {

      if (poti2Val < 10)
      {
        digitalWrite(motor, LOW);
      }
      else {
        analogWrite(motor, poti2Val);
      }
      myservo1.write(poti1Val);
      Serial.println(poti1Val);
      Serial.println(poti2Val);
      Serial.println(code);
      Serial.println("Sent");
      servowinkel = map(poti1Val, 170, 10, 180, 0);
      motorrpm = map(poti2Val, 255, 0, 100, 0);
      transmit();   // Inhalte senden
    }
  }
}

// Inhalte per Funk übertragen

void transmit()
{
  long message[8] = {int(motorrpm), int(servowinkel), (pong)};
  delay(1);
  radio.stopListening();
  radio.write(&message, sizeof(message));
  radio.startListening();
}

Somit hab ich erzielt, dass der Sender sendet, dann wartet er auf rückantwort vom Empfänger, und erst dann darf er wieder Senden, wenn er keine rückantwort bekommt, aber gesendet hat, sendet er erneut, bis antwort rein kommt.

Sieht ein wenig kompliziert aus… Funktioniert aber (noch).

Meinung dazu?

Ich würde die Rückantwort immer direkt vom Chip als Bestandteil des Acknowledges transportieren lassen.

Also mit Acknowledge Payload arbeiten, anstatt das Acknowledge völlig abzuschalten und den Rücktransport selbst zu programmieren.

und vielleicht reicht es ja auch nur zu senden, wenn sich etwas ändert.... ich zeig dir mal dazu ein Schnipsel von meiner ANwendung...

 if (Sensor[3] != Sensoralt[3])
    {
    radio.stopListening();
    radio.write(Sensor, sizeof(Sensor));
    radio.startListening();
  }
     Sensoralt[3] = Sensor[3];

Also ich hab das ja jetzt so gemacht, dass der Sender nur erneut sendet, wenn er vom Empfänger eine rückantwort bekommt.

Das funktioniert recht gut. Hat ein recieve-lost-ratio von 10:1.

Also bei 1000x Senden erhält er nur 10x keine Antwort.

Jetzt eine andere frage: Man soll ja für den NRF eine stabile 3,3V Spannungsquelle zur versorgung nutzen.

Habe mir von Pololu 2 3,3V DC/DC Wandler gekauft und versorge diese mit dem 5V Pin vom Arduino. Habe die Sendeleistung auf "MAX" gestellt, aber dann verliert er 100% vom gesendeten :|

meisterQ: Habe mir von Pololu 2 3,3V DC/DC Wandler gekauft und versorge diese mit dem 5V Pin vom Arduino. Habe die Sendeleistung auf "MAX" gestellt, aber dann verliert er 100% vom gesendeten :|

Weil vermutlich die Stromaufnahme höher ist, als der 5V Pin bzw. der 3,3 V-Regler hergibt.

Du solltest mal im Datenblatt des NRF nachlesen, welchen Strom dieser bei 100 % Leistung braucht.

Wenn die Module zu nahe beieinander sind, kann eine hohe Sendeleistung die Empfänger übersteuern.

Ich hab schon ca. 30- 40 nrf24 mit je einem nano laufen- alle bekommen ihren Saft vom 3,3V Pin des Boards und alle laufen sorgenfrei und stabil...

Ich habe inzwischen auch schon die ersten mit Antenne verbaut- die ziehen mal gut 120mA, die habe ich mit einem einfachen Festspannungsregler (in SMD) am 5V PIN eines Mega hängen- ohne Kondensator oder sonst was- und das läuft auch sehr stabil...

Laufen sie bei dir auch auf "MAX" ?

Weil sobald ich auf Max stelle, funktioniert die Verbindung garnicht mehr.

ja, sowohl die mit, als auch die ohne Antenne... mach mal ein Bild von deiner Verdrahtung... ich glaub zu lange Kabel und schlechte Verbindungen sind die einzige echte Fehlerquelle bei den Teilen...