ESP32 + Arduino Core + FreeRTOS + Blynk = дом с зачатками разума

Цели проекта


Как то так вышло, что я построил свой дом, каркасник. В моём лакшери ауле нет газа и не предвидится в ближайшее время, потому и выбрал каркасник — всё остальное, для меня, было бы очень дорого топить электричеством. Ну и ещё потому что это одна из самых дешевых технологий.
Ок, раскидал трубы по дому, повесил батареи, котёл, вроде тепло, но что то не то.


Прислушавшись к себе, я понял, что это жаба, которой не нравится, что пока меня нет дома (12-16 часов в сутки), отопление работает. А могло бы и не работатать, включаться только перед приходом, благо каркасник обладает небольшой инерционностью и позволяет быстро поднять температуру. Та же ситуация когда куда то надолго уехать из дома. Ну и вообще, бегать, крутить ручку котла при изменениях температуры на улице — как то не кошерно.


Стало понятно, что без автоматизации никуда, благо хоть котёл из самых простых, но имеет контакты для подключения реле внешнего управления. Конечно, можно было бы сразу купить котёл со всеми нужными функциями, но для меня такие котлы стоят как то негуманно. Плюс хотелось поприседать мозгами, пописать что то для души, изучить немного C, пусть и в ардуино варианте.


Собственно о требованиях:


  • управление температурой в доме по уставке
  • управление температурой теплоносителя в зависимости от температуры на улице или вручную
  • временные зоны с разными уставками, днём холоднее, ночью горячее
  • автоматический режим, с авто переходом день-ночь
  • ручной режим, без автопереходов, для выходных
  • режим без автоматики, где можно вручную задать любую температуру теплоносителя и включить/выключить котёл
  • управление отоплением локально, с кнопок и экрана и через сайт/мобильное приложение

Это было в начале, а потом меня понесло и добавились:


  • управление уличным фонарём (LED прожектором)
  • сигнализация на основе датчика движения, сирены и уличного фонаря
  • учёт энергии потреблённой котлом за день/месяц/год + за каждый месяц года
  • режим сигнализация только медленным миганием фонаря
  • режим сигнализации быстрым миганием фонаря и короткими гудками сирены
  • режим сигнализации быстрым миганием фонаря и постоянным воем сирены

Цель написания статьи — поделиться опытом, описать что то на русском, что я не смог найти в интернете. Думаю, статья будет полезна начинающим Arduino самодельщикам, которые уже немного знакомы с программированием, т.к. совсем уж базовые вещи я не описывал. Я старался писать код как можно более понятным, надеюсь это у меня получилось.


Что было в начале


Изначально проект был реализован на дикой связке Arduino Nano + ESP8266, но ESP не как шилд, а как отдельное устройство. Почему так? Да потому что это всё у меня уже было, а денег не было от слова совсем, поэтому покупать новое железо не хотелось принципиально. Почему ESP не как шилд? Сейчас уже даже и не вспомню.


Arduino рулила всеми процессами, потому что имела необходимое количество GPIO, а ESP отправляла все данные на сервер Blynk, потому что умела в интернет и не имела достаточно GPIO. Соединялись они собой через UART, и пересылали JSON с данными друг другу. Схема необычная, но проработала год почти без нареканий. Кому интересно могут посмотреть кодок.


Сразу оговорюсь, программировать я тогда не сильно умел (да и сейчас хотелось бы лучше), поэтому беременным и детям лучше не смотреть. К тому же писалось всё в Arduino IDE, не к ночи будет помянута, что сильно ограничивало в плане рефакторинга, очень уж там всё примитивно.


Железо


Итак, прошёл год, финансы позволили купить ESP32 devkit v1, которая имеет достаточно GPIO, умеет в интернет и вообще супер контроллер. Кроме шуток, по итогам работы она мне сильно понравилась.


Перечень железа:


  • ESP32 devkit v1 noname China
  • 3 датчика температуры DS18B20, температуры внутри дома, снаружи и температуры теплоносителя в трубах
  • блок из 4 реле
  • pir датчик HC-SR501

Схему рисовать не буду, думаю всё будет понятно из макросов с названиями пинов.


Почему FreeRTOS и Arduino Core


На Arduino написаны куча библиотек, в частности тот же самый Blynk, поэтому от Arduino Core особо то и не уйдёшь.


FreeRTOS потому что позволяет организовать работу маленькой железки похожей на работу полноценного промышленного контроллера. Каждую задачу можно вынести в свою таску, останавливать её, запускать, создавать когда надо, удалять — всё это намного гибче, чем написание длинной портянки Arduino кода, когда в итоге всё выполняется по очереди в функции loop.


При использовании FreeRTOS каждый таск выполнится в строго заданное время, лишь бы хватило мощности процессора. Напротив, в Arduino весь код выполняется в одной функции, в одном потоке, если что то притормозило — остальные задачи выполнятся с задержкой. Особенно это заметно при управлении быстрыми процессами, в этом проекте это мигание фонарём и гудки сиреной, будут рассмотрены ниже.


Про логику


Про FreeRTOS таски


→ Ссылка на весь кодок проекта


Итак, при использовании FreeRTOS функция setup играет роль функции main, точки входа в приложение, в ней создаются FreeRTOS tasks (далее таски), функцию loop можно не использовать вообще.


Рассмотрим небольшой таск по вычислению температуры теплоносителя:


void calculate_water_temp(void *pvParameters)
{
   while (true)
        {
        if (heating_mode == 3) {}
        else
        {
            if (temp_outside > -20)
                max_water_temp = 60;
            if (temp_outside <= -20 && temp_outside > -25)
                max_water_temp = 65;
            if (temp_outside <= -25 && temp_outside > -30)
                max_water_temp = 70;
            if (temp_outside <= -30)
                max_water_temp = 85;
        }
        vTaskDelay(1000 / portTICK_RATE_MS);
    }
}

Объявляется как функция, которая должна принимать в себя _void pvParameters, внутри функции организуется бесконечный цикл, я использовал while (true).


Производится нехитрый расчёт температуры (если позволяет режим работы) и затем таск усыпляется vTaskDelay(1000 / portTICK_RATE_MS) на 1 секунду. В этом режиме он не потребляет процессорное время, переменные, с которыми работал таск, другими словами контекст, сохраняется в стек, что бы достать их оттуда, когда придёт время.


