Компоненты · 12 мин чтения

ESP32-CAM: настройка, программирование, видеопоток и SD-карта

Как программировать ESP32-CAM: распиновка AI-Thinker, загрузка через ESP32-CAM-MB и USB-TTL, CameraWebServer, PSRAM, решение ошибок 0x105 и brownout, вспышка, фото на SD.

ESP32-CAM — недорогая плата Wi-Fi-камеры размером всего 27 × 40 мм, на которой есть процессор ESP32, 2-мегапиксельная камера OV2640, слот для microSD-карты и белый светодиод вспышки. Сама по себе она подключается к домашней сети, передаёт видео в браузер, делает фото и сохраняет их на SD-карту или отправляет через интернет. Это главная плата для камер видеонаблюдения, видеозвонков у двери, таймлапсов роста растений и роботов-машинок с видео, но USB-разъёма у неё нет, поэтому начало работы немного отличается от привычных плат ESP32.

Из этого руководства узнаешь распиновку AI-Thinker ESP32-CAM, как программировать её через адаптер ESP32-CAM-MB или USB-TTL-адаптер (включая соединение IO0-GND), как получить живую картинку в браузере с помощью готового примера CameraWebServer и какие настройки PSRAM и разделов памяти выбрать. Затем разберём причины самых частых ошибок Camera init failed with error 0x105 (камера не инициализирована) и brownout (просадка питания), научимся управлять вспышкой и соберём скетч, который по нажатию кнопки сохраняет фото на microSD-карту.

Модуль камеры AI-Thinker ESP32-CAM
ESP32-CAM: модуль ESP32-S, камера OV2640, слот microSD и вспышка на одной плате. USB-разъёма нет, поэтому для программирования нужен адаптер.

Что такое ESP32-CAM и как она работает?

Сердце платы — модуль ESP32-S с двухъядерным чипом ESP32, у которого есть Wi-Fi и Bluetooth. Камера подключается к 24-контактному разъёму на плате тонким шлейфом (FPC). ESP32 подаёт на камеру тактовый сигнал 20 MHz (XCLK) через GPIO 0 и настраивает её по похожей на I2C шине SCCB на GPIO 26 и 27. Сенсор OV2640 умеет сам сжимать изображение в JPEG, поэтому ESP32 получает не сырые пиксели, а небольшие JPEG-кадры. Эти кадры идут по 8-битной параллельной шине данных в 4 МБ PSRAM на плате, а дальше Wi-Fi доставляет их в браузер или на сервер.

ПараметрЗначение
ПроцессорESP32 (модуль ESP32-S), два ядра, до 240 MHz
Беспроводная связьWi-Fi 802.11 b/g/n (только 2,4 GHz), Bluetooth 4.2 и BLE
Память520 КБ встроенной SRAM + 4 МБ PSRAM, 4 МБ флеш-памяти
КамераOV2640, 2 Мп, до 1600×1200 (UXGA), вывод в JPEG
ХранилищеСлот microSD (до 4 ГБ по официальной документации)
ПодсветкаБелая вспышка (GPIO 4), красный светодиод состояния на обороте (GPIO 33)
Питание5 V на пин 5V (рекомендуется) или 3,3 V на пин 3V3
Ток (при 5 V)Около 180 mA с выключенной вспышкой, около 310 mA со вспышкой на полной яркости; пики при работе Wi-Fi выше
Размер27 × 40,5 × 4,5 мм

Распиновка AI-Thinker ESP32-CAM: какие пины свободны?

Вдоль двух краёв платы 16 пинов, но по-настоящему свободных среди них совсем немного: одна только камера занимает 15 GPIO, а слот microSD делит ещё шесть. В таблице ниже собраны крайние пины и случаи, когда ими можно пользоваться. Общие особенности пинов ESP32 можно посмотреть и в утилите распиновки.

ПинНазначениеМожно использовать?
5VВход питания (рекомендуется)—
3V3Вход питания 3,3 V—
GND (3 пина)Земля—
VCCВыход питания (по умолчанию 3,3 V)Не используй его для питания платы
U0R (GPIO 3)Последовательный RX: программирование и монитор портаТолько если последовательный порт не нужен
U0T (GPIO 1)Последовательный TX: программирование и монитор портаТолько если последовательный порт не нужен
GPIO 0Тактирование камеры (XCLK) и режим загрузкиНет; только замыкается на GND во время загрузки кода
GPIO 16На многих платах подключён к PSRAMНет
GPIO 2SD DATA0Если SD-карта не используется; пин конфигурации при старте (strapping)
GPIO 4SD DATA1 и вспышкаВ 1-битном режиме SD или без SD
GPIO 12SD DATA2В 1-битном режиме SD или без SD; при старте должен быть LOW
GPIO 13SD DATA3В 1-битном режиме SD или без SD; самый безопасный свободный пин
GPIO 14SD CLKЕсли SD-карта не используется
GPIO 15SD CMDЕсли SD-карта не используется; пин конфигурации при старте (strapping)
GPIO 33Красный светодиод (не выведен на гребёнку)Только светодиод; запись LOW включает его
Пин VCC — не вход: это выход стабилизатора на плате, по умолчанию на нём 3,3 V. Если подать сюда 5 V, линия 3,3 V сразу поднимется до 5 V, и ESP32 может сгореть, поэтому питание всегда подавай на пин 5V. Кроме того, если при старте на GPIO 12 HIGH, ESP32 пытается питать флеш-память от 1,8 V, и плата не запускается, так что не подключай к этому пину подтягивающий резистор или модуль, который тянет его в HIGH.

Как программировать ESP32-CAM? Два способа

На ESP32-CAM нет USB-UART-преобразователя, через который обычные платы ESP32 общаются с компьютером. Для загрузки кода есть два варианта: адаптер ESP32-CAM-MB, который вставляется снизу платы (небольшая базовая плата с чипом CH340 и разъёмом micro USB), или уже имеющийся USB-TTL-адаптер (FTDI, CH340, CP2102). Какой бы вариант ни был выбран, логика одна: если при старте GPIO 0 в LOW, ESP32 входит в режим загрузки и ждёт код; если в HIGH — запускает программу из флеш-памяти. Именно это делают кнопка IO0 на адаптере MB и провод IO0-GND, который добавляется при работе с USB-TTL-адаптером.

USB-TTL-адаптер CP2102
USB-TTL-адаптер CP2102: для программирования ESP32-CAM нужны только пины GND, 5V, TX и RX.
USB-TTL-адаптерESP32-CAMПримечания
GNDGNDОбщая земля
5V5VПитает плату; выхода 3,3 V адаптера не хватает
TXU0R (GPIO 3)Перекрёстно: передатчик к приёмнику
RXU0T (GPIO 1)Перекрёстно: приёмник к передатчику
—Между IO0 и GNDТолько во время загрузки; короткий провод между двумя соседними пинами

Если на адаптере есть перемычка выбора 3,3 V / 5 V, поставь её на 3,3 V, чтобы уровень линии TX подходил пинам ESP32. А вот питание на плату подавай с пина 5V адаптера. Выход 3,3 V у большинства адаптеров даёт лишь около 50–100 mA; как только ESP32-CAM включает Wi-Fi, это питание проседает, и плата перезагружается.

Настройки Arduino IDE: плата, схема разделов и PSRAM

  1. Открой в Arduino IDE «Менеджер плат» (Boards Manager) и установи пакет esp32 by Espressif Systems; подробно об этом — в руководстве Установка Arduino IDE.
  2. Выбери плату AI Thinker ESP32-CAM в Инструменты › Плата › esp32 (Tools › Board).
  3. Проверь, что в Partition Scheme (схема разделов) выбрано Huge APP (3MB No OTA/1MB SPIFFS): примеру с камерой нужно не меньше 3 МБ под программу.
  4. Подключи плату к адаптеру и выбери новый порт в Инструменты › Порт (COMx на Windows, /dev/cu.usbserial... или /dev/cu.wchusbserial... на macOS).
  5. Если порт вообще не появляется, установи драйвер CH340 или CP2102 и возьми USB-кабель с передачей данных.
НастройкаРекомендуемое значениеПочему?
Плата (Board)AI Thinker ESP32-CAMPSRAM, 4 МБ флеш-памяти и нужная схема разделов уже настроены
Partition SchemeHuge APP (3MB No OTA/1MB SPIFFS)Те самые 3 МБ под программу, которые нужны CameraWebServer
PSRAMВ этом описании платы всегда включенаС ESP32 Dev Module поставь PSRAM: Enabled; у ESP32 Wrover Module она уже включена
CPU Frequency240MHz (WiFi/BT)Максимальная скорость для передачи изображения и Wi-Fi
Flash Mode / Flash FrequencyQIO / 80MHzЕсли загрузка или запуск не проходят, попробуй DIO / 40MHz
Core Debug LevelNone (Info при поиске неисправностей)На уровне Info в монитор порта выводятся данные о камере и FPS
Upload Speed460800 (на этой плате не меняется)Если загрузка постоянно обрывается, выбери ESP32 Wrover Module и попробуй 115200
Монитор порта всегда открывай на скорости 115200 бод. Сообщения при старте появляются только в момент сброса, поэтому если экран пуст, нажми кнопку RST на плате. Если видишь waiting for download (ожидание загрузки), IO0 всё ещё соединён с GND: убери провод, снова нажми RST, и программа запустится.

