221 lines
7.7 KiB
C++
221 lines
7.7 KiB
C++
#include "esp_camera.h"
|
|
#include <WiFi.h>
|
|
#include <HTTPClient.h>
|
|
#include "esp_http_server.h"
|
|
|
|
// --- KONFIGURASI ---
|
|
const char* ssid = "3";
|
|
const char* password = "12345678";
|
|
const char* server_backend = "http://iotcam.appku.asia/api/register"; // Ganti IP Node.js kamu
|
|
|
|
#define PART_BOUNDARY "123456789000000000000987654321"
|
|
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 api_httpd = NULL;
|
|
httpd_handle_t stream_httpd = NULL;
|
|
|
|
// Definisi Pin untuk Board 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
|
|
|
|
unsigned long lastRegistration = 0;
|
|
const unsigned long registrationInterval = 30000;
|
|
|
|
// --- HANDLER: Video Stream (Port 81) ---
|
|
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) { res = ESP_FAIL; break; }
|
|
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; }
|
|
if(res != ESP_OK) break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// --- HANDLER: Snapshot (Port 80) ---
|
|
static esp_err_t snapshot_handler(httpd_req_t *req) {
|
|
camera_fb_t * fb = esp_camera_fb_get();
|
|
if (!fb) { httpd_resp_send_500(req); return ESP_FAIL; }
|
|
|
|
httpd_resp_set_hdr(req, "Connection", "close"); // Mencegah koneksi zombie
|
|
httpd_resp_set_type(req, "image/jpeg");
|
|
esp_err_t res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
|
|
esp_camera_fb_return(fb);
|
|
return res;
|
|
}
|
|
|
|
// --- HANDLER: Info Perangkat (Port 80) ---
|
|
static esp_err_t info_handler(httpd_req_t *req) {
|
|
char json_response[300];
|
|
snprintf(json_response, sizeof(json_response),
|
|
"{\"ssid\":\"%s\",\"rssi\":%d,\"ip\":\"%s\",\"mac\":\"%s\",\"free_heap\":%u,\"cpu_freq\":%u}",
|
|
WiFi.SSID().c_str(), WiFi.RSSI(), WiFi.localIP().toString().c_str(), WiFi.macAddress().c_str(), ESP.getFreeHeap(), ESP.getCpuFreqMHz()
|
|
);
|
|
httpd_resp_set_hdr(req, "Connection", "close"); // Mencegah koneksi zombie
|
|
httpd_resp_set_type(req, "application/json");
|
|
return httpd_resp_send(req, json_response, strlen(json_response));
|
|
}
|
|
|
|
// --- HANDLER: Pengaturan Kamera (Port 80) ---
|
|
static esp_err_t cmd_handler(httpd_req_t *req) {
|
|
char* buf;
|
|
size_t buf_len;
|
|
char variable[32] = {0,};
|
|
char value[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, "var", variable, sizeof(variable)) != ESP_OK ||
|
|
httpd_query_key_value(buf, "val", value, sizeof(value)) != ESP_OK) {
|
|
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;
|
|
}
|
|
|
|
int val = atoi(value);
|
|
sensor_t * s = esp_camera_sensor_get();
|
|
int res = 0;
|
|
|
|
if(!strcmp(variable, "effect")) res = s->set_special_effect(s, val);
|
|
else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
|
|
else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
|
|
else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val);
|
|
else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val);
|
|
|
|
if(res) { return httpd_resp_send_500(req); }
|
|
|
|
httpd_resp_set_hdr(req, "Connection", "close"); // Mencegah koneksi zombie
|
|
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
|
return httpd_resp_send(req, NULL, 0);
|
|
}
|
|
|
|
void registerToBackend() {
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
HTTPClient http;
|
|
http.begin(server_backend);
|
|
http.addHeader("Content-Type", "application/json");
|
|
String payload = "{\"ip\":\"" + WiFi.localIP().toString() + "\",\"mac\":\"" + WiFi.macAddress() + "\",\"status\":\"online\"}";
|
|
int httpResponseCode = http.POST(payload);
|
|
http.end();
|
|
}
|
|
}
|
|
|
|
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 = 10000000;
|
|
config.pixel_format = PIXFORMAT_JPEG;
|
|
|
|
if(psramFound()){
|
|
config.frame_size = FRAMESIZE_VGA;
|
|
config.jpeg_quality = 12;
|
|
config.fb_count = 2;
|
|
} else {
|
|
config.frame_size = FRAMESIZE_SVGA;
|
|
config.jpeg_quality = 12;
|
|
config.fb_count = 1;
|
|
}
|
|
|
|
if (esp_camera_init(&config) != ESP_OK) return;
|
|
|
|
sensor_t * s = esp_camera_sensor_get();
|
|
if (s != NULL) s->set_hmirror(s, 1);
|
|
|
|
WiFi.begin(ssid, password);
|
|
while (WiFi.status() != WL_CONNECTED) { delay(500); }
|
|
|
|
registerToBackend();
|
|
lastRegistration = millis();
|
|
|
|
// Server API (Port 80)
|
|
httpd_config_t config_api = HTTPD_DEFAULT_CONFIG();
|
|
config_api.server_port = 80;
|
|
config_api.ctrl_port = 32768;
|
|
config_api.max_open_sockets = 7; // Ekstra soket
|
|
if (httpd_start(&api_httpd, &config_api) == ESP_OK) {
|
|
httpd_uri_t uri_snap = { .uri = "/snapshot", .method = HTTP_GET, .handler = snapshot_handler, .user_ctx = NULL };
|
|
httpd_uri_t uri_info = { .uri = "/info", .method = HTTP_GET, .handler = info_handler, .user_ctx = NULL };
|
|
httpd_uri_t uri_cmd = { .uri = "/control", .method = HTTP_GET, .handler = cmd_handler, .user_ctx = NULL };
|
|
httpd_register_uri_handler(api_httpd, &uri_snap);
|
|
httpd_register_uri_handler(api_httpd, &uri_info);
|
|
httpd_register_uri_handler(api_httpd, &uri_cmd);
|
|
}
|
|
|
|
// Server Video (Port 81)
|
|
httpd_config_t config_stream = HTTPD_DEFAULT_CONFIG();
|
|
config_stream.server_port = 81;
|
|
config_stream.ctrl_port = 32769;
|
|
if (httpd_start(&stream_httpd, &config_stream) == ESP_OK) {
|
|
httpd_uri_t uri_stream = { .uri = "/stream", .method = HTTP_GET, .handler = stream_handler, .user_ctx = NULL };
|
|
httpd_register_uri_handler(stream_httpd, &uri_stream);
|
|
}
|
|
}
|
|
|
|
void loop() {
|
|
if(WiFi.status() != WL_CONNECTED) {
|
|
WiFi.disconnect();
|
|
WiFi.reconnect();
|
|
delay(5000);
|
|
registerToBackend();
|
|
lastRegistration = millis();
|
|
} else {
|
|
if (millis() - lastRegistration >= registrationInterval) {
|
|
registerToBackend();
|
|
lastRegistration = millis();
|
|
}
|
|
}
|
|
delay(1000);
|
|
} |