forked from pybricks/pybricks-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprotocol.ts
More file actions
371 lines (348 loc) · 9.47 KB
/
protocol.ts
File metadata and controls
371 lines (348 loc) · 9.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2023 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.
*
* @since Pybricks Profile v1.0.0
*/
StopUserProgram = 0,
/**
* Request to start the user program.
*
* @since Pybricks Profile v1.2.0
*/
StartUserProgram = 1,
/**
* Request to start the interactive REPL.
*
* @since Pybricks Profile v1.2.0
*/
StartRepl = 2,
/**
* Request to write user program metadata.
*
* @since Pybricks Profile v1.2.0
*/
WriteUserProgramMeta = 3,
/**
* Request to write to user RAM.
*
* @since Pybricks Profile v1.2.0
*/
WriteUserRam = 4,
/**
* Request to reboot in firmware update mode.
*
* @since Pybricks Profile v1.2.0
*/
ResetInUpdateMode = 5,
/**
* Request to write data to stdin.
*
* @since Pybricks Profile v1.3.0
*/
WriteStdin = 6,
/**
* Requests to write to a buffer that is pre-allocated by a user program.
*
* Parameters:
* - offset: The offset from the buffer base address (16-bit little-endian
* unsigned integer).
* - payload: The data to write.
*
* @since Pybricks Profile v1.4.0
*/
WriteAppData = 7,
}
/**
* Creates a {@link CommandType.StopUserProgram} message.
*
* @since Pybricks Profile v1.0.0
*/
export function createStopUserProgramCommand(): Uint8Array {
const msg = new Uint8Array(1);
msg[0] = CommandType.StopUserProgram;
return msg;
}
/**
* Creates a {@link CommandType.StartUserProgram} message.
*
* The optional payload parameter was added in Pybricks Profile v1.4.0.
*
* Parameters:
* - payload: Optional program identifier (one byte). Slots 0--127 are
* reserved for downloaded user programs. Slots 128--255 are
* for builtin user programs. If no program identifier is
* given, the currently active program slot will be started.
*
* @since Pybricks Profile v1.2.0. Program identifier added in Pybricks Profile v1.4.0.
*/
export function createStartUserProgramCommand(slot: number | undefined): Uint8Array {
const msg = new Uint8Array(slot === undefined ? 1 : 2);
msg[0] = CommandType.StartUserProgram;
if (slot !== undefined) {
msg[1] = slot & 0xff;
}
return msg;
}
/**
* Creates a {@link CommandType.StartRepl} message.
*
* @since Pybricks Profile v1.2.0
*/
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.
*
* @since Pybricks Profile v1.2.0
*/
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.
*
* @since Pybricks Profile v1.2.0
*/
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;
}
/**
* Creates a {@link CommandType.WriteStdin} message.
* @param payload The bytes to write.
*
* @since Pybricks Profile v1.3.0.
*/
export function createWriteStdinCommand(payload: ArrayBuffer): Uint8Array {
const msg = new Uint8Array(1 + payload.byteLength);
const view = new DataView(msg.buffer);
view.setUint8(0, CommandType.WriteStdin);
msg.set(new Uint8Array(payload), 1);
return msg;
}
/**
* Creates a {@link CommandType.WriteAppData} message.
* @param offset The offset from the buffer base address
* @param payload The bytes to write.
*
* @since Pybricks Profile v1.4.0.
*/
export function createWriteAppDataCommand(
offset: number,
payload: ArrayBuffer,
): Uint8Array {
const msg = new Uint8Array(1 + 2 + payload.byteLength);
const view = new DataView(msg.buffer);
view.setUint8(0, CommandType.WriteAppData);
view.setUint16(1, offset & 0xffff);
msg.set(new Uint8Array(payload), 3);
return msg;
}
/** Events are notifications received from the hub. */
export enum EventType {
/**
* Status report event.
*
* Received when notifications are enabled and when status changes.
*
* @since Pybricks Profile v1.0.0
*/
StatusReport = 0,
/**
* Hub wrote to stdout event.
*
* @since Pybricks Profile v1.3.0
*/
WriteStdout = 1,
/**
* Hub wrote to appdata event.
*
* @since Pybricks Profile v1.4.0
*/
WriteAppData = 2,
}
/** Status indications received by Event.StatusReport */
export enum Status {
/**
* Battery voltage is low.
*
* @since Pybricks Profile v1.0.0
*/
BatteryLowVoltageWarning = 0,
/**
* Battery voltage is critically low.
*
* @since Pybricks Profile v1.0.0
*/
BatteryLowVoltageShutdown = 1,
/**
* Battery current is too high.
*
* @since Pybricks Profile v1.0.0
*/
BatteryHighCurrent = 2,
/**
* Bluetooth Low Energy is advertising/discoverable.
*
* @since Pybricks Profile v1.0.0
*/
BLEAdvertising = 3,
/**
* Bluetooth Low Energy has low signal.
*
* @since Pybricks Profile v1.0.0
*/
BLELowSignal = 4,
/**
* Power button is currently pressed.
*
* @since Pybricks Profile v1.0.0
*/
PowerButtonPressed = 5,
/**
* User program is currently running.
*
* @since Pybricks Profile v1.0.0
*/
UserProgramRunning = 6,
/**
* Hub is shutting down.
*
* @since Pybricks Profile v1.1.0
*/
Shutdown = 7,
}
/** 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.
*
* @since Pybricks Profile v1.0.0
*/
export function parseStatusReport(msg: DataView): number {
assert(msg.getUint8(0) === EventType.StatusReport, 'expecting status report event');
return msg.getUint32(1, true);
}
/**
* Parses the payload of a write stdout.
* @param msg The raw message data.
* @returns The bytes that were written.
*
* @since Pybricks Profile v1.3.0
*/
export function parseWriteStdout(msg: DataView): ArrayBuffer {
assert(msg.getUint8(0) === EventType.WriteStdout, 'expecting write stdout event');
return msg.buffer.slice(1);
}
/**
* Parses the payload of a app data message.
* @param msg The raw message data.
* @returns The bytes that were written.
*
* @since Pybricks Profile v1.4.0
*/
export function parseWriteAppData(msg: DataView): ArrayBuffer {
assert(msg.getUint8(0) === EventType.WriteAppData, 'expecting write appdata event');
return msg.buffer.slice(1);
}
/**
* 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.
*
* @since Pybricks Profile v1.2.0
*/
HasRepl = 1 << 0,
/**
* Hub supports {@link FileFormat.MultiMpy6} (bytecode only, no native modules).
*
* @since Pybricks Profile v1.2.0
*/
UserProgramMultiMpy6 = 1 << 1,
/**
* Hub supports {@link FileFormat.MultiMpy6} that include native modules
* with MPY ABI v6.1.
*
* @since Pybricks Profile v1.3.0
*/
UserProgramMultiMpy6Native6p1 = 1 << 2,
/**
* Hub supports builtin sensor port view monitoring program.
*
* @since Pybricks Profile v1.4.0.
*/
HasPortView = 1 << 3,
/**
* Hub supports builtin IMU calibration program.
*
* @since Pybricks Profile v1.4.0.
*/
HasIMUCalibration = 1 << 4,
}
/** 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,
}