Dołącz do naszej grupy Telegram — tam osobiście odpowiadam na pytania, dzielę się poradami, schematami, kodem i trikami dotyczącymi montażu i konfiguracji.
Перейти в Telegram

NEWS

Aplikacja dla Esp32 cam monitoring i sterowanie

Ten artykuł jest częścią projektu „Platforma gąsienicowa z kamerą na ESP32-CAM”

 Przedstawiam wam unikalną aplikację dla ESP32-CAM, która otwiera nowe możliwości w zakresie monitoringu wideo i sterowania. Po długich poszukiwaniach idealnego rozwiązania do przesyłania wideo z kamery ESP32-CAM AI-Thinker na smartfon i jednoczesnego sterowania platformą gąsienicową lub kołową - w końcu stworzyłem taką aplikację i z przyjemnością się nią z wami dzielę.

Pobierz aplikację ESP32 CAM

Szkic dla ESP32-CAM tworzy punkt dostępowy WiFi o nazwie carv. Aby rozpocząć korzystanie, wystarczy uruchomić aplikację na smartfonie, nacisnąć przycisk menu (w prawym górnym rogu) i połączyć się z siecią mikrokontrolera przez ustawienia telefonu.

Interfejs jest intuicyjny: po naciśnięciu przycisku "Wideo" pod elementami sterowania pojawia się transmisja na żywo z kamery. Sterowanie odbywa się za pomocą prostych komend wysyłanych na serwer:


Komenda Opis Link

LeftPrzycisk prawego joystickahttp://host/left

RightPrzycisk prawego joystickahttp://host/right

UpPrzycisk lewego joystickahttp://host/up

DownPrzycisk lewego joystickahttp://host/down

FaraSterowanie reflektoramihttp://host/fara

AvarAwaryjne sygnały świetlnehttp://host/avar

SignSygnał dźwiękowyhttp://host/sign

Dop1Dodatkowa funkcja 1http://host/dop1

Dop2Dodatkowa funkcja 2http://host/dop2

Reset1Centralny przycisk lewego joystickahttp://host/reset1

Reset2Centralny przycisk prawego joystickahttp://host/reset2

 Transmisja wideo w czasie rzeczywistym

Przeznaczenie Link
Strumień wideohttp://host:81/stream

Mikrokontroler ESP32-CAM uruchamia dwa serwery: transmisję wideo na porcie 81 i serwer komend na standardowym porcie 80. Poniższy testowy szkic umożliwia:

  • Wyświetlanie wideo na ekranie smartfona z Androidem
  • Wyświetlanie komend w monitorze portu
  • Sterowanie diodami LED na płytce
#include <WiFi.h>
#include <WebServer.h>
#include "esp_camera.h"

// ==== Piny kamery dla AI Thinker ====
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#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

#define LED1_GPIO         33
#define LED2_GPIO         4

WebServer commandServer(80);   // Serwer poleceń
WiFiServer videoServer(81);    // Serwer strumienia wideo
TaskHandle_t videoTaskHandle;  // Zadanie strumienia wideo

bool led1_state = false;
bool led2_state = false;

// === Uniwersalny handler poleceń ===
void handleCommandGeneric(const String& name) {
  Serial.println("Odebrano polecenie: " + name);
  commandServer.send(200, "text/plain", "OK " + name);
}

// === Specjalne polecenia sterujące diodami LED ===
void handleCommand1() {
  led1_state = !led1_state;
  digitalWrite(LED1_GPIO, led1_state);
  Serial.println("Polecenie dop1: przełączono GPIO 33");
  commandServer.send(200, "text/plain", "OK dop1");
}

void handleCommand2() {
  led2_state = !led2_state;
  digitalWrite(LED2_GPIO, led2_state);
  Serial.println("Polecenie dop2: przełączono GPIO 4");
  commandServer.send(200, "text/plain", "OK dop2");
}

