Перейти к содержимому

Почему я выбрал ESP-IDF для программирования ESP32, а не Arduino?

Данная статья – начало цикла статей, посвященных программированию популярного ESP32 на “родном” для этого микроконтроллера фреймворке ESP-IDF. Без использования Arduino. В данной статье рассмотрен вопрос, зачем же это все-таки нужно и почему в конце-концов я выбрал ESP32 как основу для всех своих последних проектов домашней автоматизации. Ведь существует огромное количество микроконтроллеров других производителей: PIC (Mictochip), AVR (Atmel), ATM (STMicroelectronics), ESP8266??? Некоторые из них обладают гораздо лучшими характеристиками, например по экономичности, да и стоят иногда дешевле…

Сразу оговорюсь: я не профессиональный разработчик ESPressif Iot Development Framework (ESP IDF) – такой же любитель, как и большинство из вас. Статей по программированию в Arduino – пруд пруди. А вот статей по ESP IDF – очень мало. Сам сталкивался с этим, когда осваивал. Поэтому, только спустя несколько лет, когда появилось какое-то более менее “системное” понимание возможностей и технологий, я решил поделиться этой информацией со всеми.
 
Советую Вам также ознакомиться с другой статьей: как и на чем программировать ESP32 и ESP8266 – в ней вы найдете краткое сравнение характеристик различных IDE и платформ. Возможно она покажется вам более полезной и беспристрастной, чем эта.

Очень скоро я понял, что мне критически важны удаленный контроль и удаленное управление устройствами. А это означает, что устройство должно иметь возможность подключения к сети, желательно посредством WiFi, чтобы не плодить паутину кабелей и проводов. Встроенный WiFi “на борту” имеют ESP8266 и ESP32 производства Espressif Systems. Да, я знаю, что есть платы сетевых интерфейсов для Arduino, но…

Кроме того, меня сильно смущал тот факт, что сложный и разветвленный функционал в однопоточном приложении сделать достаточно сложно – чтобы устройство вело себя стабильно, необходимо прилагать особые усилия, чтобы каждая из функций внутри основного цикла loop() выполнялась не дольше нескольких микросекунд. При росте сложности программы и наращивании функционала сделать это становится все сложнее и сложнее.

Кроме того, ESP8266 и ESP32 (и платы на их основе) достаточно дёшевы, широкого распространены и надежны.

 

Зачем нужно это удаленное управление?

Во-первых это позволяет минимизировать количество органов управления при создании устройств – в большинстве случаев для автоматизации какого-либо процесса не нужно подключать экран, использовать много кнопок и переключателей для управления. Не нужно создавать встроенные меню для настройки каких-либо параметров. Отображение данных и настройку параметров можно перенести на смартфон или компьютер. Это позволяет использовать в качестве корпуса стандартные различные разветвительные коробки для монтажа электропроводки – это дешево и выглядит достаточно аккуратно. Либо использовать достаточно дешевый стандартные корпуса. Примеры на фотографиях ниже. Конечно, при наличии 3D-принтера можно создать любой корпус, но лично у меня пока такой возможности нет.

Контроллер управления вентиляцией в санузле. Собрано в стандартной коробке для скрытого монтажа

Устройство в стандартной монтажной коробке TDM100x100мм под столешницей стола

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

В третьих, наличие доступа устройства в сеть позволяет осуществлять обновление прошивки устройства не только с помощью кабеля, но и “по воздуху” – так называемое OTA-обновление. Это существенно облегчает и ускоряет “массовую” перепрошивку “умных” устройств при внесении каких-либо изменений и дополнений. Процесс совершенствования прошивок не останавливается с момента, когда закручен последний винт в корпус устройства.

Замена ESP8266 на ESP32

Поначалу я использовал для своих поделок ESP8266, программируя их в уже привычной среде Arduino IDE. Однако достаточно быстро столкнулся с её недостатками. Это небольшое количество выводов для подключения периферии (особенно у платок ESP-01), только один вывод ADC и, увы, нестабильная работа даже на самых простых скетчах (впрочем, возможно, в этом были виноваты достаточно “кривоватые” внешние библиотеки, которые я использовал; либо мои кривые руки).

