Translate

ESP32 Cam Kirim Foto ke Telegram

ESP32 Cam Kirim Foto ke Telegram
 

        Pada kesempatan kali ini saya akan menjelaskan bagaimana cara membuat sebuah alat dengan ESP32 cam untuk mengirim gambar ke telegram, fungsi dari alat ini yaitu untuk fitur pemantauan, jadi misal lagi di tempat kerja lalu ingin melihat kondisi real yang ada disana bisa dipantau via telegram dan gambarnya bisa di share ke group atau status media sosial. untuk lebih jelasnya berkut adalah koding dan haslnya.
 
 
1. Hasilnya
 

 
2. Program ESP32 Cam
 
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "esp_camera.h"

// =====================================================
// WIFI
// =====================================================


const char* WIFI_SSID = "hotspotkuwifi";
const char* WIFI_PASSWORD = "123456789";

// ================================
// TELEGRAM
// ================================

// Gunakan token baru
String BOT_TOKEN = "8599566365:AAHWGXsuTQp5IROs0d9HIV9gFd9DyG8xx1x";


// Chat ID tujuan
const char* CHAT_ID = "7796726911";

// =====================================================
// PIN ESP32-CAM 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

// =====================================================
// TELEGRAM UPDATE
// =====================================================

long lastUpdateID = 0;

unsigned long lastCheck = 0;

const unsigned long CHECK_INTERVAL = 1000;


// =====================================================
// INISIALISASI CAMERA
// =====================================================

bool 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;

  // Kompatibilitas beberapa versi ESP32 Core

#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR >= 2

  config.pin_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;

#else

  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;

#endif

  config.pin_pwdn  = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;

  config.xclk_freq_hz = 20000000;

  // Langsung menghasilkan JPG
  config.pixel_format = PIXFORMAT_JPEG;

  // Mulai VGA supaya stabil
  config.frame_size = FRAMESIZE_VGA;

  config.jpeg_quality = 12;

  config.fb_count = 1;


  Serial.println("Memulai kamera...");


  esp_err_t err =
    esp_camera_init(&config);


  if (err != ESP_OK) {

    Serial.printf(
      "Camera gagal. Error: 0x%x\n",
      err
    );

    return false;
  }


  sensor_t* sensor =
    esp_camera_sensor_get();


  sensor->set_framesize(
    sensor,
    FRAMESIZE_VGA
  );


  sensor->set_quality(
    sensor,
    12
  );


  Serial.println(
    "Camera berhasil."
  );


  return true;
}


// =====================================================
// CONNECT WIFI
// =====================================================

void connectWiFi() {

  WiFi.mode(WIFI_STA);

  WiFi.begin(
    WIFI_SSID,
    WIFI_PASSWORD
  );


  Serial.print(
    "Menghubungkan WiFi"
  );


  while (
    WiFi.status() != WL_CONNECTED
  ) {

    delay(500);

    Serial.print(".");
  }


  Serial.println();

  Serial.println(
    "WiFi terhubung"
  );


  Serial.print(
    "IP: "
  );

  Serial.println(
    WiFi.localIP()
  );


  Serial.print(
    "Signal: "
  );

  Serial.print(
    WiFi.RSSI()
  );

  Serial.println(
    " dBm"
  );
}


// =====================================================
// HAPUS WEBHOOK
// Supaya getUpdates bisa digunakan
// =====================================================

void deleteWebhook() {

  WiFiClientSecure client;

  client.setInsecure();


  HTTPClient https;


  String url =
    "https://api.telegram.org/bot"
    + BOT_TOKEN
    + "/deleteWebhook?drop_pending_updates=true";


  Serial.println(
    "Menyiapkan Telegram..."
  );


  if (
    https.begin(
      client,
      url
    )
  ) {

    int httpCode =
      https.GET();


    Serial.print(
      "deleteWebhook HTTP: "
    );

    Serial.println(
      httpCode
    );


    https.end();
  }
}


// =====================================================
// KIRIM FOTO JPG KE TELEGRAM
// =====================================================

