@depz/sensor-sdk 0.2.2 → 0.3.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.
Files changed (151) hide show
  1. package/README.md +13 -5
  2. package/dist/dataset.d.ts.map +1 -1
  3. package/dist/dataset.js +66 -2
  4. package/dist/dataset.js.map +1 -1
  5. package/dist/discovery.d.ts.map +1 -1
  6. package/dist/discovery.js +25 -2
  7. package/dist/discovery.js.map +1 -1
  8. package/dist/index.d.ts +21 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +16 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/protocol/bno055.d.ts +101 -0
  13. package/dist/protocol/bno055.d.ts.map +1 -0
  14. package/dist/protocol/bno055.js +105 -0
  15. package/dist/protocol/bno055.js.map +1 -0
  16. package/dist/protocol/identity.d.ts +1 -1
  17. package/dist/protocol/identity.d.ts.map +1 -1
  18. package/dist/protocol/identity.js +6 -0
  19. package/dist/protocol/identity.js.map +1 -1
  20. package/dist/protocol/vl53l7.d.ts +77 -0
  21. package/dist/protocol/vl53l7.d.ts.map +1 -0
  22. package/dist/protocol/vl53l7.js +99 -0
  23. package/dist/protocol/vl53l7.js.map +1 -0
  24. package/dist/protocol/vl53lx.d.ts +72 -0
  25. package/dist/protocol/vl53lx.d.ts.map +1 -0
  26. package/dist/protocol/vl53lx.js +87 -0
  27. package/dist/protocol/vl53lx.js.map +1 -0
  28. package/dist/sensors/bno055/bno055.d.ts +207 -0
  29. package/dist/sensors/bno055/bno055.d.ts.map +1 -0
  30. package/dist/sensors/bno055/bno055.js +547 -0
  31. package/dist/sensors/bno055/bno055.js.map +1 -0
  32. package/dist/sensors/bno055/regs.d.ts +271 -0
  33. package/dist/sensors/bno055/regs.d.ts.map +1 -0
  34. package/dist/sensors/bno055/regs.js +366 -0
  35. package/dist/sensors/bno055/regs.js.map +1 -0
  36. package/dist/sensors/bno086/bno086.d.ts +13 -0
  37. package/dist/sensors/bno086/bno086.d.ts.map +1 -1
  38. package/dist/sensors/bno086/bno086.js +26 -1
  39. package/dist/sensors/bno086/bno086.js.map +1 -1
  40. package/dist/sensors/bno086/sh2.d.ts.map +1 -1
  41. package/dist/sensors/bno086/sh2.js +5 -2
  42. package/dist/sensors/bno086/sh2.js.map +1 -1
  43. package/dist/sensors/vl53l7/vl53l7.d.ts +97 -0
  44. package/dist/sensors/vl53l7/vl53l7.d.ts.map +1 -0
  45. package/dist/sensors/vl53l7/vl53l7.js +138 -0
  46. package/dist/sensors/vl53l7/vl53l7.js.map +1 -0
  47. package/dist/sensors/vl53l8/assets/index.d.ts +5 -1
  48. package/dist/sensors/vl53l8/assets/index.d.ts.map +1 -1
  49. package/dist/sensors/vl53l8/assets/index.js +7 -1
  50. package/dist/sensors/vl53l8/assets/index.js.map +1 -1
  51. package/dist/sensors/vl53l8/assets/l7ch.d.ts +12 -0
  52. package/dist/sensors/vl53l8/assets/l7ch.d.ts.map +1 -0
  53. package/dist/sensors/vl53l8/assets/l7ch.js +18 -0
  54. package/dist/sensors/vl53l8/assets/l7ch.js.map +1 -0
  55. package/dist/sensors/vl53l8/assets/l7cx.d.ts +14 -0
  56. package/dist/sensors/vl53l8/assets/l7cx.d.ts.map +1 -0
  57. package/dist/sensors/vl53l8/assets/l7cx.js +20 -0
  58. package/dist/sensors/vl53l8/assets/l7cx.js.map +1 -0
  59. package/dist/sensors/vl53l8/cnh.d.ts +4 -3
  60. package/dist/sensors/vl53l8/cnh.d.ts.map +1 -1
  61. package/dist/sensors/vl53l8/cnh.js +4 -3
  62. package/dist/sensors/vl53l8/cnh.js.map +1 -1
  63. package/dist/sensors/vl53l8/uld.d.ts +50 -9
  64. package/dist/sensors/vl53l8/uld.d.ts.map +1 -1
  65. package/dist/sensors/vl53l8/uld.js +241 -35
  66. package/dist/sensors/vl53l8/uld.js.map +1 -1
  67. package/dist/sensors/vl53l8/vl53l8.d.ts +8 -3
  68. package/dist/sensors/vl53l8/vl53l8.d.ts.map +1 -1
  69. package/dist/sensors/vl53l8/vl53l8.js +12 -7
  70. package/dist/sensors/vl53l8/vl53l8.js.map +1 -1
  71. package/dist/sensors/vl53lx/decode.d.ts +70 -0
  72. package/dist/sensors/vl53lx/decode.d.ts.map +1 -0
  73. package/dist/sensors/vl53lx/decode.js +108 -0
  74. package/dist/sensors/vl53lx/decode.js.map +1 -0
  75. package/dist/sensors/vl53lx/uld/arith.d.ts +23 -0
  76. package/dist/sensors/vl53lx/uld/arith.d.ts.map +1 -0
  77. package/dist/sensors/vl53lx/uld/arith.js +42 -0
  78. package/dist/sensors/vl53lx/uld/arith.js.map +1 -0
  79. package/dist/sensors/vl53lx/uld/bare/core.d.ts +345 -0
  80. package/dist/sensors/vl53lx/uld/bare/core.d.ts.map +1 -0
  81. package/dist/sensors/vl53lx/uld/bare/core.js +1085 -0
  82. package/dist/sensors/vl53lx/uld/bare/core.js.map +1 -0
  83. package/dist/sensors/vl53lx/uld/bare/driver.d.ts +91 -0
  84. package/dist/sensors/vl53lx/uld/bare/driver.d.ts.map +1 -0
  85. package/dist/sensors/vl53lx/uld/bare/driver.js +206 -0
  86. package/dist/sensors/vl53lx/uld/bare/driver.js.map +1 -0
  87. package/dist/sensors/vl53lx/uld/bare/hist.d.ts +180 -0
  88. package/dist/sensors/vl53lx/uld/bare/hist.d.ts.map +1 -0
  89. package/dist/sensors/vl53lx/uld/bare/hist.js +949 -0
  90. package/dist/sensors/vl53lx/uld/bare/hist.js.map +1 -0
  91. package/dist/sensors/vl53lx/uld/bare/image.d.ts +53 -0
  92. package/dist/sensors/vl53lx/uld/bare/image.d.ts.map +1 -0
  93. package/dist/sensors/vl53lx/uld/bare/image.js +108 -0
  94. package/dist/sensors/vl53lx/uld/bare/image.js.map +1 -0
  95. package/dist/sensors/vl53lx/uld/bare/imath.d.ts +39 -0
  96. package/dist/sensors/vl53lx/uld/bare/imath.d.ts.map +1 -0
  97. package/dist/sensors/vl53lx/uld/bare/imath.js +99 -0
  98. package/dist/sensors/vl53lx/uld/bare/imath.js.map +1 -0
  99. package/dist/sensors/vl53lx/uld/bare/index.d.ts +22 -0
  100. package/dist/sensors/vl53lx/uld/bare/index.d.ts.map +1 -0
  101. package/dist/sensors/vl53lx/uld/bare/index.js +22 -0
  102. package/dist/sensors/vl53lx/uld/bare/index.js.map +1 -0
  103. package/dist/sensors/vl53lx/uld/bare/nvm.d.ts +85 -0
  104. package/dist/sensors/vl53lx/uld/bare/nvm.d.ts.map +1 -0
  105. package/dist/sensors/vl53lx/uld/bare/nvm.js +148 -0
  106. package/dist/sensors/vl53lx/uld/bare/nvm.js.map +1 -0
  107. package/dist/sensors/vl53lx/uld/bare/regs.d.ts +54 -0
  108. package/dist/sensors/vl53lx/uld/bare/regs.d.ts.map +1 -0
  109. package/dist/sensors/vl53lx/uld/bare/regs.js +496 -0
  110. package/dist/sensors/vl53lx/uld/bare/regs.js.map +1 -0
  111. package/dist/sensors/vl53lx/uld/bare/tuning.d.ts +15 -0
  112. package/dist/sensors/vl53lx/uld/bare/tuning.d.ts.map +1 -0
  113. package/dist/sensors/vl53lx/uld/bare/tuning.js +303 -0
  114. package/dist/sensors/vl53lx/uld/bare/tuning.js.map +1 -0
  115. package/dist/sensors/vl53lx/uld/base.d.ts +129 -0
  116. package/dist/sensors/vl53lx/uld/base.d.ts.map +1 -0
  117. package/dist/sensors/vl53lx/uld/base.js +166 -0
  118. package/dist/sensors/vl53lx/uld/base.js.map +1 -0
  119. package/dist/sensors/vl53lx/uld/l0x.d.ts +306 -0
  120. package/dist/sensors/vl53lx/uld/l0x.d.ts.map +1 -0
  121. package/dist/sensors/vl53lx/uld/l0x.js +1492 -0
  122. package/dist/sensors/vl53lx/uld/l0x.js.map +1 -0
  123. package/dist/sensors/vl53lx/uld/l1.d.ts +69 -0
  124. package/dist/sensors/vl53lx/uld/l1.d.ts.map +1 -0
  125. package/dist/sensors/vl53lx/uld/l1.js +262 -0
  126. package/dist/sensors/vl53lx/uld/l1.js.map +1 -0
  127. package/dist/sensors/vl53lx/uld/l3.d.ts +25 -0
  128. package/dist/sensors/vl53lx/uld/l3.d.ts.map +1 -0
  129. package/dist/sensors/vl53lx/uld/l3.js +51 -0
  130. package/dist/sensors/vl53lx/uld/l3.js.map +1 -0
  131. package/dist/sensors/vl53lx/uld/l4.d.ts +22 -0
  132. package/dist/sensors/vl53lx/uld/l4.d.ts.map +1 -0
  133. package/dist/sensors/vl53lx/uld/l4.js +99 -0
  134. package/dist/sensors/vl53lx/uld/l4.js.map +1 -0
  135. package/dist/sensors/vl53lx/uld/link.d.ts +42 -0
  136. package/dist/sensors/vl53lx/uld/link.d.ts.map +1 -0
  137. package/dist/sensors/vl53lx/uld/link.js +28 -0
  138. package/dist/sensors/vl53lx/uld/link.js.map +1 -0
  139. package/dist/sensors/vl53lx/uld/registry.d.ts +68 -0
  140. package/dist/sensors/vl53lx/uld/registry.d.ts.map +1 -0
  141. package/dist/sensors/vl53lx/uld/registry.js +139 -0
  142. package/dist/sensors/vl53lx/uld/registry.js.map +1 -0
  143. package/dist/sensors/vl53lx/uld/vl53l1-die.d.ts +172 -0
  144. package/dist/sensors/vl53lx/uld/vl53l1-die.d.ts.map +1 -0
  145. package/dist/sensors/vl53lx/uld/vl53l1-die.js +431 -0
  146. package/dist/sensors/vl53lx/uld/vl53l1-die.js.map +1 -0
  147. package/dist/sensors/vl53lx/vl53lx.d.ts +323 -0
  148. package/dist/sensors/vl53lx/vl53lx.d.ts.map +1 -0
  149. package/dist/sensors/vl53lx/vl53lx.js +688 -0
  150. package/dist/sensors/vl53lx/vl53lx.js.map +1 -0
  151. package/package.json +9 -2
