@typecad/simulator 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/board/board-sim.d.ts +52 -0
- package/dist/board/board-sim.js +188 -0
- package/dist/board/from-definition.d.ts +43 -0
- package/dist/board/from-definition.js +82 -0
- package/dist/bus/i2c-sim.d.ts +61 -0
- package/dist/bus/i2c-sim.js +155 -0
- package/dist/bus/serial-sim.d.ts +71 -0
- package/dist/bus/serial-sim.js +241 -0
- package/dist/bus/spi-sim.d.ts +65 -0
- package/dist/bus/spi-sim.js +202 -0
- package/dist/contracts.d.ts +223 -0
- package/dist/contracts.js +87 -0
- package/dist/gpio/analog-pin-sim.d.ts +40 -0
- package/dist/gpio/analog-pin-sim.js +75 -0
- package/dist/gpio/digital-pin-sim.d.ts +68 -0
- package/dist/gpio/digital-pin-sim.js +172 -0
- package/dist/gpio/interrupt-sim.d.ts +69 -0
- package/dist/gpio/interrupt-sim.js +118 -0
- package/dist/gpio/pwm-pin-sim.d.ts +41 -0
- package/dist/gpio/pwm-pin-sim.js +85 -0
- package/dist/helpers.d.ts +40 -0
- package/dist/helpers.js +57 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +21 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.js +4 -0
- package/package.json +62 -0
- package/src/board/board-sim.ts +208 -0
- package/src/board/from-definition.ts +91 -0
- package/src/bus/i2c-sim.ts +212 -0
- package/src/bus/serial-sim.ts +280 -0
- package/src/bus/spi-sim.ts +269 -0
- package/src/contracts.ts +330 -0
- package/src/gpio/analog-pin-sim.ts +91 -0
- package/src/gpio/digital-pin-sim.ts +234 -0
- package/src/gpio/interrupt-sim.ts +151 -0
- package/src/gpio/pwm-pin-sim.ts +103 -0
- package/src/helpers.ts +100 -0
- package/src/index.ts +59 -0
- package/src/types.ts +104 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @typecad/simulator — Interrupt simulation
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import type { InterruptPin, InterruptOptions } from '../contracts.js';
|
|
6
|
+
import type { InterruptHandler } from '@typecad/hal';
|
|
7
|
+
import { SimDigitalPin } from './digital-pin-sim.js';
|
|
8
|
+
|
|
9
|
+
type InterruptMode = 'rising' | 'falling' | 'change';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tracks interrupt firings for test assertions.
|
|
13
|
+
*/
|
|
14
|
+
export interface InterruptEvent {
|
|
15
|
+
/** Timestamp (ms since simulation start) */
|
|
16
|
+
timestamp: number;
|
|
17
|
+
/** The trigger mode */
|
|
18
|
+
mode: InterruptMode;
|
|
19
|
+
/** The pin value at time of interrupt */
|
|
20
|
+
pinValue: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Simulated interrupt controller for a single pin.
|
|
25
|
+
*
|
|
26
|
+
* Extends SimDigitalPin with interrupt capabilities.
|
|
27
|
+
* Tests can fire interrupts manually via `fireInterrupt()` and verify
|
|
28
|
+
* that handlers were called with the correct arguments.
|
|
29
|
+
*/
|
|
30
|
+
export class SimInterruptPin extends SimDigitalPin implements InterruptPin {
|
|
31
|
+
private _handlers: Map<InterruptMode, InterruptHandler> = new Map();
|
|
32
|
+
private _debounceMs: number = 0;
|
|
33
|
+
private _events: InterruptEvent[] = [];
|
|
34
|
+
|
|
35
|
+
constructor(pinNum: number) {
|
|
36
|
+
super(pinNum, { interrupt: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// --- InterruptPin capability methods ---
|
|
40
|
+
|
|
41
|
+
onRising(handler: InterruptHandler, options?: InterruptOptions): void {
|
|
42
|
+
this._handlers.set('rising', handler);
|
|
43
|
+
if (options?.debounce) this._debounceMs = options.debounce;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
onFalling(handler: InterruptHandler, options?: InterruptOptions): void {
|
|
47
|
+
this._handlers.set('falling', handler);
|
|
48
|
+
if (options?.debounce) this._debounceMs = options.debounce;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onChange(handler: InterruptHandler, options?: InterruptOptions): void {
|
|
52
|
+
this._handlers.set('change', handler);
|
|
53
|
+
if (options?.debounce) this._debounceMs = options.debounce;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
offInterrupts(): void {
|
|
57
|
+
this._handlers.clear();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Remove the rising-edge handler. */
|
|
61
|
+
offRising(): void {
|
|
62
|
+
this._handlers.delete('rising');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Remove the falling-edge handler. */
|
|
66
|
+
offFalling(): void {
|
|
67
|
+
this._handlers.delete('falling');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Remove all interrupt handlers. */
|
|
71
|
+
offAll(): void {
|
|
72
|
+
this._handlers.clear();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Check if any interrupt handlers are registered. */
|
|
76
|
+
hasInterrupt(): boolean {
|
|
77
|
+
return this._handlers.size > 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// --- Simulation helpers ---
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Fire an interrupt manually from test code.
|
|
84
|
+
* @param mode - The interrupt mode to fire
|
|
85
|
+
* @param pinValue - The current pin value (for event logging)
|
|
86
|
+
*/
|
|
87
|
+
fireInterrupt(mode: InterruptMode, pinValue: number = 0): void {
|
|
88
|
+
const handler = this._handlers.get(mode);
|
|
89
|
+
if (handler) {
|
|
90
|
+
handler();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Also fire 'change' handler for any edge
|
|
94
|
+
if (mode !== 'change' && this._handlers.has('change')) {
|
|
95
|
+
this._handlers.get('change')!();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this._events.push({
|
|
99
|
+
timestamp: Date.now() - this._startTime,
|
|
100
|
+
mode,
|
|
101
|
+
pinValue,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Simulate a pin value transition, automatically firing the appropriate interrupts.
|
|
107
|
+
* @param from - Previous pin value (0 or 1)
|
|
108
|
+
* @param to - New pin value (0 or 1)
|
|
109
|
+
*/
|
|
110
|
+
simulateTransition(from: number, to: number): void {
|
|
111
|
+
if (from === to) return;
|
|
112
|
+
|
|
113
|
+
if (from === 0 && to === 1) {
|
|
114
|
+
this.fireInterrupt('rising', to);
|
|
115
|
+
} else if (from === 1 && to === 0) {
|
|
116
|
+
this.fireInterrupt('falling', to);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get all recorded interrupt events.
|
|
122
|
+
*/
|
|
123
|
+
getEvents(): readonly InterruptEvent[] {
|
|
124
|
+
return this._events;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Clear interrupt event history.
|
|
129
|
+
*/
|
|
130
|
+
clearEvents(): void {
|
|
131
|
+
this._events = [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get the configured debounce time.
|
|
136
|
+
*/
|
|
137
|
+
getDebounceMs(): number {
|
|
138
|
+
return this._debounceMs;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Reset to initial state.
|
|
143
|
+
*/
|
|
144
|
+
override reset(): void {
|
|
145
|
+
super.reset();
|
|
146
|
+
this._handlers.clear();
|
|
147
|
+
this._debounceMs = 0;
|
|
148
|
+
this._events = [];
|
|
149
|
+
this._startTime = Date.now();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @typecad/simulator — Simulated PWM pin
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import type { PWMPin } from '../contracts.js';
|
|
6
|
+
import type { AnalogValue, DigitalValue } from '@typecad/hal';
|
|
7
|
+
import { SimDigitalPin } from './digital-pin-sim.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Simulated PWM pin. Extends digital pin with PWM duty cycle control.
|
|
11
|
+
*/
|
|
12
|
+
export class SimPWMPin extends SimDigitalPin implements PWMPin {
|
|
13
|
+
private _pwmPercent: number = 0;
|
|
14
|
+
private _pwmFrequency: number = 490; // Default Arduino PWM frequency
|
|
15
|
+
private _pwmResolution: number = 8; // Default 8-bit
|
|
16
|
+
private _attached: boolean = false;
|
|
17
|
+
|
|
18
|
+
constructor(pinNum: number) {
|
|
19
|
+
super(pinNum);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// --- PWMPin ---
|
|
23
|
+
|
|
24
|
+
write(value: DigitalValue | AnalogValue): void {
|
|
25
|
+
if (typeof value === 'number' && value > 1) {
|
|
26
|
+
// Analog write — treat as PWM duty cycle value
|
|
27
|
+
this.pwm((value / ((1 << this._pwmResolution) - 1)) * 100);
|
|
28
|
+
} else {
|
|
29
|
+
super.write(value as DigitalValue);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pwm(percent?: number): void {
|
|
34
|
+
// Calling pwm() (no args) activates PWM mode, pwm(x) sets duty cycle
|
|
35
|
+
// 0% duty cycle means PWM is inactive
|
|
36
|
+
this._attached = percent === undefined || percent > 0;
|
|
37
|
+
if (percent !== undefined) {
|
|
38
|
+
this._pwmPercent = Math.max(0, Math.min(100, percent));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
setFrequency(hz: number): void {
|
|
43
|
+
this._pwmFrequency = hz;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Current PWM frequency in Hz (contract method name). */
|
|
47
|
+
getPwmFrequency(): number {
|
|
48
|
+
return this._pwmFrequency;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Current PWM resolution in bits (contract method name). */
|
|
52
|
+
getPwmResolution(): number {
|
|
53
|
+
return this._pwmResolution;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @deprecated Use getPwmFrequency() to match the HAL contract. */
|
|
57
|
+
getFrequency(): number {
|
|
58
|
+
return this.getPwmFrequency();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @deprecated Use getPwmResolution() to match the HAL contract. */
|
|
62
|
+
getResolution(): number {
|
|
63
|
+
return this.getPwmResolution();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// --- Simulation helpers ---
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get the current PWM duty cycle as a percentage (0-100).
|
|
70
|
+
*/
|
|
71
|
+
getPwmPercent(): number {
|
|
72
|
+
return this._pwmPercent;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the current PWM duty cycle as a raw value (0 to 2^resolution - 1).
|
|
77
|
+
*/
|
|
78
|
+
getPwmValue(): number {
|
|
79
|
+
return Math.round((this._pwmPercent / 100) * ((1 << this._pwmResolution) - 1));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Check if PWM is currently active.
|
|
84
|
+
*/
|
|
85
|
+
isPwmActive(): boolean {
|
|
86
|
+
return this._attached;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Set the PWM resolution in bits.
|
|
91
|
+
*/
|
|
92
|
+
setResolution(bits: number): void {
|
|
93
|
+
this._pwmResolution = bits;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
override reset(): void {
|
|
97
|
+
super.reset();
|
|
98
|
+
this._pwmPercent = 0;
|
|
99
|
+
this._pwmFrequency = 490;
|
|
100
|
+
this._pwmResolution = 8;
|
|
101
|
+
this._attached = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @typecad/simulator — Result factory helpers
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Result of a byte read operation (used internally by bus simulators).
|
|
7
|
+
*/
|
|
8
|
+
export interface IByteReadResult {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
status: number;
|
|
11
|
+
bytes: Uint8Array;
|
|
12
|
+
bytesRead: number;
|
|
13
|
+
timedOut: boolean;
|
|
14
|
+
asUint8(): number;
|
|
15
|
+
asInt8(): number;
|
|
16
|
+
asUint16(endian: 'be' | 'le'): number;
|
|
17
|
+
asInt16(endian: 'be' | 'le'): number;
|
|
18
|
+
asUint32(endian: 'be' | 'le'): number;
|
|
19
|
+
asInt32(endian: 'be' | 'le'): number;
|
|
20
|
+
asString(): string;
|
|
21
|
+
asStringTrim(): string;
|
|
22
|
+
asInt(): number;
|
|
23
|
+
asFloat(): number;
|
|
24
|
+
unwrap(): Uint8Array;
|
|
25
|
+
unwrapOr(defaultValue: Uint8Array): Uint8Array;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Result of a write operation (used internally by bus simulators).
|
|
30
|
+
*/
|
|
31
|
+
export interface IWriteResult {
|
|
32
|
+
ok: boolean;
|
|
33
|
+
status: number;
|
|
34
|
+
bytesWritten: number;
|
|
35
|
+
unwrap(): number;
|
|
36
|
+
unwrapOr(defaultValue: number): number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create an IByteReadResult with the given bytes.
|
|
41
|
+
*/
|
|
42
|
+
export function createByteReadResult(
|
|
43
|
+
bytes: Uint8Array,
|
|
44
|
+
ok: boolean = true,
|
|
45
|
+
status: number = 0,
|
|
46
|
+
timedOut: boolean = false,
|
|
47
|
+
): IByteReadResult {
|
|
48
|
+
return {
|
|
49
|
+
ok,
|
|
50
|
+
status,
|
|
51
|
+
bytes,
|
|
52
|
+
bytesRead: bytes.length,
|
|
53
|
+
timedOut,
|
|
54
|
+
asUint8(): number { return bytes[0] ?? 0; },
|
|
55
|
+
asInt8(): number { const v = bytes[0] ?? 0; return v > 127 ? v - 256 : v; },
|
|
56
|
+
asUint16(endian: 'be' | 'le'): number {
|
|
57
|
+
if (bytes.length < 2) return 0;
|
|
58
|
+
return endian === 'be'
|
|
59
|
+
? ((bytes[0]! << 8) | bytes[1]!) >>> 0
|
|
60
|
+
: ((bytes[1]! << 8) | bytes[0]!) >>> 0;
|
|
61
|
+
},
|
|
62
|
+
asInt16(endian: 'be' | 'le'): number {
|
|
63
|
+
const v = this.asUint16(endian);
|
|
64
|
+
return v > 32767 ? v - 65536 : v;
|
|
65
|
+
},
|
|
66
|
+
asUint32(endian: 'be' | 'le'): number {
|
|
67
|
+
if (bytes.length < 4) return 0;
|
|
68
|
+
return endian === 'be'
|
|
69
|
+
? ((bytes[0]! << 24) | (bytes[1]! << 16) | (bytes[2]! << 8) | bytes[3]!) >>> 0
|
|
70
|
+
: ((bytes[3]! << 24) | (bytes[2]! << 16) | (bytes[1]! << 8) | bytes[0]!) >>> 0;
|
|
71
|
+
},
|
|
72
|
+
asInt32(endian: 'be' | 'le'): number {
|
|
73
|
+
const v = this.asUint32(endian);
|
|
74
|
+
return v > 2147483647 ? v - 4294967296 : v;
|
|
75
|
+
},
|
|
76
|
+
asString(): string { return new TextDecoder().decode(bytes); },
|
|
77
|
+
asStringTrim(): string { return this.asString().trim(); },
|
|
78
|
+
asInt(): number { return parseInt(this.asString(), 10) || 0; },
|
|
79
|
+
asFloat(): number { return parseFloat(this.asString()) || 0; },
|
|
80
|
+
unwrap(): Uint8Array { return bytes; },
|
|
81
|
+
unwrapOr(defaultValue: Uint8Array): Uint8Array { return ok ? bytes : defaultValue; },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create an IWriteResult with the given bytesWritten count.
|
|
87
|
+
*/
|
|
88
|
+
export function createWriteResult(
|
|
89
|
+
bytesWritten: number,
|
|
90
|
+
ok: boolean = true,
|
|
91
|
+
status: number = 0,
|
|
92
|
+
): IWriteResult {
|
|
93
|
+
return {
|
|
94
|
+
ok,
|
|
95
|
+
status,
|
|
96
|
+
bytesWritten,
|
|
97
|
+
unwrap(): number { return bytesWritten; },
|
|
98
|
+
unwrapOr(defaultValue: number): number { return ok ? bytesWritten : defaultValue; },
|
|
99
|
+
};
|
|
100
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @typecad/simulator — Barrel export
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
// --- GPIO simulation ---
|
|
6
|
+
export { SimDigitalPin } from './gpio/digital-pin-sim.js';
|
|
7
|
+
export { SimAnalogPin } from './gpio/analog-pin-sim.js';
|
|
8
|
+
export { SimPWMPin } from './gpio/pwm-pin-sim.js';
|
|
9
|
+
export { SimInterruptPin } from './gpio/interrupt-sim.js';
|
|
10
|
+
export type { InterruptEvent } from './gpio/interrupt-sim.js';
|
|
11
|
+
|
|
12
|
+
// --- Bus simulation ---
|
|
13
|
+
export { SimSerialPort } from './bus/serial-sim.js';
|
|
14
|
+
export { SimI2CBus } from './bus/i2c-sim.js';
|
|
15
|
+
export type { I2COperationLog } from './bus/i2c-sim.js';
|
|
16
|
+
export { SimSPIBus } from './bus/spi-sim.js';
|
|
17
|
+
export type { SPIOperationLog } from './bus/spi-sim.js';
|
|
18
|
+
|
|
19
|
+
// --- Board factory ---
|
|
20
|
+
export { SimBoard, createSimBoard } from './board/board-sim.js';
|
|
21
|
+
export { createBoardFromDefinition } from './board/from-definition.js';
|
|
22
|
+
|
|
23
|
+
// --- Runtime contract interfaces (relocated from @typecad/hal) ---
|
|
24
|
+
// Pin/bus contracts implemented by the Sim* classes above.
|
|
25
|
+
export type {
|
|
26
|
+
BasePin,
|
|
27
|
+
PWMPin,
|
|
28
|
+
AnalogPin,
|
|
29
|
+
InterruptPin,
|
|
30
|
+
IOutputModePin,
|
|
31
|
+
IInputModePin,
|
|
32
|
+
II2CBus,
|
|
33
|
+
II2CDeviceAccessor,
|
|
34
|
+
ISPIBus,
|
|
35
|
+
ISPIDevice,
|
|
36
|
+
IUARTBus,
|
|
37
|
+
ISerialPort,
|
|
38
|
+
PinCapabilityFlags,
|
|
39
|
+
IToneAttachment,
|
|
40
|
+
UARTStatusInfo,
|
|
41
|
+
InterruptOptions,
|
|
42
|
+
ErrorPolicy,
|
|
43
|
+
} from './contracts.js';
|
|
44
|
+
export { I2CStatus, SPIStatus, UARTStatus } from './contracts.js';
|
|
45
|
+
export { hasPWM, hasAnalogInput, hasInterrupt, assertPWM, assertAnalog, assertInterrupt } from './contracts.js';
|
|
46
|
+
|
|
47
|
+
// --- Types ---
|
|
48
|
+
export { PinMode } from '@typecad/hal';
|
|
49
|
+
export type {
|
|
50
|
+
PinChangeCallback,
|
|
51
|
+
InterruptCallback,
|
|
52
|
+
ISimI2CDevice,
|
|
53
|
+
ISimSPIDevice,
|
|
54
|
+
SimBoardType,
|
|
55
|
+
SimBoardConfig,
|
|
56
|
+
} from './types.js';
|
|
57
|
+
|
|
58
|
+
// --- Helpers ---
|
|
59
|
+
export { createByteReadResult, createWriteResult } from './helpers.js';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @typecad/simulator — Simulator-specific types
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Callback signature for pin state change events.
|
|
7
|
+
* @param pinNumber - The pin number that changed
|
|
8
|
+
* @param newValue - The new digital value (HIGH=1 or LOW=0)
|
|
9
|
+
*/
|
|
10
|
+
export type PinChangeCallback = (pinNumber: number, newValue: number) => void;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Callback signature for interrupt events.
|
|
14
|
+
* @param pinNumber - The interrupt pin number
|
|
15
|
+
* @param mode - The trigger mode ('rising', 'falling', or 'change')
|
|
16
|
+
*/
|
|
17
|
+
export type InterruptCallback = (pinNumber: number, mode: 'rising' | 'falling' | 'change') => void;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A simulated I2C device that responds to register reads and writes.
|
|
21
|
+
* Tests register mock devices to simulate sensor behavior.
|
|
22
|
+
*/
|
|
23
|
+
export interface ISimI2CDevice {
|
|
24
|
+
/**
|
|
25
|
+
* Handle a register read from the bus master.
|
|
26
|
+
* @param register - The register address being read
|
|
27
|
+
* @param count - Number of bytes requested
|
|
28
|
+
* @returns Array of byte values to return
|
|
29
|
+
*/
|
|
30
|
+
read(register: number, count: number): number[];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Handle a register write from the bus master.
|
|
34
|
+
* @param register - The register address being written
|
|
35
|
+
* @param data - The data bytes being written
|
|
36
|
+
*/
|
|
37
|
+
write(register: number, data: number[]): void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A simulated SPI device that responds to transfers.
|
|
42
|
+
*/
|
|
43
|
+
export interface ISimSPIDevice {
|
|
44
|
+
/**
|
|
45
|
+
* Handle a full-duplex SPI transfer.
|
|
46
|
+
* @param mosiData - Data sent from master (MOSI)
|
|
47
|
+
* @returns Data received from device (MISO)
|
|
48
|
+
*/
|
|
49
|
+
transfer(mosiData: number[]): number[];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Handle a register write (optional).
|
|
53
|
+
* If not provided, falls back to transfer().
|
|
54
|
+
* @param register - The register address
|
|
55
|
+
* @param data - The data bytes being written
|
|
56
|
+
*/
|
|
57
|
+
write?(register: number, data: number[]): void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Handle a register read (optional).
|
|
61
|
+
* If not provided, falls back to transfer() with dummy bytes.
|
|
62
|
+
* @param register - The register address
|
|
63
|
+
* @param count - Number of bytes to read
|
|
64
|
+
* @returns Data bytes read from the register
|
|
65
|
+
*/
|
|
66
|
+
readRegister?(register: number, count: number): number[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Board type identifiers for the simulator factory.
|
|
71
|
+
*/
|
|
72
|
+
export type SimBoardType = 'arduino-uno' | 'arduino-nano' | string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Configuration for creating a simulated board.
|
|
76
|
+
*/
|
|
77
|
+
export interface SimBoardConfig {
|
|
78
|
+
/** Board type identifier. Determines pin count and peripheral availability. */
|
|
79
|
+
boardType: SimBoardType;
|
|
80
|
+
/** Number of digital pins (default: 14 for Uno) */
|
|
81
|
+
digitalPinCount?: number;
|
|
82
|
+
/** Number of analog input pins (default: 6 for Uno) */
|
|
83
|
+
analogPinCount?: number;
|
|
84
|
+
/** Number of I2C buses (default: 1) */
|
|
85
|
+
i2cBusCount?: number;
|
|
86
|
+
/** Number of SPI buses (default: 1) */
|
|
87
|
+
spiBusCount?: number;
|
|
88
|
+
/** Number of UART ports (default: 1) */
|
|
89
|
+
uartCount?: number;
|
|
90
|
+
/**
|
|
91
|
+
* PWM-capable pin numbers. Overrides the board type's default PWM pin map.
|
|
92
|
+
* For unknown/custom board types the default is empty (no PWM pins).
|
|
93
|
+
*/
|
|
94
|
+
pwmPins?: number[];
|
|
95
|
+
/**
|
|
96
|
+
* Interrupt-capable pin numbers. Overrides the board type's default interrupt
|
|
97
|
+
* pin map. For unknown/custom board types the default is empty.
|
|
98
|
+
*/
|
|
99
|
+
interruptPins?: number[];
|
|
100
|
+
/** RX buffer size for UART simulation (default: 256) */
|
|
101
|
+
uartRxBufferSize?: number;
|
|
102
|
+
/** TX buffer size for UART simulation (default: 256) */
|
|
103
|
+
uartTxBufferSize?: number;
|
|
104
|
+
}
|