Здесь описаны решения проблем, которые связаны с программированием ESP32 в среде разработки ESP-IDF.
[Общие замечания]
Хорошая новость - среда разработки ESP-IDF [4] бесплатная, и к тому же кроссплатформенная, т. е. будет работать одинаково (почти) и на Windows, и на Linux, и на MacOS. Плохая новость - к сожалению, среда основана на Eclipse, и процесс компиляции плотно завязан на скрипты компиляции Python, поэтому приготовьтесь к тому, что будете много времени тратить на компиляцию проекта, особенно после изменения его опций компиляции. На Linux компиляция работает быстрее, чем на Windows. Поэтому компьютер обязательно должен быть современный, памяти не меньше 16 гигабайт, и обязательно рабочий диск должен быть SSD.
Известная проблема, связанная с созданием Launch Target, когда при создании нового проекта или его импорте не была подключена к COM-порту отладочная плата. В этом случае после процесса компиляции проекта в окне сообщений сборки (CDT Build Console) может появиться сообщение:
No esp launch target found. Please create/select the correct 'Launch Target'
Проблема была в том, процесс сборки требует указания наличия цели запуска (Launch Target). Цель запуска это обычно отладочная плата, подключенная через USB, и она видна на компьютере как виртуальный COM-порт.
Если Launch Target для проекта не создана или не выбрана, то сборка не завершится. Launch Target создается в среде ESP-IDF по стандартной процедуре, описанной в [1]:
1. Подключите плату ESP32 к порту USB. На компьютере должен появиться последовательный порт (COM-порт на Windows). 2. В среде ESP-IDF разверните третий выпадающий список, выберите New Launch Target. 3. В поле Name укажите любое имя. В поле IDF Target выберите esp32. В поле Serial Port выберите порт подключения платы. Кликните на кнопку Finish для сохранения конфигурации.
Перед запуском сборки выберите эту конфигурацию.
[Глюк ESP-IDF, связанный с созданием Launch Target]
Если проект был создан без подключенной платы, то будет наблюдаться глюк, когда нельзя создать цель запуска. Т. е. если пройти все описанные выше шаги New Launch Target, то цель запуска не будет создана, и компиляция будет неизменно заканчиваться ошибкой "No esp launch target found. Please create/select the correct 'Launch Target'".
Решить эту проблему можно следующим образом: создайте/импортируйте проект заново. После этого будет нормально работать New Launch Target. Мало того, в выпадающем списке магически появятся все цели, которые были до этого созданы неудачными попытками.
Даже на современном компьютере (64-разрядная операционная система Windows 10, процессор x64 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz, диск SSD, память 16 гигабайт) компиляция самого простого проекта blink может занимать 5 минут и больше. Что можно сделать с этим?
1. Отключите на время компиляции антивирус и Windows Defender.
2. Убедитесь, что среда ESP-IDF и компилируемый проект находится на диске SSD.
3. Посмотрите, чем загружена система в момент компиляции (с помощью Диспетчера задач) и закройте все ненужные сервисы и программы, которые больше всего отнимают процессорное время.
4. Настройте утилиту make на использование нескольких ядер процессора (Make -jN [2]).
5. Попробуйте перенесите мало изменяемый код в двоичные библиотеки.
6. Если совсем ничего не помогает, то перейдите с Windows на Linux.
1. Обязательно (!) подключить плату ESP32-C3-DevKitC-02v1.1 через USB к компьютеру. На компьютере должен появиться COM-порт.
2. Сконфигурировать New Launch Target (третий выпадающий список на панели инструментов) для появившегося COM-порта и используемого процессора ESP32-C3.
3. File -> New -> Espressif IDF Project -> blink.
4. idf.py set-target ESP32-C3, идем пить кофе.
5. F5, Двойной клик на sdkconfig, Example Configuration -> RMT - Addressable LED, RMT Channel 0, Blink GPIO number 8.
6. Serial flasher config -> Flash size 4 MB.
7. Component config -> FreeRTOS -> Run FreeRTOS only on first core.
Модули исходного кода добавляются в проект путем редактирования файлов CMakeLists.txt. Обычно такой файл есть в корневом каталоге проекта, а также в подкаталогах main и components проекта.
Как добавить файл с исходным кодом в проект, процесс по шагам:
1. Скопируйте *.c и его заголовочный файл *.h в подкаталог main. Предположим, это был модуль OneButton.c и заголовок OneButton.h.
2. Откройте в подкаталоге main файл CMakeLists.txt, добавьте модуль OneButton.c в проект следующим образом:
Список добавляемых исходных файлов нужно вставлять через пробел, каждое имя файла нужно заключать в двойные кавычки. Можно также добавлять каждый файл на отдельной строке:
3. Перекомпилируйте проект командой idf.py build. Система сборки обнаружит изменение в CMakeLists.txt, и в процесс компиляции будет добавлен новый модуль.
Подробнее про систему сборки и файлы CMakeLists см. [6].
Предположим, у нас есть плата ESP32-С3-WROOM-02, у которой трехцветный RGB-светодиод WS2812 подключен к ножке порта GPIO18. Как добавить поддержку драйвера RGB и управлять этим светодиодом, процесс по шагам:
1. Создайте в подкаталоге main проекта файл Kconfig.projbuild со следующим содержимым (в этом файле текст должен быть только английским):
menu "Example control RGB-LED WS2812"
choice BLINK_LED
prompt "Blink LED type"
default BLINK_LED_GPIO if IDF_TARGET_ESP32
default BLINK_LED_RMT
help
Defines the default peripheral for blink example
config BLINK_LED_RMT_CHANNEL
depends on BLINK_LED_RMT
int "RMT Channel"
range 0 7
default 0
help
Set the RMT peripheral channel.
ESP32 RMT channel from 0 to 7
ESP32-S2 RMT channel from 0 to 3
ESP32-S3 RMT channel from 0 to 3
ESP32-C3 RMT channel from 0 to 1
config BLINK_GPIO
int "Blink GPIO number"
range 0 48
default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32H2
default 18 if IDF_TARGET_ESP32S2
default 48 if IDF_TARGET_ESP32S3
default 5
help
GPIO number (IOxx) to blink on and off or the RMT signal for the addressable LED.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
config BLINK_PERIOD
int "Blink period in ms"
range 10 3600000
default 1000
help
Define the blinking period in milliseconds.
endmenu
2. Запустите команду idf.py menuconfig, и в разделе настроек "Example control RGB-LED WS2812" поменяйте следующие опции:
Blink LED type -> RMT - Addressable LED Blink GPIO number 18
Сохраните настройки (S) и выйдите из menuconfig (Esc).
3. Откройте файл CMakeLists.txt, который находится в корневом каталоге проекта. Перед директивой include добавьте директиву set, указывающую на дополнительный компонент led_strip:
4. Добавьте в основной модуль проекта (где находится функция app_main) следующий код:
#include "led_strip.h"
staticuint8_t s_led_state =0;
staticled_strip_t*pStrip_a;
staticvoidconfigure_led(void)
{
ESP_LOGI("configure_led", "Example configured to blink addressable LED!");
/* LED strip initialization with the GPIO and pixels number*/
pStrip_a = led_strip_init(CONFIG_BLINK_LED_RMT_CHANNEL, CONFIG_BLINK_GPIO, 1);
/* Set all LED off to clear all pixels */
pStrip_a->clear(pStrip_a, 50);
}
staticvoidblink_led(void)
{
/* If the addressable LED is enabled */if (s_led_state) {
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
pStrip_a->set_pixel(pStrip_a, 0, 16, 16, 16);
/* Refresh the strip to send data */
pStrip_a->refresh(pStrip_a, 100);
} else {
/* Set all LED off to clear all pixels */
pStrip_a->clear(pStrip_a, 50);
}
}
voidapp_main(void)
{
configure_led();
while(1)
{
/* Toggle the LED state */
blink_led();
s_led_state =!s_led_state;
vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS);
}
}
• Проблемы с питанием - уровень меньше 3.3V, просадки. • Проблемы с сигналами подключения SPI flash. • Проблемы с сигналами приема и передачи UART, через который происходит взаимодействие с загрузчиком.
Подробно решение таких проблем описано в статье [7].
После компиляции проектов примеров, поставляемых вместе со средой программирования ESP-IDF, в корневой папке проекта создается каталог esp_idf_components с кучей подкаталогов, причем они все пустые.
Структура каталогов этой папки esp_idf_components полностью совпадает со структурой каталогов папки c:\Espressif\frameworks\esp-idf-v4.4\components\, но эти каталоги уже не пустые, там находится исходный код компонентов, которые могут использоваться в проекте.
Возникает законный вопрос - зачем нужен каталог esp_idf_components с пустыми папками?
Оказывается, эти пустые папки каталоги нужны для того, чтобы можно было по желанию менять поведение некоторых компонентов, путем правки его исходного кода, и не затрагивая при этом исходный код оригинала. Для этого модуль исходного кода, который нужно исправить, копируется в соответствующий пустой подкаталог папки esp_idf_components, и в файл CMakeLists.txt проекта добавляется ссылка на копируемый файл. После этого система сборки будет брать для компиляции этот модуль вместо исходного, который находился в папке c:\Espressif\frameworks\esp-idf-v4.4\components\.
Покажу на примере проекта Console Example. Предположим, что нужно поменять интерфейс команд консоли. Процесс по шагам:
1. Скопируйте модуль commands.c из папки c:\Espressif\frameworks\esp-idf-v4.4\components\console\ в папку c:\Espressif\frameworks\esp-idf-v4.4\workspace\console_basic\esp_idf_components\console\.
2. Откройте на редактирование файл main\CMakeLists.txt проекта, добавьте в него ссылку на скопированный модуль:
3. На некоторых SDK для этого есть специальные функции, например макрос EMBED_FILES системы сборки ESP-IDF (см. врезку "Пример CMakeLists компонента" [6]).
В устройстве понадобилось использовать вывод GPIO2 и как вход для ADC1, и как вход для вывода устройства из режима низкого энергопотребления deep sleep. Конечно, не одновременно, а в разные моменты времени жизни приложения.
Однако при этом столкнулся с проблемой: если при пробуждении настроить GPIO2 как вход АЦП, а потом при уходе в сон попытаться перенастроить этот вывод на пробуждение из deep sleep, то функция пробуждения перестает работать. Если точнее, то устройство пробуждается сразу после ухода в сон, как будто на входе появился пробуждающий уровень лог. 0, хотя на самом деле ничего такого нет.
// Далее настройка пробуждения вызовами esp_deep_sleep_enable_gpio_wakeup
// и esp_sleep_pd_config, и затем уход в сон через esp_deep_sleep_start.
Интересно, что вызов gpio_reset_pin не восстанавливает функционал вывода из deep sleep, хотя по описанию вызов gpio_reset_pin должен возвратить вывод GPIO в состояние по умолчанию (выбор функции порта ввод/вывода общего назначения, разрешение pullup и запрет работы на вход и выход).
Squiggles are disabled for this translation unit (/home/username/myproject/src/main.c).
Решение проблемы:
1. Добавьте явное определение "compilerPath" в конфигурацию VS Code. Для этого в файле c_cpp_properties.json, который находится в папке .vscode корневого каталога проекта, путь до используемого компилятора в свойстве compilerPath, например "compilerPath": "/usr/bin/gcc":
В некоторых случаях бывает нужно подавить предупреждение компилятора, что переменная была определена и ей присвоено значение, но она после этого не используется. Например, это может случиться при использовании макроса ESP_GOTO_ON_ERROR, когда переменная ret, куда макрос записывает возвращаемое значение, не используется. Пример:
TEvent evt;
for(uint8_t i=0; i <3; i++)
{
esp_err_t ret = ESP_OK; // компилятор выдаст предупреждение на// не используемую переменную ret...
ESP_GOTO_ON_ERROR(pdTRUE == xQueueReceive(queue, &evt, 100) ? ESP_OK : ESP_FAIL,
err_measure, __FUNCTION__, "Failed to read voltage");
ESP_LOGI(__FUNCTION__, "%.3fV", voltage);err_measure:
vTaskDelay(100/portTICK_PERIOD_MS);
}
Устранить выдачу предупреждения можно следующим способом:
esp_err_t ret = ESP_OK;
(void)ret; // подавляет warning на не используемую переменную ret
Возможно дважды применен атрибут IRAM_ATTR к одной и той же функции - один раз в объявлении заголовка (или в static-объявлении функции), и второй раз в реализации. Например:
Вызов vTaskDelete(NULL) используется, чтобы задача могла удалить саму себя. Особенность такого вызова в том, что он должен быть обязательно размещен в конце функции задачи. Только при выполнении этого условия состояние задачи перейдет в eDeleted.
Если же разместить vTaskDelete(NULL) в любом другом месте тела функции задачи, то её состояние навсегда останется eRunning.