ESP32 для новичков: от логических микросхем до умных устройств
В предыдущей статье «Первый шаг к микроконтроллерам: введение в цифровые схемы» вы познакомились с логическими микросхемами серии 155 и простыми устройствами на их основе. Однако для реализации более сложной схемы таких микросхем понадобится немало. А чтобы изменить логику её работы, потребуется переработать и перепаять соединения или даже собрать устройство заново.
Сегодня цифровые устройства часто создают на базе микроконтроллеров и микрокомпьютеров. Их поведение определяется программой, которую при необходимости можно быстро изменить.
Множество окружающих нас «умных» устройств содержат микроконтроллеры: электрочайники, стиральные машины, кондиционеры, игрушки и компоненты умного дома.
Для взаимодействия с внешними устройствами микроконтроллеры оснащают линиями ввода-вывода общего назначения — General-Purpose Input/Output (GPIO). Обычно они используются для цифровых сигналов. Некоторые микроконтроллеры также содержат АЦП и ЦАП для работы с аналоговыми сигналами. Если их точности недостаточно, применяют внешние модули.
К GPIO можно подключить как простые компоненты, например кнопки и светодиоды, так и более сложные устройства — дисплеи или радиомодули. Одни соединяются с выводами напрямую, для других необходимы дополнительные модули. Так, реле позволяет управлять мощной нагрузкой, а специальные интерфейсные модули — электродвигателями.
Возможности современных микроконтроллеров выходят за рамки обработки сигналов. Они обмениваются данными по Wi-Fi и Bluetooth, работают как веб-серверы и создают точки доступа, к которым можно подключить телефон. Такая точка доступа позволяет управлять устройством даже без подключения к интернету.
В этой статье я покажу, как подключать кнопки и светодиоды к отладочной плате ESP32-S3-DevKitC-1-N16R8. Вы научитесь создавать программы в Arduino IDE, загружать их в микроконтроллер и проверять работу на макетной плате. Мы также рассмотрим прерывания, ШИМ и распиновку ESP32-S3-DevKitC-1-N16R8.
Каждый пример основан на несложной схеме, которую можно собрать самостоятельно. Повторяя их, вы получите практический опыт работы с ESP32: от включения светодиода до управления им с помощью кнопок.
Какой микроконтроллер выбрать для первых опытов
Среди популярных платформ сегодня можно выделить Arduino, ESP32, nRF52840, Raspberry Pi Pico и STM32. Они различаются числом ядер и частотой процессора, объёмом оперативной памяти, наличием беспроводной связи, количеством GPIO и другими характеристиками.
Начнём со знакомой многим экосистемы Arduino (рис. 1).

Классическая плата UNO R3 построена на 8-битном микроконтроллере ATmega328P с частотой 16 МГц и имеет 2 КБ оперативной памяти.
UNO R4 Minima и компактная Nano R4 используют 32-битный микропроцессор Renesas RA4M1 с частотой 48 МГц и 32 КБ ОЗУ, но встроенной беспроводной связи у них тоже нет. Исключение составляет UNO R4 WiFi: поддержку Wi-Fi и BLE в ней обеспечивает отдельный микроконтроллер ESP32-S3. В целом Arduino хорошо подходит для обучения и несложных задач автоматизации.
Для Arduino выпущено множество плат расширения — шилдов (Shields), которые можно устанавливать друг на друга. На рис. 2 слева показана Arduino с платой для подключения Ethernet и карты памяти SD, справа — модуль Wi-Fi.

Платформа STM32 широко распространена в профессиональной электронике и включает множество моделей. Для новичка это разнообразие может стать препятствием: экосистема инструментов достаточно сложна, а настройка периферии требует опыта. Кроме того, большинство моделей не имеют встроенной беспроводной связи.
На рис. 3 представлена отладочная плата NUCLEO-F401RE, которую удобно применять для прототипирования.

