diff --git a/WiFi_Shield.ino b/WiFi_Shield.ino index 9411987..6b2740a 100644 --- a/WiFi_Shield.ino +++ b/WiFi_Shield.ino @@ -3,6 +3,13 @@ (C) 2017-2018 Julian Metzler */ +/* + IMPORTANT + + For the HTTPS OTA update to work, the verify() method of TLSTraits + in ESP8266HTTPClient.cpp needs to be modified to always return true. +*/ + /* UPLOAD SETTINGS @@ -20,9 +27,10 @@ #define ARDUINO_OTA_ENABLEDXXX #define INIT_EEPROMXXX +#define SERIAL_DEBUGXXX #include -//#include +#include #include #include #include @@ -55,21 +63,31 @@ enum UpdateStatus { #define PIN_CONFIG 0 #define WIFI_TIMEOUT 10000 +#define UPDATE_START_DELAY 3000 /* GLOBAL VARIABLES */ unsigned long HW_GROUP = 1; // Changes with hardware changes that require software changes -unsigned long FW_VERSION = 1803260003; // Changes with each release; must always increase +unsigned long FW_VERSION = 1806240002; // Changes with each release; must always increase unsigned long SP_VERSION = 0; // Loaded from SPIFFS; changed with each SPIFFS build; must always increase (uses timestamp as version) -// HTTPS update settings -//String UPDATE_HOST = "static.mezgrman.de"; -//int UPDATE_PORT = 443; +// FW & SPIFFS update settings +const char* UPDATE_HOST = "static.mezgrman.de"; +const int UPDATE_PORT_HTTPS = 443; +const int UPDATE_PORT_HTTP = 80; +String UPDATE_PATH_BASE_HTTPS = "/firmware/wifi_shield/"; +String UPDATE_URL_BASE_HTTPS = "https://static.mezgrman.de/firmware/wifi_shield/"; +String UPDATE_URL_BASE_HTTP = "http://static.mezgrman.de/firmware/wifi_shield/"; +String UPDATE_FINGERPRINT_HTTPS = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; // Not important, will be ignored anyway -// HTTP update settings -String UPDATE_URL_BASE = "http://static.mezgrman.de/firmware/wifi_shield/"; +// Update flags +bool updateFWSecure_flag = false; +bool updateSPSecure_flag = false; +bool updateFWInsecure_flag = false; +bool updateSPInsecure_flag = false; +unsigned long updateFlagSetTimestamp = 0; // Start time of the last WiFi connection attempt unsigned long wifiTimer = 0; @@ -90,8 +108,6 @@ volatile bool btnState = 0; // Current button state volatile bool btnPressed = 0; // Flag to check if the last button press has already been processed volatile unsigned long btnTimer = 0; // Start time of last button press (only while pressed) volatile unsigned long btnDur = 0; // Duration of the last button press (only while released) -volatile unsigned long lastBtnTimer = 0; -volatile unsigned long lastMillis = 0; ESP8266WebServer server(80); WiFiServer IBISServer(5001); @@ -134,9 +150,6 @@ bool loadConfig() { return false; } - /*STA_SSID = json["WiFiSSID"].as(); - STA_PASS = json["WiFiPassword"].as(); - STA_SETUP = json["WiFiSetup"];*/ SP_VERSION = json["SPVersion"]; char curChar; @@ -168,9 +181,6 @@ bool loadConfig() { bool saveConfig() { StaticJsonBuffer<200> jsonBuffer; JsonObject& json = jsonBuffer.createObject(); - /*json["WiFiSSID"] = STA_SSID; - json["WiFiPassword"] = STA_PASS; - json["WiFiSetup"] = STA_SETUP;*/ json["SPVersion"] = SP_VERSION; File configFile = SPIFFS.open("/config.json", "w"); @@ -229,7 +239,7 @@ void handleNotFound() { server.send(404, "text/plain", message); } -String formatPageBase(String content) { +String formatPageBaseWithExtraHead(String content, String extraHead) { String page; page += ""; page += ""; @@ -238,6 +248,7 @@ String formatPageBase(String content) { page += ""; page += ""; page += "WiFi Module"; + page += extraHead; page += ""; page += ""; page += content; @@ -246,6 +257,10 @@ String formatPageBase(String content) { return page; } +String formatPageBase(String content) { + return formatPageBaseWithExtraHead(content, ""); +} + void handleRoot() { String c; c += "

