Hardware Simulation
@typecad/hal/sim runs your program's logic in Node.js against a simulated board —
virtual pins, buses, and mock devices. No board attached, no flashing: CI jobs and
failure-path tests that would be impossible on real hardware.
createSimBoard
Builds a SimBoard — a virtual board with the pin and bus populations you ask for. Typed accessors (.digital(n), .i2c(0), ...) throw if that pin or bus doesn't exist, so the sim stays honest about the hardware.
Example first
sim.test.ts
import { createSimBoard } from '@typecad/hal/sim';
const board = createSimBoard({ digitalPinCount: 14 });
const pin = board.digital(13);
pin.asOutput();
pin.write(1);
if (!pin.isHigh()) throw new Error('pin did not set');
pin.injectValue(0); // pretend the wire changed
if (!pin.isLow()) throw new Error('inject failed');
const history = pin.getHistory(); // every write, with timestampsSimBoardConfig
| Option | Default | Meaning |
|---|---|---|
| digitalPinCount | 14 | Digital pins |
| analogPinCount | 6 | Analog-capable pins |
| i2cBusCount / spiBusCount / uartCount | 1 | Bus population |
| pwmPins / interruptPins | [] | Pins with PWM or interrupt capability |
| uartRxBufferSize / uartTxBufferSize | 256 | Serial buffer sizes (bytes) |
mock devices
Attach JavaScript objects as I2C/SPI peripherals: scripted register responses for the happy path, empty reads for fault injection, and an operation log proving exactly what your code put on the wire.
Example first
i2c-mock.ts
import { createSimBoard } from '@typecad/hal/sim';
const board = createSimBoard({});
const i2c = board.i2c(0);
// a minimal BME280: respond to register reads
i2c.attachDevice(0x76, {
read(register: number, count: number): number[] {
if (register === 0xD0) return [0x60]; // chip id
return new Array(count).fill(0);
},
write(register: number, data: number[]): void {},
});
const id = i2c.device(0x76).read(0xD0, 1);
if (id[0] !== 0x60) throw new Error('wrong chip');
i2c.getLog(); // every read/write with address, register, data, timestamp
i2c.clearLog();SPI — keyed by chip select
spi-mock.ts
// SPI mocks are keyed by chip-select pin
const board = createSimBoard({});
const spi = board.spi(0);
const cs = board.digital(10);
spi.attachDevice(cs, {
transfer(mosi: number[]): number[] {
return [0xEF, 0x40, 0x18]; // JEDEC id bytes
},
});
const id = spi.device(cs).readRegister(0x9F, 3);What can be simulated
| Simulator class | What it does |
|---|---|
| SimDigitalPin | write/read/toggle, input pulls, injected values, write history |
| SimAnalogPin | inject raw counts or volts, resolution and reference voltage |
| SimPWMPin | duty writes, percent readback |
| SimInterruptPin | fire interrupts and pin transitions by hand |
| SimSerialPort | print/write into a TX buffer you can peek at, inject RX bytes |
| SimI2CBus | attach mock devices, operation log, NACK on unknown addresses |
| SimSPIBus | attach mock devices by chip-select pin, transfer logs |