[TUTO] Caméra à focale fixe basée sur ESP32

Bonjour,
Pour info, il y a quelques mois, j’ai fait ce script pour enregistrer un flux vidéo (très très lent avec mon .ino d’origine et différent du tien @Theduck38 )

#!/bin/sh
# script de capture de video
#script=`basename $0`
pid=`ps -ef|grep "IP ESP32CAM"|grep -v grep| awk '{printf("%s "), $2}'`
if [ -z "$pid" ];then
       VideoFile=$1
       Duration=$2
       sudo rm ${VideoFile}
       # enregistrement du flux video dans un fichier
       /usr/bin/ffmpeg -f mjpeg -probesize 32 -analyzeduration 0 -i "http://IP ESP32CAM:81/stream" -t ${Duration} -r 25 -s vga -an -y ${VideoFile}
else
	exit 1
fi

Si tu as le temps de tester avec ton .ino et l’url de stream qu’il propose.
Ou l’optimiser :wink:

merci pour ces infos, je n’ai pas mal pris la boutade je te rassure :stuck_out_tongue:

effectivement j’était passé de l’E14 à ton sketch directement, pourtant j’ai essayé 3 ou 4 autres en passant direct de l’un à l’autre sans soucis.

de plus mon model n’est pas un AI-thinker je sais pas si ça joue…

pour les lib, je suis passé la première fois par le manager de bibliothèques :

Je voulais rajouter une astuce concernant l’antenne, j’avais commandé un pack avec antenne externe :

le connecteur n’avais pas été modifié par le constructeur, il faut théoriquement modifier le pont d’une resistance 0 ohm pour switcher de l’antenne sur le PCB à la prise U.FL / PEX :

image

en fait il est possible de juste de créer le pont à l’étain sans toucher/enlever la précédente résistance, ça à fonctionné pour moi

c’est peut être « sale » mais pour moi, une micro-soudure de cette taille était trop risquée (de nombreux cas de « flinguage » de pistes vu sur le net :

Alors pour l’antenne, j’étais tombé sur un article dont l’auteur avait l’air de bien toucher sa bille en RF et il faisait des mesures avec les deux antennes en parallèle. C’était pire que d’utiliser juste l’antenne du PCB !
(malheureusement, même après une longue recherche dans mon historique navigateur, je n’arrive pas à retrouver l’article…)

Du coup, j’ai essayé aussi… après avoir fait le pont (sans couper), j’avais plus ou moins le même gain qu’avant sur mon signal wifi. Je l’ai très nettement amélioré après avoir coupé la piste, du genre de -90db à -70db.

interessant mais même avec une panne super petite, jai peur de ne pas y arriver (oui ya pas de sous-entendu) on est quand même sur une micro soudure de moins de 0.5mm

donc pour les personnes qui n’ont pas un pistolet à air chaud ou autre capacité de soudure très precise ça reste acceptable non ?

1 « J'aime »

En fait, si tu arrives à faire le pont à l’étain (équivalent à une résistance 0 ohm), tu peux juste faire sauter la résistance présente côté antenne PCB avec un petit tournevis… c’est ce que j’ai fait sur le mien.
Mais il faut d’abord faire le pont, et si ça marche, alors faire sauter la résistance.

Attention, pour ceux qui veulent tester au multimètre s’il y a bien un contact, l’antenne a aussi une connexion vers la masse… ça bipe donc entre masse et antenne.

J’ai une super question

je cheche un sketch qui soit une genre de combinaison de 3 choses :

  • flux RTSP
  • url avec capture generée toutes les 2/3 minutes

j’ai rien trouvé, ça existe ?

Je le coderais bien si j’avais les skills…

Après avoir testé le même code, j’ai cherché aussi une variante RTSP sans trouver mieux que ce que j’ai posté… et je n’ai pas la capacité non plus de coder ça.

Bonjour
J’ai trouvé des modules miniatures infrarouge pour la vision de nuit.
La question : comment faire pour avoir un relais piloter sur l’esp32. Il faut faire une modif de prog ?

Oui… et je ne sais pas faire malheureusement.

Bonjour à tous
J’ai une erreur de compilation que je n’arrive pas résoudre si vous pouviez m’aider svp
In file included from D:\Arduino\ESP32_RTSP_Cam_v1\libraries\AutoConnect-master\src/AutoConnect.h:34:0,
from D:\Arduino\ESP32_RTSP_Cam_v1\ESP32_RTSP_Cam_v1.ino:4:
D:\Arduino\ESP32_RTSP_Cam_v1\libraries\AutoConnect-master\src/AutoConnectAux.h: In member function ‹ bool AutoConnectAux::_parseElement(T, U) ›:

D:\Arduino\ESP32_RTSP_Cam_v1\libraries\AutoConnect-master\src/AutoConnectAux.h:130:21: error: ‹ using ArduinoJsonBuffer = ArduinoJson::DynamicJsonBuffer {aka class ArduinoJson::Internals::BlockJsonBufferArduinoJson::Internals::DefaultAllocator} › has no member named ‹ parse ›
jb = jsonBuffer.parse(in);
^
D:\Arduino\ESP32_RTSP_Cam_v1\libraries\AutoConnect-master\src/AutoConnectAux.h:131:13: error: ‹ class ArduinoJson::JsonVariant › has no member named ‹ success ›
if (!jb.success()) {
^

Hello,

Je dirais qu’il te manque la librairie « ArduinoJson » ou qu’elle est incomplète, ou trop vieille…
Essaye de mettre ça dans ton dossier « librairies » (enlever le .pdf et dézipper) :
ArduinoJson.7z.pdf (130,8 Ko)

Bonjour,
Sur l’ESP32 tu as des GPIOs ils y en a pas mal d’utilisés par la cam mais il en reste, tu peux commander un transistor ou un mofset qui commandera les modules. Après il faut voir comment tu veux les déclencher (Illumination, mouvement, …)…
Aussi attention les illuminateurs Infrarouge ont besoin d’une cam sans filtre IR.

Bonne soirée

Salut
Merci pour ton aide rapide, la compilation et le téléversement ce sont passés, j’ai eu peu de difficultés pour les autres étapes mais tout fonctionne, l’intégration sous Jeedom aussi, par contre j’ai un retard d’affichage de presque 10S, c’est normal?
merci encore pour tout

1 « J'aime »

Si tu as réglé ton Jeedom pour prendre des captures d’écran toutes les xx secondes, il faut savoir que c’est toujours le snapshot de la requête d’avant qui remonte. Donc pour une image toutes les 5s, tu as 5s de latence.

Apres moulte essai effectivement mes identifiants de ocnnections était preéconfigurés apres flash

ça passe

maintenant j’ai le meme souci que mentionné précedemment en UDP jai que du packet drop

par contre en TCP pas de soucis, mais le sperfs sont pas super (j’ai pas encore bcp tweaké)
je suis en 1024*768 et antenne externe

Bonjour

Venant d’acheter l’esp32 cam, je me suis empressé d’y envoyer cette programmation. Merci, tout fonctionne impec :slight_smile:

J’ai cependant 3 questions:

  • j’ai modifié le port du serveur web, avec le port 8111, en modifiant la ligne WebServer server(80); par WebServer server(8111);
    Mais quand je me connecte sur la page web, cela fonctionne, mais les liens vers le snapshot, le flux et le wifi ne prennent pas en compte le port. Y’a-t-il un moyen de changer cela?
    Je suppose que c’est dans cette partie:
  String url = "http://" + WiFi.localIP().toString() + "/snapshot.jpg";
  reply += "<p>Snapshot URL:<br> <a href='" + url + "'>" + url + "</a>";
  url = "http://" + WiFi.localIP().toString() + "/mjpg";
  reply += "<p>HTTP MJPEG URL:<br> <a href='" + url + "'>" + url + "</a>";
  url = "rtsp://" + WiFi.localIP().toString() + ":554/mjpeg/1";
  reply += "<p>RTSP MJPEG URL:<br> " + url;
  url = "http://" + WiFi.localIP().toString() + "/_ac";
  reply += "<p>WiFi settings page:<br> <a href='" + url + "'>" + url + "</a>";

Mais je ne sais pas le modifier.

  • Il y a une led assez puissante, qui permet de gérer un éclairage dans le noir. Est- il possible d’ajouter la possibilité d’allumer ou d’éteindre cette led, via par exemple une requête http?

  • est-il possible de protéger le serveur web de la cam par un user/password?

Merci

url = "http://" + WiFi.localIP().toString() + ":port" + "/mjpg";

Bonjour

Après pas mal de recherches, j’ai réussi à intégrer la gestion de la LED via des requêtes HTTP.

J’ai gardé le code actuel, sur lequel j’ai rajouté la LED via le asyncwebserver. Voici le code:

// SERVEUR WEB CAM From: https://community.jeedom.com/t/tuto-camera-a-focale-fixe-basee-sur-esp32/17587/49
// LED WIFI FROM: https://techtutorialsx.com/2018/02/03/esp32-arduino-async-server-controlling-http-methods-allowed/
// For installing Asyncwebserver: https://techtutorialsx.com/2017/12/01/esp32-arduino-asynchronous-http-webserver/
// modified by DRS - May 2020
//
//
#include <WiFi.h>
#include <WebServer.h>
#include <ESPAsyncWebServer.h>  //permet la connexion simultanee pour la gestion de la LED
#include <WiFiClient.h>
#include <AutoConnect.h> // Needs AutoConnect, PageBuilder libraries!
#include "soc/soc.h" //disable brownout problems
#include "soc/rtc_cntl_reg.h"  //disable brownout problems

#define ENABLE_RTSPSERVER
#define ENABLE_WEBSERVER
#define RESOLUTION  FRAMESIZE_XGA // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
#define QUALITY        10          // JPEG quality 10-63 (lower means better quality)
#define PIN_FLASH_LED  -1           // GPIO4 for AIThinker module, set to -1 if not needed!

#define LED_BUILTIN 4         // VA SERVIR POUR ALLUMER et ETEINDRE LA LED

#include "OV2640.h"
#ifdef ENABLE_RTSPSERVER
 #include "OV2640Streamer.h"
 #include "CRtspSession.h"
#endif

OV2640 cam;
boolean useRTSP = true;

WebServer server(80);
AsyncWebServer serverasync(81);
AutoConnect Portal(server);
AutoConnectConfig acConfig;

#ifdef ENABLE_RTSPSERVER
WiFiServer rtspServer(554);
CStreamer *streamer;
#endif

void setflash(byte state) {
  if (PIN_FLASH_LED>-1){
    digitalWrite(PIN_FLASH_LED, state);
  }   
}

void handle_jpg_stream(void)
{
  WiFiClient client = server.client();
  String response = "HTTP/1.1 200 OK\r\n";
  response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n";
  server.sendContent(response);

  while (1)
  {
    cam.run();
    if (!client.connected())
      break;
    response = "--frame\r\n";
    response += "Content-Type: image/jpeg\r\n\r\n";
    server.sendContent(response);

    client.write((char *)cam.getfb(), cam.getSize());
    server.sendContent("\r\n");
    if (!client.connected())
      break;
  }
}

void handle_jpg(void)
{
  setflash(1);
  WiFiClient client = server.client();
  cam.run();
  if (!client.connected())
  {
    return;
  }
  String response = "HTTP/1.1 200 OK\r\n";
  response += "Content-disposition: inline; filename=snapshot.jpg\r\n";
  response += "Content-type: image/jpeg\r\n\r\n";
  server.sendContent(response);
  client.write((char *)cam.getfb(), cam.getSize());
  setflash(0);
}

void handle_root(void)
{
  
  String reply = F("<!doctype html><html><H1>ESP32CAM</H1>");

  reply += F("<p>Resolution: ");

  switch(RESOLUTION) {
    case FRAMESIZE_QVGA:
     reply += F("QVGA (320x240)");
    break;
    case FRAMESIZE_CIF:
     reply += F("CIF (400x296)");
    break;
    case FRAMESIZE_VGA:
     reply += F("VGA (640x480)");
    break;
    case FRAMESIZE_SVGA:
     reply += F("SVGA (800x600)");
    break;
    case FRAMESIZE_XGA:
     reply += F("XGA (1024x768)");
    break;
    case FRAMESIZE_SXGA:
     reply += F("SXGA (1280x1024)");
    break;
    case FRAMESIZE_UXGA:
     reply += F("UXGA (1600x1200)");
    break;
    default:
     reply += F("Unknown");
    break;
  }
  
  reply += F("<p>PSRAM found: ");
  if (psramFound()){
   reply += F("TRUE");
  } else {
   reply += F("FALSE");
  }
  String url = "http://" + WiFi.localIP().toString() + "/snapshot.jpg";
  reply += "<p>Snapshot URL:<br> <a href='" + url + "'>" + url + "</a>";
  url = "http://" + WiFi.localIP().toString() + "/mjpg";
  reply += "<p>HTTP MJPEG URL:<br> <a href='" + url + "'>" + url + "</a>";
  url = "rtsp://" + WiFi.localIP().toString() + ":554/mjpeg/1";
  reply += "<p>RTSP MJPEG URL:<br> " + url;
  url = "http://" + WiFi.localIP().toString() + "/_ac";
  reply += "<p>WiFi settings page:<br> <a href='" + url + "'>" + url + "</a>";

  reply += F("</body></html>");
  server.send(200, "text/html", reply);
}

void handleNotFound()
{
  String message = "Server is running!\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  server.send(200, "text/plain", message);
}

void setup(){

  pinMode(LED_BUILTIN, OUTPUT);       // pour LED
  digitalWrite(LED_BUILTIN, LOW);     // pour LED
  
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector 
  Serial.begin(115200);
  //  while (!Serial){;} // debug only
  camera_config_t cconfig;
  cconfig = esp32cam_aithinker_config;
  if (psramFound()) {
    cconfig.frame_size = RESOLUTION; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    cconfig.jpeg_quality = QUALITY;
    cconfig.fb_count = 2;
  } else {
    if (RESOLUTION>FRAMESIZE_SVGA) {
     cconfig.frame_size = FRAMESIZE_SVGA;
    }
    cconfig.jpeg_quality = 12;
    cconfig.fb_count = 1;
  }
  if (PIN_FLASH_LED>-1){
    pinMode(PIN_FLASH_LED, OUTPUT);
    setflash(0);
  }  
  cam.init(cconfig);

  server.on("/", handle_root);
  server.on("/mjpg", HTTP_GET, handle_jpg_stream);
  server.on("/snapshot.jpg", HTTP_GET, handle_jpg);
  Portal.onNotFound(handleNotFound);
  acConfig.apid = "ESP-" + String((uint32_t)(ESP.getEfuseMac() >> 32), HEX);
  acConfig.psk = "configesp";
//  acConfig.apip = IPAddress(192,168,4,1);
  acConfig.hostName = acConfig.apid;
  acConfig.autoReconnect = true;
  Portal.config(acConfig);
  Portal.begin();

// POUR LED

serverasync.on("/relay/off", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", "ok");
    digitalWrite(LED_BUILTIN, LOW);
  });
  
  serverasync.on("/relay/on", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain","ok");
    digitalWrite(LED_BUILTIN, HIGH);
  });
  
  serverasync.on("/relay/toggle", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain","ok");
     digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  });
  
  serverasync.on("/relay", HTTP_GET, [](AsyncWebServerRequest *request){
     request->send(200, "text/plain", String(digitalRead(LED_BUILTIN)));
  });
  
  serverasync.begin();

  //
#ifdef ENABLE_RTSPSERVER
  if (useRTSP) {
    rtspServer.begin();
    streamer = new OV2640Streamer(cam);             // our streamer for UDP/TCP based RTP transport
  }
#endif
}

void loop()
{
  //server.handleClient();
  Portal.handleClient();

#ifdef ENABLE_RTSPSERVER
  if (useRTSP) {
    uint32_t msecPerFrame = 100;
    static uint32_t lastimage = millis();

    // If we have an active client connection, just service that until gone
    streamer->handleRequests(0); // we don't use a timeout here,
    // instead we send only if we have new enough frames
    uint32_t now = millis();
    if (streamer->anySessions()) {
      if (now > lastimage + msecPerFrame || now < lastimage) { // handle clock rollover
        streamer->streamImage(now);
        lastimage = now;

        // check if we are overrunning our max frame rate
        now = millis();
        if (now > lastimage + msecPerFrame) {
          printf("warning exceeding max frame rate of %d ms\n", now - lastimage);
        }
      }
    }

    WiFiClient rtspClient = rtspServer.accept();
    if (rtspClient) {
      Serial.print("client: ");
      Serial.print(rtspClient.remoteIP());
      Serial.println();
      streamer->addSession(rtspClient);
    }
  }
#endif
}

Quelques détails:

  • A la ligne 32, le webserver est paramétré sur le port 80
  • A la ligne 33 le AsyncWebServer est paramétré sur le port 81

Les liens:
http://ip-esp32/mjpg: streaming
http://ip-esp/snapshot.jpg: l’image
http://ip-esp:81/relay/on: pour allumer la led
http://ip-esp:81/relay/off: pour éteindre la led
http://ip-esp:81/relay/toggle: pour allumer/eteindre de manière séquentielle
http://ip-esp:81/relay: état actuel de la led (sous forme binaire)

Pour le moment, j’ai découvert le asyncwebserver, qui permet de pouvoir gérer la led sans arrêter le flux video.
Le code n’est surement pas parfait (c’est la première fois que je me plonge dedans, et je me demande si le webserver async ne peut pas être utilisé aussi pour la vidéo, au lieu d’avoir les 2.

2 « J'aime »

C’est cool d’améliorer le code… merci !
J’essaierai ta version sur un ESP de test.