Neste tutorial vais aprender a construir uma ESP32-CAM (AI-Thinker) alimentada por bateria e ativada por movimento, que tira fotografias sempre que um sensor PIR deteta movimento. Vamos discutir vários tipos de sensores PIR, opções de alimentação por bateria e os principais cuidados a ter.
Este projeto é perfeito para aplicações de vigilância, segurança ou até monitorização de vida selvagem. Se preferires gravar vídeos em vez de tirar fotografias individuais, espreita o nosso Surveillance Camera with ESP32-CAM tutorial.
Componentes Necessários
Abaixo encontras os componentes necessários para montar o projeto. Alguns itens como cabo USB, cartão micro SD ou leitor de cartões SD provavelmente já tens. Não precisas de comprar, pois nenhum destes itens é específico para este projeto. No entanto, o cartão SD não deve ser maior que 16 GB; cartões mais pequenos (4GB ou 8GB) funcionam perfeitamente.
Precisas de usar ou o USB-TTL Shield ou o FTDI USB-TTL Adapter, mas não ambos. Embora o módulo ESP32-CAM venha com o USB-TTL Shield, o FTDI USB-TTL Adapter é mais prático para programar.
Listei dois tipos diferentes de sensores de movimento. Se queres algo pequeno, escolhe o AM312. Mas se preferes ativar a câmara apenas à noite, opta pelo HC-SR501, pois pode ser facilmente equipado com um sensor de luz.

ESP32-CAM com USB-TTL Shield

FTDI USB-TTL Adapter

Conjunto de Cabos Dupont

Sensor de Movimento PIR AM312

Sensor de Movimento PIR HC-SR501

Cartão MicroSD 16GB

Leitor de Cartões 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 sobre Sensores de Movimento PIR
Existem vários métodos e sensores para detetar objetos em movimento, mas os sensores mais comuns são os PIR (Passive InfraRed), que detetam o movimento de objetos quentes. Um sensor PIR típico é composto por dois elementos piroelétricos e uma lente de Fresnel.

Elemento piroelétrico
Os elementos piroelétricos detetam calor e estão dispostos e ligados de forma a que um objeto em movimento provoque um sinal de saída positivo ou negativo. No caso de um objeto parado, os sinais dos dois elementos piroelétricos anulam-se mutuamente.
Lente de Fresnel
A lente de Fresnel (a tampa branca) foca a fraca radiação térmica do corpo humano e torna o sensor mais sensível. Podes usar um sensor PIR sem a lente de Fresnel, mas será menos sensível e pode também disparar falsos alarmes com mais frequência.

Nota que os elementos PIR não funcionam atrás de vidro (janelas) e não conseguem detetar objetos com a mesma temperatura do ambiente onde estão a operar. Garante também que montas o sensor na horizontal para máxima sensibilidade. Para mais informações vê here .
Tempo de Calibração
Os sensores PIR normalmente têm um tempo de calibração durante o qual se ajustam à temperatura ambiente. Isto pode demorar até 15 segundos e durante esse tempo podem ocorrer falsos disparos. Evita movimentos durante o tempo de calibração para permitir que o sensor se ajuste.
Tempo de Bloqueio e de Atraso
Além disso, os sensores PIR têm um tempo de bloqueio e um tempo de atraso. O tempo de bloqueio é o período durante o qual o sensor ignora qualquer movimento detetado após um disparo inicial. Isto evita que o sensor fique constantemente ativado por movimentos contínuos, permitindo-lhe reiniciar e evitar falsos alarmes. Por exemplo, se o tempo de bloqueio for de 30 segundos, o sensor não detetará nenhum movimento durante esse período, mesmo que haja movimento no seu alcance.
O tempo de atraso, por outro lado, é o intervalo que o sensor espera antes de acionar uma ação após detetar movimento. Este atraso pode ser útil em cenários onde não queres respostas imediatas ao movimento, como acender uma luz apenas após alguns segundos de movimento contínuo. Por exemplo, se o tempo de atraso for de 10 segundos, o sensor aguardará esse tempo antes de ativar um dispositivo ou enviar um sinal.
Alguns módulos de sensores PIR permitem ajustar o tempo de bloqueio e de atraso, enquanto outros têm tempos fixos. Existem muitos módulos de sensores PIR, mas a maioria baseia-se no sensor PIR AS312, que vamos discutir na próxima secção.
Sensor PIR AS312
A imagem abaixo mostra um sensor PIR AS312 e uma vista superior com a localização dos dois elementos piroelétricos assinalada. A lente de Fresnel foi removida.

