ESPNOW Master /Slave programming

I have been working up a project for a few weeks (as I'm a complete novice).
Two ESP32 development boards talking to each other as "Master and Slave"
The Master sends data to the slave consisting of the state of 8 GPIO PINS and the Slave uses the data received to operate an 8 channel relay module which triggers when a pin in taken LOW.
All is working well and any change to a GPIO on the Master is reflected on the corresponding Pin on the slave.
The Pins on the slave mirror exactly the data sent from the master so if a pin in taken low for 5mins on the master, the slave pin is low for 5 mins.
I now want to limit the length of time that the slave pin is operative.
eg if the master is held low for 5 mins the slave only goes low for 50ms and then returns to a HIGH state waiting for the next data to arrive from the master.
Can anyone suggest how I might approach this?
The slave code is reproduced here.

//Libs for espnow and wifi
#include <esp_now.h>
#include <WiFi.h>
// THIS SLAVE RECEIVES DATA FROM an MASTER ESP32 TRANSMITTING GPIO values
//When the gpio pins values are received from the Master, they are written 
//to the gpios on this slave as outputs.


uint8_t gpios[] = {16,17,18,19,21,22,23,25};

// the gpio count is saved  in a variable called,
//gpioCount ( declared below)

int gpioCount;

void setup() {
  Serial.begin(115200);
  
  //Calculation of gpio array size:
 

  gpioCount = sizeof(gpios)/sizeof(uint8_t);

  //Puts ESP in STATION MODE
  WiFi.mode(WIFI_STA);
  
  //Prints out on the Serial Monitor the STATION MODE Mac Address of this ESP32

  Serial.print("Mac Address in Station: "); 
  Serial.println(WiFi.macAddress());

  //Calls the function that will initialize the ESP-NOW protocol
  InitESPNow();

  //Registers the callback function, OnDataRecv, that will be executed when 
  //this Slave receives data from the Master.
  

  esp_now_register_recv_cb(OnDataRecv);

  //For each gpio on gpios array 
  for(int i=0; i<gpioCount; i++){
    //Sets the gpois pinmode to OUTPUT //These gpios will drive the relays on an
    //8 channel 3.3v relay module
    pinMode(gpios[i], OUTPUT);
  }
}

void InitESPNow() {
  //If the initialization was successful 
  if (esp_now_init() == ESP_OK) {
    Serial.println("ESPNow Init Success");
  }
  //If there was an initialistaion error 
  else {
    Serial.println("ESPNow Init Failed");
    ESP.restart();
  }
}

//Callback function that tells us when data from Master is received
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) {
  char macStr[18];
  //Copies the sender Mac Address to a string
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  //Prints it on Serial Monitor and shows the origin of the message
  Serial.print("Received from: "); 
  Serial.println(macStr);
  Serial.println("");

  //For each gpio pin
  for(int i=0; i<gpioCount; i++){
    //Sets its output on the slave to match the received value from the master
    digitalWrite(gpios[i], data[i]);
   // The gpios on the master are set to be INPUT and Pulled HIGH
   //each time any pin in the array gpio is taken LOW the corrsponding gpio on this slave
   // is taken LOW to match and the corresponding relay is triggered and held LOW until the 
   //pin on the master is taken HIGH again. 
     
    }
     
}

//Everytime something comes from Master 
//the OnDataRecv function is executed automatically 
//because it is added as a callback using esp_now_register_recv_cb 

void loop() {
   

}

You have accomplished a lot, you will find it is relatively easy to accomplish what you want. Do some reading on the arduino millis(), there will be a lot of examples and your skill level will improve. It will also sharpen your skills on the "if" statement.

Thanks for the pointer. I have been trying to read up on the use of millis but can’t find much in the way of examples of millis in conjunction with reading data from arrays. Any hints would be appreciated.

eg if the master is held low for 5 mins the slave only goes low for 50ms and then returns to a HIGH state waiting for the next data to arrive from the master.

