Отправить только один пакет с одного ESP32 на другой ESP32 без подтверждения

c++ esp32 wifi network

У меня есть сеть, состоящая из 10 рабов и одного хозяина. Я использую ESP-NOW для отправки сообщений между узлами. Все узлы находятся в непосредственной близости. Мне не нужна сетчатая сеть, чтобы охватить все узлы.

ESP-NOW работает потрясающе. Он прост в использовании, а библиотека проста для понимания. Но в этом проекте esp-now не работает для меня, и вот почему:

  1. Если 10 подчиненных устройств хотят отправлять данные одновременно, это не работает. С 10 узлами они могут отправлять данные каждые 200 мс. Если я уменьшу время, некоторые узлы даже выйдут из строя и перезапустятся.

  2. Поскольку все узлы не могут отправлять данные одновременно, у меня есть мастер, который проверяет каждый узел, чтобы синхронизировать, кто может отправить сообщение. В тот момент, когда подчиненный получает сообщение, он может ответить.

  3. Если один подчиненный узел по какой-либо причине отправляет данные не тому узлу, веб-сервер главного узла перестает работать. Он зависает до тех пор, пока этот узел не завершит отправку данных.

  4. Если я отправлю широковещательное сообщение, все узлы ответят. Как я могу настроить esp-now так, чтобы подтверждение не отправлялось обратно?

  5. С помощью ESP-NOW, когда вы отправляете пакет, узел назначения всегда отвечает подтверждением. Я хочу отправить это подтверждение, как только работа, которую должен был выполнить подчиненный, сработает. Поскольку я планирую работать с большим количеством узлов в непосредственной близости, я хочу сократить сетевой трафик. Более того, на ESP-NOW, если пакет не доставлен, он снова пытается увеличить сетевой трафик. Я хочу повторно отправлять только те пакеты, которые важны для сокращения сетевого трафика.

  6. ESP-NOW ограничивает количество узлов, которые вы можете иметь, особенно если вы шифруете сообщения.

Итак, мой вопрос: есть ли способ проинструктировать ESP-NOW НЕ отправлять подтверждение? Могу ли я поручить esp-now отправить только один пакет, независимо от того, произошел сбой или нет? На NRF24L01 вы можете установить это значение с помощью метода setRetries. Я просто хочу что-то вроде UDP, где это огонь, и забываю. Я не могу использовать UDP, потому что не хочу подключаться к сети Wi-Fi.

Было бы здорово, если бы у меня было 2 метода. Один для отправки простого необработанного пакета, а другой для чтения этих пакетов на другом esp32. Я знаю, что мне придется много фильтровать на принимающей стороне.


Редактировать


Вот решение, которое работает, но нуждается в улучшении

Можно отправлять необработанные пакеты с помощью функции esp_wifi_80211_tx. Но все равно esp32 отправляет 5 пакетов при использовании этой функции! Кроме того, для того, чтобы использовать эту функцию, вы должны отправить маяк Wi-Fi с любыми данными, которые вы хотите. Минимальный размер пакета маяка, по-моему, составляет 128 байт. Так что это решение, но не очень хорошее.

В любом случае, я ниже приведу код для передатчика esp32 и приемника esp32. Код передатчика должен быть скомпилирован с использованием платформы ESP-IDF, а НЕ arduino. Если я использую фреймворк arduino, он отлично компилируется (замените основную функцию настройкой), но он не работает, как только я загружаю его в esp32. Код приемника я скомпилировал с помощью arduino IDE.

код передатчика (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"

// контакт, используемый для вспышки светодиода
#define PIN GPIO_NUM_2

/*
 * Это (в настоящее время неофициальный) 802.11 raw frame TX API,
 * определено в libnet80211.a/ieee80211_output.o библиотеки esp32-wifi-lib.
 *
 * Это объявление-все, что вам нужно для использования esp_wifi_80211_tx в вашем собственном приложении.
 */
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);    
}

код приемника (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) {

    // здесь esp32 будет захватывать весь трафик Wi-Fi

    auto t = (unsigned char*)buff;

    // создать флаг, чтобы увидеть, содержит ли пакет "TTT"
    bool containsX = false;

    // индекс, где первый T находится в буфере, в случае, если он будет найден
    int indexWhereNameOfSsidWasFound = 0;

    // посмотрите, представляет ли это интерес
    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;


    // распечатайте SSID, если он найден
    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;
}


// функция настройки запускается один раз, когда вы нажимаете сброс или включаете питание платы
void setup() {

    Serial.begin(115200);    

    // задержка 2 секунды, чтобы esp32 инициализировался
    Serial.println("Starting...");
    vTaskDelay(2000 / portTICK_PERIOD_MS);

    // встроенный Wi-Fi-сниффер
    {
        // порядок инициализации имеет значение!

        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());


        // включить неразборчивость в связях, чтобы захватить все
        esp_wifi_set_promiscuous(true);
        esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler);

        // слушать на канале 1
        esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
    }
}

void loop() {
    // пусто
}

Таким образом, в основном код приемника прослушивает весь сетевой трафик в неразборчивом режиме. Что - то вроде wireshark. Если он увидит пакет, содержащий строку "TTTT", он выведет его.

Из-за большого трафика Wi-Fi это намного медленнее, чем сейчас. Я не мог понять, как отправлять пакеты меньшего размера. Наконец, при отправке пакета для захвата пакета требуется около 2 секунд. Я верю, что esp32 хранит все эти пакеты в очереди, а затем последний вызывает функцию обратного вызова.

Думаю, я вернусь к использованию esp-сейчас. Могу ли я изменить исходный код esp-сейчас? Я просто хочу удалить ту часть, где он отправляет пакет несколько раз, а также ту часть, где он отвечает подтверждением.

, 👍1

Обсуждение

Я не знаю, ESP-СЕЙЧАС, но для односторонних сообщений в сети TCP/IP используется протокол UDP. Это WiFIUdp с библиотекой Wi-Fi, @Juraj

Могу ли я отправлять UDP-сообщения без подключения к сети? Я предполагаю, что мне придется создать сеть между узлами правильно?, @Tono Nam

да, для этого потребуется стандартная сеть Wi-Fi. один из esp32 может создать его как SoftAP, @Juraj


1 ответ


2

Я предполагаю, что esp32 'raw' tx на самом деле не таков, основываясь на ограничениях на то, что может быть отправлено (данные, но не данные QoS, например).

Я думаю, что среди 5 пакетов, которые вы видите, 1.RTS, 2. CTS, 3. ДАННЫЕ, 4. ACK. Они (по крайней мере 1,2) необходимы, чтобы избежать столкновения.

ESPNow отправляет пакет, зависящий от поставщика, который (я предполагаю) не требует или не следует соглашениям CTS/RTS или ACK 802.11.

Однако программа "Точка-точка" ESPNow записана так, чтобы ожидать подтверждения пакета от получателя, и будет повторно отправляться определенное количество раз, пока не получит его.

Вещание ESPNow не ожидает и фактически не может автоматически получать одно из этих подтверждений, относящихся к ESPNow.

Если вам нужна самая быстрая и наименее надежная передача пакетов 802.11, я бы посоветовал использовать широковещательную передачу ESPNow.

Сказав это, вы должны понимать, что вы не получите полную защиту от столкновений при более обычном использовании протокола 802.11. Возможно, вам потребуется предоставить свой собственный ... например, прослушивание перед отправкой, случайное экспоненциальное отступление и кто знает, что еще.

Мне кажется, что вы отправляетесь в приключение в дикой местности :)

,