UDP.endPacket() вешает ардуино на ~2 секунды

Petra
Offline
Зарегистрирован: 28.02.2014

Добрый день.

Ардуино ведет непрерывный обмен с клиентом по TCP, время от времени нужно синхронизировать время с NTP севером по UDP. Если НТП сервер доступен, то никаких проблем нет, но если он по какой-то причине отключен (а такое вполне допустимо), то при попытке отправить UDP пакет ардуино подвисает на ~2 секунды.

Почему такое происходит? Как с этим бороться?

Arduino Mega 2560 + Ethernet Shield

maksim
Offline
Зарегистрирован: 12.02.2012

В файле \arduino\libraries\Ethernet\utility\socket.cpp

int sendUDP(SOCKET s)
{
  W5100.execCmdSn(s, Sock_SEND);
		
  /* +2008.01 bj */
  while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) 
  {
    if (W5100.readSnIR(s) & SnIR::TIMEOUT)
    {
      /* +2008.01 [bj]: clear interrupt */
      W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT));
      return 0;
    }
  }

  /* +2008.01 bj */	
  W5100.writeSnIR(s, SnIR::SEND_OK);

  /* Sent ok */
  return 1;
}

Перепите функцию так, что бы она не блокировала программу.

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

maksim пишет:

Перепите функцию так, что бы она не блокировала программу.

Так а зачем? Работа по TIMEOUT, уж 2 секунды непроблема или проблема (автору)?

maksim
Offline
Зарегистрирован: 12.02.2012

Garry пишет:

или проблема (автору)?

А вы как думаете? Или предпологаете что, ТС создал тему просто для ознакомления форума с тем фактом, что UDP.endPacket() вешает ардуино на ~2 секунды? (риторика)

Petra
Offline
Зарегистрирован: 28.02.2014

maksim пишет:

Перепите функцию так, что бы она не блокировала программу.

Удалил весь цикл while, вроде все работает, спасибо!

Цитата:

Так а зачем? Работа по TIMEOUT, уж 2 секунды непроблема или проблема (автору)?

В нем то и проблема. По TCP идет обмен с периодом 10мс, задержка в 2 секунды весьма критична.

maksim
Offline
Зарегистрирован: 12.02.2012

Petra пишет:

Удалил весь цикл while, вроде все работает, спасибо!

Это в дальнейшем может привести к неприятным последствиям, например, с более медленным инетом не будет вообще никакой синхранизации...

Добавьте свой таймаут в 10 мсек, который будет не критичен.

int sendUDP(SOCKET s)
{
  W5100.execCmdSn(s, Sock_SEND);
		
  uint32_t time = millis();
  while ( ((W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK) && millis()-time < 10) 
  {
    if (W5100.readSnIR(s) & SnIR::TIMEOUT)
    {
      /* +2008.01 [bj]: clear interrupt */
      W5100.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT));
      return 0;
    }
  }

  /* +2008.01 bj */	
  W5100.writeSnIR(s, SnIR::SEND_OK);

  /* Sent ok */
  return 1;
}

 

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

maksim пишет:

Garry пишет:

или проблема (автору)?

А вы как думаете? Или предпологаете что, ТС создал тему просто для ознакомления форума с тем фактом, что UDP.endPacket() вешает ардуино на ~2 секунды? (риторика)

Давайте язвить не будем! Да и спрашивал я не у вас...

Теперь автору:

По существу, в Eth модуле есть 2 регистра связанные с задержкой:

RTR (Retry Time-value Register) [R/W] [0x0017 – 0x0018] [0x07D0]  и 

RCR (Retry Count Register) [R/W] [0x0019] [0x08]

Сейчас они настроены так, что для ARP получается задержка 1.8 сек, видимо ваш случай. Попробуйте настроить эти регистры под ваши желания, а удалить блок кода в библиотеке всегда успеете :) !
 
 
 

 

Petra
Offline
Зарегистрирован: 28.02.2014

Спасибо, так удобнее, чем библиотеку править.

VoroninES
Offline
Зарегистрирован: 29.07.2014

