CoPilot driver¶
jumpstarter-driver-copilot controls the GPIO outputs of a
BayLibre CoPilot through Linux libgpiod.
One controller owns the managed GPIO lines; multiple output, power and USB
adapters share that request through Jumpstarter references. The driver supports
explicit relay and USB HAT wiring and reconnects to the selected USB unit without
replaying interrupted writes.
Hardware references:
Installation and host setup¶
pip install --extra-index-url https://pkg.jumpstarter.dev/simple/ jumpstarter-driver-copilot
The exporter requires Linux, a kernel exposing the CoPilot GPIO chip, and the libgpiod Python bindings version 2.2 or newer. The client may run on other platforms.
Install the udev rules from the official
CoPilot setup guide.
They create /dev/copilot/by-id/<serial>/gpiochip and the UART symlink. Reload the
rules and reconnect the unit as described there. A system service also needs
access to the GPIO device: configure its service account/group explicitly, since
an interactive desktop’s uaccess ACL may not grant access to the exporter.
The driver does not install rules, change permissions or bind kernel drivers.
Supported configurations¶
Configuration |
Available functionality |
Example file |
|---|---|---|
CoPilot without a HAT |
Native supply control and GPIO outputs |
|
CoPilot UART |
Board serial console/logs through the existing PySerial driver; no HAT required |
|
Triple-relay HAT |
Three configurable relay channels, GPIO readback and pulses |
|
Triple-USB HAT |
Per-port power control with |
|
The controller currently manages outputs only, not GPIO inputs. Readback is of an output GPIO; it does not measure supply voltage or confirm physical relay contact closure. The upstream candidate is covered by simulated GPIO and real RPC tests; these examples are not a claim of physical validation on every HAT.
For a complete bench with native supply, three named relay contacts and UART,
use examples/exporter-relay-uart.yaml. The wiring table, local startup,
boot-log capture and contact workflows are described in examples/README.md.
The relay-only example requires no PySerial installation. All examples start
with inactive outputs and require adaptation to the actual DUT wiring.
Controller configuration¶
Setting |
Default |
Meaning |
|---|---|---|
|
unset |
Select a unit through its stable udev link. With neither selector set, exactly one unit must be present. |
|
unset |
Explicit GPIO device path; mutually exclusive with |
|
|
Distinct non-negative GPIO offsets owned by this controller. |
|
|
Logical polarity, shared by all managed lines. Native power requires |
|
|
Initial assertion of all managed outputs, on first acquisition only. Reconnect always starts inactive. |
|
|
List of explicit accessory descriptions; no automatic detection. |
Accepted HAT descriptions¶
The field is hats, a list of mappings, each with a type:
|
Parameters |
Effect |
|---|---|---|
|
|
Maps relay channels 1–3 to GPIOs and enables the relay commands. At most one such entry is supported. |
|
Optional |
With |
|
No driver-defined control parameters |
Inventory metadata for another accessory; does not implement an accessory protocol. |
Omit hats or use hats: [] for the base board. Other type values are rejected.
If relay_lines is omitted it falls back to managed_lines, which must then
contain exactly three lines. Specify it explicitly when also managing GPIO0.
custom is a generic software description, not a third BayLibre HAT model.
CoPilot without a HAT¶
export:
copilot:
type: jumpstarter_driver_copilot.driver.CoPilotGPIO
config:
serial: YOUR_COPILOT_SERIAL
managed_lines: [0, 1, 2, 3]
hats: []
power:
type: jumpstarter_driver_copilot.driver.CoPilotPower
children:
controller:
ref: copilot
The copilot export provides GPIO access. The separate power export adds the
standard power client and a switch-state query; it requires managed GPIO0.
Inside a Jumpstarter shell:
j copilot info
j power on
j power state
j power off
Native power requires J1 to select GPIO-controlled supply passthrough.
state() reports the commanded switch, not measured voltage/current.
Another power feed or a bypass jumper may keep the DUT powered. The standard
streaming power read() operation is unsupported.
Raw GPIO commands do not require a HAT. They affect whatever is wired to the selected managed line; GPIO1 is not inherently a power button or relay channel:
j copilot set 1 on
j copilot set 1 off
j copilot pulse 1 --duration 1
Pulse durations are finite values from 1 to 30 seconds. The complete pulse runs on the exporter. Direct pulses and GPIO access are serialized; callers must avoid unrelated concurrent commands during external multi-output sequences.
Board serial console and logs¶
The CoPilot UART is available without a HAT. Install
jumpstarter-driver-pyserial alongside this package and add this sibling export:
export:
serial:
type: jumpstarter_driver_pyserial.driver.PySerial
config:
url: /dev/copilot/by-id/YOUR_COPILOT_SERIAL/tty
baudrate: 115200
Merge this entry into the existing export mapping; the complete configuration
is in examples/exporter-uart.yaml. Both serial placeholders must identify the
same unit. Connect the UART to the DUT and choose its voltage and baud rate using
the CoPilot UART setup guide.
The exporter account needs access to the tty as well as the GPIO device.
j serial console
# start-console is also accepted as an alias
j serial pipe --no-input | tee serial.log
Exit the interactive console with Ctrl+B three times. The log file is written on the client. PySerial owns serial transport and its lifecycle; GPIO reconnect handling does not implement UART recovery. See the PySerial driver documentation.
Triple-relay HAT¶
The BayLibre triple-relay HAT
provides three signal relays. Its jumpers select the GPIO driving each relay.
Add the following under the controller’s config, matching those jumpers:
managed_lines: [0, 1, 2, 3]
hats:
- type: triple-relay
relay_lines: [1, 2, 3]
Only with this HAT description configured, use:
j copilot relay 1 on
j copilot relay 1 off
j copilot read-relay 1
j copilot pulse-relay 1 --duration 1
Here relay 1 selects the first entry in relay_lines, whereas set 1
selects GPIO1. They refer to the same line only with a matching mapping.
read-relay reads the GPIO command, not the physical contacts. The driver cannot
detect whether the declared HAT is actually fitted.
Named contacts in the console¶
A named output is configured explicitly; reset and buttons are not built-in
CoPilot roles. For a relay on GPIO3 wired to a DUT reset contact, add:
export:
buttons:
type: jumpstarter_driver_composite.driver.Composite
children:
reset:
type: jumpstarter_driver_copilot.driver.CoPilotOutput
config:
line: 3
children:
controller:
ref: copilot
Then j buttons reset on asserts the contact and j buttons reset off releases
it. A child named power would similarly expose j buttons power on/off; that
contact is distinct from the GPIO0 native supply export j power on/off.
Composite groups and names the child commands; it does not add pulse timing or
collective release. No Buttons package is required.
The full relay example defines all three contacts under buttons and a
top-level reset reference to buttons.reset. Thus j reset on/off and
j buttons reset on/off operate the same output. These names come from YAML. CoPilotOutput can expose
another managed output without a
relay HAT, provided its wiring and electrical interface suit the target.
All adapters share the controller’s GPIO request.
Triple-USB HAT¶
The BayLibre triple-USB HAT has three USB 2.0 passthrough channels whose power is controlled by GPIOs selected through jumpers. This is not a software-controlled USB hub protocol: the driver switches the channel power line, it does not enumerate USB devices.
Describe the jumper selection in port order and add a CoPilotUSB export:
export:
copilot:
type: jumpstarter_driver_copilot.driver.CoPilotGPIO
config:
serial: YOUR_COPILOT_SERIAL
managed_lines: [0, 1, 2, 3]
hats:
- type: usb-extension
ports: 3
usb_lines: [1, 2, 3] # port 1 -> GPIO1, port 2 -> GPIO2, port 3 -> GPIO3
usb:
type: jumpstarter_driver_copilot.driver.CoPilotUSB
children:
controller:
ref: copilot
Ports are numbered from 1; port N drives usb_lines[N - 1]. Inside a
Jumpstarter shell:
j usb 1 on
j usb 1 off
j usb 1 read
j usb 1 cycle --wait 2
cycle switches the port off, waits (1 to 30 seconds), then switches it on
again, so the attached device re-enumerates; the whole sequence runs on the
exporter and the port ends enabled. read reports the GPIO command, not the
VBUS voltage or the presence of a device. With ports and usb_lines both
set, their counts must match. usb_lines must not overlap a triple-relay
relay_lines, and only one usb-extension entry may define it. Without
usb_lines, the HAT is inventory metadata and CoPilotUSB commands fail.
All managed lines share one active_low setting, and native supply on GPIO0
requires false. Per-port on, off and cycle were checked on a CoPilot Lite V1.2
with the triple-USB HAT; verify the port-to-GPIO mapping against your jumpers.
Multiple accessories¶
The hardware permits stacking extensions. GPIO assignments must match the
physical wiring: two accessories connected to the same GPIO will be controlled
together. Adding multiple hats entries does not allocate independent GPIOs or
validate electrical conflicts. Account for GPIO0’s native supply function as
well. The driver currently allows only one triple-relay description.
USB loss and cleanup¶
The selected serial stays pinned when a unit was initially selected automatically. The next operation detects a changed node or stale request and attempts to reacquire the same unit with every output inactive. Writes are never replayed. If a line was asserted when the connection was lost, subsequent assertions are blocked until the interrupted lines are released or explicitly acknowledged:
j copilot reconnect
This command releases every managed output, including supply GPIO0. Use it only
when no action is running. Releasing an interrupted line reports the interruption
once even if cleanup succeeded. A pulse interrupted by USB loss fails.
info() reports connection status, generation, interrupted lines and the last
error. Status queries may reconnect and therefore initialize outputs inactive.
Session teardown releases outputs and GPIO descriptors. Shared references can close the controller repeatedly; cleanup is idempotent. Reconnection and cleanup cannot guarantee physical relay or supply behavior while USB is disconnected.
API reference¶
- class jumpstarter_driver_copilot.driver.CoPilotGPIO¶
Generic GPIO and HAT control for a BayLibre CoPilot.
The unit is selected by USB serial number. HATs are explicitly described because the relay board uses jumpers and therefore cannot be discovered reliably in software.
- classmethod client() str¶
Return full import path of the corresponding driver client class
- cycle_usb(port: int, wait: float = 2.0) None¶
Switch a USB port off, wait, then on again; the port ends enabled.
- driver_type: ClassVar[str] = 'gpio'¶
Driver category for observability (e.g. power, storage, network, serial, console, video, composite).
- reconnect() dict¶
Explicitly release all outputs, reconnect and acknowledge interruption.
- class jumpstarter_driver_copilot.driver.CoPilotOutput¶
Expose one already-owned CoPilot line as a standard digital output.
children.controllerreferences a CoPilotGPIO instance. The controller remains the sole owner of the GPIO request; this adapter never opens GPIO. An active output means an asserted relay, not that the DUT is powered on.- classmethod client() str¶
Return full import path of the corresponding driver client class
- class jumpstarter_driver_copilot.driver.CoPilotPower¶
Native GPIO0 supply switch, independent of HATs and DUT button wiring.
J1 must select GPIO-controlled passthrough, not always-on bypass. GPIO readback reports the commanded switch, never measured DUT voltage/current.
- classmethod client()¶
Return full import path of the corresponding driver client class
- class jumpstarter_driver_copilot.driver.CoPilotUSB¶
Per-port control of the triple-USB HAT channels.
children.controllerreferences a CoPilotGPIO whoseusb-extensionHAT declaresusb_lines. Port N drivesusb_lines[N - 1]. An active line enables the channel’s power; this adapter never opens GPIO itself.- classmethod client() str¶
Return full import path of the corresponding driver client class