forked from FOSS/BangleApps
Improve firmware updater app to allow it to easily update bootloader
parent
a0e631a9dd
commit
5ce3b4adaf
|
@ -4,3 +4,4 @@
|
|||
Take 'beta' tag off
|
||||
0.03: Improve bootloader update safety. Now sets unsafeFlash:1 to allow flash with 2v11 and later
|
||||
Add CRC checks for common bootloaders that we know don't work
|
||||
0.04: Include a precompiled bootloader for easy bootloader updates
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,33 +3,42 @@
|
|||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<p><b>THIS IS CURRENTLY BETA - PLEASE USE THE NORMAL FIRMWARE UPDATE
|
||||
INSTRUCTIONS FOR <a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">BANGLE.JS</a> 1 AND <a href="https://www.espruino.com/Bangle.js2#firmware-updates" target="_blank">BANGLE.JS 2</a></b>. For usage on Bangle.js 2 you'll likely need to have an updated bootloader.</p>
|
||||
<p>This tool allows you to update the bootloader on <a href="https://www.espruino.com/Bangle.js2">Bangle.js 2</a> devices
|
||||
from within the App Loader.</p>
|
||||
|
||||
<div id="fw-unknown">
|
||||
<p><b>Firmware updates using the App Loader are only possible on
|
||||
Bangle.js 2. For firmware updates on Bangle.js 1 please
|
||||
<a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">see the Bangle.js 1 instructions</a></b></p>
|
||||
</div>
|
||||
<p>Your current firmware version is <span id="fw-version" style="font-weight:bold">unknown</span></p>
|
||||
<ul>
|
||||
<p>Your current firmware version is <span id="fw-version" style="font-weight:bold">unknown</span> and bootloader is <span id="boot-version" style="font-weight:bold">unknown</span></p>
|
||||
</ul>
|
||||
<div id="fw-ok" style="display:none">
|
||||
<p>If you have an early (KickStarter or developer) Bangle.js device and still have the old 2v10.x bootloader, the Firmware Update
|
||||
will fail with a message about the bootloader version. If so, please <a href="bootloader_espruino_2v11.52_banglejs2.hex" class="fw-link">click here to update to bootloader 2v11.52</a> and then click the 'Upload' button that appears.</p>
|
||||
<div id="latest-firmware" style="display:none">
|
||||
<p>The currently available Espruino firmware releases are:</p>
|
||||
<ul id="latest-firmware-list">
|
||||
</ul>
|
||||
<p>To update, click the link and then click the 'Upload' button that appears.</p>
|
||||
<p>To update, click a link above and then click the 'Upload' button that appears.</p>
|
||||
</div>
|
||||
<a href="#" id="advanced-btn">Advanced ▼</a>
|
||||
<div id="advanced-div" style="display:none">
|
||||
<p>Firmware updates via this tool work differently to the NRF Connect method mentioned on
|
||||
<a href="https://www.espruino.com/Bangle.js2#firmware-updates">the Bangle.js 2 page</a>. Firmware
|
||||
is uploaded to a file on the Bangle. Once complete the Bangle reboots and the bootloader copies
|
||||
the new firmware into internal Storage.</p>
|
||||
<p>In addition to the links above, you can upload a hex or zip file directly below. This file should be an <code>.app_hex</code>
|
||||
file, *not* the normal <code>.hex</code> (as that contains the bootloader as well).</p>
|
||||
<p><b>DANGER!</b> No verification is performed on uploaded ZIP or HEX files - you could
|
||||
potentially overwrite your bootloader with the wrong binary and brick your Bangle.</p>
|
||||
<input class="form-input" type="file" id="fileLoader" accept=".hex,.app_hex,.zip"/><br>
|
||||
</div>
|
||||
|
||||
<p>Or you can upload a hex or zip file here. This file should be an <code>.app_hex</code>
|
||||
file, *not* the normal <code>.hex</code> (as that contains the bootloader as well).</p>
|
||||
|
||||
<input class="form-input" type="file" id="fileLoader" accept=".hex,.app_hex,.zip"/><br>
|
||||
<p><button id="upload" class="btn btn-primary" style="display:none">Upload</button></p>
|
||||
</div>
|
||||
|
||||
<p>Firmware updates via this tool work differently to the NRF Connect method mentioned on
|
||||
<a href="https://www.espruino.com/Bangle.js2#firmware-updates">the Bangle.js page</a>. Firmware
|
||||
is uploaded to a file on the Bangle. Once complete the Bangle reboots and the bootloader copies
|
||||
the new firmware into internal Storage.</p>
|
||||
|
||||
|
||||
<pre id="log"></pre>
|
||||
|
||||
|
@ -38,7 +47,6 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.js"></script>
|
||||
|
||||
<script>
|
||||
var hex;
|
||||
var hexJS; // JS to upload hex
|
||||
var HEADER_LEN = 16; // size of app flash header
|
||||
var APP_START = 0x26000;
|
||||
|
@ -47,79 +55,114 @@ var MAX_ADDRESS = 0x1000000; // discount anything in hex file above this
|
|||
var VERSION = 0x12345678; // VERSION! Use this to test firmware in JS land
|
||||
var DEBUG = false;
|
||||
|
||||
function clearLog() {
|
||||
document.getElementById('log').innerText = "";
|
||||
console.log("Log Cleared");
|
||||
}
|
||||
function log(t) {
|
||||
document.getElementById('log').innerText += t+"\n";
|
||||
console.log(t);
|
||||
}
|
||||
|
||||
function onInit(device) {
|
||||
console.log(device);
|
||||
console.log("fwupdate init", device);
|
||||
if (device && device.version)
|
||||
document.getElementById("fw-version").innerText = device.version;
|
||||
if (device && device.id=="BANGLEJS2") {
|
||||
document.getElementById("fw-unknown").style = "display:none";
|
||||
document.getElementById("fw-ok").style = "";
|
||||
}
|
||||
|
||||
Puck.eval("E.CRC32(E.memoryArea(0xF7000,0x7000))", crc => {
|
||||
console.log("Bootloader CRC = "+crc);
|
||||
var version = `unknown (CRC 0x${crc.toString(16).padStart(8,2)})`;
|
||||
if (crc==1339551013) version = "2v10.219";
|
||||
if (crc==1207580954) version = "2v10.236";
|
||||
if (crc==3435933210) version = "2v11.52";
|
||||
document.getElementById("boot-version").innerText = version;
|
||||
});
|
||||
}
|
||||
|
||||
function checkForFileOnServer() {
|
||||
function getURL(url, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = callback;
|
||||
baseURL = url;
|
||||
xhr.open("GET", baseURL);
|
||||
xhr.open("GET", url);
|
||||
xhr.responseType = "document";
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function getFilesFromURL(url, regex, callback) {
|
||||
getURL(url, function() {
|
||||
console.log(this.responseXML)
|
||||
var files = [];
|
||||
var elements = this.responseXML.getElementsByTagName("a");
|
||||
for (var i=0;i<elements.length;i++) {
|
||||
var href = elements[i].href;
|
||||
if (regex.exec(href)) {
|
||||
files.push(href);
|
||||
}
|
||||
}
|
||||
callback(files);
|
||||
});
|
||||
getURL(url, function() {
|
||||
//console.log(this.responseXML)
|
||||
var files = [];
|
||||
var elements = this.responseXML.getElementsByTagName("a");
|
||||
for (var i=0;i<elements.length;i++) {
|
||||
var href = elements[i].href;
|
||||
if (regex.exec(href)) {
|
||||
files.push(href);
|
||||
}
|
||||
}
|
||||
callback(files);
|
||||
});
|
||||
}
|
||||
|
||||
var regex = new RegExp("_banglejs2.*zip$");
|
||||
|
||||
var domFirmwareList = document.getElementById("latest-firmware-list");
|
||||
var domFirmwareList = document.getElementById("latest-firmware-list");
|
||||
var domFirmware = document.getElementById("latest-firmware");
|
||||
console.log("Checking server...");
|
||||
|
||||
getFilesFromURL("https://www.espruino.com/binaries/", regex, function(releaseFiles) {
|
||||
getFilesFromURL("https://www.espruino.com/binaries/", regex, function(releaseFiles) {
|
||||
releaseFiles.sort().reverse().forEach(function(f) {
|
||||
var name = f.substr(f.substr(0,f.length-1).lastIndexOf('/')+1);
|
||||
var name = f.substr(f.substr(0,f.length-1).lastIndexOf('/')+1);
|
||||
console.log("Found "+name);
|
||||
domFirmwareList.innerHTML += '<li>Release: <a href="'+f+'" class="fw-link">'+name+'</a></li>';
|
||||
domFirmwareList.innerHTML += '<li>Release: <a href="'+f+'" class="fw-link">'+name+'</a></li>';
|
||||
domFirmware.style = "";
|
||||
});
|
||||
getFilesFromURL("https://www.espruino.com/binaries/travis/master/",regex, function(travisFiles) {
|
||||
travisFiles.forEach(function(f) {
|
||||
var name = f.substr(f.lastIndexOf('/')+1);
|
||||
});
|
||||
getFilesFromURL("https://www.espruino.com/binaries/travis/master/",regex, function(travisFiles) {
|
||||
travisFiles.forEach(function(f) {
|
||||
var name = f.substr(f.lastIndexOf('/')+1);
|
||||
console.log("Found "+name);
|
||||
domFirmwareList.innerHTML += '<li>Cutting Edge build: <a href="'+f+'" class="fw-link">'+name+'</a></li>';
|
||||
domFirmwareList.innerHTML += '<li>Cutting Edge build: <a href="'+f+'" class="fw-link">'+name+'</a></li>';
|
||||
domFirmware.style = "";
|
||||
});
|
||||
});
|
||||
console.log("Finished check for firmware files...");
|
||||
var fwlinks = document.querySelectorAll(".fw-link");
|
||||
for (var i=0;i<fwlinks.length;i++)
|
||||
fwlinks[i].addEventListener("click", e => {
|
||||
e.preventDefault();
|
||||
var url = e.target.href;
|
||||
downloadZipFile(url).then(info=>{
|
||||
downloadURL(e.target.href).then(info=>{
|
||||
document.getElementById("upload").style = ""; // show upload
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function downloadURL(url) {
|
||||
clearLog();
|
||||
log("Downloading "+url);
|
||||
if (url.endsWith(".zip")) {
|
||||
return downloadZipFile(url);
|
||||
} else if (url.endsWith(".hex")) {
|
||||
return downloadHexFile(url);
|
||||
} else {
|
||||
log("Unknown URL "+url+" - expecting .hex or .zip extension");
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
|
||||
function downloadHexFile(url) {
|
||||
return new Promise(resolve => {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
hexFileLoaded(this.responseText.toString());
|
||||
resolve();
|
||||
};
|
||||
xhr.open("GET", url);
|
||||
xhr.responseType = "text";
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
function downloadZipFile(url) {
|
||||
|
@ -154,15 +197,15 @@ function convertZipFile(binary) {
|
|||
if (info.bin_file.byteLength > APP_MAX_LENGTH) throw new Error("Firmware file is too big!");
|
||||
info.storageContents = new Uint8Array(info.bin_file.byteLength + HEADER_LEN)
|
||||
info.storageContents.set(new Uint8Array(info.bin_file), HEADER_LEN);
|
||||
console.log("ZIP downloaded and decoded",info);
|
||||
createJS_app(info.storageContents, APP_START, APP_START+info.bin_file.byteLength);
|
||||
log("Download complete");
|
||||
console.log("Download complete",info);
|
||||
document.getElementById("upload").style = ""; // show upload
|
||||
return info;
|
||||
}).catch(err => log("ERROR:" + err));
|
||||
}
|
||||
|
||||
function handleFileSelect(event) {
|
||||
clearLog();
|
||||
if (event.target.files.length!=1) {
|
||||
log("More than one file selected!");
|
||||
return;
|
||||
|
@ -172,13 +215,14 @@ function handleFileSelect(event) {
|
|||
var reader = new FileReader();
|
||||
if (file.name.endsWith(".hex") || file.name.endsWith(".app_hex")) {
|
||||
reader.onload = function(event) {
|
||||
hex = event.target.result.split("\n");
|
||||
log("HEX uploaded");
|
||||
document.getElementById("upload").style = ""; // show upload
|
||||
fileLoaded();
|
||||
hexFileLoaded(event.target.result);
|
||||
};
|
||||
reader.readAsText(event.target.files[0]);
|
||||
} else if (file.name.endsWith(".zip")) {
|
||||
reader.onload = function(event) {
|
||||
log("ZIP uploaded");
|
||||
convertZipFile(event.target.result);
|
||||
};
|
||||
reader.readAsArrayBuffer(event.target.files[0]);
|
||||
|
@ -187,25 +231,6 @@ function handleFileSelect(event) {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
function parseLines(dataCallback) {
|
||||
var addrHi = 0;
|
||||
hex.forEach(function(hexline) {
|
||||
if (DEBUG) console.log(hexline);
|
||||
var bytes = hexline.substr(1,2);
|
||||
var addrLo = parseInt(hexline.substr(3,4),16);
|
||||
var cmd = hexline.substr(7,2);
|
||||
if (cmd=="02") addrHi = parseInt(hexline.substr(9,4),16) << 4; // Extended Segment Address
|
||||
else if (cmd=="04") addrHi = parseInt(hexline.substr(9,4),16) << 16; // Extended Linear Address
|
||||
else if (cmd=="00") {
|
||||
var addr = addrHi + addrLo;
|
||||
var data = [];
|
||||
for (var i=0;i<16;i++) data.push(parseInt(hexline.substr(9+(i*2),2),16));
|
||||
dataCallback(addr,data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function CRC32(data) {
|
||||
var crc = 0xFFFFFFFF;
|
||||
data.forEach(function(d) {
|
||||
|
@ -278,6 +303,7 @@ function createJS_app(binary, startAddress, endAddress) {
|
|||
}
|
||||
hexJS += '\x10setTimeout(()=>E.showMessage("Rebooting..."),50);\n';
|
||||
hexJS += '\x10setTimeout(()=>E.reboot(), 1000);\n';
|
||||
log("Firmware update ready for upload");
|
||||
}
|
||||
|
||||
|
||||
|
@ -302,12 +328,32 @@ function createJS_bootloader(binary, startAddress, endAddress) {
|
|||
hexJS += 'f.erasePage(0x'+i.toString(16)+');\n';
|
||||
hexJS += `f.write(_fw,${startAddress});\n`;
|
||||
hexJS += `})()\n`;
|
||||
log("Bootloader ready for upload");
|
||||
}
|
||||
|
||||
function fileLoaded() {
|
||||
function hexFileLoaded(hexString) {
|
||||
var hex = hexString.split("\n"); // array of lines of the hex file
|
||||
function hexParseLines(dataCallback) {
|
||||
var addrHi = 0;
|
||||
hex.forEach(function(hexline) {
|
||||
if (DEBUG) console.log(hexline);
|
||||
var bytes = hexline.substr(1,2);
|
||||
var addrLo = parseInt(hexline.substr(3,4),16);
|
||||
var cmd = hexline.substr(7,2);
|
||||
if (cmd=="02") addrHi = parseInt(hexline.substr(9,4),16) << 4; // Extended Segment Address
|
||||
else if (cmd=="04") addrHi = parseInt(hexline.substr(9,4),16) << 16; // Extended Linear Address
|
||||
else if (cmd=="00") {
|
||||
var addr = addrHi + addrLo;
|
||||
var data = [];
|
||||
for (var i=0;i<16;i++) data.push(parseInt(hexline.substr(9+(i*2),2),16));
|
||||
dataCallback(addr,data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Work out addresses
|
||||
var startAddress, endAddress = 0;
|
||||
parseLines(function(addr, data) {
|
||||
hexParseLines(function(addr, data) {
|
||||
if (addr>MAX_ADDRESS) return; // ignore data out of range
|
||||
if (startAddress === undefined || addr<startAddress)
|
||||
startAddress = addr;
|
||||
|
@ -319,7 +365,7 @@ function fileLoaded() {
|
|||
// Work out data
|
||||
var binary = new Uint8Array(HEADER_LEN + endAddress-startAddress);
|
||||
binary.fill(0); // actually seems to assume a block is filled with 0 if not complete
|
||||
parseLines(function(addr, data) {
|
||||
hexParseLines(function(addr, data) {
|
||||
if (addr>MAX_ADDRESS) return; // ignore data out of range
|
||||
var binAddr = HEADER_LEN + addr - startAddress;
|
||||
binary.set(data, binAddr);
|
||||
|
@ -351,6 +397,10 @@ function handleUpload() {
|
|||
|
||||
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
|
||||
document.getElementById("upload").addEventListener("click", handleUpload);
|
||||
document.getElementById("advanced-btn").addEventListener("click", function() {
|
||||
document.getElementById("advanced-btn").style = "display:none";
|
||||
document.getElementById("advanced-div").style = "";
|
||||
});
|
||||
setTimeout(checkForFileOnServer, 10);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"id": "fwupdate",
|
||||
"name": "Firmware Update",
|
||||
"version": "0.03",
|
||||
"description": "[BETA] Uploads new Espruino firmwares to Bangle.js 2. For now, please use the instructions under https://www.espruino.com/Bangle.js2#firmware-updates",
|
||||
"version": "0.04",
|
||||
"description": "Uploads new Espruino firmwares to Bangle.js 2",
|
||||
"icon": "app.png",
|
||||
"type": "RAM",
|
||||
"tags": "tools,system",
|
||||
|
|
2
core
2
core
|
@ -1 +1 @@
|
|||
Subproject commit 35473a156efbd3e3e0d4916bb98fd09957d7ceed
|
||||
Subproject commit f97a128a28b409c576f66c63c87905f26a5cfd8b
|
Loading…
Reference in New Issue