@kog/hardware
The imperative hardware layer. The hardware hooks are thin reactive wrappers over these APIs; use this layer directly for full control or outside components.
All pin parameters accept PinLike = number | Pin. Plain numbers are native SoC GPIOs (port 0). gpio(num) builds a Pin; Pin objects allow future controllers (GPIO expanders) without API changes.
GPIO
claim(pin, mode: 'input' | 'output' | 'input-pullup' | 'input-pulldown' | 'output-open-drain'): void
release(pin): void
digitalRead(pin): number // 0 | 1
digitalWrite(pin, value: 0 | 1 | boolean): void
watch(pin, edge: 'rising' | 'falling' | 'both', cb: (level: number) => void,
opts?: { debounceMs?: number }): { stop(): void }
Watches are interrupt-driven with native debounce. Call stop() on the returned handle to detach.
ADC
analogRead(pin): number // mV
adc.subscribe(pin, { hz?, periodMs? }, cb: (mv: number) => void): { stop(): void } // default 10 Hz
PWM
const ch = pwm.open(pin, { freqHz?, resBits? }); // defaults: 1000 Hz, 10-bit
ch.setDuty(duty: number): void // 0..1
ch.close(): void
ch.channel // the native channel number
Channels sharing a frequency+resolution share hardware timers automatically. Exhaustion throws an HwError with code === 'PWM_EXHAUSTED'.
I2C
const bus = i2c.open({ sda, scl, freqHz? }); // synchronous; default 100 kHz
await bus.writeRead(addr, tx, readLen): Uint8Array // write, then read back readLen bytes
await bus.write(addr, bytes): void
await bus.read(addr, len): Uint8Array
bus.close(): void
SPI
const dev = spi.open({ sck, mosi, miso, cs?, freqHz? }); // synchronous; default 1 MHz
await dev.transfer(tx, readLen): Uint8Array // cs is bound at open
await dev.write(bytes): void
await dev.read(len): Uint8Array
dev.close(): void
tx and bytes accept a number[], an ArrayBuffer, or any typed-array view. Bus transactions execute on a native worker task and return Promises, so they can never block rendering or input.
Errors
Hardware errors throw (or reject) an HwError with a stable .code you can branch on: PIN_RESERVED (the pin belongs to the board's display, touch panel, or backlight, or to the chip's flash/PSRAM; the simulator never raises it), PIN_BUSY, PIN_INVALID, PIN_CAPABILITY (for example, an output on an input-only pin), PWM_EXHAUSTED, BUS_EXHAUSTED, ADC2_WIFI (ADC2 is unavailable while Wi-Fi is up), BUS_CLOSED, and XFER_FAILED. No error names the pin's owner or lists free resources; apart from JS-side pin validation (PIN_INVALID with a short detail), the message is just the code.