Crate defmt_embassy_usbserial

Crate defmt_embassy_usbserial 

Source
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:

FieldValue
device_class0xEF
device_sub_class0x02
device_protocol0x01

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.

Functions§

logger
USB logger task that writes full buffers out over USB.
run
Run the USB driver and defmt logger tasks.