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

Je suis pas sur le C arduino est pas ma tasse de tea … je préfere le MicroPython
et avec tout cela j’ai même pas encore regarder avec du code micropython ce que ca donne…

Compile OK avec ton tar. Pas compris pourquoi il ne s’installe pas et dit INSTALLED !
Je tenterai la 1.8.12.

Merci

Après il m’en a demandé une autre si ça te le fait encore n’hésite pas …

T’embêtes pas avec les packages… moi j’ai tapé le « ESPAsyncwebserver » dans ghithub, j’ai téléchargé le dossier du premier, et j’ai mis le tout dans \users\moi\Documents\Arduino\librairies (comme tu a dû procéder pour mettre les librairies spécifique à la caméra dans mon tuto).
Il faut faire pareil pour le « AsyncTCP ».

Pour simplifier, voici un zip avec toutes les librairies nécessaires (enlever le .pdf) :
libESPCAM.zip.pdf (284,3 Ko)

Sinon, il y a un lien dans l’en-tête du code (à partir des versions de @drs) pour l’installation du AsyncWebServer…

Tuto mis à jour avec un fichier zip contenant la v1.7 (en plus de la version d’origine) et les bonnes bibliothèques.

1.7 ou 1.7.1 ?

Oui c’était OK quand j’ai répondu à @olive car j’avais déjà vu/fait ce qu’il fallait pour AsyncTCP
thks

1.7… je remettrai à jour au fur et à mesure.
Je n’ai pas encore testé la 1.7.1

Une autre question
Comment les cde et infos sont configurer dans jeedom ? Espeasy ?

Par script pour les entrées/sorties (led / GPIO12). Voir l’en-tête du code .ino pour les paramètres du script.
Pas d’ESPEasy puisque c’est du code compilé en direct.

Comme le dit @Theduck38
tu peut manipuler ces commandes depuis ton jeedom a l’aide du pulugin script
ou depuis un scénario

ex:
Capture d’écran du 2020-05-22 16-17-16

Attention néanmoins dans tes commandes de script il te faudra ajuster l’adresse IP à celle de ta caméra…

Tiens, j’ai une question concernant les récupérations d’états sur un script… comment fais-tu ?

Il faut appeler le « refresh » du script pour avoir des états à jour… tu passes par un scénario qui rafraîchit toutes les xx minutes chaque commande, ou l’auto-actualisation du script dans l’onglet « équipement » ?

Dans le cas présenté en exemple puisque c’est le même équipement il se trouve rafraîchi a chaque action ou au refresh

Dans un scénario tu peut demandé l’état sans passer par un plugin-script

Je vous laisse découvrir …

CODE V1.8
//---------------------------------------------------------------------------------------------------------------------
// DOCS UTILES:
// ------------
// SERVEUR WEB CAM: https://community.jeedom.com/t/tuto-camera-a-focale-fixe-basee-sur-esp32/17587/49
// LED WIFI: https://techtutorialsx.com/2018/02/03/esp32-arduino-async-server-controlling-http-methods-allowed/
// Installing Asyncwebserver: https://techtutorialsx.com/2017/12/01/esp32-arduino-asynchronous-http-webserver/
// IMAGE PARAMS: https://randomnerdtutorials.com/esp32-cam-ov2640-camera-settings/
// @drs
// modified @olive v1.71 jeudi 21 mai 2020 21h27
//
// PARAMETRES A MODIFIER:
// ----------------------
// RESOLUTION: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA / QUALITY (10-63)
// WSPORT: port  web (pour affichage images)
// ASYNCPORT: port AsyncWebServer (pour gestion LED)
// Paramètres image
//
// USAGE:
// ------
// http://ip-esp:WSPORT/                page d'accueil
// http://ip-esp:WSPORT/_ac             page de config
// http://ip-esp:WSPORT/mjpg:           stream
// http://ip-esp:WSPORT/snapshot.jpg    image
//
// GESTION DE LA LED:
// ------------------
// http://ip-esp:ASYNCPORT/led           Etat de la led (format binaire)
// http://ip-esp:ASYNCPORT/led/on        Allume la LED
// http://ip-esp:ASYNCPORT/led/off       Eteint la LED
// http://ip-esp:ASYNCPORT/led/toggle    Allume/Eteint la LED
//
// GESTION DU GPIO12:
// ------------------
// http://ip-esp:ASYNCPORT/gpio12           Etat du gpio2 (format binaire)
// http://ip-esp:ASYNCPORT/gpio12/on        Allume gpio12
// http://ip-esp:ASYNCPORT/gpio12/off       Eteint gpio12
// http://ip-esp:ASYNCPORT/gpio12/toggle    Allume/Eteint gpio12
//
// INTEGRATION JEEDOM:
// -------------------
// Plugin Camera:
// Laisser le type de camera sur "Aucun"
// Renseigner l'IP et le port (webserver)
// URL de snapshot:   /snapshot.jpg
// URL de flux:       /mjpg
//
// Script gestion led:
// Action/Default
// URL: voir ci-dessus
// Cocher "Pas d'erreurs"
// La réponse doit contenir: ok (en minuscule)
// Timeout: si besoin (pas nécessaire)
//
//---------------------------------------------------------------------------------------------------------------------

