Neste artigo, você vai aprender a programar o módulo ESP32-CAM. Os módulos ESP32 Camera são relativamente baratos e muito úteis para construir sistemas simples de vigilância ou monitorização. Por exemplo, tirar fotos automaticamente de animais à noite no seu jardim ou anunciar visitantes na porta da frente via webcam são casos de uso comuns.
No entanto, carregar e executar programas nas placas de desenvolvimento comuns ESP32-CAM é um pouco complicado. Neste artigo, vamos analisar diferentes métodos para programar e resolver problemas numa placa ESP32-CAM.
Peças Necessárias
Abaixo encontrará os componentes necessários para construir o projeto. Algumas peças, como cabo USB, cartão micro SD ou leitor de cartão SD, pode já ter. Não é necessário comprá-los, pois nenhuma dessas peças é específica para este projeto.
Se não tiver um Adaptador FTDI USB-TTL, então definitivamente vai querer um. Mais cedo ou mais tarde vai precisar de um se continuar a mexer com microcontroladores ESP32. O listado é simples, mas suficiente para a maioria dos casos.

ESP32-CAM com Shield USB-TTL

Adaptador FTDI USB-TTL

Cartão MicroSD 4GB

Leitor de Cartão SD

Cabo de Dados USB

Arduino IDE
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Noções Básicas da Placa de Desenvolvimento ESP32-CAM
A placa de desenvolvimento ESP32-CAM é um módulo compacto que combina um chip ESP32-S, uma câmara, um flash incorporado e um slot para cartão microSD. A placa tem Wi-Fi e Bluetooth integrados e suporta câmaras OV2640 ou OV7670 com até 2 megapixels de resolução.


Pode encontrar a especificação detalhada da placa abaixo. Note que no código de exemplo nos referiremos ao modelo original AI-Thinker da placa ESP32-CAM, mas existem muitos clones com especificações iguais ou semelhantes.
Geralmente são programados e usados da mesma forma. Há uma exceção, porém. Se a sua placa tem uma matriz de pinos com 6 filas (em vez de apenas 4), pode ser programada mais facilmente usando o shield de programação. Veja a imagem abaixo para comparação:

Também há uma diferença no pino GND (marcado com a seta vermelha). Na Versão 1 (lado esquerdo), este pino é rotulado como GND/R e usado para resetar a placa. Na Versão 2 (lado direito), este pino serve como terra (GND).
A Versão 2, do lado direito com 4 pinos, exige que pressione manualmente os botões BOOT e RST ao carregar o código (mais sobre isso depois), enquanto a Versão 1 faz isso automaticamente. Infelizmente, a Versão 2 é bastante comum e é mais fácil usar um programador FTDI neste caso do que o shield de programação.
Características
- Módulo SoC ultra-pequeno 802.11b/g/n Wi-Fi + BT/BLE.
- CPU dual-core de 32 bits de baixo consumo para processadores de aplicação.
- Frequência principal até 240MHz, poder de computação até 600 DMIPS.
- SRAM interna de 520 KB, PSRAM externa de 4M.
- Suporta interfaces como UART/SPI/I2C/PWM/ADC/DAC.
- Suporta câmaras OV2640 e OV7670, flash incorporado.
- Suporta upload de imagem via WiFi, suporte a cartão TF.
- Suporta múltiplos modos de sono.
- Suporta modos de funcionamento STA/AP/STA+AP.
Especificações
- Tamanho: 27*40.5*4.5(±0.2)mm
- Flash SPI: 32Mbit por padrão
- RAM: Interna 520KB + PSRAM externa 4M
- BT: Padrões BT 4.2BR/EDR e BLE
- WiFi: 802.11 b/g/n/e/i
- Interfaces suportadas: UART, SPI, I2C, PWM
- Suporte a cartão TF: 4G (normalmente até 16GB funciona)
- Portas IO: 9
- Formato de saída de imagem: JPEG (apenas suportado pelo OV2640), BMP, GRAYSCALE
- Forma da antena: antena onboard, ganho 2dBi
- Segurança: WPA/WPA2/WPAS-Enterprise/WPS
- Faixa de alimentação: 5V
- Temperatura de funcionamento: -20 °C a 85 °C
Consumo de energia
- Sem flash: 180mA@5V
- Com flash na máxima luminosidade: 310mA@5V
- Deep-sleep: 6mA@5V
- Modem-sleep: 20mA@5V
- Light-sleep: 6.7mA@5V
Pinout
A imagem seguinte mostra o pinout do ESP32-CAM. Note que, em teoria, o ESP32-CAM pode funcionar com 3.3V, mas foram reportados comportamentos instáveis e imagens com marcas d’água. A alimentação recomendada é portanto 5V @ 2A.
Note também que o IO0 está ligado ao pino XCLK da câmara e deve ficar flutuante (não ligado) quando o ESP32 está a funcionar. Deve apenas puxar o IO0 para baixo, para GND, ao carregar código. Mais sobre isso depois.

