@typecad/hal 0.1.0-alpha.2 → 1.0.0-alpha.10

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 (57) hide show
  1. package/README.md +8 -7
  2. package/dist/ble.d.ts +160 -0
  3. package/dist/ble.js +157 -0
  4. package/dist/capacitive.d.ts +22 -0
  5. package/dist/capacitive.js +27 -0
  6. package/dist/emit.d.ts +139 -0
  7. package/dist/emit.js +152 -0
  8. package/dist/fs.d.ts +21 -1
  9. package/dist/fs.js +29 -21
  10. package/dist/http.d.ts +48 -0
  11. package/dist/http.js +104 -0
  12. package/dist/i2c.d.ts +7 -0
  13. package/dist/i2c.js +15 -4
  14. package/dist/index.d.ts +18 -1
  15. package/dist/index.js +17 -1
  16. package/dist/mdns.d.ts +31 -0
  17. package/dist/mdns.js +43 -0
  18. package/dist/mqtt.d.ts +33 -0
  19. package/dist/mqtt.js +48 -0
  20. package/dist/ota.d.ts +29 -0
  21. package/dist/ota.js +38 -0
  22. package/dist/power.d.ts +7 -0
  23. package/dist/power.js +10 -1
  24. package/dist/preferences.d.ts +11 -2
  25. package/dist/preferences.js +28 -27
  26. package/dist/register.d.ts +12 -4
  27. package/dist/register.js +18 -1
  28. package/dist/rmt.d.ts +48 -0
  29. package/dist/rmt.js +81 -0
  30. package/dist/spi.js +10 -7
  31. package/dist/temperature.d.ts +14 -0
  32. package/dist/temperature.js +17 -0
  33. package/dist/timer.d.ts +14 -17
  34. package/dist/timer.js +20 -22
  35. package/dist/types.d.ts +1 -1
  36. package/dist/wifi.d.ts +67 -0
  37. package/dist/wifi.js +151 -0
  38. package/package.json +6 -5
  39. package/src/ble.ts +209 -0
  40. package/src/capacitive.ts +31 -0
  41. package/src/emit.ts +180 -0
  42. package/src/fs.ts +30 -22
  43. package/src/http.ts +144 -0
  44. package/src/i2c.ts +16 -4
  45. package/src/index.ts +21 -1
  46. package/src/mdns.ts +50 -0
  47. package/src/mqtt.ts +57 -0
  48. package/src/ota.ts +44 -0
  49. package/src/power.ts +11 -1
  50. package/src/preferences.ts +56 -27
  51. package/src/register.ts +16 -4
  52. package/src/rmt.ts +125 -0
  53. package/src/spi.ts +10 -7
  54. package/src/temperature.ts +20 -0
  55. package/src/timer.ts +22 -23
  56. package/src/types.ts +1 -0
  57. package/src/wifi.ts +221 -0
package/src/emit.ts CHANGED
@@ -5,6 +5,11 @@
5
5
  // resolver. The framework strategy translates each operation into
6
6
  // framework-specific C++ at code generation time.
7
7
  //
8
+ // EMIT BOUNDARY: This file is a canonical entry point of the HAL lowering
9
+ // surface (A) — its C++ output lands in user sketches. The emitted bytes are
10
+ // covered by the TypeCAD Runtime Exception (see RUNTIME_EXCEPTION.md at the
11
+ // repository root) and are not subject to the license of this tool source.
12
+ //
8
13
  // Pin parameters accept both `number` (legacy framework pin number) and
9
14
  // `string` (MCU port name like "PB5"). The transpiler resolves port names
10
15
  // to framework pin numbers via the MCU package's pin mapping.
@@ -30,6 +35,49 @@ export function gpioSetMode(pin: number | string, mode: string): void {}
30
35
  /** Write PWM duty cycle to a pin. */
31
36
  export function pwmWrite(pin: number | string, duty: number): void {}
32
37
 
