Relay gets stuck and RF issue

Hi @Wawa .

Thanks, you are right. I've already corrected it.
I replaced the Nano with another Mega, using its 3.3V and GND as the power supply, but the TX keeps displaying Message 2 with some message lines missing. I also ran a program that simply sent text TX->RX and RX->TX. Apparently it worked fine, except that in the serial monitor of the TX, the message from the RX wasn't displayed with the same fluidity as it was in the RX serial monitor.

Button 3 button rarely fails (you press it once, and temperature is displayed correctly on the TX serial monitor).

The other two, controlling the pumps, are the main issue (Button 1 and Button 2 keep the pumps running; releasing them stops the pumps). I've tested it with other buttons, and the same thing happens. Perhaps it's a coding issue.

COMPLETE CODES

TX CODE

//TRANSMITTER

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

//PINS------------------------------------------------------------------------------------------------------------------------
// NRF24L01 Pins
RF24 radio(7, 8); // CE, CSN 
const byte address[][10] = {"00001","00002"};//addresses that receives and transfer data
// Pump Pins
const int Button1 = 6; //Controls Pump1
const int Button2 = 5; //Controls Pump2
// DS18B20 Pin
const int Button3 = 4; //Controls DS18B20

//VARIABLES------------------------------------------------------------------------------------------------------------------------
// NRF24L01 Variables
int Message1[3];
char Message2[50];

//MAIN------------------------------------------------------------------------------------------------------------------------
void setup() {
  pinMode(Button1, INPUT_PULLUP); // Pump1 - Enable internal pull-up resistor for button
  pinMode(Button2, INPUT_PULLUP); // Pump2 - Enable internal pull-up resistor for button
  pinMode(Button3, INPUT_PULLUP); // DS18B20
  
  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(address[0]);//address of transmitter
  radio.openReadingPipe(1, address[1]);//address of receiver - specify the ch-freq in which receives 
  radio.setPALevel(RF24_PA_MAX);//set power amplifier level 

  delay(10);
}

void loop() {
  
  delay(10);

  radio.stopListening(); //transmit

    Message1[0] = digitalRead(Button1); // Pump1 button current state
    Message1[1] = digitalRead(Button2); // Pump2 button current state
    Message1[2] = digitalRead(Button3); // DS18B20

    radio.write(Message1, sizeof(Message1));

  radio.startListening(); //receive

  if (radio.available()) 
  {
    // Read data into the Message2 array
    radio.read(&Message2, sizeof(Message2)); // Correct usage for arrays
    Serial.println(Message2);
  }
} 
   

RX CODE

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "BTS7960.h"

//TIMERS ()-----------------------------------------------------------------------------------------------------------------
#define BUTTON_STATE_CHECK_INTERVAL 10 // Check button state every 10 milliseconds
#define TEMP_READ_INTERVAL 1000 // Read temperature every 1 second

//PINS AND PIPES---------------------------------------------------------------------------------------------------------------
// NFR24L01 Pins
RF24 radio(7, 8); // CE, CSN 
const byte address[][10] = {"00001","00002"};//addresses that receives and transfer data
// Pump Pins
const int pinRelay1 = 9;
const int pinRelay2 = 10;
// DS18B20 Pins
#define ONE_WIRE_BUS 24 // Data wire is plugged into pin 25 on the Arduino

//VARIABLES------------------------------------------------------------------------------------------------------------------------
// NRF24L01 Variables
int Message1[3];
char Message2[50];
// Pump Variables
bool previousButtonState1 = HIGH; // Variable to store the previous state of the button. Initially high (not pressed)
bool previousButtonState2 = HIGH; // Variable to store the previous state of the button. Initially high (not pressed)
bool printedState = false; // Flag to control the printing of the state
enum State1 {OFF1,ON1}; // States of Pump1
enum State2 {OFF2,ON2}; // States of Pump2
State1 state1 = OFF1;
State2 state2 = OFF2;
State1 previousState1 = OFF1;
State2 previousState2 = OFF2;
// DS18B20 Variables
unsigned long lastDebounceTime = 0; // debounce timer for Button 3
const unsigned long debounceDelay = 1000; //milliseconds
unsigned long lastTempReadTime = 0;
enum State3 {OFF3, ON3};
State3 state3 = OFF3;
int buttonState3; //button for temperature sensor DS18B20

