Руководства · 19 мин чтения

ESP32: Brownout detector was triggered и Guru Meditation — как исправить

ESP32 пишет «Brownout detector was triggered» и бесконечно перезагружается? Исправляем питание, кабель и конденсатор, читаем коды rst, Guru Meditation, Task WDT и backtrace.

Сообщение ESP32 brownout detector was triggered означает, что напряжение питания платы на мгновение упало ниже безопасного предела и микросхема перезапустилась, чтобы не работать со сбоями. Проблема почти никогда не в коде, а в питании: слабый USB-порт, тонкий и длинный кабель, слабый стабилизатор или сервоприводы и светодиодные ленты, которые питаются от той же линии, что и ESP32. Вторая крупная причина, по которой ESP32 постоянно перезагружается, — программный сбой; в этом случае в мониторе порта появляются строки вроде Guru Meditation Error, task_wdt или Stack canary.

Хорошая новость: ESP32 сама сообщает, почему перезапустилась, нужно только уметь читать её сообщения. В этом руководстве сначала разберём коды rst в строках загрузки и сообщения об ошибках, потом навсегда избавимся от просадок питания с помощью правильного источника питания, кабеля и конденсатора. Затем перейдём к ошибкам Guru Meditation вроде LoadProhibited и StoreProhibited, переполнению стека, нулевым указателям и таймаутам сторожевого таймера, а с помощью ESP Exception Decoder отследим сбой до конкретной строки кода. В конце посмотрим на пины конфигурации при старте (strapping: GPIO0, 2, 12 и 15), которые могут сорвать загрузку.

Отладочная плата ESP32 DevKit
Отладочная плата ESP32: 5 V от USB понижаются встроенным стабилизатором до 3,3 V. Большинство проблем с просадкой питания возникает из-за коротких провалов на этой линии 3,3 V.

Почему ESP32 постоянно перезагружается? Сначала прочитай сообщение

Прежде чем искать неисправность, открой монитор порта на скорости 115200 бод и нажми кнопку EN на плате. При каждой загрузке встроенный в ПЗУ загрузчик ESP32 сначала печатает строку с датой вроде «ets Jun 8 2016 00:22:57», а затем строку вида rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT). Часть rst говорит, почему плата перезапустилась, а часть boot — в каком режиме она стартовала. Если плата застряла в цикле перезагрузок, прямо над этими строками будет сообщение об ошибке, последние слова предыдущего запуска. В таблице ниже собраны сообщения, которые встречаются чаще всего, и то, с чего начинать:

Что видно в мониторе портаЗначениеКуда смотреть
Brownout detector was triggered (сработал детектор просадки питания)Напряжение питания упало ниже порогаИсточник питания, кабель, конденсатор
Guru Meditation Error: Core 1 panic'ed (LoadProhibited)Код попытался прочитать данные по недопустимому адресуНулевой указатель, выход за границы массива
Guru Meditation Error: Core 1 panic'ed (StoreProhibited)Код попытался записать данные по недопустимому адресуНулевой указатель, переполнение буфера
Stack canary watchpoint triggered (loopTask): стек переполненСтек задачи заполнилсяБольшие локальные массивы, рекурсия
task_wdt: Task watchdog got triggered (сработал сторожевой таймер задач)Задача не отдавала процессор дольше 5 секундЦиклы без пауз, зависшие задачи
Interrupt wdt timeout on CPU0 / CPU1Обработчик прерывания или участок с отключёнными прерываниями выполнялся дольше 300 мсДолгий ISR, Serial внутри ISR
boot:0x3 (DOWNLOAD_BOOT...) waiting for download (ожидание загрузки кода)Плата стартовала в режиме программированияGPIO0 в LOW при старте
invalid header или flash read errНе удалось прочитать флеш-памятьGPIO12 в HIGH при старте, прерванная загрузка кода

Что такое «Brownout detector was triggered» и почему это происходит?

Brownout — это кратковременная просадка напряжения без полного пропадания питания. В ESP32 есть детектор просадки питания (brownout detector), который постоянно следит за линией питания, и в ядре Arduino его порог выставлен на самый низкий уровень, около 2,43 V. Как только напряжение падает ниже этого значения, микросхема защищает радиомодуль и флеш-память, печатает в монитор порта «Brownout detector was triggered» и перезапускается. Причина простая: при низком напряжении флеш-память может читаться с ошибками, сохранённые данные могут испортиться, а процессор может выполнять случайные инструкции. Детектор заставляет плату чисто перезагрузиться, вместо того чтобы вести себя непредсказуемо. Модуль ESP32-WROOM-32 рассчитан на работу от 3,0 до 3,6 V, так что когда напряжение на линии доходит до 2,43 V, оно уже давно за пределами безопасной зоны.

После сообщения о просадке в строке загрузки, в зависимости от версии ядра Arduino, будет rst:0xc (SW_CPU_RESET) или rst:0x3 (SW_RESET), потому что прерывание детектора печатает сообщение, а затем перезапускает микросхему программно. Пусть это не сбивает с толку: тот же код записывает в память микросхемы, что причиной была просадка питания, и esp_reset_reason() возвращает ESP_RST_BROWNOUT. Если же срабатывает собственный аппаратный сброс по просадке, в строке будет rst:0xf (RTCWDT_BROWN_OUT_RESET). В ядрах 3.x на основе ESP-IDF 5 эта строка стала информационным сообщением журнала (I BOD: Brownout detector was triggered). Поскольку предварительно скомпилированные библиотеки Arduino выводят только сообщения уровня ошибки, это сообщение обычно вообще не появляется, и плата перезапускается молча; если при старте виден необъяснимый rst:0x3 или rst:0xc, посмотри причину сброса через диагностический скетч ниже. Чтобы понять, почему проседает напряжение, посмотри, какой ток потребляет ESP32:

Режим работыТипичный ток (микросхема ESP32)
Глубокий сон (таймер RTC включён)Около 10 µA
Лёгкий сон (light sleep)Около 0,8 mA
Wi-Fi выключен, процессор на 240 MHz30–68 mA
Приём Wi-Fi (RX)95–100 mA
Передача Wi-Fi, 802.11b, 19,5 dBmОколо 240 mA
Калибровка радио при старте и момент подключенияВ пиках длительностью в микросекунды может превышать 400 mA