Далее таск необходимо создать в setup. Делается это вызовом метода xTaskCreate:


xTaskCreate(calculate_water_temp, "calculate_water_temp", 2048, NULL, 1, NULL);


Много аргументов, но для нас являются значимыми calculate_water_temp — имя функции, содержащей код таска и 2048 — размер стека в байтах.


Размер стека изначально ставил всем по 1024 байт, далее вычислял нужный методом тыка, если контроллер начинал падать с переполнением стека (что видно из вывод в uart), я просто увеличивал размер стека в 2 раза, если не помогало — ещё в 2 раза и так пока не заработает. Конечно это не слишком экономит память, но ESP32 имеет её достаточно, в моём случае можно было не заморачиваться с этим.


Также можно задать хендл для таска — ручка, с помощью которой таском можно управлять после создания, например — удалить. Это последний NULL в примере. Создаётся хендл так:


TaskHandle_t slow_blink_handle;


Далее, при создании таска в параметр xTaskCreate передаётся указатель на хендл:


xTaskCreate(outside_lamp_blinks, "outside_lamp_blynk", 10000, (void *)1000, 1, &slow_blink_handle);


И если мы хотим удалить таск, делаем так:


vTaskDelete(slow_blink_handle);


Как это используется можно можно посмотреть в коде таски panic_control.


Про FreeRTOS мьютексы


Мьютекс используется для исключения конфликтов между тасками при доступе к ресурсам типа uart, wifi и т.п. В моём случае понадобились мьютексы для wifi и доступа к флеш памяти.


Создаём ссылку на мьютекс:


SemaphoreHandle_t wifi_mutex;


В setup создаём мьютекс:


wifi_mutex = xSemaphoreCreateMutex();


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


xSemaphoreTake(wifi_mutex, portMAX_DELAY);


portMAX_DELAY — ждать бесконечно пока ресурс и мьютекс не освободятся другими тасками, всё это время таска будет спать.


После работы с ресурсом отдаём мьютекс что бы им могли пользоваться другие:


xSemaphoreGive(wifi_mutex);


Подробнее код можно поглядеть в таске send_data_to_blynk.


На практике неиспользование мьютексов не было заметно при работе контроллера, но при JTAG дебаге постоянно сыпались ошибки, которые исчезли после использования мьютексов.


Краткое описание тасок


get_temps — получение температуры с датчиков, раз в 30 сек, чаще не надо.
get_time_task — получение времени, с NTP серверов. Раньше время получалось из RTC модуля DS3231, но он стал глючить после года работы, поэтому я решил избавиться от него вовсе. Я решил что для меня это не несёт особых последствий, в основном время влияет на переключение временной зоны обогрева — день или ночь. Если интернет пропадёт во время работы контроллера, время просто застынет, временная зона просто останется прежней. Если контроллер выключиться и после включения не будет интернета, то время постоянно будет 0:00:00 — режим обогрева ночью.
calculate_water_temp — рассматривался выше.
detect_pir_move — получение сигнала движения с датчика HC-SR501. Датчик формирует логическую единицу +3.3В при обнаружении движения, что и обнаруживается с помощью digitalRead, кстати, пин для обнаружения для данного датчика должен быть подтянут к GND — pinMode(pir_pin, INPUT_PULLDOWN);
heating_control — переключение режимов отопления.
out_lamp_control — управление уличным фонарём.
panic_control — управление сиреной и прожектором при обнаружении движения. Для создания эффекта гудков сирены и мигания фонарём используются отдельные таски, outside_lamp_blinks и siren_beeps. При использовании FreeRTOS мигание и гудки работают просто идеально, ровно с теми интервалами что задано, на их работу не влияют другие таски, т.к. они живут в отдельных потоках. FreeRTOS гарантирует что код в таске выполнится в заданное время. При реализации этих функций в loop всё работало не так гладко, т.к. влияло выполнение другого кода.
guard_control — управление режимами охраны.
send_data_to_blynk — отправка данных в приложение Blynk.
run_blynk — таск для запуска Blynk.run() как того требует методичка по использованию Blynk. Насколько я понял это требуется для получения данных из приложения в контроллер. Вообще Blynk.run()должно быть в loop, но я принципиально не хотел туда ничего класть и сделал отдельным таском.
write_setting_to_pref — запись уставок и режимов работы для того что бы подхватить их после перезагрузки. О pref будет рассказано ниже.
count_heated_hours — подсчёт времени работы котла. Я сделал просто, если котёл включен в момента запуска таска(раз в 30 секунд), во флеш памяти значение по нужному ключу инкрементируется на единицу.
send_heated_hours_to_app — в этом таске значения извлекаются и умножаются на 0.00833 (1/120 часа), полученные часы работы котла отсылаются в приложение Blynk.
feed_watchdog — покормить Watchdog. Пришлось написать watchdog, т.к. раз в несколько дней контроллер мог зависнуть. С чем это связано — непонятно, может быть какие то помехи по питанию, но использование watchdog решает эту проблему. Таймер срабатывания watchdog 10 секунд, ничего страшного если контроллер не будет доступен в течение 10 секунд.
heart_beat — таск с пульсом. Когда я прохожу мимо контроллера, мне хочется знать что он работает нормально. Т.к. на моей плате нет встроенного светодиода, пришлось использовать светодиод UART — установить Serial.begin(9600); и писать в UART длинную строку. Работает неплохо.


ESP32 NVS wear leveling


Следующие описания довольно грубые, буквально на пальцах, только что бы передать суть вопроса. Более подробно


Для хранения данных в энергонезависимой памяти в Arduino используется EEPROM память. Это память небольшого объёма, в которой можно записывать и стирать каждый байт отдельно, в то время как flash память стирается только секторами.


В ESP32 нет EEPROM, но зато есть как правило 4 Mb flash памяти, в которой можно создавать разделы — для прошивки контроллера или для хранения пользовательских данных. Разделы для пользовательских данных бывают нескольких типов — NVS, FATFS, SPIFFS. Выбирать следует исходя из предполагаемого к записи типа данных.