38
+ // ---------------------------------------------------------------------------
39
+ // RMT — Remote Control Transceiver (addressable LEDs, IR, raw digital waveforms)
40
+ // ---------------------------------------------------------------------------
41
+ // rmtTxInit/rmtRxInit are POSITIONAL semantic primitives — the ergonomic
42
+ // opts-object form lives in hal/rmt.ts (which destructures and forwards). The
43
+ // resolver collapses object literals, so HALOpIR fields must be scalars; the
44
+ // rmt.ts wrapper bridges the ergonomic API to these positional calls.
45
+ //
46
+ // Pin params accept Pin | number | string so callers can pass a board alias
47
+ // like LED (a Pin object) directly; the transpiler resolves it to its number.
48
+ import type { Pin } from './gpio.js';
49
+ type RmtPin = Pin | number | string;
50
+
51
+ /** Positional semantic primitive. Args: pin, resolutionHz, bit0Hi, bit0Lo,
52
+ * bit1Hi, bit1Lo, msbFirst, queueDepth. Use rmtTxInit() from hal/rmt.ts. */
53
+ export function rmtTxInit(
54
+ pin: RmtPin,
55
+ resolutionHz: number,
56
+ bit0Hi: number, bit0Lo: number, bit1Hi: number, bit1Lo: number,
57
+ msbFirst: boolean, queueDepth: number,
58
+ ): void {}
59
+ /** Write bytes via the channel's bytes-encoder (timings fixed at init). */
60
+ export function rmtTxWriteBytes(pin: RmtPin, bytes: number[] | Uint8Array): void {}
61
+ /** Write raw RMT symbols — arbitrary [hiTicks, loTicks] pairs. */
62
+ export function rmtTxWriteSymbols(pin: RmtPin, symbols: [number, number][]): void {}
63
+ /** Block until the queued TX completes. */
64
+ export function rmtTxWaitDone(pin: RmtPin, timeoutMs?: number): void {}
65
+ /** Tear down the TX channel and release its slot. */
66
+ export function rmtTxDeinit(pin: RmtPin): void {}
67
+
68
+ /** Positional semantic primitive — use rmtRxInit() from hal/rmt.ts. */
69
+ export function rmtRxInit(pin: RmtPin, resolutionHz: number): void {}
70
+ /** Register a callback-by-name invoked when an RX burst completes. */
71
+ export function rmtRxOnReceived(pin: RmtPin, handler: string): void {}
72
+ /** Start receiving. */
73
+ export function rmtRxStart(pin: RmtPin): void {}
74
+ /** Stop receiving. */
75
+ export function rmtRxStop(pin: RmtPin): void {}
76
+ /** Blocking read — returns flattened symbols [d0,l0,d1,l1,…] in ticks. */
77
+ export function rmtRxRead(pin: RmtPin, maxCount: number): number[] { return []; }
78
+ /** Tear down the RX channel and release its slot. */
79
+ export function rmtRxDeinit(pin: RmtPin): void {}
80
+
33
81
  // ---------------------------------------------------------------------------
34
82
  // ADC — analog-to-digital conversion
35
83
  // ---------------------------------------------------------------------------
