Basic peer-to-peer sketch - EspNow WeMos D1 Mini

Good day!
Where could I find a sketch for WeMos D1 Mini that:

  • connects two WeMos D1 Minis peer-to-peer using EspNOW
  • Where the MASTER receives one input: on D2 (GPIO4) and sends to SLAVE
  • Where upon receiving message, the SLAVE outputs 3V to D2 (GPIO4)

I did this with 2 ESP32 using a sketch online, but when I try to upload onto 2 WeMos D1 Minis, the sketch(s) has more errors than I can figure out with my very intro-level knowledge.

Thank you in advance to anyone that can help me :slight_smile:

Post the code that you tried to load onto the ESP8266 that worked on the ESP32

Did you change the name of the library ? If I remember correctly there is a subtle difference between the ESP32 library name and the ESP8266 library name

Thank you so much for your help and expertise. I am reading and trying to learn programming, but so far have been lucky to find sketches that work - I am learning as I go.

There are three tabs in the sketch as follows:

FIRST TAB TITLED "Main":

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

// Pin used for reading
// and the value will be sent
#define PIN 4

// Channel used for connection
#define CHANNEL 1

// If MASTER is set
// the compiler will compile the Master.ino
// If you want to compile Slave.ino remove or
// comment the line below
// #define MASTER

// Structure with information
// about the next peer
esp_now_peer_info_t peer;

// Function to initialize the station mode
void modeStation() {
  // We put the ESP in station mode
  WiFi.mode(WIFI_STA);
  // We show on Serial Monitor the Mac Address
  // of this ESP when in station mode
  Serial.print("Mac Address in Station: ");
  Serial.println(WiFi.macAddress());
}

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

// Function that adds a new peer
// through your MAC address
void addPeer(uint8_t *peerMacAddress) {
  // We inform the channel
  peer.channel = CHANNEL;
  // 0 not to use encryption or 1 to use
  peer.encrypt = 0;
  // Copy array address to structure
  memcpy(peer.peer_addr, peerMacAddress, 6);
  // Add slave
  esp_now_add_peer(&peer);
}

// Function that will send the value to
// the peer that has the specified mac address
void send(const uint8_t *value, uint8_t *peerMacAddress) {
  esp_err_t result = esp_now_send(peerMacAddress, value, sizeof(value));
  Serial.print("Send Status: ");
  // If the submission was successful
  if (result == ESP_OK) {
    Serial.println("Success");
  }
  // If there was an error sending
  else {
    Serial.println("Error");
  }
}

SECOND TAB TITLED "Master" contains:

// will only compile if MASTER is set
#ifdef MASTER

// Mac Address of the peer to which we will send the data
uint8_t peerMacAddress[] = { 0x40, 0x91, 0x51, 0xBB, 0x29, 0x40};

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

  // Call the function that initializes the station mode
  modeStation();

  // Call function that initializes ESPNow
  InitESPNow();

  // Add the peer
  addPeer(peerMacAddress);

  // Registers the callback that will inform us
  // about the status of the submission.
  // The function that will be executed is onDataSent
  // and is stated below
  esp_now_register_send_cb(OnDataSent);

  // We put the pin in read mode
  pinMode(PIN, INPUT);

  // Read the pin value and send
  readAndSend();
}

// Function responsible for
// pin reading and sending
// of the value for the peer
void readAndSend() {
  // Read the value of the pin
  uint8_t value = digitalRead(PIN);
  // Send the value to the peer
  send(&value, peerMacAddress);
}

// Function that serves as a callback to warn us
// about the sending situation we made
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");

// When we receive the result of the last sending
// we can read and send again
  readAndSend();
}

// We do not need to do anything in the loop
// because whenever we receive the feedback
// of sending through the OnDataSent function
// we send the data again,
// making the data always
// being sent in sequence
void loop() {
}
#endif

THIRD (final) TAB TITLED "Slave" CONTAINS:

// will only compile if MASTER is not set
#ifndef MASTER

// Mac Address of the peer to which we will send the data
uint8_t peerMacAddress[6] = { 0x78, 0x21, 0x84, 0x75, 0x5A, 0x4C};

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

  // Call the function that initializes the station mode
  modeStation();

  // Call function that initializes ESPNow
  InitESPNow();

  // Add the peer
  addPeer(peerMacAddress);

  // Registers the callback that will inform us
  // that we receive data.
  // The function to be executed
  // is onDataRecv and is declared below
  esp_now_register_recv_cb(onDataRecv);

  // Registers the callback that will inform us
  // about the status of the submission.
  // The function to be executed
  // is onDataSent and is declared below
  esp_now_register_send_cb(onDataSent);

  // We put the pin as output
  pinMode(PIN, OUTPUT);
}