Первая загрузка шаг за шагом

  1. Собери подключение: вставь плату в адаптер MB, совместив пины, или подключи USB-TTL-адаптер по таблице выше. С USB-TTL-адаптером добавь ещё и короткий провод между IO0 и GND.
  2. Переведи плату в режим загрузки: на адаптере MB зажми кнопку IO0, нажми и отпусти RST, затем отпусти IO0. С USB-TTL-адаптером просто нажми RST, когда провод IO0-GND на месте. Некоторые адаптеры MB со схемой автосброса загружают код вообще без нажатия кнопок.
  3. Нажми Загрузка в Arduino IDE. В выводе сначала появится Connecting..., а за ним — ход записи в процентах.
  4. Когда появится строка Hard resetting via RTS pin..., загрузка закончена. Если используется USB-TTL-адаптер, сейчас убери провод IO0-GND.
  5. Открой монитор порта на 115200 бод и нажми RST: новая программа запустится.
C++ (Arduino IDE)
1// ESP32-CAM ilk yükleme testi / ESP32-CAM first upload test
2const int KIRMIZI_LED = 33;   // arkadaki kırmızı LED, ters mantık / red LED on the back, inverted logic
3
4void setup() {
5  Serial.begin(115200);
6  pinMode(KIRMIZI_LED, OUTPUT);
7  delay(500);
8
9  // PSRAM kontrolü / PSRAM check
10  if (psramFound()) {
11    Serial.print("PSRAM bulundu / PSRAM found: ");
12    Serial.print(ESP.getPsramSize());
13    Serial.println(" bayt / bytes");
14  } else {
15    Serial.println("PSRAM yok / No PSRAM");
16  }
17}
18
19void loop() {
20  digitalWrite(KIRMIZI_LED, LOW);    // LOW = yanar / LOW = on
21  delay(500);
22  digitalWrite(KIRMIZI_LED, HIGH);   // HIGH = söner / HIGH = off
23  delay(500);
24}

Если загрузка прошла, маленький красный светодиод на обороте платы мигает каждые полсекунды. У этого светодиода инверсная логика: запись LOW включает его, а HIGH выключает. В мониторе порта должно быть видно примерно 4 миллиона байт PSRAM; если там написано No PSRAM, проверь выбор платы, а если не помогает, возможно, на твоей плате нет чипа PSRAM. Этот короткий тест перед примером с камерой сразу показывает, в чём проблема — в загрузке или в камере.

Пример CameraWebServer: живая картинка в браузере

  1. Открой Файл › Примеры › ESP32 › Camera › CameraWebServer.
  2. Выбери модель камеры: в списке на вкладке board_config.h (новые версии) или в начале основного файла (старые версии) поставь // перед строкой CAMERA_MODEL_ESP_EYE и убери // перед строкой CAMERA_MODEL_AI_THINKER.
  3. Впиши имя и пароль своей Wi-Fi-сети 2,4 GHz в строки ssid и password.
  4. Переведи плату в режим загрузки и загрузи скетч; затем убери соединение IO0 и нажми RST.
  5. В мониторе порта появится строка вроде Camera Ready! Use 'http://192.168.1.45' to connect; открой этот адрес в браузере телефона или компьютера в той же сети.
  6. Запусти живую картинку кнопкой Start Stream на странице или сделай одно фото кнопкой Get Still.
C++ (Arduino IDE)
1// board_config.h (eski sürümlerde CameraWebServer.ino) / (CameraWebServer.ino in older versions)
2//#define CAMERA_MODEL_ESP_EYE      // Has PSRAM  <- kapat / comment out
3#define CAMERA_MODEL_AI_THINKER     // Has PSRAM  <- aç / enable
4
5// CameraWebServer.ino
6const char *ssid = "AgAdi";          // Wi-Fi adı / Wi-Fi name
7const char *password = "Sifre123";   // Wi-Fi şifresi / Wi-Fi password