//FUNCTIONS---------------------------------------------------------------------------------------------------------------------
// Pump Functions
void turnonPump1();
void turnoffPump1();
void turnonPump2();
void turnoffPump2();
// DS18B20 Functions
void readtemperature();

//OBJECTS------------------------------------------------------------------------------------------------------------------------
// DS18B20 Objects
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature. 

//MAIN------------------------------------------------------------------------------------------------------------------------
void setup() {

  pinMode(pinRelay1, OUTPUT); // Initialize relay pin as output
  pinMode(pinRelay2, OUTPUT); // Initialize relay pin as output
  
  turnoffPump1();// Pump1 initially off
  turnoffPump2();// Pump2 initially off
  
  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(address[1]);//address of transmitter
  radio.openReadingPipe(1, address[0]);//address of receiver - specify the ch-freq in which receives 
  radio.setPALevel(RF24_PA_MAX);//set power amplifier level (minimum since NRFs are close)

  sensors.begin(); // DS18B20
}

void loop() {

  unsigned long currentTime = millis();
  unsigned long lastButtonStateCheckTime = 0;
  
  // Check button state at regular intervals
  if (currentTime - lastButtonStateCheckTime >= BUTTON_STATE_CHECK_INTERVAL) {
    lastButtonStateCheckTime = currentTime;

    radio.startListening(); // Start listening for radio messages
  
    if (radio.available()) 
    {
      radio.read(Message1, sizeof(Message1));
      int buttonState1 = Message1[0]; // Pump1 button current state 
      int buttonState2 = Message1[1]; // Pump2 button current state
      int buttonState3 = Message1[2]; // DS18B20 button current state
      
      // Pump1 Control
      if (buttonState1 != previousButtonState1) { //Compare previous and current states of the button1
        if (buttonState1 == LOW) {  //keep the button1 activated
          turnonPump1(); // Turn on Pump1
          state1 = ON1;
        } else {
          turnoffPump1(); // Turn off Pump1
          state1 = OFF1;
        }
        previousButtonState1 = buttonState1; // Update previous button state for button 1
      }
  
      // Pump2 Control
      if (buttonState2 != previousButtonState2) { //Compare previous and current states of the button2
        if (buttonState2 == LOW) { //keep the button2 activated
          turnonPump2(); // Turn on Pump2
          state2 = ON2;
        } else {
          turnoffPump2(); // Turn off Pump2
          state2 = OFF2;
        }
        previousButtonState2 = buttonState2; // Update previous button state for button 2
      } 
  
      // DS18B20 Control
      if ((currentTime - lastTempReadTime) >= TEMP_READ_INTERVAL && buttonState3 == LOW && (currentTime - lastDebounceTime >= debounceDelay)) {
        readtemperature(); //Read temperatures from DS18B20 sensors
        state3 = ON3;
        lastDebounceTime = millis(); // Update debounce timer
      } else {
        state3 = OFF3;
      }
    }
  }

  delay(10); //Wait a short time before switching from listening to transmitting (avoid interference)
  radio.stopListening(); //transmit
    
  if (state1 != previousState1) { //Compare previous and current states of the button1 
    switch (state1) {
      case OFF1:
        strcpy(Message2, "PUMP 1 OFF");
        radio.write(&Message2, sizeof(Message2));
        Serial.println(Message2); //TEST
        break;
      case ON1:
        strcpy(Message2, "PUMP 1 ON");
        radio.write(&Message2, sizeof(Message2));
        Serial.println(Message2); //TEST
        break;
    }
    previousState1 = state1; //update state1
  }
    
  if (state2 != previousState2) { //Compare previous and current states of the button2
    switch (state2) {     
      case OFF2:
        strcpy(Message2, "PUMP 2 OFF");
        radio.write(&Message2, sizeof(Message2));
        Serial.println(Message2); //TEST
        break;
      case ON2:
        strcpy(Message2, "PUMP 2 ON");
        radio.write(&Message2, sizeof(Message2));
        Serial.println(Message2); //TEST
        break;
    }
    previousState2 = state2; //update state2
  }

  if (state3 == ON3) { //Compare previous and current states of the button3 
    
    //Read temperatures
    float temp1 = sensors.getTempCByIndex(0);
    float temp2 = sensors.getTempCByIndex(1);
       
    // Crear cadenas para las temperaturas
    char tempStr1[20];
    char tempStr2[20];
        
    // Formatear temperaturas en cadenas
    dtostrf(temp1, 4, 2, tempStr1);
    dtostrf(temp2, 4, 2, tempStr2);
       
    // Construir el mensaje
    sprintf(Message2, "T1: %sC\nT2: %sC", tempStr1, tempStr2); 
    
    radio.write(&Message2, sizeof(Message2));
    Serial.println(Message2);//TEST
  }
}

