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

Ahh, cool si ça attire des développeurs. Je regrette de ne pas m’être plus intéressé au C quand j 'étais au lycée. Je préférais le Basic et l’assembleur à l’époque… sur Amstrad !

1 « J'aime »

Passe a Python et microPython c’est une autre dimension a la fois moins strict que du c mais la manipulation objet est dés le début dans le langage et d’une simplicité proche du basic …

hello,

juste pour info, si je puis me permettre :nerd_face:
les String en C, c’est le mal…c’est pas optimisé du tout. pas une bonne pratique. mieux vaut utiliser des array de char. Si c’est juste pour une bête url, un truc ponctuel et non dynamique, et que vous ne comptez pas faire d’autres dev en C, alors ça peut le faire. Sinon, comme je disais c’est mieux de prendre des bonnes habitudes et d’abandonner les String. Se méfier des tutos qui usent à tort des String.
par contre micropython c’est pas aussi optimisé que du C, faut pas rêver, cela dit je conçois que c’est beaucoup plus simple à utiliser, c’est qd même sympa ce qu’ils ont fait pour rendre l’apprentissage plus abordable :slight_smile:

pour vos IR, y a pas besoin d’un relais… faut juste utiliser un mosfet (taillé pour la conso dont vous avez besoin, en général je choisis un qui encaisse 2x ce que j’ai besoin, et un faible rdsOn pour éviter qu’il chauffe).

2 « J'aime »

@scalz
Ouh tu m’as perdu là :blush:

Oui, ça fait plusieurs fois qu’on me parle du micropython sur ESP… j’ai pas encore essayé.
J’ai eu une bonne approche du python lorsque j’ai fait mon afficheur à leds et que j’ai développé l’API. C’est un langage qui m’a paru bien structuré et puissant.

Si tu veut te lancer en MicroPython j’ai fait un tutoriel tout publique qui part de comment installer MicroPython sur ESP et comment dialoguer avec Jeedom
c’est ici :

1 « J'aime »

@Theduck38

Bon c’est pas tout ça j’attaque le tuto ESP32-CAM la cam viens de m’être livré …

Je chasse aprés mon FTDI et j’attaque …

Une question par défaut y a t’il un firmware dedans avant de tout casser juste pour vérifier si la cam fonctionne ?

1 « J'aime »

FTDI trouvé le plus long a été d’emboîter le câble de la cam avec mes gros doigts

Bon la réponse est oui pour le soft embarqué

ça fonctionne en mode AP 192.168.1.4

url pour obtenir l’image 192.168.1.4/jpg

youpi j’ai l’image reste les affaires de CODE :wink:

A priori pas dans tous, le mien semblait vide (pas d’ap diffusé)

Bon un retour sur le tutoriel (suivi au pied de la lettre :slight_smile: )

Quand tu dit que le moniteur doit indiquer :

19:59:27.029 -> ets Jun  8 2016 00:22:57
19:59:27.029 -> 
19:59:27.029 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
19:59:27.029 -> configsip: 0, SPIWP:0xee
19:59:27.029 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
19:59:27.029 -> mode:DIO, clock div:1
19:59:27.029 -> load:0x3fff0018,len:4
19:59:27.029 -> load:0x3fff001c,len:1216
19:59:27.029 -> ho 0 tail 12 room 4
19:59:27.029 -> load:0x40078000,len:9720
19:59:27.029 -> ho 0 tail 12 room 4
19:59:27.029 -> load:0x40080400,len:6352
19:59:27.029 -> entry 0x400806b8
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
19:59:32.632 -> LinkedListElement (0x3ffe3060)->(0x3ffe3060)->(0x3ffe3060)
19:59:32.632 -> Creating TSP streamer
19:59:32.632 -> Created streamer width=1600, height=1200

Les 3 dernières lignes n’apparaissent que lorsque le changement AP a été fait …

Voilà Bravo :+1: :+1: :+1:

Me reste plus qu’a voir la partie Jeedom

et aprés tout ça mettre du micropython pour voir si ça le fait.

merci olive

je vais pas te mentir que j’ai hâte de voir ce tu peux en faire :slight_smile:

en board intéressante le TTGO journal par exemple qui embarque OLED+CAM + MICRO + temperature etc…

mais c’est moins bien suivi

Salut @drs
je suis en train de tester ta version 1.4 j’ai un probleme avec la librarie :
ESPAsyncWebServer

erreur :
/home/pi/Arduino/libraries/ESPAsyncWebServer-master/src/ESPAsyncWebServer.h:33:22: fatal error: AsyncTCP.h: No such file or directory

j’ai peut être pas charger la bonne …

Aurait tu un github source de celle que tu a utiliser ou un zip de la tienne ?

par avance merci

Edit c’est bon j’ai trouve il me manquait le AsyncTCP pour esp 32

Un premier bricolage de ta version 1.4

 String url = "http://" + WiFi.localIP().toString() + ":" + WSPORT + "/snapshot.jpg";
  reply += "<p>Snapshot URL:<br> <a href='" + url + "'>" + url + "</a>";
  url = "http://" + WiFi.localIP().toString() +":" + WSPORT + "/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() +":" + WSPORT + "/_ac";
  reply += "<p>WiFi settings page:<br> <a href='" + url + "'>" + url + "</a>";

  url = "http://" + WiFi.localIP().toString() +":" + ASYNCPORT + "/relay/on";
  reply += "<p>Led_on:<br> <a href='" + url + "'>" + url + "</a>";

  url = "http://" + WiFi.localIP().toString() +":" + ASYNCPORT + "/relay/off";
  reply += "<p>Led_off:<br> <a href='" + url + "'>" + url + "</a>";


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

Le résultat :wink:
Capture d’écran du 2020-05-20 11-03-20

quelques correction de code pour la version 1.4 de @drs :

   reply += F("FALSE");
  }
  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 += "<p>Snapshot URL:<br> <a href='" + url + "'>" + url + "</a>";
  url = IPH + "/mjpg";
  reply += "<p>HTTP MJPEG URL:<br> <a href='" + url + "'>" + url + "</a>";
  url =  IPR + "/mjpeg/1";
  reply += "<p>RTSP MJPEG URL:<br> " + url;
  url =  IPH + "/_ac";
  reply += "<p>WiFi settings page:<br> <a href='" + url + "'>" + url + "</a>";

  url =  IPA + "/relay/on";
  reply += "<p>Led_on:<br> <a href='" + url + "'>" + url + "</a>";

  url =  IPA + "/relay/off";
  reply += "<p>Led_off:<br> <a href='" + url + "'>" + url + "</a>";


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

Waow, je me suis absenté quelques jours et ça a bien avancé !
Je n’ai pas eu le temps de tester la version de @drs… bon, je crois qu’il fait moche le WE prochain.

Concernant la présence ou non d’un firmware dans la caméra reçue, moi je n’avais rien dans les miennes ; mais je les ai toutes achetées au même endroit.

1 « J'aime »

@olive
Tu peux poster le code en entier en incrémentant la version et la passer en 1.5? Ca sera plus facile pour le suivi :blush:

Oui pas de problème

le voici :

CODE 1.5
//---------------------------------------------------------------------------------------------------------------------
// 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.5 mercredi 20 mai 2020 12h30
//
// 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/relay           Etat de la led (format binaire)
// http://ip-esp:ASYNCPORT/relay/on        Allume la LED
// http://ip-esp:ASYNCPORT/relay/off       Eteint la LED
// http://ip-esp:ASYNCPORT/relay/toggle    Allume/Eteint la LED
//
// 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_XGA   // 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 = 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 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 += "<p>Snapshot URL:<br> <a href='" + url + "'>" + url + "</a>";
  url = IPH + "/mjpg";
  reply += "<p>HTTP MJPEG URL:<br> <a href='" + url + "'>" + url + "</a>";
  url =  IPR + "/mjpeg/1";
  reply += "<p>RTSP MJPEG URL:<br> " + url;
  url =  IPH + "/_ac";
  reply += "<p>WiFi settings page:<br> <a href='" + url + "'>" + url + "</a>";

  url =  IPA + "/relay/on";
  reply += "<p>Led_on:<br> <a href='" + url + "'>" + url + "</a>";

  url =  IPA + "/relay/off";
  reply += "<p>Led_off:<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;
  }

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

Arhh, comment a-tu fait ça en BBcode ???

C’est pas du bbcode tu a pas suivi le tuto avec le boot ?

bon allez c’est roue cranté → cacher le texte :wink:

Edit: si tu a des Bbcode qui fonctionne ici je suis preneur

Merci.
Moi aussi je suis preneur ; je n’arrive déjà pas à faire du texte souligné ! :grinning:

Voila mis la 1.5 ce matin et tout fonctionne :blush:

Pour moi, il reste à voir:

  • les deux serveurs web qui tournent
  • pouvoir activer/desactiver des flux (comme le rstp par exemple)

Mais j’en suis pas capable, moi