Back to Pretty OTA
This commit is contained in:
@@ -122,7 +122,7 @@ body {
|
||||
/* Batterie-Banner Styling */
|
||||
.battery-banner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
top: -100px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
|
||||
@@ -133,12 +133,13 @@ body {
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||
border-bottom: 3px solid rgba(255, 255, 255, 0.2);
|
||||
animation: batteryPulse 2s infinite;
|
||||
transform: translateY(-100%);
|
||||
transition: transform 0.5s ease;
|
||||
transition: top 0.5s ease;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.battery-banner.show {
|
||||
transform: translateY(0);
|
||||
top: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.battery-banner .banner-content {
|
||||
|
||||
1
lib/PrettyOTA/.piopm
Normal file
1
lib/PrettyOTA/.piopm
Normal file
@@ -0,0 +1 @@
|
||||
{"type": "library", "name": "PrettyOTA", "version": "1.1.3", "spec": {"owner": "lostincompilation", "id": 18541, "name": "PrettyOTA", "requirements": null, "uri": null}}
|
||||
90
lib/PrettyOTA/CHANGELOG.md
Normal file
90
lib/PrettyOTA/CHANGELOG.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Changelog
|
||||
|
||||
## Upcoming (unreleased, work in progress for next version)
|
||||
|
||||
- (WIP) Added FirmwarePull function to PrettyOTA. See README for details
|
||||
- Reworked license for clarity
|
||||
|
||||
#### Website
|
||||
- (Changed JS codestyle to match C++ codestyle)
|
||||
- Optimized CSS code
|
||||
- New success checkmark symbol with animation and reduced flash usage
|
||||
- Added pointer cursor when hovering OTA-Mode drop down menu
|
||||
- Footer has glassomorphic design now (glossy transparency)
|
||||
- Small UI improvements for an even prettier look
|
||||
- Small renamings in code
|
||||
|
||||
## Version: 1.1.3
|
||||
|
||||
- ⚠️ Deprecated `OverwriteAppVersion()` and `OverwriteAppBuildTimeAndDate()`. Use `SetAppVersion()` and `SetAppBuildTimeAndDate()` instead. The deprecated functions will be removed in a future release
|
||||
- Added `IsUpdateRunning()` method
|
||||
- Removed duplicate strings in default callbacks
|
||||
- Changed default hardwareID to `ARDUINO_BOARD` symbol
|
||||
- Added compile switch to disable ArduinoOTA functionality: `#define PRETTY_OTA_ENABLE_ARDUINO_OTA 1`. Set it to zero to disable ArduinoOTA and only use the web interface
|
||||
- Added a new python script for compressing the HTML file and convert it to the C++ array. See the new `website_compressor` directory
|
||||
|
||||
## Version: 1.1.2
|
||||
|
||||
- Removed printing of ANSI escape codes in log functions
|
||||
- Reduced used stack size by 1KB
|
||||
|
||||
## Version: 1.1.1
|
||||
|
||||
- Fixed filesystem update issue (partition not found error)
|
||||
|
||||
## Version: 1.1.0
|
||||
|
||||
- Added HardwareID which can be set using `SetHardwareID(const char* const)`
|
||||
- Truncate too long values with ellipsis
|
||||
- Showing full (possibly truncated) text on hover for each entry. If for example the HardwareID would be too long to display and gets truncated, you can see the full value when hovering over it
|
||||
- Added parameter `printWithColor` to `UseDefaultCallbacks(bool printWithColor = false);` because ArduinoIDE's serial monitor doesn't support color formatted printing with ANSI escape codes
|
||||
- Changed license
|
||||
|
||||
## Version: 1.0.5 and 1.0.6
|
||||
|
||||
- Updated README with better documentation
|
||||
- Added donation option
|
||||
|
||||
## Version: 1.0.4
|
||||
|
||||
- Fixed a bug where long strings for app version, build time and date would break the website formatting
|
||||
|
||||
## Version: 1.0.3
|
||||
|
||||
- Updated keywords.txt for ArduinoIDE
|
||||
- Finished README
|
||||
- Fixed README header logo
|
||||
- Fixed README TOC for PlatformIO
|
||||
|
||||
## Version: 1.0.2
|
||||
|
||||
- Fixed PlatformIO release
|
||||
|
||||
## Version: 1.0.1
|
||||
|
||||
- Added `PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE` macro, `OverwriteAppBuildTimeAndDate(...)` and `OverwriteAppVersion(...)` to overwrite the build time, build date and app version which get displayed on the website. This is needed when using the ArduinoIDE. See README for details
|
||||
- Added mDNS example
|
||||
- Added setCustomVersionAndBuildTime example
|
||||
- Fixed examples
|
||||
- Fixed dependencies for ArduinoIDE
|
||||
- Updated README
|
||||
|
||||
## Version: 1.0.0
|
||||
|
||||
- Backend rewrite and code cleanup for performance optimization
|
||||
- Reduced code size
|
||||
- UI improvements and cleanup
|
||||
- Support for custom URLs (ability to change the default `/update` and `/login` URLs)
|
||||
- Renamed internal URLs to avoid name conflicts
|
||||
- Added a "Log out" button to website
|
||||
- Save and restore logged in clients to NVS automatically. Clients will stay logged in even after a reboot or update of the ESP32
|
||||
- Removed dependency on Arduino MD5Builder
|
||||
- Added compile checks for incompatible libraries/boards
|
||||
- Wrote own low level ESP update manager and removed Arduino dependency
|
||||
- Switched to `std::string` instead of Arduino `String`
|
||||
- Fixed a bug where every unauthenticated request would get redirected to the login page, even for POST requests
|
||||
- Many more changes for usability and performance
|
||||
|
||||
## Version 0.3.8
|
||||
|
||||
- Initial release
|
||||
17
lib/PrettyOTA/LICENSE.md
Normal file
17
lib/PrettyOTA/LICENSE.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# License
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial applications, to alter it and redistribute it, subject to the following conditions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this Software in a product, acknowledgment in the product documentation or credits is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
3. You are not permitted to modify, replace or remove the name "PrettyOTA" or the original logo displayed within the Software's default user interface (if applicable), unless you have obtained a separate commercial license granting you such rights. This restriction applies even when redistributing modified versions of the source code.
|
||||
4. This license notice must not be removed or altered from any source code distribution.
|
||||
|
||||
**Disclaimer:**
|
||||
The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
|
||||
|
||||
## Commercial Licensing
|
||||
A separate commercial license is required for specific rights not granted herein, particularly for white-labeling or rebranding (using a different name or logo). Please refer to the README file or contact the copyright holder for details on obtaining a commercial license.
|
||||
740
lib/PrettyOTA/README.md
Normal file
740
lib/PrettyOTA/README.md
Normal file
@@ -0,0 +1,740 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/user-attachments/assets/0efbf883-8ecd-4a2a-af7b-b6620b43138b" alt="Screenshot" style="height:100px;"/>
|
||||
</p>
|
||||
|
||||
   
|
||||
|
||||
# <p align="center">PrettyOTA</p>
|
||||
### <p align="center">Over the air (OTA) update library for ESP32 series chips - (RaspberryPi Pico W coming soon)</p>
|
||||
|
||||
#### <p align="center">Simple to use, modern design - Install updates on your ESP32 over WiFi inside the browser with easy rollback feature</p>
|
||||
|
||||
### ❤️💰 Donation - Please support my work
|
||||
|
||||
<a href="https://buymeacoffee.com/lostincompilation" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
|
||||
|
||||
[or donate Bitcoin for PrettyOTA](#donation)
|
||||
|
||||
## Contents
|
||||
|
||||
- **[Features](#features)**
|
||||
- **[Supported MCUs](#supported-mcus)**
|
||||
- **[Demo](#demo)**
|
||||
- **[Minimal example](#minimal-example)**
|
||||
- **[Installation](#installation)**
|
||||
- [PlatformIO](#platformio)
|
||||
- [ArduinoIDE](#arduinoide)
|
||||
- [GitHub](#github)
|
||||
- [Dependencies](#dependencies)
|
||||
- **[Usage](#usage)**
|
||||
- [Authentication (username and password)](#authentication-username-and-password)
|
||||
- [Password as MD5 hash](#password-as-md5-hash)
|
||||
- [OTA upload directly inside PlatformIO or ArduinoIDE](#ota-upload-directly-inside-platformio-or-arduinoide)
|
||||
- [Set HardwareID](#set-hardwareid)
|
||||
- [Set firmware version number, build time and date](#set-firmware-version-number-build-time-and-date)
|
||||
- [ArduinoIDE (manual)](#arduinoide-manual)
|
||||
- [PlatformIO with ESP-IDF (automatic)](#platformio-with-esp-idf-automatic)
|
||||
- [Use mDNS](#use-mdns)
|
||||
- [Callbacks](#callbacks)
|
||||
- [Use default callbacks](#use-default-callbacks)
|
||||
- [Unmounting SPIFFS filesystem before an update](#unmounting-spiffs-filesystem-before-an-update)
|
||||
- [Custom URLs](#custom-urls)
|
||||
- [Partitions](#partitions)
|
||||
- [Save logged in clients to NVS](#save-logged-in-clients-to-nvs)
|
||||
- **[Documentation of all functions](#documentation-of-all-functions)**
|
||||
- [Begin()](#begin)
|
||||
- [SetAuthenticationDetails()](#setauthenticationdetails)
|
||||
- [UseDefaultCallbacks()](#usedefaultcallbacks)
|
||||
- [SetSerialOutputStream()](#setserialoutputstream)
|
||||
- [OnStart(), OnProgress(), OnEnd()](#onstart-onprogress-onend)
|
||||
- [SetHardwareID()](#sethardwareid)
|
||||
- [SetAppVersion()](#setappversion)
|
||||
- [SetAppBuildTimeAndDate()](#setappbuildtimeanddate)
|
||||
- [Macro - PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE()](#macro---pretty_ota_set_current_build_time_and_date)
|
||||
- [IsUpdateRunning()](#isupdaterunning)
|
||||
- **[Use PrettyOTA with ESP-IDF](#use-prettyota-with-esp-idf)**
|
||||
- **[Is Ethernet supported?](#is-ethernet-supported)**
|
||||
- **[Help I got compilation errors](#help-i-got-compilation-errors)**
|
||||
- **[Usage in commercial applications and white labeling](#usage-in-commercial-applications-and-white-labeling)**
|
||||
- [White-labeling](#white-labeling)
|
||||
- **[Donation](#donation)**
|
||||
|
||||
*See also: **[License](LICENSE.md)***
|
||||
|
||||
## Changelog <div id="changelog"/>
|
||||
|
||||
You can view the changelog here: [Changelog](CHANGELOG.md)
|
||||
|
||||
## Features <div id="features"/>
|
||||
|
||||
- ***Easy to use*** (two lines of code)
|
||||
- ***Drag and drop*** firmware or filesystem `.bin` file to start updating
|
||||
- ***Rollback*** to previous firmware with a button click
|
||||
- ***Reboot*** remotely with a button click
|
||||
- ***Show info*** about installed firmware (Firmware version, SDK version, build time)
|
||||
- ***Automatic reboot*** after update/rollback can be enabled
|
||||
- Support for **authentication** (login with username and password) using server generated keys
|
||||
- ***Asynchronous web server***
|
||||
- Support for ***ArduinoOTA*** to directly upload firmware over WiFi inside Arduino IDE and PlatformIO (tutorial included)
|
||||
|
||||
### What's planned for the future?
|
||||
|
||||
- Optional **firmware pulling from server**: Configure PrettyOTA to automatically check a server for a new firmware version and install it. This is useful for many devices which all need updates at the same time. No manual firmware upload required anymore
|
||||
- Show/hide specific board infos
|
||||
- Disable/enable filesystem updates in code
|
||||
|
||||
## Supported MCUs <div id="supported-mcus"/>
|
||||
|
||||
- **ESP32** (all variants)
|
||||
- **RP2040** and **RP2350** (**RaspberryPi Pico W**) - *coming soon!*
|
||||
- **ESP8266** - Support is planned but is not the highest priority now since the ESP8266 is end of life
|
||||
|
||||
## Demo <div id="demo"/>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/user-attachments/assets/cd5b869b-26ec-4d18-aa3c-554c78ec7306" alt="Screenshot" />
|
||||
</p>
|
||||
|
||||
## Minimal example <div id="minimal-example"/>
|
||||
|
||||
With the example code below you can access PrettyOTA at `http://IP_ADDRESS/update`, or upload via OTA inside ArduinoIDE and PlatformIO
|
||||
|
||||
Replace `IP_ADDRESS` in the URL with the IP address of your ESP32.
|
||||
|
||||
```cpp
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <PrettyOTA.h>
|
||||
|
||||
const char* WIFI_SSID = "YOUR_SSID";
|
||||
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
|
||||
|
||||
AsyncWebServer server(80); // Server on port 80 (HTTP)
|
||||
PrettyOTA OTAUpdates;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize WiFi
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
|
||||
// Wait for successful WiFi connection
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.println("[WiFi] Connection failed! Rebooting...");
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Print IP address
|
||||
Serial.println("PrettyOTA can be accessed at: http://" + WiFi.localIP().toString() + "/update");
|
||||
|
||||
// Initialize PrettyOTA
|
||||
OTAUpdates.Begin(&server);
|
||||
|
||||
// Set unique Hardware-ID for your hardware/board
|
||||
OTAUpdates.SetHardwareID("UniqueBoard1");
|
||||
|
||||
// Set firmware version to 1.0.0
|
||||
OTAUpdates.SetAppVersion("1.0.0");
|
||||
|
||||
// Set current build time and date
|
||||
PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();
|
||||
|
||||
// Start web server
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Give CPU time to other running tasks
|
||||
delay(100);
|
||||
}
|
||||
```
|
||||
|
||||
## Installation <div id="installation"/>
|
||||
|
||||
### PlatformIO <div id="platformio"/>
|
||||
|
||||
To use this library with PlatformIO, simply search for PrettyOTA inside PlatformIO Library Manager.
|
||||
|
||||
⚠️ **Important:** You must add this line to your `platformio.ini`:
|
||||
|
||||
```ini
|
||||
lib_compat_mode = strict
|
||||
```
|
||||
|
||||
### ArduinoIDE <div id="arduinoIDE"/>
|
||||
|
||||
You can download PrettyOTA from the Arduino Library Manager. Simply search for PrettyOTA inside ArduinoIDE.
|
||||
|
||||
Alternatively you can download the latest release / `.zip` file from GitHub and import it into the ArduinoIDE (Sketch->Include library->Add .ZIP library).
|
||||
|
||||
### GitHub <div id="github"/>
|
||||
|
||||
Download the latest release / `.zip` file from releases.
|
||||
|
||||
### Dependencies <div id="dependencies"/>
|
||||
|
||||
You don't have to install the dependencies manually when using ArduinoIDE or PlatformIO. Simply search for *PrettyOTA* in the library manager and install it.
|
||||
|
||||
PrettyOTA needs the following libraries:
|
||||
|
||||
- [AsyncTCP](https://github.com/ESP32Async/AsyncTCP)
|
||||
- [ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer)
|
||||
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
|
||||
|
||||
## Usage <div id="usage"/>
|
||||
|
||||
### Authentication (username and password) <div id="authentication-username-and-password"/>
|
||||
|
||||
To enable authentication with username and password, simply pass the username and password to the `Begin(...)` function:
|
||||
|
||||
```cpp
|
||||
OTAUpdates.Begin(&server, "username", "password");
|
||||
```
|
||||
|
||||
You can always change the username or password after PrettyOTA has been initialized using:
|
||||
|
||||
```cpp
|
||||
void SetAuthenticationDetails(const char* const username, const char* const password, bool passwordIsMD5Hash = false);
|
||||
```
|
||||
|
||||
To disable authentication after it has been enabled previously, pass empty values for `username` and `password` to `SetAuthenticationDetails()`:
|
||||
|
||||
```cpp
|
||||
// This will disable authentication
|
||||
SetAuthenticationDetails("", "");
|
||||
```
|
||||
|
||||
It is also possible to only have an username but no password (and vice versa). Simply leave one of the parameters (username *or* password) empty.
|
||||
|
||||
Authentication is disabled by default if you don't pass any values to `username` and `password` inside `Begin()`.
|
||||
|
||||
#### Password as MD5 hash <div id="password-as-md5-hash"/>
|
||||
|
||||
It's better not to store the password as clear text. PrettyOTA supports setting the password as a MD5 hash.
|
||||
Simply pass the hash string to `Begin(...)` as the password argument and set `passwordIsMD5Hash` to `true`:
|
||||
|
||||
```cpp
|
||||
// The password is "123" (without quotes)
|
||||
// The MD5 hash of "123" is "202cb962ac59075b964b07152d234b70"
|
||||
OTAUpdates.Begin(&server, "username", "202cb962ac59075b964b07152d234b70", true);
|
||||
```
|
||||
|
||||
### OTA upload directly inside PlatformIO or ArduinoIDE <div id="ota-upload-directly-inside-platformio-or-arduinoide"/>
|
||||
|
||||
If you don't want to use the web interface of PrettyOTA, you can directly upload the firmware via OTA using PlatformIO or ArduinoIDE.
|
||||
|
||||
For ArduinoIDE you don't have to change anything. The ESP32 will show up under boards as an WiFi OTA target.
|
||||
|
||||
For PlatformIO you have to change the `platformio.ini` file like usual for OTA uploads and add the following:
|
||||
|
||||
```ini
|
||||
upload_protocol = espota
|
||||
upload_port = 192.168.x.x
|
||||
```
|
||||
|
||||
Replace the IP address with the IP address of your ESP32.
|
||||
|
||||
### Set HardwareID <div id="set-hardwareid"/>
|
||||
|
||||
The Hardware ID should be a unique identifier for your hardware/board.
|
||||
You can set it using:
|
||||
|
||||
```cpp
|
||||
void SetHardwareID(const char* const hardwareID);
|
||||
```
|
||||
|
||||
If you don't set a Hardware ID, the default `"MyBoard1"` will be used. It is not recommended to leave the Hardware ID unchanged, as every board would get the same default value.
|
||||
|
||||
### Set firmware version number, build time and date <div id="set-firmware-version-number-build-time-and-date"/>
|
||||
|
||||
You can manually set the version number and build time/date (must be done when using ArduinoIDE or PlatformIO without ESP-IDF framework).
|
||||
If you use PlatformIO with the ESP-IDF framework (you can use both, ESP-IDF and the Arduino Framework at the same time), PrettyOTA can automatically detect the version number and build time/date for you.
|
||||
|
||||
#### ArduinoIDE (manual) <div id="arduinoide-manual"/>
|
||||
|
||||
When using the ArduinoIDE you must manually set the version number and build time/date.
|
||||
|
||||
#### Firmware version number
|
||||
|
||||
To set the firmware version inside ArduinoIDE (or setting it manually if you use PlatformIO) use the following function inside `setup()`:
|
||||
|
||||
```cpp
|
||||
void SetAppVersion(const char* const appVersion);
|
||||
```
|
||||
|
||||
#### Build time and date
|
||||
|
||||
To set the *current* build time and date you can use the macro `PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();` inside `setup().
|
||||
|
||||
The macro will call `SetAppBuildTimeAndDate(const char* const appBuildTime, const char* const appBuildDate)` with the current build time and date already filled in for you.
|
||||
|
||||
If you want to set a specific build time or date, just call `SetAppBuildTimeAndDate(...)` manually inside `setup()`:
|
||||
|
||||
```cpp
|
||||
OTAUpdates.SetAppBuildTimeAndDate("17:10:00", "Mar 31 2025");
|
||||
```
|
||||
|
||||
You don't have to follow any specific formatting for the time or date. Both parameters are strings and can be set to any value you like.
|
||||
|
||||
#### Example
|
||||
|
||||
```cpp
|
||||
void setup()
|
||||
{
|
||||
// ...
|
||||
|
||||
// Initialize PrettyOTA
|
||||
OTAUpdates.Begin(&server);
|
||||
|
||||
// Set hardware id
|
||||
OTAUpdates.SetHardwareID("MyUniqueBoard");
|
||||
|
||||
// Set firmware version to 1.0.0
|
||||
OTAUpdates.SetAppVersion("1.0.0");
|
||||
|
||||
// Set current build time and date
|
||||
PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();
|
||||
|
||||
// Or manually set a specific build time and date
|
||||
//OTAUpdates.SetAppBuildTimeAndDate("17:10:00", "Mar 31 2025");
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### PlatformIO with ESP-IDF (automatic) <div id="platformio-with-esp-idf-automatic"/>
|
||||
|
||||
⚠️ The automatic way will only work if you use the ESP-IDF framework *with* the Arduino framework. If you only use the Arduino framework inside PlatformIO you must set the version and build time / date manually like described above for ArduinoIDE.
|
||||
|
||||
#### Firmware version number
|
||||
|
||||
In PlatformIO simply create a text file called `version.txt` in your projects root folder. The content of the textfile must be one line containing the firmware version:
|
||||
|
||||
```text
|
||||
1.0.0
|
||||
```
|
||||
|
||||
PlatformIO will automatically find the `version.txt` file and set the version of your firmware, which gets displayed by PrettyOTA in the browser.
|
||||
|
||||
See [Use PrettyOTA with ESP-IDF](#use-prettyota-with-esp-idf) below for details and examples.
|
||||
|
||||
#### Build time and date
|
||||
|
||||
The build time and date will be automatically set. You don't have to do anything. PrettyOTA reads the time and date of the build from the firmware partition using `esp_ota_get_app_description()`.
|
||||
|
||||
You can set it manually too with:
|
||||
|
||||
```cpp
|
||||
OTAUpdates.SetAppBuildTimeAndDate("17:10:00", "Mar 31 2025");
|
||||
```
|
||||
|
||||
### Use mDNS <div id="use-mdns"/>
|
||||
|
||||
You can use mDNS to display the hostname for the OTA upload target inside ArduinoIDE. You can also use it to access the ESP32 using a normal URL like `http://myesp.local/update` in your local network instead of the IP address.
|
||||
|
||||
```cpp
|
||||
void setup()
|
||||
{
|
||||
// ...
|
||||
|
||||
// Setup mDNS
|
||||
// You must call "MDNS.begin()" BEFORE "OTAUpdates.Begin()"
|
||||
MDNS.begin("myesp"); // http://myesp.local/
|
||||
|
||||
// Initialize PrettyOTA
|
||||
OTAUpdates.Begin(&server);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The ArduinoIDE will now show the ESP32 as "myesp" like specified in the code above:
|
||||
|
||||
<p align="center">
|
||||
<img width="475" alt="Screenshot 2025-03-31 at 21 17 08" src="https://github.com/user-attachments/assets/c97c1196-c6da-42e0-bcbf-66895dd0bdee" />
|
||||
</p>
|
||||
|
||||
For a full example see [mDNS example](/examples/mDNS/mDNS.ino).
|
||||
|
||||
### Callbacks <div id="callbacks"/>
|
||||
|
||||
You can define your own callbacks which get called by PrettyOTA during the update.
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <PrettyOTA.h>
|
||||
|
||||
const char* WIFI_SSID = "YOUR_SSID";
|
||||
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
|
||||
|
||||
AsyncWebServer server(80); // Server on port 80 (HTTP)
|
||||
PrettyOTA OTAUpdates;
|
||||
|
||||
// Gets called when update starts
|
||||
// updateMode can be FILESYSTEM or FIRMWARE
|
||||
void OnOTAStart(NSPrettyOTA::UPDATE_MODE updateMode)
|
||||
{
|
||||
Serial.println("OTA update started");
|
||||
|
||||
if(updateMode == NSPrettyOTA::UPDATE_MODE::FIRMWARE)
|
||||
Serial.println("Mode: Firmware");
|
||||
else if(updateMode == NSPrettyOTA::UPDATE_MODE::FILESYSTEM)
|
||||
Serial.println("Mode: Filesystem");
|
||||
}
|
||||
|
||||
// Gets called while update is running
|
||||
// currentSize: Number of bytes already processed
|
||||
// totalSize: Total size of new firmware in bytes
|
||||
void OnOTAProgress(uint32_t currentSize, uint32_t totalSize)
|
||||
{
|
||||
Serial.printf("OTA Progress Current: %u bytes, Total: %u bytes\n", currentSize, totalSize);
|
||||
}
|
||||
|
||||
// Gets called when update finishes
|
||||
void OnOTAEnd(bool successful)
|
||||
{
|
||||
if (successful)
|
||||
Serial.println("OTA update finished successfully");
|
||||
else
|
||||
Serial.println("OTA update failed");
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize WiFi
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
|
||||
// Wait for successful WiFi connection
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.println("[WiFi] Connection failed! Rebooting...");
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Print IP address
|
||||
Serial.println("PrettyOTA can be accessed at: http://" + WiFi.localIP().toString() + "/update");
|
||||
|
||||
// Initialize PrettyOTA and set username and password for authentication
|
||||
OTAUpdates.Begin(&server, "admin", "123");
|
||||
|
||||
// Set unique Hardware-ID for your hardware/board
|
||||
OTAUpdates.SetHardwareID("UniqueBoard1");
|
||||
|
||||
// Set firmware version to 1.0.0
|
||||
OTAUpdates.SetAppVersion("1.0.0");
|
||||
|
||||
// Set current build time and date
|
||||
PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();
|
||||
|
||||
// Set custom callbacks
|
||||
OTAUpdates.OnStart(OnOTAStart);
|
||||
OTAUpdates.OnProgress(OnOTAProgress);
|
||||
OTAUpdates.OnEnd(OnOTAEnd);
|
||||
|
||||
// Start web server
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Give CPU time to other running tasks
|
||||
delay(100);
|
||||
}
|
||||
```
|
||||
|
||||
For the full example see [Callbacks example](/examples/callbacks/callbacks.ino).
|
||||
|
||||
### Use default callbacks <div id="use-default-callbacks"/>
|
||||
|
||||
PrettyOTA provides built-in default callbacks, which just print the update status to the SerialMonitor (or any other Stream you specified with `PrettyOTA::SetSerialOutputStream(Stream*)`).
|
||||
|
||||
To use the built-in default callbacks:
|
||||
|
||||
```cpp
|
||||
// Use built-in default callbacks.
|
||||
// Do not call OnStart, OnProgress or OnEnd anymore, since they would override using the built-in default callbacks
|
||||
OTAUpdates.UseDefaultCallbacks();
|
||||
```
|
||||
|
||||
The default callbacks also support color formatted printing. Every terminal and serial monitor supports that feature, except ArduinoIDE's serial monitor.
|
||||
To enable color formatted printing, set the `printWithColor` parameter to `true`:
|
||||
|
||||
```cpp
|
||||
// Use built-in default callbacks with color formatted printing
|
||||
OTAUpdates.UseDefaultCallbacks(true);
|
||||
```
|
||||
|
||||
When using the default callbacks you will get the following output on your Serial-Monitor during an OTA update (colors are not supported by ArduinoIDE):
|
||||
|
||||
<p align="center">
|
||||
<img width="380" alt="Screenshot 2025-03-31 at 03 35 45" src="https://github.com/user-attachments/assets/40f76183-3f94-469b-bb25-b01dd89e8606" />
|
||||
</p>
|
||||
|
||||
### Unmounting SPIFFS filesystem before an update <div id="unmounting-spiffs-filesystem-before-an-update"/>
|
||||
|
||||
If you want to upload a filesystem image, the filesystem must be unmounted before the update begins (not necessary for firmware updates).
|
||||
|
||||
You can use a custom callback for `OnStart(...)`, and unmount the filesystem (i.e. SPIFFS/LittleFS) inside the `OnStart(...)` callback:
|
||||
|
||||
```cpp
|
||||
void CustomOnStart(NSPrettyOTA::UPDATE_MODE updateMode)
|
||||
{
|
||||
// Is the filesystem going to be updated?
|
||||
if(updateMode == NSPrettyOTA::UPDATE_MODE::FILESYSTEM)
|
||||
{
|
||||
// Unmount SPIFFS filesystem here
|
||||
SPIFFS.end();
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// ...
|
||||
|
||||
OTAUpdates.Begin(&server);
|
||||
|
||||
// Set callback
|
||||
OTAUpdates.OnStart(CustomOnStart);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Custom URLs <div id="custom-urls"/>
|
||||
|
||||
PrettyOTA uses these URLs by default:
|
||||
|
||||
- Customizable URLs:
|
||||
- `/login`: Login page if authentication is enabled. Redirects to `/update` if authentication is disabled
|
||||
- `/update`: PrettyOTA's main website. Redirects to `/login` if authentication is enabled and client is not logged in
|
||||
- Fixed URLs used internally (cannot be changed):
|
||||
- `/prettyota/start`:
|
||||
- `/prettyota/upload`:
|
||||
- `/prettyota/doRollback`:
|
||||
- `/prettyota/queryInfo`:
|
||||
- `/prettyota/queryPrettyOTAInfo`:
|
||||
- `/prettyota/rebootCheck`:
|
||||
- `/prettyota/doManualReboot`:
|
||||
- `/prettyota/logout`:
|
||||
|
||||
If you want to change the `/login` and / or `/update` URLs, specify them in the `Begin()` function:
|
||||
|
||||
```cpp
|
||||
// Use custom URLs and enable authentication (username and password)
|
||||
OTAUpdates.Begin(&server, "admin", "123", false, "/myCustomUpdateURL", "/myCustomLoginURL");
|
||||
```
|
||||
|
||||
With the code above you can reach PrettyOTA under `http://YOUR_IP/myCustomUpdateURL`.
|
||||
|
||||
### Partitions <div id="partitions"/>
|
||||
|
||||
#### ArduinoIDE
|
||||
|
||||
Inside the ArduinoIDE make sure you select a partition scheme *with OTA*. For example you can use `Minimal SPIFFS (APP with OTA)`. However having a separate SPIFFS is not a requirement.
|
||||
|
||||
#### PlatformIO
|
||||
|
||||
To be able to use OTA updates you need (at least) two app partitions (for the firmware) and one ota_data partition (for configuration).
|
||||
|
||||
You also need a NVS partition where PrettyOTA can store the logged in clients. If you don't have a NVS partition, logged in clients won't be remembered after reboot/update and PrettyOTA will print an error message (but everything else will work without issues).
|
||||
|
||||
SPIFFS is not a requirement.
|
||||
|
||||
Example `partitions.csv` (4MB flash) for PlatformIO including an optional SPIFFS partition:
|
||||
|
||||
```scala
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs,data,nvs,0x9000,0x13000,
|
||||
phy,data,phy,0x1C000,0x2000,
|
||||
otadata,data,ota,0x1E000,0x2000,
|
||||
app0,app,ota_0,0x20000,0x1D0000,
|
||||
app1,app,ota_1,0x1F0000,0x1D0000,
|
||||
spiff,data,spiffs,0x3C0000,0x40000,
|
||||
```
|
||||
|
||||
I can recommend [the ESP Partition Builder Website](https://thelastoutpostworkshop.github.io/microcontroller_devkit/esp32partitionbuilder) for an easy way to manage partitions.
|
||||
|
||||
### Save logged in clients to NVS <div id="save-logged-in-clients-to-nvs"/>
|
||||
|
||||
PrettyOTA automatically saves logged in clients (sessionIDs) to the NVS partition and loads them during initialization. This enables clients to stay logged in after a reboot or firmware update of the ESP32.
|
||||
|
||||
For this to work you must have a NVS partition (for example inside the `partitions.csv` file for PlatformIO).
|
||||
Without a NVS partition saving will not work and you have to log in again after a reboot or firmware update of the ESP32.
|
||||
|
||||
## Documentation of all functions <div id="documentation-of-all-functions"/>
|
||||
|
||||
### Begin() <div id="begin"/>
|
||||
|
||||
```cpp
|
||||
// Returns true on success, false on error or if already initialized
|
||||
bool Begin(AsyncWebServer* const server, // The AsyncWebServer instance
|
||||
const char* const username = "", // (Optional) Username for authentication
|
||||
const char* const password = "", // (Optional) Password for authentication
|
||||
bool passwordIsMD5Hash = false, // (Optional) Is the password cleartext or a MD5 hash?
|
||||
const char* const mainURL = "/update", // (Optional) Main URL for PrettyOTA
|
||||
const char* const loginURL = "/login", // (Optional) Login page URL
|
||||
uint16_t OTAport = 3232); // (Optional) The port for OTA uploads inside ArduinoIDE/PlatformIO.
|
||||
// Leave it set to `3232` for compatibility with ArduinoIDE OTA upload
|
||||
```
|
||||
|
||||
#### SetAuthenticationDetails() <div id="setauthenticationdetails"/>
|
||||
|
||||
```cpp
|
||||
void SetAuthenticationDetails(const char* const username, // Username
|
||||
const char* const password, // Password
|
||||
bool passwordIsMD5Hash = false); // (Optional) Is the password cleartext or a MD5 hash?
|
||||
```
|
||||
|
||||
If `username` *and* `password` is empty, authentication will be disabled. The `/login` page then redirects automatically to the main `/update` page.
|
||||
|
||||
See [Authentication](#authentication-username-and-password) below for details and examples.
|
||||
|
||||
#### UseDefaultCallbacks() <div id="usedefaultcallbacks"/>
|
||||
|
||||
Call this function to use the built-in default callbacks. The default callbacks only print the update status to the Serial-Monitor (or any other Stream you specified).
|
||||
|
||||
See [Use default callbacks](#use-default-callbacks) for more details.
|
||||
|
||||
```cpp
|
||||
void UseDefaultCallbacks(bool printWithColor = false);
|
||||
```
|
||||
|
||||
#### SetSerialOutputStream() <div id="setserialoutputstream"/>
|
||||
|
||||
PrettyOTA outputs log messages when an error occurs. You can specify where these log messages should be printed. The default is printing to `Serial` (you must have `Serial.begin(115200);` inside `setup()` for this to work).
|
||||
|
||||
If you want to print to a different output, use the following function:
|
||||
|
||||
```cpp
|
||||
// Set the Stream to write log messages too (Example: Use &Serial as argument)
|
||||
void SetSerialOutputStream(Stream* const serialStream);
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```cpp
|
||||
// Print to Serial1 instead of default Serial
|
||||
OTAUpdates.SetSerialOutputStream(&Serial1);
|
||||
```
|
||||
|
||||
#### OnStart(), OnProgress(), OnEnd() <div id="onstart-onprogress-onend"/>
|
||||
|
||||
Set custom user defined callbacks. See [callbacks example](/examples/callbacks/callbacks.ino).
|
||||
|
||||
```cpp
|
||||
// Set custom callback functions
|
||||
void OnStart(std::function<void(NSPrettyOTA::UPDATE_MODE updateMode)> func);
|
||||
void OnProgress(std::function<void(uint32_t currentSize, uint32_t totalSize)> func);
|
||||
void OnEnd(std::function<void(bool successful)> func);
|
||||
```
|
||||
|
||||
#### SetHardwareID() <div id="sethardwareid"/>
|
||||
|
||||
See [Set HardwareID](#set-hardwareid).
|
||||
|
||||
```cpp
|
||||
void SetHardwareID(const char* const hardwareID);
|
||||
```
|
||||
|
||||
#### SetAppVersion() <div id="setappversion"/>
|
||||
|
||||
See [Set firmware version number, build time and date](#set-firmware-version-number-build-time-and-date).
|
||||
|
||||
```cpp
|
||||
static void SetAppVersion(const char* const appVersion);
|
||||
```
|
||||
|
||||
#### SetAppBuildTimeAndDate() <div id="setappbuildtimeanddate"/>
|
||||
|
||||
See [Set firmware version number, build time and date](#set-firmware-version-number-build-time-and-date).
|
||||
|
||||
```cpp
|
||||
static void SetAppBuildTimeAndDate(const char* const appBuildTime, const char* const appBuildDate);
|
||||
```
|
||||
|
||||
#### Macro - PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE() <div id="macro---pretty_ota_set_current_build_time_and_date"/>
|
||||
|
||||
See [Set firmware version number, build time and date](#set-firmware-version-number-build-time-and-date).
|
||||
|
||||
```cpp
|
||||
#define PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE() PrettyOTA::SetAppBuildTimeAndDate(__TIME__, __DATE__)
|
||||
```
|
||||
#### IsUpdateRunning() <div id="isupdaterunning"/>
|
||||
|
||||
Returns `true` if an update is currently running (either through the web interface (push) or automatically with pulling a firmware in the background).
|
||||
|
||||
```cpp
|
||||
bool IsUpdateRunning() const;
|
||||
```
|
||||
|
||||
## Use PrettyOTA with ESP-IDF <div id="use-prettyota-with-esp-idf"/>
|
||||
|
||||
PrettyOTA relies on ESPAsyncWebServer which is an Arduino library. However you can include the Arduino dependencies as a package inside your ESP-IDF project. No changes are required to your code base and the Arduino stuff is not interfering with anything in the background.
|
||||
|
||||
*Instructions on how to use ESP-IDF and Arduino framework at the same time will follow soon.*
|
||||
|
||||
## Is Ethernet supported? <div id="is-ethernet-supported"/>
|
||||
|
||||
Yes, Ethernet is supported with PrettyOTA and AsyncWebServer. You don't have to change anything regarding PrettyOTA's usage.
|
||||
|
||||
## Help I got compilation errors <div id="help-i-got-compilation-errors"/>
|
||||
|
||||
If you get the following error when compiling with PlatformIO:
|
||||
|
||||
```text
|
||||
'ip_addr_t' {aka 'struct ip_addr'} has no member named 'addr'; did you mean 'u_addr'?
|
||||
```
|
||||
|
||||
You must add this line to your `platformio.ini`:
|
||||
|
||||
```ini
|
||||
lib_compat_mode = strict
|
||||
```
|
||||
|
||||
## Usage in commercial applications and white labeling <div id="usage-in-commercial-applications-and-white-labeling"/>
|
||||
|
||||
You are allowed to use PrettyOTA for commercial purposes. An acknowledgement is required.
|
||||
However you are not allowed to modify the source code and then claim that you wrote it.
|
||||
|
||||
It is also not permitted to change the name or logo of PrettyOTA, even when redistributing changed source code versions. If you want to change the logo and/or name, you must acquire a commercial license.
|
||||
|
||||
A commercial license also includes technical support for companies, who need reliable and fast solutions.
|
||||
|
||||
*See also: [License](LICENSE.md)*
|
||||
|
||||
### White-labeling <div id="white-labeling"/>
|
||||
|
||||
If you want to white-label PrettyOTA (use a custom name and logo for commercial or private application), please **[contact me via E-Mail](mailto:marc.public.mail@gmail.com)** for inquiries.
|
||||
White-labeling is the only use case which is not free.
|
||||
|
||||
## Donation <div id="donation"/>
|
||||
|
||||
❤️ If you want to help out a student with paying rent, a donation for PrettyOTA and my work would be greatly appreciated!
|
||||
|
||||
💬 If you don't want to donate, please support by telling other people about PrettyOTA!
|
||||
|
||||
### Support me on *Buy me a coffee*
|
||||
|
||||
<a href="https://buymeacoffee.com/lostincompilation" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
|
||||
|
||||
### Bitcoin
|
||||
|
||||
<img width="150" alt="Bitcoin" src="https://github.com/user-attachments/assets/7c78d93f-60ef-45a0-afe3-e51e71e98edc" />
|
||||
|
||||
<br>
|
||||
|
||||
Wallet address (**Bitcoin**):
|
||||
|
||||
```text
|
||||
32nkLAWGsAtn3SNo1vb7RSQKLP93YJCwke
|
||||
```
|
||||
BIN
lib/PrettyOTA/examples/.DS_Store
vendored
Normal file
BIN
lib/PrettyOTA/examples/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
lib/PrettyOTA/examples/callbacks/.DS_Store
vendored
Normal file
BIN
lib/PrettyOTA/examples/callbacks/.DS_Store
vendored
Normal file
Binary file not shown.
83
lib/PrettyOTA/examples/callbacks/callbacks.ino
Normal file
83
lib/PrettyOTA/examples/callbacks/callbacks.ino
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <PrettyOTA.h>
|
||||
|
||||
const char* WIFI_SSID = "YOUR_SSID";
|
||||
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
|
||||
|
||||
AsyncWebServer server(80); // Server on port 80 (HTTP)
|
||||
PrettyOTA OTAUpdates;
|
||||
|
||||
// Gets called when update starts
|
||||
// updateMode can be FILESYSTEM or FIRMWARE
|
||||
void OnOTAStart(NSPrettyOTA::UPDATE_MODE updateMode)
|
||||
{
|
||||
Serial.println("OTA update started");
|
||||
|
||||
if(updateMode == NSPrettyOTA::UPDATE_MODE::FIRMWARE)
|
||||
Serial.println("Mode: Firmware");
|
||||
else if(updateMode == NSPrettyOTA::UPDATE_MODE::FILESYSTEM)
|
||||
Serial.println("Mode: Filesystem");
|
||||
}
|
||||
|
||||
// Gets called while update is running
|
||||
// currentSize: Number of bytes already processed
|
||||
// totalSize: Total size of new firmware in bytes
|
||||
void OnOTAProgress(uint32_t currentSize, uint32_t totalSize)
|
||||
{
|
||||
Serial.printf("OTA Progress Current: %u bytes, Total: %u bytes\n", currentSize, totalSize);
|
||||
}
|
||||
|
||||
// Gets called when update finishes
|
||||
void OnOTAEnd(bool successful)
|
||||
{
|
||||
if (successful)
|
||||
Serial.println("OTA update finished successfully");
|
||||
else
|
||||
Serial.println("OTA update failed");
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize WiFi
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
|
||||
// Wait for successful WiFi connection
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.println("[WiFi] Connection failed! Rebooting...");
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Print IP address
|
||||
Serial.println("PrettyOTA can be accessed at: http://" + WiFi.localIP().toString() + "/update");
|
||||
|
||||
// Initialize PrettyOTA and set username and password for authentication
|
||||
OTAUpdates.Begin(&server, "admin", "123");
|
||||
|
||||
// Set unique Hardware-ID for your hardware/board
|
||||
OTAUpdates.SetHardwareID("UniqueBoard1");
|
||||
|
||||
// Set firmware version to 1.0.0
|
||||
OTAUpdates.SetAppVersion("1.0.0");
|
||||
|
||||
// Set current build time and date
|
||||
PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();
|
||||
|
||||
// Set custom callbacks
|
||||
OTAUpdates.OnStart(OnOTAStart);
|
||||
OTAUpdates.OnProgress(OnOTAProgress);
|
||||
OTAUpdates.OnEnd(OnOTAEnd);
|
||||
|
||||
// Start web server
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Give CPU time to other running tasks
|
||||
delay(100);
|
||||
}
|
||||
56
lib/PrettyOTA/examples/mDNS/mDNS.ino
Normal file
56
lib/PrettyOTA/examples/mDNS/mDNS.ino
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <PrettyOTA.h>
|
||||
|
||||
const char* WIFI_SSID = "YOUR_SSID";
|
||||
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
|
||||
const char* DNS_ADDRESS = "myesp"; // http://myesp.local
|
||||
|
||||
AsyncWebServer server(80); // Server on port 80 (HTTP)
|
||||
PrettyOTA OTAUpdates;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize WiFi
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
|
||||
// Wait for successful WiFi connection
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.println("[WiFi] Connection failed! Rebooting...");
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Print address and IP
|
||||
Serial.println("PrettyOTA can be accessed at: http://" + String(DNS_ADDRESS) + ".local/update");
|
||||
Serial.println("And at: http://" + WiFi.localIP().toString() + "/update");
|
||||
|
||||
// Setup mDNS
|
||||
// You must call "MDNS.begin()" BEFORE "OTAUpdates.Begin()"
|
||||
MDNS.begin(DNS_ADDRESS);
|
||||
|
||||
// Initialize PrettyOTA
|
||||
OTAUpdates.Begin(&server);
|
||||
|
||||
// Set unique Hardware-ID for your hardware/board
|
||||
OTAUpdates.SetHardwareID("UniqueBoard1");
|
||||
|
||||
// Set firmware version to 1.0.0
|
||||
OTAUpdates.SetAppVersion("1.0.0");
|
||||
|
||||
// Set current build time and date
|
||||
PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();
|
||||
|
||||
// Start web server
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Give CPU time to other running tasks
|
||||
delay(100);
|
||||
}
|
||||
49
lib/PrettyOTA/examples/minimal/minimal.ino
Normal file
49
lib/PrettyOTA/examples/minimal/minimal.ino
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <PrettyOTA.h>
|
||||
|
||||
const char* WIFI_SSID = "YOUR_SSID";
|
||||
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
|
||||
|
||||
AsyncWebServer server(80); // Server on port 80 (HTTP)
|
||||
PrettyOTA OTAUpdates;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize WiFi
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
|
||||
// Wait for successful WiFi connection
|
||||
while (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||
{
|
||||
Serial.println("[WiFi] Connection failed! Rebooting...");
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Print IP address
|
||||
Serial.println("PrettyOTA can be accessed at: http://" + WiFi.localIP().toString() + "/update");
|
||||
|
||||
// Initialize PrettyOTA
|
||||
OTAUpdates.Begin(&server);
|
||||
|
||||
// Set unique Hardware-ID for your hardware/board
|
||||
OTAUpdates.SetHardwareID("UniqueBoard1");
|
||||
|
||||
// Set firmware version to 1.0.0
|
||||
OTAUpdates.SetAppVersion("1.0.0");
|
||||
|
||||
// Set current build time and date
|
||||
PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();
|
||||
|
||||
// Start web server
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Give CPU time to other running tasks
|
||||
delay(100);
|
||||
}
|
||||
25
lib/PrettyOTA/keywords.txt
Normal file
25
lib/PrettyOTA/keywords.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
# Syntax Coloring Map For ExampleLibrary
|
||||
|
||||
# Datatypes (KEYWORD1)
|
||||
UPDATE_MODE KEYWORD1
|
||||
UPDATE_ERROR KEYWORD1
|
||||
PULL_RESULT KEYWORD1
|
||||
|
||||
# Methods and Functions (KEYWORD2)
|
||||
Begin KEYWORD2
|
||||
SetAuthenticationDetails KEYWORD2
|
||||
IsUpdateRunning KEYWORD2
|
||||
OnStart KEYWORD2
|
||||
OnProgress KEYWORD2
|
||||
OnEnd KEYWORD2
|
||||
UseDefaultCallbacks KEYWORD2
|
||||
SetHardwareID KEYWORD2
|
||||
SetAppVersion KEYWORD2
|
||||
SetAppBuildTimeAndDate KEYWORD2
|
||||
SetSerialOutputStream KEYWORD2
|
||||
|
||||
PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE KEYWORD2
|
||||
|
||||
# Instances (KEYWORD2)
|
||||
|
||||
# Constants (LITERAL1)
|
||||
59
lib/PrettyOTA/library.json
Normal file
59
lib/PrettyOTA/library.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "PrettyOTA",
|
||||
"version": "1.1.3",
|
||||
"license": "Zlib",
|
||||
"description": "OTA (over the air) update library. A modern looking OTA web interface to easily upload firmware updates and configure automatic firmware pulling for updating many devices at once in the background (WIP). One-click firmware rollbacks and infos about your board and firmware, directly inside your browser. Includes support for OTA upload directly inside PlatformIO.",
|
||||
"keywords": "OTA, update, upgrade, wifi, web update, server, website, firmware, automatic",
|
||||
"headers": "PrettyOTA.h",
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/LostInCompilation/PrettyOTA.git"
|
||||
},
|
||||
"authors":
|
||||
[
|
||||
{
|
||||
"name": "LostInCompilation",
|
||||
"maintainer": true
|
||||
}
|
||||
],
|
||||
"dependencies":
|
||||
{
|
||||
"ESP32Async/ESPAsyncWebServer": "*",
|
||||
"bblanchon/ArduinoJson": "*"
|
||||
},
|
||||
"build":
|
||||
{
|
||||
"libCompatMode": "strict"
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"name": "Minimal",
|
||||
"base": "examples/minimal",
|
||||
"files": ["minimal.ino"]
|
||||
},
|
||||
{
|
||||
"name": "Callbacks",
|
||||
"base": "examples/callbacks",
|
||||
"files": ["callbacks.ino"]
|
||||
},
|
||||
{
|
||||
"name": "mDNS",
|
||||
"base": "examples/mDNS",
|
||||
"files": ["mDNS.ino"]
|
||||
}],
|
||||
"export": {
|
||||
"exclude": [
|
||||
".git",
|
||||
".github",
|
||||
".gitignore",
|
||||
"website",
|
||||
"website_compressor",
|
||||
"arduino-lint",
|
||||
"library.json",
|
||||
"library.properties"
|
||||
]
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"platforms": "espressif32"
|
||||
}
|
||||
11
lib/PrettyOTA/library.properties
Normal file
11
lib/PrettyOTA/library.properties
Normal file
@@ -0,0 +1,11 @@
|
||||
name=PrettyOTA
|
||||
version=1.1.3
|
||||
author=LostInCompilation <marc.public.mail@gmail.com>
|
||||
maintainer=LostInCompilation <marc.public.mail@gmail.com>
|
||||
sentence=OTA (over the air) update library. A modern looking OTA web interface to easily upload firmware updates and configure automatic firmware pulling for updating many devices at once in the background (WIP). One-click firmware rollbacks and infos about your board and firmware, directly inside your browser.
|
||||
paragraph=Includes support for OTA upload directly inside ArduinoIDE.
|
||||
category=Communication
|
||||
url=https://github.com/LostInCompilation/PrettyOTA
|
||||
architectures=esp32
|
||||
includes=PrettyOTA.h
|
||||
depends=ArduinoJson,ESP Async WebServer,Async TCP
|
||||
73
lib/PrettyOTA/src/CustomTypes.h
Normal file
73
lib/PrettyOTA/src/CustomTypes.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
Custom type declarations.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace NSPrettyOTA
|
||||
{
|
||||
enum class UPDATE_MODE : uint8_t
|
||||
{
|
||||
FIRMWARE = 0,
|
||||
FILESYSTEM
|
||||
};
|
||||
|
||||
// Return type for ESPUpdateManager
|
||||
enum class UPDATE_ERROR : uint8_t
|
||||
{
|
||||
OK = 0,
|
||||
ABORT,
|
||||
ERROR_OUT_OF_MEMORY,
|
||||
ERROR_NO_PARTITION,
|
||||
ERROR_NO_SPACE,
|
||||
ERROR_INVALID_HASH,
|
||||
ERROR_HASH_MISMATCH,
|
||||
ERROR_READ,
|
||||
ERROR_WRITE,
|
||||
ERROR_ERASE,
|
||||
ERROR_ACTIVATE,
|
||||
ERROR_MAGIC_BYTE
|
||||
};
|
||||
|
||||
// Return type for FirmwarePullManager
|
||||
enum class PULL_RESULT : uint8_t
|
||||
{
|
||||
OK = 0,
|
||||
NO_UPDATE_AVAILABLE = 1,
|
||||
NO_CONFIGURATION_PROFILE_MATCH_FOUND = 2,
|
||||
ERROR = 3
|
||||
};
|
||||
}
|
||||
401
lib/PrettyOTA/src/ESPUpdateManager.cpp
Normal file
401
lib/PrettyOTA/src/ESPUpdateManager.cpp
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
Internal handler for writing updates to ESP flash.
|
||||
|
||||
*/
|
||||
|
||||
#include "ESPUpdateManager.h"
|
||||
|
||||
using namespace NSPrettyOTA;
|
||||
|
||||
bool ESPUpdateManager::IsPartitionBootable(const esp_partition_t* const partition) const
|
||||
{
|
||||
if(!partition)
|
||||
return false;
|
||||
|
||||
uint8_t partitionData[UM_ENCRYPTED_BLOCK_SIZE] = {0};
|
||||
|
||||
// Read beginning of partition
|
||||
if(esp_partition_read(partition, 0, reinterpret_cast<uint32_t*>(partitionData), UM_ENCRYPTED_BLOCK_SIZE) != ESP_OK)
|
||||
return false;
|
||||
|
||||
// Check header magic byte
|
||||
if(partitionData[0] != ESP_IMAGE_HEADER_MAGIC)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ESPUpdateManager::CheckDataAlignment(const uint8_t* data, uint64_t size) const
|
||||
{
|
||||
// Only check 32-bit aligned blocks
|
||||
if (size == 0 || size % sizeof(uint32_t))
|
||||
return true;
|
||||
|
||||
uint64_t dwl = size / sizeof(uint32_t);
|
||||
|
||||
do {
|
||||
if (*reinterpret_cast<const uint32_t*>(data) ^ 0xffffffff)
|
||||
return true;
|
||||
|
||||
data += sizeof(uint32_t);
|
||||
} while (--dwl);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ESPUpdateManager::ResetState()
|
||||
{
|
||||
if(m_Buffer)
|
||||
delete[] m_Buffer;
|
||||
|
||||
if(m_SkipBuffer)
|
||||
delete[] m_SkipBuffer;
|
||||
|
||||
m_Buffer = nullptr;
|
||||
m_SkipBuffer = nullptr;
|
||||
|
||||
m_UpdateMode = UPDATE_MODE::FIRMWARE;
|
||||
m_UpdateSize = 0;
|
||||
m_UpdateProgress = 0;
|
||||
m_BufferSize = 0;
|
||||
m_ExpectedMD5Hash = "";
|
||||
m_TargetPartition = nullptr;
|
||||
}
|
||||
|
||||
bool ESPUpdateManager::Begin(UPDATE_MODE updateMode, const char* const expectedMD5Hash, const char* SPIFFSPartitionLabel)
|
||||
{
|
||||
if(m_UpdateSize > 0) // Already running?
|
||||
return false;
|
||||
|
||||
// Reset state
|
||||
ResetState();
|
||||
m_LastError = UPDATE_ERROR::OK;
|
||||
m_ExpectedMD5Hash = expectedMD5Hash;
|
||||
|
||||
// Convert hash to lower case
|
||||
for(char& c : m_ExpectedMD5Hash)
|
||||
c = std::tolower(c);
|
||||
|
||||
// Check hash
|
||||
if(m_ExpectedMD5Hash.length() != 32)
|
||||
{
|
||||
m_LastError = UPDATE_ERROR::ERROR_INVALID_HASH;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get target partition for update
|
||||
if(updateMode == UPDATE_MODE::FIRMWARE)
|
||||
{
|
||||
m_TargetPartition = esp_ota_get_next_update_partition(nullptr);
|
||||
if(!m_TargetPartition)
|
||||
{
|
||||
m_LastError = UPDATE_ERROR::ERROR_NO_PARTITION;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(updateMode == UPDATE_MODE::FILESYSTEM)
|
||||
{
|
||||
// Try finding SPIFFS partition (with given label) first
|
||||
m_TargetPartition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, SPIFFSPartitionLabel);
|
||||
if(!m_TargetPartition)
|
||||
{
|
||||
// No SPIFFS partition (with given label) found.
|
||||
// Fallback to searching for FAT partition (without a label)
|
||||
m_TargetPartition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, nullptr);
|
||||
if(!m_TargetPartition)
|
||||
{
|
||||
m_LastError = UPDATE_ERROR::ERROR_NO_PARTITION;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_UpdateSize = m_TargetPartition->size;
|
||||
m_UpdateMode = updateMode;
|
||||
m_MD5Hasher.Begin();
|
||||
|
||||
// Initialize update buffer
|
||||
m_Buffer = new (std::nothrow) uint8_t[SPI_FLASH_SEC_SIZE];
|
||||
if(!m_Buffer)
|
||||
{
|
||||
m_LastError = UPDATE_ERROR::ERROR_OUT_OF_MEMORY;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ESPUpdateManager::End()
|
||||
{
|
||||
if(HasError() || m_UpdateSize == 0)
|
||||
return false;
|
||||
|
||||
// Write remaining buffer
|
||||
if(m_BufferSize > 0)
|
||||
{
|
||||
if(!WriteBufferToFlash())
|
||||
return false;
|
||||
}
|
||||
|
||||
m_MD5Hasher.Calculate();
|
||||
m_UpdateSize = m_UpdateProgress;
|
||||
|
||||
// Compare expected hash to calculated firmware hash
|
||||
if(m_ExpectedMD5Hash != m_MD5Hasher.GetHashAsString())
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_HASH_MISMATCH);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify end of firmware
|
||||
if(m_UpdateMode == UPDATE_MODE::FIRMWARE)
|
||||
{
|
||||
// Enable partition by writing the stashed buffer (first 16 bytes of partition)
|
||||
if(esp_partition_write(m_TargetPartition, 0, reinterpret_cast<uint32_t*>(m_SkipBuffer), UM_ENCRYPTED_BLOCK_SIZE) != ESP_OK)
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_WRITE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!IsPartitionBootable(m_TargetPartition))
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_READ);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set boot partition
|
||||
if(esp_ota_set_boot_partition(m_TargetPartition) != ESP_OK)
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_ACTIVATE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ResetState();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ESPUpdateManager::Abort()
|
||||
{
|
||||
Abort(UPDATE_ERROR::ABORT);
|
||||
}
|
||||
|
||||
void NSPrettyOTA::ESPUpdateManager::Abort(UPDATE_ERROR reason)
|
||||
{
|
||||
ResetState();
|
||||
m_LastError = reason;
|
||||
}
|
||||
|
||||
bool NSPrettyOTA::ESPUpdateManager::WriteBufferToFlash()
|
||||
{
|
||||
uint8_t skipSize = 0;
|
||||
|
||||
// Is it the beginning of new firmware?
|
||||
if(m_UpdateProgress == 0 && m_UpdateMode == UPDATE_MODE::FIRMWARE)
|
||||
{
|
||||
// Check magic byte
|
||||
if(m_Buffer[0] != ESP_IMAGE_HEADER_MAGIC)
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_MAGIC_BYTE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stash the first 16 bytes of data and do not write them to flash now.
|
||||
// The stashed 16 bytes will be written after all data has been written to flash.
|
||||
// This way the partition stays invalid until all data and the stashed buffer has been written,
|
||||
// to prevent booting a partial firmware in case the update didn't succeed.
|
||||
skipSize = UM_ENCRYPTED_BLOCK_SIZE;
|
||||
m_SkipBuffer = new (std::nothrow) uint8_t[skipSize];
|
||||
|
||||
if(!m_SkipBuffer)
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy beginning to skip buffer
|
||||
memcpy(m_SkipBuffer, m_Buffer, skipSize);
|
||||
}
|
||||
|
||||
const uint64_t offset = m_TargetPartition->address + m_UpdateProgress;
|
||||
|
||||
// If it's the block boundary, then erase the whole block from here
|
||||
const bool eraseBlock = (m_UpdateSize - m_UpdateProgress >= UM_SPI_FLASH_BLOCK_SIZE) && (offset % UM_SPI_FLASH_BLOCK_SIZE == 0);
|
||||
|
||||
// Sector belongs to unaligned partition heading block
|
||||
const bool partitionSectorHead = (m_TargetPartition->address % UM_SPI_FLASH_BLOCK_SIZE != 0) && (offset < (m_TargetPartition->address / UM_SPI_FLASH_BLOCK_SIZE + 1) * UM_SPI_FLASH_BLOCK_SIZE);
|
||||
|
||||
// Sector belongs to unaligned partition tailing block
|
||||
const bool partitionSectorTail = (offset >= (m_TargetPartition->address + m_UpdateSize) / UM_SPI_FLASH_BLOCK_SIZE * UM_SPI_FLASH_BLOCK_SIZE);
|
||||
|
||||
if(eraseBlock || partitionSectorHead || partitionSectorTail)
|
||||
{
|
||||
if(esp_partition_erase_range(m_TargetPartition, m_UpdateProgress, eraseBlock ? UM_SPI_FLASH_BLOCK_SIZE : SPI_FLASH_SEC_SIZE) != ESP_OK)
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_ERASE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Try skipping empty blocks on unencrypted partitions
|
||||
if ((m_TargetPartition->encrypted || CheckDataAlignment(m_Buffer + (skipSize / sizeof(uint32_t)), m_BufferSize - skipSize))
|
||||
&& (esp_partition_write(m_TargetPartition, m_UpdateProgress + skipSize, reinterpret_cast<const uint32_t*>(m_Buffer) + (skipSize / sizeof(uint32_t)), m_BufferSize - skipSize) != ESP_OK))
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_WRITE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore magic byte or MD5 hash will be wrong
|
||||
if((m_UpdateProgress == 0) && (m_UpdateMode == UPDATE_MODE::FIRMWARE))
|
||||
m_Buffer[0] = ESP_IMAGE_HEADER_MAGIC;
|
||||
|
||||
// Add data to hasher
|
||||
m_MD5Hasher.AddData(m_Buffer, m_BufferSize);
|
||||
|
||||
m_UpdateProgress += m_BufferSize;
|
||||
m_BufferSize = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t ESPUpdateManager::Write(const uint8_t* const data, uint64_t size)
|
||||
{
|
||||
if(HasError() || m_UpdateSize == 0)
|
||||
return 0;
|
||||
|
||||
if(size > (m_UpdateSize - m_UpdateProgress))
|
||||
{
|
||||
Abort(UPDATE_ERROR::ERROR_NO_SPACE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t bytesLeft = size;
|
||||
|
||||
while((m_BufferSize + bytesLeft) > SPI_FLASH_SEC_SIZE)
|
||||
{
|
||||
const uint64_t toCopy = SPI_FLASH_SEC_SIZE - m_BufferSize;
|
||||
memcpy(m_Buffer + m_BufferSize, data + (size - bytesLeft), toCopy);
|
||||
|
||||
m_BufferSize += toCopy;
|
||||
|
||||
if(!WriteBufferToFlash())
|
||||
return (size - bytesLeft);
|
||||
|
||||
bytesLeft -= toCopy;
|
||||
}
|
||||
|
||||
memcpy(m_Buffer + m_BufferSize, data + (size - bytesLeft), bytesLeft);
|
||||
m_BufferSize += bytesLeft;
|
||||
|
||||
if(m_BufferSize == (m_UpdateSize - m_UpdateProgress))
|
||||
{
|
||||
if(!WriteBufferToFlash())
|
||||
return (size - bytesLeft);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
bool ESPUpdateManager::IsRollbackPossible() const
|
||||
{
|
||||
if(m_Buffer) // Update is running
|
||||
return false;
|
||||
|
||||
const esp_partition_t* const partition = esp_ota_get_next_update_partition(nullptr);
|
||||
|
||||
return IsPartitionBootable(partition);
|
||||
}
|
||||
|
||||
bool ESPUpdateManager::DoRollback()
|
||||
{
|
||||
if(m_Buffer) // Update is running
|
||||
return false;
|
||||
|
||||
const esp_partition_t* const partition = esp_ota_get_next_update_partition(nullptr);
|
||||
|
||||
if(!IsPartitionBootable(partition))
|
||||
return false;
|
||||
|
||||
if(esp_ota_set_boot_partition(partition) != ESP_OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string NSPrettyOTA::ESPUpdateManager::GetLastErrorAsString() const
|
||||
{
|
||||
switch(m_LastError)
|
||||
{
|
||||
case UPDATE_ERROR::OK:
|
||||
return "No error";
|
||||
break;
|
||||
case UPDATE_ERROR::ABORT:
|
||||
return "Aborted";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_OUT_OF_MEMORY:
|
||||
return "ERROR_OUT_OF_MEMORY: No available memory for allocation";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_NO_PARTITION:
|
||||
return "ERROR_NO_PARTITION: Partition could not be found";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_NO_SPACE:
|
||||
return "ERROR_NO_SPACE: Not enough free space";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_INVALID_HASH:
|
||||
return "ERROR_INVALID_HASH: Invalid MD5 hash";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_HASH_MISMATCH:
|
||||
return "ERROR_HASH_MISMATCH: The firmware hash does not match the expected hash";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_READ:
|
||||
return "ERROR_READ: Could not read flash";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_WRITE:
|
||||
return "ERROR_WRITE: Could not write flash";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_ERASE:
|
||||
return "ERROR_ERASE: Could not erase flash";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_ACTIVATE:
|
||||
return "ERROR_ACTIVATE: Could not activate target partition";
|
||||
break;
|
||||
case UPDATE_ERROR::ERROR_MAGIC_BYTE:
|
||||
return "ERROR_MAGIC_BYTE: Magic byte is invalid";
|
||||
break;
|
||||
|
||||
default:
|
||||
return "Unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
106
lib/PrettyOTA/src/ESPUpdateManager.h
Normal file
106
lib/PrettyOTA/src/ESPUpdateManager.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
Internal handler for writing updates to ESP flash.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// C++ API
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string> // For std::string
|
||||
#include <string.h> // For memcpy, memset
|
||||
|
||||
// ESP-IDF
|
||||
#include <esp_err.h>
|
||||
#include <esp_spi_flash.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_app_format.h>
|
||||
|
||||
// PrettyOTA
|
||||
#include "CustomTypes.h"
|
||||
#include "MD5Hasher.h"
|
||||
|
||||
namespace NSPrettyOTA
|
||||
{
|
||||
class ESPUpdateManager
|
||||
{
|
||||
private:
|
||||
// Constants
|
||||
static const uint8_t UM_ENCRYPTED_BLOCK_SIZE = 16;
|
||||
static const uint8_t UM_SPI_SECTORS_PER_BLOCK = 16;
|
||||
static const uint32_t UM_SPI_FLASH_BLOCK_SIZE = (UM_SPI_SECTORS_PER_BLOCK * SPI_FLASH_SEC_SIZE);
|
||||
|
||||
private:
|
||||
UPDATE_ERROR m_LastError = UPDATE_ERROR::OK;
|
||||
UPDATE_MODE m_UpdateMode = UPDATE_MODE::FIRMWARE;
|
||||
|
||||
uint64_t m_UpdateSize = 0;
|
||||
uint64_t m_UpdateProgress = 0;
|
||||
uint64_t m_BufferSize = 0;
|
||||
|
||||
uint8_t* m_Buffer = nullptr;
|
||||
uint8_t* m_SkipBuffer = nullptr;
|
||||
|
||||
std::string m_ExpectedMD5Hash = "";
|
||||
MD5Hasher m_MD5Hasher;
|
||||
|
||||
const esp_partition_t* m_TargetPartition = nullptr;
|
||||
|
||||
// Methods
|
||||
void ResetState();
|
||||
void Abort(UPDATE_ERROR reason);
|
||||
bool WriteBufferToFlash();
|
||||
|
||||
// Helper
|
||||
bool IsPartitionBootable(const esp_partition_t* const partition) const;
|
||||
bool CheckDataAlignment(const uint8_t* data, uint64_t size) const;
|
||||
|
||||
public:
|
||||
ESPUpdateManager() = default;
|
||||
|
||||
bool Begin(UPDATE_MODE updateMode, const char* const expectedMD5Hash, const char* const SPIFFSPartitionLabel = nullptr);
|
||||
bool End();
|
||||
void Abort();
|
||||
|
||||
uint64_t Write(const uint8_t* const data, uint64_t size);
|
||||
|
||||
bool HasError() const { return (m_LastError != UPDATE_ERROR::OK); }
|
||||
UPDATE_ERROR GetLastError() const { return m_LastError; }
|
||||
std::string GetLastErrorAsString() const;
|
||||
|
||||
bool IsRollbackPossible() const;
|
||||
bool DoRollback();
|
||||
};
|
||||
}
|
||||
281
lib/PrettyOTA/src/FirmwarePullManager.cpp
Normal file
281
lib/PrettyOTA/src/FirmwarePullManager.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
The main source file.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
====================================================================================
|
||||
Example Json file:
|
||||
|
||||
{
|
||||
"Configuration": [
|
||||
{
|
||||
"HardwareID": ["Board1", "Board2", "Board3"],
|
||||
"CustomFilter": "custom1",
|
||||
"Version": "2.0.0",
|
||||
"FirmwareURL": "https://mydomain.com/firmware/board1_2_3.fw_v2_0_0.bin"
|
||||
},
|
||||
{
|
||||
"HardwareID": "Board10",
|
||||
"Version": "3.0.0",
|
||||
"FirmwareURL": "https://mydomain.com/firmware/board10.fw_v3_0_0.bin"
|
||||
},
|
||||
{
|
||||
"HardwareID": "Board99",
|
||||
"CustomFilter": "custom99",
|
||||
"Version": "10.0.0",
|
||||
"FirmwareURL": "https://mydomain.com/firmware/board99.fw_v10_0_0.bin"
|
||||
},
|
||||
{
|
||||
"HardwareID": "Invalid",
|
||||
"Version": "10.0.0",
|
||||
"FirmwareURL": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
====================================================================================
|
||||
|
||||
*/
|
||||
|
||||
#include "FirmwarePullManager.h"
|
||||
|
||||
using namespace NSPrettyOTA;
|
||||
|
||||
void FirmwarePullManager::Begin(Stream* const serialStream, std::function<void(NSPrettyOTA::UPDATE_MODE updateMode)> onStart,
|
||||
std::function<void(uint32_t currentSize, uint32_t totalSize)> onProgress,
|
||||
std::function<void(bool successful)> onEnd)
|
||||
{
|
||||
m_SerialMonitorStream = serialStream;
|
||||
|
||||
m_OnStartUpdate = onStart;
|
||||
m_OnProgressUpdate = onProgress;
|
||||
m_OnEndUpdate = onEnd;
|
||||
}
|
||||
|
||||
void FirmwarePullManager::Log(const std::string& message)
|
||||
{
|
||||
if(!m_SerialMonitorStream)
|
||||
return;
|
||||
|
||||
m_SerialMonitorStream->println(("[FirmwarePullManager] " + message).c_str());
|
||||
}
|
||||
|
||||
PULL_RESULT FirmwarePullManager::CheckForNewFirmwareAvailable(const char* const jsonURL, std::string& out_firmwareURL)
|
||||
{
|
||||
out_firmwareURL = "";
|
||||
|
||||
this->Log("Info: Checking for new firmware version...");
|
||||
|
||||
HTTPClient http;
|
||||
http.useHTTP10(true);
|
||||
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
||||
if(!http.begin(jsonURL))
|
||||
{
|
||||
this->Log("Error: Could not initialize HTTPClient");
|
||||
return PULL_RESULT::ERROR;
|
||||
}
|
||||
|
||||
// Send HTTP GET request
|
||||
const int32_t response = http.GET();
|
||||
if(response != 200)
|
||||
{
|
||||
http.end();
|
||||
this->Log("Error (Json download): Server replied with HTTP code: " + std::to_string(response));
|
||||
return PULL_RESULT::ERROR;
|
||||
}
|
||||
|
||||
// Get received data as stream
|
||||
Stream* const stream = http.getStreamPtr();
|
||||
if(!stream)
|
||||
{
|
||||
http.end();
|
||||
this->Log("Error: Received Json is empty");
|
||||
return PULL_RESULT::ERROR;
|
||||
}
|
||||
|
||||
// Parse received Json
|
||||
JsonDocument json;
|
||||
const DeserializationError jsonError = deserializeJson(json, *stream);
|
||||
if(jsonError)
|
||||
{
|
||||
http.end();
|
||||
this->Log("Error: Could not parse Json (" + std::string(jsonError.c_str()) + ")");
|
||||
return PULL_RESULT::ERROR;
|
||||
}
|
||||
|
||||
// End connection
|
||||
http.end();
|
||||
|
||||
// Are there any "Configuration" entries
|
||||
if(json["Configuration"].as<JsonArray>().size() == 0)
|
||||
{
|
||||
this->Log("Error (Json): No valid \"Configuration\" entries found");
|
||||
return PULL_RESULT::ERROR;
|
||||
}
|
||||
|
||||
// Search if Json contains matching profile
|
||||
// Iterate all "Configuration" entries
|
||||
for (const auto configuration : json["Configuration"].as<JsonArray>())
|
||||
{
|
||||
// **********************************************************
|
||||
// Search matching HardwareID
|
||||
bool foundHardwareIDMatch = false;
|
||||
if(configuration["HardwareID"].is<JsonArray>()) // HardwareID is array
|
||||
{
|
||||
for (auto i : configuration["HardwareID"].as<JsonArray>())
|
||||
{
|
||||
if(i.as<std::string>() == m_HardwareID)
|
||||
{
|
||||
foundHardwareIDMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(configuration["HardwareID"].is<std::string>()) // HardwareID is single string (only one HardwareID)
|
||||
{
|
||||
if(configuration["HardwareID"].as<std::string>() == m_HardwareID)
|
||||
foundHardwareIDMatch = true;
|
||||
}
|
||||
else // HardwareID entry not found
|
||||
{
|
||||
this->Log("Error (Json): No valid \"HardwareID\" found in \"Configuration\". Skipping entry...");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Go to next "Configuration" if no HardwareID matched
|
||||
if(!foundHardwareIDMatch)
|
||||
continue;
|
||||
|
||||
// **********************************************************
|
||||
// Search matching CustomFilter
|
||||
bool foundCustomFilterMatch = false;
|
||||
if(configuration["CustomFilter"].is<std::string>())
|
||||
{
|
||||
if(configuration["CustomFilter"].as<std::string>() == m_CustomFilter)
|
||||
foundCustomFilterMatch = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no CustomFilter entry is present, set as match
|
||||
foundCustomFilterMatch = true;
|
||||
}
|
||||
|
||||
// Go to next "Configuration" if no CustomFilter matched
|
||||
if(!foundCustomFilterMatch)
|
||||
continue;
|
||||
|
||||
// **********************************************************
|
||||
// Check if version is present
|
||||
if(!configuration["Version"].is<std::string>() || configuration["Version"].as<std::string>().length() == 0)
|
||||
{
|
||||
this->Log("Error (Json): No valid \"Version\" found in \"Configuration\". Skipping entry...");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if version is newer (or different if downgrade is allowed)
|
||||
bool newVersionAvailable = false;
|
||||
if(m_AllowDowngrade)
|
||||
{
|
||||
if(configuration["Version"].as<std::string>() != m_CurrentAppVersion)
|
||||
newVersionAvailable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use lexicographical comparison
|
||||
if(configuration["Version"].as<std::string>() > m_CurrentAppVersion)
|
||||
newVersionAvailable = true;
|
||||
}
|
||||
|
||||
if(!newVersionAvailable)
|
||||
{
|
||||
this->Log("Info: No updated firmware version available (Current: " + std::string(m_CurrentAppVersion) + ", New: " + configuration["Version"].as<std::string>() + ")");
|
||||
return PULL_RESULT::NO_UPDATE_AVAILABLE;
|
||||
}
|
||||
|
||||
this->Log("Info: New firmware version available (Current: " + std::string(m_CurrentAppVersion) + ", New: " + configuration["Version"].as<std::string>() + ")");
|
||||
|
||||
// **********************************************************
|
||||
// Get firmware URL
|
||||
if(!configuration["FirmwareURL"].is<std::string>() || configuration["FirmwareURL"].as<std::string>().length() == 0)
|
||||
{
|
||||
this->Log("Error (Json): No valid \"FirmwareURL\" found in \"Configuration\"");
|
||||
continue;
|
||||
}
|
||||
|
||||
out_firmwareURL = configuration["FirmwareURL"].as<std::string>();
|
||||
|
||||
return PULL_RESULT::OK;
|
||||
}
|
||||
|
||||
this->Log("Warning: No matching profile found in Json");
|
||||
|
||||
return PULL_RESULT::NO_CONFIGURATION_PROFILE_MATCH_FOUND;
|
||||
}
|
||||
|
||||
PULL_RESULT FirmwarePullManager::RunPullUpdate(const char* const jsonURL)
|
||||
{
|
||||
std::string firmwareURL = "";
|
||||
|
||||
const PULL_RESULT result = CheckForNewFirmwareAvailable(jsonURL, firmwareURL);
|
||||
if(result != PULL_RESULT::OK)
|
||||
return result;
|
||||
|
||||
// Download firmware file
|
||||
HTTPClient http;
|
||||
http.useHTTP10(true);
|
||||
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
||||
if(!http.begin(firmwareURL.c_str()))
|
||||
{
|
||||
this->Log("Error: Could not initialize HTTPClient");
|
||||
return PULL_RESULT::ERROR;
|
||||
}
|
||||
|
||||
// Send HTTP GET request
|
||||
const int32_t response = http.GET();
|
||||
if(response != 200)
|
||||
{
|
||||
http.end();
|
||||
this->Log("Error (firmware download): Server replied with HTTP code: " + std::to_string(response));
|
||||
return PULL_RESULT::ERROR;
|
||||
}
|
||||
|
||||
const int32_t firmwareSize = http.getSize();
|
||||
uint8_t buffer[1280] = { 0 };
|
||||
|
||||
// End connection
|
||||
http.end();
|
||||
|
||||
return PULL_RESULT::OK;
|
||||
}
|
||||
85
lib/PrettyOTA/src/FirmwarePullManager.h
Normal file
85
lib/PrettyOTA/src/FirmwarePullManager.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
Internal handler for writing updates to ESP flash.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// C++ API
|
||||
#include <cstdint>
|
||||
//#include <functional>
|
||||
#include <string> // For std::string
|
||||
|
||||
// Arduino dependencies
|
||||
#include <HTTPClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// PrettyOTA
|
||||
#include "CustomTypes.h"
|
||||
|
||||
namespace NSPrettyOTA
|
||||
{
|
||||
class FirmwarePullManager
|
||||
{
|
||||
private:
|
||||
Stream* m_SerialMonitorStream = nullptr;
|
||||
|
||||
bool m_AllowDowngrade = false;
|
||||
std::string m_HardwareID = "";
|
||||
std::string m_CustomFilter = "";
|
||||
std::string m_CurrentAppVersion = "";
|
||||
|
||||
// User callbacks
|
||||
std::function<void(NSPrettyOTA::UPDATE_MODE updateMode)> m_OnStartUpdate = nullptr;
|
||||
std::function<void(uint32_t currentSize, uint32_t totalSize)> m_OnProgressUpdate = nullptr;
|
||||
std::function<void(bool successful)> m_OnEndUpdate = nullptr;
|
||||
|
||||
void Log(const std::string& message);
|
||||
|
||||
public:
|
||||
FirmwarePullManager() = default;
|
||||
void Begin(Stream* const serialStream,
|
||||
std::function<void(NSPrettyOTA::UPDATE_MODE updateMode)> onStart,
|
||||
std::function<void(uint32_t currentSize, uint32_t totalSize)> onProgress,
|
||||
std::function<void(bool successful)> onEnd);
|
||||
|
||||
PULL_RESULT CheckForNewFirmwareAvailable(const char* const jsonURL, std::string& out_firmwareURL);
|
||||
PULL_RESULT RunPullUpdate(const char* const jsonURL);
|
||||
|
||||
void SetHardwareID(const char* const hardwareID) { m_HardwareID = hardwareID; }
|
||||
void SetCustomFilter(const char* const customFilter) { m_CustomFilter = customFilter; }
|
||||
void SetCurrentAppVersion(const char* const currentAppVersion) { m_CurrentAppVersion = currentAppVersion; }
|
||||
|
||||
void SetAllowDowngrade(bool allowDowngrade) { m_AllowDowngrade = allowDowngrade; }
|
||||
};
|
||||
}
|
||||
74
lib/PrettyOTA/src/MD5Hasher.cpp
Normal file
74
lib/PrettyOTA/src/MD5Hasher.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
Helper class for building MD5 hashes.
|
||||
|
||||
*/
|
||||
|
||||
#include "MD5Hasher.h"
|
||||
|
||||
using namespace NSPrettyOTA;
|
||||
|
||||
void MD5Hasher::Begin()
|
||||
{
|
||||
memset(m_Buffer, 0x00, ESP_ROM_MD5_DIGEST_LEN);
|
||||
esp_rom_md5_init(&m_Context);
|
||||
}
|
||||
|
||||
void MD5Hasher::AddData(const uint8_t* data, uint32_t size)
|
||||
{
|
||||
esp_rom_md5_update(&m_Context, data, size);
|
||||
}
|
||||
|
||||
void MD5Hasher::AddData(const char* data, uint32_t size)
|
||||
{
|
||||
AddData(reinterpret_cast<const uint8_t*>(data), size);
|
||||
}
|
||||
|
||||
void MD5Hasher::Calculate()
|
||||
{
|
||||
esp_rom_md5_final(m_Buffer, &m_Context);
|
||||
}
|
||||
|
||||
void MD5Hasher::GetHashAsBytes(uint8_t out[ESP_ROM_MD5_DIGEST_LEN]) const
|
||||
{
|
||||
memcpy(out, m_Buffer, ESP_ROM_MD5_DIGEST_LEN);
|
||||
}
|
||||
|
||||
std::string MD5Hasher::GetHashAsString() const
|
||||
{
|
||||
char out_MD5Str[MD5_HASH_STR_SIZE];
|
||||
|
||||
for(uint8_t i = 0; i < ESP_ROM_MD5_DIGEST_LEN; i++)
|
||||
sprintf(out_MD5Str + (i * 2), "%02x", m_Buffer[i]);
|
||||
|
||||
return out_MD5Str;
|
||||
}
|
||||
65
lib/PrettyOTA/src/MD5Hasher.h
Normal file
65
lib/PrettyOTA/src/MD5Hasher.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
Helper class for building MD5 hashes.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string> // For std::string
|
||||
#include <string.h> // For memcpy, memset
|
||||
|
||||
#include <esp_system.h>
|
||||
#include <esp_rom_md5.h>
|
||||
|
||||
namespace NSPrettyOTA
|
||||
{
|
||||
class MD5Hasher
|
||||
{
|
||||
private:
|
||||
md5_context_t m_Context;
|
||||
uint8_t m_Buffer[ESP_ROM_MD5_DIGEST_LEN] = {0};
|
||||
|
||||
public:
|
||||
static const uint8_t MD5_HASH_STR_SIZE = (2 * ESP_ROM_MD5_DIGEST_LEN + 1);
|
||||
|
||||
MD5Hasher() = default;
|
||||
|
||||
void Begin();
|
||||
void AddData(const uint8_t* data, uint32_t size);
|
||||
void AddData(const char* data, uint32_t size);
|
||||
void Calculate();
|
||||
|
||||
void GetHashAsBytes(uint8_t out[ESP_ROM_MD5_DIGEST_LEN]) const;
|
||||
std::string GetHashAsString() const;
|
||||
};
|
||||
}
|
||||
1287
lib/PrettyOTA/src/PrettyOTA.cpp
Normal file
1287
lib/PrettyOTA/src/PrettyOTA.cpp
Normal file
File diff suppressed because it is too large
Load Diff
222
lib/PrettyOTA/src/PrettyOTA.h
Normal file
222
lib/PrettyOTA/src/PrettyOTA.h
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
The main header file. Include this file in your project.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// ********************************************************
|
||||
// Settings
|
||||
#define PRETTY_OTA_ENABLE_ARDUINO_OTA 1
|
||||
#define DEV_PRETTY_OTA_ENABLE_FIRMWARE_PULLING 0 // Do not change
|
||||
|
||||
// std-lib
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <new> //std::nothrow
|
||||
|
||||
// Arduino include
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#if (PRETTY_OTA_ENABLE_ARDUINO_OTA == 1)
|
||||
#include <ArduinoOTA.h>
|
||||
#endif
|
||||
|
||||
// ESP-IDF
|
||||
#include <esp_err.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <mdns.h>
|
||||
|
||||
// Arduino dependencies
|
||||
#include <AsyncTCP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
// PrettyOTA includes
|
||||
#include "CustomTypes.h"
|
||||
#include "MD5Hasher.h"
|
||||
#include "ESPUpdateManager.h"
|
||||
|
||||
#if (DEV_PRETTY_OTA_ENABLE_FIRMWARE_PULLING == 1)
|
||||
#include "FirmwarePullManager.h"
|
||||
#endif
|
||||
|
||||
// ********************************************************
|
||||
// Compile checks
|
||||
#ifndef ESP32
|
||||
#error PrettyOTA only supports ESP32 devices. Support for RaspberryPi Pico W will follow soon.
|
||||
#endif
|
||||
|
||||
// Is it the correct version and fork of ESP32AsyncWebServer?
|
||||
#if !defined(ASYNCWEBSERVER_VERSION) || ASYNCWEBSERVER_VERSION_MAJOR < 3
|
||||
#error PrettyOTA needs the "ESPAsyncWebServer" library (from ESP32Async) version 3.0 or newer. If you have it installed, make sure you only have one library with the name "ESPAsyncWebServer" installed (there are two libraries with the same name).
|
||||
#endif
|
||||
|
||||
// Is it the correct version of ArduinoJson?
|
||||
#if !defined(ARDUINOJSON_VERSION_MAJOR) || ARDUINOJSON_VERSION_MAJOR < 7
|
||||
#error PrettyOTA needs the "ArduinoJson" library version 7.0 or newer.
|
||||
#endif
|
||||
|
||||
class PrettyOTA
|
||||
{
|
||||
private:
|
||||
// Constants
|
||||
static const uint8_t PRETTY_OTA_VERSION_MAJOR = 1;
|
||||
static const uint8_t PRETTY_OTA_VERSION_MINOR = 1;
|
||||
static const uint8_t PRETTY_OTA_VERSION_REVISION = 3;
|
||||
|
||||
static const uint32_t BACKGROUND_TASK_STACK_SIZE = 3072;
|
||||
static const uint8_t BACKGROUND_TASK_PRIORITY = 4;
|
||||
|
||||
static const uint8_t MAX_NUM_LOGGED_IN_CLIENTS = 5;
|
||||
|
||||
// Website code
|
||||
static const uint8_t PRETTY_OTA_WEBSITE_DATA[12706];
|
||||
static const uint8_t PRETTY_OTA_LOGIN_DATA[6208];
|
||||
|
||||
private:
|
||||
// UUID generation
|
||||
using UUID_t = uint8_t[16];
|
||||
|
||||
void GenerateUUID(UUID_t* out_uuid) const;
|
||||
std::string UUIDToString(const UUID_t uuid) const;
|
||||
|
||||
private:
|
||||
// Variables
|
||||
static std::string m_AppBuildTime;
|
||||
static std::string m_AppBuildDate;
|
||||
static std::string m_AppVersion;
|
||||
static std::string m_HardwareID;
|
||||
|
||||
std::string m_LoginURL = "";
|
||||
std::string m_MainURL = "";
|
||||
|
||||
static Stream* m_SerialMonitorStream;
|
||||
AsyncWebServer* m_Server = nullptr;
|
||||
NSPrettyOTA::ESPUpdateManager m_UpdateManager;
|
||||
|
||||
#if (DEV_PRETTY_OTA_ENABLE_FIRMWARE_PULLING == 1)
|
||||
NSPrettyOTA::FirmwarePullManager m_FirmwarePullManager;
|
||||
#endif
|
||||
|
||||
bool m_IsInitialized = false;
|
||||
bool m_IsUpdateRunning = false;
|
||||
bool m_AutoRebootEnabled = true;
|
||||
bool m_RequestReboot = false;
|
||||
static bool m_DefaultCallbackPrintWithColor;
|
||||
uint32_t m_RebootRequestTime = 0;
|
||||
|
||||
// Authentication
|
||||
bool m_AuthenticationEnabled = false;
|
||||
std::string m_Username = "";
|
||||
std::string m_Password = "";
|
||||
std::vector<std::string> m_AuthenticatedSessionIDs;
|
||||
|
||||
// User callbacks
|
||||
std::function<void(NSPrettyOTA::UPDATE_MODE updateMode)> m_OnStartUpdate = nullptr;
|
||||
std::function<void(uint32_t currentSize, uint32_t totalSize)> m_OnProgressUpdate = nullptr;
|
||||
std::function<void(bool successful)> m_OnEndUpdate = nullptr;
|
||||
|
||||
private:
|
||||
// Default callback functions
|
||||
static void OnOTAStart(NSPrettyOTA::UPDATE_MODE updateMode);
|
||||
static void OnOTAProgress(uint32_t currentSize, uint32_t totalSize);
|
||||
static void OnOTAEnd(bool successful);
|
||||
|
||||
// Log functions
|
||||
static void P_LOG_I(const std::string& message);
|
||||
static void P_LOG_W(const std::string& message);
|
||||
static void P_LOG_E(const std::string& message);
|
||||
|
||||
// Methods
|
||||
static void BackgroundTask(void* parameter);
|
||||
|
||||
void EnableArduinoOTA(const char* const password, bool passwordIsMD5Hash, uint16_t OTAport);
|
||||
bool IsAuthenticated(const AsyncWebServerRequest* const request) const;
|
||||
|
||||
// NVS storage
|
||||
bool SaveSessionIDsToNVS();
|
||||
bool LoadSessionIDsFromNVS();
|
||||
|
||||
// Helper
|
||||
std::string GetVersionAsString() const;
|
||||
//std::string SHA256ToString(const uint8_t hash[32]) const;
|
||||
|
||||
public:
|
||||
PrettyOTA() = default;
|
||||
|
||||
bool Begin(AsyncWebServer* const server, const char* const username = "", const char* const password = "", bool passwordIsMD5Hash = false, const char* const mainURL = "/update", const char* const loginURL = "/login", uint16_t OTAport = 3232);
|
||||
void SetAuthenticationDetails(const char* const username, const char* const password, bool passwordIsMD5Hash = false);
|
||||
|
||||
#if (DEV_PRETTY_OTA_ENABLE_FIRMWARE_PULLING == 1)
|
||||
bool DoFirmwarePull(const char* const customFilter);
|
||||
#endif
|
||||
|
||||
// Is an update running? (web interface or pulling in background)
|
||||
bool IsUpdateRunning() const { return m_IsUpdateRunning; }
|
||||
|
||||
// Set user callbacks
|
||||
void OnStart(std::function<void(NSPrettyOTA::UPDATE_MODE updateMode)> func) { m_OnStartUpdate = func; }
|
||||
void OnProgress(std::function<void(uint32_t currentSize, uint32_t totalSize)> func) { m_OnProgressUpdate = func; }
|
||||
void OnEnd(std::function<void(bool successful)> func) { m_OnEndUpdate = func; }
|
||||
|
||||
// Use built in callbacks that print info to the serial monitor
|
||||
void UseDefaultCallbacks(bool printWithColor = false);
|
||||
|
||||
// Set the HardwareID. It should be a unique identifier for your hardware/board
|
||||
void SetHardwareID(const char* const hardwareID) { m_HardwareID = hardwareID; }
|
||||
|
||||
// Set app version
|
||||
static void SetAppVersion(const char* const appVersion);
|
||||
// Alias for backwards compatibility. DO NOT USE
|
||||
[[deprecated("Use SetAppVersion() instead.")]]
|
||||
static constexpr auto OverwriteAppVersion = SetAppVersion;
|
||||
|
||||
// Set build time and date
|
||||
static void SetAppBuildTimeAndDate(const char* const appBuildTime, const char* const appBuildDate);
|
||||
// Alias for backwards compatibility. DO NOT USE
|
||||
[[deprecated("Use SetAppBuildTimeAndDate() instead.")]]
|
||||
static constexpr auto OverwriteAppBuildTimeAndDate = SetAppBuildTimeAndDate;
|
||||
|
||||
// Set the Stream to write log messages too (Example: Use &Serial as argument)
|
||||
void SetSerialOutputStream(Stream* const serialStream) { m_SerialMonitorStream = serialStream; }
|
||||
};
|
||||
|
||||
// ********************************************************
|
||||
// Helper macro to be able to set build time and date when using ArduinoIDE.
|
||||
// This is not required for PlatformIO, however you can use it to overwrite the
|
||||
// build time and date read by PrettyOTA from the firmware image itself
|
||||
// using esp_ota_get_app_description().
|
||||
#define PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE() PrettyOTA::SetAppBuildTimeAndDate(__TIME__, __DATE__)
|
||||
131
lib/PrettyOTA/src/PrettyOTACallbacks.cpp
Normal file
131
lib/PrettyOTA/src/PrettyOTACallbacks.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
Default callbacks for PrettyOTA.
|
||||
|
||||
*/
|
||||
|
||||
#include "PrettyOTA.h"
|
||||
|
||||
const char* const ROW_OF_STARS = "************************************************";
|
||||
|
||||
// ********************************************************
|
||||
// OTA default callbacks
|
||||
void PrettyOTA::OnOTAStart(NSPrettyOTA::UPDATE_MODE updateMode)
|
||||
{
|
||||
if (!m_SerialMonitorStream)
|
||||
return;
|
||||
|
||||
m_SerialMonitorStream->println("\n");
|
||||
m_SerialMonitorStream->println(ROW_OF_STARS);
|
||||
|
||||
if(m_DefaultCallbackPrintWithColor)
|
||||
m_SerialMonitorStream->println("* \033[1;7m OTA UPDATE \033[0m *");
|
||||
else
|
||||
m_SerialMonitorStream->println("* OTA UPDATE *");
|
||||
|
||||
if(m_DefaultCallbackPrintWithColor)
|
||||
{
|
||||
if(updateMode == NSPrettyOTA::UPDATE_MODE::FIRMWARE)
|
||||
m_SerialMonitorStream->println("* \033[1mFirmware\033[0m *");
|
||||
else
|
||||
m_SerialMonitorStream->println("* \033[1mFilesystem\033[0m *");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(updateMode == NSPrettyOTA::UPDATE_MODE::FIRMWARE)
|
||||
m_SerialMonitorStream->println("* Firmware *");
|
||||
else
|
||||
m_SerialMonitorStream->println("* Filesystem *");
|
||||
}
|
||||
|
||||
m_SerialMonitorStream->println(ROW_OF_STARS);
|
||||
m_SerialMonitorStream->println("\n");
|
||||
m_SerialMonitorStream->println("Starting OTA update...");
|
||||
}
|
||||
|
||||
void PrettyOTA::OnOTAProgress(uint32_t currentSize, uint32_t totalSize)
|
||||
{
|
||||
if (!m_SerialMonitorStream)
|
||||
return;
|
||||
|
||||
static float lastPercentage = 0.0f;
|
||||
const float percentage = 100.0f * static_cast<float>(currentSize) / static_cast<float>(totalSize);
|
||||
const uint8_t numBarsToShow = static_cast<uint8_t>(percentage / 3.3333f);
|
||||
|
||||
if(percentage - lastPercentage >= 2.0f)
|
||||
{
|
||||
// Print progress bar
|
||||
m_SerialMonitorStream->print("Updating... [");
|
||||
for(uint8_t i = 0; i < 30; i++)
|
||||
{
|
||||
if (i < numBarsToShow)
|
||||
m_SerialMonitorStream->print("=");
|
||||
else
|
||||
m_SerialMonitorStream->print(" ");
|
||||
}
|
||||
m_SerialMonitorStream->printf("] %02u%%\n", static_cast<uint8_t>(percentage));
|
||||
|
||||
if(m_DefaultCallbackPrintWithColor)
|
||||
m_SerialMonitorStream->print("\033[1F"); // Move cursor to begining of previous line
|
||||
|
||||
lastPercentage = percentage;
|
||||
}
|
||||
}
|
||||
|
||||
void PrettyOTA::OnOTAEnd(bool successful)
|
||||
{
|
||||
if (!m_SerialMonitorStream)
|
||||
return;
|
||||
|
||||
if (successful)
|
||||
m_SerialMonitorStream->println("Updating... [==============================] 100%");
|
||||
|
||||
m_SerialMonitorStream->println("");
|
||||
m_SerialMonitorStream->println(ROW_OF_STARS);
|
||||
|
||||
if(m_DefaultCallbackPrintWithColor)
|
||||
{
|
||||
if (successful)
|
||||
m_SerialMonitorStream->println("* \033[1;92;7m OTA UPDATE SUCCESSFUL \033[0m *");
|
||||
else
|
||||
m_SerialMonitorStream->println("* \033[1;91;7m OTA UPDATE FAILED \033[0m *");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (successful)
|
||||
m_SerialMonitorStream->println("* OTA UPDATE SUCCESSFUL *");
|
||||
else
|
||||
m_SerialMonitorStream->println("* OTA UPDATE FAILED *");
|
||||
}
|
||||
|
||||
m_SerialMonitorStream->println(ROW_OF_STARS);
|
||||
m_SerialMonitorStream->println("");
|
||||
}
|
||||
106
lib/PrettyOTA/src/PrettyOTAUtils.cpp
Normal file
106
lib/PrettyOTA/src/PrettyOTAUtils.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025 Marc Schöndorf
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Branding or white-labeling (changing the logo and name of PrettyOTA) is permitted only
|
||||
with a commercial license. See README for details.
|
||||
|
||||
Permission is granted to anyone to use this software for private and commercial
|
||||
applications, to alter it and redistribute it, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented. You must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment is required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. You are not allowed to change the logo or name of PrettyOTA without a commercial
|
||||
license, even when redistributing modified source code.
|
||||
4. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
******************************************************
|
||||
* PRETTY OTA *
|
||||
* *
|
||||
* A better looking Web-OTA. *
|
||||
******************************************************
|
||||
|
||||
Description:
|
||||
Utils and helpers for PrettyOTA.
|
||||
|
||||
*/
|
||||
|
||||
#include "PrettyOTA.h"
|
||||
|
||||
// ********************************************************
|
||||
// Log functions
|
||||
void PrettyOTA::P_LOG_I(const std::string& message)
|
||||
{
|
||||
if (!m_SerialMonitorStream)
|
||||
return;
|
||||
|
||||
m_SerialMonitorStream->println(("[PrettyOTA] Info: " + message).c_str());
|
||||
}
|
||||
|
||||
void PrettyOTA::P_LOG_W(const std::string& message)
|
||||
{
|
||||
if (!m_SerialMonitorStream)
|
||||
return;
|
||||
|
||||
m_SerialMonitorStream->println(("[PrettyOTA] Warning: " + message).c_str());
|
||||
}
|
||||
|
||||
void PrettyOTA::P_LOG_E(const std::string& message)
|
||||
{
|
||||
if (!m_SerialMonitorStream)
|
||||
return;
|
||||
|
||||
m_SerialMonitorStream->println(("[PrettyOTA] Error: " + message).c_str());
|
||||
}
|
||||
|
||||
// ********************************************************
|
||||
// Get PrettyOTA version string
|
||||
std::string PrettyOTA::GetVersionAsString() const
|
||||
{
|
||||
return std::to_string(PRETTY_OTA_VERSION_MAJOR) + "." +
|
||||
std::to_string(PRETTY_OTA_VERSION_MINOR) + "." +
|
||||
std::to_string(PRETTY_OTA_VERSION_REVISION);
|
||||
}
|
||||
|
||||
// ********************************************************
|
||||
// UUID helpers
|
||||
void PrettyOTA::GenerateUUID(UUID_t* out_uuid) const
|
||||
{
|
||||
esp_fill_random(*out_uuid, sizeof(UUID_t));
|
||||
|
||||
(*out_uuid)[6] = 0x40 | ((*out_uuid)[6] & 0xF); // UUID version
|
||||
(*out_uuid)[8] = (0x80 | (*out_uuid)[8]) & ~0x40; // UUID variant
|
||||
}
|
||||
|
||||
std::string PrettyOTA::UUIDToString(const UUID_t uuid) const
|
||||
{
|
||||
char out[37] = {};
|
||||
|
||||
snprintf(out, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// ********************************************************
|
||||
// SHA256 helpers
|
||||
/*std::string PrettyOTA::SHA256ToString(const uint8_t hash[32]) const
|
||||
{
|
||||
static const char* const SHA256StringLookup = "0123456789abcdef";
|
||||
|
||||
std::string result = "";
|
||||
for(uint32_t i = 0; i < 32; i++)
|
||||
{
|
||||
result += SHA256StringLookup[hash[i] >> 4];
|
||||
result += SHA256StringLookup[hash[i] & 0x0F];
|
||||
}
|
||||
return result;
|
||||
}*/
|
||||
@@ -12,8 +12,7 @@
|
||||
#include <esp_now.h>
|
||||
#include <esp_wifi.h>
|
||||
|
||||
|
||||
|
||||
#include <battery.h>
|
||||
#include <communication.h>
|
||||
#include <databasebackend.h>
|
||||
#include <debug.h>
|
||||
@@ -22,8 +21,6 @@
|
||||
#include <timesync.h>
|
||||
#include <webserverrouter.h>
|
||||
#include <wificlass.h>
|
||||
#include <battery.h>
|
||||
|
||||
|
||||
const char *firmwareversion = "1.0.0"; // Version der Firmware
|
||||
|
||||
@@ -300,15 +297,14 @@ void setup() {
|
||||
setupWebSocket();
|
||||
setupLED();
|
||||
setupMqttServer(); // MQTT Server initialisieren
|
||||
//setupBattery();
|
||||
//setupRFID();
|
||||
// setupBattery();
|
||||
// setupRFID();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
loopOTA(); // OTA Loop aufrufen
|
||||
checkAutoReset();
|
||||
loopMqttServer(); // MQTT Server in der Loop aufrufen
|
||||
loopWebSocket();
|
||||
//loopBattery(); // Batterie-Loop aufrufen
|
||||
//loopRFID(); // RFID Loop aufrufen
|
||||
// loopBattery(); // Batterie-Loop aufrufen
|
||||
// loopRFID(); // RFID Loop aufrufen
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
#include <esp_wifi.h>
|
||||
#include <ElegantOTA.h> // OTA Updates
|
||||
|
||||
#include <PrettyOTA.h>
|
||||
|
||||
#include "licenceing.h"
|
||||
#include "master.h"
|
||||
#include "timesync.h"
|
||||
|
||||
String uniqueSSID;
|
||||
|
||||
PrettyOTA OTAUpdates;
|
||||
|
||||
String getUniqueSSID();
|
||||
bool isInternetAvailable();
|
||||
@@ -78,25 +81,20 @@ void setupWifi() {
|
||||
void setupOTA(AsyncWebServer *server) {
|
||||
// Initialize PrettyOTA
|
||||
|
||||
ElegantOTA.begin(server);
|
||||
//ElegantOTA.begin(server);
|
||||
|
||||
//OTAUpdates.Begin(server);
|
||||
OTAUpdates.Begin(server);
|
||||
|
||||
// Set unique Hardware-ID for your hardware/board
|
||||
//OTAUpdates.SetHardwareID("AquaCross-Master");
|
||||
OTAUpdates.SetHardwareID("AquaCross-Master");
|
||||
|
||||
// Set firmware version to 1.0.0
|
||||
//OTAUpdates.SetAppVersion(firmwareversion);
|
||||
OTAUpdates.SetAppVersion(firmwareversion);
|
||||
|
||||
// Set current build time and date
|
||||
//PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();
|
||||
PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE();
|
||||
}
|
||||
|
||||
void loopOTA(){
|
||||
|
||||
ElegantOTA.loop();
|
||||
|
||||
}
|
||||
|
||||
// Generiert eine eindeutige SSID auf Basis der MAC-Adresse.
|
||||
String getUniqueSSID() {
|
||||
|
||||
Reference in New Issue
Block a user