-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathBenbenControllerBLE.ts
105 lines (85 loc) · 3.57 KB
/
BenbenControllerBLE.ts
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
export enum BenbenControllerBLEConnectionState {
DISCONNECTED,
CONNECTING,
CONNECTED,
};
export class BenbenControllerBLE {
private static readonly SERVICE_FILTER_UUID = 0xAF30;
private static readonly SERVICE_DATA_UUID = 0xAE3A;
private static readonly CHARACTERISTIC_UUID = 0xAE3B;
private connectionState = BenbenControllerBLEConnectionState.DISCONNECTED;
private bluetoothCharacteristic: BluetoothRemoteGATTCharacteristic | null = null;
private readonly motorValues = new Float32Array(4);
public constructor() {}
public async connect() {
if (this.connectionState !== BenbenControllerBLEConnectionState.DISCONNECTED)
throw new Error('BenbenController.connect() can only be called when it is disconnected');
try {
this.connectionState = BenbenControllerBLEConnectionState.CONNECTING;
const bluetoothDevice = await navigator.bluetooth.requestDevice({
filters: [
{ services: [BenbenControllerBLE.SERVICE_FILTER_UUID] },
],
optionalServices: [BenbenControllerBLE.SERVICE_DATA_UUID],
});
const bluetoothGATTServer = bluetoothDevice.gatt;
if (!bluetoothGATTServer) throw new Error('BenbenController: device.gatt do not exist');
await bluetoothGATTServer.connect();
const bluetoothGATTService
= await bluetoothGATTServer.getPrimaryService(BenbenControllerBLE.SERVICE_DATA_UUID);
const bluetoothGATTCharacteristic
= await bluetoothGATTService.getCharacteristic(BenbenControllerBLE.CHARACTERISTIC_UUID);
this.bluetoothCharacteristic = bluetoothGATTCharacteristic;
this.connectionState = BenbenControllerBLEConnectionState.CONNECTED;
this.startSender();
} catch (e) {
this.connectionState = BenbenControllerBLEConnectionState.DISCONNECTED;
throw e;
}
}
public getConnectionState() { return this.connectionState; }
public setMotorValues(values: number[]) {
this.motorValues.set(values);
}
private sleep(t: number) {
return new Promise(resolve => setTimeout(resolve, t))
}
private async startSender() {
const INTERVAL = 50;
try {
while (true) {
const data = this.createPacket();
const startTime = performance.now();
await this.bluetoothCharacteristic?.writeValue(data);
const currentTime = performance.now();
await this.sleep(Math.max(INTERVAL - (currentTime - startTime), 0));
}
} catch (e) {
console.error(e);
this.connectionState = BenbenControllerBLEConnectionState.DISCONNECTED;
}
}
private convertMotorValue(x: number): number {
const v = Math.round(127 * x);
return v < -127 ? 1 : v > 127 ? 255 : 128 + v;
}
private createPacket(): Uint8Array {
const data = new Uint8Array([
0xCC,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
0x33,
]);
const [a, b, c, d] = this.motorValues;
data[4] = this.convertMotorValue(a);
data[5] = this.convertMotorValue(b);
data[6] = this.convertMotorValue(c);
data[7] = this.convertMotorValue(-d);
let checksum = 0;
for (let i = 1; i <= 15; ++i)
checksum += data[i];
checksum &= 0xFF;
data[16] = checksum;
return data;
}
}