1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2025-12-14 14:33:07 +01:00

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

@@ -35,4 +35,11 @@ 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="upload"></div>
<div id="loadingFrame">
<div id="loadingText" >loading data</div>
<img id="loading" src="spinner.gif"/>
</div>
<div class="content">
<div id="loadingFrame">
<div id="loadingText" ></div>
<img id="loading" src="spinner.gif"/>
</div>
</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;
}
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<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 }