Bridging Hardware and Firmware
When you design a board in typeCAD, you know exactly which MCU pins are connected to what. But your firmware code doesn’t — it just sees a generic board package with every pin available. The new hardware contract feature closes that gap.
pcb.contract() generates a JSON manifest describing which MCU pins are wired in your circuit. Your firmware toolchain consumes this to generate a board wrapper that only exposes the pins and peripherals that actually exist on your board.
The Problem
Most firmware projects start with a board support package that exposes every pin on the MCU. But your actual board only uses a subset. Without a way to communicate the hardware layout to the firmware, you end up with:
- Pin mismatches between hardware and firmware
- No automated way to know which peripherals (I2C, SPI, UART) are available
- Manual coordination between hardware and firmware developers
The Solution
After creating your board, call contract() with a pin mapping from your TypeHAL board package:
import { PCB } from '@typecad/typecad';
import { pinMapping } from '@typehal/board-arduino-uno';
let pcb = new PCB('sensor_board');
// ... add components, create nets, place everything ...
pcb.create();
// Generate the contract
pcb.contract({
mcuSymbolPattern: 'ATmega328',
pinMapping: pinMapping,
outputPath: '../fw/hw-board/contract.json',
});This produces a JSON file that looks like:
{
"version": 1,
"boardPackage": "@typehal/board-arduino-uno",
"mcu": {
"symbol": "MCU_Microchip_ATmega:ATmega328P-PU",
"reference": "U1"
},
"connectedPins": {
"D13": {
"boardName": "D13",
"net": "led_net",
"externalComponents": [
{ "reference": "R1", "symbol": "Device:R", "value": "330" },
{ "reference": "LED1", "symbol": "Device:LED", "value": "" }
]
},
"A4": {
"boardName": "A4",
"net": "i2c_sda",
"externalComponents": [
{ "reference": "U2", "symbol": "Sensor:BME280", "value": "BME280" }
]
}
},
"availablePeripherals": {
"i2c": true,
"spi": false,
"uart": false
}
}How It Works
- Finds the MCU — matches against the component symbol (e.g.
'ATmega328') or by exact reference designator - Walks every net — identifies which MCU GPIO pins are actually connected
- Collects external components — for each connected pin, lists what else is on that net
- Checks peripheral availability — reports whether I2C, SPI, and UART pins are all connected
- Writes the JSON — outputs a clean manifest for your firmware toolchain
Power, ground, clock, and reset pins are filtered out automatically — only GPIO shows up in the contract.
Multiple MCUs
If your board has more than one MCU, target a specific one by reference designator:
pcb.contract({
mcuReference: 'U1',
mcuSymbolPattern: 'ATmega328',
pinMapping: pinMapping,
});Custom Peripherals
By default, the contract checks for Arduino Uno peripheral pins. You can override these for other boards:
pcb.contract({
mcuSymbolPattern: 'ESP32',
pinMapping: esp32PinMapping,
peripheralPins: {
i2c: ['D21', 'D22'],
spi: ['D23', 'D19', 'D18'],
uart: ['D1', 'D3'],
},
boardPackage: '@typehal/board-esp32-devkit',
});What’s Next
This is the TypeCAD side of the bridge. The firmware toolchain (TypeHAL) reads this contract and generates a typed board wrapper so your firmware can only access pins that are actually wired. No more guessing.
Update to the latest @typecad/typecad to start using contracts today.