Skip to content

Commit abd585b

Browse files
committed
stable esp uploading
1 parent bad613f commit abd585b

File tree

14 files changed

+1313
-950
lines changed

14 files changed

+1313
-950
lines changed

src/esp/ESPLoader.ts

Lines changed: 1038 additions & 881 deletions
Large diffs are not rendered by default.

src/esp/StubLoader.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
import axios from 'axios';
22

3+
/*
4+
Stub loaders are uploaded and run in-memory on the target device.
5+
They are usually more efficient than the default ROM loader and
6+
more up-to-date with the latest features and bug fixes.
7+
8+
https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/flasher-stub.html
9+
10+
The data for stub loaders can be quite large, and there are different ones
11+
for different esp chips, so we download it from the internet during run-time
12+
rather than including it in the package bundle.
13+
*/
14+
315
interface StubDef {
416
data: Buffer;
517
text: Buffer;
@@ -18,24 +30,23 @@ export default class StubLoader {
1830
stubsUrl: string
1931

2032
constructor(stubsUrl?: string) {
21-
// TODO; Change branch from esp-support to main
22-
this.stubsUrl = stubsUrl || 'https://raw.githubusercontent.com/duinoapp/upload-multitool/esp-support/src/esp/stubs/';
33+
this.stubsUrl = stubsUrl || 'https://raw.githubusercontent.com/espressif/esptool/master/esptool/targets/stub_flasher/';
2334
this.stubsUrl = this.stubsUrl.replace(/\/$/, '');
2435
}
2536

2637
async loadStub(chipName: string) {
27-
const stubName = chipName.replace(/-/g, '').toLowerCase();
38+
const stubName = chipName.replace(/-/g, '').toLowerCase().replace('esp', '');
2839
if (cache[stubName]) {
2940
return cache[stubName];
3041
}
31-
const { data: res } = await axios.get(`${this.stubsUrl}/${stubName}.json`);
42+
const { data: res } = await axios.get(`${this.stubsUrl}/stub_flasher_${stubName}.json`);
3243

3344
const stub = {
3445
data: Buffer.from(res.data, 'base64'),
3546
text: Buffer.from(res.text, 'base64'),
3647
entry: res.entry,
37-
textStart: res.textStart,
38-
dataStart: res.dataStart,
48+
textStart: res.text_start,
49+
dataStart: res.data_start,
3950
} as StubDef;
4051

4152
cache[stubName] = stub;

src/esp/index.ts

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,26 @@ export const upload = async (serial: SerialPort, config: ProgramConfig) => {
1111
const log = (...args: any[]) => console.log(...args);
1212
// const term = { log, debug: log, write: config.debug };
1313

14-
// const { port } = serial;
15-
// const transport = new Transport(port, term);
16-
let espLoader;
14+
// serial.on('data', (data: Buffer) => {
15+
// console.log('read (utf8)', data.toString('utf-8'));
16+
// console.log('read (hex)', data.toString('hex'));
17+
// });
1718

19+
let espLoader;
1820
try {
19-
log('> Connecting...');
2021
espLoader = new ESPLoader(serial, {
2122
quiet: !config.verbose,
2223
} as ESPOptions);
2324
await espLoader.mainFn();
2425
// await espLoader.flash_id();
2526
log('> Connected');
27+
28+
if (config.uploadSpeed) {
29+
await espLoader.changeBaudrate(config.uploadSpeed);
30+
}
2631
} catch (err) {
2732
// eslint-disable-next-line no-console
2833
console.error(err);
29-
// log('Failed to connect:', typeof err === 'string' ? err : err.message);
3034
try {
3135
await serial.close();
3236
} catch (err2) {
@@ -37,21 +41,19 @@ export const upload = async (serial: SerialPort, config: ProgramConfig) => {
3741
}
3842

3943
try {
40-
// if (board.config?.wipe && board.config.wipe !== 'none') {
41-
// log('> Erasing device flash...');
42-
// await espLoader.erase_flash();
43-
// log('> Successfully erased device flash');
44-
// }
4544
log('> Writing main data partition, this may take a while...');
4645
await espLoader.writeFlash({
4746
fileArray: config.files.map((file) => ({ ...file, data: Buffer.from(file.data, 'base64') })),
48-
flashSize: 'keep',
49-
// flash_freq,
50-
// flash_mode,
47+
flashSize: '4MB',
48+
flashFreq: config.flashFreq || 'keep',
49+
flashMode: config.flashMode || 'keep',
5150
// compress: board.props?.build?.mcu !== 'esp8266',
5251
});
53-
await espLoader.flashDeflFinish({ reboot: true });
52+
await espLoader.reboot();
5453
await asyncTimeout(100);
54+
if (config.uploadSpeed) {
55+
await serial.update({ baudRate: config.speed || 115200 });
56+
}
5557
log('> Successfully written data partition');
5658
log('> Flashing succeeded! Have a nice day! :)');
5759
} catch (err) {
@@ -60,12 +62,6 @@ export const upload = async (serial: SerialPort, config: ProgramConfig) => {
6062
log('Failed to upload:', err instanceof Error ? err.message : err);
6163
}
6264

63-
// try {
64-
// await serial.close();
65-
// } catch (err) {
66-
// // eslint-disable-next-line no-console
67-
// console.error(err);
68-
// }
6965
};
7066

7167
export default { upload, isSupported };

src/esp/roms/esp32c3.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const EFUSE_BASE = 0x60008800;
77
export default {
88
CHIP_NAME: 'ESP32-C3',
99
IS_STUB: true,
10+
SUPPORTS_ENCRYPTION: true,
1011
IMAGE_CHIP_ID: 5,
1112
CHIP_DETECT_MAGIC_VALUE: 0x6921506f,
1213
EFUSE_BASE,

src/esp/roms/esp32s2.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { toMac } from './util';
55
export default {
66
CHIP_NAME: 'ESP32-S2',
77
IS_STUB: true,
8+
SUPPORTS_ENCRYPTION: true,
89
IMAGE_CHIP_ID: 2,
910
CHIP_DETECT_MAGIC_VALUE: 0x000007c6,
1011
MAC_EFUSE_REG: 0x3f41A044,

src/esp/roms/rom.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ interface flashSizes {
77
export default interface ROM {
88
CHIP_NAME: string;
99
IS_STUB: boolean;
10+
SUPPORTS_ENCRYPTION?: boolean;
1011
FLASH_SIZES: flashSizes;
1112
IMAGE_CHIP_ID?: number;
1213
CHIP_DETECT_MAGIC_VALUE: number;

src/global.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
declare module 'intel-hex';
2+
declare module 'pako';
3+
declare module 'crypto-js';

src/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ export interface ProgramConfig {
77
hex?: Buffer;
88
files?: ProgramFile[];
99
speed?: number;
10+
uploadSpeed?: number;
1011
tool?: string;
1112
cpu?: string;
1213
verbose?: boolean;
14+
flashMode?: string;
15+
flashFreq?: string;
1316
}

src/util/serial-helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const waitForOpen = (serial: SerialPort, timeout: number = 1000): Promise
88
let cleanup = () => {};
99
const timer = setTimeout(() => {
1010
cleanup();
11-
reject(new Error('Timeout'));
11+
reject(new Error('Timeout opening port'));
1212
}, timeout);
1313
const handleOpen = () => {
1414
cleanup();

test/boards.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,85 @@ import { expect } from 'chai';
33
import 'mocha';
44
import { SerialPort } from 'serialport';
55
import { upload } from '../src/index';
6-
import { waitForData, config, getHex } from './util';
6+
import { waitForData, config, getHex, espIdentify, ESPIdentifyResult } from './util';
77
import { waitForOpen } from '../src/util/serial-helpers';
88
import { ProgramFile } from '../src/index.d';
99

10+
const numEsps = Object.values(config.devices).filter((d) => d.espChip).length;
11+
const listPromise = espIdentify(numEsps);
12+
1013
Object.keys(config.devices).forEach((deviceRef) => {
1114
const device = config.devices[deviceRef];
1215
let key = '';
1316
let hex: Buffer | undefined;
1417
let files: ProgramFile[] | undefined;
1518
let serial: SerialPort;
19+
let flashMode: string | undefined;
20+
let flashFreq: string | undefined;
21+
let portList: ESPIdentifyResult[] = [];
1622

1723
describe(`upload to ${device.name}`, function () {
1824
this.timeout(120 * 1000);
1925

2026
before(async () => {
21-
const res = await getHex(device.code, device.fqbn);
27+
const res = await getHex(device.code, device.fqbn.trim());
2228
key = res.key;
2329
hex = res.hex;
2430
files = res.files;
31+
flashMode = res.flashMode;
32+
flashFreq = res.flashFreq;
2533
console.log('compiled hex');
2634
});
2735

2836
beforeEach(async () => {
2937
if (serial?.isOpen) await (new Promise(resolve => serial.close(resolve)));
3038

31-
// dynamically find the device path by using VID & PID
32-
const list = await SerialPort.list();
33-
const port = list.find(p => device.vendorIds.includes(p.vendorId) && device.productIds.includes(p.productId));
39+
// dynamically find the device path by using VID & PID or esp props
40+
portList = await listPromise;
41+
const port = portList.find(p => {
42+
if (device.vendorIds && device.productIds) {
43+
return device.vendorIds.includes(p.vendorId || '') && device.productIds.includes(p.productId || '');
44+
}
45+
if (device.espChip) {
46+
return p.esp?.chip === device.espChip;
47+
}
48+
if (device.mac) {
49+
return p.esp?.mac === device.mac;
50+
}
51+
return false;
52+
});
3453
if (!port) throw new Error(`could not locate ${device.name}`);
3554

3655
// connect to the device
3756
serial = new SerialPort({ path: port.path, baudRate: device.speed });
3857
await waitForOpen(serial);
39-
console.log(`connected to ${device.name}`);
58+
console.log(`connected to ${device.name} on ${port.path}`);
4059
});
4160

4261
this.afterEach(async () => {
4362
// make sure connection is closed when we're done
4463
if (serial?.isOpen) await (new Promise(resolve => serial.close(resolve)));
4564
});
4665

47-
it(`should upload to ${device.name}`, async () => {
66+
it(`should upload to ${device.name}`, async function() {
67+
this.retries(config.retries || 1);
4868
await upload(serial, {
4969
hex,
5070
files,
71+
flashMode,
72+
flashFreq,
5173
speed: device.speed,
74+
uploadSpeed: device.uploadSpeed,
5275
tool: device.tool,
5376
cpu: device.cpu,
5477
verbose: config.verbose,
5578
});
79+
5680
console.log(`uploaded to ${device.name}, validating...`);
57-
expect(await waitForData(serial, key, 3000)).to.be.true;
81+
if (device.code === 'ping') {
82+
await serial.write('ping\n');
83+
}
84+
expect(await waitForData(serial, key, 5000)).to.be.true;
5885
});
5986
});
6087
});

0 commit comments

Comments
 (0)