• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

UDP отправка в режиме станции

_hg_

New member
Товарищи, сломал себе всю голову.
ESP-12F, SDK: "ESP8266_NONOS_SDK_V2.0.0_16_07_19"
В режиме станции не передает UDP пакеты, но получает - исправно.
В режиме точки и передает и получает UDP пакеты хорошо.
Что же это за мистика ?

так инициализируется в режиме STA
Код:
        wifi_set_opmode(STATION_MODE);
        wifi_station_disconnect();
        wifi_station_set_config(&sta_config);
        wifi_station_set_auto_connect(true);
        wifi_station_connect();

        wifi_softap_dhcps_stop();
        wifi_station_dhcpc_stop();
        wifi_set_ip_info(STATION_IF, &ip_info);
        wifi_station_dhcpc_start();
так инициализируется в режиме AP
Код:
        wifi_set_opmode(SOFTAP_MODE);
        wifi_softap_set_config(&ap_config);

        wifi_station_dhcpc_stop();
        wifi_softap_dhcps_stop();
        wifi_set_ip_info(SOFTAP_IF, &ip_info);
        wifi_softap_dhcps_start();

так отправляет UDP
Код:
int net_sendto (uint16_t port, struct ipaddr *ip, char *pdata, int len)
{
    int err = 0;
    struct espconn udp_conn;
    esp_udp udp;

    memset(&udp_conn, 0, sizeof(struct espconn));
    memset(&udp, 0, sizeof(esp_udp));

    udp_conn.proto.udp = &udp;
    udp_conn.type = ESPCONN_UDP;
    udp_conn.state = ESPCONN_NONE;
    memcpy(udp_conn.proto.udp->local_ip, get_ip(), 4);
    udp_conn.proto.udp->local_port = espconn_port();
    memcpy(udp_conn.proto.udp->remote_ip, ip, 4);
    udp_conn.proto.udp->remote_port = port;
    udp_conn.reverse = NULL;

    if ((err = espconn_regist_sentcb(&udp_conn, net_udp_fin))) {
        DBG_PRINT(("** net_sendto: regist_sentcb error %d\r\n", err));
        return -1;
    }

    int status = 0;
    status = espconn_create(&udp_conn);
    if (status) {
        DBG_PRINT(("espconn_create: %d\r\n", status));
    }
    status = espconn_sendto(&udp_conn, pdata, len);
    if (status) {
        DBG_PRINT(("espconn_sent: %d\r\n", status));
    }
    return 0;
}
Причем статус отправки везде УСПЕШНО. и net_udp_fin вызывается.

... Замечено, что пробивается один-два пакета за очень долгий промежуток времени ...
 
Последнее редактирование:

_hg_

New member
Ну так и быть, отвечу сам на поставленный вопрос.
У меня периодическая задача (созданная system_os_task) запускается system_os_post в обработчике таймера.
И, видимо, так как приоритет у таймерного обработчика выше, чем у сетевых задач, то поставленное широковещание UDP как-то там внутрях SDK-эевых функций просрочивалось и не выполнялось.
В общем, поставил я трансляцию широких данных перед самым выходом из задачи. И заработало.
БЫЛО:
Код:
{
   os_timer_disarm(); // время на периодическую работу не считаем

   read_sensors();  // читаем датчики
   broadcast_metric_UDP();   // засоряем эфир

   periodic_work(); // периодичка

   os_timer_arm(); // спим

   return;
}
СТАЛО:
Код:
{
   os_timer_disarm(); // время на периодическую работу не считаем

   read_sensors();  // читаем датчики

   periodic_work(); // периодичка

   broadcast_metric_UDP();  // засоряем эфир
   os_timer_arm(); // спим

   return;
}


кстати для охотников до баг боунти:
- несколько раз случалось ловить падение в функциях выделения памяти (malloc, zalloc (замена их друг на друга ничего не меняла)). Помогла вставка printf-ов до и(или) после функций. Код показать не могу, так как дело прошлое и все уже работает без printf.
Может это компилятор косячит - не знаю. Использую GCC crosstool-ный с github.com
 

pvvx

Активный участник сообщества
Нет там никаких приоритетов. Повторность вхождения в функции LwIP? Это не баг – это ваше решение.

Основа в NON_OS системе проста – есть функция ets_run(). В ней стоит цикл – команда ожидания прерывания и опрос таблиц task/postи флагов срабатывания программных таймеров c вызовом их калбаков. Драйвер WiFi срабатывает по прерываниям и посылает postзапрос. В taskдалее отрабатывает LwIP. Т.е. всё обрабатывается по очереди, немного учитывая приоритеты в цикле ets_run(), по её таблицам калбаков.

UDP не может передаваться когда угодно – существуют ограничения в буферах LwIP и времени выхода передатчика на передачу – арбитраж WiFi от AP.

Передавать или вызывать функции LwIP в обработчике таймеров или других прерываний нельзя. Это продиктовано схемой работы ets_run() с флагами выполнения калбаков таймеров – пока их не поставит, не смогут вызваться task. Т.е. вызывать системные функции можно только из task.

Кроме того, при вызове LwIP из вашей задачи нет известности, активно или ещё его соединение (указатель на tcp/udp структуру этого соединения). Т.к. оно передается указателем адреса на структуру, то за время между вашими обращениями к нему, структура уже может быть закрыта и в данной области памяти “heap” открыта новая (вероятность 50%). :) Вам необходимо отслеживать все калбаки по соединению от LwIP, чтобы отследить эти изменения или сравнивать все данные хост и открытого порта (номерки ip и портов). Правильным методом работы с LwIP является только обслуживание калбаков от него, при вызове которых он передает указатель на структуру соединения. Вот только этот указатель и можно использовать для запросов в данном калбаке.
Китайская нашлепка esp_conn об этом не заботиться.
Так-же выявить, передаст новый пакет UDP esp_conn или нет - нет возможности.
 
Последнее редактирование:
  • Like
Реакции: _hg_
Сверху Снизу