Т.к. все записываемые данные в этом проекте типа Int, я выбрал NVS — Non-Volitile Storage. Этот тип разделов хорошо подходит для сохранения небольших по размеру, часто перезаписываемых данных. Что бы понять почему так, следует немного углубиться в организацию NVS.


Как и EEPROM, так и FLASH имеют ограничение на перезапись данных, байт в EEPROM может быть перезаписан от 100000 до 1 000 000 раз, сектор FLASH — так же. Если записывать данные раз в секунду, то получим 60сек х 60 мин х 24ч = 86 400 раз/сутки. То есть в таком режиме байт продержится 11 дней, что как бы немного. После чего байт станет недоступен для записи и чтения.


Для сглаживания этой проблемы, функции update() put() библиотеки EEPROM Arduino записывают данные только при изменении. То есть можно писать каждую секунду какие то уставки и коды режимов, которые изменяются довольно редко.


В NVS используется другой способ управления степенью износа (wear leveling). Как упоминалось выше, данные в сектор flash можно писать частями, но стереть можно только весь сектор. Поэтому запись данных в NVS осуществляется в своего рода журнал, этот журнал разделяется на страницы, которая помещается в одном секторе flash памяти. Запись данных осуществляется парами ключ: значение. По сути, это даже проще, чем с EEPROM, т.к. работать со значащим названием проще, чем с адресом в памяти. Upd: длина ключа не более 15 символов!


Если сначала записать значение 1 по ключу somekey, а затем записать значение 2 по тому же ключу, то первое значение не удалится, только пометится как удалённое (Erased), а в журнал добавиться новая запись:



При попытке прочитать данные по ключу somekey будет возвращено последнее значение данного ключа. Т.к. журнал общий, то значения разных ключей хранятся рядом друг с другом по мере записи.


У страницы есть статус, Empty — пустая, без записей, Active — в неё в данный момент пишутся данные, Full — заполнена, писать в неё нельзя. Как только на странице кончается место, она из
Active переходит в Full, а следующая Empty страница становится Active.



Насколько я понял из документации на сайте Espressif и различных форумов, очищение страниц начинается когда свободные страницы подходят к концу. Если быть точнее, то согласно вот этому, стирание произойдёт когда останется только 1 свободная страница.


Если страницу нужно очистить, то актуальные записи (Non-Erased), перемещаются на другую страницу, и страница затирается.


Таким образом, операция запись-стирание для каждой конкретной страницы происходит довольно таки редко, чем больше страниц — тем реже. Исходя из этого, я увеличил размер NVS раздела до 1 MB, при моих темпах записи этого хватит лет на 170, чего в общем то достаточно. Об изменении размера NVS раздела будет далее.


Для удобной работы с NVS в ESP32 для Arduino Core написана удобная библиотечка Preferences, как с ней работать написано здесь.


Немного о VisualGDB


Как только я начал работать с Arduino IDE, меня сразу удивил убогий функционал по сравнению с Visual Studio. Говорят, что VS тоже не фонтан, хотя меня устраивает, но писать что то более 50 строк в Arduino IDE мучительно больно и мучительно долго. Таким образом встал вопрос о выборе IDE для разработки. Т.к. я знаком с VS, я остановился на VisualGDB.


После Arduino IDE, разработка для ESP32 становится просто раем. Чего только стоят переход к определению, поиск вызовов в проекте и возможность переименовать переменную.


Изменение таблицы разделов ESP32 при работе с VisualGDB


Как говорилось выше, таблицу разделом ESP32 можно менять, рассмотрим как это можно сделать.
Таблица редактируется в виде csv файла, по умолчанию VisualGDB пишет следующую таблицу:


Name,   Type, SubType, Offset,  Size, Flags  
nvs,      data, nvs,     0x9000,  0x5000,  
otadata,  data, ota,     0xe000,  0x2000,  
app0,     app,  ota_0,   0x10000, 0x140000,  
app1,     app,  ota_1,   0x150000,0x140000,  
spiffs,   data, spiffs,  0x290000,0x170000,

Здесь мы видим раздел под NVS, два раздела под приложения и ещё несколько разделов. Из нюансов можно отметить, что app0(ваше приложение), всегда должно быть записано по смещению 0x10000, начиная от нулевого адреса, иначе загрузчик его не обнаружит. Также, смещения должны быть подобраны так, что бы разделы не "налезали" друг на друга. Сама таблица разделов пишется по смещению 0x8000. Как видно, размер NVS в данном случае 0x5000 — 20KB, что не очень много.


Я модифицировал таблицу разделов следующим образом:


Name,   Type, SubType, Offset,  Size, Flags  
app0,     app,  ota_0,   0x10000, 0x140000,  
nvs,      data, nvs,     ,  1M,  
otadata,  data, ota,     ,  0x2000,  
spiffs,   data, spiffs,  ,  0x170000,

Не забудьте добавить решётку перед Name, если будете пользоваться этой таблицей, это нужно что бы эта строка считалась комментарием.


Как видно, размер NVS увеличен до 1 MB. Если не указывать смещения, то раздел будет начинаться сразу за предыдущим, таким образом, достаточно указать смещение только для app0. CSV файлы можно редактировать в блокноте как txt и потом у сохранённого файла поменять разрешение на csv.


Далее, таблицу разделов надо преобразовать в бинарник, т.к. в контроллер она попадает именно в таком виде. Для этого запускаем конвертер:
c:\Users\userName\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\tools\gen_esp32part.exe part_table_name.csv part_table_name.bin. Первый параметр — ваш CSV, второй параметр — выходной бинарник.


Полученный бинарник следует положить в c:\Users\userName\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\tools\partitions\part_table_name.csv, после чего необходимо указать что бы при сборке решения брался именно он, а не таблица разделов по умолчанию. Сделать это можно, прописав название вашей таблицы в файле c:\Users\userName\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\boards.txt. В моём случае это esp32doit-devkit-v1.build.partitions=part_table_name
После этих манипуляций, VisualGDB при сборке приложения будет брать именно вашу таблицу разделов и класть её в
~project_folder_path\Output\board_name\Debug\project_name.ino.partitions.bin, откуда она уже зальётся в плату.


JTAG отладчик CJMC-FT232H


Насколько я знаю, это самый дешёвый отладчик, которым можно работать с ESP32, мне обошёлся примерно в 600р, на Aliexpress их полно.



