Http client OTA (#553)
This commit is contained in:
		
							parent
							
								
									33082ba2c9
								
							
						
					
					
						commit
						b77380bad1
					
				
					 9 changed files with 712 additions and 0 deletions
				
			
		
							
								
								
									
										4
									
								
								examples/http_client_ota/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								examples/http_client_ota/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | PROGRAM=http_ota | ||||||
|  | EXTRA_COMPONENTS=extras/rboot-ota extras/mbedtls extras/http_client_ota | ||||||
|  | 
 | ||||||
|  | include ../../common.mk | ||||||
							
								
								
									
										134
									
								
								examples/http_client_ota/http_get.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								examples/http_client_ota/http_get.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | #include "espressif/esp_common.h" | ||||||
|  | #include "esp/uart.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "FreeRTOS.h" | ||||||
|  | #include "task.h" | ||||||
|  | 
 | ||||||
|  | #include "http_client_ota.h" | ||||||
|  | #include "ssid_config.h" | ||||||
|  | 
 | ||||||
|  | #define vTaskDelayMs(ms) vTaskDelay((ms) / portTICK_PERIOD_MS) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * How to test | ||||||
|  |  * cd test_file | ||||||
|  |  * python -m SimpleHTTPServer 8080 | ||||||
|  |  * fill missing define SERVER and PORT, in your private_ssid_config.h | ||||||
|  |  * Ready for test. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define BINARY_PATH "/blink.bin" | ||||||
|  | #define SHA256_PATH "/blink.sha256" | ||||||
|  | 
 | ||||||
|  | // Default
 | ||||||
|  | #define SERVER "192.168.1.30" | ||||||
|  | #define PORT "8080" | ||||||
|  | 
 | ||||||
|  | #ifndef SERVER | ||||||
|  |     #error "Server address is not defined define it:`192.168.X.X`" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef PORT | ||||||
|  |     #error "Port is not defined example:`8080`" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static inline void ota_error_handling(OTA_err err) { | ||||||
|  |     printf("Error:"); | ||||||
|  | 
 | ||||||
|  |     switch(err) { | ||||||
|  |     case OTA_DNS_LOOKUP_FALLIED: | ||||||
|  |         printf("DNS lookup has fallied\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_SOCKET_ALLOCATION_FALLIED: | ||||||
|  |         printf("Impossible allocate required socket\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_SOCKET_CONNECTION_FALLIED: | ||||||
|  |         printf("Server unreachable, impossible connect\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_SHA_DONT_MATCH: | ||||||
|  |         printf("Sha256 sum does not fit downloaded sha256\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_REQUEST_SEND_FALLIED: | ||||||
|  |         printf("Impossible send HTTP request\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_DOWLOAD_SIZE_NOT_MATCH: | ||||||
|  |         printf("Dowload size don't match with server declared size\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_ONE_SLOT_ONLY: | ||||||
|  |         printf("rboot has only one slot configured, impossible switch it\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_FAIL_SET_NEW_SLOT: | ||||||
|  |         printf("rboot cannot switch between rom\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_IMAGE_VERIFY_FALLIED: | ||||||
|  |         printf("Dowloaded image binary checsum is fallied\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_UPDATE_DONE: | ||||||
|  |         printf("Ota has completed upgrade process, all ready for system software reset\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_HTTP_OK: | ||||||
|  |         printf("HTTP server has response 200, Ok\n"); | ||||||
|  |         break; | ||||||
|  |     case OTA_HTTP_NOTFOUND: | ||||||
|  |         printf("HTTP server has response 404, file not found\n"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ota_task(void *PvParameter) | ||||||
|  | { | ||||||
|  |     // Wait until we have joined AP and are assigned an IP *
 | ||||||
|  |     while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP) | ||||||
|  |         vTaskDelayMs(100); | ||||||
|  | 
 | ||||||
|  |     while (1) { | ||||||
|  |         OTA_err err; | ||||||
|  |         // Remake this task until ota work
 | ||||||
|  |         err = ota_update((ota_info *) PvParameter); | ||||||
|  | 
 | ||||||
|  |         ota_error_handling(err); | ||||||
|  | 
 | ||||||
|  |         if(err != OTA_UPDATE_DONE) { | ||||||
|  |             vTaskDelayMs(1000); | ||||||
|  |             printf("\n\n\n"); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         vTaskDelayMs(1000); | ||||||
|  |         printf("Delay 1\n"); | ||||||
|  |         vTaskDelayMs(1000); | ||||||
|  |         printf("Delay 2\n"); | ||||||
|  |         vTaskDelayMs(1000); | ||||||
|  |         printf("Delay 3\n"); | ||||||
|  | 
 | ||||||
|  |         printf("Reset\n"); | ||||||
|  |         sdk_system_restart(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ota_info info = { | ||||||
|  |     .server      = SERVER, | ||||||
|  |     .port        = PORT, | ||||||
|  |     .binary_path = BINARY_PATH, | ||||||
|  |     .sha256_path = SHA256_PATH, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void user_init(void) | ||||||
|  | { | ||||||
|  |     uart_set_baud(0, 115200); | ||||||
|  |     printf("SDK version:%s\n", sdk_system_get_sdk_version()); | ||||||
|  | 
 | ||||||
|  |     struct sdk_station_config config = { | ||||||
|  |         .ssid     = WIFI_SSID, | ||||||
|  |         .password = WIFI_PASS, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /* required to call wifi_set_opmode before station_set_config */ | ||||||
|  |     sdk_wifi_set_opmode(STATION_MODE); | ||||||
|  |     sdk_wifi_station_set_config(&config); | ||||||
|  | 
 | ||||||
|  |     xTaskCreate(ota_task, "get_task", 4096, &info, 2, NULL); | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								examples/http_client_ota/test_file/blink.bin
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/http_client_ota/test_file/blink.bin
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								examples/http_client_ota/test_file/blink.sha256
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								examples/http_client_ota/test_file/blink.sha256
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | 57dda900027355de85f0de9e6c966e3c4c16741d8eed134d209c0fb6304cf852 | ||||||
							
								
								
									
										10
									
								
								extras/http_client_ota/component.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								extras/http_client_ota/component.mk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | # Component makefile for extras/http_client_ota
 | ||||||
|  | 
 | ||||||
|  | # Expected anyone using http_client_ota includes it as 'http_client_ota/ota'
 | ||||||
|  | INC_DIRS += $(http_client_ota_ROOT) | ||||||
|  | 
 | ||||||
|  | # args for passing into compile rule generation
 | ||||||
|  | http_client_ota_INC_DIR = $(http_client_ota_ROOT) | ||||||
|  | http_client_ota_SRC_DIR = $(http_client_ota_ROOT) | ||||||
|  | 
 | ||||||
|  | $(eval $(call component_compile_rules,http_client_ota)) | ||||||
							
								
								
									
										219
									
								
								extras/http_client_ota/http_buffered_client.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								extras/http_client_ota/http_buffered_client.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,219 @@ | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | 
 | ||||||
|  | #include "lwip/err.h" | ||||||
|  | #include "lwip/sockets.h" | ||||||
|  | #include "lwip/sys.h" | ||||||
|  | #include "lwip/netdb.h" | ||||||
|  | #include "lwip/dns.h" | ||||||
|  | 
 | ||||||
|  | #include "http_buffered_client.h" | ||||||
|  | 
 | ||||||
|  | #define MAX_REQUEST_SIZE (152 / sizeof(uint32_t)) | ||||||
|  | #define vTaskDelayMs(ms) vTaskDelay((ms) / portTICK_PERIOD_MS) | ||||||
|  | 
 | ||||||
|  | typedef void (*handle_http_token)(char *); | ||||||
|  | 
 | ||||||
|  | struct http_token_table { | ||||||
|  |     char *            token; | ||||||
|  |     handle_http_token http_tock_cb; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Response struct
 | ||||||
|  | struct HTTP_response { | ||||||
|  |     unsigned int response_code; | ||||||
|  |     unsigned int length; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * \addtogroup http_buffer_client_internal | ||||||
|  |  * Http Request | ||||||
|  |  */ | ||||||
|  | const char *req = | ||||||
|  |   "GET %s HTTP/1.1\r\n" | ||||||
|  |   "Host: %s \r\n" | ||||||
|  |   "User-Agent: esp-open-rtos/0.1 esp8266\r\n" | ||||||
|  |   "Connection: close\r\n" | ||||||
|  |   "\r\n"; | ||||||
|  | 
 | ||||||
|  | static uint32_t request[MAX_REQUEST_SIZE]; | ||||||
|  | 
 | ||||||
|  | static const struct addrinfo hints = { | ||||||
|  |     .ai_family   = AF_UNSPEC, | ||||||
|  |     .ai_socktype = SOCK_STREAM, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct HTTP_response http_reponse; | ||||||
|  | 
 | ||||||
|  | // HTTP Header Token, add here function and then register it in HTTP Table callback
 | ||||||
|  | static void http_handle_cb_ContentLength(char *token) | ||||||
|  | { | ||||||
|  |     token += 16; // strlen("Content-Length:"), skip useless part
 | ||||||
|  |     while (*token) { | ||||||
|  |         if (isdigit((int) *token)) | ||||||
|  |             http_reponse.length = (unsigned int) strtol(token, &token, 10); | ||||||
|  |         else | ||||||
|  |             token++; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void parse_http_header_HTTP_STATUS(char *token) | ||||||
|  | { | ||||||
|  |     token += 8; // Skip HTTP/1.0
 | ||||||
|  | 
 | ||||||
|  |     while (*token) { | ||||||
|  |         if (isdigit((int) *token)) | ||||||
|  |             http_reponse.response_code = (unsigned int) strtol(token, &token, 10); | ||||||
|  |         else | ||||||
|  |             token++; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // HTTP Token Hanling callback
 | ||||||
|  | struct http_token_table HTTP_HEADER_TOKEN[] = { | ||||||
|  |     { .token = "Content-Length", .http_tock_cb = http_handle_cb_ContentLength }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static inline void parse_http_header(char *header) | ||||||
|  | { | ||||||
|  |     char *str1, *str2, *token, *subtoken, *saveptr1, *saveptr2; | ||||||
|  |     const char line_split[] = "\r\n", sub_chart[] = ":"; | ||||||
|  |     unsigned int j, i; | ||||||
|  | 
 | ||||||
|  |     for (j = 1, str1 = header;; j++, str1 = NULL) { | ||||||
|  |         token = strtok_r(str1, line_split, &saveptr1); | ||||||
|  |         if (token == NULL) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         str2     = token; | ||||||
|  |         subtoken = strtok_r(str2, sub_chart, &saveptr2); | ||||||
|  |         if (subtoken == NULL) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         if (j == 1) { | ||||||
|  |             // Is HTTP Header, response, HTTP Version and status
 | ||||||
|  |             parse_http_header_HTTP_STATUS(token); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (i = 0; i < sizeof(HTTP_HEADER_TOKEN) / sizeof(struct http_token_table); i++) | ||||||
|  |             if (!strcmp(subtoken, HTTP_HEADER_TOKEN[i].token)) | ||||||
|  |                 HTTP_HEADER_TOKEN[i].http_tock_cb(token); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HTTP_Client_State HttpClient_dowload(Http_client_info *info) | ||||||
|  | { | ||||||
|  |     struct addrinfo *res; | ||||||
|  |     unsigned int tot_http_pdu_rd, read_byte, full; | ||||||
|  |     int err, sock; | ||||||
|  |     char *wrt_ptr; | ||||||
|  | 
 | ||||||
|  |     err = getaddrinfo(info->server, info->port, &hints, &res); | ||||||
|  | 
 | ||||||
|  |     if (err != 0 || res == NULL) { | ||||||
|  |         if (res) | ||||||
|  |             freeaddrinfo(res); | ||||||
|  |         return HTTP_DNS_LOOKUP_FALLIED; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sock = socket(res->ai_family, res->ai_socktype, 0); | ||||||
|  |     if (sock < 0) { | ||||||
|  |         freeaddrinfo(res); | ||||||
|  |         return HTTP_SOCKET_ALLOCATION_FALLIED; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (connect(sock, res->ai_addr, res->ai_addrlen) != 0) { | ||||||
|  |         close(sock); | ||||||
|  |         freeaddrinfo(res); | ||||||
|  |         return HTTP_SOCKET_CONNECTION_FALLIED; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Release address memory
 | ||||||
|  |     freeaddrinfo(res); | ||||||
|  | 
 | ||||||
|  |     // Alloc memory for request
 | ||||||
|  |     sprintf((char *) request, req, info->path, info->server); | ||||||
|  |     if (write(sock, (char *) request, strlen((char *) request)) < 0) { | ||||||
|  |         close(sock); | ||||||
|  |         return HTTP_REQUEST_SEND_FALLIED; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     tot_http_pdu_rd = 0; | ||||||
|  |     wrt_ptr         = info->buffer; | ||||||
|  |     full = 0; | ||||||
|  | 
 | ||||||
|  |     // Ping wdog
 | ||||||
|  |     vTaskDelayMs(250); | ||||||
|  |     do { | ||||||
|  |         int free_buff_space; | ||||||
|  | 
 | ||||||
|  |         free_buff_space = info->buffer_size - full; | ||||||
|  |         read_byte       = read(sock, wrt_ptr, free_buff_space); | ||||||
|  | 
 | ||||||
|  |         // Update buffer property
 | ||||||
|  |         wrt_ptr += read_byte; | ||||||
|  |         full    += read_byte; | ||||||
|  | 
 | ||||||
|  |         if (tot_http_pdu_rd == 0) { | ||||||
|  |             // Is fist chunk, then it contains http header, parse it.
 | ||||||
|  |             unsigned int header_len, pdu_size; | ||||||
|  |             char *header, *pdu; | ||||||
|  | 
 | ||||||
|  |             pdu = strstr(info->buffer, "\r\n\r\n"); | ||||||
|  | 
 | ||||||
|  |             if (pdu != NULL) | ||||||
|  |                 pdu += 4;  // Offset by 4 bytes to start of content
 | ||||||
|  |             else           // Not all HTTP Header has been read, then continue read
 | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             header_len = pdu - info->buffer + 4; | ||||||
|  | 
 | ||||||
|  |             header = malloc(header_len + 1); // NULL string terminator
 | ||||||
|  | 
 | ||||||
|  |             memset(header, 0, header_len + 1); | ||||||
|  |             memcpy(header, info->buffer, header_len); | ||||||
|  | 
 | ||||||
|  |             parse_http_header(header); | ||||||
|  |             // Release useless memory
 | ||||||
|  |             free(header); | ||||||
|  | 
 | ||||||
|  |             // Move memory
 | ||||||
|  |             pdu_size = wrt_ptr - pdu; | ||||||
|  | 
 | ||||||
|  |             memmove(info->buffer, pdu, pdu_size); | ||||||
|  |             wrt_ptr = (info->buffer + pdu_size); | ||||||
|  | 
 | ||||||
|  |             full = pdu_size; | ||||||
|  |             tot_http_pdu_rd = pdu_size; | ||||||
|  | 
 | ||||||
|  |             if (http_reponse.response_code != HTTP_OK) | ||||||
|  |                 goto err_label; | ||||||
|  | 
 | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         tot_http_pdu_rd += read_byte; | ||||||
|  | 
 | ||||||
|  |         if (full == info->buffer_size) { | ||||||
|  |             info->buffer_full_cb(info->buffer, full); | ||||||
|  | 
 | ||||||
|  |             memset(info->buffer, 0, info->buffer_size); | ||||||
|  |             wrt_ptr = info->buffer; | ||||||
|  |             full    = 0; | ||||||
|  |             vTaskDelayMs(50); | ||||||
|  |         } | ||||||
|  |     } while (read_byte > 0); | ||||||
|  | 
 | ||||||
|  |     info->final_cb(info->buffer, full); | ||||||
|  |     if (tot_http_pdu_rd != http_reponse.length) | ||||||
|  |         http_reponse.response_code = HTTP_DOWLOAD_SIZE_NOT_MATCH; | ||||||
|  | 
 | ||||||
|  | err_label: | ||||||
|  |     close(sock); | ||||||
|  |     return http_reponse.response_code; | ||||||
|  | } /* HttpClient_dowload */ | ||||||
							
								
								
									
										29
									
								
								extras/http_client_ota/http_buffered_client.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								extras/http_client_ota/http_buffered_client.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | #ifndef HTTP_BUFFERED_CLIENT | ||||||
|  | #define HTTP_BUFFERED_CLIENT | ||||||
|  | 
 | ||||||
|  | typedef unsigned int (*http_final_cb)(char *buff, uint16_t size); | ||||||
|  | 
 | ||||||
|  | typedef enum  { | ||||||
|  |     HTTP_DNS_LOOKUP_FALLIED        = 1, | ||||||
|  |     HTTP_SOCKET_ALLOCATION_FALLIED = 2, | ||||||
|  |     HTTP_SOCKET_CONNECTION_FALLIED = 3, | ||||||
|  |     HTTP_SHA_DONT_MATCH            = 4, | ||||||
|  |     HTTP_REQUEST_SEND_FALLIED      = 5, | ||||||
|  |     HTTP_DOWLOAD_SIZE_NOT_MATCH    = 6, | ||||||
|  |     HTTP_OK                        = 200, | ||||||
|  |     HTTP_NOTFOUND                  = 404, | ||||||
|  | } HTTP_Client_State; | ||||||
|  | 
 | ||||||
|  | typedef struct  { | ||||||
|  |     char *        server; | ||||||
|  |     char *        port; | ||||||
|  |     char *        path; | ||||||
|  |     char *        buffer; | ||||||
|  |     uint16_t      buffer_size; | ||||||
|  |     http_final_cb buffer_full_cb; | ||||||
|  |     http_final_cb final_cb; | ||||||
|  | } Http_client_info; | ||||||
|  | 
 | ||||||
|  | HTTP_Client_State HttpClient_dowload(Http_client_info *info); | ||||||
|  | 
 | ||||||
|  | #endif // ifndef HTTP_BUFFERED_CLIENT
 | ||||||
							
								
								
									
										259
									
								
								extras/http_client_ota/http_client_ota.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								extras/http_client_ota/http_client_ota.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,259 @@ | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | #include "arch/cc.h" | ||||||
|  | #include "lwip/err.h" | ||||||
|  | #include "lwip/sockets.h" | ||||||
|  | #include "lwip/sys.h" | ||||||
|  | #include "lwip/netdb.h" | ||||||
|  | #include "lwip/dns.h" | ||||||
|  | #include <espressif/spi_flash.h> | ||||||
|  | #include <espressif/esp_system.h> | ||||||
|  | #include <espressif/esp_common.h> | ||||||
|  | #include <espressif/esp_system.h> | ||||||
|  | #include "mbedtls/sha256.h" | ||||||
|  | #include "http_client_ota.h" | ||||||
|  | #include "rboot-api.h" | ||||||
|  | #include "rboot.h" | ||||||
|  | #define MODULE "OTA" | ||||||
|  | 
 | ||||||
|  | #if defined(DEBUG) | ||||||
|  | # ifndef MODULE | ||||||
|  | #  error "Module not define" | ||||||
|  | # endif | ||||||
|  | 
 | ||||||
|  |     # define DEBUG_PRINT(fmt, args ...) \ | ||||||
|  |         printf("[%s]\t" fmt "\n", MODULE, ## args) | ||||||
|  | #else | ||||||
|  |     # define DEBUG_PRINT(fmt, args ...) /* Don't do anything in release builds */ | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define MAX_IMAGE_SIZE        0x100000 /*1MB images max at the moment */ | ||||||
|  | #define READ_BUFFER_LEN       512 | ||||||
|  | 
 | ||||||
|  | #define SHA256_SIZE_BIN       32 | ||||||
|  | #define SHA256_SIZE_STR       SHA256_SIZE_BIN * 2 | ||||||
|  | #define SHA256_CONV_STEP_SIZE 4 | ||||||
|  | 
 | ||||||
|  | #if SECTOR_SIZE % READ_BUFFER_LEN != 0 | ||||||
|  | # error "Incompatible SECTOR SIZE, with you current READ_BUFFER" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define SECTOR_BUFFER_SIZE (SECTOR_SIZE) | ||||||
|  | #define vTaskDelayMs(ms) vTaskDelay((ms) / portTICK_PERIOD_MS) | ||||||
|  | 
 | ||||||
|  | static ota_info *ota_inf; | ||||||
|  | static mbedtls_sha256_context *sha256_ctx; | ||||||
|  | 
 | ||||||
|  | static uint32_t flash_offset; | ||||||
|  | static uint32_t flash_limits; | ||||||
|  | 
 | ||||||
|  | static unsigned char *SHA256_output; | ||||||
|  | static uint16_t *SHA256_dowload; | ||||||
|  | static char *SHA256_str; | ||||||
|  | static char *SHA256_wrt_ptr; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * CallBack called from Http Buffered client, for ota firmaware | ||||||
|  |  */ | ||||||
|  | static unsigned int ota_firmaware_dowload_callback(char *buf, uint16_t size) | ||||||
|  | { | ||||||
|  |     if (ota_inf->sha256_path != NULL) | ||||||
|  |         mbedtls_sha256_update(sha256_ctx, (const unsigned char *) buf, size); | ||||||
|  | 
 | ||||||
|  |     if (flash_offset + size > flash_limits) { | ||||||
|  |         DEBUG_PRINT("Flash Limits override"); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Ready for flash device, the erase NANDFLASH Block
 | ||||||
|  |     if (flash_offset % SECTOR_SIZE == 0) { | ||||||
|  |         unsigned int sector; | ||||||
|  | 
 | ||||||
|  |         sector = flash_offset / SECTOR_SIZE; | ||||||
|  |         sdk_spi_flash_erase_sector(sector); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Write into Flash
 | ||||||
|  |     sdk_spi_flash_write(flash_offset, (uint32_t *) buf, size); | ||||||
|  |     flash_offset += size; | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static unsigned int SHA256_check_callback(char *buf, uint16_t size) | ||||||
|  | { | ||||||
|  |     int Current_SHA_Size; | ||||||
|  | 
 | ||||||
|  |     // Check that str does not contains other streing with SHA256
 | ||||||
|  |     if (size > SHA256_SIZE_STR) | ||||||
|  |         size = SHA256_SIZE_STR; | ||||||
|  | 
 | ||||||
|  |     Current_SHA_Size = SHA256_wrt_ptr - (char *) SHA256_str; | ||||||
|  | 
 | ||||||
|  |     if (!(Current_SHA_Size > SHA256_SIZE_STR)) { | ||||||
|  |         memcpy(SHA256_wrt_ptr, buf, size); | ||||||
|  |         SHA256_wrt_ptr += size; | ||||||
|  |     } | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void convert_SHA256_Str_TO_uint32(char *str, uint16_t *final_sha_bin) | ||||||
|  | { | ||||||
|  |     char tmp[SHA256_CONV_STEP_SIZE + 1]; | ||||||
|  |     char *wrt_ptr; | ||||||
|  |     int i; | ||||||
|  | 
 | ||||||
|  |     wrt_ptr = str; | ||||||
|  |     for (i = 0; i < SHA256_SIZE_STR / SHA256_CONV_STEP_SIZE; i++) { | ||||||
|  |         uint16_t val; | ||||||
|  |         memset(tmp, 0, sizeof(tmp)); | ||||||
|  |         memcpy(tmp, wrt_ptr, SHA256_CONV_STEP_SIZE); | ||||||
|  | 
 | ||||||
|  |         val = strtol(tmp, NULL, 16); | ||||||
|  | 
 | ||||||
|  |         final_sha_bin[i] = LWIP_PLATFORM_HTONS(val); | ||||||
|  | 
 | ||||||
|  |         wrt_ptr += SHA256_CONV_STEP_SIZE; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | OTA_err ota_update(ota_info *ota_info_par) | ||||||
|  | { | ||||||
|  |     Http_client_info http_inf; | ||||||
|  |     rboot_config rboot_config; | ||||||
|  |     HTTP_Client_State err; | ||||||
|  |     int slot; | ||||||
|  | 
 | ||||||
|  |     ota_inf = ota_info_par; | ||||||
|  | 
 | ||||||
|  |     // Malloc memory for work
 | ||||||
|  |     http_inf.buffer      = malloc(SECTOR_BUFFER_SIZE); | ||||||
|  |     http_inf.buffer_size = SECTOR_BUFFER_SIZE; | ||||||
|  |     http_inf.server      = ota_inf->server; | ||||||
|  |     http_inf.port        = ota_inf->port; | ||||||
|  | 
 | ||||||
|  |     // Check memory alignement, must be aligned
 | ||||||
|  |     if ((unsigned int) http_inf.buffer % sizeof(unsigned int)) { | ||||||
|  |         DEBUG_PRINT("Malloc return Unaligned memory"); | ||||||
|  |         free(http_inf.buffer); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (ota_inf->sha256_path != NULL) { | ||||||
|  |         sha256_ctx     = malloc(sizeof(mbedtls_sha256_context)); | ||||||
|  |         SHA256_output  = malloc(SHA256_SIZE_BIN); | ||||||
|  |         SHA256_dowload = malloc(SHA256_SIZE_BIN); | ||||||
|  |         SHA256_str     = malloc(SHA256_SIZE_STR + 1); | ||||||
|  |         SHA256_wrt_ptr = SHA256_str; | ||||||
|  |         SHA256_str[SHA256_SIZE_STR] = '\0'; | ||||||
|  |         mbedtls_sha256_init(sha256_ctx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     DEBUG_PRINT("HTTP client task starting"); | ||||||
|  | 
 | ||||||
|  |     rboot_config = rboot_get_config(); | ||||||
|  |     slot         = (rboot_config.current_rom + 1) % rboot_config.count; | ||||||
|  | 
 | ||||||
|  |     DEBUG_PRINT("Image will be saved in OTA slot %d", slot); | ||||||
|  |     if (slot == rboot_config.current_rom) { | ||||||
|  |         DEBUG_PRINT("Only one OTA slot is configured!"); | ||||||
|  |         err = OTA_ONE_SLOT_ONLY; | ||||||
|  |         goto dealloc_all; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Validate the OTA slot parameter */ | ||||||
|  |     if (rboot_config.current_rom == slot || rboot_config.count <= slot) | ||||||
|  |         DEBUG_PRINT("Current rom set to unknow value:%d", rboot_config.current_rom); | ||||||
|  | 
 | ||||||
|  |     // Calculate room limits
 | ||||||
|  |     flash_offset = rboot_config.roms[slot]; | ||||||
|  |     flash_limits = flash_offset + MAX_IMAGE_SIZE; | ||||||
|  | 
 | ||||||
|  |     if (ota_inf->sha256_path != NULL) { | ||||||
|  |         // Setup for dowload sha256
 | ||||||
|  |         http_inf.path           = ota_inf->sha256_path; | ||||||
|  |         http_inf.final_cb       = SHA256_check_callback; | ||||||
|  |         http_inf.buffer_full_cb = SHA256_check_callback; | ||||||
|  | 
 | ||||||
|  |         memset(SHA256_dowload, 0, SHA256_SIZE_BIN); | ||||||
|  |         memset(SHA256_str, 0, SHA256_SIZE_STR); | ||||||
|  | 
 | ||||||
|  |         err = HttpClient_dowload(&http_inf); | ||||||
|  | 
 | ||||||
|  |         // Check if dowload success
 | ||||||
|  |         if (err != HTTP_OK) | ||||||
|  |             goto dealloc_all; | ||||||
|  | 
 | ||||||
|  |         convert_SHA256_Str_TO_uint32(SHA256_str, SHA256_dowload); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Ping Wdog
 | ||||||
|  |     vTaskDelayMs(250); | ||||||
|  | 
 | ||||||
|  |     // Dowload Firmaware
 | ||||||
|  |     http_inf.path           = ota_inf->binary_path; | ||||||
|  |     http_inf.final_cb       = ota_firmaware_dowload_callback; | ||||||
|  |     http_inf.buffer_full_cb = ota_firmaware_dowload_callback; | ||||||
|  |     if (ota_inf->sha256_path != NULL) | ||||||
|  |         mbedtls_sha256_starts(sha256_ctx, 0);  // Start SHA256, not SHA224
 | ||||||
|  | 
 | ||||||
|  |     err = HttpClient_dowload(&http_inf); | ||||||
|  | 
 | ||||||
|  |     if (err != HTTP_OK) | ||||||
|  |         goto dealloc_all; | ||||||
|  | 
 | ||||||
|  |     if (ota_inf->sha256_path != NULL) { | ||||||
|  |         char com_res; | ||||||
|  |         mbedtls_sha256_finish(sha256_ctx, SHA256_output); | ||||||
|  |         mbedtls_sha256_free(sha256_ctx); | ||||||
|  | 
 | ||||||
|  |         com_res = !memcmp((void *) SHA256_output, (void *) SHA256_dowload, SHA256_SIZE_BIN); | ||||||
|  |         if (!com_res) { | ||||||
|  |             DEBUG_PRINT("SHA256 is not equal"); | ||||||
|  |             err = HTTP_SHA_DONT_MATCH; | ||||||
|  |             goto dealloc_all; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // Ping watch DOG
 | ||||||
|  |     vTaskDelayMs(500); | ||||||
|  |     { | ||||||
|  |         #define MESSAGE_MAX 120 | ||||||
|  |         unsigned int Rboot_verified, boot_dimension; | ||||||
|  |         char error_message[MESSAGE_MAX]; | ||||||
|  | 
 | ||||||
|  |         memset(error_message, 0, sizeof(error_message)); | ||||||
|  |         // Start verify
 | ||||||
|  |         Rboot_verified = rboot_verify_image(rboot_config.roms[slot], &boot_dimension, (const char **) &error_message); | ||||||
|  |         if (Rboot_verified) { | ||||||
|  |             // Rom OK, call final callback for let inform user that all is ready for switch and reset.
 | ||||||
|  | 
 | ||||||
|  |             vPortEnterCritical(); | ||||||
|  |             if (!rboot_set_current_rom(slot)) { | ||||||
|  |                 vPortExitCritical(); | ||||||
|  |                 err = OTA_FAIL_SET_NEW_SLOT; | ||||||
|  |                 goto dealloc_all; | ||||||
|  |             } | ||||||
|  |             vPortExitCritical(); | ||||||
|  | 
 | ||||||
|  |             // Update success, software return HTTP_200
 | ||||||
|  |             err = OTA_UPDATE_DONE; | ||||||
|  |             goto dealloc_all; | ||||||
|  |         } else { | ||||||
|  |             DEBUG_PRINT("%s", error_message); | ||||||
|  |             err = OTA_IMAGE_VERIFY_FALLIED; | ||||||
|  |             goto dealloc_all; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | dealloc_all: | ||||||
|  |     free(http_inf.buffer); | ||||||
|  | 
 | ||||||
|  |     if (ota_inf->sha256_path != NULL) { | ||||||
|  |         free(sha256_ctx); | ||||||
|  |         free(SHA256_str); | ||||||
|  |         free(SHA256_output); | ||||||
|  |         free(SHA256_dowload); | ||||||
|  |     } | ||||||
|  |     return err; | ||||||
|  | } /* ota_update */ | ||||||
							
								
								
									
										56
									
								
								extras/http_client_ota/http_client_ota.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								extras/http_client_ota/http_client_ota.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | #ifndef HTTP_OTA_H | ||||||
|  | #define HTTP_OTA_H | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @file ota_update.h | ||||||
|  |  * @author Andrea Greco <a.greco@4sigma.it> | ||||||
|  |  * @brief File containing struct and function ota update | ||||||
|  |  * File containing struct and function ota update. | ||||||
|  |  * Ota use remote HTTP server in internet, for dowload Firmaware and sha256 file. | ||||||
|  |  * Firmaware is compiled file stripped, contained in folder firmaware. | ||||||
|  |  * Sha256 is a file that contains a sha256, of Firmaware. | ||||||
|  |  * If enabled 256 is checked during firmaware download. | ||||||
|  |  */ | ||||||
|  | #include "http_buffered_client.h" | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     // Keep this aligned with \ref HTTP_Client_State
 | ||||||
|  |     OTA_DNS_LOOKUP_FALLIED        = HTTP_DNS_LOOKUP_FALLIED,/**< DNS lookup has fallied */ | ||||||
|  |     OTA_SOCKET_ALLOCATION_FALLIED = HTTP_SOCKET_ALLOCATION_FALLIED,/**< Impossible allocate required socket */ | ||||||
|  |     OTA_SOCKET_CONNECTION_FALLIED = HTTP_SOCKET_CONNECTION_FALLIED,/**< Server unreachable, impossible connect */ | ||||||
|  |     OTA_SHA_DONT_MATCH            = HTTP_SHA_DONT_MATCH,/** Sha256 sum does not fit downloaded sha256 */ | ||||||
|  |     OTA_REQUEST_SEND_FALLIED      = HTTP_REQUEST_SEND_FALLIED,/**< Impossible send HTTP request */ | ||||||
|  |     OTA_DOWLOAD_SIZE_NOT_MATCH    = HTTP_DOWLOAD_SIZE_NOT_MATCH, /**< Dowload size don't match with server declared size */ | ||||||
|  | 
 | ||||||
|  |     // Ota error
 | ||||||
|  |     OTA_ONE_SLOT_ONLY             = 20,/**< rboot has only one slot configured, impossible switch it */ | ||||||
|  |     OTA_FAIL_SET_NEW_SLOT         = 21,/**< rboot cannot switch between rom */ | ||||||
|  |     OTA_IMAGE_VERIFY_FALLIED      = 22,/**< Dowloaded image binary checsum is fallied */ | ||||||
|  |     OTA_UPDATE_DONE               = 23, /**< Ota has completed upgrade process, all ready for system software reset */ | ||||||
|  | 
 | ||||||
|  |     OTA_HTTP_OK                   = 200,/** HTTP server has response 200, Ok */ | ||||||
|  |     OTA_HTTP_NOTFOUND             = 404,/** HTTP server has response 404, file not found */ | ||||||
|  | } OTA_err; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * \brief Create ota info. | ||||||
|  |  * Struct that contains all info for start ota. | ||||||
|  |  */ | ||||||
|  | typedef struct { | ||||||
|  |     char *server;      /**< Server domain */ | ||||||
|  |     char *port;        /**< Server port   */ | ||||||
|  |     char *binary_path; /**< Server Path dowload new update binary */ | ||||||
|  |     char *sha256_path; /**< Server Path of SHA256 sum for check binary, could be NULL, check will be skipped */ | ||||||
|  | } ota_info; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * \brief Start ota update. | ||||||
|  |  * Start Ota update. | ||||||
|  |  * Ota_info contains all information about file to download, port, server. | ||||||
|  |  * \param ota_info_par ptr to ota info. | ||||||
|  |  * In case of success, and ota update is right done, \ref finish_cb is called before ESP8266 reset. | ||||||
|  |  * \return http server return Code, check out \ref HTTP_Client_State for all code. | ||||||
|  |  */ | ||||||
|  | OTA_err ota_update(ota_info *ota_info_par); | ||||||
|  | #endif // ifndef HTTP_OTA_H
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue