nRF24L01 radio.write Works, but Reports Error

I'm getting a fail error when sending data between two nRF24L01s, even though the data is being received. I first got it with sketches I've written, but I'm also getting it with Robin2's Simple one way transmission. Robin's sketch increments the message number when writing succeeds, but I've changed it to also increment when it fails, and the received data is incrementing properly.

The system I'm building uses a cheap Uno as the Tx, and a cheap Nano as the Rx. The Nano is attached to one of these boards that has a built-in socket for the transceiver, and I've also tried connecting the transceiver directly to the Nano. I've tried two of these boards with different Nanos and transceivers. I've also tried replacing the Nano with a better quality Geekcreit Uno, but that fails too. It also fails when I try the Geekcreit as the Tx. So it doesn't appear to be a problem with the hardware.

I'm using the built-in TMRh20 RF24 library version 1.3.2.

I know writing failure has come up before, but I couldn't find an example where writing is succeeding, but giving a error. Does anyone have any suggestions, or a solution?

Please post the actual programs that YOU have uploaded to your two Arduinos and also post examples of the output from the programs.

...R

Robin, firstly, Simple Rx is exactly as copied and pasted from the link in my first post:

// SimpleRx - the slave or the receiver

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

#define CE_PIN   9
#define CSN_PIN 10

const byte thisSlaveAddress[5] = {'R','x','A','A','A'};

RF24 radio(CE_PIN, CSN_PIN);

char dataReceived[10]; // this must match dataToSend in the TX
bool newData = false;

//===========

void setup() {

    Serial.begin(9600);

    Serial.println("SimpleRx Starting");
    radio.begin();
    radio.setDataRate( RF24_250KBPS );
    radio.openReadingPipe(1, thisSlaveAddress);
    radio.startListening();
}

//=============

void loop() {
    getData();
    showData();
}

//==============

void getData() {
    if ( radio.available() ) {
        radio.read( &dataReceived, sizeof(dataReceived) );
        newData = true;
    }
}

void showData() {
    if (newData == true) {
        Serial.print("Data received ");
        Serial.println(dataReceived);
        newData = false;
    }
}

The result is:

SimpleRx Starting
Data received Message 3
Data received Message 4
Data received Message 5
Data received Message 6
Data received Message 7
Data received Message 8
Data received Message 9
Data received Message 0
Data received Message 1
Data received Message 2
Data received Message 3
........

Simple Tx is exactly as copied and pasted, except that I've added updateMessage() after Serial.println(" Tx failed"), so it increments on failure, although it also failed before I did that:

// SimpleTx - the master or the transmitter

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


#define CE_PIN   9
#define CSN_PIN 10

const byte slaveAddress[5] = {'R','x','A','A','A'};


RF24 radio(CE_PIN, CSN_PIN); // Create a Radio

char dataToSend[10] = "Message 0";
char txNum = '0';


unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 1000; // send once per second


void setup() {

    Serial.begin(9600);

    Serial.println("SimpleTx Starting");

    radio.begin();
    radio.setDataRate( RF24_250KBPS );
    radio.setRetries(3,5); // delay, count
    radio.openWritingPipe(slaveAddress);
}

//====================

void loop() {
    currentMillis = millis();
    if (currentMillis - prevMillis >= txIntervalMillis) {
        send();
        prevMillis = millis();
    }
}

//====================

void send() {

    bool rslt;
    rslt = radio.write( &dataToSend, sizeof(dataToSend) );
        // Always use sizeof() as it gives the size as the number of bytes.
        // For example if dataToSend was an int sizeof() would correctly return 2

    Serial.print("Data Sent ");
    Serial.print(dataToSend);
    if (rslt) {
        Serial.println("  Acknowledge received");
        updateMessage();
    }
    else {
        Serial.println("  Tx failed");
        updateMessage();
    }
}

//================

void updateMessage() {
        // so you can see that new data is being sent
    txNum += 1;
    if (txNum > '9') {
        txNum = '0';
    }
    dataToSend[8] = txNum;
}

The result is:

SimpleTx Starting
Data Sent Message 0 Tx failed
Data Sent Message 1 Tx failed
Data Sent Message 2 Tx failed
Data Sent Message 3 Tx failed
Data Sent Message 4 Tx failed
Data Sent Message 5 Tx failed
Data Sent Message 6 Tx failed
Data Sent Message 7 Tx failed
Data Sent Message 8 Tx failed
Data Sent Message 9 Tx failed
Data Sent Message 0 Tx failed
Data Sent Message 1 Tx failed
Data Sent Message 2 Tx failed
.......

