I have a network consisting 10 slaves and one master. I am using ESP-NOW to send the messages between the nodes. All of the nodes are within close proximity. I do NOT need a mesh network to reach all the nodes.
ESP-NOW works amazing. It is simple to use and the library is easy to understand. But in this project esp-now is not working for me and here is why:
If the 10 slaves want to send data at the same time it does not work. With 10 nodes they can send data every 200ms. If I lower the time some nodes even crash and restart.
Because all nodes cannot send data at the same time I am having the master ping each node to synchronize who can send a message. The moment a slave receives a message it can reply back.
If one slave node sends data to the wrong peer for some reason the web server of the master node stops working. It freezes until that node is done sending data.
If I send a broadcast message all the nodes reply. How can I configure esp-now so that no acknowledgment is sent back?
With esp-now when you send a packet the destination node always replies with a confirmation. I want to send that confirmation once the job that the slave had to perform worked. Because I am planing to work with a lot of nodes with close proximity I want to reduce network traffic. Moreover, on esp-now if a packet is not delivered it attempts again increasing the network traffic. I only want to resend packets that are important in order to reduce network traffic.
esp-now limits the number of nodes you can have specially if you encrypt the messages.
So my question is: is there a way to instruct esp-now to NOT send an acknowledgment? Can I instruct esp-now to only send one packet regardless if it failed or not? On the NRF24L01 you can set this value with the method setRetries
. I just want something like UDP where it is a fire and forget. I cannot use UDP because I do not want to be connected to a WiFi network.
It will be great if I could have 2 methods. One for sending a simple raw packet and another for reading those packets on another esp32. I know I will be having to do a lot of filtering on the receiving side.
Edit
Here is a solution that works but needs improvement
It is possible to send raw packets with the function esp_wifi_80211_tx
. But still esp32 sends 5 packets when using that function! Moreover, in order to use that function you must send a wifi beacon with whatever data you want. The minimum size of a beacon packet I believe is 128 bytes. So this is a solution but not a good one.
Anyways I bellow is the code for the transmitter esp32 and the receiver esp32. The transmitter code has to be compiled with the ESP-IDF framework and NOT arduino. If I use arduino framework it compiles fine (replace main function with setup) but it does not work once I upload it to the esp32. The receiver code I compiled it using the arduino IDE.
transmitter code (tx)
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_event_loop.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "string.h"
// pin used to flash led
#define PIN GPIO_NUM_2
/*
* This is the (currently unofficial) 802.11 raw frame TX API,
* defined in esp32-wifi-lib's libnet80211.a/ieee80211_output.o
*
* This declaration is all you need for using esp_wifi_80211_tx in your own application.
*/
esp_err_t esp_wifi_80211_tx(wifi_interface_t ifx, const void *buffer, int len, bool en_sys_seq);
esp_err_t event_handler(void *ctx, system_event_t *event)
{
return ESP_OK;
}
void send_raw_packet_task(void *pvParameter)
{
for (;;)
{
uint8_t dataToSend[] = { 0 } ;
esp_wifi_80211_tx(WIFI_IF_AP, dataToSend, sizeof(dataToSend) , false);
// turn on and off led AND wait 10 seconds
gpio_pad_select_gpio(PIN);
gpio_set_direction(PIN, GPIO_MODE_OUTPUT);
gpio_set_level(PIN, 1);
vTaskDelay(9500 / portTICK_PERIOD_MS);
gpio_set_level(PIN, 0);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
extern "C" void app_main(void)
{
nvs_flash_init();
tcpip_adapter_init();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
// Init dummy AP to specify a channel and get WiFi hardware into
// a mode where we can send the actual fake beacon frames.
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
wifi_config_t ap_config;
// SSID = TTTT (4 times the letter t in uppercase)
wifi_ap_config_t x;
x.ssid[0] = 84; x.ssid[1] = 84; x.ssid[2] = 84; x.ssid[3] = 84;
x.ssid[4] = 0;
x.ssid_len = 4;
//x.password = "dummypassword"; // for some reason this does not compile
x.password[0] = 84; x.password[1] = 84; x.password[2] = 84; x.password[3] = 84; x.password[4] = 84; x.password[5] = 84; x.password[6] = 84; x.password[7] = 84;
x.password[8] = 0;
x.channel = 1;
x.authmode = WIFI_AUTH_OPEN;
x.ssid_hidden = 0;
x.max_connection = 4;
x.beacon_interval = 60000;
ap_config.ap = x;
//ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
auto res = esp_wifi_set_config(WIFI_IF_AP, &ap_config);
if (res == ESP_OK)
{
printf("set esp_wifi_set_config. It worked!\n");
}
else
{
printf("It did NOT worked :/\n");
return;
}
// ESP_ERROR_CHECK(esp_wifi_start());
res = esp_wifi_start();
if (res == ESP_OK)
{
printf("wifi started! It worked!\n");
}
else
{
printf("It did NOT worked :/\n");
return;
}
// ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
wifi_ps_type_t t = WIFI_PS_NONE;
res = esp_wifi_set_ps(t);
if (res == ESP_OK)
{
printf("wifi set to WIFI_PS_NONE! It worked!\n");
}
else
{
printf("It did NOT worked :/\n");
return;
}
res = esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
if (res == ESP_OK)
{
printf("wifi set channel to 1! It worked!\n");
}
else
{
printf("It did NOT worked :/\n");
return;
}
// output size of bytes that we will be sending
printf("*** Size of configuration is %i bytes. Starting task send_raw_packet_task...", (int)sizeof(ap_config));
xTaskCreate(&send_raw_packet_task, "send_raw_packet_task", 2048, NULL, 5, NULL);
}
receiver code (rx)
#include "freertos/FreeRTOS.h"
#include "esp_wifi.h"
#include "esp_wifi_types.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_event_loop.h"
#include "nvs_flash.h"
uint8_t channel = 1;
static void wifi_sniffer_packet_handler(void* buff, wifi_promiscuous_pkt_type_t type);
void wifi_sniffer_packet_handler(void* buff, wifi_promiscuous_pkt_type_t type) {
// in here esp32 will capture all wifi traffic
auto t = (unsigned char*)buff;
// create flag to see if packet contains "TTT"
bool containsX = false;
// index where the first T is located on the buffer in case it is found
int indexWhereNameOfSsidWasFound = 0;
// look to see if its a packet of interest
for (int i = 45; i < 100; i++)
{
if (t[i] == 84 && t[i + 1] == 84 && t[i + 2] == 84)
{
containsX = true;
indexWhereNameOfSsidWasFound = i;
Serial.println(i);
break;
}
}
if (containsX == false)
return;
// print SSID if found
for (int i = indexWhereNameOfSsidWasFound; i < indexWhereNameOfSsidWasFound + 20; i++)
{
Serial.print((int)t[i]);
Serial.print(" ");
}
Serial.println();
}
esp_err_t event_handler(void* ctx, system_event_t* event)
{
return ESP_OK;
}
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(115200);
// delay 2 seconds so that esp32 initializes
Serial.println("Starting...");
vTaskDelay(2000 / portTICK_PERIOD_MS);
// init wifi sniffer
{
// order of initializations matter!
wifi_country_t wifi_country = { .cc = "IT", .schan = 1, .nchan = 13 }; // Most recent esp32 library struct
nvs_flash_init();
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); /* set country for channel range [1, 13] */
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
ESP_ERROR_CHECK(esp_wifi_start());
// turn on promiscuous to capture everything
esp_wifi_set_promiscuous(true);
esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler);
// listen on channel 1
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
}
}
void loop() {
// empty
}
So basically the receiver code is listening for all network trafic in promiscuous mode. Kind of like wireshark. If it sees a packet that contains the string "TTTT" it will output it.
Because there is so much wifi traffic this is way slower than esp-now. I could not figure out how to send smaller packets. Lastly when sending a packet it takes about 2 second to capture the packet. I belive esp32 stores all those packets on a queue and then latter calls the call back function.
I guess I will go back on using esp-now. Can I modify the source code of esp-now? I just want want to remove the part where it send the packet multiple times and also the part where it replies with an acknowledgment.