Surveiller par caméra Wifi l'impression 3D dans le caisson, dans une autre pièce.
Seuls les appareils connectés au Wifi peuvent accéder
aux images de l'ESP. Évidemment ce serait encore mieux si
vous avez le Wifi activé sur votre box,
et un serveur web sur le réseau local.
Comme ce n'est pas mon cas, je passe par un tracke
chez l'hébergeur pour signaler l'IP de l'ESP.
Trois façons d'accéder aux images:
http://ip/img
http://localhost/client.php ou
http://apps.masta.fr/cam/client.php
Exécutable C qui enregistre les images dans un dossier
Pourquoi ne pas acheter une camera wifi sur amazon à 30€?
Les caméras du marché passent par un serveur chinois. Le jour où le serveur coupe caméra poubelle.
Aussi le prix total, boitier inclus sur mesure: 4€ voir onglet Composants
C'est sympa de faire du C++ embarqué, et une introduction à l'ESP32-CAM (un peu plus compliqué qu'un ESP32 standard),
et l'installation/configuration de l'Arduino IDE sous Linux.
TUTORIEL PROGRAMMATION ESP32-CAM ET ARDUINO IDE SUR LINUX
En haut l'antenne interne et le socle pour antenne externe
Vérifier résistance antenne wifi (avec une loupe) ici sur la photo macro l'antenne interne est sélectionnée
En bas à gauche le bouton reset
Dimensions: 42x27mm Alimentation: 5/12V sur le pin 5V. Caméra: OV2640 de l’entreprise OmniVision peut filmer 15 images par seconde avec une résolution de 2 mégapixels.
À noter: l'ESP doit basculer 90° sur sa gauche pour prendre une image droite.
Cet ESP existe en deux versions: câble caméra court, et câble long qui place la caméra quasiment au centre du lecteur de carte SD. C'est ce modèle en photo
Au centre en haut: emplacement carte microSD
Le flash est en bas à droite rond jaune
Brancher la caméra. Basculer avec l'ongle le support gris (on voit ses deux axes)
insérer la nappe et refermer.
Pour téléverser le programme sur l'ESP32, le connecter au convertisseur USB vers TTL
Commencer par placer un jumper entre IO0 et GND sur l'ESP32 (en orange sur le shéma).
Comme la caméra va fonctionner il faut alimenter l'ESP32 en 5V, sinon 3.3V auraient suffit.
CP2102
ESP32-CAM
GND
GND
5V
5V
TXD
U0R
RXD
U0T
Jumper
Installer Arduino IDE
Dans Synaptic installer le paquet arduino. Il installera automatiquement 315Mo.
$ sudo apt-get install arduino
Mais Apt plante il ne trouve pas deux paquets java.
Donc aller sur le site arduino,
télécharger l'IDE 122Mo,
l'extraire et depuis une console lancer ./install.sh
Dans le menu démarrer il est dans la section Programmation.
(Ou télécharger le fichier .appimage, chmod +x fichier.appimage et le lancer)
Configurer Arduino IDE pour ESP32
Menu Fichier/Préférences/URL de gestionnaire de cartes supplémentaires
Ajouter https://dl.espressif.com/dl/package_esp32_index.json
Cliquer sur OK puis
Menu Outils/Type de carte/Gestionnaire de carte saisir esp32 et installer
Enfin sélectionner cette carte: Menu Outils/Type de carte/AI Thinker ESP32-CAM en bas de la liste
À ce stade, le menu Fichier/Exemples contient des exemples ESP32.
En programmation on procède par étapes et par briques.
Écrire un premier programme de test (pour vérifier que l'ESP fonctionne) qui fait clignoter le flash
// Définition des constantes globales#define PORT_LED_FLASH 4 // Numéro de port auquel est branchée la LED servant de flash.// Fonction de démarrage, s'exécute une seule foisvoid setup()
{
pinMode(PORT_LED_FLASH, OUTPUT);
}
// Fonction principale du programme, s'exécute en bouclevoid loop()
{
digitalWrite(PORT_LED_FLASH, HIGH);
delay(1000);
digitalWrite(PORT_LED_FLASH, LOW);
delay(1000);
}
Compiler. Il se plaint que Python n'est pas installé. Pour arranger ça:
$ sudo ln -s /usr/bin/python3 /usr/bin/python
Téléverser le programme
Brancher l'ESP au PC
Menu Outils/Port: /dev/tty/usb0 apparaît.
Menu Croquis/téléverser (ou bouton barre d'outils) observer la barre de progression et le message "Téléversement terminé"
Débrancher le jumper de l'ESP32, puis appuyer sur le bouton reset.
Normalement il s'allume et le flash clignote.
#include "WiFi.h"constchar* ssid = "AndroidAP31CF";
constchar* password = "abcd1234";
voidsetup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
}
Téléverser le programme, débrancher le jumper.
Ouvrir le moniteur série: Menu Outils/Moniteur série.
Démarrer un point d'accès sur un smartphone, redémarrer avec bouton reset ou en remettant du 5V sur l'ESP32.
Vérifier sur le moniteur série à 115200 bauds, et sur le smartphone qu'un client est connecté.
on voit son adresse MAC
Consulter une page web
Étape suivante: l'ESP se connecte au tracker et affiche la réponse du serveur.
Le fichier php à uploader sur le serveur:
<?phpecho'ok de PHP';
?>
Le programme ESP32 à téléverser et à tester (source):
#include <WiFi.h>#include <HTTPClient.h>constchar* ssid = "AndroidAP31CF";
constchar* password = "abcd1234";
voidsetup() {
Serial.begin(115200);
delay(4000);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
}
voidloop() {
if ((WiFi.status() == WL_CONNECTED)) {
HTTPClient http;
http.begin("https://www.masta.fr/tracker.php");
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
Serial.println(httpCode);
Serial.println(payload);
}
else {
Serial.println("Error on HTTP request");
}
http.end(); //Free the resources
}
delay(10000);
}
Lancer un serveur web sur l'ESP
Ce serveur web capture et envoie une image à chaque requête GET sur /picture.
J'ai pas compris que mettre 50 en qualité d'image la rend encore plus pixélisée ...
Pour finaliser le code de cette caméra Wifi,
dernière étape: indiquer au tracker l'IP. Voir le code complet, le tester.
Quand le programme est validé, les pates sont déssoudées.
L'alimentation de l'ESP32 se fait par un câble USB soudé (source: PC, transfo mural, prise 12V vers USB 5V 500mA).
Tracker HTTP
<?php// si GET['addr'] est renseigné, c'est l'esp qui envoie son IPif(isset($_GET["addr"])) file_put_contents("ip",$_GET["addr"]);
?>
Code ESP32
Cet objet connecté marche à minima pour économiser l'énergie, la chauffe, donc augmenter la durée de vie de l'ESP.
L'ESP32-CAM
Se connecte automatiquement au Wifi,
indique au tracker son IP sur le réseau local,
lance un serveur web,
puis à chaque requête /img allume le flash, prend une photo et l'envoie.
Pour tester l'appli lancer un smartphone en point d'accès.
Un réseau Wifi est facile à créer, noter que l'ESP est programmé pour se connecter sur un réserau Wifi précis avec nom et mot de passe codés en dur donc pas modifiables.
Puis se connecter sur le même Wifi que l'ESP32.
1re méthode, la plus simple
Ouvrir un navigateur web, aller à http://apps.masta.fr/camerawifi/ip (sur votre domaine bien entendu) pour récupérer l'IP de l'ESP sur le réseau local,
et aller à cette adresse
http://xxx.xxx.xxx.xxx/img avec flash
http://xxx.xxx.xxx.xxx/im sans flash
Rafraichir la page rafraichit l'image.
2e méthode
Ces trois opérations en une seule page php:
Noter que cette page demande seulement 1% du processeur, idéal à laisser tourner tout en travaillant sur autre chose en jetant un oeil de temps en temps.
À 1 image par seconde 8% CPU.
<title>Cam impr 3D</title><div style="width:800px;margin:auto;"><img id="img"style="width:800px;height:600px"src=""/><br><div style="float:left">
intervalle image refresh <span id="intervalle"></span>s
<button onclick="setIntervalle()">-</button><button onclick="setIntervalle(true)">+</button></div>
Flash <input type="checkbox"id="flash"/><div style="float:right"><button onclick="seconds=0">Reset temps</button>
<span id="duree"></span></div></div><script>const divduree = document.getElementById("duree");
let seconds = 0; // nombre de secondes écouléesconst divinterval = document.getElementById("intervalle");
let interval = 5;// secondes entre chaque appel d'imagelet cpt = 0; // quand cpt arrivera à 5, demander une image
divinterval.textContent = interval;
const flash = document.getElementById("flash");
const divimg = document.getElementById("img");
function setIntervalle(plus=false){
if(plus) interval++; else interval--;
divinterval.textContent = interval;
}
// une fois par seconde rafraichit l'heure, puis demande une image à chaque intervallesetInterval(function () {
seconds++;
let str = '';
if(seconds >= 3600){
const heures = Math.floor(seconds 3600);
const nbHenS = heures * 3600;
const mn = Math.floor((seconds - nbHenS)60);
const nbMnEnS = mn * 60const s = seconds - nbHenS - mn*60;
str = heures+'h '+mn+'mn'+s+'s';
}
elseif(seconds >= 60){
const mn = Math.floor(seconds/60);
const s = seconds - mn*60;
str = mn+'mn'+s+'s';
}
else str = seconds+'s';
divduree.textContent = str;
cpt++;
if(cpt >= interval){
cpt = 0;
const strflash = (flash.checked) ? "g" : "";
divimg.src = "http://<?phpecho$ip; ?>/im"+strflash+"?"+ newDate().getTime();
}
}, 1000);
</script>
3e méthode: Timelapse, client langage C
Un exécutable écrit en langage C se connecte au tracker pour savoir l'IP de l'ESP sur le réseau local,
puis se connecte en HTTP à l'ESP pour lui demander de prendre une photo, le client C écrit l'image dans un dossier imgs du PC client (connecté au même Wifi).
Compiler avec la commande $ gcc -Wall -s timelapse.c -o timelapse
Puis pour demander 1 image toutes les 30 secondes, exécuter avec la commande ./client 30
Outil pour avoir une idée de la durée du film final afin d'ajuster l'intervalle de capture
Retenir Film court, intervalle long (1 image par minute)
Film long, intervalle court (1 image toutes les 10 secondes)
Intervalle 1 image toutes les secondes
Durée du timelapse / durée de l'impression mn
Film final à FPS
Le dossier contiendra 0 images
Durée du Film 0 secondes
Avec cette méthode c'est 0% CPU.
Simplement jeter un oeil dans la dernière image du dossier.
Quand c'est fini charger la première dans OpenShot ou Avidemux pour produire une vidéo.
// Client Caméra Wifi// Préciser en argv l'intervalle en secondes (5 par défaut)// exemple pour capturer une image toutes les 30 secondes:// ./timelapse 30#include<arpa/inet.h>#include<assert.h>#include<netdb.h>#include<netinet/in.h>#include<stdbool.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/socket.h>#include<unistd.h>char ip[15];
/*============================================================================*/// Se connecte au Tracker pour obtenir l'IP de l'ESP32-CAM.// Problème: mon sous-domaine est automatiquement redirigé en HTTPS// donc un simple wget puis lecture du fichier téléchargévoid getIpFromTracker(){
FILE* fp;
long lSize;
system("wget -q https://apps.masta.fr/camerawifi/ip");
fp = fopen ("ip", "r"); if(fp==NULL) exit(1);
fseek(fp, 0, SEEK_END);
lSize = ftell(fp);
rewind(fp);
fread (ip,1,lSize,fp);
fclose(fp);
system("rm ip");
}
/*============================================================================*/// Se connecte au port 80 et retourne un socket connectéint connexionHTTP(){
int sk = -1;
struct protoent *protoent;
struct hostent *hostent;
in_addr_t in_addr;
struct sockaddr_in sockaddr_in;
// Build the socket
protoent = getprotobyname("tcp");
if (protoent == NULL) { perror("getprotobyname"); exit(EXIT_FAILURE);}
sk = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
if (sk == -1) {perror("socket");exit(EXIT_FAILURE);}
// Build the address
hostent = gethostbyname(ip);
if (hostent == NULL) {fprintf(stderr, "error: gethostbyname(\"%s\")\n", ip); exit(EXIT_FAILURE);}
in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
if (in_addr == (in_addr_t)-1) {fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));exit(EXIT_FAILURE);}
sockaddr_in.sin_addr.s_addr = in_addr;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(80);
// connexion maintenantif (connect(sk, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {perror("connect"); exit(EXIT_FAILURE);}
return sk;
}
/*============================================================================*/// Envoi de la requête HTTP, Réception et analyse de la réponse du serveur HTTPvoid request_image(){
char s[BUFSIZ];
enum CONSTEXPR { MAX_REQUEST_LEN = 1024};
char request[MAX_REQUEST_LEN];
ssize_t nbytes_last;
int contentLength=0, b=0, n, nbytes=0, octetsrecus = 0;
char numpaquet=0, mot[10];
FILE* fp = NULL;
char nomimage[14]; // imgs/9999.jpg 13+1staticint numimage = 0;
// établit un point de communication full-duplex sur le port 80int sk = connexionHTTP();
// Envoi de la requête HTTP
n = snprintf(request, MAX_REQUEST_LEN, "GET /img HTTP/1.1\r\nHost: %s\r\n\r\n", ip);
while (nbytes < n) {
nbytes_last = write(sk, request + nbytes, n - nbytes);
if (nbytes_last == -1) {perror("write");exit(EXIT_FAILURE);}
nbytes += nbytes_last;
}
// Réception et analyse de la réponse du serveur HTTPwhile(1) {
nbytes = read(sk, s, BUFSIZ);
/* Premier paquet: le serveur répond
HTTP/1.1 200 OK
Content-Length: 28205
Content-Type: image/jpeg
Connection: close
Accept-Ranges: none
ligne vide puis le jpeg avec magic number header et pixels.
Donc parse le 1er paquet pour déterminer le Content-length,
écrire les premiers octets après l'en-tête HTTP du serveur
*/if(numpaquet==0){
numpaquet=1;
for(n=0; n<nbytes; n++){
if(s[n]=='L' && s[n+1]=='e' && s[n+2]=='n' && s[n+3]=='g'){
n += 8;
while(s[n] != '\r' && s[n] != '\n') {mot[b++]=s[n++];}
mot[b] = 0;
contentLength = atoi(mot);
//printf("content-length:%d\n", contentLength);// ouvre le fichier en écriture binairesprintf(nomimage,"imgs/%03d.jpg", numimage++);
fp = fopen(nomimage,"wb");
// poursuit jusqu'à la ligne vide de séparation...while(n++){if(s[n] == '\n' && s[n+1] == '\r') break;} n+=3;
// pour copier les premiers octets de l'image jpeg
octetsrecus = nbytes-n;
fwrite (s+n, octetsrecus, 1, fp);
break;
}
}
}
// sinon écriture du paquet entrantelse {
fwrite (s, nbytes, 1, fp);
octetsrecus += nbytes;
}
//printf("paquet %d octets écrit %d sur %d\n", nbytes, octetsrecus, contentLength);// Vérifie s'il faut stopper grâce au content-length,// sinon ça part en timeout il bloque indéfiniment sur read()if(octetsrecus >= contentLength) break;
}
if(fp) fclose(fp);
close(sk);
}
/*============================================================================*/int main(int argc, char** argv) {
int interval = 5; //secondesif(argc > 1) interval = atoi(argv[1]);
getIpFromTracker();
printf("IP from Tracker [%s]\nStart timelapse 1 image toutes les %d secondes ...\nCTRL-C pour quitter\n", ip, interval);
// boucle périodiquewhile(1){
request_image();
sleep(interval);
}
return0;
}
Composants
Le boitier imprimé
Attention l'ESP existe en deux versions ici c'est la version câble caméra long
Il est imprimé en ABS et aéré car l'ESP chauffe. fichier STL