Peripherals
Typed embedded-hal GPIO controller, line reservations, configuration and heap-free test mock.
The peripherals crate currently provides a GPIO controller contract and a deterministic mock. It is a reusable hardware abstraction, not a runtime-managed peripheral service or a complete SPI/I²C/UART implementation.
API and feature boundaries
Section titled “API and feature boundaries”The gpio feature exposes GpioController, LineId, LineCapabilities, InputConfig, OutputConfig, Pull, DriveMode, and PinState. The mock feature exposes GpioMock<N>, MockInput, MockOutput, and MockError.
GpioController declares a controller-specific Error implementing embedded_hal::digital::Error, and lifetime-bound associated types Input<'a> and Output<'a>. Input handles implement embedded_hal::digital::InputPin; output handles implement both OutputPin and StatefulOutputPin. The three controller methods are:
capabilities(LineId) -> Option<LineCapabilities>: query an existing line’s supported operations.request_input(LineId, InputConfig): reserve an input line and return a borrowed handle.request_output(LineId, OutputConfig): reserve an output line and return a borrowed handle.
The returned handles borrow the controller, so callers cannot treat them as detached, indefinitely owned device objects.
Configurations and validation
Section titled “Configurations and validation”LineId::new(offset) wraps a u16 line offset. LineCapabilities reports whether the line supports input, output, pull-up, pull-down and open-drain. InputConfig chooses Pull::Disabled, Up, or Down (disabled by default). OutputConfig adds DriveMode::PushPull or OpenDrain and an initial physical PinState::Low or High.
Controllers may reject an invalid line, a line already reserved by another handle, or a requested capability that hardware does not support. Reserving a line is exclusive until the handle is dropped. The output initial state is applied when the output is requested.
Heap-free mock for tests
Section titled “Heap-free mock for tests”GpioMock<N> receives an array of N line capabilities and stores state in fixed-capacity memory. Its line handles implement the same embedded-hal digital traits as real adapters. Tests can call set_input_level() to inject an input level, read it through InputPin, and verify output levels through StatefulOutputPin.
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};use peripherals::gpio::{ DriveMode, GpioController, GpioMock, InputConfig, LineCapabilities, LineId, OutputConfig, PinState, Pull,};
let capabilities = LineCapabilities { input: true, output: true, pull_up: true, pull_down: true, open_drain: true,};let gpio = GpioMock::new([capabilities; 2]);let line = LineId::new(0);gpio.set_input_level(line, PinState::High)?;let mut input = gpio.request_input(line, InputConfig::default())?;assert!(input.is_high()?);drop(input);
let mut output = gpio.request_output( line, OutputConfig::new(Pull::Disabled, DriveMode::PushPull, PinState::Low),)?;output.set_high()?;assert!(output.is_set_high()?);This example requires the gpio and mock features. The mock tests cover invalid IDs, exclusive reservations, release on drop, initial output level, injected input state, and unsupported pull/drive modes.
Migration and limitations
Section titled “Migration and limitations”The earlier PinData byte-buffer interface has been removed. Consumers should migrate to typed controller requests and borrowed line handles; serialized pin-state operations are no longer the GPIO API.
Board-specific adapters remain separate work. A passing mock test does not establish that a particular MCU pin, interrupt, pull resistor or electrical mode is supported. The Espressif integration remains constrained by the target/toolchain work tracked in Core issue #328.
Integration boundaries and physical pin discovery
Section titled “Integration boundaries and physical pin discovery”This is a digital GPIO controller trait, not a mounted device or a protocol bus implementation. A GpioController adapter decides which numeric LineId values exist and what LineCapabilities each reports. A client should query capabilities(id) before requesting a borrowed Input<'a> or Output<'a> handle. The handle retains exclusive line ownership until dropped.
use peripherals::gpio::{GpioController, LineId, InputConfig};let id = LineId::new(0);if let Some(caps) = controller.capabilities(id) { if caps.input { let _input = controller.request_input(id, InputConfig::default())?; // The line is reserved until _input is dropped. }}The example assumes a concrete controller implementing GpioController and the gpio Cargo feature. The controller’s Error is backend-specific; a caller may need to map it into its own error domain.
Features, memory and unimplemented capabilities
Section titled “Features, memory and unimplemented capabilities”peripherals has no default features. Enable gpio for the embedded-hal digital API; mock implies gpio and provides a deterministic fixed-size GpioMock<N>. The mock supports capability checks, injected physical input levels and simulated exclusive reservation, without heap allocation in its line-state storage.
Analog/ADC modes, PWM, interrupt subscriptions, hardware pin multiplexing, and UART/I²C/SPI discovery are not part of this GPIO trait. Likewise a future VFS-backed /devices/gpio provider must adapt these controller contracts; it is not automatically exposed by constructing GpioMock. Do not advertise host/browser pin mocks as electrical or timing-equivalent hardware.
Core implementation references
Section titled “Core implementation references”libraries/peripherals/src/gpio.rs- typed digital controller and configurations.libraries/peripherals/src/gpio/mock.rs- exclusive fixed-capacity test adapter.libraries/peripherals/Cargo.toml- feature gates.