O AS312 funciona entre 2.7-3.3V e consome apenas uma quantidade mínima de corrente (15 μA). Tem um tempo de bloqueio e de atraso de 2,3 segundos.
O AS312 já integra muitos componentes eletrónicos (ver Datasheet ), mas é necessário fornecer uma tensão de alimentação estável (regulador de tensão) e mais alguns componentes para o usar num circuito. Por isso, existem vários módulos de sensores PIR que incluem estes componentes necessários e oferecem funcionalidades adicionais, como tempos de atraso superiores a 2,3 segundos e controlo de sensibilidade.
Vamos analisar três módulos de sensores PIR comuns na próxima secção e darei algumas recomendações sobre quais usar com a ESP32-CAM.
Módulo PIR AM312
O módulo PIR AM312 é o mais simples e pequeno dos três módulos discutidos aqui. Tem um tempo de atraso e de bloqueio fixo de cerca de 2 segundos e não permite ajuste de sensibilidade.

É composto apenas pelo sensor PIR AS312, um HT7530 regulador de tensão e uma resistência de saída (20K). Vê o esquema interno abaixo. Nota que não existe proteção de polaridade e é fácil danificar o sensor se ligares os fios ao contrário.

O módulo tem três pinos: alimentação positiva (VIN), terra (GND) e saída (OUT). A tensão de alimentação vai de 2.7 a 12 Volts e a saída é de 3.3 Volts (TTL) quando é detetado movimento. O alcance de deteção é de 3-5m e o ângulo de deteção é de cerca de 100°.
Este é o sensor que recomendo para a ESP32-CAM ativada por movimento. Tem um tempo de atraso e bloqueio curto (2 seg), o que permite tirar fotos a cada 4 segundos. Pode ser ligado diretamente a um pino GPIO da ESP32-CAM e funcionou de forma fiável nos meus testes.
No entanto, se precisares de maior alcance e deteção apenas à noite, recomendo o HC-SR501, que vamos abordar na próxima secção.
Módulo PIR HC-SR501
O HC-SR501 é o sensor de movimento mais comum em aplicações domésticas, como luzes noturnas ativadas por movimento. É fácil de reconhecer pela sua lente de Fresnel grande e distinta. Vê a imagem abaixo.

Esquema do HC-SR501
O esquema do módulo HC-SR501 é muito mais complexo do que o do AM312, pois permite ajustar a sensibilidade (alcance) e o tempo de atraso. Também utiliza o RE200B PIR Sensor em vez do sensor PIR AS312. Vê o esquema abaixo.

O tempo de atraso pode ser ajustado de cerca de 0,3 segundos até 300 segundos. Continua a existir um tempo de bloqueio de cerca de 2,5 segundos. No entanto, o HC-SR501 permite tirar fotos um pouco mais frequentemente do que o AS312 (3 segundos vs 4 segundos).
Ajustar o tempo de atraso mínimo
Como vamos controlar o tempo de atraso por software, queremos definir o tempo de atraso do hardware no mínimo. Para isso, roda o potenciómetro da esquerda no sentido contrário ao dos ponteiros do relógio até à posição mostrada abaixo. Assim obténs o tempo de atraso mínimo de cerca de 0,3 segundos.

É melhor testares isto sem as complicações da ESP32-CAM primeiro. Vê o nosso tutorial How to use HC-SR501 PIR Motion Sensor . Tem um circuito de exemplo simples que mostra como acender um LED quando o sensor deteta movimento.
O outro potenciómetro do módulo HC-SR501 permite alterar o alcance de deteção (sensibilidade) de cerca de 2m até um máximo de cerca de 7m. O HC-SR501 também tem um jumper para mudar o modo de disparo. Para mais detalhes vê o nosso tutorial How to use HC-SR501 PIR Motion Sensor ou o DHC-SR501 Datasheet .
Ativação noturna com LDR
A maior vantagem (além do maior alcance) do HC-SR501 em relação ao AM312 é que podes adicionar um LDR (detetor de luz) para que o sensor só fique ativo durante a noite. Isto é ótimo para monitorizar animais noturnos. Esta função não está bem documentada, mas se retirares a lente de Fresnel vês dois furos marcados RL e RT. Vê a imagem abaixo.

