Is there a command to restart the Nework in TMRh20 RF24Network?

Hi,
I have a setup where two transmitters send data to one receiver.
For some unkwon reason (to me, of course) the communication with one of the nodes freezes from time to time.
Is there a command that I coud add to the program to restart the transmitter if multiple sending attempts fail?
I have seen that TMRh20 has another library, RF24Mesh wich is "self healing", but its application is advisable when the node's position is changing, which is not my case.
Thanks to all.

There is no reset besides a power cycle.
On the other hand, I know of no way to make the hardware malfunction,
it just does what it is commanded to do.

Without seeing all three codes,
a description of the connections and power supplies,
and a specification of the used devices,
nothing more can be said.

That may be due to inadequate power supply, noise in the power supply, noise or poor connections in the wiring or malfunction of a component other than the MCU controlling the radio.

If only one specific node freezes, that greatly narrows the debugging problem.

1 Like

That means your communication protocol is too primitive. Change the protocol so a received message always is responded to with an "ACK" message or a "NAK" message back to the sender. If the sender does not receive an ACK, or receives a NAK,then the message must be resent and a count of the number of resends is made. Then do what ever you want to do when the error happens.

the funny thing that I noticed is that when the connection with this specific module is "frozen", it can be reestablished if I turn on a second receiver, which is assembled as a portable unit.
Both receivers use the same code.
In my opinion, this rule out any hardware issue.
Is there a TX or RX buffer that may be full?
I will post the codes as requested by Whandal.
Thanks

Not in my opinion. It may be that the software cannot recover from a hardware fault, unless some other input is received.

If only one of the units freezes, that definitely points to a hardware problem.

If you have a fixed pattern to access nodes (polling for instance),
and a constant packet content,
it is possible that packets are received, but not signaled,
because they are considered duplicate.

Do you have some protocol that prevents the two transmitters from sending at the same time?

That is built into the NRFs, they don't send if someone else is sending.
It is very unlikely to start transmissions at the very same time.

Interesting. Is the NRF receiving component able to receive transmissions stacked right next to each other? Is the OP's code able to process messages that fast?

The receive buffers hold the last three packets,
newer will replace the oldest if not read.

Here are the codes.
PLEASE PAY ATTENTION*
THE PART OF THE CODE THAT IS USED TO TRANSMIT EVERY 60 SEC IS DISABLED IN THE CURRENT VERSION.
BOTH TRANSMITTERS ARE WORKING 100% OF THE TIME.


****HARDWARE:
TX1 and TX2
Arduino Pro-MIcro + RF24L01(+???) PA LNA + Serial LCD display 2x16
Both powered by a "external" 7805 regulator
RF24L01 powered by the "external" 7805 regulator via a dedicated regulator PCB ( with an extra 47uF tantalum capacitor at the output o the 3.3V regulator.

RECEIVER
ESP32 Dev Board V1 + RF24L01(+???) PA LNA + TFT display
Both powereed by an external 3.3V regulator

***TRANSMITTER 1 (THIS NEVER HANGS UP)

// Load driver for LED display
#include "Wire.h"
#include "hd44780.h"                       // main hd44780 header
#include "hd44780ioClass/hd44780_I2Cexp.h" // i2c expander i/o class header

// load RF related libraries
#include "RF24Network.h"
#include "RF24.h"
#include "SPI.h"

// Define LCD geometry and properties
hd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip
// LCD geometry
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

//---- Define sensor conections to Arduino 
//???????? const int dpPort = 1; // changed in this revision (14/07/2020)
const byte dpPort = 1;
const byte sensorPin = 7; //  In Arduino Promicro INT6 (or INT4??)  = Pin7

// The hall-effect flow sensor outputs approximately 7.0 pulses per second per litre/minute of flow
float calibrationFactor = 8.275;    // ########Changed according to data on datasheet table pag 2


float altura = 120.00; //altura da caixa dagua em cm
float flow = 0.0;
float totalFlow = 0.0; //used only for sensor calibration
float auxVol = 0.0;      // total volume pumped in this cycle

//------optFlowrate variables------
volatile byte FlowSensorPulseCount = 0;  // 255 counts is about 0.6 liters
float flowRate;
unsigned long oldTime;
float flowRateLPM = 0.0;
float totalLiters = 0.0;

//------Display without delay variables and definitions-------------------
long previousLCDMillis = 0;    // for LCD screen update
long lcdInterval = 4000;
int screen = 0;   
int screenMax = 2;
bool screenChanged = true;   // initially we have a new screen,  by definition
// defines of the screens to show
#define LEVEL       0
#define FLOWRATE       1
#define BATCHVOL         2

//------------ used to caclculate volume pumped in each pumping cycle
float old_Total = 0.0;
int i=0;

int levelDp = 0;
int rawDp = 0;
int rawDpMin = 32;
int rawDpMax = 192; // measured when actual tank was 100% full--revised 2/11/2020

float dummy1 = 0.0;
float dummy2 = 0.0;
bool  dummy3 = 0;
float dummy4 = 0.0;

//  Define if we want to print values
 bool serialPrintFlag = LOW;            //<============ SET THE DEBUG OPTION HERE (ACTIVE HIGH)

// nRF24L01(+) radio attached  (CE, CSN)
RF24 radio(9,10);
// Network uses that radio
RF24Network network(radio);
// Channel of our node
const uint16_t channel = 108;
// Address of our node
const uint16_t this_node = 02;   //changed to 02 in rev3b (was 2)
// Address of the other node
const uint16_t other_node = 00; //changed to 00 in rev3b (was 0)
// How many packets have we sent already
unsigned long packets_sent;

//-------added on rev3_newtime----------
// How often to send data 
const unsigned long Tx_interval = 30000; //ms //changed in Marco2023 (was 4000)
// When did we last send?
unsigned long last_sent =0; //changed in rev Marco2021



//The data to be transmitted by lower and upper water tanks modules is:
//         type       cisterna     caixa superior
//        -------    ----------    --------------
//  1      float      Irms         auxVol   (last pumping cycle volume)
//  2      float     flow          flow
//  3      float     Pdisch        totalLiters
//  4      float     DpFilter      dummy1
//  5      float     Pinlet        dummy2
//  6      float     lampCurrent   dummy4  //added in March 2021
//  7      int       rawDp         rawDp
//  8      bool      flapstate     dummy3

// Structure of our payload, limited to 32 bytes
struct payload_t      // 32 bytes max,  used=7*4+2+1 = 31 bytes OK! updated March 2023
{
  uint32_t counter;     // number of packets transmitted  4-bytes
  float auxVol;         //-4 bytes
  float flowRateLPM;    //-4 bytes
  float totalLiters;    //-4 bytes
  float dummy1;         //-4 bytes
  float dummy2;         //-4 bytes
  float dummy4;         //-4 bytes
  int16_t rawDp;        //-2 bytes
  bool  dummy3;         //-1 byte 
};

  payload_t payload = { packets_sent++, auxVol, flowRateLPM, totalLiters, dummy1, dummy2, dummy4, rawDp, dummy3 }; // Create a variable with the above structure

// * Invoked by interrupt once per pulse of the flow sensor.
// * Interrupt handlers should be kept as small as possible so they return quickly.
void pulseCounter()
{
  FlowSensorPulseCount++;  // Increment the pulse counter
}

void setup()
{
  Serial.begin(115200); // start serial communication

  // Print a message to the LCD
  lcd.begin(16, 2);
  lcd.backlight();
  
//-----Welcome Display Function  
  showWelcome();
 
  //*******Start Radio and define some parameters
  SPI.begin();
  radio.begin();
  network.begin(channel, this_node);
  radio.setPALevel(RF24_PA_HIGH);
  radio.setAutoAck(false);             //        added Marco2021
  radio.setDataRate( RF24_250KBPS );  //         added on rev 3C
//???  radio.setRetries(15,15); // (delay, count)     added on rev 3C-----  No need if setAutoAck = false
  radio.stopListening();   //                    added on rev 3C

// New Flowmeter initialization
 pinMode(sensorPin, INPUT);
 attachInterrupt(digitalPinToInterrupt(sensorPin), pulseCounter, RISING);

}    //------ end of setup

void loop() 
{
//------ Initialize OptFlowrate & Display without Delay variables ------
  unsigned long currentMillis = millis();
  static unsigned long previousMillis = 0;
  static unsigned long totalFlowSensorPulseCount = 0;
  
 //--------- Calculate flow once per interval --------
  const unsigned long interval = 1000;  // count the number of pulses in one second ==> 1 pps = 1 Hz
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis += interval;

    noInterrupts();  // Prevent an interrupt while we sample and reset the pulse count
    byte currentFlowSensorPulseCount = FlowSensorPulseCount;
    FlowSensorPulseCount = 0;  // Re-start the counter for the next second
    interrupts();
    totalFlowSensorPulseCount += currentFlowSensorPulseCount;

     flowRateLPM = (currentFlowSensorPulseCount / calibrationFactor) * (1000.0 / interval); // 1 calibrationFactor  pulses per second == 1 LPM
     totalLiters = totalFlowSensorPulseCount / (calibrationFactor * 60.0);  // 1 calibrationFactor pulses per second == 1 LPM so 7*60 P == 1 L

if ( serialPrintFlag == HIGH)
    {
    Serial.print("currentMillis = ");
    Serial.println(currentMillis);
    Serial.print("currentFlowSensorPulseCount = ");
    Serial.println(currentFlowSensorPulseCount);
    Serial.print("flowRateLPM = ");
    Serial.println(flowRateLPM);
    Serial.print("totalFlowSensorPulseCount = ");
    Serial.println(totalFlowSensorPulseCount);
    Serial.print("TotalLiters = ");
    Serial.println(totalLiters);
   }
  }

// Calculate the volume pumped each time the pumping turns on-off


// check if the pump has started 
if (flowRateLPM > 0.0 && i== 0 ) 
    { auxVol = totalLiters - old_Total;
     if (serialPrintFlag == HIGH)
      {
      Serial.println("esta bombeando"); 
      Serial.print("Total= ");
      Serial.println(totalLiters);
      Serial.print("old_Total= ");
      Serial.println(old_Total);
      Serial.print("Vol Bomb= ");
      Serial.println(auxVol);
     }
    }
// If the pump stopped, store the current total flow
// making i=1 would prevent variables to be updated until the pump starts again   
if (flowRateLPM == 0.0 && i==0 )
{
   old_Total = totalLiters; 
   i=1;
}

// If the pump has started again, set i=0 and start over
if (flowRateLPM > 0.0 && i==1 ) i=0; 

//********Read level from DP sensor************
  rawDp = analogRead(dpPort);
  delay (1000);
  
// ********Calculate level (in %) from measurements*********
  levelDp  = map(rawDp, rawDpMin, rawDpMax, 0, 100);

// print level on serial monitor
 if ( serialPrintFlag == HIGH)
  {
  Serial.print ("rawDp= ");
  Serial.println (rawDp);
  Serial.print ("level= ");
  Serial.println (levelDp);
  Serial.print (" %");
  }
//******* Pump the network regularly*******
  network.update();
  
//-----added in rev3_newtime-----------
  // If it's time to send a message, send it!
  unsigned long now = millis();   // move up after testing...
  if ( now - last_sent >= Tx_interval  )
  {
    last_sent = now;
    sendPayload();
  }
  
  display_Data();


}        // this is the end of the loop

  
//////////////////////////////////////////////////////////////////////////////////
//          sendPayload                                                         //
//////////////////////////////////////////////////////////////////////////////////
// ********* Transmit data ********************
void sendPayload()
{

  RF24NetworkHeader header(/*to node*/ other_node);
  bool ok = network.write(header,&payload,sizeof(payload));

//------Debug--------
 if ( serialPrintFlag == HIGH)
   {
    Serial.println("---------------------------");
    Serial.print("millis() = ");
    Serial.println(millis());
   }
    if (ok)
    { lcd.clear();
     lcd.print( "data sent OK");
     if( serialPrintFlag == HIGH) Serial.println("data sent OK");
     
    }
    else{
     lcd.clear();
     lcd.print( "TX FAILED");
     if ( serialPrintFlag == HIGH) Serial.println("TX FAILED");
    }
}

