Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Verify that the runtime is installed correctly by running:

### Connecting to the device

All commands interacting with the device require specifying the device connection using either `--port` or `--socket` option.
All commands interacting with the device require specifying the device connection using USB `--port`, `--socket` or `--ble` options.

To connect to the device using serial port, use:

Expand All @@ -56,10 +56,17 @@ To connect to the device using TCP socket, use:

jac --socket <host>:<port> <command>

To connect to the device using Bluetooth Low Energy, use:

jac --ble <device-name> <command>

To list available serial ports, use:

jac list-ports

To list available Bluetooth devices, use:
jac list-ble

To tunnel serial port over TCP, use:

jac serial-socket --port <port> --socket <port>
Expand Down
1,837 changes: 1,764 additions & 73 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
],
"dependencies": {
"@cubicap/esptool-js": "^0.3.2",
"@stoprocent/noble": "^2.3.2",
"chalk": "^5.4.1",
"cli-progress": "^3.12.0",
"crc": "^4.3.2",
Expand Down
149 changes: 149 additions & 0 deletions src/commands/ble.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { Arg, Command, Env } from "./lib/command.js";
import { stdout, stderr } from "process";
import { getDevice } from "./util.js";


enum BleKvNs {
Main = "ble_cfg",
}

enum BleKeys {
Mode = "mode",
Name = "name",
}

enum BleMode {
DISABLED,
ENABLED_STREAM,

// TODO: Can we add this for the future? (use will handle the BLE yourself)
UNMANAGED,
}

export const bleGet = new Command("Display current BLE config", {
action: async (options: Record<string, string | boolean>, args: Record<string, string>, env: Env) => {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;

const device = await getDevice(port, baudrate, socket, ble, env);

await new Promise((resolve) => setTimeout(resolve, 1000));
stdout.write("\n-----\n");

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
throw 1;
});

const mode = await device.controller.configGetInt(BleKvNs.Main, BleKeys.Mode);
const name = await device.controller.configGetString(BleKvNs.Main, BleKeys.Name);

stdout.write(`BLE Mode: ${BleMode[mode]}
Device Name: ${name}
`);

await device.controller.unlock().catch((err) => {
stderr.write("Error unlocking device: " + err + "\n");
throw 1;
});
},
chainable: true
});


export const bleDisable = new Command("Disable WiFi", {
action: async (options: Record<string, string | boolean>, args: Record<string, string>, env: Env) => {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
throw 1;
});

await device.controller.configSetInt(BleKvNs.Main, BleKeys.Mode, BleMode.DISABLED);

await device.controller.unlock().catch((err) => {
stderr.write("Error unlocking device: " + err + "\n");
throw 1;
});

stdout.write("Wifi config changed.\n");
},
chainable: true
});


export const bleEnableStream = new Command("Enable BLE stream mode", {
action: async (options: Record<string, string | boolean>, args: Record<string, string>, env: Env) => {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
throw 1;
});

await device.controller.configSetInt(BleKvNs.Main, BleKeys.Mode, BleMode.ENABLED_STREAM);

await device.controller.unlock().catch((err) => {
stderr.write("Error unlocking device: " + err + "\n");
throw 1;
});

stdout.write("Wifi config changed.\n");
},
chainable: true
});