При подключении отладчика Windows устанавливает на него неподходящие драйвера, которые необходимо поменять с помощью программы Zadig, там всё просто, не буду описывать.


Подключается к ESP32 devkit-v1 по следующей схеме:
FT232H — ESP32
AD0 — GPIO13
AD1 — GPIO12
AD2 — GPIO15
AD3 — GPIO14
После чего в Project -> VisualGDB Project Properties необходимо сделать следующие настройки:



После чего нажать на Test. Иногда бывает что соединение устанавливается не с первого раза, процесс как бы подвисает, тогда надо прерваться и повторить Test. Если всё в порядке, процесс тестирования соединения занимает около 5 секунд.


Я обычно собирал проект и заливал его через USB самой ESP32(не через отладчик), после чего начинал отладку по Debug -> Attach to Running Embedded Firmware. В коде можно ставить точки останова, смотреть значения переменных в момент останова и в окне Debug -> Windows -> Threads можно смотреть в каком FreeRTOS таске остановился код, что бывает полезно, если во время отладки произошла ошибка. Этих функций отладчика мне хватило для комфортной работы.
Когда я начал работать с NVS, отладка стала постоянно прерываться непонятными ошибками. Насколько я понял, так происходит потому что отладчику необходимо создавать что то типа дампа в разделе NVS по умолчанию, но в это время NVS уже используется контроллером. Конечно, это можно было бы обойти, создав 2 раздела NVS, один с дефолтным именем — для отладки, а другой для своих нужд. Но там уже не было ничего сложного, в добавляемом коде, он заработал с первого раза, так что я не стал это проверять.


Глюки ESP32


Как всякое устройство с Aliexpress, моя ESP32 плата имела свой, нигде не описанный глюк. Когда она пришла, я запитывал от платы кое какую периферию, работавшую по I2C, но по прошествии какого то времени, плата стала перезагружаться, если к ноге +5V было прицеплено любое потребляющее оборудование или даже просто кондесатор. Почему так — совершенно непонятно.


Сейчас я запитываю плату от китайской зярядки 0.7A, датчики ds18b20 от ноги 3.3V платы, а реле и датчик движения — от другой зарядки на 2А. GND нога платы разумеется соединена с GND контактами остального железа. Дёшево и сердито — наш вариант.


О результатах проекта


Я получил возможность гибко управлять отоплением в доме, экономя заработанные потом и кровью деньги. На данный момент если отопление поддерживает весь день 23 градуса при -5 — -7 снаружи, это где то 11 часов работа котла. Если днём поддерживать 20 градусов и греть до 23 только вечером, то это уже 9 часов работы котла. Мощность котла 6КВт, с текущей ценой киловатта 2,2р, это около 26,4р в сутки. Продолжительность отопительного сезона в наших краях 200 суток, средняя температура в отопительный сезон как раз около -5 градусов. Таким образом получается около 5000р экономии за отопительный сезон.


Стоимость оборудования не превышает 2000р, то есть затраты отобьются за несколько месяцев, не говоря уже о том, готовая система подобной автоматики стоила бы не меньше 20000р. Другое дело, что я потратил около недели чистого рабочего времени на написание прошивки и отладку, но в ходе работы я, например, наконец то понял что такое указатели в C++ и получил много другого опыта (например опыт многочасового дебага непонятных глюков). А опыт, как известно, сложно переоценить.


Скриншоты мобильного приложения Blynk:





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


