Servo control interferring with nrf24l01 signal

Hello
I am trying to build my own rc boat with my own controller. For the boat i am using an esp32 s3, a nrf24l01 rf transciever, an l298n (dual h bridge dc motor controller) to controll my dc motor, and a servo wich i am controlling directly with esp32.

For the controller i am using an esp32 s3, a nrf24l01, and two regular joysticks.

The problem that i am having is, that when i upload the receiver code to the esp32 or just boot it up, it takes at least 5-10 minutes before it works and i can control it with the controller. (And if i turn off the esp32 and turn it on again, it takes a long time again)
When i remove the servo part from the code so it just controlls the dc motor, it works just fine and starts the moment power is added or the code is uploaded. And the nrf24l01 transcievers sends signals just fine. And i have tested the servo and dc motor individually and they work fine. (And also it works after those 5-10 minutes, but it would be nice if it didnt take so much time)
Does any one have any idea of why this happens?

I dont know if this is the correct forum, but i just cant figure it out.

Best regards - Magnus

What part would that be?

What would that be? What’s the full code?

I am currently trying to use the esp32servo library, and then i have defined a function that converts the joystick values into values that i can use for the servo.
And then i just call it in a loop, that writes those values to the servo.

Here is my full reciever code:

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

#define CE_PIN 9
#define CSN_PIN 10

#define ENA_PIN 4
#define IN1_PIN 5
#define IN2_PIN 6
#define SERVO_PIN 7

RF24 radio(CE_PIN, CSN_PIN);
const byte address[6] = "00001";

struct JoystickData {
int throttle;
int steering;
};

JoystickData joyData;

// -------- Servo --------
Servo myServo;

// -------- Motor PWM (ESP32-S3 core v3.x) --------
const int motorChannel = 0; // LEDC kanal
const int motorResolution = 8; // 0-255
const int motorFreq = 20000; // 20 kHz

// -------- Failsafe --------
unsigned long lastPacketTime = 0;
const unsigned long failsafeTimeout = 300; // ms

// -------- Motor control --------
void driveMotor(int throttle) {
const int center = 2048;
const int deadzone = 100;

int diff = throttle - center;

if (abs(diff) <= deadzone) {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, LOW);
ledcWrite(ENA_PIN, 0);
return;
}

if (diff > 0) {
digitalWrite(IN1_PIN, HIGH);
digitalWrite(IN2_PIN, LOW);
} else {
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, HIGH);
diff = -diff;
}

int speed = map(diff, deadzone, center, 0, 255);
speed = constrain(speed, 0, 255);

ledcWrite(ENA_PIN, speed);
}

// -------- Servo kontrol --------
void updateServo(int steering) {
// Map joystick 0-4095 til servo vinkel 0-180°
int angle = map(steering, 0, 4095, 0, 180);
angle = constrain(angle, 0, 180);

myServo.write(angle);
}

void setup() {
Serial.begin(115200);

// Motor pins
pinMode(IN1_PIN, OUTPUT);
pinMode(IN2_PIN, OUTPUT);

// Motor PWM kanal (ESP32-S3 core v3.x)
ledcAttach(ENA_PIN, motorChannel, motorResolution); // pin, kanal, resolution
ledcWriteTone(motorChannel, motorFreq); // frekvens

// Servo
myServo.attach(SERVO_PIN); // ESP32Servo håndterer PWM

// NRF24
if (!radio.begin()) {
Serial.println("NRF24L01 not detected!");
while (1);
}

radio.openReadingPipe(1, address);
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_250KBPS); // mere stabilt
radio.setRetries(5, 15); // 5 gange, 15*250µs delay
radio.startListening();
}

void loop() {
// ---------- Check for new packet ----------
if (radio.available()) {
radio.read(&joyData, sizeof(joyData));
lastPacketTime = millis();

// Motor
driveMotor(joyData.throttle);

// Servo
static unsigned long lastServo = 0;
if (millis() - lastServo > 50) { // opdater servo max hver 50ms
  updateServo(joyData.steering);
  lastServo = millis();
}

Serial.print("Throttle: ");
Serial.print(joyData.throttle);
Serial.print("  Steering: ");
Serial.println(joyData.steering);

}

// ---------- Failsafe ----------
if (millis() - lastPacketTime > failsafeTimeout) {
// Stop motor
digitalWrite(IN1_PIN, LOW);
digitalWrite(IN2_PIN, LOW);
ledcWrite(ENA_PIN, 0);

// Servo neutral
myServo.write(90); // midtposition

}
}

  • First, follow the instructions for properly formatting code in forum postings, please.
  • Second, is there a transmitter code you could post? I suspect so. There is no guarantee the problem does not reside there, from our perspective.
  • Third, although this is likely a software issue, be prepared for others to request a simple schematic, both for completeness, and to assure ourselves that you have no underlying issues there.

