Use ESP NOW and WiFi simultaneously on ESP32

Here are the sample programs promised in reply #10.

This 'Initiator' program sends ESP-NOW data to the other programs.
In this example the data is information about the state of a push-button switch.

// use ESP-NOW to send input level information to another device
//   input can be from switch, PIR sensor, light sensor, etc.

// Compiled using Arduino 1.8.19, and ESP32 v2.0.2
// Compiled for board: ESP32 Dev Module
//   (but running on an AI Thinker ESP-CAM since I have a bunch of them)

// ...... program information
char programName[] = "InitiatorA_22A";
char versionNumber[] = "v01";
char programDate[] = "2022-09-25";
char programAuthor[] = "Donald Weiman";

// ...... hardware connections
//  Push-button switch between Vcc and GPIO13
//    or 
//  HC-SR501 PIR Motion Detector module connected to GPIO13

// ...... required libraries
#include <esp_now.h>
#include <WiFi.h>

// ...... esp-now
// remote MAC Address
uint8_t responderAddress[] = {0x34, 0x94, 0x54, 0x24, 0x3D, 0xE0};

// specify where to store information about the other devices
//   NOTE: this structure is defined in 'esp_now.h'
esp_now_peer_info_t responderInfo;

// define data structure for transmitted data
struct s_message {
    int i_id;
    int i_count; 
    char c_level[20];
} ;

// create structured data object
s_message s_switchInfo;          // to responder

// ...... switch
const byte inputPin = 13;
int oldValue = 0;
bool newValueFlag = false;
int lowCount = 0;
int highCount = 0;

// ...... misc
char thisDeviceMac[20] ;            // Mac address for this device (for info only)
int unitId = 102;                   // arbitrary 'unit id'

//  ....................................................................................
void setup() {
  Serial.begin(115200);
  delay(1000);

// ...... display sign-on message
  Serial.println();
  Serial.println();   
  Serial.print(programName);
  Serial.print(" ");
  Serial.print(versionNumber);
  Serial.print("   ");
  Serial.print(programDate);
  Serial.print("   ");
  Serial.print(programAuthor); 
  Serial.print("\n");

// ...... display MAC address (of this device)
  WiFi.macAddress().toCharArray(thisDeviceMac, 20);
  Serial.printf("\nMAC address: %s", thisDeviceMac);
  
// ...... switch
  pinMode(inputPin, INPUT_PULLDOWN);           // active HIGH

// ...... WiFi
  WiFi.mode(WIFI_STA);
  
// ...... esp-now 
  // initalize ESP-NOW 
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }  

  // register callback function
  esp_now_register_send_cb(OnDataSent);  

  // register peer (remote device)
  //   put peer information into the 'responderInfo' data structure
  memcpy(responderInfo.peer_addr, responderAddress, 6);      // address has 6 bytes
  responderInfo.channel = 0;                                 // use current channel 
  responderInfo.encrypt = false;                             // don't encrypt

  // add peer to paired device list 
  if (esp_now_add_peer(&responderInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  }

// ...... end of setup
  Serial.print("\nsetup complete\n");  
}

//  ....................................................................................
void loop() {

  checkInputLevel();

  // send message if appropriate
  if(newValueFlag == true) {
    newValueFlag = false; 
    if(esp_now_send(responderAddress, (uint8_t *) &s_switchInfo, sizeof(s_switchInfo)) != ESP_OK) {
      Serial.println("Failed to send the data");    
    }
  }
 
  // do something else ...
  
}

//  ....................................................................................
void OnDataSent(const uint8_t *macAddr, esp_now_send_status_t status) {
  // callback when data is sent
  Serial.print("\nMessage sent");
    
  // display MAC of where the data was sent
 char macStr[25];
  snprintf(macStr, sizeof(macStr)," to %02X:%02X:%02X:%02X:%02X:%02X",
            macAddr[0], macAddr[1], macAddr[2], 
             macAddr[3], macAddr[4], macAddr[5]);
  Serial.print(macStr);

  // display the status returned by the other device
  Serial.print(", status: ");
  Serial.print(status == ESP_NOW_SEND_SUCCESS ? "OK" : "Fail");

  // display the data that was sent 
  displayTransmittedData();  
}

//  ....................................................................................
void displayTransmittedData() {
// desired data is in 's_switchInfo' structure
char dataStr[50];
  snprintf(dataStr, sizeof(dataStr), "\n Device number %u, Level %s, Count %u ",
            s_switchInfo.i_id, s_switchInfo.c_level, s_switchInfo.i_count);

  Serial.print(dataStr);
  
}

