openHop MeshCore Flasher is a static, browser-based firmware flasher for loading openHop Modem firmware onto supported MeshCore-compatible devices. Flashing runs entirely in the browser using Web Serial. Production uses HTTPS static assets and a small Cloudflare Worker for same-origin release discovery.
The modem firmware variants are maintained in the openHop modem repository:
https://github.com/openhop-dev/openhop_modem
User documentation is available at:
https://docs.openhop.dev/projects/openhop-modem/flasher/
Only published stable releases are selectable, with the newest eligible semantic version selected by default. Releases are discovered via /api/firmware-releases and link to their GitHub release pages. Live, cached and fallback lists are filtered, deduplicated and sorted newest first. The catalogue excludes releases before v1.0.1 (which lack required P4 factory images); per-firmware minimumRelease further restricts availability. main is an internal file-layout template, never a selectable branch build. expandReleases: false disables template expansion and permits only explicitly configured versions present in the release list. Roles and devices without eligible files are hidden. Discovery caches successful results in the browser for 15 minutes and uses configured fallback tags when the service is unavailable; an empty successful release list stays empty. Custom BIN/ZIP uploads remain independent of catalogue release filtering.
- Flashes ESP32-family devices with esptool.js.
- Supports normal ESP32 firmware updates.
- Supports Erase Device/full ESP32 flashing with bootloader, partition table, and firmware images when those files are available.
- Flashes nRF52 devices with serial DFU packages.
- Provides a serial console for supported devices.
- ESP32-P4 Nano
- EtherMesh-1W
- Photon-1W XAIO ESP32 C6
- Heltec T114
- Heltec Tracker V2
- Heltec V3
- Heltec V4
- Heltec V4.2
- Heltec V4.3
- Ikoka Stick
- LilyGo T3S3
- LilyGO T-Beam 1W (v1.4.0 and newer)
- LilyGo T-Beam-S3 Supreme
- RAK3401 + RAK13302
- RAK4631 USB
- RAK4631 WisMesh Ethernet
- RAK WisMesh Base/Rak3112
- Seeed XIAO ESP32S3 + Wio SX1262
- Seeed XIAO nRF52 + Wio SX1262
- UnitEng Station G2
- UnitEng/BQ Voyage Station G3
Firmware release history:
https://github.com/openhop-dev/openhop_modem/releases
The flasher configuration points at raw firmware files from each release tag. ESP32-P4 devices use the complete firmware.factory.bin at 0x0 because their bootloader starts at 0x2000. Other ESP32 devices use the build-specific full-flash layout, commonly:
bootloader.binat0x0partitions.binat0x8000firmware.binat0x10000
For nRF52 devices, the flasher uses the variant's firmware.zip DFU package.
Every configured variant references a shared firmware-level flashing notice, which remains visible after release expansion replaces version notes:
- ESP32: Use Flash with Erase Device disabled for firmware-only updates. Erase Device flashes the complete factory image and erases settings. Attach a suitable antenna before powering the device and transmitting.
- nRF52: Use Flash to install the firmware DFU package. Attach a suitable antenna before powering the device and transmitting.
The nRF52 notice does not imply an erase option. Board-specific power guidance remains in the documentation below, not in the flashing notice.
This is a distinct device, not the T-Beam-S3 Supreme. It is available only when release discovery includes a published stable v1.4.0 or newer; older releases, branches and prereleases are not offered. If no eligible release is discovered (including the current v1.0.1-only fallback), the device remains hidden.
- Firmware-only update: disable Erase Device, then use Flash to write
lilygo_tbeam_1w/firmware.binat0x10000. - Initial install / full flash: enable Erase Device to write the complete
lilygo_tbeam_1w/firmware.factory.binat0x0. This erases settings. - Attach a suitable antenna before powering the device and transmitting. For sustained high-power operation, use a recommended 7.4 V battery pack rated for at least 2 A discharge; USB-C power may be marginal.
Automated catalogue tests do not validate physical hardware, fan operation or TCXO behavior.
No dependencies need to be installed and there is no build step:
flasher.jsboots the production Vue application;js/app.jsowns selection and UI state.js/catalog.jsloads the unchanged openHop catalogue, discovers releases and resolves selected-tag firmware URLs.js/flash.jsselects complete flash plans and invokes ESP32/DFU backends. Explicit per-file addresses take priority; full flash writes every configured image. P4 factory images start at zero.js/router.jsrestores direct links on initial load/reload and handles in-app history. See routing support below.js/serial.jschooses Web Serial or the upstream Android serial-over-WebUSB polyfill;lib/dfu.jspropagates rejected reads without waiting for its timeout.worker.jsandwrangler.jsoncretain the same release-proxy/static-assets deployment..assetsignoreexcludes tests and development tooling.
lib/esp32.js is the unmodified bundle.js from the official npm esptool-js 0.7.0 package (source release, P4 fix). Its Apache-2.0 license is retained in lib/esptool-js.LICENSE; bundled dependency notices remain in the bundle.
- Package:
https://registry.npmjs.org/esptool-js/-/esptool-js-0.7.0.tgz - npm integrity:
sha512-9wHuW/s9t4Vs6HKcr3pra3gbRVWwaN0eabMWodWH4KEJHEYZxgmWwZ3s0F4xcWVCZOc/L98bGZ4uWoZvR8KBKA== - Bundle SHA-256:
00ebe6fb0b202976d3e2a3c6b9a6bd438c26a81d2af6739c95ac276a64c1a84e
To refresh, download the exact pinned tarball, verify its SHA-512 against the npm integrity above, and copy package/bundle.js and package/LICENSE to those paths. No application build or npm installation is required. Do not edit the minified bundle by hand.
This version powers flash before attachment on ESP32-P4 v3.1/v3.2, including the v3.2 download-mode XPD eFuse case. Other revisions do not receive that power-up sequence. The backend passes Uint8Array firmware bytes as required by 0.7.0, preserving update/full-flash offsets and erase behavior. Register-level tests and real vendor compression tests run without a physical board; successful flashing on an affected board still requires hardware validation.
Run the dependency-free regression suites with Node.js 22 or newer (older Node versions lack browser APIs used by the existing tests):
python3 -m unittest discover -s tests -p 'test_*.py' -v
node --test tests/*.test.mjs
node --check flasher.js
for file in js/*.js lib/dfu.js lib/overflow.vue.js lib/polyfill/serial.js; do node --check "$file"; done
git diff --checkNode tests execute real catalogue, routing, Vue selection and flash orchestration with network/hardware boundaries stubbed. They do not flash hardware. Python tests cover catalogue layouts, branding, safe controls and module wiring. When changing JavaScript, bump openhop16 consistently in the entry, module imports and HTML/CSS URLs; an entry-only cache key does not invalidate its imports.
For a static UI preview, run python3 -m http.server 8000 --bind 127.0.0.1. This server does not implement /api/firmware-releases: expect fallback versions and a handled HTTP 404. Use a separately authorized local Worker preview to exercise the real proxy. Firmware binaries still require browser CORS access to raw GitHub. Do not click Flash or Enter DFU mode during ordinary UI validation.
Build URLs use /<device-slug>/<role-slug>/<version>, for example:
/openhop-uniteng-bq-voyage-station-g3/openhop-modem/v1.4.0
Opening or reloading this URL restores the device, role and exact version if it is eligible in the discovered catalogue. A device-only URL opens its role choices; a device/role URL selects the newest eligible stable release and adds that version to the URL. / still opens the device list. Browser Back/Forward restores selections; navigation remains locked during an active flash transaction. Query parameters such as ?config= and ?iframe= are preserved.
Unknown routes use the existing partial-match fallback: an unknown device returns to /, an unknown role returns to that device's role choices, and an unknown/unavailable version selects the newest eligible release and replaces the URL with its actual version. Thus a cached or fallback release list that lacks a requested version does not imply that version is available. Devices without eligible releases return to the device list; branch builds such as main are never selectable.
The server must serve index.html for application paths, without redirecting their URLs to /. Cloudflare's configured SPA fallback provides this in production. Python's simple static server does not: opening/reloading a deep path there returns HTTP 404. Use an SPA-aware local preview server to verify reloads; the release API is a separate requirement.
Desktop Chrome/Edge with Web Serial remains the recommended path. Android Chromium with WebUSB can use the upstream polyfill for compatible USB CDC ACM devices, with a USB host/OTG connection and browser permission. This is not universal USB-serial driver support: proprietary bridges, USB interface claiming, OS restrictions and bootloader re-enumeration can prevent operation. Neither Android hardware flashing nor device compatibility has been established by the automated tests. Firefox/Safari and iOS are not implied to be supported.
For nRF52, select the inner DFU firmware.zip (not an outer distribution archive). Enter DFU by 1200-baud touch or double-tap RESET, then choose the newly enumerated bootloader port when flashing. No erase control is offered without a configured erase package. Custom -merged.bin uploads use address zero and warn that flash will be erased; ordinary BIN uploads use 0x10000 without full erase. The configured ESP32 initial-install path defaults Erase Device on; disable it only when an update-only operation is intended.