Что такое PlatformIO?
Почему стоит перейти с Arduino IDE на PlatformIO
В сети выделяют следующие недостатки Arduino IDE:
- отсутствие кастомизации (темы, подсветка синтаксиса);
- нет автодополнения кода (как в современных IDE);
- не очень удобная организация проектов;
- нет поддержки каких-либо плагинов;
- неудобный менеджер библиотек;
- не очень вразумительный builder, с которым достаточно трудно искать ошибки в программе;
- другие мелкие баги, которые чаще всего встречаются при работе с Arduino IDE с операционной системе Windows.
Именно эти недостатки и вынуждают ряд пользователей переходить с Arduino IDE на PlatformIO или другие аналогичные платформы.
Преимущества PlatformIO
PlatformIO позволяет производить быструю разработку приложений и, подобно C/C++, поддерживает быстрое завершение кода и интеллектуальный код линтера, недоступные в Arduino IDE. В дополнение к этому PlatformIO предлагает полную поддержку тем с темными и светлыми цветами, умную навигацию по коду и форматирование кода. Ядро платформы включает мультиплатформенный builder, менеджер библиотек, монитор последовательного порта и многое другое.
Единый (унифицированный) отладчик поддерживает множество архитектур и платформ разработки. В отладчике есть такие функции как условные точки останова, выражения и точки наблюдения (Watchpoints), диспетчер памяти, “горячий” рестарт текущей сессии отладки. Ядро PlatformIO написано на Python 2.7 и работает на Windows, macOS, Linux, FreeBSD и даже на некоторых платформах с ARM-архитектурой: Raspberry Pi, BeagleBone, CubieBoard, Samsung ARTIK и т.п. Также в составе PlatformIO есть проводник файлов (File Explorer), который помогает в удобной организации хранения ваших проектов.
Определение идентификатора ядра (core ID) ESP32
В этом проекте мы будем использовать среду Arduino IDE для загрузки кода программы в модуль ESP32. Для определения идентификатора ядра (core ID), на котором исполняется код, существует специальная API функция.
Arduino
xPortGetCoreID()
1 | xPortGetCoreID() |
Эту функцию можно вызывать внутри функций void setup() и void loop() для определения идентификатора ядра, на котором эти функции выполняются.
Вы можете протестировать эту API функцию при помощи следующего скетча:
Arduino
void setup() {
Serial.begin(115200);
Serial.print(«setup() function running on core: «);
Serial.println(xPortGetCoreID());
}
void loop() {
Serial.print(«loop() function running on core: «);
Serial.println(xPortGetCoreID());
}
1 |
voidsetup(){ Serial.begin(115200); Serial.print(«setup() function running on core: «); Serial.println(xPortGetCoreID()); } voidloop(){ Serial.print(«loop() function running on core: «); Serial.println(xPortGetCoreID()); } |
После загрузки этого скетча в модуль откройте окно монитора последовательной связи (Serial monitor) и вы в результате выполнения представленного скетча увидите, что обе эти функции выполняются на первом ядре (core1) как показано на следующем рисунке.
Из представленного примера можно сделать вывод, что по умолчанию скетч Arduino выполняется на первом ядре (core1) модуля ESP32.
Подготовка Arduino IDE и прошивка
- Загружаем и устанавливаем Arduino IDE.
- Распаковываем скачанное в папку с библиотеками Arduino IDE (обычно это C:\Users\<Текущий пользователь>\Documents\Arduino\).
- Копируем полученный код в Arduino IDE.
- В примере вводим фактическое название нашей WiFi-сети и пароль.
В примере также присутствует функция вида:
BLYNK_WRITE(V1) { int pinValue = param.asInt(); // assigning incoming value from pin V1 to a variable // process received value }
Здесь:
- BLYNK_WRITE(V1) указывает, что функция выполнится при изменении виртуального пина 1,
- int pinValue = param.asInt(); объявляет переменную pinValue и загружает в неё текущее состояние виртуального пина (0 — пин выключен, 1 — пин включен).
Всего можно использовать 256 виртуальных пинов (V0-V255) — это огромный запас на все случаи жизни. Например, виртуальный пин 1 принимает значение 1 — подаём питание на физический пин NodeMcu и включаем этим реле, или виртуальный пин 2 принимает значение от 0 до 255 — изменяем ШИМ-сигнал, регулируем яркость (либо цвета RGB) диодной подсветки в 255 градациях яркости.
А теперь заставим нашу функцию включать физический пин D4 NodeMcu (в функции будем использовать событие виртуального пина 0, просто для удобства):
BLYNK_WRITE(V0) { int pinValue = param.asInt(); digitalWrite('''D4''', pinValue); }
Чтобы управлять этим выводом, в основной функции void setup() обязательно установим пин D4 как выход: pinMode(D4, OUTPUT);
В итоге получаем такой код для прошивки:
#define BLYNK_PRINT Serial #include <ESP8266WiFi.h> #include <BlynkSimpleEsp8266.h> char auth[] = "Мой токен"; //тут токен из e-mail char ssid[] = "YourNetworkName"; //Название WiFi-сети char pass[] = "YourPassword"; //Пароль BLYNK_WRITE(V0) //функция, отслеживающая изменение виртуального пина 0 { int pinValue = param.asInt(); //переменная текущего состояния виртуального пина digitalWrite(D4, pinValue); //задаем значение на физическом пине NodeMcu D4 равное значению виртуального пина 0 } void setup() //основная функция, выполняется один раз при подаче питания на микроконтроллер { Serial.begin(9600); //открываем серийный порт, чтобы видеть как проходит подключение к серверу blynk pinMode(D4, OUTPUT); //объявляем D4 "выходным" пином Blynk.begin(auth, ssid, pass); //авторизируемся на сервере } void loop() //основная функция, которая выполняется постоянно по кругу { Blynk.run(); //запускаем работу blynk. В этом примере - постоянную проверку виртуального пина 0 }
Заливаем прошивку в NodeMcu.
Так как пример предполагает использование различных плат, то пропустим тонкости настройки Arduino IDE для работы с NodeMcu; к тому же подобную информацию найти нетрудно.
Наш выключатель готов к работе!
Также данную схему можно использовать и для включения ПК (имитации нажатия кнопки включения). Для этого параллельно пинам кнопки Power (на материнской плате пины POWER SW) нужно подключить линии L и L1 (указанные на схеме !!!НЕ 220В!!!) и немного изменить скрипт, чтобы при изменении виртуального пина реле включалось на короткий промежуток времени, имитируя короткое нажатие.
В скрипте, в блоке:
BLYNK_WRITE(V0) //функция, отслеживающая изменение виртуального пина 0 { int pinValue = param.asInt(); //переменная текущего состояния виртуального пина if (pinValue = 0){ digitalWrite(D4, HIGH); //если работает неверно, то изменить на digitalWrite(D4, LOW); а ниже наоборот delay (100); // если задержка мала, можно увеличить digitalWrite(D4, LOW); //ДА-ДА, ИМЕННО ТУТ НИЖЕ. Если работает неверно, то изменить на digitalWrite(D4, HIGH); } }
мы добавили задержку delay (100); в 100 мс и выключили после нее физический пин D4 — digitalWrite(D4, LOW);
Настройка Arduino IDE
После установки необходимо настроить программу. Сделать это очень легко:
1. Сначала подключите вашу ардуинку к компьютеру с помощью USB кабеля.
2. Потом перейдите в «Пуск >> Панель управления >> Диспетчер устройств«. Там надо найти «Порты COM и LPT». В этом списке будет указана ваша arduino и порт к которому она подключена (COM2). Запомните это значение.
Arduino Uno в диспетчере устройств
3. Теперь запускаем Arduino IDE и сразу идем в меню «Инструменты >> Порт«. Там необходимо выбрать тот COM порт, который вы запомнили.
Выбор COM порта
4. Далее надо выбрать плату. Для этого в меню»Инструменты >> Платы» выбираете модель вашей платы.
Выбор платы ардуино
На этом настройка закончена. Не забывайте, что эти настройки придется менять если вы подключите плату к другому USB порту или будите использовать другую плату.
Void setup and void loop: mandatory functions in Arduino
The Arduino void setup and void loop functions are mandatory. Try to compile a code with one of those functions missing, and you’ll get an error.
# Example of what's happening when there is no void setup() function in your Arduino program C:\Users\user\AppData\Local\Temp\ccioFeXY.ltrans0.ltrans.o: In function `main': C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/main.cpp:43: undefined reference to `setup' collect2.exe: error: ld returned 1 exit status exit status 1 Error compiling for board Arduino Uno.
When you run a “standard” C/C++ program, you have to write a “main” function. This main function will be called first, and from there, you will call other functions and execute the functionalities of your program.
In Arduino, there is no main function. This is replaced by setup and loop. Instead of one mandatory function, you have 2.
All your Arduino program must include those functions. On top of them, you can add any number of functions, variables, and create as many files as you want. You just need to make sure that the void setup and void loop are present.
Светодиодные ленты
Светодиодная лента представляет собой цепь соединённых светодиодов. Соединены они не просто так, например обычная 12V лента состоит из сегментов по 3 светодиода в каждом. Сегменты соединены между собой параллельно, то есть на каждый приходят общие 12 Вольт. Внутри сегмента светодиоды соединены последовательно, а ток на них ограничивается общим резистором (могут стоять два для более эффективного теплоотвода):
Таким образом достаточно просто подать 12V от источника напряжения на ленту и она будет светиться. За простоту и удобство приходится платить эффективностью. Простая математика: три белых светодиода, каждому нужно по ~3.2V, суммарно это 9.6V. Подключаем ленту к 12V и понимаем, что 2.5V у нас просто уходят в тепло на резисторах. И это в лучшем случае, если резистор подобран так, чтобы светодиод горел на полную яркость.
Подключаем к Arduino
Здесь всё очень просто: смотрите предыдущий урок по управлению нагрузкой постоянного тока. Управлять можно через реле, транзистор или твердотельное реле. Нас больше всего интересует плавное управление яркостью, поэтому продублирую схему с полевым транзистором:
Конечно же, можно воспользоваться китайским мосфет-модулем! Пин VCC кстати можно не подключать, он никуда не подведён на плате.
Управление
Подключенная через транзистор лента управляется точно так же, как светодиод в предыдущей главе, то есть все примеры кода с миганием, плавным миганием и управление потенциометром подходят к этой схеме.
Про RGB и адресные светодиодные ленты мы поговорим в отдельных уроках.
https://youtube.com/watch?v=hoprWM_b6V0
Питание и мощность
Светодиодная лента потребляет немаленький ток, поэтому нужно убедиться в том, что выбранный блок питания, модуль или аккумулятор справится с задачей. Но сначала обязательно прочитайте урок по закону Ома! Потребляемая мощность светодиодной ленты зависит от нескольких факторов:
- Яркость. Максимальная мощность будет потребляться на максимальной яркости.
- Напряжение питания (чаще всего 12V). Также бывают 5, 24 и 220V ленты.
- Качество, тип и цвет светодиодов: одинаковые на вид светодиоды могут потреблять разный ток и светить с разной яркостью.
- Длина ленты. Чем длиннее лента, тем больший ток она будет потреблять.
- Плотность ленты, измеряется в количестве светодиодов на метр. Бывает от 30 до 120 штук, чем плотнее – тем больший ток будет потреблять при той же длине и ярче светить.
Лента всегда имеет характеристику мощности на погонный метр (Ватт/м), указывается именно максимальная мощность ленты при питании от номинального напряжения. Китайские ленты в основном имеют чуть меньшую фактическую мощность (в районе 80%, бывает лучше, бывает хуже). Блок питания нужно подбирать так, чтобы его мощность была больше мощности ленты, т.е. с запасом как минимум на 20%.
- Пример 1: нужно подключить 4 метра ленты с мощностью 14 Ватт на метр, лента может работать на максимальной яркости. 14*4 == 56W, с запасом 20% это будет 56*1.2 ~ 70W, ближайший блок питания в продаже будет скорее всего на 100W.
- Пример 2: берём ту же ленту, но точно знаем, что яркость во время работы не будет больше половины. Тогда можно взять блок на 70 / 2 == 35W.
Важные моменты по току и подключению:
- Подключение: допустим, у нас подключено ленты на 100W. При 12 Вольтах это будет 8 Ампер – весьма немаленький ток! Ленту нужно располагать как можно ближе к блоку питания и подключать толстыми (2.5 кв. мм и толще) проводами. Также при создании освещения есть смысл перейти на 24V ленты, потому что ток в цепи будет меньше и можно взять более тонкие провода: если бы лента из прошлого примера была 24-Вольтовой, ток был бы около 4 Ампер, что уже не так “горячо”.
- Дублирование питания: лента сама по себе является гибкой печатной платой, то есть ток идёт по тонкому слою меди. При подключении большой длины ленты ток будет теряться на сопротивлении самой ленты, и чем дальше от точки подключения – тем слабее она будет светить. Если требуется максимальная яркость на большой длине, нужно дублировать питание от блока питания дополнительными проводами, или ставить дополнительные блоки питания вдоль ленты. Дублировать питание рекомендуется каждые 2 метра, потому что на такой длине просадка яркости становится заметной уже почти на всех лентах.
- Охлаждение: светодиоды имеют не 100% КПД, плюс ток в них ограничивается резистором, и как результат – лента неслабо греется. Рекомендуется приклеивать яркую и мощную ленту на теплоотвод (алюминиевый профиль). Так она не будет отклеиваться и вообще проживёт гораздо дольше.
https://youtube.com/watch?v=HO3oeTt34_w
https://youtube.com/watch?v=GEDJifEpp7s
https://youtube.com/watch?v=9ss0_7FQCXQ
Примеры void setup()
Чаще всего внутри void setup указываются следующие инструкции:
- pinMode с указанием номера и типа пина. Это инструкция определяет режим работы пинов ардуино.
- Serial.begin с указанием скорости (чаще всего, 9600). Эта инструкция инициализирует работы с последовательным портом на указанной скорости.
- Инструкции подключения и инициализации различных объектов библиотек ардуино. Например, servo.atach(6) укажет библиотеке, что сервопривод мы подключили к 9 пину, и все последующие действия код библиотеки будет совершать именно с этим портом.
- Инициализацию глобальных переменных, если мы по каким-то причинам не можем сделать это при определении переменных в глобальной области видимости.
- Выполнение других настроек и начальных значений для переменных и объектов.
Пример:
void setup(){
Serial.begin(9600);
pinMode(13, OUTPUT); // 13 пин будет выходом (все остальные порты по умолчанию входы)
pinMode(2, PULLUP); // 2 пин будет входом с подтягивающим резистором
servo.atach(9); // Где-то выше мы объявили объект класса Servo и теперь указываем, что серво будет подсоединен к 9 порту.
}
Если говорить об ошибке redefinision of void setup’, то она возникает в том случае, если мы подключаем модуль или скетч, в котором уже есть данная функция. Это и вызывает ошибку
Оператор return
Оператор return завершает выполнение вызываемой функции и возвращает значение определенного типа. В качестве параметра команды можно указать число, символ или переменную определенного типа
Важно, чтобы возвращаемое значение соответствует типу заявленной функции. В следующем примере показано, как использовать оператор return:
int checkSensor(){ if (analogRead(0) > 400) { // чтение аналогового входа return 1; // Для значений больше 400 возвращается 1 else{ return 0; // для других возвращается 0 } }
Как вы можете видеть, в одной функции можно использовать несколько операторов return, но сработает всегда только один из них. Допустимо использование оператора return без параметров. Это позволяет досрочно прекратить работу функции, которая не возвращает никакого значения.
void имя_функции() { инструкция1; if(x==0) return; инструкция2; инструкция3; }
В приведенном выше примере инструкция1 будет выполнять всегда, когда вызывается функция. Выполнение же инструкция2 и инструкция3 зависит от результата команды if. Если условие будет выполнено (true), то будет выполнена команда return и функция завершит работу.
В случае, когда условие не выполнено команда return так же не выполняется, а выполняются инструкции инструкция2 и инструкция3, и после этого функция завершает свою работу.
Математические функции
Функция min(x, y)
Описание
Возвращает наименьшее из двух чисел, поддерживает все числовые типы данных.
Синтаксис
min(x, y)
Параметры
x — первое число
y — второе число
Возвращаемое значение
Меньшее из двух чисел
Пример
Примечания
Следует избегать использования функций внутри функции, например min(a++, b);, так как из-за внутренней реализации она может вернуть неверный результат.
Функция max(x, y)
Описание
Возвращает наибольшее из двух чисел, поддерживает все числовые типы данных.
Синтаксис
max(x, y)
Параметры
x — первое число
y — второе число
Возвращаемое значение
Наибольшее из двух чисел
Пример
Примечания
Следует избегать использования функций внутри функции, например max(a++, b);, так как из-за внутренней реализации она может вернуть неверный результат.
Функция abs()
Описание
Возвращает модуль число (абсолютное значение числа).
Синтаксис
abs(x)
Параметры
x — число
Возвращаемое значение
Вернет x, если x — положительное число или 0, иначе вернет -x
Пример
Примечания
Следует избегать использования функций внутри функции, например abs(a++);, так как из-за внутренней реализации она может вернуть неверный результат.
Функция constrain(x, a, b)
Описание
Функция проверяет, входит ли число x в диапазон . Если x меньше, чем a, то функция вернет a, если x больше, чем b, то вернет b. Иначе вернет x. Поддерживает все числовые типы.
Синтаксис
constrain(x, a, b)
Параметры
x — проверяемое число
a — нижняя граница диапазона
b — верхняя граница диапазона
Возвращаемое значение
Если x входит в диапазон , то возвращает x, если оно меньше нижней границы, то вернется a, если же оно больше верхней границы, то вернется b.
Пример
Примечания
Следует избегать использования функций внутри функции, например constrain(Serial.parseInt(), minimumValue, maximumValue);, так как из-за внутренней реализации она может вернуть неверный результат.
Функция map(value, fromLow, fromHigh, toLow, toHigh)
Описание
Функция пропорционально переносит число из одного диапазона в другой. Причем число не обязательно должно быть ограничено рамками первого интервала. Также верхняя граница интервала может быть меньше нижний границы.
Синтаксис
map(value, fromLow, fromHigh, toLow, toHigh)
Параметры
value — число, которое следует пропорциональное перенести из первого интервала во второй
fromLow — нижняя граница текущего интервала
fromHigh — верхняя граница текущего интервала
toLow — нижняя граница целевого интервала
toHigh — верхняя граница целевого интервала
Возвращаемое значение
Число, пропорционально перенесенное из одного интервала в другой
Пример
Примечания
Функция map() использует целочисленную математику, поэтому при необходимости использования в проекте вычислений с большей точностью, чем целые числа, не рекомендуется использовать данную функцию, а делать все вычисления следует вручную.
Функция pow(base, exponent)
Описание
Функция возвращает результат операции возведения числа в степень. И параметры, и результат — числа с плавающей точкой.
Синтаксис
pow(base, exponent)
Параметры
base — число
exponent — степень
Возвращаемое значение
Результат возведения в степень
Пример
Функция sq(x)
Описание
Вычисляет квадрат числа: результат умножения числа самого на себя.
Синтаксис
sq(x)
Параметры
x — число, квадрат которого требуется вычислить
Возвращаемое значение
Квадрат числа
Пример
Примечания
Функция sqrt(x)
Описание
Вычисляет квадратный корень из числа.
Синтаксис
sqrt(x)
Параметры
x — число, квадратный корень из которого требует вычислить
Возвращаемое значение
Квадратный корень
Пример
Исходный код программы (скетча)
Arduino
#include <Arduino_FreeRTOS.h>
void TaskBlink1( void *pvParameters );
void TaskBlink2( void *pvParameters );
void Taskprint( void *pvParameters );
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
xTaskCreate(
TaskBlink1
, «task1»
, 128
, NULL
, 1
, NULL );
xTaskCreate(
TaskBlink2
, «task2»
, 128
, NULL
, 1
, NULL );
xTaskCreate(
Taskprint
, «task3»
, 128
, NULL
, 1
, NULL );
vTaskStartScheduler();
}
void loop()
{
}
void TaskBlink1(void *pvParameters) {
pinMode(8, OUTPUT);
while(1)
{
Serial.println(«Task1»);
digitalWrite(8, HIGH);
vTaskDelay( 200 / portTICK_PERIOD_MS );
digitalWrite(8, LOW);
vTaskDelay( 200 / portTICK_PERIOD_MS );
}
}
void TaskBlink2(void *pvParameters)
{
pinMode(7, OUTPUT);
while(1)
{
Serial.println(«Task2»);
digitalWrite(7, HIGH);
vTaskDelay( 300 / portTICK_PERIOD_MS );
digitalWrite(7, LOW);
vTaskDelay( 300 / portTICK_PERIOD_MS );
}
}
void Taskprint(void *pvParameters) {
int counter = 0;
while(1)
{
counter++;
Serial.println(counter);
vTaskDelay(500 / portTICK_PERIOD_MS); }
}
1 |
#include <Arduino_FreeRTOS.h> voidTaskBlink1(void*pvParameters); voidTaskBlink2(void*pvParameters); voidTaskprint(void*pvParameters); voidsetup(){ // initialize serial communication at 9600 bits per second: Serial.begin(9600); xTaskCreate( TaskBlink1 ,»task1″ ,128 ,NULL ,1 ,NULL); xTaskCreate( TaskBlink2 ,»task2″ ,128 ,NULL ,1 ,NULL); xTaskCreate( Taskprint ,»task3″ ,128 ,NULL ,1 ,NULL); vTaskStartScheduler(); } voidloop() { voidTaskBlink1(void*pvParameters){ pinMode(8,OUTPUT); while(1) { Serial.println(«Task1»); digitalWrite(8,HIGH); vTaskDelay(200portTICK_PERIOD_MS); digitalWrite(8,LOW); vTaskDelay(200portTICK_PERIOD_MS); } } voidTaskBlink2(void*pvParameters) { pinMode(7,OUTPUT); while(1) { Serial.println(«Task2»); digitalWrite(7,HIGH); vTaskDelay(300portTICK_PERIOD_MS); digitalWrite(7,LOW); vTaskDelay(300portTICK_PERIOD_MS); } } voidTaskprint(void*pvParameters){ intcounter=; while(1) { counter++; Serial.println(counter); vTaskDelay(500portTICK_PERIOD_MS);} } |
Горячие клавиши Arduino IDE
Для упрощения работы в Arduino IDE используются «горячие клавиши» или хоткеи, от английского hotkeys, что в переводе и означает «горячие клавиши». Это комбинации клавиш на клавиатуре, которые выполняют различные действия в операционной системе и программах. Все команды доступны через меню «Файл», но через хоткеи работать гораздо быстрее.
Разберем горячие клавиши и их назначение.
Правка
Ctrl+Z | отмена одной операции |
Ctrl+Y | возврат одной отмененной операции |
Ctrl+F | поиск по коду |
Atrl+A | выделение всего кода |
Ctrl+P | печать содержимого вкладки |
Ctrl+X | вырезать выделенный код |
Ctrl+C | копировать выделенный код |
Ctrl+V | вставить выделенный код |
Сохранение и работа с вкладками
Ctrl+S | сохранить текущий скетч |
Ctrl+Shift+S | сохранить текущий скетч с выбором имени сохраняемого файла |
Ctrl+W | закрыть текущую вкладку |
Ctrl+Shift+N | новая вкладка |
Ctrl+Alt+Стрелка вправо | переключение на вкладку справа от активной |
Ctrl+Alt+Стрелка влево | переключение на вкладку слева от активной |
Другое
Ctrl+N | открыть новое окно редактора |
Ctrl+O | открыть существующий файл скетча |
Ctrl+Слэш ( / или русская точка) | закомментирование строки |
Ctrl+K | открыть папку со скетчами |
Ctrl+T | автоформатирование кода |
Ctrl+Shift+M | монитор порта |
Ctrl+, (русская буква Б) | страница настроек Arduino IDE. |
Синтаксис void setup()
Синтаксис функции setup очень прост:
void setup(){
// тут располагаются команды подпрограммы
}
В этом примере слово setup – это название функции. Нужно писать именно так, именно в таком регистре. Слово перед названием описывает тип возвращаемых данных. В данном случае никаких данных подпрограмма не возвращает, поэтому мы должны указать слово void. Все команды должны размещаться внутри фигурных скобок {}.
Не забывайте ставить фигурные скобки! Потеря хотя бы одной скобки сразу сделает всю программу непонятной для ардуино. Но и лишние скобки тоже не ставьте – это так же приведет к ошибке.
Recent Articles
-
Learn how to use the TP4056 properly. There’s a right way, and a wrong way, to use it to safely charge Lithium Ion batteries.
-
A tutorial on using the ADS1115 precision 16 bit ADC for low power use.
-
Arduino Nano ISP: How to program an ATmega328P using an Arduino Nano as the ISP programmmer. One common problem: Programming a sketch into the chip without a reset control — solved here.
-
I2C tutorial: Learn all about the 2 wire I2C serial protocol. Learn how easy it is to use, how it works and when to use it…
-
How to test and use an Arduino Joystick including a new library to make it super easy.
-
How to use the MCP4728, a versatile four channel DAC with built in voltage reference.
The MCP4728 chip is a four channel 12 bit DAC, with memory that outputs voltage that you can use for calibration, anywhere you want a fixed voltage.
Read morereport this ad
Программирование обоих ядер ESP32
Arduino IDE поддерживает FreeRTOS для ESP32 и поэтому API функции FreeRTOS позволяют нам создавать задачи, которые будут независимо исполняться на обоих ядрах. Задача в данном случае – это кусок кода программы, который выполняет определенные задачи, например, мигание светодиода, передача значения температуры и т.д.
Следующая функция, которую мы рассмотрим, будет создавать задачи, исполняемые на обоих ядрах. В этой функции нам необходимо будет задать ряд ее аргументов – приоритет задачи, идентификатор ядра и т.д.
Для создания этой функции нам необходимо выполнить следующую последовательность шагов:
1. Первым делом необходимо создать задачи в функции void setup. В данной статье мы создадим две задачи: одну для мигания светодиодом каждые полсекунды, а другую – для считывания значения температуры каждые 2 секунды.
Функция xTaskCreatePinnedToCore() будет содержать у нас 7 аргументов:
- Имя функции для выполнения задачи (task1).
- Любое имя, которое необходимо будет дать задаче (“task1” и т.д.).
- Размер стека в словах, выделенный для задачи (1 слово = 2 байта).
- Входные параметры задачи (может быть NULL, то есть входных параметров может не быть).
- Приоритет задачи (0 – это самый низкий приоритет).
- Идентификатор задачи (может быть NULL).
- Идентификатор ядра, на котором будет выполняться задача (0 или 1).
Теперь создадим нашу первую задачу (Task1) мигания светодиодом и укажем все необходимые аргументы для функции xTaskCreatePinnedToCore().
Arduino
xTaskCreatePinnedToCore(Task1code, «Task1», 10000, NULL, 1, NULL, 0);
1 | xTaskCreatePinnedToCore(Task1code,»Task1″,10000,NULL,1,NULL,); |
Аналогичным образом создадим задачу 2 (Task2), для которого 7-м аргументом в качестве идентификатора ядра укажем 1 (core id 1).
Arduino
xTaskCreatePinnedToCore(Task2code, «Task2», 10000, NULL, 1, NULL, 1);
1 | xTaskCreatePinnedToCore(Task2code,»Task2″,10000,NULL,1,NULL,1); |
Вы можете изменить приоритет и размер стека в зависимости от сложности задачи.
2. Теперь мы можем выполнить функции Task1code и Task2code. Эти функции содержат код для выполнения требуемой задачи. В нашем случае первой задачей будет мигание светодиодом, а второй – считывание значения температуры. Поэтому создадим две отдельные функции для выполнения каждой из этих задач за пределами функции void setup.
Код функции Task1code для мигания светодиодом каждые 0.5 секунды при этом будет выглядеть следующим образом:
Arduino
Void Task1code( void * parameter) {
Serial.print(«Task1 running on core «);
Serial.println(xPortGetCoreID());
for(;;) {//infinite loop
digitalWrite(led, HIGH);
delay(500);
digitalWrite(led, LOW);
delay(500);
}
}
1 |
VoidTask1code(void*parameter){ Serial.print(«Task1 running on core «); Serial.println(xPortGetCoreID()); for(;;){//infinite loop digitalWrite(led,HIGH); delay(500); digitalWrite(led,LOW); delay(500); } |
Аналогичным образом можно записать и код функции Task2code для считывания значения температуры.
Arduino
void Task2code( void * pvParameters ){
Serial.print(«Task2 running on core «);
Serial.println(xPortGetCoreID());
for(;;){
float t = dht.readTemperature();
Serial.print(«Temperature: «);
Serial.print(t);
delay(2000);
}
}
1 |
voidTask2code(void*pvParameters){ Serial.print(«Task2 running on core «); Serial.println(xPortGetCoreID()); for(;;){ floatt=dht.readTemperature(); Serial.print(«Temperature: «); Serial.print(t); delay(2000); } } |
3. Функцию void loop мы в нашем случае оставим пустой. Поскольку мы уже знаем, что функции loop и setup исполняются на первом ядре (core1), то мы можем выполнять задачу для первого ядра (core1) также в функции void loop.
На этом написание программы закончено, просто загрузите код программы в модуль с помощью Arduino IDE, выбрав в ней в меню Tools плату ESP32. Также убедитесь в том, что вы подсоединили датчик DHT11 к контакту D13 платы ESP32.
Результаты работы нашей программы можно посмотреть в окне монитора последовательной связи.
Более сложные системы реального времени можно проектировать аналогичным образом, запуская разные задачи одновременно на двух ядрах модуля ESP32.