251 lines
6.9 KiB
C++
251 lines
6.9 KiB
C++
#include "esp_camera.h"
|
|
#include <WiFi.h>
|
|
#include <WiFiClientSecure.h>
|
|
#include <ArduinoJson.h>
|
|
#include <FirebaseESP32.h>
|
|
#include <ESP32Servo.h>
|
|
|
|
const char* ssid = "Maratus soleha";
|
|
const char* password = "12345678";
|
|
|
|
// ================= Cloudinary =====================
|
|
const char* cloud_name = "duecjbzre";
|
|
const char* upload_preset = "esp32upload";
|
|
|
|
// ================= Firebase =====================
|
|
#define FIREBASE_AUTH "AIzaSyAHdV9uhz6NgtMN5x-NGJ5-XtdxOUpQW4g"
|
|
#define FIREBASE_HOST "https://paket-b7ef8-default-rtdb.asia-southeast1.firebasedatabase.app/"
|
|
|
|
FirebaseData firebaseData;
|
|
FirebaseAuth firebaseAuth;
|
|
FirebaseConfig firebaseConfig;
|
|
|
|
Servo myServo;
|
|
|
|
// ================= Ultrasonik =====================
|
|
#define TRIG_PIN 12
|
|
#define ECHO_PIN 13
|
|
bool objekTerdeteksi = false;
|
|
|
|
#define SERVO_PIN 14
|
|
|
|
// ================= Kamera ESP32-CAM =====================
|
|
#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
|
|
|
|
// ================= SETUP =====================
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
delay(1000);
|
|
|
|
pinMode(TRIG_PIN, OUTPUT);
|
|
pinMode(ECHO_PIN, INPUT);
|
|
|
|
WiFi.begin(ssid, password);
|
|
Serial.print("Menghubungkan WiFi");
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
delay(500);
|
|
Serial.print(".");
|
|
}
|
|
Serial.println("\n✅ WiFi terhubung!");
|
|
|
|
firebaseConfig.database_url = FIREBASE_HOST;
|
|
firebaseConfig.signer.tokens.legacy_token = FIREBASE_AUTH;
|
|
|
|
Firebase.begin(&firebaseConfig, &firebaseAuth);
|
|
Firebase.reconnectWiFi(true);
|
|
|
|
myServo.attach(SERVO_PIN);
|
|
myServo.write(0);
|
|
|
|
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 = 10;
|
|
config.fb_count = 1;
|
|
|
|
if (esp_camera_init(&config) != ESP_OK) {
|
|
Serial.println("❌ Kamera gagal diinisialisasi");
|
|
while (1);
|
|
}
|
|
}
|
|
|
|
// ================= LOOP =====================
|
|
void loop() {
|
|
float jarak = ukurJarak();
|
|
Serial.print("📏 Jarak: ");
|
|
Serial.print(jarak);
|
|
Serial.println(" cm");
|
|
|
|
// Cek jika objek baru terdeteksi (transisi: tidak ada → ada)
|
|
if (jarak > 0 && jarak <= 15.0 && !objekTerdeteksi) {
|
|
objekTerdeteksi = true;
|
|
Serial.println("📷 Objek terdeteksi! Mengambil gambar...");
|
|
ambilUploadDanSimpan();
|
|
}
|
|
|
|
// Reset flag jika objek menjauh
|
|
if (jarak > 20.0) {
|
|
objekTerdeteksi = false;
|
|
}
|
|
|
|
BukaTutupServo();
|
|
delay(500);
|
|
}
|
|
|
|
// ================= Ultrasonik =====================
|
|
float ukurJarak() {
|
|
digitalWrite(TRIG_PIN, LOW);
|
|
delayMicroseconds(2);
|
|
digitalWrite(TRIG_PIN, HIGH);
|
|
delayMicroseconds(10);
|
|
digitalWrite(TRIG_PIN, LOW);
|
|
long durasi = pulseIn(ECHO_PIN, HIGH, 30000);
|
|
if (durasi == 0) return -1;
|
|
return durasi * 0.034 / 2;
|
|
}
|
|
void BukaTutupServo() {
|
|
if (Firebase.getBool(firebaseData, "servo")) {
|
|
bool perintah = firebaseData.boolData();
|
|
Serial.print("🔧 Perintah servo: ");
|
|
Serial.println(perintah);
|
|
|
|
if (perintah == true) {
|
|
myServo.write(0); // servo membuka
|
|
} else if (perintah == false) {
|
|
myServo.write(180); // servo menutup
|
|
} else {
|
|
Serial.println("❗ Perintah tidak dikenal");
|
|
}
|
|
} else {
|
|
Serial.print("❌ Gagal membaca data servo: ");
|
|
Serial.println(firebaseData.errorReason());
|
|
}
|
|
}
|
|
|
|
// ================= Upload ke Cloudinary & Simpan Firebase =====================
|
|
void ambilUploadDanSimpan() {
|
|
camera_fb_t *fb = esp_camera_fb_get();
|
|
if (!fb) {
|
|
Serial.println("❌ Gagal mengambil gambar");
|
|
return;
|
|
}
|
|
|
|
WiFiClientSecure client;
|
|
client.setInsecure(); // Abaikan sertifikat SSL
|
|
|
|
if (!client.connect("api.cloudinary.com", 443)) {
|
|
Serial.println("❌ Gagal terhubung ke Cloudinary");
|
|
return;
|
|
}
|
|
|
|
String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
|
|
String startRequest = "--" + boundary + "\r\n" +
|
|
"Content-Disposition: form-data; name=\"file\"; filename=\"esp32.jpg\"\r\n" +
|
|
"Content-Type: image/jpeg\r\n\r\n";
|
|
|
|
String endRequest = "\r\n--" + boundary + "\r\n" +
|
|
"Content-Disposition: form-data; name=\"upload_preset\"\r\n\r\n" +
|
|
String(upload_preset) + "\r\n--" + boundary + "--\r\n";
|
|
|
|
int contentLength = startRequest.length() + fb->len + endRequest.length();
|
|
|
|
client.println("POST /v1_1/" + String(cloud_name) + "/image/upload HTTP/1.1");
|
|
client.println("Host: api.cloudinary.com");
|
|
client.println("Content-Type: multipart/form-data; boundary=" + boundary);
|
|
client.println("Content-Length: " + String(contentLength));
|
|
client.println("Connection: close");
|
|
client.println();
|
|
|
|
client.print(startRequest);
|
|
client.write(fb->buf, fb->len);
|
|
client.print(endRequest);
|
|
|
|
|
|
// Lewati header dulu
|
|
while (client.connected()) {
|
|
String line = client.readStringUntil('\n');
|
|
if (line.length() <= 1) break; // "\r" atau ""
|
|
}
|
|
|
|
// Sekarang baca JSON body dengan cara aman
|
|
String response;
|
|
while (client.available()) {
|
|
char c = client.read();
|
|
response += c;
|
|
}
|
|
|
|
// Potong hanya bagian JSON
|
|
int jsonStart = response.indexOf('{');
|
|
if (jsonStart == -1) {
|
|
Serial.println("❌ Tidak ditemukan awalan JSON");
|
|
Serial.println(response); // Untuk debug
|
|
return;
|
|
}
|
|
|
|
response = response.substring(jsonStart); // Buang header, ambil JSON murni
|
|
|
|
Serial.println("📦 JSON Akhir:");
|
|
Serial.println(response);
|
|
|
|
// Parse JSON
|
|
DynamicJsonDocument doc(4096);
|
|
DeserializationError error = deserializeJson(doc, response);
|
|
if (error) {
|
|
Serial.print("❌ Gagal parsing JSON: ");
|
|
Serial.println(error.c_str());
|
|
esp_camera_fb_return(fb);
|
|
return;
|
|
}
|
|
|
|
String secure_url = doc["secure_url"];
|
|
Serial.print("✅ URL Gambar: ");
|
|
Serial.println(secure_url);
|
|
esp_camera_fb_return(fb);
|
|
|
|
FirebaseJson json;
|
|
json.set("url", secure_url);
|
|
json.set("timestamp", millis());
|
|
json.set("status", true);
|
|
|
|
if (Firebase.setJSON(firebaseData, "/Gambar", json)) {
|
|
Serial.println("✅ Data berhasil dikirim ke Firebase");
|
|
} else {
|
|
Serial.print("❌ Gagal kirim ke Firebase: ");
|
|
Serial.println(firebaseData.errorReason());
|
|
}
|
|
|
|
} |