Very Deep Sleep and energy saving on ESP8266 – Part 5: ESP-NOW and WiFi

In the previous 4 articles on deepsleep and energy saving, I mainly focussed on establishing a WiFi connection to send data.

Part 1 General – DeepSleep
Part 2 Http requests – DeepSleep
Part 3 MQTT publish – DeepSleep
Part 4 MQTT subscribe – DeepSleep

There are other ways to send data though that cost less time and therefore less energy than establishing a WiFi connection. One of those ways is to use ESP-NOW. ESP-NOW is a kind of connectionless Wi-Fi communication protocol that is defined by Espressif. In ESP-NOW, application data is encapsulated in a vendor-specific action frame and then transmitted from one Wi-Fi device to another without connection.
The big gain in using ESP-NOW is that the ESP8266 (or ESP32 for that matter) does not need to make connection with a router, but it sends data immediately to another designated ESP, which makes the process much faster. That is also one of the drawbacks: you need an extra ESP8266 (or ESP32). That extra ESP can receive data from 20 other ESP’s.
Another drawback is that the receiving ESP is ‘isolated’. Sure you can add an LCD to read the data, but it would be handier if the received data would be available on a network, for Home Assistant or openHAB being able to do something with it, or to store it in a database.
ESP-NOW works fairly straightforward on the ESP32 and a very good “How To” can be found on randomnerdtutorials.

It is perfectly well possible though to also use it on an ESP8266 and that is what I will be doing here. I am certainly no pioneer in this. Anthony Elder in fact already published code using ESP-NOW to transfer BME280 data and go to sleep in between, and also “The guy with the Swiss accent’ did, as did some others, but usually based on Anthony’s code.
Anthony’s sketches though do not have the receiving ESP connect to a network as well, but it can be done, albeit in a bit unorthodox way. Wim3D on instructables however follows an interesting concept as well as he has the sending ESP8266 connect to MQTT if the ESP-NOW transfer goes faulty. Sample code can be found here too (pdf),

So what I will do here is:
1 use one ESP8266 to read a sensor like the BME280 and send the data via ESP-NOW, keeping the ESP in deepsleep in between.
2 use another ESP8266 to receive the incoming data and eventually send that via an http request or MQTT to the network

1 The Sender
There is plenty of information on the Web so there is no real need to reinvent the wheel. I used a sketch from Anthony Elder, that I adapted for my needs. It can be found here. Although this sketch does not have all the energy savings that can be made as shown in my parts 1-4, it is a good begin to test the ESP-NOW datatransfer.

We can see that the transfer only takes a tadd less than 300ms and part of that is not even the active connection. By removing the print statements it is possible to shave off another 40-50msec. So can we win some more energy savings here. Let’s see. It turns out that that the biggest time saver we can implement is to add WiFi.persistent( false ); right at the beginning of the Setup(). Once we do that we go from 300msec to 87msec.

That addition is now already in the sendfile I linked to earlier. When you decide to take out the Serial.print statements, do not remove the Serial.println(bme280.begin(), HEX); statement as that one initializs the BME280. You could ofcourse replace that by a simple bme280.begin(); call

2 The receiver-basic
As there is much info already available on the web, there is no need to re-invent the wheel. I adapted a sketch from Anthony Elder to serve my needs and it can be found here. It simply receives the ESPNOW messages and printes them to the serial monitor. Obviously that is still not what we want as we want it to function as either an HTTP or MQTT gateway, but for now it will do to test if we can make the ESPNOW data transfer.

2 The receiver-MQTT
Of course just printing data to the serial monitor is not going to be of much use, so I was looking for a way to make an ESP-NOW to MQTT Gateway. As it happens, ‘Swiss guy’ Andreas Spiess had already done work on that and provided a sketch that I only had to adapt slightly.
As the present ESP-NOW sdk does not support simultaneous use of ESP-NOW and a wifi connection, the WiFi connection to the broker is now started a new when an ESP-NOW message is received. After a WiFi channel is used, the ESP-NOW protocol can only start when the same WiFi channel is used. As this comes with some limitations I for now have solved it by testarting the sketch every time an MQTT message is published.  Possibly there is another way that I have not tried yet and that is to define the receiver both as AP and STA and use different channels for each and use the AP for the ESP-NOW  and the STA for internet connection. Rui Santos from randomnerdtutorials gives an example of this for the ESP32 although that is just to have the receiving ESP32 to serve a webpage as well. Whether it will work for the ESP8266 to be an MQTT or HTTP client I cannot confirm it yet.

That of course is far from ideal and makes the gateway a bit slow, but it should not be a problem with ESP-NOW messages coming in that have a sensible sleeping period in between. Whether this reset is still necessary with the new sdk I need to check (when I have time)

3 The receiver HTTP
If you prefer an HTTP connection to upload the data to an SQL server (or Thinkspeak), that of course is possible as well. This sketch sends MQTT as well as HTTP.

4 Miscellaneous
There are a couple of functions important/needed to set up the ESP-NOW sender.
Of course we begin with calling the necessary library espnow.h.
Then we set up a data structure that we will send.
We initialize the protocol with esp_now_init().
We define the role with esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
We define the peer (slave) that we will be sending to with esp_now_add_peer(remoteMac, ESP_NOW_ROLE_SLAVE, WIFI_CHANNEL, NULL, 0);
and then we register the callback with esp_now_register_send_cb([](uint8_t* mac, uint8_t sendStatus). The callback function returns whether the delivery was successful or not.
We finally send the data with esp_now_send(NULL, bs, sizeof(sensorData)); NULL means send to all peers (thats to say the ones we added)

The code for the ESP8266 starts with a bit of an awkward instruction:

extern "C" {
#include <espnow.h>
}

that is basically to instruct the compiler to expect C code rather than C++ code
Sendercode.
ReceiveESPNOW and send through MQTT/HTTP code.