Сильная сторона nRF52840 — низкое энергопотребление. Wi-Fi платформа не поддерживает, зато предлагает более экономичные BLE, Thread и Zigbee. При этом её производительность сравнительно невысока, а освоение может оказаться непростым для начинающих.
Raspberry Pi Pico — недорогая плата от Raspberry Pi на микроконтроллере RP2040: два ядра ARM Cortex-M0+ с частотой до 133 МГц и 264 КБ ОЗУ. Для программирования можно использовать MicroPython, C/C++ или Arduino IDE. В версии Pico W дополнительно доступны Wi-Fi и Bluetooth.
Для примеров в этой статье я выбрал ESP32 китайской компании Espressif Systems. Платформа сочетает встроенные Wi-Fi и Bluetooth LE, достаточную вычислительную мощность и богатый набор периферии. Программы для неё можно писать как в простой Arduino IDE, так и с помощью профессиональных инструментов.
На базе ESP32 выпускают недорогие компактные модули, а также более оснащённые платы с экранами и средствами дальней радиосвязи LoRa. При этом их стоимость остаётся относительно невысокой.
На рис. 4 слева изображена отладочная плата ESP32-S3-DevKitC-1-N16R8, справа — компактная ESP32-S3 Super Mini.

ESP32-S3-DevKitC-1-N16R8 оснащена 512 КБ встроенной SRAM, 16 МБ флеш-памяти и 8 МБ PSRAM.
Флеш-память сохраняет данные после отключения питания; в неё записывается прошивка устройства. Содержимое псевдостатической памяти Pseudo-Static RAM (PSRAM), напротив, при выключении питания теряется. Она также работает медленнее встроенной SRAM. Кроме того, предусмотрена небольшая RTC-память, сохраняющая данные в режиме глубокого сна, когда энергопотребление платы значительно снижается. Подробнее типы памяти описаны в разделе «Memory Types» документации.
Микроконтроллер имеет два процессорных ядра с частотой до 240 МГц, поддерживает Wi-Fi 4 и Bluetooth 5 LE. На плате также размещена антенна Wi-Fi.
Для первых опытов пригодятся встроенный RGB-светодиод, два разъёма USB и кнопки сброса RST и загрузки BOOT.
Производительности этого микроконтроллера достаточно для множества задач: обработки видео с камеры и звука, вывода изображений на дисплей и запуска специально подготовленных нейросетевых моделей.
ESP32-S3 Super Mini, несмотря на небольшие размеры, тоже обладает широкими возможностями. Она оснащена двумя ядрами Xtensa LX7 с частотой до 240 МГц, модулями Wi-Fi 4 и Bluetooth 5 LE, 512 КБ SRAM, разъёмом USB и выводами GPIO. Такую плату можно использовать в системах умного дома, устройствах с дисплеем и проектах по обработке звука и изображений.
На рис. 5 представлены платы Waveshare ESP32-S3-LCD-1.47 и Heltec HTIT-WB32LAF.

Компактная ESP32-S3-LCD-1.47 оснащена цветным экраном и двухъядерным процессором Xtensa LX7 с частотой до 240 МГц. Она поддерживает Wi-Fi 4 и Bluetooth 5 LE, имеет встроенную антенну, 16 МБ флеш-памяти и 8 МБ PSRAM.
Дисплей диагональю 1,47 дюйма с разрешением 172×320 и поддержкой 262 тыс. цветов годится как для экспериментов, так и для разработки графических интерфейсов.
На плате предусмотрены слот для карт TF (Micro-SD), разъём USB и кнопки RST и BOOT.
Справа на рис. 5 находится Heltec HTIT-WB32LAF. Её особенность — LoRa-трансивер SX1262, который позволяет передавать данные на расстояние в несколько километров, пусть и с небольшой скоростью. Также плата оснащена OLED-дисплеем диагональю 0,96 дюйма и разрешением 128×64, разъёмами для солнечной панели и Li-Po-аккумулятора и схемой зарядки.
Сравнение характеристик популярных современных микроконтроллеров можно найти в статье «Top 10 Microcontrollers in 2026: A Comprehensive Comparison Guide».
Чем микроконтроллер отличается от микрокомпьютера
Для взаимодействия с оборудованием применяют не только микроконтроллеры, но и микрокомпьютеры. На рис. 6 в качестве примера показаны Raspberry Pi 3 Model B+ и Orange Pi 3B.