Интерфейс работает на двух отдельных серверах: страница настроек и адрес /capture, который возвращает одно фото, находятся на порту 80, а видеопоток — по адресу /stream на порту 81 (например, http://192.168.1.45:81/stream). Этот адрес потока можно добавить на свою веб-страницу или в систему умного дома. В меню слева меняются разрешение, качество JPEG, яркость, контраст и баланс белого, а если картинка перевёрнута, помогут переключатели V-Flip и H-Mirror; в новых версиях есть ещё ползунок LED Intensity для вспышки. Обычно плата обслуживает только одного зрителя потока за раз, поэтому поток, открытый во второй вкладке, может зависнуть. Если страница вообще не открывается, проверь, что телефон и плата в одной сети; при проблемах с подключением загляни в статью ESP32 не подключается к Wi-Fi.

Таблица разрешений и FPS для ESP32-CAM

На бумаге сенсор OV2640 выдаёт 15 кадров в секунду при 1600×1200, 30 при 800×600 и 60 в размере CIF. На практике частоту кадров в основном определяют Wi-Fi-канал и размер JPEG-кадра: с ростом разрешения и качества каждый кадр становится больше и медленнее проходит через сеть. Ниже — примерные диапазоны с CameraWebServer при хорошем сигнале Wi-Fi и качестве JPEG 10–12. Чтобы измерить свою плату, поставь Core Debug Level на Info: во время трансляции монитор порта печатает для каждого кадра строку вроде MJPG: ... (x fps).

РазрешениеПикселиПримерный FPS (поток по Wi-Fi)Для чего подходит
QQVGA160×12025–30Обнаружение движения, очень слабые сети
QVGA320×24020–30Роботы-машинки, плавное живое видео
CIF400×29615–25Плавный просмотр на маленьких экранах
VGA640×48010–20Живой просмотр общего назначения
SVGA800×6008–15Более чёткий просмотр; самый большой размер без PSRAM
XGA1024×7685–10Детальные изображения
HD1280×7204–8Широкий кадр (16:9)
SXGA1280×10243–6Фото
UXGA1600×12002–5Фото в максимальном разрешении

В своём коде разрешение, качество и ориентацию можно менять через объект сенсора после успешного запуска камеры. Качество JPEG задаётся числом от 0 до 63, и чем меньше число, тем выше качество; ниже 10 кадры становятся слишком большими, и изображения могут обрываться на середине.

C++ (Arduino IDE)
1// esp_camera_init() başarılı olduktan sonra / after esp_camera_init() succeeds
2sensor_t *s = esp_camera_sensor_get();
3s->set_framesize(s, FRAMESIZE_VGA);   // 640x480 çözünürlük / resolution
4s->set_quality(s, 12);                // 0-63: küçük = daha kaliteli, 10 altı önerilmez / lower = better, below 10 not advised
5s->set_vflip(s, 1);                   // görüntüyü dikey çevir / flip vertically
6s->set_hmirror(s, 1);                 // yatay ayna / mirror horizontally
7s->set_brightness(s, 1);              // parlaklık -2..2 / brightness -2..2

Почему настройка PSRAM так важна для ESP32-CAM?

Встроенная память ESP32 — 520 КБ, и большую её часть занимает стек Wi-Fi. А один JPEG-кадр 1600×1200 в зависимости от сцены и качества может весить 100–250 КБ. Поэтому драйвер камеры размещает буферы кадров в PSRAM. CameraWebServer проверяет наличие PSRAM функцией psramFound(): если она есть, пример стартует с максимальным разрешением и двумя буферами кадров; если нет — ограничивает разрешение до SVGA (800×600) и держит один буфер во встроенной памяти.

Если запросить высокое разрешение при выключенной PSRAM, монитор порта покажет frame buffer malloc failed, а за ним Camera init failed with error 0xffffffff. Решение — выбрать плату AI Thinker ESP32-CAM (с ESP32 Dev Module поставить Инструменты › PSRAM: Enabled) или снизить разрешение и уменьшить число буферов кадров до 1. На некоторых дешёвых клонах чипа PSRAM может не быть вовсе; строка PSRAM в первом тестовом скетче сразу это покажет.

Ошибка Camera init failed with error 0x105 и другие ошибки камеры

Большинство ошибок камеры сводятся к одной из трёх причин: в коде выбрана не та модель камеры, шлейф плохо сидит в разъёме или питания не хватает. Код ошибки в мониторе порта быстро сужает круг причин:

Сообщение в мониторе портаЧто означаетРешение
Camera probe failed with error 0x105(ESP_ERR_NOT_FOUND) (камера не найдена)ESP32 не нашла камеру на шине SCCBПроверь, что выбрана CAMERA_MODEL_AI_THINKER, переподключи шлейф, питай плату от 5 V / 2 A
Detected camera not supported и 0x106Та же ситуация в новых версиях драйвера: камера не найдена или не распознанаТе же шаги, что и для 0x105; если не помогает, замени модуль камеры
frame buffer malloc failed и 0xffffffffНе удалось выделить память под буфер кадраВключи PSRAM, снизь разрешение, поставь fb_count в 1
cam_hal: EV-VSYNC-OVF или FB-OVFНовый кадр пришёл раньше, чем обработан предыдущий, и кадр пропущенИзредка — не страшно; если часто, снизь разрешение и улучши сигнал Wi-Fi
NO-SOI (нет маркера начала JPEG)Повреждённый или неполный JPEG-кадрПроверь питание и шлейф, поставь xclk_freq_hz в 10000000
Половины изображения нет, оно зелёное или в полосахКадр не поместился в буфер, или питание шумитПодними значение качества JPEG до 12 и выше, улучши питание
Вставляя шлейф, подними чёрную защёлку разъёма, вставь шлейф до упора ровно и закрой защёлку. Никогда не подключай и не отключай камеру при включённом питании: могут пострадать и камера, и ESP32. И не забудь снять защитную плёнку с объектива нового модуля камеры, иначе картинка будет размытой.

Ошибка brownout и правильное питание (5 V / 2 A)

Если монитор порта показывает Brownout detector was triggered (сработал детектор просадки питания) и плата всё время перезагружается, напряжение питания ESP32 на мгновение упало ниже безопасного уровня. ESP32-CAM потребляет короткие, но сильные пики тока в момент включения Wi-Fi-передатчика и начала съёмки. Слабый USB-порт, тонкие и длинные перемычки или выход 3,3 V адаптера за этими пиками не успевают. Вот постоянные решения:

  • Возьми источник питания на 5 V и не меньше 2 A и подключи его к пину 5V платы; стабилизатор на плате понизит напряжение до 3,3 V.
  • Провода питания делай короткими и толстыми: макетные платы и длинные перемычки добавляют просадку напряжения.
  • Поставь электролитический конденсатор на 470–1000 µF между пинами 5V и GND платы как можно ближе к ней (плюсовой вывод — к 5V).
  • Если питаешь плату от компьютера, подключай её прямо к порту на задней стороне ПК, а не через USB-хаб.
  • Для работы от батареи используй аккумулятор 18650 с повышающим модулем на 5 V; это гораздо стабильнее, чем связка батареек AA.
Литий-ионный аккумулятор 18650
Литий-ионный аккумулятор 18650: вместе с повышающим модулем на 5 V даёт стабильное питание проектам на ESP32-CAM от батареи.

В интернете часто встречается строка WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0), которая отключает детектор просадки в коде. Перезагрузки она останавливает, но проблему не решает: напряжение продолжает проседать, и теперь плата может зависать, выдавать битые кадры или терять данные при записи во флеш-память. Используй её только для временного теста; полный список сообщений о перезагрузке — в руководстве ESP32: brownout и Guru Meditation. При оценке времени работы от батареи считай, что при непрерывной трансляции плата в среднем потребляет 180 mA: аккумулятор 18650 на 2600 mAh с повышающим преобразователем с КПД 85% даёт около 1600 mAh на стороне 5 V, то есть примерно 9 часов трансляции. Свои цифры можно посчитать в калькуляторе времени работы от батареи; учти, что даже в глубоком сне плата потребляет около 6 mA из-за стабилизатора и PSRAM.