///////////////////////////////////////////////////////////////////////////////
// Auxiliary display functions                                                         //
///////////////////////////////////////////////////////////////////////////////
void showWelcome()
{
  lcd.clear(); 
  lcd.setCursor(0,0);
  lcd.print("caixa Opt Flow");
  lcd.setCursor(0, 1);
  lcd.print("rev-Marco2023");
   delay(2000); 
  lcd.clear(); 
  lcd.setCursor(0,0);
  lcd.print("17/03/2023");
   delay(2000); 
}

void showLevel()
{
  lcd.clear();
  lcd.print("rawDp= ");
  lcd.print(rawDp);
  lcd.setCursor(0, 1);
  lcd.print("nivelDp= ");
  lcd.print(levelDp);
  lcd.print(" %");
}

void showFlowrate()
{
    lcd.clear();
   lcd.print("Flow=  ");
   lcd.print(flowRateLPM);
   lcd.setCursor(0, 1);
   lcd.print("Total Flow= ");
   lcd.print(totalLiters);
}

void showBatchVolume()
{
   lcd.clear();
   lcd.print("Vol in this Cycle");
   lcd.setCursor(0, 1);
   lcd.print(auxVol);
}

////////////////////////////////////////////////////////////////////////////
//        Display Function
////////////////////////////////////////////////////////////////////////////
 // MUST WE SWITCH SCREEN?
 void display_Data()
 {
  unsigned long currentLCDMillis = millis();
  if(currentLCDMillis - previousLCDMillis > lcdInterval)              // save the last time you changed the display
  {
    previousLCDMillis = currentLCDMillis;
    screen++;
    if (screen > screenMax) screen = 0;  // all screens done? => start over
    screenChanged = true;
  }

  // DISPLAY CURRENT SCREEN
  if (screenChanged)  // -- only update the screen if the screen is changed.
  {
    screenChanged = false; // reset for next iteration
    switch(screen)
    {
    case LEVEL:
      showLevel();
      break;
    case FLOWRATE:
      showFlowrate();
      break;
    case BATCHVOL:
     showBatchVolume();
      break;
    default:
      // cannot happen -> showError() ?
      break;
    }
  }
 }

****TRANSMITTER 2 *** (THIS ONE FREEZES FROM TIME TO TIME)

//-------"CISTERNA"--------

// Load driver for LED display
#include "Wire.h"
#include "hd44780.h"                       // main hd44780 header *****USE duinoWitchery/hd44780
#include "hd44780ioClass/hd44780_I2Cexp.h" // i2c expander i/o class header

// load RF related libraries
#include "RF24Network.h"
#include "RF24.h"
#include "SPI.h"

int sleepDelay = 10000;  // in milliseconds ****CHECK IF REQUIRED****

// Load Current Sensor Library
#include "EmonLib.h"    //https://github.com/openenergymonitor/EmonLib
EnergyMonitor emon1;        //create instance

// Define LCD geometry and properties
hd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip
// LCD geometry
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

// Define sensors and interrupts ports
const int dpPort = 1;
const int interruptPin = 7; 
const int lampCurrentPort= 6;  //Changed March 2021
const int flapButton = 8;
const int displayButton = 5;
const int P_Port = 2;
const int DpFilter_Port = 3;
const int P_Inlet_Port = 7;   //Changed in March 2021

//******** Initialize and define variables ***************
float Pdisch = 0.0;
int rawPdisch = 0;     
int rawPmin =102;   // 0.5V/4,9mV * counts = 102 counts
int rawPmax = 918;  // 4.5V/4.9mV * counts = 918 counts

