Problem with transmitting data via nRF24l01

I'm using two standalone atmega328p chips - one as a controller and the other as a rc car. I want to transmit data from the controller to the car (but preferably in both ways). Right now I'm trying to transmit data from two joysticks to the car as an array and display it on oled screen on the car.
But I never get past the receiver.available() condition leaving me with no data.

Code for both below

Code for the controller

#include <Arduino.h>
#include <U8x8lib.h>
#include <Wire.h>
#include <RF24.h>
#include <nRF24L01.h>
#include <SPI.h>

#define BatteryVoltagePin 4

#define JoyStickLeftX A0
#define JoyStickLeftY A3
#define JoyStickRightX A2
#define JoyStickRightY A1

#define CE 7
#define CS 8
#define SCK 13

RF24 transmitter(CE, CS);

U8X8_SSD1306_128X64_NONAME_HW_I2C display1(U8X8_PIN_NONE);
U8X8_SSD1306_128X64_NONAME_HW_I2C display2(U8X8_PIN_NONE);

const int joyStickZerothRange = 22; // Range from the middle to ONE side for Joysticks to still register as zero
const int joyStickMinimumPoint = 1;
const int joyStickMaximumPoint = 1024;
const int joyStickMiddlePoint = joyStickMaximumPoint / 2;
const int joyStickLowRangeTopPoint = joyStickMiddlePoint - joyStickZerothRange;
const int joyStickTopRangeLowestPoint = joyStickMiddlePoint + joyStickZerothRange;

char JoyStickCharValue[11];
char *BatteryVoltageMessageNormal = "BATTERY NORMAL";
char *BatteryVoltageMessageLow = "BATTERY LOW   ";

const uint64_t addressReceiver = 0xE8E8F0F0E1LL;
const uint64_t addressTransmitter = 0xE8E8F0F1E1LL;

void drawJoyStickValues(int joyStickValuesInput[]){
  sprintf(JoyStickCharValue,"X1:%03d", abs(joyStickValuesInput[0]));
  display1.drawString(1, 1, JoyStickCharValue);
  
  sprintf(JoyStickCharValue,"Y1:%03d", abs(joyStickValuesInput[1]));
  display1.drawString(9, 1, JoyStickCharValue);
  
  sprintf(JoyStickCharValue,"X2:%03d", abs(joyStickValuesInput[2]));
  display1.drawString(1, 3, JoyStickCharValue);
  
  sprintf(JoyStickCharValue,"Y2:%03d", abs(joyStickValuesInput[3]));
  display1.drawString(9, 3, JoyStickCharValue);
}

int convertJoyStickValue(int inputValue){
  if (inputValue > joyStickLowRangeTopPoint && inputValue < joyStickTopRangeLowestPoint){
    return 0;
  }
  else if (inputValue <= joyStickLowRangeTopPoint){
    return map(inputValue, joyStickMinimumPoint, joyStickLowRangeTopPoint, -255, -1);
  }
  else if (inputValue >= joyStickTopRangeLowestPoint){
    return map(inputValue, joyStickTopRangeLowestPoint, joyStickMaximumPoint, 1, 255);
  }
}

int * getJoyStickValues(){
  static int helper[4];
  helper[0] = convertJoyStickValue(analogRead(JoyStickLeftX));
  helper[1] = convertJoyStickValue(analogRead(JoyStickLeftY));
  helper[2] = convertJoyStickValue(analogRead(JoyStickRightX));
  helper[3] = convertJoyStickValue(analogRead(JoyStickRightY));
  return helper;
}

void drawBatteryVoltage(){
    pinMode(BatteryVoltagePin, OUTPUT);
    digitalWrite(BatteryVoltagePin, LOW);
    pinMode(BatteryVoltagePin, INPUT);
    if (digitalRead(BatteryVoltagePin) == HIGH){
      display1.drawString(1, 7, BatteryVoltageMessageNormal);
    }else{
      display1.drawString(1, 7, BatteryVoltageMessageLow); 
    }
}

void sendAllData(int rotationInput, int speedInput){
  transmitter.stopListening();
  byte helper[2];
  helper[0] = rotationInput;
  helper[1] = speedInput;
  transmitter.write(helper, sizeof(helper));
  transmitter.startListening();
}