Микрокомпьютеры тоже имеют GPIO для подключения внешних устройств, но отличаются от микроконтроллеров назначением, стоимостью и энергопотреблением.
Микроконтроллеры
Микроконтроллер объединяет в одной микросхеме процессор, память и периферию — фактически целую вычислительную систему. Он выполняет прошивку без операционной системы или под управлением операционной системы реального времени RTOS. ESP32 потребляет относительно небольшой ток — около 100–300 мА, а в режиме сна расходует значительно меньше энергии. Программа на микроконтроллере запускается за доли секунды.
Микроконтроллеры используют в датчиках и IoT-устройствах, для управления двигателями, реле и бытовой техникой. Они востребованы в автомобильной и промышленной электронике — везде, где важны точное соблюдение временных интервалов, экономичность и надёжность.
Микрокомпьютеры
Микрокомпьютеры, например Raspberry Pi и Orange Pi, представляют собой полноценные компьютеры на одной плате. Их также называют одноплатными компьютерами — Single-Board Computer (SBC).
Обычно такие устройства оснащают многоядерными ARM-процессорами с частотой порядка 1–2,5 ГГц и оперативной памятью объёмом от 512 МБ до 16 ГБ. Они работают под управлением операционной системы, например Linux или Android. На плате могут быть разъёмы USB, Ethernet и HDMI, а также интерфейсы для камер.
Микрокомпьютеры применяют в качестве домашних серверов и шлюзов, медиацентров и информационных киосков, в системах компьютерного зрения и искусственного интеллекта. Они подходят для прототипирования сложных проектов и координации нескольких микроконтроллеров.
На микрокомпьютере можно запускать самые разные программы: веб-серверы, базы данных, Docker и другие приложения. Однако гарантировать время реакции такой системы нельзя. Её энергопотребление составляет примерно 2–15 Вт. Перед отключением питания операционную систему нужно корректно завершить, иначе файловая система на SD-карте может быть повреждена.
Микроконтроллер обычно значительно дешевле микрокомпьютера: первый можно купить за несколько сотен рублей, второй — за несколько тысяч или даже десятков тысяч. На цену микрокомпьютера заметно влияет объём оперативной памяти. Кроме того, для него могут понадобиться корпус, SD-карта, блок питания и вентилятор.
В сложных проектах оба типа устройств нередко работают вместе: микроконтроллер взаимодействует с оборудованием в реальном времени, а микрокомпьютер отвечает за сложную логику, сеть и нейросети.
Плата ESP32-S3-N16R8
Для примеров в этой статье используется ESP32-S3-DevKitC-1-N16R8 (рис. 7).

На плате расположены два USB-разъёма. С нижней стороны один обозначен как COM, другой — как USB. Первый подключён через мост USB-UART CH343P и используется для загрузки прошивки и отладки кода. В зависимости от производителя платы вместо него может применяться CP2102N.
Разъём USB напрямую соединён с выводами GPIO19 и GPIO20. Его можно использовать как виртуальный COM-порт, интерфейс отладки USB-JTAG или для эмуляции USB-устройств — клавиатуры, мыши и геймпада.
Подключите плату к USB-порту компьютера через разъём COM. Запустится установленная на ней прошивка, которая периодически меняет цвет RGB-светодиода (рис. 8).

На рис. 8 видны ещё три светодиода. Нижний красный индикатор показывает наличие питания. Один из двух остальных мигает при передаче данных с компьютера на ESP32 через COM, например во время загрузки прошивки, а второй — при отправке данных с ESP32 на компьютер, в том числе отладочных сообщений.
Кнопка RST перезапускает микроконтроллер и программу. Кнопка BOOT соединена с выводом GPIO0. Если удерживать её при сбросе или включении питания, ESP32-S3 перейдёт в режим загрузчика для прошивки. Во время обычной работы её можно использовать как пользовательскую кнопку.
Обозначение «N16R8» указывает на объём памяти: 16 МБ флеш-памяти (N16) и 8 МБ PSRAM (R8).
Пока подключать дополнительные компоненты не будем. Сначала установим Arduino IDE, а к работе с оборудованием вернёмся позже.
Установка Arduino IDE в Windows 11
Перейдите на сайт arduino.cc, нажмите Download и скачайте установщик Arduino IDE для Windows. Для версии 2.3.10 файл называется arduino-ide_2.3.10_Windows_64bit.exe. Его также можно найти на сайте одного из магазинов микроконтроллеров.
Запустите скачанный файл и пройдите этапы установки. При появлении запросов разрешите установку драйверов. Подробное описание процедуры приведено в статье «Как установить arduino ide на windows».
Программы для Arduino IDE пишут на C++, поэтому знание языка будет полезным. Начать можно с основ и готовых примеров, изучая остальное по мере работы. Для обучения подойдут англоязычный сайт LearnCpp.com и русскоязычный учебник «Уроки С++».
О C++ написано немало книг, в том числе «Язык программирования C++. Лекции и упражнения» Стивена Прата. Также рекомендую англоязычную документацию «Welcome to ESP32 Arduino Core’s documentation».
Запускаем первую программу
Подключив плату к компьютеру с Microsoft Windows 11, выясните, какой COM-порт ей назначен. Для этого откройте Диспетчер устройств (рис. 9) и разверните раздел Порты (COM и LPT).