// Function that serves as a callback to warn us
// we receive data
void onDataRecv(const uint8_t *mac_addr, const uint8_t *value, int len) {
  // Place the received value at the output of the pin
  digitalWrite(PIN, *value);
  // Send the read value to the next esp
  //  If this is the last, comment out this line before compiling
  // send(value, peerMacAddress);
}

// Function that serves as a callback to warn us
// about the sending situation we made
void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
}

// We do not need to do anything in the loop
// we send the data as soon as
// we get from the other esp by the callback
void loop() {
}
#endif

Here is a photo of the prototypes I built - they work, but Iam trying to make them smaller (physical size), so I switched to ESP8266 in WeMos D1 Mini

As I suggested, the library name needs to be different for an ESP8266. Change

#include <esp_now.h>

to

#include <espnow.h>

and try compiling the sketch

Thank you.

I tried that first because it showed as an error:

FROM

#include <esp_now.h>

To this:

#include <espnow.h>

I also replaced:

#include <WiFi.h>

WITH

#include <ESP8266WiFi.h>

Another error came up when I complied, and I changed:

esp_now_peer_info_t peer;

TO

// esp_now_peer_info_t peer;

Every time it sent me to an error, I researched the error, and then tried online recommendations. This continued until I was getting so far away from the original program, that I was completely lost. after 6-7 errors and changes I made to correct them, the last error code I got was about:

  if (esp_now_init() == ESP_OK) {

I spent 5 hours reading and trying to find answers - as much as I wanted to figure it out, I concluded I needed to ask for help.

I can't help feeling that you are making things complicated by having all the code in one sketch and controlling what gets compiled by means of #defines

Start with 2 separate sketches
I have always found that the examples on Getting Started with ESP-NOW (ESP8266 NodeMCU with Arduino IDE) | Random Nerd Tutorials are very useful

Thank you for trying. If you got the feeling that the author of the sketch might have "made things complicated", you can imagine how I (a newbie) feel about the author's work :slight_smile: .

I'm hopeful someone can direct me to a sketch that can get me close. Thanks again for the help you offered.

Start with the examples in the link that I posted. They will show you how to pass data from one board to another. The exact data that you pass is up to you

Have a go at writing the sketches yourself. You will learn more that way than by simply copying the work of others

1 Like

I'll give it a try using the link you sent me. I'll keep it simple and basic, and then build from there. Thanks for the help and direction :slight_smile:

Post back in this topic if you get stuck or need more advice

1 Like

Hi HeliBob, I got it working and it works exactly as I planned, where it reads an input on the CONTROLLER board, and sends the output to the same IO on the SLAVE board - works beautifully.

That's the good news. The bad news is that my design won't work in the application for which I intended :frowning: . I have searched and searched online for the answer, and I am not even sure if what I want is possible - but I keep thinking that it must be programmable.

  1. I want to install two boards into a 24VDC system. Each module (each enclosure - one for CONTROLLER and one for SLAVE) has a converter board (24V to 5V) to power the boards using available power and eliminating the need for a battery.
  2. The SLAVE has constant battery power.
  3. The CONTROLLER only receives power when signaled (a 24V signal to the board is the trigger, and only power)
  4. The idea is that I need the SLAVE to send an output at 3.3 V to D4 only when the CONTROLLER is powered.
  5. GOOD NEWS - when the CONTROLLER transmits, everything works well - signal is received by the SLAVE, and and the SLAVE outputs 3.3 V on D4
  6. BAD NEWS - when the CONTROLLER loses power, the SLAVE follows whatever instruction it was last given, which was OUTPUT D4 - and power remains at D4 of the SLAVE instead of reading LOW as I want it to.

A lot of the above are absolutes and cannot be changed, which is the challenge. All I really need to do is bring constant power to the CONTROLLER too, and then when the signal comes and goes, the CONTROLLER communicates updates to the SLAVE.

My searching on the internet was to find how to program the ESPnow to be in a loop, where it looks for a signal from the CONTROLLER every 15 seconds (for example), and when it sees the signal, it activates D4 OUTPUT to high. If the CONTROLLER stops transmitting because it lost its signal (no power to the CONTROLLER), when the SLAVE checks again in 15 seconds, it will see that the CONTROLLER is not transmitting, and then the SLAVE turns D4 to LOW (off).

I programmed last when I was 15 in 1985 on a Commodore SuperPET in Basic - a fair bit different than this :slight_smile: .

Is what I am trying to do even possible? How advanced is the programming?

Here are the programs for the CONTROLLER and SLAVE:

CONTROLLER:

#include<ESP8266WiFi.h>
#include<espnow.h>

#define MY_NAME         "CONTROLLER_NODE"
#define MY_ROLE         ESP_NOW_ROLE_CONTROLLER         // set the role of this device: CONTROLLER, SLAVE, COMBO
#define RECEIVER_ROLE   ESP_NOW_ROLE_SLAVE              // set the role of the receiver
#define WIFI_CHANNEL    1

uint8_t receiverAddress[] = {0x44, 0x17, 0x93, 0x1C, 0x3B, 0x6B};   // please update this with the MAC address of the receiver

struct __attribute__((packed)) dataPacket {
  int sensor1;

};

void transmissionComplete(uint8_t *receiver_mac, uint8_t transmissionStatus) {
  if (transmissionStatus == 0) {
    Serial.println("Data sent successfully");
  } else {
    Serial.print("Error code: ");
    Serial.println(transmissionStatus);
  }
}

void setup() {
  Serial.begin(115200);     // initialize serial port

  Serial.println();
  Serial.println();
  Serial.println();
  Serial.print("Initializing...");
  Serial.println(MY_NAME);
  Serial.print("My MAC address is: ");
  Serial.println(WiFi.macAddress());

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();        // we do not want to connect to a WiFi network

  pinMode(4, INPUT);

  if (esp_now_init() != 0) {
    Serial.println("ESP-NOW initialization failed");
    return;

  }

  esp_now_set_self_role(MY_ROLE);
  esp_now_register_send_cb(transmissionComplete);   // this function will get called once all data is sent
  esp_now_add_peer(receiverAddress, RECEIVER_ROLE, WIFI_CHANNEL, NULL, 0);

  Serial.println("Initialized.");
}

void loop() {
  dataPacket packet;

  packet.sensor1 = digitalRead(4);

  esp_now_send(receiverAddress, (uint8_t *) &packet, sizeof(packet));

  Serial.println(packet.sensor1);
}

And here is the program for the SLAVE:

#include<ESP8266WiFi.h>
#include<espnow.h>

#define MY_NAME   "SLAVE_NODE"

struct __attribute__((packed)) dataPacket {
  int sensor1;

};

void setup() {

  Serial.begin(115200);     // initialize serial port

  Serial.println();
  Serial.println();
  Serial.println();
  Serial.print("Initializing...");
  Serial.println(MY_NAME);
  Serial.print("My MAC address is: ");
  Serial.println(WiFi.macAddress());

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();        // we do not want to connect to a WiFi network

  pinMode(4, OUTPUT);
 //  digitalWrite(4, 0); attempt to start with LOW - didn't work

  if (esp_now_init() != 0) {
    Serial.println("ESP-NOW initialization failed");
    return;

  }

  esp_now_register_recv_cb(dataReceived);   // this function will get called once all data is sent

  Serial.println("Initialized.");
}

void dataReceived(uint8_t *senderMac, uint8_t *data, uint8_t dataLength) {
  char macStr[18];
  dataPacket packet;

  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", senderMac[0], senderMac[1], senderMac[2], senderMac[3], senderMac[4], senderMac[5]);

  Serial.println();
  Serial.print("Received data from: ");
  Serial.println(macStr);

  memcpy(&packet, data, sizeof(packet));

  Serial.print("sensor1: ");
  Serial.println(packet.sensor1);

  digitalWrite(4, packet.sensor1);

  Serial.print("Actual "); // my attempt to read the actual, so that I could better compare to dmm
  Serial.println(digitalRead(4)); // same as previous line

}

void loop() {

//  Serial.println("Void Loop "); //My newbie test to understand how program is flowing

}

I want to share that I thought I had about esp_now_deinit().

If I create a loop that starts with:

  1. esp_now_init()
  2. If data is received, then set the output D4 to HIGH
  3. If data is not received then set output D4 to LOW
  4. esp_now_deinit()
  5. Timer starts counting (30 seconds)
  6. Loop restarts

Just not sure how to do that or if it will work?

Have the slave send a message to the master periodically. If no reply is received then the controller is not turned on so set D4 LOW else if a valid reply is received set D4 HIGH

1 Like

I got it to work, but it is ugly and I am certain it is not how you intended - before I tried to figure out how to have the SLAVE send a message, and then create a new if/else statement, I had a quick idea from your message:

I reversed the positions of the MODULES - I put the SLAVE where the CONTROLLER was and visa versa. I reversed the OUTPUTS/INPUTS on SLAVE and CONTROLLER, and I added a digitalWrite() to the CONTROLLER to IO4=HIGH, and then used an existing if/else loop to set IO4=LOW when the CONTROLLER couldn't complete a transmission.

Again, it works, but it seems very ugly.

Unless you can see an issue with the program, I plan to build the enclosures and install a test pair of these units and run them for a few weeks or month in actual service. I have 10 cases that require this, so while the test is being done, I will read and learn, and hopefully rewrite it better for the final product :slight_smile:

CONTROLLER

// This unit checks for a signal from SLAVE every X seconds - when no signal IO4=LOW, when signal IO4=HIGH

#include<ESP8266WiFi.h>
#include<espnow.h>

#define MY_NAME         "CONTROLLER_NODE"
#define MY_ROLE         ESP_NOW_ROLE_CONTROLLER         // set the role of this device: CONTROLLER, SLAVE, COMBO
#define RECEIVER_ROLE   ESP_NOW_ROLE_SLAVE              // set the role of the receiver
#define WIFI_CHANNEL    1

uint8_t receiverAddress[] = {0x44, 0x17, 0x93, 0x1C, 0x3B, 0x6B};   // please update this with the MAC address of the receiver

struct __attribute__((packed)) dataPacket {
  int sensor1;

};

void transmissionComplete(uint8_t *receiver_mac, uint8_t transmissionStatus) {
  if (transmissionStatus == 0) {
    Serial.println("Data sent successfully");
    digitalWrite(4, HIGH);
  } else {
    Serial.print("Error code: ");
    Serial.println(transmissionStatus);
    digitalWrite(4, LOW);
  }
}

void setup() {
  Serial.begin(115200);     // initialize serial port

  Serial.println();
  Serial.println();
  Serial.println();
  Serial.print("Initializing...");
  Serial.println(MY_NAME);
  Serial.print("My MAC address is: ");
  Serial.println(WiFi.macAddress());

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();        // we do not want to connect to a WiFi network

  pinMode(4, OUTPUT);

  if (esp_now_init() != 0) {
    Serial.println("ESP-NOW initialization failed");
    return;

  }

  esp_now_set_self_role(MY_ROLE);
  esp_now_register_send_cb(transmissionComplete);   // this function will get called once all data is sent
  esp_now_add_peer(receiverAddress, RECEIVER_ROLE, WIFI_CHANNEL, NULL, 0);

  Serial.println("Initialized.");
}

void loop() {

  dataPacket packet;

  esp_now_send(receiverAddress, (uint8_t *) &packet, sizeof(packet));

  Serial.println(digitalRead(4));

  delay(5000);
}

SLAVE

// When this unit is powered, it sends packet to MASTER that sets IO4=HIGH

#include<ESP8266WiFi.h>
#include<espnow.h>

#define MY_NAME   "SLAVE_NODE"

struct __attribute__((packed)) dataPacket {
  int sensor1;

};

void setup() {

  Serial.begin(115200);     // initialize serial port

  Serial.println();
  Serial.println();
  Serial.println();
  Serial.print("Initializing...");
  Serial.println(MY_NAME);
  Serial.print("My MAC address is: ");
  Serial.println(WiFi.macAddress());

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();        // we do not want to connect to a WiFi network

  if (esp_now_init() != 0) {
    Serial.println("ESP-NOW initialization failed");
    return;

  }

  esp_now_register_recv_cb(dataReceived);   // this function will get called once all data is sent

  Serial.println("Initialized.");
}

void dataReceived(uint8_t *senderMac, uint8_t *data, uint8_t dataLength) {
  char macStr[18];
  dataPacket packet;


  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", senderMac[0], senderMac[1], senderMac[2], senderMac[3], senderMac[4], senderMac[5]);

  Serial.println();
  Serial.print("Received data from: ");
  Serial.println(macStr);

  memcpy(&packet, data, sizeof(packet));

  Serial.print("sensor1: ");
  Serial.println(packet.sensor1);

}

void loop() {

}

It was not what I intended, but if it works then use it

1 Like

Thank you so much for your guidance - I'll attach a photo of the final product when it's made, along with the code if I am able to clean it up (or make it easier). Thank you again.

Good luck with your project

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.