A porta RT (Resistor Temperature) permite adicionar um termístor, tornando o HC-SR501 menos suscetível a falsos alarmes devido a variações de temperatura ambiente. A porta RL (Resistor Light) permite adicionar um LDR (resistência dependente da luz), garantindo que a deteção de movimento só é ativada à noite.
O valor específico da resistência a usar para o LDR vai depender de quão escuro queres que esteja para o sensor ficar ativo. Terás de descobrir isto por tentativa e erro. Aqui está um link para um LDR kit with various resistor values que podes experimentar. Como referência, o divisor de tensão no esquema acima tem uma resistência de 1MΩ. Provavelmente deves começar com um LDR de valor semelhante.
Entradas e Saídas
As entradas e saídas do HC-SR501 são basicamente as mesmas do AM312. Deves fornecer uma tensão de alimentação nos pinos VCC e GND entre 4.5V e 20V, e o pino de saída ficará em nível alto (3.3V) quando for detetado movimento. A imagem abaixo mostra o pinout.

Resumindo, se queres um sensor PIR pequeno, simples e barato, escolhe o AM312. Vai funcionar bem. Se preferes maior alcance e limitar a deteção de movimento ao período noturno, opta pelo HC-SR501.
Na próxima secção, vou falar rapidamente do HC-SR505, que se revelou inadequado e não pode ser recomendado para esta aplicação.
Módulo PIR HC-SR505
O módulo PIR HC-SR505 é um pouco maior que o AM312, mas usa o mesmo sensor PIR AS312 e tem o mesmo pinout. Vê a imagem abaixo do HC-SR505

A tensão de alimentação vai de 4.5V até 20V e a saída é TTL (3.3V). O alcance de deteção é de 3-4 metros (9-12 pés) e o tempo de atraso é fixo em cerca de 8 segundos.
Além do tempo de atraso longo, que te limita a tirar uma foto apenas a cada 8 segundos, também reparei que o sensor teve problemas quando usei um power bank de 5V como alimentação. Testei vários sensores do mesmo tipo (HC-SR505), mas a saída ficava sempre em nível alto. Suspeito que o ripple de alta frequência do power bank causou isto (mais sobre isso adiante).
O HC-SR501 e o AM312 não tiveram problemas com este power bank, mas o módulo PIR HC-SR505 recusou-se a funcionar corretamente. Além disso, mesmo ao alimentar a ESP32-CAM com uma pilha de 9V ou um par de baterias 18650, que não têm ripple, o HC-SR505 não funcionou e estava sempre ativo. Novamente, suspeito que alguma interferência de alta frequência da ESP32-CAM com o HC-SR505 seja a causa.
Resumindo, o módulo PIR HC-SR505 não é adequado para tirar fotos ativadas por movimento com a ESP32-CAM. No entanto, já usei o módulo PIR HC-SR505 noutros projetos e não tive problemas nessas aplicações.
De seguida, vamos dar uma vista de olhos rápida ao próprio módulo ESP32-CAM.
Apresentando a AI-Thinker ESP32-CAM
A placa de desenvolvimento AI-Thinker ESP32-CAM é um módulo compacto que combina um chip ESP32-S, uma câmara, um flash integrado e um slot para cartão microSD. A placa tem Wi-Fi e Bluetooth integrados e suporta uma câmara OV2640 ou OV7670 com resolução até 2 megapixels.


Podes encontrar mais detalhes sobre a placa ESP32-CAM e especialmente como carregar código nela no nosso tutorial Programming the ESP32-CAM . Lê-o com atenção, pois carregar código na ESP32-CAM pode ser complicado. Neste tutorial, vou focar-me em implementar a ativação por movimento para a ESP32-CAM.
Pinout
A imagem seguinte mostra o pinout da ESP32-CAM. Nota que, em teoria, a ESP32-CAM pode funcionar a 3.3V, mas já foram reportados comportamentos instáveis. Podes usar de 5V até 15V, mas não recomendo ir além dos 12V, pois o regulador de tensão pode aquecer bastante. Vamos falar sobre diferentes opções de alimentação mais à frente.

O pino P_OUT está identificado 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 podes usar este pino para alimentar a placa! Usa o pino de 5V para isso.
Nota que a maioria dos pinos GPIO da ESP32-CAM já estão a ser usados pela câmara e pelo leitor de cartões SD. Além disso, deves evitar usar os GPIO1, GPIO3 e GPIO0, pois são necessários para programar a placa. O GPIO0 está ainda ligado ao pino XCLK da câmara e deve ficar desligado (não ligado) quando a ESP32 está a funcionar.
Finalmente, os pinos GPIO 2, 4, 12, 13, 14 e 15 são parcialmente usados pelo leitor de cartões SD. Se não usares o leitor de cartões SD, ficam disponíveis como GPIO. Para mais detalhes vê o nosso tutorial More GPIO pins for ESP32-CAM .
A seguir, vamos usar o GPIO 13 para ativação por movimento. Os detalhes de como ligar um sensor de movimento PIR à ESP32-CAM estão descritos na próxima secção.
Ligar o Sensor PIR à ESP32-CAM
Ligar o módulo PIR AM312 à ESP32-CAM é muito simples. Liga uma fonte de alimentação (bateria) de 5V (até 12V) ao pino de 5V e GND da ESP32-CAM como mostrado abaixo (fios vermelho e azul).

