L'élément distinctif de la carte de développement ESP32-CAM est sa caméra. Espressif a crée un serveur web, CameraWebServer
, qui affiche un flux vidéo en continu ou qui peut saisir des instantanés. Cet exemple est très élaboré. En outre, de très nombreux paramètres de la caméra peuvent être ajustés en temps réel à l'aide d'une interface web. La détection et la reconnaissance des visages sont même possibles, mais ces fonctionnalités ne sont plus activées par défaut dans la dernière version du serveur datant d'octobre 2021. On retrouve dans cette version une prise en charge partielle du flash. Il semblait utile de compléter cet élément, d'ajouter un adressage IP fixe et de mettre en oeuvre le protocole mDNS pour faciliter l'accès à la caméra depuis le réseau local. Le résultat est un référentiel sur GitHub : CameraWebServer-YAF compatible avec les environnements de développement Arduino et PlatformIO.
Les impatients qui veulent essayer le plus rapidement possible le serveur vidéo pour sur ESP32 développé par Espressif peuvent se rendre immédiatement à la section 7. Projet PlatformIO/Arduino modifié. Les sections précédentes documentent
- comment adapter un croquis de l'EDI Arduino en un projet PlatformIO tout en préservant sa compatibilité avec l'EDI Arduino,
- comment utiliser l'interface web du serveur vidéo,
- comment ajouter une adresse IP fixe ou un nom d'hôte local (mDNS),
- comment activer la détection et la reconnaissance des visages lorsque c'est possible,
- comment compléter la prise en charge de la DEL commencée par Espressif.
Ceux qui ont évité ces longues discussions décideront peut-être de revenir en consulter une partie quand il sera temps de se servir de la caméra. Une lecture linéaire de ce long billet n'est certainement pas nécessaire.
Table des matières
- Serveur vidéo
- Interface web de la caméra IP
- Utilisation de la caméra IP
- Adresse IP statique ou mDNS
- Détection et reconnaissance des visages
- Prise en charge du flash
- Projet PlatformIO/Arduino modifié
Serveur vidéo
Le projet CameraWebServer qu'on retrouve parmi les exemples proposés par Espressif sur son site GitHub arduino-esp32
n'est pas disponible dans PlatformIO ou, du moins, je n'ai pas réussi à le trouver. Ce n'est pas très grave, car les fichiers du projet sont facilement téléchargés. Pour commencer, j'ai créé le projet CameraWebServer
dans le répertoire /home/michel/Documents/PlatformIO/Projects/esp32/esp32_cam/
. L'EDI créera l'arborescence de base.
Il faut ouvrir un terminal, choisir le dossier /src
comme répertoire de travail et éliminer le fichier main.cpp
qui s'y trouve. Puis on télécharge les quatre fichiers du projet avec une seul wget
très long réparti sur cinq lignes.
Quand le téléversement est complété, il est préférable de changer l'extension .ino
à .cpp
. On pourrait carrément renommer CameraWebServer.ino
en main.cpp
selon l'usage dans PlatformIO. Puis il faut apporter quelques changements à ce fichier.
- Ajouter
#include Arduino.h
au tout début du croquis. - Sélectionner la bonne « caméra »
- Rajouter
\\
devant#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
à la ligne 12 - Enlever
\\
devant#define CAMERA_MODEL_AI_THINKER // Has PSRAM
à la ligne 19
- Rajouter
- Remplacer ********* par le nom du réseau Wi-Fi à la ligne 24.
- Remplacer ********* par le mot de passe du réseau Wi-Fi à la lign 25.
L'EDI PlatformIO affiche six avertissements concernant des variables non utilisées dans le fichier app_httpd.cpp
. Ce ne sont pas des erreurs, les variables sont utilisées dans des entrées de journalisation qui ne sont pas activées par défaut. Il y a une erreur au sujet de la fonction itoa
qui ne serait pas définie. Or ce n'est pas une erreur, puisque l'on peut compiler le programme qui fonctionne correctement malgré ce qu'en dit PlatformIO
. Toutefois, si l'on ne veut plus voir cette mention d'erreur, il suffit d'ajouter #include "stdlib_noniso.h"
au début du fichier.
La compilation du programme devrait se faire sans problème et alors on n'a qu'à téléverser le résultat vers le ESP32-CAM comme on l'a fait avec le croquis Blinks
.
Interface web de la caméra IP
Après le téléversement du nouveau micrologiciel du ESP32, il faut redémarrer le module. C'est avantageux de laisser la connexion en série en place puisque l'adresse IP du serveur Web sera affichée sur le terminal.
Si l'on se connecte à l'URL affichée avec un navigateur, on obtient l'interface web de la caméra. Initialement, on ne voit que le panneau de configuration, mais le flux vidéo sera affiché à la droite de celui-ci lorsqu'on appuie sur le bouton Start Stream (démarrer le flux vidéo). Une image fixe sera affichée au même endroit si l'on active le bouton Get Still (prendre un instantané). Le panneau peut être caché en cliquant sur l'icône du « hamburger » dans le coin supérieur gauche de la page.
Il y a un grand nombre de paramètres qui peuvent être ajusté dont les principaux sont la taille de l'image (sa résolution) et sa qualité (plus sa valeur est petite, meilleure est la qualité de l'image). Je ne prétends pas connaître le rôle de la plupart des contrôles, encore moins ceux des trois sous-menus en bas du panneau. Lorsque la caméra sera dans sa position finale et que j'aurais enlevé la pellicule plastique qui protège la lentille, j'expérimenterai pour trouver les valeurs optimales.
Utilisation de la caméra IP
L'interface web de la caméra est une très bonne façon d'expérimenter avec le dispositif, mais typiquement ce n'est pas ainsi que la plupart voudront exploiter la caméra. Le plus souvent, on utilisera des requêtes HTML pour soit fixer les paramètres de la caméra soit en tirer des images de capture ou un flux vidéo. Dans ce qui suit <cam-ip>
dénote l'adresse IP du dispositif.
Fonction | URL | notes |
---|---|---|
Flux vidéo | http://<cam-ip>:81/stream | Un seul client peut se connecter à la fois |
Capture d'image | http://<cam-ip>:80/capture | Format : JPEG, résolution par défaut : 320x240 pixels |
http://<cam-ip>:80/bmp | Format : BMP, résolution par défaut : 320x240 pixels | |
Status | http://<cam-ip>:80/status | Résultat : une chaîne au format JSON {"0xd3":12,"0x111":0,"0x132":54,"xclk":20,"pixformat":3,"framesize":5,"quality":10,"brightness":0,"contrast":0,"saturation":0,"sharpness":0,"special_effect":0,"wb_mode":0,"awb":1,"awb_gain":1,"aec":1,"aec2":0,"ae_level":0,"aec_value":168,"agc":1,"agc_gain":0,"gainceiling":0,"bpc":0,"wpc":1,"raw_gma":1,"lenc":0,"hmirror":0,"dcw":1,"colorbar":0} |
Paramétrer | http://<cam-ip>:80/control?requête | Format de la requête :
var=paramètre&val=valeur tel var=quality&val=2 ou var=framesize&val=9 |
http://<cam-ip>:80/reg | à documenter | |
http://<cam-ip>:80/greg | à documenter | |
http://<cam-ip>:80/pll | à documenter | |
http://<cam-ip>:80/resolution | à documenter |
Le paramètre framesize
(qui correspond au contrôle Resolution
de l'interface web) prend une valeur de 0 à 13 qui représente les différentes résolutions supportées par la caméra OV2640.
Nom | Résolution | valeur |
---|---|---|
UXGA | 1600×1200 | 13 |
SXGA | 1280×1024 | 12 |
HD | 1280×720 | 11 |
XGA | 1024×768 | 10 |
SVGA | 800×600 | 9 |
VGA | 640×480 | 8 |
HVGA | 480×320 | 7 |
CIF | 400×296 | 6 |
QVGA | 320×240 | 5 |
240X240 | 240×240 | 4 |
HQVGA | 240×176 | 3 |
QCIF | 176×144 | 2 |
QQVGA | 160×120 | 1 |
96X96 | 96×96 | 0 |
Le port par défaut du protocole HTTP est 80. Donc il n'est pas nécessaire de le spécifier explicitement dans les URLs autre que celle qui utilise le port 81 pour la diffusion en continu du flux de vidéo. Une capture d'image peut être initiée avec la requête http://<cam-ip>:80/capture
ou http://<cam-ip>/capture
.
Visionnement du flux vidéo
On peut visionner le flux vidéo en continu du ESP32-CAM dans un navigateur web ou un lecteur vidéo comme VLC de VideoLAN. Il suffit d'utiliser la URL http://<cam-ip>:81/stream
comme emplacement. Bien sur <cam-ip>
représente l'adresse IP du module. À titre d'exemple, voici comment faire dans VLC. À partir du menu principal Média
, on sélectionne Ouvrir un flux réseau...
La fenêtre qui surgit contient un mélange un peu surprenant de français et d'anglais, du moins dans la version que je possède, mais qu'importe, il suffit de saisir l'URL du flux dans le contrôle étiquetté Please enter a network URL
.
On peut faire la même chose dans Celluloid. En fait ce programme est une interface graphique à mpv qu'on peut utiliser directement depuis un terminal.
Le résultat n'est pas très convaincant. Le flux vidéo ne contient pas de code temporel selon l'avertissement. J'ai suivi le conseil et expérimenté avec le nombre d'images à la seconde (fps) pour le fixer finalement à 12 ce qui est faible pour un résolution de 800x600 pixels. Le lecteur FFPlay de FFmpeg
se pleint du format.
Si l'on autorise la journalisation avec cet utilitaire, une quantité imposante d'information est affichée alors que le flux vidéo est joué.
Que veut dire tout cela ? Encore des choses à apprendre.
Il y a une contrainte importante : le serveur web du ESP32-CAM ne peut accepter qu'un seul client du flux vidéo à la fois. Toutefois, on peut simultanément ouvrir la page d'accueil du serveur http://<cam-ip>:80/
(avec ou sans le numéro de port) et modifier en temps réel les paramètres du flux. Ou, comme dans le script, on pourrait lui envoyer des requêtes HTTP avec curl
, wget
ou un navigateur web pour modifier la résolution, la qualité, l'orientation de l'image et ainsi de suite.
Script bash
pour prendre un cliché
Voici un tout petit script bash
qui fixe la résolution de la caméra à 1280×1024 pixels et la qualité de l'image à 4.
Èvidemment il faudra ajuster l'adresse IP en fonction de l'allocation faite par le serveur DHCP du réseau local. Ci-dessous on verra qu'il est facile de donner une adresse IP fixe au ESP32-CAM ou de lui donner un nom d'hôte local ce qui facilite les choses. Ce script peut être invoqué dès que le serveur vidéo est en marche. On peut aussi l'utiliser pour modifier la qualité ou la résolution de flux vidéo qu'on regarde sur un navigateur. Le script pourait être plus rapide que d'ouvrir l'interface web du serveur.
On ajoute quelques lignes au script pour faire une capture d'image au format JPEG qu'on sauvegarde sur le disque de l'ordinateur sur lequel est activé le script. On note le délai qui est nécessaire avant que le ESP32-CAM puisse compléter les ajustements des paramètres de la caméra et que celle-ci soit en mesure de prendre un cliché.
Intégration dans Domoticz
Dès que l’on connait l'URL pour réaliser une capture d'image avec la caméra, il devient très simple de l'intégrer dans un programme de domotique comme Domoticz. Il suffit de cliquer sur Configuration, Plus d'options, Caméras puis d'activer le bouton pour aboutir dans l'écran de définition d'une caméra.
C'est assez évident ce qu'il faut saisir comme valeurs dans les champs de cette fenêtre. N'oubliez pas d'ajuster l'adresse IP et de donner un nom unique au dispositif.
Adresse IP statique ou mDNS
Clairement, il est presque obligatoire que l'adresse IP du ESP32-CAM soit statique pour que l'ajout de la caméra dans Domoticz et que le script fonctionnent. On peut faire cela avec le serveur DHCP du réseau local qui attribuera une adresse IP statique au ESP32 en fonction de son adresse MAC. Avec cette approche il n'y a rien à changer dans le croquis. La façon de procéder est différente selon le dispositif où se trouve le serveur DHCP qui est souvent un routeur provenant d'un fournisseur d'accès à Internet. Si modifier les paramètres du serveur DHCP n'est pas possible, alors on peut manuellement fixer l'adresse IP du ESP32-CAM dans le croquis CameraWebServer.cpp
(ou main.cpp
).
- ajouter les adresses IP suivantes à la ligne 27 après les définitions des paramètres du reseau Wi-Fi.
IPAddress staticIP(192, 168, 1, 127); IPAddress subnet(255, 255, 255, 0); IPAddress gateway(192, 168, 1, 1); IPAddress dns(192, 168, 1, 1);
- ajouter les lignes suivantes avant la connexion au réseau WiFi (
WiFi.begin
)if (!WiFi.config(staticIP, gateway, subnet, dns, dns)) { Serial.println("Static IP configuration failed."); }
Les adresses IP données ci-dessus ne sont qu'indicatives et doivent être ajustées pour chaque réseau domestique. Pour connaître la passerelle (gateway), qui sera le routeur le plus souvent, on peut utiliser la commande ip
ou route
dans Linux.
On peut éviter de fixer une adresse IP statique à l'aide du protocole mDNS. Pour ce faire il faut ajouter une bibliothèque et quelques lignes dans la fonction setup()
du programme CameraWebServer.cpp
(ou main.cpp
). C'est assez simple.
- ajouter la ligne
#include "ESPmDNS.h"
à la ligne 4 après#include <WiFi.h>
. - ajouter les lignes suivantes
if (MDNS.begin("esp32-cam-01")) { MDNS.addService("http", "tcp", 80); Serial.println("mDNS advertising started"); } else { Serial.println("Error starting mDNS"); }à la ligne 105 après la connexion au réseau Wi-Fi dans la fonction
setup()
Dès qu'on redémarre le module, on devrait pouvoir repérer le ESP32-CAM avec un outils comme avahi
dans Linux et ainsi obtenir son adresse IP.
En fait, il n'est souvent pas nécessaire de connaître l'adresse IP, car on peut utiliser le nom d'hôte avec plusieurs logiciels, dont curl
.
Firefox, Celluloid, Domoticz etc. peuvent afficher la vidéo en continu ou télécharger des captures en utilisant une URL contenant le nom d'hôte local.
Détection et reconnaissance des visages
Le fichier app_httpd.cpp
contient deux directives au sujet de la détection et la reconnaissance des visages.
Si la directive CONFIG_ESP_FACE_DETECT_ENABLED
est définie alors une bibliothèque, fd_forward
est ajouté au micrologiciel. Dès lors, le paramètre Face Detection
de l'interface web devient fonctionnel. Si activé et si la résolution de la vidéo est assez faible, alors le serveur vidéo identifie les visages qu'il peut détecter avec un cadre jaune. L'interface affiche le message suivant si l'on tente d'activer la détection des visages avec une résolution telle SXGA (1280x1024)
Nonobstant cet avertissement, il faut que la résolution soit inférieure à CFI (400x296 = 118 400 pixels), donc égale à QVGA (320x240 = 76 800 pixels) ou moins pour pouvoir activer la détection des visages. C'est clairement quelque chose qui demande tellement de calculs qu'il ne serait pas possible de détecter des visages en temps réels s'il fallait analyser 50% de plus de pixels.
Le micrologiciel est capable de faire encore plus, soit la reconnaissance de visages déjà « enrôller ». Il faut définir les deux directives CONFIG_ESP_FACE_DETECT_ENABLED
et CONFIG_ESP_FACE_RECOGNITION_ENABLED
, compiler le projet et puis téléverser le micrologiciel pour y incorporer cette fonctionnalité. Dans l'interface web, la reconnaissance des visages est activée avec le contrôle Face Recognition
. Cette activation entraine l'activation de la détection des visages et le bouton Enroll Face
devient fonctionnel. Avant de reconnaitre un visage, le micrologiciel doit enregistrer le visage à reconnaître. Pour ce faire on présente le visage à la caméra, on attend que le visage soit détecté et l'on clique sur le bouton Enroll Face
. Il faut faire cela cinq fois si je comprends bien la macro ENROLL_CONFIRM_TIMES
tout en modifiant l'angle et peut-être la distance du visage par rapport à la caméra. Dorénavant, quand le microcologiciel detecte un visage il indiquera s'il est reconnu ou non. Je n'ai peu expérimenté ces fonctions, car elles ne sont pas d'un grand intérêt pratique. On est épaté qu'un microcontrôleur puisse réaliser ces exploits, mais les résolutions auxquels ceci est faisable sont bien trop faibles.
Il reste à expliquer comment définir des directives pour les néophytes. Le plus simple, surtout pour l'environnement Arduino, est d'ajouter les directives au début du fichier avant la liste des bibliothèques à inclure.
fd_forward.h
et fr_formward.h
et les autres composants de la bibliothèque de la reconnaissance des visages ne sont plus présents. Cette dernière a été remplacée par la bibliothèque dl (deep learning) qui semble bien plus avancée. Le fichier app_httpd.cpp
ne prend pas en charge cette nouvelle bibliothèque. Espressif a protégé l'ancien code de reconnaissance faciale avec les directives CONFIG_ESP_FACE_DETECT_ENABLED
et CONFIG_ESP_FACE_RECOGNITION_ENABLED
qui ne sont jamais définies. C'est pour cette raison que l'on peut encore activer cette fonctionnalité pour autant qu'on utilise une version plus ancienne de noyau Arduino, comme les versions 2.0.0 or 1.0.6. D'ailleurs la reconnaissance des visages était incorporée dans CameraWebServer
dans les versions 1.0.5 et 1.0.4 et peut-être avant, mais je n'ai rien testé de si vieux.
La version stable de la plateforme Espressif 32
dans PlatformIO présentement utilisée (v.34.0 - 495c689) est encore basée sur le noyau 1.0.6 ce qui veut dire qu'on peut activer la reconnaissance des visages sans problèmes dans cet environnement. Puisque c'est une version bien plus récente du noyau qui est présentement utilisée dans l'EDI Arduino, il faut rétrader vers la version 2.0.0 de esp32
avec le gestionnaire de carte si l'on veut expérimenter avec cet aspect du serveur vidéo.
Prise en charge du flash
Dans le même fichier app_httpd.cpp
il y a une autre directive qui est pertinente pour le ESP32-CAM de Ai Thinker. Si la directive CONFIG_LED_ILLUMINATOR_ENABLED
est définie alors un contrôle additionnel, LED Intensity
est affiché dans l'interface web du serveur vidéo.
L'objectif visé est de fixer l'intensité de la DEL utilisée comme flash. La variable, led_duty
, est clairement créée dans ce but.
En plus de créer des variables, ce fragment de code fixe la valeur de la macro CONFIG_LED_LEDC_SPEED_MODE
à LEDC_HIGH_SPEED_MODE
puisque CONFIG_LED_LEDC_LOW_SPEED_MODE
n'est pas définie. Il est préférable de ne rien changer à ceci comme on verra plus tard. Un peu plus loin dans le fichier, on observe que des fonctions de gestion des requêttes HTTP associent l'étiquette led_instensity
à la variable led_duty
.
Ainsi, lorsqu'on ajuste la valeur du contrôle LED Intensity
dans l'interface web, c'est la valeur de la variable led_duty
qui est modifiée. Ultimement, la fonction enable_led
contrôle l'activation de la DEL.
Cette fonction contient plusieurs macros dont CONFIG_LED_LEDC_CHANNEL
et CONFIG_LED_MAX_INTENSITY
dont il faut fixer les valeurs puisqu'elles ne sont pas définies dans les fichiers du projet. Le nom de la première macro et la fonction ledc_set_duty
dévoilent que l'intensité de la DEL flash est contrôlée par la modulation de largeur d'impulsions (en court, MLI ou PWM pour Pulse Width Modulation selon Shakespeare). La MLI est réalisée par des composants matériels du ESP32 et non par émulation comme sur le ESP8286. Il y a un choix de 8 canaux LEDC (LED Control) pour engendrer un signal MLI sur presque n'importe quelle entrée/sortie du ESP (voir LED Control (LEDC)). Clairement CONFIG_LED_MAX_INTENSITY
est un plafond pour protéger la DEL qui, allumée à pleine puissance, dégage beaucoup de chaleur pouvant diminuer sa durée de vie et nuire au bon fonctionnement du dispositif. Quant à la macro CONFIG_LED_LEDC_SPEED_MODE
égale à LEDC_HIGH_SPEED_MODE
, elle signifie que la MLI est réalisé par un périphérique matériel et pas par émulation.
Pour terminer cette exploration du code de prise en charge du flash, soulignons que ce dernier est activé automatiquement lors de la capture de clichés au format JPEG et lors de la diffusion du flux vidéo en continu.
Malheureusement, tout cet outillage ne donne aucun résultat pratique, car le canal LEDC n'est jamais initialisé et l'entrée/sortie connectée à la DEL flash n'est jamais spécifiée. Grâce a Owen Carter, il a été relativement facile de voir comment initialiser le canal LEDC dans la fonction setup
.
Il faut aussi modifier app_httpd.cpp
. On remplace les fonctions ledc_set_duty
et ledc_update_duty
(déclarés dans ledc.h)
) avec ledcWrite
(déclarée dans esp32-hal-ledc.h
) et on y ajoute trois macros. Il faut aussi remplacer ledc.h
par esp32-hal-ledc.h
parmi les fichiers d'en-têtes à inclure.
Après tout ce bricolage, le flash fonctionne.
Mais comme c'est laid. Des macros répétées sont dans deux fichiers et la valeur maximum d'une macro dépend d'une macro dans un autre fichier. Un programmeur serait horrifié, on n'a qu'à penser aux problèmes pour un tiers qui voudrait changer une de ces valeurs. Un utilisateur serait aussi déçu. Le règlement linéaire de l'intensité du flash n'est pas naturel. En plus, si l'on fixe CONFIG_LED_MAX_INTENSITY
sur une valeur plus faible comme 128 par exemple, alors la moitié du déplacement du contrôle dans l'interface web n'au aucun effet ce qui est très déconcertant.
Le projet final élimine ces problèmes, espérons, de façon élégante. Avant de le présenter, voici un détour ou deux au sujet de la façon de régler l'intensité du flash.
Réalisation de la modulation de largeur d'impulsion
De prime abord, il ne semble pas qu'il ne soit pas possible de moduler l'intensité lumineuse d'une DEL alimentée par un signal digital qui ne peut avoir que l'une de deux valeurs : 0 volts (OFF ou 0) ou 3,3 volts (ON ou 1). Or la DEL est perçue comme allumée en permanence, mais à une intensité plus faible, s'il y a une alternance rapide des valeurs du signal digital. En fait, on exploite la persistance rétinienne qui fait qu'à partir d'une certaine fréquence on ne distingue plus les périodes allumées des périodes éteintes. L'oeil demeure quand même sensible à la quantité totale de lumière qui atteint la rétine et on peut donc modifier l'intensité perçue de la DEL en variant le ratio des durées des états allumés et éteints.
Le rapport cyclique (duty cycle) est le ratio de la durée de la partie active du signal à la durée totale d'un cycle. En d'autres mots c'est la durée de l'état allumé divisée par la somme des durées de l'état allumé et de l'état éteint qui le suit.
Le schéma ci-dessus est une représentation simplifiée de la réalisation matérielle d'une sortie MLI. L'horloge est un signal périodique à haute fréquence (supérieur à 20 KHz pour éviter la gamme audible) qui alimente le circuit. Ce signal est généralement dérivé de l'horologe du microcontrôleur ou d'un oscillateur externe. La valeur du compteur augmente d'un à chaque descente du signal de l'horloge. Un compteur de 10 bits, comme dans la figure, peut compter de 0 à 1023 (= [2^10]-1), avant de recommencer à 0. La sortie du compteur est connectée à deux comparateurs. Le premier est activé quand le compte est 0. Ce signal est connecté à l'entrée S d'une bascule dont la sortie sera alors égale à 1. Le second comparateur le second est activé quand le compte est égal à la valeur dans le registre. La sortie de ce comparateur est connectée à l'entrée R de la bascule et quand cette dernière est activée la sortie de la bascule descend à 0. On contrôle la durée de la partie active de la sortie MLI en modifiant la valeur dans le registre du second comparateur. Pour obtenir un rapport cyclique de 50% on stocke 512 dans le registre (512/1024 = 0.5), alors qu'un rapport de 25% est obtenu avec 1024/4 = 256 et ainsi de suite. Clairement, plus le nombre de bits du compteur est élevé plus la fixation du rapport cyclique sera précise. Plus l'horloge est rapide, plus précise sera la période du signal MLI.
Voici une bonne référence sur le principe de fonctionnement PWM : Modulation de Largeur d’Impulsion de Pierre-Yves Rochat, École Polytechnique Fédérale de Lausanne (2015-07-19, consulté le 2021-12-28).
On retrouve tous ces éléments dans la configuration du canal LEDC du ESP32 utilisé pour le flash.
Il y a huit canaux ou périphériques LEDC, il faut en choisir un, mais on ne peut pas utiliser le canal 0 qui est utilisé par la caméra. Le canal 7 est disponible sur le ESP32-CAM.
Quand à la fréquence de l'horloge elle a été fixée à 50 KHz.
Un compteur de 8 bits qui correspond à l'intensité du flash (0 à 255) dans l'interface web a été choisi.
Il ne restait plus qu'à associer la bonne entrée/sortie, celle qui est reliée à la DEL au périphérique LEDC. Enfin, quand on fixe le rapport cyclique dans la fonction enable_led
, c'est la valeur du registre du comparateur qui est spécifié avec la fonction
Si duty = 0
alors la DEL demeure éteinte et si duty = 255
alors la DEL est a pleine intensité. Voilà pour la « mécanique », mais il y a un problème de perception. Si duty est à 128, la tension moyenne de la DEL est réduite de la moitié par rapport à la tension maximum, mais à l'œil on n'a pas la sensation que l'intensité est réduite de la moyenne. C'est une caractéristique de la physiologie humaine que la perception n'est pas une fonction linéaire de l'intensité d'un stimulus.
Ajustement du rapport cyclique
Encore une fois, Owen Carter (easytarget) est notre guide. Voici comment il ajuste la valeur du paramètre de la fonction ledcWrite
qui fixe rapport cyclique du signal MLI.
Source : esp32-cam-webserver.ino, lignes 247-257.
La fonction qui transforme NewVal
(un chiffre entre 0 et 100) en pour obtenir le rapport cyclique brightness
est plutôt rébarbative. En plus, il faut faire attention, car c'est l'échelle qui est transformée de façon logarithmique, brightness
est une fonction exponentielle de newVal
. Cependant, j'ai bien vu que cette transformation est bien supérieure à une fonction linéaire en testant la version du serveur vidéo d'easytarget. Toutefois, y a-t-il une façon plus simple d'arriver à un résultat semblable? Dans la section Finding the not-so-ideal-but-still-quite-good curve d'un article intitulé Programming Volume Controls, Alexander Thomas dit que la transformation y = x4 est très proche de l'idéal qui serait y = abx + c (où a, b et c sont des paramètres à déterminer, mais que clairement il faut que c = -1 si la courbe doit passer par l'origine) quand il s'agit de fixer le volume. J'ai essayé cette approximation, mais le résultat était décevant, car le flash était presque éteint sur plus d'un quart de la plage du contrôle web de l'intensité. Dr. Lex dit qu'on pourrait préférér x3 ou x5. Il était temps de s'amuser un peu avec les mathématiques.
Les puissances de x
figurent sur le premier graphique. La diagonale bleue est la transformation linéaire y=x qu'on veut remplacer. On peut comparer ces puissances à la fonction de Owen Carter sur la figure à droite (ou dessous selon la largeur de l'écran). En fait le graphe qu'on voit est la fonction est normalisée (car newVal
prend des valeurs entre 0 et 100 et x des valeurs entre 0 et 1). On voit clairement que la valeur de y=x4 est beaucoup trop proche de 0 lorsque x est inférieur à 0,3. La fonction y=x4 s'approche le plus de la fonction de Carter, mais elle augmente encore trop lentement au début. La simplicité du carré est difficile à battre. J'ai donc décidé de faire une translation vers la gauche pour se servir d'une partie un peu plus abrupte de la courbe. Il faut aussi faire une translation vers le bas pour que la courbe passe par l'origine et enfin il faut appliquer une constante multiplicative pour que la courbe passe par le point (1; 1) :
Clairement, qu'importe a, y=0 si x=0 et y=1 quand x=1. Le truc est de trouver la valeur optimale de a. J'ai choisi a=0,3 et comme on peut voir le résultat est assez proche de la fonction désirée tout en étant plus simple.
Malheureusement, en éliminant l'inclusion de la bibliothèque math.h
la taille du micrologiciel n'est pas réduite. C'était une des motivations pour éviter la fonction d'Owen Carter. L'autre objectif était que la transformation soit plus rapidement calculée. Un petit test a montré sans aucun doute que la nouvelle fonction est beaucoup plus rapide, mais ce calcul est fait si peu souvent que tout gain d'efficacité n'est probablement pas significatif.
Si la question de la transformation d'un ajustement linéaire vers une fonction exponentielle qui correspond mieux aux perceptions humaines est d'intérêt, il y a une autre famille de fonctions qui vaut la peine d'être examinée:
y = (ax - 1)/(a - 1), a > 1.
Évidemment quand a = 4, on a la fonction de Carter, mais avec des valeurs supérieures à 4, on obtient une courbe plus bombée qui, néanmoins, se comporte relativement bien autour de l'origine.
Pour ceux qui préféraient la fonction d'Owen Carter (easytarget), elle est activée en définissant la directive CONFIG_EASYTARGET_INTENSITY_SCALING
au début du fichier app_httpd.cpp
. En fin de compte, la bibliothèque math.h
est incluse car la fonction pow
facilite grandement le calcul de pwmMax
sans accroître la taille du micrologiciel.
Projet PlatformIO/Arduino modifié
Le référentiel CameraWebServer-YAF sur GitHub contient toutes les modifications décrites ici. Mon objectif était de modifier le moins possible les fichiers originaux d'Espressif, mais il y a quand même d'assez nombreux changements dont la plupart ne sont pas très importants. Quant aux ajouts dont il est question ci-dessus je voulais qu'ils soient facultatifs. Cet objectif est réalisé avec l'addition d'un fichier de configuration, nommé config.h
. C'est le seul fichier où l'on doit apporter des modifications, le reste du code est fixe. Après quelques faux départs, j'ai décidé que la meilleure façon d'accommoder les deux environnements était de créer un projet PlatformIO dont le répertoire src
était remplacé par un répertoire nommé CameraWebServer
.
Le fichier CameraWebServer.ino
ne contient plus qu'un commentaire, car son contenu original a été versé dans le fichier main.cpp
. Avec ce stratagème, les exigences de l'EDI Arduino concernant le nom du croquis et du répertoire le contenant sont satisfaites. Puisqu'il n'y a aucune raison d'ouvrir CameraWebServer.ino
dans PlatformIO, on ne verra plus le message d'avertissement du service C/C++ IntelliSense
au sujet des problèmes associés aux fichiers .ino
.
Ceux qui préfèrent l'EDI Arduino n'ont besoin que du dossier CameraWebServer
. On peut obtenir la plus récente archive .zip
contenant uniquement ce répertoire depuis la page Releases du référentiel. On obtient facilement l'archive avec la commande wget
, c'est un peu plus laborieux avec curl
Les personnes qui sont plus à l'aise dans l'environnement PlatformIO trouveront l'arborescence complète du projet dans la même page. Pour obtenir la plus récente version du référentiel, elles devraient plutôt créer un clone du projet ou obtenir une archive zip selon les instructions affichées quand on active le bouton Code sur la page d'acceuil du référentiel.
Dès que le projet est ajouté à l'environnement de programmation de son choix, on peut le compiler. Il y a quelques explications sur la procédure à suivre dans PlatformIO et dans l'EDI Arduino. La compilation devrait se compléter sans erreurs, mais le binaire résultant ne fonctionnera pas tant que le projet n'est pas configuré correctement. Puisque e fichier de configuration est un peu long les explications à son sujet sont scindées en deux.
Configuration minimale
Il y a trois éléments qui doivent obligatoirement être spécifiés correctement.
- Le modèle de la carte de développement avec caméra.
- Le nom du réseau Wi-Fi auquel se branchera le serveur Web.
- Le mot de passe du réseau Wi-Fi.
Il faut choisir une et seulement une carte de développement parmi la liste ce qui est fait en enlevant les deux obliques //
devant la directive #define
appropriée.
Le but de cette sélection, qui se faisait dans le fichier CameraWebServer
dans l'exemple initial, est de définir les entrées/sorties du ESP32 affectées à la caméra. Les connexions sont définies dans le fichier camera_pins.h
. Cette liste contient des modules qui ne sont plus disponibles. Sans doute existe-t-il de nouveaux modèles qui ne sont pas dans cette liste. Peut-être que le brochage de la caméra sur une nouvelle carte est le même que sur l'une de ces anciennes cartes et on pourra alors utiliser ce modèle. Autrement on peut choisir CAMERA_MODEL_CUSTOM_CAM
et définir plus loin ses entrées/sorties.
Après avoir sélectionné le modèle, il faut définir des paramètres particuliers pour celui-ci. Voici ces valeurs pour le ESP32-CAM de AI Thinker et plusieurs de ses imitateurs.
La macro LED_BUILTIN
qui identifie l'entrée/sortie affectée à une DEL témoin est souvent déjà spécifiée dans la définition de la carte. Ce n'est pas le cas pour le ESP32-CAM. On allume la DEL avec un signal LOW
. À noter que ces deux macros ne sont pas utilisées dans le croquis. Après viennent les paramètres pour la DEL qui sert de flash photographique. Tout cela a été expliqué ci-dessus, inutile d'y revenir. Enfin il y a quatre directives qui contrôlent la résolution de la caméra, la qualité de l'image et l'orientation physique de la caméra. Voici la liste des valeurs possibles de la macro CONFIG_DEFAULT_RESOLUTION
.
Je n'ai pas d'autre type de modules ESP32 avec caméra alors je n'ai pas ajouté des blocs de configuration spécifique pour chaque modèle. Toutefois, il y a celui-ci
parce qu'il était auparavant dans CameraWebServer.ino
, et il y a aussi celui pour la carte personnalisée.
Pour vérifier cette définition et l'ajout fait à camera_pin.h
, le brochage de ESP32-CAM est utilisé ci-dessus.
Après le choix de la carte de développement, il reste à définir les coordonnées du réseau Wi-Fi.
Après ces trois étapes, il devrait être possible de compiler et de téléverser le micrologiciel sur le ESP32 et d'accéder à l'interface web. On aura alors le même micrologiciel que si l'on avait compilé l'exemple initial. Les directives qui contrôlent les ajouts à l'exemple initial sont présentées dans la section suivante.
Configuration facultative
On pourrait certainement modifier la vitesse de transmission du port sériel du ESP32, mais presque toujours vitesse par défaut est un 115200 bauds. Dans l'environnement PlatformIO il faut fixer la valeur du paramètre monitor_port
sur la même valeur dans le fichier de configuration platformio.ini
alors que dans le moniteur série de l'EDI Arduino cette valeur est choisie dans une liste déroulante au bas de la fenêtre. La deuxième directive du bloc de configuration facultative fixe la priorité des messages journalisés. Par défaut, seuls les erreurs et les messages d'une plus haute priorité sont envoyés vers le port sériel du ESP32. En temps d'utilisation normale de la caméra, il n'y a pas de raison de changer cette valeur, mais j'ai trouvé utile d'abaisser la priorité d'un niveau pour mieux saisir ce qui se produisait quand le flash était activé. On a vu qu'il était possible de prendre des clichés au format JPEG et au format BMP. N'ayant aucune raison d'utiliser ce deuxième format, j'ai rajouté la directive CONFIG_BMP_CAPTURE_DISABLED
pour éliminer cette possibilité. Par défaut elle n'est pas activée et la capture au format BMP demeure possible.
Les autres directives correspondent aux ajouts dont il a été question ci-dessus.
- Il faut définir
CONFIG_STATIC_IP_ENABLED
si l'on veut donner une adresse IP fixe au ESP32. Ce cas échéant, il faut alors ajuster les macros suivantes qu'on retrouve un peu plus loin dans le fichier de configuration.#if defined(CONFIG_STATIC_IP_ENABLED) #define CONFIG_STATICIP "192.168.1.27" #define CONFIG_SUBNET "255.255.255.0" #define CONFIG_GATEWAY "192.168.1.1" #define CONFIG_DNS1 "8.8.8.8" // Facultatif. Premier serveur DNS. Par défaut c'est GATEWAY #define CONFIG_DNS2 "8.8.8.4" // Facultatif. Second serveur DNS. Par défaut c'est GATEWAY #endifSi l'on ne connait pas l'adresse IP de la passerelle, le masque du sous-réseau et ainsi de suite on peut définirCONFIG_SHOW_NETWORK_PARAMS
, compiler et téléverser le micrologiciel pour noter les données du réseau qui seront affichées dans le moniteur sériel. Attention de ne pas définir CONFIG_STATIC_IP_ENABLED en même temps.
Voir : 4. Adresse IP statique ou mDNS - Il faut définir
CONFIG_MDNS_ADVERTISE_ENABLED
si l'on désire que le ESP32 puisse être identifié avec un nom d'hôte local (selon le protocole mDNS) en plus de son adresse IP. Le nom par défaut seraesp32-cam.local
, mais on peut modifier ce nom local un peu plus loin dans le fichier de configuration.#if defined(CONFIG_MDNS_ADVERTISE_ENABLED) //#define CONFIG_LOCAL_HOSTNAME "esp32-cam-01" // Facultatif. Remplace la valeur par défaut "esp32-cam" #endifNe pas ajouter l'extension.local
dans la valeur de la macroCONFIG_LOCAL_HOSTNAME
.
Voir : 4. Adresse IP statique ou mDNS - Il faut définir
CONFIG_ESP_FACE_DETECT_ENABLED
si l'on désire que le module détecte la présence de visages dans le flux vidéo. Cette capacité n'est plus disponible qu'avec les 2.0.1 et plus récentes du noyau ESP32-Arduino.
Voir : 5. Détection et reconnaissance des visages - Il faut définir
CONFIG_ESP_FACE_RECOGNITION_ENABLED
si l'on désire que le module reconnaisse dans le flux vidéo des visages déjà enregistrés. Actuellement, cette capacité n'est disponible qu'avec les versions 1.0.6 et 2.0.0 du noyau ESP32-Arduino.
Voir : 5. Détection et reconnaissance des visages
Enfin, si l'on utilise un module caméra avec une DEL flash et que la macro CONFIG_FLASH_LED
a été définie dans un bloc de configuration spécifique à la carte alors on peut modifier les paramètres du signal MLI qui contrôle l'intensité de la DEL.
Bien sûr que la fréquence du signal ainsi que la résolution du compteur de la durée de la phase allumée de la DEL peuvent être ajustés, mais je n'ai pas encore senti le besoin de le faire. En revanche, on voudra peut-être diminuer la valeur de la macro CONFIG_LED_MAX_INTENSITY
si le flash est allumé pendant la diffusion d'un flux vidéo de longue durée pour éviter la surchauffe. Il y a une longue discussion au sujet du flash ci-dessus: 6. Prise en charge du flash.
Note
Dans un encadré de la section sur la détection et la reconnaissance des visages, il est clairement souligné que la bibliothèque utilisée pour ces fonctions n'est plus inclue dans les versions plus récentes du noyau ESP32-Arduino. Il y a un autre changement du noyau dont il faut tenir compte. Depuis la version 2.0.0 un nouveau champ a été ajouté à la structure camera_config_t
définie dans esp_camera.h
. Appelé grab_mode
, il contrôle la façon dont les images sont extraites du tampon d'images. Par défaut, il est défini sur CAMERA_GRAP_WHEN_EMPTY
, ce qui signifie vraisemblablement qu'une capture d'image n'est effectuée qu'une fois le tampon vide. Étant donné que le tampon contient deux images par défaut, obtenir une prise d'image avec la caméra peut demandé jusqu'à trois clics du bouton Get Still dans l'interface web ou trois requêtes de type http://<cam-ip>:80/capture
ou http://<cam-ip>:80/bmp
. Ce problème est réglé en modifiant la valeur de grab_mode
vers CAMERA_GRAB_LATEST
(voir la réponse de me-no-dev).
Puisque le noyau encore utilisé dans PlatformIO est la version 1.0.6 et que le champ grab_mode
n'existait pas alors, il fallait appliquer la solution de façon sélective.
La paresse était de rigueur lorsqu'est venu le temps de définir la macro NO_GRAB_MODE
dans config.h
.
Cette définition suppose que ce projet ne sera jamais compilé avec une version du noyau ESP32-Arduino plus ancien que 1.0.6. Or CameraWebServer
existait dans les versions 1.0.5 et 1.0.4 du noyau. L'exemple était peut-être disponible dans des noyaux encore plus vieux; je n'ai pas vérifié. Si la compilation du projet échoue avec un message disant que grab_mode
n'existe pas, il faudra ajuster la directive NO_GRAB_MODE
ou passer à un noyau plus récent.