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

Вопрос о тормознутости ESP8266

lexis-megavolt

New member
День добрый. Столкнулся с такой проблемой:
приходят данные по TCP, я их пересылаю по уарту в железяку, меньше чем через 1мс железяка отвечает, я пересылаю снова по TCP, на компе время между посылкой и получением ответа 300-600 мс...
Есть ли Гуру, которые могут ткнуть пальцем что я делаю не так?
Скетч прилагаю
 

Вложения

Юрий Ботов

Moderator
Команда форума
Типичный пинг между 2-мя wifi устройствами через роутер (2 скачка) у меня в сети 370-650 мс. Не esp. Это 2 компьютера. Суть проблемы в пакетной передаче через "посредника". То есть дал роутер первому слот - тот передал, роутер принял на хранение, потом занялся другими участниками, дал роутер второму слот и переслал принятое. И обратно, соответственно также. Если на esp поднять точку доступа, и цепляться компьютером непосредственно на esp задержки как минимум в 2 раза должны сократиться, но на порядок их не сократить никак. Wifi.
 

lexis-megavolt

New member
Ну да. Роутер подключен к свитчу, а уже комп к свитчу..
Хотя пинг на компе 1-2мс. Да и страничка в браузер отдается быстро. Вот в чем проблема.
 

pvvx

Активный участник сообщества
Пробовал использовать .flush() что в TCP, что в Serial, толку мало
Отключите Nagle (там вам разжевано: Этот алгоритм плохо взаимодействует с технологией TCP с отложенным подтверждением), передавайте по два пакета, отключите у WiFi sleep (MODEM или LIGTH в режим NONE) - будет вам менее 1 мс.
Если по два пакета не сможете, то делайте постоянную передачу к ESP малых пакетов с шагом в десяток ms. Они потянут за собой АСК для передаваемых ESP TCP пакетов и дискретность (предельный тайм-аут) у вас выйдет с шагом этого пинга, невзирая на алгоритм TCP стека на вашем устройстве, ожидающем паузу в 200 мс до отправки ACK к ESP после прихода от неё пакета TCP...
Но по хорошему - чините софт на приемном устройстве - дайте его стеку TCP правильный флаг...

PS: На ваш вопрос уже было более дцати тем и подробнее найдете сами (на данном форуме).
 
Последнее редактирование:

pvvx

Активный участник сообщества
Типичный пинг между 2-мя wifi устройствами через роутер (2 скачка) у меня в сети 370-650 мс. Не esp. Это 2 компьютера.
Компы из до 1980 года? Самопал-эксклюзив? ;) Когда вышел WiFi 802.11n, то все устройства с ним уже были в состоянии обработать от 2-х тысяч пакетов в сек. Пинг надеюсь посчитаете...
 

Юрий Ботов

Moderator
Команда форума
Нормальные компы... просто реально на роутере сейчас висят 2 ethernet юзера и 12 wifi юзеров. Активно никто не флудит но надо же роутеру проверить - живы ли, и т.п. так и живет своей жизнью.
 

pvvx

Активный участник сообщества
Нормальные компы... просто реально на роутере сейчас висят 2 ethernet юзера и 12 wifi юзеров. Активно никто не флудит но надо же роутеру проверить - живы ли, и т.п. так и живет своей жизнью.
Да не - 300 ms - это уже пинг на другую сторону шарика (или системы уплотнения трафика GSM). Если у вас такой-же - кто-то в вашей цепи сидит в green режиме и просыпается только на время прихода beacon. Типа спящий смартфон с выключеннм экраном или ноут в режиме сна, но с включенным WiFi. И то, после пинка он проснется и замолотит в десятки us.
Нету у устройств таких буферов, чтобы накопить для отложения на 0.3 сек весь сетевой трафик.
Прикиньте это на 1 Gbit/s - нужна RAM более 30 МБ :) По этому принятый пакет отправляется тут-же, никого не ожидая. И время приема-передачи пакета у промежуточного роутера до десятки us. Что-бы не хотели там висящие на нем устройства, он (роутер) сам ведет арбитраж и пакет передаст тут-же в сеть WiFi, если устройство не спит (находится в режиме IPS/LPS). Из этого и заключение - тормозит самопал-ПО на ваших "ноутах".
Безусловно это всё касается нашей ближайшей метрики пространства. Если у вас другая, то всё может быть – вдруг у вас радиосигнал идет медленнее звука в пространстве… :)
 
Последнее редактирование:

lexis-megavolt

New member
Отключите Nagle (там вам разжевано: Этот алгоритм плохо взаимодействует с технологией TCP с отложенным подтверждением), передавайте по два пакета, отключите у WiFi sleep (MODEM или LIGTH в режим NONE)
Nagle отключен ставил server.setNoDelay(true);
Отключить WiFi sleep это где? В роутере или ESP? Чёт не нашел в библиотеках такой функции.
 

lexis-megavolt

New member
К чему? К самому себе? К роутеру?
пинг в связке: ESP по WiFi -> Роутер -> Свитч -> комп 1-2мс,
в этой же связке при создании подключения на порт 502 это ModbusTCP, посылаю пакет "Запрос ЭХА", уходит в устройство подключенное к ESP по uart, устройство отвечает по нему же, и по WiFi через 502 порт уходит обратно. Так вот между посылкой и ответом проходит 300-600 мс. Хотя как я писал выше время пинга 1-2 мс...
 

lexis-megavolt

New member
Если по два пакета не сможете, то делайте постоянную передачу к ESP малых пакетов с шагом в десяток ms. Они потянут за собой АСК для передаваемых ESP TCP пакетов и дискретность (предельный тайм-аут) у вас выйдет с шагом этого пинга, невзирая на алгоритм TCP стека на вашем устройстве, ожидающем паузу в 200 мс до отправки ACK к ESP после прихода от неё пакета TCP...
Но по хорошему - чините софт на приемном устройстве - дайте его стеку TCP правильный флаг..
Посылаю я по одному пакету в 12 байт. Еще бы понять как дать стеку от Arduino правильный флаг...
 

lexis-megavolt

New member
Отключил сон, WiFi.setSleepMode(WIFI_NONE_SLEEP);
толку нет...

17.05.29.10.28.39.496 Запрос 192.168.4.201 001400000006f70800000000
17.05.29.10.28.39.848 Ответ 192.168.4.201 001400000006f70800000000
350 мс
17.05.29.10.28.41.799 Запрос 192.168.4.201 001500000006f70800000000
17.05.29.10.28.42.140 Ответ 192.168.4.201 001500000006f70800000000
341 мс
17.05.29.10.37.11.269 Запрос 192.168.4.201 001600000006f70800000000
17.05.29.10.37.11.610 Ответ 192.168.4.201 001600000006f70800000000
341 мс
 

pvvx

Активный участник сообщества
Самых кривых 4-ре тестовых посылки на ESP8266, пошивка TCP2UART, выход TX ESP соединен c RX.

Конфигурация сети:
Комп (192.168.1.2) -> сетевой кабель -> роутер WiFi (192.168.1.1) -> WiFi station ESP8266 (192.168.1.225) (модуль ESP12E DevKit.

Итого четыре посылки по 12 байт, путем нажатия кнопки, из программы по TCP к ESP8266. ESP8266 принимает 12 байт и передает в свой UART на TX пин. По RX пину, соединенному с TX, ESP8266 принимает эти 12 байт, ждет паузы в 2.5 символа и передает обратно:
Снимок1491.gif

Имеет, 4-ре замера скорости пересылки туда-сюда из компа через LAN->роутер-> WiFi->ESP-> UART_TX ->UART_RX->ESP->WiFi->роутер->LAN->комп:

1) 0.003593 сек
2) 0.001689+0.001583=0.003272 сек
3) 0.003286 сек
4) 0.003337 сек

