Go Down

Topic: Transmitter nRF24L01 freezes due to EMI (Read 395 times) previous topic - next topic

kubajz22

Hi, I've built an RC car with a grass trimmer gasoline engine. I use the high power nRFs for sending data to the car and back to the remote via ACK. But I ran into a problem. Everything works fine until I pull the starting rope.. At this moment both Arduino boards usually freeze (car freezes more often actually) and can only be brought back to life either by starting the engine again (sometimes works) or resetting the boards. The reason is a massive electromagnetic interference coming from the magneto. Since shielding it will probably be quite tricky, I'd rather make the radio more resistant to it.

Facts that I discovered:
-The interference is traveling through the air, it's not bound to the car itself.
-Affected are only transmitters, nRF in receive mode seemingly doesn't react to the EMI. (Both boards are actually transmitters because of the ack. When I disabled it, it never froze.)

I'm still not sure though what exactly is the weak point. I was thinking it might be the SPI interface but since I discovered that only transmitters are affected this doesn't seem probable to me anymore.
Maybe when the module tries to transmit and there is a strong interference on the same frequency it crashes??

Have you ever tried using these two modules set as transmitters in the same place?
Here's the transmitter code if you are curious.

Code: [Select]
#include <LiquidCrystal.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int CE_PIN =  7;
const int CSN_PIN = 8;

const int online  = 6;
const int offline = 9;

const byte adresa[5] = {'O', 'z', 'z', 'a', 'k'};

RF24 komunikace(CE_PIN, CSN_PIN);

int dataProAuto[8] = {0, 0, 70, 0, 0, 0, 0, 0}; //3x tahove potenciometry rizeni, 1 pot ladeni radia, zap/vyp radio, vypnout motor, klakson
int dataOdAuta[3] = {0, 0, 0}; //napeti_auto, otacky, rychlost

int pot_plyn;
int pot_brzda;
int pot_rizeni;
int pot_ladeni;
int sp_radio;
int tl_motor;
int tl_klakson;
int spojeni = 0;
float napeti_ovladac = 0.00;
float napeti_auto = 0.00;
float rychlost = 0.0;

unsigned long soucasnyCas;
unsigned long predchoziCas;
unsigned long vysilaciInterval = 10;

unsigned long soucasnyCasDisplay;
unsigned long predchoziCasDisplay;
unsigned long DisplayInterval = 500;

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

void setup() {
  analogReference(EXTERNAL);
  pinMode(online, OUTPUT);
  pinMode(offline, OUTPUT);
  pinMode(A0, INPUT); //pot_plyn
  pinMode(A1, INPUT); //pot_brzda
  pinMode(A2, INPUT); //pot_rizeni
  pinMode(A3, INPUT); //pot_ladeni
  pinMode(A4, INPUT); //napeti_ovladac
  pinMode(22, INPUT); //sp_radio
  pinMode(23, INPUT); //tl_motor
  pinMode(24, INPUT); //tl_klakson

  lcd.begin(16, 4);
  lcd.clear();


  komunikace.begin();
  komunikace.setDataRate( RF24_250KBPS );
  komunikace.setPALevel(RF24_PA_LOW);

  komunikace.enableAckPayload();
  komunikace.setChannel(120);


  komunikace.setRetries(2, 15); // delay, count
  komunikace.openWritingPipe(adresa);
}

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

void loop() {

  soucasnyCas = millis();
  if (soucasnyCas - predchoziCas >= vysilaciInterval) {
    odeslat();
  }

  soucasnyCasDisplay = millis();
  if (soucasnyCasDisplay - predchoziCasDisplay >= DisplayInterval) {
    zobrazPrichoziData();
  }
}

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

void odeslat() {

  bool ack;
  ack = komunikace.write( &dataProAuto, sizeof(dataProAuto) );

  if (ack) {
    if ( komunikace.isAckPayloadAvailable() ) {
      komunikace.read(&dataOdAuta, sizeof(dataOdAuta));

      digitalWrite(online, HIGH);
      digitalWrite(offline, LOW);
    }
  }

  else {
    digitalWrite(offline, HIGH);
    digitalWrite(online, LOW);
  }

  aktualizujOdchoziData();

  predchoziCas = millis();
}

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

