NRF24 Interfering in burhsless motor

Hey guys!

I'm trying to control a brushless motor via radio, using the NRF24L01 modules and the RH_NRF24.h library.

At first I was using the RHReliableDatagram.h, but I realized I don't need to use the reliable transmission, as the values of the joysticks will vary over time, so I shouldn't insist on sending a value that has already changed (please, correct me if I'm wrong).

The thing is: when I add the radio code lines, my brushless motor doesn't work properly, even if the value I send to the motor is not being sent by radio. It will even spin when I send a 1000 PWM signal.

Does anyone have any idea?

Thanks!

Transmissor_v13.ino (6.23 KB)

Debug_Radio.ino (1.87 KB)

Hi caioalves1,
Congrats on your success so far.
Please post your code using code tags. People here are much more likely to help if you follow the forum etiquette as detailed in the "How to use this forum" post at the tops of each forum here.

Please tell us more about your project. Which brushless motor? How controlled? (ESC? Which one?). What power supply?

I think I understand how you described the unexpected results. The motor spins even if you write 1000 to the ESC, which is usually the signal that tells the motor to stop. Is that right?

Must mean 1000µs servo pulse.

The ServoTimer2 library used takes over timer2 completely, so that pins 3 and 11 won't do PWM any more, but
I don't think that can be a problem.

Any reason not to use the normal Servo library ?

Hey, vinceherman, thanks for your reply!

Sorry for the code, new user here hehe.

The brushless motor is the Racestar 2212 980 KV and I'm using a generic 20 A ESC called "skylinker", but I had a similar result using an Emax BLHeli 20 A. Also, I'm using a 3S/5200 mAh/15 C LiPo battery.

That's correct! When there's no radio line code, it works ok. But when I add it and even if I'm not using it in the program, the motor tries spins.

I should also mention that it's not a constant spin, but it oscilates between the speed it should have (0) and this weird speed.

TX code:

#include <RH_NRF24.h>             // Depende da biblioteca SPI do Arduino
#include <SPI.h>

// GND
// Vcc: divisor de tensão
// CE: qualquer pino digital (8)
// CSN: qualquer pino digital (10)
// SCK (slave clock): pino 13
// MOSI (Master Output / Slave Output): pino 11 
// MISO (Mater Input / Slave Output): pino 12
// IRQ (interrupção): não necessário


// Pinos
#define CLIENT_ADDRESS 1  // Endereço do cliente  (rádio controle)
#define SERVER_ADDRESS 2  // Endereço do servidor (drone)

#define Joystick_X1_Pin A0 // Joystick 1 eixo x (Guinada)
#define Joystick_Y1_Pin A1 // Joystick 1 eixo y (Aceleração)
#define Joystick_X2_Pin A2 // Joystick 1 eixo x (Rolagem)
#define Joystick_Y2_Pin A3 // Joystick 1 eixo y (Arfagem)

#define pin_div A4 // Define o pino analógico 4 como entrada da tensão
#define pin_led 1 // Define pino do LED de alerta de baixa bateria
#define pinoBotao 7// Define do push button


RH_NRF24 nrf24; // Cria instância do driver
struct Radio {
  float valor1;   // Guinada
  int valor2;   // Aceleração
  float valor3;   // Rolagem
  float valor4;   // Arfagem
  int botao;    // Botão liga-desliga PID
};
Radio pacote; 


// Variáveis
int valorpot;          // Variável que lê o potenciômetro: varia de 0 a 1024
float R1 = 10000.0;    // Valor do resistor R1 para o cálculo da tensão
float R2 = 10000.0;    // Valor do resistor R2 para o cálculo da tensão
//float v_cc[50];      // Declara v_cc como um vetor de 50 posições

float v_lido = 0;      // Declara uma variável para leitura dos valores analógicos
float v_soma=0;
float v_cc=0;
float v_medido = 0;    // Variável que armazenará a média das amostras da tensão cc

int Joystick_X1;
int Joystick_Y1;
int Joystick_X2;
int Joystick_Y2;
  
long Joystick_X1_cal = 0;
long Joystick_Y1_cal = 0;
long Joystick_X2_cal = 0;
long Joystick_Y2_cal = 0;

//////////  VOID SETUP  //////////
void setup() {

  //Ativa serial monitor
  //Serial.begin(2000000);

  pinMode(pinoBotao, INPUT_PULLUP); //DEFINE O PINO COMO ENTRADA / "_PULLUP" É PARA ATIVAR O RESISTOR INTERNO
  //DO ARDUINO PARA GARANTIR QUE NÃO EXISTA FLUTUAÇÃO ENTRE 0 (LOW) E 1 (HIGH)
  
  pinMode(pin_led, OUTPUT);   // Configura o pino do led como saída digital
  
  
  if (!nrf24.init())  // Inicializa o gerenciador de rádio
  // Padrão: 2.402 GHz (channel 2), 2Mbps, 0dBm
    Serial.println("init failed");  // Caso a inicialização falhe, imprime a mensagem
   if (!nrf24.setChannel(1))
    Serial.println("setChannel failed");
  if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm))
    Serial.println("setRF failed");    
       

  // Calibração Joystick

  for (int Cal = 0; Cal < 2000; Cal ++){   // Inicia calibração (lê 2000 valores)
    Joystick_X1 = analogRead(Joystick_X1_Pin);  // Lê eixo X do joystick 1: Guinada
//    Joystick_Y1 = analogRead(Joystick_Y1_Pin);  // Lê eixo Y do joystick 1: Aceleração
    Joystick_X2 = analogRead(Joystick_X2_Pin);  // Lê eixo X do joystick 2: Rolagem
    Joystick_Y2 = analogRead(Joystick_Y2_Pin);  // Lê eixo Y do joystick 2: Arfagem 
    
    Joystick_X1_cal += Joystick_X1;    // Soma todos os valores de X1
//    Joystick_Y1_cal += Joystick_Y1;    // Idem para Y1
    Joystick_X2_cal += Joystick_X2;    // Idem para X2
    Joystick_Y2_cal += Joystick_Y2;    // Idem para Y2
    delay(10);                         // Delay de 10 milisegundos
    }

  Joystick_X1_cal = Joystick_X1_cal/2000; 
