This repository contains a Javascript implementation of esptool, a serial flasher utility for Espressif chips. esptool-js is based on Web Serial API and works in Google Chrome and Microsoft Edge version 89 or later browsers and Google Chrome on Android version 61 or later via the web-serial-polyfill compatibility layer.
NOTE: Unlike the Python-based esptool, esptool-js doesn't implement generation of binary images out of ELF files, and doesn't include companion tools similar to espefuse.py and espsecure.py.
📚 API Documentation - Complete API reference with detailed class and method documentation.
CDN
https://unpkg.com/esptool-js/lib/index.js or https://unpkg.com/esptool-js/bundle.js to use the single bundle JavaScript file.
NPM
npm install --save esptool-js
Yarn
yarn add --save esptool-js
Nightly builds for ESPTOOL-JS
First, import the necessary classes and types:
import {
ESPLoader,
Transport,
LoaderOptions,
FlashOptions,
FlashModeValues,
FlashFreqValues,
FlashSizeValues,
IEspLoaderTerminal,
} from "esptool-js";
Request access to a serial port using the Web Serial API:
// Request port access (user will be prompted to select a device)
const port = await navigator.serial.requestPort();
// Optionally, filter by USB vendor/product IDs
const portFilters = [
{ usbVendorId: 0x10c4, usbProductId: 0xea60 } // Example: Silicon Labs CP210x
];
const port = await navigator.serial.requestPort({ filters: portFilters });
Create a Transport instance and configure the ESPLoader:
// Create transport instance
const transport = new Transport(port, true);
// Optional: Create a terminal interface for logging
const terminal: IEspLoaderTerminal = {
clean() {
console.clear();
},
writeLine(data: string) {
console.log(data);
},
write(data: string) {
console.log(data);
},
};
// Configure loader options
const loaderOptions: LoaderOptions = {
transport: transport,
baudrate: 115200, // Communication baud rate
terminal: terminal, // Optional terminal for logging
debugLogging: false, // Optional debug logging
};
// Create ESPLoader instance
const esploader = new ESPLoader(loaderOptions);
Connect to the ESP device and detect the chip:
try {
// Connect and detect chip (this will reset the device)
const chipName = await esploader.main();
console.log(`Connected to: ${chipName}`);
} catch (error) {
console.error("Failed to connect:", error);
}
Flash firmware images to the device:
// Read your firmware file (e.g., from a File input or fetch)
const firmwareData = new Uint8Array(/* your firmware binary data */);
const firmwareAddress = 0x1000; // Starting address in flash
// Configure flash options
const flashOptions: FlashOptions = {
fileArray: [
{ data: firmwareData, address: firmwareAddress }
],
flashMode: "dio" as FlashModeValues, // Flash mode: "qio", "qout", "dio", "dout"
flashFreq: "40m" as FlashFreqValues, // Flash frequency: "80m", "40m", "26m", "20m", etc.
flashSize: "4MB" as FlashSizeValues, // Flash size: "256KB", "512KB", "1MB", "2MB", "4MB", etc.
eraseAll: false, // Set to true to erase entire flash before writing
compress: true, // Compress data during transfer
reportProgress: (fileIndex, written, total) => {
const percent = (written / total) * 100;
console.log(`Progress: ${percent.toFixed(1)}%`);
},
// Optional: MD5 hash calculation for verification
calculateMD5Hash: (image: Uint8Array) => {
// Implement your MD5 calculation here
return "your-md5-hash";
},
};
// Flash the firmware
await esploader.writeFlash(flashOptions);
// Reset the device after flashing (optional)
await esploader.after("hard_reset");
Read data from flash memory:
const startAddress = 0x1000;
const size = 4096; // Read 4KB
const data = await esploader.readFlash(
startAddress,
size,
(packet, progress, totalSize) => {
// Optional progress callback
console.log(`Read: ${progress}/${totalSize} bytes`);
}
);
console.log("Read data:", data);
Erase flash memory:
// Erase entire flash
await esploader.eraseFlash();
// Or erase a specific region (if supported)
// await esploader.eraseRegion(startAddress, size);
Note: This is an advanced feature. For most use cases, the default reset strategies provided by esptool-js will work correctly. You only need to implement custom reset strategies if:
By default, ESPLoader uses built-in reset strategies that work with most ESP32/ESP8266 boards. However, if you need to implement your own reset sequence, you can provide custom reset constructors.
Reset strategies use a sequence of commands to control the DTR (Data Terminal Ready) and RTS (Request To Send) signals on the serial port:
setDTR - Set DTR signal (0=False, 1=True)setRTS - Set RTS signal (0=False, 1=True) Commands are separated by | (pipe). For example, "D0|R1|W100|D1|R0|W50|D0" represents:
import {
ClassicReset,
HardReset,
UsbJtagSerialReset,
CustomReset,
ResetConstructors,
validateCustomResetStringSequence,
} from "esptool-js";
// Option 1: Use built-in reset strategies with custom parameters
const resetConstructors: ResetConstructors = {
classicReset: (transport, resetDelay) => new ClassicReset(transport, resetDelay),
hardReset: (transport, usingUsbOtg) => new HardReset(transport, usingUsbOtg),
usbJTAGSerialReset: (transport) => new UsbJtagSerialReset(transport),
};
// Option 2: Implement a completely custom reset sequence
const customResetSequence = "D0|R1|W100|D1|R0|W50|D0";
// Validate the sequence before using it
if (validateCustomResetStringSequence(customResetSequence)) {
resetConstructors.customReset = (transport, sequenceString) =>
new CustomReset(transport, customResetSequence);
} else {
console.error("Invalid reset sequence");
}
// Apply custom reset strategies to loader options
const loaderOptions: LoaderOptions = {
transport: transport,
baudrate: 115200,
resetConstructors: resetConstructors,
};
const esploader = new ESPLoader(loaderOptions);
// Use specific reset mode when connecting
await esploader.main("default_reset"); // Uses classicReset
// await esploader.main("hard_reset"); // Uses hardReset
// await esploader.main("no_reset"); // Skips reset
If your board requires a specific reset sequence:
// Custom sequence for a board that needs longer reset pulse
const customSequence = "D0|R0|W50|D1|R1|W200|D0|R0|W100";
if (validateCustomResetStringSequence(customSequence)) {
const resetConstructors: ResetConstructors = {
customReset: (transport, _) => new CustomReset(transport, customSequence),
};
const esploader = new ESPLoader({
transport,
baudrate: 115200,
resetConstructors,
});
await esploader.main("default_reset");
}
Here's a complete example that demonstrates the full workflow:
import {
ESPLoader,
Transport,
LoaderOptions,
FlashOptions,
FlashModeValues,
FlashFreqValues,
FlashSizeValues,
} from "esptool-js";
async function flashFirmware() {
try {
// 1. Request serial port
const port = await navigator.serial.requestPort();
// 2. Create transport and loader
const transport = new Transport(port, true);
const esploader = new ESPLoader({
transport,
baudrate: 115200,
terminal: {
clean: () => console.clear(),
writeLine: (data) => console.log(data),
write: (data) => console.log(data),
},
});
// 3. Connect to device
const chipName = await esploader.main();
console.log(`Connected to: ${chipName}`);
// 4. Load firmware (example: from a file input)
const fileInput = document.getElementById("firmwareFile") as HTMLInputElement;
const file = fileInput.files[0];
const firmwareData = new Uint8Array(await file.arrayBuffer());
// 5. Flash firmware
const flashOptions: FlashOptions = {
fileArray: [{ data: firmwareData, address: 0x1000 }],
flashMode: "dio" as FlashModeValues,
flashFreq: "40m" as FlashFreqValues,
flashSize: "4MB" as FlashSizeValues,
eraseAll: false,
compress: true,
reportProgress: (fileIndex, written, total) => {
console.log(`Progress: ${((written / total) * 100).toFixed(1)}%`);
},
};
await esploader.writeFlash(flashOptions);
console.log("Firmware flashed successfully!");
// 6. Reset device
await esploader.after("hard_reset");
// 7. Disconnect
await transport.disconnect();
} catch (error) {
console.error("Error:", error);
}
}
You can use any JavaScript-compatible terminal by implementing the IEspLoaderTerminal interface:
import { IEspLoaderTerminal } from "esptool-js";
const terminal: IEspLoaderTerminal = {
clean() {
// Clear terminal output
},
writeLine(data: string) {
// Write a line with newline
},
write(data: string) {
// Write data without newline
},
};
Check the example project for a complete working implementation.
Visit https://espressif.github.io/esptool-js/ to see this tool in action.
npm install
npm run build
cd examples/typescript
npm install
npm run dev # Run local sever with example code
Then open http://localhost:1234 in a Chrome browser. The npm run build step builds the lib used in the example examples/typescript/index.html. Update this reference as described in the Installation section.
If you are testing the main branch or any Pull Request (PR) artifact you can follow these steps:
esptool-js-<version>.tgz where <version> is the current version and download it."dependencies": {
"esptool-js": "file:../path/to/esptool-js-<version>.tgz"
}
import "esptool-js/lib/index.js" when added in package.json as shown before.The code in this repository is Copyright (c) 2023 Espressif Systems (Shanghai) Co. Ltd. It is licensed under Apache 2.0 license, as described in LICENSE file.
Generated using TypeDoc