Разрыв в этой таблице и есть суть проблемы: пока код не включил Wi-Fi, плата спокойно работает примерно на 50 mA, но на строке WiFi.begin() ток за несколько микросекунд вырастает в пять-шесть раз. Если источник питания, кабель и стабилизатор не успевают за таким резким скачком, напряжение проваливается. Поэтому Espressif рекомендует для ESP32 источник питания, способный отдавать не меньше 500 mA. Симптом очень знакомый: плата загружается, печатает в монитор порта несколько строк, сбрасывается в момент запуска Wi-Fi или Bluetooth, и всё превращается в бесконечный цикл. То же самое может случиться, когда двигается сервопривод, щёлкает реле или светодиодная лента загорается белым.

Самые частые причины просадки питания

  • Тонкий или длинный USB-кабель: тонкие силовые жилы в дешёвых кабелях могут давать в сумме до 0,5 Ω туда и обратно. По закону Ома пик в 400 mA теряет на таком кабеле 0,2 V.
  • Слабый USB-порт или хаб: порты на клавиатурах и мониторах, хабы без внешнего питания и передние порты некоторых ноутбуков дают напряжению просесть при пиковом токе.
  • Падение напряжения на стабилизаторе (dropout): AMS1117, который стоит на большинстве плат, требует разницы 1,0–1,3 V между входом и выходом. Если напряжение на пине 5V падает примерно ниже 4,3 V, выход уже не может удержать 3,3 V.
  • Батарея напрямую: если подключить Li-ion аккумулятор на 3,7 V к пину 5V, из-за падения напряжения на стабилизаторе выход опустится ниже 3 V; две батарейки AA под нагрузкой быстро проседают ниже минимума ESP32.
  • Пин 3.3V на Arduino Uno: с этого пина рекомендуется брать всего около 50 mA; для ESP32 с включённым Wi-Fi этого совсем не достаточно.
  • Нагрузки на той же линии: если сервоприводы, реле, зуммеры, DC-моторы или лента WS2812B питаются от тех же 5 V, что и ESP32, каждое их движение тянет напряжение вниз.
  • Макетные платы и перемычки: разболтанные контакты и тонкие перемычки добавляют немного сопротивления в каждом соединении; если вести питание через макетную плату, становится только хуже.
  • ESP32-CAM: когда одновременно работают камера, Wi-Fi и вспышка, ток становится очень большим; этой плате нужны 5 V и не меньше 2 A. Подробности — в руководстве по ESP32-CAM.
Если мультиметр показывает 3,3 V, это ещё не значит, что питание надёжное. Мультиметр делает всего несколько измерений в секунду и показывает что-то близкое к среднему значению, а провалы, из-за которых срабатывает детектор, длятся от микросекунд до нескольких миллисекунд и на дисплее не появляются вовсе. Увидеть их может только осциллограф. Если его нет, применяй решения ниже по порядку и после каждого шага проверяй, пропала ли ошибка. Основы измерений — в статье Как пользоваться мультиметром.

Решение 1: улучши питание и кабель

Первый и самый действенный шаг — более мощное питание. Подключи плату коротким толстым кабелем с передачей данных, в котором нет сомнений, напрямую к USB-порту на задней панели компьютера или к адаптеру 5 V, рассчитанному минимум на 1 A. Порты USB 2.0 рассчитаны на 500 mA, а USB 3.0 — на 900 mA; рекомендованные Espressif 500 mA — это ровно предел порта USB 2.0, поэтому, как только добавляются потери в кабеле, начинаются проблемы. Если одна только замена кабеля убирает ошибку, виновато, скорее всего, было его сопротивление. Сколько напряжения теряет кабель, можно посчитать по формуле V = I × R в калькуляторе закона Ома.

Стабилизатор напряжения AMS1117-3.3 (3,3 V)
AMS1117-3.3: стабилизатор, который стоит на большинстве плат ESP32. Ему нужна разница 1,0–1,3 V между входом и выходом; если напряжение на пине 5V падает примерно ниже 4,3 V, на выходе уже не держится 3,3 V.
Схема питанияРиск просадкиПримечание
Короткий толстый кабель и USB-порт на задней панелиНизкийСамое простое решение на время разработки
Длинный тонкий кабель, передний порт или хабВысокийСопротивление кабеля тянет напряжение вниз при пиковом токе
Адаптер 5 V минимум на 1 A, на пин 5V или VINНизкийРекомендуется для постоянных проектов
Готовый источник 3,3 V прямо на пин 3V3Зависит от источникаДолжен отдавать не меньше 500 mA и никогда не превышать 3,6 V
Li-ion аккумулятор на 3,7 V через AMS1117ВысокийКогда на аккумуляторе меньше примерно 4 V, выход падает ниже 3 V
Li-ion аккумулятор и LDO-стабилизатор на 3,3 V с малым падением напряжения или понижающе-повышающий преобразовательНизкийДаёт стабильные 3,3 V, даже когда аккумулятор разряжается
Две батарейки AA прямо на пин 3V3Очень высокийПод нагрузкой напряжение быстро падает ниже минимума ESP32

Проекты на батарейках требуют особого внимания. Li-ion аккумулятор вроде 18650 полностью заряженным даёт 4,2 V, а разряженным — около 3,0 V. Такой диапазон не подходит ни для прямого подключения к пину 3V3 (4,2 V больше предела 3,6 V), ни для пина 5V с его AMS1117. Правильное решение — либо подавать питание от аккумулятора через повышающий модуль на 5 V на пин 5V, либо питать линию 3V3 напрямую через LDO-стабилизатор с малым падением напряжения, рассчитанный минимум на 500 mA (например, ME6211 или HT7833), плюс конденсатор. Сколько проект проработает от батареи, можно оценить в калькуляторе времени работы от батареи, а про схему зарядки рассказано в руководстве по модулю зарядки TP4056.

Решение 2: добавь конденсатор между 3V3 и GND

Конденсатор работает как маленький резервуар: во время пика тока он сам отдаёт энергию, пока стабилизатор и кабель не догонят. Поставь электролитический конденсатор на 100–470 µF между пинами 3V3 и GND платы, как можно ближе к ней, а рядом — керамический конденсатор на 100 nF. Электролитический справляется с провалами длительностью в миллисекунды, керамический — с очень короткими высокочастотными пульсациями. Эффект можно грубо оценить по формуле ΔV = I × Δt / C: даже если конденсатор на 470 µF в одиночку отдаёт дополнительные 200 mA в течение 200 µs, напряжение падает всего на 0,085 V. Маркировка 104 на керамическом конденсаторе означает 100 nF; другие коды можно расшифровать в утилите кодов конденсаторов.

