1.Cài đặt phần mềm, kết nối phần cứng để lập trình ESP32-CAM
2.Giới thiệu nhanh về thư viện esp_http_client.h
Đây là thư viện được tích hợp sẵn khi các bạn cài đặt board ESP32 trong Arduino IDE, nên không cần phải tải thêm thư viện, cứ thế build thôi.
Esp_http_client cung cấp cho các bạn các API để làm cho ESP32 có thể đóng vài trò là một Cient trong giao thức HTTP với các chức năng cụ thể như: gửi bản tin GET, POST REQUEST đến server, nhận xử lý các RESPONSE trả về từ server. Các bước cơ bản để sử dụng các API này cho HTTP request như sau:
+ esp_http_client_init(): Để sử dụng HTTP client, việc đầu tiên chúng ta phải làm là tạo một http_client bằng việc truyền vào hàm này cấu hình của esp_http_client_config_t. Nếu các tham số này chúng ta không thay đổi thì thư viện sẽ sử dụng cấu hình mặc định.
VD cách dùng:
1 2 3 4 5 6 |
const char *post_url = "http://xxx/yyy.php"; esp_http_client_handle_t http_client; // khởi tạo con trỏ cấu trúc http_client esp_http_client_config_t config_client = {0}; // khai báo biến cấu trúc của các cấu hình liên quan đến: url, request method, host, port, vv…. http_client = esp_http_client_init(&config_client); // sử dụng hàm này để khởi tạo các cài đặt làm việc cho client config_client.url = post_url; // gán địa chỉ URL cho POST Request, đây là việc rất quan trọng trong tất cả các ứng dụng liên quan đến HTTP của ESP32. config_client.method = HTTP_METHOD_POST; // tương tự với việc thiết lập phương thức GET hay POST. |
esp_http_client_perform(): Hàm này thực hiện tất cả các chức năng với cài đặt đã được tạo ra trong hàm init ở trên, bao gồm: mở kết nối, gửi dữ liệu, tải dữ liệu và đóng kết nối nếu cần thiết. Tất cả các sự kiện liên quan đến các công việc trên đều được gom vào event_handle ( định nghĩa bởi esp_http_client_config_t). Chức năng này thực hiện công việc của nó và sẽ block nhiệm vụ hiện tại đến khi nào chức năng này hoàn thành công việc.
esp_http_client_cleanup(): Sau khi hoàn thành nhiệm vụ của esp_http_client, đây là hàm cuối cùng được gọi. Nó sẽ đóng kết nối và giải phóng tất cả bộ nhớ được cấp cho HTTP client.
Trong bài hướng dẫn này chúng ta sẽ dùng chủ yếu các hàm vừa được nêu trên, để rõ ràng hơn các bạn có thể vào địa chỉ sau tham khảo.
3.Lập trình ESP32 giao tiếp với Server, nhận lệnh điều khiển chụp hình
– Thiết lập các biến và chân cho Wifi và CAMERA module.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
bool init_wifi() { int connAttempts = 0; Serial.println("\r\nConnecting to: " + String(ssid)); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED ) { delay(500); Serial.print("."); if (connAttempts > 10) return false; connAttempts++; } return true; } |
Cho CAMERA:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
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_UXGA; config.jpeg_quality = 10; config.fb_count = 2; } else { config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; 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); return; } } |
– Lập trình ESP32 gửi POST REQUEST lên Server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
static esp_err_t send_post_request() { char *rev_data =(char*) malloc(20); // bộ nhớ nhận dữ liệu respone if (rev_data == NULL) { ESP_LOGE(TAG, "Cannot malloc http receive buffer"); } //esp_err_t res = ESP_OK; // giá trị trả về nếu thực hiện hàm thành công esp_http_client_handle_t http_client; esp_http_client_config_t config_client = {0}; config_client.url = post_url_json; config_client.event_handler = _http_event_handler; config_client.method = HTTP_METHOD_POST; http_client = esp_http_client_init(&config_client); esp_err_t err = esp_http_client_perform(http_client); if (err == ESP_OK) { Serial.print("esp_http_client_get_status_code: "); Serial.println(esp_http_client_get_status_code(http_client)); } int read_len = esp_http_client_read(http_client,rev_data,content_length); data_esp32=String(rev_data); // luu toàn bộ data từ response cho xử lý command esp_http_client_cleanup(http_client); } |
– Lập trình ESP32 xử lý data nhận được, xử lý lệnh chụp hình
Để thực hiện được hàm này cần phải có thư viện arduino json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
void process_command() { String result = data_esp32; // ===============Parse JSON=============== int size = result.length()+1; char json[size]; result.toCharArray(json, size); DynamicJsonBuffer jsonBuffer(size); JsonObject& json_parsed = jsonBuffer.parseObject(json); if (!json_parsed.success()) { Serial.println("parseObject() failed"); } else Serial.println("Parse OK"); //======================================================== if (strcmp(json_parsed["led1"], "on") == 0) { Serial.println("TAKE A PHOTO"); take_send_photo(); } if (strcmp(json_parsed["led1"], "off") == 0) { Serial.println("NO TAKING PHOTO"); } } // end function |
4.Lập trình ESP32 chụp hình, gửi hình ảnh lên Internet.
Trong phần này chúng ta có 2 việc chính
– Lập trình cho ESP32 chụp hình với module CAM
1 2 3 4 5 6 7 8 9 10 |
static esp_err_t take_send_photo() { Serial.println("Taking picture..."); camera_fb_t * fb = NULL; esp_err_t res = ESP_OK; fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed"); return ESP_FAIL; } |
– Lập trình ESP32 gửi file ảnh lên Cloud
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
esp_http_client_handle_t http_client; esp_http_client_config_t config_client = {0}; config_client.url = post_url; config_client.event_handler = _http_event_handler; config_client.method = HTTP_METHOD_POST; http_client = esp_http_client_init(&config_client); esp_http_client_set_post_field(http_client, (const char *)fb->buf, fb->len); esp_http_client_set_header(http_client, "Content-Type", "image/jpg"); esp_err_t err = esp_http_client_perform(http_client); if (err == ESP_OK) { Serial.print("esp_http_client_get_status_code: "); Serial.println(esp_http_client_get_status_code(http_client)); } int content_length = esp_http_client_fetch_headers(http_client); Serial.print("length of data receive:"); Serial.print(content_length); esp_http_client_cleanup(http_client); esp_camera_fb_return(fb); } |
5.Mã nguồn cho ESP32 đầy đủ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
#include "esp_http_client.h" #include "esp_camera.h" #include <WiFi.h> #include "Arduino.h" #include <ArduinoJson.h> const char* ssid = "xxx"; const char* password = "xxx"; const char *post_url = "http://luuvachiase.000webhostapp.com/camera_upload.php"; // các bạn có thể dùng thử hoặc thay đổi theo server của mình const char *post_url_json = "http://luuvachiase.000webhostapp.com/test/test.json"; // các bạn có thê dùng thử hoặc thay đổi theo server của mình bool internet_connected = false; int content_length; bool json_ok =0,en=0; String line,data_esp32; // 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 void setup() { Serial.begin(115200); if (init_wifi()) { // Connected to WiFi internet_connected = true; Serial.println("Internet connected"); } 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_UXGA; config.jpeg_quality = 10; config.fb_count = 2; } else { config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; 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); return; } } bool init_wifi() { int connAttempts = 0; Serial.println("\r\nConnecting to: " + String(ssid)); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED ) { delay(500); Serial.print("."); if (connAttempts > 10) return false; connAttempts++; } return true; } esp_err_t _http_event_handler(esp_http_client_event_t *evt) { switch (evt->event_id) { case HTTP_EVENT_ERROR: Serial.println("HTTP_EVENT_ERROR"); break; case HTTP_EVENT_ON_CONNECTED: Serial.println("HTTP_EVENT_ON_CONNECTED"); break; case HTTP_EVENT_HEADER_SENT: Serial.println("HTTP_EVENT_HEADER_SENT"); break; case HTTP_EVENT_ON_HEADER: Serial.println(); Serial.printf("HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value); break; case HTTP_EVENT_ON_DATA: Serial.println(); Serial.printf("HTTP_EVENT_ON_DATA, len=%d", evt->data_len); if (!esp_http_client_is_chunked_response(evt->client)) { // Write out data // printf("%.*s", evt->data_len, (char*)evt->data); } content_length= evt->data_len; break; case HTTP_EVENT_ON_FINISH: Serial.println(""); Serial.println("HTTP_EVENT_ON_FINISH"); break; case HTTP_EVENT_DISCONNECTED: Serial.println("HTTP_EVENT_DISCONNECTED"); break; } return ESP_OK; } static esp_err_t take_send_photo() { Serial.println("Taking picture..."); camera_fb_t * fb = NULL; esp_err_t res = ESP_OK; fb = esp_camera_fb_get(); if (!fb) { Serial.println("Camera capture failed"); return ESP_FAIL; } esp_http_client_handle_t http_client; esp_http_client_config_t config_client = {0}; config_client.url = post_url; config_client.event_handler = _http_event_handler; config_client.method = HTTP_METHOD_POST; http_client = esp_http_client_init(&config_client); esp_http_client_set_post_field(http_client, (const char *)fb->buf, fb->len); esp_http_client_set_header(http_client, "Content-Type", "image/jpg"); esp_err_t err = esp_http_client_perform(http_client); if (err == ESP_OK) { Serial.print("esp_http_client_get_status_code: "); Serial.println(esp_http_client_get_status_code(http_client)); } int content_length = esp_http_client_fetch_headers(http_client); Serial.print("length of data receive:"); Serial.print(content_length); esp_http_client_cleanup(http_client); esp_camera_fb_return(fb); } //=======================================RECEIVE COMMAND FROM WEBSERVER TO TAKE A PICTURE======== static esp_err_t send_post_request() { char *rev_data =(char*) malloc(20); if (rev_data == NULL) { ESP_LOGE(TAG, "Cannot malloc http receive buffer"); } // esp_err_t res = ESP_OK; esp_http_client_handle_t http_client; esp_http_client_config_t config_client = {0}; config_client.url = post_url_json; config_client.event_handler = _http_event_handler; config_client.method = HTTP_METHOD_POST; http_client = esp_http_client_init(&config_client); // esp_http_client_set_post_field(client, post_data, strlen(post_data)); // chi get tu json file esp_err_t err = esp_http_client_perform(http_client); if (err == ESP_OK) { Serial.print("esp_http_client_get_status_code: "); Serial.println(esp_http_client_get_status_code(http_client)); } int read_len = esp_http_client_read(http_client,rev_data,content_length); data_esp32=String(rev_data); esp_http_client_cleanup(http_client); } void process_command() { String result = data_esp32; // ===============Parse JSON=============== int size = result.length()+1; char json[size]; result.toCharArray(json, size); DynamicJsonBuffer jsonBuffer(size); JsonObject& json_parsed = jsonBuffer.parseObject(json); if (!json_parsed.success()) { Serial.println("parseObject() failed"); } else Serial.println("Parse OK"); //=========================================================== if (strcmp(json_parsed["led1"], "on") == 0) { Serial.println("TAKE A PHOTO"); take_send_photo(); } if (strcmp(json_parsed["led1"], "off") == 0) { Serial.println("NO TAKING PHPTO"); } } // end function //================================================================================================ void loop() { send_post_request(); process_command(); delay(1000); } |
Phần tiếp theo chúng ta sẽ đi thiết kế giao diện cũng như viết PHP Script cho việc giao tiếp điều khiển ESP32 cũng như hiển thị hình ảnh chụp được lên màn hình.
Viết PHP server điều khiển ESP32-CAM chụp hình, hiển thị ảnh lên trình duyệt qua internet
****Mã nguồn PHP cho server:
Nguồn tham khảo:
-https://robotzero.one/posting-data-server-esp32/
-https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/protocols/esp_http_client.html#_CPPv420http_event_handle_cb
Good practice. I want to try it. But in the “test.json” file, I guess you didn’t show how to make the database connection.
Can you share a sample file with me?
Thanks.
Have a nice day.
Hi Alper
test.json is very simple. It is {“led1″:”off”} , the second parameter will be changed to “on” when you click “take photo” on web interface .Next, server receive new image from ESP32, it will automatically turn “off”.
The link for testing is death. I have already moved to new link:
http://iot.luuvachiase.net/esp32_cam_upload_control.php
Have fun!
Hello sir
i would to asking, how to set the image as “camera_upload/upload_esp32.jpg” in your src HTML file?
because, when we send photo with post we have sure to define that file. thanks
Sorry if I don’t understand your question very well. You can find in camera_upload.php some lines as below:
$received = file_get_contents(‘php://input’);
$fileToWrite = “camera_upload/upload_esp32.jpg”;
file_put_contents($fileToWrite, $received);
This is the simple way to set the image as ““camera_upload/upload_esp32.jpg”.
Hope this help!
em đã làm theo hướng dẫn nhưng có vẻ như mọi thứ không hoạt động đúng như ý. Lúc nhấn take photo vẫn không có ảnh hiện ra.