//  ....................................................................................
void checkInputLevel() {
  int value = digitalRead(inputPin);
  if (value != oldValue) {
    newValueFlag = true;  
    oldValue = value; 
      
    if(value == 1) {
      Serial.print("\n--> level has gone high");
      ++highCount;
      s_switchInfo.i_id = unitId;
      s_switchInfo.i_count = highCount;
      strcpy(s_switchInfo.c_level, "HIGH");
    }
    else {
    if(value == 0)
      Serial.print("\n--> level has gone low");
      ++lowCount;  
      s_switchInfo.i_id = unitId;
      s_switchInfo.i_count = lowCount;
      strcpy(s_switchInfo.c_level, "LOW");
    }
    delay(10);                                          // crude debounce
  }
}

//  ....................................................................................

This 'Responder' program acts upon the data sent by the 'Initiator' program.
In this example it activates some LEDs.

// use ESP-NOW to do something based on a message from another device
//   in this case just light an LED

// Compiled using Arduino 1.8.19, and ESP32 v2.0.2
// Compiled for board: ESP32 Dev Module
//   (but running on an AI Thinker ESP-CAM since I have a bunch of them)

// ...... program information
char programName[] = "ResponderA_22A";
char versionNumber[] = "v01";
char programDate[] = "2022-09-25";
char programAuthor[] = "Donald Weiman";

// ...... required libraries
#include <esp_now.h>
#include <WiFi.h>

// define data structure for received data
struct s_message {
    int i_id;
    int i_count; 
    char c_level[20];
} ;

// create structured data object
s_message s_switchInfo;             // from initiator

// ...... LED
const byte flashLedPin = 4;         // white led on AI Thinker ESP-CAM board
#define flashLedOff 0               // active high

const byte builtinLedPin = 33;      // red led on AI Thinker ESP-CAM board
#define builtinLedOff 1             // active low

// ...... misc
char thisDeviceMac[20] ;            // Mac address for this device (for info only)
bool newDataFlag = false;

//  ....................................................................................
void setup() {
  Serial.begin(115200);
  delay(1000);

// ...... display sign-on message
  Serial.println();
  Serial.println();   
  Serial.print(programName);
  Serial.print(" ");
  Serial.print(versionNumber);
  Serial.print("   ");
  Serial.print(programDate);
  Serial.print("   ");
  Serial.print(programAuthor); 
  Serial.print("\n");

// ...... display MAC address (of this device)
  WiFi.macAddress().toCharArray(thisDeviceMac, 20);
  Serial.printf("\nMAC address: %s", thisDeviceMac);

// ...... WiFi
  WiFi.mode(WIFI_STA);
  
// ...... esp-now 
  // initalize ESP-NOW 
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }  

  // register callback function
  esp_now_register_recv_cb(OnDataRecv); 

// ...... LEDs
  pinMode(flashLedPin, OUTPUT);           // turn LEDs off
  digitalWrite(flashLedPin, flashLedOff);
  
  pinMode(builtinLedPin, OUTPUT);
  digitalWrite(builtinLedPin, builtinLedOff);
  
// ...... end of setup
  Serial.print("\nsetup complete\n");
}

//  ....................................................................................
void loop() {

  // act on received data if appropriate
  if(newDataFlag == true) {
    newDataFlag = false; 
    // act on received data
    if(strcmp (s_switchInfo.c_level, "HIGH") == 0) {
      digitalWrite(flashLedPin, !flashLedOff);
      digitalWrite(builtinLedPin, builtinLedOff);      
    } else {
      digitalWrite(flashLedPin, flashLedOff);
      digitalWrite(builtinLedPin, !builtinLedOff);
    } 
  } 

  // do something else ...

}

//  ....................................................................................
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  // extract the received data
  char receivedData[100];
  memcpy(&s_switchInfo, incomingData, sizeof(s_switchInfo));

  // deal with received data
  newDataFlag = true;           // handled in loop()

  // display received data - (interesting, but not essential)
  char recvStr[50];  
  snprintf(recvStr, sizeof(recvStr), "\n %u  %u  %s",
           s_switchInfo.i_id, s_switchInfo.i_count, s_switchInfo.c_level);
  Serial.print(recvStr);
  
  // display MAC of sending device - (interesting, but not essential)
  char macStr[20];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.print("   Received from ");
  Serial.print(macStr);
} 

//  ....................................................................................

This 'Gateway' program is an expansion of the 'Responder' program.
In addition to activating the LEDs it also obtains a UNIX timestamp from an internet NTP server and displays that timestamp on the serial monitor.
I have reduced the time between NTP updates from the default 3600 seconds to 120 seconds so that it is easier to see that the WiFi capability is maintained along with the ESP-NOW capability.

