@hangtime/grip-connect 0.0.8 → 0.0.9
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 +4 -8
- package/package.json +1 -1
- package/src/devices/moterboard.d.ts +2 -1
- package/src/devices/moterboard.js +35 -16
- package/src/devices/moterboard.ts +45 -21
- package/src/write.js +1 -2
- package/src/write.ts +1 -2
- package/tsconfig.json +0 -2
- package/build/characteristic.d.ts +0 -9
- package/build/characteristic.js +0 -15
- package/build/connect.d.ts +0 -7
- package/build/connect.js +0 -150
- package/build/devices/entralpi.d.ts +0 -2
- package/build/devices/entralpi.js +0 -52
- package/build/devices/index.d.ts +0 -3
- package/build/devices/index.js +0 -3
- package/build/devices/moterboard.d.ts +0 -7
- package/build/devices/moterboard.js +0 -163
- package/build/devices/tindeq.d.ts +0 -17
- package/build/devices/tindeq.js +0 -37
- package/build/devices/types.d.ts +0 -20
- package/build/devices/types.js +0 -1
- package/build/disconnect.d.ts +0 -6
- package/build/disconnect.js +0 -11
- package/build/index.d.ts +0 -6
- package/build/index.js +0 -6
- package/build/notify.d.ts +0 -4
- package/build/notify.js +0 -6
- package/build/read.d.ts +0 -6
- package/build/read.js +0 -44
- package/build/write.d.ts +0 -7
- package/build/write.js +0 -33
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Force-Sensing Hangboards / Plates used by climbers for strength measurement. Exa
|
|
|
13
13
|
|
|
14
14
|
## Roadmap
|
|
15
15
|
|
|
16
|
-
- ✅
|
|
16
|
+
- ✅ Griptonite Motherboard
|
|
17
17
|
- ✅️ Connect with devices
|
|
18
18
|
- ✅️ Read / Write / Notify using Bluetooth
|
|
19
19
|
- ➡️ Calibrate Devices
|
|
@@ -80,15 +80,11 @@ motherboardButton.addEventListener("click", () => {
|
|
|
80
80
|
await read(Motherboard, "device", "hardware", 1000)
|
|
81
81
|
await read(Motherboard, "device", "firmware", 1000)
|
|
82
82
|
|
|
83
|
-
//
|
|
84
|
-
await write(Motherboard, "uart", "tx", "",
|
|
85
|
-
await write(Motherboard, "uart", "tx", "", 0)
|
|
86
|
-
await write(Motherboard, "uart", "tx", "", 1000)
|
|
87
|
-
|
|
88
|
-
await write(Motherboard, "uart", "tx", "C3,0,0,0", 5000)
|
|
83
|
+
// read calibration (required before reading data)
|
|
84
|
+
await write(Motherboard, "uart", "tx", "C", 5000)
|
|
89
85
|
|
|
90
86
|
// start stream
|
|
91
|
-
await write(Motherboard, "uart", "tx", "
|
|
87
|
+
await write(Motherboard, "uart", "tx", "S30", 15000)
|
|
92
88
|
|
|
93
89
|
// end stream
|
|
94
90
|
await write(Motherboard, "uart", "tx", "", 0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hangtime/grip-connect",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "A client that can establish connections with various Force-Sensing Hangboards/Plates used by climbers for strength measurement. Examples of such hangboards include the Motherboard, Climbro, SmartBoard, Entralpi or Tindeq Progressor",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -2,6 +2,7 @@ import { Device } from "./types";
|
|
|
2
2
|
export declare const Motherboard: Device;
|
|
3
3
|
/**
|
|
4
4
|
* handleMotherboardData
|
|
5
|
-
* @param
|
|
5
|
+
* @param uuid - Unique identifier
|
|
6
|
+
* @param receivedString - Received data string
|
|
6
7
|
*/
|
|
7
8
|
export declare function handleMotherboardData(uuid: string, receivedString: string): void;
|
|
@@ -87,35 +87,47 @@ export const Motherboard = {
|
|
|
87
87
|
* @param calibration
|
|
88
88
|
*/
|
|
89
89
|
const applyCalibration = (sample, calibration) => {
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
// Extract the calibrated value for the zero point
|
|
91
|
+
const zeroCalibration = calibration[0][2];
|
|
92
|
+
// Initialize sign as positive
|
|
93
|
+
let sign = 1;
|
|
94
|
+
// Initialize the final calibrated value
|
|
92
95
|
let final = 0;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
// If the sample value is less than the zero calibration point
|
|
97
|
+
if (sample < zeroCalibration) {
|
|
98
|
+
// Change the sign to negative
|
|
99
|
+
sign = -1;
|
|
100
|
+
// Reflect the sample around the zero calibration point
|
|
101
|
+
sample = 2 * zeroCalibration - sample;
|
|
96
102
|
}
|
|
103
|
+
// Iterate through the calibration data
|
|
97
104
|
for (let i = 1; i < calibration.length; i++) {
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
105
|
+
// Extract the lower and upper bounds of the current calibration range
|
|
106
|
+
const calibrationStart = calibration[i - 1][2];
|
|
107
|
+
const calibrationEnd = calibration[i][2];
|
|
108
|
+
// If the sample value is within the current calibration range
|
|
109
|
+
if (sample < calibrationEnd) {
|
|
110
|
+
// Interpolate to get the calibrated value within the range
|
|
101
111
|
final =
|
|
102
112
|
calibration[i - 1][1] +
|
|
103
|
-
((sample -
|
|
113
|
+
((sample - calibrationStart) / (calibrationEnd - calibrationStart)) * (calibration[i][1] - calibration[i - 1][1]);
|
|
104
114
|
break;
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
|
-
|
|
117
|
+
// Return the calibrated value with the appropriate sign (positive/negative)
|
|
118
|
+
return sign * final;
|
|
108
119
|
};
|
|
109
120
|
/**
|
|
110
121
|
* handleMotherboardData
|
|
111
|
-
* @param
|
|
122
|
+
* @param uuid - Unique identifier
|
|
123
|
+
* @param receivedString - Received data string
|
|
112
124
|
*/
|
|
113
125
|
export function handleMotherboardData(uuid, receivedString) {
|
|
114
126
|
const receivedTime = Date.now();
|
|
115
127
|
// Check if the line is entirely hex characters
|
|
116
|
-
const
|
|
117
|
-
//
|
|
118
|
-
if (
|
|
128
|
+
const isAllHex = /^[0-9A-Fa-f]+$/g.test(receivedString);
|
|
129
|
+
// Handle streaming packet
|
|
130
|
+
if (isAllHex && receivedString.length === PACKET_LENGTH) {
|
|
119
131
|
// Base-16 decode the string: convert hex pairs to byte values
|
|
120
132
|
const bytes = Array.from({ length: receivedString.length / 2 }, (_, i) => Number(`0x${receivedString.substring(i * 2, i * 2 + 2)}`));
|
|
121
133
|
// Translate header into packet, number of samples from the packet length
|
|
@@ -126,9 +138,15 @@ export function handleMotherboardData(uuid, receivedString) {
|
|
|
126
138
|
samples: [],
|
|
127
139
|
masses: [],
|
|
128
140
|
};
|
|
141
|
+
const dataView = new DataView(new Uint8Array(bytes).buffer);
|
|
129
142
|
for (let i = 0; i < NUM_SAMPLES; i++) {
|
|
130
143
|
const sampleStart = 4 + 3 * i;
|
|
131
|
-
|
|
144
|
+
// Use DataView to read the 24-bit unsigned integer
|
|
145
|
+
const rawValue = dataView.getUint8(sampleStart) |
|
|
146
|
+
(dataView.getUint8(sampleStart + 1) << 8) |
|
|
147
|
+
(dataView.getUint8(sampleStart + 2) << 16);
|
|
148
|
+
// Ensure unsigned 32-bit integer
|
|
149
|
+
packet.samples[i] = rawValue >>> 0;
|
|
132
150
|
if (packet.samples[i] >= 0x7fffff) {
|
|
133
151
|
packet.samples[i] -= 0x1000000;
|
|
134
152
|
}
|
|
@@ -146,11 +164,12 @@ export function handleMotherboardData(uuid, receivedString) {
|
|
|
146
164
|
massTotal: Math.max(-1000, left + right + center).toFixed(3),
|
|
147
165
|
massLeft: Math.max(-1000, left).toFixed(3),
|
|
148
166
|
massRight: Math.max(-1000, right).toFixed(3),
|
|
149
|
-
|
|
167
|
+
massCenter: Math.max(-1000, center).toFixed(3),
|
|
150
168
|
},
|
|
151
169
|
});
|
|
152
170
|
}
|
|
153
171
|
else if ((receivedString.match(/,/g) || []).length === 3) {
|
|
172
|
+
console.log(receivedString);
|
|
154
173
|
// if the returned notification is a calibration string add them to the array
|
|
155
174
|
const parts = receivedString.split(",");
|
|
156
175
|
const numericParts = parts.map((x) => parseFloat(x));
|
|
@@ -90,28 +90,41 @@ export const Motherboard: Device = {
|
|
|
90
90
|
* @param calibration
|
|
91
91
|
*/
|
|
92
92
|
const applyCalibration = (sample: number, calibration: number[][]): number => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
let final: number = 0
|
|
93
|
+
// Extract the calibrated value for the zero point
|
|
94
|
+
const zeroCalibration: number = calibration[0][2];
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
// Initialize sign as positive
|
|
97
|
+
let sign: number = 1;
|
|
98
|
+
|
|
99
|
+
// Initialize the final calibrated value
|
|
100
|
+
let final: number = 0;
|
|
101
|
+
|
|
102
|
+
// If the sample value is less than the zero calibration point
|
|
103
|
+
if (sample < zeroCalibration) {
|
|
104
|
+
// Change the sign to negative
|
|
105
|
+
sign = -1;
|
|
106
|
+
|
|
107
|
+
// Reflect the sample around the zero calibration point
|
|
108
|
+
sample = 2 * zeroCalibration - sample;
|
|
100
109
|
}
|
|
101
110
|
|
|
111
|
+
// Iterate through the calibration data
|
|
102
112
|
for (let i = 1; i < calibration.length; i++) {
|
|
103
|
-
|
|
104
|
-
const
|
|
113
|
+
// Extract the lower and upper bounds of the current calibration range
|
|
114
|
+
const calibrationStart: number = calibration[i - 1][2];
|
|
115
|
+
const calibrationEnd: number = calibration[i][2];
|
|
105
116
|
|
|
106
|
-
|
|
117
|
+
// If the sample value is within the current calibration range
|
|
118
|
+
if (sample < calibrationEnd) {
|
|
119
|
+
// Interpolate to get the calibrated value within the range
|
|
107
120
|
final =
|
|
108
121
|
calibration[i - 1][1] +
|
|
109
|
-
((sample -
|
|
110
|
-
break
|
|
122
|
+
((sample - calibrationStart) / (calibrationEnd - calibrationStart)) * (calibration[i][1] - calibration[i - 1][1]);
|
|
123
|
+
break;
|
|
111
124
|
}
|
|
112
125
|
}
|
|
113
|
-
|
|
114
|
-
return
|
|
126
|
+
// Return the calibrated value with the appropriate sign (positive/negative)
|
|
127
|
+
return sign * final;
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
interface Packet {
|
|
@@ -124,16 +137,17 @@ interface Packet {
|
|
|
124
137
|
|
|
125
138
|
/**
|
|
126
139
|
* handleMotherboardData
|
|
127
|
-
* @param
|
|
140
|
+
* @param uuid - Unique identifier
|
|
141
|
+
* @param receivedString - Received data string
|
|
128
142
|
*/
|
|
129
143
|
export function handleMotherboardData(uuid: string, receivedString: string): void {
|
|
130
144
|
const receivedTime: number = Date.now()
|
|
131
145
|
|
|
132
146
|
// Check if the line is entirely hex characters
|
|
133
|
-
const
|
|
147
|
+
const isAllHex: boolean = /^[0-9A-Fa-f]+$/g.test(receivedString);
|
|
134
148
|
|
|
135
|
-
//
|
|
136
|
-
if (
|
|
149
|
+
// Handle streaming packet
|
|
150
|
+
if (isAllHex && receivedString.length === PACKET_LENGTH) {
|
|
137
151
|
// Base-16 decode the string: convert hex pairs to byte values
|
|
138
152
|
const bytes: number[] = Array.from({ length: receivedString.length / 2 }, (_, i) =>
|
|
139
153
|
Number(`0x${receivedString.substring(i * 2, i * 2 + 2)}`),
|
|
@@ -148,17 +162,24 @@ export function handleMotherboardData(uuid: string, receivedString: string): voi
|
|
|
148
162
|
masses: [],
|
|
149
163
|
}
|
|
150
164
|
|
|
165
|
+
const dataView = new DataView(new Uint8Array(bytes).buffer);
|
|
166
|
+
|
|
151
167
|
for (let i = 0; i < NUM_SAMPLES; i++) {
|
|
152
168
|
const sampleStart: number = 4 + 3 * i
|
|
153
|
-
|
|
169
|
+
// Use DataView to read the 24-bit unsigned integer
|
|
170
|
+
const rawValue = dataView.getUint8(sampleStart) |
|
|
171
|
+
(dataView.getUint8(sampleStart + 1) << 8) |
|
|
172
|
+
(dataView.getUint8(sampleStart + 2) << 16);
|
|
173
|
+
|
|
174
|
+
// Ensure unsigned 32-bit integer
|
|
175
|
+
packet.samples[i] = rawValue >>> 0;
|
|
154
176
|
|
|
155
177
|
if (packet.samples[i] >= 0x7fffff) {
|
|
156
|
-
packet.samples[i] -= 0x1000000
|
|
178
|
+
packet.samples[i] -= 0x1000000;
|
|
157
179
|
}
|
|
158
180
|
|
|
159
181
|
// TODO: make sure device is calibrated
|
|
160
182
|
if (!CALIBRATION[0].length) return
|
|
161
|
-
|
|
162
183
|
packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i])
|
|
163
184
|
}
|
|
164
185
|
|
|
@@ -172,10 +193,11 @@ export function handleMotherboardData(uuid: string, receivedString: string): voi
|
|
|
172
193
|
massTotal: Math.max(-1000, left + right + center).toFixed(3),
|
|
173
194
|
massLeft: Math.max(-1000, left).toFixed(3),
|
|
174
195
|
massRight: Math.max(-1000, right).toFixed(3),
|
|
175
|
-
|
|
196
|
+
massCenter: Math.max(-1000, center).toFixed(3),
|
|
176
197
|
},
|
|
177
198
|
})
|
|
178
199
|
} else if ((receivedString.match(/,/g) || []).length === 3) {
|
|
200
|
+
console.log(receivedString)
|
|
179
201
|
// if the returned notification is a calibration string add them to the array
|
|
180
202
|
const parts: string[] = receivedString.split(",")
|
|
181
203
|
const numericParts: number[] = parts.map((x) => parseFloat(x))
|
|
@@ -185,3 +207,5 @@ export function handleMotherboardData(uuid: string, receivedString: string): voi
|
|
|
185
207
|
console.log(receivedString)
|
|
186
208
|
}
|
|
187
209
|
}
|
|
210
|
+
|
|
211
|
+
|
package/src/write.js
CHANGED
|
@@ -10,9 +10,8 @@ export const write = (board, serviceId, characteristicId, message, duration = 0)
|
|
|
10
10
|
const encoder = new TextEncoder();
|
|
11
11
|
const characteristic = getCharacteristic(board, serviceId, characteristicId);
|
|
12
12
|
if (characteristic) {
|
|
13
|
-
const value = message + "\n";
|
|
14
13
|
characteristic
|
|
15
|
-
.writeValue(encoder.encode(
|
|
14
|
+
.writeValue(encoder.encode(message))
|
|
16
15
|
.then(() => {
|
|
17
16
|
setTimeout(() => {
|
|
18
17
|
resolve();
|
package/src/write.ts
CHANGED
|
@@ -19,9 +19,8 @@ export const write = (
|
|
|
19
19
|
const characteristic = getCharacteristic(board, serviceId, characteristicId)
|
|
20
20
|
|
|
21
21
|
if (characteristic) {
|
|
22
|
-
const value = message + "\n"
|
|
23
22
|
characteristic
|
|
24
|
-
.writeValue(encoder.encode(
|
|
23
|
+
.writeValue(encoder.encode(message))
|
|
25
24
|
.then(() => {
|
|
26
25
|
setTimeout(() => {
|
|
27
26
|
resolve()
|
package/tsconfig.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/// <reference types="web-bluetooth" />
|
|
2
|
-
import { Device } from "./devices/types";
|
|
3
|
-
/**
|
|
4
|
-
* getCharacteristic
|
|
5
|
-
* @param board
|
|
6
|
-
* @param serviceId
|
|
7
|
-
* @param characteristicId
|
|
8
|
-
*/
|
|
9
|
-
export declare const getCharacteristic: (board: Device, serviceId: string, characteristicId: string) => BluetoothRemoteGATTCharacteristic | undefined;
|
package/build/characteristic.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* getCharacteristic
|
|
3
|
-
* @param board
|
|
4
|
-
* @param serviceId
|
|
5
|
-
* @param characteristicId
|
|
6
|
-
*/
|
|
7
|
-
export const getCharacteristic = (board, serviceId, characteristicId) => {
|
|
8
|
-
const boardService = board.services.find((service) => service.id === serviceId);
|
|
9
|
-
if (boardService) {
|
|
10
|
-
const boardCharacteristic = boardService.characteristics.find((characteristic) => characteristic.id === characteristicId);
|
|
11
|
-
if (boardCharacteristic) {
|
|
12
|
-
return boardCharacteristic.characteristic;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
};
|
package/build/connect.d.ts
DELETED
package/build/connect.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { notifyCallback } from "./notify";
|
|
2
|
-
import { handleMotherboardData } from "./devices/moterboard";
|
|
3
|
-
let server;
|
|
4
|
-
const receiveBuffer = [];
|
|
5
|
-
/**
|
|
6
|
-
* onDisconnected
|
|
7
|
-
* @param board
|
|
8
|
-
* @param event
|
|
9
|
-
*/
|
|
10
|
-
const onDisconnected = (event, board) => {
|
|
11
|
-
board.device = undefined;
|
|
12
|
-
const device = event.target;
|
|
13
|
-
console.log(`Device ${device.name} is disconnected.`);
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* handleNotifications
|
|
17
|
-
* @param event
|
|
18
|
-
* @param onNotify
|
|
19
|
-
*/
|
|
20
|
-
const handleNotifications = (event, board) => {
|
|
21
|
-
const characteristic = event.target;
|
|
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
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else if (board.name === "ENTRALPI") {
|
|
41
|
-
// TODO: handle Entralpi notify
|
|
42
|
-
// characteristic.value!.getInt16(0) / 100;
|
|
43
|
-
if (notifyCallback) {
|
|
44
|
-
notifyCallback({ uuid: characteristic.uuid, value: value });
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
else if (board.name === "Tindeq") {
|
|
48
|
-
// TODO: handle Tindeq notify
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
if (notifyCallback) {
|
|
52
|
-
notifyCallback({ uuid: characteristic.uuid, value: value });
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
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;
|
|
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);
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* Return all service UUIDs
|
|
103
|
-
* @param device
|
|
104
|
-
*/
|
|
105
|
-
function getAllServiceUUIDs(device) {
|
|
106
|
-
return device.services.map((service) => service.uuid);
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Connect to the BluetoothDevice
|
|
110
|
-
* @param device
|
|
111
|
-
* @param onSuccess
|
|
112
|
-
*/
|
|
113
|
-
export const connect = async (board, onSuccess) => {
|
|
114
|
-
try {
|
|
115
|
-
const deviceServices = getAllServiceUUIDs(board);
|
|
116
|
-
// setup filter list
|
|
117
|
-
const filters = [];
|
|
118
|
-
if (board.name) {
|
|
119
|
-
filters.push({
|
|
120
|
-
name: board.name,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
if (board.companyId) {
|
|
124
|
-
filters.push({
|
|
125
|
-
manufacturerData: [
|
|
126
|
-
{
|
|
127
|
-
companyIdentifier: board.companyId,
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
const device = await navigator.bluetooth.requestDevice({
|
|
133
|
-
filters: filters,
|
|
134
|
-
optionalServices: deviceServices,
|
|
135
|
-
});
|
|
136
|
-
board.device = device;
|
|
137
|
-
if (!board.device.gatt) {
|
|
138
|
-
console.error("GATT is not available on this device");
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
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);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
console.error(error);
|
|
149
|
-
}
|
|
150
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
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
|
-
};
|
package/build/devices/index.d.ts
DELETED
package/build/devices/index.js
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Device } from "./types";
|
|
2
|
-
export declare const Tindeq: Device;
|
|
3
|
-
export declare const Commands: {
|
|
4
|
-
TARE_SCALE: number;
|
|
5
|
-
START_MEASURING: number;
|
|
6
|
-
STOP_MEASURING: number;
|
|
7
|
-
GET_APP_VERSION: number;
|
|
8
|
-
GET_ERROR_INFO: number;
|
|
9
|
-
CLEAR_ERR_INFO: number;
|
|
10
|
-
GET_BATTERY_LEVEL: number;
|
|
11
|
-
SLEEP: number;
|
|
12
|
-
};
|
|
13
|
-
export declare const NotificationTypes: {
|
|
14
|
-
COMMAND_RESPONSE: number;
|
|
15
|
-
WEIGHT_MEASURE: number;
|
|
16
|
-
LOW_BATTERY_WARNING: number;
|
|
17
|
-
};
|
package/build/devices/tindeq.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export const Tindeq = {
|
|
2
|
-
name: "Tindeq",
|
|
3
|
-
services: [
|
|
4
|
-
{
|
|
5
|
-
name: "Progressor Service",
|
|
6
|
-
id: "progressor",
|
|
7
|
-
uuid: "7e4e1701-1ea6-40c9-9dcc-13d34ffead57",
|
|
8
|
-
characteristics: [
|
|
9
|
-
{
|
|
10
|
-
name: "Write",
|
|
11
|
-
id: "tx",
|
|
12
|
-
uuid: "7e4e1703-1ea6-40c9-9dcc-13d34ffead57",
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
name: "Notify",
|
|
16
|
-
id: "rx",
|
|
17
|
-
uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57",
|
|
18
|
-
},
|
|
19
|
-
],
|
|
20
|
-
},
|
|
21
|
-
],
|
|
22
|
-
};
|
|
23
|
-
export const Commands = {
|
|
24
|
-
TARE_SCALE: 0x64,
|
|
25
|
-
START_MEASURING: 0x65,
|
|
26
|
-
STOP_MEASURING: 0x66,
|
|
27
|
-
GET_APP_VERSION: 0x6b,
|
|
28
|
-
GET_ERROR_INFO: 0x6c,
|
|
29
|
-
CLEAR_ERR_INFO: 0x6d,
|
|
30
|
-
GET_BATTERY_LEVEL: 0x6f,
|
|
31
|
-
SLEEP: 0x6e,
|
|
32
|
-
};
|
|
33
|
-
export const NotificationTypes = {
|
|
34
|
-
COMMAND_RESPONSE: 0,
|
|
35
|
-
WEIGHT_MEASURE: 1,
|
|
36
|
-
LOW_BATTERY_WARNING: 2,
|
|
37
|
-
};
|
package/build/devices/types.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/// <reference types="web-bluetooth" />
|
|
2
|
-
interface Characteristic {
|
|
3
|
-
name: string;
|
|
4
|
-
id: string;
|
|
5
|
-
uuid: string;
|
|
6
|
-
characteristic?: BluetoothRemoteGATTCharacteristic;
|
|
7
|
-
}
|
|
8
|
-
interface Service {
|
|
9
|
-
name: string;
|
|
10
|
-
id: string;
|
|
11
|
-
uuid: string;
|
|
12
|
-
characteristics: Characteristic[];
|
|
13
|
-
}
|
|
14
|
-
export interface Device {
|
|
15
|
-
name: string;
|
|
16
|
-
companyId?: number;
|
|
17
|
-
services: Service[];
|
|
18
|
-
device?: BluetoothDevice;
|
|
19
|
-
}
|
|
20
|
-
export {};
|
package/build/devices/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/build/disconnect.d.ts
DELETED
package/build/disconnect.js
DELETED
package/build/index.d.ts
DELETED
package/build/index.js
DELETED
package/build/notify.d.ts
DELETED
package/build/notify.js
DELETED
package/build/read.d.ts
DELETED
package/build/read.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { notifyCallback } from "./notify";
|
|
2
|
-
import { getCharacteristic } from "./characteristic";
|
|
3
|
-
/**
|
|
4
|
-
* read
|
|
5
|
-
* @param characteristic
|
|
6
|
-
*/
|
|
7
|
-
export const read = (board, serviceId, characteristicId, duration = 0) => {
|
|
8
|
-
return new Promise((resolve, reject) => {
|
|
9
|
-
if (board.device?.gatt?.connected) {
|
|
10
|
-
const characteristic = getCharacteristic(board, serviceId, characteristicId);
|
|
11
|
-
if (characteristic) {
|
|
12
|
-
characteristic
|
|
13
|
-
.readValue()
|
|
14
|
-
.then((value) => {
|
|
15
|
-
let decodedValue;
|
|
16
|
-
const decoder = new TextDecoder("utf-8");
|
|
17
|
-
switch (characteristicId) {
|
|
18
|
-
case "level":
|
|
19
|
-
decodedValue = value.getUint8(0);
|
|
20
|
-
break;
|
|
21
|
-
default:
|
|
22
|
-
decodedValue = decoder.decode(value);
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
if (notifyCallback) {
|
|
26
|
-
notifyCallback({ uuid: characteristic.uuid, value: decodedValue });
|
|
27
|
-
}
|
|
28
|
-
setTimeout(() => {
|
|
29
|
-
resolve();
|
|
30
|
-
}, duration);
|
|
31
|
-
})
|
|
32
|
-
.catch((error) => {
|
|
33
|
-
reject(error);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
reject(new Error("Characteristic is undefined"));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
reject(new Error("Device is not connected"));
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
};
|
package/build/write.d.ts
DELETED
package/build/write.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { getCharacteristic } from "./characteristic";
|
|
2
|
-
/**
|
|
3
|
-
* write
|
|
4
|
-
* @param characteristic
|
|
5
|
-
* @param message
|
|
6
|
-
*/
|
|
7
|
-
export const write = (board, serviceId, characteristicId, message, duration = 0) => {
|
|
8
|
-
return new Promise((resolve, reject) => {
|
|
9
|
-
if (board.device?.gatt?.connected) {
|
|
10
|
-
const encoder = new TextEncoder();
|
|
11
|
-
const characteristic = getCharacteristic(board, serviceId, characteristicId);
|
|
12
|
-
if (characteristic) {
|
|
13
|
-
const value = message + "\n";
|
|
14
|
-
characteristic
|
|
15
|
-
.writeValue(encoder.encode(value))
|
|
16
|
-
.then(() => {
|
|
17
|
-
setTimeout(() => {
|
|
18
|
-
resolve();
|
|
19
|
-
}, duration);
|
|
20
|
-
})
|
|
21
|
-
.catch((error) => {
|
|
22
|
-
reject(error);
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
reject(new Error("Characteristics is undefined"));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
reject(new Error("Device is not connected"));
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
};
|