diff --git a/firmware/.idea/firmware.iml b/firmware/.idea/firmware.iml index 58ca04c..91a038c 100644 --- a/firmware/.idea/firmware.iml +++ b/firmware/.idea/firmware.iml @@ -1,2 +1,8 @@ - \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/firmware/.idea/modules.xml b/firmware/.idea/modules.xml deleted file mode 100644 index cb860b6..0000000 --- a/firmware/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/firmware/Makefile b/firmware/Makefile index 1c91e7e..bc8dc8b 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -2,7 +2,7 @@ PROGRAM=fiatlux EXTRA_CFLAGS=-O3 -Ifsdata -EXTRA_COMPONENTS=extras/i2s_dma extras/ws2812_i2s extras/dhcpserver extras/mbedtls extras/httpd extras/sntp extras/cpp_support +EXTRA_COMPONENTS=extras/i2s_dma extras/ws2812_i2s extras/dhcpserver extras/rboot-ota extras/mbedtls extras/httpd extras/sntp extras/cpp_support LIBS = hal m diff --git a/firmware/fiatlux.c b/firmware/fiatlux.c index 98d0efb..9e3efeb 100644 --- a/firmware/fiatlux.c +++ b/firmware/fiatlux.c @@ -22,9 +22,9 @@ void user_init(void) wifi_available_semaphore = xSemaphoreCreateBinary(); - xTaskCreate(wifi_task, "wifi_task", 512, NULL, 2, NULL); + xTaskCreate(wifi_task, "wifi_task", 1024, NULL, 1, NULL); - xTaskCreate(&httpd_task, "httpd_task", 512, NULL, 2, NULL); + xTaskCreate(&httpd_task, "httpd_task", 1024, NULL, 2, NULL); xTaskCreate(&lux_task, "lux_task", 512, NULL, 1, NULL); } diff --git a/firmware/fsdata/fs/index.html b/firmware/fsdata/fs/index.html index 2244e40..8ceefa7 100644 --- a/firmware/fsdata/fs/index.html +++ b/firmware/fsdata/fs/index.html @@ -16,9 +16,48 @@
+
+

System

+
+
+

Firmware Update

+
+
+
+ +
+
+ +
+
+
+
+
+

Restart

+
+
+
+ +
+
+
+
+
+

Reset Config

+
+
+
+ +
+
+
+ +

Status

@@ -133,19 +172,18 @@ diff --git a/firmware/system.c b/firmware/system.c index 350f86e..f9dcadd 100644 --- a/firmware/system.c +++ b/firmware/system.c @@ -3,35 +3,114 @@ // #include "system.h" +#include "crc32.h" #include -#include #include #include -#include #include +#include +#include +#include +#include -void system_clear_config(){ +#define min(a, b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +void system_clear_config() { vPortEnterCritical(); - uint32_t num_sectors = 5 + DEFAULT_SYSPARAM_SECTORS; - uint32_t start = sdk_flashchip.chip_size - num_sectors * sdk_flashchip.sector_size; + uint32_t num_sectors = 0x2000 / sdk_flashchip.sector_size; + uint32_t start = 0x00100000; for (uint32_t i = 0; i < num_sectors; i++) { spiflash_erase_sector(start + i * sdk_flashchip.sector_size); } + if(sysparam_create_area(start, num_sectors, true) == SYSPARAM_OK) { + sysparam_init(start, 0); + } + sysparam_init(start, start + 0x2000); sdk_system_restart(); } -void system_init_config(){ - uint32_t base_addr; +void system_init_config() { + uint32_t base_addr = 0x00100000; uint32_t num_sectors; + sysparam_init(base_addr, 0); if(sysparam_get_info(&base_addr, &num_sectors) != SYSPARAM_OK) { printf("Warning: WiFi config, sysparam not initialized\n"); - num_sectors = DEFAULT_SYSPARAM_SECTORS; - base_addr = sdk_flashchip.chip_size - (5 + num_sectors) * sdk_flashchip.sector_size; + num_sectors = 0x2000 / sdk_flashchip.sector_size; if(sysparam_create_area(base_addr, num_sectors, true) == SYSPARAM_OK) { sysparam_init(base_addr, 0); } sdk_system_restart(); } +} + +#define MAX_IMAGE_SIZE 0x100000 + +struct { + rboot_write_status status; + uint32_t head; + uint32_t base; + uint16_t seq; + uint8_t slot; +} otaflash_context; + +void system_otaflash_init() { + rboot_config conf; + conf = rboot_get_config(); + otaflash_context.slot = (conf.current_rom + 1) % conf.count; + otaflash_context.base = rboot_get_slot_offset(otaflash_context.slot); + otaflash_context.status = rboot_write_init(otaflash_context.base); + otaflash_context.head = otaflash_context.base; + otaflash_context.seq = 0; +} + +int system_otaflash_chunk(uint8_t *data, uint16_t len, uint16_t seq, uint32_t hash) { + uint32_t local_hash = crc32(data, len); + if(hash == local_hash && otaflash_context.seq == seq) { + if(otaflash_context.head % SECTOR_SIZE == 0) { + sdk_spi_flash_erase_sector(otaflash_context.head / SECTOR_SIZE); + } + if(((uint32_t) data) % 4) { + uint32 buf[len / 4]; + memcpy(buf, data, len); + sdk_spi_flash_write(otaflash_context.head, buf, len); + } else { + sdk_spi_flash_write(otaflash_context.head, (uint32_t *) data, len); + } + otaflash_context.head += len; + otaflash_context.seq++; + return 0x88; + } else { + return 0xff; + } + +} + +void system_otaflash_verify_chunk(void *ctx, void *data, size_t len) { + uint32_t digest = *(uint32_t *) ctx; + digest = crc32_partial(digest, data, len); + *(uint32_t *) ctx = digest; +} + +int system_otaflash_verify_and_switch(uint32_t len, uint32_t hash) { + + uint32_t digest = 0; + rboot_digest_image(otaflash_context.base, min(len, MAX_IMAGE_SIZE), system_otaflash_verify_chunk, &digest); + + if(hash != digest) { + printf("OTA failed to verify firmware\r\n"); + return 0x99; + } + + vPortEnterCritical(); + if(!rboot_set_current_rom(otaflash_context.slot)) { + printf("OTA failed to set new rboot slot\r\n"); + } + sdk_system_restart(); + vPortExitCritical(); // | should not be reached + return 0x77; // | } \ No newline at end of file diff --git a/firmware/system.h b/firmware/system.h index 14f5188..ac97a9b 100644 --- a/firmware/system.h +++ b/firmware/system.h @@ -5,13 +5,22 @@ #ifndef FIRMWARE_SYSTEM_H #define FIRMWARE_SYSTEM_H +#include + #ifdef __cplusplus extern "C" { #endif void system_clear_config(); + void system_init_config(); +void system_otaflash_init(); + +int system_otaflash_chunk(uint8_t *data, uint16_t len, uint16_t seq, uint32_t hash); + +int system_otaflash_verify_and_switch(uint32_t len, uint32_t hash); + #ifdef __cplusplus } #endif diff --git a/firmware/web.cpp b/firmware/web.cpp index c900018..6176a43 100644 --- a/firmware/web.cpp +++ b/firmware/web.cpp @@ -38,7 +38,7 @@ void websocket_task(void *pvParameter) { has_changed = {true, true, true}; for (;;) { - if(pcb == NULL || pcb->state != ESTABLISHED) { + if(pcb == nullptr || pcb->state != ESTABLISHED) { printf("Connection closed, deleting task\n"); break; } @@ -46,14 +46,14 @@ void websocket_task(void *pvParameter) { //Global Info if(has_changed.global) { has_changed.global = false; - timeval tv; - gettimeofday(&tv, NULL); - int uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; + timeval tv{}; + gettimeofday(&tv, nullptr); + size_t uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; int heap = (int) xPortGetFreeHeapSize(); uint32_t chip_id = sdk_system_get_chip_id(); uint32_t flash_id = sdk_spi_flash_get_id(); uint32_t flash_size = sdk_flashchip.chip_size >> 10; - char *hostname = NULL; + char *hostname = nullptr; sysparam_get_string("hostname", &hostname); /* Generate response in JSON format */ @@ -80,21 +80,16 @@ void websocket_task(void *pvParameter) { //Connection Info if(has_changed.connection) { has_changed.connection = false; - timeval tv; - gettimeofday(&tv, NULL); - int connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000; + timeval tv{}; + gettimeofday(&tv, nullptr); + size_t connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000; - printf("conn %d: " - IPSTR - " <-> " - IPSTR - " \n", pcb->netif_idx, IP2STR(&pcb->local_ip), IP2STR(&pcb->remote_ip)); + printf("conn %d: " IPSTR " <-> " IPSTR " \n", pcb->netif_idx, IP2STR(&pcb->local_ip), + IP2STR(&pcb->remote_ip)); char response[160]; size_t len = snprintf(response, sizeof(response), "{\"connage\" : \"%d\"," - "\"clientip\" : \"" - IPSTR - "\"" + "\"clientip\" : \"" IPSTR "\"" "}", connuptime, IP2STR(&pcb->remote_ip)); if(len < sizeof(response)) { LOCK_TCPIP_CORE(); @@ -137,10 +132,10 @@ void websocket_task(void *pvParameter) { if(opmode == SOFTAP_MODE || opmode == STATIONAP_MODE) { uint8_t hwaddr[6]; sdk_wifi_get_macaddr(SOFTAP_IF, hwaddr); - ip_info info; + ip_info info{}; sdk_wifi_get_ip_info(SOFTAP_IF, &info); - char *apssid = NULL; + char *apssid = nullptr; sysparam_get_string("wifi_ap_ssid", &apssid); /* Generate response in JSON format */ @@ -148,12 +143,8 @@ void websocket_task(void *pvParameter) { size_t len = snprintf(response, sizeof(response), "{\"opmode\" : \"%s\"," " \"apssid\" : \"%s\"," - " \"apip\" : \"" - IPSTR - "\"," - " \"apmac\" : \"" - MACSTR - "\"" + " \"apip\" : \"" IPSTR "\"," + " \"apmac\" : \"" MACSTR "\"" "}", opmode_str, apssid, IP2STR(&info.ip), MAC2STR(hwaddr)); free(apssid); if(len < sizeof(response)) { @@ -169,7 +160,7 @@ void websocket_task(void *pvParameter) { if(opmode == STATION_MODE || opmode == STATIONAP_MODE) { uint8_t hwaddr[6]; sdk_wifi_get_macaddr(STATION_IF, hwaddr); - ip_info info; + ip_info info{}; sdk_wifi_get_ip_info(STATION_IF, &info); char *stassid = nullptr; sysparam_get_string("wifi_sta_ssid", &stassid); @@ -179,12 +170,8 @@ void websocket_task(void *pvParameter) { size_t len = snprintf(response, sizeof(response), "{\"opmode\" : \"%s\"," " \"stassid\" : \"%s\"," - " \"staip\" : \"" - IPSTR - "\"," - " \"stamac\" : \"" - MACSTR - "\"" + " \"staip\" : \"" IPSTR "\"," + " \"stamac\" : \"" MACSTR "\"" "}", opmode_str, stassid, IP2STR(&info.ip), MAC2STR(hwaddr)); free(stassid); if(len < sizeof(response)) { @@ -198,34 +185,49 @@ void websocket_task(void *pvParameter) { } vTaskDelayMs(500); - { - uint8_t response[3]; - uint16_t val = 0; - val = sdk_system_adc_read(); - response[2] = (uint8_t) val; - response[1] = val >> 8; - response[0] = 'V'; - websocket_write(pcb, response, 3, WS_BIN_MODE); - } - vTaskDelayMs(500); } - vTaskDelete(NULL); + vTaskDelete(nullptr); } +struct fw_frame { + char t; + uint8_t reserved[3]; + uint16_t seq; + uint16_t len; + uint32_t hash; + uint8_t data[]; +} __attribute__((packed)); + +struct fw_check { + char t; + uint8_t reserved[3]; + uint32_t len; + uint32_t hash; +} __attribute__((packed)); + /** * This function is called when websocket frame is received. * * Note: this function is executed on TCP thread and should return as soon * as possible. */ -void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode) { +void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, + uint8_t /*mode*/) { //mode should be WS_BIN_MODE or WS_TEXT_MODE uint8_t response[3]; uint16_t val = 0; char cmd = '0'; + bool togl = 0; + switch (data[0]) { + case 'R': // Restart + cmd = 'R'; + break; + case 'X': // Clear Config + cmd = 'X'; + break; case 'D': // Disable LED signal_led(false); val = 1; @@ -236,9 +238,29 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode) val = 0; cmd = 'G'; break; + case 'F': + togl = ~togl; + signal_led(togl); + { + auto *f = (fw_frame *) data; + if(f->seq == 0) { + system_otaflash_init(); + } + val = system_otaflash_chunk(f->data, ntohs(f->len), ntohs(f->seq), ntohl(f->hash)); + } + cmd = 'F'; + break; + case 'C': + signal_led(false); + { + auto *f = (fw_check *) data; + val = system_otaflash_verify_and_switch(ntohl(f->len), ntohl(f->hash)); + } + cmd = 'C'; + break; default: printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char *) data); - printf("Unknown command\n"); + printf("Unknown command %c\n", data[0]); val = 0; break; } @@ -247,7 +269,18 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode) response[1] = val >> 8; response[0] = cmd; + LOCK_TCPIP_CORE(); websocket_write(pcb, response, 3, WS_BIN_MODE); + UNLOCK_TCPIP_CORE(); + + if(data[0] == 'R') { // Restart + vTaskDelay(500 / portTICK_PERIOD_MS); + vPortEnterCritical(); + sdk_system_restart(); + } else if(data[0] == 'X') { // Clear Config + vTaskDelay(500 / portTICK_PERIOD_MS); + system_clear_config(); + } } /** @@ -262,10 +295,10 @@ void websocket_open_cb(struct tcp_pcb *pcb, const char *uri) { } extern "C" void httpd_task(void *pvParameters) { + (void) pvParameters; while (!uxSemaphoreGetCount(wifi_available_semaphore)) vTaskDelay(500 / portTICK_PERIOD_MS); - websocket_register_callbacks((tWsOpenHandler) websocket_open_cb, (tWsHandler) websocket_cb); httpd_init();