Comparado com uma placa ESP32 padrão, a placa ESP32-CAM tem muito menos pinos GPIO disponíveis. Isto porque a maioria dos pinos GPIO são usados pela câmara e pelo leitor de cartão SD. Além disso, deve evitar usar GPIO1, GPIO3 e GPIO0, pois são necessários para programar a placa. Para mais informações veja o More GPIO pins for ESP32-CAM tutorial.
O pino P_OUT é rotulado como VCC em algumas placas. É um pino de saída de energia que fornece 3.3V ou 5V dependendo de um solder pad. Não pode usar este pino para alimentar a placa! Use o pino 5V para isso.
Tenha também cuidado com os pinos de terra.Evite usar o pino GND ao lado do GPIO1!Para algumas placas ESP32-CAM, este pino é rotulado como GND/R e usado para resetar a placa. Não pode usá-lo como terra. Para estar seguro, use sempre o pino GND próximo do GPIO0.
Finalmente, os pinos GPIO 2, 4, 12, 13, 14 e 15 são usados pelo leitor de cartão SD. Se não usar o leitor de cartão SD, eles estão disponíveis para GPIO. O LED de flash incorporado está ligado ao GPIO 4, o que faz com que o LED de flash acenda quando o leitor de cartão SD está em uso. Pode usar o seguinte código para evitar isso:
SD_MMC.begin("/sdcard", true)
Pode encontrar informações adicionais sobre o pinout da placa ESP32-CAM neste tutorial da RandomNerd: ESP32-CAM AI-Thinker Pinout Guide: GPIOs Usage Explained.
Esquemas
Se precisar de uma compreensão detalhada da ligação interna da placa ESP32-CAM, veja os esquemas seguintes (source). Clique na imagem para abrir ou download o PDF com os esquemas em resolução total.

Instalar o Core ESP32
Para programar o ESP32-CAM, vai precisar do Arduino IDE com o suporte para placas ESP32 instalado. Felizmente, instalar o Core ESP32 é muito simples. Basta iniciar o seu Arduino IDE e seguir os passos indicados abaixo. Se tiver problemas, pode encontrar instruções mais detalhadas no nosso tutorial How to Program ESP32 with Arduino IDE.
URLs adicionais para o gestor de placas
Primeiro abra o diálogo de Preferências selecionando “Preferences…” no menu “File”:

Isto abrirá o diálogo de Preferências mostrado abaixo. Na aba Settings encontrará uma caixa de edição na parte inferior do diálogo rotulada “Additional boards manager URLs“:

Neste campo copie a seguinte URL: “https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json“
Isto fará com que o Arduino IDE saiba onde encontrar as bibliotecas do core ESP32. A seguir vamos instalar as bibliotecas do core ESP32 usando o Boards Manager.
Boards Manager
Abra o BOARDS MANAGER clicando no ícone da placa na barra lateral, como mostrado abaixo:

Verá o BOARDS MANAGER aparecer à direita da barra lateral. Digite “ESP32” no campo de pesquisa no topo e deverá ver dois tipos de placas ESP32; as “Arduino ESP32 Boards” e as placas “esp32 by Espressif”. Queremos as bibliotecas esp32 da Espressif. Clique no botão INSTALL e aguarde até o download e instalação terminarem.

Depois de instalado, o seu Boards Manager deverá parecer com isto, embora a versão real (aqui 3.0.0-a) possa ser diferente.

No próximo passo, mostro como selecionar a placa ESP32 que quer programar.
Selecionar a Placa
Para selecionar uma placa, vá ao menu Tools. Clique em Board: “Arduino Uno”. Pode ver uma placa diferente da mostrada abaixo, o que é normal. Vá ao submenu e deverá ver as “Arduino AVR Boards” e abaixo as “esp32” (como pode ver, também tenho as placas esp8266 instaladas). Clique em “esp32” e verá uma lista grande de placas disponíveis.

Encontre a placa AI Thinker ESP32-CAM na lista e clique nela.
Agora está pronto para carregar e executar código na placa ESP32-CAM. Vou mostrar duas formas diferentes de fazer isso. Pode usar um dos Shields de Programação que são frequentemente vendidos junto com o módulo ESP32-CAM. Ou pode usar um programador FTDI separado e mais universal. Começamos pelo Shield de Programação.
Carregar código usando um Shield de Programação
Se quiser programar um chip ESP32 diretamente, precisa usar a interface serial UART. Esta está disponível através dos pinos U0TXD e U0RXD que vê no pinout. No entanto, queremos programar o ESP32 usando a porta USB do nosso computador (e o Arduino IDE). Para isso precisamos de um conversor USB para UART.
Shield de Programação
Muitas placas de desenvolvimento com ESP32 têm um chip integrado (CH340) que faz essa conversão USB para UART, mas o módulo ESP32-CAM infelizmente não tem. Portanto, precisa de um Shield de Programação, como o mostrado abaixo, ou de um programador FTDI, que discutiremos mais tarde.