Виды конденсаторов
У электролитического конденсатора длинная ножка — плюс (+), она идёт к 3V3; ножка со стороны полосы на корпусе идёт к GND. Для линии 3,3 V достаточно конденсатора на 6,3 V или 10 V.
Никогда не ставь электролитический конденсатор наоборот: он нагревается, вздувается и может взорваться. Если конденсатор подключается к линии 5V, выбирай рассчитанный минимум на 10 V. Очень большие конденсаторы (от 1000 µF) при включении потребляют большой зарядный ток, который может на мгновение перегрузить USB-порт и замедлить нарастание напряжения на линии 3,3 V; для большинства проектов на линии 3V3 хватает 470 µF. Как работают конденсаторы, рассказано в статье Что такое конденсатор?

Решение 3: питай сервоприводы, моторы и светодиодные ленты отдельно

Даже если сама ESP32 питается правильно, нагрузки на той же линии могут тянуть напряжение вниз. Даже маленький сервопривод вроде SG90 в момент начала движения потребляет несколько сотен mA; светодиод WS2812B на полном белом берёт около 60 mA, так что лента на 30 светодиодов может дойти до 1,8 A. Питай такие нагрузки не от пина 5V или 3V3 ESP32, а от отдельного источника 5 V; например, адаптер на 12 V можно понизить до 5 V понижающим модулем LM2596. Самое важное правило — соединить GND обоих источников; без общей земли у сигнальной линии нет опорного уровня, и сервопривод или лента ведут себя случайным образом. Подробно об этом — в статьях Arduino постоянно перезагружается? и GND и общая земля.

Понижающий модуль LM2596 (step-down, buck)
Понижающий модуль LM2596: эффективно понижает 12 V до 5 V и идеально подходит, чтобы питать нагрузки вроде сервоприводов, реле или светодиодных лент отдельно от ESP32. Выходное напряжение задаётся подстроечным резистором; перед подключением выставь его мультиметром на 5 V.
Сгладить пики тока можно и программно. Запускай Wi-Fi до того, как двигать сервоприводы или зажигать ленту; ограничь яркость ленты через setBrightness(); включай сервоприводы и реле с интервалом в несколько сотен миллисекунд, а не все сразу. Снижение мощности передатчика Wi-Fi через WiFi.setTxPower() тоже уменьшает пиковый ток; подробности — в статье ESP32 не подключается к Wi-Fi.

Отключить детектор просадки — это решение?

Самое популярное «решение» в интернете — отключить детектор одной строкой. Код ниже отключает детектор, обнуляя регистр brownout в ESP32, и сообщение об ошибке пропадает. Но провалы напряжения никуда не деваются; просто никто больше о них не предупреждает. Используй этот код только для диагностики: если с отключённым детектором плата работает без проблем, это подтверждает, что дело точно в питании. После этого удали строку и исправь питание.

C++ (Arduino IDE)
1#include "soc/soc.h"
2#include "soc/rtc_cntl_reg.h"
3
4void setup() {
5  // YALNIZ TEŞHİS İÇİN: brownout dedektörünü kapat / DIAGNOSIS ONLY: disable the brownout detector
6  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
7
8  Serial.begin(115200);
9  Serial.println("Brownout dedektoru kapali / brownout detector disabled");
10}
11
12void loop() {
13}
Не оставляй детектор отключённым навсегда. Строка WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0) не просто глушит предупреждение: она отключает и аппаратный сброс детектора, и его задачу выключать флеш-память и радио при низком напряжении, так что микросхема продолжает работать без защиты далеко ниже своего минимума 3,0 V. Когда напряжение опускается ниже 2,4 V, данные, записанные во флеш-память (Preferences, EEPROM, LittleFS), а во время OTA-обновления даже сама программа могут повредиться, Wi-Fi может отваливаться без видимой причины, а плата — падать уже с ошибками Guru Meditation или полностью зависать. Настройка не постоянная: микросхема заново включает детектор при каждой загрузке, поэтому строка должна стоять первой в setup(), и после её удаления защита возвращается. Этот регистр есть в классической ESP32, а также в S2, S3 и C3; в новых моделях вроде ESP32-C6 его нет, и код не скомпилируется. Настоящее решение всегда одно — исправить питание.

Коды причины rst: ESP32 сообщает, почему перезапустилась

Значение rst:, которое ПЗУ печатает при каждой загрузке, показывает, чем закончился предыдущий запуск. При нормальной загрузке значение boot равно 0x13 (SPI_FAST_FLASH_BOOT): плата запускает программу из флеш-памяти. В таблице ниже — коды, которые встречаются чаще всего. Одних кодов ПЗУ не всегда достаточно: и ESP.restart(), и перезапуск после Guru Meditation выглядят как 0xc, а просадка питания в зависимости от версии ядра появляется как 0xc или 0x3. Чтобы их различить, используй скетч с esp_reset_reason() из следующего раздела.

Код rstНазваниеЧто означает
0x1POWERON_RESETПитание подано впервые или нажата кнопка EN
0x3SW_RESETПрограмма сбросила всю цифровую систему; в новых версиях ядра появляется и после просадки питания
0x5DEEPSLEEP_RESETПлата проснулась после глубокого сна (это нормально)
0x7 / 0x8TG0WDT_SYS_RESET / TG1WDT_SYS_RESETСторожевой таймер группы таймеров аппаратно сбросил систему; прерывания слишком долго были отключены или не смог отработать даже обработчик паники
0x9RTCWDT_SYS_RESETСторожевой таймер RTC сбросил цифровое ядро
0xcSW_CPU_RESETПрограммный перезапуск: ESP.restart(), после Guru Meditation или после просадки питания в ядре Arduino
0xfRTCWDT_BROWN_OUT_RESETАппаратный сброс по просадке питания: нестабильное напряжение питания
0x10RTCWDT_RTC_RESETСторожевой таймер RTC сбросил всю микросхему вместе с RTC; чаще всего это бывает на платах, которые зависают во время загрузки

Диагностический скетч: вывести причину сброса, память и стек

Загрузи код ниже отдельным скетчем или добавь его строки в setup() и loop() проблемного проекта. При каждом запуске плата выводит в монитор порта причину сброса понятным текстом, номер загрузки по счёту и состояние памяти. Счётчик, помеченный RTC_DATA_ATTR, хранится в памяти RTC, поэтому переживает программные сбросы и глубокий сон и обнуляется только при полном отключении питания. Если счётчик растёт каждые несколько секунд, сразу ясно: плата в цикле перезагрузок.

C++ (Arduino IDE)
1#include <esp_system.h>
2
3RTC_DATA_ATTR uint32_t bootCount = 0;   // yazılım resetinde korunur / survives software resets
4
5// Reset nedenini okunabilir yazıya çevir / turn the reset reason into readable text
6const char* reasonText(esp_reset_reason_t r) {
7  switch (r) {
8    case ESP_RST_POWERON:   return "POWERON: guc acilisi ya da EN / power-on or EN button";
9    case ESP_RST_SW:        return "SW: ESP.restart()";
10    case ESP_RST_PANIC:     return "PANIC: Guru Meditation";
11    case ESP_RST_INT_WDT:   return "INT_WDT: kesme watchdog'u / interrupt watchdog";
12    case ESP_RST_TASK_WDT:  return "TASK_WDT: gorev watchdog'u / task watchdog";
13    case ESP_RST_WDT:       return "WDT: diger watchdog / other watchdog";
14    case ESP_RST_DEEPSLEEP: return "DEEPSLEEP: derin uykudan uyanma / deep sleep wake-up";
15    case ESP_RST_BROWNOUT:  return "BROWNOUT: gerilim dusmesi / supply voltage dropped";
16    default:                return "UNKNOWN: bilinmiyor / unknown";
17  }
18}
19
20void setup() {
21  Serial.begin(115200);
22  delay(1000);                          // Seri Monitör'ün açılmasını bekle / wait for the Serial Monitor
23  bootCount++;
24  Serial.printf("Acilis sayisi / boot count: %u\n", (unsigned)bootCount);
25  Serial.printf("Reset nedeni / reset reason: %s\n", reasonText(esp_reset_reason()));
26}
27
28void loop() {
29  // Bellek durumu / memory status
30  Serial.printf("Bos heap / free heap: %u B, en dusuk / minimum: %u B, en buyuk blok / largest block: %u B\n",
31                (unsigned)ESP.getFreeHeap(), (unsigned)ESP.getMinFreeHeap(), (unsigned)ESP.getMaxAllocHeap());
32  // loopTask yığınında hiç kullanılmamış en küçük alan / smallest unused space on the loopTask stack
33  Serial.printf("loopTask bos yigin / free stack: %u B\n", (unsigned)uxTaskGetStackHighWaterMark(NULL));
34  delay(5000);
35}

Значения, которые печатаются в цикле, выдают и скрытые проблемы. Если свободная куча (free heap) немного уменьшается при каждом проходе, в программе утечка памяти, и плата рано или поздно упадёт. Если самый большой блок намного меньше свободной кучи, память фрагментирована; обычно виноваты объекты String, которые постоянно растут и уменьшаются внутри цикла. Если свободный стек loopTask опускается ниже нескольких сотен байт, переполнение стека совсем близко. Если причина сброса — BROWNOUT, сосредоточься на разделах про питание; если PANIC, TASK_WDT или INT_WDT — на программных разделах ниже.

Что такое Guru Meditation Error? Читаем отчёт о сбое строка за строкой

Guru Meditation Error — это отчёт о сбое, который печатает обработчик паники, когда процессор ESP32 сталкивается с ошибкой, после которой не может продолжить работу; название пришло с экрана ошибок старых компьютеров Amiga. Сначала отчёт выглядит пугающе, но важны в нём всего несколько строк. Core 1 показывает, на каком ядре произошла ошибка: в Arduino setup() и loop() работают на ядре 1, а стек Wi-Fi — на ядре 0. Имя в скобках — тип исключения. Когда отчёт заканчивается, плата печатает «Rebooting...» и перезапускается; в строке загрузки будет rst:0xc.

СтрокаЗначение
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled. (исключение не обработано)Код на ядре 1 попытался прочитать данные по недопустимому адресу, и ошибка не была обработана
PC : 0x400d1a2cАдрес инструкции, на которой произошёл сбой; если он начинается с 0x400d, из флеш-памяти выполнялся твой код или библиотека
EXCCAUSE: 0x0000001cКод исключения; 0x1c, то есть 28, означает LoadProhibited
EXCVADDR: 0x00000000Адрес, к которому шло обращение; 0 или очень маленькое число указывает на нулевой указатель
Backtrace: 0x400d1a29:0x3ffb2270 0x400d0f6e:0x3ffb2290 ...Цепочка вызовов; каждая пара имеет вид PC:SP, и декодер превращает её в строку кода
ELF file SHA256: ...Отпечаток программы, в которой произошёл сбой; декодеру нужна точно та же сборка
Rebooting...Обработчик паники программно перезапускает микросхему

LoadProhibited, StoreProhibited и другие типы исключений

ИсключениеEXCCAUSEЧто произошло?Частая причина
LoadProhibited28 (0x1c)Чтение по недопустимому адресуНулевой или неинициализированный указатель, удалённый объект
StoreProhibited29 (0x1d)Запись по недопустимому адресуЗапись через нулевой указатель, выход буфера за границы массива
InstrFetchProhibited20 (0x14)Выборка инструкции по недопустимому адресуНеназначенный указатель на функцию или обратный вызов (callback)
IllegalInstruction0 (0x0)Недопустимая инструкцияПовреждённый стек, пропущенный return в функции, которая должна возвращать значение
LoadStoreAlignment9 (0x9)Невыровненный доступ к памятиПриведение массива байтов к типу вроде uint32_t*
IntegerDivideByZero6 (0x6)Целочисленное деление на нольДеление на счётчик, который может быть равен нулю
Unhandled debug exception-Отладочное исключениеStack canary watchpoint triggered: переполнение стека
Interrupt wdt timeout on CPU0/CPU1-Истёк сторожевой таймер прерыванийДолгий ISR, Serial или delay внутри ISR
Cache disabled but cached memory region accessed (обращение к кэшируемой памяти при отключённом кэше)-Обращение к коду во флеш-памяти, пока флеш занятаОбработчик прерывания без IRAM_ATTR

Самая частая из этих ошибок — использование нулевого указателя (null pointer). NULL — это адрес 0, а в ESP32 вокруг адреса 0 нет читаемой памяти, поэтому чтение оттуда, куда указывает пустой указатель, даёт LoadProhibited, а запись туда — StoreProhibited. EXCVADDR в этом случае — отличная подсказка: 0x00000000 означает обращение прямо по нулевому указателю, а маленькие значения вроде 0x00000004 или 0x00000010 — к полю внутри объекта по пустому указателю (NULL плюс смещение поля). В проектах Arduino чаще всего функция возвращает NULL в значении «не найдено», а это значение используется без проверки. В примере ниже strstr() возвращает NULL, если не находит текст; без проверки плата падает с EXCVADDR 0x00000005.