I'll post what I've been working on in a bit.

This is what I've written. It's a handheld model railway control panel, with a pot, 4x4 keypad, OLED and nRF24L01 connected to an Uno. The pot controls loco speed and direction with a centre off setting (there's also a zero off option, but I'm removing that). The keypad numbers switch points, letters A to D set the route to the fiddle yard (staging yard) track, *[Letter] toggles track sections on or off (it's a DC layout), and *[Number] is for future use. Current data is displayed on the OLED.

This is the Tx, that doesn't have any output to the serial monitor, so I've attached a photo of the OLED display:

//*******KEYPAD*******
#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the cymbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {A2, A3, 6 , 7}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad keys = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
bool keyAlt = false;

//*******POT*******
int potPin = A1;    // select the input pin for the potentiometer
int potVal = 0;       // variable to store the value coming from the sensor
int potOldVal = 0;
//****This option not used, but variable needed as code still in place****
bool potCentreOff = true; //Use centre off, or full scale with separate direction switch
//***End of unused bit****
int potPWM = 0; //Pot value mapped to PWM range, using poCentreOff setting
char potDirection; //[F]orward, [R]everse, [S]top

//*******OLED*******
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

//*******DATA STRUCTURE*******
struct Tx_Data {
  byte speed;
  byte direction;
  bool sections[4];
  byte fyTrack;
  byte point;
  byte other;
} controlData;


bool dataChanged = false;

//*******STATUS*******
char statusFYTrack;
char statusSections[4];


//*******RADIO*******
#include <SPI.h>
#include "RF24.h"

byte addresses[][6] = {"HEtx1", "HErx1", "HErx2"};
RF24 radio(9, 10);

void setup() {
  //*******OLED*******
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }
  display.clearDisplay();
  display.setCursor(0, 10);
  display.setTextColor(WHITE, BLACK);
  display.setTextWrap(false);
  display.setTextSize(3);
  display.println(F("Holmans"));
  display.setCursor(35, 40);
  display.println(F("End"));
  display.display();
  delay(2000);
  display.clearDisplay();
  display.setTextSize(2);
  display.drawFastHLine(0, 40, 128, WHITE);
  display.display();

  //*******RADIO*******
  radio.begin();
  radio.setPALevel(RF24_PA_LOW); //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX (-18dBm, -12dBm,-6dBM, and 0dBm)


}

