mirror of
https://github.com/meshcore-dev/flasher.meshcore.dev.git
synced 2026-04-20 22:13:50 +00:00
310 lines
No EOL
14 KiB
HTML
310 lines
No EOL
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MeshCore flasher</title>
|
|
<link href="./css/beer.css" rel="stylesheet">
|
|
<link href="./css/flasher.css" rel="stylesheet">
|
|
<script type="module" src="./flasher.js"></script>
|
|
</head>
|
|
|
|
<body class="dark">
|
|
<div id="app" v-cloak>
|
|
<div class="snackbar" :class="snackbar.class" popover>
|
|
<i v-if="snackbar.icon">{{ snackbar.icon }}</i>
|
|
<span v-html="snackbar.text"></span>
|
|
</div>
|
|
<div class="flash-container" data-iframe-height>
|
|
<div v-if="flashing.active">
|
|
<header>
|
|
<nav>
|
|
<i>developer_board</i>
|
|
<span class="small">{{ selected.device.name }}</span>
|
|
<i>chevron_right</i>
|
|
<i>{{ selected.firmware.icon || config.role[selected.firmware.role].icon }}</i>
|
|
<span class="small strong">{{ selected.firmware.title || config.role[selected.firmware.role].title }}</span>
|
|
</nav>
|
|
</header>
|
|
<article v-if="flashing.error">
|
|
<div class="row">
|
|
<div class="max">
|
|
<h6>Flashing failed!</h6>
|
|
<p><span>{{ flashing.error }}</span></p>
|
|
</div>
|
|
</div>
|
|
<nav class="small-margin">
|
|
<button @click="retry">
|
|
<i>bolt</i>
|
|
<span>Retry</span>
|
|
</button>
|
|
<button @click="close">
|
|
<i>arrow_back</i>
|
|
<span>Close</span>
|
|
</button>
|
|
</nav>
|
|
</article>
|
|
<article v-else>
|
|
<div class="row">
|
|
<div class="max" v-if="flashing.percent < 100">
|
|
<h6><progress class="circle small"></progress> Flashing...</h6>
|
|
<p>Please do not disconnect the device</p>
|
|
</div>
|
|
<div class="max" v-else>
|
|
<h6>Flashing complete!</h6>
|
|
<nav class="small-margin">
|
|
<button @click="openSerialGUI()" v-if="selected.firmware.role === 'roomServer' || selected.firmware.role === 'repeater'">
|
|
<i>manufacturing</i>
|
|
<span>Configure via USB</span>
|
|
<div class="tooltip left max">Open GUI to configure Repeaters and Room servers via USB serial</div>
|
|
</button>
|
|
<button @click="close">
|
|
<i>arrow_back</i>
|
|
<span>Close</span>
|
|
</button>
|
|
<button @click="retry">
|
|
<i>bolt</i>
|
|
<span>Flash Again</span>
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
<div class="autoscroller">
|
|
<pre class="term" v-if="flashing.log">{{ flashing.log }}</pre>
|
|
</div>
|
|
<nav>
|
|
<progress :value="flashing.percent" max="100"></progress>
|
|
</nav>
|
|
</article>
|
|
</div>
|
|
<div v-else-if="selected.firmware">
|
|
<header>
|
|
<nav>
|
|
<button class="circle transparent" @click="stepBack"><i>arrow_back</i></button>
|
|
<i>developer_board</i>
|
|
<a class="small" href="javascript:;" @click="stepBack">{{ selected.device.name }}</a>
|
|
<i>chevron_right</i>
|
|
</nav>
|
|
<article class="no-margin">
|
|
<ul class="list">
|
|
<li>
|
|
<i>{{ getRoleFwValue(selected.firmware, 'icon') }}</i>
|
|
<div class="max">
|
|
<h6 class="small strong">{{ getRoleFwValue(selected.firmware, 'title') }} {{ getRoleFwValue(selected.firmware, 'subTitle') }}</h6>
|
|
<span class="text-wrap">{{ getRoleFwValue(selected.firmware, 'tooltip') }}</span>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</article>
|
|
<nav class="version">
|
|
<div class="field label suffix border small">
|
|
<select v-model="selected.version">
|
|
<template v-for="(version, versionName) in selected.firmware.version">
|
|
<option v-if="version.files.length > 0">{{ versionName }}</option>
|
|
</template>
|
|
</select>
|
|
<label>Version</label>
|
|
<i>arrow_drop_down</i>
|
|
</div>
|
|
<div class="max pre" v-if="getSelFwValue('notes')">
|
|
<b>Changelog:</b><br>{{ getSelFwValue('notes').replace('Change log:\r\n', '') }}
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
<ul class="list border" v-if="selected.device.type === 'esp32'">
|
|
<li>
|
|
<label class="checkbox">
|
|
<input type="checkbox" v-model="selected.wipe">
|
|
<span>Erase device</span>
|
|
<div class="tooltip right max">
|
|
DO NOT carry out a full erase if you are simply updating your MeshCore device,
|
|
otherwise it will erase your MeshCore identity for that device.
|
|
</div>
|
|
</label>
|
|
</li>
|
|
</ul>
|
|
<template v-if="selected.device.type === 'nrf52'">
|
|
<div class="small-space"></div>
|
|
<button @click="dfuMode" :disabled="selected.nrfEraserFlashing || !flashing.supported">
|
|
<i>{{ flashing.dfuComplete ? 'check' : 'code' }}</i>
|
|
<span>{{ flashing.dfuComplete ? 'DFU mode active' : 'Enter DFU mode' }}</span>
|
|
<div class="tooltip right max" v-if="flashing.supported">
|
|
Enter DFU mode - this mode enables you to flash your firmware.
|
|
If you did not trigger the DFU mode manually, please click this button.
|
|
</div>
|
|
<div class="tooltip right max" v-else>
|
|
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
|
|
</div>
|
|
</button>
|
|
<button @click="nrfErase" :disabled="!flashing.supported || selected.nrfEraserFlashing || selected.nrfEraserFlashingPercent > 0">
|
|
<i v-if="selected.nrfEraserFlashingPercent === 100">check</i>
|
|
<progress v-else-if="selected.nrfEraserFlashing" class="circle small"></progress>
|
|
<i v-else>delete_forever</i>
|
|
|
|
<span v-if="selected.nrfEraserFlashingPercent === 100">Flash Erased!</span>
|
|
<span v-if="selected.nrfEraserFlashingPercent > 0">Flashing erase firmware: {{selected.nrfEraserFlashingPercent}}%</span>
|
|
<span v-else-if="selected.nrfEraserFlashing">Connecting...</span>
|
|
<span v-else>Erase Flash</span>
|
|
|
|
<div class="tooltip right max" v-if="flashing.supported">
|
|
Flash special firmware that will format your flash and erase all user data.<br>
|
|
Click <b>Enter DFU mode</b> before erasing, if you did not trigger the DFU mode manually
|
|
</div>
|
|
<div class="tooltip right max" v-else>
|
|
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
|
|
</div>
|
|
</button>
|
|
</template>
|
|
<div class="medium-space"></div>
|
|
<nav class="small-margin">
|
|
<template v-if="canFlash(selected.device)">
|
|
<button @click="flashDevice" :disabled="selected.nrfEraserFlashing || !flashing.supported">
|
|
<i>bolt</i>
|
|
<span>Flash!</span>
|
|
<div class="tooltip right max" v-if="flashing.supported">
|
|
Upload the firmware into your device. Existing firwmare will get overwritten.
|
|
<span v-if="selected.device.type === 'nrf52'">
|
|
If you did not trigger DFU mode manually, use the <b>Enter DFU mode</b> before flashing
|
|
</span>
|
|
</div>
|
|
<div class="tooltip right max" v-else>
|
|
Your browser doesn't support Web Serial API. Please use Chrome or Edge on Desktop
|
|
</div>
|
|
</button>
|
|
<div class="max"></div>
|
|
</template>
|
|
<button data-ui="#down" class="active" v-if="!getSelFwValue('customFile')">
|
|
<i>download</i>
|
|
<span>Download</span><i>arrow_drop_down</i>
|
|
<menu class="left no-wrap" id="down" data-ui="#down">
|
|
<li v-for="file in getSelFwValue('files')">
|
|
<a data-ui="menu-selector" :href="getFirmwarePath(file)" download>{{ file.title }}</a>
|
|
</li>
|
|
<li v-if="selected.device.type === 'nrf52' && selected.device.erase">
|
|
<a data-ui="menu-selector" :href="`${config.staticPath}/${selected.device.erase.replace('.zip', '.uf2')}`" download>{{ selected.device.erase.replace('.zip', '.uf2') }}</a>
|
|
</li>
|
|
</menu>
|
|
<div class="tooltip left max">Download a copy of the firmware files for use with other flashers</div>
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
<div v-else-if="selected.device">
|
|
<header>
|
|
<nav>
|
|
<button class="circle transparent" @click="stepBack"><i>arrow_back</i></button>
|
|
<i>developer_board</i>
|
|
<span class="small strong">{{ selected.device.name }}</span>
|
|
</nav>
|
|
<nav class="no-margin">
|
|
<h6 class="small max">Choose role</h6>
|
|
</nav>
|
|
</header>
|
|
<ul class="list border">
|
|
<template v-for="firmware in selected.device.firmware">
|
|
<li v-if="firmwareHasData(firmware)" :class="getRoleFwValue(firmware, 'class')">
|
|
<button class="transparent" @click="selected.firmware = firmware">
|
|
<i>{{ getRoleFwValue(firmware, 'icon') }}</i>
|
|
<span>{{ getRoleFwValue(firmware, 'title') }} {{ getRoleFwValue(firmware, 'subTitle') }}</span>
|
|
<div class="tooltip right max" v-if="getRoleFwValue(firmware, 'tooltip')" v-html="getRoleFwValue(firmware, 'tooltip')"></div>
|
|
</button>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</div>
|
|
<div v-else>
|
|
<header>
|
|
<nav>
|
|
<i>bolt</i>
|
|
<h5 class="small max logo-top">
|
|
<img src="img/meshcore.svg"> <span>flasher</span>
|
|
</h5>
|
|
<button class="transparent hide-mobile" @click="openSerialGUI()">
|
|
<i>manufacturing</i>
|
|
<span>Repeater Setup</span>
|
|
<div class="tooltip left max">Open GUI to configure Repeaters and Room servers via USB serial</div>
|
|
</button>
|
|
<button class="transparent" @click="openSerialCon()">
|
|
<i>terminal</i>
|
|
<span>Console</span>
|
|
<div class="tooltip left max">Open serial console to manage Routers and Room servers</div>
|
|
</button>
|
|
</nav>
|
|
<nav class="no-margin">
|
|
<h6 class="small max">Choose device</h6>
|
|
</nav>
|
|
</header>
|
|
<div class="field label prefix suffix border w-100 no-margin">
|
|
<i>search</i>
|
|
<input type="text" placeholder=" " v-model="deviceFilterText" autofocus>
|
|
<label>Filter</label>
|
|
</div>
|
|
<ul class="list border">
|
|
<li v-for="(deviceGroup, type) in devices">
|
|
<details open>
|
|
<summary style="background-color: var(--surface-container)">
|
|
<div>
|
|
<strong>{{ type === 'community' ? 'Community Firmware' : 'Ripple Firmware' }}</strong>
|
|
<div class="tooltip right max">
|
|
<template v-if="type === 'ripple'">
|
|
Freemium firmware provided by <a class="inverse-link" target="_blank" href="https://buymeacoffee.com/ripplebiz">Ripple Radios</a>
|
|
</template>
|
|
<template v-else>
|
|
Open Source <a class="inverse-link" target="_blank" href="https://github.com/meshcore-dev/MeshCore">Community firmware</a>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</summary>
|
|
<ul class="list border">
|
|
<li v-for="device in deviceGroup">
|
|
<template v-if="device.firmware.length > 0">
|
|
<button class="device-select transparent" @click="selected.device = device">
|
|
<img class="icon" :title="device.type" :src="`/img/${device.type}.svg`" v-if="device.type !== 'noflash'">
|
|
<i v-else>developer_board</i>
|
|
<span>{{ device.name }}</span>
|
|
<div class="tooltip right max" v-if="device.tooltip" v-html="device.tooltip"></div>
|
|
<div class="max"></div>
|
|
</button>
|
|
<div class="max"></div>
|
|
<img class="icon" :src="device.icon" v-if="device.icon" >
|
|
</template>
|
|
</li>
|
|
</ul>
|
|
</details>
|
|
</li>
|
|
<li>
|
|
<label>
|
|
<i>unknown_document</i>
|
|
<strong style="font-size: small;">Custom Firmware</strong>
|
|
<div class="tooltip right max">
|
|
Flash custom firmware file from your computer.
|
|
Flasher supports <b>.bin</b> files for esp32 devices and OTA <b>.zip</b> files for nRF52 devices.
|
|
</div>
|
|
<input type="file" accept=".zip,.bin" @change="customFirmwareLoad">
|
|
</label>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div v-if="serialCon.opened" class="overlay active">
|
|
<datalist id="command-db">
|
|
<option v-for="(desc, command) in commandReference" :value="command">{{ desc }}</option>
|
|
</datalist>
|
|
<header>
|
|
<nav>
|
|
<button class="circle transparent" @click="closeSerialCon()"><i>arrow_back</i></button>
|
|
<h6 class="small max">Serial Console</h6>
|
|
</nav>
|
|
</header>
|
|
<pre class="console" ref="consoleWindow" tabindex="0" @mouseup="consoleMouseUp">
|
|
<code>{{ serialCon.content }}</code>
|
|
<div class="holder">
|
|
<span>></span>
|
|
<input ref="consoleEditBox" class="console-input" type="text" v-model="serialCon.edit" @keydown.enter.prevent="sendCommand(serialCon.edit)" list="command-db">
|
|
</div>
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
<script src="./lib/iframeResizer.contentWindow.min.js"></script>
|
|
</body>
|
|
</html> |