Ethernet Library 2.0

sadman41
Offline
Зарегистрирован: 19.10.2016

Коллеги, конечно многие из вас следят за обновками и модными тенденциями в библиотеках Arduino, но, думаю, что есть и такие, как я - сидят себе на 1.6.11 и в ус не дуют, пока баг не словят. 

Так вот, для последних сообщаю, что в актуальных версиях Arduino IDE поставляется Ethernet library 2.0 (это не Ethernet2 для w5500) - feature list. Там многое зафиксено, но многое и не лежит на поверхности, а спрятано в исходниках.

Сильно я пока ее не прошерстил, из интересного нашел это (размер памяти под сокет можно переконфигурировать без особых извращений): 

// By default, each socket uses 2K buffers inside the Wiznet chip.  If
// MAX_SOCK_NUM is set to fewer than the chip's maximum, uncommenting
// this will use larger buffers within the Wiznet chip.  Large buffers
// can really help with UDP protocols like Artnet.  In theory larger
// buffers should allow faster TCP over high-latency links, but this
// does not always seem to work in practice (maybe Wiznet bugs?)
//#define ETHERNET_LARGE_BUFFERS
 
andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

sadman41 пишет:

 Ethernet library 2.0 (это не Ethernet2 для w5500) 

А какую лучше использовать? я немного с Ethernet2 для w5500 работаю, пока проблем не заметил.

sadman41
Offline
Зарегистрирован: 19.10.2016

Точную историю развития я не могу привести (не сильно следил), но ежели гадать по внутренностям, то могу предположить что Ethernet и Ethernet2 - это форки драйвера, написанного с демонстрационными целями корейцем из Wiznet.  Сначала Arduino.cc и пр. выпускался Ethernet Shield на W5100 и для него была изготовлена библиотека (включена в состав IDE) Ethernet.h. Через какое-то время вышел Ethernet Shield 2 на W5500 и в библиотеке Ethernet.h подменили нижний уровень (работающий с регистрами по SPI), оставив всё, что сверху, нетронутым. Обозвали это, стало быть Ethernet2.h. Оба варианта можно было считать библиотекой Ethernet v1.0.0.

Ethernet v2.0.0, поддерживающий одновременно W5100/W5200/W5500 пилился отдельно (вроде как) от команды Arduino товарищем Paul Stoffregen. Я какое-то время почитывал Issues, не находя для себя причин менять шило на мыло, но теперь вижу, что прожект вышел удачным, раз он включен в стоковый состав библиотек IDE. 

Я вчера подменил им старую стоковую 1.0.0 в одной из прошивок средней величины, интеграция прошла почти бескровно. Пришлось, правда, кое-что из использованных недокументированных и самопальных фич подпиливать - но я знал, на что шёл, когда свернул на кривую дорожку правки кода драйвера )) Пока наблюдаю за поведением макета, взрывов и самовольного выкачивания всего Интернета в EEPROM до данного момента не случилось.

Конечно объем объектного кода подрастет, так как выбор методы работы с конкретным чипом осуществляется не условной компиляцией, а автоопределением и if-ами.

Проблемы... Тут сложно сказать, потому что я несколько лет сидел на стоковом 1.0.0, писал что-то простенькое и тоже особо не наблюдал глюков. Но в более-менее сложных проектах с неустойчивой связью вылезает, конечно, сущность демодрайвера. Например, день назад (после чего и полез искать альтернативы) я закончил итерацию отладки ситуации в которой "веб-страница с Arduino показывается, а наружу данные не пушатся". Крайне странная ситуация - контроллер не висит, сетевой адаптер не висит, веб-интерфейс работает, а исходящих коннектов нет (но должны быть). Проиcходило это внезапно, на удаленном сегменте, два раза за полгода. На столе воспроизвести баг не удалось. Но, насколько я понял из исходника драйвера и навернутой вокруг диагностики, в том случае, если в процессе connect() возникает проблема, то сокет может подвиснуть в последнем состоянии стейт-машины чипа, но не по его вине - просто команда DISCONN не засылается, считается, что и так "все будет заеб...". Соотвественно - в следующей итерации посылки пакета данный сокет не может быть использован. Берется следующий... Драйвер "падает на нож и так три раза подряд". В итоге все сокеты как бы заняты, прошивка не может инициировать новый коннект. А веб-интерфейс... Ну, под него-то сокет был зарезервирован на старте, встал в LISTEN и как работал, так и работает. 