void loop() {
  dataChanged = false;
  //*******READ KEYPAD*******
  char keyPressed = keys.getKey();

  if (keyPressed) {
    display.setCursor(0, 0);
    display.print(F("          "));
    display.setCursor(0, 0);
    if (keyPressed == '*') {
      keyAlt = true;
      //display.setCursor(0, 0);
      display.print(F("*"));
    } else {
      if (keyPressed == 'A' || keyPressed == 'B' || keyPressed == 'C' || keyPressed == 'D') {
        if (keyAlt == true) {
          switch (keyPressed) {
            case 'A':
              controlData.sections[0] = !controlData.sections[0];
              dataChanged = true;
              break;
            case 'B':
              controlData.sections[1] = !controlData.sections[1];
              dataChanged = true;
              break;
            case 'C':
              controlData.sections[2] = !controlData.sections[2];
              dataChanged = true;
              break;
            case 'D':
              controlData.sections[3] = !controlData.sections[3];
              dataChanged = true;
              break;
          }
          display.print(F("Section "));
          display.print(keyPressed);
        } else {
          controlData.fyTrack = keyPressed;
          dataChanged = true;
          display.print(F("FY Track "));
          display.print(keyPressed);
        }
      } else {
        if (keyAlt == true) {
          //controlData.other = keyPressed; //No other options currently available
          //display.print("Other "); //No other options currently available
          //display.print(keyPressed); //No other options currently available
          display.print(F("!Unused!")); //No other options currently available
          keyAlt = false;
        } else {
          if (keyPressed != '9' && keyPressed != '0' && keyPressed != '#') {
            controlData.point = keyPressed;
            dataChanged = true;
            display.print(F("Point "));
            display.print(keyPressed);
          } else {
            display.print(F("!Unused!"));
          }
        }
      }
      keyAlt = false;
    }
    display.display();

  }


  //*******READ POT*******
  potVal = analogRead(potPin);    // read the value from the sensor
  if (potVal - 3 > potOldVal || potVal + 3 < potOldVal) {
    //Centre off
    if (potCentreOff == true) {
      potPWM = 0;
      potDirection = 'S';
      if (potVal < 500) {
        potPWM = map(potVal, 0, 500, 255, 0);
        potDirection = 'R';
      }
      if (potVal > 523) {
        potPWM = map(potVal, 523, 1023, 0, 255);
        potDirection = 'F';
      }
      //Zero off
    } else {
      //****This section not currently used****
      potPWM = map(potVal, 0, 1023, 0, 255);
      if (potVal < 3) {
        potPWM = 0;
        potDirection = 'S';
      }
      //****End of unused section****
    }
    display.setCursor(0, 20);
    if (potDirection == 'S') {
      display.print(F("Stop    "));
    } else {
      display.print(F("Speed:     "));
      display.setCursor(73, 20);
      display.print(potDirection);
      display.print(potPWM);
    }
    display.display();
    controlData.direction = potDirection;
    controlData.speed = potPWM;
    dataChanged = true;
    potOldVal = potVal;
  }



  //If acknowledged update status display
  statusFYTrack = controlData.fyTrack;
  //Add section switch status
  display.setTextSize(1);
  display.setCursor(0, 45);
  display.print(F("FY Track: "));
  display.print(statusFYTrack);
  display.setCursor(0, 55);
  display.print(F("Sections On:         "));
  display.setCursor(75, 55);
  if (controlData.sections[0] == true) {
    display.print(F("A "));
  }
  if (controlData.sections[1] == true) {
    display.print(F("B "));
  }
  if (controlData.sections[2] == true) {
    display.print(F("C "));
  }
  if (controlData.sections[3] == true) {
    display.print(F("D "));
  }


  display.display();
  display.setTextSize(2);


  if (dataChanged) {
    radio.openWritingPipe(addresses[0]);
    //radio.openReadingPipe(1, addresses[1]);
    //radio.stopListening();
    if (!radio.write(&controlData, sizeof(controlData))) {
      display.setCursor(0, 0);
      display.print(F("!Tx Error!"));
      display.display();
    } else {
      display.setCursor(120, 0);
      display.print(F("*"));
      display.display();
    }
    //radio.startListening();
    //dataChanged = false;
  }




}

Rx to follow, as the message was too long.

This is the Rx. There will be several of them for different parts of the layout when I get that far. It's just a Nano and nRF24L01 at the moment, and received data displays on the serial monitor.

//*******DATA STRUCTURE*******
struct Tx_Data {
  byte speed;
  byte direction;
  bool sections[4];
  byte fyTrack;
  byte point;
  byte other;
} data;
bool dataChanged = false;

//*******RADIO*******
#include <SPI.h>
#include "RF24.h"

byte addresses[][6] = {"HEtx1", "HErx1", "HErx2"};
RF24 radio(9, 10);

void setup() {

  Serial.begin(9600);
  Serial.println(F("HErx1"));

  //*******RADIO*******
  radio.begin();
  radio.setPALevel(RF24_PA_LOW); //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX (-18dBm, -12dBm,-6dBM, and 0dBm)

  //radio.openWritingPipe(addresses[1]);
  radio.openReadingPipe(1, addresses[0]);
  radio.startListening();
}

void loop() {
  if (radio.available()) {
    radio.read( &data, sizeof(data) );             // Get the payload
    Serial.print(char(data.direction));
    Serial.print(data.speed);
    Serial.print(" Sect:");
    if (data.sections[0]) {
      Serial.print("A");
    }
    if (data.sections[1]) {
      Serial.print("B");
    }
    if (data.sections[2]) {
      Serial.print("C");
    }
    if (data.sections[3]) {
      Serial.print("D");
    }
    Serial.print(" FY:");
    Serial.print(char(data.fyTrack));
    Serial.print(" Point:");
    Serial.println(char(data.point));
  }


}

I've had to attach a screenshot of the serial monitor, as I couldn't paste the text as displayed. However, this is how it pastes into LibreOffice, with a # where there's no data. I don't know if that's relevant.

