├── CMakeLists.txt ├── LICENSE ├── README.md ├── images ├── esp32.jpeg └── esp_logo.png ├── main ├── CMakeLists.txt ├── Kconfig.projbuild ├── cmd.h ├── component.mk ├── main.c └── multipart-upload.c ├── partitions.csv └── sdkconfig.defaults /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | # (Not part of the boilerplate) 6 | # This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. 7 | #set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) 8 | 9 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 10 | project(multipart-upload) 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp-idf-multipart-upload 2 | A multipart file upload example for esp-idf. 3 | 4 | # Server Side 5 | Download the server from [here](https://github.com/nopnop2002/multipart-upload-server). 6 | 7 | --- 8 | 9 | # ESP32 Side 10 | 11 | ## Software requirements 12 | ESP-IDF V4.4/V5.x. 13 | ESP-IDF V5.0 is required when using ESP32-C2. 14 | ESP-IDF V5.1 is required when using ESP32-C6. 15 | 16 | 17 | ## Installation 18 | ``` 19 | git clone https://github.com/nopnop2002/esp-idf-multipart-upload 20 | cd esp-idf-multipart-upload/ 21 | idf.py set-target {esp32/esp32s2/esp32s3/esp32c2/esp32c3} 22 | idf.py menuconfig 23 | idf.py flash 24 | ``` 25 | 26 | 27 | ## Configuration 28 | You have to set this config value with menuconfig. 29 | - CONFIG_ESP_WIFI_SSID 30 | SSID of your wifi. 31 | - CONFIG_ESP_WIFI_PASSWORD 32 | PASSWORD of your wifi. 33 | - CONFIG_ESP_MAXIMUM_RETRY 34 | Maximum number of retries when connecting to wifi. 35 | - CONFIG_ESP_WEB_SERVER 36 | IP address or mDNS host name of your WEB Server. 37 | - CONFIG_ESP_WEB_PORT 38 | Port number of your WEB Server. 39 | - CONFIG_ESP_WEB_PATH 40 | Path of your WEB Server. 41 | 42 | ![menuconfig-1](https://user-images.githubusercontent.com/6020549/99719529-c07e8700-2aef-11eb-8a11-e5a7aaf2cbd4.jpg) 43 | ![menuconfig-2](https://user-images.githubusercontent.com/6020549/99719539-c3797780-2aef-11eb-9cc4-4053c2640434.jpg) 44 | 45 | ## WiFi Setting 46 | ![menuconfig-3](https://user-images.githubusercontent.com/6020549/99719544-c70cfe80-2aef-11eb-8242-9ee855b5c8c2.jpg) 47 | 48 | ## HTTP Server Setting 49 | 50 | ![menuconfig-4](https://user-images.githubusercontent.com/6020549/178969271-63738e64-0a88-48be-9b24-8927b499a1bf.jpg) 51 | 52 | ## About multipart/form-data 53 | This example send this HTTP header. 54 | If the parameter expected by the server is not "upfile", you need to change it. 55 | ``` 56 | POST PATH HTTP/1.1 57 | HOST: HOST:PORT 58 | User-Agent: esp-idf/X.Y.Z esp32 59 | Accept: */* 60 | Content-Type: multipart/form-data; boundary=X-ESPIDF_MULTIPART 61 | Content-Length: xxx 62 | 63 | --X-ESPIDF_MULTIPART 64 | Content-Disposition: form-data; name="upfile"; filename="hoge.jpg" 65 | Content-Type: Content-Type: application/octet-stream 66 | 67 | binary-data 68 | 69 | --X-ESPIDF_MULTIPART-- 70 | ``` 71 | 72 | 73 | -------------------------------------------------------------------------------- /images/esp32.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-multipart-upload/4762b7dafe3487c6c99c0910fbc68ed8c4c5804f/images/esp32.jpeg -------------------------------------------------------------------------------- /images/esp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-multipart-upload/4762b7dafe3487c6c99c0910fbc68ed8c4c5804f/images/esp_logo.png -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_SRCS main.c multipart-upload.c) 2 | set(COMPONENT_ADD_INCLUDEDIRS ".") 3 | 4 | register_component() 5 | spiffs_create_partition_image(storage ../images FLASH_IN_PROJECT) 6 | -------------------------------------------------------------------------------- /main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application Configuration" 2 | 3 | menu "WiFi Setting" 4 | config ESP_WIFI_SSID 5 | string "WiFi SSID" 6 | default "myssid" 7 | help 8 | SSID (network name) for the example to connect to. 9 | 10 | config ESP_WIFI_PASSWORD 11 | string "WiFi Password" 12 | default "mypassword" 13 | help 14 | WiFi password (WPA or WPA2) for the example to use. 15 | 16 | config ESP_MAXIMUM_RETRY 17 | int "Maximum retry" 18 | default 5 19 | help 20 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 21 | endmenu 22 | 23 | menu "HTTP Server Setting" 24 | 25 | config WEB_SERVER 26 | string "HTTP Server" 27 | default "HttpSserver.local" 28 | help 29 | HTTP Server host name or ip address for the example to use. 30 | 31 | config WEB_PORT 32 | string "HTTP Port" 33 | default "8080" 34 | help 35 | HTTP Server port for the example to use. 36 | 37 | config WEB_PATH 38 | string "HTTP Url" 39 | default "/upload_multipart" 40 | help 41 | HTTP Server url for the example to use. 42 | 43 | endmenu 44 | 45 | endmenu 46 | -------------------------------------------------------------------------------- /main/cmd.h: -------------------------------------------------------------------------------- 1 | #define CMD_SEND 100 2 | #define CMD_HALT 400 3 | 4 | typedef struct { 5 | uint16_t command; 6 | char localFileName[64]; 7 | char remoteFileName[64]; 8 | TaskHandle_t taskHandle; 9 | } REQUEST_t; 10 | 11 | typedef struct { 12 | uint16_t command; 13 | char response[256]; 14 | TaskHandle_t taskHandle; 15 | } RESPONSE_t; 16 | 17 | -------------------------------------------------------------------------------- /main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) 5 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | /* Mulitipart Upload Example 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | #include 10 | #include "freertos/FreeRTOS.h" 11 | #include "freertos/task.h" 12 | #include "freertos/queue.h" 13 | #include "freertos/event_groups.h" 14 | #include "esp_system.h" 15 | #include "esp_wifi.h" 16 | #include "esp_event.h" 17 | #include "esp_log.h" 18 | #include "nvs_flash.h" 19 | #include "esp_vfs.h" 20 | #include "esp_spiffs.h" 21 | 22 | #include "lwip/err.h" 23 | #include "lwip/sys.h" 24 | 25 | #include "cmd.h" 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "MAIN"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | QueueHandle_t xQueueRequest; 41 | QueueHandle_t xQueueResponse; 42 | 43 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 44 | { 45 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 46 | esp_wifi_connect(); 47 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 48 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 49 | esp_wifi_connect(); 50 | s_retry_num++; 51 | ESP_LOGI(TAG, "retry to connect to the AP"); 52 | } else { 53 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 54 | } 55 | ESP_LOGI(TAG,"connect to the AP fail"); 56 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 57 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 58 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 59 | s_retry_num = 0; 60 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 61 | } 62 | } 63 | 64 | void wifi_init_sta(void) 65 | { 66 | s_wifi_event_group = xEventGroupCreate(); 67 | 68 | ESP_ERROR_CHECK(esp_netif_init()); 69 | 70 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 71 | esp_netif_create_default_wifi_sta(); 72 | 73 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 74 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 75 | 76 | esp_event_handler_instance_t instance_any_id; 77 | esp_event_handler_instance_t instance_got_ip; 78 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 79 | ESP_EVENT_ANY_ID, 80 | &event_handler, 81 | NULL, 82 | &instance_any_id)); 83 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 84 | IP_EVENT_STA_GOT_IP, 85 | &event_handler, 86 | NULL, 87 | &instance_got_ip)); 88 | 89 | wifi_config_t wifi_config = { 90 | .sta = { 91 | .ssid = CONFIG_ESP_WIFI_SSID, 92 | .password = CONFIG_ESP_WIFI_PASSWORD, 93 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 94 | * However these modes are deprecated and not advisable to be used. Incase your Access point 95 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 96 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 97 | 98 | .pmf_cfg = { 99 | .capable = true, 100 | .required = false 101 | }, 102 | }, 103 | }; 104 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 105 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); 106 | ESP_ERROR_CHECK(esp_wifi_start() ); 107 | 108 | ESP_LOGI(TAG, "wifi_init_sta finished."); 109 | 110 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 111 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 112 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 113 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 114 | pdFALSE, 115 | pdFALSE, 116 | portMAX_DELAY); 117 | 118 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 119 | * happened. */ 120 | if (bits & WIFI_CONNECTED_BIT) { 121 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 122 | } else if (bits & WIFI_FAIL_BIT) { 123 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 124 | } else { 125 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 126 | } 127 | 128 | /* The event will not be processed after unregister */ 129 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 130 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 131 | vEventGroupDelete(s_wifi_event_group); 132 | } 133 | 134 | void http_post_task(void *pvParameters); 135 | 136 | void app_main(void) 137 | { 138 | // Initialize NVS 139 | esp_err_t ret = nvs_flash_init(); 140 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 141 | ESP_ERROR_CHECK(nvs_flash_erase()); 142 | ret = nvs_flash_init(); 143 | } 144 | ESP_ERROR_CHECK(ret); 145 | 146 | // Initialize WiFi 147 | wifi_init_sta(); 148 | 149 | ESP_LOGI(TAG, "Initializing SPIFFS"); 150 | esp_vfs_spiffs_conf_t conf = { 151 | .base_path = "/spiffs", 152 | .partition_label = NULL, 153 | .max_files = 8, 154 | .format_if_mount_failed = true 155 | }; 156 | 157 | // Use settings defined above toinitialize and mount SPIFFS filesystem. 158 | // Note: esp_vfs_spiffs_register is anall-in-one convenience function. 159 | ret = esp_vfs_spiffs_register(&conf); 160 | 161 | if (ret != ESP_OK) { 162 | if (ret == ESP_FAIL) { 163 | ESP_LOGE(TAG, "Failed to mount or format filesystem"); 164 | } else if (ret == ESP_ERR_NOT_FOUND) { 165 | ESP_LOGE(TAG, "Failed to find SPIFFS partition"); 166 | } else { 167 | ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)",esp_err_to_name(ret)); 168 | } 169 | return; 170 | } 171 | 172 | // Create Queue 173 | xQueueRequest = xQueueCreate( 1, sizeof(REQUEST_t) ); 174 | xQueueResponse = xQueueCreate( 1, sizeof(RESPONSE_t) ); 175 | configASSERT( xQueueRequest ); 176 | configASSERT( xQueueResponse ); 177 | 178 | // Create Task 179 | xTaskCreate(&http_post_task, "POST", 4096, NULL, 5, NULL); 180 | 181 | // Search Directory 182 | DIR* dir = opendir("/spiffs/"); 183 | assert(dir != NULL); 184 | 185 | REQUEST_t requestBuf; 186 | requestBuf.command = CMD_SEND; 187 | requestBuf.taskHandle = xTaskGetCurrentTaskHandle(); 188 | RESPONSE_t responseBuf; 189 | while (true) { 190 | struct dirent*pe = readdir(dir); 191 | if (!pe) break; 192 | ESP_LOGI(TAG, "d_name=%s d_ino=%d d_type=%x", pe->d_name,pe->d_ino, pe->d_type); 193 | strcpy(requestBuf.remoteFileName, pe->d_name); 194 | strcpy(requestBuf.localFileName, "/spiffs/"); 195 | strcat(requestBuf.localFileName, pe->d_name); 196 | if (xQueueSend(xQueueRequest, &requestBuf, 10) != pdPASS) { 197 | ESP_LOGE(TAG, "xQueueSend fail"); 198 | } else { 199 | xQueueReceive(xQueueResponse, &responseBuf, portMAX_DELAY); 200 | ESP_LOGI(TAG, "\n%s", responseBuf.response); 201 | #if 0 202 | for(int i = 0; i < strlen(responseBuf.response); i++) { 203 | putchar(responseBuf.response[i]); 204 | } 205 | printf("\n"); 206 | #endif 207 | } 208 | } // end while 209 | closedir(dir); 210 | ESP_LOGI(TAG, "All file uploded"); 211 | } 212 | -------------------------------------------------------------------------------- /main/multipart-upload.c: -------------------------------------------------------------------------------- 1 | /* HTTP POST Example using plain POSIX sockets 2 | 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include "freertos/FreeRTOS.h" 14 | #include "freertos/task.h" 15 | #include "esp_system.h" 16 | #include "esp_log.h" 17 | 18 | #include "lwip/err.h" 19 | #include "lwip/sockets.h" 20 | #include "lwip/sys.h" 21 | #include "lwip/netdb.h" 22 | #include "lwip/dns.h" 23 | 24 | #include "cmd.h" 25 | 26 | /* Constants that are configurable in menuconfig */ 27 | #if 0 28 | #define CONFIG_WEB_SERVER "192.168.10.43" 29 | #define CONFIG_WEB_PORT "8080" 30 | #define CONFIG_WEB_PATH "/upload_multipart" 31 | #endif 32 | 33 | #define BOUNDARY "X-ESPIDF_MULTIPART" 34 | 35 | extern QueueHandle_t xQueueRequest; 36 | extern QueueHandle_t xQueueResponse; 37 | 38 | static const char *TAG = "POST"; 39 | 40 | void http_post_task(void *pvParameters) 41 | { 42 | const struct addrinfo hints = { 43 | .ai_family = AF_INET, 44 | .ai_socktype = SOCK_STREAM, 45 | }; 46 | struct addrinfo *res; 47 | struct in_addr *addr; 48 | char recv_buf[64]; 49 | 50 | REQUEST_t requestBuf; 51 | RESPONSE_t responseBuf; 52 | 53 | while(1) { 54 | ESP_LOGI(TAG,"Waitting...."); 55 | xQueueReceive(xQueueRequest, &requestBuf, portMAX_DELAY); 56 | ESP_LOGI(TAG,"requestBuf.command=%d", requestBuf.command); 57 | if (requestBuf.command == CMD_HALT) break; 58 | 59 | ESP_LOGI(TAG,"requestBuf.localFileName=%s", requestBuf.localFileName); 60 | ESP_LOGI(TAG,"requestBuf.remoteFileName=%s", requestBuf.remoteFileName); 61 | struct stat statBuf; 62 | if (stat(requestBuf.localFileName, &statBuf) == 0) { 63 | ESP_LOGI(TAG, "st_size=%d", (int)statBuf.st_size); 64 | } else { 65 | strcpy(responseBuf.response,"local file not found"); 66 | xQueueSend(xQueueResponse, &responseBuf, 10); 67 | ESP_LOGE(TAG, "stat fail"); 68 | continue; 69 | } 70 | 71 | int err = getaddrinfo(CONFIG_WEB_SERVER, CONFIG_WEB_PORT, &hints, &res); 72 | 73 | if(err != 0 || res == NULL) { 74 | strcpy(responseBuf.response,"DNS lookup failed"); 75 | xQueueSend(xQueueResponse, &responseBuf, 10); 76 | ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res); 77 | vTaskDelay(1000 / portTICK_PERIOD_MS); 78 | continue; 79 | } 80 | 81 | /* Code to print the resolved IP. 82 | Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */ 83 | addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; 84 | ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr)); 85 | 86 | int s = socket(res->ai_family, res->ai_socktype, 0); 87 | if(s < 0) { 88 | strcpy(responseBuf.response,"Failed to allocate socket"); 89 | xQueueSend(xQueueResponse, &responseBuf, 10); 90 | ESP_LOGE(TAG, "... Failed to allocate socket."); 91 | freeaddrinfo(res); 92 | vTaskDelay(1000 / portTICK_PERIOD_MS); 93 | continue; 94 | } 95 | ESP_LOGI(TAG, "... allocated socket"); 96 | 97 | if(connect(s, res->ai_addr, res->ai_addrlen) != 0) { 98 | strcpy(responseBuf.response,"socket connect failed"); 99 | xQueueSend(xQueueResponse, &responseBuf, 10); 100 | ESP_LOGE(TAG, "... socket connect failed errno=%d", errno); 101 | close(s); 102 | freeaddrinfo(res); 103 | vTaskDelay(4000 / portTICK_PERIOD_MS); 104 | continue; 105 | } 106 | 107 | ESP_LOGI(TAG, "... connected"); 108 | freeaddrinfo(res); 109 | 110 | char HEADER[512]; 111 | char header[128]; 112 | 113 | sprintf(header, "POST %s HTTP/1.1\r\n", CONFIG_WEB_PATH); 114 | strcpy(HEADER, header); 115 | sprintf(header, "Host: %s:%s\r\n", CONFIG_WEB_SERVER, CONFIG_WEB_PORT); 116 | strcat(HEADER, header); 117 | sprintf(header, "User-Agent: esp-idf/%d.%d.%d esp32\r\n", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH); 118 | strcat(HEADER, header); 119 | sprintf(header, "Accept: */*\r\n"); 120 | strcat(HEADER, header); 121 | sprintf(header, "Content-Type: multipart/form-data; boundary=%s\r\n", BOUNDARY); 122 | strcat(HEADER, header); 123 | 124 | char BODY[512]; 125 | sprintf(header, "--%s\r\n", BOUNDARY); 126 | strcpy(BODY, header); 127 | sprintf(header, "Content-Disposition: form-data; name=\"upfile\"; filename=\"%s\"\r\n", requestBuf.remoteFileName); 128 | strcat(BODY, header); 129 | sprintf(header, "Content-Type: application/octet-stream\r\n\r\n"); 130 | strcat(BODY, header); 131 | 132 | char END[128]; 133 | sprintf(header, "\r\n--%s--\r\n\r\n", BOUNDARY); 134 | strcpy(END, header); 135 | 136 | int dataLength = strlen(BODY) + strlen(END) + statBuf.st_size; 137 | sprintf(header, "Content-Length: %d\r\n\r\n", dataLength); 138 | strcat(HEADER, header); 139 | 140 | ESP_LOGD(TAG, "[%s]", HEADER); 141 | if (write(s, HEADER, strlen(HEADER)) < 0) { 142 | ESP_LOGE(TAG, "... socket send failed"); 143 | close(s); 144 | vTaskDelay(4000 / portTICK_PERIOD_MS); 145 | continue; 146 | } 147 | ESP_LOGI(TAG, "HEADER socket send success"); 148 | 149 | ESP_LOGD(TAG, "[%s]", BODY); 150 | if (write(s, BODY, strlen(BODY)) < 0) { 151 | ESP_LOGE(TAG, "... socket send failed"); 152 | close(s); 153 | vTaskDelay(4000 / portTICK_PERIOD_MS); 154 | continue; 155 | } 156 | ESP_LOGI(TAG, "BODY socket send success"); 157 | 158 | FILE* f=fopen(requestBuf.localFileName, "rb"); 159 | uint8_t dataBuffer[128]; 160 | if (f == NULL) { 161 | ESP_LOGE(TAG, "Failed to open file for reading"); 162 | } 163 | while(!feof(f)) { 164 | int len = fread(dataBuffer, 1, sizeof(dataBuffer), f); 165 | if (write(s, dataBuffer, len) < 0) { 166 | ESP_LOGE(TAG, "... socket send failed"); 167 | close(s); 168 | vTaskDelay(4000 / portTICK_PERIOD_MS); 169 | continue; 170 | } 171 | } 172 | fclose(f); 173 | ESP_LOGI(TAG, "DATA socket send success"); 174 | 175 | if (write(s, END, strlen(END)) < 0) { 176 | ESP_LOGE(TAG, "... socket send failed"); 177 | close(s); 178 | vTaskDelay(4000 / portTICK_PERIOD_MS); 179 | continue; 180 | } 181 | ESP_LOGI(TAG, "END socket send success"); 182 | 183 | struct timeval receiving_timeout; 184 | receiving_timeout.tv_sec = 5; 185 | receiving_timeout.tv_usec = 0; 186 | if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout, 187 | sizeof(receiving_timeout)) < 0) { 188 | ESP_LOGE(TAG, "... failed to set socket receiving timeout"); 189 | close(s); 190 | vTaskDelay(4000 / portTICK_PERIOD_MS); 191 | continue; 192 | } 193 | ESP_LOGI(TAG, "... set socket receiving timeout success"); 194 | 195 | /* Read HTTP response */ 196 | int readed; 197 | bzero(responseBuf.response, sizeof(responseBuf.response)); 198 | do { 199 | bzero(recv_buf, sizeof(recv_buf)); 200 | ESP_LOGD(TAG, "Start read now=%"PRIu32, xTaskGetTickCount()); 201 | readed = read(s, recv_buf, sizeof(recv_buf)-1); 202 | ESP_LOGD(TAG, "End read now=%"PRIu32" readed=%d", xTaskGetTickCount(), readed); 203 | #if 0 204 | for(int i = 0; i < readed; i++) { 205 | putchar(recv_buf[i]); 206 | } 207 | #endif 208 | strcat(responseBuf.response, recv_buf); 209 | } while(readed > 0); 210 | #if 0 211 | printf("\n"); 212 | #endif 213 | 214 | /* send HTTP response */ 215 | if (xQueueSend(xQueueResponse, &responseBuf, 10) != pdPASS) { 216 | ESP_LOGE(TAG, "xQueueSend fail"); 217 | } 218 | 219 | ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d.", readed, errno); 220 | close(s); 221 | 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild 3 | nvs, data, nvs, 0x9000, 0x6000, 4 | phy_init, data, phy, 0xf000, 0x1000, 5 | factory, app, factory, 0x10000, 1M, 6 | storage, data, spiffs, , 0xF0000, 7 | -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # 2 | # Partition Table 3 | # 4 | CONFIG_PARTITION_TABLE_CUSTOM=y 5 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 6 | CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" 7 | --------------------------------------------------------------------------------