bool sendPhotoTelegram(
  String chatID
) {

  Serial.println();
  Serial.println(
    "=============================="
  );

  Serial.println(
    "MENGAMBIL FOTO"
  );

  Serial.println(
    "=============================="
  );


  // ===================================================
  // BUANG FRAME PERTAMA
  // ===================================================

  camera_fb_t* fb =
    esp_camera_fb_get();


  if (fb) {

    esp_camera_fb_return(fb);

    fb = NULL;
  }


  delay(200);


  // ===================================================
  // AMBIL FOTO BARU
  // ===================================================

  fb =
    esp_camera_fb_get();


  if (!fb) {

    Serial.println(
      "Gagal mengambil gambar!"
    );

    return false;
  }


  Serial.print(
    "Resolusi: "
  );

  Serial.print(
    fb->width
  );

  Serial.print(
    " x "
  );

  Serial.println(
    fb->height
  );


  Serial.print(
    "Ukuran JPG: "
  );

  Serial.print(
    fb->len
  );

  Serial.println(
    " bytes"
  );


  // ===================================================
  // CONNECT TELEGRAM
  // ===================================================

  WiFiClientSecure client;

  client.setInsecure();

  client.setTimeout(15000);


  Serial.println(
    "Menghubungkan Telegram..."
  );


  if (
    !client.connect(
      "api.telegram.org",
      443
    )
  ) {

    Serial.println(
      "Koneksi Telegram gagal!"
    );


    esp_camera_fb_return(fb);

    return false;
  }


  Serial.println(
    "Telegram connected"
  );


  // ===================================================
  // MULTIPART FORM DATA
  // ===================================================

  String boundary =
    "ESP32CAMBoundary";


  String head =

    "--" + boundary + "\r\n"

    "Content-Disposition: form-data; "
    "name=\"chat_id\"\r\n\r\n"

    + chatID +

    "\r\n"

    "--" + boundary + "\r\n"

    "Content-Disposition: form-data; "
    "name=\"photo\"; "
    "filename=\"esp32cam.jpg\"\r\n"

    "Content-Type: image/jpeg\r\n\r\n";


  String tail =

    "\r\n--"
    + boundary
    + "--\r\n";


  size_t contentLength =
    head.length()
    +
    fb->len
    +
    tail.length();


  // ===================================================
  // HTTP POST
  // ===================================================

  client.print(
    "POST /bot"
  );

  client.print(
    BOT_TOKEN
  );

  client.println(
    "/sendPhoto HTTP/1.1"
  );


  client.println(
    "Host: api.telegram.org"
  );


  client.println(
    "Connection: close"
  );


  client.print(
    "Content-Type: multipart/form-data; boundary="
  );

  client.println(
    boundary
  );


  client.print(
    "Content-Length: "
  );

  client.println(
    contentLength
  );


  client.println();


  // ===================================================
  // KIRIM HEADER
  // ===================================================

  client.print(
    head
  );


  // ===================================================
  // KIRIM DATA JPG
  // ===================================================

  size_t sent = 0;

  unsigned long sendTimeout =
    millis();


  while (
    sent < fb->len
  ) {

    size_t remaining =
      fb->len - sent;


    size_t chunkSize =
      remaining > 1024
      ? 1024
      : remaining;


    size_t written =
      client.write(
        fb->buf + sent,
        chunkSize
      );


    if (written > 0) {

      sent += written;

      sendTimeout =
        millis();

    } else {

      if (
        millis() - sendTimeout >
        10000
      ) {

        Serial.println(
          "Timeout mengirim JPG"
        );

        break;
      }

      delay(10);
    }


    delay(1);
  }


  client.print(
    tail
  );


  Serial.print(
    "JPG terkirim: "
  );

  Serial.print(
    sent
  );

  Serial.print(
    "/"
  );

  Serial.println(
    fb->len
  );


  // Frame sudah selesai
  esp_camera_fb_return(fb);

  fb = NULL;


  // ===================================================
  // BACA RESPONSE TELEGRAM
  // ===================================================

  bool telegramOK =
    false;


  unsigned long timeout =
    millis();


  Serial.println(
    "Menunggu response Telegram..."
  );


  while (
    millis() - timeout <
    15000
  ) {

    while (
      client.available()
    ) {

      String line =
        client.readStringUntil('\n');


      Serial.println(
        line
      );


      if (
        line.indexOf(
          "\"ok\":true"
        ) >= 0
      ) {

        telegramOK =
          true;
      }


      timeout =
        millis();
    }


    if (
      !client.connected()
    ) {

      break;
    }


    delay(10);
  }


  client.stop();


  if (telegramOK) {

    Serial.println();
    Serial.println(
      "FOTO BERHASIL DIKIRIM!"
    );

  } else {

    Serial.println();
    Serial.println(
      "FOTO GAGAL DIKIRIM!"
    );
  }


  return telegramOK;
}


// =====================================================
// CEK PESAN TELEGRAM
// =====================================================

void checkTelegram() {

  WiFiClientSecure client;

  client.setInsecure();


  HTTPClient https;


  String url =
    "https://api.telegram.org/bot"
    + BOT_TOKEN
    + "/getUpdates?offset="
    + String(lastUpdateID + 1)
    + "&limit=5&timeout=0";


  if (
    !https.begin(
      client,
      url
    )
  ) {

    Serial.println(
      "HTTP Telegram gagal"
    );

    return;
  }


  int httpCode =
    https.GET();


  if (
    httpCode != 200
  ) {

    Serial.print(
      "getUpdates HTTP Error: "
    );

    Serial.println(
      httpCode
    );


    https.end();

    return;
  }


  String payload =
    https.getString();


  https.end();


  // ===================================================
  // PARSE JSON
  // ===================================================

  DynamicJsonDocument doc(
    16384
  );


  DeserializationError error =
    deserializeJson(
      doc,
      payload
    );


  if (error) {

    Serial.print(
      "JSON error: "
    );

    Serial.println(
      error.c_str()
    );

    return;
  }


  JsonArray results =
    doc["result"].as<JsonArray>();


  for (
    JsonObject update : results
  ) {

    long updateID =
      update["update_id"] | 0;


    if (
      updateID >
      lastUpdateID
    ) {

      lastUpdateID =
        updateID;
    }


    // Pastikan update berisi message

    if (
      update["message"].isNull()
    ) {

      continue;
    }


    const char* textRaw =
      update["message"]["text"] | "";


    String text =
      String(textRaw);


    // Telegram chat ID sekarang bisa besar.
    // Ambil sebagai 64-bit.

    int64_t chatIDNumber =
      update["message"]["chat"]["id"]
      .as<int64_t>();


    char chatBuffer[32];


    snprintf(
      chatBuffer,
      sizeof(chatBuffer),
      "%lld",
      (long long)chatIDNumber
    );


    String chatID =
      String(chatBuffer);


    Serial.println();
    Serial.println(
      "------------------------------"
    );

    Serial.print(
      "Chat ID : "
    );

    Serial.println(
      chatID
    );


    Serial.print(
      "Pesan   : "
    );

    Serial.println(
      text
    );


    // =================================================
    // PERINTAH /foto
    // =================================================

    if (
      text == "/foto"
      ||
      text.startsWith(
        "/foto@"
      )
    ) {

      Serial.println(
        "Perintah /foto diterima"
      );


      sendPhotoTelegram(
        chatID
      );
    }
  }
}


// =====================================================
// SETUP
// =====================================================

void setup() {

  Serial.begin(
    115200
  );


  delay(1000);


  Serial.println();
  Serial.println(
    "================================"
  );

  Serial.println(
    "ESP32-CAM TELEGRAM /FOTO"
  );

  Serial.println(
    "================================"
  );


  // CAMERA

  if (
    !setupCamera()
  ) {

    Serial.println(
      "Camera gagal."
    );

    return;
  }


  // WIFI

  connectWiFi();


  // getUpdates tidak bisa digunakan
  // jika bot masih memakai webhook.

  deleteWebhook();


  Serial.println();
  Serial.println(
    "================================"
  );

  Serial.println(
    "SISTEM SIAP"
  );

  Serial.println(
    "Kirim /foto ke Telegram"
  );

  Serial.println(
    "================================"
  );
}


// =====================================================
// LOOP
// =====================================================

void loop() {

  // Reconnect WiFi

  if (
    WiFi.status() !=
    WL_CONNECTED
  ) {

    connectWiFi();
  }


  // Cek Telegram setiap 1 detik

  if (
    millis() - lastCheck >=
    CHECK_INTERVAL
  ) {

    lastCheck =
      millis();


    checkTelegram();
  }
}
 
 
 
3. VIDEO HASILNYA
 


ESP32 Cam Deteksi dan Counter / Hitung Benda / Orang Lewat

ESP32 Cam Deteksi dan Counter / Hitung Benda / Orang Lewat 
 

         Pada kesempatan kali ini saya akan menjelaskan mengenai bagaimana cara membuat sebuah esp32 cam bisa berfungsi sebagai penghitung orang yang masuk atau keluar, atau juga bisa menghitung benda yang lewat didepan kamera. untuk detailnya berikut adalah koding dan interfacenya.
 
 
1. Interface
 
 
 
2. Program ESP32 Cam
 
#include <WiFi.h>
#include <WebServer.h>
#include "esp_camera.h"

// ======================================================
// ESP32-CAM AI THINKER PIN
// ======================================================

#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


// ======================================================
// HOTSPOT
// ======================================================

const char* AP_SSID = "ESP32CAM_COUNTER";
const char* AP_PASSWORD = "12345678";

WebServer server(80);


// ======================================================
// UKURAN FRAME
// QQVGA = 160 x 120
// ======================================================

#define FRAME_WIDTH   160
#define FRAME_HEIGHT  120
#define FRAME_SIZE    (FRAME_WIDTH * FRAME_HEIGHT)

uint8_t* previousFrame = NULL;


// ======================================================
// ZONA DETEKSI
// ======================================================

// Zona A
#define ZONE_A_X1  30
#define ZONE_A_X2  60

// Zona B
#define ZONE_B_X1  100
#define ZONE_B_X2  130

// Area vertikal jalan
#define ZONE_Y1    20
#define ZONE_Y2    105


// ======================================================
// SENSITIVITAS
// ======================================================

// Perbedaan brightness pixel
#define PIXEL_DIFF_THRESHOLD 25

// Berapa persen pixel harus berubah
#define MOTION_THRESHOLD 12

// Timeout kendaraan mencapai zona berikutnya
#define CROSS_TIMEOUT 3000

// Frame tenang sebelum siap menghitung lagi
#define CLEAR_FRAME_REQUIRED 5


// ======================================================
// COUNTER
// ======================================================

unsigned long vehicleCount = 0;

unsigned long countAToB = 0;
unsigned long countBToA = 0;

String lastDirection = "-";

int motionA = 0;
int motionB = 0;


// ======================================================
// STATE MACHINE
// ======================================================

enum DetectionState {

  STATE_IDLE,

  STATE_WAIT_B,

  STATE_WAIT_A,

  STATE_LOCKED

};

DetectionState state = STATE_IDLE;

unsigned long stateStartTime = 0;

int clearFrameCounter = 0;


// ======================================================
// HTML WEBSERVER
// ======================================================

const char PAGE[] PROGMEM = R"rawliteral(

<!DOCTYPE html>

<html>

<head>

<meta
name="viewport"
content="width=device-width, initial-scale=1">

<title>ESP32 Vehicle Counter</title>

<style>

body {

  background:#111;
  color:white;

  font-family:Arial;
  text-align:center;

  margin:0;
  padding:20px;

}

.container {

  max-width:500px;
  margin:auto;

}

.card {

  background:#222;

  padding:20px;
  margin:15px 0;

  border-radius:12px;

}

h1 {

  font-size:25px;

}

.counter {

  font-size:70px;

  color:#00ff88;

  font-weight:bold;

}

.label {

  color:#aaa;

  font-size:13px;

}

.value {

  font-size:24px;

  margin:7px;

}

.zone {

  display:flex;

  justify-content:space-around;

}

.zonebox {

  width:45%;

  padding:15px;

  background:#333;

  border-radius:10px;

}

button {

  width:100%;

  padding:15px;

  background:#d32f2f;

  color:white;

  border:none;

  border-radius:8px;

  font-size:16px;

  cursor:pointer;

}

.status {

  color:#ffb74d;

}

</style>

</head>


<body>

<div class="container">

<h1>
ESP32-CAM Vehicle Counter
</h1>


<div class="card">

<div class="label">
JUMLAH KENDARAAN
</div>

<div
class="counter"
id="count">
0
</div>

</div>


<div class="card">

<div class="zone">

<div class="zonebox">

<div class="label">
A → B
</div>

<div
class="value"
id="ab">
0
</div>

</div>


<div class="zonebox">

<div class="label">
B → A
</div>

<div
class="value"
id="ba">
0
</div>

</div>

</div>

</div>


<div class="card">

<div class="label">
ARAH TERAKHIR
</div>

<div
class="value"
id="direction">
-
</div>


<div class="label">
STATUS SISTEM
</div>

<div
class="value status"
id="state">
MENUNGGU
</div>

</div>


<div class="card">

<div class="zone">

<div class="zonebox">

<div class="label">
MOTION ZONA A
</div>

<div
class="value"
id="motionA">
0%
</div>

</div>


<div class="zonebox">

<div class="label">
MOTION ZONA B
</div>

<div
class="value"
id="motionB">
0%
</div>

</div>

</div>

</div>


<div class="card">

<button onclick="resetCounter()">

RESET COUNTER

</button>

</div>


<div class="label">

WiFi: ESP32CAM_COUNTER

<br>

IP: 192.168.4.1

</div>

</div>


<script>

function updateData() {

  fetch("/status")

  .then(response => response.json())

  .then(data => {

    document.getElementById(
      "count"
    ).innerHTML =
      data.count;


    document.getElementById(
      "ab"
    ).innerHTML =
      data.ab;


    document.getElementById(
      "ba"
    ).innerHTML =
      data.ba;


    document.getElementById(
      "direction"
    ).innerHTML =
      data.direction;


    document.getElementById(
      "state"
    ).innerHTML =
      data.state;


    document.getElementById(
      "motionA"
    ).innerHTML =
      data.motionA + "%";


    document.getElementById(
      "motionB"
    ).innerHTML =
      data.motionB + "%";

  });

}


function resetCounter() {

  fetch("/reset");

}


setInterval(
  updateData,
  500
);

updateData();

</script>

</body>

</html>

)rawliteral";


// ======================================================
// HITUNG PERUBAHAN PIXEL DALAM ZONA
// ======================================================

int calculateMotion(

  uint8_t* current,

  uint8_t* previous,

  int x1,
  int x2,

  int y1,
  int y2

) {

  unsigned long changed = 0;

  unsigned long total = 0;


  // Skip beberapa pixel supaya lebih ringan
  const int STEP = 2;


  for (
    int y = y1;
    y < y2;
    y += STEP
  ) {

    for (
      int x = x1;
      x < x2;
      x += STEP
    ) {

      int index =
        y * FRAME_WIDTH + x;


      int difference =

        abs(
          (int)current[index]
          -
          (int)previous[index]
        );


      if (
        difference >
        PIXEL_DIFF_THRESHOLD
      ) {

        changed++;
      }


      total++;
    }
  }


  if (
    total == 0
  ) {

    return 0;
  }


  return
    (changed * 100)
    /
    total;
}


// ======================================================
// PROSES COUNTER
// ======================================================

void processVehicle(

  bool zoneA,
  bool zoneB

) {

  unsigned long now =
    millis();


  switch(state) {


    // ==================================================
    // MENUNGGU KENDARAAN
    // ==================================================

    case STATE_IDLE:


      if (
        zoneA &&
        !zoneB
      ) {

        state =
          STATE_WAIT_B;

        stateStartTime =
          now;

        Serial.println(
          "Zona A aktif -> menunggu Zona B"
        );
      }


      else if (
        zoneB &&
        !zoneA
      ) {

        state =
          STATE_WAIT_A;

        stateStartTime =
          now;

        Serial.println(
          "Zona B aktif -> menunggu Zona A"
        );
      }


      break;



    // ==================================================
    // A -> B
    // ==================================================

    case STATE_WAIT_B:


      if (zoneB) {

        vehicleCount++;

        countAToB++;

        lastDirection =
          "A -> B";


        Serial.println();
        Serial.println(
          "========================="
        );

        Serial.println(
          "KENDARAAN TERHITUNG"
        );

        Serial.print(
          "Arah  : "
        );

        Serial.println(
          "A -> B"
        );

        Serial.print(
          "Total : "
        );

        Serial.println(
          vehicleCount
        );

        Serial.println(
          "========================="
        );


        state =
          STATE_LOCKED;

        clearFrameCounter =
          0;
      }


      else if (
        now - stateStartTime >
        CROSS_TIMEOUT
      ) {

        state =
          STATE_IDLE;
      }


      break;



    // ==================================================
    // B -> A
    // ==================================================

    case STATE_WAIT_A:


      if (zoneA) {

        vehicleCount++;

        countBToA++;

        lastDirection =
          "B -> A";


        Serial.println();
        Serial.println(
          "========================="
        );

        Serial.println(
          "KENDARAAN TERHITUNG"
        );

        Serial.print(
          "Arah  : "
        );

        Serial.println(
          "B -> A"
        );

        Serial.print(
          "Total : "
        );

        Serial.println(
          vehicleCount
        );

        Serial.println(
          "========================="
        );


        state =
          STATE_LOCKED;

        clearFrameCounter =
          0;
      }


      else if (
        now - stateStartTime >
        CROSS_TIMEOUT
      ) {

        state =
          STATE_IDLE;
      }


      break;



    // ==================================================
    // TUNGGU KENDARAAN KELUAR
    // ==================================================

    case STATE_LOCKED:


      if (
        !zoneA &&
        !zoneB
      ) {

        clearFrameCounter++;


        if (
          clearFrameCounter >=
          CLEAR_FRAME_REQUIRED
        ) {

          state =
            STATE_IDLE;

          clearFrameCounter =
            0;

          Serial.println(
            "Area kosong. Siap kendaraan berikutnya."
          );
        }

      }

      else {

        clearFrameCounter = 0;
      }


      break;
  }
}


// ======================================================
// NAMA STATUS
// ======================================================

String getStateName() {

  switch(state) {

    case STATE_IDLE:

      return "MENUNGGU";


    case STATE_WAIT_B:

      return "A TERDETEKSI";


    case STATE_WAIT_A:

      return "B TERDETEKSI";


    case STATE_LOCKED:

      return "KENDARAAN LEWAT";

  }


  return "-";
}


// ======================================================
// WEBSERVER
// ======================================================

void setupWebServer() {


  server.on(
    "/",
    HTTP_GET,
    []() {

      server.send_P(
        200,
        "text/html",
        PAGE
      );
    }
  );


  server.on(
    "/status",
    HTTP_GET,
    []() {

      String json =
        "{";


      json +=
        "\"count\":" +
        String(vehicleCount) +
        ",";


      json +=
        "\"ab\":" +
        String(countAToB) +
        ",";


      json +=
        "\"ba\":" +
        String(countBToA) +
        ",";


      json +=
        "\"direction\":\"" +
        lastDirection +
        "\",";


      json +=
        "\"state\":\"" +
        getStateName() +
        "\",";


      json +=
        "\"motionA\":" +
        String(motionA) +
        ",";


      json +=
        "\"motionB\":" +
        String(motionB);


      json += "}";


      server.send(
        200,
        "application/json",
        json
      );

    }
  );


  server.on(
    "/reset",
    HTTP_GET,
    []() {

      vehicleCount = 0;

      countAToB = 0;

      countBToA = 0;

      lastDirection = "-";

      state =
        STATE_IDLE;


      server.send(
        200,
        "text/plain",
        "RESET OK"
      );


      Serial.println(
        "Counter di-reset"
      );
    }
  );


  server.begin();


  Serial.println(
    "Webserver aktif"
  );
}