Время передачи по UART на 115200 12-ти символов и 2.5 символов паузы, для анализа конца блока и начала передачи его по wifi = (12 символов + 2.5 символа пауза)*10 бит на символ/115200 baud = 0.001259 сек.

Итого: путь туда-сюда через LAN->роутер-> WiFi->ESP->обработка-> ESP ->WiFi->роутер->LAN выходит примерно 2 мс.
Без обработки время пути TCP пакета от компа до стека TCP на ESP и обратно в комп указано в первой транзакции SYN на картинке и равно 0.00097 сек.
 

lexis-megavolt

New member
Классно что у вас все работает. Я не пойму почему у меня нет?
Можете проанализировать мой код?
Заранее благодарен

Код:
#include <ESP8266WiFi.h>

//#define PRINT_DEBUG
//how many clients should be able to telnet to this ESP8266
#define MAX_SRV_CLIENTS 2
const char* ssid = "**********";
const char* password = "**********";

WiFiServer MdbTcpServer(502);
WiFiClient MdbTcpServerClients[MAX_SRV_CLIENTS];


#define TIME_OUT 500ul
#define LN_BUFF 255
typedef enum {
  st_idle = 0,
  st_tcp_rx,
  st_tcp_rx_done,
  st_uart_rx,
  st_uart_done

} t_state;

int8_t num_client;
uint8_t buff2send[LN_BUFF];
uint16_t ln_buff_rx, ln_buff_tx;
t_state stateTR;
unsigned long time_start;

void DefVal()
{
  stateTR = st_idle;
  num_client = -1;
  ln_buff_rx = 0;
  ln_buff_tx = 0;
}



void setup() {
  Serial1.begin(115200);
  WiFi.setSleepMode(WIFI_NONE_SLEEP);
  WiFi.begin(ssid, password);
  Serial1.print("\nConnecting to "); Serial1.println(ssid);
  uint8_t i = 0;
  while (WiFi.status() != WL_CONNECTED && i++ < 20) delay(500);
  if(i == 21){
    Serial1.print("Could not connect to"); Serial1.println(ssid);
    while(1) delay(500);
  }

  DefVal();
  //start UART and the MdbTcpServer
  Serial.begin(115200);
  MdbTcpServer.begin();
  MdbTcpServer.setNoDelay(true);
#ifdef PRINT_DEBUG
  Serial1.print("Ready! Use 'telnet ");
  Serial1.print(WiFi.localIP());
#endif
}

void loop() {
  uint8_t i;
  uint16_t tmp_ln;
  //check if there are any new clients
  if (MdbTcpServer.hasClient()) { // если есть клиенты
    for (i = 0; i < MAX_SRV_CLIENTS; i++) {
      //find free/disconnected spot
      if (!MdbTcpServerClients || !MdbTcpServerClients.connected()) {
        MdbTcpServerClients.stop();
        #ifdef PRINT_DEBUG
          Serial.print("disconnect client = "); Serial.println(i);
        #endif
        MdbTcpServerClients = MdbTcpServer.available();
      }
    }
  }
  // перебираем данные в ожидании
  if (stateTR == st_idle) {
    //check clients for data
    for (i = 0; i < MAX_SRV_CLIENTS; i++) {
      if (++num_client > (MAX_SRV_CLIENTS - 1)) num_client = 0; // круговой перебор клиентов
      if (MdbTcpServerClients[num_client] && MdbTcpServerClients[num_client].connected()) {
        if (MdbTcpServerClients[num_client].available()) {
          ln_buff_rx = 0;
          ln_buff_tx = 0;
          stateTR = st_tcp_rx; // Начался прием!
          time_start = millis();
          #ifdef PRINT_DEBUG
            Serial.print("Start TCP RX:"); Serial.println(time_start);
          #endif
          break;
        }
      }
    }
  }
  // Анализ принятых пакетов
  if(stateTR == st_tcp_rx) {
    // есть данные
    size_t len = MdbTcpServerClients[num_client].available();
    if (len > 0){
      while(len){
        //get data from the TCP client and push it to the Buff
        buff2send[ln_buff_rx] = MdbTcpServerClients[num_client].read();
        ln_buff_rx++;
        len--;
        // контроль переполнения
        if (ln_buff_rx > (LN_BUFF - 1)) ln_buff_rx = 0;
        #ifdef PRINT_DEBUG
          Serial.print(buff2send[ln_buff_rx]); Serial.print(":byte ms:"); Serial.println(millis());
        #endif
      }
      time_start = millis(); // Сместим таймаут приема
    }
    // начнем анализ пакета
    if (ln_buff_rx > 8){
      // пришел корректный пакет
      #ifdef COUNT_LEN_PACKET
      tmp_ln = ((uint16_t)buff2send[4] << 8) | buff2send[5];
      if ((tmp_ln + 6) == ln_buff_rx)
      #endif
      {
        Serial.write(buff2send, ln_buff_rx);
        #ifdef PRINT_DEBUG
          Serial.print("uart RX from client = "); Serial.println(num_client);
        #endif
        stateTR = st_tcp_rx_done;
        ln_buff_tx = 0;
        time_start = millis();
      }
    }
  }
  // недождались корректного запроса по TCP
  if ((stateTR == st_tcp_rx) && ((time_start +  TIME_OUT) < millis())){
    stateTR = st_idle;
    ln_buff_rx = 0;
    ln_buff_tx = 0;
    #ifdef PRINT_DEBUG
      Serial.print("TCP packet drop"); Serial.print(buff2send[ln_buff_rx]); Serial.print(":byte ms:"); Serial.println(millis());
    #endif
  }
  // пришли данные по уарту
  if  (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read(); // данные из уарта забираем в любом случае
    #ifdef PRINT_DEBUG
      Serial.print(inChar);
    #endif
    // обработка пакета от уарта
    if (stateTR == st_tcp_rx_done || stateTR == st_uart_rx) {
      // add it to the buff:
      buff2send[ln_buff_tx] = inChar;
      ln_buff_tx++;
      stateTR = st_uart_rx;
      if (ln_buff_tx > (LN_BUFF - 1)) ln_buff_tx = 0;
      if (ln_buff_tx > 8){ // начнем анализ пакета
        // пришел корректный пакет
        #ifdef COUNT_LEN_PACKET
        tmp_ln = ((uint16_t)buff2send[4] << 8) | buff2send[5];
        if ((tmp_ln + 6) == ln_buff_tx)
        #endif
        {
          stateTR = st_uart_done;
          #ifdef PRINT_DEBUG
            Serial.println("st_uart_done");
          #endif
        }
      }
    }
  }
  // даные из уарта не пришли
  if ((stateTR == st_tcp_rx_done) && ((time_start +  TIME_OUT) < millis())){
    stateTR = st_idle;
    ln_buff_tx = 0;
  }
  // Отдаем данные из буфера в TCP
  if (stateTR == st_uart_done) {
    size_t len = ln_buff_tx;
    uint8_t sbuf[len];
    for (i = 0; i < ln_buff_tx; i++) {
      sbuf = buff2send;
    }
    //push UART data to connected client
    if (MdbTcpServerClients[num_client] && MdbTcpServerClients[num_client].connected()) {
      stateTR = st_idle;
      ln_buff_tx = 0;
      ln_buff_rx = 0;
      // потом сделать разбивку на пакеты по 64 байта
      MdbTcpServerClients[num_client].write(sbuf, len);
      MdbTcpServerClients[num_client].flush();
      #ifdef PRINT_DEBUG
        Serial.print("uart TX to "); Serial.println(num_client);
      #endif
      }
   
  }
}
 
Сверху Снизу