#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
#include "esp_camera.h"        // Pas sur de l'utilité. A vérifier

#define ENABLE_RTSPSERVER
#define ENABLE_WEBSERVER
#define RESOLUTION  FRAMESIZE_CIF   // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
#define QUALITY        10           // JPEG quality 10-63 (lower means better quality)

// PARAMETRES IMAGE
#define BRIGHT    1           // Brightness: -2 to 2
#define CONTRAST  0           // Contrast: -2 to 2
#define SATUR     0           // Color Saturation: -2 to 2
#define SPECIAL   0           // Special effects: 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
#define WHITEBAL  1           // White Balance: 0 = disable , 1 = enable
#define AWBGAIN   1           // White Balance Gain: 0 = disable , 1 = enable
#define AWBMODE   3           // White Balance Mode: 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
#define EXPOS     1           // Exposure Control: 0 = disable , 1 = enable
#define AEC2      0           // 0 = disable , 1 = enable
#define AELEV     0           // -2 to 2
#define AECVAL    300         // 0 to 1200
#define GAINCT    1           // 0 = disable , 1 = enable
#define GAINAGC   0           // 0 = disable , 1 = enable
#define GAINCELL  0           // 0 = disable , 1 = enable
#define BPC       0           // 0 = disable , 1 = enable
#define WPC       1           // 0 = disable , 1 = enable
#define RAWGMA    1           // 0 = disable , 1 = enable
#define LENC      1           // Lens Correction: 0 = disable , 1 = enable
#define MIRROR    0           // Horizontal Mirror: 0 = disable , 1 = enable
#define FLIP      0           // Vertical Flip: 0 = disable , 1 = enable
#define DCW       1           // 0 = disable , 1 = enable
#define CBAR      0           // Set a colorBar: 0 = disable , 1 = enable

#define LED_BUILTIN 4         // VA SERVIR POUR ALLUMER et ETEINDRE LA LED - NE PAS MODIFIER CETTE VALEUR (GPIO4)

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

OV2640 cam;
boolean useRTSP = true;

#define WSPORT  80                // modifier le port pour Webserver Image
#define ASYNCPORT  81             // modifier le port pour Webserver LED
WebServer server(WSPORT); 
AsyncWebServer serverasync(ASYNCPORT);
AutoConnect Portal(server);
AutoConnectConfig acConfig;



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



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)
{
  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());
  
}

void handle_root(void)
{
  String reply ="<!doctype html>\
  <style type=text/css>\
  body {\
  background-color: #272727; \
  }\
  <style>\
  th, td {\
  border: 1px solid black;\
  background-color: #FFFF00; \
  }\
  table {\
  border-collapse: collapse;\  
  background-color: #00AA00; \
  }\
  a {\
   color: red; \  
  }\
  </style>";
  reply +=F("<html><head><meta charset='UTF-8'></head><table><tr><td colspan='2'><H1>ESP32CAM</H1></td></tr>");
  reply +=F("<tr><td>VERSION </td><td>1.8</td></tr>");
  reply += F("<tr><td>Resolution</td>");

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

  //LED
  url =  IPA + "/led/on";
  reply += "<tr><td>Led_on</td><td><a href='" + url + "'>" + url + "</a></td></tr>";

  url =  IPA + "/led/off";
  reply += "<tr><td>Led_off</td><td><a href='" + url + "'>" + url + "</a></td></tr>";

  url =  IPA + "/led/toggle";
  reply += "<tr><td>Led_toggle</td><td><a href='" + url + "'>" + url + "</a></td></tr>";

  url =  IPA + "/led";
  reply += "<tr><td>Led_etat</td><td><a href='" + url + "'>" + url + "</a></td></tr>";


  //GPIO12
  url =  IPA + "/gpio12/on";
  reply += "<tr><td>gpio12_on</td><td><a href='" + url + "'>" + url + "</a></td></tr>";

  url =  IPA + "/gpio12/off";
  reply += "<tr><td>gpio12_off</td><td><a href='" + url + "'>" + url + "</a></td></tr>";

  url =  IPA + "/gpio12/toggle";
  reply += "<tr><td>gpio12_toggle</td><td><a href='" + url + "'>" + url + "</a></td></tr>";

  url =  IPA + "/gpio12";
  reply += "<tr><td>gpio12_etat</td><td><a href='" + url + "'>" + url + "</a></td></tr>";
  
  reply += "</table><img src='"+IPH+"/mjpg'>\
  </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

  pinMode(12, OUTPUT);       // pour RELAIS SUR GPIO12
  digitalWrite(12, LOW);     // pour RELAIS SUR GPIO12  
  
  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;
  }

  cam.init(cconfig);