float DpFilter = 0.0; //&&&& added in rev3
float voltageRatio1 = 0.0;
float voltageRatio2 = 0.0;
float kFilter = 0.0;   // This is the k used in DP = k Q**2
float Pinlet = 0.0;

boolean flapState = 0;
int pumpState = 0;

float altura = 120.00; //altura da caixa dagua em cm
int levelDp = 0;
int16_t rawDp = 0;
int rawDpMin = 32;
int rawDpMax = 248; // measured when actual tank was 100% full

//------optFlowrate variables------
float calibrationFactor = 8.275; // Changed according to sensor datasheet
volatile byte FlowSensorPulseCount = 0; 
float flowRate;
unsigned long oldTime;
float flowRateLPM = 0.0;
float totalLiters = 0.0;
float flowCorrectionFactor = 2.357;  // Defined in Lamp Current Rev 1 (April 2021)

//------Display without delay variables and definitions-------------------
long previousLCDMillis = 0;    // for LCD screen update
long lcdInterval = 4000;
int screen = 0;   
int screenMax = 4;
bool screenChanged = true;   // initially we have a new screen,  by definition
// defines of the screens to show
#define LEVEL       0
#define FLOWRATE      1
#define PUMPDATA        2
#define FILTER            3
#define MISC                4


//******Define Current Measurement Stuff**********
const int mains = 127.0;    // define mains voltage
const int sctPin = 36;      // define sensor pin 
const int calConstant = 20; // define calibration cosntant used by library  ***was 30 changed to 20 to test the 20A/1V sensor in REV2 Dez2021
//%%%%% limit minimum measured current by software, since there is always noise 
float minCurrent = 0.3;  // minimum current = 300mA changed rev 0_B 10/01/20
float Irms = 0.0;            // zero measured current 

//********** define RF related parameters*************
// nRF24L01(+) radio attached  (CE, CSN)
RF24 radio(9,10);
// Network uses that radio
RF24Network network(radio);
// Channel of our node
  const uint16_t channel = 108;
// Address of our node
  const uint16_t this_node = 01;
// Address of the other node
  const uint16_t other_node = 00;
// How many packets have we sent already
  unsigned long packets_sent;

//-------added on rev3TX----------
// How often to send data 
  const unsigned long Tx_interval = 1000; //ms  <=======SET DATA TRANSMISSION TIME HERE
// When did we last send?
  unsigned long last_sent;
  
//  Define if we want to print values
bool serialPrintFlag = HIGH;            //<============ SET THE DEBUG OPTION HERE (ACTIVE HIGH)============================

// ------- Interrupt for flow Measurement---------
// * Invoked by interrupt once per pulse of the flow sensor.
// * Interrupt handlers should be kept as small as possible so they return quickly.
void pulseCounter()
{
 FlowSensorPulseCount++;  // Increment the pulse counter
}

/*
//---------Fast ADC-------------------
#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
*/

//------Lamp Current ----------------
float nVPP;   // Voltage measured across resistor
float nCurrThruResistorPP; // Peak Current Measured Through Resistor
float nCurrThruResistorRMS; // RMS current through Resistor
float lampCurrent;     // Actual RMS current in Wire
float noiseVPP = 0.08;

//The data to be transmitted by lower and upper water tanks modules is:
//         type       cisterna     caixa superior
//        -------    ----------    --------------
//  1      float     Irms          auxVol   (last pumping cycle volume)
//  2      float     flow          flow
//  3      float     Pdisch        totalLiters
//  4      float     DpFilter      dummy1
//  5      float     Pinlet        dummy2
//  6      float     lampCurrent   dummy4  //added in March 2021
//  7      int       rawDp         rawDp
//  8      bool      flapstate     dummy3

// Structure of our payload, limited to 32 bytes
struct payload_t      // 32 bytes max,  used=7*4+2+1 =31 bytes OK!     revised March 2023
{
 uint32_t counter;      // number of packets transmitted  4-bytes        
 float   Irms ;         // - 4 bytes
 float   flowRateLPM;   // - 4 bytes                 
 float   Pdisch;        // - 4 bytes 
 float   DpFilter;      // - 4 bytes
 float   Pinlet;        // - 4 bytes **added in rev4
 float   lampCurrent;   // - 4 bytes ** added March 2021
 int16_t rawDp;         // - 2 bytes  
 boolean flapstate;     // - 1 byte    
};

payload_t payload = { packets_sent++, Irms, flowRateLPM, Pdisch, DpFilter, Pinlet, lampCurrent, rawDp, flapState }; // Create a variable with the above structure


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

// Print a message to the LCD
 lcd.begin(16, 2);
 lcd.backlight();
 showWelcome();
 delay(2000);

 //*******Start Radio and define some parameters
 SPI.begin();
 radio.begin();
 network.begin(channel, this_node);
 radio.setPALevel( RF24_PA_MAX ) ;
 radio.setDataRate( RF24_250KBPS );           //added on rev 5
//???    radio.setRetries(15,15); // (delay, count)     added on rev 5  disbled,needs setAutoAck = true
   radio.setAutoAck(false); //                    active on rev 3 April 2023
   radio.stopListening();   //                    added on rev 5

//**** Current meter initialization ******
emon1.current(sctPin, calConstant);

//**** New Flowmeter initialization****
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), pulseCounter, RISING);

//***** Start the Fast ADC ********
/*
#if FASTADC
// set prescale to 16
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
#endif
*/

//*****Lamp Current********
pinMode(lampCurrentPort, INPUT);

}  //-------- This is the end of Setup