Thank you for your attention to this matter

Welcome!

Without an annotated schematic showing all connections, power, ground and power sources the following is just a SWAG. This may help you find the problem.

Power Stability Issues with RF24 Radio Modules

As described in the RF24 Common Issues Guide, radio modules, especially the PA+LNA versions, are highly reliant on a stable power source. The 3.3V output from Arduino is not stable enough for these modules in many applications. While they may work with an inadequate power supply, you may experience lost packets or reduced reception compared to modules powered by a more stable source. The Nano/UNO 5V is also a problem, it has problems supplying enough current during transmit.

Symptoms of Power Issues:

  1. Radio module performance may improve when touched, indicating power stability issues.
  2. These issues are often caused by the absence of a capacitor, a common cost-saving omission by some manufacturers.

Temporary Patch:

  1. Add Capacitors: Place capacitors close to the VCC and GND pins of the radio module. A 10uF capacitor is usually sufficient, but the exact value can depend on your circuit layout.
  2. Use Low ESR Capacitors: Capacitors with low Equivalent Series Resistance (ESR) are recommended, as they provide better power stability and performance.
    1. Be sure the transmitter and receiver are at least a meter apart, more is better.

Adding the appropriate capacitors can greatly improve the reliability of your RF24 module by ensuring a stable power supply, thus minimizing packet loss and enhancing overall performance. A separate power supply for the radios is the best solution.

I have tried to create a diagram for both parts:
Here is the schematic diagram of the controller/transmitter.

And here is the schematic of the reciever/boat part.

I have done my best, but i am a beginner so i am sorry if it looks bad and has flaws, but i hope it shows the nesecarry.

And here is both codes. (Hopefully correctly formatted)
Transmitter code

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

struct JoystickData {
  int throttle;
  int steering;
};

#define CE_PIN 7
#define CSN_PIN 8

RF24 radio(CE_PIN, CSN_PIN);
const byte address[6] = "00001";

// ------Joysticks------
#define ThrottlePinX_PIN 4
#define SteeringPinX_PIN 1

JoystickData joyData;

void setup() {
  Serial.begin(115200);

  if (!radio.begin()) {
    Serial.println("NRF24L01 not detected");
    while (1);
  }

  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_LOW);
  radio.stopListening();
}

void loop() {
  joyData.throttle = analogRead(ThrottlePinX_PIN);
  joyData.steering = analogRead(SteeringPinX_PIN);

  bool success = radio.write(&joyData, sizeof(joyData));

  if (success) {
    Serial.print("T: ");
    Serial.print(joyData.throttle);
    Serial.print("  S: ");
    Serial.println(joyData.steering);
  }

  delay(50);
}

Reciever code

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

#define CE_PIN 9
#define CSN_PIN 10

#define ENA_PIN 4
#define IN1_PIN 5
#define IN2_PIN 6
#define SERVO_PIN 1

RF24 radio(CE_PIN, CSN_PIN);
const byte address[6] = "00001";

struct JoystickData {
  int throttle;
  int steering;
};

JoystickData joyData;

// -------- Servo --------
Servo myServo;

// -------- Motor PWM (ESP32-S3 core v3.x) --------
const int motorChannel = 0; // LEDC kanal
const int motorResolution = 8; // 0-255
const int motorFreq = 20000;   // 20 kHz

// -------- Failsafe --------
unsigned long lastPacketTime = 0;
const unsigned long failsafeTimeout = 300; // ms

// -------- Motor kontrol --------
void driveMotor(int throttle) {
  const int center = 2048;
  const int deadzone = 100;

  int diff = throttle - center;

  if (abs(diff) <= deadzone) {
    digitalWrite(IN1_PIN, LOW);
    digitalWrite(IN2_PIN, LOW);
    ledcWrite(ENA_PIN, 0);
    return;
  }

  if (diff > 0) {
    digitalWrite(IN1_PIN, HIGH);
    digitalWrite(IN2_PIN, LOW);
  } else {
    digitalWrite(IN1_PIN, LOW);
    digitalWrite(IN2_PIN, HIGH);
    diff = -diff;
  }

  int speed = map(diff, deadzone, center, 0, 255);
  speed = constrain(speed, 0, 255);

  ledcWrite(ENA_PIN, speed);
}

