SDK docs menu

class

Board

A simulated board, and your session on it.

from hardlabs_sdk import Board

Get one from Client.start (booted) or Client.board (not yet booted). A board is a context manager: leaving the with block calls stop.

Parts are attributes by reference designator: board.U3 is board.part("U3").

Waits count simulated time, so a slow network or a busy server doesn't change a result.

Usage

from hardlabs_sdk import Client

with Client().start("hardlabs/demo-board") as board:
    board.wait_for("temp")
    board.do("temperature-sensor-mcp9808.overheat-75-c")
    board.wait_for("ALARM", within=5)

Attributes

idstr
The simulation's id.
namestr
The simulation's name: owner/project@version.

Board.startmethod

Boots the board afresh, restarting it if it is running.

board.start(firmware=None, *, keep_memory=False) -> Board

Called without firmware, it boots the firmware it booted last, or the board's own if none has been given.

The firmware is one ELF, or the images a flash holds. A raw .bin needs the address it is flashed at ("app.bin@0x08010000"). ELF and Intel HEX files carry their own addresses. The processor boots from the image at the lowest address, as it would from flash. An image can also name a memory chip ("config.bin@U7", or "config.bin@U7:0x100" for an offset in it).

A fresh start gives every memory chip the images you named and leaves the rest blank, so the same images start every run from the same state. keep_memory carries the running board's memory over instead, as restart does.

Serial ports opened with serial stay open, at the same address, across a restart.

Usage

board.start("build/app.elf")
board.start(["bootloader.bin@0x08000000", "app.bin@0x08010000", "config.bin@U7"])

Arguments

firmwarestr | Path | Image | list | Nonedefault None
A path, an "image@address" or "image@U7" string, an Image, or a list of them.
keep_memorybooldefault False
Keep what the memory chips hold instead of starting them from the images.

Returns

Board — The board itself.

Raises

  • FileNotFoundError — A firmware file doesn't exist.
  • ValueError — A raw image has no address, or its address isn't a number.
  • HardLabsError — The server couldn't boot the board.

Board.restartmethod

Power-cycles the board: the firmware boots again, and the memory chips keep what they hold.

board.restart(firmware=None) -> Board

Called without firmware, it boots the same firmware again. Pass new firmware to boot that instead; images for memory chips are left out, since the chips keep what they hold. Serial ports you have open keep their addresses.

Usage

board.restart()
board.restart("build/app-v2.elf")

Arguments

firmwarestr | Path | Image | list | Nonedefault None
As start takes it.

Returns

Board — The board itself.

Raises

Board.stopmethod

Ends the session and closes its serial ports.

board.stop(*, save_memory=None)

Leaving a with block calls it. Calling it again does nothing. What the memory chips hold goes with the session unless you save it first.

Usage

board.stop(save_memory="nvm/")

Arguments

save_memoryUnion[str, Path, None]default None
A folder to save every memory chip into first, as save_memory does.

Board.memory_loadedproperty

What the last start put into the memory chips.

board.memory_loaded -> list[dict]

A list of dicts, one per image: memory, offset, bytes and name.

Board.memoriesmethod

Lists the board's memory chips: what memory and file@U7 images name.

board.memories() -> list[Memory]

Returns

list[Memory] — Every memory chip, with its size.

Board.memorymethod

Returns a memory chip by reference designator or slot, in any case.

board.memory(name) -> Memory

Usage

board.memory("U7").read(0, 16)

Arguments

namestr
E.g. "U7".

Returns

Memory — The chip.

Raises

  • KeyError — The board has no such memory chip. The message lists the ones it has.

Board.save_memorymethod

Saves every memory chip's contents into a folder.

board.save_memory(directory) -> list[Path]

Each chip goes into <name>.bin, and a memory.json says which is which: a record of the run, and what memory_images starts the next board from.

Usage

board.save_memory("nvm/")
board.start(["app.elf", *memory_images("nvm/")])

Arguments

