@hangtime/grip-connect 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -26
- package/build/connect.d.ts +1 -1
- package/build/connect.js +79 -78
- package/build/devices/moterboard.d.ts +5 -0
- package/build/devices/moterboard.js +84 -0
- package/build/read.d.ts +1 -1
- package/build/read.js +7 -3
- package/build/write.js +2 -1
- package/package.json +2 -2
- package/src/characteristic.d.ts +9 -0
- package/src/characteristic.js +15 -0
- package/src/connect.d.ts +1 -1
- package/src/connect.js +79 -78
- package/src/connect.ts +86 -83
- package/src/devices/entralpi.d.ts +2 -0
- package/src/devices/entralpi.js +52 -0
- package/src/devices/index.d.ts +3 -0
- package/src/devices/index.js +3 -0
- package/src/devices/moterboard.d.ts +7 -0
- package/src/devices/moterboard.js +163 -0
- package/src/devices/moterboard.ts +106 -0
- package/src/devices/tindeq.d.ts +17 -0
- package/src/devices/tindeq.js +37 -0
- package/src/devices/types.d.ts +20 -0
- package/src/devices/types.js +1 -0
- package/src/disconnect.d.ts +6 -0
- package/src/disconnect.js +11 -0
- package/src/index.d.ts +6 -0
- package/src/index.js +6 -0
- package/src/notify.d.ts +4 -0
- package/src/notify.js +6 -0
- package/src/read.d.ts +6 -0
- package/src/read.js +44 -0
- package/src/read.ts +12 -3
- package/src/write.d.ts +7 -0
- package/src/write.js +33 -0
- package/src/write.ts +2 -1
- package/tsconfig.json +2 -2
package/src/connect.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { notifyCallback } from "./notify";
|
|
2
|
+
import { handleMotherboardData } from "./devices/moterboard";
|
|
3
|
+
let server;
|
|
4
|
+
const receiveBuffer = [];
|
|
2
5
|
/**
|
|
3
6
|
* onDisconnected
|
|
4
7
|
* @param board
|
|
@@ -16,63 +19,83 @@ const onDisconnected = (event, board) => {
|
|
|
16
19
|
*/
|
|
17
20
|
const handleNotifications = (event, board) => {
|
|
18
21
|
const characteristic = event.target;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const elementKeys = [
|
|
36
|
-
"frames",
|
|
37
|
-
"cycle",
|
|
38
|
-
"unknown",
|
|
39
|
-
"eleven",
|
|
40
|
-
"dynamic1",
|
|
41
|
-
"pressure1",
|
|
42
|
-
"left",
|
|
43
|
-
"dynamic2",
|
|
44
|
-
"pressure2",
|
|
45
|
-
"right",
|
|
46
|
-
];
|
|
47
|
-
const dataObject = {};
|
|
48
|
-
if (parsedDecimalArray) {
|
|
49
|
-
elementKeys.forEach((key, index) => {
|
|
50
|
-
dataObject[key] = parsedDecimalArray[index];
|
|
51
|
-
});
|
|
22
|
+
const value = characteristic.value;
|
|
23
|
+
if (value) {
|
|
24
|
+
if (board.name === "Motherboard") {
|
|
25
|
+
if (value) {
|
|
26
|
+
for (let i = 0; i < value.byteLength; i++) {
|
|
27
|
+
receiveBuffer.push(value.getUint8(i));
|
|
28
|
+
}
|
|
29
|
+
let idx;
|
|
30
|
+
while ((idx = receiveBuffer.indexOf(10)) >= 0) {
|
|
31
|
+
const line = receiveBuffer.splice(0, idx + 1).slice(0, -1); // Combine and remove LF
|
|
32
|
+
if (line.length > 0 && line[line.length - 1] === 13)
|
|
33
|
+
line.pop(); // Remove CR
|
|
34
|
+
const decoder = new TextDecoder("utf-8");
|
|
35
|
+
const receivedString = decoder.decode(new Uint8Array(line));
|
|
36
|
+
handleMotherboardData(characteristic.uuid, receivedString);
|
|
37
|
+
}
|
|
52
38
|
}
|
|
39
|
+
}
|
|
40
|
+
else if (board.name === "ENTRALPI") {
|
|
41
|
+
// TODO: handle Entralpi notify
|
|
42
|
+
// characteristic.value!.getInt16(0) / 100;
|
|
53
43
|
if (notifyCallback) {
|
|
54
|
-
notifyCallback({ uuid: characteristic.uuid, value:
|
|
44
|
+
notifyCallback({ uuid: characteristic.uuid, value: value });
|
|
55
45
|
}
|
|
56
46
|
}
|
|
57
|
-
else if (
|
|
58
|
-
// TODO: handle
|
|
59
|
-
// notifyCallback({ uuid: characteristic.uuid, value: characteristic.value!.getInt8(0) / 100 })
|
|
47
|
+
else if (board.name === "Tindeq") {
|
|
48
|
+
// TODO: handle Tindeq notify
|
|
60
49
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (notifyCallback) {
|
|
66
|
-
notifyCallback({ uuid: characteristic.uuid, value: receivedString });
|
|
50
|
+
else {
|
|
51
|
+
if (notifyCallback) {
|
|
52
|
+
notifyCallback({ uuid: characteristic.uuid, value: value });
|
|
53
|
+
}
|
|
67
54
|
}
|
|
68
55
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* onConnected
|
|
59
|
+
* @param event
|
|
60
|
+
* @param board
|
|
61
|
+
*/
|
|
62
|
+
const onConnected = async (board, onSuccess) => {
|
|
63
|
+
try {
|
|
64
|
+
const services = await server?.getPrimaryServices();
|
|
65
|
+
if (!services || services.length === 0) {
|
|
66
|
+
console.error("No services found");
|
|
67
|
+
return;
|
|
75
68
|
}
|
|
69
|
+
for (const service of services) {
|
|
70
|
+
const matchingService = board.services.find((boardService) => boardService.uuid === service.uuid);
|
|
71
|
+
if (matchingService) {
|
|
72
|
+
// Android bug: Introduce a delay before getting characteristics
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
74
|
+
const characteristics = await service.getCharacteristics();
|
|
75
|
+
for (const characteristic of matchingService.characteristics) {
|
|
76
|
+
const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid);
|
|
77
|
+
if (matchingCharacteristic) {
|
|
78
|
+
const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid);
|
|
79
|
+
if (element) {
|
|
80
|
+
element.characteristic = matchingCharacteristic;
|
|
81
|
+
// notify
|
|
82
|
+
if (element.id === "rx") {
|
|
83
|
+
matchingCharacteristic.startNotifications();
|
|
84
|
+
matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) => handleNotifications(event, board));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.warn(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Call the onSuccess callback after successful connection and setup
|
|
95
|
+
onSuccess();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error(error);
|
|
76
99
|
}
|
|
77
100
|
};
|
|
78
101
|
/**
|
|
@@ -83,7 +106,7 @@ function getAllServiceUUIDs(device) {
|
|
|
83
106
|
return device.services.map((service) => service.uuid);
|
|
84
107
|
}
|
|
85
108
|
/**
|
|
86
|
-
*
|
|
109
|
+
* Connect to the BluetoothDevice
|
|
87
110
|
* @param device
|
|
88
111
|
* @param onSuccess
|
|
89
112
|
*/
|
|
@@ -111,37 +134,15 @@ export const connect = async (board, onSuccess) => {
|
|
|
111
134
|
optionalServices: deviceServices,
|
|
112
135
|
});
|
|
113
136
|
board.device = device;
|
|
114
|
-
device.
|
|
115
|
-
|
|
116
|
-
const services = await server?.getPrimaryServices();
|
|
117
|
-
if (!services || services.length === 0) {
|
|
118
|
-
console.error("No services found");
|
|
137
|
+
if (!board.device.gatt) {
|
|
138
|
+
console.error("GATT is not available on this device");
|
|
119
139
|
return;
|
|
120
140
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
for (const characteristic of matchingService.characteristics) {
|
|
126
|
-
const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid);
|
|
127
|
-
if (matchingCharacteristic) {
|
|
128
|
-
const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid);
|
|
129
|
-
if (element) {
|
|
130
|
-
element.characteristic = matchingCharacteristic;
|
|
131
|
-
// notify
|
|
132
|
-
if (element.id === "rx") {
|
|
133
|
-
matchingCharacteristic.startNotifications();
|
|
134
|
-
matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) => handleNotifications(event, board));
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
console.warn(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
141
|
+
server = await board.device?.gatt?.connect();
|
|
142
|
+
board.device.addEventListener("gattserverdisconnected", (event) => onDisconnected(event, board));
|
|
143
|
+
if (server.connected) {
|
|
144
|
+
await onConnected(board, onSuccess);
|
|
143
145
|
}
|
|
144
|
-
onSuccess();
|
|
145
146
|
}
|
|
146
147
|
catch (error) {
|
|
147
148
|
console.error(error);
|
package/src/connect.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Device } from "./devices/types"
|
|
2
2
|
import { notifyCallback } from "./notify"
|
|
3
|
+
import { handleMotherboardData } from "./devices/moterboard"
|
|
4
|
+
|
|
5
|
+
let server: BluetoothRemoteGATTServer
|
|
6
|
+
const receiveBuffer: number[] = []
|
|
3
7
|
|
|
4
8
|
/**
|
|
5
9
|
* onDisconnected
|
|
@@ -18,62 +22,88 @@ const onDisconnected = (event: Event, board: Device): void => {
|
|
|
18
22
|
*/
|
|
19
23
|
const handleNotifications = (event: Event, board: Device): void => {
|
|
20
24
|
const characteristic = event.target as BluetoothRemoteGATTCharacteristic
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
const value = characteristic.value
|
|
26
|
+
if (value) {
|
|
27
|
+
if (board.name === "Motherboard") {
|
|
28
|
+
if (value) {
|
|
29
|
+
for (let i = 0; i < value.byteLength; i++) {
|
|
30
|
+
receiveBuffer.push(value.getUint8(i))
|
|
31
|
+
}
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const elementKeys = [
|
|
40
|
-
"frames",
|
|
41
|
-
"cycle",
|
|
42
|
-
"unknown",
|
|
43
|
-
"eleven",
|
|
44
|
-
"dynamic1",
|
|
45
|
-
"pressure1",
|
|
46
|
-
"left",
|
|
47
|
-
"dynamic2",
|
|
48
|
-
"pressure2",
|
|
49
|
-
"right",
|
|
50
|
-
]
|
|
51
|
-
const dataObject: { [key: string]: number } = {};
|
|
52
|
-
|
|
53
|
-
if (parsedDecimalArray) {
|
|
54
|
-
elementKeys.forEach((key: string, index: number) => {
|
|
55
|
-
dataObject[key] = parsedDecimalArray[index];
|
|
56
|
-
});
|
|
33
|
+
let idx: number
|
|
34
|
+
while ((idx = receiveBuffer.indexOf(10)) >= 0) {
|
|
35
|
+
const line = receiveBuffer.splice(0, idx + 1).slice(0, -1) // Combine and remove LF
|
|
36
|
+
if (line.length > 0 && line[line.length - 1] === 13) line.pop() // Remove CR
|
|
37
|
+
const decoder = new TextDecoder("utf-8")
|
|
38
|
+
const receivedString = decoder.decode(new Uint8Array(line))
|
|
39
|
+
handleMotherboardData(characteristic.uuid, receivedString)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} else if (board.name === "ENTRALPI") {
|
|
43
|
+
// TODO: handle Entralpi notify
|
|
44
|
+
// characteristic.value!.getInt16(0) / 100;
|
|
45
|
+
if (notifyCallback) {
|
|
46
|
+
notifyCallback({ uuid: characteristic.uuid, value: value })
|
|
57
47
|
}
|
|
48
|
+
} else if (board.name === "Tindeq") {
|
|
49
|
+
// TODO: handle Tindeq notify
|
|
50
|
+
} else {
|
|
58
51
|
if (notifyCallback) {
|
|
59
|
-
notifyCallback({ uuid: characteristic.uuid, value:
|
|
52
|
+
notifyCallback({ uuid: characteristic.uuid, value: value })
|
|
60
53
|
}
|
|
61
|
-
} else if (characteristic.value!.byteLength === 14) {
|
|
62
|
-
// TODO: handle 14 byte data
|
|
63
|
-
// notifyCallback({ uuid: characteristic.uuid, value: characteristic.value!.getInt8(0) / 100 })
|
|
64
54
|
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* onConnected
|
|
59
|
+
* @param event
|
|
60
|
+
* @param board
|
|
61
|
+
*/
|
|
62
|
+
const onConnected = async (board: Device, onSuccess: () => void): Promise<void> => {
|
|
63
|
+
try {
|
|
64
|
+
const services = await server?.getPrimaryServices()
|
|
65
|
+
|
|
66
|
+
if (!services || services.length === 0) {
|
|
67
|
+
console.error("No services found")
|
|
68
|
+
return
|
|
70
69
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
|
|
71
|
+
for (const service of services) {
|
|
72
|
+
const matchingService = board.services.find((boardService) => boardService.uuid === service.uuid)
|
|
73
|
+
|
|
74
|
+
if (matchingService) {
|
|
75
|
+
// Android bug: Introduce a delay before getting characteristics
|
|
76
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
77
|
+
|
|
78
|
+
const characteristics = await service.getCharacteristics()
|
|
79
|
+
|
|
80
|
+
for (const characteristic of matchingService.characteristics) {
|
|
81
|
+
const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid)
|
|
82
|
+
|
|
83
|
+
if (matchingCharacteristic) {
|
|
84
|
+
const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid)
|
|
85
|
+
if (element) {
|
|
86
|
+
element.characteristic = matchingCharacteristic
|
|
87
|
+
|
|
88
|
+
// notify
|
|
89
|
+
if (element.id === "rx") {
|
|
90
|
+
matchingCharacteristic.startNotifications()
|
|
91
|
+
matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) =>
|
|
92
|
+
handleNotifications(event, board),
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
console.warn(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
76
101
|
}
|
|
102
|
+
|
|
103
|
+
// Call the onSuccess callback after successful connection and setup
|
|
104
|
+
onSuccess()
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(error)
|
|
77
107
|
}
|
|
78
108
|
}
|
|
79
109
|
/**
|
|
@@ -84,7 +114,7 @@ function getAllServiceUUIDs(device: Device) {
|
|
|
84
114
|
return device.services.map((service) => service.uuid)
|
|
85
115
|
}
|
|
86
116
|
/**
|
|
87
|
-
*
|
|
117
|
+
* Connect to the BluetoothDevice
|
|
88
118
|
* @param device
|
|
89
119
|
* @param onSuccess
|
|
90
120
|
*/
|
|
@@ -117,45 +147,18 @@ export const connect = async (board: Device, onSuccess: () => void): Promise<voi
|
|
|
117
147
|
|
|
118
148
|
board.device = device
|
|
119
149
|
|
|
120
|
-
device.
|
|
121
|
-
|
|
122
|
-
const server = await device.gatt?.connect()
|
|
123
|
-
const services = await server?.getPrimaryServices()
|
|
124
|
-
|
|
125
|
-
if (!services || services.length === 0) {
|
|
126
|
-
console.error("No services found")
|
|
150
|
+
if (!board.device.gatt) {
|
|
151
|
+
console.error("GATT is not available on this device")
|
|
127
152
|
return
|
|
128
153
|
}
|
|
129
154
|
|
|
130
|
-
|
|
131
|
-
const matchingService = board.services.find((boardService) => boardService.uuid === service.uuid)
|
|
155
|
+
server = await board.device?.gatt?.connect()
|
|
132
156
|
|
|
133
|
-
|
|
134
|
-
const characteristics = await service.getCharacteristics()
|
|
157
|
+
board.device.addEventListener("gattserverdisconnected", (event) => onDisconnected(event, board))
|
|
135
158
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (matchingCharacteristic) {
|
|
140
|
-
const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid)
|
|
141
|
-
if (element) {
|
|
142
|
-
element.characteristic = matchingCharacteristic
|
|
143
|
-
|
|
144
|
-
// notify
|
|
145
|
-
if (element.id === "rx") {
|
|
146
|
-
matchingCharacteristic.startNotifications()
|
|
147
|
-
matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) =>
|
|
148
|
-
handleNotifications(event, board),
|
|
149
|
-
)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} else {
|
|
153
|
-
console.warn(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
159
|
+
if (server.connected) {
|
|
160
|
+
await onConnected(board, onSuccess)
|
|
157
161
|
}
|
|
158
|
-
onSuccess()
|
|
159
162
|
} catch (error) {
|
|
160
163
|
console.error(error)
|
|
161
164
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const Entralpi = {
|
|
2
|
+
name: "ENTRALPI",
|
|
3
|
+
services: [
|
|
4
|
+
{
|
|
5
|
+
name: "Device Information",
|
|
6
|
+
id: "device",
|
|
7
|
+
uuid: "0000180a-0000-1000-8000-00805f9b34fb",
|
|
8
|
+
characteristics: [],
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "Battery Service",
|
|
12
|
+
id: "battery",
|
|
13
|
+
uuid: "0000180f-0000-1000-8000-00805f9b34fb",
|
|
14
|
+
characteristics: [],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "Generic Attribute",
|
|
18
|
+
id: "attribute",
|
|
19
|
+
uuid: "00001801-0000-1000-8000-00805f9b34fb",
|
|
20
|
+
characteristics: [],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "UART ISSC Transparent Service",
|
|
24
|
+
id: "uart",
|
|
25
|
+
uuid: "0000fff0-0000-1000-8000-00805f9b34fb",
|
|
26
|
+
characteristics: [
|
|
27
|
+
{
|
|
28
|
+
name: "TX",
|
|
29
|
+
id: "tx",
|
|
30
|
+
uuid: "0000fff5-0000-1000-8000-00805f9b34fb",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "RX",
|
|
34
|
+
id: "rx",
|
|
35
|
+
uuid: "0000fff4-0000-1000-8000-00805f9b34fb",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "Weight Scale",
|
|
41
|
+
id: "weight",
|
|
42
|
+
uuid: "0000181d-0000-1000-8000-00805f9b34fb",
|
|
43
|
+
characteristics: [],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "Generic Access",
|
|
47
|
+
id: "access",
|
|
48
|
+
uuid: "00001800-0000-1000-8000-00805f9b34fb",
|
|
49
|
+
characteristics: [],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { notifyCallback } from "../notify";
|
|
2
|
+
const PACKET_LENGTH = 32;
|
|
3
|
+
const NUM_SAMPLES = 3;
|
|
4
|
+
const CALIBRATION = [[], [], [], []];
|
|
5
|
+
export const Motherboard = {
|
|
6
|
+
name: "Motherboard",
|
|
7
|
+
companyId: 0x2a29,
|
|
8
|
+
services: [
|
|
9
|
+
{
|
|
10
|
+
name: "Device Information",
|
|
11
|
+
id: "device",
|
|
12
|
+
uuid: "0000180a-0000-1000-8000-00805f9b34fb",
|
|
13
|
+
characteristics: [
|
|
14
|
+
// {
|
|
15
|
+
// name: 'Serial Number (Blocked)',
|
|
16
|
+
// id: 'serial'
|
|
17
|
+
// uuid: '00002a25-0000-1000-8000-00805f9b34fb'
|
|
18
|
+
// },
|
|
19
|
+
{
|
|
20
|
+
name: "Firmware Revision",
|
|
21
|
+
id: "firmware",
|
|
22
|
+
uuid: "00002a26-0000-1000-8000-00805f9b34fb",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "Hardware Revision",
|
|
26
|
+
id: "hardware",
|
|
27
|
+
uuid: "00002a27-0000-1000-8000-00805f9b34fb",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "Manufacturer Name",
|
|
31
|
+
id: "manufacturer",
|
|
32
|
+
uuid: "00002a29-0000-1000-8000-00805f9b34fb",
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "Battery Service",
|
|
38
|
+
id: "battery",
|
|
39
|
+
uuid: "0000180f-0000-1000-8000-00805f9b34fb",
|
|
40
|
+
characteristics: [
|
|
41
|
+
{
|
|
42
|
+
name: "Battery Level",
|
|
43
|
+
id: "level",
|
|
44
|
+
uuid: "00002a19-0000-1000-8000-00805f9b34fb",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Unknown Service",
|
|
50
|
+
id: "unknown",
|
|
51
|
+
uuid: "10ababcd-15e1-28ff-de13-725bea03b127",
|
|
52
|
+
characteristics: [
|
|
53
|
+
{
|
|
54
|
+
name: "Unknown 01",
|
|
55
|
+
id: "01",
|
|
56
|
+
uuid: "10ab1524-15e1-28ff-de13-725bea03b127",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "Unknown 02",
|
|
60
|
+
id: "02",
|
|
61
|
+
uuid: "10ab1525-15e1-28ff-de13-725bea03b127",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "UART Nordic Service",
|
|
67
|
+
id: "uart",
|
|
68
|
+
uuid: "6e400001-b5a3-f393-e0a9-e50e24dcca9e",
|
|
69
|
+
characteristics: [
|
|
70
|
+
{
|
|
71
|
+
name: "TX",
|
|
72
|
+
id: "tx",
|
|
73
|
+
uuid: "6e400002-b5a3-f393-e0a9-e50e24dcca9e",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "RX",
|
|
77
|
+
id: "rx",
|
|
78
|
+
uuid: "6e400003-b5a3-f393-e0a9-e50e24dcca9e",
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* applyCalibration
|
|
86
|
+
* @param sample
|
|
87
|
+
* @param calibration
|
|
88
|
+
*/
|
|
89
|
+
const applyCalibration = (sample, calibration) => {
|
|
90
|
+
const zeroCalib = calibration[0][2];
|
|
91
|
+
let sgn = 1;
|
|
92
|
+
let final = 0;
|
|
93
|
+
if (sample < zeroCalib) {
|
|
94
|
+
sgn = -1;
|
|
95
|
+
sample = 2 * zeroCalib - sample;
|
|
96
|
+
}
|
|
97
|
+
for (let i = 1; i < calibration.length; i++) {
|
|
98
|
+
const calibStart = calibration[i - 1][2];
|
|
99
|
+
const calibEnd = calibration[i][2];
|
|
100
|
+
if (sample < calibEnd) {
|
|
101
|
+
final =
|
|
102
|
+
calibration[i - 1][1] +
|
|
103
|
+
((sample - calibStart) / (calibEnd - calibStart)) * (calibration[i][1] - calibration[i - 1][1]);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return sgn * final;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* handleMotherboardData
|
|
111
|
+
* @param line
|
|
112
|
+
*/
|
|
113
|
+
export function handleMotherboardData(uuid, receivedString) {
|
|
114
|
+
const receivedTime = Date.now();
|
|
115
|
+
// Check if the line is entirely hex characters
|
|
116
|
+
const allHex = /^[0-9A-Fa-f]+$/g.test(receivedString);
|
|
117
|
+
// Decide if this is a streaming packet
|
|
118
|
+
if (allHex && receivedString.length === PACKET_LENGTH) {
|
|
119
|
+
// Base-16 decode the string: convert hex pairs to byte values
|
|
120
|
+
const bytes = Array.from({ length: receivedString.length / 2 }, (_, i) => Number(`0x${receivedString.substring(i * 2, i * 2 + 2)}`));
|
|
121
|
+
// Translate header into packet, number of samples from the packet length
|
|
122
|
+
const packet = {
|
|
123
|
+
received: receivedTime,
|
|
124
|
+
sampleNum: new DataView(new Uint8Array(bytes).buffer).getUint16(0, true),
|
|
125
|
+
battRaw: new DataView(new Uint8Array(bytes).buffer).getUint16(2, true),
|
|
126
|
+
samples: [],
|
|
127
|
+
masses: [],
|
|
128
|
+
};
|
|
129
|
+
for (let i = 0; i < NUM_SAMPLES; i++) {
|
|
130
|
+
const sampleStart = 4 + 3 * i;
|
|
131
|
+
packet.samples[i] = bytes[sampleStart] | (bytes[sampleStart + 1] << 8) | (bytes[sampleStart + 2] << 16);
|
|
132
|
+
if (packet.samples[i] >= 0x7fffff) {
|
|
133
|
+
packet.samples[i] -= 0x1000000;
|
|
134
|
+
}
|
|
135
|
+
// TODO: make sure device is calibrated
|
|
136
|
+
if (!CALIBRATION[0].length)
|
|
137
|
+
return;
|
|
138
|
+
packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i]);
|
|
139
|
+
}
|
|
140
|
+
const left = packet.masses[0];
|
|
141
|
+
const center = packet.masses[1];
|
|
142
|
+
const right = packet.masses[2];
|
|
143
|
+
notifyCallback({
|
|
144
|
+
uuid,
|
|
145
|
+
value: {
|
|
146
|
+
massTotal: Math.max(-1000, left + right + center).toFixed(3),
|
|
147
|
+
massLeft: Math.max(-1000, left).toFixed(3),
|
|
148
|
+
massRight: Math.max(-1000, right).toFixed(3),
|
|
149
|
+
massCentre: Math.max(-1000, center).toFixed(3),
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else if ((receivedString.match(/,/g) || []).length === 3) {
|
|
154
|
+
// if the returned notification is a calibration string add them to the array
|
|
155
|
+
const parts = receivedString.split(",");
|
|
156
|
+
const numericParts = parts.map((x) => parseFloat(x));
|
|
157
|
+
CALIBRATION[numericParts[0]].push(numericParts.slice(1));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// unhanded data
|
|
161
|
+
console.log(receivedString);
|
|
162
|
+
}
|
|
163
|
+
}
|