//FUNCTIONS------------------------------------------------------------------------------------------------------------------------
void turnonPump1() {
  digitalWrite(pinRelay1, LOW);
}

void turnoffPump1() {
  digitalWrite(pinRelay1, HIGH);
}

void turnonPump2() {
  digitalWrite(pinRelay2, LOW);
}

void turnoffPump2() {
  digitalWrite(pinRelay2, HIGH);
}

void readtemperature(){
  sensors.requestTemperatures(); // Send the command to get temperatures  
}

Doesn't that block for up to 750ms?
Leo..

Hi, @danielarg1990

You are using by the looks of it the PLUS version of the NRF, this requires more power.

Check the specs on your NRF unit.

Keep some separation between TX and RX, like 1m so that you do not overload the RX.

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

Wait, TX is the one that has the buttons, or does it? Your naming of the stations is getting confusing (at least for me). I'm going to assume the station with the buttons correctly displays each button press, but that the other station without buttons sometimes skips a beat. Correct? This would denote a radio problem.

When I was doing some projects with NRF transceivers, I ran into this issue a lot, too. I found that when there was no fault tolerance (i.e. I needed to make sure the signal was received), I had to resort to an acknowledgement routine, where the receiving station sends a checksum back to the sender within a given timeframe after the original payload message was sent.

In principle, the auto-ack feature of the NRF chip can manage this for you; google a bit on "NRF24 auto ack" and see if you can use it.