void loop() 
 {
//------ Initialize OptFlowrate & Display without Delay variables ------
 unsigned long currentMillis = millis();
 static unsigned long previousMillis = 0;
 static unsigned long totalFlowSensorPulseCount = 0;
 
//--------- Calculate flow once per interval --------
 const unsigned long interval = 1000;  // count the number of pulses in one second ==> 1 pps = 1 Hz
 if (currentMillis - previousMillis >= interval)
 {
   previousMillis += interval;

   noInterrupts();  // Prevent an interrupt while we sample and reset the pulse count
   byte currentFlowSensorPulseCount = FlowSensorPulseCount;
   FlowSensorPulseCount = 0;  // Re-start the counter for the next second
   interrupts();
   totalFlowSensorPulseCount += currentFlowSensorPulseCount;
    flowRateLPM = (currentFlowSensorPulseCount / calibrationFactor) * (1000.0 / interval) / flowCorrectionFactor; // correction factor added in lamp_current_Rev1
    totalLiters = totalFlowSensorPulseCount / (calibrationFactor * 60.0) / flowCorrectionFactor; // correction factor added in lamp_current_Rev1

 if(serialPrintFlag == HIGH)
   {
   Serial.println("---------------OptFlowrate Values------------------------");
   Serial.print("currentMillis = ");
   Serial.println(currentMillis);
   Serial.print("currentFlowSensorPulseCount = ");
   Serial.println(currentFlowSensorPulseCount);
   Serial.print("flowRateLPM = ");
   Serial.println(flowRateLPM);
   Serial.print("totalFlowSensorPulseCount = ");
   Serial.println(totalFlowSensorPulseCount);
   Serial.print("TotalLiters = ");
   Serial.println(totalLiters);
  }
 }    // --------this is the end of Optflow Calcs

 
  //********Read level from DP sensor************
 rawDp = analogRead(dpPort);
 delay (1000);

 //********Read Pump discharge pressure via sensor********
  rawPdisch = analogRead(P_Port);

 //******** Read MPX5010 #1 voltage output (filter pressure drop)********
  voltageRatio1 = (float)analogRead(DpFilter_Port)/1023;  

 //******** Read MPX5010 #2 voltage output (filter inlet pressure )********
  voltageRatio2 = (float)analogRead(P_Inlet_Port)/1023;  

// ********Calculate level (in %) from measurements *********
levelDp  = map(rawDp, rawDpMin, rawDpMax, 0, 100);

//********Calculate Pump Dischrge Pressure ***********
Pdisch = map(rawPdisch, rawPmin, rawPmax, 0, 100) / 14.2; // max sensor pressure = 100 psi

//******Calculate Filter Pressure Drop *************
DpFilter = 1133.3 * (voltageRatio1 - 0.04);

//******Calculate Filter Inlet Pressure *************
Pinlet = 1133.3 * (voltageRatio2 - 0.04);

//*********Calculate Filter k ***********
kFilter = DpFilter / sq(flowRateLPM) ;

//********Check discharge flap position*********
flapState = digitalRead(flapButton);

//****** Print values in serial monitor
if(serialPrintFlag == HIGH)
 {
 Serial.println("++++++++++++++Output values++++++++++++++++++++++++++++++");
 Serial.print("rawDp= ");
 Serial.println (rawDp);
 Serial.print("% Level=  ");
 Serial.println (levelDp);
 Serial.print ("Irms= ");
 Serial.println (Irms);
 Serial.print ("rawPdisch= ");
 Serial.println (rawPdisch);
  Serial.print ("Pdisch= ");
 Serial.println (Pdisch);
 Serial.print ("voltageRatio1= ");
 Serial.println (voltageRatio1);
 Serial.print ("DpFilter= ");
 Serial.println (DpFilter);
Serial.print ("voltageRatio2= ");
 Serial.println (voltageRatio2);
 Serial.print ("P_Inlet= ");
 Serial.println (Pinlet);
 Serial.print ("Lamp Current ");
 Serial.println (lampCurrent);
 }

//********Calculate Pump Current*******
Irms = emon1.calcIrms(1480); 
// set irms = 0 if calculated value is lower than minCurrent 
if (Irms <= minCurrent ) Irms = 0.0;

//*******Calculate Lamp Current*******

// Get CT reading
nVPP = getVPP();

// If there is no output from the sensor nVPP should be 2.5V =/- noise voltage
// Since lamp current = 0.2 A lets assume that any reading between +/- 2.55v is noise
// and nVPP would be defined as ZERO!
    float diff = nVPP - 2.5;

// check if mesured voltage is noise, if so, make volatage reading equal to zero
    if ( abs(diff) < noiseVPP) nVPP = 0;

// Calculate Current across burden resistor (1kOHM)in mA
    nCurrThruResistorPP = nVPP;

/*
//Use Formula for SINE wave to convert to RMS in mA
   nCurrThruResistorRMS = nCurrThruResistorPP * 0.707;
*/

// Multiply current through burden resistor by CT turn ratio to get load current
// I added 10 turns of  "sense wire" to the transformer to increase output
// So the final turn ratio of this ct is 10 X 1000:1
// I will use peak current instead of rms since the lamp is "similar" to a resistive load
//????   lampCurrent = nCurrThruResistorRMS;
      lampCurrent= nCurrThruResistorPP / 10;
      
 if(serialPrintFlag == HIGH)
   {
   Serial.println("****************Lamp current*************************************");
   Serial.print(" nVPP= ");
   Serial.println(nVPP);
   Serial.print(" nCurrThruResistorPP= ");
   Serial.println(nCurrThruResistorPP);
   Serial.print(" nCurrThruResistorRMS= ");
   Serial.println(nCurrThruResistorRMS);
   Serial.print(" lampCurrent= ");
   Serial.println(lampCurrent);
   }

//***********Pump the network regularly*********************
 network.update();


////////////////////////////////////////////////////////////////
//          code should only be used to test display and transmission
//////////////////////////////////////////////////////////////////////
/*
// uint32_t counter;      //  will not be changed    
 Irms = 6.66;
 flowRateLPM = 4,32;              
 Pdisch = 8,76;
 DpFilter = 123;
 Pinlet = 789;
 rawDp = 150;  
 flapState = HIGH;
*/

//-----Transmission Delay -----added in rev3TX-----------
 // If it's time to send a message, send it! 
unsigned long now = millis();   // move to the init. section after testing
 if (now - last_sent >= Tx_interval)
 {
   last_sent = now;
   sendPayload(); 
 }
 
 display_Data();
 
}      //-------- This is the end of loop


//////////////////////////////////////////////////////////////////////////////////
// sendPayload();          // send payload
//////////////////////////////////////////////////////////////////////////////////
// *********função para transmitir dados por RF*********
void sendPayload()
{
 if(serialPrintFlag == HIGH)
   {
   Serial.println("****************Send Payload*************************************");
   Serial.print(" val1= ");
   Serial.println(Irms);
   Serial.print(" val2= ");
   Serial.println(flowRateLPM);
   Serial.print(" val3= ");
   Serial.println(Pdisch);
   Serial.print(" val4= ");
   Serial.println(DpFilter);
   Serial.print(" val5= ");
   Serial.println(Pinlet);
   Serial.print(" val7= ");
   Serial.println(lampCurrent);
   Serial.print(" val8= ");
   Serial.println(rawDp);
   Serial.print(" val9= ");
   Serial.println(flapState);
   }
   
 RF24NetworkHeader header(/*to node*/ other_node);00000000000000000000000000000000000000
 bool ok = network.write(header,&payload,sizeof(payload));

/*
//**** this section should be used for testing TX/RX ONLY!!!*******

 if (ok)
   { lcd.clear();
    lcd.print( "data sent OK");
   }
   else{
    lcd.clear();
    lcd.print( "TX FAILED");
 }
*/
}

///////////////////////////////////////////////////////////////////////////////
// Auxiliary display functions                                               //
///////////////////////////////////////////////////////////////////////////////
void showWelcome()
{
 lcd.setCursor(0,0);
 lcd.print("Cistern Opt Flow");
 lcd.clear(); 
 lcd.print("Lamp Current");
 lcd.setCursor(0, 1);
 lcd.print("Rev 3 12/04/2023");
}

void showLevel()
{
 lcd.clear();
 lcd.print("rawDp= ");
 lcd.print(rawDp);
 lcd.setCursor(0, 1);
 lcd.print("nivelDp= ");
 lcd.print(levelDp);
 lcd.print(" %");
}

void showFlowrate()
 {
  lcd.clear();
  lcd.print("Flow=  ");
  lcd.print(flowRateLPM);
  lcd.setCursor(0, 1);
  lcd.print("Filter Dp= ");
  lcd.print (DpFilter);
 }

void showPumpData()
{
  lcd.clear();
   if (Irms != 0.0) 
   {      
   lcd.print ("PUMP (A)= ");
   lcd.print (Irms);
   }
 else lcd.print ("Pump is OFF");
   lcd.setCursor(0, 1);
   lcd.print ("Pd (kgf)= ");
   lcd.print (Pdisch);
}

void showFilterData()
{
 lcd.clear();
 lcd.print ("Filter Coef=");
 lcd.print (kFilter);  
 lcd.setCursor(0, 1);
 lcd.print("P_inlet= ");
 lcd.print(Pinlet);
}

void showMisc()
{
  lcd.clear();
  lcd.print("LAMP (A)=  ");
  lcd.print(lampCurrent);
  lcd.setCursor(0, 1);
  lcd.print("Flap is ");
 if (flapState == LOW) lcd.print ("CLOSED");
 else lcd.print("OPEN");
}



////////////////////////////////////////////////////////////////////////////
//        Display Function
////////////////////////////////////////////////////////////////////////////
// MUST WE SWITCH SCREEN?
void display_Data()
{
 unsigned long currentLCDMillis = millis();
 if(currentLCDMillis - previousLCDMillis > lcdInterval)              // save the last time you changed the display
 {
   previousLCDMillis = currentLCDMillis;
   screen++;
   if (screen > screenMax) screen = 0;  // all screens done? => start over
   screenChanged = true;
 }

 // DISPLAY CURRENT SCREEN
 if (screenChanged)  // -- only update the screen if the screen is changed.
 {
   screenChanged = false; // reset for next iteration
   switch(screen)
   {
   case LEVEL:
     showLevel();
     break;
   case FLOWRATE:
     showFlowrate();
     break;
   case PUMPDATA:
    showPumpData();
     break;
   case FILTER:
    showFilterData();
     break;
   case MISC:
    showMisc();
     break;
   default:
     // cannot happen -> showError() ?
     break;
   }
 }
}