В названии устройства должен упоминаться мост USB-UART, например CH343 или CP2102. В моём случае контроллеру назначен COM4. Это название понадобится при работе с ESP32 в Arduino IDE.
Если порт определился, откройте Arduino IDE. В меню Tools выберите Port, а затем в разделе Serial Port укажите порт, к которому подключена плата (рис. 10).

У меня выбран COM4, но на вашем компьютере номер может отличаться.
Затем откройте File → Preferences и введите в поле Additional boards manager URLs следующий адрес:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Нажмите OK, чтобы сохранить настройки.
Теперь установим поддержку нужной платформы. В меню Tools выберите Board, затем Boards manager. Введите в поиске «esp Espressif» и найдите пакет esp32 от Espressif Systems (рис. 11).

Нажмите INSTALL и дождитесь окончания установки.
После этого откройте Tools → Board и в разделе esp32 выберите ESP32S3 Dev Module (рис. 12).

Если настройки выполнены верно, название платы появится на панели инструментов, а выбранный COM-порт — в строке состояния (рис. 13).

Для N16R8 в меню Tools установите Flash Size: 16MB и PSRAM: OPI PSRAM. Иначе PSRAM не будет работать. Для примера с мигающим светодиодом это, впрочем, не критично.
Если плата подключена к компьютеру через разъём с обозначением USB, для отладки через Serial установите USB CDC On Boot: Enabled. При подключении через COM эту опцию следует оставить выключенной.
Об отладке я расскажу в одной из следующих статей цикла.
Создание скетча
Создайте на компьютере папку RGB-Blink и сохраните в ней файл RGB-Blink.ino со следующим содержимым:
#define RGB_PIN 48
void setup() {
randomSeed(esp_random()); // аппаратный генератор случайных чисел
}
void loop() {
uint8_t r, g, b;
do {
r = random(0, 256);
g = random(0, 256);
b = random(0, 256);
} while (r + g + b < 100); // отсекаем слишком тёмные цвета
// Яркость уменьшена в 4 раза, светодиод очень яркий
neopixelWrite(RGB_PIN, r / 4, g / 4, b / 4);
delay(1000);
}
Готовый файл также доступен в моём репозитории на GitHub.
Откройте его в Arduino IDE (рис. 14).

Чтобы скомпилировать программу, загрузить её на плату и запустить, нажмите кнопку Upload со стрелкой, выделенную на рис. 14.
В нижней части окна, на панели Output, появятся сообщения о компиляции и загрузке (рис. 15).

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