К тому же оказалось, что у connect() тупо нет программного таймаута. И если удаленный конец является слоупоком, то можно легко подвиснуть на N sec, а то и на watchdog нарваться.

И это только пара историй о драйвере v1.0.0 ))

В v2.0.0 часть тупых вещей исправлена. Насколько это так? Время покажет. По исходнику видно, что он переработан чувствительно.

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

ясно, спасибо, будем тестировать.

sadman41
Offline
Зарегистрирован: 19.10.2016

Подыму тему, так как руки дошли слегонца покопаться у неё внутрях.

Вот примерно таким образом выкидывается медленный клиент (в прежней версии библиотеке он мог висеть на проводе бесконечно или вообще влёгкую заDDoS-ить систему на W5100 в четыре хода):

  static EthernetClient ethClient;
  static uint32_t clientConnectTime;
  ...

  if (!ethClient) {
     ethClient = ethServer.accept();
     if (ethClient) {
        clientConnectTime = millis();
        Serial.print(F("[*] HTTP => New client ")); Serial.print(ethClient.remoteIP()); 
        Serial.print(F(" @ ")); Serial.println(clientConnectTime);
        ...
     } 
  }

  if (ethClient) {
     // 10 sec for all operations
     if ((millis() - clientConnectTime) > 10000UL) {
       ...
       Serial.print(F("[*] HTTP => DROP client ")); Serial.print(ethClient.remoteIP()); 
       Serial.print(F(" @ ")); Serial.println(millis()); 
       ethClient.stop();
       return;
     }

     // Read data from the socket
     if (ethClient.available()) {
        ...
     }
  }

Обратите внимание на способ получения активного сокета (выделено в коде): https://www.arduino.cc/en/Reference/EthernetServerAccept

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Да неужели у них дошли руки? :)))

sadman41
Offline
Зарегистрирован: 19.10.2016

Наверняка за это время те ардуинщики, кому интересны сетевые фокусы, прошерстили мануал и исходники библиотеки. Ну, а если нет, то кратко сообщу о тех нововведениях, которые мне показались полезными:

Ethernet.init() позволяет произвести предконфигурирование пина CS для нестандартных шилдов либо самоделок. Должен вызываться перед begin();
Ethernet.hardwareStatus() возвращает enum модели сетевого чипа (W5100, W5200, W5500 или никакой). Можно использовать для определения кол-ва доступных сокетов (теоретически) или для проверки чипа на наличие (вдруг он отстегнулся в полёте);
Ethernet.linkStatus() отдаёт состояние линка (позволяет обнаружить отключение кабеля или, теоретически, его дефективность). К сожалению W5100 не имеет соответствующего функционала на уровне чипа, поэтому с ним использование данной функции бессмысленно;
- if(server) показывает чем закончился begin(), есть ли свободные сокеты и т.д. Т.е. позволяет быстро определить способность системы обслуживать новые соединения в целом;
setConnectionTimeout() - крайне нужная штука. В прежней версии библиотеке connect() блокировал весь процессинг пока соединение не пройдёт удачно, не будет отвергнуто либо чип не наткнется на закрытый порт. Т.е. поведение функции было непредсказуемо. Сейчас же управление будет передано обратно, если коннект не состоялся в течении одной секунды (значение по-умолчанию, изменяемая величина);
- Ethernet.begin(mac) на самом деле может быть применена как Ethernet.begin(mac, timeout, responseTimeout), где timeout - время выделенное на все действия по получению адреса, responseTimeout - время, выделенное на каждый запрос к DHCP (их несколько). Таким образом - более не требуется висеть в begin() целую минуту при упавшем DHCP, можно быстро перейти к назначению статического IP;
EthernetClient.connected() теперь возвращает true даже если при входящем соединении данные не начали поступать. Ранее было невозможно отследить наличие открытого соединения до тех пор, пока в сокет не поступал хотя бы один байт данных извне;
- EthernetClient.status() - не описана, но существует. Любители пощупать голый чип могут узнать в каком состоянии находится сокет, с которым они работают или просмотреть состояния всех сокетов. Раньше приходилось использовать "дыру в системе" и брать информацию с экземпляра класса W5100. Если нужно или не забуду - приведу пример;
- EthernetClient.getSocketNumber() - возвращает номер привязанного к текущему коннекту сокета;

На этом пока всё, остальные фичи простым домоседам наврядли понадобятся.