void setup(void) {
  // set joystick and battery pins to inputs
  pinMode(JoyStickLeftX, INPUT);
  pinMode(JoyStickLeftY, INPUT);
  pinMode(JoyStickRightX, INPUT);
  pinMode(JoyStickRightX, INPUT);
  pinMode(BatteryVoltagePin, INPUT);
  // start transmitter
  transmitter.begin();
  transmitter.openWritingPipe(addressTransmitter);
  transmitter.openReadingPipe(1, addressReceiver);
  transmitter.startListening();
  transmitter.powerUp();
  transmitter.enableDynamicPayloads();
  transmitter.setDataRate(RF24_250KBPS);
  // start displays
  display1.setI2CAddress(0x7A);
  display1.begin();
  display1.setFont(u8x8_font_victoriamedium8_u); // choose a suitable font
  display1.clearDisplay();
  display2.setI2CAddress(0x78);
  display2.begin();
  display2.setFont(u8x8_font_victoriamedium8_u); // choose a suitable font
  display2.clearDisplay();
}

void loop(void) {
  int * joyStickValues = getJoyStickValues();

  drawBatteryVoltage();
  drawJoyStickValues(joyStickValues);

  int speedValue = joyStickValues[1]; // Y value of left joystick
  int rotationValue = joyStickValues[2]; // X value of right joystick

  sendAllData(rotationValue, speedValue);
}

Code for the car

#include <Arduino.h>
#include <U8x8lib.h>
#include <Wire.h>
#include <RF24.h>
#include <nRF24L01.h>
#include <SPI.h>

#define BatteryVoltagePin A3

// Define pins for left motor
#define LeftMotorForward 0
#define LeftMotorBackward 1
#define LeftMotorSpeed 5

// Define pins for 
#define RightMotorForward 2
#define RightMotorBackward 3
#define RightMotorSpeed 6

#define CE 7
#define CS 8

// Initialze display
U8X8_SSD1306_128X64_NONAME_HW_I2C display1(U8X8_PIN_NONE);
RF24 receiver(CE, CS);

// Define variables for battery voltage measurment
char BatteryVoltageMapped[12];
char *dataMessage;

const uint64_t addressReceiver = 0xE8E8F0F0E1LL;
const uint64_t addressTransmitter = 0xE8E8F0F1E1LL;

char *NRFMessageNotAvailable = "NO DATA     ";
char *NRFMessageAvailable = "READING DATA";

void drawBatteryVoltage(void) {
  int helper = map(analogRead(BatteryVoltagePin), 852, 1023, 61, 74);
  sprintf(BatteryVoltageMapped, "BATTERY:%02dV", helper); //TODO: add decimal point
  display1.drawString(1, 1, BatteryVoltageMapped);
}

void getData(){
  if (receiver.available()){
    display1.drawString(1, 3, NRFMessageAvailable);
    byte data[2];
    receiver.read(data, sizeof(data));
    sprintf(dataMessage,"ANGLE:%03d", int(data[0]));
    display1.drawString(1, 5, dataMessage);
    sprintf(dataMessage,"SPEED:%03d", int(data[1]));
    display1.drawString(1, 7, dataMessage);
  }
  else {
    display1.drawString(1, 3, NRFMessageNotAvailable);
  }
}

void setup(void) {

// Set all pins regarding motors to outputs
  pinMode(LeftMotorForward, OUTPUT);
  pinMode(LeftMotorBackward, OUTPUT);
  pinMode(LeftMotorSpeed, OUTPUT);
  pinMode(RightMotorForward, OUTPUT);
  pinMode(RightMotorBackward, OUTPUT);
  pinMode(RightMotorSpeed, OUTPUT);

  // Start display
  display1.setI2CAddress(0x78); // Specify address
  display1.begin(); // Start transmission
  display1.setFont(u8x8_font_victoriamedium8_u); // Choose a suitable font
  display1.clearDisplay(); // Reset contents to clear it
  // Start receiver
  receiver.openWritingPipe(addressReceiver);
  receiver.openReadingPipe(0, addressTransmitter);
  receiver.begin();
  receiver.powerUp();
  receiver.enableDynamicPayloads();
  receiver.setDataRate(RF24_250KBPS);
  receiver.startListening();
  
  delay(50);  
}



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

Have you had the radios working with library example codes?

I tried running simplified version of the code (I followed this tutorial) without any success. Should I try implementing this code? Is there anyway I can check if the modules are damaged?

Simplified code below

Code for transmitter

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

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

const byte address[6] = "00001";

void setup() {
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
}

void loop() {
  const char text[] = "Hello World";
  radio.write(text, sizeof(text));
  delay(1000);
}

Code for receiver

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

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

const byte address[6] = "00001";

U8X8_SSD1306_128X64_NONAME_HW_I2C display1(U8X8_PIN_NONE);