Управление вспышкой (GPIO 4)

Яркий белый светодиод на лицевой стороне платы управляется через транзистор, подключённый к GPIO 4: пин только переключает транзистор, а большой ток светодиода идёт от линии питания. digitalWrite(4, HIGH) включает его, LOW — выключает. Светодиод очень яркий, и если он долго горит, греются и он, и плата; лучше зажигать вспышку всего на несколько сотен миллисекунд во время съёмки. GPIO 4 — это ещё и линия DATA1 microSD-карты, поэтому когда карта работает в 4-битном режиме, вспышка сама мигает при каждом чтении и записи. Чтобы этого не было, запускай SD-карту в 1-битном режиме (SD_MMC.begin("/sdcard", true)); на некоторых платах светодиод всё равно может еле заметно светиться.

Сохранение фото на microSD-карту с ESP32-CAM

Скетч ниже при каждом нажатии кнопки на GPIO 13 включает вспышку, делает фото 1600×1200 и сохраняет его на microSD-карту как foto1.jpg, foto2.jpg... Отформатируй microSD-карту в FAT32 на компьютере; хотя официальная документация упоминает карты до 4 ГБ, на практике карты FAT32 на 16–32 ГБ обычно работают нормально. Один вывод кнопки подключи к GPIO 13, другой — к GND; скетч включает встроенный подтягивающий резистор, так что дополнительный резистор не нужен. Как устроено подключение кнопки, читай в статье Как подключить кнопку к Arduino.