do I understand right. Master is sending low for 5 minutes.
example:
12:00:00 master send low
12:00:01 master send low
12:00:02 master send low
12:00:03 master send low
....
12:04:59 master send low
12:05:00 master send low
that's a "master is held low for 5 mins"
and the other part of your description is
"the slave only goes low for 50ms and then returns to a HIGH state waiting for the next data to arrive from the master"

12:00:00,000 low received by slave slave LOW
12:00:00,001 low received by slave slave LOW
12:00:00,002 low received by slave slave LOW
...
12:00:00,049 low received by slave slave LOW
12:00:00,050 low received by slave slave HIGH

12:00:01,000 low received by slave slave LOW
12:00:01,001 low received by slave slave LOW
12:00:01,002 low received by slave slave LOW
...
12:00:01,049 low received by slave slave LOW
12:00:01,050 low received by slave slave HIGH

12:00:02,000 low received by slave slave LOW
12:00:02,001 low received by slave slave LOW
12:00:02,002 low received by slave slave LOW
...
12:00:02,049 low received by slave slave LOW
12:00:02,050 low received by slave slave HIGH

that is in exaple-numbers what you have described so far. I'm unsiure if this is what you really mean.
So please confirm or correct with example-numbers or with a picture that has a timing-diagram
what the functionality shall bee. In this diagram the master-logig-level (LOW/HIGH) and the slaves logic-level (LOW/HIGH) shall be seen with the timing-correlation to each other.

best regards Stefan

12:00:00,050 low received by slave slave LOW

Hi Stefan

Your example is correct but it need not be 5 Mins exactly, that was just an example.
Here is another description.

A PIR sensor connected to the master takes pins on the Master from HIGH to LOW when activated.

Present situation:

When a pin on the master is taken from HIGH to LOW, the same pin no. on the slave is taken from HIGH to LOW for as long as the pin on the master remains LOW (all the time the PIR remains activated)

When the PIR sensor deactivates, that pin on the master moves back from LOW to HIGH , the same pin on the slave moves back from LOW to HIGH. They are like identical twins.

What I need is when a pin on the master is taken from HIGH to LOW , the same pin no. on the slave will move from HIGH to LOW , BUT ONLY FOR 50ms before returning to HIGH on the slave, even though the pin on the master continues to be held LOW when the PIR remains activated.

The next time the PIR activates the pin on the Master will move from HIGH to LOW and whole sequence will repeat itself.

This is to be true for each of the 8 PINs in the array gpios in my code.

Result is that each time the PIR is activated the pin on the slave will be taken LOW momentarily (50ms)

I hope you can follow this.
Graham

If you want a pin to go LOW for a period of time you need to save the value of millis() when the pin changes from HIGH to LOW and then check for when the desired interval has elapsed with a few lines of code like this

if (millis() - timeLowStarted >= interval) {
   digitalWrite(pin, HIGH);
}

...R

PS ... your problem seems to have nothing to do with your Title.

Thanks Robin,
I used the Millis approach as suggested and managed to get the Pin to go from LOW to high for 50ms but unfortunately because the Slave is continuously reading in data from the Master (as per the code I submitted) the PIN immediately is sent LOW again in response to a PIR on the master sending a LOW signal to the slave.
So I get a chattering relay which is driven by the GPIO on the slave, 50ms ON, OFF, Immediately ON for 50ms, OFF,Immediately ON for 50ms etc

Maybe the Title of my problem has turned out to be appropriate as its more to do with how ESP NOW functions.
Thanks for your help.

on your receiver detect the HIGH-LOW-transistion start 50ms-timer
if (**!**Relais_switched) turn on relay

if 50 ms are over and set a boolean-flag "Relais_switched" to true

if (Relais_switched) do nothing

only if a LOW-HIGH-transition occurs set the flag-variable Relais_switched back to false

if receiver detects HIGH for this channel set Relais_switched = false

best regards Stefan

I'm not sure I understand what is written in Reply #7 but I think the following is what it is intended to say.

On the receiver you need to check the incoming value to see if it has changed. I don't know what values are being sent but supposing that 1 is sent when the PIR is On and 0 is sent when it is Off. Then your receiver needs to check when the value changes from 0 to 1 and then switch on the output pin LOW (for 50 msecs).