// ======================================================
// SETUP
// ======================================================

void setup() {

  Serial.begin(
    115200
  );


  Serial.println();
  Serial.println(
    "=============================="
  );

  Serial.println(
    "ESP32-CAM VEHICLE COUNTER"
  );

  Serial.println(
    "=============================="
  );


  // ==================================================
  // CAMERA CONFIG
  // ==================================================

  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;


  // Grayscale jauh lebih ringan
  // daripada RGB / JPEG

  config.pixel_format =
    PIXFORMAT_GRAYSCALE;


  config.frame_size =
    FRAMESIZE_QQVGA;


  config.jpeg_quality =
    12;


  config.fb_count =
    1;


  // ==================================================
  // START CAMERA
  // ==================================================

  esp_err_t err =
    esp_camera_init(
      &config
    );


  if (
    err != ESP_OK
  ) {

    Serial.printf(
      "Camera error: 0x%x\n",
      err
    );

    return;
  }


  Serial.println(
    "Camera berhasil"
  );


  // ==================================================
  // ALOKASI FRAME SEBELUMNYA
  // ==================================================

  if (
    psramFound()
  ) {

    previousFrame =
      (uint8_t*)
      ps_malloc(
        FRAME_SIZE
      );

  }

  else {

    previousFrame =
      (uint8_t*)
      malloc(
        FRAME_SIZE
      );
  }


  if (
    previousFrame == NULL
  ) {

    Serial.println(
      "Gagal alokasi previousFrame"
    );

    return;
  }


  memset(
    previousFrame,
    0,
    FRAME_SIZE
  );


  // ==================================================
  // ACCESS POINT
  // ==================================================

  WiFi.mode(
    WIFI_AP
  );


  IPAddress localIP(
    192,
    168,
    4,
    1
  );


  IPAddress gateway(
    192,
    168,
    4,
    1
  );


  IPAddress subnet(
    255,
    255,
    255,
    0
  );


  WiFi.softAPConfig(
    localIP,
    gateway,
    subnet
  );


  WiFi.softAP(
    AP_SSID,
    AP_PASSWORD
  );


  Serial.println();
  Serial.println(
    "HOTSPOT AKTIF"
  );


  Serial.print(
    "SSID     : "
  );

  Serial.println(
    AP_SSID
  );


  Serial.print(
    "Password : "
  );

  Serial.println(
    AP_PASSWORD
  );


  Serial.print(
    "IP       : "
  );

  Serial.println(
    WiFi.softAPIP()
  );


  setupWebServer();


  Serial.println();
  Serial.println(
    "Buka:"
  );

  Serial.println(
    "http://192.168.4.1"
  );


  Serial.println(
    "=============================="
  );
}


// ======================================================
// LOOP
// ======================================================

void loop() {

  server.handleClient();


  camera_fb_t* fb =
    esp_camera_fb_get();


  if (
    fb == NULL
  ) {

    delay(20);

    return;
  }


  // Pastikan grayscale
  // 160 x 120

  if (
    fb->format !=
    PIXFORMAT_GRAYSCALE
  ) {

    esp_camera_fb_return(
      fb
    );

    return;
  }


  // ==================================================
  // FRAME PERTAMA
  // ==================================================

  static bool firstFrame =
    true;


  if(firstFrame) {

    memcpy(
      previousFrame,
      fb->buf,
      FRAME_SIZE
    );


    firstFrame =
      false;


    esp_camera_fb_return(
      fb
    );


    delay(100);

    return;
  }


  // ==================================================
  // HITUNG MOTION
  // ==================================================

  motionA =
    calculateMotion(

      fb->buf,
      previousFrame,

      ZONE_A_X1,
      ZONE_A_X2,

      ZONE_Y1,
      ZONE_Y2

    );


  motionB =
    calculateMotion(

      fb->buf,
      previousFrame,

      ZONE_B_X1,
      ZONE_B_X2,

      ZONE_Y1,
      ZONE_Y2

    );


  bool zoneAActive =
    motionA >=
    MOTION_THRESHOLD;


  bool zoneBActive =
    motionB >=
    MOTION_THRESHOLD;


  processVehicle(
    zoneAActive,
    zoneBActive
  );


  // ==================================================
  // SIMPAN FRAME SEKARANG
  // ==================================================

  memcpy(
    previousFrame,
    fb->buf,
    FRAME_SIZE
  );


  esp_camera_fb_return(
    fb
  );


  delay(70);
}

 
 
3. VIDEO HASILNYA
 

 

ESP32 Cam Deteksi Object / Benda

ESP32 Cam Deteksi Object / Benda


        Pada kesempatan kali ini saya akan menjelaskan mengenai bagaimana cara membuat sebuah ESP32 Cam dapat mendeteksi object yang berada di epannya. untuk contoh pada video kali ini objectnya yaitu bola, lampu dan spidol yang mana masihng2 memiliki size yang berbeda. untuk detailnya berikut kodingnya.
 
 
1. Interface
 

 
 2. Program ESP32 Cam
 
 #include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>


//================ WIFI =================

const char* ssid = "ESP32-OBJECT";
const char* password = "12345678";

WebServer server(80);

Preferences memory;


//================ AI THINKER CAMERA =================

#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



//================ DATA OBJECT =================


struct ObjectData
{
  bool aktif;

  float area;

  float ratio;

  float red;