Podes fornecer mais de 5V para alimentar a placa. Não te preocupes. Um regulador de tensão está ligado ao pino de 5V e reduz a tensão de entrada para o nível necessário à placa. Mas não ultrapasses os 12V, senão o regulador pode aquecer demasiado.
De forma semelhante, liga o módulo PIR AM312 à fonte de alimentação (fios vermelho e azul). Atenção à polaridade correta, pois o AM312 não tem proteção de polaridade. Depois liga a saída S ou OUT do AM312 ao pino GPIO13 da ESP32-CAM (fio verde).
Não ligues o AM312 ao pino de 3.3V para alimentação. Em teoria deveria funcionar, mas na prática tive problemas de deteção ao usar o AM312 a 3.3V. Se usares o módulo PIR HC-SR501 em vez do AM312, liga-o da mesma forma (GND a GND, VCC a 5V-12V).
Tanto o AM312 como o HC-SR501 suportam tensões de alimentação de 5V a 12V. Assim, podes alimentar a ESP32-CAM e o sensor PIR com a mesma fonte de alimentação (bateria) e a mesma tensão.
Circuito com Transístor
Nota que existem muitos tutoriais que sugerem um circuito com transístor ao ligar um sensor PIR à ESP32-CAM. Isto NÃO é necessário! A saída do sensor PIR (mesmo a do AM312) consegue acionar facilmente a entrada GPIO. Ligar o sensor PIR diretamente à ESP32-CAM é suficiente, funciona bem e é muito mais simples.
Carregar o Código
Se usares um FTDI USB-TTL Adapter podes manter o sensor PIR ligado enquanto carregas e testas o código. Se usares o USB-TTL Shield, tens de desligar e voltar a ligar o sensor PIR sempre que carregas e executas novo código, o que é bastante incómodo.
Por isso recomendo vivamente usar um FTDI USB-TTL Adapter. A imagem abaixo mostra como fica. Nem precisas de breadboard; só alguns fios Dupont.