C++ (Arduino IDE)
1#include "esp_camera.h"
2#include "FS.h"
3#include "SD_MMC.h"
4
5// AI-Thinker ESP32-CAM kamera pinleri / AI-Thinker ESP32-CAM camera pins
6#define PWDN_GPIO_NUM     32
7#define RESET_GPIO_NUM    -1
8#define XCLK_GPIO_NUM      0
9#define SIOD_GPIO_NUM     26
10#define SIOC_GPIO_NUM     27
11#define Y9_GPIO_NUM       35
12#define Y8_GPIO_NUM       34
13#define Y7_GPIO_NUM       39
14#define Y6_GPIO_NUM       36
15#define Y5_GPIO_NUM       21
16#define Y4_GPIO_NUM       19
17#define Y3_GPIO_NUM       18
18#define Y2_GPIO_NUM        5
19#define VSYNC_GPIO_NUM    25
20#define HREF_GPIO_NUM     23
21#define PCLK_GPIO_NUM     22
22
23#define FLAS_LED  4    // beyaz flaş LED'i / white flash LED
24#define BUTON    13    // butonun diğer ucu GND'de / other button leg to GND
25
26int fotoNo = 1;        // sıradaki dosya numarası / next file number
27bool hazir = false;    // kamera ve SD hazır mı? / are camera and SD ready?
28
29bool kameraBaslat() {
30  camera_config_t config = {};
31  config.ledc_channel = LEDC_CHANNEL_0;
32  config.ledc_timer   = LEDC_TIMER_0;
33  config.pin_d0 = Y2_GPIO_NUM;
34  config.pin_d1 = Y3_GPIO_NUM;
35  config.pin_d2 = Y4_GPIO_NUM;
36  config.pin_d3 = Y5_GPIO_NUM;
37  config.pin_d4 = Y6_GPIO_NUM;
38  config.pin_d5 = Y7_GPIO_NUM;
39  config.pin_d6 = Y8_GPIO_NUM;
40  config.pin_d7 = Y9_GPIO_NUM;
41  config.pin_xclk     = XCLK_GPIO_NUM;
42  config.pin_pclk     = PCLK_GPIO_NUM;
43  config.pin_vsync    = VSYNC_GPIO_NUM;
44  config.pin_href     = HREF_GPIO_NUM;
45  config.pin_sccb_sda = SIOD_GPIO_NUM;
46  config.pin_sccb_scl = SIOC_GPIO_NUM;
47  config.pin_pwdn     = PWDN_GPIO_NUM;
48  config.pin_reset    = RESET_GPIO_NUM;
49  config.xclk_freq_hz = 20000000;           // 20 MHz kamera saati / 20 MHz camera clock
50  config.pixel_format = PIXFORMAT_JPEG;
51
52  if (psramFound()) {
53    config.frame_size   = FRAMESIZE_UXGA;   // 1600x1200
54    config.jpeg_quality = 10;               // küçük = daha kaliteli / lower = better quality
55    config.fb_count     = 2;
56    config.fb_location  = CAMERA_FB_IN_PSRAM;
57    config.grab_mode    = CAMERA_GRAB_LATEST;
58  } else {
59    config.frame_size   = FRAMESIZE_SVGA;   // PSRAM yoksa 800x600 / 800x600 without PSRAM
60    config.jpeg_quality = 12;
61    config.fb_count     = 1;
62    config.fb_location  = CAMERA_FB_IN_DRAM;
63    config.grab_mode    = CAMERA_GRAB_WHEN_EMPTY;
64  }
65
66  esp_err_t err = esp_camera_init(&config);
67  if (err != ESP_OK) {
68    Serial.printf("Camera init failed 0x%x (%s)\n", err, esp_err_to_name(err));
69    return false;
70  }
71  return true;
72}
73
74void fotoCek() {
75  digitalWrite(FLAS_LED, HIGH);              // flaşı yak / flash on
76  delay(150);                                // pozlama otursun / let exposure settle
77
78  camera_fb_t *fb = esp_camera_fb_get();     // eski kareyi at / discard the stale frame
79  if (fb) esp_camera_fb_return(fb);
80  fb = esp_camera_fb_get();                  // asıl fotoğraf / the real photo
81  digitalWrite(FLAS_LED, LOW);               // flaşı söndür / flash off
82
83  if (!fb) {
84    Serial.println("Kare alinamadi / Capture failed");
85    return;
86  }
87
88  String yol = "/foto" + String(fotoNo) + ".jpg";
89  File dosya = SD_MMC.open(yol, FILE_WRITE);
90  if (dosya) {
91    dosya.write(fb->buf, fb->len);           // JPEG verisini yaz / write the JPEG data
92    dosya.close();
93    Serial.printf("Kaydedildi / Saved: %s (%u bayt / bytes)\n", yol.c_str(), (unsigned)fb->len);
94    fotoNo++;
95  } else {
96    Serial.println("Dosya acilamadi / Could not open file");
97  }
98  esp_camera_fb_return(fb);                  // tamponu geri ver / return the buffer
99}
100
101void setup() {
102  Serial.begin(115200);
103  pinMode(BUTON, INPUT_PULLUP);
104
105  if (!kameraBaslat()) return;               // kamera yoksa dur / stop without a camera
106
107  // 1-bit mod: GPIO 4, 12 ve 13 boşta kalır / 1-bit mode keeps GPIO 4, 12 and 13 free
108  if (!SD_MMC.begin("/sdcard", true)) {
109    Serial.println("SD kart takilamadi / SD card mount failed");
110    return;
111  }
112  if (SD_MMC.cardType() == CARD_NONE) {
113    Serial.println("SD kart yok / No SD card");
114    return;
115  }
116
117  pinMode(FLAS_LED, OUTPUT);
118  digitalWrite(FLAS_LED, LOW);
119
120  // Var olan fotoğrafların üzerine yazma / don't overwrite existing photos
121  while (SD_MMC.exists("/foto" + String(fotoNo) + ".jpg")) fotoNo++;
122
123  hazir = true;
124  Serial.println("Hazir, butona bas / Ready, press the button");
125}
126
127void loop() {
128  if (!hazir) {                              // hata varsa bekle / wait if setup failed
129    delay(1000);
130    return;
131  }
132  if (digitalRead(BUTON) == LOW) {
133    delay(30);                               // buton sıçramasını (bounce) önle / debounce
134    if (digitalRead(BUTON) == LOW) {
135      fotoCek();
136      while (digitalRead(BUTON) == LOW) delay(10);   // bırakılmasını bekle / wait for release
137    }
138  }
139}