HErx1
F0 Sect: FY:# Point:#
F0 Sect: FY:# Point:#
F0 Sect: FY:# Point:#
F7 Sect: FY:# Point:#
F62 Sect: FY:# Point:#
F91 Sect: FY:# Point:#
F99 Sect: FY:# Point:#
F99 Sect: FY:# Point:5
F99 Sect: FY:# Point:6
F99 Sect: FY:B Point:6
F99 Sect: FY:C Point:6
F104 Sect: FY:C Point:6
F123 Sect: FY:C Point:6
F145 Sect: FY:C Point:6
F147 Sect: FY:C Point:6
F147 Sect:B FY:C Point:6
F147 Sect:BC FY:C Point:6

Screenshot from 2019-05-06 22-28-10.png

Images from Reply #3 and #4 so we don't have to download them. See this Simple Image Guide

Screenshot from 2019-05-06 22-28-10.png

...R

My wild guess is that your Tx is sending the data too often and some messages fail.

I am using nRF24s for wireless control of model trains and my Tx sends a message about 5 times per second regardless of whether the data has changed. That allows the Rx to know that there has been a communication failure if there are no message within some time period - I think mine reports a fail if there is no message for 1 second - i.e. 5 consecutive missed messages.

I also start with a much slower data rate - once per second - so I can check that each message is correctly received.

With a wireless system there is always a risk that a message will fail and the system must allow for that.

You may want to experiment with the number of auto retries when a message fails.

If you find you are suffering a very high failure rate try a different channel in case something is interfering with the nRF24 signal.

...R

Robin2:
Images from Reply #3 and #4 so we don't have to download them. See this Simple Image Guide

.......

...R

Thanks Robin. I couldn't see how to do it, or I would have!

Robin2:
My wild guess is that your Tx is sending the data too often and some messages fail.

I am using nRF24s for wireless control of model trains and my Tx sends a message about 5 times per second regardless of whether the data has changed. That allows the Rx to know that there has been a communication failure if there are no message within some time period - I think mine reports a fail if there is no message for 1 second - i.e. 5 consecutive missed messages.

I also start with a much slower data rate - once per second - so I can check that each message is correctly received.

With a wireless system there is always a risk that a message will fail and the system must allow for that.

You may want to experiment with the number of auto retries when a message fails.

If you find you are suffering a very high failure rate try a different channel in case something is interfering with the nRF24 signal.

...R

Thanks. I'll try all those things, but if it doesn't work with your simple example, when it does for other people, I wonder if it will solve it. I'll try as soon as I can.

It's your fault I discovered Arduinos Robin, thanks to your posts about RC locos on RMweb ;).

John_SB:
but if it doesn't work with your simple example, when it does for other people, I wonder if it will solve it.

Perhaps I have misunderstood, but I thought you said that my examples do work. If not there seems little point trying anything else until you do get them working.

It's your fault I discovered Arduinos Robin, thanks to your posts about RC locos on RMweb ;).

Have fun.

...R

Robin2:
Perhaps I have misunderstood, but I thought you said that my examples do work. If not there seems little point trying anything else until you do get them working.

...R

No. I thought I made it clear in my first post that your simple example is producing the same error.

John_SB:
No. I thought I made it clear in my first post that your simple example is producing the same error.

Sorry, I got confused.

It sounds like the Rx is not transmitting its acknowledgement (or the Tx is not receiving it). Have you tried swapping the two nRF24s?

A common cause of transmission failure is an inadequate 3.3v power supply. Try powering the nRF24s from a pair of AA alkaline cells (3v) with the battery GND connected to the Arduino GND.

Do you have a 3rd (or 4th) nRF24 that you can try in case one of them is faulty?

Are all the nRF24s the same and are they all the low-power version with the PCB antenna?

...R

I've got two identical receivers, although so far I'm only connecting to one. I've tried both of them with the same result. I haven't tried changing the nRF24L01 on the transmitter yet. All the nRF24s are the same, low power with PCB antenna version. I've been powering them from the USB connection, which is plugged into one of the externally powered ports on my USB hub, so there shouldn't be an significant limit to the power available.

I've got a bigger problem with this sketch at the moment, that I've just posted a question on, so will get back to this one later.

John_SB:
I've been powering them from the USB connection,

nRF24L01+ modules use 3.3v so you can't connect then directly to the 5v from a USB port.

There must be something in between that you need to tell us about.

...R

@goodgrief, as yours is a new question I have suggested to the Moderator to move it to its own Thread.

If you are not getting suitable advice in the French section and wish to see if the English section will be more productive please post your code here and tell us in detail what it does and what you want it to do that is different,

...R
Simple nRF24L01+ Tutorial

Many Thanks Robin2, i will follow your advice !

@goodgrief's question is now here

...R