add handling for board type to images and webflasher

This commit is contained in:
andreas 2023-09-07 16:53:32 +02:00
parent 0d5343ddf4
commit 2b42cc53e7
7 changed files with 224 additions and 92 deletions

View File

@ -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())

View File

@ -16,3 +16,8 @@
#endif
#define FIRMWARE_TYPE GWSTRINGIFY(PIO_ENV_BUILD)
#ifdef PIO_ENV_BOARD
#define BOARD_INFO "@@" GWSTRINGIFY(PIO_ENV_BOARD)
#else
#define BOARD_INFO ""
#endif

View File

@ -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,
{},
{}
};

View File

@ -36,3 +36,10 @@ body {
.hidden{
display: none !important;
}
.uploadFile{
width: 0;
height: 0;
}
.uploadButton{
margin-left: 0.5em;
}

View File

@ -11,11 +11,12 @@
<body>
<div class="heading"></div>
<div class="console"></div>
<div class="content">
<div id="loadingFrame">
<div id="loadingText" ></div>
<div class="upload"></div>
<div id="loadingFrame">
<div id="loadingText" >loading data</div>
<img id="loading" src="spinner.gif"/>
</div>
</div>
<div class="content">
</div>
<div id="terminal"></div>
</body>

View File

@ -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 assetName;
return;
}
//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,30 +272,43 @@ 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;
if (fullData === undefined && updateData === undefined) return;
let version;
if (fullData !== undefined){
let vinfo=checkImage(fullData,true);
let version=vinfo.version;
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');
let tb;
if (fullData !== undefined) {
tb = addEl('button', 'installButton', btLine, 'Initial');
tb.addEventListener('click', async () => {
enableConsole(false, true);
await espInstaller.runFlash(
@ -223,10 +316,12 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
fullData,
FULL_START,
version,
(ch) => checkChip(ch, name)
checkChip
)
enableConsole(true);
});
}
if (updateData !== undefined) {
tb = addEl('button', 'installButton', btLine, 'Update');
tb.addEventListener('click', async () => {
enableConsole(false, true);
@ -235,13 +330,17 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
updateData,
UPDATE_START,
version,
(ch) => checkChip(ch, name)
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<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}`;
}
buildCustomButtons("dummy",updateData,fullData,info);
showLoading(false);
await handleZip(custom);
}
} catch(error){
showLoading(false);
showLoading();
alert(errorText+error)
};
}

View File

@ -236,22 +236,19 @@ class ESPInstaller{
* @param {*} repo
* @param {*} version
* @param {*} address
* @param {*} assetName the name of the asset file.
* can be a function - will be called with the chip family
* and must return the asset file name
* @param {*} checkChip will be called with the found chip and the data and the isFull flag
* @returns
*/
async installClicked(isFull, user, repo, version, address, assetName) {
try {
await this.connect();
let assetFileName = assetName;
if (typeof (assetName) === 'function') {
assetFileName = assetName(this.getChipFamily());
}
let imageData = await this.getReleaseAsset(user, repo, version, assetFileName);
let imageData = await this.getReleaseAsset(user, repo, version, assetName);
if (!imageData || imageData.length == 0) {
throw new Error(`no image data fetched`);
}
if (checkChip) {
await checkChip(this.getChipFamily(),imageData,isFull);
}
let fileList = [
{ data: imageData, address: address }
];
@ -274,13 +271,14 @@ class ESPInstaller{
* @param {*} address
* @param {*} imageData the data to be flashed
* @param {*} version the info shown in the dialog
* @param {*} checkChip will be called with the found chip and the data
* @returns
*/
async runFlash(isFull,imageData,address,version,assetName){
async runFlash(isFull,imageData,address,version,checkChip){
try {
await this.connect();
if (typeof (assetName) === 'function') {
assetName(this.getChipFamily()); //just check
if (checkChip) {
await checkChip(this.getChipFamily(),imageData,isFull); //just check
}
let fileList = [
{ data: imageData, address: address }