  float green;

  float blue;
};



ObjectData data[4];


String namaObject[4]=
{
  "BOLA",
  "SPIDOL",
  "LAMPU",
  "UNKNOWN"
};



String hasil="BELUM ADA";

float areaNow=0;
float ratioNow=0;

float rNow=0;
float gNow=0;
float bNow=0;

int confidence=0;



unsigned long timer=0;



//================ SIMPAN =================


void saveData(int id)
{

memory.begin("object",false);

String key="d"+String(id);


memory.putBytes(
key.c_str(),
&data[id],
sizeof(ObjectData)
);


memory.end();

}



//================ LOAD =================


void loadData()
{

memory.begin("object",true);


for(int i=0;i<4;i++)
{

String key="d"+String(i);


memory.getBytes(
key.c_str(),
&data[i],
sizeof(ObjectData)
);

}


memory.end();

}



//================ RESET =================


void resetData()
{

for(int i=0;i<4;i++)
{

data[i].aktif=false;

}


memory.begin("object",false);

memory.clear();

memory.end();


}



//================ BACA OBJECT =================


void bacaObject()
{


camera_fb_t *fb =
esp_camera_fb_get();


if(!fb)
return;



int total=0;

long R=0;
long G=0;
long B=0;



int minX=320;
int maxX=0;

int minY=240;
int maxY=0;



for(int y=0;y<fb->height;y+=2)
{

for(int x=0;x<fb->width;x+=2)
{


int index=(y*fb->width+x)*2;



uint16_t pixel =
(fb->buf[index]<<8)
|
fb->buf[index+1];



int r =
((pixel>>11)&31)*255/31;


int g =
((pixel>>5)&63)*255/63;


int b =
(pixel&31)*255/31;



// object beda warna dari background

if(
abs(r-g)>20 ||
abs(g-b)>20 ||
abs(r-b)>20
)

{


total++;


R+=r;

G+=g;

B+=b;



if(x<minX) minX=x;

if(x>maxX) maxX=x;

if(y<minY) minY=y;

if(y>maxY) maxY=y;


}


}

}



if(total>20)
{


areaNow=total;


rNow=R/total;

gNow=G/total;

bNow=B/total;



float w=maxX-minX;

float h=maxY-minY;



if(h>0)

ratioNow=w/h;


klasifikasi();


}

else

{

hasil="TIDAK ADA";

confidence=0;

}



esp_camera_fb_return(fb);


}



//================ KLASIFIKASI =================


void klasifikasi()
{


float best=99999;

int index=-1;



for(int i=0;i<4;i++)

{


if(!data[i].aktif)
continue;



float nilai=0;



nilai += abs(areaNow-data[i].area)/100;


nilai += abs(ratioNow-data[i].ratio)*20;


nilai += abs(rNow-data[i].red)/20;


nilai += abs(gNow-data[i].green)/20;


nilai += abs(bNow-data[i].blue)/20;



if(nilai<best)

{

best=nilai;

index=i;

}


}



if(index>=0)

{


confidence=
100-(best*5);



if(confidence<0)
confidence=0;



if(confidence>100)
confidence=100;



hasil=namaObject[index];


}

else

{

hasil="UNKNOWN";

confidence=0;

}



}



//================ KALIBRASI =================


void kalibrasi()
{


if(!server.hasArg("objek"))
return;



String obj=
server.arg("objek");



int id=-1;



if(obj=="BOLA") id=0;

if(obj=="SPIDOL") id=1;

if(obj=="LAMPU") id=2;

if(obj=="UNKNOWN") id=3;



if(id>=0)

{


data[id].aktif=true;

data[id].area=areaNow;

data[id].ratio=ratioNow;

data[id].red=rNow;

data[id].green=gNow;

data[id].blue=bNow;


saveData(id);


}



server.send(
200,
"text/plain",
"KALIBRASI OK "+obj
);


}



//================ DATA JSON =================


void dataJSON()
{


String json="{";


json+="\"hasil\":\""+hasil+"\",";


json+="\"confidence\":"+String(confidence)+",";


json+="\"area\":"+String(areaNow);


json+="}";



server.send(
200,
"application/json",
json
);


}



//================ WEB =================


void home()
{


String html=R"rawliteral(

<html>

<body style="text-align:center;font-family:Arial">


<h2>
ESP32 OBJECT DETECTOR
</h2>


<h1 id="hasil">
-
</h1>


<h3>
Confidence:
<span id="conf">
0
</span>%
</h3>


<h3>
Area:
<span id="area">
0
</span>
</h3>



<hr>


<h3>
KALIBRASI
</h3>


<button onclick="kal('BOLA')">
⚽ BOLA
</button>


<button onclick="kal('SPIDOL')">
🖊️ SPIDOL
</button>


<button onclick="kal('LAMPU')">
💡 LAMPU
</button>


<button onclick="kal('UNKNOWN')">
UNKNOWN
</button>



<br><br>


<button onclick="reset()">
RESET
</button>



<script>


setInterval(()=>{


fetch('/data')

.then(r=>r.json())

.then(d=>{


hasil.innerHTML=d.hasil;

conf.innerHTML=d.confidence;

area.innerHTML=d.area;


});


},500);



function kal(x)
{

fetch('/kal?objek='+x);

}


function reset()
{

fetch('/reset');

}



</script>


</body>

</html>

)rawliteral";



server.send(
200,
"text/html",
html
);


}



//================ SETUP =================


void setup()
{


Serial.begin(115200);



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_RGB565;


config.frame_size=FRAMESIZE_QQVGA;


config.fb_count=1;



esp_camera_init(&config);



loadData();



WiFi.mode(WIFI_AP);


WiFi.softAP(
ssid,
password
);



Serial.println(
WiFi.softAPIP()
);



server.on("/",home);

server.on("/data",dataJSON);

server.on("/kal",kalibrasi);


server.on("/reset",[](){

resetData();

server.send(
200,
"text/plain",
"RESET"
);

});


server.begin();


}