// -------- Servo kontrol --------
void updateServo(int steering) {
  // Map joystick 0-4095 til servo vinkel 0-180°
  int angle = map(steering, 0, 4095, 0, 180);
  angle = constrain(angle, 0, 180);

  myServo.write(angle);
}

void setup() {
  Serial.begin(115200);

  // Motor pins
  pinMode(IN1_PIN, OUTPUT);
  pinMode(IN2_PIN, OUTPUT);

  // Motor PWM kanal (ESP32-S3 core v3.x)
  ledcAttach(ENA_PIN, motorChannel, motorResolution); // pin, kanal, resolution
  ledcWriteTone(motorChannel, motorFreq);            // frekvens

  // Servo
  myServo.attach(SERVO_PIN);  // ESP32Servo håndterer PWM

  // NRF24
  if (!radio.begin()) {
    Serial.println("NRF24L01 not detected!");
    while (1);
  }

  radio.openReadingPipe(1, address);
  radio.setPALevel(RF24_PA_LOW);
  radio.setDataRate(RF24_250KBPS);  // mere stabilt
  radio.setRetries(5, 15);           // 5 gange, 15*250µs delay
  radio.startListening();
}

void loop() {
  // ---------- Check for new packet ----------
  if (radio.available()) {
    radio.read(&joyData, sizeof(joyData));
    lastPacketTime = millis();

    // Motor
    driveMotor(joyData.throttle);

    // Servo
    static unsigned long lastServo = 0;
    if (millis() - lastServo > 50) { // opdater servo max hver 50ms
      updateServo(joyData.steering);
      lastServo = millis();
    }

    Serial.print("Throttle: ");
    Serial.print(joyData.throttle);
    Serial.print("  Steering: ");
    Serial.println(joyData.steering);
  }

  // ---------- Failsafe ----------
  if (millis() - lastPacketTime > failsafeTimeout) {
    // Stop motor
    digitalWrite(IN1_PIN, LOW);
    digitalWrite(IN2_PIN, LOW);
    ledcWrite(ENA_PIN, 0);

    // Servo neutral
    myServo.write(90); // midtposition
  }
}

I hope this can help shedding some light on what is going on :)

Hi, @mag12345
Welcome to the forum.

Thanks for the schematics, a good effort of a beginner. :+1:

What sort of 9V battery are you using to power the motor and what are the motor specs?

Do you have a DMM? Digital MultiMeter?

Tom.... :smiley: :+1: :coffee: :australia:

Hi Tom

I am just using a regular 6lr61 9v battery. (Currently just using this for testing. I will try to find another one when i am done testing)

I am using a 6-12 volt dc motor. (I dont really know any of the specs - i cant find any)

And yes i do have a digital multimeter.

One of these;

They will not be able to provide enough current.
Check the batteries output voltages with your DMM under load.

Some images of your project would also help.

Tom.... :smiley: :+1: :coffee: :australia:

I am aware that the 9v is not able to provide enough current. Like i said i only use it for testing. And the 9v battery should have no effect on the esp32 - only the motor. And also like i mentioned in my first post the motor works just fine when the servo is not there in code.

I am currently not able to send any pictures but i will send some as soon as possible.

Magnus ;)

By that, do you mean you tested each library separately with test codes? Or just physically tested the devices.

I have tested both servo and dc motor individually with test codes.

Which ESP32 S3 product? Pinouts vary, you may need to provide a link to the product for surety.

#define SteeringPinX_PIN 1

On a Nano, these would be a concern if you use serial, because pin 1 is the TX pin.

Don’t know about the ESP32, never looked at where, or if, the pins for Serial Monitor port are. But I do note that GPIO1 is also U0TXD on some images on the web, so I have “concerns”:


I have to run, so I thought I’d just throw this your way, in case it’s relevant.

edit - also came across this thread, which may or may not help:

It is an esp32 s3 n16r8.

I dont think there is any problem with using the gpio 1. On the esp32 s3 the rx and tx pins are gpio 44 and 43.
There is a pinout diagram of the esp32 s3 on this website:
https://docs.espressif.com/projects/esp-idf/en/v5.0.1/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1-v1.0.html

That is a sure sign that the servo power supply is inadequate, and according to the schematic diagram, it is shared with the ESP32, which is a very bad idea.

The pic below gives the general idea of how servos and motors should be powered. Be sure that all the grounds are shared.

I will try that, but is there a way to get a more compact setup where i dont need 4 diffrent battery packs? I am trying to do it all as light as possible while still maintaining efficiency and battery life.

You can use one heavy duty battery pack for the motors and servos, and one of much lighter duty for the electronics.

Design of circuits like this requires a reasonable basic understanding of electronic circuitry.