mirror of
https://github.com/0015/ThatProject.git
synced 2026-01-12 17:27:43 +03:00
OTA Solution - Build your own OTA platform (2/2, ESP32 OTA Application)
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
#include "ButtonCont.h"
|
||||
#define RIGHT_BUTTON_PIN 35
|
||||
|
||||
static ButtonCont* instance = NULL;
|
||||
|
||||
void released(Button2& btn) {
|
||||
instance->released_cb();
|
||||
}
|
||||
|
||||
ButtonCont::ButtonCont() {}
|
||||
|
||||
ButtonCont::ButtonCont(FuncPtrVoid f) {
|
||||
instance = this;
|
||||
button = new Button2(RIGHT_BUTTON_PIN);
|
||||
button->setReleasedHandler(released);
|
||||
released_cb = f;
|
||||
}
|
||||
|
||||
ButtonCont::~ButtonCont() {
|
||||
delete button;
|
||||
}
|
||||
|
||||
void ButtonCont::loop() {
|
||||
button->loop();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "Button2.h"
|
||||
class ButtonCont {
|
||||
private:
|
||||
Button2* button;
|
||||
friend void receiveCallback(Button2& btn);
|
||||
typedef void (*FuncPtrVoid)(void);
|
||||
|
||||
public:
|
||||
FuncPtrVoid released_cb;
|
||||
ButtonCont(FuncPtrVoid f);
|
||||
ButtonCont();
|
||||
~ButtonCont();
|
||||
|
||||
void loop();
|
||||
void eventTrigger();
|
||||
};
|
||||
138
ESP32_OTA/Simple_OTA_Solution/Simple_OTA_Application/Display.cpp
Normal file
138
ESP32_OTA/Simple_OTA_Solution/Simple_OTA_Application/Display.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "Display.h"
|
||||
|
||||
Display::Display() {
|
||||
tft = new TFT_eSPI();
|
||||
}
|
||||
|
||||
Display::~Display() {
|
||||
delete tft;
|
||||
}
|
||||
|
||||
void Display::initTFT() {
|
||||
tft->init();
|
||||
tft->fillScreen(TFT_BLACK);
|
||||
tft->setRotation(1);
|
||||
tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft->setTextDatum(MC_DATUM);
|
||||
tft->setFreeFont(&Orbitron_Light_24);
|
||||
tft->drawString("Waiting for WIFI-", tft->width() / 2, 50);
|
||||
}
|
||||
|
||||
void Display::fillBlackScreen() {
|
||||
tft->fillScreen(TFT_BLACK);
|
||||
}
|
||||
|
||||
void Display::showVersion(int buildNum) {
|
||||
String text = "Build#: ";
|
||||
text += buildNum;
|
||||
tft->drawString(text, tft->width() / 2, 80);
|
||||
}
|
||||
|
||||
void Display::timeUpdate(String date, String time) {
|
||||
tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft->setTextDatum(MC_DATUM);
|
||||
//tft->setFreeFont(&Orbitron_Light_24);
|
||||
tft->setFreeFont(&Satisfy_24);
|
||||
tft->setTextPadding(0);
|
||||
tft->drawString(date, tft->width() / 2, 50);
|
||||
tft->setTextPadding(tft->width() - 20);
|
||||
tft->drawString(time, tft->width() / 2, 80);
|
||||
}
|
||||
|
||||
void Display::showVersionBelow(int buildNum) {
|
||||
tft->setFreeFont(&FreeSansBold9pt7b);
|
||||
tft->setTextColor(TFT_YELLOW, TFT_BLACK);
|
||||
tft->setTextDatum(BL_DATUM);
|
||||
String text = "Current Build#: ";
|
||||
text += buildNum;
|
||||
tft->drawString(text, 0, tft->height());
|
||||
}
|
||||
|
||||
void Display::newMessage(String msg) {
|
||||
tft->setFreeFont(&FreeSansBold9pt7b);
|
||||
tft->setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
tft->setTextDatum(TR_DATUM);
|
||||
tft->drawString(msg, tft->width(), 0);
|
||||
}
|
||||
|
||||
void Display::downloadScreen(int percent) {
|
||||
if (percent == 0) {
|
||||
this->fillBlackScreen();
|
||||
tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft->setFreeFont(&Orbitron_Light_24);
|
||||
tft->setTextDatum(MC_DATUM);
|
||||
tft->drawString("Downloading...", tft->width() / 2, 50);
|
||||
}
|
||||
|
||||
this->drawProgressBar(20, tft->height() / 2 + 20, tft->width() - 40, 20, percent, TFT_RED, TFT_YELLOW);
|
||||
}
|
||||
|
||||
void Display::drawProgressBar(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t percent, uint16_t frameColor, uint16_t barColor) {
|
||||
if (percent == 0) {
|
||||
tft->fillRoundRect(x, y, w, h, 3, TFT_BLACK);
|
||||
}
|
||||
uint8_t margin = 2;
|
||||
uint16_t barHeight = h - 2 * margin;
|
||||
uint16_t barWidth = w - 2 * margin;
|
||||
tft->drawRoundRect(x, y, w, h, 3, frameColor);
|
||||
tft->fillRect(x + margin, y + margin, barWidth * percent / 100.0, barHeight, barColor);
|
||||
}
|
||||
|
||||
void Display::downloadFailure(String cpName) {
|
||||
this->fillBlackScreen();
|
||||
tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft->setFreeFont(&Orbitron_Light_24);
|
||||
tft->setTextDatum(MC_DATUM);
|
||||
tft->drawString("MD5 checksum", tft->width() / 2, 10);
|
||||
tft->drawString("Wrong!", tft->width() / 2, 40);
|
||||
tft->drawString("Contact to ", tft->width() / 2, 70);
|
||||
tft->drawString(cpName, tft->width() / 2, 100);
|
||||
}
|
||||
|
||||
void Display::downloadSuccess() {
|
||||
this->fillBlackScreen();
|
||||
tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft->setFreeFont(&Orbitron_Light_24);
|
||||
tft->setTextDatum(MC_DATUM);
|
||||
tft->drawString("MD5 checksum", tft->width() / 2, 10);
|
||||
tft->drawString("Correct!", tft->width() / 2, 40);
|
||||
tft->drawString("Countdown", tft->width() / 2, 70);
|
||||
|
||||
tft->setTextColor(TFT_RED, TFT_BLACK);
|
||||
tft->setTextPadding(tft->width() - 20);
|
||||
for (int i = 3; i > -1; --i) {
|
||||
tft->drawString(String(i), tft->width() / 2, 100);
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void Display::firmwareScreen(bool isStart, bool isDone) {
|
||||
this->fillBlackScreen();
|
||||
tft->setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft->setFreeFont(&Orbitron_Light_24);
|
||||
tft->setTextDatum(MC_DATUM);
|
||||
|
||||
if (isStart) {
|
||||
tft->drawString("Updating", tft->width() / 2, 40);
|
||||
tft->drawString("Firmware!", tft->width() / 2, 70);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDone) {
|
||||
tft->drawString("OTA Done!", tft->width() / 2, 10);
|
||||
tft->drawString("Rebooting", tft->width() / 2, 40);
|
||||
tft->drawString("Countdown", tft->width() / 2, 70);
|
||||
|
||||
} else {
|
||||
tft->drawString("Sorry.", tft->width() / 2, 10);
|
||||
tft->drawString("OTA Failure!", tft->width() / 2, 40);
|
||||
tft->drawString("Try it again.", tft->width() / 2, 70);
|
||||
}
|
||||
|
||||
tft->setTextColor(TFT_RED, TFT_BLACK);
|
||||
tft->setTextPadding(tft->width() - 20);
|
||||
for (int i = 3; i > -1; --i) {
|
||||
tft->drawString(String(i), tft->width() / 2, 100);
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
class Display {
|
||||
private:
|
||||
TFT_eSPI* tft;
|
||||
void drawProgressBar(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t percent, uint16_t frameColor, uint16_t barColor);
|
||||
|
||||
public:
|
||||
Display();
|
||||
~Display();
|
||||
void initTFT();
|
||||
void showVersion(int buildNum);
|
||||
void showVersionBelow(int buildNum);
|
||||
void fillBlackScreen();
|
||||
void timeUpdate(String date, String time);
|
||||
void newMessage(String msg);
|
||||
void downloadScreen(int percent);
|
||||
void downloadFailure(String cpName);
|
||||
void downloadSuccess();
|
||||
void firmwareScreen(bool isStart, bool isDone);
|
||||
};
|
||||
117
ESP32_OTA/Simple_OTA_Solution/Simple_OTA_Application/FileIO.cpp
Normal file
117
ESP32_OTA/Simple_OTA_Solution/Simple_OTA_Application/FileIO.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "FileIO.h"
|
||||
|
||||
FileIO::FileIO() {
|
||||
if (!SPIFFS.begin()) {
|
||||
//if (!SPIFFS.begin(true)) {
|
||||
Serial.println("SPIFFS initialisation failed!");
|
||||
while (1) yield();
|
||||
}
|
||||
}
|
||||
|
||||
void FileIO::format() {
|
||||
Serial.println("SPIFFS Format!");
|
||||
SPIFFS.format();
|
||||
}
|
||||
|
||||
void FileIO::listSPIFFS() {
|
||||
Serial.println(F("\r\nListing SPIFFS files:"));
|
||||
static const char line[] PROGMEM = "=================================================";
|
||||
|
||||
Serial.println(FPSTR(line));
|
||||
Serial.println(F(" File name Size"));
|
||||
Serial.println(FPSTR(line));
|
||||
|
||||
fs::File root = SPIFFS.open("/");
|
||||
if (!root) {
|
||||
Serial.println(F("Failed to open directory"));
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
Serial.println(F("Not a directory"));
|
||||
return;
|
||||
}
|
||||
|
||||
fs::File file = root.openNextFile();
|
||||
while (file) {
|
||||
|
||||
if (file.isDirectory()) {
|
||||
Serial.print("DIR : ");
|
||||
String fileName = file.name();
|
||||
Serial.print(fileName);
|
||||
} else {
|
||||
String fileName = file.name();
|
||||
Serial.print(" " + fileName);
|
||||
// File path can be 31 characters maximum in SPIFFS
|
||||
int spaces = 33 - fileName.length(); // Tabulate nicely
|
||||
if (spaces < 1) spaces = 1;
|
||||
while (spaces--) Serial.print(" ");
|
||||
String fileSize = (String)file.size();
|
||||
spaces = 10 - fileSize.length(); // Tabulate nicely
|
||||
if (spaces < 1) spaces = 1;
|
||||
while (spaces--) Serial.print(" ");
|
||||
Serial.println(fileSize + " bytes");
|
||||
}
|
||||
|
||||
file = root.openNextFile();
|
||||
}
|
||||
|
||||
Serial.println(FPSTR(line));
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
fs::File FileIO::openFile(String fileName, bool isReadOnly) {
|
||||
if (isReadOnly) {
|
||||
return SPIFFS.open(fileName, "r");
|
||||
} else {
|
||||
this->removeFile(fileName);
|
||||
return SPIFFS.open(fileName, "w");
|
||||
}
|
||||
}
|
||||
|
||||
void FileIO::closeFile(fs::File file) {
|
||||
file.close();
|
||||
}
|
||||
|
||||
void FileIO::removeFile(String fileName) {
|
||||
if (SPIFFS.exists(fileName)) {
|
||||
SPIFFS.remove(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
int FileIO::getFileSize(String fileName) {
|
||||
fs::File file = SPIFFS.open(fileName, "r");
|
||||
|
||||
if (!file) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fileSize = file.size();
|
||||
file.close();
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
void FileIO::mdContextInit() {
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_MD5;
|
||||
mbedtls_md_init(&ctx);
|
||||
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0);
|
||||
mbedtls_md_starts(&ctx);
|
||||
}
|
||||
|
||||
void FileIO::mdContextUpdate(const unsigned char* buff, int c) {
|
||||
mbedtls_md_update(&ctx, buff, c);
|
||||
}
|
||||
|
||||
String FileIO::md5Result() {
|
||||
byte md5Result[16];
|
||||
mbedtls_md_finish(&ctx, md5Result);
|
||||
mbedtls_md_free(&ctx);
|
||||
|
||||
String checksum = "";
|
||||
for (int i = 0; i < sizeof(md5Result); i++) {
|
||||
char str[3];
|
||||
|
||||
sprintf(str, "%02x", (int)md5Result[i]);
|
||||
checksum += str;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef FileIO_H_
|
||||
#define FileIO_H_
|
||||
|
||||
#include "SPIFFS.h"
|
||||
#include "mbedtls/md.h"
|
||||
class FileIO {
|
||||
private:
|
||||
mbedtls_md_context_t ctx;
|
||||
|
||||
public:
|
||||
static constexpr const char* TEMP_BIN_FILE = "/download_firmware.bin";
|
||||
FileIO();
|
||||
void format();
|
||||
void listSPIFFS();
|
||||
fs::File openFile(String fileName, bool isReadOnly);
|
||||
void closeFile(fs::File file);
|
||||
void removeFile(String fileName);
|
||||
int getFileSize(String fileName);
|
||||
void mdContextInit();
|
||||
void mdContextUpdate(const unsigned char* buff, int c);
|
||||
String md5Result();
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef MyFirmware_H_
|
||||
#define MyFirmware_H_
|
||||
#include <Arduino.h>
|
||||
|
||||
struct firmware_t {
|
||||
String company;
|
||||
int build_num;
|
||||
String build_date;
|
||||
String server_file_path;
|
||||
int file_size;
|
||||
String md5_checksum;
|
||||
};
|
||||
|
||||
typedef struct firmware_t Firmware;
|
||||
|
||||
#endif
|
||||
168
ESP32_OTA/Simple_OTA_Solution/Simple_OTA_Application/Network.cpp
Normal file
168
ESP32_OTA/Simple_OTA_Solution/Simple_OTA_Application/Network.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "Network.h"
|
||||
#include <WiFi.h>
|
||||
#define WIFI_SSID "ThatProject"
|
||||
#define WIFI_PASS "California"
|
||||
#define BASE_URL "http://192.168.0.2:9001/api"
|
||||
|
||||
const String API_KEY = "THIS_IS_MY_OWN_API_KEY";
|
||||
|
||||
Network::Network() {
|
||||
localServerTime = 0;
|
||||
}
|
||||
|
||||
void Network::WiFiBegin() {
|
||||
WiFi.disconnect();
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(1000);
|
||||
Serial.println("Connecting to WiFi..");
|
||||
}
|
||||
|
||||
Serial.println("Connected to the WiFi network");
|
||||
}
|
||||
|
||||
void Network::fetchLocalServerTime() {
|
||||
if ((WiFi.status() == WL_CONNECTED)) {
|
||||
String targetURL = BASE_URL;
|
||||
targetURL += "/get/time";
|
||||
|
||||
http.begin(targetURL);
|
||||
|
||||
if (http.GET() == HTTP_CODE_OK) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
if (error) {
|
||||
Serial.print(F("deserializeJson() failed: "));
|
||||
Serial.println(error.f_str());
|
||||
return;
|
||||
}
|
||||
|
||||
String time = doc["timestamp"];
|
||||
Serial.println(time);
|
||||
Serial.println(time.toInt());
|
||||
localServerTime = time.toInt();
|
||||
|
||||
} else {
|
||||
Serial.println("Error on HTTP request");
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
}
|
||||
|
||||
long Network::getLocalServerTime() {
|
||||
return localServerTime;
|
||||
}
|
||||
|
||||
Firmware Network::checkVersion() {
|
||||
|
||||
Firmware firmware;
|
||||
firmware.build_num = -1;
|
||||
|
||||
if ((WiFi.status() == WL_CONNECTED)) {
|
||||
|
||||
String targetURL = BASE_URL;
|
||||
targetURL += "/get/version";
|
||||
http.begin(targetURL);
|
||||
|
||||
if (http.GET() == HTTP_CODE_OK) {
|
||||
String payload = http.getString();
|
||||
Serial.println(payload);
|
||||
|
||||
DeserializationError error = deserializeJson(doc, payload);
|
||||
if (error) {
|
||||
Serial.print(F("deserializeJson() failed: "));
|
||||
Serial.println(error.f_str());
|
||||
return firmware;
|
||||
}
|
||||
|
||||
firmware.company = doc["companyName"].as<String>();
|
||||
firmware.build_num = doc["buildNum"];
|
||||
firmware.build_date = doc["buildDate"].as<String>();
|
||||
firmware.server_file_path = doc["serverFilePath"].as<String>();
|
||||
firmware.file_size = doc["fileSize"];
|
||||
firmware.md5_checksum = doc["md5Checksum"].as<String>();
|
||||
|
||||
} else {
|
||||
Serial.println("Error on HTTP request");
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
return firmware;
|
||||
}
|
||||
|
||||
String Network::fileDownload(FuncPtrInt callback, FileIO** fileIO, String target_path) {
|
||||
|
||||
String md5CheckSum = "wrong";
|
||||
|
||||
if ((WiFi.status() == WL_CONNECTED)) {
|
||||
fs::File file = (*fileIO)->openFile(FileIO::TEMP_BIN_FILE, false);
|
||||
|
||||
String targetURL = BASE_URL;
|
||||
targetURL += "/post/update";
|
||||
|
||||
http.begin(targetURL);
|
||||
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
String httpRequestData = "api_key=" + API_KEY + "&target_path=" + target_path;
|
||||
|
||||
if (file && http.POST(httpRequestData) == HTTP_CODE_OK) {
|
||||
callback(0);
|
||||
(*fileIO)->mdContextInit();
|
||||
|
||||
int fileSize = http.getSize();
|
||||
Serial.print("File Length: ");
|
||||
Serial.println(fileSize);
|
||||
|
||||
int unDownloadSize = fileSize;
|
||||
int downloadSize = 0;
|
||||
int preDownloadedPercent = 0;
|
||||
|
||||
uint8_t buff[128] = { 0 };
|
||||
|
||||
WiFiClient* stream = http.getStreamPtr();
|
||||
while (http.connected() && (fileSize > 0 || fileSize == -1)) {
|
||||
size_t size = stream->available();
|
||||
if (size) {
|
||||
int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size));
|
||||
file.write(buff, c);
|
||||
(*fileIO)->mdContextUpdate(buff, c);
|
||||
|
||||
if (fileSize > 0) fileSize -= c;
|
||||
|
||||
downloadSize += c;
|
||||
int downloadedPercent = int((downloadSize * 100 / unDownloadSize));
|
||||
|
||||
if (preDownloadedPercent != downloadedPercent) {
|
||||
callback(downloadedPercent);
|
||||
preDownloadedPercent = downloadedPercent;
|
||||
}
|
||||
}
|
||||
|
||||
delay(1);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.print("[HTTP] connection closed or file end.\n");
|
||||
|
||||
|
||||
(*fileIO)->closeFile(file);
|
||||
(*fileIO)->listSPIFFS();
|
||||
Serial.println("======MD5 CHECKSUM");
|
||||
md5CheckSum = (*fileIO)->md5Result();
|
||||
Serial.println(md5CheckSum);
|
||||
|
||||
|
||||
} else {
|
||||
Serial.println("Error on HTTP request");
|
||||
}
|
||||
|
||||
http.end();
|
||||
}
|
||||
|
||||
return md5CheckSum;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <HTTPClient.h>
|
||||
#include "FileIO.h"
|
||||
#include "MyFirmware.h"
|
||||
|
||||
class Network {
|
||||
private:
|
||||
HTTPClient http;
|
||||
StaticJsonDocument<300> doc;
|
||||
long localServerTime;
|
||||
typedef void (*FuncPtrInt)(int);
|
||||
|
||||
|
||||
public:
|
||||
Network();
|
||||
void WiFiBegin();
|
||||
void fetchLocalServerTime();
|
||||
long getLocalServerTime();
|
||||
Firmware checkVersion();
|
||||
String fileDownload(FuncPtrInt callback, FileIO** fileIO, String target_path);
|
||||
};
|
||||
@@ -0,0 +1,174 @@
|
||||
#include <Arduino.h>
|
||||
#include "SimpleOTA.h"
|
||||
|
||||
static SimpleOTA *instance = NULL;
|
||||
|
||||
SimpleOTA::SimpleOTA() {
|
||||
t1 = 0;
|
||||
t2 = 0;
|
||||
currentState = NONE;
|
||||
instance = this;
|
||||
}
|
||||
|
||||
SimpleOTA::~SimpleOTA() {
|
||||
}
|
||||
|
||||
void buttonCallbackEvent() {
|
||||
Serial.println("buttonCallbackEvent");
|
||||
if (instance->currentState == SERVER_FOUND && instance->version->hasNewUpdate()) {
|
||||
instance->startDownload();
|
||||
}
|
||||
}
|
||||
|
||||
void networkDownloadEvent(int percent) {
|
||||
Serial.print("networkDownloadEvent downloadPercent: ");
|
||||
if (percent == 0) {
|
||||
instance->currentState = FIRMWARE_DOWNLOAD_START;
|
||||
}
|
||||
Serial.println(percent);
|
||||
instance->display->downloadScreen(percent);
|
||||
}
|
||||
|
||||
void SimpleOTA::begin() {
|
||||
this->initDisplay();
|
||||
this->initVersion();
|
||||
this->initNetwork();
|
||||
this->initFileIO();
|
||||
this->initButton();
|
||||
}
|
||||
|
||||
void SimpleOTA::loop() {
|
||||
|
||||
switch (currentState) {
|
||||
case NETWORK_CONNECTED:
|
||||
|
||||
if (millis() - t1 >= 1000) {
|
||||
t1 = millis();
|
||||
this->requestLocalServerTime();
|
||||
}
|
||||
break;
|
||||
|
||||
case SERVER_FOUND:
|
||||
if (millis() - t1 >= 1000) {
|
||||
t1 = millis();
|
||||
this->updateTime();
|
||||
display->showVersionBelow(version->getCurrentVersion());
|
||||
}
|
||||
|
||||
if (millis() - t2 >= 1000 * 10) {
|
||||
t2 = millis();
|
||||
this->serverFirmwareCheck();
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
buttonCont->loop();
|
||||
}
|
||||
|
||||
void SimpleOTA::initDisplay() {
|
||||
Serial.println("initDisplay");
|
||||
display = new Display();
|
||||
display->initTFT();
|
||||
}
|
||||
|
||||
void SimpleOTA::initVersion() {
|
||||
Serial.println("initVersion");
|
||||
version = new VersionCont();
|
||||
display->showVersion(version->getCurrentVersion());
|
||||
}
|
||||
|
||||
void SimpleOTA::initNetwork() {
|
||||
Serial.println("initNetwork");
|
||||
network = new Network();
|
||||
currentState = NETWORK_BEGIN;
|
||||
network->WiFiBegin();
|
||||
currentState = NETWORK_CONNECTED;
|
||||
this->requestLocalServerTime();
|
||||
}
|
||||
|
||||
void SimpleOTA::initFileIO() {
|
||||
Serial.println("initFileIO");
|
||||
fileIO = new FileIO();
|
||||
//fileIO->format();
|
||||
fileIO->listSPIFFS();
|
||||
}
|
||||
|
||||
void SimpleOTA::initButton() {
|
||||
Serial.println("initButton");
|
||||
|
||||
void (*ptr)(void) = &buttonCallbackEvent;
|
||||
buttonCont = new ButtonCont(ptr);
|
||||
}
|
||||
|
||||
void SimpleOTA::requestLocalServerTime() {
|
||||
network->fetchLocalServerTime();
|
||||
|
||||
if (network->getLocalServerTime() != 0) {
|
||||
rtc.setTime(network->getLocalServerTime());
|
||||
display->fillBlackScreen();
|
||||
currentState = SERVER_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleOTA::updateTime() {
|
||||
struct tm timeinfo = rtc.getTimeStruct();
|
||||
display->timeUpdate(rtc.getDate(), rtc.getTime());
|
||||
}
|
||||
|
||||
void SimpleOTA::serverFirmwareCheck() {
|
||||
version->setNewFirmware(network->checkVersion());
|
||||
if (version->newFirmwareVersion() == -1) {
|
||||
display->newMessage("Server Not Responding");
|
||||
} else {
|
||||
if (version->hasNewUpdate()) {
|
||||
display->newMessage("New Build Available! ->");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleOTA::startDownload() {
|
||||
|
||||
void (*ptr)(int) = &networkDownloadEvent;
|
||||
|
||||
bool compareMD5Checksum = version->md5CompareTo(network->fileDownload(ptr, &fileIO, version->getFirmwareServerPath()));
|
||||
bool compareFileSize = version->fileSizeCompareTo(fileIO->getFileSize(FileIO::TEMP_BIN_FILE));
|
||||
|
||||
Serial.println("======compareMD5Checksum");
|
||||
Serial.println(compareMD5Checksum);
|
||||
Serial.println("======Downloaded File SIze");
|
||||
Serial.println(compareFileSize);
|
||||
|
||||
if (compareMD5Checksum && compareFileSize) {
|
||||
display->downloadSuccess();
|
||||
this->updateFirmware();
|
||||
} else {
|
||||
display->downloadFailure(version->getCPName());
|
||||
delay(5000);
|
||||
display->fillBlackScreen();
|
||||
currentState = SERVER_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleOTA::updateFirmware() {
|
||||
currentState = FIRMWARE_DOWNLOAD_START;
|
||||
display->firmwareScreen(true, false);
|
||||
Updater *updater = new Updater();
|
||||
if (updater->updateFromFS(&fileIO)) {
|
||||
Serial.println("UPDATE DONE");
|
||||
|
||||
currentState = FIRMWARE_UPDATED;
|
||||
version->saveVersion(version->newFirmwareVersion());
|
||||
display->firmwareScreen(false, true);
|
||||
|
||||
ESP.restart();
|
||||
} else {
|
||||
Serial.println("UPDATE FAILURE");
|
||||
display->firmwareScreen(false, false);
|
||||
delay(3000);
|
||||
display->fillBlackScreen();
|
||||
currentState = SERVER_FOUND;
|
||||
}
|
||||
delete updater;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "Display.h"
|
||||
#include "VersionCont.h"
|
||||
#include "Network.h"
|
||||
#include "ButtonCont.h"
|
||||
#include "Updater.h"
|
||||
#include <ESP32Time.h>
|
||||
|
||||
typedef enum {
|
||||
NONE,
|
||||
NETWORK_BEGIN,
|
||||
NETWORK_CONNECTED,
|
||||
SERVER_FOUND,
|
||||
FIRMWARE_DOWNLOAD_START,
|
||||
FIRMWARE_UPDATED
|
||||
} SimpleOTA_State_t;
|
||||
|
||||
class SimpleOTA {
|
||||
private:
|
||||
Display *display;
|
||||
VersionCont *version;
|
||||
Network *network;
|
||||
FileIO *fileIO;
|
||||
ButtonCont *buttonCont;
|
||||
ESP32Time rtc;
|
||||
|
||||
long t1, t2;
|
||||
|
||||
void initDisplay();
|
||||
void initVersion();
|
||||
void initNetwork();
|
||||
void initFileIO();
|
||||
void initButton();
|
||||
void requestLocalServerTime();
|
||||
void updateTime();
|
||||
void serverFirmwareCheck();
|
||||
void startDownload();
|
||||
void updateFirmware();
|
||||
friend void buttonCallbackEvent();
|
||||
friend void networkDownloadEvent(int percent);
|
||||
|
||||
SimpleOTA_State_t currentState;
|
||||
|
||||
public:
|
||||
SimpleOTA();
|
||||
~SimpleOTA();
|
||||
void begin();
|
||||
void loop();
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "SimpleOTA.h"
|
||||
|
||||
SimpleOTA *simpleOTA;
|
||||
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
Serial.begin(115200);
|
||||
simpleOTA = new SimpleOTA();
|
||||
simpleOTA->begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
simpleOTA->loop();
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "Updater.h"
|
||||
|
||||
void Updater::performUpdate(Stream& updateSource, size_t updateSize) {
|
||||
if (Update.begin(updateSize)) {
|
||||
size_t written = Update.writeStream(updateSource);
|
||||
if (written == updateSize) {
|
||||
Serial.println("Written : " + String(written) + " successfully");
|
||||
} else {
|
||||
Serial.println("Written only : " + String(written) + "/" + String(updateSize) + ". Retry?");
|
||||
}
|
||||
if (Update.end()) {
|
||||
Serial.println("OTA done!");
|
||||
if (Update.isFinished()) {
|
||||
Serial.println("Update successfully completed. Rebooting.");
|
||||
} else {
|
||||
Serial.println("Update not finished? Something went wrong!");
|
||||
}
|
||||
} else {
|
||||
Serial.println("Error Occurred. Error #: " + String(Update.getError()));
|
||||
}
|
||||
|
||||
} else {
|
||||
Serial.println("Not enough space to begin OTA");
|
||||
}
|
||||
}
|
||||
|
||||
// check given FS for valid update.bin and perform update if available
|
||||
bool Updater::updateFromFS(FileIO** fileIO) {
|
||||
|
||||
fs::File updateBin = (*fileIO)->openFile(FileIO::TEMP_BIN_FILE, true);
|
||||
|
||||
if (updateBin) {
|
||||
if (updateBin.isDirectory()) {
|
||||
Serial.println("Error, update.bin is not a file");
|
||||
return false;
|
||||
}
|
||||
bool updateResult = false;
|
||||
size_t updateSize = updateBin.size();
|
||||
|
||||
if (updateSize > 0) {
|
||||
Serial.println("Try to start update");
|
||||
this->performUpdate(updateBin, updateSize);
|
||||
updateResult = true;
|
||||
} else {
|
||||
Serial.println("Error, file is empty");
|
||||
updateResult = false;
|
||||
}
|
||||
|
||||
(*fileIO)->closeFile(updateBin);
|
||||
(*fileIO)->removeFile(FileIO::TEMP_BIN_FILE);
|
||||
return updateResult;
|
||||
} else {
|
||||
Serial.println("Could not load update.bin from SPIFFS");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "FileIO.h"
|
||||
#include <Update.h>
|
||||
|
||||
class Updater {
|
||||
private:
|
||||
void performUpdate(Stream& updateSource, size_t updateSize);
|
||||
|
||||
public:
|
||||
bool updateFromFS(FileIO** fileIO);
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "VersionCont.h"
|
||||
#include <EEPROM.h>
|
||||
#define EEPROM_SIZE 1
|
||||
|
||||
VersionCont::VersionCont() {
|
||||
this->loadVersion();
|
||||
hasNewFirmware = false;
|
||||
}
|
||||
|
||||
int VersionCont::getCurrentVersion() {
|
||||
return firmwareVersion;
|
||||
}
|
||||
|
||||
void VersionCont::loadVersion() {
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
firmwareVersion = EEPROM.read(0);
|
||||
|
||||
if (firmwareVersion >= 255 || firmwareVersion == 0) {
|
||||
firmwareVersion = 1;
|
||||
this->saveVersion(firmwareVersion);
|
||||
}
|
||||
}
|
||||
|
||||
void VersionCont::saveVersion(int buildNum) {
|
||||
if (buildNum > 255) buildNum = 255;
|
||||
|
||||
EEPROM.write(0, buildNum);
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
void VersionCont::setNewFirmware(Firmware firmware) {
|
||||
newFirmware = firmware;
|
||||
|
||||
if (newFirmware.build_num != -1) {
|
||||
Serial.printf("buildNum: %d\n", newFirmware.build_num);
|
||||
Serial.printf("fileSize: %d\n", newFirmware.file_size);
|
||||
Serial.printf("MD5Checksum: %s\n", newFirmware.md5_checksum.c_str());
|
||||
Serial.printf("build_date: %s\n", newFirmware.build_date.c_str());
|
||||
Serial.printf("company: %s\n", newFirmware.company.c_str());
|
||||
Serial.printf("server_file_path: %s\n", newFirmware.server_file_path.c_str());
|
||||
|
||||
if (firmwareVersion < newFirmware.build_num) {
|
||||
hasNewFirmware = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool VersionCont::hasNewUpdate() {
|
||||
return hasNewFirmware;
|
||||
}
|
||||
|
||||
bool VersionCont::md5CompareTo(String checksum) {
|
||||
return newFirmware.md5_checksum == checksum;
|
||||
}
|
||||
|
||||
bool VersionCont::fileSizeCompareTo(int fileSize) {
|
||||
return newFirmware.file_size == fileSize;
|
||||
}
|
||||
|
||||
int VersionCont::newFirmwareVersion() {
|
||||
return newFirmware.build_num;
|
||||
}
|
||||
|
||||
String VersionCont::getCPName() {
|
||||
return newFirmware.company != NULL ? newFirmware.company : "";
|
||||
}
|
||||
|
||||
String VersionCont::getFirmwareServerPath() {
|
||||
return newFirmware.server_file_path != NULL ? newFirmware.server_file_path : "";
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
#include "MyFirmware.h"
|
||||
class VersionCont {
|
||||
private:
|
||||
int firmwareVersion;
|
||||
void loadVersion();
|
||||
Firmware newFirmware;
|
||||
bool hasNewFirmware;
|
||||
|
||||
public:
|
||||
VersionCont();
|
||||
int getCurrentVersion();
|
||||
void saveVersion(int buildNum);
|
||||
void setNewFirmware(Firmware firmware);
|
||||
bool hasNewUpdate();
|
||||
bool md5CompareTo(String checksum);
|
||||
bool fileSizeCompareTo(int fileSize);
|
||||
int newFirmwareVersion();
|
||||
String getCPName();
|
||||
String getFirmwareServerPath();
|
||||
};
|
||||
Reference in New Issue
Block a user