////////////////////////////////////////////////////////
//     getVPP()
///////////////////////////////////////////////////////

//  The following function takes one second worth of samples
//  and returns the peak value that is measured

float getVPP()
{
 float result;
 int readValue;             //value read from the sensor
 int maxValue = 0;          // store max value here
  uint32_t start_time = millis();
  while((millis()-start_time) < 1000) //sample for 1 Sec
  {
      readValue = analogRead(lampCurrentPort);
      // see if you have a new maxValue
      if (readValue > maxValue) 
      {
          /*record the maximum sensor value*/
          maxValue = readValue;
      }
  }
/*   
//--for debugging--
   Serial.print(" maxValue= ");
   Serial.println(maxValue);
*/
  
// Convert the digital data to a voltage
  result = (maxValue * 5.0)/1024.0;
 
  return result;
}
 

RECEIVER*******

#include <TFT_eSPI.h> // Hardware-specific library

// In the TFT_ESPI user_setup.h file //&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// be sure to comment OUT the line for TFT_CS // <======== BE SURE TO DO THIS EVERYTIME A NEW COMPUTER IS USED
// //#define TFT_CS 21 // Chip select control pin // OR the library is Re-installed

// load RF related libraries
#include "SPI.h"
#include "RF24.h"
#include "RF24Network.h"

//----- Using faster Graphic Library
TFT_eSPI tft = TFT_eSPI(); // Invoke TFT custom library

// My setup for Harware SPI for ILI9341**
// The pins used by the TFT display are defined in the user_setup.h file
// ####### DO NOT ERASE THIS #######
// TFT_MISO 19
// TFT_MOSI 23
// TFT_SCLK 18
// TFT_CS 15
// TFT_DC 2 // Data Command control pin
// TFT_RST 4 // Reset pin (could connect to RST pin)

//----- define RF24L01 variables
#define RF24_CSN 3
#define RF24_CE 1
#define NumNodes 2 // Number of Transmitters
RF24 radio(RF24_CE,RF24_CSN); // NRF24L01 used SPI pins radio (CE, CSN) #####Check this pin assignment######
RF24Network network(radio);
const uint16_t channel = 108; //changed on Marco2021
const uint16_t this_node = 00; // Address of our node

// Structure of our payload
// ESP32 group data in 4 bytes chunks, so data structure has to be rearranged

//The data to be transmitted by lower and upper water tanks modules is:
// type cisterna caixa superior
// ------- ---------- --------------
// 1 float Irms auxVol (last pumping cycle volume)
// 2 float flow flow
// 3 float Pdisch totalLiters
// 4 float DpFilter dummy1
// 5 float Pinlet dummy2
// 6 float lampCurrent dummy4 //added in March 2021
// 7 int rawDp rawDp
// 8 bool flapstate dummy3

// Structure of our payload
struct payload_t // 32 bytes max, used=7*4+2+1 = 31 bytes OK! revised March 2023
{
uint32_t counter; // number of packets transmitted
float val1; // - 4 bytes
float val2; // - 4 bytes
float val3; // - 4 bytes
float val4; // - 4 bytes
float val5; // - 4 bytes
float val6; // - 4 bytes
int16_t val7; // - 2 bytes
bool val8; // - 1 byte
};

payload_t payload; // Create a variable with the above structure

unsigned long NodeCounter[NumNodes+1];

//******** Initialize and define variables ***************

unsigned long startTime; //timer used to ckeck if data has been received
unsigned long clearTime = 90000; // in milliseconds
unsigned long waitTime = 60000; // in milliseconds
unsigned long pumpOnMillis =0; // time when pump has been turned ON
unsigned long pumpOnDelay = 10000; // wait 10 s to check if the flow has been established normally
unsigned long lastCisternaTime;
unsigned long lastCaixaTime;
unsigned long dataLossTime = 300000; // Changed in Rev2A Nov 2011
const int LCDinterval = 5000; //added in rev noDelays (03/2021)
unsigned long buzzer_millis_new;
unsigned long buzzer_millis_old;
unsigned long fault_ID_mil[7];
unsigned long fault_ID_mil_Old[7] = { 0, 0, 0, 0, 0, 0, 0 };

bool flapState = LOW;
float Pdisch = 0.0;
float PdischLow = 0.5; // Check value !!!! ***** added in June 2021

float flow[3] = {0.0, 0.0 , 0.0};
float totalLiters = 0.0;
float auxVol = 0.0;
float pumpFlowLow = 2.0; // ***Check Value info added June 2021

int rawDp[3] = {0, 0 , 0};
int rawDpMin = 32;
int rawDpMax[3] = {0, 243 , 187}; // values changed in rev 2_A
int levelDp[3] = {0, 0 , 0};
int levelDpLow = 30; // critical level in %
int levelDpMin = 20; // minimal level in %
int levelDpWarning = 50; // warning leval in %

float Irms = 0.0;
float current = 0.0;

float DpFilter = 0.0; // filter Dp in mmca
float DpFilter_max = 600.0;
float kFilter;
float Pinlet = 0.0;
float PinletLow = 1.0; // Changed in rev 3A- this variable has no useful purpose
float lampCurrent = 0.0;
float lampCurrentLow = 0.1;

float dummy1 = 0.0;
float dummy2 = 0.0;
bool dummy3 = LOW;
float dummy4 = 0.0;

bool faultStatus = LOW;
bool warningStatus = LOW;
bool ackAlarmStatus = LOW;
bool delayRunning = LOW;
bool pump_State = LOW;
bool last_Pump_State = LOW;
bool flowDelay = LOW;
bool clearDataFlag = true;
bool cisternaRxLoss = false;
bool caixaRxLoss = false;
bool buzzer_ON_OFF_Toggle = LOW; //is buzzer ON or OFF
bool buzzer_Status = LOW;
bool levelWarningStatus = LOW;

int x = 1;
unsigned long t1;
unsigned long t2;

//----FlowMin[]--------CHECK ACTUAL VALUES MEASURED BY THE FLOW SENSORS!!!-----------------
// average flowrate to reservoir 1 is 4.0 l/min --- let's set the alarm @ 50% of normal flowrate, so flowMin[1] = 2.0
// Calculated flowrate for the installed pump is 20 l/min --- let's set the alarm @ 50% of normal flowrate, so flowMin[2] = 10.0
float flowMin[3] = { 0.0 , 1.3 , 10.0 }; // minimum flow values used to trigger alarm condition // <=======Min flow changed to 1.3

//----Toggle button & Misc stuff----
int buzzerState=0;
int alarm_Button_New;
int alarm_Button_Old=1;
//???int ack_Button_New;
//???int ack_Button_Old=1;
int dt=100;
bool fault_ID[7] = {0,0,0,0,0,0,0};
bool fault_ID_Old[7] = {0,0,0,0,0,0,0};
unsigned long fault_ID_millis[6] = {0,0,0,0,0,0};
unsigned long time_between_failures = 3600000;
int i= 1;

// Define if we want to print values
bool serialPrintFlag = LOW; //<============ SET THE DEBUG OPTION HERE (ACTIVE HIGH)

//------Define Pin numbers for buttons & LEDs
const int pump_On_Pin = 26;
const int alarm_On_Pin = 12;
const int alarm_Toggle_Button_Pin = 13;
const int ack_Alarm_Button_Pin = 33;
const int buzzerPin = 25;
const int fault_Led_Pin = 27;
const int warning_Led_Pin = 14;

//------Display without delay variables and definitions-------------------
unsigned long previousLCDMillis = 0; // for LCD screen update
unsigned long lcdInterval = 10000;
int screen = 0;
int screenMax = 1;
bool screenChanged = true; // initially we have a new screen, by definition
// defines of the screens to show
#define PROCESS_VARIABLES 0
#define CHECK_FAULTS 1
//???#define BATCHVOL 2