Basta empilhar o módulo ESP32-CAM em cima do Shield de Programação como mostrado abaixo, ligar um cabo USB do Shield ao computador, e está pronto para programar.

Ligando o Shield de Programação ao Computador
Primeiro, certifique-se de que o cabo USB está bem ligado e a funcionar. Deve ouvir um som do computador e um LED vermelho no Shield deve acender ao ligar o cabo USB. Depois, assegure-se de que tem a placa AI Thinker ESP32-CAM e a Porta Serial correta selecionadas. Veja a imagem abaixo.

A Porta Serial que vê provavelmente será diferente. Depende do seu computador e da porta USB que usa para ligar ao ESP32-CAM.
Modo de Programação
Para carregar um programa no ESP32-CAM, precisa fazer o seguinte:
Inicie o upload através do botão de upload no Arduino IDE:

No Shield de Programação, pressione e mantenha pressionado o botão IO0 no lado do módulo:

Espere até que o texto “Connecting …..” apareça no Painel de Saída:

Depois (enquanto ainda mantém o IO0 pressionado) pressione e solte o botão RST (meio segundo) no ESP32-CAM (NÃO no Shield de Programação!). Este botão está numa posição bastante incómoda, mas o botão RST no Shield de Programação não funcionou para mim. A imagem abaixo mostra a localização do botão RST que precisa ser pressionado.

Quando os pontos no texto “Connecting …..” pararem de aparecer, pode soltar o botão IO0 também. Depois disso, deverá ver o seguinte texto no Painel de Saída, indicando que o código está a ser carregado:

Modo de Execução
Quando o texto “Hard resetting via RTS pin…” aparecer no Painel de Saída, o upload está completo. Pode então executar o programa carregado pressionando o botão RST no módulo ESP32-CAM. Como antes, não use o botão RST no Shield de Programação e não pressione o botão IO0.
Pode usar o seguinte código de teste para tentar o upload.
Código de teste para ESP32-CAM com Shield de Programação
Este é um programa simples Blink que acende o LED de flash no ESP32-CAM por 10 ms, depois espera 2 segundos e repete o ciclo.
int flashPin = 4;
void setup() {
pinMode(flashPin, OUTPUT);
}
void loop() {
digitalWrite(flashPin, HIGH);
delay(10);
digitalWrite(flashPin, LOW);
delay(2000);
}
Se vir o LED a piscar, carregou com sucesso um programa no ESP32-CAM.
Pode ter notado que carregar código no ESP32-CAM é um procedimento realmente complicado. Esperaria que o Shield de Programação colocasse automaticamente o ESP32 em modo de programação, sem precisar pressionar botões, mas isso não funcionou com nenhum dos dois Shields de Programação que experimentei.
Mesmo assim, o timing parece ser crítico e a comunicação é frágil. Frequentemente tive que tentar mais de uma vez para conseguir um upload bem-sucedido. Muitas vezes, desligar e ligar o cabo USB ajudava. Às vezes o upload começava sem eu pressionar botões depois de mudar de porta USB.
Na próxima seção, mostro como usar um programador FTDI, que torna o upload de código um pouco mais fácil, mas ainda não totalmente automático.
Carregar código usando um programador FTDI
Um Programador FTDI ou Adaptador FTDI USB-TTL faz essencialmente o mesmo trabalho que o Shield de Programação para o ESP32-CAM. Converte sinais USB em sinais seriais e permite programar microcontroladores como Arduino e ESP32 via interface UART.

Ao contrário do Shield de Programação, o programador FTDI não é para uma placa específica, mas pode ser usado com todos os microcontroladores que suportam comunicação serial. Embora o Programador FTDI seja mais flexível, exige que faça as ligações entre o programador FTDI e o microcontrolador você mesmo. E é isso que faremos na seção seguinte.
Ligando o Programador FTDI ao ESP32-CAM
A imagem seguinte mostra como ligar o Programador FTDI ao módulo ESP32-CAM:

As ligações são simples. Comece por ligar o GND do programador ao GND do módulo ESP32-CAM (fio azul). Depois faça o mesmo com a alimentação 5V (fio vermelho). Note que alguns programadores FTDI têm jumpers ou interruptores para mudar de 3.3V para 5V. Fique atento a isso.
Depois ligamos o pino U0T (U0TXD) do ESP32-CAM ao pino RXD do programador (fio amarelo). De forma semelhante, U0R liga-se ao TXD (fio verde). Com isso, a comunicação serial está estabelecida.
Para colocar o ESP32-CAM em modo de programação, o pino IO0 precisa estar ligado ao terra (GND). Mas se quiser executar o programa, o pino IO0 deve ficar desconectado. Por isso, adicionei um interruptor entre IO0 e GND (fio roxo) que me permite alternar entre modo de programação e modo de execução. Veja a imagem abaixo:

Selecionar placa ESP32-CAM
Ligue o Programador FTDI com o ESP32-CAM ligado à porta USB, clique no menu suspenso no Arduino IDE e depois em “Select other board and port…”:

Isto abrirá um diálogo onde deve digitar “ESP32-CAM” na barra de pesquisa. Verá a placa “AI Thinker ESP32-CAM” em Boards. Clique nela e na porta COM para ativá-la e depois clique OK:

ESP32-CAM não reconhecido na PORTA COM
Se não conseguir selecionar uma PORTA apesar de ter o ESP32-CAM ligado via programador FTDI, então falta o driver CP210X. Vá a SILICON LABS Software Downloads e faça o download do driver CP210x para o seu sistema operativo, por exemplo, para Windows é “CP210x VCP Windows”:

Isto vai descarregar um ficheiro ZIP. Descompacte e execute o instalador. Depois disso, o seu ESP32-CAM deverá aparecer como ligado a uma porta USB. Se ainda tiver problemas, pode ter que instalar o FTDI Driver para o programador FTDI também.
Modo de Programação
Programar usando o programador FTDI é muito semelhante a usar o Shield de Programação. Comece por colocar o módulo ESP32-CAM em modo de programação, acionando o interruptor (IO0 ligado a GND).
Depois pressione o botão Upload no Arduino IDE:

Espere até que o texto “Connecting …..” apareça no monitor serial:

Depois pressione e solte rapidamente o botão RST no ESP32-CAM.

Os pontos após “Connecting …..” devem parar de aparecer e deverá ver o progresso do upload no Painel de Saída:

O que também parece funcionar é colocar o ESP32-CAM em modo de programação (acionar interruptor), depois pressionar e soltar o botão RST, e então pressionar o botão Upload no Arduino IDE. Isso evita que tenha que “cronometar” a mensagem de conexão.
Modo de Execução
Quando o texto “Hard resetting via RTS pin…” aparecer no Painel de Saída, o upload está completo. Desligue o interruptor para mudar do modo de programação para o modo de execução e pressione o botão RST no ESP32-CAM para iniciar o programa.
Pode usar o mesmo código de teste mostrado acima ou experimentar algo um pouco mais avançado como o código seguinte.
Código de teste para ESP32-CAM com Programador FTDI
Este é essencialmente o mesmo que um programa Blink, mas estamos a aumentar lentamente o brilho do LED de flash em 255 passos, depois desligamos por um segundo e repetimos o ciclo.
int flashPin = 4;
void setup() {
pinMode(flashPin, OUTPUT);
}
void loop() {
for (int b = 0; b < 255; b++) {
analogWrite(flashPin, b);
delay(1);
}
analogWrite(flashPin, 0);
delay(1000);
}
Como a programação continua complicada (mesmo com o programador FTDI), quer testar primeiro a função do cartão SD e da câmara antes de tentar executar um programa mais complexo. Nas duas seções seguintes, mostro como fazer isso.
Testar o Cartão SD
Segundo o datasheet, o ESP32-CAM suporta apenas cartões microSD de 4GB. No entanto, cartões microSD de 8GB e 16GB funcionam geralmente bem. Cartões maiores precisam ser reformatados com FAT32. Use o guiformat.exe software do ridgecrop para formatar cartões SD maiores com FAT32.
O programa de teste seguinte cria um ficheiro de teste, escreve algum texto nele e lê o texto. Se funcionar, o seu cartão SD está a funcionar.
#include "SD_MMC.h"
#include "FS.h"
#include "LittleFS.h"
int flashPin = 4;
void setup() {
Serial.begin(115200);
SD_MMC.begin();
LittleFS.begin(true);
File file = LittleFS.open("/test.txt", FILE_WRITE);
file.print("*** Test successful ***");
file.close();
file = LittleFS.open("/test.txt");
while (file.available()) {
Serial.write(file.read());
}
file.close();
pinMode(flashPin, OUTPUT);
analogWrite(flashPin, 0);
}
void loop() {
}
Aqui uma explicação mais detalhada do código.
Bibliotecas e Inicialização
Começamos por incluir as bibliotecas necessárias para operações com cartão SD e sistema de ficheiros: SD_MMC.h, FS.h, e LittleFS.h.
#include "SD_MMC.h" #include "FS.h" #include "LittleFS.h"
Também definimos uma variável flashPin para representar o pino ligado ao LED de flash.
int flashPin = 4;
Função Setup
Na função setup(), inicializamos a comunicação serial a 115200 baud.
void setup() {
Serial.begin(115200);
Depois inicializamos o cartão SD e o sistema de ficheiros LittleFS. Chamar LittleFS.begin com true garante que o sistema de ficheiros para LittleFS é criado. Normalmente só precisa fazer isso uma vez, mas não faz mal chamar a função assim sempre.
SD_MMC.begin(); LittleFS.begin(true);
Em seguida, criamos um ficheiro chamado test.txt em modo de escrita, escrevemos uma mensagem de teste e fechamos o ficheiro.
File file = LittleFS.open("/test.txt", FILE_WRITE);
file.print("*** Test successful ***");
file.close();
Reabrimos o ficheiro em modo de leitura, lemos o conteúdo carácter a carácter e imprimimos no monitor serial.
file = LittleFS.open("/test.txt");
while (file.available()) {
Serial.write(file.read());
}
file.close();
Finalmente, definimos o flashPin como pino de saída e desligamos o LED de flash definindo o seu valor PWM para 0. Faço isso porque escrever no cartão SD liga automaticamente o LED de flash, o que é irritante. O código de teste funciona bem sem estas duas linhas, mas não gostava do flash nos olhos sempre que corria o teste.
pinMode(flashPin, OUTPUT); analogWrite(flashPin, 0);
Função Loop
A função loop() fica vazia, pois tudo acontece na função setup. Para correr o programa novamente, precisa de resetar o ESP32-CAM.
void loop() {
}
Saída no Monitor Serial
Se tudo funcionar corretamente, verá as seguintes linhas no Monitor Serial. Note a última linha com “*** Test successful ***”, que prova que conseguimos escrever e ler do cartão SD.

Outros Testes
Alternativamente, pode correr o teste mais complexo que vem com a biblioteca ESP32. Tem saída de debug adicional, que pode ser útil. Veja o sketch SDMMC_Test.ino. O mesmo código está disponível nos exemplos do Sketch: File -> Examples -> Examples for AI-Thinker ESP32-CAM -> SDMMC -> SDMMC_Test.
Testar a Câmara
Depois do cartão SD, queremos testar a função da câmara. O código abaixo é uma versão simplificada de programas mais complexos. Cada vez que o ESP32-CAM inicia (botão reset é pressionado), tira uma foto e guarda no cartão SD.
Este código é específico para o modelo AI-Thinker, mas funciona bem na maioria das outras placas de câmara baseadas em ESP32, desde que tenham PSRAM (veja model definitions). Só precisa substituir o pin definitions conforme o seu modelo.
#include "esp_camera.h"
#include "soc/rtc_cntl_reg.h"
#include "SD_MMC.h"
#include "EEPROM.h"
// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
void configCamera() {
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
esp_err_t err = esp_camera_init(&config);
sensor_t* s = esp_camera_sensor_get();
s->set_brightness(s, 0);
s->set_contrast(s, 0);
s->set_saturation(s, 0);
s->set_special_effect(s, 0);
s->set_whitebal(s, 1);
s->set_awb_gain(s, 1);
s->set_wb_mode(s, 0);
s->set_exposure_ctrl(s, 1);
s->set_aec2(s, 0);
s->set_ae_level(s, 0);
s->set_aec_value(s, 300);
s->set_gain_ctrl(s, 1);
s->set_agc_gain(s, 0);
s->set_gainceiling(s, (gainceiling_t)0);
s->set_bpc(s, 0);
s->set_wpc(s, 1);
s->set_raw_gma(s, 1);
s->set_lenc(s, 1);
s->set_hmirror(s, 0);
s->set_vflip(s, 0);
s->set_dcw(s, 1);
s->set_colorbar(s, 0);
}
unsigned int incCounter() {
unsigned int cnt = 0;
int adr = 0;
EEPROM.get(adr, cnt);
EEPROM.put(adr, cnt + 1);
EEPROM.commit();
return cnt;
}
void skipPictures(int n) {
for(int i=0; i<n; i++) {
camera_fb_t* fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
}
}
void takePicture() {
camera_fb_t* fb = esp_camera_fb_get();
unsigned int cnt = incCounter();
String path = "/pic" + String(cnt) + ".jpg";
Serial.println(path);
File file = SD_MMC.open(path.c_str(), FILE_WRITE);
file.write(fb->buf, fb->len);
file.close();
esp_camera_fb_return(fb);
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
SD_MMC.begin();
EEPROM.begin(16);
configCamera();
skipPictures(n);
takePicture();
esp_deep_sleep_start();
}
void loop() {
}
Este código é um bom ponto de partida para casos de uso mais avançados. Por exemplo, tirar uma foto quando um sensor de movimento ou luz é ativado. Ou tirar fotos em intervalos de tempo definidos. A seguir, um pouco mais de detalhe sobre como este código funciona.
Constantes e Bibliotecas
Primeiro, incluímos bibliotecas necessárias como esp_camera.h, soc/rtc_cntl_reg.h, SD_MMC.h, e EEPROM.h. Estas bibliotecas fornecem funcionalidades para configuração da câmara, operações com cartão SD e uso de EEPROM.
#include "esp_camera.h" #include "soc/rtc_cntl_reg.h" #include "SD_MMC.h" #include "EEPROM.h"
Configuração da Câmara
A função configCamera() configura os parâmetros da câmara, como canal LEDC, pinos para linhas de dados (D0-D7), XCLK, PCLK, VSYNC, HREF, SCCB SDA, SCCB SCL, PWDN, RESET, e várias configurações da câmara como formato de pixel, tamanho do frame, qualidade JPEG, etc.
void configCamera() {
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
...
sensor_t* s = esp_camera_sensor_get();
s->set_brightness(s, 0);
s->set_contrast(s, 0);
s->set_saturation(s, 0);
...
}
Como mencionado, dependendo do tipo de câmara, as definições dos pinos podem mudar. Também, se não estiver satisfeito com a qualidade das fotos, há muitos parâmetros que pode ajustar.
Função de Incremento do Contador
A função incCounter() lê e incrementa um valor de contador armazenado na memória EEPROM. Recupera a contagem atual, incrementa, escreve de volta na EEPROM e retorna a contagem anterior.
unsigned int incCounter() {
unsigned int cnt = 0;
int adr = 0;
EEPROM.get(adr, cnt);
EEPROM.put(adr, cnt + 1);
EEPROM.commit();
return cnt;
}
Isto garante que temos um contador contínuo para as fotos tiradas, mesmo após resetar o ESP32-CAM. Este contador é também usado para criar nomes únicos para os ficheiros de imagem que guardamos no cartão SD.
Se quiser resetar o contador, pode carregar e executar o seguinte código:
#include "EEPROM.h"
void setup() {
EEPROM.begin(512);
unsigned int cnt = 0;
int adr = 0;
EEPROM.put(adr, cnt);
EEPROM.commit();
}
void loop() {
}
Ignorar Fotos
A função SkipPictures(n) captura n imagens e descarta-as. A razão para isso é a seguinte: A câmara tem várias funções automáticas integradas, como balanço de branco automático e outras, que demoram algum tempo e vários frames para se ajustarem ao ambiente. Após um reinício do deep sleep, precisamos dar algum tempo para a câmara ajustar.
void skipPictures(int n) {
for(int i=0; i<n; i++) {
camera_fb_t* fb = esp_camera_fb_get();
esp_camera_fb_return(fb);
}
}
Se tirar a primeira imagem logo após o reinício, verá que é de qualidade horrível. Normalmente com um forte tom azul, ou muito escura ou clara. No entanto, ao ignorar os primeiros frames, normalmente (mas não sempre) obtém fotos de boa qualidade.
Também pode tentar desativar o balanço de branco automático e outras funções automáticas nas configurações da câmara, mas aí precisa de um ambiente bastante estável para ajustar as outras configurações. Caso contrário, será difícil obter fotos de boa qualidade.
Captura e Armazenamento de Imagem
A função seguinte captura uma imagem usando a câmara e guarda-a no cartão SD. Recupera um buffer de frame da câmara, incrementa o contador, cria um caminho de ficheiro único para a imagem, escreve os dados da imagem no ficheiro e liberta o buffer.
void takePicture() {
camera_fb_t* fb = esp_camera_fb_get();
unsigned int cnt = incCounter();
String path = "/pic" + String(cnt) + ".jpg";
Serial.println(path);
File file = SD_MMC.open(path.c_str(), FILE_WRITE);
file.write(fb->buf, fb->len);
file.close();
esp_camera_fb_return(fb);
}
Note que usamos a variável contador para criar nomes de ficheiros. Se não gostar dos nomes, este é o local para alterar.
Função Setup
Na função setup(), primeiro desativamos o detector de brownout. O ESP32-CAM é demasiado sensível a erros de brownout se a alimentação fornecer corrente baixa durante o arranque. Chamar WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0) evita este problema.
Depois inicializamos a comunicação serial, iniciamos o cartão SD, iniciamos a EEPROM, configuramos a câmara, capturamos uma imagem e iniciamos o modo deep sleep. Isto significa que, cada vez que o ESP32-CAM inicia ou reseta, uma nova foto é tirada e guardada no cartão SD.
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
SD_MMC.begin();
EEPROM.begin(512);
configCamera();
takePicture();
esp_deep_sleep_start();
}
Função Loop
A função loop() está vazia. Tudo acontece durante o setup e apenas uma foto é tirada, depois a placa entra em deep sleep e espera por um reinício.
Exemplo de Servidor Web da Câmara
Se quiser usar um teste que inclua as capacidades WiFi do ESP32, o exemplo CameraWebServer é bom. Pode encontrá-lo em File -> Examples -> ESP32 -> Camera -> CameraWebServer.

