Files
timelapse_esp32/main/main.c

264 lines
7.9 KiB
C

#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include <esp_system.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include <string.h>
#include <stdlib.h>
#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;
}