Skip to content

Commit

Permalink
Add Ota Update and File update
Browse files Browse the repository at this point in the history
app.js: move previewBoard to index.js (not app specific)

index.js:
- add previewBoard
- remove (+) on fileTbl (file type instead)
- add file type (for upload and update)

SysModFiles: add upload variable
SysModSytem: add update variable

SysModWeb: serveUpload an serveUpdate: setValue (progress) to file variables
  • Loading branch information
ewowi committed Mar 19, 2024
1 parent 4b4ae45 commit d43aff0
Show file tree
Hide file tree
Showing 6 changed files with 1,497 additions and 1,400 deletions.
30 changes: 2 additions & 28 deletions data/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ function appName() {
return "Led";
}

function userFun(data) {
let buffer = new Uint8Array(data);
function userFun(buffer) {
if (buffer[0]==1 && jsonValues.pview) {
let pviewNode = gId("pview");

Expand Down Expand Up @@ -43,11 +42,6 @@ function userFun(data) {

return true;
}
else if (buffer[0]==0) {
let pviewNode = gId("board");
// console.log(buffer, pviewNode);
previewBoard(pviewNode, buffer);
}
return false;
}

Expand Down Expand Up @@ -331,24 +325,4 @@ function preview3D(canvasNode, buffer) {
renderer.render( scene, camera);
}); //import OrbitControl
}); //import Three
} //preview3D

function previewBoard(canvasNode, buffer) {
let ctx = canvasNode.getContext('2d');
//assuming 20 pins
let mW = 10; // matrix width
let mH = 2; // matrix height
let pPL = Math.min(canvasNode.width / mW, canvasNode.height / mH); // pixels per LED (width of circle)
let lOf = Math.floor((canvasNode.width - pPL*mW)/2); //left offeset (to center matrix)
let i = 5;
ctx.clearRect(0, 0, canvasNode.width, canvasNode.height);
for (let y=0.5;y<mH;y++) for (let x=0.5; x<mW; x++) {
if (buffer[i] + buffer[i+1] + buffer[i+2] > 20) { //do not show nearly blacks
ctx.fillStyle = `rgb(${buffer[i]},${buffer[i+1]},${buffer[i+2]})`;
ctx.beginPath();
ctx.arc(x*pPL+lOf, y*pPL, pPL*0.4, 0, 2 * Math.PI);
ctx.fill();
}
i+=3;
}
}
} //preview3D
87 changes: 84 additions & 3 deletions data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ let uiFunCommands = [];
let model = []; //model.json (as send by the server), used by FindVar
let savedView = null;
const UINT8_MAX = 255;
const UINT16_MAX = 256*256-1;