Скетч запускает SD-карту в 1-битном режиме, поэтому использует только GPIO 2, 14 и 15, а GPIO 4 остаётся для вспышки и GPIO 13 — для кнопки. Перед съёмкой один кадр захватывается и сразу возвращается: камера выдаёт кадры непрерывно, так что первый кадр в буфере мог быть снят ещё до включения вспышки, а автоэкспозиции нужна короткая пауза, чтобы подстроиться под новый свет. Номер файла определяется по файлам, которые уже лежат на карте, поэтому старые фото не перезаписываются даже после перезагрузки. Общие правила записи данных на SD-карту — в руководстве Модуль SD-карты.

MicroPython и новые платы с камерой

ESP32-CAM можно программировать и на MicroPython, но в официальной сборке MicroPython нет драйвера камеры; придётся записать прошивку от сообщества с добавленной поддержкой камеры. Поэтому для новичков путь через Arduino IDE гораздо проще. Если не хочется возиться с платой без USB-разъёма, присмотрись к платам с камерой на ESP32-S3, у которых есть USB и больше памяти (например, Seeed XIAO ESP32S3 Sense или ESP32-S3-EYE); эти платы тоже есть в списке моделей CameraWebServer.

Идеи проектов на ESP32-CAM

  • Фото при движении: делай снимок, когда PIR-датчик замечает человека, и отправляй его на телефон; пошаговое руководство — в статье Фото с ESP32-CAM в Telegram.
  • Таймлапс: пусть плата просыпается каждые 10 минут, делает фото, сохраняет его на SD-карту и снова уходит в глубокий сон; так можно наблюдать, как растёт растение или меняется погода.
  • Камера дверного звонка: фотографируй порог при нажатии кнопки звонка и отправляй уведомление через Telegram-бота.
  • Робот-машинка с видео: управляй машинкой, глядя на плавный живой поток QVGA на телефоне.
  • Камера для питомца: смотри живой поток в домашней сети и при необходимости включай вспышку с веб-страницы.
  • Своя панель управления: встрой живой поток в свою веб-страницу и добавь рядом показания датчиков и управление реле; путь показывает руководство ESP32 и WebSocket.

Частые ошибки и как их исправить

СимптомВероятная причинаРешение
«Failed to connect to ESP32: No serial data received» (в старых версиях — «Timed out waiting for packet header»; нет ответа от платы)Плата не в режиме загрузки, или перепутаны TX и RXСоедини IO0 с GND или используй кнопку IO0, нажми RST; проверь, что TX идёт к U0R, а RX — к U0T
«Wrong boot mode detected (0x13)» (неверный режим загрузки)При старте на GPIO 0 не было LOWПосле соединения IO0-GND всегда нажимай RST
«waiting for download» в мониторе портаIO0 всё ещё соединён с GNDУбери провод IO0 и нажми RST
Мусорные символы в мониторе портаНеверная скоростьПоставь в мониторе порта 115200 бод
Платы нет в списке портовНет драйвера или кабель только для зарядкиУстанови драйвер CH340 или CP2102, возьми кабель с передачей данных
Загрузка не проходит со вставленной SD-картойGPIO 2 одновременно линия SD и пин конфигурации при стартеНа время загрузки вынимай microSD-карту
Очень медленная или часто зависающая картинкаСлабый сигнал Wi-Fi или высокое разрешениеПерейди на VGA или QVGA, поставь плату ближе к роутеру, при необходимости используй внешнюю антенну
Вспышка постоянно слабо светитсяSD-карта в 4-битном режиме или GPIO 4 висит в воздухеЗапускай SD-карту в 1-битном режиме, настрой GPIO 4 как OUTPUT и запиши LOW
Если хочешь поставить внешнюю антенну (IPEX), какую антенну использует плата, решает положение крошечного резистора 0 Ω рядом с антенной; одного подключения антенны мало, нужно ещё перепаять резистор. Если плата не стартует или SD-карта не запускается, отключи все модули от GPIO 2, 12 и 13 и попробуй снова: GPIO 2 и 12 — пины конфигурации при старте, а GPIO 13 — линия DATA3 SD-карты, которая не должна быть в LOW во время инициализации карты.