sadman41
Offline
Зарегистрирован: 19.10.2016

Продолжаю ковыряться в Ethernet 2.0. Сегодня мне придётся расстроить любителей делать стабильность через механизмы сторожевого таймера. К сожалению, как и ранее, библиотека закрывает глаза на его существование, в чём можно убедиться даже без анализа исходных текстов, путём простого эксперимента.

Включаем USE_DHCP, вытаскиваем сетевой кабель из гнезда и смотрим в Serial Monitor... Наверняка кто-то думает, что ничего страшного тут нет - проскочим setup() и приключения закончились. Но не тут-то было. Для продления аренды IP-адреса необходимо вызвать Ethernet.maintain(), который делает такой же длинный запрос к DHCP-серверу, как и Ethernet.begin(mac). Любая задержка - и МК идёт в ребут. 

#include "avr/wdt.h"
#include <Ethernet.h>

//#define USE_DHCP

void printSocketsState(void);
uint8_t howMuchSocketsHave(void);

const uint32_t reportInterval = 5000;
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress staticIp(192, 168, 0, 42);
EthernetServer ethServer(80);

void setup() {
  uint8_t success;

  wdt_disable();
  wdt_enable(WDTO_2S);

  Serial.begin(115200);
  Serial.print("\nDebug started @ "); Serial.println(millis());

  Ethernet.linkStatus();

  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found");
    return;
  }

  Serial.print("Configure Ethernet using ");

#if defined(USE_DHCP)
  Serial.print("DHCP... ");
  success = (Ethernet.begin(mac) != 0);
  Serial.println(success ? "success" : "failed");
#else
  Serial.print("static IP address");
  success = false;
#endif

  if (!success) {
    Ethernet.begin(mac, staticIp);
  }
  ethServer.begin();
  Serial.print("\nWork on: "); Serial.print(Ethernet.localIP());
  Serial.print(" / "); Serial.println(Ethernet.subnetMask());
}

void loop() {
  static uint32_t lastReportTime;
  uint32_t nowTime;
  
  wdt_reset();
  Ethernet.maintain();

  nowTime = millis();
  if (nowTime - lastReportTime > reportInterval) {
    printSocketsState();
    lastReportTime = nowTime;
  }
}

// Returns number of socket onboard
uint8_t howMuchSocketsHave() {
  switch (Ethernet.hardwareStatus()) {
    case EthernetW5100:
      return 0x04;
    case EthernetW5500:
      return 0x08;
    case EthernetNoHardware:
    default:
      return 0x00;
  };
}

// Shows socket state
void printSocketsState() {
  uint8_t maxSockNum = howMuchSocketsHave();
  Serial.print("\nSockets report @ "); Serial.println(millis());

  if (0x00 == maxSockNum) {
    Serial.println("Hardware unsupported or not exist");
    return;
  }
  Serial.print("\t##\tState\tcurrent/last peer\n");

  for (uint8_t currentSocket = 0x00; currentSocket < maxSockNum; currentSocket++) {
    // Get socket status
    EthernetClient ethClient(currentSocket);
    uint8_t sockStatus = ethClient.status();

    Serial.print("\t0"); Serial.print(currentSocket); Serial.print("\t0x");
    if (sockStatus < 0x0A) {
      Serial.print("0");
    }
    Serial.print(sockStatus, HEX); Serial.print("\t");
    Serial.print(ethClient.remoteIP()); Serial.print(":"); Serial.println(ethClient.remotePort());
  }
}

Впрочем, если вы думаете, что с переходом на статический IP всё нормализуется, то мне придётся вас огорчить - проблему можно словить, например, прямо на .print(), так как он, в тёмных глубинах классов, вызывает EthernetClass::socketSend(), который содержит следующее:

	// if freebuf is available, start.
	do {
		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
		freesize = getSnTX_FSR(s);
		status = W5100.readSnSR(s);
		SPI.endTransaction();
		if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT)) {
			ret = 0;
			break;
		}
		yield();
	} while (freesize < ret);

Таким образом, в случае проблем с отправкой данных, функция блокирует выполнение остального кода на неопределённое время и... Hello watchdog's reset!

Примечание#1: в коде, который расположен в начале, приведена обещанная функция просмотра состояния сокетов, которая, при определённой сноровке, помогает понять отчего заклинило обмен по Ethernet.

Примечание#2: наверняка вы заметили бесхозный Ethernet.linkStatus(). Но он там для дела - без него Ethernet.hardwareStatus(), вызванный до Ethernet.begin() работает неправильно. Думаю, что это тривиальный баг.

P.S. Совсем забыл. Чтобы изменить состояние сокета - достаточно чем-нибудь (например телнет-клиентом) приконнектится на 80-й порт ардуино-сервера.

mixail844
Offline
Зарегистрирован: 30.04.2012

andycat пишет:

sadman41 пишет:

 Ethernet library 2.0 (это не Ethernet2 для w5500) 

А какую лучше использовать? я немного с Ethernet2 для w5500 работаю, пока проблем не заметил.

кину свои 5 копеек в тему W5500 : 

пользовался для написания серверной части Modbus TCP на C , столкнулся с таким "глюком"  : если сконфигурировать в W5500 сокет TCP с буффером больше 4KB и флагом SF_TCP_NODELAY ,функция send отрабатывает "верно" но данные по факту не отправляются ( даже при пакете до 200 байт) а при 4KB и меньше все шлется . прошу у кого есть шилд на данной железке подтвердить или опровергнуть .Пользовался драйверами из официального репозитория Wiznet на GitHub ~месячной давности .

sadman41
Offline
Зарегистрирован: 19.10.2016
sadman41
Offline
Зарегистрирован: 19.10.2016

Небольшая ремарка на предмет W5100 vs W5500:

Чем выигрывает W5500.

- W5500 поддерживает восемь сокетов, т.е. позволяет иметь восемь одновременных соединений. Редко кому нужно, конечно.  Размер одного сокета можно раздуть до 16Кб (за счёт урезания остальных). Так же он умеет коммуницировать с МК в потоковом режиме: SPI Operation Mode supports two modes, the Variable Length Data Mode and the Fixed Length Data Mode. Variable Length Data Mode (VDM) - Data Length is controlled by SCSn. Fixed Length Data Mode (FDM) - 1, 2, or 4 Byte Data Length. Т.е. МК открыл SPI-сессию и копипастит гигабайты данных не останавливаясь. 
- W5100 ограничен четырьмя сокетами с максимальным размером в 8Кб. К сожалению справляется только с однобайтовым FDM: МК открыл сессию, считал/записал один байт данных, закрыл сессию. Открыл... закрыл... Медленно и печально. Правда, у него есть адский Direct Bus Interface mode с 15-битной адресной и 8-битной шиной данных, но эта история не про Ардуину.

Чем выигрывает W5100 (лично для меня).

W5100 не умничает и не лезет помогать программисту,  когда его не просят: например, если у него не осталось доступных сокетов, находящихся в состоянии LISTEN, то он оставляет управление TCP-сессией удалённой (инициирующей) стороне. Она сама решает сколько ожидать открытия сессии;
W5500 открывает любой коннект и делает ему reject, если  доступных сокетов в режиме LISTEN не осталось. И даже, если порт назначения в TCP-пакете не совпадает с любым заданным в чипе, - всё равно делает reject.

Таким образом часть соединений будет отвергнута, если МК сильно загружен не успевает переводить сокеты в LISTEN, или же получает частые, но короткие сессии. А это наврядли может понравится удалённой системе. Например - с Web-сервера, который внезапно захостился на Arduino, будет недополучена часть картинок или CSS-файл.

Вы можете убедиться в этом самостоятельно, проэкспериментировав с опциями, расположенными в Ethernet.h:

#define MAX_SOCK_NUM 1
#define ETHERNET_LARGE_BUFFERS

Просто откройте телнетом два соединения на вашу Arduino....

АртДуино
Offline
Зарегистрирован: 19.08.2015

Доброго дня.

У меня эта библиотека на Arduino Due + Arduino Ethernet R2 не работает.

Пробовал различные варианты подсоединения пинов и инициализации. Компилировал демонстрационный проект. Ничего не помогло. Выдает ошибку: Ethernet shield was not found.

С библиотеками 1.х работает без проблем.

Что делать?

АртДуино
Offline
Зарегистрирован: 19.08.2015

Проблема решена.

foxvlad@yandex.ru
Offline
Зарегистрирован: 08.11.2015

sadman41 пишет:

Небольшая ремарка на предмет W5100 vs W5500:

Чем выигрывает W5500.