Плата перезапустится, как после нажатия RST, и начнёт выполнять загруженную программу.
Принцип работы RGB-Blink.ino
Скетч RGB-Blink.ino каждую секунду меняет цвет встроенного RGB-светодиода, выбирая новый случайным образом.
В начале кода указан номер вывода, к которому подключён светодиод:
#define RGB_PIN 48
На официальной ESP32-S3-DevKitC-1 светодиод соединён с GPIO48 в ревизии 1.0 или с GPIO38 в ревизии 1.1. У клонов и других плат на ESP32-S3 подключение может быть иным, поэтому проверьте документацию производителя.
Макрос RGB_BUILTIN позволяет не привязывать программу к конкретному номеру вывода.
На плате установлен не обычный трёхцветный светодиод, а адресуемый светодиод со встроенным контроллером — WS2812 или совместимый с ним, также известный как NeoPixel. Для управления им достаточно одной линии GPIO.
Программы для ESP32 обычно содержат две функции — setup и loop. Первая выполняется при запуске и проводит необходимую инициализацию. Затем многократно вызывается loop, которая служит основным циклом программы.
При запуске функция randomSeed инициализирует генератор случайных чисел:
void setup() {
randomSeed(esp_random());
}
Затем loop вызывает random, чтобы получить значения компонентов цвета от 0 до 255:
uint8_t r, g, b;
do {
r = random(0, 256);
g = random(0, 256);
b = random(0, 256);
} while (r + g + b < 100);
Верхняя граница, переданная вторым аргументом random, не включается в диапазон. Поэтому указано 256, а не 255.
Условие while отбрасывает слишком тёмные цвета.
Цвет светодиоду задаёт функция neopixelWrite:
neopixelWrite(RGB_PIN, r / 4, g / 4, b / 4);
Она передаёт значения яркости красного, зелёного и синего компонентов. Поскольку встроенный светодиод светит очень ярко, каждое значение делится на четыре: остаётся примерно 25% максимальной яркости.
Перед следующим проходом loop делает паузу на одну секунду:
delay(1000);
Функция esp_random возвращает 32-битное значение аппаратного генератора ESP32. В этом скетче randomSeed использует его для инициализации генератора псевдослучайных чисел Arduino, а random затем формирует последовательность для выбора цветов.
Поэтому после перезапуска порядок цветов меняется. Аппаратный генератор выдаёт полноценно случайные числа только при работающем радиоблоке, но для выбора цвета светодиода в этом примере ограничение несущественно.
Подключение кнопок
В статье «Первый шаг к микроконтроллерам: введение в цифровые схемы» я показывал схемы на цифровых логических микросхемах с двумя кнопками, реализующие операции 2И-НЕ, 2И и исключающее ИЛИ. Их входы подтягивались к питанию резисторами. В схемах со счётчиками и сдвиговым регистром для подавления дребезга контактов применялся конденсатор.
С помощью микроконтроллера эти схемы можно упростить. ESP32-S3-DevKitC-1-N16R8 позволяет подтягивать большинство входов GPIO к питанию без внешних резисторов, а дребезг кнопок устранять программно.
Соберите на макетной плате схему с микроконтроллером и двумя кнопками, как показано на рис. 17.

