espressif / esptool-js

Javascript implementation of flasher tool for Espressif chips, running in web browser using WebSerial.
https://espressif.github.io/esptool-js/
Apache License 2.0
251 stars 101 forks source link

Compatibiity With Electron #113

Open conalllaverty opened 8 months ago

conalllaverty commented 8 months ago

Hi, Does anyone have an example project of being able to flash an ESP32 using esptool-js from an Electron app?

I'm currently getting this error.

Error: TypeError: Cannot read properties of undefined (reading 'get_info')
    at new ESPLoader (esploader.js:124:1)
    at HTMLButtonElement.<anonymous> (renderer.js:73:27)
// renderer.js

const { SerialPort } = require('serialport');
const requireESM = require('esm')(module);
const esptool = requireESM('esptool-js');
const fs = require('fs');
const axios = require('axios');

const dropdown = document.getElementById('serial-dropdown');
const status = document.getElementById('status');

let selectedPort;

function updateStatus(text) {
    status.innerHTML = text;
}

// Consts
const REMOTE_FIRMWARE_URL = "";
const REMOTE_BOOTLOADER_URL = "";
const REMOTE_PARTITIONS_URL = "";

async function listSerialPorts() {
    try {
        const ports = await SerialPort.list();

        // Clear the dropdown
        dropdown.innerHTML = '';

        if (ports.length > 0) {
            selectedPort = ports[0].path;
        }
        ports.forEach(port => {
            const option = document.createElement('option');
            option.value = port.path;
            option.text = port.path + (port.manufacturer ? ` (${port.manufacturer})` : '');
            dropdown.appendChild(option);
        });
    } catch (error) {
        console.error('Error listing serial ports:', error);
    }
}

listSerialPorts();

dropdown.addEventListener('change', (e) => {
    console.log(`Selected serial port: ${e.target.value}`);
    selectedPort = e.target.value;
});

document.getElementById('refreshButton').addEventListener('click', async () => {
    listSerialPorts();
});

// Flashing
async function downloadFile(url, outputPath) {
    const response = await axios({
        method: 'get',
        url: url,
        responseType: 'arraybuffer'
    });
    fs.writeFileSync(outputPath, new Uint8Array(response.data));
}

document.getElementById('flashButton').addEventListener('click', async () => {
    updateStatus('Flashing...');
    try {
        const espLoader = new esptool.ESPLoader({
            port: selectedPort,
            chip: 'esp32',
            logLevel: 'debug'
        });

        updateStatus('Erasing flash...');

        // Erase the flash first.
        await espLoader.eraseFlash();

        updateStatus('Downloading files...');

        // Download the necessary files
        await downloadFile(REMOTE_FIRMWARE_URL, './tempFirmware.bin');
        await downloadFile(REMOTE_BOOTLOADER_URL, './tempBootloader.bin');
        await downloadFile(REMOTE_PARTITIONS_URL, './tempPartitions.bin');

        updateStatus('Flashing bootloader...');

        // Flash the bootloader.
        const bootloader = await espLoader.readFirmware('./tempBootloader.bin');
        await espLoader.flashFirmware(bootloader, 0x10000);

        updateStatus('Flashing firmware...');

        // Flash the firmware.
        const firmware = await espLoader.readFirmware('./tempFirmware.bin');
        await espLoader.flashFirmware(firmware, 0x1000);

        updateStatus('Flashing partition...');

        // Flash the partitions.
        const partitions = await espLoader.readPartitions('./tempPartitions.bin');
        await espLoader.flashPartitions(partitions, 0x8000);

        await espLoader.close();

        updateStatus('Cleanup...');

        // Clean up the temporary files
        fs.unlinkSync('./tempFirmware.bin');
        fs.unlinkSync('./tempBootloader.bin');
        fs.unlinkSync('./tempPartitions.bin');

        console.log('Flashing complete');
        updateStatus('Flashing completed successfully!');
    } catch (error) {
        console.error('Error:', error);
        updateStatus(error.toString());
    }
});
ruigouveiamaciel commented 8 months ago

esptool-js will not work on electron because it requires the window.navigator.serial API which is not supported on whatever browser electron uses. Since you're already using Electron you could package your app with the python version of esptool which is more supported and works better.

sahilrastogi94 commented 8 months ago

esptool-js will not work on electron because it requires the window.navigator.serial API which is not supported on whatever browser electron uses. Since you're already using Electron you could package your app with the python version of esptool which is more supported and works better.

esptool-js uses the Web Serial API which is supported in electron. Electron uses chromium browser which supports both Web Serial and Web Bluetooth APIs. Documentation for the same can be found here - https://www.electronjs.org/docs/latest/tutorial/devices#web-serial-api

ruigouveiamaciel commented 8 months ago

I'm sorry, I completely missed that page. It seems electron has a couple extra steps to work with web serial so that might be why esptool-js doesn't work out of the box.

sahilrastogi94 commented 8 months ago

I don't have an example code but esptool-js should work with electron. Just try using the Web Serial API instead of the serialport package.