sensor_t * s = esp_camera_sensor_get();
  s->set_brightness(s, BRIGHT);
  s->set_contrast(s, CONTRAST);
  s->set_saturation(s, SATUR);
  s->set_special_effect(s, SPECIAL);
  s->set_whitebal(s, WHITEBAL);
  s->set_awb_gain(s, AWBGAIN);
  s->set_wb_mode(s, AWBMODE);
  s->set_exposure_ctrl(s, EXPOS);
  s->set_aec2(s, AEC2);
  s->set_ae_level(s, AELEV);
  s->set_aec_value(s, AECVAL);
  s->set_gain_ctrl(s, GAINCT);
  s->set_agc_gain(s, GAINAGC);
  s->set_gainceiling(s, (gainceiling_t)GAINCELL);
  s->set_bpc(s, BPC);
  s->set_wpc(s, WPC);
  s->set_raw_gma(s, RAWGMA);
  s->set_lenc(s, LENC);
  s->set_hmirror(s, MIRROR);
  s->set_vflip(s, FLIP);
  s->set_dcw(s, DCW);
  s->set_colorbar(s, CBAR);

  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();

  Serial.println(" ");
  Serial.println("Adresse IP: " + WiFi.localIP().toString() );
  Serial.println(" ");

// POUR LED
serverasync.on("/led/off", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", "ok");
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("LED TURNED OFF");
  });
  
  serverasync.on("/led/on", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain","ok");
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.println("LED TURNED ON");
  });
  
  serverasync.on("/led/toggle", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain","ok");
     digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
     Serial.println("LED TURNED TOGGLE");
  });
  
  serverasync.on("/led", HTTP_GET, [](AsyncWebServerRequest *request){
     request->send(200, "text/plain", String(digitalRead(LED_BUILTIN)));
  });



// POUR GPIO 12
serverasync.on("/gpio12/off", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", "ok");
    digitalWrite(12, LOW);
    Serial.println("gpio12 TURNED OFF");
  });
  
  serverasync.on("/gpio12/on", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain","ok");
    digitalWrite(12, HIGH);
    Serial.println("gpio12 TURNED ON");
  });
  
  serverasync.on("/gpio12/toggle", HTTP_ANY, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain","ok");
     digitalWrite(12, !digitalRead(12));
     Serial.println("gpio12 TURNED TOGGLE");
  });
  
  serverasync.on("/gpio12", HTTP_GET, [](AsyncWebServerRequest *request){
     request->send(200, "text/plain", String(digitalRead(12)));
  });
  
  
  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
}

@Theduck38 tu va devoir corriger le post 7 aussi :wink:

1 « J'aime »

:ok_hand:
Bon boulot ! c’est OK pour moi (+ led OK mais pas testé le GPIO hormis etat qui est cohérent)

:grin: Je croyais que tu allais sortir une version avec tous les param de gestion cam en param optionels d’une url de params

http://192.168.1.x/params?RESOLUTION=FRAMESIZE_CIF?QUALITY=10?BRIGHT=1?WHITEBAL=0...

Mais là j’exagère peut-être :thinking:

Je suis pas sur que ce soit interessant en ligne de commande mais pourquoi pas ?

pour tester le gpio il te suffit ou d’une voltmetre ou d’une led …

A mettre entre GND et IO12 tu doit trouvé 0 ou 3 volts.

Yes ça je maîtrise pas l’utilité sur cette cam. Merci

Cela avait été demandé en début de post pour avoir un contact sec il faut bien sur ajouter un relais …

Super, j’ai mis à jour le post avec les bibliothèques et la version actuelle.

Concernant le relais, c’était pour pouvoir commander un filtre IR si j’ai bien compris, voire déclencher de l’éclairage led IR pour une caméra nocturne. C’est un sujet que je ne maîtrise pas du tout (filtre IR, pas filtre etc…), mais ça pourrait être intéressant ; d’autant que les caméras devraient être directement compatibles.

J’ai tout commandé pour tester. Relais, les ir… Des que ça arrive je teste et vous dit.