On the computer
Library :
A C++ library has been created to handle ESPNOW packets with Linux. You can find it here : ESPNOW_lib.
The code also contains a main() that send back every ESPNOW packet received (it acts like an echo).
If you want to use this code don't forget to turn on the monitor mode on your wireless interface and also tune it to the right channel (because the library can be set-up to use any channel). As a reminder, this is how you do it :
sudo ifconfig wlan0 down
sudo iwconfig wlan0 mode monitor
sudo ifconfig wlan0 up
sudo iwconfig wlan0 channel 1
Berkeley Packet Filter (BP Filter) :
The BP Filter that has been attached to the socket was generated using the tcpdump command :
sudo tcpdump -i wlp5s0 'type 0 subtype 0xd0
and wlan[24:4]=0x7f18fe34
and wlan[32]=221
and wlan[33:4]&0xffffff = 0x18fe34
and wlan[37]=0x4
and wlan dst 11:22:33:44:55:66
and wlan src 77:88:99:aa:bb:cc' -dd
It filters packets according to :
- Type and subtype : Action frames (0xd)
- Category code of the action frame : Vendor specific (0x7f)
- OUI : Espressif (0x18fe34)
- Type of the vendor specific action frame : ESPNOW (0x4)
- Source and Destination addresses
The Assembly code generated has been modified a little bit afterward to fit any source & destination addresses. You can see it at line struct sock_filter temp_code[this->bpf.len] in ESPNOW_manager.cpp.
On the ESP-Module
Acknowledgment (ACK) :
The aim of this project is to have a fast wireless connection between 2 devices, with the least delay.
However, the ESP-NOW protocol is built to wait for an ACK frame after every packet sent. It ensures the reception. And, while the ACK is not received, the ESP-NOW library tries to send the same packet over and over again.
This slows down the whole process !!
Moreover, in our robotic case, if a packet is lost
there is no point on trying to send it again. In fact, this packet is
now outdated and it is possible to send a newer one (which is more
interesting for our system).
The easy way
found to avoid ACKs, is to send the packet over the broadcast address
(and not directly to the other device address). So the ESP will
neither send or wait for ACKs.
In the code, you simply need to declare the peer with the FF:FF:FF:FF:FF:FF mac address :
esp_now_peer_info_t peer;
for (int i = 0; i < 6; ++i ) {
peer.peer_addr[i] = 255;
}
Remark:
Setting the ESP to send over broadcast to avoid ACKs is only
available on ESP32. That is what motivated the choice to change from
ESP8266 to ESP32.
Data rate :
The modification of
the data rate was decisive to decrease of the round trip time. The
default rate for ESP-NOW on the ESP32 is 1 MBps. At this rate, physically send a packet (header + 127
Byte of payload) takes approximately 1.5ms. So the round trip cannot
be less than 3 ms!
By disabling some
default 'safeties' in the WiFi library, it is possible to change the
data rate directly at the WiFi level (and bypass the default 1 MBps
in the ESPNOW library). Now the data rate can be set up to 54 MBps
which correspond to a physical sending time of 0.05 ms. To change it, here is what you need to do :
#include <WiFi.h>
#include <esp_wifi_internal.h>
#include <esp_now.h>
int main() {
//...
WiFi.disconnect();
WiFi.mode(WIFI_STA);
/*Stop wifi to change config parameters*/
esp_wifi_stop();
esp_wifi_deinit();
/*Disabling AMPDU is necessary to set a fix rate*/
wifi_init_config_t my_config = WIFI_INIT_CONFIG_DEFAULT(); //We use the default config ...
my_config.ampdu_tx_enable = 0; //... and modify only what we want.
esp_wifi_init(&my_config); //set the new config
esp_wifi_start();
/*You'll see that in the next subsection ;)*/
esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);
/*set the rate*/
esp_wifi_internal_set_fix_rate(ESP_IF_WIFI_STA, true, DATARATE);
/*rest of the code :*/
esp_now_init();
//...
}
Remark: Of course,
increasing the data rate too much can diminish the robustness of the
transmission.
Frequency / Channel :
Finally, as you saw in the previous piece of code, it is also possible to change the channel of transmission.
Choosing the right
channel, with the fewest devices using it, better a lot the
performances :
- It reduces the perturbations and noise on the channel, and so, decreases packet loss.
- It reduces the time of the round trip. Since the frequency is less used, the network card doesn't have to wait long for the frequency to be free. And so more packets can be sent almost immediately.
- It tends to better the consistency of the performances.
Examples :
- Linux C++ code (does a simple echo using the 'library' we wrote) : https://github.com/thomasfla/Linux-ESPNOW/tree/master/ESPNOW_lib
- ESP32 code to send packets at 1kHz and measure round trip time and some other things : https://github.com/thomasfla/Linux-ESPNOW/tree/master/ESP32-Test
- (the PYTHON_serial folder contains some scripts to plot what the ESP sends back over the serial port).
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
> Remark: Setting the ESP to send over broadcast to avoid ACKs is only available on ESP32. That is what motivated the choice to change from ESP8266 to ESP32.
I don't think that is true... I have a WeMos D1 mini (with an ESP8266) sitting here on my desk right now, happily sending ESP-NOW messages to the broadcast address.
Are you sure? yes | no