WiFi Module

"; @@ -266,15 +281,6 @@ void handleRoot() { c += ""; c += ""; c += "Check for firmware update"; - c += "

btnDur = "; - c += btnDur; - c += "

"; - c += "

lastBtnTimer = "; - c += lastBtnTimer; - c += "

"; - c += "

lastMillis = "; - c += lastMillis; - c += "

"; server.send(200, "text/html", formatPageBase(c)); } @@ -290,8 +296,10 @@ void handle_wifi_setup() { void handle_check_update() { String c; - UpdateStatus fwStatus = checkForFWUpdate(); - UpdateStatus spStatus = checkForSPUpdate(); + UpdateStatus fwStatusSecure = checkForUpdateSecure(false); + UpdateStatus spStatusSecure = checkForUpdateSecure(true); + UpdateStatus fwStatusInsecure = checkForUpdateInsecure(false); + UpdateStatus spStatusInsecure = checkForUpdateInsecure(true); c += "

Update Status

"; c += ""; c += ""; - if (fwStatus == US_AVAILABLE) { - c += ""; - c += ""; - } else if (fwStatus == US_FAILED) { - c += ""; - } else if (fwStatus == US_NO_UPDATE) { - c += ""; + /*if (fwStatusSecure == US_AVAILABLE) { + c += ""; + c += ""; + } else if (fwStatusSecure == US_FAILED) { + c += ""; + } else if (fwStatusSecure == US_NO_UPDATE) { + c += ""; + }*/ + if (fwStatusInsecure == US_AVAILABLE) { + c += ""; + c += ""; + } else if (fwStatusInsecure == US_FAILED) { + c += ""; + } else if (fwStatusInsecure == US_NO_UPDATE) { + c += ""; } c += ""; c += ""; @@ -316,32 +332,59 @@ void handle_check_update() { c += "-"; c += SP_VERSION; c += ""; - if (spStatus == US_AVAILABLE) { - c += ""; - c += ""; - } else if (spStatus == US_FAILED) { - c += ""; - } else if (spStatus == US_NO_UPDATE) { - c += ""; + /*if (spStatusSecure == US_AVAILABLE) { + c += ""; + c += ""; + } else if (spStatusSecure == US_FAILED) { + c += ""; + } else if (spStatusSecure == US_NO_UPDATE) { + c += ""; + }*/ + if (spStatusInsecure == US_AVAILABLE) { + c += ""; + c += ""; + } else if (spStatusInsecure == US_FAILED) { + c += ""; + } else if (spStatusInsecure == US_NO_UPDATE) { + c += ""; } c += "
Firmware version:"; @@ -300,13 +308,21 @@ void handle_check_update() { c += "-"; c += FW_VERSION; c += "Update available
Update check failedNo update availableUpdate available (HTTPS)
Update check failed (HTTPS)No update available (HTTPS)Update available (HTTP)
Update check failed (HTTP)No update available (HTTP)
Filesystem version:Update available
Update check failedNo update availableUpdate available (HTTPS)
Update check failed (HTTPS)No update available (HTTPS)Update available (HTTP)
Update check failed (HTTP)No update available (HTTP)
"; server.send(200, "text/html", formatPageBase(c)); } -void handle_update_fw() { - server.sendHeader("Location", "/check-update", true); +void handle_update_fw_https() { + updateFWSecure_flag = true; + updateFlagSetTimestamp = millis(); + server.sendHeader("Location", "/update-running", true); server.send(303, "text/plain", ""); - if (checkForFWUpdate() == US_AVAILABLE) { - doFWUpdate(); - } } -void handle_update_sp() { - server.sendHeader("Location", "/check-update", true); +void handle_update_sp_https() { + updateSPSecure_flag = true; + updateFlagSetTimestamp = millis(); + server.sendHeader("Location", "/update-running", true); server.send(303, "text/plain", ""); - if (checkForSPUpdate() == US_AVAILABLE) { - doSPUpdate(); - } +} + +void handle_update_fw_http() { + updateFWInsecure_flag = true; + updateFlagSetTimestamp = millis(); + server.sendHeader("Location", "/update-running", true); + server.send(303, "text/plain", ""); +} + +void handle_update_sp_http() { + updateSPInsecure_flag = true; + updateFlagSetTimestamp = millis(); + server.sendHeader("Location", "/update-running", true); + server.send(303, "text/plain", ""); +} + +void handle_update_running() { + String c; + c += "