ESP32-CAM — самый простой способ собрать Wi-Fi-камеру на недорогой плате, и почти все трудности с ней связаны с подключением для программирования или с питанием. Когда эти две вещи в порядке, плата транслирует живое видео со своего веб-сервера, делает фото и сохраняет их на SD-карту. В приложении BlueGrays есть проекты на ESP32: подключение к Wi-Fi, веб-сервер, mDNS, запись данных на microSD и сигнализация движения с уведомлениями в Telegram — у каждого пошаговые схемы подключения и готовый код.

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

Как программировать ESP32-CAM?

У ESP32-CAM нет USB-разъёма, поэтому нужен адаптер ESP32-CAM-MB или USB-TTL-адаптер. В Arduino IDE выбирается плата AI Thinker ESP32-CAM, а плата переводится в режим загрузки нажатием RST, пока GPIO 0 соединён с GND (или пока зажата кнопка IO0 на MB). После загрузки убери соединение IO0 и снова нажми RST.

Можно ли программировать ESP32-CAM через Arduino Uno?

Да: если соединить пин RESET у Uno с GND, её USB-чип работает как мост USB-UART. В этом случае подключение прямое, а не перекрёстное: D0 (RX) у Uno идёт к U0R, D1 (TX) — к U0T, а IO0 по-прежнему замкнут на GND. Сигналы Uno — 5 V, и это работа ESP32 на пределе; для постоянного использования надёжнее адаптер MB или USB-TTL-адаптер на 3,3 V.

Какое напряжение нужно ESP32-CAM?

Рекомендуется питать плату от 5 V через пин 5V; стабилизатор на плате понизит напряжение до 3,3 V. Можно подать и 3,3 V на пин 3V3, но если источник не выдерживает пиков тока Wi-Fi, плата будет постоянно перезагружаться. Источник питания, способный выдать не меньше 2 A, предотвращает большинство ошибок brownout.

Как исправить ошибку Camera init failed на ESP32-CAM?

Код ошибки 0x105 или 0x106 означает, что камера вообще не найдена: сначала проверь, что в коде выбрана CAMERA_MODEL_AI_THINKER, затем что шлейф вставлен до конца, и питай плату от 5 V / 2 A. Если проблема остаётся, возможно, неисправен модуль камеры. Если видишь 0xffffffff вместе с frame buffer malloc failed, включи PSRAM или снизь разрешение.

Работает ли ESP32-CAM без интернета?

Да. Чтобы смотреть картинку, нужна только локальная сеть, а не интернет; ESP32-CAM может и сама создать Wi-Fi-сеть с помощью WiFi.softAP(), тогда она открывается в браузере по адресу 192.168.4.1. Её можно использовать и вовсе без сети — как камеру, которая только сохраняет фото на microSD-карту.

Может ли ESP32-CAM записывать видео?

Готовые примеры транслируют живое видео в MJPEG и делают отдельные фото; видеофайл напрямую они не записывают. Есть проекты от сообщества, которые пишут последовательные JPEG-кадры на microSD-карту и собирают из них файл AVI, но частота кадров ограничена даже на низких разрешениях. Микрофона на плате нет, так что и звук она записывать не может.

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

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

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

4.7(37)
99
Мигание встроенным светодиодом ESP32
#106Новичок

Мигание встроенным светодиодом ESP32

Мигание встроенным синим светодиодом на ESP32 DevKit V1. Внешние компоненты не нужны! Первый проект, который стоит попробовать после покупки ESP32. Встроенный светодиод подключён к GPIO 2.

4.8(29)
53
Подключение 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 (с выводом IP на LCD)
#129Продвинутый

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

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

4.8(16)
26
mDNS на ESP32: доступ к устройству через bluegrays-esp.local (с экраном LCD)
#131Продвинутый
Premium

mDNS на ESP32: доступ к устройству через bluegrays-esp.local (с экраном LCD)

В этом проекте к ESP32 добавляется служба mDNS (Multicast DNS), и к устройству можно обращаться по понятному имени вроде bluegrays-esp.local вместо IP-адреса. Даже если IP, выданный DHCP, меняется, устройство всегда доступно по одному имени. На устройстве также работает маленький веб-сервер, через который по адресу http://bluegrays-esp.local/ из браузера отправляются команды светодиоду. I2C LCD показывает имя хоста, IP-адрес и состояние светодиода.

4.8(14)
23
Регистратор температуры на ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
#144Продвинутый
Premium

Регистратор температуры на ESP32 (DHT22 + DS1307 RTC + microSD + OLED)

Собери на ESP32 регистратор температуры с отметками времени! Каждую минуту читай температуру и влажность с DHT22, бери реальное время из DS1307 RTC и записывай всё на карту microSD в формате CSV. OLED показывает текущую температуру (крупным шрифтом), влажность в процентах, время и счётчик записей. Файл /temp_log.csv на SD-карте открывается в Excel, Google Sheets или Python pandas — идеально для теплиц, комнат, лабораторий и хранения продуктов.

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

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

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

0.0(0)
9