O ESP32-CAM é uma placa microcontrolada que possui uma câmera de vídeo integrada e um soquete para cartão microSD. É fácil de usar e perfeito para monitorar sua casa ou para dispositivos IoT que exigem uma câmera com funções avançadas, como rastreamento e reconhecimento de imagens. Nesse artigo, vamos criar uma câmera de segurança com ESP32-CAM e acessar ela de qualquer lugar do mundo de forma segura, rápida e gratuita.
Vamos criar um sistema de câmera de segurança que podemos acessar em qualquer lugar do mundo. Quando o sistema estiver todo configurado, você poderá visualizar a câmera em tempo real em qualquer lugar do mundo de forma segura e de graça, utilizando a tecnologia do ngrok para criar um túnel entre os serviços locais do computador e a internet.
Todos os materiais podem ser comprados em nossa loja.
Aqui, no blog, existe um artigo muito bom demonstrando com programa a ESP32-CAM: https://blog.eletrogate.com/introducao-ao-esp32-cam/
Siga os passos do artigo para fazer alguns testes. Neste tutorial, vamos utilizar nosso próprio firmware.
Para programar o ESP32-CAM você precisa de um módulo conversor USB/TTL/FTDI ou Módulo programador ESP32-CAM-MB
/** * @file esp32Cam.ino * Modificado por: Saulo Aislan * Baseado em: https://github.com/Picaio/espcam/tree/master de Picaio * @brief Firmware para criacao de um server stream de video. * @version 0.1 * * @copyright Copyright (c) 2022 * */ #include <WiFi.h> #include <WebServer.h> #include <WiFiClient.h> #include "src/OV2640.h" // Arquivos da biblioteca OV2640 // Selecione o modelo da camera //#define CAMERA_MODEL_WROVER_KIT //#define CAMERA_MODEL_ESP_EYE //#define CAMERA_MODEL_M5STACK_PSRAM //#define CAMERA_MODEL_M5STACK_WIDE #define CAMERA_MODEL_AI_THINKER #include "camera_pins.h" // Arquivo com os pinos #define SSID1 "SEU_SSID" #define PWD1 "SUA_SENHA" OV2640 cam; // Instanciação da classe OV2640 WebServer server(8880); // Porta do endereco do ESP32 // Dados para o protocolo HTTP (HTTP_header(cabeçalho), HTTP_boundary(Limite de cada parte) e HTTP_contentType(Tipo de conteudo)) const char HTTP_header[] = "HTTP/1.1 200 OK\r\n" \ "Access-Control-Allow-Origin: *\r\n" \ "Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n"; const char HTTP_boundary[] = "\r\n--123456789000000000000987654321\r\n"; const char HTTP_contentType[] = "Content-Type: image/jpeg\r\nContent-Length: "; const int headerLen = strlen(HTTP_header); const int boundaryLen = strlen(HTTP_boundary); const int cntLen = strlen(HTTP_contentType); /** * @brief FUNCAO QUE RECEBE A REQUISICAO GET E EXECUTA O CLIENT MOSTRANDO O VIDEO */ void handleStream(void) { char buf[32]; int dataSize; WiFiClient client = server.client(); client.write(HTTP_header, headerLen); // Escreve no cliente o header e seu tamanho client.write(HTTP_boundary, boundaryLen); // Escreve no cliente o boundary e seu tamanho while (true) { if (!client.connected()) break; cam.run(); // Starta a camera dataSize = cam.getSize(); // Atibui o tamanho dos dados da camera para dataSize client.write(HTTP_contentType, cntLen); // Escreve no cliente o tipo de conteudo e seu tamanho sprintf(buf, "%d\r\n\r\n", dataSize); // Formata o dataSize e salva no buffer client.write(buf, strlen(buf)); // Escreve no cliente o buffer e seu tamanho client.write((char *)cam.getfb(), dataSize); // Escreve no cliente o video capturado client.write(HTTP_boundary, boundaryLen); // Escreve no cliente o boundary e seu tamanho fechando o protocolo HTTP } } /** * @brief FUNCAO QUE RECEBE A REQUISICAO GET NOT FOUND E PRINTA UMA MENSAGEM */ void handlePageNotFound() { String mensagem = "Server em execucao!\n\n"; // String com a mensagem para imprimir na tela mensagem.concat(F("URI: ")); // Concatena a string URI na string mensagem mensagem.concat(server.uri()); // Imprime a URI mensagem.concat(F("\nMetodo: ")); mensagem.concat((server.method() == HTTP_GET) ? "GET" : "POST"); // Verifica se foi uma requisicao GET ou POST mensagem.concat(F("\nArgumentos: ")); mensagem.concat(server.args()); // Imprime os argumentos mensagem.concat(F("\n")); server.send(200, "text/plain", mensagem); // Envia para o navegador } /** * @brief FUNCAO SETUP */ void setup() { Serial.begin(115200); // Configuracoes de GIPOS da camera camera_config_t configuracao; configuracao.ledc_channel = LEDC_CHANNEL_0; configuracao.ledc_timer = LEDC_TIMER_0; configuracao.pin_d0 = Y2_GPIO_NUM; configuracao.pin_d1 = Y3_GPIO_NUM; configuracao.pin_d2 = Y4_GPIO_NUM; configuracao.pin_d3 = Y5_GPIO_NUM; configuracao.pin_d4 = Y6_GPIO_NUM; configuracao.pin_d5 = Y7_GPIO_NUM; configuracao.pin_d6 = Y8_GPIO_NUM; configuracao.pin_d7 = Y9_GPIO_NUM; configuracao.pin_xclk = XCLK_GPIO_NUM; configuracao.pin_pclk = PCLK_GPIO_NUM; configuracao.pin_vsync = VSYNC_GPIO_NUM; configuracao.pin_href = HREF_GPIO_NUM; configuracao.pin_sscb_sda = SIOD_GPIO_NUM; configuracao.pin_sscb_scl = SIOC_GPIO_NUM; configuracao.pin_pwdn = PWDN_GPIO_NUM; configuracao.pin_reset = RESET_GPIO_NUM; configuracao.xclk_freq_hz = 20000000; configuracao.pixel_format = PIXFORMAT_JPEG; // Definicao do formato do video, selecione um framesize if(psramFound()){ // Verifica se a camera suportar PSRAM configuracao.frame_size = FRAMESIZE_VGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA // Quantidade de pixels que irá compor a imagem. // Pode ser reduzido para aumentar o framerate. // Opções: VGA, CIF, QVGA, UXGA, SXGA, XGA, SVGA configuracao.jpeg_quality = 9; // Define o fator de compressão da imagem, 0-63 numero menor significa maior qualidade configuracao.fb_count = 2; } else { configuracao.frame_size = FRAMESIZE_CIF; configuracao.jpeg_quality = 12; configuracao.fb_count = 1; } cam.init(configuracao); // Inicializacao da camera com as configuracoes sensor_t * sensorConfig = esp_camera_sensor_get(); sensorConfig->set_hmirror(sensorConfig, 1); // Espelhamento horizontal 0 = Desabilita , 1 = Habilita sensorConfig->set_vflip(sensorConfig, 1); // Espelhamento vertical 0 = Desabilita , 1 = Habilita // Secao do WiFi WiFi.mode(WIFI_STA); WiFi.begin(SSID1, PWD1); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print(F(".")); } Serial.println(F("WiFi conectado")); Serial.println(WiFi.localIP()); Serial.print(F("Link do Stream: http://")); Serial.print(WiFi.localIP()); server.on("/", HTTP_GET, handleStream); server.onNotFound(handlePageNotFound); server.begin(); } /** * @brief FUNCAO DE LOOP */ void loop() { server.handleClient(); }
#if defined(CAMERA_MODEL_WROVER_KIT) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 21 #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 19 #define Y4_GPIO_NUM 18 #define Y3_GPIO_NUM 5 #define Y2_GPIO_NUM 4 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 #elif defined(CAMERA_MODEL_ESP_EYE) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 4 #define SIOD_GPIO_NUM 18 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 36 #define Y8_GPIO_NUM 37 #define Y7_GPIO_NUM 38 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 35 #define Y4_GPIO_NUM 14 #define Y3_GPIO_NUM 13 #define Y2_GPIO_NUM 34 #define VSYNC_GPIO_NUM 5 #define HREF_GPIO_NUM 27 #define PCLK_GPIO_NUM 25 #elif defined(CAMERA_MODEL_M5STACK_PSRAM) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 25 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 32 #define VSYNC_GPIO_NUM 22 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 #elif defined(CAMERA_MODEL_M5STACK_WIDE) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 22 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 32 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 #elif defined(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 #else #error "Camera model not selected" #endif
Para utilizar a câmera, é necessário a biblioteca OV2640. Você pode fazer o download dos arquivos neste link. Extraia os arquivos OV2640.cpp e OV2640.h e salve eles em uma pasta nomeada de src dentro da mesma pasta do arquivo principal. Essa deve ser a estrutura do diretório:
└── esp32Cam # Pasta principal
├── esp32Cam.ino # Firmware principal
├── camera_pins.h # Arquivo com as definições dos pinos
└── src # Sources
├── OV2640.cpp # Biblioteca .cpp
└── OV2640.h # Biblioteca .h
Antes de fazer o upload do código, habilite a PSRAM em ferramentas:
Se tudo ocorreu corretamente, irá aparecer essa mensagem no monitor serial:
ATENÇÂO! Pode ocorrer alguns problemas para abrir o link. O principal problema é a falta de corrente da porta USB do computador, então, de preferência, alimente o módulo com uma Fonte 5V 1A Bivolt. Para outros problemas acesse: https://randomnerdtutorials.com/esp32-cam-troubleshooting-guide/
O ngrok é um aplicativo multiplataforma que pode criar uma URL de túnel ou encaminhamento para que as solicitações de Internet cheguem ao computador local. Para utilizar o ngrok o primeiro passo é fazer o download no link: https://ngrok.com/download, conforme seu sistema operacional. Ao baixar o arquivo .zip, o descompacte e execute o arquivo ngrok que foi extraído nesse processo. Ao executar, irá abrir um terminal como na figura:
Recomendo que crie uma conta e assine o plano gratuito, pois ao executar o ngrok com uma conta vinculada, você elimina as restrições decorrentes do uso sem conta.
Para vincular o ngrok a sua conta, você precisa do Authtoken, que pode ser visualizado na seção Your Authroken, no menu lateral:
com o token em mãos, basta executar, no terminal aberto no passo anterior, o seguinte comando:
ngrok config add-authtoken
seu-authtoken-aqui
Ao programar o ESP32-CAM, ele irá criar um servidor local para podermos acessar a câmera (visto na seção Código e Configuração). Mas, queremos acessar o ESP32 fora da rede local. Para isso, temos que criar um túnel na porta 8880, expondo esse servidor do ESP32-CAM via ngrok. Pra isso, execute o seguinte comando no terminal do ngrok:
ngrok http --basic-auth="saulo:saulo123" 192.168.1.19:8880 --authtoken seu-token-aqui
Onde:
Após executar o comando, você irá visualizar algo semelhante a isso:
Na imagem acima, a URL de forwarding é o link em que o servidor do ESP32-CAM está disponível para o mundo pela internet. Nesse exemplo, quando alguém acessar o endereço https://84e0-177-37-144-211.sa.ngrok.io, o tráfego será retransmitido para http://192.168.1.19:8880 por meio do sistema do túnel da ngrok, que está em execução na máquina.
Se, ao tentar acessar o link gerado pelo ngrok, aparecer essa mensagem:
Significa que o tamanho do header do request HTTP configurado na IDE do Arduino é menor do que está sendo transmitido. Com isso, é necessário aumentar o tamanho máximo de request HTTP do ESP32. Para isso, abra o arquivo sdkconfig em …\Arduino15\packages\esp32\hardware\esp32\2.0.0-rc1\tools\sdk (Talvez o caminho do arquivo seja um pouco diferente no seu ambiente):
no arquivo sdkconfig, procure por CONFIG_HTTPD_MAX_REQ_HDR_LEN:
altere o valor para 1024 ou 2048 e faça o upload do código para o ESP32 novamente. Certifique que ele pegou o mesmo IP que está configurado no ngrok. Caso contrário, terá que reconfigurar o ngrok.
Nesse tutorial, aprendemos a montar uma câmera de segurança com o ESP32-CAM acessível pela internet e protegida com login e senha. Esse projeto é totalmente Open-source e você pode modificar o que quiser, adicionar ou remover componentes e deixar sua imaginação fluir. Para mais material como este, continue acompanhando as postagens semanais do blog e não deixe de visitar nossa loja. Lá, você encontra todos os componentes necessários para desenvolver esse e muitos outros projetos!
Que a força estejam com vocês e até mais!
Referências utilizadas no artigo:
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!