#include #include #include #include #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_http_server.h" #include "esp_netif.h" #include "esp_eth.h" #include "esp_http_client.h" #include "esp_tls.h" #include "esp_camera.h" #include "img_converters.h" #include "esp_timer.h" #include "protocol_examples_common.h" #define MAX_HTTP_RECV_BUFFER 512 #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 #define INTERVAL 5 * 1000 // millisecond #define PART_BOUNDARY "123456789000000000000987654321" static const char *TAG = "timelapse"; esp_err_t _http_event_handler(esp_http_client_event_t *evt); static void post_data(camera_fb_t * photo); static camera_config_t cam_config = { .ledc_channel = LEDC_CHANNEL_0, .ledc_timer = LEDC_TIMER_0, .pin_d0 = Y2_GPIO_NUM, .pin_d1 = Y3_GPIO_NUM, .pin_d2 = Y4_GPIO_NUM, .pin_d3 = Y5_GPIO_NUM, .pin_d4 = Y6_GPIO_NUM, .pin_d5 = Y7_GPIO_NUM, .pin_d6 = Y8_GPIO_NUM, .pin_d7 = Y9_GPIO_NUM, .pin_xclk = XCLK_GPIO_NUM, .pin_pclk = PCLK_GPIO_NUM, .pin_vsync = VSYNC_GPIO_NUM, .pin_href = HREF_GPIO_NUM, .pin_sscb_sda = SIOD_GPIO_NUM, .pin_sscb_scl = SIOC_GPIO_NUM, .pin_pwdn = PWDN_GPIO_NUM, .pin_reset = RESET_GPIO_NUM, .xclk_freq_hz = 20000000, .pixel_format = PIXFORMAT_JPEG, // need to enable psram in menuconfig .frame_size = FRAMESIZE_UXGA, .jpeg_quality = 10, .fb_count = 2 }; static esp_err_t init_camera() { esp_err_t err = esp_camera_init(&cam_config); if (err != ESP_OK) { ESP_LOGE(TAG, "Camera Init Failed, error 0x%x", err); return err; } return ESP_OK; } void app_main(void) { // initialization ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); // connect to wifi ESP_ERROR_CHECK(example_connect()); // OV2640 camera module if (init_camera() != ESP_OK) { esp_restart(); } vTaskDelay( 1000 / portTICK_RATE_MS ); camera_fb_t * pic = NULL; // pointer while (1) { ESP_LOGI(TAG, "taking a photo..."); pic = esp_camera_fb_get(); // use pic->buf to access the image ESP_LOGI(TAG, "Photo taken, size is %zu bytes", pic->len); ESP_LOGI(TAG, "sending the photo ..."); post_data(pic); esp_camera_fb_return(pic); vTaskDelay(INTERVAL / portTICK_RATE_MS); } } static const uint32_t CHUNK_SIZE = 1024; static const uint32_t MSG_SIZE = 256; static const char * uploadPath = "/upload"; static const char * server = "192.168.1.107:3001"; static const char * boundary = "boundary"; static void post_data(camera_fb_t * pic){ char url[128]; sprintf(url, "http://%s%s", server, uploadPath); esp_http_client_config_t config = { .url = url, .event_handler = _http_event_handler}; esp_http_client_handle_t client = esp_http_client_init(&config); esp_http_client_set_method(client, HTTP_METHOD_POST); char head[MSG_SIZE]; char tail[MSG_SIZE]; sprintf(head, "--%s\r\n" "Content-Disposition: form-data; name=\"photo\"; filename=\"esp32.jpg\"\r\n" "Content-Type: image/jpeg\r\n\r\n", boundary); sprintf(tail, "\r\n--%s--\r\n", boundary); size_t totalLen = pic->len + strlen(head) + strlen(tail); char tmpStr[MSG_SIZE]; // send header so that the server knows what to do with chunks after this sprintf(tmpStr, "%u", totalLen); esp_http_client_set_header(client, "Content-Length", tmpStr); ESP_LOGI(TAG, "Content-Length: %s", tmpStr); sprintf(tmpStr, "multipart/form-data; boundary=%s", boundary); esp_http_client_set_header(client, "Content-Type", tmpStr); ESP_LOGI(TAG, "Content-Type: %s", tmpStr); esp_http_client_set_header(client, "Accept", "*/*"); esp_http_client_set_header(client, "Expect", "100-continue"); err_t err; err = esp_http_client_open(client, totalLen); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_http_client_open failed"); esp_http_client_cleanup(client); return; } // the empty body of the header, must be sent to complete the message sprintf(tmpStr, "\r\n"); esp_http_client_write(client, tmpStr, strlen(tmpStr)); // wait a bit for response, expecting code 100 vTaskDelay(10 / portTICK_RATE_MS); esp_http_client_fetch_headers(client); int http_status_code = esp_http_client_get_status_code(client); if (http_status_code != 100) { ESP_LOGE(TAG, "Expecting HTTP status code 100, received: %u", http_status_code); return; } else { ESP_LOGI(TAG, "HTTP status code %d, continue sending data ...", http_status_code); } // got 100, proceed to send head esp_http_client_write(client, head, strlen(head)); ESP_LOGI(TAG, "head: %s", (char *)head); // data in chunks size_t nChunks = pic->len / CHUNK_SIZE + 1; uint8_t *ptr = pic->buf; size_t fbLen = pic->len; ESP_LOGI(TAG, "photo size %u bytes, will be posted in %u chunks", fbLen, nChunks); for (size_t n = 0; n < fbLen; n += CHUNK_SIZE) { if (n + CHUNK_SIZE < fbLen) { esp_http_client_write(client, (char *)ptr, CHUNK_SIZE); //if (n<2) { // sprintf(tmpStr, "%u:", n); // for (int i = 0; i < 15; i++) { // char nextChar[4]; // sprintf(nextChar, "%x", ptr[i]); // strcat(tmpStr, nextChar); // } // ESP_LOGI(TAG, "%.16s", tmpStr); //} ptr += CHUNK_SIZE; } else if (fbLen % CHUNK_SIZE > 0){ esp_http_client_write(client, (char *)ptr, fbLen % CHUNK_SIZE); } } // and the tail ESP_LOGI(TAG, "tail: %s", (char *)tail); esp_http_client_write(client, tail, strlen(tail)); // check HTTP response int response_length = esp_http_client_fetch_headers(client); http_status_code = esp_http_client_get_status_code(client); ESP_LOGI(TAG, "HTTP status: %d, content-length: %d", http_status_code, response_length); if (response_length > 0 && response_length < CHUNK_SIZE) { esp_http_client_read(client, tmpStr, response_length); ESP_LOGI(TAG, "HTTP response: %s", tmpStr); } esp_http_client_cleanup(client); } esp_err_t _http_event_handler(esp_http_client_event_t *evt) { switch(evt->event_id) { case HTTP_EVENT_ERROR: ESP_LOGD(TAG, "HTTP_EVENT_ERROR"); break; case HTTP_EVENT_ON_CONNECTED: ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED"); break; case HTTP_EVENT_HEADER_SENT: ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT"); break; case HTTP_EVENT_ON_HEADER: ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value); break; case HTTP_EVENT_ON_DATA: ESP_LOGD(TAG, "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); } break; case HTTP_EVENT_ON_FINISH: ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH"); break; case HTTP_EVENT_DISCONNECTED: ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED"); int mbedtls_err = 0; esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL); if (err != 0) { ESP_LOGI(TAG, "Last esp error code: 0x%x", err); ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err); } break; } return ESP_OK; }