void setup()
{
// ----define Led pins as outputs
pinMode(alarm_On_Pin, OUTPUT);
pinMode(pump_On_Pin, OUTPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(alarm_Toggle_Button_Pin, INPUT_PULLUP);
pinMode(ack_Alarm_Button_Pin, INPUT_PULLUP);
pinMode(warning_Led_Pin, OUTPUT);
pinMode(fault_Led_Pin, OUTPUT);

// it seems that Arduino IDE does not define the pins as outputs so we do it manuallly here
pinMode(TFT_CS, OUTPUT);
digitalWrite(TFT_CS, HIGH);
pinMode(RF24_CSN, OUTPUT); // check if it is required
pinMode(RF24_CE, OUTPUT); // check if it is required

digitalWrite(RF24_CSN, HIGH); // check if it is required

//-----Start Serial Communication-------------
Serial.begin(115200); // start serial communication

//-----Start TFT, set rotation and background color------
tft.init();
tft.setRotation(0);
tft.setTextSize(2);

// -----Start nRF24L01--------------------------
SPI.begin();
radio.begin();
network.begin(channel, this_node);
radio.setDataRate( RF24_250KBPS ); //added on rev 3C
radio.startListening(); //added on rev 3C

//------- Initialize display
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(3);
tft.println("Receptor Casa");
tft.println("ESP32 NoDelays");
tft.println(" Rev 3A");
tft.println("14/04/2023"); // was 01/12/2021
delay(2000);

startTime = millis(); // start the timer
lastCisternaTime = millis(); // start Cisterna RX timer
lastCaixaTime = millis(); // start Caixa RX timer

// Use this function to define the values of the variables while testing code
//??????? input_Data(); //============> USED IN TEST ALARM ONLY!!!!

} //--- this is the end of Setup

void loop()
{
network.update(); // Pump the network regularly

// for debugging
if(serialPrintFlag == HIGH)
{
bool net = network.available();
Serial.print(" net availlable? ");
Serial.println(net);
Serial.print(" millis= ");
Serial.println(millis());
}

// Is there anything ready for us?
if ( network.available() ) // Changed from while... in April 2023
{
getRadioData(); // If so, grab it and print it out
if(serialPrintFlag == HIGH)
{
Serial.print(" network available? ");
Serial.println(millis());
Serial.print("cisternaRxLoss ");
Serial.println(cisternaRxLoss);
}
}

// -------Calculate output variables from raw data ---------
levelDp[1] = map(rawDp[1], rawDpMin, rawDpMax[1], 0, 100);
levelDp[2] = map(rawDp[2], rawDpMin, rawDpMax[2], 0, 100);
kFilter = DpFilter / sq(flow[1]) ;

//------Light warning LEDs and Sound Buzzer--------------
Buzzer_and_Leds();

//---Display data------
display_Data() ; //function name changed in display without delay

// sound buzzer if FAULT 7 had occured ( Added REv 3A Dez 2021 )
if (buzzer_ON_OFF_Toggle ==HIGH && levelWarningStatus == HIGH)
{
beep_Buzzer();
}
} //-------this is the end of the loop

//////////////////////////////////////////////////////////////////////////////////
// getRadioData() // get Network data
//////////////////////////////////////////////////////////////////////////////////
void getRadioData(){
tft.setTextColor(ILI9341_RED);
RF24NetworkHeader header;

if (serialPrintFlag == HIGH)
{
Serial.println("header created");
Serial.println(header.from_node);
//?????tft.print("header created"); // for debugging
//????tft.println(header.from_node); // for debugging
}

network.read(header,&payload,sizeof(payload));

//------Copy read values to Cisterna variables-----
NodeCounter[header.from_node] = payload.counter;
if (header.from_node ==1)
{
//????? tft.println("Reading Cisterna Data"); // <==================Reading Cisterna Data Message
Irms = payload.val1;
flow[1] = payload.val2;
Pdisch = payload.val3;
DpFilter = payload.val4;
Pinlet = payload.val5;
lampCurrent = payload.val6;
rawDp[1] = payload.val7;
flapState = payload.val8;
clearDataFlag = false;

// Cisterna data should be updated at last every 60 seconds
if ( (millis() - lastCisternaTime) > dataLossTime)
{
cisternaRxLoss = true;
lastCisternaTime = millis(); // restart the Cisterna RX timer
}
else cisternaRxLoss = false;
if(serialPrintFlag == HIGH)
{
Serial.print("cisternaRxLoss ");
Serial.println(cisternaRxLoss);
Serial.print(" millis= ");
Serial.println(millis());
Serial.print(" lastCisternaTime= ");
Serial.println(lastCisternaTime);
}
}

//------Copy read values to Caixa variables-----
if (header.from_node ==2)
{
//?????? tft.println("Reading Caixa Data"); //<==================Reading caixa Data Message
auxVol = payload.val1; // this is the batch volume
flow[2] = payload.val2;
totalLiters = payload.val3;
dummy2= payload.val4;
dummy1= payload.val5;
dummy4= payload.val6;
rawDp[2] = payload.val7;
dummy3 = payload.val8;
clearDataFlag = false;

// Caixa data should be updated at last every 60 seconds
if ( (millis() - lastCaixaTime) > dataLossTime)
{
caixaRxLoss = true;
lastCaixaTime = millis(); // restart the Caixa RX timer
}
else caixaRxLoss = false;
}

if(serialPrintFlag == HIGH)
{
Serial.print("Received packet #");
Serial.print(NodeCounter[header.from_node]);
Serial.print(" from node#: ");
Serial.println(header.from_node);
Serial.print("val1= ");
Serial.println(payload.val1);
Serial.print(" val2= ");
Serial.println(payload.val2);
Serial.print(" val3= ");
Serial.println(payload.val3);
Serial.print(" val4= ");
Serial.println(payload.val4);
Serial.print(" val5= ");
Serial.println(payload.val5);
Serial.print(" val6= ");
Serial.println(payload.val6);
Serial.print(" val7= ");
Serial.println(payload.val7);
Serial.print(" val8= ");
Serial.println(payload.val8);
}
} //--- end of getRadioData

////////////////////////////////////////////////////////////////////////////
// Display Function
////////////////////////////////////////////////////////////////////////////
// MUST WE SWITCH SCREEN?
void display_Data()
{
unsigned long currentLCDMillis = millis();
if(currentLCDMillis - previousLCDMillis > lcdInterval) // save the last time you changed the display
{
previousLCDMillis = currentLCDMillis;
screen++;
if (screen > screenMax) screen = 0; // all screens done? => start over
screenChanged = true;
}

// DISPLAY CURRENT SCREEN
if (screenChanged) // -- only update the screen if the screen is changed.
{
screenChanged = false; // reset for next iteration
switch(screen)
{
case PROCESS_VARIABLES:
display_variables();
break;
case CHECK_FAULTS:
display_faults();
break;
default:
// cannot happen -> showError() ?
break;
}
}
} //----- end of display_data

//////////////////////////////////////////////////////////////////////////////////
// display Variables
//////////////////////////////////////////////////////////////////////////////////
void display_variables()
{
// customize output printing using node id
// index 1 2
// place cisterna caixa

//CISTERNA
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(3);
tft.setTextColor(ILI9341_WHITE);
if (cisternaRxLoss == true) tft.setTextColor(ILI9341_YELLOW);
tft.println(" CISTERNA");
tft.setTextSize(2);
tft.print("NIVEL= ");
tft.print(levelDp[1]);
tft.println(" %");
tft.print("l/min= ");
tft.println(flow[1]);
if (Irms !=0) // Pump is ON
{
tft.print ("CURR.= ");
tft.print (Irms, 1);
tft.println (" A");
digitalWrite(pump_On_Pin, HIGH);
}
else // Pump is OFF
{
tft.println("Pump is OFF");
digitalWrite(pump_On_Pin, LOW);
}
tft.print("Flap ");
if (flapState == LOW) tft.println ("CLOSED");
else tft.println("OPEN");
tft.print("Pdisch= ");
tft.println(Pdisch);
tft.println (" ");
tft.print("P_Inlet= ");
tft.println(Pinlet);
tft.print("Dp Filter= ");
tft.println(DpFilter);
kFilter = DpFilter / sq(flow[1]) ; //-------Calculate Filter k ---------
tft.print("K Filter= ");
tft.println(kFilter);
tft.print("UV Lamp (A)= ");
tft.println(lampCurrent);

//CAIXA**************
tft.println (" ");
//??? tft.fillScreen(ILI9341_BLACK); // printing on the same screen
tft.setTextSize(3);
//??? tft.setCursor(0, 0); // printing on the same screen
tft.setTextColor(ILI9341_WHITE);
if (caixaRxLoss == true) tft.setTextColor(ILI9341_YELLOW);
tft.println("** CX. SUP **");
tft.setTextSize(2);
tft.print("NIVEL= ");
tft.print(levelDp[2]);
tft.println(" %");
tft.print("l/min= ");
tft.println(flow[2]);
tft.print("vol(l)= ");
tft.println(totalLiters);
tft.println("ult bomb(l)= ");
tft.println(auxVol);
tft.setTextColor(ILI9341_WHITE);
} // -----end of display_variables

//////////////////////////////////////////////////////////////////////////////////
// Display Faults -- Identify possible faults in the system and turn alarm ON
//////////////////////////////////////////////////////////////////////////////////
void display_faults(){
faultStatus = LOW;
warningStatus = LOW; //-------------Added in Rev1 01/09/2019
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0 ,0);
tft.setTextColor(ILI9341_RED); tft.setTextSize(2);
tft.println(" CHECKING FAILURES");
tft.println(" -------");

//?????? flowDelay = LOW; // this is HIGH while waiting to purge air from pump discharge pipe
pump_State = LOW;
// check if PUMP has been turned on and start flow delay timer
if (Irms > 0.0)
{
pump_State = HIGH;
if (pump_State == HIGH && last_Pump_State == LOW)
{
pumpOnMillis = millis();
last_Pump_State = HIGH;
flowDelay = HIGH;
}
if ((millis() - pumpOnMillis) > pumpOnDelay) flowDelay = LOW;
} // do this if pump is ON ONLY
else last_Pump_State = LOW;

//++++++++++++++++++++++++++++++++++Should be used for debug only+++++++++++++++++++++++++++
/*
Serial.println ("---------------------------------Faults---------------------------------------");
Serial.println("checking pump State & Delay ");
Serial.println("FAULT 1 & 2----- Nivel Baixo nivel cisterna= " + String(levelDp[1]) + " nivel caixa= " + String(levelDp[2]));
Serial.println("FAULT 3----- vaz mininha vaz minima= " + String(flow[1]));
Serial.println("FAULT 4----- UV Failure Lamp Current= " + String(lampCurrent));
Serial.println("FAULT 5------Pump Protection Failure Irms " + String(Irms) + " Nivel Cisterna= " + String(levelDp[1]));
Serial.println("FAULT 6----- RF LINK LOSS Cisterna " +String(cisternaRxLoss) + " Caixa " + String(caixaRxLoss));
Serial.println("-----------------------------------Warnings-----------------------------------");
Serial.println("fault 7------Check Level nivel cisterna= " + String(levelDp[1]) + " nivel caixa= " + String(levelDp[2]));
Serial.println("fault 8------High Filter DP Filter DP= " + String(DpFilter));
Serial.println("fault 9------No Flow with Pump ON Irms= " + String(Irms) + " pumpOnMills= " + String(pumpOnMillis) + " flowDelay= "+ String(flowDelay));
Serial.println("flaut 10-----Flap closed & pump ON Irms= " + String(Irms) + " flapState= " + String(flapState));
*/
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// FAULT 1 --- low level in reservoir 1 (Cisterna)
if ( levelDp[1] < levelDpLow) {
faultStatus = HIGH;
fault_ID[1] = HIGH;
tft.println("VERY LOW LEVEL IN");
tft.print("Cisterna = ");
tft.print(levelDp[1]);
tft.println(" %");
tft.println(" ");
}

// FAULT 2 --- Low level in reservoir 2 (Caixa)
if (levelDp[2] < levelDpLow ) {
faultStatus = HIGH;
fault_ID[2] = HIGH;
tft.println("VERY LOW LEVEL IN");
tft.print("Caixa Sup = ");
tft.println(levelDp[2]);
tft.println(" %");
tft.println(" ");
}

// FAULT 3 --- Low flow to reservoir 1 (Cisterna)
if ( flow[1] <= flowMin[1] ){
faultStatus = HIGH;
fault_ID[3] = HIGH;
tft.println("LOW FLOW TO CISTERNA");
tft.print("Flow(l/m) = ");
tft.println (flow[1]);
tft.println(" ");
}

// FAULT 4--- UV Lamp Current too low
if ( lampCurrent < lampCurrentLow ){
faultStatus = HIGH;
fault_ID[4] = HIGH;
tft.println("LOW UV LAMP CURRENT");
tft.print("lampCurrent = ");
tft.print (lampCurrent);
tft.println(" A");
tft.println(" ");
}

// FAULT 5 -- Pump on with low level in reservoir 1
if ( Irms > 0.0 && levelDp[1] <= levelDpMin) {
faultStatus = HIGH;
fault_ID[5] = HIGH;
tft.println("PUMP PROTECTION FAILURE");
tft.print("level = ");
tft.print(levelDp[1]);
tft.println(" %");
tft.println(" ");
}

// FAULT 6 ---RF Link Loss //Added in June 2021
// %%% Fault cond always displayed ---- need to debug
// the fuction getRadioData will only be called if a network is available
// So, if there is NO network available we must ALSO set the error condition flag
if ( caixaRxLoss == HIGH || cisternaRxLoss == HIGH )
{
faultStatus = HIGH;
fault_ID[6] = HIGH;
}
if ( caixaRxLoss == HIGH ) {tft.println("CAIXA LINK LOSS"); tft.println(" ");}
if ( cisternaRxLoss ==HIGH ) {tft.println("CIST LINK LOSS"); tft.println(" ");}

// -----This is the beginning of the warnings section-------------------------------

// Change Text colour to yellow
tft.setTextColor(ILI9341_YELLOW);

// FAULT 7 -- Level in Reservoirs 1 or 2 below normal
if ( levelDp[1] <= levelDpWarning || levelDp[2] <= levelDpWarning)
{
if ( fault_ID[1] == LOW && fault_ID[2] == LOW )
{ // check if the levels are NOT in the VERY LOW condition to set Warning
warningStatus = HIGH;
levelWarningStatus = HIGH;
tft.println("CHECK LEVEL");
if (levelDp[1] <= levelDpWarning)
{
tft.print("Cisterna= ");
tft.print(levelDp[1]);
tft.println(" %");
}
if (levelDp[2] <= levelDpWarning)
{
tft.print("Caixa= ");
tft.print(levelDp[2]);
tft.println(" %");
tft.println(" ");
}
}
}
else
levelWarningStatus = LOW;

//FAULT 8 -- Pressure drop very high in Filter
if ( DpFilter > DpFilter_max ){
warningStatus = HIGH;
tft.println("HIGH FILTER DP");
tft.print("DpFilter = ");
tft.println (DpFilter);
tft.println(" ");
}

// FAULT 9 -- low or no flow (after 60 sec) with pump on
if (Irms > 0.0 && flowDelay == LOW && flow[2] <= flowMin[2]){
warningStatus = HIGH;
tft.println("PUMP LOW FLOW FAIL");
tft.print("Flow(l/min) = ");
tft.println(flow[2]);
tft.println(" ");
}

// FAULT 10 -- Flap closed with pump ON
if ( Irms > 0.0 && flapState == LOW){
warningStatus = HIGH;
tft.println("FLAP CLOSED FAILURE");
tft.print("Flow(l/m) = ");
tft.println(flow[2]);
tft.print("Current= ");
tft.print(Irms, 1);
tft.println(" A");
tft.println(" ");
}

// FAULT 11--- Filter Inlet Pressure too low (air in pipe)
// ----This Warning was delete since it makes no sense

// FAULT 12 -- Pump Cavitation ( Pump ON AND (low Pdisch OR low flow to reservoir 2 )
//--- first implementation in JUNE 2021--- check if works before adding to critical faults

if ( Irms > 0.5 && ( Pdisch <= PdischLow || flow[2] <= pumpFlowLow )) {
warningStatus = HIGH;
tft.println("PUMP CAVITATION");
tft.print("Pdisch = ");
tft.println (Pdisch);
tft.print("Vazao(l/m) = ");
tft.println (flow[2]);
tft.println(" ");
}

// Check if there there are faults and/or warnings and turn ON the corresponding LEDs
if ( faultStatus == HIGH) digitalWrite(fault_Led_Pin, HIGH);
else digitalWrite(fault_Led_Pin, LOW);
if ( warningStatus == HIGH )digitalWrite(warning_Led_Pin, HIGH);
else digitalWrite(warning_Led_Pin, LOW);

//If no faults were detected, display message
if (faultStatus == LOW && warningStatus == LOW) {
tft.fillScreen(ILI9341_BLACK); // used in test alarm version
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_GREEN); tft.setTextSize(3);
tft.println("NO FAULTS");
tft.println("DETECTED");
delay(1000); // Added in Rev 3C ***Check if it is required
}
if(serialPrintFlag == HIGH)
{
Serial.println ("---------------------------Fault Status Indicators-------------------------");
Serial.println ("faultStatus= " +String(faultStatus));
Serial.println ("warningStatus= " +String(warningStatus));
}
} //---- end of display_faults()

