Merge branch 'main' of NabuCasa/esp-web-flasher

pull/34/head
Melissa LeBlanc-Williams 4 years ago
commit 7729ef1a05

@ -1,2 +0,0 @@
# Scrubbed by Glitch 2020-11-13T22:00:25+0000

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"

@ -0,0 +1,9 @@
categories:
- title: "⬆️ Dependencies"
collapse-after: 1
labels:
- "dependencies"
template: |
## What's Changed
$CHANGES

@ -0,0 +1,26 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
- run: script/build
- run: npm exec -- prettier --check src

@ -0,0 +1,22 @@
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
name: Node.js Package
on:
release:
types: [published]
jobs:
publish-npm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

@ -0,0 +1,14 @@
name: Release Drafter
on:
push:
branches:
- main
jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored

@ -1 +1,3 @@
*.DS_Store
dist
node_modules

@ -0,0 +1 @@
src/stubs

@ -1,5 +1,26 @@
# Adafruit ESPTool
# This package is deprecated. Use [esptool-js](https://github.com/espressif/esptool-js/) instead.
A Web Serial tool for updating your ESP bootloader. Please submit any new issues or pull requests to https://github.com/NabuCasa/esp-web-flasher.
# ESP Web Flasher
A live copy of the tool is hosted here: https://nabucasa.github.io/esp-web-flasher/
JavaScript package to install firmware on ESP devices via the browser using WebSerial.
_This project is a collaboration between [Adafruit](https://www.adafruit.com/) and [Nabu Casa](https://www.nabucasa.com/)._
## Used by
- [Adafruit WipperSnapper](https://learn.adafruit.com/quickstart-adafruit-io-wippersnapper)
- [ESP Web Tools](https://github.com/esphome/esp-web-tools), the one click button to install your ESP devices via the browser.
## Local development
- Clone this repository.
- Install dependencies with `npm`
- Run `script/develop`
- Open http://localhost:5004/
## Origin
This project was originally written by [Melissa LeBlanc-Williams](https://github.com/makermelissa). [Nabu Casa](https://www.nabucasa.com) ported the code over to TypeScript and in March 2022 took over maintenance from Adafruit. In July 2022, the Nabucasa stopped maintaining the project in favor of an official, but very early release of Espressif's [esptool-js](https://github.com/espressif/esptool-js/). Due to the instability of the tool, Adafruit updated their fork with Nabucasa's changes and took over maintenance once again.
A live copy of the tool is hosted here: https://adafruit.github.io/Adafruit_WebSerial_ESPTool/

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="115" height="40" viewBox="0 0 115 40">
<g fill="#FFF" fill-rule="evenodd">
<path d="M33.793 27.417c.52-.533 1.13-.75 2.304-.75h4.728c1.317 0 2.093.24 2.565.75.588.602.777 1.306.777 3.556v9.002h-3.6v-1.523C39.016 39.758 38.452 40 37.299 40h-1.765c-1.152 0-1.763-.147-2.281-.676-.447-.462-.752-1.236-.752-2.977 0-1.646.235-2.663.8-3.244.587-.605 1.34-.896 3.668-.896 1.248 0 2.54.147 3.6.389v-1.187c0-1.308-.072-1.573-.236-1.766-.166-.148-.4-.196-.94-.196h-1.81c-.496 0-.755.048-.896.196-.187.193-.212.458-.26 1.258h-3.408c.022-2.225.234-2.953.775-3.484zm6.775 9.242v-2.226h-2.375c-1.366 0-1.694.097-1.93.338-.234.243-.282.534-.282 1.138 0 .68.048 1.04.235 1.235.189.192.447.217.917.217h1.012c.752 0 1.247-.071 2.423-.702zM54.568 39.974v-1.76C52.87 39.701 52.28 40 51.171 40h-1.426c-1.427 0-2.19-.248-2.78-.843-.81-.82-1.132-2.012-1.132-6.006 0-4.018.392-5.158 1.231-6.003.59-.594 1.377-.842 2.73-.842h1.475c1.133 0 1.749.248 3.3 1.562v-6.201h3.764v18.307h-3.765zm0-3.597V29.88c-.79-.372-1.354-.546-1.968-.546h-1.353c-.69 0-1.011.1-1.232.321-.344.323-.442.895-.442 3.497 0 2.605.098 3.153.442 3.5.22.222.542.298 1.232.298h1.229c.64 0 1.255-.175 2.092-.572zM60.46 27.417c.518-.533 1.13-.75 2.305-.75h4.728c1.316 0 2.093.24 2.564.75.587.602.776 1.306.776 3.556v9.002h-3.599v-1.523C65.682 39.758 65.117 40 63.965 40h-1.762c-1.154 0-1.767-.147-2.283-.676-.447-.462-.753-1.236-.753-2.977 0-1.646.235-2.663.8-3.244.588-.605 1.34-.896 3.668-.896 1.248 0 2.541.147 3.6.389v-1.187c0-1.308-.072-1.573-.235-1.766-.164-.148-.401-.196-.942-.196h-1.81c-.495 0-.753.048-.894.196-.188.193-.212.458-.26 1.258h-3.41c.024-2.225.236-2.953.776-3.484zm6.774 9.242v-2.226H64.86c-1.365 0-1.694.097-1.93.338-.234.243-.282.534-.282 1.138 0 .68.048 1.04.236 1.235.188.192.445.217.916.217h1.01c.754 0 1.248-.071 2.424-.702zM72.5 40V25.692c0-1.715.227-2.61.803-3.18.577-.572 1.228-.845 2.706-.845h3.158v2.88H77.31c-.5 0-.65.05-.8.175-.127.15-.176.42-.176 1.093v.548h2.833v2.955h-2.808V40H72.5M80.833 40V26.69h3.727v2.033c1.39-1.429 2.096-2.056 3.169-2.056h1.438v3.653h-1.73c-1.024 0-1.804.096-2.877.532V40h-3.727M101.667 26.667v13.308h-3.63V38.21c-1.65 1.523-2.191 1.79-3.37 1.79h-1.58c-1.131 0-1.84-.242-2.332-.751-.52-.556-.755-1.258-.755-3.194v-9.388h3.63v8.977c0 .555.07.871.282 1.09.212.216.424.29 1.062.29h1.037c.612 0 1.155-.146 2.027-.533v-9.824h3.629M103.333 21.667h3.334v2.78h-3.334v-2.78zm0 4.67h3.334V40h-3.334V26.338zM115 36.84h-1.648c-.626 0-.874-.052-.998-.15-.126-.152-.176-.351-.176-.955v-6.648H115v-3.011h-2.822v-1.91h-3.845v12.599c0 1.404.175 2.031.625 2.483.475.478 1.173.752 2.472.752 1.023 0 2.047-.15 2.645-.301l.925-.24v-2.62"/>
<path d="M39.974 17.619c-.131-.82-.575-1.054-1.647-1.398-.815-.261-6.272-2-6.272-2s-3.802-1.436-6.622.595c-.38.275-.773.586-1.169.92.333-.396.644-.79.917-1.17 2.026-2.824.583-6.626.583-6.626s-1.747-5.455-2.01-6.27c-.346-1.072-.58-1.516-1.4-1.644-.82-.13-1.179.22-1.837 1.135-.5.693-3.84 5.348-3.84 5.348s-2.54 3.173-1.48 6.484c.143.446.318.916.513 1.395-.274-.439-.552-.856-.83-1.233-2.059-2.8-6.118-2.603-6.118-2.603s-5.727-.022-6.584-.024c-1.125-.003-1.62.084-1.995.822-.377.74-.154 1.191.51 2.098.506.691 3.9 5.307 3.9 5.307s2.233 3.398 5.707 3.412c.467.002.967-.019 1.482-.057-.501.126-.983.262-1.426.408-3.298 1.094-4.365 5.017-4.365 5.017L3.933 33.79c-.35 1.07-.421 1.567.166 2.153.587.587 1.083.515 2.153.162.813-.267 6.25-2.07 6.25-2.07s3.92-1.073 5.007-4.375c.146-.443.28-.924.404-1.424-.035.514-.055 1.013-.053 1.48.021 3.475 3.422 5.703 3.422 5.703l5.31 3.89c.91.663 1.36.885 2.1.507.738-.377.823-.872.818-1.998-.003-.855-.036-6.584-.036-6.584s.19-4.061-2.612-6.116c-.377-.276-.793-.553-1.232-.826.479.194.948.367 1.393.508 3.31 1.056 6.48-1.49 6.48-1.49s4.646-3.35 5.34-3.852c.912-.66 1.26-1.02 1.13-1.84zM20.249 13.4c.624.099.934 1.428.692 2.968-.243 1.541-.946 2.71-1.57 2.612-.626-.098-.936-1.427-.693-2.967.243-1.541.946-2.711 1.57-2.613zm-8 4.087c.286-.564 1.645-.448 3.035.26 1.39.705 2.285 1.736 1.999 2.3-.288.564-1.646.447-3.036-.258-1.39-.708-2.285-1.738-1.998-2.302zm4.218 7.684c-1.102 1.104-2.358 1.637-2.806 1.19-.447-.448.084-1.705 1.185-2.81 1.102-1.103 2.358-1.635 2.806-1.188.447.447-.083 1.704-1.185 2.808zm6.068 2.587c-.563.288-1.594-.605-2.303-1.994-.71-1.39-.828-2.75-.265-3.037.563-.287 1.595.605 2.304 1.994.71 1.389.828 2.75.264 3.037zm1.463-6.434c-1.54.245-2.87-.062-2.97-.687-.097-.625 1.07-1.33 2.61-1.575 1.54-.245 2.87.061 2.968.686.1.624-1.068 1.33-2.608 1.576z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

@ -45,7 +45,7 @@ input, select, button {
color: #333;
}
#notSupported {
.notSupported, .notSupported a {
background-color: red;
color: white;
}

@ -13,7 +13,7 @@ input, select, button {
color: #000;
}
#notSupported {
.notSupported, .notSupported a {
background-color: red;
color: white;
}
@ -76,4 +76,4 @@ input, select, button {
.progress-bar {
border-color: #333;
}
}

@ -34,7 +34,8 @@
color: #000;
}
button, .firmware {
button,
.firmware {
height: 25px;
font-size: 16px;
border-radius: 15px;
@ -54,7 +55,10 @@ p {
margin: 0;
}
input, select, button, label {
input,
select,
button,
label {
font-weight: 600;
outline: none;
}
@ -75,12 +79,6 @@ div.clear {
clear: both;
}
.Adafruit-Logo {
width: 115px;
height: 40px;
object-fit: contain;
}
.main {
overflow-x: hidden;
overflow-y: auto;
@ -125,7 +123,8 @@ div.clear {
height: calc(100vh - 330px);
}
#commands, #log {
#commands,
#log {
transition: height 0.5s;
}
@ -144,7 +143,7 @@ div.clear {
font-size: 16px;
overflow-x: hidden;
overflow-x: auto;
transition : color 0.1s linear;
transition: color 0.1s linear;
padding: 0 50px;
border: 20px solid #000;
-ms-overflow-style: none;
@ -158,12 +157,18 @@ div.clear {
}
.footer {
height: 45px;
line-height: 45px;
padding-left: 70px;
padding-right: 70px;
}
.footer a {
color: blue;
}
.footer .controls {
line-height: 45px;
}
.footer button {
font-size: 14px;
margin: 0 10px;
@ -276,10 +281,7 @@ div.clear {
.footer .onoffswitch {
margin-right: 20px;
}
#fpsCounter {
margin-right: 10px;
top: 7px;
}
#butClear {
@ -310,12 +312,12 @@ div.clear {
}
.firmware > input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.firmware {
@ -372,3 +374,10 @@ div.clear {
border-width: 2px;
border-style: solid;
}
.justify {
display: flex;
justify-content: space-between;
}
.center {
text-align: center;
}

@ -1,16 +1,212 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Redirecting to NabuCasa ESP Web Flasher</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ESP Web Flasher</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
// Redirect to HTTPS if HTTP is requested.
window.location.href = 'https://nabucasa.github.io/esp-web-flasher/';
if (
window.location.hostname !== "localhost" &&
window.location.protocol === "http:"
) {
window.location.href = "https:" + window.location.href.substring(5);
}
</script>
<!-- import the web page's stylesheets along with the themes -->
<link rel="stylesheet" href="https://use.typekit.net/qtk5kiq.css" />
<link rel="stylesheet" href="css/style.css" />
<link
rel="stylesheet"
href="css/light.css"
id="light"
class="alternate"
disabled
/>
<link
rel="stylesheet"
href="css/dark.css"
id="dark"
class="alternate"
disabled
/>
<!-- import the webpage's javascript file -->
<script module>
window.esptoolPackage = import(
// In development we import locally.
window.location.hostname === "localhost"
? "/dist/web/index.js"
: "https://unpkg.com/esp-web-flasher@5.1.4/dist/web/index.js?module"
);
</script>
<script src="js/script.js" module defer></script>
</head>
<body>
Please use <a href="https://nabucasa.github.io/esp-web-flasher/">https://nabucasa.github.io/esp-web-flasher/</a> to flash your ESP boards.
<header class="header">
<div class="left"></div>
<div class="right">
<select id="baudRate"></select>
<button id="butConnect" type="button">Connect</button>
</div>
</header>
<main class="main">
<div class="notSupported">
This website is no longer maintained.
<a href="https://espressif.github.io/esptool-js/"
>Use esptool-js instead.</a
>
</div>
<div id="notSupported" class="notSupported">
Sorry, <b>Web Serial</b> is not supported on this device, make sure
you're running Chrome 89 or later.
</div>
<div class="subheader">
<div class="title left">ESP Web Flasher</div>
<div class="right">
<label for="darkmode"> Dark Mode </label>
<div class="onoffswitch">
<input
type="checkbox"
name="darkmode"
class="onoffswitch-checkbox"
id="darkmode"
/>
<label class="onoffswitch-label" for="darkmode">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</div>
<div id="app">
<div id="commands">
<div class="upload">
<label
>Offset: 0x
<input class="offset" type="text" value="0" />
</label>
<label class="firmware">
<input type="file" accept=".bin" />
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="17"
viewbox="0 0 20 17"
>
<path
d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"
/>
</svg>
<span>Choose a file&hellip;</span>
</label>
<div class="progress-bar hidden"><div></div></div>
</div>
<div class="upload">
<label
>Offset: 0x
<input class="offset" type="text" value="0" />
</label>
<label class="firmware">
<input type="file" accept=".bin" />
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="17"
viewbox="0 0 20 17"
>
<path
d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"
/>
</svg>
<span>Choose a file&hellip;</span>
</label>
<div class="progress-bar hidden"><div></div></div>
</div>
<div class="upload">
<label
>Offset: 0x
<input class="offset" type="text" value="0" />
</label>
<label class="firmware">
<input type="file" accept=".bin" />
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="17"
viewbox="0 0 20 17"
>
<path
d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"
/>
</svg>
<span>Choose a file&hellip;</span>
</label>
<div class="progress-bar hidden"><div></div></div>
</div>
<div class="upload">
<label
>Offset: 0x
<input class="offset" type="text" value="0" />
</label>
<label class="firmware">
<input type="file" accept=".bin" />
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="17"
viewbox="0 0 20 17"
>
<path
d="M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z"
/>
</svg>
<span>Choose a file&hellip;</span>
</label>
<div class="progress-bar hidden"><div></div></div>
</div>
<div class="buttons">
<button id="butErase" type="button" disabled="disabled">
Erase
</button>
<button id="butProgram" type="button" disabled="disabled">
Program
</button>
</div>
</div>
<div id="log"></div>
</div>
</main>
<footer class="footer">
<div class="justify controls">
<div>
<label for="autoscroll">Autoscroll </label>
<div class="onoffswitch">
<input
type="checkbox"
name="autoscroll"
class="onoffswitch-checkbox"
id="autoscroll"
/>
<label class="onoffswitch-label" for="autoscroll">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
<button id="butClear" type="button">Clear Text</button>
</div>
<div class="center">
Maintained by <a href="https://www.nabucasa.com">Nabu Casa</a>. Derived
from
<a href="https://github.com/adafruit/Adafruit_WebSerial_ESPTool"
>Adafruit WebSerial ESPTool</a
>
by
<a href="https://github.com/makermelissa">Melissa LeBlanc-Williams</a>.
</div>
</footer>
</body>
</html>

@ -1,103 +1,64 @@
// Note: the code will still work without this line, but without it you
// will see an error in the editor
/* global EspLoader, ESP_ROM_BAUD, port, reader, inputBuffer */
'use strict';
let espTool;
let isConnected = false;
let espStub;
const baudRates = [921600, 115200, 230400, 460800];
const bufferSize = 512;
const colors = ['#00a7e9', '#f89521', '#be1e2d'];
const measurementPeriodId = '0001';
const colors = ["#00a7e9", "#f89521", "#be1e2d"];
const measurementPeriodId = "0001";
const maxLogLength = 100;
const log = document.getElementById('log');
const butConnect = document.getElementById('butConnect');
const baudRate = document.getElementById('baudRate');
const butClear = document.getElementById('butClear');
const butErase = document.getElementById('butErase');
const butProgram = document.getElementById('butProgram');
const autoscroll = document.getElementById('autoscroll');
const lightSS = document.getElementById('light');
const darkSS = document.getElementById('dark');
const darkMode = document.getElementById('darkmode');
const log = document.getElementById("log");
const butConnect = document.getElementById("butConnect");
const baudRate = document.getElementById("baudRate");
const butClear = document.getElementById("butClear");
const butErase = document.getElementById("butErase");
const butProgram = document.getElementById("butProgram");
const autoscroll = document.getElementById("autoscroll");
const lightSS = document.getElementById("light");
const darkSS = document.getElementById("dark");
const darkMode = document.getElementById("darkmode");
const firmware = document.querySelectorAll(".upload .firmware input");
const progress = document.querySelectorAll(".upload .progress-bar");
const offsets = document.querySelectorAll('.upload .offset');
const appDiv = document.getElementById('app');
let colorIndex = 0;
let activePanels = [];
let bytesReceived = 0;
let currentBoard;
let buttonState = 0;
document.addEventListener('DOMContentLoaded', () => {
let debug = 0;
var getParams = {}
location.search.substr(1).split("&").forEach(function(item) {getParams[item.split("=")[0]] = item.split("=")[1]})
if (getParams["debug"] !== undefined) {
if (getParams["debug"] == "1" || getParams["debug"] == "2") {
debug = parseInt(getParams["debug"]);
}
}
const offsets = document.querySelectorAll(".upload .offset");
const appDiv = document.getElementById("app");
espTool = new EspLoader({
updateProgress: updateProgress,
logMsg: logMsg,
debugMsg: debugMsg,
debug: debug})
butConnect.addEventListener('click', () => {
document.addEventListener("DOMContentLoaded", () => {
butConnect.addEventListener("click", () => {
clickConnect().catch(async (e) => {
errorHandle(e);
disconnect();
console.error(e);
errorMsg(e.message || e);
if (espStub) {
await espStub.disconnect();
}
toggleUIConnected(false);
});
});
butClear.addEventListener('click', clickClear);
butErase.addEventListener('click', clickErase);
butProgram.addEventListener('click', clickProgram);
butClear.addEventListener("click", clickClear);
butErase.addEventListener("click", clickErase);
butProgram.addEventListener("click", clickProgram);
for (let i = 0; i < firmware.length; i++) {
firmware[i].addEventListener('change', checkFirmware);
firmware[i].addEventListener("change", checkFirmware);
}
for (let i = 0; i < offsets.length; i++) {
offsets[i].addEventListener('change', checkProgrammable);
offsets[i].addEventListener("change", checkProgrammable);
}
autoscroll.addEventListener('click', clickAutoscroll);
baudRate.addEventListener('change', changeBaudRate);
darkMode.addEventListener('click', clickDarkMode);
window.addEventListener('error', function(event) {
console.log("Got an uncaught error: ", event.error)
autoscroll.addEventListener("click", clickAutoscroll);
baudRate.addEventListener("change", changeBaudRate);
darkMode.addEventListener("click", clickDarkMode);
window.addEventListener("error", function (event) {
console.log("Got an uncaught error: ", event.error);
});
if ('serial' in navigator) {
const notSupported = document.getElementById('notSupported');
notSupported.classList.add('hidden');
if ("serial" in navigator) {
const notSupported = document.getElementById("notSupported");
notSupported.classList.add("hidden");
}
initBaudRate();
loadAllSettings();
updateTheme();
logMsg("Adafruit WebSerial ESPTool loaded.");
if (debug) {
logMsg("Debugging Level " + debug + " Enabled.");
}
logMsg("ESP Web Flasher loaded.");
});
/**
* @name connect
* Opens a Web Serial connection to a micro:bit and sets up the input and
* output stream.
*/
async function connect() {
logMsg("Connecting...")
await espTool.connect()
readLoop().catch((error) => {
toggleUIConnected(false);
});
}
function initBaudRate() {
for (let rate of baudRates) {
var option = document.createElement("option");
@ -107,38 +68,8 @@ function initBaudRate() {
}
}
function updateProgress(part, percentage) {
let progressBar = progress[part].querySelector("div");
progressBar.style.width = percentage + "%";
}
/**
* @name disconnect
* Closes the Web Serial connection.
*/
async function disconnect() {
toggleUIToolbar(false);
await espTool.disconnect()
}
/**
* @name readLoop
* Reads data from the input stream and places it in the inputBuffer
*/
async function readLoop() {
reader = port.readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}
inputBuffer = inputBuffer.concat(Array.from(value));
}
}
function logMsg(text) {
log.innerHTML += text+ "<br>";
log.innerHTML += text + "<br>";
// Remove old log content
if (log.textContent.split("\n").length > maxLogLength + 1) {
@ -147,24 +78,24 @@ function logMsg(text) {
}
if (autoscroll.checked) {
log.scrollTop = log.scrollHeight
log.scrollTop = log.scrollHeight;
}
}
function debugMsg(debugLevel, ...args) {
function getStackTrace() {
let stack = new Error().stack;
stack = stack.split("\n").map(v => v.trim());
for (let i=0; i<3; i++) {
stack.shift();
}
//console.log(stack);
stack = stack.split("\n").map((v) => v.trim());
stack.shift();
stack.shift();
let trace = [];
for (let line of stack) {
line = line.replace("at ", "");
trace.push({
"func": line.substr(0, line.indexOf("(") - 1),
"pos": line.substring(line.indexOf(".js:") + 4, line.lastIndexOf(":"))
func: line.substr(0, line.indexOf("(") - 1),
pos: line.substring(line.indexOf(".js:") + 4, line.lastIndexOf(":")),
});
}
@ -176,7 +107,8 @@ function debugMsg(debugLevel, ...args) {
let stack = getStackTrace();
stack.shift();
let top = stack.shift();
let prefix = '<span class="debug-function">[' + top.func + ":" + top.pos + ']</span> ';
let prefix =
'<span class="debug-function">[' + top.func + ":" + top.pos + "]</span> ";
for (let arg of args) {
if (arg === undefined) {
logMsg(prefix + "undefined");
@ -189,9 +121,16 @@ function debugMsg(debugLevel, ...args) {
} else if (typeof arg == "boolean") {
logMsg(prefix + (arg ? "true" : "false"));
} else if (Array.isArray(arg)) {
logMsg(prefix + "[" + arg.map(value => espTool.toHex(value)).join(", ") + "]");
} else if (typeof arg == "object" && (arg instanceof Uint8Array)) {
logMsg(prefix + "[" + Array.from(arg).map(value => espTool.toHex(value)).join(", ") + "]");
logMsg(prefix + "[" + arg.map((value) => toHex(value)).join(", ") + "]");
} else if (typeof arg == "object" && arg instanceof Uint8Array) {
logMsg(
prefix +
"[" +
Array.from(arg)
.map((value) => toHex(value))
.join(", ") +
"]"
);
} else {
logMsg(prefix + "Unhandled type of argument:" + typeof arg);
console.log(arg);
@ -200,24 +139,11 @@ function debugMsg(debugLevel, ...args) {
}
}
function errorHandle(error) {
console.error(error);
if (error.message) {
errorMsg(error.message)
} else {
errorMsg(error);
}
}
function errorMsg(text) {
logMsg('<span class="error-message">Error:</span> ' + text);
console.error(text);
}
function formatMacAddr(macAddr) {
return macAddr.map(value => value.toString(16).toUpperCase().padStart(2, "0")).join(":");
}
/**
* @name updateTheme
* Sets the theme to Adafruit (dark) mode. Can be refactored later for more themes
@ -225,7 +151,7 @@ function formatMacAddr(macAddr) {
function updateTheme() {
// Disable all themes
document
.querySelectorAll('link[rel=stylesheet].alternate')
.querySelectorAll("link[rel=stylesheet].alternate")
.forEach((styleSheet) => {
enableStyleSheet(styleSheet, false);
});
@ -241,15 +167,10 @@ function enableStyleSheet(node, enabled) {
node.disabled = !enabled;
}
/**
* @name reset
* Reset the Panels, Log, and associated data
*/
async function reset() {
bytesReceived = 0;
// Clear the log
log.innerHTML = "";
function formatMacAddr(macAddr) {
return macAddr
.map((value) => value.toString(16).toUpperCase().padStart(2, "0"))
.join(":");
}
/**
@ -257,37 +178,37 @@ async function reset() {
* Click handler for the connect/disconnect button.
*/
async function clickConnect() {
if (espTool.connected()) {
await disconnect();
if (espStub) {
await espStub.disconnect();
await espStub.port.close();
toggleUIConnected(false);
espStub = undefined;
return;
}
await connect();
const esploaderMod = await window.esptoolPackage;
toggleUIConnected(true);
const esploader = await esploaderMod.connect({
log: (...args) => logMsg(...args),
debug: (...args) => debugMsg(...args),
error: (...args) => errorMsg(...args),
});
try {
if (await espTool.sync()) {
toggleUIToolbar(true);
appDiv.classList.add("connected");
let baud = parseInt(baudRate.value);
logMsg("Connected to " + await espTool.chipName());
logMsg("MAC Address: " + formatMacAddr(espTool.macAddr()));
espTool = await espTool.runStub();
if (baud != ESP_ROM_BAUD) {
let chipType = await espTool.chipType();
if ([ESP32, ESP32C3].includes(chipType)) {
logMsg("WARNING: ESP32 and ESP32C3 are having issues working at speeds faster than 115200. Continuing at 115200 for now...")
} else {
await changeBaudRate(baud);
}
}
}
} catch(e) {
errorHandle(e);
await disconnect();
toggleUIConnected(false);
return;
await esploader.initialize();
logMsg("Connected to " + esploader.chipName);
logMsg("MAC Address: " + formatMacAddr(esploader.macAddr()));
espStub = await esploader.runStub();
toggleUIConnected(true);
toggleUIToolbar(true);
espStub.addEventListener("disconnect", () => {
toggleUIConnected(false);
espStub = false;
});
} catch (err) {
await esploader.disconnect();
throw err;
}
}
@ -296,11 +217,11 @@ async function clickConnect() {
* Change handler for the Baud Rate selector.
*/
async function changeBaudRate() {
saveSetting('baudrate', baudRate.value);
if (isConnected) {
saveSetting("baudrate", baudRate.value);
if (espStub) {
let baud = parseInt(baudRate.value);
if (baudRates.includes(baud)) {
await espTool.setBaudrate(baud);
await espStub.setBaudrate(baud);
}
}
}
@ -310,7 +231,7 @@ async function changeBaudRate() {
* Change handler for the Autoscroll checkbox.
*/
async function clickAutoscroll() {
saveSetting('autoscroll', autoscroll.checked);
saveSetting("autoscroll", autoscroll.checked);
}
/**
@ -319,7 +240,7 @@ async function clickAutoscroll() {
*/
async function clickDarkMode() {
updateTheme();
saveSetting('darkmode', darkMode.checked);
saveSetting("darkmode", darkMode.checked);
}
/**
@ -327,17 +248,19 @@ async function clickDarkMode() {
* Click handler for the erase button.
*/
async function clickErase() {
if (window.confirm("This will erase the entire flash. Click OK to continue.")) {
if (
window.confirm("This will erase the entire flash. Click OK to continue.")
) {
baudRate.disabled = true;
butErase.disabled = true;
butProgram.disabled = true;
try {
logMsg("Erasing flash memory. Please wait...");
let stamp = Date.now();
await espTool.eraseFlash();
await espStub.eraseFlash();
logMsg("Finished. Took " + (Date.now() - stamp) + "ms to erase.");
} catch(e) {
errorHandle(e);
} catch (e) {
errorMsg(e);
} finally {
butErase.disabled = false;
baudRate.disabled = false;
@ -370,7 +293,7 @@ async function clickProgram() {
baudRate.disabled = true;
butErase.disabled = true;
butProgram.disabled = true;
for (let i=0; i< 4; i++) {
for (let i = 0; i < 4; i++) {
firmware[i].disabled = true;
offsets[i].disabled = true;
}
@ -380,14 +303,21 @@ async function clickProgram() {
let contents = await readUploadedFileAsArrayBuffer(binfile);
try {
let offset = parseInt(offsets[file].value, 16);
await espTool.flashData(contents, offset, file);
const progressBar = progress[file].querySelector("div");
await espStub.flashData(
contents,
(bytesWritten, totalBytes) => {
progressBar.style.width =
Math.floor((bytesWritten / totalBytes) * 100) + "%";
},
offset
);
await sleep(100);
logMsg("To run the new firmware, please reset your device.");
} catch(e) {
errorHandle(e);
} catch (e) {
errorMsg(e);
}
}
for (let i=0; i< 4; i++) {
for (let i = 0; i < 4; i++) {
firmware[i].disabled = false;
offsets[i].disabled = false;
progress[i].classList.add("hidden");
@ -396,6 +326,7 @@ async function clickProgram() {
butErase.disabled = false;
baudRate.disabled = false;
butProgram.disabled = getValidFiles().length == 0;
logMsg("To run the new firmware, please reset your device.");
}
function getValidFiles() {
@ -404,7 +335,7 @@ function getValidFiles() {
// and will also return a list of files to program
let validFiles = [];
let offsetVals = [];
for (let i=0; i<4; i++) {
for (let i = 0; i < 4; i++) {
let offs = parseInt(offsets[i].value, 16);
if (firmware[i].files.length > 0 && !offsetVals.includes(offs)) {
validFiles.push(i);
@ -427,7 +358,7 @@ async function checkProgrammable() {
* Handler for firmware upload changes
*/
async function checkFirmware(event) {
let filename = event.target.value.split("\\" ).pop();
let filename = event.target.value.split("\\").pop();
let label = event.target.parentNode.querySelector("span");
let icon = event.target.parentNode.querySelector("svg");
if (filename != "") {
@ -464,7 +395,7 @@ function convertJSON(chunk) {
function toggleUIToolbar(show) {
isConnected = show;
for (let i=0; i< 4; i++) {
for (let i = 0; i < 4; i++) {
progress[i].classList.add("hidden");
progress[i].querySelector("div").style.width = "0";
}
@ -477,9 +408,9 @@ function toggleUIToolbar(show) {
}
function toggleUIConnected(connected) {
let lbl = 'Connect';
let lbl = "Connect";
if (connected) {
lbl = 'Disconnect';
lbl = "Disconnect";
} else {
toggleUIToolbar(false);
}
@ -488,9 +419,9 @@ function toggleUIConnected(connected) {
function loadAllSettings() {
// Load all saved settings or defaults
autoscroll.checked = loadSetting('autoscroll', true);
baudRate.value = loadSetting('baudrate', baudRates[0]);
darkMode.checked = loadSetting('darkmode', false);
autoscroll.checked = loadSetting("autoscroll", true);
baudRate.value = loadSetting("baudrate", 115200);
darkMode.checked = loadSetting("darkmode", false);
}
function loadSetting(setting, defaultValue) {
@ -506,6 +437,13 @@ function saveSetting(setting, value) {
window.localStorage.setItem(setting, JSON.stringify(value));
}
function ucWords(text) {
return text
.replace("_", " ")
.toLowerCase()
.replace(/(?<= )[^\s]|^./g, (a) => a.toUpperCase());
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
return new Promise((resolve) => setTimeout(resolve, ms));
}

@ -1,5 +1,6 @@
MIT License
Copyright (c) 2021 Nabu Casa
Copyright (c) 2020 Melissa LeBlanc-Williams for Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

2338
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,28 @@
{
"name": "esp-web-flasher",
"version": "5.1.5",
"description": "Flash ESP devices using WebSerial",
"main": "dist/index.js",
"repository": "https://github.com/nabucasa/ESP-Web-flasher",
"author": "Nabu Casa",
"license": "MIT",
"scripts": {
"prepublishOnly": "script/build"
},
"devDependencies": {
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-typescript": "^8.2.1",
"@types/pako": "^2.0.0",
"@types/w3c-web-serial": "^1.0.1",
"prettier": "^2.3.0",
"rollup": "^2.48.0",
"rollup-plugin-terser": "^7.0.2",
"serve": "^13.0.2",
"typescript": "^4.6.2"
},
"dependencies": {
"pako": "^2.0.3",
"tslib": "^2.2.0"
}
}

@ -0,0 +1,27 @@
import { nodeResolve } from "@rollup/plugin-node-resolve";
import json from "@rollup/plugin-json";
import { terser } from "rollup-plugin-terser";
const config = {
input: "dist/index.js",
output: {
dir: "dist/web",
format: "module",
},
// preserveEntrySignatures: false,
plugins: [nodeResolve(), json()],
};
if (process.env.NODE_ENV === "production") {
config.plugins.push(
terser({
ecma: 2019,
toplevel: true,
output: {
comments: false,
},
})
);
}
export default config;

@ -0,0 +1,8 @@
# Stop on errors
set -e
cd "$(dirname "$0")/.."
rm -rf dist
NODE_ENV=production npm exec -- tsc
NODE_ENV=production npm exec -- rollup -c

@ -0,0 +1,17 @@
# Stop on errors
set -e
cd "$(dirname "$0")/.."
rm -rf dist
# Quit all background tasks when script exits
trap "kill 0" EXIT
# Run tsc once as rollup expects those files
npm exec -- tsc || true
npm exec -- serve -p 5004 &
npm exec -- tsc --watch &
npm exec -- rollup -c --watch &
wait

@ -42,6 +42,6 @@ for key, stub in stubs.items():
jsondata = json.dumps(code)
f = open("../stubs/" + key + ".json", "w+")
f = open("../src/stubs/" + key + ".json", "w+")
f.write(jsondata)
f.close()
f.close()

@ -0,0 +1,303 @@
import { toByteArray } from "./util";
export interface Logger {
log(msg: string, ...args: any[]): void;
error(msg: string, ...args: any[]): void;
debug(msg: string, ...args: any[]): void;
}
export const baudRates = [
115200, 128000, 153600, 230400, 460800, 921600, 1500000, 2000000,
];
export const FLASH_SIZES = {
"512KB": 0x00,
"256KB": 0x10,
"1MB": 0x20,
"2MB": 0x30,
"4MB": 0x40,
"2MB-c1": 0x50,
"4MB-c1": 0x60,
"8MB": 0x80,
"16MB": 0x90,
};
export const ESP32_FLASH_SIZES = {
"1MB": 0x00,
"2MB": 0x10,
"4MB": 0x20,
"8MB": 0x30,
"16MB": 0x40,
"32MB": 0x50,
"64MB": 0x60,
"128MB": 0x70,
};
export const DETECTED_FLASH_SIZES = {
0x12: "256KB",
0x13: "512KB",
0x14: "1MB",
0x15: "2MB",
0x16: "4MB",
0x17: "8MB",
0x18: "16MB",
0x19: "32MB",
0x1a: "64MB",
};
export const FLASH_WRITE_SIZE = 0x400;
export const STUB_FLASH_WRITE_SIZE = 0x4000;
export const FLASH_SECTOR_SIZE = 0x1000; // Flash sector size, minimum unit of erase.
export const ESP_ROM_BAUD = 115200;
export const ESP8266_SPI_REG_BASE = 0x60000200;
export const ESP8266_BASEFUSEADDR = 0x3ff00050;
export const ESP8266_MACFUSEADDR = 0x3ff00050;
export const ESP8266_SPI_USR_OFFS = 0x1c;
export const ESP8266_SPI_USR1_OFFS = 0x20;
export const ESP8266_SPI_USR2_OFFS = 0x24;
export const ESP8266_SPI_MOSI_DLEN_OFFS = -1;
export const ESP8266_SPI_MISO_DLEN_OFFS = -1;
export const ESP8266_SPI_W0_OFFS = 0x40;
export const ESP8266_UART_DATE_REG_ADDR = 0x60000078;
export const ESP8266_BOOTLOADER_FLASH_OFFSET = 0x0;
export const ESP32_SPI_REG_BASE = 0x3ff42000;
export const ESP32_BASEFUSEADDR = 0x3ff5a000;
export const ESP32_MACFUSEADDR = 0x3ff5a000;
export const ESP32_SPI_USR_OFFS = 0x1c;
export const ESP32_SPI_USR1_OFFS = 0x20;
export const ESP32_SPI_USR2_OFFS = 0x24;
export const ESP32_SPI_MOSI_DLEN_OFFS = 0x28;
export const ESP32_SPI_MISO_DLEN_OFFS = 0x2c;
export const ESP32_SPI_W0_OFFS = 0x80;
export const ESP32_UART_DATE_REG_ADDR = 0x60000078;
export const ESP32_BOOTLOADER_FLASH_OFFSET = 0x1000;
export const ESP32S2_SPI_REG_BASE = 0x3f402000;
export const ESP32S2_BASEFUSEADDR = 0x3f41a000;
export const ESP32S2_MACFUSEADDR = 0x3f41a044;
export const ESP32S2_SPI_USR_OFFS = 0x18;
export const ESP32S2_SPI_USR1_OFFS = 0x1c;
export const ESP32S2_SPI_USR2_OFFS = 0x20;
export const ESP32S2_SPI_MOSI_DLEN_OFFS = 0x24;
export const ESP32S2_SPI_MISO_DLEN_OFFS = 0x28;
export const ESP32S2_SPI_W0_OFFS = 0x58;
export const ESP32S2_UART_DATE_REG_ADDR = 0x60000078;
export const ESP32S2_BOOTLOADER_FLASH_OFFSET = 0x1000;
export const ESP32S3_SPI_REG_BASE = 0x60002000;
export const ESP32S3_BASEFUSEADDR = 0x60007000;
export const ESP32S3_MACFUSEADDR = 0x60007000 + 0x044;
export const ESP32S3_SPI_USR_OFFS = 0x18;
export const ESP32S3_SPI_USR1_OFFS = 0x1c;
export const ESP32S3_SPI_USR2_OFFS = 0x20;
export const ESP32S3_SPI_MOSI_DLEN_OFFS = 0x24;
export const ESP32S3_SPI_MISO_DLEN_OFFS = 0x28;
export const ESP32S3_SPI_W0_OFFS = 0x58;
export const ESP32S3_UART_DATE_REG_ADDR = 0x60000080;
export const ESP32S3_BOOTLOADER_FLASH_OFFSET = 0x0;
export const ESP32C3_SPI_REG_BASE = 0x60002000;
export const ESP32C3_BASEFUSEADDR = 0x60008800;
export const ESP32C3_MACFUSEADDR = 0x60008800 + 0x044;
export const ESP32C3_SPI_USR_OFFS = 0x18;
export const ESP32C3_SPI_USR1_OFFS = 0x1c;
export const ESP32C3_SPI_USR2_OFFS = 0x20;
export const ESP32C3_SPI_MOSI_DLEN_OFFS = 0x24;
export const ESP32C3_SPI_MISO_DLEN_OFFS = 0x28;
export const ESP32C3_SPI_W0_OFFS = 0x58;
export const ESP32C3_UART_DATE_REG_ADDR = 0x6000007c;
export const ESP32C3_BOOTLOADER_FLASH_OFFSET = 0x0;
export interface SpiFlashAddresses {
regBase: number;
baseFuse: number;
macFuse: number;
usrOffs: number;
usr1Offs: number;
usr2Offs: number;
mosiDlenOffs: number;
misoDlenOffs: number;
w0Offs: number;
uartDateReg: number;
flashOffs: number;
}
export const SYNC_PACKET = toByteArray(
"\x07\x07\x12 UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"
);
export const CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000;
// These values for the families are made up; nothing that esptool uses.
export const CHIP_FAMILY_ESP8266 = 0x8266;
export const CHIP_FAMILY_ESP32 = 0x32;
export const CHIP_FAMILY_ESP32S2 = 0x3252;
export const CHIP_FAMILY_ESP32S3 = 0x3253;
export const CHIP_FAMILY_ESP32C3 = 0x32c3;
export const CHIP_FAMILY_ESP32C6 = 0x32c6;
export const CHIP_FAMILY_ESP32H2 = 0x3272;
export type ChipFamily =
| typeof CHIP_FAMILY_ESP8266
| typeof CHIP_FAMILY_ESP32
| typeof CHIP_FAMILY_ESP32S2
| typeof CHIP_FAMILY_ESP32S3
| typeof CHIP_FAMILY_ESP32C3
| typeof CHIP_FAMILY_ESP32C6
| typeof CHIP_FAMILY_ESP32H2;
export const CHIP_DETECT_MAGIC_VALUES = {
0xfff0c101: { name: "ESP8266", family: CHIP_FAMILY_ESP8266 },
0x00f01d83: { name: "ESP32", family: CHIP_FAMILY_ESP32 },
0x000007c6: { name: "ESP32-S2", family: CHIP_FAMILY_ESP32S2 },
0x9: { name: "ESP32-S3", family: CHIP_FAMILY_ESP32S3 },
0xeb004136: { name: "ESP32-S3(beta2)", family: CHIP_FAMILY_ESP32S3 },
0x6921506f: { name: "ESP32-C3", family: CHIP_FAMILY_ESP32C3 },
0x1b31506f: { name: "ESP32-C3", family: CHIP_FAMILY_ESP32C3 },
0xca26cc22: { name: "ESP32-H2", family: CHIP_FAMILY_ESP32H2 },
0x0da1806f: { name: "ESP32-C6(beta)", family: CHIP_FAMILY_ESP32C6 },
};
// Commands supported by ESP8266 ROM bootloader
export const ESP_FLASH_BEGIN = 0x02;
export const ESP_FLASH_DATA = 0x03;
export const ESP_FLASH_END = 0x04;
export const ESP_MEM_BEGIN = 0x05;
export const ESP_MEM_END = 0x06;
export const ESP_MEM_DATA = 0x07;
export const ESP_SYNC = 0x08;
export const ESP_WRITE_REG = 0x09;
export const ESP_READ_REG = 0x0a;
export const ESP_ERASE_FLASH = 0xd0;
export const ESP_ERASE_REGION = 0xd1;
export const ESP_SPI_SET_PARAMS = 0x0b;
export const ESP_SPI_ATTACH = 0x0d;
export const ESP_CHANGE_BAUDRATE = 0x0f;
export const ESP_SPI_FLASH_MD5 = 0x13;
export const ESP_CHECKSUM_MAGIC = 0xef;
export const ESP_FLASH_DEFL_BEGIN = 0x10;
export const ESP_FLASH_DEFL_DATA = 0x11;
export const ESP_FLASH_DEFL_END = 0x12;
export const ROM_INVALID_RECV_MSG = 0x05;
export const USB_RAM_BLOCK = 0x800;
export const ESP_RAM_BLOCK = 0x1800;
// Timeouts
export const DEFAULT_TIMEOUT = 3000;
export const CHIP_ERASE_TIMEOUT = 600000; // timeout for full chip erase in ms
export const MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2; // longest any command can run in ms
export const SYNC_TIMEOUT = 100; // timeout for syncing with bootloader in ms
export const ERASE_REGION_TIMEOUT_PER_MB = 30000; // timeout (per megabyte) for erasing a region in ms
export const MEM_END_ROM_TIMEOUT = 500;
/**
* @name timeoutPerMb
* Scales timeouts which are size-specific
*/
export const timeoutPerMb = (secondsPerMb: number, sizeBytes: number) => {
let result = Math.floor(secondsPerMb * (sizeBytes / 0x1e6));
if (result < DEFAULT_TIMEOUT) {
return DEFAULT_TIMEOUT;
}
return result;
};
export const getSpiFlashAddresses = (
chipFamily: ChipFamily
): SpiFlashAddresses => {
switch (chipFamily) {
case CHIP_FAMILY_ESP32:
return {
regBase: ESP32_SPI_REG_BASE,
baseFuse: ESP32_BASEFUSEADDR,
macFuse: ESP32_MACFUSEADDR,
usrOffs: ESP32_SPI_USR_OFFS,
usr1Offs: ESP32_SPI_USR1_OFFS,
usr2Offs: ESP32_SPI_USR2_OFFS,
mosiDlenOffs: ESP32_SPI_MOSI_DLEN_OFFS,
misoDlenOffs: ESP32_SPI_MISO_DLEN_OFFS,
w0Offs: ESP32_SPI_W0_OFFS,
uartDateReg: ESP32_UART_DATE_REG_ADDR,
flashOffs: ESP32_BOOTLOADER_FLASH_OFFSET,
};
case CHIP_FAMILY_ESP32S2:
return {
regBase: ESP32S2_SPI_REG_BASE,
baseFuse: ESP32S2_BASEFUSEADDR,
macFuse: ESP32S2_MACFUSEADDR,
usrOffs: ESP32S2_SPI_USR_OFFS,
usr1Offs: ESP32S2_SPI_USR1_OFFS,
usr2Offs: ESP32S2_SPI_USR2_OFFS,
mosiDlenOffs: ESP32S2_SPI_MOSI_DLEN_OFFS,
misoDlenOffs: ESP32S2_SPI_MISO_DLEN_OFFS,
w0Offs: ESP32S2_SPI_W0_OFFS,
uartDateReg: ESP32S2_UART_DATE_REG_ADDR,
flashOffs: ESP32S2_BOOTLOADER_FLASH_OFFSET,
};
case CHIP_FAMILY_ESP32S3:
return {
regBase: ESP32S3_SPI_REG_BASE,
usrOffs: ESP32S3_SPI_USR_OFFS,
baseFuse: ESP32S3_BASEFUSEADDR,
macFuse: ESP32S3_MACFUSEADDR,
usr1Offs: ESP32S3_SPI_USR1_OFFS,
usr2Offs: ESP32S3_SPI_USR2_OFFS,
mosiDlenOffs: ESP32S3_SPI_MOSI_DLEN_OFFS,
misoDlenOffs: ESP32S3_SPI_MISO_DLEN_OFFS,
w0Offs: ESP32S3_SPI_W0_OFFS,
uartDateReg: ESP32S3_UART_DATE_REG_ADDR,
flashOffs: ESP32S3_BOOTLOADER_FLASH_OFFSET,
};
case CHIP_FAMILY_ESP8266:
return {
regBase: ESP8266_SPI_REG_BASE,
usrOffs: ESP8266_SPI_USR_OFFS,
baseFuse: ESP8266_BASEFUSEADDR,
macFuse: ESP8266_MACFUSEADDR,
usr1Offs: ESP8266_SPI_USR1_OFFS,
usr2Offs: ESP8266_SPI_USR2_OFFS,
mosiDlenOffs: ESP8266_SPI_MOSI_DLEN_OFFS,
misoDlenOffs: ESP8266_SPI_MISO_DLEN_OFFS,
w0Offs: ESP8266_SPI_W0_OFFS,
uartDateReg: ESP8266_UART_DATE_REG_ADDR,
flashOffs: ESP8266_BOOTLOADER_FLASH_OFFSET,
};
case CHIP_FAMILY_ESP32C3:
return {
regBase: ESP32C3_SPI_REG_BASE,
baseFuse: ESP32C3_BASEFUSEADDR,
macFuse: ESP32C3_MACFUSEADDR,
usrOffs: ESP32C3_SPI_USR_OFFS,
usr1Offs: ESP32C3_SPI_USR1_OFFS,
usr2Offs: ESP32C3_SPI_USR2_OFFS,
mosiDlenOffs: ESP32C3_SPI_MOSI_DLEN_OFFS,
misoDlenOffs: ESP32C3_SPI_MISO_DLEN_OFFS,
w0Offs: ESP32C3_SPI_W0_OFFS,
uartDateReg: ESP32C3_UART_DATE_REG_ADDR,
flashOffs: ESP32C3_BOOTLOADER_FLASH_OFFSET,
};
default:
return {
regBase: -1,
baseFuse: -1,
macFuse: -1,
usrOffs: -1,
usr1Offs: -1,
usr2Offs: -1,
mosiDlenOffs: -1,
misoDlenOffs: -1,
w0Offs: -1,
uartDateReg: -1,
flashOffs: -1,
};
}
};
export class SlipReadError extends Error {
constructor(message: string) {
super(message);
this.name = "SlipReadError";
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,27 @@
import { ESP_ROM_BAUD, Logger } from "./const";
import { ESPLoader } from "./esp_loader";
export type { Logger } from "./const";
export { ESPLoader } from "./esp_loader";
export {
CHIP_FAMILY_ESP32,
CHIP_FAMILY_ESP32S2,
CHIP_FAMILY_ESP32S3,
CHIP_FAMILY_ESP8266,
CHIP_FAMILY_ESP32C3,
CHIP_FAMILY_ESP32C6,
CHIP_FAMILY_ESP32H2,
} from "./const";
export const connect = async (logger: Logger) => {
// - Request a port and open a connection.
const port = await navigator.serial.requestPort();
logger.log("Connecting...");
await port.open({ baudRate: ESP_ROM_BAUD });
logger.log("Connected successfully.");
return new ESPLoader(port, logger);
};

@ -0,0 +1,108 @@
const lut = {
b: { u: DataView.prototype.getInt8, p: DataView.prototype.setInt8, bytes: 1 },
B: {
u: DataView.prototype.getUint8,
p: DataView.prototype.setUint8,
bytes: 1,
},
h: {
u: DataView.prototype.getInt16,
p: DataView.prototype.setInt16,
bytes: 2,
},
H: {
u: DataView.prototype.getUint16,
p: DataView.prototype.setUint16,
bytes: 2,
},
i: {
u: DataView.prototype.getInt32,
p: DataView.prototype.setInt32,
bytes: 4,
},
I: {
u: DataView.prototype.getUint32,
p: DataView.prototype.setUint32,
bytes: 4,
},
q: {
// @ts-ignore
u: DataView.prototype.getInt64,
// @ts-ignore
p: DataView.prototype.setInt64,
bytes: 8,
},
Q: {
// @ts-ignore
u: DataView.prototype.getUint64,
// @ts-ignore
p: DataView.prototype.setUint64,
bytes: 8,
},
};
export const pack = (format: string, ...data: number[]) => {
let pointer = 0;
if (format.replace(/[<>]/, "").length != data.length) {
throw "Pack format to Argument count mismatch";
}
let bytes: number[] = [];
let littleEndian = true;
for (let i = 0; i < format.length; i++) {
if (format[i] == "<") {
littleEndian = true;
} else if (format[i] == ">") {
littleEndian = false;
} else {
pushBytes(format[i], data[pointer]);
pointer++;
}
}
function pushBytes(formatChar: string, value: number) {
if (!(formatChar in lut)) {
throw "Unhandled character '" + formatChar + "' in pack format";
}
let dataSize = lut[formatChar].bytes;
let view = new DataView(new ArrayBuffer(dataSize));
let dataViewFn = lut[formatChar].p.bind(view);
dataViewFn(0, value, littleEndian);
for (let i = 0; i < dataSize; i++) {
bytes.push(view.getUint8(i));
}
}
return bytes;
};
export const unpack = (format: string, bytes: number[]) => {
let pointer = 0;
let data: number[] = [];
let littleEndian = true;
for (let c of format) {
if (c == "<") {
littleEndian = true;
} else if (c == ">") {
littleEndian = false;
} else {
pushData(c);
}
}
function pushData(formatChar: string) {
if (!(formatChar in lut)) {
throw "Unhandled character '" + formatChar + "' in unpack format";
}
let dataSize = lut[formatChar].bytes;
let view = new DataView(new ArrayBuffer(dataSize));
for (let i = 0; i < dataSize; i++) {
view.setUint8(i, bytes[pointer + i] & 0xff);
}
let dataViewFn = lut[formatChar].u.bind(view);
data.push(dataViewFn(0, littleEndian));
pointer += dataSize;
}
return data;
};

@ -0,0 +1 @@
{"text": "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", "text_start": 1077411840, "entry": 1077413328, "data": "DEDKPw==", "data_start": 1070295976}

@ -0,0 +1 @@
{"text": "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", "text_start": 1077411840, "entry": 1077413488, "data": "DEDIPw==", "data_start": 1070164904}

@ -0,0 +1 @@
{"text": "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", "text_start": 1077411840, "entry": 1077413328, "data": "DEDIPw==", "data_start": 1070164904}

File diff suppressed because one or more lines are too long

@ -0,0 +1,48 @@
import {
ChipFamily,
CHIP_FAMILY_ESP32,
CHIP_FAMILY_ESP32S2,
CHIP_FAMILY_ESP32S3,
CHIP_FAMILY_ESP8266,
CHIP_FAMILY_ESP32C3
} from "../const";
import { toByteArray } from "../util";
interface LoadedStub {
text: string;
data: string;
text_start: number;
entry: number;
data_start: number;
}
interface Stub {
text: number[];
data: number[];
text_start: number;
entry: number;
data_start: number;
}
export const getStubCode = async (chipFamily: ChipFamily): Promise<Stub> => {
let stubcode!: LoadedStub;
if (chipFamily == CHIP_FAMILY_ESP32) {
stubcode = await import("./esp32.json");
} else if (chipFamily == CHIP_FAMILY_ESP32S2) {
stubcode = await import("./esp32s2.json");
} else if (chipFamily == CHIP_FAMILY_ESP32S3) {
stubcode = await import("./esp32s3.json");
} else if (chipFamily == CHIP_FAMILY_ESP8266) {
stubcode = await import("./esp8266.json");
} else if (chipFamily == CHIP_FAMILY_ESP32C3) {
stubcode = await import("./esp32c3.json");
}
// Base64 decode the text and data
return {
...stubcode,
text: toByteArray(atob(stubcode.text)),
data: toByteArray(atob(stubcode.data)),
};
};

@ -0,0 +1,49 @@
/**
* @name slipEncode
* Take an array buffer and return back a new array where
* 0xdb is replaced with 0xdb 0xdd and 0xc0 is replaced with 0xdb 0xdc
*/
export const slipEncode = (buffer: number[]): number[] => {
let encoded = [0xc0];
for (let byte of buffer) {
if (byte == 0xdb) {
encoded = encoded.concat([0xdb, 0xdd]);
} else if (byte == 0xc0) {
encoded = encoded.concat([0xdb, 0xdc]);
} else {
encoded.push(byte);
}
}
encoded.push(0xc0);
return encoded;
};
/**
* @name toByteArray
* Convert a string to a byte array
*/
export const toByteArray = (str: string): number[] => {
let byteArray: number[] = [];
for (let i = 0; i < str.length; i++) {
let charcode = str.charCodeAt(i);
if (charcode <= 0xff) {
byteArray.push(charcode);
}
}
return byteArray;
};
export const hexFormatter = (bytes: number[]) =>
"[" + bytes.map((value) => toHex(value)).join(", ") + "]";
export const toHex = (value: number, size = 2) => {
let hex = value.toString(16).toUpperCase();
if (hex.startsWith("-")) {
return "-0x" + hex.substring(1).padStart(size, "0");
} else {
return "0x" + hex.padStart(size, "0");
}
};
export const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));

@ -0,0 +1,20 @@
{
"compilerOptions": {
"lib": ["es2019", "dom"],
"target": "es2019",
"module": "es2020",
"moduleResolution": "node",
"resolveJsonModule": true,
"outDir": "dist",
"declaration": true,
"experimentalDecorators": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"suppressImplicitAnyIndexErrors": true,
"importHelpers": true
},
"include": ["src/*"]
}
Loading…
Cancel
Save