diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..75dab17 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,9 @@ +# Deploying a new version + +* Update the OTA_VERSION in `include/config.h`, `include/config.sample.h`, + `data/_version.txt`, and `update.manifest`. +* Commit the changes. +* Tag the commit with the new version string. +* Push everything. +* Create theimages using `pio run` and `pio run -t buildfs`. +* Upload `update.manifest`, `firmware.bin` and `spiffs.bin`. diff --git a/data/_version.txt b/data/_version.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/data/_version.txt @@ -0,0 +1 @@ +1 diff --git a/include/config.sample.h b/include/config.sample.h index d13fff6..05fe964 100644 --- a/include/config.sample.h +++ b/include/config.sample.h @@ -1,13 +1,21 @@ #pragma once #include +// This is a simple number indicating the version for the HTTP Updater. +#define OTA_VERSION 1 +// Comment out to prevent automatic updates. +#define OTA_UPDATE_URL "https://files.schle.nz/esmp3/update.manifest" +#define OTA_CHECK_INTERVAL 12*60*60*1000 // 12 hours + #define SHOW_DEBUG //#define SHOW_TRACE #define FTP_DEBUG #define DELAY_AFTER_DEBUG_AND_TRACE 0 -#define WIFI_SSID "---CHANGEME---" -#define WIFI_PASS "---CHANGEME---" +// Here you can define WiFi data to use. But actually, the better way to do +// this is by using /_wifi.txt on the sd card. +//#define WIFI_SSID "---CHANGEME---" +//#define WIFI_PASS "---CHANGEME---" #define VS1053_SLEEP_DELAY 5000 #define POSITION_SEND_INTERVAL 5000 diff --git a/include/controller.h b/include/controller.h index 00ce87e..980281b 100644 --- a/include/controller.h +++ b/include/controller.h @@ -32,6 +32,7 @@ private: unsigned long _last_rfid_scan_at = 0; unsigned long _last_position_info_at = 0; + unsigned long _last_update_check_at = 0; String _serial_buffer = String(); String _cmd_queue = ""; void _execute_command_ls(String path); diff --git a/include/updater.h b/include/updater.h new file mode 100644 index 0000000..b781fc2 --- /dev/null +++ b/include/updater.h @@ -0,0 +1,8 @@ +#pragma once + +class Updater { +public: + static void run(); + static bool do_update(int cmd, String url); + static void update_spiffs(); +}; diff --git a/src/controller.cpp b/src/controller.cpp index 1326620..9994cd2 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -3,6 +3,7 @@ #include "config.h" #include "playlist.h" #include "http_server.h" +#include "updater.h" #include Controller::Controller(Player* p, PlaylistManager* playlist_manager) { @@ -50,6 +51,14 @@ void Controller::loop() { process_message(_cmd_queue); _cmd_queue = ""; } + + #ifdef OTA_UPDATE_URL + if (!player->is_playing() && _last_update_check_atselect_sd(false); DEBUG("Starting SPIFFS...\n"); + uint16_t spiffs_version = 0; SPIFFS.begin(true); + if (SPIFFS.exists("/_version.txt")) { + File f = SPIFFS.open("/_version.txt", "r"); + spiffs_version = f.readString().toInt(); + f.close(); + DEBUG("SPIFFS filesystem version is %d.\n", spiffs_version); + } else { + DEBUG("No SPIFFS filesystem version found - setting spiffs_version to 0.\n"); + } DEBUG("Initializing PlaylistManager...\n"); pm = new PlaylistManager(); @@ -93,9 +103,15 @@ void setup() { f.close(); } SPIMaster::select_sd(false); + + if (!connected) { + #if defined(WIFI_SSID) and defined(WIFI_PASS) DEBUG("Trying hardcoded WiFi data...\n"); connected = connect_to_wifi(WIFI_SSID, WIFI_PASS); + #else + DEBUG("No hardcoded WiFi data set.\n"); + #endif } if (!connected) { INFO("No WiFi connection!\n"); @@ -118,6 +134,12 @@ void setup() { } else { INFO("Could not fetch current time via NTP.\n"); } + + #ifdef OTA_UPDATE_URL + if (spiffs_version < OTA_VERSION) { + Updater::run(); + } + #endif INFO("Initialization completed.\n"); } diff --git a/src/updater.cpp b/src/updater.cpp new file mode 100644 index 0000000..9d956c3 --- /dev/null +++ b/src/updater.cpp @@ -0,0 +1,127 @@ +#include +#include +#include "config.h" +#include "updater.h" +#include "http_client_wrapper.h" + +void Updater::run() { + DEBUG("Updater is running...\n"); + HTTPClientWrapper* http = new HTTPClientWrapper(); + DEBUG("Requesting update info...\n"); + bool result = http->get(OTA_UPDATE_URL); + if (!result) { + ERROR("Updater failed requesting %s.\n", OTA_UPDATE_URL); + return; + } + String line = http->readUntil("\n"); + if (!line.startsWith("VERSION=")) { + ERROR("Expected first line to be VERSION.\n"); + return; + } + uint16_t version = line.substring(8).toInt(); + if (version==0) { + ERROR("Could not parse version number.\n"); + return; + } + DEBUG("Found version %d. My version is %d.\n", version, OTA_VERSION); + if (version <= OTA_VERSION) { + return; + } + line = http->readUntil("\n"); + if (!line.startsWith("IMAGE_PATH=")) { + ERROR("Expected second line to be IMAGE_PATH.\n"); + return; + } + String image_path = line.substring(11); + image_path.trim(); + line = http->readUntil("\n"); + if (!line.startsWith("SPIFFS_PATH=")) { + ERROR("Expected third line to be SPIFFS_PATH.\n"); + return; + } + String spiffs_path = line.substring(12); + spiffs_path.trim(); + + http->close(); + delete http; + + result = do_update(U_FLASH, image_path); + if (result) { + do_update(U_SPIFFS, spiffs_path); + } + DEBUG("Done. Rebooting...\n"); + ESP.restart(); +} + +bool Updater::do_update(int command, String url) { + HTTPClientWrapper* http = new HTTPClientWrapper(); + bool result = http->get(url); + if (!result) { + ERROR("Updater failed requesting %s.\n", url.c_str()); + return false; + } + + result = Update.begin(http->getSize(), command); + if (!result) { + ERROR("Update could not be started.\n"); + return false; + } + uint8_t buf[512]; + uint16_t len; + while((len = http->read(buf, 512))) { + Update.write(buf, 512); + } + + http->close(); + delete http; + + result = Update.end(); + if (!result) { + ERROR("Writing the update failed somewhere. Aborted.\n"); + return false; + } + return true; +} + +void Updater::update_spiffs() { + HTTPClientWrapper* http = new HTTPClientWrapper(); + DEBUG("Requesting update info...\n"); + bool result = http->get(OTA_UPDATE_URL); + if (!result) { + ERROR("Updater failed requesting %s.\n", OTA_UPDATE_URL); + return; + } + String line = http->readUntil("\n"); + if (!line.startsWith("VERSION=")) { + ERROR("Expected first line to be VERSION.\n"); + return; + } + uint16_t version = line.substring(8).toInt(); + if (version==0) { + ERROR("Could not parse version number.\n"); + return; + } else if (version > OTA_VERSION) { + DEBUG("Found newer version %d. My version is %d. Starting full update!\n", version, OTA_VERSION); + run(); + } else { + DEBUG("Loading SPIFFS image.\n"); + line = http->readUntil("\n"); + if (!line.startsWith("IMAGE_PATH=")) { + ERROR("Expected second line to be IMAGE_PATH.\n"); + return; + } + line = http->readUntil("\n"); + if (!line.startsWith("SPIFFS_PATH=")) { + ERROR("Expected third line to be SPIFFS_PATH.\n"); + return; + } + String spiffs_path = line.substring(12); + spiffs_path.trim(); + do_update(U_SPIFFS, spiffs_path); + DEBUG("Done. Rebooting...\n"); + delay(500); + ESP.restart(); + } + http->close(); + delete http; +} diff --git a/update.manifest b/update.manifest new file mode 100644 index 0000000..69f7d45 --- /dev/null +++ b/update.manifest @@ -0,0 +1,3 @@ +VERSION=1 +IMAGE_PATH=https://files.schle.nz/esmp3/firmware.bin +SPIFFS_PATH=https://files.schle.nz/esmp3/spiffs.bin