You need code on your receiver something like this pseudo code

if (new data is available) {
   previousValue = value
   value = new value from wireless
   if (value == 1 and previousValue == 0) {
      set Pin LOW
      // etc
   }
}

...R

Thanks,

after a couple of days of learning about millis and bool I think i'm on the brink of succeeding.

But I have syntax (and probably other errors) which wont let the code compile.
This is what I have added to the code I original posted.
Declared the following global variables

       unsigned long currentMillis = millis(); // get current time
                unsigned long Relay_onTime; // millis() returns an unsigned long.
                unsigned long interval=25; // the time we need to wait
                bool Relay_on=true;

Added this code in the Setup immediately below the section which reads in the data from the master (0 equals relay on, 1=relay off

//For each gpio pin
  for(int i=0; i<gpioCount; i++){
    //Sets its output on the slave to match the received value from the master
    digitalWrite(gpios[i], data[i]);
  
    if(digitalRead(gpios[i] == LOW));{
      Relay_onTime[i]=millis();
      Relay_on[i] = true;
    }
      if (Relay_on)[i];
      {
   if (currentMillis[i] - Relay_onTime[i]) < interval){
    digitalWrite(gpios[i], LOW);
    }
    else {
      Relay_on[i]=false;
      digitalWrite(gpios[i], HIGH);
    }
      }
    previousMillis = millis();

The error I'm getting is

invalid types 'long unsigned int[long unsigned int]' for array subscript

Any help appreciated
I have also renamed the problem Switching relays on and off after period of time elapsed

sirocco1234:
The error I'm getting is

invalid types 'long unsigned int[long unsigned int]' for array subscript

Any help appreciated

You need to post the complete program and enough of the error message so we can see what line of the program it refers to.

...R

PS ... you need to edit the Title in your Original Post for it to apply generally

OK here is the full code for the slave including my latest additions.

//Libs for espnow and wifi
#include <esp_now.h>
#include <WiFi.h>

//Gpios we'll write the values received from the Master
//It's important that the Master source code has this same array
//with the same gpios in the same order

uint8_t gpios[] = {16,17,18,19,21,22,23,25};

//In the setup function we'll calculate the gpio count and put in this variable,
//gpioCount so we don't need to change this variable everytime we change
//the gpios array total size, everything will be calculated automatically
//on setup function

int gpioCount;
   // Timing variable declarations           
                unsigned long currentMillis = millis(); // get current time
                unsigned long Relay_onTime; // millis() returns an unsigned long.
                unsigned long interval=100; // the time we need to wait
                bool Relay_on=true;

void setup() {
  Serial.begin(115200);
  
  
  
  //Calculation of gpio array size:
   gpioCount = sizeof(gpios)/sizeof(uint8_t);

//Puts ESP32 in STATION MODE
  WiFi.mode(WIFI_STA);
  
  //Shows on the Serial Monitor the STATION MODE Mac Address of this ESP32
 
  Serial.print("Mac Address in Station: "); 
  Serial.println(WiFi.macAddress());

  //Calls the function that will initialize the ESP-NOW protocol
  InitESPNow();

  //Registers the callback function that will be executed when 
  //this Slave receives data from the Master.
  //The function in this case is called OnDataRecv

  esp_now_register_recv_cb(OnDataRecv);

  //For each gpio on gpios array
  for(int i=0; i<gpioCount; i++){
    //Sets the gpois pinmode to OUTPUT to drive the 8 channel relay module 
    pinMode(gpios[i], OUTPUT);
  }
}

void InitESPNow() {
  //If the initialization was successful 
  if (esp_now_init() == ESP_OK) {
    Serial.println("ESPNow Init Success");
  }
  //If there was an initialistaion error 
  else {
    Serial.println("ESPNow Init Failed");
    ESP.restart();
  }
}

//Callback function that tells us when data from Master is received
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) {
  char macStr[18];
  //Copies the sender Mac Address to a string
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  //Prints it on Serial Monitor and shows the origin of the message
  Serial.print("Received from: "); 
  Serial.println(macStr);
  Serial.println("");

  
  //For each gpio pin
  for(int i=0; i<gpioCount; i++){
    //Sets its output on the slave to match the received value from the master WHEN HIGH received Relay Off, LOW  Relay ON
    //  PIRs on the master produce the HIGH/LOW sent to the slave 
    digitalWrite(gpios[i], data[i]);
  // This section tests if the data received from the master is LOW or HIGH
  //and if LOW switches on a relay for 50ms before moving high again.
  //it needs to remain High until the PIR on the master deactivates which results in the data received from the Master being a HIGH again
    if(digitalRead(gpios[i] == LOW));{
      Relay_onTime[i]=millis();
      Relay_on[i] = true;
    }
      if (Relay_on)[i];
      {
   if (currentMillis[i] - Relay_onTime[i]) < interval){
    digitalWrite(gpios[i], LOW);
    }
    else {
      Relay_on[i]=false;
      digitalWrite(gpios[i], HIGH);
    }
      }
    previousMillis = millis();
 
 


  
  
   

 
 
 //Serial.print("PIN STATES: ");Serial.print(gpios[i]);
 //Serial.println(" - ");Serial.print(data[i]);
 //Serial.println(" ");
 
  }
}

