Skip to content

Commit

Permalink
Merge pull request #21 from kaluma-project/v1.2
Browse files Browse the repository at this point in the history
V1.2
  • Loading branch information
niklauslee authored Feb 24, 2022
2 parents 9179bd0 + 3ac6eaa commit 7ce3c97
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 34 deletions.
70 changes: 55 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@

Kaluma CLI is a command-line tool to program devices and boards running [Kaluma](https://kalumajs.org) runtime. It communicates with devices and boards via serial ports. Before using CLI, please ensure that your device or board is connected to a serial port.

- [Kaluma CLI](#kaluma-cli)
- [Install](#install)
- [Usage](#usage)
- [`help` command](#help-command)
- [`ports` command](#ports-command)
- [`flash` command](#flash-command)
- [`erase` command](#erase-command)
- [`shell` command](#shell-command)
- [`bundle` command](#bundle-command)
- [`put` command](#put-command)
- [`get` command](#get-command)
- [License](#license)

## Install

Install CLI via `npm` globally.
Expand All @@ -27,14 +40,6 @@ npm install @kaluma/cli --save-dev

## Usage

- [`help`](#help-command)
- [`ports`](#ports-command)
- [`flash`](#flash-command)
- [`erase`](#erase-command)
- [`bundle`](#bundle-command)
- [`put`](#put-command)
- [`get`](#get-command)

### `help` command

Print help for commands and options.
Expand All @@ -58,20 +63,23 @@ Flash code (.js file) to device.
> You can flash only a single .js file to Kaluma. If you have multiple .js files, you need to bundle them with `--bundle` option or `bundle` command.
```sh
kaluma flash <file> --port <port> [--bundle] [--no-load] [...]
kaluma flash <file> [--port <port>] [--bundle] [--no-load] [...]
```

- `<file>` : Path to the file to upload.
- `-p, --port <port>` option : Path to a serial port where device is connected. You can check the available serial ports using [`ports`](#ports-command) command. (e.g. `/dev/tty.usbmodem*` or `COM*`)
- `-p, --port <port>` option : Path to a serial port where device is connected. You can check the available serial ports using `ports` command. (e.g. `/dev/tty*` or `COM*`). Or, you can pass a port query with serial device's VID (Vendor ID) and PID (Product ID) (e.g. `@<vid>`, `@<vid>:<pid>`). (**Default:** `@2e8a` - This is VID of Respberry Pi, so automatically finds the port of Raspberry Pi Pico if you omit `--port` option)
- `--no-load` option : Skip code loading after flash. Use this option if you don't want to run the flashed code immediately.
- `-b, --bundle` option : Bundle .js code before flash. If you use this option, you can also use all options of [`bundle`](#bundle-command) command.

Examples:

```sh
# flash index.js
# flash index.js to port: /dev/tty.usbmodem1441
kaluma flash index.js --port /dev/tty.usbmodem1441

# flash index.js to Raspberry Pi Pico (vid: 2e8a)
kaluma flash index.js

# flash index.js without load
kaluma flash index.js --port /dev/tty.usbmodem1441 --no-load

Expand All @@ -84,15 +92,41 @@ kaluma flash index.js --port /dev/tty.usbmodem1441 --bundle
Erase code in device.

```sh
kaluma erase --port <port>
kaluma erase [--port <port>]
```

- `-p, --port <port>` option: See [`flash`](#flash-command) command.

Example:

```sh
# erase code in flash of port: /dev/tty.usbmodem1441
kaluma erase --port /dev/tty.usbmodem1441

# erase code in flash of Raspberry Pi Pico (vid: 2e8a)
kaluma erase
```

### `shell` command

> **THIS IS EXPERIMENTAL FEATURE** - It may not work in some shells (zsh in macOS). Works well in VSCode Terminal.
Shell connect (binds standard I/O to serial port).

```sh
kaluma shell [--port <port>]
```

- `-p, --port <port>` option: See [`flash`](#flash-command) command.

Example:

```sh
# shell connect to the port: /dev/tty.usbmodem1441
kaluma shell --port /dev/tty.usbmodem1441

# shell connect to Raspberry Pi Pico (vid: 2e8a)
kaluma shell
```

### `bundle` command
Expand All @@ -106,7 +140,7 @@ kaluma bundle <file> [--output <file>] [--minify] [--sourcemap]
```

- `<file>` : Path to the file to bundle.
- `-o, --output <file>` option : Output path of bundled code. Default is `bundle.js`.
- `-o, --output <file>` option : Output path of bundled code. (**Default:** `bundle.js`).
- `-m, --minify` option : Minify the bundled code. It can reduce the code size, but it may harden to debug.
- `-s, --sourcemap` option : Generates source-map file.

Expand All @@ -131,7 +165,7 @@ kaluma bundle index.js --sourcemap
Copy a file from host computer to device.

```sh
kaluma put <src> <dest> --port <port>
kaluma put <src> <dest> [--port <port>]
```

- `<src>` Path to a file to send in host computer.
Expand All @@ -143,14 +177,17 @@ Examples:
```sh
# copy 'host.txt' [host] to '/dir/device.txt' [device]
kaluma put host.txt /dir/device.txt --port /dev/tty.usbmodem1441

# copy 'host.txt' [host] to '/dir/device.txt' [Raspberry Pi Pico]
kaluma put host.txt /dir/device.txt
```

### `get` command

Copy a file from device to host computer.

```sh
kaluma get <src> <dest> --port <port>
kaluma get <src> <dest> [--port <port>]
```

- `<src>` Path to a file in device. Absolute file path is required.
Expand All @@ -162,6 +199,9 @@ Examples:
```sh
# copy '/dir/device.txt` [device] to 'host.txt' [host]
kaluma get /dir/device.txt host.txt --port /dev/tty.usbmodem1441

# copy '/dir/device.txt` [Raspberry Pi Pico] to 'host.txt' [host]
kaluma get /dir/device.txt host.txt
```

## License
Expand Down
140 changes: 122 additions & 18 deletions bin/kaluma.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ const serialOptions = {
baudRate: 115200,
};

const optionDescriptions = {
port: "port where device is connected. allows port path (/dev/tty*, COM*) or port query (@<vid>, @<vid>:<pid>). Raspberry Pi's VID is 2e8a",
output: "output file path",
minify: "minify bundled code",
sourcemap: "generate sourcemap",
bundle: "bundle file",
noLoad: "skip code loading",
};

function delay(time) {
return new Promise((resolve) => {
setTimeout(() => {
Expand All @@ -37,8 +46,86 @@ function colorSize(size) {
return colors.yellow(`[${filesize(parseInt(size)).human()}]`);
}

function bind(serial) {
process.stdin.setRawMode(true);
process.stdin.on("data", (chunk) => {
if (chunk[0] === 0x1a) {
// ctrl+z
process.exit(0);
} else {
serial.write(chunk);
}
});
serial.on("data", (chunk) => {
process.stdout.write(chunk);
});
}

async function findPort(portOrQuery, exit) {
let port = null;
const ports = await SerialPort.list();
ports.forEach((p) => {
if (p.vendorId) {
p.vendorId = p.vendorId.toLowerCase();
}
if (p.productId) {
p.productId = p.productId.toLowerCase();
}
});
if (portOrQuery.startsWith("@")) {
const query = portOrQuery.substr(1).toLowerCase();
let vid = null;
let pid = null;
if (query.includes(":")) {
const terms = query.split(":");
vid = terms[0];
pid = terms[1];
} else {
vid = query;
}
let result = ports.find(
(p) => p.vendorId === vid && (pid === null || p.productId === pid)
);
if (result) {
port = result.path;
}
} else {
let result = ports.find((p) => p.path === portOrQuery);
if (result) {
port = result.path;
}
}
if (exit && port === null) {
console.log(`port not found: ${portOrQuery}`);
process.exit(2);
}
return port;
}

program.version(config.version);

program
.command("shell")
.description("[EXPERIMENTAL] shell connect (exit: ctrl+z)")
.option("-p, --port <port>", optionDescriptions.port, "@2e8a")
.action(async function (options) {
// find port
const port = await findPort(options.port, true);

// shell connect
const serial = new SerialPort(port, serialOptions);
serial.open(async (err) => {
if (err) {
console.error(err);
} else {
console.log(`connected to ${port}`);
console.log(colorName(`To exit: ctrl+z`));
bind(serial);
serial.write("\r.hi\r");
}
});
});

program
.command("ports")
.description("list available serial ports")
Expand All @@ -60,16 +147,18 @@ program
program
.command("flash <file>")
.description("flash code (.js file) to device")
.requiredOption("-p, --port <port>", "port where device is connected")
.option("--no-load", "skip code loading", false)
.option("-b, --bundle", "bundle file", false)
.option("-o, --output <file>", "port where device is connected", "bundle.js")
.option("-m, --minify", "minify bundled code", false)
.option("-s, --sourcemap", "generate sourcemap", false)
.option("-p, --port <port>", optionDescriptions.port, "@2e8a")
.option("--no-load", optionDescriptions.noLoad, false)
.option("-b, --bundle", optionDescriptions.bundle, false)
.option("-o, --output <file>", optionDescriptions.output, "bundle.js")
.option("-m, --minify", optionDescriptions.minify, false)
.option("-s, --sourcemap", optionDescriptions.sourcemap, false)
.action(async function (file, options) {
let port = options.port;
let code = fs.readFileSync(file, "utf8");

// find port
const port = await findPort(options.port, true);

// bundle code if required
if (options.bundle) {
const stats = await bundle(file, Object.assign(options));
Expand All @@ -91,6 +180,7 @@ program
if (err) {
console.error(err);
} else {
console.log(`connected to ${port}`);
try {
process.stdout.write(colors.grey("flashing "));
const result = await flash(serial, code, () => {
Expand Down Expand Up @@ -118,14 +208,18 @@ program
program
.command("erase")
.description("erase code in device")
.requiredOption("-p, --port <port>", "port where device is connected")
.option("-p, --port <port>", optionDescriptions.port, "@2e8a")
.action(async function (options) {
let port = options.port;
// find port
const port = await findPort(options.port, true);

// erase
const serial = new SerialPort(port, serialOptions);
serial.open(async (err) => {
if (err) {
console.error(err);
} else {
console.log(`connected to ${port}`);
await erase(serial);
console.log("erased.");
}
Expand All @@ -135,9 +229,9 @@ program
program
.command("bundle <file>")
.description("bundle codes")
.option("-o, --output <file>", "port where device is connected", "bundle.js")
.option("-m, --minify", "minify bundled code", false)
.option("-s, --sourcemap", "generate sourcemap", false)
.option("-o, --output <file>", optionDescriptions.output, "bundle.js")
.option("-m, --minify", optionDescriptions.minify, false)
.option("-s, --sourcemap", optionDescriptions.sourcemap, false)
.action(async function (file, options) {
try {
const stats = await bundle(file, options);
Expand All @@ -157,8 +251,8 @@ program
program
.command("put <src> <dest>")
.description("copy a file from host to device")
.requiredOption("-p, --port <port>", "port where device is connected")
.action(function (src, dest, options) {
.option("-p, --port <port>", optionDescriptions.port, "@2e8a")
.action(async function (src, dest, options) {
const srcPath = path.resolve(src);
if (!fs.existsSync(srcPath)) {
console.log(`file not found: ${src}`);
Expand All @@ -170,12 +264,17 @@ program
}
const stat = fs.statSync(srcPath);
const fileSize = stat.size;
const port = options.port;

// find port
const port = await findPort(options.port, true);

// put
const serial = new SerialPort(port, serialOptions);
serial.open(async (err) => {
if (err) {
console.error(err);
} else {
console.log(`connected to ${port}`);
const bs = new BufferedSerial(serial);
process.stdout.write(colors.grey("copying "));
await put(bs, srcPath, dest, fileSize, () => {
Expand All @@ -195,8 +294,8 @@ program
program
.command("get <src> <dest>")
.description("copy a file from device to host")
.requiredOption("-p, --port <port>", "port where device is connected")
.action(function (src, dest, options) {
.option("-p, --port <port>", optionDescriptions.port, "@2e8a")
.action(async function (src, dest, options) {
try {
const destPath = path.resolve(dest);
if (fs.existsSync(destPath)) {
Expand All @@ -207,12 +306,17 @@ program
console.log(`full path required: ${src}`);
return;
}
const port = options.port;

// find port
const port = await findPort(options.port, true);

// get
const serial = new SerialPort(port, serialOptions);
serial.open(async (err) => {
if (err) {
console.error(err);
} else {
console.log(`connected to ${port}`);
const bs = new BufferedSerial(serial);
const fun = `
function (fn) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@kaluma/cli",
"description": "Kaluma CLI (Command Line Interface)",
"version": "1.1.0",
"version": "1.2.0",
"author": "Minkyu Lee <[email protected]>",
"repository": {
"type": "git",
Expand Down

0 comments on commit 7ce3c97

Please sign in to comment.