2018-11-26 11:58:15 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the "bluetoothheater" distribution
|
|
|
|
* (https://gitlab.com/mrjones.id.au/bluetoothheater)
|
|
|
|
*
|
2019-09-08 00:14:36 +00:00
|
|
|
* Copyright (C) 2019 Ray Jones, James Clark
|
2018-11-26 11:58:15 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-07-07 07:18:38 +00:00
|
|
|
#include <Arduino.h>
|
|
|
|
#include "BTCWifi.h"
|
2018-11-04 05:19:22 +00:00
|
|
|
#include "BTCota.h"
|
2019-02-11 08:34:11 +00:00
|
|
|
#include "../cfg/BTCConfig.h"
|
2019-07-25 07:40:23 +00:00
|
|
|
#include "../../lib/esp32FOTA/src/esp32fota.h" // local copy used due to a couple of issues
|
2019-06-15 23:09:29 +00:00
|
|
|
#include "../Utility/helpers.h"
|
2019-07-07 07:18:38 +00:00
|
|
|
#include <SPIFFS.h>
|
|
|
|
#include <Update.h>
|
|
|
|
#include <ArduinoOTA.h>
|
2019-07-09 12:19:21 +00:00
|
|
|
#include "../Utility/MODBUS-CRC16.h"
|
|
|
|
#include "esp_ota_ops.h"
|
2019-05-20 12:09:59 +00:00
|
|
|
|
2020-06-29 19:46:31 +00:00
|
|
|
//#define TESTFOTA
|
2020-04-20 03:39:39 +00:00
|
|
|
// #define SYNCHRONOUS_FOTA
|
2019-08-03 02:42:49 +00:00
|
|
|
|
2019-07-25 11:27:57 +00:00
|
|
|
bool CheckFirmwareCRC(int filesize);
|
|
|
|
void onSuccess();
|
2019-08-03 02:42:49 +00:00
|
|
|
void onWebProgress(size_t progress, size_t total);
|
2019-05-11 02:18:06 +00:00
|
|
|
|
2020-06-19 06:49:40 +00:00
|
|
|
esp32FOTA FOTA("afterburner-fota-http", int(getVersion()*1000), getVersion(true) != 0 ? true : false);
|
2019-06-06 01:32:43 +00:00
|
|
|
unsigned long FOTAtime = millis() + 60000; // initial check in a minutes time
|
2019-05-20 12:09:59 +00:00
|
|
|
int FOTAauth = 0;
|
2019-05-11 02:18:06 +00:00
|
|
|
|
|
|
|
#include <esp_int_wdt.h>
|
|
|
|
#include <esp_task_wdt.h>
|
|
|
|
|
|
|
|
void hard_restart() {
|
|
|
|
esp_task_wdt_init(1,true);
|
|
|
|
esp_task_wdt_add(NULL);
|
|
|
|
while(true);
|
|
|
|
}
|
2018-11-04 05:19:22 +00:00
|
|
|
|
2019-12-15 06:58:11 +00:00
|
|
|
void initFOTA(){
|
|
|
|
FOTA.setupAsync("");
|
|
|
|
}
|
|
|
|
|
2018-11-04 05:19:22 +00:00
|
|
|
void initOTA(){
|
2019-05-14 11:29:35 +00:00
|
|
|
ArduinoOTA.setHostname("AfterburnerOTA");
|
2018-11-04 05:19:22 +00:00
|
|
|
|
|
|
|
ArduinoOTA
|
|
|
|
.onStart([]() {
|
|
|
|
String type;
|
|
|
|
if (ArduinoOTA.getCommand() == U_FLASH)
|
|
|
|
type = "sketch";
|
|
|
|
else // U_SPIFFS
|
|
|
|
type = "filesystem";
|
|
|
|
|
|
|
|
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
2019-02-11 08:34:11 +00:00
|
|
|
SPIFFS.end();
|
2018-11-06 17:18:12 +00:00
|
|
|
DebugPort.println("Start updating " + type);
|
2018-12-13 08:42:35 +00:00
|
|
|
DebugPort.handle(); // keep telnet spy alive
|
2019-05-11 02:18:06 +00:00
|
|
|
ShowOTAScreen();
|
2019-05-20 12:09:59 +00:00
|
|
|
feedWatchdog(); // we get stuck here for a while, don't let the watchdog bite!
|
2018-11-04 05:19:22 +00:00
|
|
|
})
|
|
|
|
.onEnd([]() {
|
2018-11-06 17:18:12 +00:00
|
|
|
DebugPort.println("\nEnd");
|
2018-12-13 08:42:35 +00:00
|
|
|
DebugPort.handle(); // keep telnet spy alive
|
2019-07-25 11:27:57 +00:00
|
|
|
forceBootInit();
|
2019-05-11 02:18:06 +00:00
|
|
|
delay(100);
|
|
|
|
// DebugPort.end(); // force graceful close of telnetspy - ensures a client will reconnect cleanly
|
2018-11-04 05:19:22 +00:00
|
|
|
})
|
|
|
|
.onProgress([](unsigned int progress, unsigned int total) {
|
2019-07-06 13:46:20 +00:00
|
|
|
feedWatchdog();
|
2019-05-11 02:18:06 +00:00
|
|
|
int percent = (progress / (total / 100));
|
2019-07-06 13:46:20 +00:00
|
|
|
static int prevPC = 0;
|
|
|
|
if(percent != prevPC) {
|
|
|
|
prevPC = percent;
|
2020-04-20 03:39:39 +00:00
|
|
|
DebugPort.printf("OTA progress: %u%%\r\n", percent);
|
2019-07-06 13:46:20 +00:00
|
|
|
DebugPort.handle(); // keep telnet spy alive
|
|
|
|
ShowOTAScreen(percent);
|
|
|
|
}
|
2018-11-04 05:19:22 +00:00
|
|
|
})
|
|
|
|
.onError([](ota_error_t error) {
|
2018-11-06 17:18:12 +00:00
|
|
|
DebugPort.printf("Error[%u]: ", error);
|
|
|
|
if (error == OTA_AUTH_ERROR) DebugPort.println("Auth Failed");
|
|
|
|
else if (error == OTA_BEGIN_ERROR) DebugPort.println("Begin Failed");
|
|
|
|
else if (error == OTA_CONNECT_ERROR) DebugPort.println("Connect Failed");
|
|
|
|
else if (error == OTA_RECEIVE_ERROR) DebugPort.println("Receive Failed");
|
|
|
|
else if (error == OTA_END_ERROR) DebugPort.println("End Failed");
|
2019-05-11 02:18:06 +00:00
|
|
|
DebugPort.handle(); // keep telnet spy alive
|
2018-11-04 05:19:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
ArduinoOTA.begin();
|
2019-12-15 06:58:11 +00:00
|
|
|
|
2018-11-04 05:19:22 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 21:51:52 +00:00
|
|
|
void doOTA()
|
2019-06-04 20:15:12 +00:00
|
|
|
{
|
2018-11-04 05:19:22 +00:00
|
|
|
ArduinoOTA.handle();
|
2019-05-20 12:09:59 +00:00
|
|
|
|
|
|
|
// manage Firmware OTA
|
2019-05-29 08:01:51 +00:00
|
|
|
// this is where the controller contacts a web server to discover if new firmware is available
|
2019-06-02 20:34:45 +00:00
|
|
|
// if so, it can download and implant using OTA and become effective next reboot!
|
2019-05-20 12:09:59 +00:00
|
|
|
long tDelta = millis() - FOTAtime;
|
|
|
|
if(tDelta > 0) {
|
2020-01-04 07:13:40 +00:00
|
|
|
// FOTAtime = millis() + 6000; // 6 seconds
|
2019-06-06 01:32:43 +00:00
|
|
|
// FOTAtime = millis() + 60000; // 60 seconds
|
2019-06-16 10:37:25 +00:00
|
|
|
// FOTAtime = millis() + 600000; // 10 minutes
|
2020-01-04 07:13:40 +00:00
|
|
|
FOTAtime = millis() + 3600000; // 1 hour
|
|
|
|
#ifdef SYNCHRONOUS_FOTA
|
|
|
|
if ((WiFi.status() == WL_CONNECTED)) // bug workaround in FOTA where execHTTPcheck does not return false in this condition
|
|
|
|
{
|
|
|
|
#endif
|
2020-05-13 00:37:31 +00:00
|
|
|
FOTA.onProgress(NULL); // important - keeps watchdog fed
|
2019-09-08 00:14:36 +00:00
|
|
|
FOTA.onComplete(CheckFirmwareCRC); // upload complete, but not yet verified
|
2019-07-25 11:27:57 +00:00
|
|
|
FOTA.onSuccess(onSuccess);
|
2019-08-03 02:42:49 +00:00
|
|
|
#ifdef TESTFOTA
|
2020-03-25 09:28:12 +00:00
|
|
|
FOTA.setCheckURL("http://afterburner.mrjones.id.au/fota/fotatest.json");
|
2019-08-03 02:42:49 +00:00
|
|
|
#else
|
2020-03-25 09:28:12 +00:00
|
|
|
FOTA.setCheckURL("http://afterburner.mrjones.id.au/fota/fota.json");
|
2019-08-03 02:42:49 +00:00
|
|
|
#endif
|
2020-01-04 07:13:40 +00:00
|
|
|
|
|
|
|
#ifdef SYNCHRONOUS_FOTA
|
|
|
|
// Synchronous polling - very prone to flakey Internet, causing watchdog reboots
|
2019-05-29 08:01:51 +00:00
|
|
|
DebugPort.println("Checking for new firmware...");
|
|
|
|
if(FOTA.execHTTPcheck()) {
|
|
|
|
DebugPort.println("New firmware available on web server!");
|
|
|
|
if(FOTAauth == 2) { // user has authorised update (was == 1 before auth.)
|
2020-05-13 00:37:31 +00:00
|
|
|
FOTA.onProgress(onWebProgress); // important - keeps watchdog fed
|
2019-05-29 08:01:51 +00:00
|
|
|
FOTA.execOTA(); // go ahead and do the update, reading new file from web server
|
2020-05-13 00:37:31 +00:00
|
|
|
FOTA.onProgress(NULL); // avoid rogue web update pop ups during browser update!
|
2019-05-29 08:01:51 +00:00
|
|
|
FOTAauth = 0; // and we're done.
|
|
|
|
}
|
2020-05-13 00:37:31 +00:00
|
|
|
else {
|
2019-05-29 08:01:51 +00:00
|
|
|
FOTAauth = 1; // flag that new firmware is available
|
2020-05-13 00:37:31 +00:00
|
|
|
}
|
2019-05-20 12:09:59 +00:00
|
|
|
}
|
2019-06-06 01:32:43 +00:00
|
|
|
else {
|
|
|
|
FOTAauth = 0; // cancel
|
|
|
|
}
|
2020-01-04 07:13:40 +00:00
|
|
|
} // Wifi (STA) Connected
|
2020-05-13 00:37:31 +00:00
|
|
|
FOTA.onProgress(NULL);
|
2020-01-04 07:13:40 +00:00
|
|
|
|
2019-12-15 06:58:11 +00:00
|
|
|
#else
|
2020-01-04 07:13:40 +00:00
|
|
|
|
|
|
|
// Asynchronous polling - not prone to flakey Internet
|
|
|
|
FOTA.execAsyncHTTPcheck(); // version number is collected asynchronously, setting FOTAauth if newer
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
} // tDelta > 0
|
|
|
|
|
|
|
|
#ifndef SYNCHRONOUS_FOTA
|
|
|
|
// version number is collected asynchronously after initiating the update check
|
|
|
|
if(FOTA.getNewVersion()) {
|
|
|
|
if(FOTAauth == 2) { // user has authorised update (was == 1 before auth.)
|
2020-05-13 00:37:31 +00:00
|
|
|
FOTA.onProgress(onWebProgress); // important - keeps watchdog fed
|
2020-01-04 07:13:40 +00:00
|
|
|
FOTA.execOTA(); // go ahead and do the update, reading new file from web server
|
2020-05-13 00:37:31 +00:00
|
|
|
FOTA.onProgress(NULL); // avoid rogue web update pop ups during browser update!
|
2020-01-04 07:13:40 +00:00
|
|
|
FOTAauth = 0; // and we're done.
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
FOTAauth = 1; // flag that new firmware is available
|
|
|
|
}
|
2019-12-15 06:58:11 +00:00
|
|
|
}
|
|
|
|
#endif
|
2018-11-04 05:19:22 +00:00
|
|
|
};
|
2019-05-20 12:09:59 +00:00
|
|
|
|
2020-01-04 07:13:40 +00:00
|
|
|
|
2019-08-03 02:42:49 +00:00
|
|
|
int isUpdateAvailable(bool test)
|
2019-05-20 12:09:59 +00:00
|
|
|
{
|
2020-03-25 09:28:12 +00:00
|
|
|
FOTA.process(); // manage any queued responses
|
|
|
|
|
2019-05-20 12:09:59 +00:00
|
|
|
if(test) {
|
2020-01-04 07:13:40 +00:00
|
|
|
if(FOTAauth >= 1) {
|
2019-08-03 02:42:49 +00:00
|
|
|
return FOTA.getNewVersion();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
2019-05-20 12:09:59 +00:00
|
|
|
}
|
2020-01-04 07:13:40 +00:00
|
|
|
else { // used to initiate update
|
|
|
|
if(FOTAauth == 1)
|
|
|
|
FOTAauth = 2;
|
|
|
|
return FOTA.getNewVersion();
|
2019-05-20 12:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void checkFOTA()
|
|
|
|
{
|
|
|
|
FOTAtime = millis();
|
2019-07-09 12:19:21 +00:00
|
|
|
}
|
|
|
|
|
2020-01-04 07:13:40 +00:00
|
|
|
|
2019-07-09 12:19:21 +00:00
|
|
|
const int CRCbufsize = 1024;
|
|
|
|
uint8_t CRCReadBuff[CRCbufsize];
|
2019-07-30 20:27:04 +00:00
|
|
|
|
|
|
|
// CRC of everything, including our extra CRC bytes should return ZERO :-)
|
2019-09-08 00:14:36 +00:00
|
|
|
bool CheckFirmwareCRC(int filesize)
|
2019-07-30 20:27:04 +00:00
|
|
|
{
|
|
|
|
const esp_partition_t* pUsePartition = esp_ota_get_next_update_partition(NULL);
|
|
|
|
if(NULL == pUsePartition) {
|
|
|
|
DebugPort.println("CheckCRC: FAILED - bad partition?");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if((filesize & 0x3) != 2) { // mod 4 == 2?
|
|
|
|
// we expect 2 extra bytes where the custom CRC is added
|
|
|
|
// all normal applications without CRC are multiples of 4
|
|
|
|
DebugPort.println("CheckCRC: FAILED - bad source file size");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CModBusCRC16 CRCengine;
|
|
|
|
|
|
|
|
int processed = 0;
|
|
|
|
while(processed < filesize) {
|
|
|
|
int toRead = filesize - processed;
|
|
|
|
if(toRead > CRCbufsize)
|
|
|
|
toRead = CRCbufsize;
|
|
|
|
|
|
|
|
ESP.flashRead(pUsePartition->address + processed, (uint32_t*)CRCReadBuff, toRead);
|
|
|
|
CRCengine.process(toRead, CRCReadBuff);
|
|
|
|
processed += toRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool retval = CRCengine.get() == 0;
|
|
|
|
DebugPort.printf("CheckCRC: %s\r\n", retval ? "OK" : "FAILED");
|
|
|
|
|
|
|
|
return retval;
|
2019-07-25 11:27:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void onSuccess()
|
|
|
|
{
|
|
|
|
forceBootInit();
|
|
|
|
}
|
2019-08-03 02:42:49 +00:00
|
|
|
|
|
|
|
void onWebProgress(size_t progress, size_t total)
|
|
|
|
{
|
|
|
|
feedWatchdog();
|
|
|
|
int percent = (progress / (total / 100));
|
|
|
|
static int prevPC = 0;
|
|
|
|
if(percent != prevPC) {
|
|
|
|
prevPC = percent;
|
|
|
|
ShowOTAScreen(percent, eOTAWWW);
|
|
|
|
}
|
|
|
|
}
|