//Nothing to do in  the loop.
//Everytime something comes from Master //the OnDataRecv function is executed automatically //because is added as callback using esp_now_register_recv_cb 
void loop() {
 

}

I have attached the error message as a notepad txt file.

ESP32 slave Erro rmsg.txt (41.7 KB)

This is the part of the error message that identifies the line in the code

sketch_oct06c:88:21: error: invalid types 'long unsigned int[int]' for array subscript

       Relay_onTime[i]=millis();

                     ^

sketch_oct06c:89:17: error: invalid types 'bool[int]' for array subscript

       Relay_on[i] = true;

...R

You have these two lines of code

      Relay_onTime[i]=millis();
      Relay_on[i] = true;

Neither of those variables is an array so you cannot use the [ i ] notation with them.

What are you trying to do?

...R

Working from this point in the code

//For each gpio pin
for(int i=0; i<gpioCount; i++){

//Sets its output on the slave to match the received value from the master WHEN HIGH received Relay Off, //LOW Relay ON -- PIRs on the master produce the HIGH/LOW sent to the slave

digitalWrite(gpios_, data*);*_
I am trying to check the values written to array gpios

if the values = HIGH -- do nothing as the PIRs on the master are inactive and a stream of HIGHs are being sent by the master and received by the slave.
if any of the values in the array = LOW --one or more PIRs have been activated causing LOWs to be sent from master for as long as the PIRs remain activated
in this case these LOW signals cause relays to be switched on.
I need any relay which has been switched on to remain on for 50ms and then be switched off by writing its gpio value HIGH and for this to remain HIGH until the PIR is deactivated and the master starts sending HIGHs again.
The coding I have added is my attempt to implement your previously suggested pseodocode.
I have obviously failed on this occasion!

In this piece of code

uint8_t gpios[] = {16,17,18,19,21,22,23,25};

//In the setup function we'll calculate the gpio count and put in this variable,
//gpioCount so we don't need to change this variable everytime we change
//the gpios array total size, everything will be calculated automatically
//on setup function

int gpioCount;
   // Timing variable declarations           
unsigned long currentMillis = millis(); // get current time
unsigned long Relay_onTime; // millis() returns an unsigned long.
unsigned long interval=100; // the time we need to wait
bool Relay_on=true;

you have 8 elements in the array gpios[]

If you want 8 separate relays to match those gpios[] then you you probably need to change these lines to

unsigned long Relay_onTime[8]; 
bool Relay_on[8] = {true, true, true, true, true, true, true, true};

Then they should not cause the error on line 88

But I'm only guessing wildly at what you really want to happen.

...R

Thanks for your help but I need to go back to the drawing board.
I have got it going but very erratically I think speed at which ESP32 executes instructions is interferring with the for , if else processes in the code. I think I need to allocate different gpio pins for the relays .