ESP32 Tutorial 45/55 - Benutzerdefinierter Streaming-Server mit LED-Steuerung CAM-3 l SunFounder's ESP32 Lernkit
In diesem Tutorial werden wir untersuchen, wie man einen benutzerdefinierten Streaming-Server mit dem ESP32-Erweiterungsboard von SunFounder einrichtet. Das Projekt ermöglicht es Ihnen, Live-Video in Ihren Browser zu streamen, während Sie gleichzeitig eine LED direkt über die Benutzeroberfläche steuern. Diese Kombination von Funktionen bietet ein praktisches Erlebnis mit IoT- und Webtechnologien.
Wir werden die integrierten WLAN-Funktionen des ESP32 nutzen, um einen Webserver zu erstellen, der Videos streamt und LED-Steuerbefehle verarbeitet. Das Projekt umfasst Programmierung, Verkabelung und das Verständnis, wie die Komponenten miteinander interagieren. Wenn Sie ein klareres Verständnis der Einrichtung wünschen, sollten Sie sich das Video bei (im Video bei 00:00) ansehen.
Hardware erklärt
Die Hauptkomponenten für dieses Projekt umfassen den ESP32-Mikrocontroller, ein Kameramodul, eine LED und einen Widerstand. Der ESP32 ist ein vielseitiger Mikrocontroller mit integriertem Wi-Fi und Bluetooth, was ihn ideal für IoT-Anwendungen macht. Das Kameramodul ermöglicht uns das Erfassen von Videos, während die LED ein einfaches Ausgabegerät zur Steuerung bietet.
Die LED ist über einen Widerstand verbunden, um den Strom zu begrenzen und Schäden sowohl an der LED als auch am Mikrocontroller zu verhindern. Dieses Setup ermöglicht es uns, die LED über unsere Weboberfläche ein- und auszuschalten und zeigt die Fähigkeiten des ESP32 im Umgang mit Eingaben und Ausgaben über ein Netzwerk.
Datenblattdetails
| Hersteller | Espressif |
|---|---|
| Teilenummer | ESP32-WROOM-32 |
| Logik/IO-Spannung | 3,3 V |
| Versorgungsspannung | 3,0-3,6 V |
| Ausgangsstrom (pro Kanal) | 12 mA |
| Spitzenstrom (pro Kanal) | 40 mA |
| PWM-Frequenzrichtlinien | 1 kHz |
| Eingangslogikschwellen | 0,2 V (niedrig) / 0,8 V (hoch) |
| Spannungsabfall / RDS(on)/ Sättigung | 0,2 V (typ.) |
| Thermische Grenzen | Maximale Anschluss- temperatur: 125 °C |
| Paket | QFN48 |
| Notizen / Varianten | Verfügbar in verschiedenen Konfigurationen |
- Stellen Sie sicher, dass das ESP32 mit einem stabilisierten 3,3 V-Netzteil versorgt wird.
- Verwenden Sie einen strombegrenzenden Widerstand (220 Ohm) mit der LED, um Schäden zu vermeiden.
- Halten Sie ordnungsgemäße Verbindungen aufrecht, um schwebende Eingänge zu vermeiden.
- Überprüfen Sie, ob die Wi-Fi-Anmeldeinformationen korrekt und case-sensitiv sind.
- Verwenden Sie eine stabile Stromquelle für gleichbleibende Leistung.
- Berücksichtigen Sie die Wärmeabgabe in geschlossenen Räumen.
Verdrahtungsanweisungen

Um den ESP32 und die LED zu verkabeln, beginnen Sie damit, den längeren Pin der LED mit einem geeigneten GPIO-Pin zu verbinden, in diesem Fall verwenden wir Pin 14. Der kürzere Pin sollte mit der Masseleitung auf Ihrem Breadboard verbunden werden. Platzieren Sie als Nächstes einen 220-Ohm-Widerstand in Reihe mit der LED, indem Sie ein Ende mit dem GPIO-Pin (Pin 14) und das andere Ende mit der Masse verbinden. Stellen Sie sicher, dass der ESP32 korrekt mit Strom versorgt wird, entweder über den Mikro-USB-Port oder mit einer 18650-Lithiumbatterie.
Für das Kameramodul stellen Sie sicher, dass Sie die erforderlichen Pins gemäß dem verwendeten Kameramodell anschließen, da die Verkabelung leicht variieren kann. Der ESP32 wird den Video-Stream über seine integrierten Fähigkeiten verwalten, und die LED-Steuerung wird über die Webschnittstelle, die wir im Code einrichten werden, gesteuert.
Code-Beispiele & Anleitung
Das Programm beginnt damit, notwendige Bibliotheken einzufügen und die Wi-Fi-Anmeldeinformationen zu definieren. Sie müssen ersetzenssidundpasswordmit Ihren aktuellen Wi-Fi-Anmeldeinformationen, um das ESP32 mit Ihrem Netzwerk zu verbinden.
const char* ssid = "SSID";
const char* password = "PASSWORD";Als nächstes definieren wir den LED-Pin und richten die Kameraeinstellungen ein. Der für die LED verwendete Pin wird definiert alsLED_PIN, das später im Code verwendet wird, um den Zustand der LED zu steuern.
#define LED_PIN 14
pinMode(LED_PIN, OUTPUT);Im Anfrage-Handler für die LED-Steuerung überprüfen wir den vom Webinterface empfangenen Befehl. Je nachdem, ob der Befehl "ein" oder "aus" ist, verwenden wirdigitalWrite(LED_PIN, 1);um die LED einzuschalten unddigitalWrite(LED_PIN, 0);es auszuschalten.
if(!strcmp(variable, "on")) {
Serial.println("ON");
digitalWrite(LED_PIN, 1);
}
else if(!strcmp(variable, "off")) {
Serial.println("OFF");
digitalWrite(LED_PIN, 0);
}Diese Logik ermöglicht es der Webschnittstelle, effektiv mit dem ESP32 zu kommunizieren, wodurch eine Echtzeitsteuerung der LED basierend auf Benutzerinteraktionen ermöglicht wird. Der vollständige Code wird unter dem Artikel zum weiteren Erkunden geladen.
Demonstration / Was zu erwarten ist
Sobald alles eingerichtet und der Code hochgeladen ist, sollten Sie in der Lage sein, die IP-Adresse des ESP32 in Ihrem Webbrowser aufzurufen. Das Streaming-Video wird erscheinen, und Sie können die LED mit den Tasten auf der Benutzeroberfläche steuern. Durch Klicken auf "EIN" wird die LED eingeschaltet, während "AUS" sie ausschaltet. Stellen Sie sicher, dass der ESP32 und Ihr Computer mit demselben Netzwerk verbunden sind, um eine ordnungsgemäße Funktionalität zu gewährleisten (im Video um 12:30).
Video-Zeitraffer
- 00:00 Start
- 1:51 Einführung in das Projekt
- 2:31 Dokumentationsseite
- 3:33 Verkabelung Erklärt
- 5:08 Arduino-Code erklärt
- 13:28 Auswahl des ESP32-Boards und des COM-Ports in der Arduino IDE
- 15:10 Demonstration
/*
* Hiermit wird allen Personen, die eine Kopie dieser Software und der zugehörigen Dokumentationsdateien erhalten, kostenlos die Erlaubnis erteilt. Der obige Copyright-Hinweis und dieser Erlaubnishinweis sind in allen Kopien oder wesentlichen Teilen der Software einzufügen.
*/
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" // Braunausfallprobleme deaktivieren
#include "soc/rtc_cntl_reg.h" // Braunausfallprobleme deaktivieren
#include "esp_http_server.h"
// Ersetzen Sie die nächsten Variablen durch Ihre SSID/Passwort-Kombination.
const char* ssid = "SSID";
const char* password = "PASSWORD";
#define PART_BOUNDARY "123456789000000000000987654321"
#define CAMERA_MODEL_AI_THINKER
// #define CAMERA_MODEL_M5STACK_PSRAM
// #define CAMERA_MODEL_M5STACK_ OHNE_PSRAM
// #define CAMERA_MODEL_M5STACK_PSRAM_B
// #define CAMERA_MODEL_WROVER_KIT
#if defined(CAMERA_MODEL_WROVER_KIT)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 21
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 19
#define Y4_GPIO_NUM 18
#define Y3_GPIO_NUM 5
#define Y2_GPIO_NUM 4
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 25
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 32
#define VSYNC_GPIO_NUM 22
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#elif defined(CAMERA_MODEL_M5STACK_WITHOUT_PSRAM)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 25
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 17
#define VSYNC_GPIO_NUM 22
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#elif defined(CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM 33
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#elif defined(CAMERA_MODEL_M5STACK_PSRAM_B)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 22
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 32
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#else
#error "Camera model not selected"
#endif
#define LED_PIN 14
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t camera_httpd = NULL;
httpd_handle_t stream_httpd = NULL;
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<html>
<head>
<title>ESP32-CAM Robot</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px;}
table { margin-left: auto; margin-right: auto; }
td { padding: 8 px; }
.button {
background-color: #2f4468;
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 18px;
margin: 6px 3px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
img { width: auto ;
max-width: 100% ;
height: auto ;
transform: rotate(180deg);
}
</style>
</head>
<body>
<h1>ESP32 CAMERA</h1>
<img src="" id="photo" >
<table>
<tr><td align="center"><button class="button" onmousedown="toggleCheckbox('on');" ontouchstart="toggleCheckbox('on');onmouseup="toggleCheckbox('on');" ontouchend="toggleCheckbox('on');">ON</button></td>
<td align="center"><button class="button" onmousedown="toggleCheckbox('off');" ontouchstart="toggleCheckbox('off');onmouseup="toggleCheckbox('off');" ontouchend="toggleCheckbox('off');">OFF</button></td></tr>
</table>
<script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/action?go=" + x, true);
xhr.send();
}
window.onload = document.getElementById("photo").src = window.location.href.slice(0, -1) + ":81/stream";
</script>
</body>
</html>
)rawliteral";
static esp_err_t index_handler(httpd_req_t *req){
httpd_resp_set_type(req, "text/html");
return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));
}
static esp_err_t stream_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){
return res;
}
while(true){
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else {
if(fb->width > 400){
if(fb->format != PIXFORMAT_JPEG){
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
esp_camera_fb_return(fb);
fb = NULL;
if(!jpeg_converted){
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
}
}
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(fb){
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if(_jpg_buf){
free(_jpg_buf);
_jpg_buf = NULL;
}
if(res != ESP_OK){
break;
}
// Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
}
return res;
}
static esp_err_t cmd_handler(httpd_req_t *req){
char* buf;
size_t buf_len;
char variable[32] = {0,};
buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len > 1) {
buf = (char*)malloc(buf_len);
if(!buf){
httpd_resp_send_500(req);
return ESP_FAIL;
}
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
if (httpd_query_key_value(buf, "go", variable, sizeof(variable)) == ESP_OK) {
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
free(buf);
} else {
httpd_resp_send_404(req);
return ESP_FAIL;
}
sensor_t * s = esp_camera_sensor_get();
int res = 0;
if(!strcmp(variable, "on")) {
Serial.println("ON");
digitalWrite(LED_PIN, 1);
}
else if(!strcmp(variable, "off")) {
Serial.println("OFF");
digitalWrite(LED_PIN, 0);
}
else {
res = -1;
}
if(res){
return httpd_resp_send_500(req);
}
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
return httpd_resp_send(req, NULL, 0);
}
void startCameraServer(){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80;
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
httpd_uri_t cmd_uri = {
.uri = "/action",
.method = HTTP_GET,
.handler = cmd_handler,
.user_ctx = NULL
};
httpd_uri_t stream_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
if (httpd_start(&camera_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(camera_httpd, &index_uri);
httpd_register_uri_handler(camera_httpd, &cmd_uri);
}
config.server_port += 1;
config.ctrl_port += 1;
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(stream_httpd, &stream_uri);
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Braunout-Detektor deaktivieren
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
Serial.setDebugOutput(false);
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_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_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;
if(psramFound()){
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Kamera-Initialisierung
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
// Wi-Fi-Verbindung
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http: // ");
Serial.println(WiFi.localIP());
// Webserver-Streaming starten
startCameraServer();
}
void loop() {
}
Common Course Links
Common Course Files
Ressourcen & Referenzen
-
Dokumentation
Dateien📁
Keine Dateien verfügbar.