Detalhes sobre como ligar o FTDI USB-TTL Adapter e como carregar código podem ser encontrados no nosso tutorial Programming the ESP32-CAM . Na próxima secção, mostro-te o código para a ESP32-CAM ativada por movimento.
Código para a ESP32-CAM Ativada por Movimento
O código seguinte é uma implementação completa para uma ESP32-CAM ativada por movimento. Dá uma vista de olhos rápida para teres uma ideia geral antes de entrares nos detalhes.
Este código coloca a ESP32-CAM em deep sleep e espera por um sinal no GPIO13, indicando que o sensor PIR detetou movimento. Se for detetado movimento, a ESP32-CAM acorda, tira uma foto, guarda-a com um número sequencial no cartão SD e volta a dormir.
// Motion activated ES32-CAM
// Makerguides.com
#include "esp_camera.h"
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.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;
EEPROM.get(0, cnt);
EEPROM.put(0, cnt + 1);
EEPROM.commit();
return cnt;
}
void enableFlash(bool enable) {
if (enable) {
rtc_gpio_hold_dis(GPIO_NUM_4);
} else {
pinMode(GPIO_NUM_4, OUTPUT);
digitalWrite(GPIO_NUM_4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);
}
}
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 = "/img" + String(cnt) + ".jpg";
File file = SD_MMC.open(path.c_str(), FILE_WRITE);
file.write(fb->buf, fb->len);
file.close();
esp_camera_fb_return(fb);
}
void deepSleep(int atleast) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
rtc_gpio_pulldown_en(GPIO_NUM_13);
rtc_gpio_pullup_dis(GPIO_NUM_13);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1);
delay(atleast);
esp_deep_sleep_start();
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
enableFlash(true);
EEPROM.begin(8);
SD_MMC.begin();
configCamera();
skipPictures(10);
takePicture();
enableFlash(false);
deepSleep(5000);
}
void loop() {
}
Vamos analisar o código passo a passo para perceber como funciona a configuração da câmara e o processo de captura de imagem.
Includes
Na primeira parte incluímos as bibliotecas necessárias para controlar a câmara, o cartão SD e a EEPROM. Também precisamos de algumas funções relacionadas com o relógio em tempo real (rtc) durante o deep sleep.
#include "esp_camera.h" #include "soc/rtc_cntl_reg.h" #include "driver/rtc_io.h" #include "SD_MMC.h" #include "EEPROM.h"
Constantes
Existem vários modelos de placas ESP32-CAM. Este código é específico para o modelo AI-Thinker, mas funcionará bem na maioria das outras placas de câmara baseadas em ESP32, desde que tenham PSRAM (ver model definitions ).
Na secção de código abaixo definimos os pinos específicos para o modelo AI-Thinker. Se tiveres uma placa ESP32-CAM diferente, provavelmente terás de alterar estas constantes. Aqui podes encontrar o pin definitions para outros modelos suportados.
// 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
Configuração da Câmara
A função configCamera() configura a câmara com definições específicas como canal LEDC, pinos para linhas de dados, sinais de clock e definições de imagem como formato, tamanho e qualidade. Se não estiveres satisfeito com a qualidade da imagem da ESP32-CAM, experimenta alterar estas definições.
void configCamera() {
sensor_t* s = esp_camera_sensor_get();
s->set_brightness(s, 0);
s->set_contrast(s, 0);
...
s->set_colorbar(s, 0);
}
Incrementar Contador
A função incCounter() lê e incrementa um contador guardado na memória EEPROM. Este contador é usado para nomear as imagens capturadas de forma sequencial. Sempre que é chamada, incrementa o contador. O contador precisa de ser guardado em EEPROM para não ser esquecido durante o deep sleep.
unsigned int incCounter() {
unsigned int cnt = 0;
EEPROM.get(0, cnt);
EEPROM.put(0, cnt + 1);
EEPROM.commit();
return cnt;
}
O código é muito simples. Primeiro obtemos o valor atual do contador via EEPROM.get(0, cnt) , depois guardamos o contador incrementado via EEPROM.put(0, cnt + 1 ) e confirmamos a alteração usando EEPROM.commit() . Por fim, devolvemos o valor atual do contador. Da próxima vez que a função for chamada, devolve o contador incrementado que acabámos de guardar e incrementa novamente.
Nota que precisamos de reservar memória para o contador na EEPROM. Isto é feito mais à frente, ao chamar EEPROM.begin(8) na função setup(). O contador é um valor inteiro sem sinal de 4 bytes. Mas, por precaução, reservo 8 bytes de memória – só para garantir.
Um contador inteiro sem sinal de 4 bytes pode contar até 4.294.967.295 antes de dar overflow. É mais do que suficiente para enumerar todas as imagens que queremos guardar no cartão SD.
Controlo do Flash
A função enableFlash() ativa ou desativa o flash com base no parâmetro enable . Este é um ponto delicado. O problema com a ESP32-CAM é que ao escrever no cartão SD o LED do flash acende e, ao entrar em deep sleep, o LED do flash fica ligado.
Basta escrever LOW para GPIO4 , onde o LED do flash está ligado, não é suficiente. Durante o deep sleep este nível de saída perde-se. Para manter GPIO4 baixo durante o deep sleep, é preciso chamar rtc_gpio_hold_en(GPIO_NUM_4) , que diz à ESP32 para preservar o estado do GPIO4 durante o deep sleep.
Mas GPIO4 também faz parte da interface do cartão SD, por isso não podemos mantê-lo fixo nesse nível. Assim, quando a ESP32-CAM acorda, desativamos o hold de GPIO4 via rtc_gpio_hold_dis(GPIO_NUM_4) e deixamos a ESP32-CAM controlá-lo para tirar a foto e gravar no cartão SD.
Antes de colocar a ESP32-CAM em deep sleep, chamamos enableFlash(false) , que desliga o LED do flash e preserva esse estado durante o deep sleep.
void enableFlash(bool enable) {
if (enable) {
rtc_gpio_hold_dis(GPIO_NUM_4);
} else {
pinMode(GPIO_NUM_4, OUTPUT);
digitalWrite(GPIO_NUM_4, LOW);
rtc_gpio_hold_en(GPIO_NUM_4);
}
}
Ignorar Imagens
A função SkipPictures(n) captura n imagens e descarta-as. O motivo é o seguinte: a câmara tem várias funções automáticas integradas, como balanço de brancos automático e outras, que precisam de algum tempo e vários frames para se ajustarem ao ambiente. Após um reinício vindo do deep sleep, é preciso dar algum tempo à câmara para se 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 tirares a primeira imagem após um reinício, vais ver que a qualidade é péssima. Normalmente com um tom azul forte, ou demasiado escura ou clara. No entanto, ao ignorar os primeiros frames, normalmente (mas não sempre) consegues fotos de boa qualidade.
Também podes tentar desativar o balanço de brancos automático e outras funções automáticas nas definições da câmara, mas aí precisas de um ambiente bastante estável para conseguires afinar as restantes definições. Caso contrário, será difícil obter fotos de boa qualidade.
Tirar Fotografia
A função takePicture() captura uma imagem usando a câmara e guarda-a no cartão SD com um nome de ficheiro único baseado no valor do contador. Primeiro criamos um frame buffer fb chamando esp_camera_fb_get() . Depois criamos um nome de ficheiro único para a foto e guardamos o frame buffer como ficheiro JPG (img{cnt}.jpg) no cartão SD. Por fim, libertamos a memória usada pelo frame buffer chamando esp_camera_fb_return .
void takePicture() {
camera_fb_t* fb = esp_camera_fb_get();
unsigned int cnt = incCounter();
String path = "/img" + String(cnt) + ".jpg";
File file = SD_MMC.open(path.c_str(), FILE_WRITE);
file.write(fb->buf, fb->len);
file.close();
esp_camera_fb_return(fb);
}
Deep Sleep
A função deepSleep() coloca a ESP32 em modo deep sleep durante um período especificado após capturar a imagem. Isto ajuda a poupar energia quando o dispositivo não está a tirar fotos.
As três primeiras linhas ( esp_sleep_pd_config, rtc_gpio_pulldown_en, rtc_gpio_pullup_dis ) ativam o resistor pull-down interno em GPIO13 , onde o sensor PIR está ligado. O código/ESP32-CAM funciona sem usar o pull-down interno, mas preferi jogar pelo seguro aqui.
Depois disso, especificamos a fonte de wakeup via esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1) . Isto significa que se GPIO13 ficar em nível alto (1 = sensor PIR detetou movimento), acordamos a ESP32-CAM do deep sleep.
Antes de entrar em deep sleep, delay durante alguns segundos ( atleast ) para evitar tirar fotos demasiado frequentemente. Usei 5 segundos e não podes ir muito mais rápido devido aos tempos de bloqueio e atraso do próprio sensor PIR. Mas podes certamente aumentar o tempo, por exemplo para 10 ou 60 segundos, se quiseres reduzir o número de fotos tiradas.
void deepSleep(int atleast) {
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
rtc_gpio_pulldown_en(GPIO_NUM_13);
rtc_gpio_pullup_dis(GPIO_NUM_13);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1);
delay(atleast);
esp_deep_sleep_start();
}
Função Setup
A função setup() é onde juntamos tudo. Primeiro chamamos WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0) para desativar a deteção de brownout . Se a alimentação for instável ou a ESP32-CAM consumir demasiada energia ao iniciar o WiFi ou ao gravar no cartão SD, isto evita um reset automático. Para mais detalhes vê este post no blog Disabling the ESP32 Brownout detector .
Não tive problemas com brownouts e provavelmente podes desativar esta linha. A vantagem da deteção de brownout é reduzir o risco de corrupção do cartão SD. A desvantagem é que pode causar resets acidentais e indesejados. Vai depender da fonte de alimentação que usares qual das opções é preferível.
Depois de especificar o tratamento de brownout, ativamos o LED do flash via enableFlash(true) . Depois reservamos 8 bytes de memória na EEPROM para guardar o contador de ficheiros, iniciamos o cartão SD com SD_MMC.begin() e configuramos a câmara.
Por fim, tiramos a fotografia chamando a nossa função takePicture() . Depois disso, desligamos o LED do flash e colocamos a ESP32-CAM em deep sleep (pelo menos 5 segundos).
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
enableFlash(true);
EEPROM.begin(8);
SD_MMC.begin();
configCamera();
takePicture();
enableFlash(false);
deepSleep(5000);
}
Função Loop
A função loop() está vazia neste código, pois a funcionalidade principal está implementada na função setup. A ESP32 executa a função setup, entra em deep sleep, acorda quando é detetado movimento e executa novamente a função setup. A função loop nunca é executada.
void loop() {
// Empty loop
}
E é isto. Aqui tens o código completo para uma ESP32-CAM ativada por movimento.
Quando executares o código podes reparar que o contador de ficheiros começa num número arbitrário e não em zero. Podes repor o contador de ficheiros executando o código seguinte.
Repor Contador de Ficheiros
Carregar e executar o código abaixo vai repor o contador de ficheiros guardado no endereço 0 para zero. Depois disso, terás de carregar novamente o código original para tirar fotos.
#include "EEPROM.h"
void setup() {
EEPROM.begin(512);
unsigned int cnt = 0;
EEPROM.put(0, cnt);
EEPROM.commit();
}
void loop() {
}
Já temos o código e as ligações, agora vamos falar sobre a alimentação do projeto.
ESP32-CAM Alimentada por Bateria
Muitas vezes vais querer usar uma ESP32-CAM ativada por movimento alimentada por bateria, por exemplo para monitorização de vida selvagem. Encontrar a fonte de alimentação certa pode ser complicado. A seguir mostro-te quatro opções que explorei.
Power Bank USB 1
A primeira opção é usar um power bank USB comum. Toda a gente com telemóvel costuma ter um destes para recarregar o telefone em caso de emergência. Experimentei usar o power bank mostrado abaixo.