@@ -0,0 +1,688 @@
1
+ /**
2
+ * VL53L 1D ToF family — VL53L0X, VL53L1CX, VL53L1CB, VL53L3CX, VL53L4CD,
3
+ * VL53L4CX — over the `APP_VL53L0_4` register bridge (contracts/12).
4
+ *
5
+ * The MCU is a thin I2C bridge that knows no sensor; every ULD runs here on
6
+ * the host (`uld/`). Two axes pick how a board is driven (`uld/registry.ts`):
7
+ * the **product** (whose parameter set to load — normally the part the
8
+ * board's device name carries; naming a neighbour borrows its driver) and the
9
+ * **driver kind** (`uld`, `ulp` or `histogram`). Every product answers with
10
+ * the same `Vl53lxMeasurement`; what differs is `supports(group)`.
11
+ *
12
+ * Mirrors the Python reference `depz_sensor_sdk.vl53lx`. TS-only difference:
13
+ * the product-level description is `identifyProduct()`, because
14
+ * `DepzDevice.identify()` already classifies the firmware.
15
+ */
16
+ import { DepzDevice, StreamQueue } from "../../device/device.js";
17
+ import { DepzError, DepzTimeoutError, StatusError } from "../../errors.js";
18
+ import { Status } from "../../protocol/common.js";
19
+ import { XFER_MAX, XSHUT_OFF, XSHUT_ON, XSHUT_RESET, packReadReg, packSetI2cSpeed, packWriteReg, packXshut, unpackRegData, unpackStream, } from "../../protocol/vl53l4.js";
20
+ import { Vl53lxCmd, Vl53lxRpt, packVl53lxSetAddrWidth, packVl53lxStartStream, unpackVl53lxInfo, } from "../../protocol/vl53lx.js";
21
+ import { chunkedRead, chunkedWrite } from "../reg-bridge.js";
22
+ import { BridgePlatform } from "./uld/base.js";
23
+ import { ProtocolError, Vl53Error } from "./uld/link.js";
24
+ import * as registry from "./uld/registry.js";
25
+ export { XSHUT_OFF, XSHUT_ON, XSHUT_RESET };
26
+ /**
27
+ * Statuses that mean "this distance is real". A histogram product reports 6
28
+ * on the first frame of a stream and 11 on a merged pulse: use this, not
29
+ * `status === 0`, as the validity test on histogram products.
30
+ */
31
+ export const PLOTTABLE_STATUSES = [0, 6, 11];
32
+ function toVl53lxMeasurement(timestampUs, m, bins = null) {
33
+ return {
34
+ timestampUs,
35
+ distanceMm: m.distanceMm,
36
+ status: m.status,
37
+ statusText: m.statusText,
38
+ signalKcps: m.signalKcps,
39
+ ambientKcps: m.ambientKcps,
40
+ sigmaMm: m.sigmaMm,
41
+ spads: m.spads,
42
+ targets: [...m.targets],
43
+ extra: { ...m.extra },
44
+ bins,
45
+ valid: m.status === 0,
46
+ plottable: PLOTTABLE_STATUSES.includes(m.status),
47
+ };
48
+ }
49
+ /**
50
+ * The distances a chart should draw for one measurement: the plottable
51
+ * targets in driver order, or the single distance on a light driver.
52
+ */
53
+ export function plotDistances(m) {
54
+ if (m.targets.length > 0) {
55
+ return m.targets.filter((t) => PLOTTABLE_STATUSES.includes(t.status)).map((t) => t.distanceMm);
56
+ }
57
+ return PLOTTABLE_STATUSES.includes(m.status) ? [m.distanceMm] : [];
58
+ }
59
+ /** The first plottable target, or null when the frame produced nothing usable. */
60
+ export function primaryTarget(m) {
61
+ for (const t of m.targets)
62
+ if (PLOTTABLE_STATUSES.includes(t.status))
63
+ return t;
64
+ return null;
65
+ }
66
+ function isThenable(x) {
67
+ return typeof x === "object" && x !== null && typeof x.then === "function";
68
+ }
69
+ /**
70
+ * A board of the VL53L 1D family (firmware `APP_VL53L0_4`).
71
+ *
72
+ * `init()` binds the (product, driver kind) pair and runs the ULD's
73
+ * sensorInit; `configure()` re-initialises and applies the ranging
74
+ * configuration — call it before every run. Then `startRanging()` arms the MCU
75
+ * stream and measurements arrive via `onMeasurement` / `measurements()` /
76
+ * `getMeasurement()`. Configuration must not change while ranging.
77
+ *
78
+ * This generic class serves any product (read from the board's device name,
79
+ * or `product` at init); the per-product subclasses (`Vl53l0x`...) fix it.
80
+ */
81
+ export class Vl53lx extends DepzDevice {
82
+ /** The product this class drives; null = read it from the device name. */
83
+ static PRODUCT = null;
84
+ driverInst = null;
85
+ productName = null;
86
+ driverKindName = null;
87
+ caveat = null;
88
+ boardNameCache = null;
89
+ measureCbs = [];
90
+ measureQueues = [];
91
+ rangingFlag = false;
92
+ streamParseErrorCount = 0;
93
+ /** Keeps asynchronous histogram decodes in frame order. */
94
+ decodeChain = Promise.resolve();
95
+ /** MCU timestamp of the latest register read (poll-mode measurements). */
96
+ lastRegTimestampUs = 0n;
97
+ sleepImpl;
98
+ platform;
99
+ /**
100
+ * The `BridgeDevice` the ULD ports talk to: register transfers split at the
101
+ * bridge's 253-byte limit; a non-OK status or a missing answer surfaces as
102
+ * `ProtocolError`, exactly what the ports catch.
103
+ */
104
+ bridge = {
105
+ readReg: async (addr, length) => {
106
+ try {
107
+ return await chunkedRead(addr, length, {
108
+ chunkSize: XFER_MAX,
109
+ readChunk: async (a, n) => {
110
+ const rep = await this.request(Vl53lxCmd.ReadReg, packReadReg(a, n), {
111
+ matcher: DepzDevice.expectReport(Vl53lxRpt.RegData, unpackRegData),
112
+ timeoutMs: 2000,
113
+ });
114
+ return { data: rep.data, timestampUs: rep.timestampUs };
115
+ },
116
+ onTimestamp: (ts) => {
117
+ this.lastRegTimestampUs = ts;
118
+ },
119
+ });
120
+ }
121
+ catch (err) {
122
+ throw Vl53lx.asProtocolError(err, true);
123
+ }
124
+ },
125
+ writeReg: async (addr, data) => {
126
+ try {
127
+ await chunkedWrite(addr, data, {
128
+ chunkSize: XFER_MAX,
129
+ writeChunk: (a, chunk) => this.request(Vl53lxCmd.WriteReg, packWriteReg(a, chunk), {
130
+ okCompletes: true,
131
+ timeoutMs: 2000,
132
+ }),
133
+ });
134
+ }
135
+ catch (err) {
136
+ throw Vl53lx.asProtocolError(err);
137
+ }
138
+ },
139
+ setI2cSpeed: async (khz) => {
140
+ try {
141
+ await this.request(Vl53lxCmd.SetI2cSpeed, packSetI2cSpeed(khz), { okCompletes: true });
142
+ }
143
+ catch (err) {
144
+ throw Vl53lx.asProtocolError(err);
145
+ }
146
+ },
147
+ setAddrWidth: async (width) => {
148
+ try {
149
+ await this.request(Vl53lxCmd.SetAddrWidth, packVl53lxSetAddrWidth(width), {
150
+ okCompletes: true,
151
+ });
152
+ }
153
+ catch (err) {
154
+ throw Vl53lx.asProtocolError(err);
155
+ }
156
+ },
157
+ clearI2cErrors: async () => {
158
+ // A resetting die NACKs its own address for a moment; only the driver
159
+ // knows those NACKs were expected, so the host zeroes the counter once
160
+ // its init is through. A v2.00 bridge does not know the command.
161
+ try {
162
+ await this.request(Vl53lxCmd.ClearI2cErrors, new Uint8Array(0), { okCompletes: true });
163
+ }
164
+ catch (err) {
165
+ if (err instanceof StatusError && err.status === Status.ErrInvalidCmd) {
166
+ throw new DepzError("the board's firmware predates APP_VL53L0_4_v0.24 (protocol v2.01) " +
167
+ "and cannot clear its I2C error counter — reflash it");
168
+ }
169
+ throw Vl53lx.asProtocolError(err);
170
+ }
171
+ },
172
+ xshut: async (action) => {
173
+ try {
174
+ await this.request(Vl53lxCmd.Xshut, packXshut(action), {
175
+ okCompletes: true,
176
+ timeoutMs: 1000,
177
+ });
178
+ }
179
+ catch (err) {
180
+ throw Vl53lx.asProtocolError(err);
181
+ }
182
+ },
183
+ };
184
+ /**
185
+ * A reset makes the die NACK for a moment (the VL53L0X soft reset does it);
186
+ * the driver waits those out, and they are no bus fault.
187
+ */
188
+ async sensorInit(drv) {
189
+ await drv.sensorInit();
190
+ await this.bridge.clearI2cErrors();
191
+ }
192
+ /** StatusError / DepzTimeoutError (and a short register read) → ProtocolError. */
193
+ static asProtocolError(err, shortReadToo = false) {
194
+ if (err instanceof StatusError || err instanceof DepzTimeoutError) {
195
+ return new ProtocolError(err.message);
196
+ }
197
+ // chunkedRead reports a short reply as a plain DepzError.
198
+ if (shortReadToo && err instanceof DepzError && err.constructor === DepzError) {
199
+ return new ProtocolError(err.message);
200
+ }
201
+ return err;
202
+ }
203
+ constructor(transport, opts) {
204
+ super(transport, opts);
205
+ this.sleepImpl = opts?.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
206
+ this.platform = new BridgePlatform(this.bridge, (ms) => this.sleepImpl(ms));
207
+ }
208
+ get classProduct() {
209
+ return this.constructor.PRODUCT;
210
+ }
211
+ // ── identity ─────────────────────────────────────────────────────────────
212
+ /**
213
+ * The board's device name (bootloader metablock), e.g.
214
+ * `DEPZ ToF Sensor VL53L4CX USB v2.1 TOVJALN523`. Read once, then cached.
215
+ */
216
+ async boardName() {
217
+ if (this.boardNameCache === null)
218
+ this.boardNameCache = await this.getDeviceName();
219
+ return this.boardNameCache;
220
+ }
221
+ /** The product the device name carries, or null on an unstamped board. */
222
+ async detected() {
223
+ return registry.productFromBoardName(await this.boardName());
224
+ }
225
+ /** The product init() bound (null before init). */
226
+ get product() {
227
+ return this.productName;
228
+ }
229
+ get driverKind() {
230
+ return this.driverKindName;
231
+ }
232
+ /** The ULD port in use — escape hatch for product-specific calls. */
233
+ get driver() {
234
+ if (this.driverInst === null)
235
+ throw new DepzError("call init() first");
236
+ return this.driverInst;
237
+ }
238
+ get initialized() {
239
+ return this.driverInst !== null;
240
+ }
241
+ /** The driver kinds a product has (default: this board's). */
242
+ async driverKinds(product) {
243
+ const name = product || this.classProduct || (await this.detected());
244
+ return name ? registry.driverKinds(name) : [];
245
+ }
246
+ // ── lifecycle ────────────────────────────────────────────────────────────
247
+ /**
248
+ * Bind the (product, driver kind) pair and initialise the sensor.
249
+ *
250
+ * `product` defaults to this class's product, else the board's device name;
251
+ * `driver` defaults to the product's first kind (`uld`, else `ulp`, else
252
+ * `histogram`). A pair the table has no row for throws
253
+ * `NotImplementedError` naming what the product has.
254
+ */
255
+ async init(driver, opts = {}) {
256
+ this.requireNotRanging();
257
+ const name = opts.product || this.classProduct || (await this.detected());
258
+ if (!name) {
259
+ throw new DepzError(`board '${await this.boardName()}' carries no product number — pass product explicitly`);
260
+ }
261
+ const kind = driver || registry.driverKinds(name)[0];
262
+ if (kind === undefined) {
263
+ throw new registry.NotImplementedError(`${name} has no driver in this build`);
264
+ }
265
+ const [DriverCls, caveat] = registry.driverFor(name, kind);
266
+ // Sticky on the bridge and 2 after a reset: set before the first register
267
+ // access, model id included.
268
+ await this.bridge.setAddrWidth(DriverCls.ADDR_WIDTH);
269
+ const drv = new DriverCls(this.platform, name);
270
+ await this.sensorInit(drv);
271
+ this.driverInst = drv;
272
+ this.productName = name;
273
+ this.driverKindName = kind;
274
+ this.caveat = caveat;
275
+ }
276
+ /** Everything about what is connected (needs init()). */
277
+ async identifyProduct() {
278
+ const drv = this.driver;
279
+ const modelId = await drv.modelId();
280
+ const detected = await this.detected();
281
+ return {
282
+ board: await this.boardName(),
283
+ detected,
284
+ product: this.productName,
285
+ driver: this.driverKindName,
286
+ driverClass: drv.constructor.name,
287
+ kinds: registry.driverKinds(this.productName),
288
+ modelId,
289
+ modelIdOk: registry.modelIdOk(this.productName, modelId),
290
+ supports: new Set(drv.SUPPORTS),
291
+ modes: [...drv.MODES],
292
+ reachMm: registry.reachMm(detected || this.productName),
293
+ driverReachMm: drv.reachMm(),
294
+ budgetMs: drv.BUDGET_MS,
295
+ budgetChoices: this.budgetChoices(),
296
+ histogram: drv.HISTOGRAM,
297
+ maxKhz: drv.MAX_KHZ,
298
+ caveat: this.caveat,
299
+ };
300
+ }
301
+ /**
302
+ * The lines a UI should show about the chosen pair: product named by hand,
303
+ * a borrowed driver, a pair that reaches less than the board is rated for,
304
+ * the driver's caveat.
305
+ */
306
+ async notes() {
307
+ const drv = this.driver;
308
+ const detected = await this.detected();
309
+ const out = [];
310
+ if (detected === null) {
311
+ out.push(`${this.productName}: board name carries no product number, named by hand`);
312
+ }
313
+ else if (detected !== this.productName) {
314
+ out.push(`${this.productName}: borrowing this driver - the board says it is a ${detected}`);
315
+ }
316
+ const rated = registry.reachMm(detected || this.productName);
317
+ const reach = drv.reachMm();
318
+ if (rated && reach && reach < rated) {
319
+ out.push(`${this.productName}/${this.driverKindName}: this pair reaches ${reach} mm, the board ` +
320
+ `is rated ${rated} mm - past ${reach} mm the phase wraps and frames come back ` +
321
+ "with status 4");
322
+ }
323
+ if (this.caveat)
324
+ out.push(`${this.productName}/${this.driverKindName}: ${this.caveat}`);
325
+ return out;
326
+ }
327
+ /**
328
+ * Whether this product/driver serves an optional capability group: mode,
329
+ * timing, offset, calib_offset, xtalk, calib_xtalk, thresholds,
330
+ * signal_thresh, sigma_thresh, roi, temp_update, refspad.
331
+ */
332
+ supports(group) {
333
+ return this.driver.SUPPORTS.has(group);
334
+ }
335
+ /** Named ranging modes, first = what init leaves; [] if none. */
336
+ get modes() {
337
+ return [...this.driver.MODES];
338
+ }
339
+ // ── sensor power (XSHUT) and bridge diagnostics ─────────────────────────
340
+ /**
341
+ * Drive XSHUT: XSHUT_OFF / XSHUT_ON / XSHUT_RESET (1 ms pulse + 5 ms wait;
342
+ * the host confirms the boot). OFF and RESET stop the stream; the sensor
343
+ * then holds none of the configuration — init() again.
344
+ */
345
+ async xshut(action) {
346
+ await this.request(Vl53lxCmd.Xshut, packXshut(action), { okCompletes: true, timeoutMs: 1000 });
347
+ this.rangingFlag = false;
348
+ this.driverInst = null;
349
+ }
350
+ /** RPT_VL53_INFO — the bridge's own counters; safe while streaming. */
351
+ bridgeInfo() {
352
+ return this.request(Vl53lxCmd.GetInfo, undefined, {
353
+ matcher: DepzDevice.expectReport(Vl53lxRpt.Info, unpackVl53lxInfo),
354
+ });
355
+ }
356
+ // ── configuration (init() first; not while ranging) ─────────────────────
357
+ /**
358
+ * Re-initialise the sensor and apply a ranging configuration. The re-init
359
+ * is deliberate (the only way to know what the configuration registers
360
+ * hold). `mode` goes on before the budget; `offsetMm` / `xtalkKcps`
361
+ * re-apply a stored calibration.
362
+ */
363
+ async configure(opts = {}) {
364
+ this.requireNotRanging();
365
+ const drv = this.driver;
366
+ await this.sensorInit(drv);
367
+ if (opts.mode != null)
368
+ await drv.setMode(opts.mode);
369
+ await drv.setRangeTiming(opts.budgetMs ?? 50, opts.interMs ?? 0);
370
+ if (opts.offsetMm != null)
371
+ await this.need("offset").setOffset(opts.offsetMm);
372
+ if (opts.xtalkKcps != null)
373
+ await this.need("xtalk").setXtalk(opts.xtalkKcps);
374
+ }
375
+ /** → [timingBudgetMs, interMeasurementMs]; 0 for the period = continuous. */
376
+ getRangeTiming() {
377
+ return this.driver.getRangeTiming();
378
+ }
379
+ /** The ranging mode in use, or null on a product without modes. */
380
+ async getMode() {
381
+ return this.supports("mode") ? this.driver.getMode() : null;
382
+ }
383
+ /** The only budgets accepted right now (ascending), or [] for any in range. */
384
+ budgetChoices() {
385
+ return [...this.driver.budgetChoices()];
386
+ }
387
+ /** The nearest budget this product will actually accept. */
388
+ snapBudget(budgetMs) {
389
+ const choices = this.budgetChoices();
390
+ if (choices.length > 0) {
391
+ let best = choices[0];
392
+ for (const c of choices)
393
+ if (Math.abs(c - budgetMs) < Math.abs(best - budgetMs))
394
+ best = c;
395
+ return best;
396
+ }
397
+ const [low, high] = this.driver.BUDGET_MS;
398
+ return Math.max(low, Math.min(high, budgetMs));
399
+ }
400
+ // Capability-gated passthroughs. Each throws DepzError on a product/driver
401
+ // that does not serve the group (see supports()).
402
+ getOffsetMm() {
403
+ return this.need("offset").getOffset();
404
+ }
405
+ async setOffsetMm(offsetMm) {
406
+ this.requireNotRanging();
407
+ await this.need("offset").setOffset(offsetMm);
408
+ }
409
+ getXtalkKcps() {
410
+ return this.need("xtalk").getXtalk();
411
+ }
412
+ async setXtalkKcps(xtalkKcps) {
413
+ this.requireNotRanging();
414
+ await this.need("xtalk").setXtalk(xtalkKcps);
415
+ }
416
+ /**
417
+ * Offset calibration against a flat target at `targetDistMm`; returns the
418
+ * offset now programmed. Store it on the host (sensor RAM, lost on reset).
419
+ */
420
+ async calibrateOffset(targetDistMm, nbSamples) {
421
+ this.requireNotRanging();
422
+ return this.need("calib_offset").calibrateOffset(targetDistMm, nbSamples);
423
+ }
424
+ /** Crosstalk calibration; returns the xtalk now programmed (kcps). */
425
+ async calibrateXtalk(targetDistMm, nbSamples) {
426
+ this.requireNotRanging();
427
+ return this.need("calib_xtalk").calibrateXtalk(targetDistMm, nbSamples);
428
+ }
429
+ /** → [distanceLowMm, distanceHighMm, window]. */
430
+ getDetectionThresholds() {
431
+ return this.need("thresholds").getDetectionThresholds();
432
+ }
433
+ /** Arm the distance-window interrupt; stays armed until the next init/configure. */
434
+ async setDetectionThresholds(distanceLowMm, distanceHighMm, window) {
435
+ this.requireNotRanging();
436
+ await this.need("thresholds").setDetectionThresholds(distanceLowMm, distanceHighMm, window);
437
+ }
438
+ getSignalThresholdKcps() {
439
+ return this.need("signal_thresh").getSignalThreshold();
440
+ }
441
+ async setSignalThresholdKcps(signalKcps) {
442
+ this.requireNotRanging();
443
+ await this.need("signal_thresh").setSignalThreshold(signalKcps);
444
+ }
445
+ getSigmaThresholdMm() {
446
+ return this.need("sigma_thresh").getSigmaThreshold();
447
+ }
448
+ async setSigmaThresholdMm(sigmaMm) {
449
+ this.requireNotRanging();
450
+ await this.need("sigma_thresh").setSigmaThreshold(sigmaMm);
451
+ }
452
+ /** → [x, y] SPAD window size. */
453
+ getRoi() {
454
+ return this.need("roi").getRoi();
455
+ }
456
+ async setRoi(x, y) {
457
+ this.requireNotRanging();
458
+ await this.need("roi").setRoi(x, y);
459
+ }
460
+ getRoiCenter() {
461
+ return this.need("roi").getRoiCenter();
462
+ }
463
+ async setRoiCenter(centerSpad) {
464
+ this.requireNotRanging();
465
+ await this.need("roi").setRoiCenter(centerSpad);
466
+ }
467
+ /** Re-run VHV after an ambient change over 8 °C. */
468
+ async startTemperatureUpdate() {
469
+ this.requireNotRanging();
470
+ await this.need("temp_update").startTemperatureUpdate();
471
+ }
472
+ /** VL53L0X: re-measure the reference SPADs → [count, isAperture]. */
473
+ async performRefSpadManagement() {
474
+ this.requireNotRanging();
475
+ return this.need("refspad").performRefSpadManagement();
476
+ }
477
+ // ── ranging ──────────────────────────────────────────────────────────────
478
+ /**
479
+ * Start the sensor's ranging loop and arm the MCU stream: one
480
+ * RPT_VL53_STREAM per INT edge, followed on the MCU by the driver's
481
+ * interrupt-release writes.
482
+ */
483
+ async startRanging() {
484
+ this.requireNotRanging();
485
+ const drv = this.driver;
486
+ await drv.startRanging();
487
+ const [addr, length] = drv.streamBlock();
488
+ await this.request(Vl53lxCmd.StartStream, packVl53lxStartStream(addr, length, drv.CLEAR_STEPS), { okCompletes: true });
489
+ this.rangingFlag = true;
490
+ }
491
+ async stopRanging() {
492
+ if (!this.rangingFlag)
493
+ return;
494
+ // Clear host state and stop the sensor even if the STOP_STREAM ack fails.
495
+ try {
496
+ await this.request(Vl53lxCmd.StopStream, undefined, { okCompletes: true });
497
+ }
498
+ finally {
499
+ this.rangingFlag = false;
500
+ await this.driver.stopRanging();
501
+ }
502
+ }
503
+ get ranging() {
504
+ return this.rangingFlag;
505
+ }
506
+ /**
507
+ * Single poll-mode measurement: start ranging, wait for data-ready, read,
508
+ * release the interrupt, stop. Rejects while the stream runs.
509
+ */
510
+ async measureOnce(timeoutMs = 1000) {
511
+ this.requireNotRanging();
512
+ const drv = this.driver;
513
+ await drv.startRanging();
514
+ let m;
515
+ let bins = null;
516
+ try {
517
+ await drv.waitDataReady(timeoutMs / 1000);
518
+ if (drv.HISTOGRAM) {
519
+ const [addr, length] = drv.streamBlock();
520
+ const h = drv;
521
+ bins = await h.binData(await this.platform.rdMulti(addr, length));
522
+ m = h.toMeasurement(bins);
523
+ }
524
+ else {
525
+ m = await drv.readMeasurement();
526
+ }
527
+ await drv.clearInterrupt();
528
+ }
529
+ finally {
530
+ await drv.stopRanging();
531
+ }
532
+ return toVl53lxMeasurement(this.lastRegTimestampUs, m, bins);
533
+ }
534
+ /** Subscribe to streamed measurements. Returns an unsubscribe function. */
535
+ onMeasurement(cb) {
536
+ this.measureCbs.push(cb);
537
+ return () => {
538
+ this.measureCbs = this.measureCbs.filter((c) => c !== cb);
539
+ };
540
+ }
541
+ /** Async iterator over measurements — bounded, drop-oldest (contract 07 §3). */
542
+ measurements(maxsize = 64) {
543
+ const queue = new StreamQueue(maxsize, () => {
544
+ this.measureQueues = this.measureQueues.filter((q) => q !== queue);
545
+ deregister();
546
+ });
547
+ this.measureQueues.push(queue);
548
+ const deregister = this.registerStream(queue);
549
+ return queue;
550
+ }
551
+ /** Wait for the next streamed measurement. */
552
+ async getMeasurement(timeoutMs = 2000) {
553
+ const queue = new StreamQueue(1);
554
+ this.measureQueues.push(queue);
555
+ const deregister = this.registerStream(queue);
556
+ let timer;
557
+ try {
558
+ const result = await Promise.race([
559
+ queue.next(),
560
+ new Promise((_, reject) => {
561
+ timer = setTimeout(() => reject(new DepzTimeoutError(Vl53lxRpt.Stream, timeoutMs)), timeoutMs);
562
+ }),
563
+ ]);
564
+ if (result.done === true)
565
+ throw new DepzError("device closed");
566
+ return result.value;
567
+ }
568
+ finally {
569
+ if (timer !== undefined)
570
+ clearTimeout(timer);
571
+ this.measureQueues = this.measureQueues.filter((q) => q !== queue);
572
+ deregister();
573
+ }
574
+ }
575
+ /** Stream reports dropped because the driver failed to decode them. */
576
+ get streamParseErrors() {
577
+ return this.streamParseErrorCount;
578
+ }
579
+ /** Drop counters of all live measurement queues (diagnostics). */
580
+ get streamDroppedCounts() {
581
+ return this.measureQueues.map((q) => q.droppedCount);
582
+ }
583
+ // ── internal ─────────────────────────────────────────────────────────────
584
+ need(group) {
585
+ const drv = this.driver;
586
+ if (!drv.SUPPORTS.has(group)) {
587
+ throw new DepzError(`${this.productName}/${this.driverKindName} does not support '${group}'` +
588
+ (this.caveat ? ` (${this.caveat})` : ""));
589
+ }
590
+ return drv;
591
+ }
592
+ requireNotRanging() {
593
+ if (this.rangingFlag)
594
+ throw new DepzError("stopRanging() first — the stream owns the bus");
595
+ }
596
+ emit(out) {
597
+ for (const cb of [...this.measureCbs])
598
+ cb(out);
599
+ for (const q of [...this.measureQueues])
600
+ q.push(out);
601
+ }
602
+ handleReport(pkt) {
603
+ if (pkt.cmd !== Vl53lxRpt.Stream || pkt.payload.length < 12)
604
+ return false;
605
+ const drv = this.driverInst;
606
+ if (drv === null)
607
+ return true;
608
+ const sample = unpackStream(pkt.payload);
609
+ // Decoded exactly once per sample: the histogram driver steps its own
610
+ // frame-pair state on every frame it sees.
611
+ try {
612
+ if (!drv.HISTOGRAM) {
613
+ this.emit(toVl53lxMeasurement(sample.timestampUs, drv.decode(sample.data)));
614
+ return true;
615
+ }
616
+ const h = drv;
617
+ const bins = h.binData(sample.data);
618
+ if (!isThenable(bins)) {
619
+ this.emit(toVl53lxMeasurement(sample.timestampUs, h.toMeasurement(bins), bins));
620
+ return true;
621
+ }
622
+ // An async binData: keep frames in order through one chain.
623
+ this.decodeChain = this.decodeChain.then(async () => {
624
+ try {
625
+ const b = await bins;
626
+ this.emit(toVl53lxMeasurement(sample.timestampUs, h.toMeasurement(b), b));
627
+ }
628
+ catch {
629
+ this.streamParseErrorCount += 1;
630
+ }
631
+ });
632
+ }
633
+ catch {
634
+ // A bad sample must not kill the read pump.
635
+ this.streamParseErrorCount += 1;
636
+ }
637
+ return true;
638
+ }
639
+ }
640
+ /**
641
+ * VL53L0X (2 m, 1-byte register addresses, 400 kHz): ULD API 1.0.4 with
642
+ * ranging profiles, offset and crosstalk calibration, ref-SPAD management.
643
+ */
644
+ export class Vl53l0x extends Vl53lx {
645
+ static PRODUCT = "VL53L0X";
646
+ }
647
+ /** VL53L1CX (4 m): VL53L1X ULD (short / long) or the histogram driver. */
648
+ export class Vl53l1cx extends Vl53lx {
649
+ static PRODUCT = "VL53L1CX";
650
+ }
651
+ /** VL53L1CB (8 m, cover-glass module): same die and drivers as VL53L1CX. */
652
+ export class Vl53l1cb extends Vl53lx {
653
+ static PRODUCT = "VL53L1CB";
654
+ }
655
+ /** VL53L3CX (3 m): ST's ULP driver (single target) or the histogram driver. */
656
+ export class Vl53l3cx extends Vl53lx {
657
+ static PRODUCT = "VL53L3CX";
658
+ }
659
+ /**
660
+ * VL53L4CX (6 m): the histogram driver only — to run it light, borrow a
661
+ * sibling's with `init(undefined, { product: "VL53L4CD" })`.
662
+ */
663
+ export class Vl53l4cx extends Vl53lx {
664
+ static PRODUCT = "VL53L4CX";
665
+ }
666
+ /** Class per product (VL53L4CD on this firmware uses the generic class). */
667
+ export const VL53LX_CLASS_BY_PRODUCT = {
668
+ VL53L0X: Vl53l0x,
669
+ VL53L1CX: Vl53l1cx,
670
+ VL53L1CB: Vl53l1cb,
671
+ VL53L3CX: Vl53l3cx,
672
+ VL53L4CX: Vl53l4cx,
673
+ };
674
+ /**
675
+ * Pick the 1D-family class (contract 12 §1): the production PID model, then
676
+ * the product the device name carries, then the generic class.
677
+ */
678
+ export function resolveVl53lxClass(usbModel, deviceName) {
679
+ let product = (usbModel ?? "").toUpperCase() || null;
680
+ if (product === null || !registry.PRODUCTS.includes(product)) {
681
+ product = registry.productFromBoardName(deviceName ?? "");
682
+ }
683
+ return VL53LX_CLASS_BY_PRODUCT[product ?? ""] ?? Vl53lx;
684
+ }
685
+ export const VL53LX_PRODUCTS = registry.PRODUCTS;
686
+ export const VL53LX_DRIVER_KINDS = registry.DRIVER_KINDS;
687
+ export { ProtocolError, Vl53Error };
688
+ //# sourceMappingURL=vl53lx.js.map