// === Zadanie strumienia wideo na rdzeniu 0 ===
void videoStreamTask(void *parameter) {
  videoServer.begin();
  Serial.println("Serwer strumienia wideo uruchomiony na porcie 81");

  while (true) {
    WiFiClient client = videoServer.available();
    if (client) {
      Serial.println("Klient połączony ze strumieniem wideo");

      String response = "HTTP/1.1 200 OK\r\n";
      response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n";
      client.print(response);

      while (client.connected()) {
        camera_fb_t * fb = esp_camera_fb_get();
        if (!fb) {
          Serial.println("Błąd pobierania klatki");
          break;
        }

        client.print("--frame\r\n");
        client.print("Content-Type: image/jpeg\r\n\r\n");
        client.write(fb->buf, fb->len);
        client.print("\r\n");

        esp_camera_fb_return(fb);
        delay(50);
        yield();
      }

      client.stop();
      Serial.println("Klient rozłączony ze strumieniem wideo");
    }

    delay(10);
  }
}

// === Inicjalizacja kamery ===
void setupCamera() {
  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;
  config.frame_size = FRAMESIZE_VGA;
  config.jpeg_quality = 12;
  config.fb_count = 1;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Błąd kamery: 0x%x\n", err);
  }
}

// === Konfiguracja ===
void setup() {
  Serial.begin(115200);

  pinMode(LED1_GPIO, OUTPUT);
  pinMode(LED2_GPIO, OUTPUT);
  digitalWrite(LED1_GPIO, LOW);
  digitalWrite(LED2_GPIO, LOW);

  IPAddress ip(192, 168, 4, 1);
  IPAddress gateway(192, 168, 4, 1);
  IPAddress subnet(255, 255, 255, 0);
  WiFi.softAPConfig(ip, gateway, subnet);
  WiFi.softAP("carv");

  Serial.println("Punkt dostępowy: carv");
  Serial.print("IP: ");
  Serial.println(WiFi.softAPIP());

  setupCamera();

  // Strona główna sterowania
  commandServer.on("/", HTTP_GET, []() {
    String html = "<html><body><h2>Sterowanie ESP32-CAM</h2>";
    html += "<p><a href='http://192.168.4.1:81/stream' target='_blank'>▶ Oglądaj wideo</a></p>";
    const char* commands[] = {
      "left", "right", "up", "down", "fara", "avar", "sign", "dop1", "dop2", "reset1", "reset2"
    };
    for (auto& cmd : commands) {
      html += "<p><a href='/" + String(cmd) + "'>" + String(cmd) + "</a></p>";
    }
    html += "</body></html>";
    commandServer.send(200, "text/html", html);
  });

  // Obsługa poleceń
  commandServer.on("/dop1", HTTP_GET, handleCommand1);
  commandServer.on("/dop2", HTTP_GET, handleCommand2);

  commandServer.on("/left",    HTTP_GET, []() { handleCommandGeneric("left"); });
  commandServer.on("/right",   HTTP_GET, []() { handleCommandGeneric("right"); });
  commandServer.on("/up",      HTTP_GET, []() { handleCommandGeneric("up"); });
  commandServer.on("/down",    HTTP_GET, []() { handleCommandGeneric("down"); });

  // Zmienione linie: fara wykonuje dop2, avar wykonuje dop1
  commandServer.on("/fara",    HTTP_GET, handleCommand2);
  commandServer.on("/avar",    HTTP_GET, handleCommand1);

  commandServer.on("/sign",    HTTP_GET, []() { handleCommandGeneric("sign"); });
  commandServer.on("/reset1",  HTTP_GET, []() { handleCommandGeneric("reset1"); });
  commandServer.on("/reset2",  HTTP_GET, []() { handleCommandGeneric("reset2"); });

  commandServer.begin();
  Serial.println("Serwer poleceń uruchomiony (port 80)");

  // Uruchomienie strumienia wideo w osobnym zadaniu
  xTaskCreatePinnedToCore(
    videoStreamTask,
    "VideoStream",
    8192,
    NULL,
    1,
    &videoTaskHandle,
    0
  );
}

// === Główna pętla ===
void loop() {
  commandServer.handleClient();
}

 To podstawowa implementacja, którą możesz dostosować do własnych potrzeb. W najbliższym czasie przedstawię ulepszoną wersję do sterowania platformą gąsienicową, w tym modele STL i schemat elektryczny. Śledźcie aktualizacje!