export const bleSetName = new Command("Set BLE device name", {
action: async (options: Record<string, string | boolean>, args: Record<string, string>, env: Env) => {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;

const name = args["name"] as string | undefined;

if (!name) {
stderr.write("Name is required\n");
throw 1;
}

if (name && name.length >= 20) {
stderr.write("Name is too long\n");
throw 1;
}

const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
throw 1;
});

// TODO: Can we enable stream mode here?
await device.controller.configSetInt(BleKvNs.Main, BleKeys.Mode, BleMode.ENABLED_STREAM);
await device.controller.configSetString(BleKvNs.Main, BleKeys.Name, name);

await device.controller.unlock().catch((err) => {
stderr.write("Error unlocking device: " + err + "\n");
throw 1;
});

stdout.write("Wifi config changed.\n");
},
args: [
new Arg("name", "Name of the BLE device", { required: true }),
],
chainable: true
});
3 changes: 2 additions & 1 deletion src/commands/flash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,10 @@ const cmd = new Command("Flash code to device (replace contents of ./code)", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const from = options["from"] as string;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
3 changes: 2 additions & 1 deletion src/commands/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ const cmd = new Command("Format device storage", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
3 changes: 2 additions & 1 deletion src/commands/get-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ const cmd = new Command("Get example project from device", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;

const path_ = args["path"] as string;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
9 changes: 9 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const jac = new Program("jac", "Tools for controlling devices running Jaculus",
"port": new Opt("Serial port to use (default: first available)"),
"baudrate": new Opt("Baudrate to use", { defaultValue: "921600" }),
"socket": new Opt("host:port to use"),
"ble": new Opt("Bluetooth LE address to use", { defaultValue: undefined }),
},
action: async (options: Record<string, string | boolean>) => {
if (options["help"]) {
Expand Down Expand Up @@ -67,9 +68,12 @@ import fomat from "./format.js";
import resourcesLs from "./resources-ls.js";
import resourcesRead from "./resources-read.js";
import getExamples from "./get-examples.js";
import listBle from "./list-ble.js";
import { wifiAdd, wifiRemove, wifiGet, wifiSetAp, wifiSetSta, wifiDisable } from "./wifi.js";
import { bleGet, bleDisable, bleEnableStream, bleSetName} from "./ble.js";

jac.addCommand("list-ports", listPorts);
jac.addCommand("list-ble", listBle);
jac.addCommand("serial-socket", serialSocket);
jac.addCommand("install", install);
jac.addCommand("build", build);
Expand Down Expand Up @@ -103,6 +107,11 @@ jac.addCommand("wifi-rm", wifiRemove);
jac.addCommand("wifi-sta", wifiSetSta);
jac.addCommand("wifi-disable", wifiDisable);

jac.addCommand("ble-get", bleGet);
jac.addCommand("ble-disable", bleDisable);
jac.addCommand("ble-enable-stream", bleEnableStream);
jac.addCommand("ble-set-name", bleSetName);


const args = process.argv.slice(2);

Expand Down
40 changes: 40 additions & 0 deletions src/commands/list-ble.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Arg, Command, Opt } from "./lib/command.js";
import { stdout } from "process";
import { scanBleDevices } from "../link/ble/bleScan.js";

const cmd = new Command("List available BLE devices", {
action: async (options: Record<string, string | boolean>, args: Record<string, string>) => {
const timeout = options["timeout"] ? Number(options["timeout"]) : 4000;
const showUuids = !!options["show-uuids"];
// Scan for BLE devices
let table = await scanBleDevices(timeout, showUuids);
// Add header row
table = [{ name: "Name", rssi: "RSSI", uuid: showUuids ? "UUID" : undefined }, ...table];
// Output formatting
let maxNameLength = 0;
let maxUuidLength = 0;
for (const row of table) {
maxNameLength = Math.max(maxNameLength, row.name.length);
if (showUuids && row.uuid) maxUuidLength = Math.max(maxUuidLength, row.uuid.length);
}
let first = true;
for (const row of table) {
let line = row.name.padEnd(maxNameLength) + " " + row.rssi.padEnd(4);
if (showUuids && row.uuid) line += " " + row.uuid.padEnd(maxUuidLength);
stdout.write(line + "\n");
if (first) {
first = false;
let sep = "-".repeat(maxNameLength) + " " + "-".repeat(4);
if (showUuids) sep += " " + "-".repeat(maxUuidLength);
stdout.write(sep + "\n");
}
}
return;
},
options: {
"timeout": new Opt("Scan timeout in milliseconds (default: 4000)", { defaultValue: "4000" }),
"show-uuids": new Opt("Show UUIDs of discovered devices", { isFlag: true }),
},
});

export default cmd;
3 changes: 2 additions & 1 deletion src/commands/ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ const cmd = new Command("List files in a directory", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const path = args["path"] as string;
const directoryFlag = options["directory"] as boolean;
const sizeFlag = options["size"] as boolean;

const flags = (directoryFlag ? "d" : "") + (sizeFlag ? "s" : "");

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
3 changes: 2 additions & 1 deletion src/commands/mkdir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ const cmd = new Command("Create a directory on device", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const path = args["path"] as string;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
3 changes: 2 additions & 1 deletion src/commands/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ const cmd = new Command("Monitor program output", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

device.programOutput.onData((data) => {
stdout.write(data);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ const cmd = new Command("Download a file/directory from device", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const local = args["local"] as string;
const remote = args["remote"] as string;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
3 changes: 2 additions & 1 deletion src/commands/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ const cmd = new Command("Read a file from device", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const path = args["path"] as string;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
3 changes: 2 additions & 1 deletion src/commands/resources-ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ const cmd = new Command("List available resources", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
3 changes: 2 additions & 1 deletion src/commands/resources-read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ const cmd = new Command("Read a resource from device", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const name = args["name"] as string;
const outfile = options["outfile"] as string | undefined;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
3 changes: 2 additions & 1 deletion src/commands/rm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ const cmd = new Command("Delete a file on device", {
const port = options["port"] as string;
const baudrate = options["baudrate"] as string;
const socket = options["socket"] as string;
const ble = options["ble"] as string | undefined;
const path = args["path"] as string;

const device = await getDevice(port, baudrate, socket, env);
const device = await getDevice(port, baudrate, socket, ble, env);

await device.controller.lock().catch((err) => {
stderr.write("Error locking device: " + err + "\n");
Expand Down
Loading