Освой Arduino играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

ESP32: Базовые примеры

Мигаем светодиодом
RGB-светодиод
Измеряем температуру чипа
Датчик Холла для обнаружения магнита
Базовая информация о модуле: getSdkVersion(), esp_get_idf_version()
Программная перезагрузка платы: restart()
Случайное число: esp_random()
BASE64
Джойстик
Датчик температуры и влажности DHT11/DHT22
Задействуем разные ядра микроконтроллера

Если использовать стандартный скетч из File | Examples | 01.Basics | Blink, то получим ошибку. Причина в следующем - у ESP32 нет константы LED_BUILTIN, которая указывает на встроенный светодиод (что достаточно странно). Поэтому следует явно указать вывод платы, как это было в старых примерах. Кроме того, встроенный светодиод находится не на выводе 13, а на выводе 2. С учётом этих особенностей скетч для мигания встроенным светодиодом будет следующим.


// Мигаем встроенным светодиодом на ESP32
const int LED = 2;

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  delay(1000);
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED, LOW);
}

RGB-светодиод

У ESP32 нет функции analogWrite(), поэтому стандартный способ не подходит. Взамен можно использовать другие функции, о которых в другой статье. А пока простой пример для включения основных цветов без промежуточных вариантов для модуля RGB-светодиода. Используем выводы 12, 13, 14 и GND.


void setup()
{
	pinMode(12, OUTPUT);
	pinMode(13, OUTPUT);
	pinMode(14, OUTPUT);
}

void loop()
{
	digitalWrite(12, HIGH);
	digitalWrite(13, HIGH);
	digitalWrite(14, HIGH);
	delay(2000);

	digitalWrite(12, LOW);
	digitalWrite(13, LOW);
	digitalWrite(14, LOW);
	delay(2000);

	digitalWrite(12, LOW);
	digitalWrite(13, HIGH);
	digitalWrite(14, LOW);
	delay(2000);

	digitalWrite(12, LOW);
	digitalWrite(13, LOW);
	digitalWrite(14, HIGH);
	delay(2000);
}

Другой пример работы с RGB-модулем

Измеряем температуру чипа

У платы есть встроенный температурный датчик, измеряющий температуру чипа. Датчик не получится использовать для измерения окружающей температуры воздуха, поэтому в большинстве случаев он бесполезен. Скорее всего он пригодится при использовании очень ресурсоёмких задач, когда есть риск спалить процессор.

При тестировании у меня всегда выводилось 53.33 градуса. Даже не знаю, можно ли ему вообще доверять.


#ifdef __cplusplus
extern "C" {
#endif

uint8_t temprature_sens_read();

#ifdef __cplusplus
}
#endif

uint8_t temprature_sens_read();

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.print("Temperature: ");

  // Convert raw temperature in F to Celsius degrees
  Serial.print((temprature_sens_read() - 32) / 1.8);
  Serial.println(" C");
  delay(1000);
}

Датчик Холла для обнаружения магнита

У платы есть встроенный датчик Холла, который умеет обнаруживать магниты. Напишем скетч, который при обнаружении магнита будет включать светодиод. Также в Serial Monitor выводятся текущие показания.


const int LED = 2;

void setup() {
  Serial.begin(115200);
  pinMode(LED, OUTPUT);
}

void loop() {
  int sensor = hallRead();  // считываем показания датчика Холла
  Serial.print("Sensor Reading:");
  Serial.println(sensor);

  digitalWrite(LED, (sensor < 0) ? HIGH : LOW); // включаем светодиод при обнаружении магнита
  delay(500);
}

Смотри также пример для MicroPython.

Цифро-аналоговый преобразователь (DAC, digital to analog converter)

У платы есть 2 аналоговых выхода с ЦАП (8 бит): вывод 25 (DAC1) и вывод 26 (DAC2). Аналоговый выход цифро-аналогового преобразователя позволяет формировать 8-битные уровни напряжения.


#define DAC1 25
 
void setup() {
  Serial.begin(115200);
}
 
void loop() {
  int value = 128; // 255= 3.3V, 128=1.65V
  
  dacWrite(DAC1, value);
  delay(1000);
}

Запустите скетч и проверьте мультиметром значение напряжения на выводе 25. Можете менять в скетче уровень напряжения.

Базовая информация о модуле: getSdkVersion(), esp_get_idf_version()

Версия SDK, размер флеш-памяти, кучи (heap). У функции getSdkVersion() есть аналог в виде низкоуровневой функции esp_get_idf_version(), которая вернёт такой же ответ.


void setup() {
  Serial.begin(115200);
  Serial.println("SDK");
  Serial.println(ESP.getSdkVersion());
  Serial.println("SDK через низкоуровневую функцию:");
  Serial.println(esp_get_idf_version());
}

void loop() {}

Ответ на момент написания примера.