// use ESP-NOW to do something based on a message from another device
//   and also connect to WiFi to display a web page, deal with MQTT etc
//     in this case just light an LED and get UNIX timestamp from an NTP server

// NOTE: WiFi router must be on Channel 1 for this simple version to work

// Compiled using Arduino 1.8.19, and ESP32 v2.0.2
// Compiled for board: ESP32 Dev Module
//   (but running on an AI Thinker ESP-CAM since I have a bunch of them)

// Based on "ResponderA_22A.ino v01

// ...... program information
char programName[] = "ResponderA_22A";
char versionNumber[] = "v01";
char programDate[] = "2022-09-25";
char programAuthor[] = "Donald Weiman";

// ...... required libraries
#include <esp_now.h>
#include <WiFi.h>
#include "esp_sntp.h"               // ntp

// define data structure for received data
struct s_message {
    int i_id;
    int i_count; 
    char c_level[20];
} ;

// create structured data object
s_message s_switchInfo;             // from initiator

// ...... LED
const byte flashLedPin = 4;         // white led on AI Thinker ESP-CAM board
#define flashLedOff 0               // active high

const byte builtinLedPin = 33;      // red led on AI Thinker ESP-CAM board
#define builtinLedOff 1             // active low

// ...... misc
char thisDeviceMac[20] ;            // Mac address for this device (for info only)
bool newDataFlag = false;

// ...... WiFi
const char* ssid = "plugh";         // on WiFi channel 1
const char* pass = "xyzzy";

// ...... NTP
char ntpPool[] = "pool.ntp.org";
char ntpTzEnvData[] = "EST+5EDT,M3.2.0/2:00:00,M11.1.0/2:00:00";    // US/Eastern
bool ntpUpdateFlag = false;
bool changeNtpSync = true;             // false --> use default 3600 seconds
const uint32_t ntpSyncInterval = 120;   // true --> use this interval (seconds)

// ...... timing parameters (for example procedure in loop())
time_t timestampNow;

//  ....................................................................................
void setup() {
  Serial.begin(115200);
  delay(1000);

// ...... display sign-on message
  Serial.println();
  Serial.println();   
  Serial.print(programName);
  Serial.print(" ");
  Serial.print(versionNumber);
  Serial.print("   ");
  Serial.print(programDate);
  Serial.print("   ");
  Serial.print(programAuthor); 
  Serial.print("\n");

// ...... display MAC address (of this device)
  WiFi.macAddress().toCharArray(thisDeviceMac, 20);
  Serial.printf("\nMAC address: %s", thisDeviceMac);

// ...... WiFi
  WiFi.mode(WIFI_AP_STA);          // AP needed to maintain ESP-NOW
  WiFi.begin(ssid, pass);

  // Wait for connection
  Serial.println("");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

// ...... NTP
  Serial.print("\nconnecting to NTP time server");
  connectToNtp(); 
  
// ...... esp-now 
  // initalize ESP-NOW 
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }  

  // register callback function
  esp_now_register_recv_cb(OnDataRecv); 

// ...... LEDs
  pinMode(flashLedPin, OUTPUT);           // turn LEDs off
  digitalWrite(flashLedPin, flashLedOff);
  
  pinMode(builtinLedPin, OUTPUT);
  digitalWrite(builtinLedPin, builtinLedOff);
  
// ...... end of setup
  Serial.print("\nsetup complete\n");
}

//  ....................................................................................
void loop() {

  // act on received data if appropriate
  if(newDataFlag == true) {
    newDataFlag = false; 
    // act on received data
    if(strcmp (s_switchInfo.c_level, "HIGH") == 0) {
      digitalWrite(flashLedPin, !flashLedOff);
      digitalWrite(builtinLedPin, builtinLedOff);      
    } else {
      digitalWrite(flashLedPin, flashLedOff);
      digitalWrite(builtinLedPin, !builtinLedOff);
    } 
  } 

  // NTP
  timestampNow = time(nullptr);
  
  if(ntpUpdateFlag == true) {
    ntpUpdateFlag = false;
    Serial.print("\n *** NTP update ***    ");
    Serial.print(timestampNow);    
  }
  
  // do something else ...

}

