Добрый день, уважаемый читатель!
У ESP32, как известно, имеется встроенный сетевой интерфейс для подключения к сети WiFi. И в подавляющем большинстве случаев все им и пользуются. Но WiFi не всегда и не везде применим, иногда все решает только подключение через кабель – Ethernet. Например, если ваше устройство будет работать в закрытом металлическом ящике или на значительном удалении от какой-либо точки доступа. ESP32 отнюдь не ограничен возможностями WiFi и BT, на нем вполне можно использовать и Ethernet, и GPRS/LTE (PPP over UART).
Некоторое время назад, я публиковал обзор платы Kincony KC868-A16, на которой предусмотрен ethernet-интерфейс. Вот на её примере я и попробую рассказать, как подключить ESP32 к сети не по воздуху, а кабелем.
Немного теории
Когда достаточно давно я написал статью, как подключать ESP32 с использованием ESP-IDF к сети WiFi, там было приведено описание шагов, которые необходимо предпринять для создания сетевого подключения. В частности – необходимость написания обработчиков событий WiFi и инициализация промежуточного TCP/IP интерфейса ESP-NETIF. При этом читатели иногда оставляли под статьей примерно такие комментарии: “а зачем мне знать про все эти ESP-NETIF и низкоуровневые процедуры“? Действительно, а зачем? Зачем espressif так заморочился и наворотил сложностей? Неужели нельзя было сделать по простому, по ардуински – “wifi_connect()”, а все остальное спрятать от излишне любопытных пользователей внутри платформы ESP-IDF?
Дело в том, что адаптер ESP-NETIF (NETwork InterFace) служит посредником между драйвером ввода-вывода (WiFi, ETHERNET, GPRS…) и сетевым стеком, обеспечивая маршрутизацию пакетов данных между ними. Этот абстрактный слой позволяет микроконтроллеру автоматически переключаться между WiFi или Ethernet соединениями автоматически, без использования дополнительного кода, но с использованием заранее заданных программистом приоритетов. Кроме того, ESP-NETIF полностью изолирует ваш прикладной код от сетевого стека ESP-IDF – для всего, что вам может потребоваться, вы должны использовать либо NETIF, либо более высокоуровневые функции. ESP-IDF в настоящее время реализует ESP-NETIF только для библиотеки lwIP TCP/IP. Однако сам адаптер не зависит от конкретной реализации TCP/IP и допускает различные варианты. Подробнее об NETIF читайте в официальном руководстве, (как всегда немного запутанном). Ну и на этом, про NETIF в рамках данной статьи, пожалуй достаточно.
Последовательность действий для подключения к сети через кабель почти ничем не отличается от подключения к сети WiFi, даже немного проще – ведь при проводном подключении не нам не потребуется авторизация. Те же самые обработчики сетевых событий, примерно та же самая их последовательность:
- Установить сетевой драйвер MAC и PHY, соответствующий вашем чипу.
- Создать новый экземпляр ESP-NETIF для ethernet-подключения. При этом мы можем задать приоритет подключения, с помощью которого система будет определять, какой сетевой интерфейс следует использовать в первую очередь, по возможности.
- И, как всегда в китайских поделках, приклеить созданный экземпляр ESP-NETIF к сетевому драйверу. Да, да, с помощью клея, правда виртуального.
- Создать и зарегистрировать обработчики событий ETHERNET_EVENT_START, ETHERNET_EVENT_STOP, ETHERNET_EVENT_CONNECTED, ETHERNET_EVENT_DISCONNECTED, IP_EVENT_ETH_GOT_IP
- Нажать красную кнопку с надписью esp_eth_start.
Об этом и пойдет речь в данной статье.
Принципы передачи данных в сети Ethernet
В начале нам необходимо прояснить, каким именно физическим способом мы будем подключать микроконтроллер к сети. Как и в случае с другими интерфейсами, нам потребуется некое устройство – приемопередатчик, которое сможет отправлять и получать пакеты данных, преобразовывая их в электрические сигналы. И если приемопередатчик WiFi уже встроен в чип ESP32, то в случае с ethernet нам придется задействовать внешнее устройство.
Если говорить по простому, то драйвер Ethernet-подключения состоит из двух основных частей – MAC и PHY:
- MAC – реализует канальный уровень передачи данных (аббревиатура от англ. Media Access Control), это цифровой интерфейс, который отвечает за управление и подключение физической среды физического уровня.
- PHY – реализует физический уровень, то есть это уже приемопередатчик электрических сигналов (аббревиатура от англ. Physical layer), который “занимается” формированием и получением аналоговых сигналов, которые “бегут” по витой паре сетевого кабеля к роутеру и обратно. Интерфейс PHY состоит из двух независимых каналов для передатчика и приемника.
При этом интерфейс MAC может быть реализован в самой ESP32, а вот для PHY уровня нам по любому потребуется дополнительная микросхемка. ESP-IDF на текущий момент поддерживает несколько ethernet-чипов, в том числе “внутренние” EMAC и “внешние” с SPI-интерфейсом:
- Davicom DM9051 SPI to Ethernet MAC Controller
- Texas Instuments DP83848 Single Port 10/100 Mb/s Ethernet Physical Layer Transceiver
- IC+ IP101 Single Port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver
- Micrel KSZ80xx 10Base-T/100Base-TX/100Base-FX Physical Layer Transceiver
- Microchip KSZ8851SNL Single-Port Ethernet Controller with SPI
- SMSC LAN87xx Small Footprint RMII 10/100 Ethernet Transceiver
- Realtek RTL8201 10/100M Fast Ethernet Phyceiver
- WIZnet W5500 Hardwired TCP/IP embedded Ethernet controller
В микросхемах DM9051, KSZ8851 и W5500 реализован как PHY, так и MAC-контроллер, поэтому их можно связать с ESP32 с помощью SPI-интерфейса. В данной статье я не буду их рассматривать, просто потому, что с ними не работал.
Для остальных чипов для связи уровней MAC и PHY придется воспользоваться интерфейсом MII (аббревиатура от англ. Media Independent Interface). Каждый уровень имеет свои данные, тактовые и управляющие сигналы. Интерфейс управления — это двухсигнальный интерфейс: один — тактовый сигнал, а другой — сигнал данных. Через интерфейс управления верхние уровни могут полностью отслеживать и контролировать PHY. Всего для интерфейса данных MII требуется аж 18 сигналов. Для микроконтроллеров это слишком жирно – почти все GPIO уйдут на обслуживание Ethernet. Поэтому в ESP32 для связи с общественностью PHY уровнем может быть использована облегченная версия интерфейса RMII (аббревиатура от англ. Reduced Media Independent Interface), которая требует всего 9 сигналов (GPIO). Но при этом PHY и MAC синхронизируются уже только одним и тем же источником таковых сигналов REF_CLK 50MHz, который должен обладать достаточно высокой стабильностью.
ESP-IDF Ethernet API предусматривает три варианта генерации опорного сигнала:
- a) C помощью внутреннего генератора ESP 25 Mhz с умножителем
- b) C помощью внешнего кварцевого генератора 50 MHz
- c) Некоторые контроллеры EMAC могут генерировать REF_CLC помощью внутренней высокоточной ФАПЧ.
В случае использования внутреннего тактового сигнала следует выбрать режим CONFIG_ETH_RMII_CLK_OUTPUT, в случае использования внешнего тактового сигнала – режим CONFIG_ETH_RMII_CLK_INPUT. При этом следует помнить, что для вывода тактового сигнала можно использовать только GPIO0 / GPIO16 / GPIO17, а для его получения извне – только GPIO0. Да и GPIO16 / GPIO17 могут быть заняты внешним SPIRAM модулем памяти. Поэтому для использования в качестве опорного сигнала в некоторых случаях остается только GPIO0. Но GPIO0 – довольно важный strapping pin, который отвечает за перевод контроллера в режим загрузки прошивки. Поэтому, если вы собираетесь использовать внешний опорный генератор, то на время сброса ESP тактовый сигнал необходимо отключить, программным или “аппаратным” способом, например отключив питание кварцевого генератора.
На Kincony KC868-A16 использован чип линейки LAN87xx с внутренним тактовым генератором, и для передачи тактового сигнала использован GPIO17, так как SPIRAM не предусмотрено.
В итоге параметры Ethernet интерфейса на Kincony KC868-A16 выглядят так:
На этом, полагаю, с физическими основами, в данном конкретном случае уже можно и закончить. Давайте прекратим это словоблудие и начнем уже давить кнопочки компуктера.
Программирование
Перво-наперво необходимо подключить к проекту необходимые API:
Объявляем глобальные (в рамках модуля) переменные для хранения указателей на различные драйверы и интерфейсы:
Для чего они нужны – будет понятно ниже.
Устанавливаем драйвер
Теперь можно установить драйвер “встроенного” MAC. Делается это не просто, а очень просто:
После этого можно установить драйвер и для железяки:
Здесь я “выбираю” нужный драйвер с помощью условных макросов препроцессора, но это не обязательно, можно выбрать один и сразу.
Когда все готово, можно уже установить сам драйвер:
Код выглядит громоздко, но на самом деле работает только одна строчка. Итак, поздравляю вас, самый первый этап мы выполнили. Идем дальше.
Создаем системный цикл событий и обработчики
Так как вся система связи, как и в случае с wifi, работает на событиях, нам нужно запустить цикл событий по умолчанию. Но он может быть запущен ранее, поэтому просто игнорируем ошибку ESP_ERR_INVALID_STATE:
Обработчиков у меня два. В принципе, ничего особо важного они не делают, просто выводят сообщения в лог и ретранслируют события дальше, в прикладной цикл событий – по ним уже запускаются другие задачи и процессы. Измените их по своему вкусу:
Инициализируем TCP/IP
Теперь все готово для того, чтобы создать новый экземпляр NETIF специально для Ethernet:
Обратите внимание! Здесь я задал максимально возможный приоритет для Ethernet-интерфейса – 32767. Поэтому когда подключен сетевой кабель, все пакеты будут идти именно через этот интерфейс. Но стоит нам отключить кабель от платы, wifi автоматически вступает в работу (при условии, что он подключен, конечно). Можно так же подправить и библиотеку wifi, указав желаемый приоритет и там, но я пока не стал. Работает и так.
Клеим!
Позвольте! – скажут внимательные читатели – А как же связан NETIF и MAC? И будут совершенно правы! Ведь мы ранее установили драйвер, но при создании NETIF нигде его не использовали…
Берем баночку цианоакрилата или хотя бы термоклей, намазываем и крепко прижимаем друг к другу на несколько секунд. А если серьезно, то так:
Ну и если все до сих прошло гладко, без сучка и задоринки, то можно подключать созданные ранее обработчики:
3, 2, 1, ПУСК!
Просто оставлю это здесь:
Подключаем кабель, и наслаждаемся тем, как мигают индикаторы и весело, с песнями и плясками, бегут по проводам маленькие байтики….
Как вы убедились, ничего сложного в этом нет. Получилось даже быстрее и проще, чем с WiFi-подключениями.
Ссылки
- Исходный вариант статьи, где можно скопировать код: https://kotyara12.ru/iot/esp32-ethernet/
- Готовую библиотеку можно посмотреть и даже скачать тута: github.com/kotyara12/reEthernet. Однако её следует использовать с некоей осмотрительностью, так как она хочет утащить за собой еще небольшую кучку других моих библиотек
и завалить вас с головой. Но, к счастью, от них можно избавится, например rLog заменить на стандартный esp_log, а reEsp32 и reEvents убрать вовсе, немного изменив код. - Справочное руководство по ethernet API здеся: docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_eth.html. Со временем ссылка может устареть по не зависящим от меня причинам. Прошу понять и простить.
Засим разрешите откланяться, с вами был ваш покорный слуга Александр aka kotyara12. И вы, это, заходите если что….