void zobrazPrichoziData() {

  napeti_ovladac = analogRead(A4);
  napeti_ovladac = napeti_ovladac * 2 * (5.00 / 1023.00);
  napeti_auto = dataOdAuta[0] * 2 * (5.00 / 1023.00);
  rychlost = dataOdAuta[2] * (5.00 / 1023.00);
  rychlost = rychlost / 0.926 * 1000;
  rychlost = rychlost / 14;
  rychlost = rychlost / 60 * 0.79 * 3.6;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Auto:");
  lcd.print(napeti_auto);
  lcd.print("V");
  lcd.setCursor(0, 1);
  lcd.print("Ovladac:");
  lcd.print(napeti_ovladac);
  lcd.print("V");
  lcd.setCursor(0, 2);
  lcd.print(dataOdAuta[1]);
  lcd.setCursor(5, 2);
  lcd.print(" ot ");
  lcd.setCursor(9, 2);
  lcd.print(rychlost, 1);
  lcd.setCursor(13, 2);
  lcd.print(" km");

  lcd.setCursor(0, 3);
  if (sp_radio == 1) {
    lcd.print("R:ON");
  }
  else {
    lcd.print("R:OFF ");
  }

  lcd.setCursor(6, 3);
  lcd.print(pot_ladeni);
  predchoziCasDisplay = millis();
}

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

void aktualizujOdchoziData() {
  pot_plyn = analogRead(A0);
  pot_plyn = map(pot_plyn, 0, 1023, 180, 110);
  pot_brzda = analogRead(A1);
  pot_brzda = map(pot_brzda, 0, 1023, 0, 180);
  pot_rizeni = analogRead(A2);
  pot_rizeni = map(pot_rizeni, 0, 1023, 122, 59);
  pot_ladeni = analogRead(A3);
  pot_ladeni = map(pot_ladeni, 0, 1023, 0, 210);
  pot_ladeni = pot_ladeni + 870;


  if (digitalRead(22) == HIGH) {
    sp_radio = 1;
  }
  else {
    sp_radio = 0;
  }

  if (digitalRead(23) == HIGH) {
    tl_motor = 1;
  }
  else {
    tl_motor = 0;
  }

  if (digitalRead(24) == HIGH) {
    tl_klakson = 1;
  }
  else {
    tl_klakson = 0;
  }

  spojeni++;

  dataProAuto[0] = pot_plyn;
  dataProAuto[1] = pot_brzda;
  dataProAuto[2] = pot_rizeni;
  dataProAuto[3] = pot_ladeni;
  dataProAuto[4] = sp_radio;
  dataProAuto[5] = tl_motor;
  dataProAuto[6] = tl_klakson;
  dataProAuto[7] = spojeni;
}

Robin2

#1
Jan 26, 2019, 01:25 pm Last Edit: Jan 26, 2019, 01:25 pm by Robin2
I have nothing with which to replicate your experience.

I think if it was my problem I would try to find out how far from the engine the Arduino / nRF24 needs to be to eliminate the problem.

The orientation of the antenna may also matter.

Can you experiment to see if the problem is due to the nRF24 module being close to the engine, or whether the interference is being picked up in the cabling between the Arduino and the nRF24.

If the cabling seems to be the problem maybe you could make a very close linkage between the nRF24 and the Arduino - something like this (using an Attiny84 in this Pic, but you could equally use an Atmega 328)



...R
Two or three hours spent thinking and reading documentation solves most programming problems.

kubajz22

Thank you for your reply. I've just found out that the program stops at this line:

ack = komunikace.write( &dataProAuto, sizeof(dataProAuto) );

-exactly what I expected. I'm currently browsing through the libraries to hopefully find the potential cause of the "freezing". Also, the Arduino still reacts properly to external interrupts, but when it returns back to the main program it remains stuck.

PS: The interference can be replicated by connecting and quickly disconnecting a big inductor with a non-closed core to a power supply.

