Expand description
defmt global logger over USB serial for use with Embassy.
To use this crate spawn the run task, and use defmt as normal. Messages will be sent via
USB-CDC to the host, where you should use something such as the defmt-print CLI tool to
print them to your terminal.
§Quickstart
Here’s an example of using it with embassy_rp, with the general HAL setup elided.
#[embassy_executor::task]
async fn defmtusb_wrapper(usb: Peri<'static, embassy_rp::peripherals::USB>) {
let driver = embassy_rp::usb::Driver::new(usb, Irqs);
let usb_config = {
let mut c = embassy_usb::Config::new(0x1234, 0x5678);
c.serial_number = Some("defmt");
c.max_packet_size_0 = 64;
c.composite_with_iads = true;
c.device_class = 0xEF;
c.device_sub_class = 0x02;
c.device_protocol = 0x01;
c
};
defmt_embassy_usbserial::run(driver, usb_config).await;
}
// Inside your main function.
spawner.must_spawn(defmtusb_wrapper(peripherals.USB));
loop {
defmt::info!("Hello! {=u64:ts}", Instant::now().as_secs());
delay.delay_ms(1000).await;
}§Wrapper task
A wrapper task is required because this library can’t provide a task for you to spawn, since it
has to be generic over the USB driver struct. While the quickstart example provides a
straightforward example of constructing both the driver and the configuration in this task,
ultimately the only requirement is that it awaits [defmt_embassy_usbserial::run].
Of course, defmt_embassy_usbserial::run is just an async function whose returned future can
be joined, etc.
§Configuration
For USB-CDC to be set up properly, you must set the correct values in the configuration
struct. If composite_with_iads is true (the default), you must use the following values
as embassy-usb will fail an assertion if you do not:
| Field | Value |
|---|---|
device_class | 0xEF |
device_sub_class | 0x02 |
device_protocol | 0x01 |
If composite_with_iads is false, you do not have to use these exact values: the standard
CDC device class code (device_class) is 0x02. You should choose the values appropriate to
your application. If your only concern is transporting defmt logs over USB serial, default to
the values in the table above.
§Examples
Please see the device-examples/ directory in the repository for device-specific “hello world”
examples. These examples have all been tested on real hardware and are known to work.
§Known limitations
§Old or corrupt defmt messages are received after reconnecting
If you stop reading the logs from the USB serial port, for example by closing defmt-print,
when you reconnect you will likely receive part of one old defmt message (and potentially
several complete out-out-date messages), and the first up-to-date defmt message will be
corrupt.
This is ultimately because the internal buffers are not aware of defmt frame boundaries. The first case will occur because the writing task will block part-way through writing an internal buffer to the USB serial port, and continues writing that now-stale buffer when you start reading again. (This may be avoided in a future release by way of a timeout.)
The second is because that buffer may end part-way through a defmt message, and the next buffer
that is written will likely start part-way through a defmt message. defmt-print may
explicitly report these frames as malformed, or may silently misinterpret values to be included
in a format message.
Note as well that ceasing to read from the serial port does not disable defmt logging; it seems that only disconnecting from USB will trigger the event that toggles the logger.
§High message latency
It may take some time for you to start receiving messages, and they may come through in bursts. This is due to the implementation waiting until one of its internal buffers is full before writing to the USB serial port. This effect will be more pronounced if you choose a larger buffer size feature, and if you have messages with few (or no) formatting parameters, as this greatly reduces the size of the data that needs to be transferred.
It may be possible to make the implementation aware of defmt messages, so that they come through in a more stream-like manner. Suggestions and contributions on this would be greatly appreciated.
§Buffers flushed only every 100ms
Conversely, if you have a high volume of messages, there is at present a 100ms delay after writing an internal buffer to USB. It is planned to make this configurable.
§Acknowledgements
Thank you to spcan, the original author of defmtusb. Thanks as well to the friendly and helpful members of the various embedded Rust Matrix rooms.
§License
Dual-licensed under the Mozilla Public License 2.0 and the MIT license, at your option.