Skip to content

Commit

Permalink
Merge pull request #159 from HowardsPlayPen/PR
Browse files Browse the repository at this point in the history
Changes to allow optional integration of PsychicHttp
  • Loading branch information
ayushsharma82 committed Feb 28, 2024
2 parents 234461a + 0caecf5 commit e2c2aee
Show file tree
Hide file tree
Showing 4 changed files with 324 additions and 0 deletions.
114 changes: 114 additions & 0 deletions examples/PsychicHttp/main.cpp
@@ -0,0 +1,114 @@
/*
-----------------------
ElegantOTA - Async Demo Example
-----------------------
NOTE: Make sure you have enabled Async Mode in ElegantOTA before compiling this example!
Guide: https://docs.elegantota.pro/async-mode/
Skill Level: Beginner
This example provides with a bare minimal app with ElegantOTA functionality which works
with AsyncWebServer.
Github: https://github.com/ayushsharma82/ElegantOTA
WiKi: https://docs.elegantota.pro
Works with both ESP8266 & ESP32
-------------------------------
Upgrade to ElegantOTA Pro: https://elegantota.pro
*/

#if defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#elif defined(ESP32)
#include <WiFi.h>
#endif

#include <ElegantOTA.h>
#include <PsychicHttp.h>

PsychicWebSocketHandler websocketHandler;
PsychicEventSource eventSource;
PsychicHttpServer server;


const char* ssid = "........";
const char* password = "........";

unsigned long ota_progress_millis = 0;

void onOTAStart() {
// Log when OTA has started
Serial.println("OTA update started!");
// <Add your own code here>
}

void onOTAProgress(size_t current, size_t final) {
// Log every 1 second
if (millis() - ota_progress_millis > 1000) {
ota_progress_millis = millis();
Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
}
}

void onOTAEnd(bool success) {
// Log when OTA has finished
if (success) {
Serial.println("OTA update finished successfully!");
} else {
Serial.println("There was an error during OTA update!");
}
// <Add your own code here>
}

void setup(void) {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());

server.listen(80); // NOTE: for PsychicHttp you MUST call listen() before registering any urls using .on()

// Set Authentication Credentials
ElegantOTA.setAuth("test", "test");

//setup server config stuff here
server.config.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)

server.onOpen([](PsychicClient *client) {
Serial.printf("[http] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
});

//example callback everytime a connection is closed
server.onClose([](PsychicClient *client) {
Serial.printf("[http] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
});

ElegantOTA.begin(&server); // Start ElegantOTA
// ElegantOTA callbacks
ElegantOTA.onStart(onOTAStart);
ElegantOTA.onProgress(onOTAProgress);
ElegantOTA.onEnd(onOTAEnd);

Serial.println("HTTP server started");
}

void loop(void) {
ElegantOTA.loop();
}
25 changes: 25 additions & 0 deletions examples/PsychicHttp/platform.ini
@@ -0,0 +1,25 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:esp32cam]
platform = espressif32
board = esp32cam
framework = arduino
monitor_speed = 115200
lib_ldf_mode = deep
; NOTE the below build flag activates the code for using PsychicHttp
build_flags = -DELEGANTOTA_USE_PSYCHIC=1
lib_deps =
; I found that the packaged up version was not sufficient / recent enough
https://github.com/hoeken/PsychicHttp.git
; if you were pulling ElegantOTA from this temporay fork
;https://github.com/HowardsPlayPen/ElegantOTA.git
; To pull mainline from the main repository
https://github.com/ayushsharma82/ElegantOTA.git
182 changes: 182 additions & 0 deletions src/ElegantOTA.cpp
Expand Up @@ -23,6 +23,26 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
response->addHeader("Content-Encoding", "gzip");
request->send(response);
});
#elif defined(ELEGANTOTA_USE_PSYCHIC)
PsychicEndpoint* endpoint = _server->on("/update", HTTP_GET, [this](PsychicRequest *request){

PsychicResponse response(request);
response.setCode(200);
response.setContentType("text/html");

response.addHeader("Content-Encoding", "gzip");

//add our actual content
response.setContent(ELEGANT_HTML, sizeof(ELEGANT_HTML));

return response.send();

});
if(endpoint && _authenticate )
{
endpoint->setAuthentication(_username, _password);
}