@@ -99,6 +147,15 @@ export function i2cRequestFrom(bus: string, address: number, quantity: number, s
99
147
  export function i2cAvailable(bus: string): number { return 0; }
100
148
  /** Read a byte from I2C. */
101
149
  export function i2cRead(bus: string): number { return 0; }
150
+ /**
151
+ * Drain `count` bytes requested from the I2C bus into a caller-provided buffer.
152
+ * Semantic primitive: lowers to the `i2c.read_buffer` HAL op. The `buffer`
153
+ * argument is emitted as a placeholder (`__HAL_READ_BUF__`) that the var-init
154
+ * transformer rewrites to the caller's own buffer variable, so bytes land in
155
+ * the `uint8_t data[N]` declared in user scope — NOT an internal temp that
156
+ * decays to a pointer on return. Keeps `data.length` / `data[i]` valid.
157
+ */
158
+ export function i2cReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void {}
102
159
 
103
160
  // ---------------------------------------------------------------------------
104
161
  // SPI — serial peripheral interface
@@ -118,6 +175,14 @@ export function spiEndTx(bus: string): void {}
118
175
  export function spiCsLow(pin: number | string): void {}
119
176
  /** Set SPI chip-select pin HIGH. */
120
177
  export function spiCsHigh(pin: number | string): void {}
178
+ /**
179
+ * Read `count` bytes from the SPI bus into a caller-provided buffer by clocking
180
+ * dummy (0x00) transfers. Semantic primitive: lowers to the `spi.read_buffer`
181
+ * HAL op (per-byte `bus.transfer(0)` read loop). The `buffer` placeholder is
182
+ * rewritten to the caller's variable. Mirrors i2cReadBuffer. The caller is
183
+ * responsible for asserting/de-asserting chip-select around it.
184
+ */
185
+ export function spiReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void {}
121
186
  /** Set SPI data mode. */
122
187
  export function spiSetMode(bus: string, mode: number): void {}
123
188
  /** Set SPI bit order. */
@@ -201,6 +266,104 @@ export function powerDeepSleep(ms: number): void {}
201
266
  export function powerLightSleep(): void {}
202
267
  /** Set the CPU frequency (MHz). */
203
268
  export function powerSetCpuFrequency(mhz: number): void {}
269
+ /** Enter deep sleep until `pin` reaches `level` (pin wakeup). Architecture-aware:
270
+ * ext0 on Xtensa (RTC pins), gpio-wakeup on RISC-V. */
271
+ export function powerDeepSleepPin(pin: number, level: number): void {}
272
+
273
+ // ---------------------------------------------------------------------------
274
+ // Preferences (NVS-backed key/value store)
275
+ // ---------------------------------------------------------------------------
276
+
277
+ export function preferencesBegin(namespace: string, readOnly: boolean): void {}
278
+ export function preferencesEnd(): void {}
279
+ export function preferencesClear(): void {}
280
+ export function preferencesRemove(key: string): void {}
281
+ export function preferencesPutInt(key: string, value: number): void {}
282
+ export function preferencesGetInt(key: string, defaultValue: number): number { return 0; }
283
+ export function preferencesPutUInt(key: string, value: number): void {}
284
+ export function preferencesGetUInt(key: string, defaultValue: number): number { return 0; }
285
+ export function preferencesPutBool(key: string, value: boolean): void {}
286
+ export function preferencesGetBool(key: string, defaultValue: boolean): boolean { return false; }
287
+ export function preferencesPutFloat(key: string, value: number): void {}
288
+ export function preferencesGetFloat(key: string, defaultValue: number): number { return 0; }
289
+ export function preferencesPutString(key: string, value: string): void {}
290
+ export function preferencesGetString(key: string, defaultValue: string): string { return ""; }
291
+
292
+ export function wifiConnect(ssid: string, password?: string, timeoutMs?: number): boolean { return false; }
293
+ export function wifiConnectStart(ssid: string, password?: string): void {}
294
+ export function wifiDisconnect(): void {}
295
+ export function wifiStatus(): number { return 0; }
296
+ export function wifiIsConnected(): boolean { return false; }
297
+ export function wifiLocalIp(): string { return ""; }
298
+ export function wifiRssi(): number { return 0; }
299
+ export function wifiMac(): string { return ""; }
300
+ export function wifiSetHostname(name: string): void {}
301
+ export function wifiSetStaticIp(ip: string, gateway: string, subnet: string, dns?: string): void {}
302
+ export function wifiSetAutoReconnect(enabled: boolean): void {}
303
+ export function wifiSetPowerSave(mode: string): void {}
304
+ export function wifiSetTxPower(dbm: number): void {}
305
+ export function wifiOnEvent(event: string, handler: string): void {}
306
+ export function wifiApStart(ssid: string, password?: string, channel?: number, hidden?: boolean, maxClients?: number): boolean { return false; }
307
+ export function wifiApStop(): void {}
308
+ export function wifiApClientCount(): number { return 0; }
309
+ export function wifiApIp(): string { return ""; }
310
+ export function wifiApSetChannel(channel: number): void {}
311
+ export function wifiApSetHidden(hidden: boolean): void {}
312
+ export function wifiApSetMaxClients(maxClients: number): void {}
313
+ export function wifiScan(): number { return 0; }
314
+ export function wifiScanStart(): void {}
315
+ export function wifiScanCount(): number { return 0; }
316
+ export function wifiScanSsid(index: number): string { return ""; }
317
+ export function wifiScanRssi(index: number): number { return 0; }
318
+ export function wifiScanEncryption(index: number): number { return 0; }
319
+ export function wifiScanChannel(index: number): number { return 0; }
320
+ export function wifiSaveCredentials(ssid: string, password: string): void {}
321
+ export function wifiConnectSaved(timeoutMs?: number): boolean { return false; }
322
+ export function wifiClearCredentials(): void {}
323
+ export function wifiWaitConnected(timeoutMs?: number): boolean { return false; }
324
+ export function wifiWaitDisconnected(): void {}
325
+
326
+ // ---------------------------------------------------------------------------
327
+ // HTTP client
328
+ // ---------------------------------------------------------------------------
329
+
330
+ export function httpBegin(method: string, url: string): void {}
331
+ export function httpReset(): void {}
332
+ export function httpSetHeader(name: string, value: string): void {}
333
+ export function httpSetTimeout(ms: number): void {}
334
+ export function httpSetMaxBody(bytes: number): void {}
335
+ export function httpSetBody(data: string, json?: boolean): void {}
336
+ export function httpSetInsecure(): void {}
337
+ export function httpSetCaCert(pem: string): void {}
338
+ export function httpSend(): boolean { return false; }
339
+ export function httpSendStart(): void {}
340
+ export function httpStatus(): number { return 0; }
341
+ export function httpOk(): boolean { return false; }
342
+ export function httpBody(): string { return ""; }
343
+ export function httpContentLength(): number { return 0; }
344
+ export function httpResponseHeader(name: string): string { return ""; }
345
+
346
+ // ---------------------------------------------------------------------------
347
+ // BLE (NimBLE GATT peripheral)
348
+ // ---------------------------------------------------------------------------
349
+
350
+ export function bleServerBegin(name: string): void {}
351
+ export function bleAdvertiseStart(): void {}
352
+ export function bleAdvertiseStop(): void {}
353
+ export function bleAddService(uuid: string): void {}
354
+ export function bleAddChar(index: number, uuid: string, type: string, perms: number, svcIndex: number): void {}
355
+ export function bleOnRead(index: number, handler: string): void {}
356
+ export function bleOnWrite(index: number, handler: string): void {}
357
+ export function bleOnConnect(handler: string): void {}
358
+ export function bleOnDisconnect(handler: string): void {}
359
+ export function bleNotify(index: number, value: number | string): void {}
360
+ export function bleIsConnected(): boolean { return false; }
361
+ export function bleClientCount(): number { return 0; }
362
+ export function bleSetName(name: string): void {}
363
+ export function bleUntilConnected(timeoutMs?: number): boolean { return false; }
364
+ export function bleUntilConnectedStart(): void {}
365
+ export function bleSetTxPower(dbm: number): void {}
366
+ export function bleStatus(): number { return 0; }
204
367
 
205
368
  // ---------------------------------------------------------------------------
206
369
  // Raw C++ escape hatch
@@ -208,3 +371,20 @@ export function powerSetCpuFrequency(mhz: number): void {}
208
371
 
209
372
  /** Emit raw C++ code (escape hatch for unsupported operations). */
210
373
  export function rawCpp(code: string): void {}
374
+
375
+ /**
376
+ * Emit raw C++ in expression context. Use when an IDF macro or constructor
377
+ * must produce a value (e.g. `WIFI_INIT_CONFIG_DEFAULT()` expands to a struct
378
+ * initializer; there's no TS-side way to construct it). The type parameter
379
+ * is purely a TS hint — the transpiler doesn't check it; it just emits the
380
+ * raw text in expression position.
381
+ *
382
+ * const cfg = rawCpp<wifi_init_config_t>('WIFI_INIT_CONFIG_DEFAULT()');
383
+ *
384
+ * lowers to:
385
+ *
386
+ * wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
387
+ */
388
+ export function rawCppExpr<T>(code: string): T {
389
+ return undefined as unknown as T;
390
+ }
package/src/fs.ts CHANGED
@@ -1,46 +1,54 @@
1
- import { rawCpp } from './emit.js';
2
- import { include } from './include.js';
3
-
4
1
  /**
5
2
  * FSClass provides a high-level abstraction for filesystem operations.
6
- * Maps to SD.h or LittleFS depending on the board configuration.
3
+ *
4
+ * Lowered to native filesystem HAL ops (fs.*): ESP-IDF mounts an SD card via
5
+ * esp_vfs_fat_sdmmc_mount (FAT on SDMMC/SDSPI); Arduino uses SD.h / LittleFS.
6
+ * The per-framework runtime shim owns the open/read/write/close dance and
7
+ * returns heap strings for readText.
8
+ *
9
+ * The semantic primitives (fsBegin / fsReadText / ...) are resolved to fs.*
10
+ * HAL ops by the transpiler's hal-plugins switch; this class is the
11
+ * ergonomic, type-checking surface.
7
12
  */
8
13
  export class FSClass {
9
14
  static readonly __instance_name = "FS";
10
15
 
16
+ /** Mount the filesystem. Returns true on success. */
11
17
  begin(): boolean {
12
- include("<FS.h>");
13
- rawCpp(`return FS.begin();`);
18
+ fsBegin();
14
19
  return true;
15
20
  }
16
21
 
22
+ /** Read a UTF-8 text file into a string. Returns "" if the file is missing
23
+ * or unreadable. The returned buffer is caller-owned. */
17
24
  readText(path: string): string {
18
- include("<FS.h>");
19
- rawCpp(`File f = FS.open(${path}.c_str(), "r");`);
20
- rawCpp(`if (!f) return "";`);
21
- rawCpp(`String s = f.readString();`);
22
- rawCpp(`f.close();`);
23
- rawCpp(`return s.c_str();`);
24
- return "";
25
+ return fsReadText(path);
25
26
  }
26
27
 
28
+ /** Write a string to a file (overwrites). Silently no-ops if the file
29
+ * cannot be opened for writing. */
27
30
  writeText(path: string, content: string): void {
28
- include("<FS.h>");
29
- rawCpp(`File f = FS.open(${path}.c_str(), "w");`);
30
- rawCpp(`if (f) { f.print(${content}.c_str()); f.close(); }`);
31
+ fsWriteText(path, content);
31
32
  }
32
33
 
34
+ /** True if a file exists at the path. */
33
35
  exists(path: string): boolean {
34
- include("<FS.h>");
35
- rawCpp(`return FS.exists(${path}.c_str());`);
36
- return false;
36
+ return fsExists(path);
37
37
  }
38
38
 
39
+ /** Delete a file. Returns true if deleted. */
39
40
  remove(path: string): boolean {
40
- include("<FS.h>");
41
- rawCpp(`return FS.remove(${path}.c_str());`);
42
- return false;
41
+ return fsRemove(path);
43
42
  }
44
43
  }
45
44
 
46
45
  export const FS = new FSClass();
46
+
47
+ // ── Semantic primitives (resolved to fs.* HAL ops by the transpiler) ──
48
+ // These are inert at runtime (tests, type-checking); the cuttlefish transpiler
49
+ // intercepts calls by name and lowers them to typed HAL op IR.
50
+ export function fsBegin(): void {}
51
+ export function fsReadText(path: string): string { return ""; }
52
+ export function fsWriteText(path: string, content: string): void {}
53
+ export function fsExists(path: string): boolean { return false; }
54
+ export function fsRemove(path: string): boolean { return false; }
package/src/http.ts ADDED
@@ -0,0 +1,144 @@
1
+ import {
2
+ httpBegin,
3
+ httpReset,
4
+ httpSetHeader,
5
+ httpSetTimeout,
6
+ httpSetMaxBody,
7
+ httpSetBody,
8
+ httpSetInsecure,
9
+ httpSetCaCert,
10
+ httpSend,
11
+ httpStatus,
12
+ httpOk,
13
+ httpBody,
14
+ httpContentLength,
15
+ httpResponseHeader,
16
+ } from './emit.js';
17
+
18
+ export enum HttpMethod {
19
+ GET = 0,
20
+ POST = 1,
21
+ PUT = 2,
22
+ DELETE = 3,
23
+ HEAD = 4,
24
+ PATCH = 5,
25
+ }
26
+
27
+ /**
28
+ * Fluent HTTP/S request builder, lowered to native ESP-IDF
29
+ * `esp_http_client` by framework-esp32 (TLS via esp-tls / mbedTLS bundle).
30
+ * Response fields are read from this object after send() — mirrors
31
+ * `await WiFi.connect(); WiFi.localIP()`.
32
+ *
33
+ * No `include()` calls here — ESP-IDF headers are framework-owned and added
34
+ * via forcedIncludes when the program uses http.* ops.
35
+ */
36
+ export class HttpRequest {
37
+ private _method: string;
38
+ private _url: string;
39
+
40
+ constructor(method: string, url: string) {
41
+ this._method = method;
42
+ this._url = url;
43
+ }
44
+
45
+ header(name: string, value: string): this {
46
+ httpSetHeader(name, value);
47
+ return this;
48
+ }
49
+
50
+ timeout(ms: number): this {
51
+ httpSetTimeout(ms);
52
+ return this;
53
+ }
54
+
55
+ maxBody(bytes: number): this {
56
+ httpSetMaxBody(bytes);
57
+ return this;
58
+ }
59
+
60
+ body(data: string): this {
61
+ httpSetBody(data, false);
62
+ return this;
63
+ }
64
+
65
+ jsonBody(json: string): this {
66
+ httpSetBody(json, true);
67
+ return this;
68
+ }
69
+
70
+ /** Skip TLS certificate verification (development only). */
71
+ insecure(): this {
72
+ httpSetInsecure();
73
+ return this;
74
+ }
75
+
76
+ caCert(pem: string): this {
77
+ httpSetCaCert(pem);
78
+ return this;
79
+ }
80
+
81
+ /** Blocking at top level; cooperatively awaitable inside async functions. */
82
+ send(): Promise<boolean> {
83
+ httpBegin(this._method, this._url);
84
+ httpSend();
85
+ return Promise.resolve(false);
86
+ }
87
+
88
+ status(): number {
89
+ return httpStatus();
90
+ }
91
+
92
+ ok(): boolean {
93
+ return httpOk();
94
+ }
95
+
96
+ /** Response body as a C string (valid until the next request). */
97
+ text(): string {
98
+ return httpBody();
99
+ }
100
+
101
+ contentLength(): number {
102
+ return httpContentLength();
103
+ }
104
+
105
+ responseHeader(name: string): string {
106
+ return httpResponseHeader(name);
107
+ }
108
+ }
109
+
110
+ export class HttpClass {
111
+ static readonly __instance_name = "Http";
112
+
113
+ get(url: string): HttpRequest {
114
+ httpReset();
115
+ return new HttpRequest("GET", url);
116
+ }
117
+
118
+ post(url: string): HttpRequest {
119
+ httpReset();
120
+ return new HttpRequest("POST", url);
121
+ }
122
+
123
+ put(url: string): HttpRequest {
124
+ httpReset();
125
+ return new HttpRequest("PUT", url);
126
+ }
127
+
128
+ del(url: string): HttpRequest {
129
+ httpReset();
130
+ return new HttpRequest("DELETE", url);
131
+ }
132
+
133
+ head(url: string): HttpRequest {
134
+ httpReset();
135
+ return new HttpRequest("HEAD", url);
136
+ }
137
+
138
+ patch(url: string): HttpRequest {
139
+ httpReset();
140
+ return new HttpRequest("PATCH", url);
141
+ }
142
+ }
143
+
144
+ export const Http = new HttpClass();
package/src/i2c.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, rawCpp } from './emit.js';
1
+ import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, i2cReadBuffer, rawCpp } from './emit.js';
2
2
  import { include } from './include.js';