C++ (Arduino IDE)
1const char* response = "temp=23.5;hum=40";   // sunucudan gelen yanıt / reply from a server
2
3void setup() {
4  Serial.begin(115200);
5  delay(500);
6
7  // YANLIŞ: "pres=" yoksa strstr NULL döner ve kart çöker (EXCVADDR 0x00000005)
8  // WRONG: if "pres=" is missing strstr returns NULL and the board crashes (EXCVADDR 0x00000005)
9  // float pressure = atof(strstr(response, "pres=") + 5);
10
11  // DOĞRU: işaretçiyi kullanmadan önce NULL kontrolü yap / RIGHT: check for NULL before using the pointer
12  const char* found = strstr(response, "pres=");
13  if (found != nullptr) {
14    float pressure = atof(found + 5);
15    Serial.println(pressure);
16  } else {
17    Serial.println("pres alani bulunamadi / pres field not found");
18  }
19}
20
21void loop() {
22}
Выработай несколько привычек против нулевых указателей: инициализируй указатели значением nullptr при объявлении, проверяй, что возвращают функции begin() библиотек, присваивай указателю nullptr после delete объекта, а при записи в массив проверяй, что индекс меньше размера массива. Если в Arduino IDE в «Файл › Настройки» (File › Preferences) выставить «Предупреждения компилятора» (Compiler warnings) на Все, пропущенный return и похожие ошибки будут видны уже при компиляции.

Переполнение стека: ошибка «Stack canary watchpoint triggered»

В ESP32 у каждой задачи (task) есть стек фиксированного размера, где хранятся локальные переменные, вызовы функций и адреса возврата. В Arduino у задачи loopTask, которая выполняет setup() и loop(), стек размером 8192 байта. Локальный массив на 10 000 байт внутри loop(), функция, которая вызывает сама себя (рекурсия), или библиотечная функция с большим числом локальных переменных выйдут за этот предел. В конце стека есть защитная область (canary); когда стек до неё доходит, плата печатает Guru Meditation Error: Core 1 panic'ed (Unhandled debug exception) и сразу под этим Debug exception reason: Stack canary watchpoint triggered (loopTask). Имя в скобках — задача, у которой переполнился стек. Иногда та же проблема выглядит как сообщение ***ERROR*** A stack overflow in task loopTask has been detected (обнаружено переполнение стека в задаче loopTask). Вот как это исправить:

  • Убери большие массивы из стека: объяви массив глобально или добавь перед ним static; тогда он будет жить в постоянной RAM, а не в стеке.
  • Увеличь стек loopTask: добавь в самый верх скетча SET_LOOP_TASK_STACK_SIZE(16 * 1024);, чтобы увеличить стек loop() до 16 КБ.
  • Дай своим задачам достаточно места: в ESP32 размер стека, который передаётся в xTaskCreate и xTaskCreatePinnedToCore, указывается в байтах, а не в словах, как во FreeRTOS на других платформах. 1024 байта мало даже для задачи, которая использует Serial.printf; начни с 4096.
  • Измеряй: uxTaskGetStackHighWaterMark(NULL) возвращает в байтах наименьший объём стека, который задача до сих пор ни разу не использовала; если он опускается ниже нескольких сотен байт, увеличь стек.
  • Избегай рекурсии: преврати функции, которые вызывают сами себя, в циклы или ограничь глубину вызовов.

Сторожевой таймер задач: ошибка «task_wdt: Task watchdog got triggered»

Сторожевой таймер (watchdog) — это таймер, который замечает зависшую программу и спасает плату. Сторожевой таймер задач (Task WDT) в ESP32 ожидает, что зарегистрированные в нём задачи будут через равные промежутки подавать сигнал «я жив». В ядре Arduino по умолчанию он следит за задачей простоя (IDLE) ядра 0, а его таймаут — 5 секунд. Если задача, работающая на ядре 0, 5 секунд не отдаёт процессор, задача IDLE не может выполниться, и в мониторе порта появляется сообщение, начинающееся с E (10257) task_wdt: Task watchdog got triggered. За ним идут список задач, которые не сбросили сторожевой таймер вовремя (обычно - IDLE (CPU 0)), и под заголовком Tasks currently running: задачи, которые выполнялись в этот момент. В ядре Arduino это вызывает панику со строкой task_wdt: Aborting., и плата перезапускается.

Самые частые причины: цикл, закреплённый за ядром 0 через xTaskCreatePinnedToCore, в котором ни разу не вызывается vTaskDelay(); цикл while, который ждёт ответа датчика без таймаута; или delay() и долгие операции внутри обратных вызовов таких библиотек, как AsyncWebServer. В последнем случае в списке вместо IDLE будет задача async_tcp. Решение — разбивать долгие операции на маленькие части и добавлять в циклы короткие паузы вроде vTaskDelay(1). Сама функция loop() по умолчанию не отслеживается; при желании вызови в setup() enableLoopWDT(), и плата будет перезапускаться также, если loop() зависнет дольше чем на 5 секунд. Отключение сторожевого таймера через disableCore0WDT() прячет ошибку, но не исправляет зависший код. Подробнее о задачах и ядрах — в статье FreeRTOS и два ядра ESP32. Код ниже показывает и правильную структуру задачи, и меры против переполнения стека:

C++ (Arduino IDE)
1SET_LOOP_TASK_STACK_SIZE(16 * 1024);   // loop() yığını 8 KB yerine 16 KB / loop() stack 16 KB instead of 8 KB
2
3volatile uint32_t sampleCount = 0;
4
5// 0. çekirdekte çalışan görev / task running on core 0
6void sensorTask(void* parameter) {
7  for (;;) {
8    sampleCount = sampleCount + 1;       // sensörünü burada oku / read your sensor here
9    vTaskDelay(pdMS_TO_TICKS(10));       // IDLE görevine sıra ver, task_wdt tetiklenmez / let IDLE run, no task_wdt
10  }
11}
12
13void setup() {
14  Serial.begin(115200);
15  // Yığın boyutu bayt cinsindendir: 4096 bayt / stack size is in bytes: 4096 bytes
16  xTaskCreatePinnedToCore(sensorTask, "sensorTask", 4096, NULL, 1, NULL, 0);
17}
18
19void loop() {
20  static uint8_t buffer[10000];          // static: yığında değil kalıcı RAM'de / static: in RAM, not on the stack
21  memset(buffer, 0, sizeof(buffer));
22
23  Serial.printf("Ornek / samples: %u, loopTask bos yigin / free stack: %u B\n",
24                (unsigned)sampleCount, (unsigned)uxTaskGetStackHighWaterMark(NULL));
25  delay(1000);
26}

Interrupt wdt timeout и IRAM_ATTR: сбои в прерываниях