#else
_server->on("/update", HTTP_GET, [&](){
if (_authenticate && !_server->authenticate(_username, _password)) {
Expand Down Expand Up @@ -101,6 +121,98 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,

return request->send((Update.hasError()) ? 400 : 200, "text/plain", (Update.hasError()) ? _update_error_str.c_str() : "OK");
});
#elif defined(ELEGANTOTA_USE_PSYCHIC)
endpoint = _server->on("/ota/start", HTTP_GET, [this](PsychicRequest *request) {

// Get header x-ota-mode value, if present
OTA_Mode mode = OTA_MODE_FIRMWARE;
// Get mode from arg
PsychicWebParameter * p = request->getParam("mode");
if (request->hasParam("mode") && p) {

String argValue = p->value();
if (argValue == "fs") {
ELEGANTOTA_DEBUG_MSG("OTA Mode: Filesystem\n");
mode = OTA_MODE_FILESYSTEM;
} else {
ELEGANTOTA_DEBUG_MSG("OTA Mode: Firmware\n");
mode = OTA_MODE_FIRMWARE;
}
}

// Get file MD5 hash from arg
p = request->getParam("hash");
if (request->hasParam("") && p) {
String hash = p->value();
ELEGANTOTA_DEBUG_MSG(String("MD5: "+hash+"\n").c_str());
if (!Update.setMD5(hash.c_str())) {
ELEGANTOTA_DEBUG_MSG("ERROR: MD5 hash not valid\n");
return request->reply(400, "text/plain", "MD5 parameter invalid");
}
}

#if UPDATE_DEBUG == 1
// Serial output must be active to see the callback serial prints
Serial.setDebugOutput(true);
#endif

// Pre-OTA update callback
if (preUpdateCallback != NULL) preUpdateCallback();

// Start update process
#if defined(ESP8266)
uint32_t update_size = mode == OTA_MODE_FILESYSTEM ? ((size_t)FS_end - (size_t)FS_start) : ((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
if (mode == OTA_MODE_FILESYSTEM) {
close_all_fs();
}
Update.runAsync(true);
if (!Update.begin(update_size, mode == OTA_MODE_FILESYSTEM ? U_FS : U_FLASH)) {
ELEGANTOTA_DEBUG_MSG("Failed to start update process\n");
// Save error to string
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
}
#elif defined(ESP32)
if (!Update.begin(UPDATE_SIZE_UNKNOWN, mode == OTA_MODE_FILESYSTEM ? U_SPIFFS : U_FLASH)) {
ELEGANTOTA_DEBUG_MSG("Failed to start update process\n");
// Save error to string
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
}
#elif defined(TARGET_RP2040)
uint32_t update_size = 0;
// Gather FS Size
if (mode == OTA_MODE_FILESYSTEM) {
update_size = ((size_t)&_FS_end - (size_t)&_FS_start);
LittleFS.end();
} else {
FSInfo64 i;
LittleFS.begin();
LittleFS.info64(i);
update_size = i.totalBytes - i.usedBytes;
}
// Start update process
if (!Update.begin(update_size, mode == OTA_MODE_FILESYSTEM ? U_FS : U_FLASH)) {
ELEGANTOTA_DEBUG_MSG("Failed to start update process because there is not enough space\n");
_update_error_str = "Not enough space";
return _server->send(400, "text/plain", _update_error_str.c_str());
}
#endif

return request->reply((Update.hasError()) ? 400 : 200, "text/plain", (Update.hasError()) ? _update_error_str.c_str() : "OK");
});

if(endpoint && _authenticate )
{
endpoint->setAuthentication(_username, _password);
}

#else
_server->on("/ota/start", HTTP_GET, [&]() {
if (_authenticate && !_server->authenticate(_username, _password)) {
Expand Down Expand Up @@ -243,6 +355,76 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
return;
}
});
#elif defined(ELEGANTOTA_USE_PSYCHIC)

PsychicUploadHandler *uploadHandler = new PsychicUploadHandler();
uploadHandler->onUpload([this](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) {

//Upload handler chunks in data
if (!index) {
// Reset progress size on first frame
_current_progress_size = 0;
}

// Write chunked data to the free sketch space
if(len){
if (Update.write(data, len) != len) {
return request->reply(400, "text/plain", "Failed to write chunked data to free space");
}
_current_progress_size += len;
// Progress update callback
if (progressUpdateCallback != NULL) progressUpdateCallback(_current_progress_size, request->contentLength());
}

if (last) { // if the final flag is set then this is the last frame of data
if (!Update.end(true)) { //true to set the size to the current progress
// Save error to string
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
}
}else{
return ESP_OK; // TODO is this the right return code here?
}


return ESP_OK;
});

//gets called after upload has been handled
uploadHandler->onRequest([this](PsychicRequest *request)
{
// Post-OTA update callback
if (postUpdateCallback != NULL) postUpdateCallback(!Update.hasError());

PsychicResponse response(request);

response.setContentType("text/plain");
response.addHeader("Connection", "close");
response.addHeader("Access-Control-Allow-Origin", "*");

bool hasError = (Update.hasError());
response.setCode(hasError ? 400 : 200);
response.setContent(hasError ? _update_error_str.c_str() : "OK");

esp_err_t err = response.send();

// Set reboot flag
if (!Update.hasError()) {
if (_auto_reboot) {
_reboot_request_millis = millis();
_reboot = true;
}
}
return err;
});

uploadHandler->setAuthentication(_username, _password);

_server->on("/ota/upload", HTTP_POST, uploadHandler);

#else
_server->on("/ota/upload", HTTP_POST, [&](){
if (_authenticate && !_server->authenticate(_username, _password)) {
Expand Down
3 changes: 3 additions & 0 deletions src/ElegantOTA.h
Expand Up @@ -67,6 +67,9 @@ _____ _ _ ___ _____ _
#include "AsyncTCP.h"
#include "ESPAsyncWebServer.h"
#define ELEGANTOTA_WEBSERVER AsyncWebServer
#elif ELEGANTOTA_USE_PSYCHIC == 1
#include <PsychicHttp.h>
#define ELEGANTOTA_WEBSERVER PsychicHttpServer
#else
#include "WiFi.h"
#include "WiFiClient.h"
Expand Down

0 comments on commit e2c2aee

Please sign in to comment.