ESP32-S3-DevKitC-1-N16R8 довольно широкая, поэтому для доступа ко всем выводам я соединил две макетные платы. Если соединить их не получается, проверьте, что платы одного типа и из одной поставки.
Кнопки подключены к GPIO4 и GPIO5, а также к земле — контакту GND платы. Внешние подтягивающие резисторы не нужны.
Программа esp32s3_onboard_rgb_polling
Скетч esp32s3_onboard_rgb_polling.ino управляет цветом встроенного RGB-светодиода ESP32-S3-DevKitC-1-N16R8.
К GPIO4 и GPIO5 подключены две кнопки. При нажатии первой GPIO4 соединяется с землёй и светодиод становится синим. Вторая кнопка, подключённая к GPIO5, включает зелёный цвет. Одновременное нажатие обеих кнопок даёт голубой цвет.
Номера выводов кнопок задаются так:
const int BTN_BLUE = 4;
const int BTN_GREEN = 5;
При инициализации setup включает подтяжку GPIO4 и GPIO5 к питанию и с помощью setLed выключает RGB-светодиод:
void setup() {
pinMode(BTN_BLUE, INPUT_PULLUP);
pinMode(BTN_GREEN, INPUT_PULLUP);
setLed(false, false);
}
Функция setLed устанавливает яркость отдельных цветовых компонентов:
void setLed(bool green, bool blue) {
neopixelWrite(LED_PIN, 0, green ? BRIGHT : 0, blue ? BRIGHT : 0);
}
Управление цветом происходит в цикле loop:
void loop() {
uint8_t raw = 0;
if (digitalRead(BTN_BLUE) == LOW) raw |= 0b01;
if (digitalRead(BTN_GREEN) == LOW) raw |= 0b10;
if (raw != lastRaw) {
lastRaw = raw;
lastChange = millis();
}
if (raw != stable && (millis() - lastChange) > DEBOUNCE_MS) {
stable = raw;
setLed(stable & 0b10, stable & 0b01);
}
delay(1);
}
Функция считывает кнопки, ждёт завершения дребезга и обновляет состояние светодиода. Она повторяется непрерывно, примерно раз в миллисекунду.
В начале каждого прохода состояния кнопок записываются в два бита переменной raw: бит 0 соответствует синей кнопке, бит 1 — зелёной.
Поскольку в setup включена подтяжка к питанию, нажатой считается кнопка, на входе которой присутствует логический ноль LOW. Возможны четыре двоичных значения:
-
0b00— ни одна кнопка не нажата; -
0b01— нажата синяя кнопка; -
0b10— нажата зелёная кнопка; -
0b11— нажаты обе кнопки.
Если текущее «сырое» состояние отличается от предыдущего, произошло изменение: кнопку нажали или отпустили либо её контакт дребезжит.
Новое состояние сохраняется в lastRaw, а текущее время в миллисекундах — в lastChange. Пока контакт дребезжит и показания меняются, время последнего изменения постоянно обновляется:
if (raw != lastRaw) {
lastRaw = raw;
lastChange = millis();
}
Затем проверяются два условия: текущее состояние отличается от последнего подтверждённого и с момента изменения прошло более 30 мс. Если оба выполнены, дребезг считается завершённым и светодиоду задаётся нужный цвет.
Выражение stable & 0b10 выделяет бит зелёной кнопки, а stable & 0b01 — синей:
if (raw != stable && (millis() - lastChange) > DEBOUNCE_MS) {
stable = raw;
setLed(stable & 0b10, stable & 0b01);
}
Работа с прерываниями
Для обработки сигналов оборудования часто используют прерывания. Получив сигнал, например от кнопки, процессор приостанавливает основную программу и передаёт управление специальной функции — обработчику прерывания Interrupt Service Routine (ISR).
Обработчик должен выполнять работу быстро, чтобы процессор как можно скорее вернулся к основной программе.
В скетче esp32s3_onboard_rgb_interrupt.ino нажатия обрабатываются с помощью прерываний, поэтому постоянно опрашивать состояние кнопок в цикле не требуется.
При инициализации setup назначает обеим кнопкам обработчик onButtonChange:
void setup() {
pinMode(BTN_BLUE, INPUT_PULLUP);
pinMode(BTN_GREEN, INPUT_PULLUP);
setLed(false, false);
attachInterrupt(digitalPinToInterrupt(BTN_BLUE), onButtonChange, CHANGE);
attachInterrupt(digitalPinToInterrupt(BTN_GREEN), onButtonChange, CHANGE);
}
Когда уровень на выводе кнопки меняется, выполнение основного цикла loop прерывается и вызывается onButtonChange:
void IRAM_ATTR onButtonChange() {
lastEdge = millis();
changed = true;
}
Режим CHANGE в attachInterrupt обеспечивает срабатывание как при нажатии, так и при отпускании кнопки.
Обработчик onButtonChange делает минимум необходимого: сохраняет время события в lastEdge и устанавливает флаг changed.
Атрибут IRAM_ATTR размещает обработчик во внутренней оперативной памяти IRAM, а не во флеш-памяти. Это важно, поскольку при записи во флеш-память ESP32 кэш может отключаться и код из неё в этот момент недоступен для выполнения.
Обе кнопки используют один обработчик. Их состояние определяется позднее, в loop:
void loop() {
if (changed && (millis() - lastEdge) > DEBOUNCE_MS) {
changed = false;
bool blue = (digitalRead(BTN_BLUE) == LOW);
bool green = (digitalRead(BTN_GREEN) == LOW);
setLed(green, blue);
}
}
Программа проверяет, произошло ли изменение и прошло ли более 30 мс с последнего изменения уровня:
const uint32_t DEBOUNCE_MS = 30;
…
changed && (millis() - lastEdge) > DEBOUNCE_MS
Когда условие выполняется, флаг changed сбрасывается и считывается текущее состояние обеих кнопок. Затем результат передаётся setLed, которая устанавливает цвет светодиода.
Поскольку программа считывает обе кнопки, неважно, какая из них была нажата первой и вызвала прерывание.
Переменные lastEdge и changed записываются обработчиком прерывания, а читаются в основном цикле loop. Поэтому они объявлены с модификатором volatile:
volatile uint32_t lastEdge = 0;
volatile bool changed = false;
Этот модификатор сообщает компилятору, что значение может измениться вне основного потока выполнения, например в обработчике прерывания, и его нужно читать из памяти при каждом обращении.
Без volatile компилятор может сохранить значение в регистре. В результате, например, цикл while, ожидающий изменения флага, может его так и не заметить.
При этом volatile обеспечивает чтение значения из памяти, но не делает составные операции атомарными.
Между проверкой и сбросом changed может возникнуть прерывание. Если это произойдёт, флаг будет потерян, но состояние кнопок считывается после сброса и изменение уровня будет учтено. Если же прерывание произойдёт после сброса, флаг установится снова и событие будет обработано на следующем проходе loop.
Подключение светодиодов к GPIO
Мы разобрались, как подключать кнопки к ESP32 и обрабатывать их сигналы. Теперь подключим к GPIO внешние светодиоды.
Соедините светодиоды с выводами GPIO6 и GPIO7 через резисторы номиналом 300 Ом (рис. 18).