//  Joystick_Y1_cal = Joystick_Y1_cal/2000; 
  Joystick_X2_cal = Joystick_X2_cal/2000; 
  Joystick_Y2_cal = Joystick_Y2_cal/2000;

}

void loop() {

  //%%%%%%%%%%%%%% BATERIA - CARGA %%%%%%%%%%%%%          // JOGAR ISSO PRO SETUP PRA SER MAIS EFICIENTE
/*
  for(int i = 0; i < 50; i++){       // Itera 50 vezes
    v_lido = analogRead(pin_div);    // Lê o valor analógico do pino A4 e armazena na variável v_lido
    v_soma = v_soma + v_lido;        // Soma todas as leituras na variável v_soma
  }
  
  v_soma = v_soma/50;               // Tira a média das leituras
  v_medido = 0.009775*v_soma;       // Calcula o valor final

  if (v_medido < 7.5) {             // Caso a voltagem seja menor que 7.5 V
    digitalWrite(pin_led, HIGH);    // Acende o LED 13
  }
  else  {                           // Caso contrário
    digitalWrite(pin_led, LOW);     // Apaga o LED 13
  }
  Serial.println(v_medido);
  */
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/*
  if(digitalRead(pinoBotao) == HIGH){ //SE A LEITURA DO PINO FOR IGUAL A LOW, FAZ
    pacote.botao = 0;
  }
  else {
    pacote.botao = 1;
  }

//Serial.println(pacote.botao);
*/

  //%%%%%%%%%%%%%% CONTROLE %%%%%%%%%%%%%%%%%%%%

  Joystick_X1 = analogRead(Joystick_X1_Pin) - Joystick_X1_cal + 512;  // Lê eixo X do joystick 1: Guinada
  Joystick_Y1 = analogRead(Joystick_Y1_Pin);  // Lê eixo Y do joystick 1: Aceleração
  Joystick_X2 = analogRead(Joystick_X2_Pin) - Joystick_X2_cal + 512;  // Lê eixo X do joystick 2: Rolagem
  Joystick_Y2 = analogRead(Joystick_Y2_Pin) - Joystick_Y2_cal + 512;  // Lê eixo Y do joystick 2: Arfagem 
/*
  Serial.print("Guinada: ");      Serial.print(Joystick_X1);
  Serial.print(" | Aceleração: ");  Serial.print(Joystick_Y1);
  Serial.print(" | Rolagem: ");     Serial.print(Joystick_X2);
  Serial.print(" | Arfagem: ");     Serial.print(Joystick_Y2);
  Serial.print(" | ");
*/  
  pacote.valor1 = map(Joystick_X1, 30, 945, -1000, 1000)/100.0;  // Lê eixo X do joystick 1: Guinada
  //pacote.valor2 = map(Joystick_Y1, 0, 1024, 1000, 2000);  // Lê eixo Y do joystick 1: Aceleração
  pacote.valor2 = Joystick_Y1+930;
  pacote.valor3 = map(Joystick_X2, 47, 975, -1000, 1000)/100.0;  // Lê eixo X do joystick 2: Rolagem
  pacote.valor4 = map(Joystick_Y2, 79, 990, -1000, 1000)/100.0;  // Lê eixo Y do joystick 2: Arfagem 

  
  
  // Imprime leitura dos analógicos já mapeados
//  
//  Serial.print("Guinada: ");      Serial.print(pacote.valor1);
//  Serial.print(" | Aceleração: ");  Serial.print(pacote.valor2);
//  Serial.print(" | Rolagem: ");     Serial.print(pacote.valor3);
//  Serial.print(" | Arfagem: ");     Serial.println(pacote.valor4);
//


// Envio da mensagem
  nrf24.send((uint8_t *)&pacote, sizeof(pacote));

}