//================ LOOP =================


void loop()
{

server.handleClient();


if(millis()-timer>700)

{

bacaObject();

timer=millis();

}


}
 
 
 
3. VIDEO HASILNYA
 

ESP32 CAM DETEKSI WARNA BOLA

ESP32 CAM DETEKSI WARNA BOLA
 

       Pada kesempatan kali ini saya akan menjelaskan mengenai bagaimana cara membuat sebuah alat yang menggunkan ESP32 Cam sebagai sensor untuk deteksi warna bola. jadi ada 4 bola dengan wana berbeda lalu ditaruh didepan kamera, kemudian kamera mendeteksi nilai RGBnya dan akan menampilkan warna bolanya. berikut kodingnya.
 
 
1. Interface
 


2. Program ESP32 Cam
 
 #include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>


// ================= WIFI =================

const char* ssid = "ESP32-COLOR";
const char* password = "12345678";

WebServer server(80);


// ================= FLASH =================

#define FLASH_LED 4



// ================= 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



// ================= RGB =================

int R=0;
int G=0;
int B=0;


String color="UNKNOWN";

String ballColor="TIDAK ADA";

unsigned long timerRGB=0;



// ================= DATA KALIBRASI =================


struct ColorData
{

int r;
int g;
int b;

String nama;

};



ColorData dataWarna[5];

int jumlahWarna=0;



// ================= SIMPAN KALIBRASI =================


void simpanKalibrasi(String nama)
{


if(jumlahWarna>=5)
return;



dataWarna[jumlahWarna].r=R;

dataWarna[jumlahWarna].g=G;

dataWarna[jumlahWarna].b=B;


dataWarna[jumlahWarna].nama=nama;



jumlahWarna++;



Serial.println("KALIBRASI");

Serial.println(nama);

Serial.print(R);
Serial.print(",");
Serial.print(G);
Serial.print(",");
Serial.println(B);


}



// ================= RESET =================


void resetKalibrasi()
{

jumlahWarna=0;


server.send(
200,
"text/plain",
"RESET OK"
);

}



// ================= BACA RGB =================


void readRGB()
{


camera_fb_t *fb =
esp_camera_fb_get();



if(!fb)
return;



long totalR=0;
long totalG=0;
long totalB=0;


int count=0;



int roi=80;



int startX=(fb->width-roi)/2;

int startY=(fb->height-roi)/2;



for(int y=startY;y<startY+roi;y+=2)
{


for(int x=startX;x<startX+roi;x+=2)
{


int index=(y*fb->width+x)*2;



uint16_t pixel=
(fb->buf[index]<<8)
|
fb->buf[index+1];



int r=
((pixel>>11)&31)*255/31;


int g=
((pixel>>5)&63)*255/63;


int b=
(pixel&31)*255/31;



totalR+=r;

totalG+=g;

totalB+=b;


count++;


}


}



if(count)
{

R=totalR/count;

G=totalG/count;

B=totalB/count;

}



esp_camera_fb_return(fb);



color=detectColor();



}



// ================= DETEKSI WARNA =================


String detectColor()
{


if(jumlahWarna==0)
return "BELUM KALIBRASI";



long jarakMin=999999;


String hasil="UNKNOWN";



for(int i=0;i<jumlahWarna;i++)
{


long dr=R-dataWarna[i].r;

long dg=G-dataWarna[i].g;

long db=B-dataWarna[i].b;



long jarak=

dr*dr+
dg*dg+
db*db;



if(jarak<jarakMin)

{

jarakMin=jarak;

hasil=dataWarna[i].nama;

}


}



if(jarakMin>8000)

return "UNKNOWN";


return hasil;


}



// ================= DETEKSI BOLA =================


void detectBall()
{


if(color=="MERAH")

ballColor="🔴 BOLA MERAH";


else if(color=="HIJAU")

ballColor="🟢 BOLA HIJAU";


else if(color=="BIRU")

ballColor="🔵 BOLA BIRU";


else if(color=="KUNING")

ballColor="🟡 BOLA KUNING";


else if(color=="PUTIH")

ballColor="⚪ BOLA PUTIH";


else

ballColor="⚫ TIDAK ADA BOLA";


}



// ================= WEB DATA =================


void dataRGB()
{


String json="{";


json+="\"R\":"+String(R)+",";

json+="\"G\":"+String(G)+",";

json+="\"B\":"+String(B)+",";


json+="\"COLOR\":\""+color+"\",";


json+="\"BALL\":\""+ballColor+"\"";


json+="}";


server.send(
200,
"application/json",
json
);


}



// ================= WEB KALIBRASI =================


void webKalibrasi()
{


if(server.hasArg("warna"))
{


simpanKalibrasi(
server.arg("warna")
);


}


server.send(
200,
"text/plain",
"OK"
);


}



// ================= HALAMAN WEB =================


void home()
{


String html=R"rawliteral(

<html>

<head>

<meta name='viewport' content='width=device-width'>


<style>

body{

text-align:center;

font-family:Arial;

}


button{

font-size:18px;

padding:10px;

margin:5px;

}


</style>


</head>



<body>


<h2>
ESP32 COLOR BALL DETECTOR
</h2>



<h3>RGB</h3>


R :
<span id="r">
0
</span>

<br>


G :
<span id="g">
0
</span>

<br>


B :
<span id="b">
0
</span>



<h2 id="warna">
UNKNOWN
</h2>


<hr>


<h2>
DETEKSI BOLA
</h2>


<h1 id="bola">
⚫ TIDAK ADA
</h1>



<hr>


<h3>
KALIBRASI WARNA
</h3>


<button onclick="kal('MERAH')">
MERAH
</button>


<button onclick="kal('HIJAU')">
HIJAU
</button>


<button onclick="kal('BIRU')">
BIRU
</button>


<button onclick="kal('KUNING')">
KUNING
</button>


<button onclick="kal('PUTIH')">
PUTIH
</button>


<br>


<button onclick="reset()">
RESET
</button>




<script>


function kal(w)

{

fetch('/kal?warna='+w);

}



function reset()

{

fetch('/reset');

}




setInterval(()=>{


fetch('/data')

.then(x=>x.json())

.then(d=>{


r.innerHTML=d.R;

g.innerHTML=d.G;

b.innerHTML=d.B;


warna.innerHTML=d.COLOR;


bola.innerHTML=d.BALL;



});


},500);



</script>


</body>

</html>


)rawliteral";



