Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,42 @@ Example manifest:
}
```

Builds can optionally include `flashSizeMB` and `psramSizeMB` to target specific hardware variants. When multiple builds share the same `chipFamily`, the most specific match wins:

```json
{
"builds": [
{
"chipFamily": "ESP32-S3",
"flashSizeMB": 16,
"psramSizeMB": 8,
"parts": [{ "path": "s3-16mb-8psram.bin", "offset": 0 }]
},
{
"chipFamily": "ESP32-S3",
"flashSizeMB": 4,
"parts": [{ "path": "s3-4mb.bin", "offset": 0 }]
},
{
"chipFamily": "ESP32-S3",
"parts": [{ "path": "s3-generic.bin", "offset": 0 }]
}
]
}
```

A build with no qualifiers acts as a fallback for that chip family.

## Diagnostics

ESP Web Tools includes a diagnostics button that reads hardware information from a connected ESP device without installing any firmware:

```html
<esp-web-diagnostics-button></esp-web-diagnostics-button>
```

This displays the chip description, features, crystal frequency, MAC address, flash size, and PSRAM size. Like the install button, it supports `activate`, `unsupported`, and `not-allowed` slots for customization, and uses the same CSS custom properties for styling.

## Development

Run `script/develop`. This starts a server. Open it on http://localhost:5001.
Expand Down
97 changes: 95 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@
? "/dist/web/install-button.js"
: "https://unpkg.com/esp-web-tools/dist/web/install-button.js?module"
);
import(
window.location.hostname === "localhost"
? "/dist/web/diagnostics-button.js"
: "https://unpkg.com/esp-web-tools/dist/web/diagnostics-button.js?module"
);
</script>
</head>
<body>
Expand All @@ -156,6 +161,7 @@ <h1>ESP Web Tools</h1>
<li>Connect device to the Wi-Fi network</li>
<li>Visit the device's hosted web interface</li>
<li>Access logs and send terminal commands</li>
<li>Read device hardware information (chip, flash, PSRAM)</li>
<li>
Add devices to
<a href="https://www.home-assistant.io">Home Assistant</a>
Expand Down Expand Up @@ -186,6 +192,16 @@ <h2 id="demo">Try a live demo</h2>
>.
</i>
</esp-web-install-button>
<p>
You can also read hardware information from a connected ESP device
without installing any firmware:
</p>
<esp-web-diagnostics-button>
<i slot="unsupported">
Diagnostics are not available because your browser does not support
Web Serial. Open this page in Google Chrome or Microsoft Edge instead.
</i>
</esp-web-diagnostics-button>

<h2 id="used-by">Products using ESP Web Tools</h2>
<div class="projects">
Expand Down Expand Up @@ -332,8 +348,9 @@ <h2>How it works</h2>
>Web Serial</a
>, <a href="https://www.improv-wifi.com/">Improv Wi-Fi</a> (optional),
and a manifest which describes the firmware. ESP Web Tools detects the
chipset of the connected ESP device and automatically selects the right
firmware variant from the manifest.
chipset, flash size, and PSRAM of the connected ESP device and
automatically selects the best matching firmware variant from the
manifest.
</p>
<p>
Web Serial is available in Google Chrome and Microsoft Edge
Expand Down Expand Up @@ -517,6 +534,53 @@ <h3 id="manifest">Creating your manifest</h3>
where it should be installed. Part paths are resolved relative to the
path of the manifest, but can also be URLs to other hosts.
</p>

<h4>Hardware qualifiers</h4>
<p>
Builds can optionally include <code>flashSizeMB</code> and
<code>psramSizeMB</code> to target specific hardware variants within the
same chip family. ESP Web Tools detects the connected device's flash size
and PSRAM size and selects the most specific matching build.
</p>
<pre>
{
"name": "My Firmware",
"version": "1.0.0",
"builds": [
{
"chipFamily": "ESP32-S3",
"flashSizeMB": 16,
"psramSizeMB": 8,
"parts": [{ "path": "s3-16mb-8psram.bin", "offset": 0 }]
},
{
"chipFamily": "ESP32-S3",
"flashSizeMB": 4,
"parts": [{ "path": "s3-4mb.bin", "offset": 0 }]
},
{
"chipFamily": "ESP32-S3",
"parts": [{ "path": "s3-generic.bin", "offset": 0 }]
}
]
}</pre
>
<p>
The matching algorithm uses a most-specific-match-wins approach:
</p>
<ul>
<li>
A build whose qualifier does <em>not</em> match the device is excluded
(e.g. a build requiring 16 MB flash won't match a 4 MB device).
</li>
<li>
Among remaining builds, the one with the most matching qualifiers wins.
</li>
<li>Ties are broken by manifest order (first wins).</li>
<li>
A build with no qualifiers acts as a fallback for that chip family.
</li>
</ul>
<p>
If your firmware is supported by Home Assistant, you can add the
optional key <code>home_assistant_domain</code>. If present, ESP Web
Expand Down Expand Up @@ -654,6 +718,35 @@ <h4>Replace the button and message with a custom one</h4>
</pre
>

<h3 id="diagnostics">Diagnostics button</h3>
<p>
ESP Web Tools also includes a diagnostics button that reads hardware
information from a connected ESP device without installing any firmware.
It displays the chip description, features, crystal frequency, MAC
address, flash size, and PSRAM size.
</p>
<p>
Load the diagnostics button JavaScript on your website:
</p>
<pre>
&lt;script
type="module"
src="https://unpkg.com/esp-web-tools@10/dist/web/diagnostics-button.js?module"
>&lt;/script></pre
>
<p>Then add the button element:</p>
<pre>
&lt;esp-web-diagnostics-button>&lt;/esp-web-diagnostics-button></pre
>
<p>
Like the install button, you can customize it using the
<code>activate</code>, <code>unsupported</code>, and
<code>not-allowed</code> slots, and the same CSS custom properties
(<code>--esp-tools-button-color</code>,
<code>--esp-tools-button-text-color</code>,
<code>--esp-tools-button-border-radius</code>).
</p>

<h2>Why we created ESP Web Tools</h2>
<div class="videoWrapper">
<lite-youtube
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import babel from "@rollup/plugin-babel";
import commonjs from "@rollup/plugin-commonjs";

const config = {
input: "dist/install-button.js",
input: ["dist/install-button.js", "dist/diagnostics-button.js"],
output: {
dir: "dist/web",
format: "module",
Expand Down
2 changes: 2 additions & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface Build {
| "ESP32-S2"
| "ESP32-S3"
| "ESP8266";
flashSizeMB?: number;
psramSizeMB?: number;
parts: {
path: string;
offset: number;
Expand Down
138 changes: 138 additions & 0 deletions src/diagnostics-button.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
export class DiagnosticsButton extends HTMLElement {
public static isSupported = "serial" in navigator;

public static isAllowed = window.isSecureContext;

private static style = `
button {
position: relative;
cursor: pointer;
font-size: 14px;
font-weight: 500;
padding: 10px 24px;
color: var(--esp-tools-button-text-color, #fff);
background-color: var(--esp-tools-button-color, #03a9f4);
border: none;
border-radius: var(--esp-tools-button-border-radius, 9999px);
}
button::before {
content: " ";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0.2;
border-radius: var(--esp-tools-button-border-radius, 9999px);
}
button:hover::before {
background-color: rgba(255,255,255,.8);
}
button:focus {
outline: none;
}
button:focus::before {
background-color: white;
}
button:active::before {
background-color: grey;
}
:host([active]) button {
color: rgba(0, 0, 0, 0.38);
background-color: rgba(0, 0, 0, 0.12);
box-shadow: none;
cursor: unset;
pointer-events: none;
}
.hidden {
display: none;
}`;

public renderRoot?: ShadowRoot;

public connectedCallback() {
if (this.renderRoot) {
return;
}

this.renderRoot = this.attachShadow({ mode: "open" });

if (!DiagnosticsButton.isSupported || !DiagnosticsButton.isAllowed) {
this.toggleAttribute("install-unsupported", true);
this.renderRoot.innerHTML = !DiagnosticsButton.isAllowed
? "<slot name='not-allowed'>You can only use this on HTTPS websites or on localhost.</slot>"
: "<slot name='unsupported'>Your browser does not support Web Serial. Use Google Chrome or Microsoft Edge.</slot>";
return;
}

this.toggleAttribute("install-supported", true);

const slot = document.createElement("slot");

slot.addEventListener("click", (ev) => {
ev.preventDefault();
this._connect();
});

slot.name = "activate";
const button = document.createElement("button");
button.innerText = "Diagnostics";
slot.append(button);

if (
"adoptedStyleSheets" in Document.prototype &&
"replaceSync" in CSSStyleSheet.prototype
) {
const sheet = new CSSStyleSheet();
sheet.replaceSync(DiagnosticsButton.style);
this.renderRoot.adoptedStyleSheets = [sheet];
} else {
const styleSheet = document.createElement("style");
styleSheet.innerText = DiagnosticsButton.style;
this.renderRoot.append(styleSheet);
}
this.renderRoot.append(slot);
}

private async _connect() {
import("./diagnostics-dialog.js");
let port: SerialPort | undefined;
try {
port = await navigator.serial.requestPort();
} catch (err: any) {
if ((err as DOMException).name !== "NotFoundError") {
alert(`Error: ${err.message}`);
}
return;
}

if (!port) {
return;
}

try {
await port.open({ baudRate: 115200, bufferSize: 8192 });
} catch (err: any) {
alert(err.message);
return;
}

const el = document.createElement("ewt-diagnostics-dialog");
(el as any).port = port;
el.addEventListener(
"closed",
async () => {
// Port is closed by the dialog via transport.disconnect(); ignore if already closed.
try {
await port!.close();
} catch {
// already closed
}
},
{ once: true },
);
document.body.appendChild(el);
}
}

customElements.define("esp-web-diagnostics-button", DiagnosticsButton);
Loading