ESP32-CAM Upload Foto ke Server (ESP32CAM Bootstrap 5 Gallery)

ESP32CAM Bootstrap 5 Gallery

Hallo teman-teman sekalian, kebetulan saya lagi ada modul esp32cam. Kesempatan buat coba-coba dam mempelajari barang baru. oke langsung saja, pada case ini, kita akan melakukan upload data gambar hasil dari jepretan modul ESP32CAM ke server. Metodenya gimana nigh? mengingat processor esp yang lebih yahud untuk urusan IoT dan kapasitas memori, maka kita dapat menggunakan metode HTTP POST. Data yang dikirim ke server dengan POST disimpan di body dari struktur request HTTP.

Sebagaimana pada penggunaan di metode GET, pada metode POST ini kita juga memerlukan bantuan API. webAPI kita buat menggunakan program php (yang saya paham aja). Nah fungsi API ini nantinya digunakan untuk menampung data yang dikirim via metode POST kemudian diuraikan dan dicek. Apabila kondisinya memenuhi (dalam hal ini merupakan gambar yang valid) maka dilakukan proses penyimpanan file gambar tersebut di server. Ok langsung kita cus saja... ikuti langkah berikut

 XAMPP Control panel

Jalankan service Apache dan MySQL (klik tombol start )

ESP32CAM Bootstrap 5 Gallery

Pembuatan program web untuk Gallery display dan API

Ok, pertama kita akan buat API untuk proses upload file, kita siapkan dulu folder di direktori C:\xampp\htdocs . Kita siapkan folder dengan nama esp32cam_gallery 

ESP32CAM Bootstrap 5 Gallery
Masuk kedalam folder esp32cam_gallery  yang telah dibuat. kemudian buat lagi folder dengan nama captured_images , folder ini nantinya digunakan untuk menyimpan gambar. File API upload_img.php akan mengakses folder ini untuk menympan data gambar yang telah diterima.

ESP32CAM upload post image

Masih di dalam folder esp32cam_gallery, selanjutnya kita akan buat file dengan nama upload_img.php via notepad ++ , buka program notepad++ kemudian kopi paste program dibawah ini. selanjutnya simpan dengan nama  upload_img.php  di folder/direktori yang tadi kita buat (C:\xampp\htdocs\esp32cam_gallery)

<?php
//based on PHP File Upload basic example https://www.w3schools.com/php/php_file_upload.asp

date_default_timezone_set('Asia/Jakarta');
$target_dir = "captured_images/"; //folder untuk menyimpan gambar
$date   = new DateTime(); //this returns the current date time
$date_string = $date->format('Y-m-d His');
$target_file = $target_dir . $date_string. basename($_FILES["imageFile"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
$file_name = pathinfo($target_file,PATHINFO_BASENAME) ; 

// Check if image file is a actual image or fake image
if(isset($_POST["imageFile"])) {	
	$check = getimagesize($_FILES["imageFile"]["tmp_name"]);
	  if($check !== false) {
		echo "File is an image - " . $check["mime"] . ".";
		$uploadOk = 1;
	  } else {
		echo "File is not an image.";
		$uploadOk = 0;
	  }
  
}

// Check if file already exists
if (file_exists($target_file)) {
  echo "Sorry, file already exists.";
  $uploadOk = 0;
}

// Check file size
if ($_FILES["imageFile"]["size"] > 500000) {
  echo "Sorry, your file is too large.";
  $uploadOk = 0;
}

// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg" && $imageFileType != "gif" ) {
  echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
  $uploadOk = 0;
}

// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
  echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
} else {
  if (move_uploaded_file($_FILES["imageFile"]["tmp_name"], $target_file)) {
	
    echo "Photo berhasil dipuload di server dengan nama " .$file_name;
	
  } else {
    echo "Sorry, Ada error dalam proses upload photo.";
  }
}
?>

Selanjutnya kita buat file untuk melakukan testing upload gambar secara lokal dari komputer kita. Buat lembar baru dengan nama tester.php masukkan kode dibawah ini dan kemudian simpan di folder yang sama dengan file sebelumnya.

<!doctype html>
<html lang="en">
<head>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" 
	rel="stylesheet" 
	integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" 
	crossorigin="anonymous"
    >
</head>
<body>
<div class="container">
 
  <div class="d-flex align-items-center" style="margin-top:15%;">
  
    <div class="col-md6" style="padding:10px; border:2px solid black">
      <h2>Test Upload manual</h2>
	  <br>
	<form action="upload_img.php" method="post" enctype="multipart/form-data">
	
	  <div class="form-group">
		<label for="image">Input Image</label>
		<input type="file" name="imageFile" id="imageFile">
	  </div>
	  <br>
	  <input type="submit" value="Upload Image" name="submit" class="btn btn-primary">
	</form>
	</div>
  </div>
</div>
</body>
</html>

Sebelum melangkah lebih jauh, mari kita coba apakah file  API upload_img.php sudah berfungsi dengan baik.. akses file tester.php web browser di http://localhost/esp32cam_gallery/tester.php . Pilih gambar (bebas aja) kemudian klik upload image. Kalau berhasil akan muncul nitif seperti dibawah, selanjutnya dicek di folder captured_images apakah gembar benar-benar terupload atau boongan. nah apabila ada kendala sampai sini silahkan komen-komen...

ESP32CAM upload post image

ESP32CAM upload post image

ESP32CAM Upload Foto ke Server

Dengan berhasil test diatas maka dapat diasumsikan file upload_img.php sudah berfungsi dengan baik. selanjutnya kita akan buat halaman gallery yang digunakan untuk menampilkan hasil photo esp32cam nantinya. kita akan menggunakan framework Bootstrap 5 yang diakses secara CDN. buat file index.php dengan kode berikut ini

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>ESP32CAM Gallery Demo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  
 </head>
  <body>
   <div class="container" style="padding-top:30px;">
    <div class="d-flex justify-content-center" ><h1>ESP32CAM PHOTO Gallery</h1></div>
	<hr class="mt-2 mb-5">
<?php
  // Image extensions
  $image_extensions = array("png","jpg","jpeg","gif");

  // Check delete HTTP GET request - remove images
  if(isset($_GET["delete"])){
	$imageFileType = strtolower(pathinfo($_GET["delete"],PATHINFO_EXTENSION));
	if (file_exists($_GET["delete"]) && ($imageFileType == "jpg" ||  $imageFileType == "png" ||  $imageFileType == "jpeg") ) {
	  unlink($_GET["delete"]);
	  echo "<script>
			   $(document).ready(function(){
					$('#myModalOK').modal('show');
				});
				
			 </script>";
	}
	else {
	  echo 'File not found - <a href="galeria.php">refresh</a>';
	}
  }
  // Target directory
  $dir = 'captured_images/';
  if (is_dir($dir)){
	?>
	<div class="row text-center text-lg-start">
	<?php
	$count = 1;
	$files = scandir($dir);
	rsort($files);
	foreach ($files as $file) {
	if ($file != '.' && $file != '..') {?>
		<div class="col-lg-3 col-md-4 col-6" style="padding-bottom:30px;">
		 <div class="row">
		   <a href="<?php echo $dir . $file; ?>" class="d-block mb-4 h-100">
			<img class="img-fluid img-thumbnail" src="<?php echo $dir . $file; ?>" alt="">
		   </a>
		  </div>
		  <div class="row justify-content-end">
			<div class="col md-8">
			  <p><?php echo $file; ?></p>
			</div>
			<div class="col md-4">
			  <a href="index.php?delete=<?php echo $dir . $file; ?>" class="btn btn-danger btn-sm">Delete</a>
			</div>
		   
		  </div>
		</div>
	<?php
		   $count++;
		  }
		}
	   if($count==1) { echo "<p>No images found</p>"; } 
	  }
	  
	?>
	</div>
	
	<!-- Modal Delete OK-->
	<div class="modal fade" id="myModalOK" tabindex="-1" aria-labelledby="myModalOKLabel" aria-hidden="true">
	  <div class="modal-dialog modal-dialog-centered">
		<div class="modal-content">
		  <div class="modal-header" style="background-color:#2cc791;">
			<h5 class="modal-title" id="exampleModalLabel" style="color:white;">Success</h5>
			<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
		  </div>
		  <div class="modal-body">
			Successfully delete image file <b><?php $path_parts = pathinfo($_GET["delete"],PATHINFO_BASENAME) ; echo $path_parts; ?> </b>
		  </div>
		  <div class="modal-footer">
			<a class="btn btn-primary" href="index.php" role="button">OK</a>
		  </div>
		</div>
	  </div>
	</div>
	
	<!-- Modal Delete Not OK-->
	<div class="modal fade" id="myModalOK" tabindex="-1" aria-labelledby="myModalOKLabel" aria-hidden="true">
	  <div class="modal-dialog modal-dialog-centered">
		<div class="modal-content">
		  <div class="modal-header" style="background-color:#fc8403;">
			<h5 class="modal-title" id="exampleModalLabel" style="color:white;">Success</h5>
			<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
		  </div>
		  <div class="modal-body">
			Something wrong while deleting file <?php $path_parts = pathinfo($_GET["delete"],PATHINFO_BASENAME) ; echo $path_parts; ?>
		  </div>
		  <div class="modal-footer">
			<a class="btn btn-secondary" href="galeria.php" role="button">OK</a>
		  </div>
		</div>
	  </div>
	</div>
  <div>
  </body>
</html>

ok, semua file sudah dibuat, cek ulang struktur direktorinya (kalau ada yang kelewat). pastikan seperti berikut ya!

ESP32CAM Upload Foto ke Server
Kalau sudah ok, ayok kita langsung cek di gallerynya (file index.php). akses via web browser ke http://localhost/esp32cam_gallery/index.php . karena tadi baru upload satu gambar maka kurang lebih akan tampil sebagai berikut dibawah. nah nantinya halaman ini akan menampilkan hasil jepretan kamera esp32CAM ya. 
bootstrap 5 image gallery

Program ESP32-CAM via arduino IDE

Ok, sebelum bermain-main dengan ESP microcontroller, pastikan kita telah menginstall boardnya terlebih dahulu. Nah untuk esp8266 dan esp32 itu beda ya untuk board nya yang di install di arduino IDE. hal ini merupakan hal dasar jd silahkan dipelajari di youtube atau tutorial lain

Nah peralatan/modul yang diperlukan

  • Arduino IDE
  • ESP32-CAM CAMERA OV2640  (cari di OL shop)
  • Downloder program bisa menggunakan Dev Board USB to TTL CH340, atau menggunakan arduino uno, atau menggunakan downloader2 lainnya (USB serial TTL)
  • wifi hotspot
untuk program kita buat sederhana terlebih dahulu. Jadi program akan melakukan pengambilan gambar dengan jangka waktu tertentu, selanjutnya kegiatan ini akan terus berulang. Proses interval akan dikerjakan oleh fungsi milis(). sebelum upload program ke perangkat, cek terlebih dahulu alamat servernya (alamat akan diakses saat proses pengiriman gambar). hal lain yang perlu diperhatikan adalah perangkat esp32cam dan server harus dalam 1 jaringan (menggunakan wifi yang sama). buka cmd trus ketikkan ipconfig. nah sebagai contoh disini alamat server terdeteksi 192.168.1.4 (alamat ini nantinya yang akan dituliskan pada program)
bootstrap 5 image gallery

Ok sekarang untuk program ESP32-CAM nya dapat di salin seperti berikut ini.
#include <Arduino.h>
#include <WiFi.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"

const char* ssid = "Android_AP";
const char* password = "YourWifiPassword";

String serverName = "192.168.1.4";  //alamat server  
String serverPath = "/esp32cam_gallery/upload_img.php";     
const int serverPort = 80;

WiFiClient client;

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

const int Interval = 30000;    // proses pengambilan photo interval 30 detik
unsigned long previousMillis = 0; 
String jsonres;

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("ESP32-CAM IP Address: ");
  Serial.println(WiFi.localIP());

  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;

  // init with high specs to pre-allocate larger buffers
  if(psramFound()){
    config.frame_size = FRAMESIZE_VGA;
    config.jpeg_quality = 12;  //0-63 lower number means higher quality
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 8;  //0-63 lower number means higher quality
    config.fb_count = 1;
  }
  
  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    delay(1000);
    ESP.restart();
  }

  sensor_t * s = esp_camera_sensor_get();
  s->set_framesize(s, FRAMESIZE_SVGA); //UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= Interval) {
    Serial.println("Bersiap kirim foto ke server..");
    sendPhoto();
    previousMillis = currentMillis;
  }
}

void sendPhoto() {
  String AllData;
  String DataBody;
 
  //pre capture for accurate timing
  for (int i = 0; i <= 3; i++) {
    camera_fb_t * fb = NULL;
    fb = esp_camera_fb_get();
     if(!fb) {
        Serial.println("Camera capture failed");
        delay(1000);
        ESP.restart();
        return;
      } 
    esp_camera_fb_return(fb);
    delay(200);
  }
  
  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();

  Serial.println("Connecting to server: " + serverName);

  if (client.connect(serverName.c_str(), serverPort)) {
    Serial.println("Connection successful!");   
     
    String post_data = "--dataMarker\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"_esp32Photo.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String head =  post_data;
    String boundary = "\r\n--dataMarker--\r\n";
    //Serial.println(head);
    uint32_t imageLen = fb->len;
    uint32_t dataLen = head.length() + boundary.length();
    uint32_t totalLen = imageLen + dataLen;
    
    client.println("POST " + serverPath + " HTTP/1.1");
    client.println("Host: " + serverName);
    client.println("Content-Length: " + String(totalLen));
    client.println("Content-Type: multipart/form-data; boundary=dataMarker");
    client.println();
    client.print(head);
  
    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    //Serial.println(fbLen);
    for (size_t n=0; n<fbLen; n=n+1024) {
      if (n+1024 < fbLen) {
        client.write(fbBuf, 1024);
        fbBuf += 1024;
      }
      else if (fbLen%1024>0) {
        size_t remainder = fbLen%1024;
        client.write(fbBuf, remainder);
      }
    }   
    client.print(boundary);
    
    esp_camera_fb_return(fb);
   
    int timoutTimer = 10000;
    long startTimer = millis();
    boolean state = false;
    Serial.println("Response:");
    while ((startTimer + timoutTimer) > millis()) {
      Serial.print(".");
      delay(200);
         
      // Skip HTTP headers   
      while (client.available()) {
        char c = client.read();
        if (c == '\n') {
          if (AllData.length()==0) { state=true; }
          AllData = "";
        }
        else if (c != '\r') { AllData += String(c); }
        if (state==true) { DataBody += String(c); }
        startTimer = millis();
      }
      if (DataBody.length()>0) { break; }
    }
    client.stop();
    Serial.println(DataBody);
    Serial.println("##############");
    Serial.println();
    
  }
  else {
    DataBody = "Connection to " + serverName +  " failed.";
    Serial.println(DataBody);
  }
}

Upload program ke esp32-cam, namun sebelumnya pastikan board yang dipilih adalah Ai Thinker ESP32-CAM
Arduino ESP32CAM Upload Foto ke Server


Ok, langsung test untuk mengambil gambar. untuk prosesny dapat dipantau via serial monitor. atau langsung cek di http://localhost/esp32cam_gallery/index.php

ESP32CAM Upload Foto ke Server

ESP32CAM photo gallery


Kesimpulan
Untuk hasil, jangan ngarep klo gambarnya bakal bening sebening air pegunungan.  Gambarnya hanya sebatas VGA (untuk jenis kamera tipe tertentu) jadi ya lumayan blur-blur. Atau barangkali ada temen-temen yang bisa mendapatkan hasil gambar yang tokcer, silahkan untuk komen-komen. Untuk platform API, dapat digunakan untuk keperluan yang lebih luas lagi dengan file yang lebih bagus (dngan perangkat lain tentunya). Thanks, selamat mencoba