Если моя писанина кому то поможет, буду искренне рад. Буду рад любым замечаниям и предложениям.


Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 62

    +1

    я ещё только собираюсь брать быка за рога, но тоже смутила перспектива программировать ESP32 из Arduino IDE, как самая рекомендуемая. искал варианты. ещё советуют VS Code, но к поделкам на Electron'е у меня тоже мало доверия, несмотря на то, что VS Code очень хвалят.
    хотелось что-то вроде привычных Atmel Studio, Visual Studio.
    спасибо вам за содержательную и полезную во многих смыслах статью.

      +1
      Arduino в Visual Studio? Да легко: есть плагин Visual micro.
        +1
        Я в процессе, как раз пытаюсь приручить платку TTGO на основе ESP32. В том материале, которым руководствуюсь, используется Эклипс и соответствующий плагин. Говорят, работает, но у меня пока не вышло:)
          0

          На сайте espressif исчерпывающий мануал. Рекомендую под линуксом пробовать.

          +3

          Для vs code есть PlatformIO, очень удобно.

            0
            Спасибо за совет, удалось прошить плату из VS Code с PlatformIO каким-то тестовым проектом. Проблемы остались — собирается 5 минут, кодировка в терминале не та, но вектор, похоже, верный
              0
              >собирается 5 минут
              Собирается или собирается + заливается? У меня только первая сборка только долгая. А дальше у меня собирается секунд за 30.
              Ещё стоит посмотреть в сторону конфигурации сборки. Я для себя остановился на
              ;Build options
              build_flags = -std=c++14 -Wno-unknown-pragmas
            +1

            Посмотрите platformio. Она позволяет генерировать все необходимые настройки для разных ide и редакторов (например clion, vscode начинают просто работать без каких либо расширений), и полезной фичей будет взрослая работа с пакетами ( как pip или npm)

            +1
            Вместо кучи if/else можно заюзать весьма полезную функцию «map» и менять температуру плавно (если это возможно и целесообразно):
            void calculate_water_temp(void *pvParameters)
            {
            	while (true)
            	{
            		if (heating_mode != 3)
            		{
            			max_water_temp = map(temp_outside, -30, -10, 85, 50);
            		}
            		vTaskDelay(1000 / portTICK_RATE_MS);
            	}
            }
              0

              Спасибо за предложение! Да, вы правы, оно и возможно и целесообразно. И функцию map знакома почти любому ардуинщику по преобразованию AnalogRead в напряжение. Я думал об этом, но потом стало лень вычислять диапазоны для мапинга. Когда ни будь я так и сделаю.

                +1

                т.е. никогда?))

              0
              Зачем заводить отдельную задачу для сторожевого таймера — если Вам достаточно контролировать только то, что живет RTOS, что у Вас и реализовано, то сторожевой таймер вполне можно сбрасывать и в idle задаче.
                0

                А как к ней обратится? Есть какой то отдельный метод? У нас ведь нет хэндла для idle задачи.

                  +1
                  В файле настройки опций RTOS — FreeRTOSConfig.h есть макро #define configUSE_IDLE_HOOK, его надо установить в 1. При этом, когда все задачи находятся в состоянии ожидания, RTOS будет вызывать функцию vApplicationIdleHook(), которая обявлена как weak (__attribute__((weak)) для компиляторов gcc и g++, для других может быть как то по другому) и может быть переопределена на пользовательскую.
                    0
                    Спасибо! Интересная возможность, надо попробовать.
                  0

                  Так таймер должен контролировать не работу Idle, а зависание задач. Каждая критичная (имеющая дело с внешней средой) задача должна сообщить ему, что не зависла в ожидании данных или готовности аппаратуры.

                    +1
                    Для умеренно критичных к сбоям применений достаточно контролировать именно Idle: если какая-либо задача зависнет, то она не будет попадать в состояние ожидания и, соответственно, Idle задача не будет вызываться, что приведет к срабатыванию ватчдога. Если же задача застрянет в бесконечном состоянии ожидания, то это уже не сбой, а ошибка в логике программы, а бороться с ошибками программиста с помощью сторожевого таймера — порочный путь. Хочу напомнить, что в оригинале у автора сброс ватчдога осуществляется отдельной задачей и никак не контролирует корректность работы других задач, а лишь работу самой ОС.

                    Для очень критичных приложений можно организовать контроль времени цикла каждой задачи, но это потребует организации двойного ватчдога — программного, с независимым счетчиком времени для каждой задачи, и аппаратного, контролирующего работу программного.
                  +2
                  Вот это все за неделю? Включая разобраться со средой, отладкой на железе???
                  В любом случае очень здорово.
                  Пожалуй, только вопросы:
                  1) Увидев «FreeRTOS» и «Blynk» в одном флаконе, я сначла подумал, что Blynk будет использоваться не как «черный ящик», а через API. Не было нужды, все и так прекрасно работает?
                  Просто сталкиваюсь с проблемами при пропадании Интернета — реконнекты Блинка блокируют все чуть ли не на пол минуты. Не будет ли глюков, если шедулер будет многократно аварийно завершать такие попытки (при гудках и т.п.)?
                  Я для отладки просто на роутере блокировал данному хосту Интернет, и через несколько раз наичналась нестабильность.
                  2) Насколько отладчик экономит время при написании таких не слишком больших задач по сравнению с обычной отладочной печатью в serial?
                  На бесплатном PlatformIO под VScode, насколько я понял, аппаратная отладка становится доступной только при оформлении платной подписки.
                    0
                    На самом деле неделя рабочего времени это ни мало, ни много, 7*8=56 часов. Занимался я этим по вечерам и выходным, так что месяц то провозился.
                    По вопросам:
                    1) Проблем нет, всё просто работает. К API Blynk я обращался из кода, для пробы, там ничего сложного, но зачем? :) Что могу сказать по отсутствию инета и реконнектам Blynk, бывает что я забываю заплатить за инет и плата работает без инета, вроде всё в порядке, но я не проверял в это время сработку сигнализации, что бы таски с гудками и морганием работали. На днях попробую поставить дом на охрану и выключить роутер.
                    2) Если честно, то не очень то и экономит, может процентов на 10 побыстрее. :) Но сам процесс с использованием отладчика мне нравится больше. Тут дело в том, что в отладке ESP32 средствами VisualGDB не поддерживаются так называемые «живые переменные», это когда можно смотреть значения переменных не останавливая контроллер. Т.е. нужно поставить точку останова, что бы посмотреть, если бы эта фича поддерживалась, было бы прям удобно и быстрее.
                      +1
                      Я так делаю, код из обычного настенного выключателя, если blynk или wifi зависает, всё реконнектится автоматически периодичность проверки настраивается (для удобства настройки/отладки вынесено в глобальные переменные), локальная работа выключателя не блокируется. RTOS не использую, есть много удобных механизмов типа диспетчеризации задач типа TimeAlarms. Аптайм несколько лет.

                      Заголовок спойлера
                      unsigned long                 //  счетчики таймеров для:
                            WifiLoopTime = millis(), // проверки wifi 
                            BlynkLoopTime = 0;       // проверки коннекта к серверу blynk
                      
                      setup()
                        WiFi.mode(WIFI_STA);
                        WiFi.begin();
                        WiFi.setAutoReconnect(true);
                        Blynk.config("***************", IPAddress(***********), 8442);
                        Blynk.connect(1666); // 5sec
                      
                      loop()
                        if (WiFi.status() == WL_CONNECTED)
                          {
                          if (Blynk.connected())
                            Blynk.run();
                          else 
                            if (millis()> BlynkLoopTime+20000)
                            {
                             BlynkLoopTime=millis();
                             Blynk.connect(1666);
                            }
                          httpServer.handleClient();
                          }
                        else
                            {
                            if (millis()> WifiLoopTime+20000)
                              {
                              Serial.println("WI-FI Not connected");
                              WiFi.mode(WIFI_OFF);
                              Serial.println("WI-FI disconnecting");
                              WiFi.begin();
                              Serial.println("WI-FI connecting.");
                              WifiLoopTime=millis();
                              }
                             else 
                              if (millis()> Seconds+1000)
                              {
                               Seconds=millis();
                               Serial.print(".");
                              }
                            }

                    0
                    Прошу прощения, я с FreeRTOS пока дела не имел. И, поизучав внимательнее код, удивился, что в smart_home_2019.ino не вижу, к примеру, #include «freertos/FreeRTOS.h». На вид самые обычные инклуды.
                    Также не нашел, как в smart_home_2019.ino подцепляются таски (tasks_functions.ino).
                    Или на гитхабе только кусочки проекта? Можете пояснить?
                      0
                      Также не нашел, как в smart_home_2019.ino подцепляются таски (tasks_functions.ino).
                      В конце функции setup() ряд вызовов функций xTaskCreate()
                        0
                        Да, вызовы вижу. Я не понимаю, как система без #include знает, что они находятся в tasks_functions.ino
                        Вряд ли это зарезериврованное имя файла.
                        Ну да ладно, у вас среда разработки другая. У меня ж вообще не .ino, а main.c главный файл…
                        Все равно много интересного увидел.
                          +1
                          Ну да ладно, у вас среда разработки другая.
                          Это не у меня, а у автора. Я ардуиновскими и околоардуиновскими софтверными инструментами не пользуюсь. Полагаю, что часть «магии» просто скрыта от пользователя, ведь подключить заголовочники можно и через опции командной строки компилятора (для gcc и g++ это -include).
                            0
                            Да, точно. Зависимости же можно и явно в настроечных файлах указывать…
                            0
                            А, вы про это. Скорее всего среда разработки считает, что файлы с разрешением ino в одной папке находятся как бы в одном пространстве имён. Такое же поведение и Arduino IDE.
                            Но, ссылку на метод для рестарта, который был изначально в из tasks_functions.ino, в smart_home_2019.ino не было видно, пришлось перенести этот метод в smart_home_2019.ino.
                          0
                          На гитхабе весь код, который написал я. В visual GDB это всё как то компилируется без #include «freertos/FreeRTOS.h» и прочего. Какая то чёрная магия, но всё вроде работает :) Таски подцепляются в конце setup().
                            0
                            Для ESP32 в ArduinoIDE FreeRTOS подключена «по умолчанию», #include «freertos/FreeRTOS.h» уже не нужен. Пользовательский скетч для ESP32 — это просто задача FreeRTOS, которая запускается на ядре процессора CORE1, на CORE0 выполняется сама RTOS. Для VisualGDB не знаю, но вероятно, реализовано похожим образом.
                              0
                              Да, точно. Для ESP32 «freertos/FreeRTOS.h» инклудится в самом Arduino.h
                              А вот ESP8266 такого нет. Там xCreateTask И прочие радости неизвестны, что меня и смутило.
                                0
                                Ох и потратил я на это времени в свое время, прежде чем разобрался как это работает ))) Если быть точным, то в cores\esp32\main.cpp

                                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


                                Здесь же видно, что для задачи по умолчанию выделяется всего 8192 (байт? слов? — в разных источниках по разному) стека. И это далеко не весь доступный контроллеру стек — у меня на одном контроллере дополнительно запущено еще с десяток задач и все работает.

                                В общем, при программировании ESP32 под ArduinoIDE без FreeRTOS вообще никак.
                                  0
                                  А с учетом, что я «не настоящий свращик», вообще мрак :)
                                  Я как раз очень сильно заморачивался, как сделать, чтобы и Blynk использовать, и чтобы определенные действия даже без Блинка (и вообще без Интернета) работали (по расписанию).
                                  Похоже, что использование планировщика FreeRTOS как раз то, что мне нужно.
                                    0
                                    Для ESP32 размер стека именно в байтах. А для STM32 вроде как в словах.
                            –1
                            Практически каждая статья жителя России об «умном доме» (sic!) по своей сути описывает «наколенное изобретательство велосипеда», то-бишь программируемого термостата. Я понимаю, что
                            … хотелось поприседать мозгами, пописать что то для души...
                            — это основополагающее (а не отсутствие знаний о том, что такие дивайсы уже «сто лет в обед» изобретены и продаются буквально за «копейки» (дешевые варианты, без Alexa и WiFi), но не «стрёмно»-ли писать свой говнокод для подобной, достаточно важной функции? Пусть код и простенький, но без тестирования про-QA я бы, лично, не рискнул.
                            Еще вот этот момент удивил:
                            управление температурой теплоносителя в зависимости от температуры на улице или вручную
                            — а при чем тут улица-то? Вы, часом, не улицу ли греете? :D
                              0
                              Ну так есть модель дома и его теплоотдачи. Обогреватель в стабильном режиме должен вкачивать столько же джоулей, сколько уходит сквозь стены. А это зависит от уличной температуры. Через температуру в доме это можно мерить, но кажется с большими волнами — чтобы получить управляющий сигнал нужно получить изменение температуры в доме. То есть снизить комфорт жителей.
                                0
                                Могу предположить, что если сделать все время низкую температуру теплоносителя, в холод ее будет не хватать. Скажем, если мощность котла недостаточна.
                                Но зачем такая точность, почему все время зимой не держать 85 градусов (а летом 70) — тоже не очень понимаю.

                                Еще можно аналогию провести с «почему инверторный кндиционер лучше обычного».
                                Одно дело большой гистерезис (разница температур между включением и выключением), а другое — плавное регулирование мощности.
                                Возможно здесь изменение температуры — такой аналог управления мощностью?
                                Но котел все равно ведь вкл/выкл сам делает…

                                Ну т.е. разве что избежать слишком частых включений/выключений, что имеет некоторый смысл.
                                  0

                                  Если честно, я придумал этот кусок кода год назад, и сейчас уже не помню, почему мне тогда не понравилась высокая температура в трубах. В этом году просто перенёс на esp32. По сути да, можно и без этого.

                                  0
                                  Странный комментарий. Если вам так удобно, считайте что я грею улицу.
                                    0
                                    Чем он «странный»? Не «страннее», чем очередное «изобретение велосипеда из говна и палок». А вы вообще когда-нибудь дело с обыкновенными, производящимися десятки лет, программируемыми термостатами, имели? (правда, понятное дело, функций «включения сирены» и «управления фонарем» в них нет :)
                                      0
                                      Проблема подобных комментариев в том, что никто и никогда не дает ссылку на готовый продукт.
                                      А человеку, может, было интересно сделать из говна и палок термостат, да еще и иметь возможность получить некую инфографику. При этом, он не знает, что ему далее может потребоваться, что будет лишним, а что он захочет добавить.
                                        0
                                        «Какие руки, какие ноги?!» Удав из мультика Какие ссылки? Тут, все-таки, хабр, а не форум для блондинок — запросить google/yandex «программируемый термостат купить» должно уметь 100% посетителей, не говоря уж о «разработчиках микроконтроллерных приложений».

                                        То, что автору было интересно — я и не сомневался (подобно авторам 99% постов с подобным содержимым — их тут на хабре, как...), что и сразу написал. Удивление вызвало то, что автор данной наколенной поделки меряет… температуру «за бортом». Все, что нужно — это расположить термостат в доме, по возможности, подальше от радиаторов (чтобы измерял, так сказать, «амбиентную температуру» — вот и все, и весь секрет Полишинеля).

                                        Уже более 15 лет я пользуюсь простенькими программируемыми термостатами от Honeywell. Работают, лучше чем часы (там чаще нужно смазывать — это о grandfather clock, или менять батарейки), раз в три года сменил батареечку — и все. Работают вне зависимости от наличия интернета или электричества. А вот обеспечить аварийное питания для oil boiler-а — это задача, реально заслуживающая внимания (но отнюдь не кустарных поделок на ардуине).

                                        Про «инфографику» отопления я, с вашего позволения, промолчу, чтобы грязно не ругаться. Вся «инфографика» — это счета за oil (три бака в год уходит примерно); копеечные колебания плюс/минус не в счет, но вот после weatherization (это утепление дома, «халявное», в моем случае) разница была заметна и без «инфографики» — лучший «инфограф» это свой собственный кошелек :)
                                          0
                                          Гуглить тут, конечно, каждый умеет. Но речь то шла о сравнении поделки автора с «обыкновенными программируемыми термостатами». Да, у меня самого есть пара таких, один копеечный с алиекспресса сейчас подогревает компрессор морозилки стоящей в не отапливаемом помещении. Второй теплым полом в санузле управляет. Но, черт возьми, ни один из них не умеет понижать температуру по расписанию.
                                          Почему автор измеряет температуру на улице — а почему нет? Если у него получается регулировать так температуру, то и хорошо. Хотя, как по мне, задача подогнать параметры регулятора так гораздо сложнее.
                                          Да и ваша «инфографика» по счетам за отопления — гораздо менее показательна. У нас тут зима на зиму не приходится. Прошлая была очень теплой, раза три снег всего выпадал. А вот три года назад, как выпал, так и не стаял. Как бы это на счетах отразилось?
                                          Вообще, статья то про RTOS на esp32. Крутилка для котла тут как пример.
                                            0
                                            А если внимательно прочесть сначала, то можно увидеть что у автора не было средств на прграммируемые термостаты и пр., и он просто сделал «конфетку» из того, что у него на тот момент было. Ориентируется он на собственный кошелёк, а не на ваш. Моё почтение автору статьи.
                                    0
                                    При подключении отладчика Windows устанавливает на него неподходящие драйвера, которые необходимо поменять с помощью программы Zadig, там всё просто, не буду описывать.

                                    Странно у меня все из коробки заработало с этой платой. Вам пришлось переустанавливать драйвера?
                                      0
                                      Да, пришлось, Win10.
                                      +1

                                      Спасибо за статью. Очень вовремя!
                                      Не более как неделю запустил в ещё пока строящемся доме отопление от котла. Котел умеет как eBUS, так и простой вход З/Р.
                                      Установщики предлагали систему EktoControl (11к), но я в ней не увидел необходимости.
                                      Имеется в наличии пара wemos d1 mini pro (v1.1), один ds18b20, решил как раз на этих выходных попробовать сделать управление для котла.
                                      На си писал давно, но тулинг меня начал убивать :) может я просто зря решил не хватать Ардуино, а попытаться использовать NonOS / RTOS SDK.
                                      Попробовал по их инструкциям, надо сделать в Windows кучу софта, ок. Не завелось. Попробовал CLion + esp8266 plugin. Ну вроде что-то работало, но подсказки кода нет.
                                      Сейчас рабочий вариант нашел только VSCode + PlatformIO на основе NonOS. Но расстраивает свежее сообщение в гитхабе, что NonOS всё.

                                        0
                                        VSCode + PlatformIO на основе NonOS

                                        А почему не VSCode + PlatformIO на основе Ардуино?
                                        [env:ttgo-t1]
                                        platform = espressif32
                                        board = ttgo-t1
                                        framework = arduino
                                        Удобного шедулера в NonOS все равно нет. В чем ее прелесть? А так — куча ардуионовских библиотек подходит…
                                          0
                                          Оправдаюсь тем, что не знаю в полной мере, что такое Ардуино и вся это железячство. А так как на Си писал, решил, что этот путь может оказаться прямее.

                                          Сейчас вижу, что, скорее всего, был неправ.

                                          А с ограничением на таски пока не столкнулся. В целом хочу сделать достаточно простую вещь — временами опрашивать 18b20, по формуле считать когда вкл/выкл котёл, дёргать реле, а заодно отправлять всё измеренное и произошедшее по mqtt.

                                          Так как в доме не живу, а зимой даже планирую некоторое время его не посещать, хотелось бы знать, что там всё ОКъ — скорее всего куплю самый-самый дешёвый 3g-модем (покрытие норм) с раздачей Wi-Fi.
                                            0
                                            Можно ещё датчик движения повесить или концевик на дверь, что бы знать что никто не залез внутрь. Ну и дальше как время/желание позволит — датчик дыма, датчик протечки :) код на все эти вещи несложный. Главное что бы датчики работали норм, иначе долгий дебаг и всё такое.
                                            0
                                            к сведению
                                            Время создания: Sun, 08 Dec 2019 05:17:43 -0800
                                            Тема: [platformio/platform-espressif8266] Support for ESP-IDF style ESP8266_RTOS_SDK

                                            Several days ago Espressif has deprecated NonOS SDK
                                            Support Policy for ESP8266 NonOS
                                            Starting from December 2019,
                                            We will not add any new features to the ESP8266 NonOS SDK.
                                            We will only fix critical bugs in the ESP8266 NonOS SDK.
                                          0

                                          Самодельный Nest получился.
                                          А зачем днём поддерживать +20 если дома никого нет?
                                          ИМХО +16 достаточно, и повышать к желаемой температуре ко времени прихода домой.

                                            0
                                            Это можно поменять, в тёплую погоду обычно ставлю меньше. Но вообще, на 3 градуса дом разогревается, почти в любой мороз, за пару часов. Если выставить меньше, то бывает холодновато по приезду.
                                            +1

                                            Тоже присматриваюсь freertos на esp32. Сейчас работает похожий проект (для гаража сделал), связка: дома — esp32+LoRa и srm32+lora Переодически вылазят какие-то фантомы в данных (раз в сутки или в 2-e), все сделано через loop. Спасибо за реальный пример rtos — сяду перелопачивать!

                                              0
                                              а что за фантомы в данных, можно по конкретнее? от какого устройства приходят?
                                                0
                                                В общем между двумя lora (ra-01), поднял собственный протокол типа запрос/ответ, с адресацией (на будущее), контролем доставки, контроль ошибок, метками времени (esp с по ntp получает, тоже задел на будущее). Создал 2 абстрактных объекта сервер и клиент (не привязывая к пердааемым данным), все через систему событий. Грешил на датчик dht22, он начинал через сутки после запуска присылать какие-то дикие значения, с непонятной переодичностью (раз в 2-4). Но вроде нашёл корень зла — в событийной системе для глобальных переменных установил volatile :) и STM через некоторое время при использовании промежуточных буферов (использовал для расчёта crc и формировании отправки пакета) и работе в полевых условиях (в гараже порой до — 20) memcpy подмешивал шнягу. Побщем х/з сам первый раз с таким встретился. Разобрался с указателями и ссылками — убрал все стековые буфера.
                                                  0
                                                  После этого фантомные данные пропали? Я тоже делал подобное на esp32, но у меня аппаратное crc включено и все равно иногда приходят фантомные данные если устройства находятся достаточно близко. И в документации на sx1276 встречал что возможны фантомные данные в режиме неявного заголовка.
                                                    0

                                                    В домашних условиях уже сутки без косяков. Сейчас код вылизываю — есть еще куда стремиться. И буду портировать на atmega16 (наковырял кучу, валяются без дела)

                                                      0

                                                      Смотрите в сторону crc-16 modbus, заточено по мк. Зашил все в свой протокол, появилась возможность вычислять "уровень сигнала" и ошибок в данных ноль (за неделю). Модули на родных пружинках через 3 ж/б стены (80 байт данных), в среднем 10-15 раз в минуту в обе стороны на 300 метров, без проблем!

                                                +1
                                                Как то однажды мне тоже надоело разглядывать лапшу из arduino кода при добавлении очередного датчика. По этому, вспомнив, что я все-таки программист, написал событийную систему, где каждое устройство, это некий объект, способный передавать сигналы другим объектам. В итоге, получилась довольно легко расширяемая система, в которой отдельно пишутся объекты (датчики или устройства), а отдельно задаются связи между ними. В моем проекте, это делалось при старте, но можно было и на лету.
                                                Freertos у меня не было, потому как в загашнике валялись только платы arduino uno.
                                                Реализовал на этом проект поливалки с парой датчиков влажности, дисплеем, кнопками для задания уровней срабатывания датчиков. Потом добавил часы реального времени и управление досветкой растений в зависимости от длинны суток, включалась она утром и вечером. Хотел даже написать статью, но писать статьи для меня сложнее чем писать код. Если вдруг кому то интересно, то проект лежит здесь.
                                                И да, писать в arduino ide — сущий ад, проще один раз прикрутить qt creator.
                                                  0
                                                  Итак, при использовании FreeRTOS функция setup играет роль функции main, точки входа в приложение, в ней создаются FreeRTOS tasks (далее таски)...

                                                  Я правильно понимаю, что вы пользуетесь тем, что arduino-esp32 фреймворк запускает одновременно функции setup() и loop() как единственную задачу во FreeRTOS, судя по коду main.cpp фреймворка?
                                                  Часть кода mail.cpp
                                                  void loopTask(void *pvParameters)
                                                  {
                                                      setup();
                                                      for(;;) {
                                                          if(loopTaskWDTEnabled){
                                                              esp_task_wdt_reset();
                                                          }
                                                          loop();
                                                          if (serialEventRun) serialEventRun();
                                                      }
                                                  }
                                                  
                                                  extern "C" void app_main()
                                                  {
                                                      loopTaskWDTEnabled = false;
                                                      initArduino();
                                                      xTaskCreateUniversal(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, CONFIG_ARDUINO_RUNNING_CORE);
                                                  }


                                                  Причем эта единственная задача имеет по умолчанию стек 8192.
                                                  Правильно ли я далее понимаю, что новые таски, которые вы создаете в setup(), расходуют ресурсы головного таска? Или во FreeRTOS не может быть подтасков, и задачи созданные в любом месте и из любой задачи уравниваются друг с другом и расходуют только единые ресурсы процессора?

                                                  Я прошу заранее простить, если вопрос непрофессионален. Только начинаю разбираться с ESP32, и вообще пытаюсь настроить окружение, чтобы использовать Arduino.h как компоненту для ESP-IDF, но тут еще те проблемы…
                                                    0
                                                    Никто не ответит? Автор ушел?
                                                      0
                                                      Или во FreeRTOS не может быть подтасков, и задачи созданные в любом месте и из любой задачи уравниваются друг с другом и расходуют только единые ресурсы процессора
                                                      — это происходит именно так. Таск можно запустить из другого таска, он будет «равноценный» всем остальным, созданным в setup. Ресурсы у него будут свои, стек и процессорное время.
                                                      Интересно вы залезли в main, я там даже не был. Не ожидал что там так просто всё реализовано.
                                                      +1
                                                      Занимаюсь как хобби похожими проектами.
                                                      Отвечу на вопрос Глюки ESP32:
                                                      Проблема в том что при включении проседает питание (как пример висит конденсатор)
                                                      Решаеться очень просто: Надо прицепить конденсатор ~10uf между EN и GND.
                                                      Будет загружаться стабильно

                                                      Подружить Arduino и Esp-IDF, смотри пример github.com/Yurik72/esphapcontroller
                                                      уже подружено :)

                                                      Ну или для любителей Arduino IDE visual studio IDE
                                                      Пример библиотеки для Arduino но все задачи написаны через Free RTOS
                                                      github.com/Yurik72/ESPHap

                                                      Подзадачи и процессорное время:
                                                      Тут могу добавить что у ESP32 два ядра процессора (0 и 1)
                                                      Как правило на 1 работает Wifi и 0 по умолчанию работает для того же Arduino
                                                      (loop)

                                                      Но можно вешать свои задачи на любое ядро через
                                                      xTaskCreatePinnedToCore… и задавать приоритеты

                                                      На разных ядрах действительно выполняються задачи паралельно
                                                      Примеры можете глянуть в ссылках выше

                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                      Самое читаемое