//  ....................................................................................
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  // extract the received data
  char receivedData[100];
  memcpy(&s_switchInfo, incomingData, sizeof(s_switchInfo));

  // deal with received data
  newDataFlag = true;           // handled in loop()

  // display received data - (interesting, but not essential)
  char recvStr[50];  
  snprintf(recvStr, sizeof(recvStr), "\n %u  %u  %s",
           s_switchInfo.i_id, s_switchInfo.i_count, s_switchInfo.c_level);
  Serial.print(recvStr);
  
  // display MAC of sending device - (interesting, but not essential)
  char macStr[20];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.print("   Received from ");
  Serial.print(macStr);
  
  // display UNIX timestamp obtained from NTP server
  Serial.print(" at ");
  Serial.print(timestampNow);
}

//  ....................................................................................
// NTP time synchronization callback
// this callback will place the current UNIX timestamp that was obtained 
//   via NTP into a structure of type 'timeval' which is defined in time.h 
// it looks like this must be done even if the data isn't subsequently used

// NOTE: This is apparently being executed in a different core from the 
//       main program.  Any Serial.print data here may be interspersed with 
//       Serial.print data from the main program.

void time_is_set (struct timeval *bogus) {
  ntpUpdateFlag = true;
}

// ....................................................................................  
void connectToNtp() {
  // possibly change NTP sync interval (default = 3600 seconds)  
  if(changeNtpSync == true) {
    sntp_set_sync_interval(ntpSyncInterval * 1000);
  }
    
  // register a callback to notify about the time synchronization process
  sntp_set_time_sync_notification_cb(time_is_set);

  // obtain system time from an NTP server, do not adjust for timezone or dst here
  configTime(0, 0, ntpPool);                // obtain time from an NTP server
  
  // set the TZ environment variable to the correct value for the device location
  setenv("TZ", ntpTzEnvData, 1);
  
  // update the C library runtime data for the new time zone
  tzset();   
}

//  ....................................................................................

Here's what the output of the 'Gateway' program looks like on the serial monitor.

ResponderA_22A v01   2022-09-25   Donald Weiman

MAC address: 34:94:54:24:3D:E0
.....
Connected to Area51
IP address: 10.12.12.214

connecting to NTP time server
setup complete

 *** NTP update ***    1664199483
 102  5  HIGH   Received from 34:94:54:24:22:58 at 1664199497
 102  5  LOW   Received from 34:94:54:24:22:58 at 1664199497
 102  6  HIGH   Received from 34:94:54:24:22:58 at 1664199497
 102  6  LOW   Received from 34:94:54:24:22:58 at 1664199498
 102  7  HIGH   Received from 34:94:54:24:22:58 at 1664199502
 102  7  LOW   Received from 34:94:54:24:22:58 at 1664199504
 102  8  HIGH   Received from 34:94:54:24:22:58 at 1664199590
 102  8  LOW   Received from 34:94:54:24:22:58 at 1664199590
 *** NTP update ***    1664199603
 102  9  HIGH   Received from 34:94:54:24:22:58 at 1664199608
 102  9  LOW   Received from 34:94:54:24:22:58 at 1664199608
 102  10  HIGH   Received from 34:94:54:24:22:58 at 1664199609
 102  10  LOW   Received from 34:94:54:24:22:58 at 1664199610
 102  11  HIGH   Received from 34:94:54:24:22:58 at 1664199664
 102  11  LOW   Received from 34:94:54:24:22:58 at 1664199664
 102  12  HIGH   Received from 34:94:54:24:22:58 at 1664199680
 102  12  LOW   Received from 34:94:54:24:22:58 at 1664199685
 102  13  HIGH   Received from 34:94:54:24:22:58 at 1664199688
 102  13  LOW   Received from 34:94:54:24:22:58 at 1664199691
 102  14  HIGH   Received from 34:94:54:24:22:58 at 1664199719
 *** NTP update ***    1664199724
 102  14  LOW   Received from 34:94:54:24:22:58 at 1664199725
 102  15  HIGH   Received from 34:94:54:24:22:58 at 1664199823
 102  15  LOW   Received from 34:94:54:24:22:58 at 1664199823
 *** NTP update ***    1664199844
 *** NTP update ***    1664199964
 *** NTP update ***    1664200084
 *** NTP update ***    1664200205
 *** NTP update ***    1664200325
 *** NTP update ***    1664200446
 *** NTP update ***    1664200566
 102  16  HIGH   Received from 34:94:54:24:22:58 at 1664200644

Note that the majority of the additional code in the 'Gateway' program is there to deal with obtaining the timestamp and this capability has been provided as a means of showing that the WiFi is working. As I mentioned in my previous post actually adding this WiFi capability is trivial.

All three programs are available in this zip file.
ProgramCode.zip (5.9 KB)

EDIT: I created the 'Gateway' program by starting with the 'Responder' program. I see that I forgot to change the name in the "program information" section so the program name shows up improperly in the serial output.

Don

2 Likes