Потом я увидел и заказал на одной известной китайской торговой площадке отладочные платы с новым (по сравнению с ESP8266, разумеется) контроллером – ESP32 Devkit V4. По стоимости эти платы не намного выше ESP8266, и одно всем известное зеленое земноводное не сильно сопротивлялось при заказе.

Один из вариантов отладочных плат с ESP32. В основном я использую именно эти

Возможности ESP32 по сравнению с ESP8266 гораздо шире:

  • двухядерный процессор и гораздо больше памяти
  • многопоточная операционная система FreeRTOS на борту (даже в Arduino, не удивляйтесь – подробности ниже)
  • достаточно большое количество выводов для подключения датчиков и периферии
  • гораздо большее количество выводов ADC (АЦП – аналого-цифрового-преобразователя). Впрочем, на ESP32 ADC выводы достаточно коряво сделаны и далеко не все их можно использовать, оказывается (этому вопросу я планирую посвятить отдельную статью)
  • две отдельные шины I2C, которые можно использовать одновременно
  • отличная документация на сайте Espressif Systems. Конечно, документация на английском, но Гуголь легко справляется с переводом

В итоге, несколько платок с ESP8266 уже пару лет пылятся в коробочке, а количество заказанных ESP32 и устройств на них исчисляется десятками. В июле было отключено последнее устройство, которое, впрочем более-менее успешно работало несколько лет.

 

FreeRTOS и нафига она нужна…

Первые опыты с ESP32 я проводил в старой-доброй Arduino IDE. Привычный интерфейс, привычные loop() и setup(). Однако у меня сразу же возник закономерный вопрос – если Arduino это сугубо однопоточная система, а ESP32 имеет сразу два ядра, то как использовать это самое второе ядро?

Изучая примеры для ESP32, легко находится масса примеров программирования ESP32 с использованием операционной системы реального времени FreeRTOS для микроконтроллеров. Хорошо помню, что на тот момент изучать FreeRTOS у меня не было никакого, я сделал очередной проект “по образу и подобию” и закрыл тему.

Но любопытство дало свои результаты. Через какое-то время я обнаружил в файлах платформы Esp32 для Arduino файл-“обертку” для всех скетчей (%profile%\packages\esp32\hardware\esp32\%version%\cores\esp32\main.cpp). Содержимое этого файла таково:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "Arduino.h"

TaskHandle_t loopTaskHandle = NULL;

#if CONFIG_AUTOSTART_ARDUINO

bool loopTaskWDTEnabled;

void loopTask(void *pvParameters)
{
setup();
    for(;;) {
        if(loopTaskWDTEnabled){
            esp_task_wdt_reset();
        }
        loop();
    }
}

extern "C" void app_main()
{
    loopTaskWDTEnabled = false;
    initArduino();
    xTaskCreateUniversal(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, CONFIG_ARDUINO_RUNNING_CORE);
}

#endif

Эта “обертка” используется для всех скетчей, которые Вы создаете в классической Arduino IDE.

Какую информацию отсюда можно почерпнуть? Видно, что FreeRTOS всегда по умолчанию подключается к Вашему скетчу (через #include “freertos/FreeRTOS.h”), а любой классический скетч Arduino (то есть якобы написанный без использования задач) – это всегда задача FreeRTOS, которая выполняется на втором ядре процессора #1, и которой выделено всего 8192 байт стека! Сама FreeRTOS при этом выполняется на ядре процессора #0. Становится немного понятнее, как на ESP32 реализована работа с двумя ядрами.

Дальнейшее исследование данного вопроса привело на ресурсы, посвященные ESP-IDF. Оказалось, что ESP-IDF (Espressif IoT Development Framework) – это основной (legacy) фреймворк для программирования микроконтроллеров на базе ESP32, разработанный тем же Espressif Systems. А используемый в Arduino IDE фреймворк Arduino Core for ESP32 это адаптация ESP-IDF, чтобы втиснуть его в узкие рамки Arduino IDE.

Если вы продолжите программирование в данном классическом варианте (один скетч – одна задача), то ваш Arduino скетч получит только доступ только к 8192 байт стека (на ESP32 стек указывается именно в байтах, а не словах, как в классической FreeRTOS). В принципе, это достаточно много, даже слишком много. Почти наверняка вы даже не сможете его (стек задачи) полностью израсходовать (если только не будете предпринимать к этому специальных усилий, вроде 100500 аргументов в каждой функции). Остальную память вы сможете использовать в качестве общей кучи (heap). Подробнее о распределении памяти на ESP32 вы можете почитать в другой статье.

На самом деле, параллельно с вашим скетчем при этом будут работать ещё несколько задач, созданных и запущенных самим фреймворком Arduino Core for ESP32, например: планировщик задач, цикл событий, IPC и т.д., но это не отразится на вашем скетче. В принципе, на этом можно и закончить статью – вы вполне сможете написать рабочий классический скетч в классической Arduino IDE, но с ограничение стека 8192 байт.

Отсюда вывод: если Вы хотите использовать ESP32 “на всю катушку” придется осваивать FreeRTOS (как основу ESP-IDF)Хотите этого Вы или нет. Иначе это выглядит как “микроскопом гвозди забивать”.

ESP-IDF – основной framework для ESP32

ESP-IDF представляет собой основную среду разработки программного обеспечения для оборудования на основе чипа ESP32 от Espressif. ESP-IDF по сути это портированная на ESP32 версия FreeRTOS. Как вы, наверное, заметили, этот тот же старый – добрый Си и Си++, никакой принципиальной разницы в языках программирования нет. Если вы научились сносно разбираться в скетчах Arduino, то осваивать принципиально новый язык вам не придется. Разница будет заметна только в подходах к программированию для однозадачной среды и многозадачной.

В привычной для многих Arduino главной задачей каждой из функций в главном цикле loop() было как можно быстрее завершить свою работу и передать управление следующей. Взаимодействие между различными частями программы осуществлялось с помощью callback-ов. Использование delay() считается плохой практикой, так как это непосредственно повлияет на остальные части вашей программы.

В многозадачной среде каждый из модулей вашей прошивки работает поочередно, когда ОС выделяет задаче квант процессорного времени. И, в принципе delay() не мешает другим процессам, однако его использование всё равно можно считать нежелательным. Когда какая-либо из задач завершила очередной цикл своей работы (например отправила HTTPS-запрос), её следует отправить в ожидание следующего “входящего” события, и тогда ОС не будет “попусту” выделять ей процессорное время.

Возможности многозадачной операционной системы лично меня просто восхищают. Создав несколько задач для разных функций прошивки, можно больше не опасаться, что пауза в одной из задач вызовет завивание всего контроллера. Например отключение WiFi роутера не вызывает катастрофы в сервисах измерения данных с сенсоров и обработки результатов. ESP-IDF уже включает в себя встроенные драйверы, библиотеки и модули почти “на все случаи жизни”: GPIO, UART, Modbus, SPI, SDIO, I2C, I2S, PWM, WIFI, PING, клиент MQTT, HTTP(S) и многое, многое другое. Для основных прикладных задач больше не нужны библиотеки сторонних разработчиков – почти все необходимое уже включено в состав платформы. Просто отлично, на мой взгляд, реализовано хранение параметров на flash-памяти: не по адресам, а по именами переменных (это ооочень удобно, по сравнению с arduino-вскими способами). Всё это достаточно хорошо документировано и описано, есть много примеров.

ESP-IDF – это Open Source проект, что позволяет любому участвовать в совершенствовании проекта и устранении багов, если таковые вдруг будут найдены. В настоящий момент “в работе” у разработчиков ESP-IDF находится issue для добавления нового функционала для MQTT-клиента, а ещё раньше были исправлены две найденные мной проблемы.

Но, как всегда, есть “подводные камешки”.

Во-первых, придется отказаться от привычной Arduino IDE, так как возможности установить “чистый” ESP-IDF в Arduino IDE нет. А использовать “урезанный” вариант мне не хотелось. Впрочем, нет худа без добра – я перешел на гораздо более удобную и бесплатную IDE – VS Code (хотя и без недостатков).

Во-вторых: в ESP-IDF отсутствуют как класс драйвера для всевозможных датчиков и экранов (например DHT22, LCD1602 и т.д.). Производители плат создают драйвера для ArduinoIDE, так как она очень популярна; а вот для ESP-IDF драйвера придется написать самому, копаясь в даташитах, благо это не сложно. Но в этом есть и свои плюсы – написав собственный драйвер, вы лучше разберетесь как работает то или иное периферийное устройство, а заодно и драйвер можно улучшить. Например: пока я пользовался готовыми драйверами для датчиков температуры и влажности, я и понятия не имел, что внутри многих из них есть встроенные нагреватели для исправления дрейфа показаний во сильно влажной среде.

Впрочем, сейчас довольно много готовых драйверов можно найти на GitHub. В частности, вы можете бесплатно использовать и мои драйвера.

 

Плюсы и минусы ESP-IDF

В качестве некоторого итога статьи, приведу плюсы и минусы использования ESP-IDF в своих проектах, с моей точки зрения:

Достоинства:

  • “Родная” ( native ) для микроконтроллера среда программирования, предоставляемая разработчиком чипов. Уже только одно это говорит само за себя
  • Многопоточная операционная система жесткого реального времени позволяет легко наращивать функционал системы без усложнения прошивки и превращения кода в “клубок спагетти”
  • Возможность использования всех аппаратных возможностей чипа (например из под Arduino нет возможности одновременного использования двух шин I2C)
  • Возможность конфигурирования ESP-IDF под свои нужды и задачи (в Arduino IDE хотя и используется базовая часть ESP-IDF, но настройка опций невозможна)
  • Возможность произвольного конфигурирования разделов FLASH – памяти (в Arduino IDE есть только несколько заранее определенных схем разметки, сформированным каким-то дядей)
  • Использование циклов событий (это такой микро MQTT-брокер внутри RTOS) позволяет организовать “правильное” взаимодействие между различными функциональными модулями прошивки и избавиться от многоярусных callback-ов.
  • Встроенный MQTT клиент с outbox-ом (очередью отправки)
  • NVS-хранилище для хранения данных и параметров

Недостатки:

  • В “комплекте поставки” ESP-IDF отсутствуют драйвера для сенсоров, устройств, дисплеев и т.д. Сторонних библиотек драйверов для ESP-IDF создано пока гораздо меньше, чем для Arduino. Кроме того, вам придется поискать их вручную на GitHub или в реестре библиотек PlatformIO. Но в ESP-IDF есть всё необходимое, чтобы создать такой драйвер самому.
  • Придется отказаться от привычной для многих Arduino IDE.

Что лично для вас важнее – решать только вам.

Visual Studio Code + PlatformIO

Как я уже писал выше, чтобы начать “кодить” с использованием ESP-IDF, необходимо использовать другую IDE. Существует несколько вариантов, но самыми популярными вариантом является бесплатный Visual Studio Code. Но собственно VSCode это просто универсальный редактор для любых языков программирования, чтобы иметь возможность писать программы для ESP32, ESP8266 и других микроконтроллеров, необходимо установить специальное расширение – plugin. Существует как минимум два plugin-а, позволяющих работать с ESP-IDF:

  • родной plugin от Espressif, который позволяет работать только с ESP-IDF, собственно
  • популярный проект PlatformIO, который позволит вам работать с множеством самых разных микроконтроллеров и с помощью разных технологий. Кстати, PlatformIO легко позволяет продолжить работу с Arduino кодом, но в более комфортной обстановке.

На PlatformIO, собственно, я и остановился. Более подробно процесс установки VSCode и PlatformIO описан в другой статье.

Почему не Arduino?

PlatformIO позволяет работать с очень многими платформами и микроконтроллерами. В принципе, ничто не мешало мне продолжить работу с уже привычными платами. Но, поскольку я довольно плотно подсел на ESP32 и ESP-IDF, мне уже совсем не хочется распылять свои время и усилия на совершенно разный код, писать и модифицировать целый ворох различных библиотек и т.д. Я практически не собираю устройств “для опытов” – есть задача – решаем её, только так. Возможностей ESP32 с лихвой хватает для всех бытовых задач, цена позволяет поставить её в любую розетку – что еще нужно? Поэтому в настоящее время я покинул мир Arduino, возможно это временно, возможно навсегда. Прощай и спасибо за опыт!

 

Вместо заключения

Поскольку я довольно продолжительное время занимаюсь программированием для “взрослых” компьютеров, освоение многопоточного (многозадачного) программирования не вызывало у меня трудностей, однако у новичков некоторые моменты могут вызывать трудности, в частности синхронизация потоков, совместное использование ресурсов и т.д. Вот все эти моменты мы и будем обсуждать в следующих статьях. Пока я планирую поделиться с вами, с чего начать; и с какими трудностями лично мне пришлось столкнуться; какие модули и библиотеки и для чего можно использовать.

PS: Я не профессиональный программист встраиваемых систем (хотя профессиональный программист учетных программ и баз данных), и я не освоил и четверти всех возможностей, которые предоставляет ESP32 и ESP-IDF. Просто потому, что мне пока не нужно было использовать некоторые возможности. Предлагаю вам осваивать этот микроконтроллер и его среду программирования вместе.

Подписывайтесь, будет интересно… 👍

Полезные ссылки:

  1. Официальная документация по ESP-IDF (на английском)
  2. Андрей Курниц “FreeRTOS — операционная система для микроконтроллеров”
  3. FreeRTOS — практическое применение
  4. Справочник по Free RTOS API
  5. Канал на Дзене
  6. Мои репозитории

Пожалуйста, оцените статью:
[ 5 из 5, всего 2 оценок ]

8 комментариев для “Почему я выбрал ESP-IDF для программирования ESP32, а не Arduino?”

  1. Александр

    По мне так Ардуино проще…
    Распределение задач делается таймером… Можно даже одним.
    Второе ядро тоже можно задействовать…
    Мне после стм32 с ужасным с++ самое то программить на простой платформе, а не решать проблемы синтаксиса.
    Ардуино… Вещь!

    1. Не согласен, ардуино убогая платформа. Хотя и унифицированная.
      А проблемы синтаксиса и у адруино, и у esp-idf абсолютно одни и те же, потому что и там и там си или си++
      Ну что можно сказать – “если вы не любите кошек, значит вы не умеете их готовить”…

  2. Спасибо, ваши статьи хорошо аргументированы. Пожалуй, из “обзорных” статей по esp32, ваши – лучшие. Я согласен с тем, что проблема выбора платформы сугубо индивидуальна. Я переходил на ESP32 в ритме “надо вчера”. Потому, естественно, arduino framework (конечно же PlatformIO !). Потом попробовал ESP_IDF framework. Выдержал один день. Заплевал весь монитор. “Все не так”. Бросил, вернулся на ардуино. Эта статья побудила потратить несколько дней на еще одну попытку. Теперь могу более аргументировано сказать, почему я не согласен с тем. что ESP_IDF круче.
    1. Все высказывания “не поддерживает второе ядро, таймеры и прочее” – неправда. Да, если остаться в рамках setup-loop, то это так. Но никто не мешает использовать xTaskCreate и остальные методы freertos. И все сразу поддерживается и работает.
    2. Второй I2C – нужен очень редко. Как правило, если не хватило одного, то и двух не хватит. Несколько девайсов с одинаковым адресом? Копеечный I2C мультиплексор решает. Настройки параметров? Несколько раз их просмотрел и … так и не нашел ни одного, который бы меня интересовал. Но, возможно, кому-то и надо. Хотя, для ардуино эти параметры не имеют смысла, ибо не используются. Тоже самое с разделами флеша. Но, может, кому-то и надо…
    А вот дальше начинаются минусы….
    Сразу скажу, что “помигать лампочкой” – не задача для ESP32 и не буду такие рассматривать. А вот что-то серьезнее сразу возникают проблемы.
    1. С. Да, именно язык С. Смотришь на “нативный” ESP_IDF и детство вспоминаешь… Язык С был хорош в 90-е. Когда компьютеры были большие, а программы маленькие. Сейчас наоборот. Потому и возникли все эти С++/С#/Java/… Потому что модуль на 1000 строк написать на С будет стоить намного больше, чем на С++. И никакой производительностью и экномией памяти это не окупается. За час работі девелопера можно купить еще десяток ЕСП и сделать многопроцессорній кластер. Да, у espressif есть попытка сделать С++ фреймворк (https://github.com/espressif/esp-idf-cxx/). Последний коммит несколько месяцев назад. Значит, проект мертвый.
    2. К сожалению, организация кода в ESP_IDF крайне плохая. Даже, если посмотреть в ваш репозиторий. Работа с wifi – 1000+ строк кода!!! Это как? Это что??? Зачем надо было писать столько кода, если есть “нативный” фреймворк. Тот же код на ардуино – до сотни строк, если реализовать все эти же функции.
    3. Ну и еще насчет организации кода. Раз уж решил пробовать, то надо держаться. Решил я написать на ESP_IDF свой класс-обертку для wifi, чтобы пользоваться нормальной инкапсуляцией без всех этих “потрохов наружу”. Базировался на вашем примере и на примере от espressif. Это тихий ужас. Зачем мне кучи этих низкоуровневых вызовов? Я не хочу лезть в тонкости сетевых протоколов. Мне нужно соединение и передача MQTT. От WiFi мне нужно – передал ssid/password и получил сигнал о том. что все готово. Я не хочу конфигурировать непонятными мне константами непонятный netif. Где-то нахомутал и как-то неправильно инициализировал netif. Бывает… Но офигел от того, что где-то в потрохах это все упало с assert и контроллер перегрузился. И это было последней каплей! Я понимаю, что из-за моей ошибки wifi не смог инициализироваться. Я был готов получить ошибку и идти дальше. Именно такой подход вы и декларировали – какой-то поток упал, а остальные продолжили работать. Но espressif думает иначе. Это говорит о том, что у них очень несистемный подход к разработке.
    Потому, увы, но нет. Программисты espressif остались в 90-х годах прошлого века. Потому будем использовать альтернативы.
    Просто ради интереса попробовал .NET NanoFramework – классно, но прожорливый до такой степени, что, скорее всего, сдеалть на нем что-то, сложнее блинкера нереально.

    1. И тут я со многим не согласен.
      Но спорить и переубеждать не буду – живите с адруино, я не против 🙂
      Только учтите что адруино для есп тоже “поверх” esp-idf.

  3. “любой классический скетч Arduino (то есть якобы написанный без использования задач) – это всегда задача FreeRTOS, которая выполняется на втором ядре процессора #1, и которой выделено всего 8192 байт стека! Сама FreeRTOS при этом выполняется на ядре процессора #0.”

    А если ESPшка одноядерная, тогда как?

    1. Ядро процессора, на котором будет запущена задача по умолчанию, указана в макросе препроцессора CONFIG_ARDUINO_RUNNING_CORE.

      Соответственно одноядерных процессоров:
      CONFIG_ARDUINO_RUNNING_CORE = 0,
      для двухядерных:
      CONFIG_ARDUINO_RUNNING_CORE = 1

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *