Suchcode

ESP32 Tutorial 45/55 - Benutzerdefinierter Streaming-Server mit LED-Steuerung CAM-3 l SunFounder's ESP32 Lernkit

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

HerstellerEspressif
TeilenummerESP32-WROOM-32
Logik/IO-Spannung3,3 V
Versorgungsspannung3,0-3,6 V
Ausgangsstrom (pro Kanal)12 mA
Spitzenstrom (pro Kanal)40 mA
PWM-Frequenzrichtlinien1 kHz
Eingangslogikschwellen0,2 V (niedrig) / 0,8 V (hoch)
Spannungsabfall / RDS(on)/ Sättigung0,2 V (typ.)
Thermische GrenzenMaximale Anschluss- temperatur: 125 °C
PaketQFN48
Notizen / VariantenVerfü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

esp32-45-streaming-sever-wriing

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

Bilder

ESP32-40-micro-sd-card-2
ESP32-40-micro-sd-card-2
esp32-45-streaming-sever-wriing
esp32-45-streaming-sever-wriing
846-ESP32 Tutorial 45/55- Arduino code for Custom Streamign Server
Sprache: C++
/*
 * 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" // Braun­ausfall­probleme deaktivieren
#include "soc/rtc_cntl_reg.h" // Braun­ausfall­probleme 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() {

}

Dateien📁

Keine Dateien verfügbar.