Last active
December 15, 2020 15:58
-
-
Save ypelletier/47512b0f85bb8605f989920b8d93d899 to your computer and use it in GitHub Desktop.
Affichage dans une page web de l'image vidéo d'une ESP32-CAM, avec lent panoramique horizontal obtenu grâce à un servomoteur.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Affichage dans une page web de l'image vidéo | |
d'une ESP32-CAM, avec lent panoramique horizontal | |
obtenu grâce à un servomoteur. | |
Pour plus de détails: | |
https://electroniqueamateur.blogspot.com/2020/02/mouvement-panoramique-avec-esp32-cam-et.html | |
*/ | |
#include "esp_camera.h" | |
#include <WiFi.h> | |
#include "esp_http_server.h" | |
// paramètres de votre réseau WiFi | |
const char* ssid = "**********"; | |
const char* password = "***********"; | |
#define PART_BOUNDARY "123456789000000000000987654321" | |
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; | |
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; | |
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; | |
httpd_handle_t stream_httpd = NULL; | |
httpd_handle_t camera_httpd = NULL; | |
int numero_port; // numéro du port du stream server | |
const int broche_servo = 2; // servomoteur branché à GPIO 2 | |
// rapport cyclique correspondant à la position minimale du servomoteur | |
const int position_min = 3700; | |
// rapport cyclique correspondant à la position maximale du servomoteur | |
const int position_max = 7000; | |
const int vitesse_servo = 10; | |
int position_servo = position_min; | |
bool direction_servo = 1; // 1 à l'allée, 0 au retour | |
// ******************************************************** | |
// stream_handler: routine qui gère le streaming vidéo | |
static esp_err_t stream_handler(httpd_req_t *req) { | |
camera_fb_t * fb = NULL; | |
esp_err_t res = ESP_OK; | |
size_t _jpg_buf_len = 0; | |
uint8_t * _jpg_buf = NULL; | |
char * part_buf[64]; | |
static int64_t last_frame = 0; | |
if (!last_frame) { | |
last_frame = esp_timer_get_time(); | |
} | |
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); | |
if (res != ESP_OK) { | |
return res; | |
} | |
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); | |
while (true) { | |
fb = esp_camera_fb_get(); | |
if (!fb) { | |
Serial.println("Echec de la capture de camera"); | |
res = ESP_FAIL; | |
} else { | |
if (fb->width > 400) { | |
if (fb->format != PIXFORMAT_JPEG) { | |
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); | |
esp_camera_fb_return(fb); | |
fb = NULL; | |
if (!jpeg_converted) { | |
Serial.println("Echec de la compression JPEG"); | |
res = ESP_FAIL; | |
} | |
} else { | |
_jpg_buf_len = fb->len; | |
_jpg_buf = fb->buf; | |
} | |
} | |
} | |
if (res == ESP_OK) { | |
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); | |
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); | |
} | |
if (res == ESP_OK) { | |
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); | |
} | |
if (res == ESP_OK) { | |
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); | |
} | |
if (fb) { | |
esp_camera_fb_return(fb); | |
fb = NULL; | |
_jpg_buf = NULL; | |
} else if (_jpg_buf) { | |
free(_jpg_buf); | |
_jpg_buf = NULL; | |
} | |
if (res != ESP_OK) { | |
break; | |
} | |
// ------- gestion du mouvement du servomoteur ---------------- | |
ledcWrite(broche_servo, position_servo); | |
if (direction_servo){ | |
position_servo = position_servo + vitesse_servo; | |
if (position_servo > position_max){ | |
direction_servo = 0; | |
} | |
} | |
else{ | |
position_servo = position_servo - vitesse_servo; | |
if (position_servo < position_min){ | |
direction_servo = 1; | |
} | |
} | |
} | |
last_frame = 0; | |
return res; | |
} | |
// ******************************************************** | |
// web_handler: construction de la page web | |
static esp_err_t web_handler(httpd_req_t *req) { | |
httpd_resp_set_type(req, "text/html"); | |
httpd_resp_set_hdr(req, "Content-Encoding", "identity"); | |
sensor_t * s = esp_camera_sensor_get(); | |
char pageWeb[175] = ""; | |
strcat(pageWeb, "<!doctype html> <html> <head> <title id='title'>ESP32-CAM</title> </head> <body> <img id='stream' src='http://"); | |
// l'adresse du stream server (exemple: 192.168.0.145:81): | |
char adresse[20] = ""; | |
sprintf (adresse, "%d.%d.%d.%d:%d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3], numero_port); | |
strcat(pageWeb, adresse); | |
strcat(pageWeb, "/stream'> </body> </html>"); | |
int taillePage = strlen(pageWeb); | |
return httpd_resp_send(req, (const char *)pageWeb, taillePage); | |
} | |
// ******************************************************** | |
// startCameraServer: démarrage du web server et du stream server | |
void startCameraServer() { | |
httpd_config_t config = HTTPD_DEFAULT_CONFIG(); | |
httpd_uri_t index_uri = { | |
.uri = "/", | |
.method = HTTP_GET, | |
.handler = web_handler, | |
.user_ctx = NULL | |
}; | |
httpd_uri_t stream_uri = { | |
.uri = "/stream", | |
.method = HTTP_GET, | |
.handler = stream_handler, | |
.user_ctx = NULL | |
}; | |
Serial.printf("Demarrage du web server sur le port: '%d'\n", config.server_port); | |
if (httpd_start(&camera_httpd, &config) == ESP_OK) { | |
httpd_register_uri_handler(camera_httpd, &index_uri); | |
} | |
config.server_port += 1; | |
config.ctrl_port += 1; | |
Serial.printf("Demarrage du stream server sur le port: '%d'\n", config.server_port); | |
if (httpd_start(&stream_httpd, &config) == ESP_OK) { | |
httpd_register_uri_handler(stream_httpd, &stream_uri); | |
} | |
numero_port = config.server_port; | |
} | |
// ******************************************************** | |
// initialisation de la caméra, connexion au réseau WiFi. | |
void setup() { | |
Serial.begin(115200); | |
Serial.println(); | |
Serial.println("===="); | |
// définition des broches pour le modèle AI Thinker - ESP32-CAM | |
camera_config_t config; | |
config.ledc_channel = LEDC_CHANNEL_0; | |
config.ledc_timer = LEDC_TIMER_0; | |
config.pin_d0 = 5; | |
config.pin_d1 = 18; | |
config.pin_d2 = 19; | |
config.pin_d3 = 21; | |
config.pin_d4 = 36; | |
config.pin_d5 = 39; | |
config.pin_d6 = 34; | |
config.pin_d7 = 35; | |
config.pin_xclk = 0; | |
config.pin_pclk = 22; | |
config.pin_vsync = 25; | |
config.pin_href = 23; | |
config.pin_sscb_sda = 26; | |
config.pin_sscb_scl = 27; | |
config.pin_pwdn = 32; | |
config.pin_reset = -1; | |
config.xclk_freq_hz = 20000000; | |
config.pixel_format = PIXFORMAT_JPEG; //YUV422|GRAYSCALE|RGB565|JPEG | |
config.frame_size = FRAMESIZE_VGA; // QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA | |
config.jpeg_quality = 10; // 0-63 ; plus bas = meilleure qualité | |
config.fb_count = 2; // nombre de frame buffers | |
// initialisation de la caméra | |
esp_err_t err = esp_camera_init(&config); | |
if (err != ESP_OK) { | |
Serial.printf("Echec de l'initialisation de la camera, erreur 0x%x", err); | |
return; | |
} | |
sensor_t * s = esp_camera_sensor_get(); | |
// initialisation du servo moteur | |
ledcSetup(2, 50, 16); // canal, fréquence, résolution | |
ledcAttachPin(broche_servo, 2); // broche, canal | |
Serial.println(""); | |
Serial.print("Connexion au reseau WiFi: "); | |
Serial.println(ssid); | |
delay(100); | |
WiFi.begin(ssid, password); | |
while (WiFi.status() != WL_CONNECTED) { | |
delay(250); | |
} | |
Serial.println("WiFi connecte"); | |
Serial.println(""); | |
delay(100); | |
startCameraServer(); | |
Serial.print("La camera est prete. Utilisez 'http://"); | |
Serial.print(WiFi.localIP()); | |
Serial.println("' pour vous connecter."); | |
} | |
// ******************************************************** | |
// loop ne fait rien! | |
void loop() { | |
delay(10000); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment