diff --git a/main/main.cpp b/main/main.cpp index 1795378..895cfce 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -19,27 +19,6 @@ static const char *TAG = "blinky"; #include "screen_leds.hpp" -// mDNS / NetBIOS - -#include -#include - -static void start_mdns_service(const char *hostname) { - // Initialize mDNS service - ESP_ERROR_CHECK(mdns_init()); - - // Set hostname - mdns_hostname_set(hostname); - - // Set default instance - mdns_instance_name_set("Blinky Lights"); - - // NetBIOS too - netbiosns_init(); - netbiosns_set_name(hostname); -} - - // Dummy FS static void start_filesystem() { @@ -106,16 +85,10 @@ extern "C" void app_main(void) { // WiFi config_wifi(); - // mDNS - start_mdns_service("blinky-jr"); - - // HTTP Server - httpd_handle_t server = start_webserver(); - // OTA Server - start_ota_serv(server); + start_ota_serv(); - // Dummy Filesystem + // Flash Filesystem start_filesystem(); // TODO: Scrape this out log_dir("/spiffs"); diff --git a/main/ota.cpp b/main/ota.cpp index 13cf9a9..6dd399d 100644 --- a/main/ota.cpp +++ b/main/ota.cpp @@ -1,17 +1,16 @@ static const char *TAG = "ota"; #include -#include #include +#include #include #include -#include "ota.hpp" +#include +#include "ota.hpp" -// The real version probably lives somewhere else. Too lazy to find it -template static inline T MIN(T a, T b) { return a < b ? a : b; } // TODO: Stick this somewhere useful? static void reboot_soon_task(void*) { @@ -30,134 +29,141 @@ static void reboot_soon() { ); } -// HTTP Server Helpers. -// TODO: Share these? -static inline esp_err_t serv_err(httpd_req_t *req, const char *msg) { - ESP_LOGE(TAG, "%s", msg); - return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, msg); -} - -static inline esp_err_t serv_progress(httpd_req_t *req, int progress) { - ESP_LOGI(TAG, "%d%% complete", progress); - /* - char msg[100]; - snprintf(msg, sizeof(msg), "%d%% complete\n", progress); - httpd_resp_set_status(req, "102 Processing"); - httpd_resp_set_type(req, HTTPD_TYPE_TEXT); - return httpd_resp_send(req, msg, HTTPD_RESP_USE_STRLEN); - */ - return ESP_OK; -} - - -// OTA POST -static esp_err_t ota_post_handler(httpd_req_t *req) -{ - esp_err_t err; - - const esp_partition_t *part = esp_ota_get_next_update_partition(NULL); - if (!part) return serv_err(req, "Could not find update partition"); - int remaining = req->content_len; - ESP_LOGI(TAG, "Starting OTA: %d bytes", remaining); +class OTA_Session { +public: + OTA_Session() : + part(esp_ota_get_next_update_partition(NULL)), + ota(0), failed(false), total_written(0) + { + if (!part) { + ESP_LOGE(TAG, "Could not find update partition"); + failed = true; + return; + } - esp_ota_handle_t ota; - err = esp_ota_begin(part, remaining, &ota); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_begin: %d", err); - return serv_err(req, "Could not start OTA"); + auto err = esp_ota_begin(part, OTA_SIZE_UNKNOWN, &ota); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_begin: %d", err); + failed = true; + return; + } } - constexpr int progress_step = 10; - int next_progress = 0; - constexpr size_t CHUNK_MAX = 4096; - char *chunk_buf = new char[CHUNK_MAX]; - while (remaining > 0) { - int chunk_size = MIN((unsigned int)remaining, CHUNK_MAX); - - int nread = httpd_req_recv(req, chunk_buf, chunk_size); - if (nread <= 0) { /* 0 return value indicates connection closed */ - ESP_LOGE(TAG, "httpd_req_recv(%d): %d", chunk_size, nread); + ~OTA_Session() { + if (failed) { + ESP_LOGE(TAG, "Aborting OTA due to previous failure"); esp_ota_abort(ota); - delete[] chunk_buf; - - /* Check if timeout occurred */ - if (nread == HTTPD_SOCK_ERR_TIMEOUT) { - /* In case of timeout one can choose to retry calling - * httpd_req_recv(), but to keep it simple, here we - * respond with an HTTP 408 (Request Timeout) error */ - httpd_resp_send_408(req); - } - - /* In case of error, returning ESP_FAIL will - * ensure that the underlying socket is closed */ - return ESP_FAIL; + return; } - err = esp_ota_write(ota, chunk_buf, nread); + ESP_LOGI(TAG, "Finalizing OTA (%d bytes)", total_written); + auto err = esp_ota_end(ota); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_write(%d): %d", nread, err); + ESP_LOGE(TAG, "esp_ota_end: %d", err); esp_ota_abort(ota); - delete[] chunk_buf; - return serv_err(req, "Failed to write OTA chunk"); + return; + } + + ESP_LOGI(TAG, "Setting new boot partition"); + err = esp_ota_set_boot_partition(part); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_set_boot_partition: %d", err); + return; } - remaining -= nread; + ESP_LOGI(TAG, "OTA Successful"); + + reboot_soon(); + } - int progress = 100 * (req->content_len - remaining) / req->content_len; - if (progress >= next_progress) { - serv_progress(req, progress); - next_progress += progress_step; + bool write(const void *data, int len) { + if (failed) return false; + auto err = esp_ota_write(ota, data, len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_write(%d): %d", len, err); + failed = true; + return false; } + total_written += len; + return true; } - delete[] chunk_buf; - ESP_LOGI(TAG, "Finalizing OTA"); +private: + const esp_partition_t* part; + esp_ota_handle_t ota; + bool failed; + int total_written; +}; - err = esp_ota_end(ota); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_end: %d", err); - esp_ota_abort(ota); - return serv_err(req, "Failed to finalize OTA"); + +static void* ota_open(const char *filename, const char *mode, u8_t write) { + if (strcmp(mode, "octet") != 0) { + ESP_LOGE(TAG, "Unexpected mode: %s", mode); + return NULL; } - ESP_LOGI(TAG, "Setting new boot partition"); - err = esp_ota_set_boot_partition(part); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_set_boot_partition: %d", err); - return serv_err(req, "Failed to set partition"); + if (strcmp(filename, "blinky.bin") != 0) { + ESP_LOGE(TAG, "Unexpected filename: %s", mode); + return NULL; } - ESP_LOGI(TAG, "OTA Successful"); - httpd_resp_sendstr(req, "OTA Successful\n"); + if (!write) { + ESP_LOGE(TAG, "Illegal read attempt"); + return NULL; + } - reboot_soon(); + ESP_LOGI(TAG, "Starting OTA"); - return ESP_FAIL; + return new OTA_Session(); } +static void ota_close(void* handle) { + OTA_Session *session = (OTA_Session*)handle; + if (session) delete session; + else ESP_LOGE(TAG, "Attempt to close invalid session"); +} -int start_ota_serv(httpd_handle_t server) { - ESP_LOGI(TAG, "Starting OTA server"); +static int ota_read(void* handle, void* buf, int len) { + ESP_LOGE(TAG, "Super illegal read of %d bytes", len); + return -1; +} - // Register handlers (move these to components) - httpd_uri_t uri_ota_post = { - .uri = "/ota", - .method = HTTP_POST, - .handler = ota_post_handler, - .user_ctx = NULL - }; - ESP_ERROR_CHECK(httpd_register_uri_handler(server, &uri_ota_post)); +static int ota_write(void* handle, struct pbuf* p) { + OTA_Session *session = (OTA_Session*)handle; + if (!session) { + ESP_LOGE(TAG, "Attempt to write to invalid session"); + return -1; + } - // mDNS Entry - mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0); - mdns_service_instance_name_set("_http", "_tcp", "ESP32S2 OTA"); + for (; p; p = p->next) { + if (!session->write(p->payload, p->len)) { + ESP_LOGE(TAG, "Failed to write %d bytes", p->len); + return -1; + } + } + + return 0; +} + + +static tftp_context ota_context = { + .open = ota_open, + .close = ota_close, + .read = ota_read, + .write = ota_write +}; + +int start_ota_serv() { + ESP_LOGI(TAG, "Starting OTA server"); + + ESP_ERROR_CHECK(tftp_init(&ota_context)); return 0; } int stop_ota_serv() { - // TODO - return -1; + tftp_cleanup(); + return 0; } diff --git a/main/ota.hpp b/main/ota.hpp index ef126c5..143a136 100644 --- a/main/ota.hpp +++ b/main/ota.hpp @@ -1,7 +1,4 @@ #pragma once -#include - - -int start_ota_serv(httpd_handle_t); +int start_ota_serv(); int stop_ota_serv();