rp-usb-console

Crates.iorp-usb-console
lib.rsrp-usb-console
version0.2.4
created_at2025-09-30 19:14:22.959332+00
updated_at2026-01-10 20:23:10.167245+00
descriptionZero-heap USB CDC logging and command channel for RP2040 with Embassy async framework
homepagehttps://github.com/petersallai/rp-usb-console
repositoryhttps://github.com/petersallai/rp-usb-console
max_upload_size
id1861561
size818,137
Peter Sallai (petersallai)

documentation

https://docs.rs/rp-usb-console

README

rp-usb-console

When developing for the Raspberry Pi Pico, you typically have two choices: debugging with a hardware probe or deploying programs directly via USB. The latter is quick and convenient, but it doesn’t allow runtime communication with your application. Embassy’s built-in usb-logger lets you stream logs over USB, yet it lacks a way to send commands back (such as rebooting into BOOTSEL mode). rp-usb-console fills this gap by providing a lightweight, zero-heap solution for both logging and command exchange over USB, designed specifically for the RP2040 and the Embassy async framework. Initially, I created this library for my own use.

Quick start

Quickstart

Command list

  • "/LT": set global loglevel to TRACE
  • "/LD": set global loglevel to DEBUG
  • "/LI": set global loglevel to INFO
  • "/LW": set global loglevel to WARN
  • "/LE": set global loglevel to ERROR
  • "/LO": set global loglevel to OFF
  • "/LM <module_filter>,": Set specific loglevel for modules containing module filter
  • "BS": Reboot RP2040 to Boot Select (to deploy applications)
  • "RS": Reset RP2040

All command must be ended with /r or /n or both.

Features

  • Zero-heap logging: Fixed-size 255-byte log message buffers with no dynamic allocation
  • USB CDC ACM interface: Standard USB serial communication
  • Non-blocking operation: Log messages are dropped if the channel is full to maintain real-time behavior
  • Packet fragmentation: Large messages are split into 64-byte USB packets for reliable transmission
  • Bidirectional communication: Both logging output and line-buffered command input over USB
  • Configurable command sink: Forward commands to your own channel or disable forwarding entirely
  • Embassy integration: Designed specifically for Embassy executor and RP2040

Design Goals

  • Predictable real-time behavior: No blocking on full channels
  • Memory efficiency: Fixed buffers, no heap allocation
  • Reliability: Fragmented transmission reduces host latency issues
  • Safety: Single-core RP2040 assumptions with proper synchronization

Usage

Add this to your Cargo.toml:

[dependencies]
rp-usb-console = "0.1.0"

Basic usage example:

use embassy_executor::Spawner;
use embassy_sync::channel::Channel;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use log::info;

use rp_usb_console::USB_READ_BUFFER_SIZE;

// Create a channel for receiving commands from USB
static COMMAND_CHANNEL: Channel<CriticalSectionRawMutex, [u8; USB_READ_BUFFER_SIZE], 4> = Channel::new();

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let p = embassy_rp::init(Default::default());
    
    // Initialize USB logging with Info level
    rp_usb_console::start(
        spawner,
        log::LevelFilter::Info,
        p.USB,
        Some(COMMAND_CHANNEL.sender()),
    );
    
    // Now you can use standard log macros
    info!("Hello over USB!");
    
    // Handle incoming commands in a separate task
    spawner.spawn(command_handler()).unwrap();
}

#[embassy_executor::task]
async fn command_handler() {
    let receiver = COMMAND_CHANNEL.receiver();
    loop {
    let command = receiver.receive().await;
    // Process received command data (terminated by CR/LF; trailing zeros may be present)
    info!("Received command: {:?}", command);
    }
}

API Reference

start()

Initialize the USB CDC interface and spawn necessary tasks.

pub fn start(
    spawner: Spawner, 
    level: LevelFilter, 
    usb_peripheral: Peri<'static, USB>, 
    command_sender: Option<Sender<'static, CriticalSectionRawMutex, [u8; USB_READ_BUFFER_SIZE], 4>>
)

Parameters:

  • spawner: Embassy task spawner
  • level: Log level filter (e.g., LevelFilter::Info)
  • usb_peripheral: RP2040 USB peripheral
  • command_sender: Optional channel sender for receiving line-buffered USB commands (None disables forwarding)

LogMessage

Fixed-size message buffer with USB packet fragmentation support.

  • Size: 255 bytes maximum
  • Truncation: Messages exceeding capacity are truncated with ellipsis
  • Fragmentation: Automatically split into 64-byte USB packets

Implementation Details

Memory Management

  • No heap allocation: All buffers are statically allocated
  • Fixed message size: 255 bytes per log message
  • Channel capacity: 4 pending log messages maximum

USB Protocol

  • Packet size: 64 bytes per USB transfer
  • Fragmentation: Large messages split across multiple packets
  • Connection handling: Automatic reconnection support

Threading Model

  • Single-core: Designed for RP2040's single-core architecture
  • Embassy tasks: Three main tasks handle USB device, TX, and RX operations
  • Non-blocking: Log operations never block the caller

Limitations

  • Messages longer than 255 bytes are truncated
  • Full log channels result in dropped messages
  • Currently uses CRLF line endings
  • No built-in timestamp support

Future Improvements

  • Backpressure metrics (dropped message counter)
  • Configurable line ending options (CRLF/LF)
  • Optional timestamp formatting
  • Extended formatting features

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Commit count: 15

cargo fmt