Уважаемый sadman41, может вы подскажете как победить одну беду или где-то в библиотеке или тогданезгаю где стоит задержка примерно в секунду. Когда создаю UDP прослушку сокетов на Arduino - все хорошо, "НО" после получения пакета Arduino ждет как будто delay(1000) и игнорит пакеты,проходит это время и опять принимает,причем это не случайно,а постоянно.

sadman41
Offline
Зарегистрирован: 19.10.2016

В самой библиотеке я таких delay() не наблюдал.

Без исследования исходного кода и воспроизведения проблемы на реальном железе на Ваш вопрос ответить невозможно.

miks69
Offline
Зарегистрирован: 16.02.2020

Добрый день, есть Arduino Mega 2560 R3 + Ethernet W5100 shield. Загружаем библиотеку Ethernet v2.0, открываем Examples\WebServer, компилируем и запускаем скетч примера, который в ответ на запрос браузера формирует простую HTML-страницу с автоматическим обновлением каждые 5 сек, и наблюдаем следующую картину: после загрузки страницы на 3-м или 4-м обновлении страницы браузер зависает. Смотрим в Serial-монитор и видим, что очередной запрос принят скетчем не полностью. После принудительного обновления страницы в браузере все повторяется. Тенденция, однако..

Что это, глючный шилд или баг в библиотеке?

sadman41
Offline
Зарегистрирован: 19.10.2016

Вот на моём. Никаких затыков.

Ethernet WebServer Example
server is at 192.168.0.55

new client #0 @ 1065

GET / HTTP/1.1
Host: 192.168.0.55
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.0.55/
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7

....

client disconnected

new client #105 @ 311973

GET /favicon.ico HTTP/1.1
Host: 192.168.0.55
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://192.168.0.55/
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7


 

sadman41
Offline
Зарегистрирован: 19.10.2016

Друзья, я пару недель в фоновом режиме позанимался с ICMP и накропал пару небольших "библиотек". В кавычках - потому, что классически подключить их возможности нет. Паразитируя на Ethernet Library 2.0, они используют пару функций, до которых штатно не дотянешься. 

Первая "библиотечка" реализует функционал ICMP. Вторая, погоняя первой, делает ICMP Ping и ICMP Traceroute. Обе утилиты запускаются как в блокирующем режиме, так и в асинхронно. Теоретически они должны работать и с W5500 и с W5100 (начинал я с одним, а добивал - с другим). 

Я не совсем уверен что все баги выловлены и API приведено к удобному состоянию, поэтому если кто-то из увлекающихся может потестить со знанием дела - будет великолепно. На всякие гитхабы пока выкладывать не готов. Если кто-то готов погонять - отпишитесь.

workpage
Offline
Зарегистрирован: 17.05.2020

Пытался подружить esp32 и w5500.

Лажа какая-то.

Начнём с того, что в одном скетче не могут находиться одновременно

#include <WiFi.h> 
#include <WebServer.h>
#include <Ethernet.h>

Ошибка компиляции. Переменная передачи значения не соответвует стандарту arduino. Приплыли. WebServer.h со всеми его плюшками с ethernet использовать нельзя. Те же костыли с websocket.

Пришлось переделывать готовый прототип на lan8720. 

Во первых эта 8720 в 10(!) раз дешевле в закупке, обвязки в 5 раз меньше, так ещё 100% совместимость с esp32.

Может кому-нибудь эта информация будет полезна.

sadman41
Offline
Зарегистрирован: 19.10.2016

Покажите драйвер под 8720, совместимый со средой Arduino.

workpage
Offline
Зарегистрирован: 17.05.2020

sadman41 пишет:

Покажите драйвер под 8720, совместимый со средой Arduino.

Для esp32:

#include <ETH.h> 

 

workpage
Offline
Зарегистрирован: 17.05.2020

sadman41 пишет:

Покажите драйвер под 8720, совместимый со средой Arduino.

См также 

http://arduino.ru/forum/apparatnye-voprosy/podklyuchenie-ethernet-lan8720-i-esp32-devkit-c-esp32-devkit-v1

sadman41
Offline
Зарегистрирован: 19.10.2016

А ссылкой на гитхаб и для ATMega328?

workpage
Offline
Зарегистрирован: 17.05.2020

Мега не потянет. Там тактирование 50мГц...

sadman41
Offline
Зарегистрирован: 19.10.2016

Ещё немного подчистил и погонял ICMP Ping library, выложил для критики и предложений: https://github.com/zbx-sadman/Arduino/tree/master/ICMP