🧰 Hardware Abstraction Layer (HAL)

The HAL is the set of TypeScript objects you use to talk to the board β€” each pin, bus, and built-in feature is an object, and the HAL checks that you're using them in ways the hardware actually supports. A wrong pin assignment, a missing setup step, or two features fighting over the same pin is caught before the firmware reaches the board.

Areas

Every class follows one shape: construction carries the configuration (pin flags, baud, period, broker URI β€” all set where the object is created). Units are what the hardware talks: nanoseconds for PWM, raw counts for ADC, milliseconds throughout.

Start here

Peripherals & I/O

Networking & data

UI

Language & toolchain

Test & verify

Compile-time directives

Three functions that exist purely for code generation β€” the TypeScript never executes.

directive

rawCpp / rawCppExpr

Inject raw C++ at the call site β€” for features the HAL doesn't cover, such as mDNS or OTA.

Example first

raw-cpp.ts
import { rawCpp, rawCppExpr } from '@typecad/hal';

rawCpp('PORTB |= (1 << PB5)');      // statement injected at the call site
const pb5 = rawCppExpr('PORTB');    // expression form β€” where a value is expected
directive

include

Adds a C++ #include to the generated firmware.

Example first

include.ts
import { include } from '@typecad/hal';

include('<Wire.h>');   // adds a C++ #include to the output; no-op while type-checking
directive

board

Resolves a board definition value at compile time β€” the generated board module's facts, by path.

Example first

board-facts.ts
import { board } from '@typecad/hal';

const resolution = board('peripherals.pwm.resolution');

Pin model

Pins are typed objects, not integer constants. Everything imports from @typecad/hal, including the named board pins. Using a pin for something it cannot do (analog on a digital-only pad, PWM on a non-PWM pin) is caught at build time with the valid pins named.

pin-arguments.ts
import { GPIO, ADC, PWM, LED, A1, PB6 } from '@typecad/hal';

const led = new GPIO(LED, GPIO.OUTPUT | GPIO.OUTPUT_INIT_LOW);  // set / get / toggle
const sense = new ADC(A1);                                      // read / readMillivolts
const motor = new PWM(PB6, { periodNs: 20_000_000 });           // setPulse / setDuty

Timing, threads, async

One Time object with a blocking sleep, monotonic clocks, and a sub-millisecond spin:

time.ts
import { Time } from '@typecad/hal';

Time.sleep(1000);        // blocks at the top level
await Time.sleep(50);    // inside an async function: other tasks run while you wait
const ms = Time.now();   // milliseconds since boot, monotonic, no wrap
Time.busyWaitUs(3);      // a sub-millisecond spin

Periodic and concurrent work is a Thread β€” a real background thread, not a callback timer. Construction carries the stack size and priority; start(fn) schedules it; join() blocks until it exits. For deterministic sub-millisecond periodic execution use the hardware Counter instead.

thread.ts
import { Thread } from '@typecad/hal';

const blinker = new Thread(0, { stackKb: 2 });   // 0 = the first thread slot
blinker.start(() => {
  while (true) {
    led.toggle();
    Time.sleep(500);
  }
});
blinker.join();          // park main here β€” the thread runs forever

Long waits β€” Time.sleep, network requests β€” can yield, so the rest of your program keeps running while you wait. When you need to wait without blocking other tasks, Async gives you the cooperative forms:

async.ts
import { Async } from '@typecad/hal';

async function heartbeat() {
  while (true) {
    led.toggle();
    await Time.sleep(500);      // other tasks run while you wait
  }
}

await Async.sleep(1000);        // non-blocking sleep
await Async.yield();            // let other tasks run once

Board singletons

Bus singletons are ready to use β€” UART0.writeLine(...) needs no construction, and I2C0.device(0x44) hands back a working target. A board that lacks a controller does not export its name β€” that is a module-resolution error at build time, not a runtime surprise.

singletons.ts
import { LED, UART0, I2C0, GPIO } from '@typecad/hal';

const led = new GPIO(LED, GPIO.OUTPUT);
UART0.writeLine('hello');
const dev = I2C0.device(0x44);      // register verbs callable immediately
Get started

Something wrong or missing? Open an issue β€” we'd rather hear about it.

Β© 2026 typeCAD β€” code-defined electronics github