@typecad/hal 1.0.0-alpha.6 → 1.0.0-alpha.8

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/dist/wifi.js ADDED
@@ -0,0 +1,151 @@
1
+ import { wifiConnect, wifiConnectStart, wifiDisconnect, wifiStatus, wifiIsConnected, wifiLocalIp, wifiRssi, wifiMac, wifiSetHostname, wifiSetStaticIp, wifiSetAutoReconnect, wifiSetPowerSave, wifiSetTxPower, wifiOnEvent, wifiApStart, wifiApStop, wifiApClientCount, wifiApIp, wifiApSetChannel, wifiApSetHidden, wifiApSetMaxClients, wifiScan, wifiScanStart, wifiScanCount, wifiScanSsid, wifiScanRssi, wifiScanEncryption, wifiScanChannel, wifiSaveCredentials, wifiConnectSaved, wifiClearCredentials, wifiWaitConnected, wifiWaitDisconnected, } from './emit.js';
2
+ import { callback } from './callback.js';
3
+ /** Normalized WiFi link status (mapped from esp_wifi events by the runtime shim). */
4
+ export var WiFiStatus;
5
+ (function (WiFiStatus) {
6
+ WiFiStatus[WiFiStatus["Idle"] = 0] = "Idle";
7
+ WiFiStatus[WiFiStatus["Connecting"] = 1] = "Connecting";
8
+ WiFiStatus[WiFiStatus["Connected"] = 2] = "Connected";
9
+ WiFiStatus[WiFiStatus["ConnectFailed"] = 3] = "ConnectFailed";
10
+ WiFiStatus[WiFiStatus["Disconnected"] = 4] = "Disconnected";
11
+ })(WiFiStatus || (WiFiStatus = {}));
12
+ export var WiFiEncryption;
13
+ (function (WiFiEncryption) {
14
+ WiFiEncryption[WiFiEncryption["Open"] = 0] = "Open";
15
+ WiFiEncryption[WiFiEncryption["WEP"] = 1] = "WEP";
16
+ WiFiEncryption[WiFiEncryption["WPA"] = 2] = "WPA";
17
+ WiFiEncryption[WiFiEncryption["WPA2"] = 3] = "WPA2";
18
+ WiFiEncryption[WiFiEncryption["WPA3"] = 4] = "WPA3";
19
+ WiFiEncryption[WiFiEncryption["Enterprise"] = 5] = "Enterprise";
20
+ })(WiFiEncryption || (WiFiEncryption = {}));
21
+ /**
22
+ * WiFi radio / link control, lowered to native ESP-IDF (`esp_wifi` /
23
+ * `esp_netif` / `esp_event` / `nvs_flash`) by framework-esp32.
24
+ *
25
+ * No `include()` calls here — ESP-IDF headers are framework-owned and added
26
+ * via forcedIncludes when the program uses wifi.* ops (the Preferences
27
+ * lesson: HAL files must not carry platform headers). Frameworks without a
28
+ * wifi lowering reject these ops with a diagnostic.
29
+ */
30
+ export class WiFiClass {
31
+ /** Blocking at top level; cooperatively awaitable inside async functions. */
32
+ connect(ssid, password, timeoutMs = 15000) {
33
+ wifiConnect(ssid, password, timeoutMs);
34
+ return Promise.resolve(false);
35
+ }
36
+ /** Fire-and-forget STA begin; poll status() / untilConnected(). */
37
+ connectAsync(ssid, password) {
38
+ wifiConnectStart(ssid, password);
39
+ }
40
+ untilConnected(timeoutMs = 15000) {
41
+ wifiWaitConnected(timeoutMs);
42
+ return Promise.resolve(false);
43
+ }
44
+ untilDisconnected() {
45
+ wifiWaitDisconnected();
46
+ return Promise.resolve();
47
+ }
48
+ disconnect() {
49
+ wifiDisconnect();
50
+ }
51
+ isConnected() {
52
+ return wifiIsConnected();
53
+ }
54
+ status() {
55
+ return wifiStatus();
56
+ }
57
+ localIP() {
58
+ return wifiLocalIp();
59
+ }
60
+ rssi() {
61
+ return wifiRssi();
62
+ }
63
+ macAddress() {
64
+ return wifiMac();
65
+ }
66
+ hostname(name) {
67
+ wifiSetHostname(name);
68
+ return this;
69
+ }
70
+ staticIP(ip, gateway, subnet, dns) {
71
+ wifiSetStaticIp(ip, gateway, subnet, dns);
72
+ return this;
73
+ }
74
+ autoReconnect(enabled) {
75
+ wifiSetAutoReconnect(enabled);
76
+ return this;
77
+ }
78
+ powerSave(mode) {
79
+ wifiSetPowerSave(mode);
80
+ return this;
81
+ }
82
+ /** Cap TX power in dBm (roughly 2–20). Safe to call before connect() —
83
+ * the value is applied after the radio starts. */
84
+ txPower(dbm) {
85
+ wifiSetTxPower(dbm);
86
+ return this;
87
+ }
88
+ saveCredentials(ssid, password) {
89
+ wifiSaveCredentials(ssid, password);
90
+ }
91
+ connectSaved(timeoutMs = 15000) {
92
+ return wifiConnectSaved(timeoutMs);
93
+ }
94
+ clearCredentials() {
95
+ wifiClearCredentials();
96
+ }
97
+ onConnect(handler) {
98
+ wifiOnEvent("connect", callback(handler));
99
+ }
100
+ onDisconnect(handler) {
101
+ wifiOnEvent("disconnect", callback(handler));
102
+ }
103
+ startAP(ssid, password) {
104
+ return wifiApStart(ssid, password);
105
+ }
106
+ apChannel(ch) {
107
+ wifiApSetChannel(ch);
108
+ return this;
109
+ }
110
+ apHidden(hidden) {
111
+ wifiApSetHidden(hidden);
112
+ return this;
113
+ }
114
+ apMaxClients(n) {
115
+ wifiApSetMaxClients(n);
116
+ return this;
117
+ }
118
+ stopAP() {
119
+ wifiApStop();
120
+ }
121
+ apClientCount() {
122
+ return wifiApClientCount();
123
+ }
124
+ apIP() {
125
+ return wifiApIp();
126
+ }
127
+ scan() {
128
+ return wifiScan();
129
+ }
130
+ scanAsync() {
131
+ wifiScanStart();
132
+ return Promise.resolve();
133
+ }
134
+ scanCount() {
135
+ return wifiScanCount();
136
+ }
137
+ scanSSID(i) {
138
+ return wifiScanSsid(i);
139
+ }
140
+ scanRSSI(i) {
141
+ return wifiScanRssi(i);
142
+ }
143
+ scanEncryption(i) {
144
+ return wifiScanEncryption(i);
145
+ }
146
+ scanChannel(i) {
147
+ return wifiScanChannel(i);
148
+ }
149
+ }
150
+ WiFiClass.__instance_name = "WiFi";
151
+ export const WiFi = new WiFiClass();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typecad/hal",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.8",
4
4
  "description": "TypeCAD hardware abstraction layer — GPIO, I2C, SPI, UART as regular TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,7 +13,8 @@