void setup() {
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
  display1.setI2CAddress(0x78);
  display1.begin();
  display1.setFont(u8x8_font_victoriamedium8_u); // choose a suitable font
  display1.clearDisplay();
}

void loop() {
  if (radio.available()) {
    char text[32] = "";
    radio.read(text, sizeof(text));
    display1.drawString(1, 1, text);
  }
}

In post #30 of Robin2's simple rf24 tutorial there is a test program that checks the physical connection between the radio module and the Arduino. Load and run that code for each module then copy and paste the serial monitor output to a new post and we can better tell if the radios are connected to their respective processors.

Other things that I have found to look at are:

If you read and, closely, follow Robin2's simple rf24 tutorial you should be able to get them working. That tutorial sure helped me. The code in the examples has been proven to work many many times. If it does not work for you, there is likely a hardware problem.

Make sure the rf24 power supply can provide enough current. This is especially true for the high power (external antenna) modules. I use homemade adapters like these. They are powered by 5V and have a 3.3V regulator on the board. Robin2 also has suggested trying with a 2 AA cell battery pack.

If using the high powered radios make sure to separate them by a few meters. They may not work too close together. Try the lower power settings.

Reset the radios by cycling power to them after uploading new code. I have found that to help. They do not reset with the Arduino.

Switch to 1MB data rate to catch the not so cloned clones.
radio.setDataRate( RF24_1MBPS );

Also for some clones, change TMRh20's RF24.cpp line 44
_SPI.setClockDivider(SPI_CLOCK_DIV2);
Into
_SPI.setClockDivider(SPI_CLOCK_DIV4);

Have a look at the common problems page.

I tried running the code for testing physical connections. I had to take the chips out and place them on UNO board I have and then I connected corresponsing modules to them. So I checked connection on my PCBs (I can share those too) multiple times to verify that they are correct.

I had to run the test mulptiple times, each time I unplugged the USB from the board completely. The data rate is changing correctly.

I will now try following the tutorial and post again once I have any results.

More about the setup

I have two custom PCBs I designed and I'm using one Atmega328P chip on each. Right now both are powered from lab power supply. I'm supplying the boards with 7.4V 10A max and then using regulators for 5V and 3.3V. On the car (receiver) I'm using nRF24l01+ without external anthenna and on the controller (transmitter) the same module but with external anthenna.

Serial outputs for receiver and transmitter

I had to merge them into one picture, since I couldn't figure out how to copy from serial monitor output and as a new user I can only upload one picture per post
The first one is receiver and the second is transmitter

The connection tests look OK.

I would be better in the future to copy the serial monitor output and paste into a post as text. To copy from serial monitor, highlight the text to be copied and touch ctrl-c. That will copy to the clipboard then just past into a post windoe.

Can you post schematics for these?

I tried running modified version of the code from the tutorial (I only edited the CE and CSN pins) with one microcontroller in the UNO board and one on separately. When I had the master in the UNO board, at the start it was only showing that the transmission is failing. Then for a few messages (around 10) it started working and then it stopped and I couldn't get it to work again afterwards even when I restarted both the master and slave.

I'm new to PCB designing and these are actually my first boards so any help is appreciated :slight_smile:

Schematics for car and controller

The first one is the car schematics the second is the controller
Schematics are available for download here

I do not see the decoupling caps (0.1uF ceramic) one from Vcc to ground and another AVcc to ground. Those caps are important and required. Those caps must be located as close to the processor chip as is possible.

Also it is a good idea to bypass Aref to ground with a 0.1uF ceramic cap if you are using the analog inputs, though not required.

I am assuming that your car is battery powered? The L298 motor driver is ancient and inefficient technology. It will drop 2V to over 4V of your motor supply voltage and dissipate that power as waste heat. And that 2V to 4V never reaches your motor. It is hard to find a worse motor driver, nowadays. Look to Pololu for a good line of reasonbly priced DC modern motor drivers. Choose a driver based on motor rated voltage and motor stall current. Stall current should be listed on the motor data sheet. The modern driver is likely to be more compact, as well.

I redesigned the circuits. The only issue I found was that my local stores probably don't think L298 is archaic and they don't sell any other suitable motor driver. So I just designed double H-bridge using MOSFETs. I also added missing pull-up resistors to the SDA and SCL lines.

Anyway, did you notice anything wrong with the connection between the nRF and Atmega? Only thing I found that could be affecting it is that MISO line can have pull-down resistor but it's supposedly not necessary.

Schematics for car and controller

I also uploaded them here

Car

Controller

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.