Сторожевой таймер прерываний (Interrupt WDT) куда менее терпелив: если обработчик прерывания (ISR) или участок кода с отключёнными прерываниями выполняется дольше 300 мс, плата падает с Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1). Чаще всего причина в Serial.print, delay(), I2C или Wi-Fi внутри ISR. Ещё одна ловушка — флеш-память: пока Preferences, EEPROM или LittleFS пишут во флеш, кэш флеш-памяти на мгновение отключается. Если в этот момент приходит прерывание, а его обработчик лежит во флеш-памяти, плата сообщает Cache disabled but cached memory region accessed. Решение — пометить ISR атрибутом IRAM_ATTR, чтобы они жили в RAM, и внутри только поднимать флаг; настоящую работу выполняет loop(). Переменные, общие для ISR и loop(), должны быть volatile.

C++ (Arduino IDE)
1const int BUTTON_PIN = 27;              // buton GPIO 27 ile GND arasında / button between GPIO 27 and GND
2volatile bool buttonPressed = false;    // ISR ile loop arasında paylaşılan bayrak / flag shared by the ISR and loop
3uint32_t pressCount = 0;
4
5void IRAM_ATTR onButton() {             // IRAM_ATTR: fonksiyon flaşta değil RAM'de durur / the function lives in RAM, not flash
6  buttonPressed = true;                 // yalnızca bayrak kaldır, Serial yok / only set a flag, no Serial
7}
8
9void setup() {
10  Serial.begin(115200);
11  pinMode(BUTTON_PIN, INPUT_PULLUP);
12  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButton, FALLING);
13}
14
15void loop() {
16  if (buttonPressed) {
17    buttonPressed = false;
18    pressCount++;
19    Serial.printf("Butona basildi / button pressed: %u\n", (unsigned)pressCount);   // yavaş iş burada / slow work here
20  }
21}

Превращаем Backtrace в строку кода с ESP Exception Decoder

Адреса в строке Backtrace сами по себе ничего не говорят; инструмент, который превращает их в файл, функцию и номер строки исходного кода, называется ESP Exception Decoder. В старой Arduino IDE 1.8 это был плагин в меню «Инструменты». В Arduino IDE 2 используется одноимённое расширение, созданное сообществом:

  1. Скачай файл .vsix из раздела Releases проекта dankeboy36/esp-exception-decoder на GitHub. Для Arduino IDE 2 выбирай релиз 1.x; новые релизы 2.x сделаны для VS Code.
  2. Закрой Arduino IDE и скопируй файл в папку %UserProfile%\.arduinoIDE\plugins на Windows или ~/.arduinoIDE/plugins на macOS и Linux; если папки нет, создай её.
  3. Запусти IDE, открой скетч, который падает, скомпилируй его кнопкой Проверить (Verify) и загрузи в плату.
  4. Открой монитор порта и дождись, пока сбой повторится.
  5. Открой палитру команд сочетанием Ctrl+Shift+P (Cmd+Shift+P на macOS) и выполни ESP Exception Decoder: Show Decoder Terminal.
  6. Скопируй из монитора порта блок Guru Meditation вместе со строкой Backtrace и вставь его в открывшийся терминал; инструмент покажет для каждого адреса имя файла, функцию и номер строки.

Если расширение установить не получается, то же самое можно сделать вручную. Меню Sketch › Export Compiled Binary («Скетч › Экспорт скомпилированного бинарного файла») кладёт файл с расширением .elf в папку build внутри папки скетча. Команда xtensa-esp32-elf-addr2line -pfiaC -e sketch.ino.elf 0x400d1a29 0x400d0f6e из набора инструментов ESP32 превращает эти адреса в имена функций и номера строк. Если ты работаешь в PlatformIO, достаточно добавить строку monitor_filters = esp32_exception_decoder в platformio.ini; тогда вывод сбоя расшифровывается в мониторе порта автоматически.

Декодер даёт правильный результат только с файлом .elf той же самой сборки, в которой произошёл сбой. Если изменить хотя бы одну строку и перекомпилировать, адреса сдвинутся, и вывод будет указывать на не те строки. Если строка Backtrace заканчивается словом CORRUPTED, стек повреждён; обычно это признак переполнения стека или буфера, вышедшего за свои границы.

Пины конфигурации при старте: как GPIO0, GPIO2, GPIO12 и GPIO15 могут сорвать загрузку

При запуске ESP32 смотрит на уровень некоторых пинов, чтобы решить, как загружаться. Это пины конфигурации при старте (strapping pins), и важен только их уровень в момент сброса; после загрузки их можно использовать как обычные GPIO. Проблема возникает, когда подключённый к одному из них датчик, кнопка или модуль тянет пин к неправильному уровню во время старта. Тогда плата вообще не запускает программу, входит в режим программирования или не может прочитать флеш-память; со стороны это тоже выглядит как «ESP32 не загружается» или «постоянно перезагружается». Полную карту пинов своей платы можно посмотреть в утилите распиновки.

ПинВстроенный резисторРоль при стартеЧто будет при неправильном уровне?
GPIO0Подтяжка (pull-up)LOW — режим программирования (download), HIGH — обычная загрузкаboot:0x3 (DOWNLOAD_BOOT) и waiting for download; программа не запускается
GPIO2Стягивающий (pull-down)Для входа в режим программирования должен быть в LOW или не подключён; для обычной загрузки не важенСхема, которая тянет его к HIGH, мешает загрузке кода
GPIO12 (MTDI)Стягивающий (pull-down)Выбирает напряжение питания флеш-памяти: LOW — 3,3 V, HIGH — 1,8 VПри HIGH флеш-память на 3,3 V не получает нормального питания; плата не загружается или уходит в цикл перезагрузок
GPIO15 (MTDO)Подтяжка (pull-up)LOW отключает сообщения загрузки из ПЗУСтроки загрузки не появляются в мониторе порта, диагностика усложняется
GPIO5Подтяжка (pull-up)Задаёт тайминги SDIOНа отладочных платах проблемы бывают редко
Подтягивающий резистор, реле или модуль датчика на GPIO12, который при старте тянет пин к HIGH, — самая скрытая причина того, что ESP32 вообще не запускается. Если этот пин всё же нужен, проверь, что модуль оставляет его в LOW в момент загрузки. Другой вариант — команда espefuse.py set_flash_voltage 3.3V, которая идёт вместе с esptool: она навсегда записывает напряжение флеш-памяти в eFuse и убирает влияние GPIO12 при старте; однако это действие необратимо, и выполнять его можно только на модулях с флеш-памятью на 3,3 V (например, ESP32-WROOM-32).

У новых плат пины конфигурации при старте другие: у ESP32-S3 это GPIO0, GPIO3, GPIO45 и GPIO46, а у ESP32-C3 на загрузку влияют GPIO2, GPIO8 и GPIO9. ESP32-S3 использует те же имена исключений Xtensa, что и классическая ESP32. А вот на ESP32-C3 на базе RISC-V вместо LoadProhibited будет Load access fault, вместо StoreProhibited — Store access fault, а роль EXCVADDR берёт на себя регистр MTVAL. Сообщение о просадке питания одинаково на всех моделях.

ESP32 и Raspberry Pi Pico в MicroPython

Если ESP32 программируется на MicroPython, просадки питания происходят точно так же, потому что проблема в железе, а не в программе. А вот ошибки в коде Python выглядят не как Guru Meditation, а как понятный Traceback, и плата падает редко. Причину сброса можно прочитать через machine.reset_cause(), но учти: порт MicroPython для ESP32 не возвращает просадку питания отдельным значением, а сообщает о ней как о PWRON_RESET, так же как о включении питания. Если плата, которую никто не трогает, постоянно стартует с PWRON_RESET, снова подозревай питание. Код ниже — для ESP32; на Raspberry Pi Pico модуль machine знает меньше типов сброса. У Pico понижающе-повышающий стабилизатор на входе VSYS работает с любым напряжением от 1,8 до 5,5 V, поэтому проблемы с просадкой там встречаются гораздо реже; чтобы познакомиться с платой, загляни в статью Что такое Raspberry Pi Pico?

MicroPython
1import machine, gc, time
2
3# Reset nedenleri (ESP32 için) / reset causes (for ESP32)
4causes = {
5    machine.PWRON_RESET: "PWRON: guc acilisi ya da brownout / power-on or brownout",
6    machine.HARD_RESET: "HARD: cokme ya da machine.reset() / crash or machine.reset()",
7    machine.WDT_RESET: "WDT: watchdog",
8    machine.DEEPSLEEP_RESET: "DEEPSLEEP: derin uykudan uyanma / deep sleep wake-up",
9    machine.SOFT_RESET: "SOFT: Ctrl+D ile yazilim reseti / soft reset with Ctrl+D",
10}
11print("Reset nedeni / reset cause:", causes.get(machine.reset_cause(), "bilinmiyor / unknown"))
12
13while True:
14    gc.collect()                                     # çöp toplayıcıyı çalıştır / run the garbage collector
15    print("Bos RAM / free RAM:", gc.mem_free(), "bayt / bytes")
16    time.sleep(5)

Частые ошибки и их решения

СимптомВероятная причинаРешение
Brownout detector was triggered при запуске Wi-FiКабель, порт или стабилизатор не успевают за пиковым токомКороткий толстый кабель, адаптер на 1 A, 470 µF + 100 nF между 3V3 и GND
Сброс, когда поворачивается сервопривод или щёлкает релеНагрузка сидит на одной линии питания с ESP32Питай нагрузку от отдельного источника 5 V и соедини GND
Сбросы начинаются, когда батарея немного разрядитсяПадение напряжения на AMS1117 (dropout)Используй LDO-стабилизатор или понижающе-повышающий преобразователь
Guru Meditation, EXCVADDR 0x00000000Нулевой указатель (null pointer)Проверяй указатель перед использованием
Stack canary watchpoint triggered (loopTask): стек переполненСтек loop() заполненСделай большие массивы static или используй SET_LOOP_TASK_STACK_SIZE
task_wdt: - IDLE (CPU 0)Цикл без пауз на ядре 0Добавь vTaskDelay в цикл
Interrupt wdt timeout on CPU1ISR выполняется слишком долгоВ ISR только поднимай флаг, используй IRAM_ATTR
Плата молчит, boot:0x3 и waiting for downloadGPIO0 в LOW при стартеУбери схему с GPIO0, нажми EN
Плата вообще не загружается, ошибка чтения флеш-памятиGPIO12 в HIGH при стартеПеренеси подтяжку или модуль с GPIO12 на другой пин
Failed to connect to ESP32 при загрузке кодаGPIO2 в HIGH, или плата не может войти в режим программированияОсвободи GPIO2, удерживай BOOT в момент начала загрузки
При поиске неисправности соблюдай порядок: сначала замени кабель и источник питания, потом добавь конденсатор, затем запитай сервоприводы и светодиоды отдельно. Если сообщение о просадке пропало, а сбросы продолжаются, посмотри причину сброса в диагностическом скетче и переходи к программным разделам. Если выставить «Инструменты › Core Debug Level» (Tools › Core Debug Level) на Verbose, ядро ESP32 будет печатать гораздо более подробные сообщения о Wi-Fi и системных событиях. При проблемах с загрузкой кода загляни ещё в статью Код не загружается в Arduino.

Плата работает стабильно: что дальше?

  • Устройство, бережное к батарее: с глубоким сном ESP32 плата спит между измерениями и потребляет ток на уровне микроампер.
  • Обновления по воздуху: загружай код без USB-кабеля с помощью ESP32 OTA.
  • Панель в реальном времени: смотри данные датчиков в браузере вживую через ESP32 WebSocket.
  • Уведомления: с Telegram-ботом на ESP32 причина сброса будет приходить на телефон при каждом перезапуске платы.
  • Световое шоу: безопасно управляй лентой WS2812B NeoPixel с отдельным питанием.

Большинство проблем с перезагрузкой ESP32 — это либо питание, либо несколько строк кода с ошибкой; если правильно прочитать сообщение, и то и другое исправляется быстро. Чтобы освежить основы, загляни в статью Что такое ESP32? В приложении BlueGrays есть проекты на ESP32 со светодиодами, сервоприводами, лентами NeoPixel, подключением к Wi-Fi, веб-сервером, OTA-обновлениями и глубоким сном, у каждого пошаговая схема и код, так что приёмы питания и диагностики из этой статьи можно сразу опробовать в настоящих проектах.

Частые вопросы

Как исправить ESP32 brownout detector was triggered?

Эта ошибка значит, что напряжение питания ESP32 на мгновение упало ниже примерно 2,43 V. Сначала возьми короткий толстый USB-кабель и источник 5 V минимум на 1 A, затем добавь между 3V3 и GND электролитический конденсатор на 100–470 µF и керамический на 100 nF. Нагрузки вроде сервоприводов, реле и светодиодных лент питай от отдельного источника и соедини GND.

Почему ESP32 постоянно перезагружается?

Самые частые причины — недостаточное питание (просадка, brownout), программные сбои (Guru Meditation), таймауты сторожевого таймера и пины конфигурации при старте, застрявшие на неправильном уровне. Открой монитор порта на скорости 115200 бод и нажми EN; последнее сообщение перед сбросом и код rst при загрузке покажут причину. Функция esp_reset_reason() позволяет прочитать её и прямо из кода.

Что значит Guru Meditation Error LoadProhibited?