Длинные выводы светодиодов — аноды — подключите к резисторам, а короткие соедините с землёй, контактом GND платы.
Программа button2led.ino
Простой скетч button2led.ino позволяет управлять светодиодами с помощью кнопок.
При инициализации для выводов кнопок включается подтяжка к питанию, а выводы светодиодов переводятся в режим выхода:
const int BTN1_PIN = 4;
const int BTN2_PIN = 5;
const int LED1_PIN = 6;
const int LED2_PIN = 7;
void setup() {
pinMode(BTN1_PIN, INPUT_PULLUP);
pinMode(BTN2_PIN, INPUT_PULLUP);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
}
В цикле loop функция digitalRead считывает кнопки, а digitalWrite включает соответствующий светодиод при нажатии:
void loop() {
digitalWrite(LED1_PIN, digitalRead(BTN1_PIN) == LOW);
digitalWrite(LED2_PIN, digitalRead(BTN2_PIN) == LOW);
}
Подавление дребезга здесь не реализовано: его влияние не будет заметно глазу.
Программа esp32s3_2leds.ino
Скетч esp32s3_2leds.ino плавно меняет яркость двух светодиодов в противофазе: когда один становится ярче, второй гаснет, и наоборот (рис. 19).

Яркость регулируется с помощью широтно-импульсной модуляции (ШИМ).
При инициализации вызовы ledcAttach привязывают выводы к ШИМ-каналам с заданными частотой и разрядностью:
const int LED1_PIN = 6;
const int LED2_PIN = 7;
const uint32_t PWM_FREQ = 5000; // 5 кГц, мерцания не видно
const uint8_t PWM_BITS = 8; // 0..255
…
void setup() {
ledcAttach(LED1_PIN, PWM_FREQ, PWM_BITS);
ledcAttach(LED2_PIN, PWM_FREQ, PWM_BITS);
}
Яркость изменяется в loop. В начале прохода вычисляется остаток от деления millis на 3000 мс — положение внутри трёхсекундного периода. Оно переводится в фазу от 0 до 2π, после чего вычисляется синус.
Синус принимает значения от −1 до 1. Следующее выражение преобразует этот диапазон в значения от 0 до 1:
float b1 = (sinf(phase) + 1.0f) / 2.0f;
Так определяется яркость первого светодиода. Для второго она вычисляется следующим образом:
float b2 = 1.0f - b1;
Благодаря этому светодиоды работают в противофазе.
Затем оба значения обрабатывает gamma8, учитывающая нелинейное восприятие яркости человеческим глазом. Она возвращает число от 0 до 255, которое передаётся в ledcWrite для обновления параметров ШИМ на каждом выводе.
В конце прохода выполняется задержка на пять миллисекунд:
const uint32_t PERIOD_MS = 3000; // полный цикл «вдох-выдох»
uint8_t gamma8(float x) { // x: 0.0..1.0
return (uint8_t)(powf(x, 2.2f) * 255.0f + 0.5f);
}
void loop() {
uint32_t t = millis() % PERIOD_MS;
float phase = (float)t / PERIOD_MS * 2.0f * PI;
// Синус превращаем из диапазона -1..1 в 0..1
float b1 = (sinf(phase) + 1.0f) / 2.0f;
float b2 = 1.0f - b1; // противофаза
ledcWrite(LED1_PIN, gamma8(b1));
ledcWrite(LED2_PIN, gamma8(b2));
delay(5);
}
Выводы GPIO платы ESP32-S3-DevKitC-1-N16R8
При автоматизации с помощью микроконтроллера важно понимать ограничения выводов GPIO. Их распиновка для ESP32-S3-DevKitC-1-N16R8 приведена на рис. 20.