SDK
v3.2.3-14-gd3e562907
SDK через низкоуровневую функцию:
v3.2.3-14-gd3e562907

Программная перезагрузка платы: restart()

У платы есть готовая функция restart() для программной перезагрузки. Смотри пример в API.

Перепишем пример, добавив счётчик. Он будет уменьшаться раз каждую секунду с 10 до 0.


int counter = 10;

void setup() {
  Serial.begin(115200);
}

void loop() {

  Serial.println(counter);

  if (counter == 0) {
    Serial.println("Reset..");
    ESP.restart();
  }

  counter--;
  delay(1000);
}

Случайное число: esp_random()

У Arduino есть стандартные функции для получения случайных чисел random(). У ESP32 есть своя дополнительная функция esp_random(), которая возвращает число от 0 до UINT32_MAX (наибольшее беззнаковое число INT). Чтобы число было действительно случайным, должен работать Wi-Fi или Bluetooth-модуль для взятия значений из сигналов беспроводной связи.

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


void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("-----------");
  Serial.println(esp_random());
  Serial.println(random(10)); // 0-9
  Serial.println(random(10, 20)); // 10-19

  delay(1000);
}

BASE64

Переводим строку в схему кодирования BASE64. Для этого есть готовая библиотека base64.h.


#include <base64.h>

void setup() {
  Serial.begin(115200);
  String toEncode = "Hello Kitty";
  String encoded = base64::encode(toEncode);
  Serial.println(encoded);

}

void loop() {}

Раскодировать сообщение можно на онлайн-сервисах, например, https://www.base64decode.org/.

Джойстик

Рассмотрим пример использования джойстика.

Подключение (вместо 5В используем 3В). Скетч для джойстика остаётся без изменений, только поменяем номера выводов


KY-023 | ESP32 
----------------
   GND | GND 
   +5V | 3V3 
   VRx | 2
   VRy | 4 
    SW | 23
	
const int xPin = 2;
const int yPin = 4;
const int buttonPin = 23;

Если для плат Arduino возвращаемые значения джойстика в спокойном состоянии находятся в районе 511-512, то у ESP32 в районе 1445 (x), 1870 (y). А общий диапазон значений: 0-4095.

Датчик температуры и влажности DHT11/DHT22

Кроме стандартной библиотеки для Arduino, есть отдельная библиотека для ESP32, доступная через менеджер библиотек по ключевым словам "DHT sensor library for ESPx by beegee".

Общий пример.


#include "DHTesp.h"
 
DHTesp dht;
 
void setup()
{
  Serial.begin(115200);
 
  dht.setup(27);
}
 
void loop()
{
  float humidity = dht.getHumidity(); 
 
  Serial.print("Humidity: ");
  Serial.println(humidity);
 
  delay(10000);
}

Есть продвинутый вариант примера для датчика DHT22. Датчик требует задержки как минимум 2 секунды, мы можем узнать более точное значение задержки через функцию getMinimumSamplingPeriod()


#include "DHTesp.h"
 
DHTesp dht;
 
void setup()
{
  Serial.begin(115200);
 
  dht.setup(27);
 
  Serial.print("Minimum Sampling Period: ");
  Serial.println(dht.getMinimumSamplingPeriod());
}
 
void loop()
{
  delay(dht.getMinimumSamplingPeriod());
 
  float temperature = dht.getTemperature(); 
 
  Serial.println("------------------");
  Serial.print("Temperature: ");
  Serial.println(temperature);
 
}

Задействуем разные ядра микроконтроллера

Обычно ESP32 работает на одном ядре из двух возможных. Проверить можно через функцию xPortGetCoreID().


void setup() {
  Serial.begin(115200);
  Serial.print("Функция setup() запущена на ядре: ");
  Serial.println(xPortGetCoreID());
}
void loop() {
  Serial.print("Функция loop() запущена на ядре: ");
  Serial.println(xPortGetCoreID());
  delay(3000);
}

// Результат
// Функция setup() запущена на ядре: 1
// Функция loop() запущена на ядре: 1

Arduino IDE поддерживает FreeRTOS и её API позволяет создавать задачи, которые могут запускаться независимо на обеих ядрах.

Создадим для примера задачу при помощи функции xTaskCreatePinnedToCore(), которая содержит семь (!) параметров.

  1. Имя функции для задачи
  2. Имя задачи (строка)
  3. Размер стека под задачу в word(1 word = 2bytes)
  4. Входящий параметр задачи (может быть NULL)
  5. Приоритет задачи (0 самый маленький)
  6. Описатель задачи (может быть NULL)
  7. Идентификатор ядра, на котором будет выполняться задача (0 или 1)

// Пример
xTaskCreatePinnedToCore(Task1code, "Task1", 10000, NULL, 1, NULL,  0); 

Дополнительные материалы

M5Stack. Датчик температуры и влажности DHT11 (ESP32)

Реклама