@depz/sensor-sdk 0.1.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 +69 -0
- package/dist/dataset.d.ts +93 -0
- package/dist/dataset.d.ts.map +1 -0
- package/dist/dataset.js +253 -0
- package/dist/dataset.js.map +1 -0
- package/dist/device/device.d.ts +185 -0
- package/dist/device/device.d.ts.map +1 -0
- package/dist/device/device.js +421 -0
- package/dist/device/device.js.map +1 -0
- package/dist/device/link.d.ts +62 -0
- package/dist/device/link.d.ts.map +1 -0
- package/dist/device/link.js +130 -0
- package/dist/device/link.js.map +1 -0
- package/dist/discovery.d.ts +109 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +281 -0
- package/dist/discovery.js.map +1 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/bno086.d.ts +36 -0
- package/dist/protocol/bno086.d.ts.map +1 -0
- package/dist/protocol/bno086.js +38 -0
- package/dist/protocol/bno086.js.map +1 -0
- package/dist/protocol/common.d.ts +103 -0
- package/dist/protocol/common.d.ts.map +1 -0
- package/dist/protocol/common.js +121 -0
- package/dist/protocol/common.js.map +1 -0
- package/dist/protocol/crc.d.ts +17 -0
- package/dist/protocol/crc.d.ts.map +1 -0
- package/dist/protocol/crc.js +55 -0
- package/dist/protocol/crc.js.map +1 -0
- package/dist/protocol/framing.d.ts +65 -0
- package/dist/protocol/framing.d.ts.map +1 -0
- package/dist/protocol/framing.js +152 -0
- package/dist/protocol/framing.js.map +1 -0
- package/dist/protocol/fwdepz.d.ts +21 -0
- package/dist/protocol/fwdepz.d.ts.map +1 -0
- package/dist/protocol/fwdepz.js +46 -0
- package/dist/protocol/fwdepz.js.map +1 -0
- package/dist/protocol/identity.d.ts +14 -0
- package/dist/protocol/identity.d.ts.map +1 -0
- package/dist/protocol/identity.js +28 -0
- package/dist/protocol/identity.js.map +1 -0
- package/dist/protocol/sr04.d.ts +38 -0
- package/dist/protocol/sr04.d.ts.map +1 -0
- package/dist/protocol/sr04.js +58 -0
- package/dist/protocol/sr04.js.map +1 -0
- package/dist/protocol/usb-ids.d.ts +60 -0
- package/dist/protocol/usb-ids.d.ts.map +1 -0
- package/dist/protocol/usb-ids.js +79 -0
- package/dist/protocol/usb-ids.js.map +1 -0
- package/dist/protocol/vl53l8.d.ts +59 -0
- package/dist/protocol/vl53l8.d.ts.map +1 -0
- package/dist/protocol/vl53l8.js +116 -0
- package/dist/protocol/vl53l8.js.map +1 -0
- package/dist/sensors/bno086/bno086.d.ts +197 -0
- package/dist/sensors/bno086/bno086.d.ts.map +1 -0
- package/dist/sensors/bno086/bno086.js +543 -0
- package/dist/sensors/bno086/bno086.js.map +1 -0
- package/dist/sensors/bno086/reports.d.ts +289 -0
- package/dist/sensors/bno086/reports.d.ts.map +1 -0
- package/dist/sensors/bno086/reports.js +397 -0
- package/dist/sensors/bno086/reports.js.map +1 -0
- package/dist/sensors/bno086/sh2.d.ts +307 -0
- package/dist/sensors/bno086/sh2.d.ts.map +1 -0
- package/dist/sensors/bno086/sh2.js +480 -0
- package/dist/sensors/bno086/sh2.js.map +1 -0
- package/dist/sensors/bno086/shtp.d.ts +98 -0
- package/dist/sensors/bno086/shtp.d.ts.map +1 -0
- package/dist/sensors/bno086/shtp.js +205 -0
- package/dist/sensors/bno086/shtp.js.map +1 -0
- package/dist/sensors/sr04.d.ts +65 -0
- package/dist/sensors/sr04.d.ts.map +1 -0
- package/dist/sensors/sr04.js +121 -0
- package/dist/sensors/sr04.js.map +1 -0
- package/dist/sensors/vl53l8/assets/ch.d.ts +16 -0
- package/dist/sensors/vl53l8/assets/ch.d.ts.map +1 -0
- package/dist/sensors/vl53l8/assets/ch.js +22 -0
- package/dist/sensors/vl53l8/assets/ch.js.map +1 -0
- package/dist/sensors/vl53l8/assets/cx.d.ts +16 -0
- package/dist/sensors/vl53l8/assets/cx.d.ts.map +1 -0
- package/dist/sensors/vl53l8/assets/cx.js +22 -0
- package/dist/sensors/vl53l8/assets/cx.js.map +1 -0
- package/dist/sensors/vl53l8/assets/decode.d.ts +7 -0
- package/dist/sensors/vl53l8/assets/decode.d.ts.map +1 -0
- package/dist/sensors/vl53l8/assets/decode.js +35 -0
- package/dist/sensors/vl53l8/assets/decode.js.map +1 -0
- package/dist/sensors/vl53l8/assets/index.d.ts +19 -0
- package/dist/sensors/vl53l8/assets/index.d.ts.map +1 -0
- package/dist/sensors/vl53l8/assets/index.js +11 -0
- package/dist/sensors/vl53l8/assets/index.js.map +1 -0
- package/dist/sensors/vl53l8/cnh.d.ts +113 -0
- package/dist/sensors/vl53l8/cnh.d.ts.map +1 -0
- package/dist/sensors/vl53l8/cnh.js +299 -0
- package/dist/sensors/vl53l8/cnh.js.map +1 -0
- package/dist/sensors/vl53l8/uld.d.ts +346 -0
- package/dist/sensors/vl53l8/uld.d.ts.map +1 -0
- package/dist/sensors/vl53l8/uld.js +1388 -0
- package/dist/sensors/vl53l8/uld.js.map +1 -0
- package/dist/sensors/vl53l8/vl53l8.d.ts +182 -0
- package/dist/sensors/vl53l8/vl53l8.d.ts.map +1 -0
- package/dist/sensors/vl53l8/vl53l8.js +411 -0
- package/dist/sensors/vl53l8/vl53l8.js.map +1 -0
- package/dist/transport/loopback.d.ts +18 -0
- package/dist/transport/loopback.d.ts.map +1 -0
- package/dist/transport/loopback.js +53 -0
- package/dist/transport/loopback.js.map +1 -0
- package/dist/transport/node.d.ts +51 -0
- package/dist/transport/node.d.ts.map +1 -0
- package/dist/transport/node.js +123 -0
- package/dist/transport/node.js.map +1 -0
- package/dist/transport/replay.d.ts +68 -0
- package/dist/transport/replay.d.ts.map +1 -0
- package/dist/transport/replay.js +196 -0
- package/dist/transport/replay.js.map +1 -0
- package/dist/transport/types.d.ts +23 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +7 -0
- package/dist/transport/types.js.map +1 -0
- package/dist/transport/webserial.d.ts +69 -0
- package/dist/transport/webserial.d.ts.map +1 -0
- package/dist/transport/webserial.js +196 -0
- package/dist/transport/webserial.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,1388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VL53L8CX ULD driver — TypeScript port of the parts of vl53l8cx_api.c
|
|
3
|
+
* (ULD 2.1.0, tools/doc/VL53L8CX_Linux_driver_2.1.0) needed for init (sensor
|
|
4
|
+
* firmware download), 8x8 continuous ranging and result parsing.
|
|
5
|
+
*
|
|
6
|
+
* 1:1 async mirror of the Python reference `depz_sensor_sdk.vl53l8.uld`
|
|
7
|
+
* (itself a proven port of the C driver). The sensor is reached through a
|
|
8
|
+
* `platform` object the caller provides — every method that touches it is
|
|
9
|
+
* async.
|
|
10
|
+
*
|
|
11
|
+
* Register sequences are a 1:1 port of the C code; do not "simplify" them.
|
|
12
|
+
* Configuration: NB_TARGET_PER_ZONE = 1, all output blocks enabled (matches
|
|
13
|
+
* the default platform.h of the C driver).
|
|
14
|
+
*/
|
|
15
|
+
// Sensor firmware variant. The register/DCI protocol is identical; only the
|
|
16
|
+
// downloaded sensor FW (and hence its checksum at 0x812FFC) and the NVM/config
|
|
17
|
+
// blobs differ. 'cx' = VL53L8CX ULD 2.1.0; 'ch' = VL53L7CH/VL53L8CH (VL53LMZ)
|
|
18
|
+
// ULD 2.0.16 — used to try running CH firmware on VL53L8CX silicon.
|
|
19
|
+
export const FW_CHECKSUM = {
|
|
20
|
+
cx: 0xcadf7caf,
|
|
21
|
+
ch: 0x0c0b6c9e,
|
|
22
|
+
};
|
|
23
|
+
export const RESOLUTION_4X4 = 16;
|
|
24
|
+
export const RESOLUTION_8X8 = 64;
|
|
25
|
+
export const RANGING_MODE_CONTINUOUS = 1;
|
|
26
|
+
export const RANGING_MODE_AUTONOMOUS = 3;
|
|
27
|
+
export const TARGET_ORDER_CLOSEST = 1;
|
|
28
|
+
export const TARGET_ORDER_STRONGEST = 2;
|
|
29
|
+
// Status codes (subset, matches C defines)
|
|
30
|
+
export const STATUS_OK = 0;
|
|
31
|
+
export const STATUS_TIMEOUT_ERROR = 1;
|
|
32
|
+
export const STATUS_CORRUPTED_FRAME = 2;
|
|
33
|
+
export const STATUS_LASER_SAFETY = 3;
|
|
34
|
+
export const STATUS_FW_CHECKSUM_FAIL = 5;
|
|
35
|
+
export const MCU_ERROR = 66;
|
|
36
|
+
export const STATUS_INVALID_PARAM = 127;
|
|
37
|
+
export const STATUS_ERROR = 255;
|
|
38
|
+
// Block headers for NB_TARGET_PER_ZONE == 1
|
|
39
|
+
export const START_BH = 0x0000000d;
|
|
40
|
+
export const METADATA_BH = 0x54b400c0;
|
|
41
|
+
export const COMMONDATA_BH = 0x54c00040;
|
|
42
|
+
export const AMBIENT_RATE_BH = 0x54d00104;
|
|
43
|
+
export const SPAD_COUNT_BH = 0x55d00404;
|
|
44
|
+
export const NB_TARGET_DETECTED_BH = 0xdb840401;
|
|
45
|
+
export const SIGNAL_RATE_BH = 0xdbc40404;
|
|
46
|
+
export const RANGE_SIGMA_MM_BH = 0xdec40402;
|
|
47
|
+
export const DISTANCE_BH = 0xdf440402;
|
|
48
|
+
export const REFLECTANCE_BH = 0xe0440401;
|
|
49
|
+
export const TARGET_STATUS_BH = 0xe0840401;
|
|
50
|
+
export const MOTION_DETECT_BH = 0xd85808c0;
|
|
51
|
+
/** VL53LMZ CNH data output block (VL53L8CH). */
|
|
52
|
+
export const CNH_DATA_IDX = 0xc048;
|
|
53
|
+
export const METADATA_IDX = 0x54b4;
|
|
54
|
+
export const SPAD_COUNT_IDX = 0x55d0;
|
|
55
|
+
export const AMBIENT_RATE_IDX = 0x54d0;
|
|
56
|
+
export const NB_TARGET_DETECTED_IDX = 0xdb84;
|
|
57
|
+
export const SIGNAL_RATE_IDX = 0xdbc4;
|
|
58
|
+
export const RANGE_SIGMA_MM_IDX = 0xdec4;
|
|
59
|
+
export const DISTANCE_IDX = 0xdf44;
|
|
60
|
+
export const REFLECTANCE_EST_PC_IDX = 0xe044;
|
|
61
|
+
export const TARGET_STATUS_IDX = 0xe084;
|
|
62
|
+
export const MOTION_DETEC_IDX = 0xd858;
|
|
63
|
+
export const NVM_DATA_SIZE = 492;
|
|
64
|
+
export const OFFSET_BUFFER_SIZE = 488;
|
|
65
|
+
export const XTALK_BUFFER_SIZE = 776;
|
|
66
|
+
export const DCI_ZONE_CONFIG = 0x5450;
|
|
67
|
+
export const DCI_FREQ_HZ = 0x5458;
|
|
68
|
+
export const DCI_INT_TIME = 0x545c;
|
|
69
|
+
export const DCI_RANGING_MODE = 0xad30;
|
|
70
|
+
export const DCI_DSS_CONFIG = 0xad38;
|
|
71
|
+
export const DCI_TARGET_ORDER = 0xae64;
|
|
72
|
+
export const DCI_SHARPENER = 0xaed8;
|
|
73
|
+
export const DCI_SINGLE_RANGE = 0xd964;
|
|
74
|
+
export const DCI_OUTPUT_CONFIG = 0xd968;
|
|
75
|
+
export const DCI_OUTPUT_ENABLES = 0xd970;
|
|
76
|
+
export const DCI_OUTPUT_LIST = 0xd980;
|
|
77
|
+
export const DCI_PIPE_CONTROL = 0xdb80;
|
|
78
|
+
export const UI_CMD_STATUS = 0x2c00;
|
|
79
|
+
export const UI_CMD_START = 0x2c04;
|
|
80
|
+
export const UI_CMD_END = 0x2fff;
|
|
81
|
+
// ── advanced-feature DCI indices / sizes (ST ULD vl53l8cx_api.h + plugins) ──
|
|
82
|
+
export const CONFIGURATION_SIZE = 972;
|
|
83
|
+
export const DCI_VHV_CONFIG = 0xad60;
|
|
84
|
+
export const DCI_CAL_CFG = 0x5470;
|
|
85
|
+
export const DCI_XTALK_CFG = 0xad94;
|
|
86
|
+
export const DCI_MOTION_DETECTOR_CFG = 0xbfac;
|
|
87
|
+
export const DCI_DET_THRESH_CONFIG = 0x5488;
|
|
88
|
+
export const DCI_DET_THRESH_GLOBAL_CONFIG = 0xb6e0;
|
|
89
|
+
export const DCI_DET_THRESH_START = 0xb6e8;
|
|
90
|
+
export const DCI_DET_THRESH_VALID_STATUS = 0xb9f0;
|
|
91
|
+
export const NB_THRESHOLDS = 64;
|
|
92
|
+
export const LAST_THRESHOLD = 128;
|
|
93
|
+
// Power modes (vl53l8cx_api.h)
|
|
94
|
+
export const POWER_MODE_SLEEP = 0;
|
|
95
|
+
export const POWER_MODE_WAKEUP = 1;
|
|
96
|
+
export const POWER_MODE_DEEP_SLEEP = 2;
|
|
97
|
+
// Detection-threshold measurement selectors + scale factors (plugin source):
|
|
98
|
+
// get divides, set multiplies by these.
|
|
99
|
+
export const DIST_MM = 1;
|
|
100
|
+
export const SIGNAL_PER_SPAD_KCPS = 2;
|
|
101
|
+
export const RANGE_SIGMA_MM = 4;
|
|
102
|
+
export const AMBIENT_PER_SPAD_KCPS = 8;
|
|
103
|
+
export const NB_TARGET_DETECTED = 9;
|
|
104
|
+
export const TAR_STATUS = 12;
|
|
105
|
+
export const NB_SPADS_ENABLED = 13;
|
|
106
|
+
export const MOTION_INDICATOR = 19;
|
|
107
|
+
const THRESH_SCALE = {
|
|
108
|
+
[DIST_MM]: 4,
|
|
109
|
+
[SIGNAL_PER_SPAD_KCPS]: 2048,
|
|
110
|
+
[RANGE_SIGMA_MM]: 128,
|
|
111
|
+
[AMBIENT_PER_SPAD_KCPS]: 2048,
|
|
112
|
+
[NB_SPADS_ENABLED]: 256,
|
|
113
|
+
[MOTION_INDICATOR]: 65535,
|
|
114
|
+
};
|
|
115
|
+
// Threshold `type` (window) selectors.
|
|
116
|
+
export const THRESH_IN_WINDOW = 0;
|
|
117
|
+
export const THRESH_OUT_OF_WINDOW = 1;
|
|
118
|
+
export const THRESH_LESS_THAN_EQUAL_MIN = 2;
|
|
119
|
+
export const THRESH_GREATER_THAN_MAX = 3;
|
|
120
|
+
export const THRESH_EQUAL_MIN = 4;
|
|
121
|
+
export const THRESH_NOT_EQUAL_MIN = 5;
|
|
122
|
+
// Threshold combine operation.
|
|
123
|
+
export const THRESH_OP_NONE = 0;
|
|
124
|
+
export const THRESH_OP_OR = 0;
|
|
125
|
+
export const THRESH_OP_AND = 2;
|
|
126
|
+
function fromHexConst(hex) {
|
|
127
|
+
const out = new Uint8Array(hex.length / 2);
|
|
128
|
+
for (let i = 0; i < out.length; i++)
|
|
129
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
// Const command tables lifted verbatim from vl53l8cx_plugin_xtalk.h (BSD-3).
|
|
133
|
+
export const GET_XTALK_CMD = fromHexConst("540000409fd800c09fe401409ff800409ffc0404a0fc0100a10c0100a11c00c0a1280902a2480040a24c0081a2540081a25c0081a2640081a26c0084a28c00820000000f07020044");
|
|
134
|
+
// VL53L8CX_CALIBRATE_XTALK config table (vl53l8cx_plugin_xtalk.h, BSD-3).
|
|
135
|
+
export const CALIBRATE_XTALK = fromHexConst("545000800004080800000404ad3000800301060300000100ad38010001e0014000100010010001000000000154580040041a0200545c01400001005100000fa00fa003e802801f400000050054700080032003200000000854780100011b0021003300000200000104010802548801400000000000000000000000000000000000000800ad48010001f40000030600100808080800000008ad6001000000008000000000201f01f400001d0aad70008008001f4000000001ad78008000a0032000010190ad80004000002800ad8400800000320003200000ad8c00800258ff380000000cad94010000019000fffffc000000040000000100ada400c00480061a0080058000000106adb000c00480061a1900058000000190adbc044000000000000000000012002500000006000000050000000500000006000000040000000f0000005a00000000000000090b0c0b0b030311050101010100000000000d0000ae00010400000004000000080000000a0000000c0000000d0000000e000000080000000800000010000000100000002000000020000000060000050a02000c0800000000ae400040000000ffae44004000100401ae48004000001000ae4c004000000001ae500140000000140400280003206c000000000000000000ae64004000000001aed8010000c805dc00000ccd0104000000012601b5500282a3e8a3b8a438a428a648a448a788a748ac10a79099bc99b49afc9abc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5a002820088030000820082040404080080040109020908040400800401040100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5f0004000040000b39c01004000051e021b087c8000120100010800b6c000c0000060000000200000000000aea8004000000405aeac00800100010000020000aeb4004000000000aeb800810000000000000000aec000810000000000000000aec800810801010800000008aed000810108080800000001b5f400800000000000000000b5fc00800000000000000000b604004000000000b608004400000000000000000000000000000000b618004400000000000000000000000000000000b628004400000000000000000000000000000000b638004400000000000000000000000000000000b648010000000000000000000000000000000000b658010000000000000000000000000000000000b6680100000000000000000000000000000000005470008000000000000000020000000f000103d4");
|
|
136
|
+
export const NB_TARGET_PER_ZONE = 1;
|
|
137
|
+
const STATUS_NAMES = {
|
|
138
|
+
[STATUS_TIMEOUT_ERROR]: "TIMEOUT",
|
|
139
|
+
[STATUS_CORRUPTED_FRAME]: "CORRUPTED_FRAME",
|
|
140
|
+
[STATUS_LASER_SAFETY]: "LASER_SAFETY",
|
|
141
|
+
[STATUS_FW_CHECKSUM_FAIL]: "FW_CHECKSUM_FAIL",
|
|
142
|
+
[MCU_ERROR]: "MCU_ERROR",
|
|
143
|
+
[STATUS_INVALID_PARAM]: "INVALID_PARAM",
|
|
144
|
+
[STATUS_ERROR]: "ERROR",
|
|
145
|
+
};
|
|
146
|
+
export class Vl53l8cxError extends Error {
|
|
147
|
+
code;
|
|
148
|
+
where;
|
|
149
|
+
constructor(code, where = "") {
|
|
150
|
+
const name = STATUS_NAMES[code] ?? `0x${code.toString(16)}`;
|
|
151
|
+
super(`VL53L8CX ${name} (code ${code}) ${where}`.trimEnd());
|
|
152
|
+
this.name = "Vl53l8cxError";
|
|
153
|
+
this.code = code;
|
|
154
|
+
this.where = where;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** VL53L8CX_SwapBuffer: byte-reverse every 32-bit word (tail untouched). */
|
|
158
|
+
export function swapBuffer(data) {
|
|
159
|
+
const out = data.slice();
|
|
160
|
+
const n4 = (data.length >>> 2) << 2;
|
|
161
|
+
for (let i = 0; i < n4; i += 4) {
|
|
162
|
+
out[i] = data[i + 3];
|
|
163
|
+
out[i + 1] = data[i + 2];
|
|
164
|
+
out[i + 2] = data[i + 1];
|
|
165
|
+
out[i + 3] = data[i];
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
/** union Block_header: type[3:0], size[15:4], idx[31:16]. */
|
|
170
|
+
export function bhFields(bh) {
|
|
171
|
+
return [bh & 0xf, (bh >>> 4) & 0xfff, (bh >>> 16) & 0xffff];
|
|
172
|
+
}
|
|
173
|
+
export function bhSetSize(bh, size) {
|
|
174
|
+
return ((bh & ~0xfff0) | ((size & 0xfff) << 4)) >>> 0;
|
|
175
|
+
}
|
|
176
|
+
function dvOf(buf) {
|
|
177
|
+
return new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
178
|
+
}
|
|
179
|
+
function packU32ArrayLE(values) {
|
|
180
|
+
const out = new Uint8Array(values.length * 4);
|
|
181
|
+
const dv = new DataView(out.buffer);
|
|
182
|
+
for (let i = 0; i < values.length; i++)
|
|
183
|
+
dv.setUint32(i * 4, values[i] >>> 0, true);
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
function hex4(n) {
|
|
187
|
+
return n.toString(16).toUpperCase().padStart(4, "0");
|
|
188
|
+
}
|
|
189
|
+
function hex8(n) {
|
|
190
|
+
return (n >>> 0).toString(16).toUpperCase().padStart(8, "0");
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* is_alive register sequence, standalone so the device facade can probe
|
|
194
|
+
* without constructing a full driver (which needs the blob assets).
|
|
195
|
+
* Returns [deviceId, revisionId]; alive when (0xF0, 0x0C).
|
|
196
|
+
*/
|
|
197
|
+
export async function readDeviceRevisionId(p) {
|
|
198
|
+
await p.wrMulti(0x7fff, Uint8Array.of(0x00));
|
|
199
|
+
const deviceId = (await p.rdMulti(0x00, 1))[0];
|
|
200
|
+
const revisionId = (await p.rdMulti(0x01, 1))[0];
|
|
201
|
+
await p.wrMulti(0x7fff, Uint8Array.of(0x02));
|
|
202
|
+
return [deviceId, revisionId];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Mirror of VL53L8CX_Motion_Configuration (156 bytes, plugin source).
|
|
206
|
+
* `pack()` reproduces the C struct byte layout (`<i3I12B64b32B32B`).
|
|
207
|
+
*/
|
|
208
|
+
export class MotionConfig {
|
|
209
|
+
refBinOffset = 0;
|
|
210
|
+
detectionThreshold = 0;
|
|
211
|
+
extraNoiseSigma = 0;
|
|
212
|
+
nullDenClipValue = 0;
|
|
213
|
+
memUpdateMode = 0;
|
|
214
|
+
memUpdateChoice = 0;
|
|
215
|
+
sumSpan = 0;
|
|
216
|
+
featureLength = 0;
|
|
217
|
+
nbOfAggregates = 0;
|
|
218
|
+
nbOfTemporalAccumulations = 0;
|
|
219
|
+
minNbForGlobalDetection = 0;
|
|
220
|
+
globalIndicatorFormat1 = 0;
|
|
221
|
+
globalIndicatorFormat2 = 0;
|
|
222
|
+
spare1 = 0;
|
|
223
|
+
spare2 = 0;
|
|
224
|
+
spare3 = 0;
|
|
225
|
+
mapId = new Array(64).fill(0);
|
|
226
|
+
indicatorFormat1 = new Array(32).fill(0);
|
|
227
|
+
indicatorFormat2 = new Array(32).fill(0);
|
|
228
|
+
pack() {
|
|
229
|
+
const out = new Uint8Array(156);
|
|
230
|
+
const dv = new DataView(out.buffer);
|
|
231
|
+
let o = 0;
|
|
232
|
+
dv.setInt32(o, this.refBinOffset | 0, true);
|
|
233
|
+
o += 4;
|
|
234
|
+
dv.setUint32(o, this.detectionThreshold >>> 0, true);
|
|
235
|
+
o += 4;
|
|
236
|
+
dv.setUint32(o, this.extraNoiseSigma >>> 0, true);
|
|
237
|
+
o += 4;
|
|
238
|
+
dv.setUint32(o, this.nullDenClipValue >>> 0, true);
|
|
239
|
+
o += 4;
|
|
240
|
+
const bytes12 = [
|
|
241
|
+
this.memUpdateMode,
|
|
242
|
+
this.memUpdateChoice,
|
|
243
|
+
this.sumSpan,
|
|
244
|
+
this.featureLength,
|
|
245
|
+
this.nbOfAggregates,
|
|
246
|
+
this.nbOfTemporalAccumulations,
|
|
247
|
+
this.minNbForGlobalDetection,
|
|
248
|
+
this.globalIndicatorFormat1,
|
|
249
|
+
this.globalIndicatorFormat2,
|
|
250
|
+
this.spare1,
|
|
251
|
+
this.spare2,
|
|
252
|
+
this.spare3,
|
|
253
|
+
];
|
|
254
|
+
for (const b of bytes12)
|
|
255
|
+
dv.setUint8(o++, b & 0xff);
|
|
256
|
+
for (let i = 0; i < 64; i++)
|
|
257
|
+
dv.setInt8(o++, this.mapId[i] | 0);
|
|
258
|
+
for (let i = 0; i < 32; i++)
|
|
259
|
+
dv.setUint8(o++, this.indicatorFormat1[i] & 0xff);
|
|
260
|
+
for (let i = 0; i < 32; i++)
|
|
261
|
+
dv.setUint8(o++, this.indicatorFormat2[i] & 0xff);
|
|
262
|
+
return out;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/** Set MotionConfig.map_id for the given resolution (pure — no I/O). */
|
|
266
|
+
export function motionConfigSetResolution(cfg, resolution) {
|
|
267
|
+
if (resolution === RESOLUTION_4X4) {
|
|
268
|
+
for (let i = 0; i < 16; i++)
|
|
269
|
+
cfg.mapId[i] = i;
|
|
270
|
+
for (let i = 16; i < 64; i++)
|
|
271
|
+
cfg.mapId[i] = -1;
|
|
272
|
+
}
|
|
273
|
+
else if (resolution === RESOLUTION_8X8) {
|
|
274
|
+
for (let i = 0; i < 64; i++)
|
|
275
|
+
cfg.mapId[i] = ((i % 8) >> 1) + 4 * Math.floor(i / 16);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "motion set_resolution");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The default motion-indicator configuration used by `motionIndicatorInit`
|
|
283
|
+
* for a resolution (pure — same bytes the sensor is programmed with).
|
|
284
|
+
*/
|
|
285
|
+
export function defaultMotionConfig(resolution) {
|
|
286
|
+
const cfg = new MotionConfig();
|
|
287
|
+
cfg.refBinOffset = 13633;
|
|
288
|
+
cfg.detectionThreshold = 2883584;
|
|
289
|
+
cfg.extraNoiseSigma = 0;
|
|
290
|
+
cfg.nullDenClipValue = 0;
|
|
291
|
+
cfg.memUpdateMode = 6;
|
|
292
|
+
cfg.memUpdateChoice = 2;
|
|
293
|
+
cfg.sumSpan = 4;
|
|
294
|
+
cfg.featureLength = 9;
|
|
295
|
+
cfg.nbOfAggregates = 16;
|
|
296
|
+
cfg.nbOfTemporalAccumulations = 16;
|
|
297
|
+
cfg.minNbForGlobalDetection = 1;
|
|
298
|
+
cfg.globalIndicatorFormat1 = 8;
|
|
299
|
+
cfg.globalIndicatorFormat2 = 0;
|
|
300
|
+
motionConfigSetResolution(cfg, resolution);
|
|
301
|
+
return cfg;
|
|
302
|
+
}
|
|
303
|
+
/** Xtalk margin (kcps/spad) → raw DCI value (round(kcps * 2048)). */
|
|
304
|
+
export function xtalkMarginToRaw(marginKcps) {
|
|
305
|
+
return Math.round(marginKcps * 2048) >>> 0;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Pack 64 detection thresholds into the DCI_DET_THRESH_START payload plus the
|
|
309
|
+
* 8-byte valid-status block (pure — mirror of set_detection_thresholds). Each
|
|
310
|
+
* threshold's low/high are scaled by its measurement selector.
|
|
311
|
+
*/
|
|
312
|
+
export function packDetectionThresholds(thresholds) {
|
|
313
|
+
const valid = new Uint8Array(8).fill(0x05);
|
|
314
|
+
const start = new Uint8Array(NB_THRESHOLDS * 12);
|
|
315
|
+
const dv = new DataView(start.buffer);
|
|
316
|
+
for (let k = 0; k < NB_THRESHOLDS; k++) {
|
|
317
|
+
const t = thresholds[k] ?? {};
|
|
318
|
+
const meas = (t.measurement ?? 0) | 0;
|
|
319
|
+
const scale = THRESH_SCALE[meas] ?? 1;
|
|
320
|
+
const low = ((t.lowThresh ?? 0) | 0) * scale;
|
|
321
|
+
const high = ((t.highThresh ?? 0) | 0) * scale;
|
|
322
|
+
const off = k * 12;
|
|
323
|
+
dv.setInt32(off, low | 0, true);
|
|
324
|
+
dv.setInt32(off + 4, high | 0, true);
|
|
325
|
+
dv.setUint8(off + 8, meas & 0xff);
|
|
326
|
+
dv.setUint8(off + 9, (t.type ?? 0) & 0xff);
|
|
327
|
+
dv.setUint8(off + 10, (t.zoneNum ?? 0) & 0xff);
|
|
328
|
+
dv.setUint8(off + 11, (t.operation ?? 0) & 0xff);
|
|
329
|
+
}
|
|
330
|
+
return { start, valid };
|
|
331
|
+
}
|
|
332
|
+
export class VL53L8CX {
|
|
333
|
+
p;
|
|
334
|
+
variant;
|
|
335
|
+
fwChecksum;
|
|
336
|
+
firmware;
|
|
337
|
+
defaultCfg;
|
|
338
|
+
defaultXtalk;
|
|
339
|
+
getNvmCmd;
|
|
340
|
+
offsetData = new Uint8Array(0);
|
|
341
|
+
xtalkData = new Uint8Array(0);
|
|
342
|
+
streamcount = 255;
|
|
343
|
+
dataReadSize = 0;
|
|
344
|
+
/** { fw, host } when the FW disagrees with the api.c formula. */
|
|
345
|
+
frameSizeMismatch = null;
|
|
346
|
+
/** [idx, type, size] of the blocks in the last parsed frame. */
|
|
347
|
+
lastBlocks = [];
|
|
348
|
+
/** Whether the motion-indicator output block is configured. */
|
|
349
|
+
motionPresent = false;
|
|
350
|
+
outputEnableW3;
|
|
351
|
+
frameTail;
|
|
352
|
+
footerIdOff;
|
|
353
|
+
constructor(platform, assets, variant = "cx") {
|
|
354
|
+
this.p = platform;
|
|
355
|
+
this.variant = variant;
|
|
356
|
+
this.fwChecksum = FW_CHECKSUM[variant];
|
|
357
|
+
// start_ranging output config differs slightly by FW variant:
|
|
358
|
+
// cx (ULD 2.1.0): OUTPUT_ENABLES word[3]=0xC0000000, frame tail +32
|
|
359
|
+
// ch (VL53LMZ 2.0.16): OUTPUT_ENABLES word[3]=0, frame tail +24
|
|
360
|
+
// footerIdOff: where the footer id sits relative to the frame end
|
|
361
|
+
// (cx 2.1.0 = size-12; ch 2.0.16 = size-4).
|
|
362
|
+
if (variant === "ch") {
|
|
363
|
+
this.outputEnableW3 = 0x00000000;
|
|
364
|
+
this.frameTail = 24;
|
|
365
|
+
this.footerIdOff = 4;
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
this.outputEnableW3 = 0xc0000000;
|
|
369
|
+
this.frameTail = 32;
|
|
370
|
+
this.footerIdOff = 12;
|
|
371
|
+
}
|
|
372
|
+
this.firmware = assets.firmware;
|
|
373
|
+
this.defaultCfg = assets.defaultCfg;
|
|
374
|
+
this.defaultXtalk = assets.defaultXtalk;
|
|
375
|
+
this.getNvmCmd = assets.getNvmCmd;
|
|
376
|
+
if (this.firmware.length !== 0x15000) {
|
|
377
|
+
throw new Error(`firmware.bin (${variant}) has wrong size — rerun the blob extractor`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// ---------------- low-level helpers ----------------
|
|
381
|
+
async rdByte(addr) {
|
|
382
|
+
return (await this.p.rdMulti(addr, 1))[0];
|
|
383
|
+
}
|
|
384
|
+
async wrByte(addr, value) {
|
|
385
|
+
await this.p.wrMulti(addr, Uint8Array.of(value));
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* _vl53l8cx_poll_for_answer(): poll `size` bytes at `address` until
|
|
389
|
+
* buf[pos] & mask == expected. 10 ms period, 2 s timeout.
|
|
390
|
+
*/
|
|
391
|
+
async pollForAnswer(size, pos, address, mask, expected, where = "") {
|
|
392
|
+
let timeout = 0;
|
|
393
|
+
for (;;) {
|
|
394
|
+
const buf = await this.p.rdMulti(address, size);
|
|
395
|
+
await this.p.sleepMs(10);
|
|
396
|
+
if (timeout >= 200) {
|
|
397
|
+
throw new Vl53l8cxError(STATUS_TIMEOUT_ERROR, where);
|
|
398
|
+
}
|
|
399
|
+
if (size >= 4 && buf[2] >= 0x7f) {
|
|
400
|
+
throw new Vl53l8cxError(MCU_ERROR, where);
|
|
401
|
+
}
|
|
402
|
+
timeout += 1;
|
|
403
|
+
if ((buf[pos] & mask) === expected) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async pollForMcuBoot() {
|
|
409
|
+
let timeout = 0;
|
|
410
|
+
while (timeout < 500) {
|
|
411
|
+
const go2Status0 = await this.rdByte(0x06);
|
|
412
|
+
if (go2Status0 & 0x80) {
|
|
413
|
+
const go2Status1 = await this.rdByte(0x07);
|
|
414
|
+
if (go2Status1 & 0x01) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
await this.p.sleepMs(1);
|
|
419
|
+
timeout += 1;
|
|
420
|
+
if (go2Status0 & 0x01) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
throw new Vl53l8cxError(STATUS_TIMEOUT_ERROR, "mcu boot");
|
|
425
|
+
}
|
|
426
|
+
// ---------------- DCI access ----------------
|
|
427
|
+
async dciReadData(index, dataSize) {
|
|
428
|
+
const cmd = new Uint8Array(12);
|
|
429
|
+
cmd[0] = (index >> 8) & 0xff;
|
|
430
|
+
cmd[1] = index & 0xff;
|
|
431
|
+
cmd[2] = (dataSize & 0xff0) >> 4;
|
|
432
|
+
cmd[3] = (dataSize & 0xf) << 4;
|
|
433
|
+
cmd[7] = 0x0f;
|
|
434
|
+
cmd[9] = 0x02;
|
|
435
|
+
cmd[11] = 0x08;
|
|
436
|
+
await this.p.wrMulti(UI_CMD_END - 11, cmd);
|
|
437
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, `dci read 0x${hex4(index)}`);
|
|
438
|
+
let buf = await this.p.rdMulti(UI_CMD_START, dataSize + 12);
|
|
439
|
+
buf = swapBuffer(buf);
|
|
440
|
+
return buf.slice(4, 4 + dataSize);
|
|
441
|
+
}
|
|
442
|
+
async dciWriteData(index, data) {
|
|
443
|
+
const dataSize = data.length;
|
|
444
|
+
const footer = Uint8Array.of(0x00, 0x00, 0x00, 0x0f, 0x05, 0x01, ((dataSize + 8) >> 8) & 0xff, (dataSize + 8) & 0xff);
|
|
445
|
+
const address = UI_CMD_END - (dataSize + 12) + 1;
|
|
446
|
+
const header = Uint8Array.of((index >> 8) & 0xff, index & 0xff, (dataSize & 0xff0) >> 4, (dataSize & 0xf) << 4);
|
|
447
|
+
const swapped = swapBuffer(data);
|
|
448
|
+
const buf = new Uint8Array(4 + dataSize + 8);
|
|
449
|
+
buf.set(header, 0);
|
|
450
|
+
buf.set(swapped, 4);
|
|
451
|
+
buf.set(footer, 4 + dataSize);
|
|
452
|
+
await this.p.wrMulti(address, buf);
|
|
453
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, `dci write 0x${hex4(index)}`);
|
|
454
|
+
}
|
|
455
|
+
async dciReplaceData(index, dataSize, newData, newDataPos) {
|
|
456
|
+
const data = await this.dciReadData(index, dataSize);
|
|
457
|
+
data.set(newData, newDataPos);
|
|
458
|
+
await this.dciWriteData(index, data);
|
|
459
|
+
}
|
|
460
|
+
// ---------------- offset / xtalk upload ----------------
|
|
461
|
+
async sendOffsetData(resolution) {
|
|
462
|
+
let buf = new Uint8Array(OFFSET_BUFFER_SIZE);
|
|
463
|
+
buf.set(this.offsetData.subarray(0, OFFSET_BUFFER_SIZE));
|
|
464
|
+
if (resolution === RESOLUTION_4X4) {
|
|
465
|
+
const dss4x4 = Uint8Array.of(0x0f, 0x04, 0x04, 0x00, 0x08, 0x10, 0x10, 0x07);
|
|
466
|
+
buf.set(dss4x4, 0x10);
|
|
467
|
+
buf = swapBuffer(buf);
|
|
468
|
+
const dv = dvOf(buf);
|
|
469
|
+
const signalGrid = [];
|
|
470
|
+
const rangeGrid = [];
|
|
471
|
+
for (let k = 0; k < 64; k++) {
|
|
472
|
+
signalGrid.push(dv.getUint32(0x3c + 4 * k, true));
|
|
473
|
+
rangeGrid.push(dv.getInt16(0x140 + 2 * k, true));
|
|
474
|
+
}
|
|
475
|
+
for (let j = 0; j < 4; j++) {
|
|
476
|
+
for (let i = 0; i < 4; i++) {
|
|
477
|
+
signalGrid[i + 4 * j] = Math.floor((signalGrid[2 * i + 16 * j + 0] +
|
|
478
|
+
signalGrid[2 * i + 16 * j + 1] +
|
|
479
|
+
signalGrid[2 * i + 16 * j + 8] +
|
|
480
|
+
signalGrid[2 * i + 16 * j + 9]) /
|
|
481
|
+
4);
|
|
482
|
+
rangeGrid[i + 4 * j] = Math.floor((rangeGrid[2 * i + 16 * j + 0] +
|
|
483
|
+
rangeGrid[2 * i + 16 * j + 1] +
|
|
484
|
+
rangeGrid[2 * i + 16 * j + 8] +
|
|
485
|
+
rangeGrid[2 * i + 16 * j + 9]) /
|
|
486
|
+
4);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
for (let k = 16; k < 64; k++) {
|
|
490
|
+
signalGrid[k] = 0;
|
|
491
|
+
rangeGrid[k] = 0;
|
|
492
|
+
}
|
|
493
|
+
for (let k = 0; k < 64; k++) {
|
|
494
|
+
dv.setUint32(0x3c + 4 * k, signalGrid[k] >>> 0, true);
|
|
495
|
+
dv.setInt16(0x140 + 2 * k, Math.max(-32768, Math.min(32767, rangeGrid[k])), true);
|
|
496
|
+
}
|
|
497
|
+
buf = swapBuffer(buf);
|
|
498
|
+
}
|
|
499
|
+
// Shift the buffer 8 bytes left (drop NVM header) and append the
|
|
500
|
+
// footer at 0x1E0. In C the shift reads past the 488-byte region, but
|
|
501
|
+
// those bytes are then overwritten by the footer, so this is identical.
|
|
502
|
+
const footer = Uint8Array.of(0x00, 0x00, 0x00, 0x0f, 0x03, 0x01, 0x01, 0xe4);
|
|
503
|
+
const out = new Uint8Array(OFFSET_BUFFER_SIZE);
|
|
504
|
+
out.set(buf.subarray(8), 0);
|
|
505
|
+
out.set(footer, OFFSET_BUFFER_SIZE - 8);
|
|
506
|
+
await this.p.wrMulti(0x2e18, out);
|
|
507
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "offset data");
|
|
508
|
+
}
|
|
509
|
+
async sendXtalkData(resolution) {
|
|
510
|
+
let buf = new Uint8Array(XTALK_BUFFER_SIZE);
|
|
511
|
+
buf.set(this.xtalkData.subarray(0, XTALK_BUFFER_SIZE));
|
|
512
|
+
if (resolution === RESOLUTION_4X4) {
|
|
513
|
+
const res4x4 = Uint8Array.of(0x0f, 0x04, 0x04, 0x17, 0x08, 0x10, 0x10, 0x07);
|
|
514
|
+
const dss4x4 = Uint8Array.of(0x00, 0x78, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08);
|
|
515
|
+
const profile4x4 = Uint8Array.of(0xa0, 0xfc, 0x01, 0x00);
|
|
516
|
+
buf.set(res4x4, 0x08);
|
|
517
|
+
buf.set(dss4x4, 0x20);
|
|
518
|
+
buf = swapBuffer(buf);
|
|
519
|
+
const dv = dvOf(buf);
|
|
520
|
+
const signalGrid = [];
|
|
521
|
+
for (let k = 0; k < 64; k++) {
|
|
522
|
+
signalGrid.push(dv.getUint32(0x34 + 4 * k, true));
|
|
523
|
+
}
|
|
524
|
+
for (let j = 0; j < 4; j++) {
|
|
525
|
+
for (let i = 0; i < 4; i++) {
|
|
526
|
+
signalGrid[i + 4 * j] = Math.floor((signalGrid[2 * i + 16 * j + 0] +
|
|
527
|
+
signalGrid[2 * i + 16 * j + 1] +
|
|
528
|
+
signalGrid[2 * i + 16 * j + 8] +
|
|
529
|
+
signalGrid[2 * i + 16 * j + 9]) /
|
|
530
|
+
4);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
for (let k = 16; k < 64; k++) {
|
|
534
|
+
signalGrid[k] = 0;
|
|
535
|
+
}
|
|
536
|
+
for (let k = 0; k < 64; k++) {
|
|
537
|
+
dv.setUint32(0x34 + 4 * k, signalGrid[k] >>> 0, true);
|
|
538
|
+
}
|
|
539
|
+
buf = swapBuffer(buf);
|
|
540
|
+
buf.set(profile4x4, 0x134);
|
|
541
|
+
buf.set(new Uint8Array(4), 0x078);
|
|
542
|
+
}
|
|
543
|
+
await this.p.wrMulti(0x2cf8, buf);
|
|
544
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "xtalk data");
|
|
545
|
+
}
|
|
546
|
+
// ---------------- public API ----------------
|
|
547
|
+
/** Returns [deviceId, revisionId]; alive when (0xF0, 0x0C). */
|
|
548
|
+
async isAlive() {
|
|
549
|
+
return readDeviceRevisionId(this.p);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* vl53l8cx_init(): boot the sensor MCU, download the 84 KB sensor
|
|
553
|
+
* firmware, upload NVM offset / xtalk / default configuration.
|
|
554
|
+
* `progress(text)` is an optional UI callback.
|
|
555
|
+
*/
|
|
556
|
+
async init(progress) {
|
|
557
|
+
const note = (text) => {
|
|
558
|
+
if (progress)
|
|
559
|
+
progress(text);
|
|
560
|
+
};
|
|
561
|
+
const wr = (addr, value) => this.wrByte(addr, value);
|
|
562
|
+
const rd = (addr) => this.rdByte(addr);
|
|
563
|
+
// SW reboot sequence
|
|
564
|
+
note("SW reboot...");
|
|
565
|
+
await wr(0x7fff, 0x00);
|
|
566
|
+
await wr(0x0009, 0x04);
|
|
567
|
+
await wr(0x000f, 0x40);
|
|
568
|
+
await wr(0x000a, 0x03);
|
|
569
|
+
await rd(0x7fff);
|
|
570
|
+
await wr(0x000c, 0x01);
|
|
571
|
+
await wr(0x0101, 0x00);
|
|
572
|
+
await wr(0x0102, 0x00);
|
|
573
|
+
await wr(0x010a, 0x01);
|
|
574
|
+
await wr(0x4002, 0x01);
|
|
575
|
+
await wr(0x4002, 0x00);
|
|
576
|
+
await wr(0x010a, 0x03);
|
|
577
|
+
await wr(0x0103, 0x01);
|
|
578
|
+
await wr(0x000c, 0x00);
|
|
579
|
+
await wr(0x000f, 0x43);
|
|
580
|
+
await this.p.sleepMs(1);
|
|
581
|
+
await wr(0x000f, 0x40);
|
|
582
|
+
await wr(0x000a, 0x01);
|
|
583
|
+
await this.p.sleepMs(100);
|
|
584
|
+
// Wait for sensor booted
|
|
585
|
+
note("Waiting for sensor boot...");
|
|
586
|
+
await wr(0x7fff, 0x00);
|
|
587
|
+
await this.pollForAnswer(1, 0, 0x06, 0xff, 1, "sensor boot");
|
|
588
|
+
await wr(0x000e, 0x01);
|
|
589
|
+
await wr(0x7fff, 0x02);
|
|
590
|
+
// Enable FW access
|
|
591
|
+
await wr(0x7fff, 0x01);
|
|
592
|
+
await wr(0x06, 0x01);
|
|
593
|
+
await this.pollForAnswer(1, 0, 0x21, 0xff, 0x04, "fw access");
|
|
594
|
+
await wr(0x7fff, 0x00);
|
|
595
|
+
// Enable host access to GO1
|
|
596
|
+
await rd(0x7fff);
|
|
597
|
+
await wr(0x0c, 0x01);
|
|
598
|
+
// Power ON status
|
|
599
|
+
await wr(0x7fff, 0x00);
|
|
600
|
+
await wr(0x101, 0x00);
|
|
601
|
+
await wr(0x102, 0x00);
|
|
602
|
+
await wr(0x010a, 0x01);
|
|
603
|
+
await wr(0x4002, 0x01);
|
|
604
|
+
await wr(0x4002, 0x00);
|
|
605
|
+
await wr(0x010a, 0x03);
|
|
606
|
+
await wr(0x103, 0x01);
|
|
607
|
+
await wr(0x400f, 0x00);
|
|
608
|
+
await wr(0x21a, 0x43);
|
|
609
|
+
await wr(0x21a, 0x03);
|
|
610
|
+
await wr(0x21a, 0x01);
|
|
611
|
+
await wr(0x21a, 0x00);
|
|
612
|
+
await wr(0x219, 0x00);
|
|
613
|
+
await wr(0x21b, 0x00);
|
|
614
|
+
// Wake up MCU
|
|
615
|
+
await wr(0x7fff, 0x00);
|
|
616
|
+
await rd(0x7fff);
|
|
617
|
+
await wr(0x7fff, 0x01);
|
|
618
|
+
// Download FW into VL53L8CX
|
|
619
|
+
note("Downloading sensor FW (84 KB)... bank 1/3");
|
|
620
|
+
await wr(0x7fff, 0x09);
|
|
621
|
+
await this.p.wrMulti(0, this.firmware.subarray(0, 0x8000));
|
|
622
|
+
note("Downloading sensor FW... bank 2/3");
|
|
623
|
+
await wr(0x7fff, 0x0a);
|
|
624
|
+
await this.p.wrMulti(0, this.firmware.subarray(0x8000, 0x10000));
|
|
625
|
+
note("Downloading sensor FW... bank 3/3");
|
|
626
|
+
await wr(0x7fff, 0x0b);
|
|
627
|
+
await this.p.wrMulti(0, this.firmware.subarray(0x10000, 0x15000));
|
|
628
|
+
await wr(0x7fff, 0x01);
|
|
629
|
+
// Check if FW correctly downloaded
|
|
630
|
+
await wr(0x7fff, 0x01);
|
|
631
|
+
await wr(0x06, 0x03);
|
|
632
|
+
await this.p.sleepMs(5);
|
|
633
|
+
await wr(0x7fff, 0x00);
|
|
634
|
+
await rd(0x7fff);
|
|
635
|
+
await wr(0x0c, 0x01);
|
|
636
|
+
// Reset MCU and wait boot
|
|
637
|
+
note("Booting sensor MCU...");
|
|
638
|
+
await wr(0x7fff, 0x00);
|
|
639
|
+
await wr(0x114, 0x00);
|
|
640
|
+
await wr(0x115, 0x00);
|
|
641
|
+
await wr(0x116, 0x42);
|
|
642
|
+
await wr(0x117, 0x00);
|
|
643
|
+
await wr(0x0b, 0x00);
|
|
644
|
+
await rd(0x7fff);
|
|
645
|
+
await wr(0x0c, 0x00);
|
|
646
|
+
await wr(0x0b, 0x01);
|
|
647
|
+
await this.pollForMcuBoot();
|
|
648
|
+
await wr(0x7fff, 0x02);
|
|
649
|
+
// Firmware checksum (0x812FFC & 0xFFFF); value depends on the FW variant
|
|
650
|
+
// (see FW_CHECKSUM). cx = ULD 2.1.0 FW; ch = VL53LMZ ULD 2.0.16 FW.
|
|
651
|
+
const crcBuf = swapBuffer(await this.p.rdMulti(0x2ffc, 4));
|
|
652
|
+
const crc = dvOf(crcBuf).getUint32(0, true);
|
|
653
|
+
if (crc !== this.fwChecksum) {
|
|
654
|
+
throw new Vl53l8cxError(STATUS_FW_CHECKSUM_FAIL, `crc=0x${hex8(crc)} (expected 0x${hex8(this.fwChecksum)} for ${this.variant})`);
|
|
655
|
+
}
|
|
656
|
+
note("Sensor FW checksum OK");
|
|
657
|
+
// Get offset NVM data
|
|
658
|
+
note("Reading NVM offset data...");
|
|
659
|
+
await this.p.wrMulti(0x2fd8, this.getNvmCmd);
|
|
660
|
+
await this.pollForAnswer(4, 0, UI_CMD_STATUS, 0xff, 2, "nvm read");
|
|
661
|
+
const nvm = await this.p.rdMulti(UI_CMD_START, NVM_DATA_SIZE);
|
|
662
|
+
this.offsetData = nvm.slice(0, OFFSET_BUFFER_SIZE);
|
|
663
|
+
await this.sendOffsetData(RESOLUTION_4X4);
|
|
664
|
+
// Default xtalk
|
|
665
|
+
note("Uploading default xtalk...");
|
|
666
|
+
this.xtalkData = this.defaultXtalk;
|
|
667
|
+
await this.sendXtalkData(RESOLUTION_4X4);
|
|
668
|
+
// Default configuration
|
|
669
|
+
note("Uploading default configuration...");
|
|
670
|
+
await this.p.wrMulti(0x2c34, this.defaultCfg);
|
|
671
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "default config");
|
|
672
|
+
const pipeCtrl = Uint8Array.of(NB_TARGET_PER_ZONE, 0x00, 0x01, 0x00);
|
|
673
|
+
await this.dciWriteData(DCI_PIPE_CONTROL, pipeCtrl);
|
|
674
|
+
await this.dciWriteData(DCI_SINGLE_RANGE, packU32ArrayLE([0x01]));
|
|
675
|
+
note("Sensor init done");
|
|
676
|
+
}
|
|
677
|
+
async getResolution() {
|
|
678
|
+
const buf = await this.dciReadData(DCI_ZONE_CONFIG, 8);
|
|
679
|
+
return buf[0] * buf[1];
|
|
680
|
+
}
|
|
681
|
+
async setResolution(resolution) {
|
|
682
|
+
if (resolution === RESOLUTION_4X4) {
|
|
683
|
+
let buf = await this.dciReadData(DCI_DSS_CONFIG, 16);
|
|
684
|
+
buf[0x04] = 64;
|
|
685
|
+
buf[0x06] = 64;
|
|
686
|
+
buf[0x09] = 4;
|
|
687
|
+
await this.dciWriteData(DCI_DSS_CONFIG, buf);
|
|
688
|
+
buf = await this.dciReadData(DCI_ZONE_CONFIG, 8);
|
|
689
|
+
buf[0x00] = 4;
|
|
690
|
+
buf[0x01] = 4;
|
|
691
|
+
buf[0x04] = 8;
|
|
692
|
+
buf[0x05] = 8;
|
|
693
|
+
await this.dciWriteData(DCI_ZONE_CONFIG, buf);
|
|
694
|
+
}
|
|
695
|
+
else if (resolution === RESOLUTION_8X8) {
|
|
696
|
+
let buf = await this.dciReadData(DCI_DSS_CONFIG, 16);
|
|
697
|
+
buf[0x04] = 16;
|
|
698
|
+
buf[0x06] = 16;
|
|
699
|
+
buf[0x09] = 1;
|
|
700
|
+
await this.dciWriteData(DCI_DSS_CONFIG, buf);
|
|
701
|
+
buf = await this.dciReadData(DCI_ZONE_CONFIG, 8);
|
|
702
|
+
buf[0x00] = 8;
|
|
703
|
+
buf[0x01] = 8;
|
|
704
|
+
buf[0x04] = 4;
|
|
705
|
+
buf[0x05] = 4;
|
|
706
|
+
await this.dciWriteData(DCI_ZONE_CONFIG, buf);
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "set_resolution");
|
|
710
|
+
}
|
|
711
|
+
await this.sendOffsetData(resolution);
|
|
712
|
+
await this.sendXtalkData(resolution);
|
|
713
|
+
}
|
|
714
|
+
async getRangingFrequencyHz() {
|
|
715
|
+
return (await this.dciReadData(DCI_FREQ_HZ, 4))[1];
|
|
716
|
+
}
|
|
717
|
+
async setRangingFrequencyHz(hz) {
|
|
718
|
+
await this.dciReplaceData(DCI_FREQ_HZ, 4, Uint8Array.of(hz), 0x01);
|
|
719
|
+
}
|
|
720
|
+
async setRangingMode(mode) {
|
|
721
|
+
const buf = await this.dciReadData(DCI_RANGING_MODE, 8);
|
|
722
|
+
let singleRange;
|
|
723
|
+
if (mode === RANGING_MODE_CONTINUOUS) {
|
|
724
|
+
buf[0x01] = 0x1;
|
|
725
|
+
buf[0x03] = 0x3;
|
|
726
|
+
singleRange = 0x00;
|
|
727
|
+
}
|
|
728
|
+
else if (mode === RANGING_MODE_AUTONOMOUS) {
|
|
729
|
+
buf[0x01] = 0x3;
|
|
730
|
+
buf[0x03] = 0x2;
|
|
731
|
+
singleRange = 0x01;
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "set_ranging_mode");
|
|
735
|
+
}
|
|
736
|
+
await this.dciWriteData(DCI_RANGING_MODE, buf);
|
|
737
|
+
await this.dciWriteData(DCI_SINGLE_RANGE, packU32ArrayLE([singleRange]));
|
|
738
|
+
}
|
|
739
|
+
async getRangingMode() {
|
|
740
|
+
const buf = await this.dciReadData(DCI_RANGING_MODE, 8);
|
|
741
|
+
return buf[0x01] === 0x1 ? RANGING_MODE_CONTINUOUS : RANGING_MODE_AUTONOMOUS;
|
|
742
|
+
}
|
|
743
|
+
async getIntegrationTimeMs() {
|
|
744
|
+
const buf = await this.dciReadData(DCI_INT_TIME, 20);
|
|
745
|
+
return Math.floor(dvOf(buf).getUint32(0, true) / 1000);
|
|
746
|
+
}
|
|
747
|
+
/** Integration time 2..1000 ms. No effect in continuous ranging mode. */
|
|
748
|
+
async setIntegrationTimeMs(timeMs) {
|
|
749
|
+
if (!(timeMs >= 2 && timeMs <= 1000)) {
|
|
750
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "set_integration_time_ms");
|
|
751
|
+
}
|
|
752
|
+
const data = new Uint8Array(4);
|
|
753
|
+
new DataView(data.buffer).setUint32(0, timeMs * 1000, true);
|
|
754
|
+
await this.dciReplaceData(DCI_INT_TIME, 20, data, 0x00);
|
|
755
|
+
}
|
|
756
|
+
async getSharpenerPercent() {
|
|
757
|
+
return Math.floor(((await this.dciReadData(DCI_SHARPENER, 16))[0xd] * 100) / 255);
|
|
758
|
+
}
|
|
759
|
+
/** Sharpener 0..99 % (0 = disabled). */
|
|
760
|
+
async setSharpenerPercent(pct) {
|
|
761
|
+
if (pct >= 100) {
|
|
762
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "set_sharpener_percent");
|
|
763
|
+
}
|
|
764
|
+
await this.dciReplaceData(DCI_SHARPENER, 16, Uint8Array.of(Math.floor((pct * 255) / 100)), 0xd);
|
|
765
|
+
}
|
|
766
|
+
async getTargetOrder() {
|
|
767
|
+
return (await this.dciReadData(DCI_TARGET_ORDER, 4))[0];
|
|
768
|
+
}
|
|
769
|
+
async setTargetOrder(order) {
|
|
770
|
+
if (order !== TARGET_ORDER_CLOSEST && order !== TARGET_ORDER_STRONGEST) {
|
|
771
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "set_target_order");
|
|
772
|
+
}
|
|
773
|
+
await this.dciReplaceData(DCI_TARGET_ORDER, 4, Uint8Array.of(order), 0x0);
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Start ranging. When `cnhDataSize` is given (bytes, from
|
|
777
|
+
* CnhConfig.requiredMemory), a VL53L8CH CNH data block is appended to the
|
|
778
|
+
* output list so each frame also carries the compact-network-histogram
|
|
779
|
+
* buffer. The CNH frame is far larger than the MCU stream cap, so the
|
|
780
|
+
* caller must read it in poll-mode (checkDataReady + getRangingData),
|
|
781
|
+
* not via the MCU INT stream.
|
|
782
|
+
*/
|
|
783
|
+
async startRanging(cnhDataSize = null) {
|
|
784
|
+
const resolution = await this.getResolution();
|
|
785
|
+
// A 0 / non-standard resolution produces a zero-sized output config and
|
|
786
|
+
// faults the sensor MCU (GO2 0x9C) instead of streaming. Refuse early
|
|
787
|
+
// with a clear error; call setResolution() first.
|
|
788
|
+
if (resolution !== RESOLUTION_4X4 && resolution !== RESOLUTION_8X8) {
|
|
789
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, `bad resolution ${resolution} — call set_resolution first`);
|
|
790
|
+
}
|
|
791
|
+
this.dataReadSize = 0;
|
|
792
|
+
this.streamcount = 255;
|
|
793
|
+
// All outputs enabled (default platform.h): bits 0..11
|
|
794
|
+
const outputBhEnable = [0x00000fff, 0, 0, this.outputEnableW3];
|
|
795
|
+
const output = [
|
|
796
|
+
START_BH,
|
|
797
|
+
METADATA_BH,
|
|
798
|
+
COMMONDATA_BH,
|
|
799
|
+
AMBIENT_RATE_BH,
|
|
800
|
+
SPAD_COUNT_BH,
|
|
801
|
+
NB_TARGET_DETECTED_BH,
|
|
802
|
+
SIGNAL_RATE_BH,
|
|
803
|
+
RANGE_SIGMA_MM_BH,
|
|
804
|
+
DISTANCE_BH,
|
|
805
|
+
REFLECTANCE_BH,
|
|
806
|
+
TARGET_STATUS_BH,
|
|
807
|
+
MOTION_DETECT_BH,
|
|
808
|
+
];
|
|
809
|
+
if (cnhDataSize !== null) {
|
|
810
|
+
// vl53lmz_add_output_block: CNH block, type 4, size in 32-bit words.
|
|
811
|
+
const cnhBh = (((CNH_DATA_IDX << 16) | ((Math.floor(cnhDataSize / 4) & 0xfff) << 4) | 4) >>> 0);
|
|
812
|
+
output.push(cnhBh);
|
|
813
|
+
outputBhEnable[0] = (outputBhEnable[0] | (1 << (output.length - 1))) >>> 0;
|
|
814
|
+
}
|
|
815
|
+
for (let i = 0; i < output.length; i++) {
|
|
816
|
+
if (output[i] === 0 ||
|
|
817
|
+
!((outputBhEnable[Math.floor(i / 32)] & (1 << i % 32)) >>> 0)) {
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
const [bhType, bhSizeIn, bhIdx] = bhFields(output[i]);
|
|
821
|
+
let bhSize = bhSizeIn;
|
|
822
|
+
if (bhType >= 0x1 && bhType < 0x0d) {
|
|
823
|
+
if (bhIdx >= 0x54d0 && bhIdx < 0x54d0 + 960) {
|
|
824
|
+
bhSize = resolution;
|
|
825
|
+
}
|
|
826
|
+
else if (bhIdx === CNH_DATA_IDX) {
|
|
827
|
+
// keep CNH block size; not zone-scaled
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
bhSize = resolution * NB_TARGET_PER_ZONE;
|
|
831
|
+
}
|
|
832
|
+
output[i] = bhSetSize(output[i], bhSize);
|
|
833
|
+
this.dataReadSize += bhType * bhSize;
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
this.dataReadSize += bhSize;
|
|
837
|
+
}
|
|
838
|
+
this.dataReadSize += 4;
|
|
839
|
+
}
|
|
840
|
+
this.dataReadSize += this.frameTail;
|
|
841
|
+
await this.dciWriteData(DCI_OUTPUT_LIST, packU32ArrayLE(output));
|
|
842
|
+
const headerConfig = packU32ArrayLE([this.dataReadSize, output.length + 1]);
|
|
843
|
+
await this.dciWriteData(DCI_OUTPUT_CONFIG, headerConfig);
|
|
844
|
+
await this.dciWriteData(DCI_OUTPUT_ENABLES, packU32ArrayLE(outputBhEnable));
|
|
845
|
+
// Start xshut bypass (interrupt mode)
|
|
846
|
+
await this.wrByte(0x7fff, 0x00);
|
|
847
|
+
await this.wrByte(0x09, 0x05);
|
|
848
|
+
await this.wrByte(0x7fff, 0x02);
|
|
849
|
+
// Start ranging session
|
|
850
|
+
await this.p.wrMulti(UI_CMD_END - 3, Uint8Array.of(0x00, 0x03, 0x00, 0x00));
|
|
851
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "start ranging");
|
|
852
|
+
// The FW reports the actual frame size it will stream. The C driver
|
|
853
|
+
// asserts equality, but the released FW blobs consistently report
|
|
854
|
+
// 4 bytes less than the api.c formula — trust the FW value.
|
|
855
|
+
let buf = await this.dciReadData(0x5440, 12);
|
|
856
|
+
const tmp = dvOf(buf).getUint16(0x8, true);
|
|
857
|
+
this.frameSizeMismatch = null;
|
|
858
|
+
if (tmp !== this.dataReadSize) {
|
|
859
|
+
this.frameSizeMismatch = { fw: tmp, host: this.dataReadSize };
|
|
860
|
+
this.dataReadSize = tmp;
|
|
861
|
+
}
|
|
862
|
+
// Laser safety fault check
|
|
863
|
+
buf = await this.dciReadData(0xe0c4, 8);
|
|
864
|
+
if (buf[0x6] !== 0) {
|
|
865
|
+
throw new Vl53l8cxError(STATUS_LASER_SAFETY);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
async stopRanging() {
|
|
869
|
+
const autoStopFlag = dvOf(await this.p.rdMulti(0x2ffc, 4)).getUint32(0, true);
|
|
870
|
+
if (autoStopFlag !== 0x4ff) {
|
|
871
|
+
await this.wrByte(0x7fff, 0x00);
|
|
872
|
+
// Provoke MCU stop
|
|
873
|
+
await this.wrByte(0x15, 0x16);
|
|
874
|
+
await this.wrByte(0x14, 0x01);
|
|
875
|
+
// Poll for G02 status 0 MCU stop
|
|
876
|
+
let tmp = 0;
|
|
877
|
+
let timeout = 0;
|
|
878
|
+
while (((tmp & 0x80) >> 7) === 0) {
|
|
879
|
+
tmp = await this.rdByte(0x06);
|
|
880
|
+
await this.p.sleepMs(10);
|
|
881
|
+
timeout += 1;
|
|
882
|
+
if (timeout > 500) {
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
// Check GO2 status 1
|
|
888
|
+
let tmp = await this.rdByte(0x06);
|
|
889
|
+
if (tmp & 0x80) {
|
|
890
|
+
tmp = await this.rdByte(0x07);
|
|
891
|
+
if (tmp !== 0x84 && tmp !== 0x85) {
|
|
892
|
+
// non-fatal: C code ORs it into status; we just continue
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
// Undo MCU stop
|
|
896
|
+
await this.wrByte(0x7fff, 0x00);
|
|
897
|
+
await this.wrByte(0x14, 0x00);
|
|
898
|
+
await this.wrByte(0x15, 0x00);
|
|
899
|
+
// Stop xshut bypass
|
|
900
|
+
await this.wrByte(0x09, 0x04);
|
|
901
|
+
await this.wrByte(0x7fff, 0x02);
|
|
902
|
+
}
|
|
903
|
+
async checkDataReady() {
|
|
904
|
+
const buf = await this.p.rdMulti(0x0, 4);
|
|
905
|
+
if (buf[0] !== this.streamcount &&
|
|
906
|
+
buf[0] !== 255 &&
|
|
907
|
+
buf[1] === 0x05 &&
|
|
908
|
+
(buf[2] & 0x05) === 0x05 &&
|
|
909
|
+
(buf[3] & 0x10) === 0x10) {
|
|
910
|
+
this.streamcount = buf[0];
|
|
911
|
+
return true;
|
|
912
|
+
}
|
|
913
|
+
if (buf[3] & 0x80) {
|
|
914
|
+
throw new Vl53l8cxError(buf[2], "GO2 error status");
|
|
915
|
+
}
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
/** Poll-mode read: fetch one results frame from reg 0x00 and parse it. */
|
|
919
|
+
async getRangingData() {
|
|
920
|
+
const raw = await this.p.rdMulti(0x0, this.dataReadSize);
|
|
921
|
+
return this.parseFrame(raw);
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Parse one raw results frame (dataReadSize bytes read from reg 0x00).
|
|
925
|
+
* Used both by poll-mode getRangingData() and by the host when the MCU
|
|
926
|
+
* pushes frames over the INT-driven stream. Returns per-zone arrays
|
|
927
|
+
* distanceMm, targetStatus, nbTargetDetected, signalPerSpad (kcps/SPAD),
|
|
928
|
+
* ambientPerSpad (kcps/SPAD), nbSpadsEnabled, rangeSigmaMm, reflectance
|
|
929
|
+
* (%), plus the per-frame scalar siliconTempDegc.
|
|
930
|
+
*/
|
|
931
|
+
parseFrame(raw) {
|
|
932
|
+
this.streamcount = raw[0];
|
|
933
|
+
const buf = swapBuffer(raw);
|
|
934
|
+
const dv = dvOf(buf);
|
|
935
|
+
const results = {
|
|
936
|
+
distanceMm: new Array(RESOLUTION_8X8 * NB_TARGET_PER_ZONE).fill(0),
|
|
937
|
+
targetStatus: new Array(RESOLUTION_8X8 * NB_TARGET_PER_ZONE).fill(0),
|
|
938
|
+
nbTargetDetected: new Array(RESOLUTION_8X8).fill(0),
|
|
939
|
+
signalPerSpad: new Array(RESOLUTION_8X8 * NB_TARGET_PER_ZONE).fill(0),
|
|
940
|
+
ambientPerSpad: new Array(RESOLUTION_8X8).fill(0),
|
|
941
|
+
nbSpadsEnabled: new Array(RESOLUTION_8X8).fill(0),
|
|
942
|
+
rangeSigmaMm: new Array(RESOLUTION_8X8 * NB_TARGET_PER_ZONE).fill(0),
|
|
943
|
+
reflectance: new Array(RESOLUTION_8X8 * NB_TARGET_PER_ZONE).fill(0),
|
|
944
|
+
siliconTempDegc: 0,
|
|
945
|
+
cnhRaw: null,
|
|
946
|
+
motion: null,
|
|
947
|
+
};
|
|
948
|
+
this.lastBlocks = [];
|
|
949
|
+
let i = 16;
|
|
950
|
+
while (i + 4 <= this.dataReadSize) {
|
|
951
|
+
const bh = dv.getUint32(i, true);
|
|
952
|
+
const [bhType, bhSize, bhIdx] = bhFields(bh);
|
|
953
|
+
const msize = bhType > 0x1 && bhType < 0xd ? bhType * bhSize : bhSize;
|
|
954
|
+
// The C driver reads into an oversized temp_buffer, so it walks past
|
|
955
|
+
// the last real block into footer/garbage harmlessly. Our buffer is
|
|
956
|
+
// exact-sized: stop once a block would run past the end (= we have
|
|
957
|
+
// reached the footer; all data blocks precede it). Without this the
|
|
958
|
+
// parser raised struct.error on some frames (seen at 15 Hz).
|
|
959
|
+
if (i + 4 + msize > this.dataReadSize) {
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
this.lastBlocks.push([bhIdx, bhType, bhSize]);
|
|
963
|
+
if (bhIdx === METADATA_IDX) {
|
|
964
|
+
results.siliconTempDegc = dv.getInt8(i + 12);
|
|
965
|
+
}
|
|
966
|
+
else if (bhIdx === DISTANCE_IDX) {
|
|
967
|
+
const out = [];
|
|
968
|
+
for (let k = 0; k < Math.floor(msize / 2); k++)
|
|
969
|
+
out.push(dv.getInt16(i + 4 + 2 * k, true));
|
|
970
|
+
results.distanceMm = out;
|
|
971
|
+
}
|
|
972
|
+
else if (bhIdx === TARGET_STATUS_IDX) {
|
|
973
|
+
results.targetStatus = Array.from(buf.subarray(i + 4, i + 4 + msize));
|
|
974
|
+
}
|
|
975
|
+
else if (bhIdx === NB_TARGET_DETECTED_IDX) {
|
|
976
|
+
results.nbTargetDetected = Array.from(buf.subarray(i + 4, i + 4 + msize));
|
|
977
|
+
}
|
|
978
|
+
else if (bhIdx === SIGNAL_RATE_IDX) {
|
|
979
|
+
const out = [];
|
|
980
|
+
for (let k = 0; k < Math.floor(msize / 4); k++)
|
|
981
|
+
out.push(dv.getUint32(i + 4 + 4 * k, true));
|
|
982
|
+
results.signalPerSpad = out;
|
|
983
|
+
}
|
|
984
|
+
else if (bhIdx === AMBIENT_RATE_IDX) {
|
|
985
|
+
const out = [];
|
|
986
|
+
for (let k = 0; k < Math.floor(msize / 4); k++)
|
|
987
|
+
out.push(dv.getUint32(i + 4 + 4 * k, true));
|
|
988
|
+
results.ambientPerSpad = out;
|
|
989
|
+
}
|
|
990
|
+
else if (bhIdx === SPAD_COUNT_IDX) {
|
|
991
|
+
const out = [];
|
|
992
|
+
for (let k = 0; k < Math.floor(msize / 4); k++)
|
|
993
|
+
out.push(dv.getUint32(i + 4 + 4 * k, true));
|
|
994
|
+
results.nbSpadsEnabled = out;
|
|
995
|
+
}
|
|
996
|
+
else if (bhIdx === RANGE_SIGMA_MM_IDX) {
|
|
997
|
+
const out = [];
|
|
998
|
+
for (let k = 0; k < Math.floor(msize / 2); k++)
|
|
999
|
+
out.push(dv.getUint16(i + 4 + 2 * k, true));
|
|
1000
|
+
results.rangeSigmaMm = out;
|
|
1001
|
+
}
|
|
1002
|
+
else if (bhIdx === REFLECTANCE_EST_PC_IDX) {
|
|
1003
|
+
results.reflectance = Array.from(buf.subarray(i + 4, i + 4 + msize));
|
|
1004
|
+
}
|
|
1005
|
+
else if (bhIdx === CNH_DATA_IDX) {
|
|
1006
|
+
results.cnhRaw = buf.slice(i + 4, i + 4 + msize);
|
|
1007
|
+
}
|
|
1008
|
+
i += msize + 4;
|
|
1009
|
+
}
|
|
1010
|
+
// Convert to real format (fixed-point scaling, per ST GetRangingData).
|
|
1011
|
+
results.distanceMm = results.distanceMm.map((d) => Math.floor(d / 4));
|
|
1012
|
+
results.rangeSigmaMm = results.rangeSigmaMm.map((s) => s / 128.0);
|
|
1013
|
+
// No target detected -> status 255. Iterate the zones actually present
|
|
1014
|
+
// in this frame (16 for 4x4, 64 for 8x8), not a fixed 64.
|
|
1015
|
+
const nzones = results.nbTargetDetected.length;
|
|
1016
|
+
for (let z = 0; z < nzones; z++) {
|
|
1017
|
+
if (results.nbTargetDetected[z] === 0) {
|
|
1018
|
+
for (let t = 0; t < NB_TARGET_PER_ZONE; t++) {
|
|
1019
|
+
const idx = NB_TARGET_PER_ZONE * z + t;
|
|
1020
|
+
if (idx < results.targetStatus.length) {
|
|
1021
|
+
results.targetStatus[idx] = 255;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
// Motion indicator block (optional): global indicator + per-zone map.
|
|
1027
|
+
// Present when the motion detector is configured; harmless otherwise.
|
|
1028
|
+
if (this.motionPresent) {
|
|
1029
|
+
results.motion = this.parseMotion(buf);
|
|
1030
|
+
}
|
|
1031
|
+
// Header/footer id match check (footer id offset is variant-specific:
|
|
1032
|
+
// cx 2.1.0 = size-12, ch 2.0.16 = size-4).
|
|
1033
|
+
const foff = this.footerIdOff;
|
|
1034
|
+
if (buf[0x8] !== buf[this.dataReadSize - foff] ||
|
|
1035
|
+
buf[0x9] !== buf[this.dataReadSize - foff + 1]) {
|
|
1036
|
+
throw new Vl53l8cxError(STATUS_CORRUPTED_FRAME);
|
|
1037
|
+
}
|
|
1038
|
+
return results;
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Extract the MOTION_INDICATOR results block (index 0xD858) if it was walked
|
|
1042
|
+
* in the last frame: global_indicator_1/2 (u32), status/nb_of_detected/
|
|
1043
|
+
* nb_of_aggregates/spare (u8), motion[32] (u32 per aggregate).
|
|
1044
|
+
*/
|
|
1045
|
+
parseMotion(buf) {
|
|
1046
|
+
const dv = dvOf(buf);
|
|
1047
|
+
let hasBlock = false;
|
|
1048
|
+
for (const [idx] of this.lastBlocks) {
|
|
1049
|
+
if (idx === MOTION_DETEC_IDX) {
|
|
1050
|
+
hasBlock = true;
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
if (!hasBlock)
|
|
1055
|
+
return null;
|
|
1056
|
+
let i = 16;
|
|
1057
|
+
while (i + 4 <= this.dataReadSize) {
|
|
1058
|
+
const bh = dv.getUint32(i, true);
|
|
1059
|
+
const [t, s, idx] = bhFields(bh);
|
|
1060
|
+
const msize = t > 0x1 && t < 0xd ? t * s : s;
|
|
1061
|
+
if (i + 4 + msize > this.dataReadSize)
|
|
1062
|
+
break;
|
|
1063
|
+
if (idx === MOTION_DETEC_IDX) {
|
|
1064
|
+
const motion = [];
|
|
1065
|
+
for (let k = 0; k < 32; k++)
|
|
1066
|
+
motion.push(dv.getUint32(i + 16 + 4 * k, true));
|
|
1067
|
+
return {
|
|
1068
|
+
globalIndicator1: dv.getUint32(i + 4, true),
|
|
1069
|
+
globalIndicator2: dv.getUint32(i + 8, true),
|
|
1070
|
+
status: dv.getUint8(i + 12),
|
|
1071
|
+
nbOfDetectedAggregates: dv.getUint8(i + 13),
|
|
1072
|
+
nbOfAggregates: dv.getUint8(i + 14),
|
|
1073
|
+
motion,
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
i += msize + 4;
|
|
1077
|
+
}
|
|
1078
|
+
return null;
|
|
1079
|
+
}
|
|
1080
|
+
// ---------------- power modes (vl53l8cx_api.c) ----------------
|
|
1081
|
+
async getPowerMode() {
|
|
1082
|
+
await this.wrByte(0x7fff, 0x00);
|
|
1083
|
+
const tmp = await this.rdByte(0x09);
|
|
1084
|
+
let mode;
|
|
1085
|
+
if (tmp === 0x04) {
|
|
1086
|
+
mode = POWER_MODE_WAKEUP;
|
|
1087
|
+
}
|
|
1088
|
+
else if (tmp === 0x02) {
|
|
1089
|
+
mode = (await this.rdByte(0x000f)) === 0x43 ? POWER_MODE_DEEP_SLEEP : POWER_MODE_SLEEP;
|
|
1090
|
+
}
|
|
1091
|
+
else {
|
|
1092
|
+
await this.wrByte(0x7fff, 0x02);
|
|
1093
|
+
throw new Vl53l8cxError(STATUS_ERROR, "get_power_mode");
|
|
1094
|
+
}
|
|
1095
|
+
await this.wrByte(0x7fff, 0x02);
|
|
1096
|
+
return mode;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* WAKEUP (1), SLEEP (0) or DEEP_SLEEP (2). Not allowed while ranging. Wake
|
|
1100
|
+
* from DEEP_SLEEP re-runs init() (the FW blob is lost).
|
|
1101
|
+
*/
|
|
1102
|
+
async setPowerMode(powerMode) {
|
|
1103
|
+
const current = await this.getPowerMode();
|
|
1104
|
+
if (powerMode === current)
|
|
1105
|
+
return;
|
|
1106
|
+
if (powerMode === POWER_MODE_WAKEUP) {
|
|
1107
|
+
await this.wrByte(0x7fff, 0x00);
|
|
1108
|
+
await this.wrByte(0x09, 0x04);
|
|
1109
|
+
const stored = await this.rdByte(0x000f);
|
|
1110
|
+
if (stored === 0x43)
|
|
1111
|
+
await this.wrByte(0x000f, 0x40);
|
|
1112
|
+
await this.pollForAnswer(1, 0, 0x06, 0x01, 1, "wakeup");
|
|
1113
|
+
await this.wrByte(0x7fff, 0x02);
|
|
1114
|
+
if (stored === 0x43)
|
|
1115
|
+
await this.init();
|
|
1116
|
+
}
|
|
1117
|
+
else if (powerMode === POWER_MODE_SLEEP) {
|
|
1118
|
+
await this.wrByte(0x7fff, 0x00);
|
|
1119
|
+
await this.wrByte(0x09, 0x02);
|
|
1120
|
+
await this.pollForAnswer(1, 0, 0x06, 0x01, 0, "sleep");
|
|
1121
|
+
await this.wrByte(0x7fff, 0x02);
|
|
1122
|
+
}
|
|
1123
|
+
else if (powerMode === POWER_MODE_DEEP_SLEEP) {
|
|
1124
|
+
await this.wrByte(0x7fff, 0x00);
|
|
1125
|
+
await this.wrByte(0x09, 0x02);
|
|
1126
|
+
await this.pollForAnswer(1, 0, 0x06, 0x01, 0, "deep sleep");
|
|
1127
|
+
await this.wrByte(0x000f, 0x43);
|
|
1128
|
+
await this.wrByte(0x7fff, 0x02);
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "set_power_mode");
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
// ---------------- xtalk (vl53l8cx_plugin_xtalk.c) ----------------
|
|
1135
|
+
/** Xtalk margin in kcps/spad. */
|
|
1136
|
+
async getXtalkMargin() {
|
|
1137
|
+
const buf = await this.dciReadData(DCI_XTALK_CFG, 16);
|
|
1138
|
+
return dvOf(buf).getUint32(0, true) / 2048.0;
|
|
1139
|
+
}
|
|
1140
|
+
async setXtalkMargin(marginKcps) {
|
|
1141
|
+
if (marginKcps > 10000)
|
|
1142
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "set_xtalk_margin");
|
|
1143
|
+
const raw = xtalkMarginToRaw(marginKcps);
|
|
1144
|
+
const data = new Uint8Array(4);
|
|
1145
|
+
new DataView(data.buffer).setUint32(0, raw >>> 0, true);
|
|
1146
|
+
await this.dciReplaceData(DCI_XTALK_CFG, 16, data, 0x00);
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Read the live 776-byte xtalk calibration blob back from the sensor FW (as
|
|
1150
|
+
* produced by calibrateXtalk). Restores the current resolution afterwards.
|
|
1151
|
+
*/
|
|
1152
|
+
async getCaldataXtalk() {
|
|
1153
|
+
const footer = Uint8Array.of(0x00, 0x00, 0x00, 0x0f, 0x00, 0x01, 0x03, 0x04);
|
|
1154
|
+
const resolution = await this.getResolution();
|
|
1155
|
+
await this.setResolution(RESOLUTION_8X8);
|
|
1156
|
+
await this.p.wrMulti(0x2fb8, GET_XTALK_CMD);
|
|
1157
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "get xtalk");
|
|
1158
|
+
const buf = await this.p.rdMulti(UI_CMD_START, XTALK_BUFFER_SIZE + 4);
|
|
1159
|
+
const out = new Uint8Array(XTALK_BUFFER_SIZE);
|
|
1160
|
+
out.set(buf.subarray(8, XTALK_BUFFER_SIZE), 0);
|
|
1161
|
+
out.set(footer, XTALK_BUFFER_SIZE - 8);
|
|
1162
|
+
this.xtalkData = out;
|
|
1163
|
+
await this.setResolution(resolution);
|
|
1164
|
+
return this.xtalkData;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Restore a previously saved 776-byte xtalk calibration blob. The blob is
|
|
1168
|
+
* re-uploaded to the FW by the next setResolution()/startRanging().
|
|
1169
|
+
*/
|
|
1170
|
+
async setCaldataXtalk(xtalkData) {
|
|
1171
|
+
if (xtalkData.length !== XTALK_BUFFER_SIZE) {
|
|
1172
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, `xtalk blob must be ${XTALK_BUFFER_SIZE} bytes`);
|
|
1173
|
+
}
|
|
1174
|
+
const resolution = await this.getResolution();
|
|
1175
|
+
this.xtalkData = xtalkData.slice();
|
|
1176
|
+
await this.setResolution(resolution);
|
|
1177
|
+
}
|
|
1178
|
+
// ---------------- detection thresholds (plugin) ----------------
|
|
1179
|
+
async getDetectionThresholdsEnable() {
|
|
1180
|
+
return (await this.dciReadData(DCI_DET_THRESH_GLOBAL_CONFIG, 8))[1];
|
|
1181
|
+
}
|
|
1182
|
+
async setDetectionThresholdsEnable(enabled) {
|
|
1183
|
+
const grp = Uint8Array.of(0x01, 0x00, 0x01, 0x00);
|
|
1184
|
+
let tmp;
|
|
1185
|
+
if (enabled) {
|
|
1186
|
+
grp[1] = 0x01;
|
|
1187
|
+
tmp = 0x04;
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
grp[1] = 0x00;
|
|
1191
|
+
tmp = 0x0c;
|
|
1192
|
+
}
|
|
1193
|
+
await this.dciReplaceData(DCI_DET_THRESH_GLOBAL_CONFIG, 8, grp, 0x00);
|
|
1194
|
+
await this.dciReplaceData(DCI_DET_THRESH_CONFIG, 20, Uint8Array.of(tmp), 0x11);
|
|
1195
|
+
}
|
|
1196
|
+
/** Return the 64 detection thresholds; low/high rescaled to real units. */
|
|
1197
|
+
async getDetectionThresholds() {
|
|
1198
|
+
const raw = await this.dciReadData(DCI_DET_THRESH_START, NB_THRESHOLDS * 12);
|
|
1199
|
+
const dv = dvOf(raw);
|
|
1200
|
+
const out = [];
|
|
1201
|
+
for (let k = 0; k < NB_THRESHOLDS; k++) {
|
|
1202
|
+
const off = k * 12;
|
|
1203
|
+
const low = dv.getInt32(off, true);
|
|
1204
|
+
const high = dv.getInt32(off + 4, true);
|
|
1205
|
+
const meas = dv.getUint8(off + 8);
|
|
1206
|
+
const scale = THRESH_SCALE[meas] ?? 1;
|
|
1207
|
+
out.push({
|
|
1208
|
+
lowThresh: Math.floor(low / scale),
|
|
1209
|
+
highThresh: Math.floor(high / scale),
|
|
1210
|
+
measurement: meas,
|
|
1211
|
+
type: dv.getUint8(off + 9),
|
|
1212
|
+
zoneNum: dv.getUint8(off + 10),
|
|
1213
|
+
operation: dv.getUint8(off + 11),
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
return out;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Program the 64 detection thresholds (missing entries default to zeros).
|
|
1220
|
+
*/
|
|
1221
|
+
async setDetectionThresholds(thresholds) {
|
|
1222
|
+
const { start, valid } = packDetectionThresholds(thresholds);
|
|
1223
|
+
await this.dciWriteData(DCI_DET_THRESH_VALID_STATUS, valid);
|
|
1224
|
+
await this.dciWriteData(DCI_DET_THRESH_START, start);
|
|
1225
|
+
}
|
|
1226
|
+
async setDetectionThresholdsAutoStop(autoStop) {
|
|
1227
|
+
await this.dciReplaceData(DCI_PIPE_CONTROL, 4, Uint8Array.of(autoStop ? 1 : 0), 0x03);
|
|
1228
|
+
}
|
|
1229
|
+
// ---------------- motion indicator (plugin) ----------------
|
|
1230
|
+
/**
|
|
1231
|
+
* Initialize a motion-indicator configuration (default distance window) and
|
|
1232
|
+
* write it to the sensor. Enables the motion output block so subsequent
|
|
1233
|
+
* frames carry motion data.
|
|
1234
|
+
*/
|
|
1235
|
+
async motionIndicatorInit(resolution) {
|
|
1236
|
+
const cfg = defaultMotionConfig(resolution);
|
|
1237
|
+
await this.dciWriteData(DCI_MOTION_DETECTOR_CFG, cfg.pack());
|
|
1238
|
+
this.motionPresent = true;
|
|
1239
|
+
return cfg;
|
|
1240
|
+
}
|
|
1241
|
+
async motionIndicatorSetResolution(cfg, resolution) {
|
|
1242
|
+
motionConfigSetResolution(cfg, resolution);
|
|
1243
|
+
await this.dciWriteData(DCI_MOTION_DETECTOR_CFG, cfg.pack());
|
|
1244
|
+
}
|
|
1245
|
+
async motionIndicatorSetDistanceMotion(cfg, distanceMinMm, distanceMaxMm) {
|
|
1246
|
+
if (distanceMaxMm - distanceMinMm > 1500 || distanceMinMm < 400 || distanceMaxMm > 4000) {
|
|
1247
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "motion set_distance");
|
|
1248
|
+
}
|
|
1249
|
+
cfg.refBinOffset = Math.trunc((distanceMinMm / 37.5348 - 4.0) * 2048.5);
|
|
1250
|
+
cfg.featureLength = Math.trunc((distanceMaxMm - distanceMinMm) / 10.0 / 15.01392 + 30.02784 / 15.01392 + 0.5);
|
|
1251
|
+
await this.dciWriteData(DCI_MOTION_DETECTOR_CFG, cfg.pack());
|
|
1252
|
+
}
|
|
1253
|
+
// ---------------- xtalk calibration run (plugin) ----------------
|
|
1254
|
+
/**
|
|
1255
|
+
* Program the sensor output list/config/enables for `resolution` (a copy of
|
|
1256
|
+
* the block startRanging() uses inline, kept separate so startRanging stays
|
|
1257
|
+
* byte-identical for the replay gate); used by calibrateXtalk.
|
|
1258
|
+
*/
|
|
1259
|
+
async programOutputConfig(resolution, cnhDataSize = null) {
|
|
1260
|
+
let dataReadSize = 0;
|
|
1261
|
+
const outputBhEnable = [0x00000fff, 0, 0, this.outputEnableW3];
|
|
1262
|
+
const output = [
|
|
1263
|
+
START_BH,
|
|
1264
|
+
METADATA_BH,
|
|
1265
|
+
COMMONDATA_BH,
|
|
1266
|
+
AMBIENT_RATE_BH,
|
|
1267
|
+
SPAD_COUNT_BH,
|
|
1268
|
+
NB_TARGET_DETECTED_BH,
|
|
1269
|
+
SIGNAL_RATE_BH,
|
|
1270
|
+
RANGE_SIGMA_MM_BH,
|
|
1271
|
+
DISTANCE_BH,
|
|
1272
|
+
REFLECTANCE_BH,
|
|
1273
|
+
TARGET_STATUS_BH,
|
|
1274
|
+
MOTION_DETECT_BH,
|
|
1275
|
+
];
|
|
1276
|
+
if (cnhDataSize !== null) {
|
|
1277
|
+
const cnhBh = (((CNH_DATA_IDX << 16) | ((Math.floor(cnhDataSize / 4) & 0xfff) << 4) | 4) >>> 0);
|
|
1278
|
+
output.push(cnhBh);
|
|
1279
|
+
outputBhEnable[0] = (outputBhEnable[0] | (1 << (output.length - 1))) >>> 0;
|
|
1280
|
+
}
|
|
1281
|
+
for (let i = 0; i < output.length; i++) {
|
|
1282
|
+
if (output[i] === 0 || !((outputBhEnable[Math.floor(i / 32)] & (1 << i % 32)) >>> 0)) {
|
|
1283
|
+
continue;
|
|
1284
|
+
}
|
|
1285
|
+
const [bhType, bhSizeIn, bhIdx] = bhFields(output[i]);
|
|
1286
|
+
let bhSize = bhSizeIn;
|
|
1287
|
+
if (bhType >= 0x1 && bhType < 0x0d) {
|
|
1288
|
+
if (bhIdx >= 0x54d0 && bhIdx < 0x54d0 + 960) {
|
|
1289
|
+
bhSize = resolution;
|
|
1290
|
+
}
|
|
1291
|
+
else if (bhIdx === CNH_DATA_IDX) {
|
|
1292
|
+
// keep CNH block size
|
|
1293
|
+
}
|
|
1294
|
+
else {
|
|
1295
|
+
bhSize = resolution * NB_TARGET_PER_ZONE;
|
|
1296
|
+
}
|
|
1297
|
+
output[i] = bhSetSize(output[i], bhSize);
|
|
1298
|
+
dataReadSize += bhType * bhSize;
|
|
1299
|
+
}
|
|
1300
|
+
else {
|
|
1301
|
+
dataReadSize += bhSize;
|
|
1302
|
+
}
|
|
1303
|
+
dataReadSize += 4;
|
|
1304
|
+
}
|
|
1305
|
+
dataReadSize += this.frameTail;
|
|
1306
|
+
await this.dciWriteData(DCI_OUTPUT_LIST, packU32ArrayLE(output));
|
|
1307
|
+
await this.dciWriteData(DCI_OUTPUT_CONFIG, packU32ArrayLE([dataReadSize, output.length + 1]));
|
|
1308
|
+
await this.dciWriteData(DCI_OUTPUT_ENABLES, packU32ArrayLE(outputBhEnable));
|
|
1309
|
+
return dataReadSize;
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* vl53l8cx_calibrate_xtalk: run on-device crosstalk calibration.
|
|
1313
|
+
*
|
|
1314
|
+
* NOTE: ported from ST ULD source but NOT verified against live hardware in
|
|
1315
|
+
* this SDK — the get/set caldata-xtalk buffer path IS the tested save/restore
|
|
1316
|
+
* route. Saves & restores resolution/frequency/int-time/sharpener/target-
|
|
1317
|
+
* order/xtalk-margin/ranging-mode around the run.
|
|
1318
|
+
*/
|
|
1319
|
+
async calibrateXtalk(reflectancePercent, nbSamples, distanceMm) {
|
|
1320
|
+
if (!(reflectancePercent >= 1 && reflectancePercent <= 99)) {
|
|
1321
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "reflectance 1..99");
|
|
1322
|
+
}
|
|
1323
|
+
if (!(distanceMm >= 600 && distanceMm <= 3000)) {
|
|
1324
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "distance 600..3000");
|
|
1325
|
+
}
|
|
1326
|
+
if (!(nbSamples >= 1 && nbSamples <= 16)) {
|
|
1327
|
+
throw new Vl53l8cxError(STATUS_INVALID_PARAM, "nb_samples 1..16");
|
|
1328
|
+
}
|
|
1329
|
+
const footer = Uint8Array.of(0x00, 0x00, 0x00, 0x0f, 0x00, 0x01, 0x03, 0x04);
|
|
1330
|
+
const cmd = Uint8Array.of(0x00, 0x03, 0x00, 0x00);
|
|
1331
|
+
const saved = {
|
|
1332
|
+
resolution: await this.getResolution(),
|
|
1333
|
+
frequency: await this.getRangingFrequencyHz(),
|
|
1334
|
+
integration: await this.getIntegrationTimeMs(),
|
|
1335
|
+
sharpener: await this.getSharpenerPercent(),
|
|
1336
|
+
targetOrder: await this.getTargetOrder(),
|
|
1337
|
+
xtalkMargin: await this.getXtalkMargin(),
|
|
1338
|
+
rangingMode: await this.getRangingMode(),
|
|
1339
|
+
};
|
|
1340
|
+
await this.setResolution(RESOLUTION_8X8);
|
|
1341
|
+
await this.p.wrMulti(0x2c28, CALIBRATE_XTALK);
|
|
1342
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "calib cmd");
|
|
1343
|
+
const refl = reflectancePercent * 16;
|
|
1344
|
+
const dist = distanceMm * 4;
|
|
1345
|
+
const u16 = (v) => {
|
|
1346
|
+
const b = new Uint8Array(2);
|
|
1347
|
+
new DataView(b.buffer).setUint16(0, v & 0xffff, true);
|
|
1348
|
+
return b;
|
|
1349
|
+
};
|
|
1350
|
+
await this.dciReplaceData(DCI_CAL_CFG, 8, u16(dist), 0x00);
|
|
1351
|
+
await this.dciReplaceData(DCI_CAL_CFG, 8, u16(refl), 0x02);
|
|
1352
|
+
await this.dciReplaceData(DCI_CAL_CFG, 8, Uint8Array.of(nbSamples), 0x04);
|
|
1353
|
+
await this.programOutputConfig(RESOLUTION_8X8);
|
|
1354
|
+
await this.p.wrMulti(UI_CMD_END - 3, cmd);
|
|
1355
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "calib start");
|
|
1356
|
+
let timeout = 0;
|
|
1357
|
+
for (;;) {
|
|
1358
|
+
const buf = await this.p.rdMulti(0x0, 4);
|
|
1359
|
+
if (buf[0] !== STATUS_ERROR) {
|
|
1360
|
+
if (buf[2] >= 0x7f && ((buf[3] & 0x80) >> 7) === 1) {
|
|
1361
|
+
this.xtalkData = this.defaultXtalk; // XTALK_FAILED
|
|
1362
|
+
}
|
|
1363
|
+
break;
|
|
1364
|
+
}
|
|
1365
|
+
if (timeout >= 400)
|
|
1366
|
+
throw new Vl53l8cxError(STATUS_ERROR, "xtalk calibration");
|
|
1367
|
+
await this.p.sleepMs(50);
|
|
1368
|
+
timeout += 1;
|
|
1369
|
+
}
|
|
1370
|
+
await this.p.wrMulti(0x2fb8, GET_XTALK_CMD);
|
|
1371
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "get xtalk");
|
|
1372
|
+
const buf = await this.p.rdMulti(UI_CMD_START, XTALK_BUFFER_SIZE + 4);
|
|
1373
|
+
const out = new Uint8Array(XTALK_BUFFER_SIZE);
|
|
1374
|
+
out.set(buf.subarray(8, XTALK_BUFFER_SIZE), 0);
|
|
1375
|
+
out.set(footer, XTALK_BUFFER_SIZE - 8);
|
|
1376
|
+
this.xtalkData = out;
|
|
1377
|
+
await this.p.wrMulti(0x2c34, this.defaultCfg);
|
|
1378
|
+
await this.pollForAnswer(4, 1, UI_CMD_STATUS, 0xff, 0x03, "restore cfg");
|
|
1379
|
+
await this.setResolution(saved.resolution);
|
|
1380
|
+
await this.setRangingFrequencyHz(saved.frequency);
|
|
1381
|
+
await this.setIntegrationTimeMs(saved.integration);
|
|
1382
|
+
await this.setSharpenerPercent(saved.sharpener);
|
|
1383
|
+
await this.setTargetOrder(saved.targetOrder);
|
|
1384
|
+
await this.setXtalkMargin(saved.xtalkMargin);
|
|
1385
|
+
await this.setRangingMode(saved.rangingMode);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
//# sourceMappingURL=uld.js.map
|