mirror of
https://github.com/Roslund/sthlm-mesh.git
synced 2026-07-06 09:51:03 +02:00
deploy: 65b87c4071
This commit is contained in:
+17
-10
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
[
|
||||
{
|
||||
"hwModelSlug": "rak4631",
|
||||
"architecture": "nrf52",
|
||||
"displayName": "RAK WisBlock 4631",
|
||||
"versions": ["2.6.11.60ec05e", "2.6.4.b89355f"]
|
||||
},
|
||||
{
|
||||
"hwModelSlug": "tlora-t3s3-v1",
|
||||
"architecture": "esp32",
|
||||
"displayName": "LILYGO T-LoRa T3-S3",
|
||||
"versions": ["2.6.11.60ec05e53"]
|
||||
},
|
||||
{
|
||||
"hwModelSlug": "heltec-v3",
|
||||
"architecture": "esp32",
|
||||
"displayName": "Heltec V3",
|
||||
"partitionScheme": "8MB",
|
||||
"versions": ["2.6.11.60ec05e53"]
|
||||
}
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,85 @@
|
||||
async function flashFirmware(board, version, fullEraseInstall) {
|
||||
const { ESPLoader, Transport } = await import('https://unpkg.com/esptool-js/bundle.js')
|
||||
const logBox = document.getElementById('espLog');
|
||||
|
||||
|
||||
// Helper functions
|
||||
function log(...m) {
|
||||
logBox.textContent+=m.join(' ')+'\n';logBox.scrollTop=logBox.scrollHeight;
|
||||
}
|
||||
|
||||
function convertToBinaryString(bytes) {
|
||||
let binaryString = "";
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
binaryString += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return binaryString;
|
||||
}
|
||||
|
||||
async function fetchBinaryContent(url) {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const buffer = await response.arrayBuffer();
|
||||
return convertToBinaryString(new Uint8Array(buffer));
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
log(`\n=== ${board.displayName} · ${version} (${fullEraseInstall ? 'full' : 'update'}) ===`);
|
||||
|
||||
let port = await navigator.serial.requestPort({});
|
||||
let transport = new Transport(port, true);
|
||||
let loader = new ESPLoader({
|
||||
transport: transport,
|
||||
baudrate: 460800,
|
||||
terminal:{ writeLine:log, write:log, clean(){} }
|
||||
});
|
||||
|
||||
await loader.main();
|
||||
log('Connected.');
|
||||
|
||||
let fileArray, eraseAll = false;
|
||||
|
||||
if (!fullEraseInstall) {
|
||||
// UPDATE – write app only
|
||||
const app = await fetchBinaryContent(`/firmware/${board.hwModelSlug}/${version}/firmware-${board.hwModelSlug}-${version}-update.bin`);
|
||||
|
||||
fileArray = [
|
||||
{ address: 0x10000, data: app }
|
||||
];
|
||||
} else {
|
||||
// FULL ERASE, and firmware + littlefs flash
|
||||
// ota is share, need to af logic if supporting other devices...
|
||||
let otaOffset=0x260000, spiffsOffset=0x300000;
|
||||
if (board.partitionScheme==='8MB'){ otaOffset=0x340000; spiffsOffset=0x670000; }
|
||||
if (board.partitionScheme==='16MB'){otaOffset=0x650000; spiffsOffset=0xC90000; }
|
||||
|
||||
const factory = await fetchBinaryContent(`/firmware/${board.hwModelSlug}/${version}/firmware-${board.hwModelSlug}-${version}.bin`);
|
||||
const ota = await fetchBinaryContent(`/firmware/bleota-s3.bin`);
|
||||
const lfs = await fetchBinaryContent(`/firmware/${board.hwModelSlug}/${version}/littlefswebui-${board.hwModelSlug}-${version}.bin`);
|
||||
|
||||
fileArray = [
|
||||
{ address: 0x0000, data: factory },
|
||||
{ address: otaOffset, data: ota },
|
||||
{ address: spiffsOffset,data: lfs }
|
||||
];
|
||||
eraseAll = true;
|
||||
}
|
||||
|
||||
const flashOptions = {
|
||||
fileArray: fileArray,
|
||||
flashSize: 'keep',
|
||||
eraseAll: eraseAll,
|
||||
compress: true,
|
||||
flashMode: 'keep',
|
||||
flashFreq: 'keep',
|
||||
};
|
||||
|
||||
await loader.writeFlash(flashOptions);
|
||||
|
||||
} catch(e){ console.error(e); log('❌ Error:', e); }
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
let boards = [];
|
||||
let selectedBoard = {};
|
||||
let selectedVersion = '';
|
||||
|
||||
fetch('/firmware/firmware.json')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
boards = data;
|
||||
showFirmwareAccordion(boards);
|
||||
});
|
||||
|
||||
async function showFirmwareAccordion(boards) {
|
||||
const accordion = document.getElementById('firmwareAccordion');
|
||||
|
||||
boards.forEach((board, idx) => {
|
||||
const items = board.versions.map(version => {
|
||||
if (board.architecture === 'nrf52') {
|
||||
return renderNRF52Item(board.hwModelSlug, version);
|
||||
}
|
||||
if (board.architecture === 'esp32') {
|
||||
return renderESP32Item(board.hwModelSlug, version);
|
||||
}
|
||||
}).join('');
|
||||
|
||||
const extra = (board.architecture === 'nrf52') ? renderNRF52EraseItem() : '';
|
||||
|
||||
accordion.insertAdjacentHTML('beforeend', renderAccordionItem(board, items + extra, idx === 0));
|
||||
});
|
||||
}
|
||||
|
||||
// ── Template renderers ──
|
||||
function renderNRF52Item(hwModelSlug, version) {
|
||||
return `
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>${hwModelSlug}-${version}</span>
|
||||
<span>
|
||||
<a class="btn btn-sm btn-outline-secondary me-2"
|
||||
href="/firmware/${hwModelSlug}/${version}/${hwModelSlug}-${version}-ota.zip">Download OTA</a>
|
||||
<a class="btn btn-sm btn-outline-primary"
|
||||
href="/firmware/${hwModelSlug}/${version}/${hwModelSlug}-${version}.uf2">Download UF2</a>
|
||||
</span>
|
||||
</li>`;
|
||||
}
|
||||
|
||||
function renderESP32Item(hwModelSlug, version) {
|
||||
return `
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>${version}</span>
|
||||
<span>
|
||||
<button class="btn btn-sm btn-outline-primary open-modal-btn"
|
||||
data-board="${hwModelSlug}" data-version="${version}">Flash
|
||||
</button>
|
||||
</span>
|
||||
</li>`;
|
||||
}
|
||||
|
||||
function renderNRF52EraseItem() {
|
||||
return `
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>nrf_erase2.uf2</span>
|
||||
<a class="btn btn-sm btn-outline-danger"
|
||||
href="/firmware/nrf_erase2.uf2">Download UF2</a>
|
||||
</li>`;
|
||||
}
|
||||
|
||||
function renderAccordionItem(board, bodyHtml, isOpen) {
|
||||
return `
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button ${isOpen ? '' : 'collapsed'}" type="button"
|
||||
data-bs-toggle="collapse" data-bs-target="#body-${board.hwModelSlug}"
|
||||
aria-expanded="${isOpen}" aria-controls="body-${board.hwModelSlug}">
|
||||
<strong>${board.displayName}</strong>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="body-${board.hwModelSlug}" class="accordion-collapse collapse ${isOpen ? 'show' : ''}">
|
||||
<div class="accordion-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
${bodyHtml}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
// ── Modal logic ──
|
||||
document.addEventListener('click', ev => {
|
||||
if (!ev.target.matches('.open-modal-btn')) return;
|
||||
|
||||
|
||||
const modalEl = document.getElementById('flashModal');
|
||||
const flashModal = new bootstrap.Modal(modalEl);
|
||||
const titleEl = document.getElementById('flashModalLabel');
|
||||
const eraseChk = document.getElementById('eraseSwitch');
|
||||
const logBox = document.getElementById('espLog');
|
||||
|
||||
let currentBoardSlug = ev.target.dataset.board;
|
||||
selectedVersion = ev.target.dataset.version;
|
||||
selectedBoard = boards.find(b => b.hwModelSlug === currentBoardSlug);
|
||||
|
||||
titleEl.textContent = `Flash – ${selectedBoard.displayName} ${selectedVersion}`;
|
||||
eraseChk.checked = false;
|
||||
logBox.textContent = '';
|
||||
|
||||
flashModal.show();
|
||||
});
|
||||
|
||||
// ── Start Flash Button ──
|
||||
const startBtn = document.getElementById('startFlashBtn');
|
||||
startBtn.addEventListener('click', async () => {
|
||||
const fullEraseInstall = document.getElementById('eraseSwitch').checked;
|
||||
|
||||
startBtn.disabled = true;
|
||||
await flashFirmware(selectedBoard, selectedVersion, fullEraseInstall);
|
||||
startBtn.disabled = false;
|
||||
});
|
||||
Reference in New Issue
Block a user