Не совсем понял, как настраивать эти регистры... Видимо, слегка нуб в электронике(

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

А такой вопрос на ESP8266:

Udp.beginPacket(ip, 8283);
  Udp.write(buf);
  Serial.println(Udp.endPacket());
 //delay(1000);
 ESP.restart();

не отправляет данные. Если раскоментировать задержку то отправляет. Как понять необходимую задержку, а точнее что проверить чтоб понять что пакет ушел? Serial.println(Udp.endPacket()); возвращает 1.

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

p-a-h-a пишет:

не отправляет данные. Если раскоментировать задержку то отправляет. Как понять необходимую задержку, а точнее что проверить чтоб понять что пакет ушел? Serial.println(Udp.endPacket()); возвращает 1.

Уменьшить делей до 1 миллис. Если не отправит, то до двух.. Ну и т/д

Очевидно, что процессор отрабатывает рестарт быстрее, чем модем отправку байта

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Rumata пишет:

Уменьшить делей до 1 миллис. Если не отправит, то до двух.. Ну и т/д

Ну, вообще-то, зная скорость соединения и объем данных, величина задержки определяется по правилам арифметики (а не подбором).

Но вопрос, мне кажется, в другом: Udp.flush();

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

andriano пишет:

Ну, вообще-то, зная скорость соединения и объем данных, величина задержки определяется по правилам арифметики (а не подбором).

Но вопрос, мне кажется, в другом: Udp.flush();

С первым не поспоришь. А flush() тут не при чем. endPacket() фактически выполняет передачу пакета

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Как это ни при чем?

void setup() {
  Serial.begin(115200);
  Serial.println("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHI");
  Serial.flush();
  for(byte i = 0; i < 10; i++) {
    Serial.println(Serial.availableForWrite());
    delay(1);
  }
}

void loop() {}

Посмотрите разницу так и с закомментированным flush().

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

andriano пишет:

Как это ни при чем?

Посмотрите разницу так и с закомментированным flush().

void WiFiUDP::flush(){
  if(!rx_buffer) return;
  cbuf *b = rx_buffer;
  rx_buffer = 0;
  delete b;
}

Чем же UDP.flush() ускоряет отправку? Если только вместо delay() его использовать?

 

UPD вообще то, если бы вопрошавший p-a-h-a взял на себя труд проверить гипотезу c flush() в своем проекте, было бы интересно

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

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

 

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Спасибо за ответы. flush я вписывал перед сном еще до того как вопрос задал. Задержки методом тыка не подобрать т.к. иногда короткой хватает, чаще нет. Роутер кривой, в нем еще большая проблема. Изначально стоит задача отправить пакет на погодный сервер narodmon.ru:8283. Протоколы UDP, TCP, Json. Через роутер TP-link по TCP c отключенным DHCP пакет данных уходит за 0,27сек успев снять 4 показания погодных данных. Сейчас столкнулся с роутером Netis. Через него 1,1 сек. Стал разбираться, выяснилось что роутер тупит, причем wifi сеть соединяется быстро а вот WiFiClient client; client.connect(rtcData.Host, 8283); как раз занимают 0,7-0,8 секунды. Хотел через UDP попробовать выкрутится, но не тут то было. В общем гадать думаю смысла нет, грешу на кривой роутер. 

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

p-a-h-a, дурацкий вопрос можно? Нафига вообще  ESP.restart();?

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

В длинном коде прибора ESP.deepSleep(330e6); Для отладки написал короткий код с немедленной перезагрузкой. Особой разницы в данном примере между сном и перезагрузкой не вижу.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Rumata пишет:

endPacket() фактически выполняет передачу пакета

Rumata пишет:

andriano пишет:

Как это ни при чем?

Посмотрите разницу так и с закомментированным flush().

void WiFiUDP::flush(){
  if(!rx_buffer) return;
  cbuf *b = rx_buffer;
  rx_buffer = 0;
  delete b;
}

Чем же UDP.flush() ускоряет отправку? Если только вместо delay() его использовать?

Возможно.

Я предположил, что UDP аналогичен Serial, но, похоже ошибся: в Serial нет endPacket(), а flush() работает по-другому.