function gId(c) {return d.getElementById(c);}
function cE(e) { return d.createElement(e); }
Expand All @@ -43,7 +44,14 @@ function makeWS() {
ws.binaryType = "arraybuffer";
ws.onmessage = (e)=>{
if (e.data instanceof ArrayBuffer) { // preview packet
userFun(e.data);
let buffer = new Uint8Array(e.data);
if (buffer[0]==0) {
let pviewNode = gId("board");
// console.log(buffer, pviewNode);
previewBoard(pviewNode, buffer);
}
else
userFun(buffer);
}
else {
// console.log("onmessage", e.data);
Expand Down Expand Up @@ -241,7 +249,7 @@ function createHTML(json, parentNode = null, rowNr = UINT8_MAX) {
let tbodyNode = cE("tbody");
varNode.appendChild(tbodyNode);

if (!variable.ro) {
if (!variable.ro && variable.id != "fileTbl") { //fileTbl has upload file
let buttonNode = cE("input");
buttonNode.type = "button";
buttonNode.value = "+";
Expand Down Expand Up @@ -368,6 +376,32 @@ function createHTML(json, parentNode = null, rowNr = UINT8_MAX) {
varNode = cE("progress");
varNode.min = variable.min?variable.min:0; //if not specified then unsigned value (min=0)
if (variable.max) varNode.max = variable.max;
} else if (variable.type == "file") {
//https://github.com/smford/esp32-asyncwebserver-fileupload-example/blob/master/example-01/example-01.ino

varNode = cE("span");

inputNode = cE("input");
inputNode.type = variable.type;
inputNode.addEventListener('change', (event) => {
let fileNode = event.target;
let file = fileNode.files[0];
let formData = new FormData();
console.log("file " + variable.id, file, formData, file.size);
fileNode.parentNode.querySelector("progress").max = Math.round(file.size / 10000); //set progress max in blocks of 10K

formData.append("file", file);
fetch('/' + variable.id, {method: "POST", body: formData});
});
varNode.appendChild(inputNode);

let progressNode = cE("progress");
// if (variable.max) progressNode.max = variable.max;
progressNode.hidden = true;
varNode.appendChild(progressNode);

let spanNode = cE("span"); //fail or success
varNode.appendChild(spanNode);
} else {
//input types: text, search, tel, url, email, and password.

Expand Down Expand Up @@ -903,7 +937,34 @@ function changeHTML(variable, commandJson, rowNr = UINT8_MAX) {
node.value = commandJson.value;
}
}
else {//inputs and progress type
else if (node.className == "file") {
if (variable.ro) { //text and numbers read only
// console.log("changeHTML value span not select", variable, node, commandJson, rowNr);
} else {
// console.log("file update", node.id, commandJson.value);
let inputNode = node.querySelector("input");
let progressNode = node.querySelector("progress");
let spanNode = node.querySelector("span");
if (commandJson.value == UINT16_MAX - 10) {
progressNode.hidden = true;
spanNode.innerText = "🟢";
inputNode.value = null;
console.log("succes");
}
else if (commandJson.value == UINT16_MAX - 20) {
progressNode.hidden = true;
spanNode.innerText = "🔴";
inputNode.value = null;
console.log("fail");
}
else {
spanNode.innerText = "";
progressNode.hidden = false;
progressNode.value = commandJson.value;
}
//You cannot set it to a client side disk file system path, due to security reasons.
}
} else {//inputs and progress type
if (variable.ro && nodeType == "span") { //text and numbers read only
// console.log("changeHTML value span not select", variable, node, commandJson, rowNr);
node.textContent = commandJson.value;
Expand Down Expand Up @@ -1416,4 +1477,24 @@ function setTheme(node) {
function getTheme() {
let value = localStorage.getItem('theme');
if (value && value != "null") changeHTMLTheme(value);
}

function previewBoard(canvasNode, buffer) {
let ctx = canvasNode.getContext('2d');
//assuming 20 pins
let mW = 10; // matrix width
let mH = 2; // matrix height
let pPL = Math.min(canvasNode.width / mW, canvasNode.height / mH); // pixels per LED (width of circle)
let lOf = Math.floor((canvasNode.width - pPL*mW)/2); //left offeset (to center matrix)
let i = 5;
ctx.clearRect(0, 0, canvasNode.width, canvasNode.height);
for (let y=0.5;y<mH;y++) for (let x=0.5; x<mW; x++) {
if (buffer[i] + buffer[i+1] + buffer[i+2] > 20) { //do not show nearly blacks
ctx.fillStyle = `rgb(${buffer[i]},${buffer[i+1]},${buffer[i+2]})`;
ctx.beginPath();
ctx.arc(x*pPL+lOf, y*pPL, pPL*0.4, 0, 2 * Math.PI);
ctx.fill();
}
i+=3;
}
}
6 changes: 6 additions & 0 deletions src/Sys/SysModFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ void SysModFiles::setup() {
default: return false;
}});

ui->initFile(parentVar, "upload", nullptr, UINT16_MAX, false, [](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
case f_UIFun:
ui->setLabel(var, "Upload File");
default: return false;
}});

ui->initProgress(parentVar, "drsize", UINT16_MAX, 0, files->totalBytes(), true, [](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
case f_ValueFun:
mdl->setValue(var, files->usedBytes());
Expand Down
6 changes: 6 additions & 0 deletions src/Sys/SysModSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ void SysModSystem::setup() {
// ui->initText(parentVar, "date", __DATE__, 16, true);
// ui->initText(parentVar, "time", __TIME__, 16, true);

ui->initFile(parentVar, "update", nullptr, UINT16_MAX, false, [](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun
case f_UIFun:
ui->setLabel(var, "OTA Update");
return true;
default: return false;
}});

// static char msgbuf[32];
// snprintf(msgbuf, sizeof(msgbuf)-1, "%s rev.%d", ESP.getChipModel(), ESP.getChipRevision());
Expand Down
47 changes: 30 additions & 17 deletions src/Sys/SysModWeb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,11 @@ void SysModWeb::serveIndex(WebRequest *request) {
void SysModWeb::serveUpload(WebRequest *request, const String& filename, size_t index, byte *data, size_t len, bool final) {

// curl -F '[email protected]' 192.168.8.213/upload
USER_PRINT_Async("handleUpload r:%s f:%s i:%d l:%d f:%d\n", request->url().c_str(), filename.c_str(), index, len, final);
USER_PRINT_Async("serveUpload r:%s f:%s i:%d l:%d f:%d\n", request->url().c_str(), filename.c_str(), index, len, final);

mdl->setValue("upload", index/10000);
web->sendResponseObject(); //otherwise not send in asyn_tcp thread

if (!index) {
String finalname = filename;
if (finalname.charAt(0) != '/') {
Expand All @@ -507,39 +511,48 @@ void SysModWeb::serveUpload(WebRequest *request, const String& filename, size_t
}
if (final) {
request->_tempFile.close();
// USER_PRINT("File uploaded: "); // WLEDMM
// USER_PRINTLN(filename); // WLEDMM
// if (filename.equalsIgnoreCase("/cfg.json") || filename.equalsIgnoreCase("cfg.json")) { // WLEDMM
// request->send(200, "text/plain", F("Configuration restore successful.\nRebooting..."));
// doReboot = true;
// } else {
// if (filename.equals("/presets.json") || filename.equals("presets.json")) { // WLEDMM
// request->send(200, "text/plain", F("Presets File Uploaded!"));
// } else
request->send(200, "text/plain", F("File Uploaded!"));
// }
// cacheInvalidate++;

mdl->setValue("upload", UINT16_MAX - 10); //success
web->sendResponseObject(); //otherwise not send in asyn_tcp thread

request->send(200, "text/plain", F("File Uploaded!"));

files->filesChanged = true;
}
}

void SysModWeb::serveUpdate(WebRequest *request, const String& filename, size_t index, byte *data, size_t len, bool final) {

// curl -F '[email protected]' 192.168.8.213/upload
USER_PRINT_Async("handleUpdate r:%s f:%s i:%d l:%d f:%d\n", request->url().c_str(), filename.c_str(), index, len, final);
if(!index){
// USER_PRINT_Async("serveUpdate r:%s f:%s i:%d l:%d f:%d\n", request->url().c_str(), filename.c_str(), index, len, final);

mdl->setValue("update", index/10000);
web->sendResponseObject(); //otherwise not send in asyn_tcp thread

if (!index) {
USER_PRINTF("OTA Update Start\n");
// WLED::instance().disableWatchdog();
// usermods.onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init)
// lastEditTime = millis(); // make sure PIN does not lock during update
Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
}
if (!Update.hasError()) Update.write(data, len);

if (!Update.hasError())
Update.write(data, len);
else {
mdl->setValue("update", UINT16_MAX - 20); //fail
web->sendResponseObject(); //otherwise not send in asyn_tcp thread
}

if (final) {
bool success = Update.end(true);
mdl->setValue("update", success?UINT16_MAX - 10:UINT16_MAX - 20);
web->sendResponseObject(); //otherwise not send in asyn_tcp thread

char message[64];
const char * serverName = mdl->getValue("serverName");

print->fFormat(message, sizeof(message)-1, "Update of %s (...%d) %s", serverName, WiFi.localIP()[3], Update.end(true)?"Successful":"Failed");
print->fFormat(message, sizeof(message)-1, "Update of %s (...%d) %s", serverName, WiFi.localIP()[3], success?"Successful":"Failed");

USER_PRINTF("%s\n", message);
request->send(200, "text/plain", message);
Expand Down
Loading

0 comments on commit d43aff0

Please sign in to comment.