directoryUnion[str, Path]
The folder. It is made if needed.

Returns

list[Path] — The files written, one per chip.

Board.statusmethod

Returns the session's state as the API reports it.

board.status() -> dict

Returns

dict — The raw state: whether it is running, its serial ports, its firmware.

Board.firmwareproperty

What the session runs, or None before start.

board.firmware -> dict | None

A dict with the firmware's name and sha256 and, for images, images: where each one was loaded.

Board.clockmethod

Returns the simulated time since the board booted.

board.clock() -> float

Returns

float — Simulated seconds.

Board.waitmethod

Lets simulated time pass.

board.wait(seconds) -> float

Arguments

secondsfloat
Simulated seconds to wait.

Returns

float — The clock afterwards, as clock returns it.

Raises

Board.sendmethod

Types one line into the firmware's console, as if Enter were pressed after it.

board.send(line, *, console=None)

The line can be up to 512 characters. Output printed before the call can't match a later wait_for, so a wait after send finds the reply.

Usage

board.send("status")
board.wait_for("mode: SAFE")

Arguments

linestr
The line to type.
consolestr | Nonedefault None
The console's name, when the board has more than one.

Board.linesmethod

Returns the console's lines so far.

board.lines(*, console=None) -> list[Line]

Arguments

consolestr | Nonedefault None
The console's name, when the board has more than one.

Returns

list[Line] — Up to the last 2000 lines.

Board.wait_formethod

Waits for a console line that contains text, and returns it.

board.wait_for(text, *, within=30.0, console=None) -> Line

The match uses up that line and every line before it, so a second wait_for with the same text finds a later line.

Usage

board.do("reset.press")
board.wait_for("app ready", within=5)

Arguments

textstr
The text to find. It matches anywhere in a line.
withinfloatdefault 30.0
Simulated seconds to wait.
consolestr | Nonedefault None
The console's name, when the board has more than one.

Returns

Line — The line that matched.

Raises

Board.expect_consolemethod

Waits for several texts to appear in order, each on a later line.

board.expect_console(*texts, within=30.0, console=None) -> list[Line]

The time allowed covers all of them together.

Usage

board.send("status")
board.expect_console("mode: SAFE", "faults: 0")

Arguments

*textsstr
The texts to find, in order.
withinfloatdefault 30.0
Simulated seconds to wait for them all.
consolestr | Nonedefault None
The console's name, when the board has more than one.

Returns

list[Line] — The line each text matched.

Raises

Board.controlsmethod

Lists everything you can do to the board.

board.controls() -> list[Control]

The names are the same in the SDK, the CLI and scenario files.

Usage

for c in board.controls():
    print(c.usage)

Returns

list[Control] — The board's controls.

Board.domethod

Works one of the board's controls.

board.do(control, value=None, **values) -> str

Controls are named <panel>.<control>. The part after the dot is enough when only one panel has it. A part's controls also answer to its reference designator: U3.set-ambient-temperature.

Usage

board.do("reset.press")
board.do("usb.plugged-in", True)
board.do("battery.level", 30)
board.do("bench-supply.set", volts=5.0)
board.do("U3.set-ambient-temperature", 30)

Arguments

controlstr
The control's name. controls lists them.
valueAnydefault None
True/False for a toggle, a number for a number or slider. Leave it out for a button or preset.
**valuesfloat
The named values for a form, vector or generator.

Returns

str — The control's output, if it has any.

Raises

  • HardLabsError — The board has no such control, or the value is out of range.

Board.readingsmethod

Returns the values the board's panels show now.

board.readings(*names) -> dict[str, Reading]

Arguments

*namesstr
The readings to return. Every reading when none are given.

Returns

dict[str, Reading] — The readings, by name.

Board.readingmethod

Returns one reading, with its unit and label. read returns only its value.

board.reading(name) -> Reading

Arguments

namestr
The reading's name, <panel>.<reading>.

Returns

Reading — The reading.

Raises

Board.readmethod

Returns the value of one reading.

board.read(name) -> Any

Usage

assert board.read("power-monitor.bus-current") < 0.2

Arguments

namestr
The reading's name, <panel>.<reading> or <ref>.<reading>.

Returns

Any — A number, True/False, a name, or None when the board doesn't report it.

Raises

Board.partsmethod

Lists the board's parts, optionally filtered.

board.parts(*, bus=None, address=None, connected_to=None, status=None) -> list[Part]

Passives are left out. Parts the simulation doesn't model are listed too, so the list shows what is left out. Filters combine.

Usage

board.parts(bus="i2c0")
board.parts(connected_to="U1", status="not_simulated")

Arguments

busstr | Nonedefault None
Only parts on this bus, as the board names it: "i2c0".
addressint | str | Nonedefault None
Only parts at this I2C address: 0x6B or "0x6B".
connected_tostr | Part | Nonedefault None
Only parts wired to this part (a reference designator or a Part). For the processor, this includes the parts on its buses.
statusstr | Nonedefault None
Only parts simulated this far: simulated, partial, folded, not_simulated or not_fitted.

Returns

list[Part] — The parts that match.

Raises

  • ValueError — address isn't a number.

Board.findmethod

Returns the one part that matches the filters.

board.find(**filters) -> Part

Usage

imu = board.find(bus="i2c0", address=0x6B)

Arguments

**filtersAny
The same filters as parts.

Returns

Part — The part.

Raises

  • LookupError — No part matches, or more than one does.

Board.mcuproperty

The board's processor, as a Part.

board.mcu -> Part

board.mcu.connected() lists every part it reaches. Raises LookupError when the parts list doesn't name the processor.

Board.partmethod

Returns one part by its reference designator. board.U3 is the same.

board.part(ref) -> Part

Arguments

refstr
The reference designator, in any case: "U3" or "u3".

Returns

Part — The part.

Raises

  • KeyError — The board has no such part. The message lists the parts it has.

Board.problemsmethod

Lists what has gone wrong in the run so far.

board.problems() -> list[dict]

That includes the firmware's crashes, failed asserts and unexpected restarts, and behaviour the simulation may not cover. Check these before deciding a failure is the firmware's.

Returns

list[dict] — Each with source, title and, when there is one, detail.

Board.serial_portsmethod

Lists the serial ports serial can open.

board.serial_ports() -> list[str]

Returns

list[str] — console, the board's own ports (rs485), and usb:<controller> ports.

Board.serialmethod

Opens one of the board's serial ports on this computer.

board.serial(port='console', *, pty=False) -> SerialBridge

Your own test framework opens the port by its address, like a real one. Bytes pass through unchanged in both directions. The address stays the same when the board restarts.

Usage

import serial

bus = board.serial("rs485")
port = serial.serial_for_url(bus.url, baudrate=115200, timeout=5)
port.write(frame)

Arguments

portstrdefault 'console'
The port's name. serial_ports lists them.
ptybooldefault False
Open a pseudo-terminal (a device file) instead of a socket:// address. macOS and Linux only.

Returns

SerialBridge — The open port. Its url is what pyserial opens.

Raises

  • HardLabsError — The board has no such port, or pty was asked for on Windows.

Board.run_unit_testsmethod

Collects the results of a test binary running on the board.

board.run_unit_tests(*, within=300.0, done=None, console=None) -> UnitTestReport

Reads Unity or GoogleTest output from the console until the framework's summary line appears, or done does. A run that doesn't finish in time returns a report with finished set to False rather than raising.

Usage

board = Client().start("company_name/board_name", "build/tests.elf")
report = board.run_unit_tests(within=120)
assert report.ok, report.summary()

Arguments

withinfloatdefault 300.0
Simulated seconds to wait for the run to finish.
donestr | Nonedefault None
A line that marks the end of the run, for output with no summary line.
consolestr | Nonedefault None
The console's name, when the board has more than one.

Returns

UnitTestReport — The results.