Files
pybricks-code/src/ble-pybricks-service/protocol.ts
T

166 lines
4.8 KiB
TypeScript

// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
//
// Definitions related to the Pybricks Bluetooth low energy GATT service.
import { assert } from '../utils';
/** Pybricks service UUID. */
export const pybricksServiceUUID = 'c5f50001-8280-46da-89f4-6d8051e4aeef';
/** Pybricks control/event characteristic UUID. */
export const pybricksControlEventCharacteristicUUID =
'c5f50002-8280-46da-89f4-6d8051e4aeef';
/** Pybricks hub capabilities characteristic UUID. */
export const pybricksHubCapabilitiesCharacteristicUUID =
'c5f50003-8280-46da-89f4-6d8051e4aeef';
/** Commands are instructions sent to the hub. */
export enum CommandType {
/** Request to stop the user program, if it is running. */
StopUserProgram = 0,
/** Request to start the user program. */
StartUserProgram = 1,
/** Request to start the interactive REPL. */
StartRepl = 2,
/** Request to write user program metadata. */
WriteUserProgramMeta = 3,
/** Request to write to user RAM. */
WriteUserRam = 4,
/** Request to reboot in firmware update mode. */
ResetInUpdateMode = 5,
}
/**
* Creates a {@link CommandType.StopUserProgram} message.
*/
export function createStopUserProgramCommand(): Uint8Array {
const msg = new Uint8Array(1);
msg[0] = CommandType.StopUserProgram;
return msg;
}
/**
* Creates a {@link CommandType.StartUserProgram} message.
*/
export function createStartUserProgramCommand(): Uint8Array {
const msg = new Uint8Array(1);
msg[0] = CommandType.StartUserProgram;
return msg;
}
/**
* Creates a {@link CommandType.StartRepl} message.
*/
export function createStartReplCommand(): Uint8Array {
const msg = new Uint8Array(1);
msg[0] = CommandType.StartRepl;
return msg;
}
/**
* Creates a {@link CommandType.WriteUserProgramMeta} message.
* @param size The size of the user program in bytes.
*/
export function createWriteUserProgramMetaCommand(size: number): Uint8Array {
const msg = new Uint8Array(5);
const view = new DataView(msg.buffer);
view.setUint8(0, CommandType.WriteUserProgramMeta);
view.setUint32(1, size, true);
return msg;
}
/**
* Creates a {@link CommandType.WriteUserRam} message.
* @param offset The offset from the user RAM base address.
* @param payload The bytes to write.
*/
export function createWriteUserRamCommand(
offset: number,
payload: ArrayBuffer,
): Uint8Array {
const msg = new Uint8Array(5 + payload.byteLength);
const view = new DataView(msg.buffer);
view.setUint8(0, CommandType.WriteUserRam);
view.setUint32(1, offset, true);
msg.set(new Uint8Array(payload), 5);
return msg;
}
/** Events are notifications received from the hub. */
export enum EventType {
/** Status report. Received when notifications are enabled and when status changes. */
StatusReport = 0,
}
/** Status indications received by Event.StatusReport */
export enum Status {
/** Battery voltage is low. */
BatteryLowVoltageWarning = 0,
/** Battery voltage is critically low. */
BatteryLowVoltageShutdown = 1,
/** Battery current is too high. */
BatteryHighCurrent = 2,
/** Bluetooth Low Energy is advertising/discoverable. */
BLEAdvertising = 3,
/** Bluetooth Low Energy has low signal. */
BLELowSignal = 4,
/** Power button is currently pressed. */
PowerButtonPressed = 5,
/** User program is currently running. */
UserProgramRunning = 6,
}
/** Converts a Status enum value to a bit flag. */
export function statusToFlag(status: Status): number {
return 1 << status;
}
/** Gets the event type from a message. */
export function getEventType(msg: DataView): EventType {
return msg.getUint8(0) as EventType;
}
/**
* Parses the payload of a status report message.
* @param msg The raw message data.
* @returns The status as bit flags.
*/
export function parseStatusReport(msg: DataView): number {
assert(msg.getUint8(0) === EventType.StatusReport, 'expecting status report event');
return msg.getUint32(1, true);
}
/**
* Protocol error. Thrown e.g. when there is a malformed message.
*/
export class ProtocolError extends Error {
/**
* Creates a new ProtocolError.
* @param message The error message
* @param value The bytecodes that caused the error
*/
constructor(message: string, public value: DataView) {
super(message);
}
}
/**
* Hub capability flags for the hub capabilities characteristic.
*/
export enum HubCapabilityFlag {
/** Hub has an interactive REPL. */
HasRepl = 1 << 0,
/** Hub supports {@link FileFormat.MultiMpy6} */
UserProgramMultiMpy6 = 1 << 1,
}
/** Supported user program file formats. */
export enum FileFormat {
/** MicroPython .mpy file with MPY v5. */
Mpy5,
/** MicroPython .mpy file with MPY v6. */
Mpy6,
/** Pybricks multi-MicroPython .mpy file with MPY v6. */
MultiMpy6,
}