Caméra ESP32 S3 - Sonnette vidéo intelligente avec un bouton

Une sonnette de magasin avec caméra coûte beaucoup d'argent et nécessite un compte payant. Celle-ci coûte quelques euros et n'a besoin que de votre WiFi.

Un visiteur appuie sur le bouton. L'ESP32 S3 prend une photo et l'envoie sur votre téléphone via Telegram, avec un lien vers la vidéo en direct. Vous regardez la photo tout de suite, et vous appuyez sur le lien quand vous voulez voir ce qui se passe en ce moment. La petite LED sur la carte répond au visiteur, pour qu'il sache que la sonnette a fonctionné.

ESP32 S3 Camera - Smart Video Doorbell with a Button

Ce que vous allez construire :

  1. Un bouton de sonnette qui prend une photo du visiteur
  2. Un message Telegram avec la photo et un lien vers la vidéo en direct
  3. Une page vidéo en direct que n'importe qui avec le lien peut ouvrir sur un téléphone ou un ordinateur
  4. Une LED qui répond au visiteur, pour qu'il sache que la sonnette a fonctionné
  5. Un temps de silence, pour qu'un enfant appuyant dix fois sur le bouton n'envoie pas dix photos

Matériel Requis

1×ESP32 S3 N16R8 OV5640 Camera
1×Alternativement: ESP32 S3 N16R8 OV2640 Camera
1×Câble USB Type-A vers Type-C (pour PC USB-A)
1×Câble USB Type-C vers Type-C (pour PC USB-C)
1×Bouton avec capuchon pour plaque d'essai
1×Kit bouton pour plaque d'essai
1×Bouton poussoir pour montage sur panneau
1×Module bouton poussoir
1×Plaque d'essai
1×Fils de connexion
1×Recommandé: Screw Terminal Expansion Board for ESP32 S3
1×Recommandé: Breakout Expansion Board for ESP32 S3
1×Recommandé: Power Splitter for ESP32 S3

Ou vous pouvez acheter les kits suivants:

1×Kit de Capteurs DIYables (18 capteurs/écrans)
Divulgation : Certains des liens fournis dans cette section sont des liens affiliés Amazon. Nous pouvons recevoir une commission pour tout achat effectué via ces liens, sans coût supplémentaire pour vous. Nous vous remercions de votre soutien.

※ Note:

La carte caméra ESP32 S3 a 40 broches, mais le bornier à vis et la carte de répartition ont 44 broches. Elles fonctionnent quand même ensemble. Placez la carte caméra au centre de la carte à 44 broches, afin que 2 broches restent libres du côté gauche et 2 broches restent libres du côté droit.

Le bornier à vis vous permet de connecter des fils avec un tournevis, sans soudure. La carte de répartition vous donne des broches faciles pour une breadboard.

Comment ça fonctionne

Tout le projet tient en six étapes :

ÉtapeCe qui se passe
1Un visiteur appuie sur le bouton
2La LED s'allume, pour que le visiteur voie que la sonnette fonctionne
3La caméra prend une photo
4L'ESP32 S3 envoie la photo ET le lien vidéo en direct sur votre téléphone via Telegram
5La LED clignote deux fois. Le visiteur sait que le message est en route.
6Vous appuyez sur le lien quand vous voulez regarder la porte en direct

Le message sur votre téléphone ressemble à ceci :

Telegram
Telegram 12:45
Welcome to Telegram!
ArduinoBot 10:19
Chatting with Arduino...
telegram-botfather
BotFather Yesterday
Your bot has been created.

ArduinoBot

bot
Today
Somebody is at the door! Live video: http\://192.168.0.2/stream
10:15 AM

Pourquoi une photo ET un lien ?

Une photo et une vidéo en direct sont bonnes pour des choses différentes :

Photo dans le messageLien vidéo en direct
Arrive tout seulOuiNon, vous devez appuyer dessus
Fonctionne de partoutOui, toujoursSeulement à la maison, sauf si vous faites de la redirection de port
Montre un momentOuiNon, elle montre maintenant
Montre ce que le visiteur fait ensuiteNonOui
Coûte de la batterie et du WiFiQuelques secondesPendant que vous regardez

Ainsi, la photo vous dit que quelqu'un est là, et le lien vous permet de voir ce qu'il fait maintenant. C'est ainsi que fonctionne une vraie sonnette vidéo.

※ Note:

La vidéo en direct s'arrête d'elle-même après 60 secondes. C'est voulu. Pendant que quelqu'un regarde la vidéo, la carte est occupée et ne peut pas voir le bouton. La limite de temps garantit que la sonnette revient toujours à la vie. Pour la changer, modifiez la ligne #define STREAM_SECONDS 60.

How the ESP32 S3 Video Doorbell Works

La LED parle au visiteur

Une sonnette sans réponse est une mauvaise sonnette. Personne ne sait si elle a fonctionné, alors les gens appuient encore et encore. La petite LED de couleur déjà présente sur la carte donne une réponse pour chaque situation :

Ce que fait la LEDCe que ça veut dire
3 clignotements quand vous allumez la carteLa sonnette est prête
Reste alluméeLa photo est en train d'être prise et envoyée. Veuillez patienter.
2 clignotements lentsLa photo a été envoyée. Quelqu'un va venir.
5 clignotements rapidesQuelque chose s'est mal passé. Pas de WiFi, ou Telegram a refusé.
1 long clignotementVous avez appuyé trop tôt après la dernière sonnerie.

※ Note:

C'est la partie qui donne au projet un aspect fini. Le code ne fait que quelques lignes, mais un visiteur qui reçoit une réponse n'appuiera pas vingt fois sur le bouton.

Pourquoi un bouton a besoin d'aide

Un bouton poussoir, ce sont deux morceaux de métal. Quand ils se touchent, ils rebondissent l'un contre l'autre plusieurs fois dans les premières millisecondes. Sans protection, un appui ressemble à cinq appuis, et votre téléphone reçoit cinq photos.

Vous pourriez écrire vous-même le code d'attente, mais la bibliothèque ezButton le fait déjà. Trois lignes suffisent pour tout le travail :

ezButton button(BUTTON_PIN, EXTERNAL_PULLDOWN); // 1. make the button button.setDebounceTime(50); // 2. in setup() button.loop(); // 3. first line in loop()

Ensuite, vous demandez simplement si quelqu'un a appuyé :

if (button.isPressed()) { ringTheDoorbell(); }

※ Note:

isPressed() est vrai seulement au moment où le bouton s'enfonce, pas pendant qu'il est maintenu. Ainsi, un appui fait le travail une seule fois, même quand un visiteur garde le bouton enfoncé pendant trois secondes.

※ Note:

button.loop() doit être la première ligne à l'intérieur de votre propre loop(). C'est la ligne qui lit la broche et fait l'attente. Si vous l'oubliez, le bouton ne fonctionne jamais.

À propos du module de bouton

Nous utilisons un module de bouton, pas un simple bouton poussoir. Un module, c'est le même bouton, mais il est monté sur une petite carte qui a déjà la résistance dessus, et il a trois broches propres. Vous n'avez qu'à brancher trois fils, et rien ne peut être dans le mauvais sens.

Push Button Module

La résistance sur le module est une résistance de tirage vers le bas (pull-down). Elle maintient la broche à GND tant que personne ne touche le bouton :

BoutonLa broche lit
Personne ne le toucheLOW, la résistance la maintient basse
Quelqu'un appuie dessusHIGH, le bouton relie la broche au 3,3V

Nous le disons à la bibliothèque avec un seul mot :

ezButton button(BUTTON_PIN, EXTERNAL_PULLDOWN);

※ Note:

EXTERNAL_PULLDOWN signifie « la résistance est à l'extérieur de la puce, sur le module, et elle tire la broche vers le bas ». La bibliothèque sait alors que HIGH veut dire appuyé.

Avez-vous un simple bouton poussoir au lieu d'un module ? Alors la carte doit créer elle-même la résistance. Le code a les deux lignes. Échangez les marques // :

// A bare push button, with one leg on the pin and the other leg on GND. // The board uses its own resistor inside, and the pin reads LOW when pressed. ezButton button(BUTTON_PIN); // A button module, which carries its own resistor. // ezButton button(BUTTON_PIN, EXTERNAL_PULLDOWN);
Ce que vous avezLigne à utiliserCâblage
Module de boutonezButton button(BUTTON_PIN, EXTERNAL_PULLDOWN);VCC, GND et OUT
Bouton poussoir simpleezButton button(BUTTON_PIN);Une patte à la broche, une patte à GND

※ Note:

N'utilisez jamais les deux. Avec un bouton simple, vous avez besoin de la résistance à l'intérieur de la carte, et avec un module, vous ne devez pas l'utiliser, car le module en a déjà une.

Créer votre bot Telegram

Vous avez besoin d'un jeton de bot (bot token) et d'un ID de chat. Cela prend trois minutes.

Étape 1 : Créer votre bot

  1. Installez Telegram sur votre téléphone, et créez un compte.
  2. Dans Telegram, cherchez @BotFather. Il a une coche bleue.
  3. Ouvrez la conversation avec BotFather et parlez-lui ainsi :
Telegram
Telegram 12:45
Welcome to Telegram!
ArduinoBot 10:19
Chatting with Arduino...
telegram-botfather
BotFather Yesterday
Your bot has been created.
BotFather

BotFather

bot
Today
/newbot
10:15 AM ✓✓
Alright, a new bot. How are we going to call it? Please choose a name for your bot.
10:16 AM
My Doorbell
10:17 AM ✓✓
Good. Now let's choose a username for your bot. It must end in bot.
10:18 AM
my_esp32_doorbell_bot
10:19 AM ✓✓
Done! Congratulations on your new bot. Use this token to access the HTTP API: 8012345678:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
10:20 AM
  1. Copiez le jeton de bot du dernier message et gardez-le en lieu sûr.

Étape 2 : Trouver votre ID de chat

  1. Dans Telegram, cherchez le nom de votre nouveau bot et ouvrez la conversation.
  2. Envoyez n'importe quel message à votre bot, par exemple hello. Cette étape est nécessaire. Telegram ne laisse pas un bot vous écrire avant que vous ne lui ayez écrit.
Telegram
Telegram 12:45
Welcome to Telegram!
ArduinoBot 10:19
Chatting with Arduino...
telegram-botfather
BotFather Yesterday
Your bot has been created.

ArduinoBot

bot
Today
hello
10:15 AM ✓✓
  1. Ouvrez cette adresse dans votre navigateur web. Mettez votre propre jeton de bot à la place de <YOUR_BOT_TOKEN> :
https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates
  1. Le navigateur affiche un long texte. Cherchez "chat":{"id":. Le nombre qui suit est votre ID de chat.

※ Note:

Le navigateur affiche-t-il {"ok":true,"result":[]} avec rien à l'intérieur ? Alors vous n'avez pas encore envoyé de message à votre bot. Retournez à l'étape 2, envoyez hello à votre bot, puis rechargez l'adresse.

※ Note:

Ne montrez jamais votre jeton de bot à personne, et ne le mettez jamais sur un site web ou sur GitHub. S'il fuite, envoyez /revoke à BotFather et vous en obtenez un nouveau.

Le lien vidéo en direct

Le lien à l'intérieur du message Telegram vient d'une ligne en haut du code :

const char *PUBLIC_ADDRESS = ""; // CHANGE IT, or leave it empty
Ce que vous écrivez làCe que le lien devientOù ça fonctionne
Rien (vide)http://192.168.0.2/stream, l'adresse de la carteSeulement à la maison, sur le même WiFi
Votre propre adressehttp://my-house.duckdns.org:8080/streamDe n'importe où dans le monde

Le code choisit tout seul :

String getLiveLink() { if (strlen(PUBLIC_ADDRESS) > 0) return String(PUBLIC_ADDRESS) + "/stream"; return "http://" + WiFi.localIP().toString() + "/stream"; }

※ Note:

Commencez avec le réglage vide. Testez d'abord tout à la maison. C'est seulement quand la sonnette fonctionne bien qu'il faut penser à l'ouvrir sur internet.

Pourquoi le lien peut ne pas fonctionner en dehors de chez vous

L'adresse de la carte, par exemple 192.168.0.2, est une adresse privée. Elle n'existe qu'à l'intérieur de votre maison. Votre téléphone la trouve quand il est sur votre propre WiFi, mais internet ne peut pas la voir du tout. Ainsi, le lien fonctionne à la maison, et ne fait rien quand vous êtes absent.

Pour ouvrir le lien depuis l'extérieur, vous avez besoin d'une adresse publique : l'adresse de votre routeur, avec la redirection de port, pour que le routeur transmette le visiteur à la carte. Chaque routeur fait cela un peu différemment, alors consultez le manuel de votre propre routeur, ou demandez à votre fournisseur internet.

Quand vous l'avez, écrivez cette adresse dans le code :

const char *PUBLIC_ADDRESS = "http://203.0.113.25:8080";

※ Note:

La plupart des routeurs reçoivent de temps en temps une nouvelle adresse publique, et votre lien cesse alors de fonctionner. Un nom DDNS gratuit, par exemple de DuckDNS ou No-IP, pointe toujours vers votre maison, donc vous pouvez écrire http://my-house.duckdns.org:8080 et l'oublier ensuite.

Mettez un mot de passe dessus

Quand vous ouvrez votre caméra sur internet, n'importe qui trouvant l'adresse peut regarder votre porte. Des programmes scannent tout internet toute la journée à la recherche de caméras ouvertes.

Alors, dès que vous utilisez PUBLIC_ADDRESS, activez le mot de passe :

#define USE_PASSWORD true const char *WEB_USER = "admin"; // CHANGE IT const char *WEB_PASSWORD = "esp32s3"; // CHANGE IT

Le navigateur web les demande la première fois, et s'en souvient ensuite.

※ Note:

C'est un mot de passe simple, envoyé en http et non en https. Il arrête un inconnu qui trouve votre adresse, mais il n'est pas assez fort pour une banque. Pour une porte d'entrée, c'est un juste équilibre. Voulez-vous plus de sécurité ? Alors n'ouvrez pas du tout la caméra sur internet, et ne regardez la vidéo en direct que lorsque vous êtes chez vous.

Schéma de câblage entre le bouton et l'ESP32 S3

ESP32 S3 Video Doorbell Wiring Diagram

Cette image a été créée avec Fritzing. Cliquez pour agrandir l'image.

Broche du module de boutonBroche ESP32 S3
VCC (ou +)3.3V
GND (ou -)GND
OUT (ou S, ou SIG)GPIO47

C'est tout le câblage. Trois fils, et aucune résistance.

※ Note:

Vous n'avez pas besoin d'une LED. La carte en a déjà une petite de couleur, et le code utilise celle-ci :

#define LED_PIN LED_BUILTIN

※ Note:

Votre carte n'a pas de LED ? Alors connectez votre propre LED avec une résistance de 220 ohms à une broche libre, par exemple GPIO21, et changez la ligne en #define LED_PIN 21.

Notes de sécurité

Utilisez 3,3V pour le module de bouton, pas 5V. Les broches de l'ESP32 S3 sont faites pour le 3,3V, et le 5V peut les endommager.

Le module de bouton n'a besoin d'aucune résistance supplémentaire, car la résistance est déjà sur le module.

La caméra utilise les broches GPIO4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17 et 18. N'utilisez pas ces broches. GPIO47 est libre, donc nous l'utilisons pour le bouton.

※ Note:

Un vrai bouton de sonnette a quatre pattes, mais seules deux sont nécessaires. Si votre bouton ne fait rien, tournez-le d'un quart de tour sur la breadboard et réessayez. Deux pattes déjà reliées à l'intérieur ne fonctionneront jamais.

Installer les bibliothèques

Ce projet a besoin de deux bibliothèques.

DIYables ESP32 WebServer crée la page vidéo en direct :

  1. Ouvrez Arduino IDE.
  2. Cliquez sur l'icône Libraries dans la barre latérale gauche.
  3. Saisissez DIYables ESP32 WebServer dans la zone de recherche.
  4. Trouvez la bibliothèque de DIYables.io et cliquez sur INSTALL.
  • Search for DIYables ESP32 WebServer created by DIYables.io and click the Install button.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Library Manager
Type:
All
Topic:
All
DIYables ESP32 WebServer by DIYables.io
DIYables ESP32 WebServer library designed for ESP32 boards. It includes multi-page web server capabilities and WebSocket support for real-time communication, perfect for IoT projects and DIYables ESP32 boards. More info
1.0.1
INSTALL
Newbiely.ino
···
1 void setup() {
Output
Serial Monitor
Ln 1, Col 1
ESP32S3 Dev Module on COM15
1

ezButton lit le bouton et s'occupe du rebond :

  1. Saisissez ezButton dans la même zone de recherche.
  2. Trouvez la bibliothèque d'ArduinoGetStarted.com et cliquez sur INSTALL.
  • Search for ezButton created by ArduinoGetStarted.com and click the Install button.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Library Manager
Type:
All
Topic:
All
ezButton by ArduinoGetStarted.com
Button library supports debounce, pressed/released events and the press counting. It is easy to use with multiple buttons. The library can be used for push-button, momentary switches, toggle switch, magnetic contact switch (door sensor)... It is designed for not only beginners but also experienced users. More info
1.0.6
INSTALL
Newbiely.ino
···
1 void setup() {
Output
Serial Monitor
Ln 1, Col 1
ESP32S3 Dev Module on COM15
1

Réglages d'Arduino IDE pour la caméra ESP32 S3

La caméra ne fonctionne pas avec les réglages par défaut d'Arduino IDE. Vous devez changer certains éléments dans le menu Tools avant de téléverser le code.

Élément du menu ToolsSélectionnez ceci
BoardESP32S3 Dev Module
Flash Size16MB (128Mb)
PSRAMOPI PSRAM
Partition SchemeHuge APP (3MB No OTA/1MB SPIFFS)
CPU Frequency240MHz (WiFi)
USB ModeHardware CDC and JTAG
Upload ModeUART0 / Hardware CDC
Upload Speed921600
USB CDC On BootEnabled

Code ESP32 S3

Changez votre WiFi, votre jeton de bot et votre ID de chat en haut du code.

Les réglages que vous pouvez changer :

NomCommence avecCe que ça fait
PUBLIC_ADDRESSvideLe lien dans le message. Vide veut dire l'adresse de la carte.
USE_PASSWORDfalseRéglez-le sur true quand vous ouvrez la caméra sur internet
QUIET_TIME10000Millisecondes à attendre avant la prochaine sonnerie
STREAM_SECONDS60La vidéo en direct s'arrête d'elle-même après ce nombre de secondes
DEBOUNCE_TIME50Millisecondes à attendre, car le bouton rebondit
/* * Ce code ESP32 S3 a été développé par newbiely.fr * Ce code ESP32 S3 est mis à disposition du public sans aucune restriction. * Pour des instructions complètes et des schémas de câblage, veuillez visiter: * https://newbiely.fr/tutorials/esp32-s3/esp32-s3-camera-smart-video-doorbell-with-a-button */ #include <WiFi.h> #include <WiFiClientSecure.h> #include <DIYables_ESP32_WebServer.h> // DIYables ESP32 WebServer library #include <ezButton.h> // ezButton library #include "esp_camera.h" const char *WIFI_SSID = "YOUR_WIFI_SSID"; // CHANGE IT const char *WIFI_PASSWORD = "YOUR_WIFI_PASSWORD"; // CHANGE IT const char *BOT_TOKEN = "1234567890:ABCDefGhIJKlmNoPQRsTUVwXYZ1234567890"; // CHANGE IT const char *CHAT_ID = "1234567890"; // CHANGE IT // The address of the live video that goes into the Telegram message. // // Leave it EMPTY to use the address of the board on your own WiFi. The link then // works only when your phone is at home, on the same WiFi. // // Did you make port forwarding on your router? Then write your own address here, // for example "http://my-house.duckdns.org:8080", and the link works from anywhere. const char *PUBLIC_ADDRESS = ""; // CHANGE IT, or leave it empty // A camera that anybody on the internet can watch is a bad idea. // Set this to true when you use PUBLIC_ADDRESS, and the page asks for a password. #define USE_PASSWORD false const char *WEB_USER = "admin"; // CHANGE IT const char *WEB_PASSWORD = "esp32s3"; // CHANGE IT #define BUTTON_PIN 47 // the OUT pin of the button module is connected to GPIO47 // The board already has a small colour LED on it, so you do not need // an extra LED and resistor. This LED answers the visitor. #define LED_PIN LED_BUILTIN // Has your board no LED on it? Then connect your own LED with a 220 ohm // resistor to a free pin, and use this line instead: // #define LED_PIN 21 #define DEBOUNCE_TIME 50 // wait 50 milliseconds, because the button bounces #define QUIET_TIME 10000 // wait 10 seconds before the next ring (10000 milliseconds) #define STREAM_SECONDS 60 // the live video stops by itself after 60 seconds // Camera pins of the ESP32-S3-N16R8 camera board. // The same pins work for the OV2640, OV3660 and OV5640 versions. #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 15 #define SIOD_GPIO_NUM 4 #define SIOC_GPIO_NUM 5 #define Y9_GPIO_NUM 16 #define Y8_GPIO_NUM 17 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 12 #define Y5_GPIO_NUM 10 #define Y4_GPIO_NUM 8 #define Y3_GPIO_NUM 9 #define Y2_GPIO_NUM 11 #define VSYNC_GPIO_NUM 6 #define HREF_GPIO_NUM 7 #define PCLK_GPIO_NUM 13 #define TELEGRAM_HOST "api.telegram.org" #define BOUNDARY "diyablesphoto" #define STREAM_MARK "diyablesframe" DIYables_ESP32_WebServer server(80); // The line below is for a BARE push button wired to GND. ezButton then // uses the internal pull-up resistor of the board, and the pin reads LOW // while the button is held down. // ezButton button(BUTTON_PIN); // If you use a BUTTON MODULE instead, the module carries its own resistor // on the board, so the internal one must not be used. Comment the line // above and use this line instead: ezButton button(BUTTON_PIN, EXTERNAL_PULLDOWN); unsigned long lastRingTime = 0; const char INDEX_HTML[] = R"rawliteral( <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>ESP32 S3 Doorbell</title> <style> body { background:#111; color:#eee; font-family:sans-serif; text-align:center; margin:0; padding:16px; } img { width:100%; max-width:800px; border-radius:8px; } p { color:#aaa; font-size:14px; } </style> </head> <body> <h2>Who is at the door?</h2> <img id="live" src="/stream"> <p>The live video stops by itself after a minute, so the doorbell button works again.</p> <script> // When the video stops, load the page again and start a new one document.getElementById("live").onerror = function () { setTimeout(function () { location.reload(); }, 1000); }; </script> </body> </html> )rawliteral"; // Returns the name of the camera sensor that is installed on the board const char *getSensorName(int pid) { switch (pid) { case OV2640_PID: return "OV2640 (2 MP, max UXGA 1600x1200)"; case OV3660_PID: return "OV3660 (3 MP, max QXGA 2048x1536)"; case OV5640_PID: return "OV5640 (5 MP, max QSXGA 2592x1944)"; default: return "Unknown sensor"; } } bool initCamera() { camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; config.pin_d3 = Y5_GPIO_NUM; config.pin_d4 = Y6_GPIO_NUM; config.pin_d5 = Y7_GPIO_NUM; config.pin_d6 = Y8_GPIO_NUM; config.pin_d7 = Y9_GPIO_NUM; config.pin_xclk = XCLK_GPIO_NUM; config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; config.pin_sccb_sda = SIOD_GPIO_NUM; config.pin_sccb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; config.frame_size = FRAMESIZE_SVGA; // 800x600 - small, so it is sent quickly config.jpeg_quality = 12; // 0 to 63, a lower number means a better image config.fb_count = 1; config.fb_location = CAMERA_FB_IN_DRAM; config.grab_mode = CAMERA_GRAB_WHEN_EMPTY; if (psramFound()) { config.fb_location = CAMERA_FB_IN_PSRAM; config.jpeg_quality = 10; config.fb_count = 2; config.grab_mode = CAMERA_GRAB_LATEST; } else { config.frame_size = FRAMESIZE_QVGA; Serial.println("WARNING: PSRAM not found. Please set PSRAM to OPI PSRAM in the Tools menu."); } esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x\n", err); return false; } sensor_t *s = esp_camera_sensor_get(); Serial.printf("Camera sensor: %s\n", getSensorName(s->id.PID)); // Each sensor needs slightly different settings switch (s->id.PID) { case OV2640_PID: s->set_vflip(s, 0); s->set_hmirror(s, 0); break; case OV3660_PID: s->set_vflip(s, 1); // the OV3660 image is upside down by default s->set_brightness(s, 1); // make the image a little brighter s->set_saturation(s, -2); // make the colors a little softer break; case OV5640_PID: s->set_vflip(s, 0); s->set_hmirror(s, 0); break; default: break; } return true; } // Builds the link to the live video that goes into the Telegram message String getLiveLink() { if (strlen(PUBLIC_ADDRESS) > 0) return String(PUBLIC_ADDRESS) + "/stream"; return "http://" + WiFi.localIP().toString() + "/stream"; } // Blinks the LED, so the visitor knows what is happening void blinkLed(int times, int speed) { for (int i = 0; i < times; i++) { digitalWrite(LED_PIN, HIGH); delay(speed); digitalWrite(LED_PIN, LOW); delay(speed); } } // Sends one photo and the live link to your Telegram chat bool sendPhotoToTelegram(camera_fb_t *fb) { WiFiClientSecure client; client.setInsecure(); // do not check the certificate of the Telegram server client.setTimeout(15); Serial.println("Connecting to Telegram..."); if (!client.connect(TELEGRAM_HOST, 443)) { Serial.println("Cannot connect to Telegram"); return false; } // The text under the photo, with the address of the live video String caption = "Somebody is at the door!\n"; caption += "Live video: " + getLiveLink(); // The first part of the message: who receives the photo, and the text String head = "--" BOUNDARY "\r\n"; head += "Content-Disposition: form-data; name=\"chat_id\"\r\n\r\n"; head += String(CHAT_ID) + "\r\n"; head += "--" BOUNDARY "\r\n"; head += "Content-Disposition: form-data; name=\"caption\"\r\n\r\n"; head += caption + "\r\n"; head += "--" BOUNDARY "\r\n"; head += "Content-Disposition: form-data; name=\"photo\"; filename=\"doorbell.jpg\"\r\n"; head += "Content-Type: image/jpeg\r\n\r\n"; // The last part of the message String tail = "\r\n--" BOUNDARY "--\r\n"; size_t totalLength = head.length() + fb->len + tail.length(); // Send the HTTP header client.print("POST /bot" + String(BOT_TOKEN) + "/sendPhoto HTTP/1.1\r\n"); client.print("Host: " TELEGRAM_HOST "\r\n"); client.print("Content-Type: multipart/form-data; boundary=" BOUNDARY "\r\n"); client.print("Content-Length: " + String(totalLength) + "\r\n"); client.print("Connection: close\r\n\r\n"); // Send the message client.print(head); // Send the photo in small pieces of 1024 bytes. // We check every piece. If one fails, Telegram would wait for ever. size_t sent = 0; for (size_t i = 0; i < fb->len; i += 1024) { size_t piece = fb->len - i; if (piece > 1024) piece = 1024; size_t written = client.write(fb->buf + i, piece); if (written != piece) { Serial.printf("Sending stopped after %u of %u bytes\n", (unsigned)(sent + written), (unsigned)fb->len); client.stop(); return false; } sent += written; } client.print(tail); // Read the answer of Telegram. // Do NOT stop when the connection closes. Telegram answers and closes at once, // and the answer is often still waiting in the buffer at that moment. String answer = ""; unsigned long startTime = millis(); while (millis() - startTime < 15000) { while (client.available()) { answer += (char)client.read(); startTime = millis(); } if (answer.length() > 0 && !client.connected()) break; // the whole answer arrived and the server hung up delay(10); } client.stop(); if (answer.length() == 0) { Serial.println("Telegram did not answer at all."); Serial.println("Check your internet, and check that your network does not block Telegram."); return false; } if (answer.indexOf("\"ok\":true") > 0) { Serial.println("The photo and the live link were sent to Telegram"); return true; } Serial.println("Telegram did not accept the photo. The answer was:"); Serial.println(answer); return false; } // The whole doorbell: light on, photo, send, answer with the LED void ringTheDoorbell() { Serial.println("The doorbell was pressed!"); digitalWrite(LED_PIN, HIGH); // the light stays on while we work camera_fb_t *fb = esp_camera_fb_get(); if (!fb) { Serial.println("Photo capture failed"); digitalWrite(LED_PIN, LOW); blinkLed(5, 100); // five fast blinks mean "something went wrong" return; } Serial.printf("Photo taken: %u bytes\n", fb->len); bool sent = sendPhotoToTelegram(fb); esp_camera_fb_return(fb); // give the memory back to the camera digitalWrite(LED_PIN, LOW); if (sent) blinkLed(2, 200); // two slow blinks mean "the message is on its way" else blinkLed(5, 100); // five fast blinks mean "something went wrong" } // Shows the web page with the live video void handleHome(WiFiClient &client, const String &method, const String &request, const QueryParams &params, const String &jsonData) { server.sendResponse(client, INDEX_HTML, "text/html"); } // Sends the live video to the phone or the computer that opened the link void handleStream(WiFiClient &client, const String &method, const String &request, const QueryParams &params, const String &jsonData) { Serial.println("Somebody is watching the live video"); client.print("HTTP/1.1 200 OK\r\n"); client.print("Content-Type: multipart/x-mixed-replace; boundary=" STREAM_MARK "\r\n"); client.print("Access-Control-Allow-Origin: *\r\n"); client.print("Connection: close\r\n\r\n"); unsigned long startTime = millis(); while (client.connected()) { // Stop by ourselves after a while, so the doorbell button works again if (millis() - startTime >= (unsigned long)STREAM_SECONDS * 1000) { Serial.println("The live video stopped after the time limit"); break; } camera_fb_t *fb = esp_camera_fb_get(); if (!fb) { Serial.println("Frame capture failed"); break; } size_t len = fb->len; client.print("--" STREAM_MARK "\r\n"); client.print("Content-Type: image/jpeg\r\n"); client.printf("Content-Length: %u\r\n\r\n", len); size_t sent = client.write(fb->buf, len); client.print("\r\n"); esp_camera_fb_return(fb); if (sent != len) // the phone closed the page break; } Serial.println("The live video was closed"); } void setup() { Serial.begin(115200); delay(1000); // The library watches the button and handles the bouncing for us button.setDebounceTime(DEBOUNCE_TIME); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); if (!initCamera()) { Serial.println("Stopped"); blinkLed(10, 100); while (true) delay(1000); } WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting to WiFi"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); WiFi.setSleep(false); // keep WiFi fast for the live video if (USE_PASSWORD) server.enableAuthentication(WEB_USER, WEB_PASSWORD); server.addRoute("/", handleHome); server.addRoute("/stream", handleStream); server.begin(); Serial.print("Live video on your own WiFi: http://"); Serial.print(WiFi.localIP()); Serial.println("/stream"); Serial.println("The link inside the Telegram message is: " + getLiveLink()); blinkLed(3, 150); // three blinks mean "the doorbell is ready" Serial.println("Ready. Waiting for somebody to press the button..."); } void loop() { server.handleClient(); // answers the phone that wants to watch the live video button.loop(); // the library must do its work first, in every loop // isPressed() is true only in the moment the button goes down if (button.isPressed()) { if (millis() - lastRingTime >= QUIET_TIME) { ringTheDoorbell(); lastRingTime = millis(); } else { Serial.println("Too soon after the last ring. Nothing is sent."); blinkLed(1, 400); } } }

Étapes rapides

  • Nouveau sur l'ESP32 S3 ? Terminez d'abord notre guide ESP32 S3 - Premiers pas.
  • Créez votre bot Telegram et trouvez votre ID de chat. Voir les étapes ci-dessus.
  • Installez les bibliothèques DIYables ESP32 WebServer et ezButton.
  • Débranchez le câble USB, puis câblez le module de bouton comme dans le tableau ci-dessus.
  • Copiez le code ci-dessus et collez-le dans Arduino IDE.
  • Changez WIFI_SSID, WIFI_PASSWORD, BOT_TOKEN et CHAT_ID. Laissez PUBLIC_ADDRESS vide pour l'instant.
  • Changez les réglages dans le menu Tools. Voir le tableau ci-dessus.
  • Compilez et téléversez le code sur la carte ESP32 S3 en cliquant sur le bouton Upload dans Arduino IDE.
Arduino IDE Upload Code
  • Ouvrez le Serial Monitor dans Arduino IDE.
How to open serial monitor on Arduino IDE
  • Appuyez une fois sur le bouton RESET. La LED clignote trois fois, pour vous montrer que la sonnette est prête.
  • Pointez la caméra vers votre visage et appuyez sur le bouton de la sonnette.
  • La LED reste allumée un moment, puis clignote deux fois.
  • Regardez votre téléphone. La photo est dans votre conversation Telegram, avec le lien en dessous.
Telegram
Telegram 12:45
Welcome to Telegram!
ArduinoBot 10:19
Chatting with Arduino...
telegram-botfather
BotFather Yesterday
Your bot has been created.

ArduinoBot

bot
Today
Somebody is at the door! Live video: http\://192.168.0.2/stream
10:15 AM
  • Appuyez sur le lien. Votre téléphone ouvre la vidéo en direct de votre porte.
  • Regardez un moment, puis fermez la page. La sonnette est de nouveau prête.
  • Astuce : Voulez-vous que le lien fonctionne quand vous n'êtes pas chez vous ? Lisez Le lien vidéo en direct ci-dessus, et faites la redirection de port.
ESP32 S3 Doorbell Photo in Telegram
ESP32 S3 Doorbell Live Video

Serial Monitor

Les relevés suivants ont été capturés en 2026 :

∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
[2026-09-28 17:00:01] Camera sensor: OV5640 (5 MP, max QSXGA 2592x1944) [2026-09-28 17:00:01] Connecting to WiFi... [2026-09-28 17:00:04] Live video on your own WiFi: http://192.168.0.2/stream [2026-09-28 17:00:04] The link inside the Telegram message is: http://192.168.0.2/stream [2026-09-28 17:00:05] Ready. Waiting for somebody to press the button... [2026-09-28 17:02:18] The doorbell was pressed! [2026-09-28 17:02:18] Photo taken: 51353 bytes [2026-09-28 17:02:18] Connecting to Telegram... [2026-09-28 17:02:21] The photo and the live link were sent to Telegram [2026-09-28 17:02:25] Too soon after the last ring. Nothing is sent. [2026-09-28 17:02:33] Somebody is watching the live video [2026-09-28 17:02:51] The live video was closed
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

※ Note:

Too soon after the last ring (Trop tôt après la dernière sonnerie) montre le temps de silence en train de faire son travail. Quelqu'un a appuyé de nouveau sur le bouton après 4 secondes, et le code n'a pas envoyé de deuxième photo.

※ Note:

Somebody is watching the live video (Quelqu'un regarde la vidéo en direct) apparaît quand vous appuyez sur le lien. Si vous ne voyez jamais cette ligne, le lien n'a pas atteint la carte. Lisez le dépannage ci-dessous.

Les deux lignes au début vous disent ce qui est entré dans le message. Quand vous réglez PUBLIC_ADDRESS, la deuxième ligne devient votre propre adresse :

∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
[2026-09-28 17:00:04] Live video on your own WiFi: http://192.168.0.2/stream [2026-09-28 17:00:04] The link inside the Telegram message is: http://my-house.duckdns.org:8080/stream
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

Dépannage

Rien ne se passe quand j'appuie sur le bouton.

  • Vérifiez les trois fils. VCC vers 3,3V, GND vers GND, OUT vers GPIO47.
  • Est-ce que button.loop() est la première ligne à l'intérieur de loop() ? Sans cela, la bibliothèque ne lit jamais la broche.
  • Votre module a-t-il une résistance de tirage vers le haut (pull-up) au lieu d'une résistance de tirage vers le bas ? Alors changez le code en ezButton button(BUTTON_PIN, EXTERNAL_PULLUP);
  • Regardez le Serial Monitor pendant que vous appuyez. Aucune ligne là veut dire que c'est le câblage, pas le code.

La sonnette sonne toute seule, sans que personne ne soit là.

Le fil OUT est mal fixé, donc la broche capte du bruit. Enfoncez bien le fil. Un module avec sa propre résistance ne devrait jamais faire cela s'il est correctement câblé.

Un appui envoie deux ou trois photos.

Augmentez DEBOUNCE_TIME de 50 à 150.

La LED clignote cinq fois après chaque appui.

Cinq clignotements rapides veulent dire que la photo n'a pas pu être envoyée. Lisez le Serial Monitor :

  • Telegram did not answer at all (Telegram n'a pas répondu du tout) veut dire que la photo est partie mais que rien n'est revenu. Vérifiez votre internet, et essayez un partage de connexion depuis un téléphone, car certains réseaux bloquent Telegram.
  • Sending stopped after ... bytes (L'envoi s'est arrêté après ... octets) veut dire que le WiFi s'est coupé en plein milieu. Rapprochez la carte du routeur.
  • Cannot connect to Telegram (Impossible de se connecter à Telegram) veut dire qu'il n'y a pas d'internet du tout.
  • Unauthorized (Non autorisé) veut dire que votre jeton de bot est faux.
  • chat not found (chat introuvable) veut dire que votre ID de chat est faux.

Le Serial Monitor affiche Forbidden: bot can't initiate conversation with a user.

Vous n'avez jamais écrit à votre bot. Ouvrez Telegram, trouvez votre bot, et envoyez-lui n'importe quel message. Puis appuyez sur RESET sur la carte.

La LED ne s'allume jamais.

Le code utilise la LED présente sur la carte. Toutes les cartes n'en ont pas une. Si la vôtre n'en a pas, connectez votre propre LED avec une résistance de 220 ohms à GPIO21, et changez la ligne en #define LED_PIN 21.

Le lien dans le message n'ouvre rien.

  • Laissez PUBLIC_ADDRESS vide et réessayez à la maison, sur le même WiFi que la carte. Cela doit fonctionner en premier.
  • Votre téléphone doit être sur le WiFi, pas sur les données mobiles. Une adresse privée comme 192.168.0.2 n'existe pas en dehors de votre maison.
  • Écrivez http://, pas https://. La carte n'utilise pas https.

Le lien fonctionne à la maison mais pas à l'extérieur.

C'est normal avec un PUBLIC_ADDRESS vide. Pour que ça fonctionne de partout, vous avez besoin de la redirection de port et d'un nom DDNS. Lisez Le lien vidéo en direct ci-dessus.

J'ai fait la redirection de port, et ça ne fonctionne toujours pas.

  • Donnez à la carte une adresse fixe dans votre routeur, sinon la redirection pointe vers le mauvais appareil après une coupure de courant.
  • Certains fournisseurs internet vous donnent une adresse partagée avec d'autres maisons (CGNAT). Alors la redirection de port ne peut pas fonctionner du tout. Demandez à votre fournisseur une vraie adresse publique.
  • Testez depuis les données mobiles, pas depuis votre WiFi de la maison. Beaucoup de routeurs ne peuvent pas ouvrir leur propre adresse publique depuis l'intérieur.

La vidéo en direct s'arrête après une minute.

C'est voulu. Pendant que quelqu'un regarde, la carte ne peut pas voir le bouton. Rechargez la page pour regarder à nouveau, ou changez #define STREAM_SECONDS 60.

Le bouton de la sonnette ne fait rien pendant que quelqu'un regarde la vidéo.

La carte peut faire une seule chose à la fois. Fermez la page vidéo, et le bouton fonctionne à nouveau. C'est aussi pour cela que la vidéo s'arrête d'elle-même après une minute.

La photo montre le visiteur d'en bas, ou sa tête est coupée.

Déplacez la caméra. Une caméra de sonnette devrait se trouver à environ 150 cm au-dessus du sol, pour regarder un visage et non une poitrine.

La photo est sombre le soir.

La caméra ne peut pas voir dans le noir. Voir le tutoriel Caméra ESP32 S3 - Ajouter une LED flash pour des photos de nuit. Une lumière à une porte est aussi accueillante pour le visiteur.

La photo prend beaucoup de temps.

Utilisez une taille d'image plus petite. FRAMESIZE_VGA (640 × 480) fait environ la moitié de SVGA.

Le Serial Monitor n'affiche rien après les messages de démarrage.

Si votre câble USB est dans le port nommé UART, réglez USB CDC On Boot sur Disabled. S'il est dans le port nommé USB, réglez-le sur Enabled.

Applications

Un bouton, une caméra et un message. Cette petite idée trouve sa place à beaucoup d'endroits. Voici des projets pratiques que vous pouvez construire avec ce code :

  1. Sonnette vidéo pour votre maison : Voyez le visiteur sur votre téléphone, de n'importe où.
  2. Sonnette pour une personne sourde : La photo et l'alerte téléphone remplacent le son.
  3. Sonnette de réception de bureau : Un visiteur sonne, et la bonne personne voit qui c'est.
  4. Sonnette d'atelier ou de garage : Sachez que quelqu'un est à la porte arrière pendant qu'une machine fait du bruit.
  5. Bouton d'aide pour une personne âgée : Un appui envoie une photo de la pièce à un membre de la famille.
  6. Sonnette d'hôtel ou de chambre d'invité : Le propriétaire voit l'invité sans marcher jusqu'à la porte.
  7. Sonnette de livraison d'entrepôt : Le chauffeur sonne, et le bureau voit le camion.
  8. Bouton de service de magasin : Un client demande de l'aide, et une photo montre où il se trouve.
  9. Entrée d'école ou de club : Gardez un enregistrement photo de chaque personne qui a sonné.
  10. Bouton de photomaton : Appuyez pour prendre votre propre photo et la recevoir sur votre téléphone.

Vidéo

Regardez ci-dessous le tutoriel vidéo étape par étape de ce projet ESP32 S3.

Défis

  1. Débutant : Changez le texte Somebody is at the door! par votre propre message, par exemple le nom de votre maison.
  2. Intermédiaire : Ajoutez un buzzer qui fait un son à l'intérieur de la maison quand le bouton est appuyé, pour que vous entendiez la sonnette en plus de la voir.
  3. Avancé : Enregistrez aussi chaque photo sur la carte microSD, afin de garder un enregistrement de tous vos visiteurs même quand internet est en panne. Voir le tutoriel Caméra ESP32 S3 - Enregistrer une photo sur carte SD à l'ouverture de la porte.

Commentaires

※ NOS MESSAGES

  • N'hésitez pas à partager le lien de ce tutoriel. Cependant, veuillez ne pas utiliser notre contenu sur d'autres sites web. Nous avons investi beaucoup d'efforts et de temps pour créer ce contenu, veuillez respecter notre travail !