Skip to content

Commit 729b0ff

Browse files
committed
Add help and version command line flags by replacing minimist for sade
1 parent b36b761 commit 729b0ff

File tree

5 files changed

+239
-166
lines changed

5 files changed

+239
-166
lines changed

demo/emulator-run.ts

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,70 @@
11
import * as fs from 'fs';
2+
import packageJson from '../package.json';
3+
import sade from 'sade';
24
import { GDBTCPServer } from '../src/gdb/gdb-tcp-server.js';
5+
import { RP2040 } from '../src/index.js';
36
import { Simulator } from '../src/simulator.js';
47
import { bootromB1 } from './bootrom.js';
58
import { loadHex } from './intelhex.js';
69
import { loadUF2 } from './load-flash.js';
7-
import minimist from 'minimist';
8-
9-
const args = minimist(process.argv.slice(2), {
10-
string: [
11-
'image', // An image to load, hex and UF2 are supported
12-
],
13-
});
14-
15-
const simulator = new Simulator();
16-
const mcu = simulator.rp2040;
17-
mcu.loadBootrom(bootromB1);
18-
19-
const imageName = args.image ?? 'hello_uart.hex';
20-
21-
// Check the extension of the file
22-
const extension = imageName.split('.').pop();
23-
if (extension === 'hex') {
24-
// Create an array with the compiled code of blink
25-
// Execute the instructions from this array, one by one.
26-
const hex = fs.readFileSync(imageName, 'utf-8');
27-
28-
console.log(`Loading hex image ${imageName}`);
29-
loadHex(hex, mcu.flash, 0x10000000);
30-
} else if (extension === 'uf2') {
31-
console.log(`Loading uf2 image ${imageName}`);
32-
loadUF2(imageName, mcu);
33-
} else {
34-
console.log(`Unsupported file type: ${extension}`);
35-
process.exit(1);
10+
11+
type CliOptions = {
12+
image: string;
13+
gdb: boolean;
14+
'gdb-port': number;
15+
};
16+
17+
function loadImage(imageName: string, mcu: RP2040) {
18+
const extension = imageName.split('.').pop();
19+
20+
if (extension === 'hex') {
21+
// Create an array with the compiled code of blink
22+
// Execute the instructions from this array, one by one.
23+
const hex = fs.readFileSync(imageName, 'utf-8');
24+
console.log(`Loading hex image ${imageName}`);
25+
loadHex(hex, mcu.flash, 0x10000000);
26+
} else if (extension === 'uf2') {
27+
console.log(`Loading uf2 image ${imageName}`);
28+
loadUF2(imageName, mcu);
29+
} else {
30+
console.log(`Unsupported file type: ${extension}`);
31+
process.exit(1);
32+
}
3633
}
3734

38-
const gdbServer = new GDBTCPServer(simulator, 3333);
39-
console.log(`RP2040 GDB Server ready! Listening on port ${gdbServer.port}`);
35+
function simulateImage(opts: CliOptions) {
36+
const simulator = new Simulator();
37+
const mcu = simulator.rp2040;
38+
mcu.loadBootrom(bootromB1);
4039

41-
mcu.uart[0].onByte = (value) => {
42-
process.stdout.write(new Uint8Array([value]));
43-
};
40+
try {
41+
loadImage(opts.image, mcu);
42+
} catch (err) {
43+
const message = err instanceof Error ? err.message : String(err);
44+
45+
console.log(`Error: Failed to load image file: "${message}"`);
46+
process.exit(1);
47+
}
48+
49+
if (opts.gdb) {
50+
const gdbServer = new GDBTCPServer(simulator, opts['gdb-port']);
51+
console.log(`RP2040 GDB Server ready! Listening on port ${gdbServer.port}`);
52+
}
53+
54+
mcu.uart[0].onByte = (value) => {
55+
process.stdout.write(new Uint8Array([value]));
56+
};
57+
58+
simulator.rp2040.core.PC = 0x10000000;
59+
simulator.execute();
60+
}
4461

45-
simulator.rp2040.core.PC = 0x10000000;
46-
simulator.execute();
62+
sade('rp2040js', true)
63+
.version(packageJson.version)
64+
.describe(packageJson.description)
65+
.option('-i, --image', 'Provide an image to run (.uf2, .hex)', 'hello_uart.hex')
66+
.option('-g, --gdb', 'If a GDB server should be started or not', true)
67+
.option('-p, --gdb-port', 'The port to start the gdb server on', 3333)
68+
.example('--image ./hello_world.uf2')
69+
.action(simulateImage)
70+
.parse(process.argv);

demo/micropython-run.ts

Lines changed: 92 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,64 @@
11
import fs from 'fs';
2-
import minimist from 'minimist';
2+
import packageJson from '../package.json';
3+
import sade from 'sade';
34
import { GDBTCPServer } from '../src/gdb/gdb-tcp-server.js';
5+
import { RP2040 } from '../src/index.js';
46
import { Simulator } from '../src/simulator.js';
57
import { USBCDC } from '../src/usb/cdc.js';
68
import { ConsoleLogger, LogLevel } from '../src/utils/logging.js';
79
import { bootromB1 } from './bootrom.js';
810
import { loadCircuitpythonFlashImage, loadMicropythonFlashImage, loadUF2 } from './load-flash.js';
911

10-
const args = minimist(process.argv.slice(2), {
11-
string: [
12-
'image', // UF2 image to load; defaults to "RPI_PICO-20230426-v1.20.0.uf2"
13-
'expect-text', // Text to expect on the serial console, process will exit with code 0 if found
14-
],
15-
boolean: [
16-
'gdb', // start GDB server on 3333
17-
'circuitpython', // use CircuitPython instead of MicroPython
18-
],
19-
});
20-
const expectText = args['expect-text'];
21-
22-
const simulator = new Simulator();
23-
const mcu = simulator.rp2040;
24-
mcu.loadBootrom(bootromB1);
25-
mcu.logger = new ConsoleLogger(LogLevel.Error);
26-
27-
let imageName: string;
28-
if (!args.circuitpython) {
29-
imageName = args.image ?? 'RPI_PICO-20230426-v1.20.0.uf2';
30-
} else {
31-
imageName = args.image ?? 'adafruit-circuitpython-raspberry_pi_pico-en_US-8.0.2.uf2';
32-
}
33-
console.log(`Loading uf2 image ${imageName}`);
34-
loadUF2(imageName, mcu);
35-
36-
if (fs.existsSync('littlefs.img') && !args.circuitpython) {
37-
console.log(`Loading uf2 image littlefs.img`);
38-
loadMicropythonFlashImage('littlefs.img', mcu);
39-
} else if (fs.existsSync('fat12.img') && args.circuitpython) {
40-
loadCircuitpythonFlashImage('fat12.img', mcu);
41-
// Instead of reading from file, it would also be possible to generate the LittleFS image on-the-fly here, e.g. using
42-
// https://github.com/wokwi/littlefs-wasm or https://github.com/littlefs-project/littlefs-js
43-
}
12+
type CliOptions = {
13+
image: string | null;
14+
'expect-text': string | null;
15+
gdb: boolean;
16+
'gdb-port': number;
17+
'circuit-python': boolean;
18+
};
19+
20+
function loadImage(mcu: RP2040, image: string | null, useCircuitPython: boolean) {
21+
let selectedImage: string;
22+
if (image) selectedImage = image;
23+
else if (useCircuitPython)
24+
selectedImage = 'adafruit-circuitpython-raspberry_pi_pico-en_US-8.0.2.uf2';
25+
else selectedImage = 'RPI_PICO-20230426-v1.20.0.uf2';
4426

45-
if (args.gdb) {
46-
const gdbServer = new GDBTCPServer(simulator, 3333);
47-
console.log(`RP2040 GDB Server ready! Listening on port ${gdbServer.port}`);
27+
console.log(`Loading uf2 image ${selectedImage}`);
28+
29+
try {
30+
loadUF2(selectedImage, mcu);
31+
} catch (err) {
32+
const message = err instanceof Error ? err.message : String(err);
33+
34+
console.log(`Error: Failed to load image file: "${message}"`);
35+
process.exit(1);
36+
}
37+
38+
if (fs.existsSync('littlefs.img') && !useCircuitPython) {
39+
console.log(`Loading uf2 image littlefs.img`);
40+
loadMicropythonFlashImage('littlefs.img', mcu);
41+
} else if (fs.existsSync('fat12.img') && useCircuitPython) {
42+
loadCircuitpythonFlashImage('fat12.img', mcu);
43+
// Instead of reading from file, it would also be possible to generate the LittleFS image on-the-fly here, e.g. using
44+
// https://github.com/wokwi/littlefs-wasm or https://github.com/littlefs-project/littlefs-js
45+
}
4846
}
4947

50-
const cdc = new USBCDC(mcu.usbCtrl);
51-
cdc.onDeviceConnected = () => {
52-
if (!args.circuitpython) {
53-
// We send a newline so the user sees the MicroPython prompt
54-
cdc.sendSerialByte('\r'.charCodeAt(0));
55-
cdc.sendSerialByte('\n'.charCodeAt(0));
56-
} else {
48+
function handleDeviceConnected(cdc: USBCDC, useCircuitPython: boolean) {
49+
if (useCircuitPython) {
5750
cdc.sendSerialByte(3);
51+
return;
5852
}
59-
};
6053

61-
let currentLine = '';
62-
cdc.onSerialData = (value) => {
54+
// We send a newline so the user sees the MicroPython prompt
55+
cdc.sendSerialByte('\r'.charCodeAt(0));
56+
cdc.sendSerialByte('\n'.charCodeAt(0));
57+
}
58+
59+
function handleSerialData(value: Uint8Array, expectText: string | null) {
6360
process.stdout.write(value);
61+
let currentLine = '';
6462

6563
for (const byte of value) {
6664
const char = String.fromCharCode(byte);
@@ -75,20 +73,52 @@ cdc.onSerialData = (value) => {
7573
currentLine += char;
7674
}
7775
}
78-
};
79-
80-
if (process.stdin.isTTY) {
81-
process.stdin.setRawMode(true);
8276
}
83-
process.stdin.on('data', (chunk) => {
84-
// 24 is Ctrl+X
85-
if (chunk[0] === 24) {
86-
process.exit(0);
87-
}
88-
for (const byte of chunk) {
89-
cdc.sendSerialByte(byte);
77+
78+
function simulateMicropythonImage(opts: CliOptions) {
79+
const simulator = new Simulator();
80+
const mcu = simulator.rp2040;
81+
mcu.loadBootrom(bootromB1);
82+
mcu.logger = new ConsoleLogger(LogLevel.Error);
83+
84+
loadImage(mcu, opts.image, opts['circuit-python']);
85+
86+
if (opts.gdb) {
87+
const gdbServer = new GDBTCPServer(simulator, opts['gdb-port']);
88+
console.log(`RP2040 GDB Server ready! Listening on port ${gdbServer.port}`);
9089
}
91-
});
9290

93-
simulator.rp2040.core.PC = 0x10000000;
94-
simulator.execute();
91+
const cdc = new USBCDC(mcu.usbCtrl);
92+
cdc.onDeviceConnected = () => handleDeviceConnected(cdc, opts['circuit-python']);
93+
cdc.onSerialData = (value) => handleSerialData(value, opts['expect-text']);
94+
95+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
96+
97+
process.stdin.on('data', (chunk) => {
98+
// 24 is Ctrl+X
99+
if (chunk[0] === 24) {
100+
process.exit(0);
101+
}
102+
for (const byte of chunk) {
103+
cdc.sendSerialByte(byte);
104+
}
105+
});
106+
107+
simulator.rp2040.core.PC = 0x10000000;
108+
simulator.execute();
109+
}
110+
111+
sade('rp2040js-micropython', true)
112+
.version(packageJson.version)
113+
.describe(packageJson.description)
114+
.option('-i, --image', 'UF2 image to load')
115+
.option(
116+
'-e, --expect-text',
117+
'Text to expect on the serial console, process will exit with code 0 if found',
118+
)
119+
.option('-g, --gdb', 'If a GDB server should be started on 3333 or not', false)
120+
.option('-p, --gdb-port', 'The port to start the gdb server on', 3333)
121+
.option('-c, --circuit-python', 'If CircuitPython should be used instead of MicroPython', false)
122+
.example('--image ./my-image.uf2')
123+
.action(simulateMicropythonImage)
124+
.parse(process.argv);

0 commit comments

Comments
 (0)