Распиновка семейства также доступна на этой странице в разделе ESP32-S3-DevKitC-1 Pin Layout.
Хотя программа теоретически может обращаться к разным GPIO, выбирать выводы для внешних устройств нужно внимательно, чтобы избежать конфликтов.
Для обычных задач подходят GPIO1, 2, 4–18, 21, 38–42 и 47. На рис. 20 они отмечены зелёным. Номера соответствуют обозначениям на плате.
Следующие выводы использовать нельзя; на рисунке они отмечены красным или отсутствуют:
-
GPIO33–37 — заняты PSRAM модуля R8 и недоступны.
-
GPIO26–32 — подключены к SPI FLASH и PSRAM и не выведены наружу.
-
GPIO22–25 — физически отсутствуют у ESP32-S3.
С осторожностью также следует относиться к GPIO0, GPIO3, GPIO45, GPIO46, GPIO43, GPIO44, GPIO19 и GPIO20:
-
GPIO0 — соединён с кнопкой BOOT и используется для конфигурации при запуске.
-
GPIO3 — участвует в выборе источника JTAG и конфигурации при старте.
-
GPIO45 — используется для выбора напряжения SPI Flash.
-
GPIO46 — связан с выводом журнала ROM-загрузчика.
-
GPIO43, GPIO44 — используются консолью через USB-UART.
-
GPIO19, GPIO20 — соединены с разъёмом USB на плате.
Вывод GPIO48, а в версии платы 1.1 — GPIO38, управляет встроенным RGB-светодиодом.
Дополнительные сведения об аппаратной части и выводах приведены в документации «ESP32-S3-DevKitC-1 v1.0 User Guide».
Напряжение на выводах не должно превышать 3,3 В. Сигналы с уровнем 5 В необходимо подавать через делитель или преобразователь уровней.
Ток через вывод не должен превышать 20 мА. Абсолютный максимум составляет около 40 мА, но это предельное, а не рабочее значение. Поэтому светодиоды подключают через резисторы 220–330 Ом.
Периферийные функции ESP32-S3 распределяются через GPIO-матрицу. Благодаря этому I2C, SPI, UART, I2S и ШИМ (LEDC) можно назначить практически на любой подходящий GPIO. Стандартные назначения следующие:
-
I2C: SDA — GPIO8, SCL — GPIO9
-
SPI: CS — GPIO10, MOSI — GPIO11, SCK — GPIO12, MISO — GPIO13
На ESP32-S3-DevKitC-1-N16R8 доступны следующие возможности:
-
Два АЦП: ADC1 на GPIO1–10 и ADC2 на GPIO11–20. Wi-Fi-драйвер может периодически занимать ADC2, из-за чего чтение иногда завершается неудачно. Поэтому датчики предпочтительнее подключать к ADC1.
-
Сенсорные входы (Touch) доступны на GPIO1–14.
-
GPIO15 и GPIO16 предназначены для часового кварца 32 кГц (XTAL_32K_P/N). На официальной ESP32-S3-DevKitC-1 кварц не установлен — это видно на принципиальной схеме Espressif, — поэтому выводы можно использовать как обычные GPIO.
-
GPIO39–42 — выводы JTAG: MTCK, MTDO, MTDI и MTMS. Если внешний JTAG-отладчик не подключён, они могут работать как обычные GPIO.
Что изучить дальше
За пределами вводной статьи осталось немало важных тем. В следующих материалах об ESP32 мы рассмотрим подключение различной периферии, работу с Wi-Fi, отладку программ и другие вопросы.
В следующей статье перейдём от светодиодов к более серьёзной нагрузке и разберём управление лампами, электродвигателями и реле через GPIO. Выход микроконтроллера рассчитан на небольшой ток, поэтому подобные устройства подключают с помощью транзисторных ключей и драйверов. Я покажу, как их выбирать и подключать, а также приведу примеры программ.
Продолжайте изучать встраиваемые устройства на практике, собирая собственные схемы и экспериментируя с ними.
Автор @AlexandreFrolov
НЛО прилетело и оставило читателям нашего блога промокод:
скидка 15% на заказ нового VDS — HABRFIRSTVDS.
Создатели правил игры: семь экономистов, изменивших мир
ИИ воскрешает мёртвых: благо это или зло?
Мои ИИ-агенты нашли неизвестное поселение майя
Возможно, мы искали источник сознания не в той области мозга
Объединяются ли на самом деле все три квантовых взаимодействия в единое?
Учёные выяснили: кошки мяукают, требуя от хозяев порядка в своём мире
Как стреляли по телевизорам: история светового пистолета
Новая электрическая расчёска избавила мышей от облысения