Update in progress...

"; + c += "

Please wait while the update is being downloaded and installed.

"; + server.send(200, "text/html", formatPageBaseWithExtraHead(c, "")); } /* @@ -354,11 +397,8 @@ void ISR_config() { // Calculate the last press duration if (btnState) { btnTimer = millis(); - lastBtnTimer = btnTimer; btnDur = 0; } else { - lastMillis = millis(); - btnDur = lastMillis - btnTimer; btnTimer = 0; // Discard presses <= 50ms if (btnDur > 50) { @@ -423,71 +463,160 @@ void resetWiFiCredentials() { FIRMWARE & SPIFFS UPDATE */ -UpdateStatus checkForFWUpdate() { - /*WiFiClientSecure httpsClient; - if (!httpsClient.connect(UPDATE_HOST.c_str(), UPDATE_PORT)) { - //Serial.print("connection failed"); - } - httpsClient.println("GET /firmware/wifi_shield/1/firmware.version HTTP/1.1"); - httpsClient.println("Host: " + UPDATE_HOST); - httpsClient.println("Connection: close"); - httpsClient.println(); - delay(1000); - while (httpsClient.available()) { - String line = httpsClient.readStringUntil('\n'); - Serial.print(line); - } - return 2;*/ - String url = UPDATE_URL_BASE + HW_GROUP + "/firmware.version"; - HTTPClient httpClient; - httpClient.begin(url); - int httpCode = httpClient.GET(); - if (httpCode == 200) { - String newFWVersion = httpClient.getString(); - unsigned long newVersion = newFWVersion.toInt(); - if (newVersion > FW_VERSION ) { - return US_AVAILABLE; - } +UpdateStatus checkForUpdateSecure(bool spiffs) { + String url = UPDATE_PATH_BASE_HTTPS + HW_GROUP; + if (spiffs) { + url += "/spiffs.version"; } else { - return US_FAILED; + url += "/firmware.version"; } - return US_NO_UPDATE; -} - -void doFWUpdate() { - // Set both LEDs on during update - setLEDStatus(1); - pinMode(2, OUTPUT); - digitalWrite(2, LOW); - String url = UPDATE_URL_BASE + HW_GROUP + "/firmware.bin"; - t_httpUpdate_return ret = ESPhttpUpdate.update(url); - -} - -UpdateStatus checkForSPUpdate() { - String url = UPDATE_URL_BASE + HW_GROUP + "/spiffs.version"; - HTTPClient httpClient; - httpClient.begin(url); - int httpCode = httpClient.GET(); - if (httpCode == 200) { - String newSPVersion = httpClient.getString(); - unsigned long newVersion = newSPVersion.toInt(); + WiFiClientSecure httpsClient; + if (!httpsClient.connect(UPDATE_HOST, UPDATE_PORT_HTTPS)) return US_FAILED; + httpsClient.println("GET " + url + " HTTP/1.0"); + httpsClient.print("Host: "); + httpsClient.println(UPDATE_HOST); + httpsClient.println("Connection: close"); + httpsClient.println(); + while (httpsClient.connected()) { + String line = httpsClient.readStringUntil('\n'); + if (line == "\r") { + // Headers received + break; + } + } + String newVersionStr = httpsClient.readStringUntil('\n'); + unsigned long newVersion = newVersionStr.toInt(); + if (spiffs) { if (newVersion > SP_VERSION ) { return US_AVAILABLE; } + } else { + if (newVersion > FW_VERSION ) { + return US_AVAILABLE; + } + } + return US_NO_UPDATE; +} + +UpdateStatus checkForUpdateInsecure(bool spiffs) { + String url = UPDATE_URL_BASE_HTTP + HW_GROUP; + if (spiffs) { + url += "/spiffs.version"; + } else { + url += "/firmware.version"; + } + HTTPClient httpClient; + httpClient.begin(url); + int httpCode = httpClient.GET(); + if (httpCode == 200) { + String newVersionStr = httpClient.getString(); + unsigned long newVersion = newVersionStr.toInt(); + if (spiffs) { + if (newVersion > SP_VERSION ) { + return US_AVAILABLE; + } + } else { + if (newVersion > FW_VERSION ) { + return US_AVAILABLE; + } + } } else { return US_FAILED; } return US_NO_UPDATE; } -void doSPUpdate() { +UpdateStatus checkForUpdate(bool spiffs) { + // First check securely, fallback to insecure + UpdateStatus statusSecure = US_FAILED;//checkForUpdateSecure(spiffs); + if (statusSecure == US_FAILED) { + return checkForUpdateInsecure(spiffs); + } else { + return statusSecure; + } +} + +UpdateStatus checkForFWUpdate() { + return checkForUpdate(false); +} + +UpdateStatus checkForSPUpdate() { + return checkForUpdate(true); +} + +t_httpUpdate_return doUpdateSecure(bool spiffs) { // Set both LEDs on during update setLEDStatus(1); pinMode(2, OUTPUT); digitalWrite(2, LOW); - String url = UPDATE_URL_BASE + HW_GROUP + "/spiffs.bin"; - t_httpUpdate_return ret = ESPhttpUpdate.updateSpiffs(url); + + t_httpUpdate_return ret; + String url = UPDATE_URL_BASE_HTTPS + HW_GROUP; + if (spiffs) { + url += "/spiffs.bin"; + ret = ESPhttpUpdate.updateSpiffs(url, "", UPDATE_FINGERPRINT_HTTPS); + } else { + url += "/firmware.bin"; + ret = ESPhttpUpdate.update(url, "", UPDATE_FINGERPRINT_HTTPS); + } + if (ret == HTTP_UPDATE_OK) { + ESP.restart(); + } + return ret; +} + +t_httpUpdate_return doUpdateInsecure(bool spiffs) { + // Set both LEDs on during update + setLEDStatus(1); + pinMode(2, OUTPUT); + digitalWrite(2, LOW); + + t_httpUpdate_return ret; + String url = UPDATE_URL_BASE_HTTP + HW_GROUP; + if (spiffs) { + url += "/spiffs.bin"; + ret = ESPhttpUpdate.updateSpiffs(url, ""); + } else { + url += "/firmware.bin"; + ret = ESPhttpUpdate.update(url, ""); + } + if (ret == HTTP_UPDATE_OK) { + ESP.restart(); + } + return ret; +} + +void doUpdate(bool spiffs) { + // Set both LEDs on during update + setLEDStatus(1); + pinMode(2, OUTPUT); + digitalWrite(2, LOW); + + t_httpUpdate_return ret; + // Try secure update first + ret = HTTP_UPDATE_FAILED;//doUpdateSecure(spiffs); + + if (ret == HTTP_UPDATE_FAILED) { + // Failover to insecure update + ret = doUpdateInsecure(spiffs); + + if (ret == HTTP_UPDATE_FAILED) { + setLEDStatus(0); + digitalWrite(2, LOW); + pinMode(2, INPUT); + for (int i = 0; i < 3; i++) { + blinkLEDStatusLoop(750); + } + } + } +} + +void doFWUpdate() { + doUpdate(false); +} + +void doSPUpdate() { + doUpdate(true); } /* @@ -537,19 +666,30 @@ void setup() { doWiFiConfigViaAP(); } + // Set up time + configTime(1 * 3600, 0, "pool.ntp.org"); + IBISServer.begin(); server.onNotFound(handleNotFound); server.on("/", handleRoot); server.on("/wifi-setup", handle_wifi_setup); server.on("/check-update", handle_check_update); - server.on("/update-fw", handle_update_fw); - server.on("/update-sp", handle_update_sp); + server.on("/update-fw-https", handle_update_fw_https); + server.on("/update-sp-https", handle_update_sp_https); + server.on("/update-fw-http", handle_update_fw_http); + server.on("/update-sp-http", handle_update_sp_http); + server.on("/update-running", handle_update_running); server.serveStatic("/main.css", SPIFFS, "/main.css"); server.serveStatic("/favicon.ico", SPIFFS, "/favicon.ico"); server.begin(); +#ifdef SERIAL_DEBUG + Serial.begin(115200); + Serial.setDebugOutput(1); +#else IBIS_init(); +#endif for (int i = 0; i < 3; i++) { blinkLEDStatusLoop(125); @@ -558,10 +698,10 @@ void setup() { if (AP_ACTIVE) { setLEDStatus(1); } else { - if (checkForFWUpdate()) { + if (checkForFWUpdate() == US_AVAILABLE) { doFWUpdate(); } - if (checkForSPUpdate()) { + if (checkForSPUpdate() == US_AVAILABLE) { doSPUpdate(); } } @@ -592,6 +732,45 @@ void loop() { setLEDStatus(dur > 500); } + // Update a certain time after flag is set + // (to give the web server time to send the redirect after initiating the update) + if (updateFWSecure_flag) { + if (millis() - updateFlagSetTimestamp >= UPDATE_START_DELAY) { + updateFWSecure_flag = false; + updateFlagSetTimestamp = 0; + if (checkForUpdateSecure(false) == US_AVAILABLE) { + doUpdateSecure(false); + } + } + } + if (updateSPSecure_flag) { + if (millis() - updateFlagSetTimestamp >= UPDATE_START_DELAY) { + updateSPSecure_flag = false; + updateFlagSetTimestamp = 0; + if (checkForUpdateSecure(true) == US_AVAILABLE) { + doUpdateSecure(true); + } + } + } + if (updateFWInsecure_flag) { + if (millis() - updateFlagSetTimestamp >= UPDATE_START_DELAY) { + updateFWInsecure_flag = false; + updateFlagSetTimestamp = 0; + if (checkForUpdateInsecure(false) == US_AVAILABLE) { + doUpdateInsecure(false); + } + } + } + if (updateSPInsecure_flag) { + if (millis() - updateFlagSetTimestamp >= UPDATE_START_DELAY) { + updateSPInsecure_flag = false; + updateFlagSetTimestamp = 0; + if (checkForUpdateInsecure(true) == US_AVAILABLE) { + doUpdateInsecure(true); + } + } + } + // Check if the button has been pressed and for how long if (btnPressed) { btnPressed = 0; @@ -623,9 +802,6 @@ void loop() { break; } default: { - for (int i = 0; i < selectedOption; i++) { - blinkLEDStatusLoop(125); - } break; } } diff --git a/firmware.bin b/firmware.bin index 042df70..fb580c0 100644 Binary files a/firmware.bin and b/firmware.bin differ diff --git a/firmware.version b/firmware.version index 2262200..f8b3738 100644 --- a/firmware.version +++ b/firmware.version @@ -1 +1 @@ -1803260003 \ No newline at end of file +1806240002 \ No newline at end of file