13
13
  "build": "tsc",
14
14
  "prepublishOnly": "npm run build",
15
15
  "test:hw": "npm exec -- cuttlefish-test",
16
- "test:hw:basics": "npm exec -- cuttlefish-test tests/01-gpio.test.ts"
16
+ "test:hw:basics": "npm exec -- cuttlefish-test tests/01-gpio.test.ts",
17
+ "test:hw:preferences": "npm exec -- cuttlefish-test tests/14-preferences.test.ts"
17
18
  },
18
19
  "license": "MIT",
19
20
  "publishConfig": {
@@ -49,9 +50,9 @@
49
50
  },
50
51
  "author": "typecad0",
51
52
  "devDependencies": {
52
- "@typecad/expect": "1.0.0-alpha.6",
53
- "@typecad/board-arduino-uno": "1.0.0-alpha.6",
54
- "@typecad/mcu-atmega328p": "1.0.0-alpha.6"
53
+ "@typecad/expect": "1.0.0-alpha.8",
54
+ "@typecad/board-arduino-uno": "1.0.0-alpha.8",
55
+ "@typecad/mcu-atmega328p": "1.0.0-alpha.8"
55
56
  },
56
57
  "sideEffects": false,
57
58
  "exports": {
package/src/ble.ts ADDED
@@ -0,0 +1,209 @@
1
+ import {
2
+ bleServerBegin,
3
+ bleAdvertiseStart,
4
+ bleAdvertiseStop,
5
+ bleAddService,
6
+ bleAddChar,
7
+ bleOnRead,
8
+ bleOnWrite,
9
+ bleOnConnect,
10
+ bleOnDisconnect,
11
+ bleNotify,
12
+ bleIsConnected,
13
+ bleClientCount,
14
+ bleSetName,
15
+ bleUntilConnected,
16
+ bleUntilConnectedStart,
17
+ bleSetTxPower,
18
+ bleStatus,
19
+ } from './emit.js';
20
+ import { callback } from './callback.js';
21
+
22
+ /** Characteristic value encoding — drives both TS callback types and C++ marshalling. */
23
+ export enum BleValueType {
24
+ Uint8 = 'uint8',
25
+ Uint16 = 'uint16',
26
+ Uint32 = 'uint32',
27
+ Int8 = 'int8',
28
+ Int16 = 'int16',
29
+ Int32 = 'int32',
30
+ Float32 = 'float32',
31
+ Utf8 = 'utf8',
32
+ Boolean = 'boolean',
33
+ Bytes = 'bytes',
34
+ }
35
+
36
+ /** GATT characteristic permission flags. Combine with `|`. */
37
+ export enum BlePerm {
38
+ Read = 1,
39
+ Write = 2,
40
+ Notify = 4,
41
+ }
42
+
43
+ /** BLE peripheral status (mirrored by the runtime shim). */
44
+ export enum BleStatus {
45
+ Idle = 0,
46
+ Initializing = 1,
47
+ Advertising = 2,
48
+ Connected = 3,
49
+ Error = 4,
50
+ }
51
+
52
+ export enum BleAdvertisingMode {
53
+ Connectable = 'connectable',
54
+ NonConnectable = 'non_connectable',
55
+ }
56
+
57
+ /** A well-known GATT characteristic entry in the catalog. */
58
+ export interface GattCharacteristicDef {
59
+ readonly uuid: string;
60
+ readonly type: BleValueType;
61
+ readonly read?: boolean;
62
+ readonly write?: boolean;
63
+ readonly notify?: boolean;
64
+ }
65
+
66
+ /**
67
+ * Standard GATT services/characteristics. Autocomplete walks the hierarchy:
68
+ * GATT.ENVIRONMENTAL. -> TEMPERATURE, HUMIDITY, ...
69
+ * Pass the .uuid, .type, and computed perms to BleServer.characteristic().
70
+ */
71
+ export const GATT = {
72
+ DEVICE_INFO: {
73
+ MANUFACTURER_NAME: { uuid: '2A29', type: BleValueType.Utf8, read: true },
74
+ MODEL_NUMBER: { uuid: '2A24', type: BleValueType.Utf8, read: true },
75
+ FIRMWARE_REVISION: { uuid: '2A26', type: BleValueType.Utf8, read: true },
76
+ },
77
+ ENVIRONMENTAL: {
78
+ TEMPERATURE: { uuid: '2A6E', type: BleValueType.Int16, read: true, notify: true },
79
+ HUMIDITY: { uuid: '2A6F', type: BleValueType.Uint16, read: true, notify: true },
80
+ PRESSURE: { uuid: '2A6D', type: BleValueType.Uint32, read: true, notify: true },
81
+ },
82
+ BATTERY: {
83
+ LEVEL: { uuid: '2A19', type: BleValueType.Uint8, read: true, notify: true },
84
+ },
85
+ } as const;
86
+
87
+ /** The value passed to/from callbacks — narrowed per characteristic by type. */
88
+ export type CharValue = number | string | boolean | Uint8Array;
89
+
90
+ /**
91
+ * BLE GATT peripheral control, lowered to native ESP-IDF NimBLE
92
+ * (`nimble_host` / `ble_gap` / `ble_gatts`) by framework-esp32.
93
+ *
94
+ * No `include()` calls here — NimBLE headers are framework-owned and added via
95
+ * forcedIncludes when the program uses ble.* ops.
96
+ *
97
+ * Transpiler note: method bodies pass parameters directly into semantic calls
98
+ * (no local consts / module counters) so the resolver can statically track every
99
+ * argument. The characteristic index is carried through the chain via
100
+ * `this._charCount` fieldValues, mirroring how HttpRequest carries _method/_url.
101
+ */
102
+ export class BleClass {
103
+ static readonly __instance_name = "Ble";
104
+
105
+ /** Begin building a GATT server with the given advertised device name. */
106
+ server(name: string): BleServer {
107
+ bleSetName(name);
108
+ return new BleServer(name, 0, 1);
109
+ }
110
+
111
+ /** Initialize NimBLE, register services, and start advertising. */
112
+ begin(): void {
113
+ bleServerBegin("TypeCAD");
114
+ bleAdvertiseStart();
115
+ }
116
+
117
+ advertise(): void { bleAdvertiseStart(); }
118
+ stopAdvertising(): void { bleAdvertiseStop(); }
119
+
120
+ /** Blocking at top level; cooperatively awaitable inside async functions. */
121
+ untilConnected(timeoutMs: number = 0): Promise<boolean> {
122
+ bleUntilConnected(timeoutMs);
123
+ return Promise.resolve(false);
124
+ }
125
+
126
+ untilConnectedStart(): void { bleUntilConnectedStart(); }
127
+ isConnected(): boolean { return bleIsConnected(); }
128
+ status(): BleStatus { return bleStatus() as BleStatus; }
129
+ clientCount(): number { return bleClientCount(); }
130
+ txPower(dbm: number): this { bleSetTxPower(dbm); return this; }
131
+ notify(index: number, value: number): void { bleNotify(index, value); }
132
+ }
133
+
134
+ /**
135
+ * Fluent GATT server builder. Returned by `Ble.server()`.
136
+ *
137
+ * Single-class fluent chain (like HttpRequest): characteristic() returns `this`,
138
+ * so onRead/onWrite/onSubscribe chain directly. The _charCount field tracks
139
+ * which characteristic slot the callbacks attach to.
140
+ *
141
+ * Field tracking (read by the transpiler resolver via ctor field assignment):
142
+ * _name — advertised device name
143
+ * _charCount — current characteristic index (the last characteristic() target)
144
+ * _svcCount — current service index
145
+ */
146
+ export class BleServer {
147
+ private _name: string;
148
+ private _charCount: number;
149
+ private _lastChar: number;
150
+ private _svcCount: number;
151
+
152
+ constructor(name: string, charCount: number, svcCount: number) {
153
+ this._name = name;
154
+ this._charCount = charCount;
155
+ this._lastChar = charCount;
156
+ this._svcCount = svcCount;
157
+ }
158
+
159
+ /** Add a characteristic by UUID, value type, and permissions.
160
+ * Combine permissions with `|`: `BlePerm.Read | BlePerm.Notify`.
161
+ * Returns this for chaining. */
162
+ characteristic(uuid: string, type: BleValueType, perms: number): this {
163
+ bleAddChar(this._charCount, uuid, type, perms, this._svcCount);
164
+ return this;
165
+ }
166
+
167
+ /** Begin a new service grouping. Subsequent characteristics attach to it. */
168
+ service(uuid: string): this {
169
+ bleAddService(uuid);
170
+ return this;
171
+ }
172
+
173
+ /** Register a read handler for the most recently added characteristic. */
174
+ onRead(handler: () => CharValue): this {
175
+ bleOnRead(this._lastChar, callback(handler));
176
+ return this;
177
+ }
178
+
179
+ /** Register a write handler for the most recently added characteristic. */
180
+ onWrite(handler: (value: number) => void): this {
181
+ bleOnWrite(this._lastChar, callback(handler));
182
+ return this;
183
+ }
184
+
185
+ /** Register a connect handler (called when a central connects). */
186
+ onConnect(handler: () => void): this {
187
+ bleOnConnect(callback(handler));
188
+ return this;
189
+ }
190
+
191
+ /** Register a disconnect handler (called when a central disconnects). */
192
+ onDisconnect(handler: () => void): this {
193
+ bleOnDisconnect(callback(handler));
194
+ return this;
195
+ }
196
+
197
+ /** Push a new value to subscribed clients on the most recently added characteristic. */
198
+ notify(value: number): void {
199
+ bleNotify(this._lastChar, value);
200
+ }
201
+
202
+ /** Initialize NimBLE, register services, and start advertising. */
203
+ begin(): void {
204
+ bleServerBegin(this._name);
205
+ bleAdvertiseStart();
206
+ }
207
+ }
208
+
209
+ export const Ble = new BleClass();
@@ -0,0 +1,31 @@
1
+ /**
2
+ * CapacitiveClass — ESP32 on-chip capacitive touch pins.
3
+ *
4
+ * ESP32-family chips have dedicated capacitive-sensing GPIOs (10 on classic
5
+ * ESP32, up to 14 on S3) that read touch/proximity without external
6
+ * components — distinct from the I2C/SPI touch *display* controllers (FT6336U,
7
+ * GT911, etc.). Lowered to capacitive.* HAL ops: ESP-IDF's touch_sensor driver.
8
+ *
9
+ * Pin numbers are the touch-pad-capable GPIOs (GPIO4, GPIO0, GPIO2, ... on
10
+ * classic ESP32); the framework maps them to touch_channel indices.
11
+ */
12
+ export class CapacitiveClass {
13
+ static readonly __instance_name = "Capacitive";
14
+
15
+ /** Read the raw capacitive value of a touch pin. Higher = more capacitance
16
+ * (touched). The raw scale is chip-dependent; use a threshold calibrated
17
+ * against the untouched reading. */
18
+ read(pin: number): number {
19
+ return capacitiveRead(pin);
20
+ }
21
+
22
+ /** True when the pin's reading exceeds `threshold` (a convenience over read()). */
23
+ isTouched(pin: number, threshold: number): boolean {
24
+ return capacitiveRead(pin) > threshold;
25
+ }
26
+ }
27
+
28
+ export const Capacitive = new CapacitiveClass();
29
+
30
+ // ── Semantic primitive (resolved to capacitive.* HAL op by the transpiler) ──
31
+ export function capacitiveRead(pin: number): number { return 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
  // ---------------------------------------------------------------------------
@@ -218,6 +266,104 @@ export function powerDeepSleep(ms: number): void {}
218
266
  export function powerLightSleep(): void {}
219
267
  /** Set the CPU frequency (MHz). */
220
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; }
221
367
 
222
368
  // ---------------------------------------------------------------------------
223
369
  // Raw C++ escape hatch
@@ -225,3 +371,20 @@ export function powerSetCpuFrequency(mhz: number): void {}
225
371
 
226
372
  /** Emit raw C++ code (escape hatch for unsupported operations). */
227
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; }