This repository contains both the firmware for the Framework Laptop 16 input modules, as well as the tool to control them.
Rust firmware project setup based off of: https://github.com/rp-rs/rp2040-project-template
See pages of the individual modules for details about how they work and how they're controlled.
All modules are built with an RP2040 microcontroller Features that all modules share
- Firmware written in bare-metal Rust
- Reset into RP2040 bootloader when firmware crashes/panics
- Sleep Mode to save power
- API over USB ACM Serial Port - Requires no Drivers on Windows and Linux
- Go to sleep
- Reset into bootloader
- Control and read module state (brightness, displayed image, ...)
To build your own application see the: API command documentation
Or use our inputmodule-control
app, which you can download from the latest
GH Actions run or
the release page.
Optionally there are is also a Python script.
For device specific commands, see their individual documentation pages.
To ensure that the input module's port is accessible, install the udev
rule and trigger a reload:
sudo cp release/50-framework-inputmodule.rules /etc/udev/rules.d/
sudo udevadm control --reload && sudo udevadm trigger
> inputmodule-control --list
/dev/ttyACM0
VID 0x32AC
PID 0x0020
SN FRAKDEAM0020110001
Product LED_Matrix
/dev/ttyACM1
VID 0x32AC
PID 0x0021
SN FRAKDEAM0000000000
Product B1_Display
By default a command will be sent to all devices that can be found, to apply it
to a single device, specify the COM port.
In this example the command is targeted at b1-display
, so it will only apply
to this module type.
# Example on Linux
> inputmodule-control --serial-dev /dev/ttyACM0 b1-display --pattern black
# Example on Windows
> inputmodule-control.exe --serial-dev COM5 b1-display --pattern black
By default the app tries to connect with the device and aborts if it can't connect. But you might want to start the app, have it wait until the device is connected and then send the command.
> inputmodule-control b1-display --pattern black
Failed to find serial devivce. Please manually specify with --serial-dev
# No failure, waits until the device is connected, sends command and exits
> inputmodule-control --wait-for-device b1-display --pattern black
# If the device is already connected, it does nothing, just wait 1s.
# This means you can run this command by a system service and restart it when
# it finishes. Then it will only ever do anything if the device reconnects.
> inputmodule-control --wait-for-device b1-display --pattern black
Device already present. No need to wait. Not executing command.
First, put the module into bootloader mode.
This can be done either by pressing the bootsel button while plugging it in or by using one of the following commands:
inputmodule-control led-matrix --bootloader
inputmodule-control b1-display --bootloader
inputmodule-control c1-minimal --bootloader
Then the module will present itself in the same way as a USB thumb drive. Copy the UF2 firmware file onto it and the device will flash and reset automatically. Alternatively when building from source, run one of the following commands:
cargo run -p ledmatrix
cargo run -p b1display
cargo run -p c1minimal
Dependencies: Rust
Prepare Rust toolchain (once):
rustup target install thumbv6m-none-eabi
cargo install flip-link
Build:
cargo make --cwd ledmatrix
cargo make --cwd b1display
cargo make --cwd c1minimal
Generate the UF2 update file:
cargo make --cwd ledmatrix uf2
cargo make --cwd b1display uf2
cargo make --cwd c1minimal uf2
Dependencies: Rust, pkg-config, libudev
Currently have to specify the build target because it's not possible to specify a per package build target. Tracking issue: rust-lang/cargo#9406
# Build it
> cargo make --cwd inputmodule-control
# Build and run it, showing the tool version
> cargo make --cwd inputmodule-control run -- --version
> inputmodule-control led-matrix --version
Device Version: 0.2.0
On Linux:
> lsusb -d 32ac: -v 2> /dev/null | grep -P 'ID 32ac|bcdDevice'
Bus 003 Device 078: ID 32ac:0021 Framework Laptop 16 B1 Display
bcdDevice 0.10
When the Rust code panics, the RP2040 resets itself into bootloader mode. This means a new firmware can be written to overwrite the old one.
Additionally the panic message is written to XIP RAM, which can be read with picotool:
sudo picotool save -r 0x15000000 0x15004000 message.bin
strings message.bin | head