Terá que alterar ligeiramente o código em CameraWebServer.ino. Primeiro, precisa definir o modelo da câmara que está a usar. Certifique-se de que só o modelo que tem está definido!
// =================== // Select camera model // =================== //#define CAMERA_MODEL_WROVER_KIT // Has PSRAM //#define CAMERA_MODEL_ESP_EYE // Has PSRAM ... //#define CAMERA_MODEL_M5STACK_UNITCAM // No PSRAM #define CAMERA_MODEL_AI_THINKER // Has PSRAM //#define CAMERA_MODEL_TTGO_T_JOURNAL // No PSRAM //#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
Depois, tem que inserir as credenciais WiFi da sua rede doméstica:
// =========================== // Enter your WiFi credentials // =========================== const char* ssid = "**********"; const char* password = "**********";
Após carregar o código e resetar o ESP32-CAM, deverá ver o seguinte texto no Monitor Serial:

CameraWebServerNote a última linha, que fornece a URL (endereço IP) onde o WebServer está a correr. Copie e cole esta URL na barra de endereços do seu navegador e deverá ver o seguinte site, que transmite imagens da câmara do seu ESP32-CAM para o navegador.

CameraWebServer no navegador webE é isso! Isto deve dar-lhe informação e exemplos suficientes para começar com o módulo ESP32-CAM.
Se precisar de um código de exemplo mais simples para streaming de vídeo, veja o tutorial Stream Video with ESP32-CAM. E para uma câmara ativada por movimento, veja o tutorial Motion Activated ESP32-CAM.
Resumo
O módulo ESP32-CAM é uma placa agradável e capaz, uma vez que a consiga pôr a funcionar. No entanto, carregar código de forma fiável para o módulo é complicado. Muitas vezes tive que tentar várias vezes e desligar o cabo USB para conseguir. Na verdade, o método mais fiável foi o seguinte:
- Desligar o cabo USB
- Colocar a placa em modo de Programação
- Ligar o cabo USB
- Iniciar o Upload no Arduino IDE
Isto funcionou sempre e não exigia pressionar o botão RST durante o upload. Para depois executar o ESP32-CAM, só tinha que desativar o modo de programação e pressionar reset. Não era necessário desligar o cabo USB para isso. Só para carregar um novo programa tinha que desligar a placa e repetir os passos.
Outro truque que descobri foi que remover o cartão SD ajudava quando a placa se recusava a entrar em modo de programação. Isso acontecia frequentemente quando o código previamente carregado usava o cartão SD.
Se tiver dificuldades com a sua placa ESP32-CAM, abaixo estão alguns links com informações adicionais e uma lista de perguntas frequentes com respostas.
Finalmente, se procura uma placa que não exija um shield de programação separado ou um programador FTDI para flashing, veja a ESP32-WROVER CAM, que também tem mais GPIOs livres, mas é maior. Se precisar de uma placa muito pequena e com microfone, recomendo a XIAO-ESP32-S3-Sense.
Divirta-se a mexer!
Links
Aqui alguns links que achei úteis para começar com o ESP32-CAM:
- Getting Started With ESP32-CAM
- How to Program / Upload Code to ESP32-CAM AI-Thinker (Arduino IDE)
- ESP32-CAM Troubleshooting Guide: Most Common Problems Fixed
- ESP32-CAM AI-Thinker Pinout Guide: GPIOs Usage Explained
- ESP32-CAM Take Photo and Save to MicroSD Card
- Camera Pin Definitions
- Supported Camera Models
Perguntas Frequentes
E aqui estão algumas perguntas comuns e soluções para ajudar a resolver problemas:
P: Por que o meu ESP32-CAM não se conecta ao Wi-Fi?
R: Verifique se inseriu o SSID e a senha corretos no seu código. Certifique-se de que a rede Wi-Fi está dentro do alcance e a funcionar corretamente.
P: Como posso melhorar a qualidade da imagem da câmara?
R: Ajuste as configurações da câmara no seu código para otimizar a qualidade da imagem. Experimente diferentes resoluções e taxas de frames para encontrar a melhor configuração para o seu projeto.
P: Como posso adicionar mais GPIOs?
R: Pode inicializar a interface do cartão SD em modo 1-bit, o que liberta mais dois pinos GPIO. Para mais detalhes, veja o tutorial More GPIO pins for ESP32-CAM.
P: Por que o meu ESP32-CAM não está a capturar imagens nítidas?
R: Condições de iluminação fracas podem afetar a qualidade da imagem. Garanta iluminação adequada para a câmara capturar imagens nítidas. Ajuste as configurações da câmara no seu código para qualidade ótima. Também certifique-se de ter removido a pequena película protetora da lente.

P: O que devo fazer se o meu ESP32-CAM não responde a comandos do monitor serial?
R: Verifique as configurações de comunicação serial no seu código e assegure-se de que a taxa de baud correta (115200) está definida tanto no código quanto no monitor serial. Verifique as ligações entre a placa ESP32-CAM e o computador.
P: Por que o meu cartão SD não está a ser detectado?
R: Certifique-se de que o cartão SD está corretamente inserido no slot e formatado corretamente (FAT32). Verifique o seu código para garantir que a inicialização do cartão SD está correta. Cartões de 4-16GB devem funcionar bem. Cartões de maior capacidade podem causar problemas.
P: O meu ESP32-CAM está a aquecer, isso é normal?
R: É normal que o ESP32-CAM aqueça durante a operação, mas se estiver excessivamente quente, verifique se há curtos-circuitos ou problemas na alimentação.
P: Como posso reduzir o consumo de energia do ESP32-CAM?
R: Desative periféricos desnecessários e otimize o seu código para minimizar o consumo de energia. Considere usar modos de sono para economizar energia quando a placa não estiver em uso.
P: Por que as imagens tiradas pelo ESP32-CAM têm um tom azulado após um reinício?
R: A câmara tem funções automáticas, como balanço de branco automático, que demoram algum tempo para se ajustar ao ambiente. Sem isso, obterá imagens com tom azulado ou muito escuras ou claras. Uma forma fácil de evitar isso é tirar várias fotos após um reinício e ignorá-las. Veja a função skipPictures() no tutorial que faz exatamente isso.
P: Por que o meu ESP32-CAM não está a capturar imagens?
R: Verifique se o módulo da câmara está corretamente ligado à placa ESP32-CAM. Assegure-se de que os pinos da câmara estão corretamente ligados e que o módulo da câmara é suportado pelo seu código.
P: Como posso transmitir vídeo do ESP32-CAM?
R: Implemente um servidor de streaming no ESP32-CAM usando bibliotecas como ESP32-CAM-Webserver para transmitir vídeo via Wi-Fi. Certifique-se de que a sua rede suporta a largura de banda do streaming de vídeo.
P: Por que o meu ESP32-CAM não é reconhecido pelo Arduino IDE?
R: Certifique-se de que instalou o pacote de suporte para placas ESP32 no Arduino IDE. Verifique o cabo USB e as ligações da porta para garantir comunicação correta com a placa ESP32-CAM.
P: Por que o meu ESP32-CAM continua a mostrar “Timed out waiting for packet header” durante o upload do código?
R: Este erro pode ocorrer devido a uma conexão USB lenta ou instável. Tente usar um cabo USB ou porta diferente e assegure-se de que a placa ESP32-CAM está ligada e em modo bootloader antes de carregar o código.
P: O que devo fazer se o meu ESP32-CAM congelar durante o upload do código?
R: Desligue o cabo USB, reinicie a placa ESP32-CAM e tente carregar o código novamente. Certifique-se de que o seu código não está a causar bloqueios ou falhas durante o upload.
P: Posso carregar código para o ESP32-CAM sem fios?
R: Sim, pode carregar código sem fios para o ESP32-CAM usando programação OTA (Over-The-Air). Implemente a funcionalidade OTA no seu código e siga os passos necessários para carregar código sem conexão USB.
P: Por que o meu ESP32-CAM não entra em modo bootloader para carregar código?
R: Verifique a ligação do pino GPIO0 ao GND na placa ESP32-CAM. Assegure-se de que está a pressionar o botão reset no momento certo para entrar em modo bootloader para carregar código.
P: Como posso resolver o erro “A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header” ao carregar código?
R: Este erro pode ocorrer devido a configurações incorretas da taxa de baud ou cabo USB defeituoso. Tente alterar a taxa de baud nas configurações do Arduino IDE ou usar um cabo USB diferente para estabelecer uma conexão estável com a placa ESP32-CAM.
P: Que passos devo seguir se vir o erro “esptool.FatalError: Timed out waiting for packet header” ao carregar código para o ESP32-CAM?
R: Este erro pode indicar um problema de timeout na comunicação entre o computador e a placa ESP32-CAM. Verifique o cabo USB, a porta e as ligações da placa para quaisquer problemas. Reinicie o IDE, reinicie a placa e tente carregar o código novamente.
P: O que devo fazer se vir o erro “Brownout detector was triggered”?
R: Este erro geralmente ocorre se a alimentação do ESP32-CAM for insuficiente e causar uma queda na tensão de alimentação. Um circuito interno do ESP32 detecta este problema, envia esta mensagem de erro para o Monitor Serial e reseta a placa. Deve garantir alimentar o ESP32-CAM pelo pino 5V com uma fonte que forneça 1A de corrente. Para mais informações, veja o nosso tutorial Fix brownout of ESP32-CAM.

