add support for flashing esp32 devices

This commit is contained in:
liamcottle
2024-07-24 20:26:12 +12:00
parent 3a82479fe1
commit 4946a8a71a
2 changed files with 261 additions and 48 deletions

View File

@@ -26,6 +26,7 @@
<div id="app" class="space-y-2 p-3"> <div id="app" class="space-y-2 p-3">
<!-- header -->
<div class="flex border bg-gray-50 p-3 rounded shadow"> <div class="flex border bg-gray-50 p-3 rounded shadow">
<div class="mr-3"> <div class="mr-3">
<img src="reticulum_logo_512.png" class="w-14 h-14"/> <img src="reticulum_logo_512.png" class="w-14 h-14"/>
@@ -39,15 +40,43 @@
<div class="border bg-gray-50 rounded shadow"> <div class="border bg-gray-50 rounded shadow">
<div class="border-b px-2 py-1"> <div class="border-b px-2 py-1">
1. Put device into DFU Mode 1. Select your device
</div> </div>
<div class="p-3"> <div class="p-3">
<div class="flex mb-1 space-x-1">
<div class="min-w-[70px] my-auto text-right">Product</div>
<select v-model="selectedProduct" class="min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block pl-2 pr-8">
<option :value="null" disabled>Select a Product</option>
<option v-for="product of products" :value="product">{{ product.name }}</option>
</select>
</div>
<div class="flex space-x-1">
<div class="min-w-[70px] my-auto text-right">Model</div>
<select v-model="selectedModel" class="min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block pl-2 pr-8">
<option :value="null" disabled>Select a Model</option>
<option v-if="selectedProduct" v-for="model of selectedProduct.models" :value="model">{{ model.name }}</option>
</select>
</div>
</div>
<!-- button to enter dfu mode on PLATFORM_NRF52 -->
<div v-if="selectedProduct?.platform === 0x70" class="p-3 border-t">
<button @click="enterDfuMode" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded"> <button @click="enterDfuMode" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
Enter DFU Mode Enter DFU Mode
</button> </button>
</div> </div>
<div class="border-t px-2 py-1">
<div class="text-sm space-x-1">
<span>Can't find your device? Open an issue on</span>
<a target="_blank" href="https://github.com/liamcottle/rnode-flasher" class="text-blue-500 hover:underline">GitHub</a>
</div>
</div>
</div> </div>
<div class="border bg-gray-50 rounded shadow"> <div class="border bg-gray-50 rounded shadow">
@@ -58,18 +87,11 @@
<div class="p-3"> <div class="p-3">
<div class="flex mb-2">
<div class="text-sm bg-red-100 px-2 py-1 border rounded">
<span class="text-red-500">*</span>
<span>Only RAK4631 can be flashed at this time, ESP32 support is coming later.</span>
</div>
</div>
<div class="mb-2"> <div class="mb-2">
<input ref="file" type="file"/> <input ref="file" type="file"/>
</div> </div>
<div v-if="!isFlashing"> <div v-if="!isFlashing" class="space-x-1">
<button @click="flash" :disabled="isFlashing" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded"> <button @click="flash" :disabled="isFlashing" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
Flash Now Flash Now
</button> </button>
@@ -103,27 +125,9 @@
</div> </div>
<div class="p-3"> <div class="p-3">
<button @click="provision" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
<div class="flex mb-1 space-x-1"> Provision
<div class="min-w-[70px] my-auto text-right">Product</div> </button>
<select v-model="selectedProduct" class="min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block pl-2 pr-8">
<option :value="null" disabled>Select a Product</option>
<option v-for="product of products" :value="product">{{ product.name }}</option>
</select>
</div>
<div class="flex mb-1 space-x-1">
<div class="min-w-[70px] my-auto text-right">Model</div>
<select v-model="selectedModel" class="min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block pl-2 pr-8">
<option :value="null" disabled>Select a Model</option>
<option v-if="selectedProduct" v-for="model of selectedProduct.models" :value="model">{{ model.name }}</option>
</select>
</div>
<div>
<button @click="provision" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
Provision
</button>
</div>
</div> </div>
</div> </div>
@@ -220,6 +224,13 @@
</div> </div>
<!-- setup esptool-js -->
<script type="module">
import { ESPLoader, Transport } from "https://unpkg.com/esptool-js@0.4.5/bundle.js";
window.ESPLoader = ESPLoader;
window.Transport = Transport;
</script>
<script> <script>
Vue.createApp({ Vue.createApp({
data() { data() {
@@ -231,9 +242,34 @@
selectedProduct: null, selectedProduct: null,
selectedModel: null, selectedModel: null,
products: [ products: [
{
name: "Heltec LoRa32 v3",
id: ROM.PRODUCT_H32_V3,
platform: ROM.PLATFORM_ESP32,
models: [
{
id: ROM.MODEL_C5,
name: "433 MHz",
},
{
id: ROM.MODEL_CA,
name: "868 MHz / 915 MHz / 923 MHz",
},
],
flash_config: {
flash_size: "8MB",
flash_files: {
"0xe000": "rnode_firmware_heltec32v3.boot_app0",
"0x0": "rnode_firmware_heltec32v3.bootloader",
"0x10000": "rnode_firmware_heltec32v3.bin",
"0x8000": "rnode_firmware_heltec32v3.partitions",
},
},
},
{ {
name: "RAK4631", name: "RAK4631",
id: ROM.PRODUCT_RAK4631, id: ROM.PRODUCT_RAK4631,
platform: ROM.PLATFORM_NRF52,
models: [ models: [
{ {
id: ROM.MODEL_11, id: ROM.MODEL_11,
@@ -245,39 +281,66 @@
}, },
], ],
}, },
{
name: "Heltec LoRa32 v3",
id: ROM.PRODUCT_H32_V3,
models: [
{
id: ROM.MODEL_C5,
name: "433 MHz",
},
{
id: ROM.MODEL_CA,
name: "868 MHz / 915 MHz / 923 MHz",
},
],
},
{ {
name: "LilyGO T-Beam", name: "LilyGO T-Beam",
id: ROM.PRODUCT_TBEAM, id: ROM.PRODUCT_TBEAM,
platform: ROM.PLATFORM_ESP32,
models: [ models: [
{ {
id: ROM.MODEL_E4, id: ROM.MODEL_E4,
name: "433 MHz (with SX1278 chip)", name: "433 MHz (with SX1278 chip)",
flash_config: {
flash_size: "4MB",
flash_files: {
"0xe000": "rnode_firmware_tbeam.boot_app0",
"0x1000": "rnode_firmware_tbeam.bootloader",
"0x10000": "rnode_firmware_tbeam.bin",
"0x210000": "console_image.bin",
"0x8000": "rnode_firmware_tbeam.partitions",
},
},
}, },
{ {
id: ROM.MODEL_E9, id: ROM.MODEL_E9,
name: "868/915/923 MHz (with SX1276 chip)", name: "868/915/923 MHz (with SX1276 chip)",
flash_config: {
flash_size: "4MB",
flash_files: {
"0xe000": "rnode_firmware_tbeam.boot_app0",
"0x1000": "rnode_firmware_tbeam.bootloader",
"0x10000": "rnode_firmware_tbeam.bin",
"0x210000": "console_image.bin",
"0x8000": "rnode_firmware_tbeam.partitions",
},
},
}, },
{ {
id: ROM.MODEL_E3, id: ROM.MODEL_E3,
name: "433 MHz (with SX1268 chip)", name: "433 MHz (with SX1268 chip)",
flash_config: {
flash_size: "4MB",
flash_files: {
"0xe000": "rnode_firmware_tbeam_sx1262.boot_app0",
"0x1000": "rnode_firmware_tbeam_sx1262.bootloader",
"0x10000": "rnode_firmware_tbeam_sx1262.bin",
"0x210000": "console_image.bin",
"0x8000": "rnode_firmware_tbeam_sx1262.partitions",
},
},
}, },
{ {
id: ROM.MODEL_E8, id: ROM.MODEL_E8,
name: "868/915/923 MHz (with SX1262 chip)", name: "868/915/923 MHz (with SX1262 chip)",
flash_config: {
flash_size: "4MB",
flash_files: {
"0xe000": "rnode_firmware_tbeam_sx1262.boot_app0",
"0x1000": "rnode_firmware_tbeam_sx1262.bootloader",
"0x10000": "rnode_firmware_tbeam_sx1262.bin",
"0x210000": "console_image.bin",
"0x8000": "rnode_firmware_tbeam_sx1262.partitions",
},
},
}, },
], ],
}, },
@@ -352,6 +415,22 @@
}, },
async flash() { async flash() {
switch(this.selectedProduct?.platform){
case ROM.PLATFORM_ESP32: {
await this.flashEsp32();
break;
}
case ROM.PLATFORM_NRF52: {
await this.flashNrf52();
break;
}
default: {
alert("Please select a device first");
break;
}
}
},
async flashNrf52() {
// ensure firmware file selected // ensure firmware file selected
const file = this.$refs["file"].files[0]; const file = this.$refs["file"].files[0];
@@ -388,6 +467,129 @@
this.isFlashing = false; this.isFlashing = false;
} }
// close port
console.log("Closing serial port");
await this.serialPort.close();
},
async flashEsp32() {
// ensure ESPLoader is available
if(!window.ESPLoader){
alert("esptool-js could not be loaded.");
return;
}
// ensure flash config is known, use from selected model, or fallback to selected product
const flashConfig = this.selectedModel?.flash_config ?? this.selectedProduct?.flash_config;
if(!flashConfig){
alert("Flash config is not available for the selected device.");
return;
}
// ensure firmware file selected
const file = this.$refs["file"].files[0];
if(!file){
alert("Select a firmware file first");
return;
}
// ask for serial port
const serialPort = await this.askForSerialPort();
if(!serialPort){
return;
}
// update progress
this.isFlashing = true;
this.flashingProgress = 0;
try {
// read zip file
const blobReader = new window.zip.BlobReader(file);
const zipReader = new window.zip.ZipReader(blobReader);
const zipEntries = await zipReader.getEntries();
// get files to flash
const filesToFlash = [];
for(const [address, filename] of Object.entries(flashConfig.flash_files)){
// find file inside zip
const file = zipEntries.find((zipEntry) => zipEntry.filename === filename);
if(!file){
throw filename + " not found in firmware file!";
}
// get file data as binary string
const blob = await file.getData(new window.zip.BlobWriter());
const data = await this.readAsBinaryString(blob); // fixme: deprecated, but works for now
// add to files to flash
filesToFlash.push({
"address": parseInt(address),
"data": data,
});
}
// init transport and esploader
const transport = new window.Transport(serialPort, true);
const esploader = new window.ESPLoader({
transport: transport,
baudrate: 921600,
debugLogging: false,
enableTracing: false,
terminal: {
clean() {
},
writeLine(data) {
console.log(data);
},
write(data) {
console.log(data);
},
},
});
// load device info
await esploader.main();
// flash device
await esploader.writeFlash({
fileArray: filesToFlash,
flashSize: flashConfig.flash_size,
flashMode: "DIO",
flashFreq: "80MHz",
eraseAll: false,
compress: true,
calculateMD5Hash: (image) => CryptoJS.MD5(CryptoJS.enc.Latin1.parse(image)),
reportProgress: (fileIndex, written, total) => {
const currentFilePercentage = (written / total) * 100;
this.flashingProgress = Math.floor(currentFilePercentage);
},
});
// reboot device
await transport.setDTR(false);
await new Promise((resolve) => setTimeout(resolve, 100));
await transport.setDTR(true);
// flashing successful
alert("Firmware has been flashed!");
} catch(e) {
alert("Firmware flashing failed: " + e);
console.log(e);
} finally {
this.isFlashing = false;
}
// close port
console.log("Closing serial port");
await serialPort.close();
}, },
async detect() { async detect() {
@@ -849,6 +1051,21 @@
alert("TNC mode has been disabled!"); alert("TNC mode has been disabled!");
}, },
async readAsBinaryString(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.readAsBinaryString(blob);
});
},
},
watch: {
selectedProduct() {
// reset selected model when changing selected product
this.selectedModel = null;
},
}, },
}).mount('#app'); }).mount('#app');
</script> </script>

View File

@@ -178,10 +178,6 @@ class Nrf52DfuFlasher {
console.log("Sending firmware"); console.log("Sending firmware");
await this.sendFirmware(firmware, progressCallback); await this.sendFirmware(firmware, progressCallback);
// close port
console.log("Closing serial port");
await this.serialPort.close();
// todo // todo
// sleep(self.dfu_transport.get_activate_wait_time()) // sleep(self.dfu_transport.get_activate_wait_time())