• Skip to main content
  • Skip to primary sidebar

RNTLab.com

The Ultimate Shortcut to Learn Electronics and Programming with Open Source Hardware and Software

  • Courses
  • Forum
    • Forum
    • Ask Question
  • Shop
  • Account
  • Blog
  • Login

ESP NOW Millis timing issues

Q&A Forum › Category: ESP32 › ESP NOW Millis timing issues
0 Vote Up Vote Down
Graham Bennett asked 5 years ago

This is my first post , so please be kind.

I have 2 ESP32 dev kit C set up as master and slave.

You can see from the coding below that if any of the input gpios on the master is taken LOW then the corresponding pin on the slave also moves to LOW and triggers an 8 channel relay module which stays on until the pin on the master moves back to HIGH and the Pin on the slave moves HIGH.

My challenge is for the Pin on the slave to only move LOW for 100ms and return to HIGH even though the master continues to send LOW until the pin on the master moves back to HIGH again.

The Pins on the master are taken low by PIR sensors which can be active for as long as they are triggered, but the corresponding pin on the slave must only go LOW for 100ms.

I have tried numerous combinations of millis() based loops on the slave to try and manipulate the pin data received ,but nothing seems to work.

Does anyone have any ideas on how this can be achieved.?

Graham

// This sketch sends pin data to a single slave (S1) at 
//0x24, 0x6f, 0x28, 0x7c, 0x7a, 0x10
//The Gpios in the array 16,17,18,19,21,22,23,25
//Are set to PULL UP internally and when any of these pins are taken LOW
//The corresponding pin on the slave is taken from HI to LOW to activate
//the relay board which needs a LOW input to trigger
// The sketch also provides for a local relay attached to the Master to be triggered at the same time as the slave
//Libs for espnow and wifi
#include <esp_now.h>
#include <WiFi.h>
//Channel used in the connection
#define CHANNEL 1
//Gpios that we are going to read (digitalRead) and send to the Slaves
//It's important that the Slave 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};
//Sets up the array for the local relays
uint8_t relayPins[]={2,4,5,13,26,27,32,33};
//In the setup function we'll calculate the gpio and relay count and put in these variables,
//so we don't need to change this variable everytime we change
//the gpios array total size, everything will be calculated automatically
//in setup function
int gpioCount;
int relayCount;
//Slaves Mac Addresses that will receive data from the Master
//If you want to send data to all Slaves, use only the broadcast address {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}.
//If you want to send data to specific Slaves, put their Mac Addresses seperated with comma (use WiFi.macAddress())
//to find out the Mac Address of the ESPs while in STATION MODE)
uint8_t macSlaves[][6] = {
//To send to specific Slaves MAC addresses seperated by a comma eg
// {0x24, 0x62, 0xAB, 0xF3, 0xC3, 0xC8}, {0x24, 0x0A, 0xC4, 0x0E, 0x4E, 0xC3}
{0x24, 0x6f, 0x28, 0x7c, 0x7a, 0x10}
//Or to send to all Slaves
//{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
};
void setup() {
Serial.begin(115200);
//Calculation of gpio and local relay array sizes:
//sizeof(gpios) returns how many bytes "gpios" array points to.
//Elements in this array are of type uint8_t.
//sizeof(uint8_t) returns how many bytes uint8_t type has.
//Therefore if we want to know how many gpios and relays there are,
//we divide the total byte count of the array by how many bytes
//each element has.
gpioCount = sizeof(gpios)/sizeof(uint8_t);
relayCount = sizeof(relayPins)/sizeof(uint8_t);
//Puts ESP in STATION MODE
WiFi.mode(WIFI_STA);
//Shows on the Serial Monitor the STATION MODE Mac Address of this ESP
Serial.print("Mac Address in Station: "); 
Serial.println(WiFi.macAddress());
//Calls the function that will initialize the ESP-NOW protocol
InitESPNow();
//Calculation of the size of the slaves array:
//sizeof(macSlaves) returns how many bytes the macSlaves array points to.
//Each Slave Mac Address is an array with 6 elements.
//If each element is sizeof(uint8_t) bytes
//then the total of slaves is the division of the total amount of bytes
//by how many elements each MAc Address has
//by how much bytes each element has.
int slavesCount = sizeof(macSlaves)/6/sizeof(uint8_t);
//For each Slave
for(int i=0; i<slavesCount; i++){
//We create a variable that will store the slave information
esp_now_peer_info_t slave;
//We inform the channel
slave.channel = CHANNEL;
// 0 not to use encryption or 1 to use
slave.encrypt = 0;
//Copies the array address to the structure 
memcpy(slave.peer_addr, macSlaves[i], sizeof(macSlaves[i]));
//Add the slave
esp_now_add_peer(&slave);
}
//Registers the callback that will give us feedback about the data sent
//The function that will be executed is called OnDataSent
esp_now_register_send_cb(OnDataSent);

//for each pin in the gpio array
for(int i=0; i<gpioCount; i++){
//Set the GPIO mode to INPUT puuled up by internal resistors
pinMode(gpios[i], INPUT_PULLUP);

// Set the local relay pins to OUTPUT. These need to be taken LOW to activate the relays
for(int i = 0; i < relayCount; i++)
pinMode(relayPins[i], OUTPUT);
}
//Call the send function
send();
}
void InitESPNow() {
//If the initialization was successful
if (esp_now_init() == ESP_OK) {
Serial.println("ESPNow Init Success");
}
//If there was an initialisation error
else {
Serial.println("ESPNow Init Failed");
ESP.restart();
}
}
//Function that will read the gpios and send
//the read values to the other ESP Slaves
void send(){
//Array that will store the read values
uint8_t values[gpioCount];
//For each gpio
for(int i=0; i<gpioCount; i++){
//Reads the value (HIGH or LOW) of the gpio
//and stores the value on the array
values[i] = digitalRead(gpios[i]);
}
//To broadcast to all the ESPs use {0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF} but to send
//to a specific ESP call the function
//esp_now_send for each Mac Address with its parameters as below.

uint8_t broadcast[] = {0x24, 0x6f, 0x28, 0x7c, 0x7a, 0x10};
esp_err_t result = esp_now_send(broadcast, (uint8_t*) &values, sizeof(values));
Serial.print("Send Status: ");
//If successful
if (result == ESP_OK) {
Serial.println("Success");
}
//If send failed
else {
Serial.println("Error");
}
}
//Callback function that gives us feedback about the sent data
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
char macStr[18];
//Copies the receiver 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 the Mac address to which the message has been sent on Serial Monitor
Serial.print("Sent to: ");
Serial.println(macStr);
//Shows the status of the sent message and whether succful or not.
Serial.print("Status: ");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
//print gpioCount for ref
//Serial.print("gpioCount: ");
//Serial.println(gpioCount);

//print sizeof gpios for ref
//Serial.print("sizeof(gpios): ");
//Serial.println(sizeof (gpios));

//print sizeof uint8_t data for ref
//Serial.print("sizeof(uint8_t): ");
//Serial.println(sizeof (uint8_t));

//Send new data
send();
}
//In this example we are going to using the broadcast address {0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF}
//to send the values to all Slaves.
//If you want to send to a specific Slave, you have to put its Mac Address on macAddr.
//If you want to send to more then one specific Slave you will need to create
//a "for loop" and call esp_now_send for each mac address on the macSlaves array uint8_t macAddr[] = {0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF};
//esp_err_t result = esp_now_send(macAddr, (uint8_t*) &values, sizeof(values)); Serial.print("Send Status: ");
//If it was successful if (result == ESP_OK) { Serial.println("Success"); }
//if it failed else { Serial.println("Error"); } }
//Callback function that gives us feedback about the sent data
//void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { char macStr[18]; //Copies the receiver 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 Serial.print("Sent to: "); Serial.println(macStr); //Prints if it was successful or not Serial.print("Status: "); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail"); //Sends again send(); }
//The Loop function is only used to operate the local relays in line with GPIO inputs.
//Every time we receive feedback about the last sent data, //we'll be calling the send function again, //therefore the data is always being sent in Setup

void loop() {
//if an input GPIO is taken low, take the corresponding local relay low.
for(int i = 0; i < gpioCount; i++) {
int buttonState = digitalRead(gpios[i]);
digitalWrite(relayPins[i], buttonState);

}
}

Code:

#include <esp_now.h>
#include <WiFi.h>
uint8_t gpios[] = {16,17,18,19,21,22,23,25};
int gpioCount;
void setup() {
Serial.begin(115200);

gpioCount = sizeof(gpios)/sizeof(uint8_t);
WiFi.mode(WIFI_STA);

Serial.print("Mac Address in Station: ");
Serial.println(WiFi.macAddress());
InitESPNow();
esp_now_register_recv_cb(OnDataRecv);
for(int i=0; i<gpioCount; i++){
//Sets the gpois pinmode to OUTPUT
pinMode(gpios[i], OUTPUT);
}
}
void InitESPNow() {
if (esp_now_init() == ESP_OK) {
Serial.println("ESPNow Init Success");
}

else {
Serial.println("ESPNow Init Failed");
ESP.restart();
}
}

void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) {
char macStr[18];
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]);
Serial.print("Received from: ");
Serial.println(macStr);
Serial.println("");

for(int i=0; i<gpioCount; i++){

digitalWrite(gpios[i], data[i]);

}
}
void loop() {
}
6 Answers
0 Vote Up Vote Down
Steve Mercer answered 5 years ago

You may want to use interrupts. There is a great article series over at Adafruit.

0 Vote Up Vote Down
Graham Bennett answered 5 years ago

Thanks Steve
The fundamental issue seems to be that the slave is continually receiving a stream of HIGHs from the master,
I’m really unsure as to whether I deal with the timing on the Slave or whether to get the master to send short duration LOWs that are mirrored on the slave
I sense that the master will be the place to start.
I really and stuck on this one so any help is appreciated.

0 Vote Up Vote Down
Steve Mercer answered 5 years ago

The issue appears to be at the slave end so that is where I would put it. All you are doing is creating a state machine. 

0 Vote Up Vote Down
Sara Santos Staff answered 5 years ago

Hi Graham.

In the sender side:

In your ESP-NOW sender board, you have a PIR motion sensor attached. So, when it detects movement, it will send a HIGH signal. In order to only send one message when it goes from LOW to HIGH, you should use interrupts.

In the “Learn ESP32 with Arduino IDE eBook” there’s a section about a interrupts (page 76).

For example, imagine your PIR sensor is on GPIO 27

const int motionSensor = 27;

Create a boolean variable to indicate if it is time to send a high signal:

bool sendMessage = false;

Then, set hte PIR as interrupt in the setup()

// PIR Motion Sensor mode INPUT_PULLUP
pinMode(motionSensor, INPUT_PULLUP);
// Set motionSensor pin as interrupt, assign interrupt function and set RISING mode
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);

Before the setup(), you must declare the detectsMovement callback function. When that function is called, set the sendMessage function to true.

void IRAM_ATTR detectsMovement() {
  Serial.println("MOTION DETECTED!!!");
  sendMessage = true;
}

Then, in the loop(), you can check the state of the sendMessage variable, and if it is true, send the message via ESP-NOW

void loop(){
  if (sendMessage==true){
    SEND WHATEVER MESSAGE YOU WANT USING ESP-NOW 
    sendMessage = false; //set the sendMessage to false, so that it doesn't send any additional messages.
  }
}

In your ESP-NOW receiver board, when it receives a message from the sender board, you can use a flag variable in the OnDataRecv function that will change value if it is time to send a pulse to the output.

Then, in the loop() check the state of that variable and send the pulse to the output. Because you just want to set it to high for 100ms, most of the times, there is not harm if you use a delay:

digitalWrite(led, HIGH);
delay(100);
digitalWrite(led, LOW);

I hope this helps.
Regards,
Sara

0 Vote Up Vote Down
Graham Bennett answered 5 years ago

Thanks for that Sara,
this will take me a bit of time to adsorb, but one immediate question

I have 8 PIRs in the array on gpios 16,17,18,19,21,22,23,25
Will I need 8 interrupts to be setup or can the interrupts be linked directly to the 8 pins on the array.
Any PIR can be activated at any time, sometimes two at the same time.?
Graham
0 Vote Up Vote Down
Sara Santos Staff answered 5 years ago

Hi.
I think you need multiple interrupts. One for each pin.
Regards,
Sara

Primary Sidebar

Login to Ask or Answer Questions

This Forum is private and it’s only available for members enrolled in our Courses.

Login »

Latest Course Updates

  • [eBook Updated] Learn Raspberry Pi Pico/Pico W with MicroPython eBook – Version 1.2 May 26, 2025
  • [New Edition] Build ESP32-CAM Projects eBook – 2nd Edition April 16, 2025

You must be logged in to view this content.

Contact Support - Refunds - Privacy - Terms - MakerAdvisor.com - Member Login

Copyright © 2013-2025 · RandomNerdTutorials.com · All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.