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