Это значит, что код на ESP32 попытался прочитать данные по недопустимому адресу памяти. Если EXCVADDR равен 0 или очень маленькому числу, почти наверняка был использован нулевой указатель. Вставь строку Backtrace в ESP Exception Decoder, чтобы найти файл и строку, где произошла ошибка.

Можно ли отключить детектор просадки питания ESP32?

Да, его отключает строка WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0), но она только глушит предупреждение. Провалы напряжения продолжаются, поэтому данные во флеш-памяти могут повредиться, а плата — падать с другими ошибками. Используй это лишь ненадолго, чтобы убедиться, что проблема в питании.

Как исправить ESP32 Task watchdog got triggered?

Это сообщение значит, что отслеживаемая задача 5 секунд не отдавала процессор; в Arduino в списке обычно указана задача IDLE ядра 0. Добавь vTaskDelay() в циклы своих задач, используй таймауты при ожидании датчиков и избегай delay() и долгих операций в обратных вызовах веб-сервера. Отключение сторожевого таймера прячет ошибку, но не устраняет причину.

Какое питание нужно ESP32?

Espressif рекомендует для ESP32 источник питания, который может отдавать не меньше 500 mA; на практике адаптер 5 V на 1 A даёт безопасный запас. Если используется ESP32-CAM или добавлены нагрузки вроде сервоприводов и светодиодных лент, выбирай источник на 2 A и больше и по возможности питай эти нагрузки от отдельной линии.

Все проекты
Мигающий светодиод на ESP32
#103Новичок

Мигающий светодиод на ESP32

Учит базовой работе с выходами GPIO на ESP32. Эффект мигания получается включением и выключением светодиода с цифрового пина. Это ESP32-версия классического проекта мигания на Arduino UNO; базовые команды мигания в среде Arduino те же, но расположение пинов ESP32 и уровни GPIO 3,3 V отличаются от Arduino UNO.

4.7(37)
99
Подключение ESP32 к Wi-Fi в режиме STA (с экраном LCD)
#127Продвинутый

Подключение ESP32 к Wi-Fi в режиме STA (с экраном LCD)

В этом проекте ESP32 надёжно и устойчиво к сбоям подключается к домашней сети Wi-Fi в режиме станции (STA). SSID и пароль хранятся в отдельном файле secrets.h, чтобы не утекли в Git; настроено автоматическое переподключение, а уровень сигнала RSSI и IP-адрес выводятся на I2C LCD-экран. Встроенный светодиод служит индикатором состояния. Это менеджер подключения уровня реальных устройств.

4.4(21)
34
Сервопривод на ESP32 (управление потенциометром)
#118Средний
Premium

Сервопривод на ESP32 (управление потенциометром)

В этом проекте сервопривод SG90 управляется потенциометром. Угол сервопривода задаётся сопоставлением аналогового значения потенциометра с диапазоном 0–180 градусов. Библиотека ESP32Servo формирует сигналы сервопривода на подходящих выходных пинах GPIO у ESP32; этот проект использует GPIO 18. Это базовый способ управления для роборук, подвесов камер и механизмов автоматических дверей.

4.3(29)
42
Управление лентой WS2812B NeoPixel на ESP32
#126Средний
Premium

Управление лентой WS2812B NeoPixel на ESP32

Адресуемые светодиоды WS2812B (NeoPixel) — умные RGB-светодиоды, которыми управляют цепочкой через один пин данных. В этом проекте лента WS2812B из 8 светодиодов управляется библиотекой Adafruit_NeoPixel и показывает плавную радужную анимацию. Логика 3,3V у ESP32 работает с большинством WS2812B, но на случай нестабильности показано решение с последовательным резистором 470Ω и конденсатором 1000µF. Анимация получается сдвигом значения оттенка.

4.9(19)
30
Асинхронный веб-сервер на ESP32 (с выводом IP на LCD)
#129Продвинутый

Асинхронный веб-сервер на ESP32 (с выводом IP на LCD)

В этом проекте ESP32 превращается в асинхронный веб-сервер, которым можно управлять с телефона или компьютера в той же сети Wi-Fi. Благодаря библиотеке ESPAsyncWebServer сервер может обрабатывать несколько запросов одновременно (в отличие от классического WebServer.h, который блокируется на одном запросе). Открыв IP-адрес ESP32 в браузере, ты нажимаешь кнопки ON/OFF (маршруты /on и /off) и включаешь и выключаешь светодиод на GPIO 5.

4.8(16)
26
Глубокий сон ESP32: экономия батареи (OLED + счётчик в RTC RAM)
#135Продвинутый
Premium

Глубокий сон ESP32: экономия батареи (OLED + счётчик в RTC RAM)

Режим глубокого сна (Deep Sleep) у ESP32 снижает потребление до ~10 µA, выключая процессор и большую часть периферии, — это важно для IoT-устройств на батарейках. В этом проекте используются три источника пробуждения: таймер (15 секунд), внешний пин (EXT0 — кнопка GPIO 33) и касание (T0/GPIO 4). Переменные с RTC_DATA_ATTR сохраняются во время глубокого сна — счётчики загрузок, пробуждений по таймеру и по кнопке ведутся непрерывно.

4.3(10)
16
Обновление ESP32 по воздуху (OTA): беспроводная установка прошивки (с экраном LCD)
#132Продвинутый
Premium

Обновление ESP32 по воздуху (OTA): беспроводная установка прошивки (с экраном LCD)

В этом проекте ты узнаешь, как загружать новую прошивку в ESP32 по Wi-Fi без USB-кабеля. С библиотекой ArduinoOTA ESP32 становится видна в сети и автоматически появляется как bluegrays-ota на вкладке «Сетевой порт» в Arduino IDE. Первая загрузка выполняется по USB, а все следующие обновления — без проводов: это важно для устройств в эксплуатации. I2C LCD показывает состояние Wi-Fi, IP-адрес, имя хоста, процент загрузки OTA и сообщения об ошибках.

4.6(14)
22
Сигнализация движения на ESP32 (PIR + зуммер + светодиод + Telegram)
#145Продвинутый
Premium

Сигнализация движения на ESP32 (PIR + зуммер + светодиод + Telegram)

Умная охранная сигнализация, которая при обнаружении движения PIR-датчиком включает зуммер и светодиод и одновременно сразу отправляет владельцу уведомление в Telegram. В ней есть 30-секундная пауза, ограничивающая частоту повторных тревог; этот механизм не проверяет срабатывания PIR и не устраняет ложные тревоги. Подходит для дома, гаража, склада или мастерской. С ESP32-CAM можно добавить уведомления с фото, а с обычной ESP32 DevKit V1 приходят текстовые.

0.0(0)
9