I've had an application where I didn't end up using auto-ack, but I used a manual/custom acknowledgement instead, which I did like this:

  • Station 1 sends message and calculates checksum of byte. Goes into RX-mode, starts timer.
  • Station 2 gets the message, calculates checksum and sends it back to sender.
    If the checksum was received in time by station 1, the process ended there. If not, station 1 would wait another few milliseconds (to prevent both ends TX-ing at the same time), then start the process anew. I allowed up to X retries, where I set X to a pessimistic value I observed in real-world values (i.e. it would take e.g. 5 retries in worst case conditions, so I'd set maximum retries to 10 or so).

The only thing that went 'wrong' under these conditions would be the instance where station 2 would acknowledge receipt with the checksum, and station 1 would fail to receive it. In this case, the message would be delivered multiple times. My application didn't suffer from this, so I left it like this, but you could try to protect against that, too, in your case, by e.g. adding a provision for a unique message identifier so that station 2 would know if it receives the same identical message multiple times.

The main issue is evidently that it gets pretty complex, pretty fast, if you want to do high-reliability communications this way. See if auto-ack cuts it, since it'll keep life relatively simple.

The problem with all things wireless is that regardless of what you do with PA's, antenna's, module placement/orientation etc, you always end up with data transfer problems sooner or later. They may be very infrequent, or they may happen all the time, depending on....the alignment of the planets!? It's really a crap-shoot. So that's why you see so much protocol overhead in wireless communications dedicated to error detection and recovery. And that's also why you see so many people asking "can you wire these things together instead" if someone suggests using a wireless link (evidently, your application requires wireless!)

1 Like

When I press the button, the temperature sensor sends me only one measurement taken within a maximum range of 1 second. (This is to prevent multiple measurements while I press and release the button)

Hi @TomGeorge .
I used LM2596 modules to power each NRF24L01 separately, aiming for 3.3V output. However, they began behaving erratically, displaying symbols like '???' and 'Received from: ???'. As far as I know, the LM2596 is stable. They only function reliably when connected directly to the Arduino's VCC-GND. (Connection diagrams below)

Moreover, the NRF24L01 consumes 0.16mA while transmitting or receiving (which isn't too much for an Arduino, so it shouldn't cause message glitches on the TX side serial monitor). (See images below)... I'm already beginning to doubt my abilities :expressionless:

Ignore the background PCBs and actuators; they're not connected for testing.

Thank you @rsmls .

  1. Sorry for the confusion, I hope this image can clarify it:
  1. Nice tip! I'll look into the auto-ack feature. I'm encountering a similar issue where the message is sent and processed correctly, but the returned result is incomplete, particularly with "Message 2". It appears there's a discrepancy between the transmission and reception windows. I should consider implementing a time delay to ensure complete reception.

  2. And truth be told, I've tried so many things that I'm starting to consider it an RF-related issue, possibly related to the quality of the modules, etc.

Hi,
Try the 10uF cap on the output of the 2596.

Is your NRF to plus model?

nRF24L01 – How It Works, Arduino Interface, Circuits, Codes.

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

Hi! yes it is the NRF24L01+PA.

I did it, but the NRF24L01 simply doesn't respond even though its Vcc and GND terminals show 3.29V, exactly the same as when powered by the Arduino.

I also tested it with an XL 4016 STEP DOWN and got the same result as with the LM2596, following the same wiring (image below).

So, currently, my issues are:

  1. Skipping line messages on TX Serial Monitor. (I will review the auto-ack feature suggested by @rsmls ).
  2. Unable to power NRF24L01 with an external 12v/5A power supply throught step-downs (LM2596 and XL4016).


I know the feeling, but frankly, if both modules are capable of transmitting and receiving (i.e. at least some data makes it through OK), you can count on this being a coding issue. The problem is that this gets so complicated and you lose oversight, and it doesn't help that 'stuff' happens in the background, too (i.e. the auto-ack etc.)

It may help to make a process chart with four columns: the TX microcontroller on the left, then the TX NRF module, then the RX NRF module and the RX microcontroller on the right. Then draw the process of getting data from one end to another using boxes/flow chart shapes, decision diamonds etc. It helps to visualize what happens exactly, in what sequence, under which conditions. Once you've got that waterproofed, you can convert it into a design for your software and start coding.

1 Like

Hi @rsmls ,

Yes, I think it's a code issue too. I found a fix for glitchy message reception. Since both NRF24L01 modules are separated by 5m, I switched "MAX" to "MIN" in the code, and now messages are received bidirectionally perfectly. Perhaps, in the field, once the distance exceeds 100 meters, I'll change it back to MAX. Moreover, I replaced the NANO on one side with a MEGA nonetheless, to minimize power risks in communication. :grinning:

  radio.setPALevel(RF24_PA_MIN);//set power amplifier level 

Thank you all for the support!!

1 Like

Oh yeah, that pops up more often; apparently these modules sometimes struggle if they're close together and set to a high PA level. I've never had this happen despite extensive testing (sometimes for days on end), but I always used the bare modules with stock antenna, or at best a simple dipole soldered to the PCB. No additional PA module etc.

1 Like

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