RX code:

#include <Wire.h>
#include <ServoTimer2.h>
#include <RH_NRF24.h>             // Bilioteca dependente da SPI.h, da Arduino
#include <SPI.h>                  // Biblioteca nativa da Arduino

ServoTimer2 Mot3;

RH_NRF24 nrf24;       // Instância do driver de rádio
struct Radio {  // Variáveis para recebimento dos dados de rádio    antigo: tipoPacote
  float value1;   // Guinada
  int value2;   // Aceleração
  float value3;   // Rolagem
  float value4;   // Arfagem
  int botao;
};
Radio pacote;

float a, b, c;
int throttle;
int d;

void setup() {
  // put your setup code here, to run once:
  //Serial.begin(2000000);
  
  Mot3.attach(6);

  if (!nrf24.init())  // Inicializa o gerenciador de rádio
     // Padrão: 2.402 GHz (channel 2), 2Mbps, 0dBm
    Serial.println("init failed");   // Caso a inicialização falhe, imprime a mensagem
  if (!nrf24.setChannel(1))
    Serial.println("setChannel failed");
  if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm))
    Serial.println("setRF failed"); 
   Mot3.write(1000);   
}

void loop() {
  // put your main code here, to run repeatedly:
  
  Mot3.write(1000);

  if (nrf24.available())   // Se o RadioManager estiver disponível
  {
    // Espera pela mensagem do cliente
    uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];    
    uint8_t len = sizeof(buf);

    if (nrf24.recv(buf, &len))
    {
       
      memcpy(&pacote,&buf,len);

//      Serial.print(pacote.value1); Serial.print(" | ");
      //Serial.println(pacote.value2); //Serial.print(" | ");
//      Serial.print(pacote.value3); Serial.print(" | ");
//      Serial.println(pacote.value4);
      
      a = pacote.value1;   // Guinada  
      throttle =        pacote.value2;          // Aceleração
      b = pacote.value3;   // Rolagem
      c = pacote.value4;   // Arfagem
      d = pacote.botao;
    } 
  }
}

