@hangtime/grip-connect 0.1.1 → 0.2.0
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 +14 -20
- package/package.json +1 -1
- package/src/battery.d.ts +6 -0
- package/src/battery.js +19 -0
- package/src/battery.ts +21 -0
- package/src/calibration.js +3 -4
- package/src/calibration.ts +3 -3
- package/src/commands/tindeq.js +12 -12
- package/src/connect.js +11 -5
- package/src/connect.ts +10 -4
- package/src/data.d.ts +8 -0
- package/src/data.js +109 -0
- package/src/data.ts +133 -0
- package/src/devices/entralpi.d.ts +0 -6
- package/src/devices/entralpi.js +0 -16
- package/src/devices/entralpi.ts +0 -17
- package/src/devices/motherboard.d.ts +0 -6
- package/src/devices/motherboard.js +0 -110
- package/src/devices/motherboard.ts +0 -134
- package/src/disconnect.js +3 -4
- package/src/disconnect.ts +3 -3
- package/src/index.d.ts +3 -1
- package/src/index.js +5 -1
- package/src/index.ts +9 -1
- package/src/info.d.ts +6 -0
- package/src/info.js +23 -0
- package/src/info.ts +25 -0
- package/src/is-connected.d.ts +7 -0
- package/src/is-connected.js +10 -0
- package/src/is-connected.ts +11 -0
- package/src/notify.js +1 -1
- package/src/notify.ts +2 -2
- package/src/read.js +2 -1
- package/src/read.ts +2 -1
- package/src/stop.js +3 -4
- package/src/stop.ts +3 -3
- package/src/stream.js +12 -8
- package/src/stream.ts +12 -7
- package/src/write.js +7 -4
- package/src/write.ts +8 -4
- package/tsconfig.json +2 -2
package/README.md
CHANGED
|
@@ -13,30 +13,29 @@ Force-Sensing Hangboards / Plates used by climbers for strength measurement. Exa
|
|
|
13
13
|
|
|
14
14
|
## Roadmap
|
|
15
15
|
|
|
16
|
+
**Help wanted:** Do you own any of these devices? Use Google Chrome's Bluetooth Internals
|
|
17
|
+
`chrome://bluetooth-internals/#devices` and press `Start Scan` to look for your device, click on `Inspect` and share all
|
|
18
|
+
available services with us.
|
|
19
|
+
|
|
16
20
|
- ✅ Griptonite Motherboard
|
|
17
21
|
- ✅️ Connect with devices
|
|
18
22
|
- ✅️ Read / Write / Notify using Bluetooth
|
|
19
|
-
- ➡️ Calibrate Devices
|
|
20
23
|
- ✅️ Output weight/force stream
|
|
21
24
|
- ✅ Tindeq Progressor
|
|
22
25
|
- ✅️ Connect with devices
|
|
23
26
|
- ✅️ Read / Write / Notify using Bluetooth
|
|
24
|
-
-
|
|
25
|
-
- ➡️ Output weight/force stream
|
|
27
|
+
- ✅️ Output weight/force stream
|
|
26
28
|
- ✅ Entralpi
|
|
27
29
|
- ✅️ Connect with devices
|
|
28
30
|
- ✅️ Read / Write / Notify using Bluetooth
|
|
29
|
-
-
|
|
30
|
-
- ➡️ Output weight/force stream
|
|
31
|
+
- ✅️️ Output weight/force stream
|
|
31
32
|
- ➡️ Climbro
|
|
32
33
|
- ➡️ Connect with devices
|
|
33
34
|
- ➡️ Read / Write / Notify using Bluetooth
|
|
34
|
-
- ➡️ Calibrate Devices
|
|
35
35
|
- ➡️ Output weight/force stream
|
|
36
36
|
- ➡️ SmartBoard
|
|
37
37
|
- ➡️ Connect with devices
|
|
38
38
|
- ➡️ Read / Write / Notify using Bluetooth
|
|
39
|
-
- ➡️ Calibrate Devices
|
|
40
39
|
- ➡️ Output weight/force stream
|
|
41
40
|
|
|
42
41
|
## Development
|
|
@@ -53,37 +52,32 @@ npm install
|
|
|
53
52
|
$ npm install @hangtime/grip-connect
|
|
54
53
|
```
|
|
55
54
|
|
|
56
|
-
## Example usage (Motherboard)
|
|
55
|
+
## Example usage (with a Motherboard)
|
|
57
56
|
|
|
58
|
-
Simply importing the utilities you need from `@hangtime/grip-connect`.
|
|
59
|
-
`Motherboard`, `Tindeq` and `Entralpi`.
|
|
57
|
+
Simply importing the utilities you need from `@hangtime/grip-connect`.
|
|
60
58
|
|
|
61
59
|
```html
|
|
62
60
|
<button id="motherboard" type="button">Connect Motherboard</button>
|
|
63
61
|
```
|
|
64
62
|
|
|
65
63
|
```js
|
|
66
|
-
import { Motherboard,
|
|
64
|
+
import { Motherboard, battery, connect, disconnect, info, notify, stream } from "@hangtime/grip-connect"
|
|
67
65
|
|
|
68
66
|
const motherboardButton = document.querySelector("#motherboard")
|
|
69
67
|
|
|
70
68
|
motherboardButton.addEventListener("click", () => {
|
|
71
69
|
connect(Motherboard, async () => {
|
|
72
|
-
// Listen for notifications
|
|
70
|
+
// Listen for stream notifications
|
|
73
71
|
notify((data) => {
|
|
72
|
+
// data: { massTotal: 0, massLeft: 0, massRight: 0, massCenter: 0 }
|
|
74
73
|
console.log(data)
|
|
75
74
|
})
|
|
76
75
|
|
|
77
76
|
// Read battery + device info
|
|
78
|
-
await
|
|
79
|
-
await
|
|
80
|
-
await read(Motherboard, "device", "hardware", 250)
|
|
81
|
-
await read(Motherboard, "device", "firmware", 250)
|
|
82
|
-
|
|
83
|
-
// Read calibration (required before reading data)
|
|
84
|
-
await calibration(Motherboard)
|
|
77
|
+
await battery(Motherboard)
|
|
78
|
+
await info(Motherboard)
|
|
85
79
|
|
|
86
|
-
// Start streaming (for a minute) remove parameter for a continues stream
|
|
80
|
+
// Start weight streaming (for a minute) remove parameter for a continues stream
|
|
87
81
|
await stream(Motherboard, 60000)
|
|
88
82
|
|
|
89
83
|
// Manually call stop method if stream is continues
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hangtime/grip-connect",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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": {
|
package/src/battery.d.ts
ADDED
package/src/battery.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { write } from "./write";
|
|
2
|
+
import { read } from "./read";
|
|
3
|
+
import { isConnected } from "./is-connected";
|
|
4
|
+
import { Motherboard, Tindeq } from "./devices";
|
|
5
|
+
import { TindeqCommands } from "./commands";
|
|
6
|
+
/**
|
|
7
|
+
* Get Battery / Voltage information
|
|
8
|
+
* @param board
|
|
9
|
+
*/
|
|
10
|
+
export const battery = async (board) => {
|
|
11
|
+
if (isConnected(board)) {
|
|
12
|
+
if (board.name === "Motherboard") {
|
|
13
|
+
await read(Motherboard, "battery", "level", 250);
|
|
14
|
+
}
|
|
15
|
+
if (board.name === "Tindeq") {
|
|
16
|
+
await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_BATT_VLTG), 250);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
package/src/battery.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Device } from "./devices/types"
|
|
2
|
+
import { write } from "./write"
|
|
3
|
+
import { read } from "./read"
|
|
4
|
+
import { isConnected } from "./is-connected"
|
|
5
|
+
import { Motherboard, Tindeq } from "./devices"
|
|
6
|
+
import { TindeqCommands } from "./commands"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get Battery / Voltage information
|
|
10
|
+
* @param board
|
|
11
|
+
*/
|
|
12
|
+
export const battery = async (board: Device): Promise<void> => {
|
|
13
|
+
if (isConnected(board)) {
|
|
14
|
+
if (board.name === "Motherboard") {
|
|
15
|
+
await read(Motherboard, "battery", "level", 250)
|
|
16
|
+
}
|
|
17
|
+
if (board.name === "Tindeq") {
|
|
18
|
+
await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_BATT_VLTG), 250)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/calibration.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
+
import { isConnected } from "./is-connected";
|
|
2
|
+
import { write } from "./write";
|
|
1
3
|
import { Motherboard } from "./devices";
|
|
2
4
|
import { MotherboardCommands } from "./commands";
|
|
3
|
-
import { write } from "./write";
|
|
4
5
|
/**
|
|
5
6
|
* write command to get calibration
|
|
6
7
|
* @param board
|
|
7
8
|
*/
|
|
8
9
|
export const calibration = async (board) => {
|
|
9
|
-
if (
|
|
10
|
-
return;
|
|
11
|
-
if (board.device.gatt?.connected) {
|
|
10
|
+
if (isConnected(board)) {
|
|
12
11
|
if (board.name === "Motherboard") {
|
|
13
12
|
await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_CALIBRATION), 2500);
|
|
14
13
|
}
|
package/src/calibration.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Device } from "./devices/types"
|
|
2
|
+
import { isConnected } from "./is-connected"
|
|
3
|
+
import { write } from "./write"
|
|
2
4
|
import { Motherboard } from "./devices"
|
|
3
5
|
import { MotherboardCommands } from "./commands"
|
|
4
|
-
import { write } from "./write"
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* write command to get calibration
|
|
8
9
|
* @param board
|
|
9
10
|
*/
|
|
10
11
|
export const calibration = async (board: Device): Promise<void> => {
|
|
11
|
-
if (
|
|
12
|
-
if (board.device.gatt?.connected) {
|
|
12
|
+
if (isConnected(board)) {
|
|
13
13
|
if (board.name === "Motherboard") {
|
|
14
14
|
await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_CALIBRATION), 2500)
|
|
15
15
|
}
|
package/src/commands/tindeq.js
CHANGED
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
* Using other commands can seriously harm your device
|
|
4
4
|
*/
|
|
5
5
|
export const TindeqCommands = {
|
|
6
|
-
TARE_SCALE:
|
|
7
|
-
START_WEIGHT_MEAS:
|
|
8
|
-
STOP_WEIGHT_MEAS:
|
|
9
|
-
START_PEAK_RFD_MEAS:
|
|
10
|
-
START_PEAK_RFD_MEAS_SERIES:
|
|
11
|
-
ADD_CALIB_POINT:
|
|
12
|
-
SAVE_CALIB:
|
|
13
|
-
GET_APP_VERSION:
|
|
14
|
-
GET_ERR_INFO:
|
|
15
|
-
CLR_ERR_INFO:
|
|
16
|
-
SLEEP:
|
|
17
|
-
GET_BATT_VLTG:
|
|
6
|
+
TARE_SCALE: "d", // 0x64,
|
|
7
|
+
START_WEIGHT_MEAS: "e", // 0x65,
|
|
8
|
+
STOP_WEIGHT_MEAS: "f", // 0x66,
|
|
9
|
+
START_PEAK_RFD_MEAS: "g", // 0x67,
|
|
10
|
+
START_PEAK_RFD_MEAS_SERIES: "h", // 0x68,
|
|
11
|
+
ADD_CALIB_POINT: "i", // 0x69,
|
|
12
|
+
SAVE_CALIB: "j", // 0x6a,
|
|
13
|
+
GET_APP_VERSION: "k", // 0x6b,
|
|
14
|
+
GET_ERR_INFO: "l", // 0x6c,
|
|
15
|
+
CLR_ERR_INFO: "m", // 0x6d,
|
|
16
|
+
SLEEP: "n", // 0x6e,
|
|
17
|
+
GET_BATT_VLTG: "o", // 0x6f,
|
|
18
18
|
};
|
|
19
19
|
export const NotificationTypes = {
|
|
20
20
|
COMMAND_RESPONSE: 0,
|
package/src/connect.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { notifyCallback } from "./notify";
|
|
2
|
-
import { handleMotherboardData } from "./
|
|
3
|
-
import { handleEntralpiData } from "./devices/entralpi";
|
|
2
|
+
import { handleMotherboardData } from "./data";
|
|
4
3
|
let server;
|
|
5
4
|
const receiveBuffer = [];
|
|
6
5
|
/**
|
|
@@ -41,7 +40,14 @@ const handleNotifications = (event, board) => {
|
|
|
41
40
|
const buffer = value.buffer;
|
|
42
41
|
const rawData = new DataView(buffer);
|
|
43
42
|
const receivedData = rawData.getUint16(0) / 100;
|
|
44
|
-
|
|
43
|
+
if (notifyCallback) {
|
|
44
|
+
notifyCallback({
|
|
45
|
+
uuid: characteristic.uuid,
|
|
46
|
+
value: {
|
|
47
|
+
massTotal: receivedData,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
45
51
|
}
|
|
46
52
|
}
|
|
47
53
|
else if (board.name === "Tindeq") {
|
|
@@ -102,9 +108,9 @@ const onConnected = async (board, onSuccess) => {
|
|
|
102
108
|
* Return all service UUIDs
|
|
103
109
|
* @param device
|
|
104
110
|
*/
|
|
105
|
-
|
|
111
|
+
const getAllServiceUUIDs = (device) => {
|
|
106
112
|
return device.services.map((service) => service.uuid);
|
|
107
|
-
}
|
|
113
|
+
};
|
|
108
114
|
/**
|
|
109
115
|
* Connect to the BluetoothDevice
|
|
110
116
|
* @param device
|
package/src/connect.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Device } from "./devices/types"
|
|
2
2
|
import { notifyCallback } from "./notify"
|
|
3
|
-
import { handleMotherboardData } from "./
|
|
4
|
-
import { handleEntralpiData } from "./devices/entralpi"
|
|
3
|
+
import { handleMotherboardData } from "./data"
|
|
5
4
|
|
|
6
5
|
let server: BluetoothRemoteGATTServer
|
|
7
6
|
const receiveBuffer: number[] = []
|
|
@@ -43,7 +42,14 @@ const handleNotifications = (event: Event, board: Device): void => {
|
|
|
43
42
|
const buffer: ArrayBuffer = value.buffer
|
|
44
43
|
const rawData: DataView = new DataView(buffer)
|
|
45
44
|
const receivedData: number = rawData.getUint16(0) / 100
|
|
46
|
-
|
|
45
|
+
if (notifyCallback) {
|
|
46
|
+
notifyCallback({
|
|
47
|
+
uuid: characteristic.uuid,
|
|
48
|
+
value: {
|
|
49
|
+
massTotal: receivedData,
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
}
|
|
47
53
|
}
|
|
48
54
|
} else if (board.name === "Tindeq") {
|
|
49
55
|
// TODO: handle Tindeq notify
|
|
@@ -110,7 +116,7 @@ const onConnected = async (board: Device, onSuccess: () => void): Promise<void>
|
|
|
110
116
|
* Return all service UUIDs
|
|
111
117
|
* @param device
|
|
112
118
|
*/
|
|
113
|
-
|
|
119
|
+
const getAllServiceUUIDs = (device: Device) => {
|
|
114
120
|
return device.services.map((service) => service.uuid)
|
|
115
121
|
}
|
|
116
122
|
/**
|
package/src/data.d.ts
ADDED
package/src/data.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { notifyCallback } from "./notify";
|
|
2
|
+
const PACKET_LENGTH = 32;
|
|
3
|
+
const NUM_SAMPLES = 3;
|
|
4
|
+
export const CALIBRATION = [[], [], [], []];
|
|
5
|
+
/**
|
|
6
|
+
* applyCalibration
|
|
7
|
+
* @param sample
|
|
8
|
+
* @param calibration
|
|
9
|
+
*/
|
|
10
|
+
const applyCalibration = (sample, calibration) => {
|
|
11
|
+
// Extract the calibrated value for the zero point
|
|
12
|
+
const zeroCalibration = calibration[0][2];
|
|
13
|
+
// Initialize sign as positive
|
|
14
|
+
let sign = 1;
|
|
15
|
+
// Initialize the final calibrated value
|
|
16
|
+
let final = 0;
|
|
17
|
+
// If the sample value is less than the zero calibration point
|
|
18
|
+
if (sample < zeroCalibration) {
|
|
19
|
+
// Change the sign to negative
|
|
20
|
+
sign = -1;
|
|
21
|
+
// Reflect the sample around the zero calibration point
|
|
22
|
+
sample = /* 2 * zeroCalibration */ -sample;
|
|
23
|
+
}
|
|
24
|
+
// Iterate through the calibration data
|
|
25
|
+
for (let i = 1; i < calibration.length; i++) {
|
|
26
|
+
// Extract the lower and upper bounds of the current calibration range
|
|
27
|
+
const calibrationStart = calibration[i - 1][2];
|
|
28
|
+
const calibrationEnd = calibration[i][2];
|
|
29
|
+
// If the sample value is within the current calibration range
|
|
30
|
+
if (sample < calibrationEnd) {
|
|
31
|
+
// Interpolate to get the calibrated value within the range
|
|
32
|
+
final =
|
|
33
|
+
calibration[i - 1][1] +
|
|
34
|
+
((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
|
|
35
|
+
(calibration[i][1] - calibration[i - 1][1]);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Return the calibrated value with the appropriate sign (positive/negative)
|
|
40
|
+
return sign * final;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* handleMotherboardData
|
|
44
|
+
*
|
|
45
|
+
* @param uuid - Unique identifier
|
|
46
|
+
* @param receivedData - Received data string
|
|
47
|
+
*/
|
|
48
|
+
export const handleMotherboardData = (uuid, receivedData) => {
|
|
49
|
+
const receivedTime = Date.now();
|
|
50
|
+
// Check if the line is entirely hex characters
|
|
51
|
+
const isAllHex = /^[0-9A-Fa-f]+$/g.test(receivedData);
|
|
52
|
+
// Handle streaming packet
|
|
53
|
+
if (isAllHex && receivedData.length === PACKET_LENGTH) {
|
|
54
|
+
// Base-16 decode the string: convert hex pairs to byte values
|
|
55
|
+
const bytes = Array.from({ length: receivedData.length / 2 }, (_, i) => Number(`0x${receivedData.substring(i * 2, i * 2 + 2)}`));
|
|
56
|
+
// Translate header into packet, number of samples from the packet length
|
|
57
|
+
const packet = {
|
|
58
|
+
received: receivedTime,
|
|
59
|
+
sampleNum: new DataView(new Uint8Array(bytes).buffer).getUint16(0, true),
|
|
60
|
+
battRaw: new DataView(new Uint8Array(bytes).buffer).getUint16(2, true),
|
|
61
|
+
samples: [],
|
|
62
|
+
masses: [],
|
|
63
|
+
};
|
|
64
|
+
const dataView = new DataView(new Uint8Array(bytes).buffer);
|
|
65
|
+
for (let i = 0; i < NUM_SAMPLES; i++) {
|
|
66
|
+
const sampleStart = 4 + 3 * i;
|
|
67
|
+
// Use DataView to read the 24-bit unsigned integer
|
|
68
|
+
const rawValue = dataView.getUint8(sampleStart) |
|
|
69
|
+
(dataView.getUint8(sampleStart + 1) << 8) |
|
|
70
|
+
(dataView.getUint8(sampleStart + 2) << 16);
|
|
71
|
+
// Ensure unsigned 32-bit integer
|
|
72
|
+
packet.samples[i] = rawValue >>> 0;
|
|
73
|
+
if (packet.samples[i] >= 0x7fffff) {
|
|
74
|
+
packet.samples[i] -= 0x1000000;
|
|
75
|
+
}
|
|
76
|
+
// if (!CALIBRATION[0].length) return
|
|
77
|
+
packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i]);
|
|
78
|
+
}
|
|
79
|
+
// invert center and right values
|
|
80
|
+
packet.masses[1] *= -1;
|
|
81
|
+
packet.masses[2] *= -1;
|
|
82
|
+
// map to variables
|
|
83
|
+
const left = packet.masses[0];
|
|
84
|
+
const center = packet.masses[1];
|
|
85
|
+
const right = packet.masses[2];
|
|
86
|
+
if (notifyCallback) {
|
|
87
|
+
notifyCallback({
|
|
88
|
+
uuid,
|
|
89
|
+
value: {
|
|
90
|
+
massTotal: Math.max(-1000, left + right + center).toFixed(1),
|
|
91
|
+
massLeft: Math.max(-1000, left).toFixed(1),
|
|
92
|
+
massRight: Math.max(-1000, right).toFixed(1),
|
|
93
|
+
massCenter: Math.max(-1000, center).toFixed(1),
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else if ((receivedData.match(/,/g) || []).length === 3) {
|
|
99
|
+
console.log(receivedData);
|
|
100
|
+
// if the returned notification is a calibration string add them to the array
|
|
101
|
+
const parts = receivedData.split(",");
|
|
102
|
+
const numericParts = parts.map((x) => parseFloat(x));
|
|
103
|
+
CALIBRATION[numericParts[0]].push(numericParts.slice(1));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// unhanded data
|
|
107
|
+
console.log(receivedData);
|
|
108
|
+
}
|
|
109
|
+
};
|
package/src/data.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { notifyCallback } from "./notify"
|
|
2
|
+
|
|
3
|
+
const PACKET_LENGTH: number = 32
|
|
4
|
+
const NUM_SAMPLES: number = 3
|
|
5
|
+
export const CALIBRATION = [[], [], [], []]
|
|
6
|
+
/**
|
|
7
|
+
* applyCalibration
|
|
8
|
+
* @param sample
|
|
9
|
+
* @param calibration
|
|
10
|
+
*/
|
|
11
|
+
const applyCalibration = (sample: number, calibration: number[][]): number => {
|
|
12
|
+
// Extract the calibrated value for the zero point
|
|
13
|
+
const zeroCalibration: number = calibration[0][2]
|
|
14
|
+
|
|
15
|
+
// Initialize sign as positive
|
|
16
|
+
let sign: number = 1
|
|
17
|
+
|
|
18
|
+
// Initialize the final calibrated value
|
|
19
|
+
let final: number = 0
|
|
20
|
+
|
|
21
|
+
// If the sample value is less than the zero calibration point
|
|
22
|
+
if (sample < zeroCalibration) {
|
|
23
|
+
// Change the sign to negative
|
|
24
|
+
sign = -1
|
|
25
|
+
|
|
26
|
+
// Reflect the sample around the zero calibration point
|
|
27
|
+
sample = /* 2 * zeroCalibration */ -sample
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Iterate through the calibration data
|
|
31
|
+
for (let i = 1; i < calibration.length; i++) {
|
|
32
|
+
// Extract the lower and upper bounds of the current calibration range
|
|
33
|
+
const calibrationStart: number = calibration[i - 1][2]
|
|
34
|
+
const calibrationEnd: number = calibration[i][2]
|
|
35
|
+
|
|
36
|
+
// If the sample value is within the current calibration range
|
|
37
|
+
if (sample < calibrationEnd) {
|
|
38
|
+
// Interpolate to get the calibrated value within the range
|
|
39
|
+
final =
|
|
40
|
+
calibration[i - 1][1] +
|
|
41
|
+
((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
|
|
42
|
+
(calibration[i][1] - calibration[i - 1][1])
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Return the calibrated value with the appropriate sign (positive/negative)
|
|
47
|
+
return sign * final
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface Packet {
|
|
51
|
+
received: number
|
|
52
|
+
sampleNum: number
|
|
53
|
+
battRaw: number
|
|
54
|
+
samples: number[]
|
|
55
|
+
masses: number[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* handleMotherboardData
|
|
60
|
+
*
|
|
61
|
+
* @param uuid - Unique identifier
|
|
62
|
+
* @param receivedData - Received data string
|
|
63
|
+
*/
|
|
64
|
+
export const handleMotherboardData = (uuid: string, receivedData: string): void => {
|
|
65
|
+
const receivedTime: number = Date.now()
|
|
66
|
+
|
|
67
|
+
// Check if the line is entirely hex characters
|
|
68
|
+
const isAllHex: boolean = /^[0-9A-Fa-f]+$/g.test(receivedData)
|
|
69
|
+
|
|
70
|
+
// Handle streaming packet
|
|
71
|
+
if (isAllHex && receivedData.length === PACKET_LENGTH) {
|
|
72
|
+
// Base-16 decode the string: convert hex pairs to byte values
|
|
73
|
+
const bytes: number[] = Array.from({ length: receivedData.length / 2 }, (_, i) =>
|
|
74
|
+
Number(`0x${receivedData.substring(i * 2, i * 2 + 2)}`),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
// Translate header into packet, number of samples from the packet length
|
|
78
|
+
const packet: Packet = {
|
|
79
|
+
received: receivedTime,
|
|
80
|
+
sampleNum: new DataView(new Uint8Array(bytes).buffer).getUint16(0, true),
|
|
81
|
+
battRaw: new DataView(new Uint8Array(bytes).buffer).getUint16(2, true),
|
|
82
|
+
samples: [],
|
|
83
|
+
masses: [],
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const dataView = new DataView(new Uint8Array(bytes).buffer)
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < NUM_SAMPLES; i++) {
|
|
89
|
+
const sampleStart: number = 4 + 3 * i
|
|
90
|
+
// Use DataView to read the 24-bit unsigned integer
|
|
91
|
+
const rawValue =
|
|
92
|
+
dataView.getUint8(sampleStart) |
|
|
93
|
+
(dataView.getUint8(sampleStart + 1) << 8) |
|
|
94
|
+
(dataView.getUint8(sampleStart + 2) << 16)
|
|
95
|
+
|
|
96
|
+
// Ensure unsigned 32-bit integer
|
|
97
|
+
packet.samples[i] = rawValue >>> 0
|
|
98
|
+
|
|
99
|
+
if (packet.samples[i] >= 0x7fffff) {
|
|
100
|
+
packet.samples[i] -= 0x1000000
|
|
101
|
+
}
|
|
102
|
+
// if (!CALIBRATION[0].length) return
|
|
103
|
+
packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i])
|
|
104
|
+
}
|
|
105
|
+
// invert center and right values
|
|
106
|
+
packet.masses[1] *= -1
|
|
107
|
+
packet.masses[2] *= -1
|
|
108
|
+
// map to variables
|
|
109
|
+
const left: number = packet.masses[0]
|
|
110
|
+
const center: number = packet.masses[1]
|
|
111
|
+
const right: number = packet.masses[2]
|
|
112
|
+
if (notifyCallback) {
|
|
113
|
+
notifyCallback({
|
|
114
|
+
uuid,
|
|
115
|
+
value: {
|
|
116
|
+
massTotal: Math.max(-1000, left + right + center).toFixed(1),
|
|
117
|
+
massLeft: Math.max(-1000, left).toFixed(1),
|
|
118
|
+
massRight: Math.max(-1000, right).toFixed(1),
|
|
119
|
+
massCenter: Math.max(-1000, center).toFixed(1),
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
} else if ((receivedData.match(/,/g) || []).length === 3) {
|
|
124
|
+
console.log(receivedData)
|
|
125
|
+
// if the returned notification is a calibration string add them to the array
|
|
126
|
+
const parts: string[] = receivedData.split(",")
|
|
127
|
+
const numericParts: number[] = parts.map((x) => parseFloat(x))
|
|
128
|
+
;(CALIBRATION[numericParts[0]] as number[][]).push(numericParts.slice(1))
|
|
129
|
+
} else {
|
|
130
|
+
// unhanded data
|
|
131
|
+
console.log(receivedData)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -1,8 +1,2 @@
|
|
|
1
1
|
import { Device } from "./types";
|
|
2
2
|
export declare const Entralpi: Device;
|
|
3
|
-
/**
|
|
4
|
-
* handleEntralpiData
|
|
5
|
-
* @param uuid - Unique identifier
|
|
6
|
-
* @param receivedData - Received data string
|
|
7
|
-
*/
|
|
8
|
-
export declare function handleEntralpiData(uuid: string, receivedData: number): void;
|
package/src/devices/entralpi.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { notifyCallback } from "../notify";
|
|
2
1
|
export const Entralpi = {
|
|
3
2
|
name: "ENTRALPI",
|
|
4
3
|
services: [
|
|
@@ -51,18 +50,3 @@ export const Entralpi = {
|
|
|
51
50
|
},
|
|
52
51
|
],
|
|
53
52
|
};
|
|
54
|
-
/**
|
|
55
|
-
* handleEntralpiData
|
|
56
|
-
* @param uuid - Unique identifier
|
|
57
|
-
* @param receivedData - Received data string
|
|
58
|
-
*/
|
|
59
|
-
export function handleEntralpiData(uuid, receivedData) {
|
|
60
|
-
if (notifyCallback) {
|
|
61
|
-
notifyCallback({
|
|
62
|
-
uuid,
|
|
63
|
-
value: {
|
|
64
|
-
massTotal: receivedData,
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
package/src/devices/entralpi.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Device } from "./types"
|
|
2
|
-
import { notifyCallback } from "../notify"
|
|
3
2
|
|
|
4
3
|
export const Entralpi: Device = {
|
|
5
4
|
name: "ENTRALPI",
|
|
@@ -53,19 +52,3 @@ export const Entralpi: Device = {
|
|
|
53
52
|
},
|
|
54
53
|
],
|
|
55
54
|
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* handleEntralpiData
|
|
59
|
-
* @param uuid - Unique identifier
|
|
60
|
-
* @param receivedData - Received data string
|
|
61
|
-
*/
|
|
62
|
-
export function handleEntralpiData(uuid: string, receivedData: number): void {
|
|
63
|
-
if (notifyCallback) {
|
|
64
|
-
notifyCallback({
|
|
65
|
-
uuid,
|
|
66
|
-
value: {
|
|
67
|
-
massTotal: receivedData,
|
|
68
|
-
},
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1,8 +1,2 @@
|
|
|
1
1
|
import { Device } from "./types";
|
|
2
2
|
export declare const Motherboard: Device;
|
|
3
|
-
/**
|
|
4
|
-
* handleMotherboardData
|
|
5
|
-
* @param uuid - Unique identifier
|
|
6
|
-
* @param receivedData - Received data string
|
|
7
|
-
*/
|
|
8
|
-
export declare function handleMotherboardData(uuid: string, receivedData: string): void;
|