////////////////////////////////////////////////////////
// beep_Buzzer
///////////////////////////////////////////////////////
void beep_Buzzer(){
t2=millis();
if (t2-t1 >=2000)
{
x=1-x;
t1=millis();
digitalWrite(buzzerPin,x);
if(serialPrintFlag == HIGH)Serial.println ("millis= " +String(millis())+ " x= " + String(x));
}
}

//------------No need to clear data since we added a reception loss failure---------------
/*
//////////////////////////////////////////////////////////////////////////////////
// input_Data()
//////////////////////////////////////////////////////////////////////////////////

// &&&&& USE FOR DEBUGGING ONLY &&&&&&&&&&
// Use this fuction to input data to the receiver to test code

void input_Data(){
Serial.println(" Simulating Operation ");
tft.setTextColor(ILI9341_RED);
tft.setTextSize(3);
tft.print("Simulating Operation");
delay (1000);

// Values that will be used as received
//------Cisterna------------
Serial.println(" Cisterna Data ");
Irms = 0.0; // Pump OFF
flow[1] = 2.0; // Normal Contition
Pdisch = 2.6; // Pump OFF
DpFilter = 100.0; // Norm =100, max= 600
Pinlet = 20.0; // Normal Condition (min= 10.0)
lampCurrent = 0.17; // Lamp ON
rawDp[1] = 200; // Normal Operation (Max=243 , warrning=50%, Low=30%, min= 20%)
flapState = HIGH; // Pump OFF

cisternaRxLoss = LOW; // Cisterna RF Link OK

//-------Caixa-------------
Serial.println(" Caixa Data");
auxVol = 0.0; // Not used for alarms ==>his is the batch volume
flow[2] = 2.1; // min= 2.0
totalLiters = 0.0; // Not used for alarms
dummy2= 0.0; // Not used for alarms
dummy1= 0.0; // Not used for alarms
dummy4= 0.0; // Not used for alarms
rawDp[2] = 170.0; // Normal Operation (Max=187 , warrning=50%, Low=30%, min= 20%)
dummy3 = LOW;

caixaRxLoss = LOW; // Caixa RF Link OK

//??? clearDataFlag = false; // check if required

// Print values used in this simulation
Serial.println ("clearDataFlag= " + String(clearDataFlag));
Serial.println ("levelDp[1]= " + String(levelDp[1]));
Serial.println ("levelDp[2]= " + String(Irms));
Serial.println ("Irms= " + String(Irms));
Serial.println ("rawDp[1]= " + String(rawDp[1]));
Serial.println ("Pdisch= " + String(Pdisch);
Serial.println ("DpFilter= " + String(DpFilter));
Serial.println ("Pinlet= " + String(Pinlet));
Serial.println ("kFilter= " + String(kFilter));
Serial.println ("auxVol= " + String(auxVol));
Serial.println ("flow[2]= " + String(flow[2]));
Serial.println ("totalLiters= " + String(totalLiters));
Serial.println ("lampCurrent= " + String(lampCurrent));

} // This is the end of input_Data

*/