I will try shielding the wires and then I'll share the results.

kubajz22

Seems like I nailed it. If there was an interference the program got trapped in this while loop which it could never leave:

Code: [Select]

while( ! ( get_status()  & ( _BV(TX_DS) | _BV(MAX_RT) ))) {
#if defined (FAILURE_HANDLING) || defined (RF24_LINUX)
if(millis() - timer > 95){
errNotify();
#if defined (FAILURE_HANDLING)
  return 0;
#else
  delay(100);
#endif
}
#endif
}


I enabled the FAILURE_HANDLING inside RF24_config.h and it doesn't stop anymore. It's probably just the first step and it will be necessary to do some tweaks on my design but I'm still quite happy about that.

Robin2

Thank you for your reply. I've just found out that the program stops at this line:

ack = komunikace.write( &dataProAuto, sizeof(dataProAuto) );

-exactly what I expected.
If that is the equivalent of
Code: [Select]
rslt = radio.write( &dataToSend, sizeof(dataToSend) );
in the first example in my Simple nRF24L01+ Tutorial
then I believe it may hang if retries are disabled and no acknowledgement is received. Have you studied the Nordic nRF24 datasheet? (Good reading if you suffer from insomnia)

I would not expect the system to fail through being unable to transmit - how would it know? All it can tell is that its transmission was not effective if it does not get an acknowledgement.

What happens if you don't use any acknolwedgements?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

kubajz22

It fails even with ack disabled. I know it's strange but enabling FAILURE_HANDLING resolves the problem. I modified the code to tell me when it failed and it printed a message every time I pulled the starter rope. Then it continued doing its thing like it's supposed to. Retries are enabled and set to (2, 15). But if this was the case it would fail every time I had the receiver turned off.

I think that this behavior has something to do with the firmware of the nRF24L01 chip or something like that.

And yes, it's your code.

Robin2

every time I pulled the starter rope.
Do you mean "whenever the engine is running", or do you only mean "while the engine is being started" ?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

kubajz22

I've never actually run it mounted on the car.

But I spoke too soon. While the problem appeared to be solved by the FAILURE_HANDLING I discovered that the whole communication is still not immune to the noise (only the remote side seem to be). I will try to shield the HV lead but even if that works it's still quite dangerous and unpredictable if some interference coming from another source appears.

It is weird that even with five character (or byte?) "password" (const byte address - in your sketch R, X, A, A, A) you can easily knock the system down.

Also, when the noise is picked up, the Arduino starts to behave really strangely and unpredictably. It changes values in the integer array used to save the received data resulting in servos chaos etc.

The values are 0, 2056 and I believe -14700. I have no idea what's going on.

Robin2

It is weird that even with five character (or byte?) "password" (const byte address - in your sketch R, X, A, A, A)
It's not a password, it's an address.

If the system can't receive the address reliably it does not recognize any of the message. It would be the same as your neighbour's mail going into your mailbox - you wouldn't read it.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

kubajz22

Yes but still.. How would the interference be able to replicate it?

Robin2

Yes but still.. How would the interference be able to replicate it?
I don't understand. What is replicated by the interference? How do you know?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

kubajz22

So when you need a specific address to come before the message for the module to react to it, how would the interference be able to simulate the one specific sequence. I don't know, I will still try to somehow shield the SPI bus and see whether it helps or not ( double shielded USB cable doesn't help at all, maybe ferrite beads will..). The biggest problem is that I am still not sure what is giving up and how to cure it. And without an oscilloscope, I'm kind of blindfolded.

Robin2

So when you need a specific address to come before the message for the module to react to it, how would the interference be able to simulate the one specific sequence.
You have not provided any evidence that it has.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

kubajz22

Anyway, I'll buy a resistor spark plug and possibly put a capacitor across it which should help even more.

PS: putting a ferrite core around the SPI wires seems to help a lot, at least with the inductor arcing test.

kubajz22

I've just tested some resistor plugs I had laying around and there was no problem whatsoever. I can't believe they are so effective.

Go Up