MarkT:
Must mean 1000µs servo pulse.

The ServoTimer2 library used takes over timer2 completely, so that pins 3 and 11 won't do PWM any more, but
I don't think that can be a problem.

Any reason not to use the normal Servo library ?

Hey, Mark. Yeah, that's what I meant.

Hmmm, I'm using pins 3, 5, 6 and 9 for the whole project, but in this debugging code, I'm only using pin 6.

Back then when I started the project, I was using a cheap radio module and the VirtualWire library, that's why I switched from the regular Servo to the ServoTimer2.h library.

Why are you including the Wire library?

      memcpy(&pacote,&buf,len);

This will corrupt memory as len can be larger than the size of a Radio struct. You need to force
len to be <= sizeof (struct Radio)

You will see corrupt packets come in, you need to check packets for validity and not just assume this.
You probably need a further checksum in your packet too to help with this.

IIRC there is a dynamic packet length mode for this device? Without it you won't even know how
long the sending packet was.

Alternatively you can call recv with the length set to the length you are interested in. At least then you
won't overflow your struct, even if the packet is garbled.

Thanks, Mark! You're absolutely right.

I've removed the memcpy line and it works fine. Do you have any idea on how I could retrieve the received values so I can send them to the motors? (Sorry, I'm quite noob with radio transmission).

I was using the memcpy function based on an older code I had here, because I couldn't transmit a value larger than 255 through the radio. I'm trying to send 4 analog values and one digital.

Regarding the Wire library, it is there cause I'm using a gyroscope in the project. I forgot to erase it when I created this debug code I posted here.

Also, do you think there's a problem with this line on the TX side?

nrf24.send((uint8_t *)&pacote, sizeof(pacote));

Just replace "len" with "sizeof (struct Radio)" ?

MarkT:
You will see corrupt packets come in, you need to check packets for validity and not just assume this.

The nRF24 has a lot of inbuilt error checking. I would not expect to see corrupt packets being passed to the Arduino.

...R

MarkT:
Just replace "len" with "sizeof (struct Radio)" ?

Unfortunately, it didn't work. I'm considering using a different library.

Have a look at this Simple nRF24L01+ Tutorial.

Wireless problems can be very difficult to debug so get the wireless part working on its own before you start adding any other features.

The examples are as simple as I could make them and they have worked for other Forum members. If you get stuck it will be easier to help with code that I am familiar with. Start by getting the first example to work

My code uses the TMRh20 version of the RF24 library.

...R

Robin2:
The nRF24 has a lot of inbuilt error checking. I would not expect to see corrupt packets being passed to the Arduino.

...R

The nRF24L1 uses 1 or 2 bytes of checksum only, you frequently see spurious packets in reality as its listening
all the time - at high baud rates there's 10000's of candidate packets a second to ignore...
It defaults to a 1 byte preamble too, which isn't great.

So the majority of false-packet rejection is down to the network address matching, should that feature
be enabled. This does very little to prevent corrupted packets, that's entirely down to the checksum,
and the checksum can't protect the packet length byte for obvious reasons.

Alas although the library used enables addressing, its uses a common default address (unless overridden,
which this code doesn't), so that any other transceiver in range will likely interfere if using the same RH_NRF
library.

Personally I'd include a further 32 bit checksum at the head of the user packet to validate the packet
length and contents.

MarkT:
The nRF24L1 uses 1 or 2 bytes of checksum only, you frequently see spurious packets in reality as its listening
all the time

Interesting.

I have never experienced any in a few different projects

...R

Hey guys, sorry for the delay.

I've switched from the rh_nrf24.h library to RF24.h and nRF24L01.h, as Robin suggested in his code. Also, I started using Servo.h again, instead of ServoTimer2.h.

Everything works fine now. Don't know why the Radiohead library wasn't working though...

Thank you all!