3
3
 
4
4
  export class I2CDevice {
@@ -10,6 +10,16 @@ export class I2CDevice {
10
10
  this._address = address;
11
11
  }
12
12
 
13
+ /** The 7-bit I2C address this accessor targets. Exposed so I2CDevice
14
+ * structurally satisfies the @typecad/simulator II2CDeviceAccessor contract
15
+ * (which declares `readonly address`), letting the same driver function be
16
+ * typed against the contract and accept either a real board device or a
17
+ * simulated one. The transpiler strips HAL class bodies to IR, so this
18
+ * getter carries no runtime cost in the generated C++. */
19
+ get address(): number {
20
+ return this._address;
21
+ }
22
+
13
23
  writeByte(register: number, value: number): void {
14
24
  include("<Wire.h>");
15
25
  i2cBeginTx(this._bus, this._address);
@@ -41,9 +51,11 @@ export class I2CDevice {
41
51
  i2cWrite(this._bus, register);
42
52
  i2cEndTx(this._bus, false);
43
53
  i2cRequestFrom(this._bus, this._address, count, true);
44
- rawCpp(`static uint8_t __buf[${count}];`);
45
- rawCpp(`for (int __i = 0; __i < ${count}; __i++) __buf[__i] = ${this._bus}.read();`);
46
- rawCpp(`return __buf;`);
54
+ // Drain the requested bytes into the caller's buffer (declared by the
55
+ // Uint8Array return marker as `uint8_t data[count]`). Using the semantic
56
+ // primitive — NOT rawCpp — keeps the buffer in user scope so it survives
57
+ // the return (no decayed pointer) and `data.length` / `data[i]` work.
58
+ i2cReadBuffer(this._bus, count, new Uint8Array(count));
47
59
  return new Uint8Array(count);
48
60
  }
49
61
  }
package/src/index.ts CHANGED
@@ -19,7 +19,7 @@ export type { SPIBitOrder, SPIMode, SPISettings } from './types.js';
19
19
  export { include } from './include.js';
20
20
  export { board } from './board.js';
21
21
  export { callback } from './callback.js';
22
- export { rawCpp, boardResolve } from './emit.js';
22
+ export { rawCpp, rawCppExpr, boardResolve } from './emit.js';
23
23
  export { HIGH, LOW, OUTPUT, INPUT, INPUT_PULLUP, INPUT_PULLDOWN, OUTPUT_OPEN_DRAIN, ANALOG, LED_BUILTIN, LSBFIRST, MSBFIRST, WDTO_15MS, WDTO_30MS, WDTO_60MS, WDTO_120MS, WDTO_250MS, WDTO_500MS, WDTO_1S, WDTO_2S, WDTO_4S, WDTO_8S } from './constants.js';
24
24
  export { delay, millis, micros, delayMicroseconds, map, constrain, TimingClass, Timing } from './timing.js';
25
25
  export { freeHeap, setInterval, setTimeout, clearInterval, clearTimeout } from './timing.js';
@@ -44,5 +44,25 @@ export { DACClass, DAC } from './dac.js';
44
44
  export { PreferencesClass, Preferences } from './preferences.js';
45
45
  export { HardwareTimer, Timer0, Timer1, Timer2 } from './timer.js';
46
46
  export { FSClass, FS } from './fs.js';
47
+ export { fsBegin, fsReadText, fsWriteText, fsExists, fsRemove } from './fs.js';
48
+ export { MdnsClass, MDNS } from './mdns.js';
49
+ export { mdnsStart, mdnsSetHostname, mdnsAddService, mdnsAnnounce, mdnsStop } from './mdns.js';
50
+ export { MqttClass, MQTT } from './mqtt.js';
51
+ export { mqttConnect, mqttOnMessage, mqttSubscribe, mqttPublish, mqttConnected, mqttDisconnect } from './mqtt.js';
52
+ export { OtaClass, OTA } from './ota.js';
53
+ export { otaFromUrl, otaBegin, otaWrite, otaApply } from './ota.js';
54
+ export { TemperatureClass, Temperature } from './temperature.js';
55
+ export { tempRead } from './temperature.js';
56
+ export { hwtimerSetFrequency, hwtimerOnOverflow, hwtimerStart, hwtimerStop } from './timer.js';
57
+ export { CapacitiveClass, Capacitive } from './capacitive.js';
58
+ export { capacitiveRead } from './capacitive.js';
47
59
  export { PowerClass, Power } from './power.js';
48
60
  export { AsyncClass, Async } from './async.js';
61
+ export { WiFiClass, WiFi, WiFiStatus, WiFiEncryption } from './wifi.js';
62
+ export { HttpClass, Http, HttpRequest, HttpMethod } from './http.js';
63
+ export {
64
+ BleClass, Ble, BleServer,
65
+ BleValueType, BlePerm, BleStatus, BleAdvertisingMode, GATT,
66
+ } from './ble.js';
67
+ export type { GattCharacteristicDef, CharValue } from './ble.js';
68
+ export { RmtChannel } from './rmt.js';
package/src/mdns.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * MdnsClass — mDNS service discovery (ESP-IDF esp_mdns).
3
+ *
4
+ * Lowered to native mDNS HAL ops (mdns.*): ESP-IDF's esp_mdns component
5
+ * advertises the device on the local network as `<hostname>.local` and
6
+ * publishes services (e.g. `_http._tcp`) for discovery by Bonjour/Avahi.
7
+ *
8
+ * The semantic primitives (mdnsStart / mdnsAddService / ...) are resolved to
9
+ * mdns.* HAL ops by the transpiler; this class is the ergonomic surface.
10
+ *
11
+ * Requires WiFi to be connected (mDNS rides on the station interface).
12
+ */
13
+ export class MdnsClass {
14
+ static readonly __instance_name = "MDNS";
15
+
16
+ /** Initialize mDNS and set the host name (advertised as <name>.local). */
17
+ start(hostname: string): boolean {
18
+ mdnsStart(hostname);
19
+ return true;
20
+ }
21
+
22
+ /** Set/override the host name after start(). */
23
+ setHostname(name: string): void {
24
+ mdnsSetHostname(name);
25
+ }
26
+
27
+ /** Publish a service instance. proto is "_tcp" or "_udp". */
28
+ addService(instance: string, proto: string, port: number): void {
29
+ mdnsAddService(instance, proto, port);
30
+ }
31
+
32
+ /** Advertise that the device is reachable (sends a probe/announce). */
33
+ announce(): void {
34
+ mdnsAnnounce();
35
+ }
36
+
37
+ /** Tear down the mDNS responder. */
38
+ stop(): void {
39
+ mdnsStop();
40
+ }
41
+ }
42
+
43
+ export const MDNS = new MdnsClass();
44
+
45
+ // ── Semantic primitives (resolved to mdns.* HAL ops by the transpiler) ──
46
+ export function mdnsStart(hostname: string): void {}
47
+ export function mdnsSetHostname(name: string): void {}
48
+ export function mdnsAddService(instance: string, proto: string, port: number): void {}
49
+ export function mdnsAnnounce(): void {}
50
+ export function mdnsStop(): void {}
package/src/mqtt.ts ADDED
@@ -0,0 +1,57 @@
1
+ import { callback } from './callback.js';
2
+
3
+ /**
4
+ * MqttClass — MQTT 3.1.1 pub/sub client (ESP-IDF esp_mqtt).
5
+ *
6
+ * Lowered to native MQTT HAL ops (mqtt.*): ESP-IDF's esp_mqtt_client_* API.
7
+ * Covers the common IoT pub/sub path: connect to a broker, publish, subscribe
8
+ * with an onMessage callback, and disconnect. The runtime shim owns the event
9
+ * loop translation (ESP-IDF's MQTT event handler → the user's TS callback).
10
+ *
11
+ * Requires a network connection (WiFi) before connect().
12
+ */
13
+ export class MqttClass {
14
+ static readonly __instance_name = "MQTT";
15
+
16
+ /** Connect to a broker URI (e.g. "mqtt://broker.local" or "mqtts://..."). */
17
+ connect(brokerUri: string, clientId: string): boolean {
18
+ mqttConnect(brokerUri, clientId);
19
+ return true;
20
+ }
21
+
22
+ /** Set a handler invoked for every received PUBLISH on a subscribed topic.
23
+ * The handler receives (topic, payload). */
24
+ onMessage(handler: (topic: string, payload: string) => void): void {
25
+ mqttOnMessage(callback(handler));
26
+ }
27
+
28
+ /** Subscribe to a topic filter (e.g. "sensors/#"). */
29
+ subscribe(topic: string): void {
30
+ mqttSubscribe(topic);
31
+ }
32
+
33
+ /** Publish a message to a topic. */
34
+ publish(topic: string, data: string): void {
35
+ mqttPublish(topic, data);
36
+ }
37
+
38
+ /** True if the client is currently connected to the broker. */
39
+ connected(): boolean {
40
+ return mqttConnected();
41
+ }
42
+
43
+ /** Disconnect from the broker and free the client. */
44
+ disconnect(): void {
45
+ mqttDisconnect();
46
+ }
47
+ }
48
+
49
+ export const MQTT = new MqttClass();
50
+
51
+ // ── Semantic primitives (resolved to mqtt.* HAL ops by the transpiler) ──
52
+ export function mqttConnect(brokerUri: string, clientId: string): void {}
53
+ export function mqttOnMessage(handler: string): void {}
54
+ export function mqttSubscribe(topic: string): void {}
55
+ export function mqttPublish(topic: string, data: string): void {}
56
+ export function mqttConnected(): boolean { return false; }
57
+ export function mqttDisconnect(): void {}
package/src/ota.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * OtaClass — over-the-air firmware update (ESP-IDF esp_https_ota / esp_app_format).
3
+ *
4
+ * Lowered to native OTA HAL ops (ota.*): ESP-IDF's esp_https_ota downloads a
5
+ * firmware image over HTTPS and writes it to the OTA partition, then reboots
6
+ * into it. The runtime shim owns the session/progress/verification dance.
7
+ *
8
+ * Requires a network connection (WiFi) and that the partition table defines
9
+ * at least two OTA app slots (the framework's default partitions.csv does).
10
+ */
11
+ export class OtaClass {
12
+ static readonly __instance_name = "OTA";
13
+
14
+ /** Download and apply a firmware image from an HTTPS URL, then reboot.
15
+ * Blocks until the update is written and verified. Returns true on success
16
+ * (the reboot happens inside the shim on success, so a true return means
17
+ * the new firmware is about to boot). */
18
+ fromUrl(url: string): boolean {
19
+ return otaFromUrl(url);
20
+ }
21
+
22
+ /** Begin a manual update session (caller writes chunks via write()). */
23
+ begin(): boolean {
24
+ return otaBegin();
25
+ }
26
+
27
+ /** Write a chunk of firmware data to the OTA partition. */
28
+ write(chunk: string): void {
29
+ otaWrite(chunk);
30
+ }
31
+
32
+ /** Finalize the update: verify, set the boot partition, and reboot. */
33
+ apply(): void {
34
+ otaApply();
35
+ }
36
+ }
37
+
38
+ export const OTA = new OtaClass();
39
+
40
+ // ── Semantic primitives (resolved to ota.* HAL ops by the transpiler) ──
41
+ export function otaFromUrl(url: string): boolean { return false; }
42
+ export function otaBegin(): boolean { return false; }
43
+ export function otaWrite(chunk: string): void {}
44
+ export function otaApply(): void {}