From 2b42cc53e773286793adc789ef183445d5de97aa Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 7 Sep 2023 16:53:32 +0200 Subject: [PATCH] add handling for board type to images and webflasher --- extra_script.py | 6 +- lib/appinfo/GwAppInfo.h | 7 +- src/main.cpp | 3 +- webinstall/install.css | 7 + webinstall/install.html | 9 +- webinstall/install.js | 264 +++++++++++++++++++++++++++----------- webinstall/installUtil.js | 20 ++- 7 files changed, 224 insertions(+), 92 deletions(-) diff --git a/extra_script.py b/extra_script.py index 8a50062..a91c147 100644 --- a/extra_script.py +++ b/extra_script.py @@ -268,7 +268,10 @@ def prebuild(env): genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE)) generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings) version="dev"+datetime.now().strftime("%Y%m%d") - env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) + env.Append(CPPDEFINES=[ + ('GWDEVVERSION',version), + ('PIO_ENV_BOARD',env.get('BOARD_MCU')) + ]) def cleangenerated(source, target, env): od=outPath() @@ -290,3 +293,4 @@ env.Append( ) #script does not run on clean yet - maybe in the future env.AddPostAction("clean",cleangenerated) +#print(env.Dump()) diff --git a/lib/appinfo/GwAppInfo.h b/lib/appinfo/GwAppInfo.h index 12ff16d..79e4622 100644 --- a/lib/appinfo/GwAppInfo.h +++ b/lib/appinfo/GwAppInfo.h @@ -15,4 +15,9 @@ #endif #endif -#define FIRMWARE_TYPE GWSTRINGIFY(PIO_ENV_BUILD) \ No newline at end of file +#define FIRMWARE_TYPE GWSTRINGIFY(PIO_ENV_BUILD) +#ifdef PIO_ENV_BOARD +#define BOARD_INFO "@@" GWSTRINGIFY(PIO_ENV_BOARD) +#else +#define BOARD_INFO "" +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b3b29b7..f42a363 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,6 +76,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting //assert length of firmware name and version CASSERT(strlen(FIRMWARE_TYPE) <= 32, "environment name (FIRMWARE_TYPE) must not exceed 32 chars"); CASSERT(strlen(VERSION) <= 32, "VERSION must not exceed 32 chars"); +CASSERT(strlen(BOARD_INFO) <= 32,"BOARD_INFO must not exceed 32 chars"); //https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/app_image_format.html //and removed the bugs in the doc... __attribute__((section(".rodata_custom_desc"))) esp_app_desc_t custom_app_desc = { @@ -86,7 +87,7 @@ __attribute__((section(".rodata_custom_desc"))) esp_app_desc_t custom_app_desc = FIRMWARE_TYPE, "00:00:00", "2021/12/13", - "0000", + BOARD_INFO, {}, {} }; diff --git a/webinstall/install.css b/webinstall/install.css index 26a1a45..6731764 100644 --- a/webinstall/install.css +++ b/webinstall/install.css @@ -35,4 +35,11 @@ body { } .hidden{ display: none !important; +} +.uploadFile{ + width: 0; + height: 0; +} +.uploadButton{ + margin-left: 0.5em; } \ No newline at end of file diff --git a/webinstall/install.html b/webinstall/install.html index da4d0bd..be9dc94 100644 --- a/webinstall/install.html +++ b/webinstall/install.html @@ -11,11 +11,12 @@
+
+
+
loading data
+ +
-
-
- -
diff --git a/webinstall/install.js b/webinstall/install.js index 95929df..059e431 100644 --- a/webinstall/install.js +++ b/webinstall/install.js @@ -11,7 +11,8 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; const HDROFFSET = 288; const VERSIONOFFSET = 16; const NAMEOFFSET = 48; - const MINSIZE = HDROFFSET + NAMEOFFSET + 32; + const CHIPOFFSET=NAMEOFFSET+64; + const MINSIZE = HDROFFSET + CHIPOFFSET + 32; const imageCheckBytes = { 0: 0xe9, //image magic 288: 0x32, //app header magic @@ -46,12 +47,24 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; } let version = decodeFromBuffer(content, startOffset+ HDROFFSET + VERSIONOFFSET, 32); let fwtype = decodeFromBuffer(content, startOffset+ HDROFFSET + NAMEOFFSET, 32); + let chip=decodeFromBuffer(content,startOffset+HDROFFSET+CHIPOFFSET,32); let rt = { fwtype: fwtype, version: version, + chip:chip }; return rt; } + const readFile=(file)=>{ + return new Promise((resolve,reject)=>{ + let reader = new FileReader(); + reader.addEventListener('load', function (e) { + resolve(e.target.result); + + }); + reader.readAsBinaryString(file); + }); + } const checkImageFile=(file,isFull)=>{ let minSize=MINSIZE+(isFull?(UPDATE_START-FULL_START):0); return new Promise(function (resolve, reject) { @@ -97,12 +110,22 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; hFrame.textContent=''; let h=addEl('h2',undefined,hFrame,`ESP32 Install ${info}`) } - const checkChip=(chipFamily,assetName)=>{ - //for now only ESP32 - if (chipFamily != "ESP32"){ - throw new Error(`unexpected chip family ${chipFamily}, expected ESP32`); + const checkChip= async (chipFamily,data,isFull)=>{ + let info=checkImage(data,isFull); + if (info.chip && info.chip.match(/^@@/)){ + let chip=info.chip.substr(2); + let compare=chipFamily.toLowerCase().replace(/[^a-z0-9]*/g,''); + if (compare !== chip){ + let res=confirm("different chip signatures - image("+chip+"), chip ("+compare+")\nUse this image any way?"); + if (! res) throw new Error("user abort"); + } + return; } - return assetName; + //for now only ESP32/ESP32-S3 + if (chipFamily != "ESP32" && chipFamily != "ESP32-S3"){ + throw new Error(`unexpected chip family ${chipFamily}, expected ESP32/ESP32-S3`); + } + return; } const baudRates=[1200, 2400, @@ -138,6 +161,63 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; enableConsole(true); }) } + const handleLocalFile= async (file)=>{ + setValue('content',''); + showLoading("loading "+file.name); + try { + if (file.name.match(/.zip$/)) { + //zip + await handleZip(file); + } + else { + if (! file.name.match(/\.bin$/)){ + throw new Error("only .zip or .bin"); + } + let data=await readFile(file); + let isFull=false; + let info; + try{ + info=checkImage(data,true); + isFull=true; + }catch (e){ + try{ + info=checkImage(data); + } + catch(x){ + throw new Error(file.name+" is no image: "+x); + } + } + if (isFull){ + buildCustomButtons("dummy",undefined,data,file.name,"Local"); + } + else{ + buildCustomButtons("dummy",data,undefined,file.name,"Local"); + } + } + } catch (e) { + alert(e); + } + showLoading(); + } + const buildUploadButtons= (element)=>{ + let bFrame=document.querySelector(element||'.upload'); + if (! bFrame) return; + bFrame.textContent=''; + let it=addEl('div','item',bFrame); + addEl('div','version',it,`Local File`); + let cLine=addEl('div','buttons',it); + let fi=addEl('input','uploadFile',cLine); + fi.setAttribute('type','file'); + fi.addEventListener('change',async (ev)=>{ + let files=ev.target.files; + if (files.length < 1) return; + await handleLocalFile(files[0]); + }); + let bt=addEl('button','uploadButton',cLine,'upload'); + bt.addEventListener('click',()=>{ + fi.click(); + }); + } const buildButtons=(user,repo,element)=>{ let bFrame=document.querySelector(element||'.content'); if (! bFrame) return; @@ -148,7 +228,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; alert("no version found in release data"); return; } - addEl('div','version',bFrame,`Version: ${version}`); + addEl('div','version',bFrame,`Prebuild: ${version}`); let items={}; releaseData.assets.forEach((asset)=>{ let name=asset.name; @@ -179,7 +259,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; repo, version, 4096, - (chip)=>checkChip(chip,item.basic) + checkChip ) enableConsole(true); }); @@ -192,56 +272,75 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; repo, version, 65536, - (chip)=>checkChip(chip,item.update) + checkChip ) enableConsole(true); }); } } - const buildCustomButtons = (name, updateData, fullData,info,element) => { + const buildCustomButtons = (name, updateData, fullData,info,title,element) => { let bFrame = document.querySelector(element || '.content'); if (!bFrame) return; - let vinfo=checkImage(fullData,true); - let version=vinfo.version; - let uinfo=checkImage(updateData); - if (uinfo.version != version){ - throw new Error("different versions in full("+version+") and update("+uinfo.version+") image"); + if (fullData === undefined && updateData === undefined) return; + let version; + if (fullData !== undefined){ + let vinfo=checkImage(fullData,true); + version=vinfo.version; + } + if (updateData !== undefined){ + let uinfo=checkImage(updateData); + if (version !== undefined){ + if (uinfo.version != version){ + throw new Error("different versions in full("+version+") and update("+uinfo.version+") image"); + } + } + else{ + version=uinfo.version; + } } bFrame.textContent = ''; let item=addEl('div','item',bFrame); - addEl('div', 'version', item, "Custom "+version); + addEl('div', 'version', item, title+" "+version); if (info){ addEl('div','version',item,info); } let btLine = addEl('div', 'buttons', item); - let tb = addEl('button', 'installButton', btLine, 'Initial'); - tb.addEventListener('click', async () => { - enableConsole(false, true); - await espInstaller.runFlash( - true, - fullData, - FULL_START, - version, - (ch) => checkChip(ch, name) - ) - enableConsole(true); - }); - tb = addEl('button', 'installButton', btLine, 'Update'); - tb.addEventListener('click', async () => { - enableConsole(false, true); - await espInstaller.runFlash( - false, - updateData, - UPDATE_START, - version, - (ch) => checkChip(ch, name) - ) - enableConsole(true); - }); + let tb; + if (fullData !== undefined) { + tb = addEl('button', 'installButton', btLine, 'Initial'); + tb.addEventListener('click', async () => { + enableConsole(false, true); + await espInstaller.runFlash( + true, + fullData, + FULL_START, + version, + checkChip + ) + enableConsole(true); + }); + } + if (updateData !== undefined) { + tb = addEl('button', 'installButton', btLine, 'Update'); + tb.addEventListener('click', async () => { + enableConsole(false, true); + await espInstaller.runFlash( + false, + updateData, + UPDATE_START, + version, + checkChip + ) + enableConsole(true); + }); + } } - const showLoading=(on)=>{ - setVisible('loadingFrame',on); + const showLoading=(title)=>{ + setVisible('loadingFrame',title !== undefined); + if (title){ + setValue('loadingText',title); + } }; class BinaryStringWriter extends zip.Writer { @@ -260,6 +359,51 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; return this.binaryString; } } + const handleZip = async (zipFile) => { + showLoading("loading zip"); + let reader; + let title="Custom"; + if (typeof(zipFile) === 'string'){ + setValue('loadingText', 'downloading custom build') + reader= new zip.HttpReader(zipFile); + } + else{ + setValue('loadingText', 'loading zip file'); + reader = new zip.BlobReader(zipFile); + title="Local"; + } + let zipReader = new zip.ZipReader(reader); + const entries = (await zipReader.getEntries()); + let fullData; + let updateData; + let base = ""; + let environment; + let buildflags; + for (let i = 0; i < entries.length; i++) { + if (entries[i].filename.match(/-all.bin$/)) { + fullData = await (entries[i].getData(new BinaryStringWriter())); + base = entries[i].filename.replace("-all.bin", ""); + } + if (entries[i].filename.match(/-update.bin$/)) { + updateData = await (entries[i].getData(new BinaryStringWriter())); + base = entries[i].filename.replace("-update.bin", ""); + } + if (entries[i].filename === 'buildconfig.txt') { + let txt = await (entries[i].getData(new zip.TextWriter())); + environment = txt.replace(/.*pio run *.e */, '').replace(/ .*/, ''); + buildflags = txt.replace(/.*PLATFORMIO_BUILD_FLAGS="/, '').replace(/".*/, ''); + } + } + let info; + if (environment !== undefined && buildflags !== undefined) { + info = `env=${environment}, flags=${buildflags}`; + } + if (updateData === undefined && fullData === undefined){ + throw new Error("no firmware files found in zip"); + } + buildCustomButtons("dummy", updateData, fullData, info,title); + showLoading(); + } window.onload = async () => { if (! ESPInstaller.checkAvailable()){ showError("your browser does not support the ESP flashing (no serial)"); @@ -280,47 +424,19 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; espLoaderTerminal = new XtermOutputHandler('terminal'); espInstaller = new ESPInstaller(espLoaderTerminal); buildConsoleButtons(); + buildUploadButtons(); if (! custom){ buildHeading(`${user}:${repo}`); releaseData = await espInstaller.getReleaseInfo(user, repo); buildButtons(user, repo); + showLoading(); } else{ errorText="unable to download custom build"; - showLoading(true); - setValue('loadingText','downloading custom build') - let reader= new zip.HttpReader(custom); - let zipReader= new zip.ZipReader(reader); - const entries=(await zipReader.getEntries()); - let fullData; - let updateData; - let base=""; - let environment; - let buildflags; - for (let i=0;i