Close

Implementation

A project log for Linux-ESPNOW

An attempt at implementing a direct link between a linux station and an ESP module using ESPNOW protocol for real time robot control

EtienneEtienne 03/29/2019 at 07:570 Comments

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 :


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 :

Examples :

Discussions