Para isso terei que aprender várias coisas, portanto vamos por "baby steps"... Neste primeiro passo vamos ver como tirar uma foto e conseguir analisá-la.
![]() |
A ideia de um auto-retrato parecia boa, mas a execução deixou a desejar |
As rotinas para interagir com a câmera estão em https://github.com/espressif/esp32-camera. Para começar a brincar precisamos:
- Preparar a configuração da câmera
- Iniciar a câmera através da função esp_camera_init()
- Solicitar a foto através da função esp_camera_fb_get()
- Processar a foto
- Devolver a área de memória recebida de esp_camera_fb_get() através de esp_camera_fb_return()
O programa abaixo faz isso. A configuração foi feita mesclando o exemplo no link acima como um exemplo do Randon Nerd Tutorials. O tamanho da foto está como VGA (640x480), porque o tamanho UXVGA (1600x1200) estourou a memória no próximo passo. O "processamento" é só mostrar o tempo para tirar a foto e o tamanho do arquivo.
- #include "esp_camera.h"
- // Conexões da ESP32-CAM
- #define CAM_PIN_PWDN 32
- #define CAM_PIN_RESET -1 // usar software reset
- #define CAM_PIN_XCLK 0
- #define CAM_PIN_SIOD 26
- #define CAM_PIN_SIOC 27
- #define CAM_PIN_D7 35
- #define CAM_PIN_D6 34
- #define CAM_PIN_D5 39
- #define CAM_PIN_D4 36
- #define CAM_PIN_D3 21
- #define CAM_PIN_D2 19
- #define CAM_PIN_D1 18
- #define CAM_PIN_D0 5
- #define CAM_PIN_VSYNC 25
- #define CAM_PIN_HREF 23
- #define CAM_PIN_PCLK 22
- // Configuração da camera
- static camera_config_t camera_config = {
- .pin_pwdn = CAM_PIN_PWDN,
- .pin_reset = CAM_PIN_RESET,
- .pin_xclk = CAM_PIN_XCLK,
- .pin_sscb_sda = CAM_PIN_SIOD,
- .pin_sscb_scl = CAM_PIN_SIOC,
- .pin_d7 = CAM_PIN_D7,
- .pin_d6 = CAM_PIN_D6,
- .pin_d5 = CAM_PIN_D5,
- .pin_d4 = CAM_PIN_D4,
- .pin_d3 = CAM_PIN_D3,
- .pin_d2 = CAM_PIN_D2,
- .pin_d1 = CAM_PIN_D1,
- .pin_d0 = CAM_PIN_D0,
- .pin_vsync = CAM_PIN_VSYNC,
- .pin_href = CAM_PIN_HREF,
- .pin_pclk = CAM_PIN_PCLK,
- .xclk_freq_hz = 20000000,
- .ledc_timer = LEDC_TIMER_0,
- .ledc_channel = LEDC_CHANNEL_0,
- .pixel_format = PIXFORMAT_JPEG,
- .frame_size = FRAMESIZE_VGA,
- .jpeg_quality = 12, //0-63 lower number means higher quality
- .fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
- };
- // Iniciacao do programa
- void setup() {
- Serial.begin(115200);
- // Iniciar a camera
- esp_err_t err = esp_camera_init(&camera_config);
- if (err != ESP_OK) {
- Serial.print("Erro ");
- Serial.print (err);
- Serial.println (" ao iniciar a camera!");
- // Não pode prosseguir
- for (;;) {
- delay (1000);
- }
- }
- }
- // Laço principal
- void loop() {
- // Tirar uma foto
- unsigned long inicio = millis();
- camera_fb_t * fb = esp_camera_fb_get();
- unsigned long fim = millis();
- if (fb) {
- Serial.print ("Obteve foto, tempo = ");
- Serial.println (fim-inicio);
- Serial.print ("Tamanho = ");
- Serial.println (fb->len);
- esp_camera_fb_return(fb);
- } else {
- Serial.println ("Erro ao tirar a foto!");
- }
- // Dá um tempo entre as fotos
- delay(3000);
- }
Como recomendado na biblioteca, o formato solicitado é JPG. Este formato implica em uma compressão, a perda de qualidade não é problema mas precisamos descompactar para acessarmos os pixels. Para isso existe a rotina jpg2bmp(). A notar que tanto esta rotina como esp_camera_fb_get() montam em memória exatamente uma imagem do arquivo, incluindo os respectivos cabeçalhos. Novamente a área retornada precisa ser liberada (nesta caso através de free).
Abaixo um outra versão da rotina loop() que gera o bmp e informa a resolução da foto.
Abaixo um outra versão da rotina loop() que gera o bmp e informa a resolução da foto.
- // Estrutura do cabeçalho de arquivo BMP
- typedef struct {
- uint32_t filesize;
- uint32_t reserved;
- uint32_t fileoffset_to_pixelarray;
- uint32_t dibheadersize;
- int32_t width;
- int32_t height;
- uint16_t planes;
- uint16_t bitsperpixel;
- uint32_t compression;
- uint32_t imagesize;
- uint32_t ypixelpermeter;
- uint32_t xpixelpermeter;
- uint32_t numcolorspallette;
- uint32_t mostimpcolor;
- } bmp_header_t;
- // Laço principal com a conversão
- void loop() {
- // Dá um tempo entre as fotos
- delay(10000);
- // Tirar uma foto
- unsigned long inicio = millis();
- camera_fb_t * fb = esp_camera_fb_get();
- unsigned long fim = millis();
- if (fb) {
- Serial.print ("Obteve foto, tempo = ");
- Serial.println (fim-inicio);
- Serial.print ("Tamanho = ");
- Serial.println (fb->len);
- uint8_t * buf = NULL;
- size_t buf_len = 0;
- inicio = millis();
- bool converteu = frame2bmp(fb, &buf, &buf_len);
- fim = millis();
- if (converteu) {
- Serial.print ("Converteu foto, tempo = ");
- Serial.println (fim-inicio);
- bmp_header_t * bitmap = (bmp_header_t*)&buf[2];
- Serial.print ("Largura = ");
- Serial.println (bitmap->width);
- Serial.print ("Altura = ");
- Serial.println (-bitmap->height);
- free(buf);
- } else {
- Serial.println ("Erro na conversão!");
- }
- esp_camera_fb_return(fb);
- } else {
- Serial.println ("Erro ao tirar a foto!");
- }
- }
O próximo passo será comparar duas fotos para detectar movimento.
Um comentário:
Olá.
O meu deixa a primeira foto esverdeada e depois fica uma foto em defasagem... Se eu fizer a captura e pegar a segunda imagem fica perfeita, com uma qualidade top.
Outra dificuldade é quando está exposta ao sol, a minha gera nulos que não são codificados corretamente. (Já testou a sua na claridade do sol do meio dia?)
Postar um comentário