# 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

## 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>,<Loglevel>": 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`:
```toml
[dependencies]
rp-usb-console = "0.1.0"
```
Basic usage example:
```rust
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.
```rust
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](LICENSE) file for details.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.