server.send(
200,
"text/html",
html
);


}



// ================= SETUP =================


void setup()
{


Serial.begin(115200);



pinMode(
FLASH_LED,
OUTPUT
);


digitalWrite(
FLASH_LED,
LOW
);





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_RGB565;


config.frame_size=FRAMESIZE_QQVGA;


config.fb_count=1;



esp_camera_init(&config);





WiFi.mode(WIFI_AP);


WiFi.softAP(
ssid,
password
);



Serial.println(
WiFi.softAPIP()
);



server.on("/",home);

server.on("/data",dataRGB);

server.on("/kal",webKalibrasi);

server.on("/reset",resetKalibrasi);



server.begin();


}



// ================= LOOP =================


void loop()
{


server.handleClient();



if(millis()-timerRGB>500)

{


readRGB();


detectBall();


timerRGB=millis();


}


}
 
 
3. VIDEO HASILNYA
 


 

ESP32 Cam Deteksi Kecepatan dan Gerakan

ESP32 Cam Deteksi Kecepatan dan Gerakan
 
      
         Pada kesempatan kali ini saya akan menjelaskan mengenai bagaimana cara untuk membuat sebuah alat yang bisa untuk mendeteksi kecepatan dan gerakan menggunakan ESP32 Cam yang mana deteksinya menggunakan kamera. untuk lebih jelasnya berikut kodingnya.
 
 
1. Interface
 
 
 
 
2. Program ESP32 Cam 
 
 #include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>


//================ WIFI =================

const char* ssid="ESP32-SPEED";
const char* password="12345678";

WebServer server(80);



//================ CAMERA 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



//================ ROI DETEKSI =================

#define ROI_X 40
#define ROI_Y 30
#define ROI_W 80
#define ROI_H 60



uint8_t frameLama[ROI_W*ROI_H];

bool pertama=true;



int nilaiMotion=0;



//================ SPEED =================

// perkiraan jarak perpindahan objek

float jarakObjek=2.0;


unsigned long waktuMulai=0;

unsigned long waktuTerakhir=0;


bool objekAktif=false;


float speedKMH=0;



//================ DETEKSI MOTION =================


void detectMotion()
{


camera_fb_t *fb =
esp_camera_fb_get();



if(!fb)
return;



int bedaPixel=0;

int totalPixel=0;


int indexROI=0;



for(int y=ROI_Y;
y<ROI_Y+ROI_H;
y++)
{

for(int x=ROI_X;
x<ROI_X+ROI_W;
x++)
{


int index =
y*fb->width+x;



uint8_t pixel =
fb->buf[index];



if(!pertama)
{

int beda =
abs(pixel-frameLama[indexROI]);


if(beda>25)
{
bedaPixel++;
}

}


frameLama[indexROI]=pixel;


indexROI++;

totalPixel++;

}

}



esp_camera_fb_return(fb);



if(pertama)
{

pertama=false;

return;

}



nilaiMotion =
(bedaPixel*100)/totalPixel;



Serial.print("Motion : ");
Serial.print(nilaiMotion);
Serial.println("%");



//================ HITUNG SPEED =================


if(nilaiMotion > 15)
{


if(!objekAktif)
{

waktuMulai=millis();

objekAktif=true;


Serial.println("OBJEK TERDETEKSI");

}


waktuTerakhir=millis();


}


else
{


if(objekAktif && 
millis()-waktuTerakhir>700)
{


unsigned long waktuAkhir =
millis();



float waktu =
(waktuAkhir-waktuMulai)/1000.0;



if(waktu>0)
{

float speedMS =
jarakObjek/waktu;


speedKMH =
speedMS*3.6;

}


objekAktif=false;


Serial.print("Speed : ");

Serial.print(speedKMH);

Serial.println(" Km/h");

}



if(millis()-waktuTerakhir>2000)
{

speedKMH=0;

}

}


}



//================ WEB DATA =================


void dataSpeed()
{

String json="{";


json+="\"motion\":";

json+=String(nilaiMotion);



json+=",";


json+="\"speed\":";

json+=String(speedKMH,2);



json+="}";


server.send(
200,
"application/json",
json
);

}



//================ WEB =================


void home()
{


String html=R"rawliteral(

<html>

<head>

<meta name='viewport' content='width=device-width'>

</head>


<body style='text-align:center;font-family:Arial'>


<h2>
ESP32 CAMERA SPEED
</h2>


<h3>
Motion :
<span id="motion">
0
</span>
%
</h3>



<h1>

<span id="speed">
0
</span>

Km/h

</h1>



<script>


setInterval(()=>{


fetch('/data')

.then(x=>x.json())

.then(d=>{


motion.innerHTML=d.motion;


speed.innerHTML=d.speed;



});


},500);



</script>



</body>

</html>

)rawliteral";


server.send(
200,
"text/html",
html
);

}



//================ SETUP =================


void setup()
{

Serial.begin(115200);



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_GRAYSCALE;


config.frame_size=
FRAMESIZE_QQVGA;


config.fb_count=1;



esp_camera_init(&config);



// WIFI AP

WiFi.mode(WIFI_AP);


WiFi.softAP(
ssid,
password
);



Serial.println(
WiFi.softAPIP()
);



server.on(
"/",
home
);


server.on(
"/data",
dataSpeed
);


server.begin();

}



//================ LOOP =================


void loop()
{

server.handleClient();


detectMotion();


delay(100);

}
 
 
 
3. VIDEO HASILNYA