Este é um power bank de 30000mAh que poderia alimentar a ESP32-CAM durante muito tempo. No entanto, este power bank tem desligamento automático. Se a carga na saída for muito baixa (<80mA), o que acontece quando a ESP32-CAM entra em deep sleep (6mA), o power bank desliga-se.
A ESP32-CAM funcionava durante 10-15 segundos, tirava algumas fotos e depois nunca mais acordava. Demorei algum tempo a perceber isto, a pensar que o problema era do código ou das ligações : (
Lição aprendida: não uses um power bank com desligamento automático para isto.
Power Bank USB 2
Tinha um segundo power bank, mostrado abaixo, com capacidade de 10000mAh.

Este power bank não desliga automaticamente, mas ao testá-lo com o módulo PIR HC-SR505, não consegui que o sensor funcionasse corretamente. A saída do sensor estava sempre em nível alto, mesmo usando o módulo isoladamente (sem a ESP32-CAM), apenas com a alimentação e um LED.
Uma análise ao osciloscópio revelou que este power bank tem ripple visível na saída de 5V, com frequência de cerca de 160Hz e amplitude de 88mV. Suspeito que isto tenha causado os problemas com o módulo PIR HC-SR505, pois este sensor funcionou bem quando alimentado por uma pilha de 9V, por exemplo.

Enquanto a ESP32-CAM com um sensor PIR HC-SR501 ou AM312 funcionou bem com este power bank nos testes, fiquei um pouco preocupado com o comportamento a longo prazo. Será que este setup seria fiável durante vários dias ou semanas?

Acho que a melhor opção é usar uma bateria recarregável simples, sem o circuito interno de boost de um power bank USB, que causa estes ripples na tensão de saída.
Bateria Recarregável de 9V
Para alimentar a ESP32-CAM e o sensor PIR podes simplesmente usar uma pilha de 9V. Aqui usei uma pilha recarregável de 9V com 1000mAh de capacidade, ligada diretamente ao pino de 5V da ESP32-CAM e ao pino VCC do sensor PIR.

Este setup funciona bem, mas o tempo de funcionamento não é grande coisa. Embora a ESP32-CAM consuma apenas 6mA em deep sleep, pode chegar aos 240mA quando está ativa. Assim, em deep sleep poderíamos funcionar durante 1000mAh / 6mA = 166,67 horas = 6,9 dias, o que não é mau. Mas se tirares fotos frequentemente, o tempo de funcionamento será muito menor.
O pequeno vídeo abaixo mostra que a ESP32-CAM com um módulo PIR AM312 pode funcionar com uma pilha de 9V.

Na próxima secção vamos ver baterias com maior capacidade para aumentar o tempo de funcionamento.
Pack de Baterias Recarregáveis 18650
Vamos então usar uma bateria maior. Uma bateria de lítio 18650 tem uma tensão nominal de 3,6V e capacidades entre 1800mAh e 2800mAh. Duas em série dão-nos 7,2V de saída, o que é confortável para a ESP32-CAM e permite alguma margem para queda de tensão.

Com uma capacidade de 2800mAh podemos quase triplicar o tempo de funcionamento (comparado com a bateria de 1000mAh). E se precisares de mais capacidade, basta adicionar outro par de 18650 e duplicar novamente o tempo de funcionamento.
Mas atenção à polaridade correta ao ligar as baterias ao circuito. As 18650 podem fornecer correntes elevadas e já queimei um dos meus sensores PIR por causa deste erro.
Na secção seguinte, tento fazer uma estimativa mais precisa do tempo de funcionamento.
Tempo de Funcionamento
Medi o consumo em deep sleep da ESP32-CAM com o sensor PIR AM312 ligado e obtive 3,6mA em deep sleep. Quando ativa e a tirar uma foto, o consumo era cerca de 120mA.
Se usarmos as duas baterias de lítio 18650 em série com capacidade total de 2800mAh, obtemos 2800mAh / 3,6mA = 777,7 horas = 32 dias de funcionamento em deep sleep. Ou seja, um mês de funcionamento, mas sem tirar fotos.
Vamos assumir que tiramos 100 fotos durante todo o período. Como a ESP32-CAM está ativa durante 5 segundos cada vez que tira uma foto, isto dá 100 * 120mA * 5seg / 60 = 1000mAh.
Se subtrairmos esses 1000mAh à capacidade da bateria de 2800mAh, ficamos com um tempo de funcionamento de 1800mA / 3,6mA = 500 horas = 20 dias. Quase três semanas; acho que é razoável. Claro que temperaturas baixas ou tirar mais fotos podem reduzir bastante o tempo de funcionamento. Por outro lado, adicionar outro par de baterias 18650 é fácil e duplica o tempo de funcionamento.
Conclusões
Neste tutorial, construímos com sucesso uma ESP32-CAM ativada por movimento usando um sensor PIR. Aprendemos os conceitos básicos dos sensores de movimento PIR e como ligá-los à ESP32-CAM AI-Thinker. Ao implementar o código fornecido, a nossa ESP32-CAM pode agora capturar imagens sempre que for detetado movimento.
Se quiseres ser notificado sempre que for detetado movimento, vê o nosso ESP32 send Telegram Message tutorial, que te ensina a enviar mensagens para a app Telegram no teu telemóvel.
Além disso, explorámos a possibilidade de alimentar a ESP32-CAM com baterias para uma solução mais portátil. Este projeto abre várias possibilidades para criar sistemas de vigilância, câmaras de vida selvagem ou qualquer aplicação que precise de deteção de movimento e captura de imagens.
As lições mais importantes são que é preciso ter cuidado na escolha dos sensores PIR e da solução de alimentação por bateria. Pôr a ESP32-CAM a funcionar não é fácil, mas o resultado final compensa.
Boas experiências!
Perguntas Frequentes
Aqui estão algumas perguntas e soluções comuns para te ajudar a resolver problemas:
P: Existem sensores PIR específicos que funcionam melhor com a ESP32-CAM?
R: Sim, usa o HC-SR501 ou o AM312. Não uses o HC-SR505, pois o tempo de atraso é demasiado longo e tem problemas quando ligado à ESP32-AM
P: Como posso ajustar a sensibilidade do sensor PIR?
R: Alguns sensores PIR (incluindo o HC-SR501) vêm com um potenciómetro que permite ajustar a sensibilidade. Ao rodar o potenciómetro, podes afinar o alcance de deteção do sensor.
P: Posso alimentar a ESP32-CAM com uma bateria?
R: Sim, podes alimentar a ESP32-CAM com uma bateria para uma solução portátil. Se usares um power bank USB, certifica-te de que não tem desligamento automático. A melhor opção é um par de baterias 18650 ou qualquer outra solução de bateria que forneça energia estável entre 5V e 12V.
P: Preciso de modificar o código para diferentes sensores PIR?
R: O código fornecido neste tutorial é genérico e deve funcionar com a maioria dos sensores PIR.
P: Qual é o alcance de um sensor PIR para detetar movimento?
R: Vai depender do sensor específico e das condições do ambiente, mas normalmente ronda os 5 metros. Nota que não podes colocar o detetor atrás de vidro e ele não deteta objetos com a mesma temperatura do ambiente.
P: Existem sensores de movimento alternativos que possam ser usados com a ESP32-CAM?
R: Sim, existem outros sensores de movimento como sensores ultrassónicos, barreiras fotoelétricas ou sensores de micro-ondas que também podem ser usados com a ESP32-CAM.
P: Como posso aumentar a autonomia da bateria da ESP32-CAM?
R: Para aumentar a autonomia da ESP32-CAM, podes aumentar o tempo de atraso. Além disso, usar uma bateria de maior capacidade prolonga a duração da bateria.
P: Porque é que as imagens tiradas pela ESP32-CAM ficam com um tom azul após um reinício?
R: A câmara tem funções automáticas, como o balanço de brancos automático, que precisam de algum tempo para se ajustar ao ambiente. Sem isso, vais obter fotos azuladas ou demasiado escuras ou claras. Uma forma simples de evitar isto é tirar várias fotos após um reinício e ignorá-las. Vê a função skipPictures() no tutorial, que faz exatamente isso.

