Ver código fonte

Change OTA from HTTP to TFTP

By doing this and eliminating mDNS, we save loads of RAM.
S3
jrhoffa 3 anos atrás
pai
commit
64a49904bb
3 arquivos alterados com 110 adições e 134 exclusões
  1. +2
    -29
      main/main.cpp
  2. +107
    -101
      main/ota.cpp
  3. +1
    -4
      main/ota.hpp

+ 2
- 29
main/main.cpp Ver arquivo

@@ -19,27 +19,6 @@ static const char *TAG = "blinky";
#include "screen_leds.hpp"


// mDNS / NetBIOS

#include <mdns.h>
#include <lwip/apps/netbiosns.h>

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");


+ 107
- 101
main/ota.cpp Ver arquivo

@@ -1,17 +1,16 @@
static const char *TAG = "ota";
#include <esp_log.h>

#include <mdns.h>
#include <esp_ota_ops.h>
#include <lwip/apps/tftp_server.h>

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

#include "ota.hpp"
#include <cstring>

#include "ota.hpp"

// The real version probably lives somewhere else. Too lazy to find it
template <class T> 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;
}

+ 1
- 4
main/ota.hpp Ver arquivo

@@ -1,7 +1,4 @@
#pragma once

#include <esp_http_server.h>


int start_ota_serv(httpd_handle_t);
int start_ota_serv();
int stop_ota_serv();

Carregando…
Cancelar
Salvar