////////////////////////////////////////////////////////////////////////////
// Buzzer_and_Leds()
////////////////////////////////////////////////////////////////////////////

//-----------Check buttons, Light Led's & sound buzzer when required
void Buzzer_and_Leds(){

// Toggle BUZZER ON/OFF and display LED status accordingly

alarm_Button_New = digitalRead(alarm_Toggle_Button_Pin);
if(alarm_Button_Old == 0 && alarm_Button_New == 1)
{
if (buzzer_ON_OFF_Toggle == LOW)
{
digitalWrite(alarm_On_Pin,HIGH);
buzzer_ON_OFF_Toggle = HIGH;
tft.fillScreen(ILI9341_BLACK); // used in test alarm
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_RED); tft.setTextSize(3);
tft.println("BUZZER IS ON");
}
else
{
digitalWrite(alarm_On_Pin, LOW);
buzzer_ON_OFF_Toggle = LOW;
tft.fillScreen(ILI9341_BLACK); // used in test alarm
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_RED); tft.setTextSize(3);
tft.println("BUZZER IS OFF");
}
}
alarm_Button_Old=alarm_Button_New;
delay(dt);

} // ------- this is the end of buzzer & LEDs function

Absolutely the worst strategy, if you like to reliably transmit something.

I think, Marco wanted to get rid of the pesky "transmission failed" errors.

If you use this mode, your sends degenerate to a single send,
without any knowledge about a rececption, write will always signal success.

This will lead to two packets sent as fragments.
A network header is needed in each packet, if you are using RF24Network.

defining DISABLE_FRAGMENTION truncates the actual transmitted payload to 24 bytes
https://nrf24.github.io/RF24Network/RF24Network__config_8h.html

suggests a limit of 24 bytes for non fragmented packets.

I have no experience with the RF24Network, I prefer the native packet interface.

I can not tell, which node this code belongs to,
but that code needlessly uses String, and wastes RAM on normal AVRs.
There are a lot of string constants in the code in other places.

What Arduinos are you using?

Hint: Using ctrl-T in the IDE does a nice formatting and indentation of your code,
which would make it a lot easier to read.

Please edit your post containing the code so that all parts are in their own code tags.

Hi, @rogerio414

Can you please post images of your projects?

Can you please post schematics of your projects?
Images of hand drawn schematics will be fine, include ALL power supplies, component names and pin labels.

Thanks.. Tom... :smiley: :+1: :coffee: :australia:
PS. At this stage you should have schematics drawn, if not t hen reverse engineer your projects.

This comment was added on March2021 becuse my bench setup was freezing randomly.
After I changed that the communication was very reliable

Here are the schematics
Esquema Caixa Sup Arduino Nov 2021.pdf (410.2 KB)
Esquema Cisterna Arduino Nov 2021.pdf (523.1 KB)
Esquema Receptor ESP32 Nov 2021.pdf (524.0 KB)

For some reason I can't use the ''' code ''' to include the receiver code (the last one).
I tried to edit this post many times in different ways but unfortunately the end result is the same.
Can you help me to do that?

I can not understand your question.
would you please expand it ?