IoT

ESP32-CAM Acessado Remotamente com Ngrok

Eletrogate 13 de outubro de 2022

Introdução

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.


Como Funciona?

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.


Materiais Necessários para o Projeto ESP32-CAM Acessado Remotamente com Ngrok

Todos os materiais podem ser comprados em nossa loja.

cta_cart


Conhecendo o ESP32-CAM

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.


Código e Configuração

Para programar o ESP32-CAM você precisa de um módulo conversor USB/TTL/FTDI ou Módulo programador ESP32-CAM-MB

Mapa de pinos:

Circuito de ligação:

Arquivo principal: esp32Cam.ino

/**
 * @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();
}

Arquivo com os defines dos pinos: camera_pins.h

#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/


Instalando e Configurando o Ngrok

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

Colocando o ESP32-CAM na internet

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:

  • –basic-auth=”saulo:saulo123″ é o comando de login e senha para acessar o página do ESP32-CAM, saulo o usuário e saulo123 a senha. Você pode escolher qualquer usuário e senha.
  • 192.168.1.19:8880 é o IP e porta que o ESP32 pegou da sua rede local
  • seu-token-aqui insira o seu token

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.


Conclusão

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

Referências utilizadas no artigo:

  • https://www.instructables.com/id/9-RTSP-Video-Streamer-Using-the-ESP32-CAM-Board/
  • https://rntlab.com/question/esp32-camera-headers-and-browser/
  • https://create.arduino.cc/projecthub/CiferTech/how-to-access-esp32-cam-worldwide-using-ngrok-210aa0
  • https://randomnerdtutorials.com/esp32-cam-video-streaming-face-recognition-arduino-ide
  • https://randomnerdtutorials.com/esp32-cam-ov2640-camera-settings/
  • https://medium.com/desenvolvendo-com-paixao/ngrok-do-localhost-para-o-mundo-5445ad08419
  • https://randomnerdtutorials.com/esp32-cam-troubleshooting-guide/
  • https://robotzero.one/esp32-cam-custom-html/
  • https://dashboard.ngrok.com/get-started/setup
  • https://dronebotworkshop.com/esp32-cam-intro/

 


Sobre o Autor


Saulo Aislan

Graduando em Tecnologia em Telemática pelo IFPB – Campus de Campina Grande – PB. Tenho experiência com os microcontroladores da família Arduino, ESP8266, ESP32, STM32 e microprocessador Raspberry Pi. Tenho projetos na áreas de IoTs voltada para a indústria 4.0, agroindústria e indústria aeroespacial civil utilizando LoRa, Bluetooth, ZigBee e Wi-Fi. Atualmente estudando e desenvolvendo em FreeRTOS para sistemas em tempo real com ESP32 e LoRaWan para Smart City e compartilhando alguns projetos no blog da Eletrogate.


Eletrogate

13 de outubro de 2022

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!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!