├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── README.md ├── example-artemis ├── .cargo │ └── config.toml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── memory.x ├── src │ └── main.rs └── svl-defmt-wrapper.sh ├── example-defmt-serial.png ├── example-pi-pico ├── .cargo │ └── config.toml ├── Cargo.toml ├── README.md ├── memory.x ├── pi2pico-serial.png ├── pico-defmt-serial-output.png ├── pico-elf2uf2-defmt-wrapper.sh ├── pico-pi4-openocd-defmt-wrapper.sh └── src │ └── main.rs ├── example-std ├── .cargo │ └── config.toml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── defmt-print-runner.sh └── src │ └── main.rs └── src └── lib.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | DEFMT_LOG: debug 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Install latest nightly 21 | uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: nightly 24 | override: true 25 | 26 | - name: Install deps 27 | run: | 28 | sudo apt-get -y update 29 | sudo apt-get -y install build-essential libssl-dev gcc-arm-none-eabi binutils-arm-none-eabi libclang-dev clang curl git 30 | cargo install defmt-print 31 | 32 | - name: Add rust target for thumbv6m, thumbv7em and musl 33 | run: | 34 | rustup target add thumbv7em-none-eabihf 35 | rustup target add thumbv6m-none-eabi 36 | rustup target add x86_64-unknown-linux-musl 37 | 38 | - name: Build 39 | run: cargo build --verbose 40 | 41 | - name: Doc tests 42 | run: cargo test --doc --target thumbv7em-none-eabihf 43 | 44 | # Disabled until ambiq-hal supports eh-1 45 | # - name: Build artemis example 46 | # working-directory: example-artemis 47 | # run: cargo build --verbose 48 | 49 | # Disable until example can be updated for eh-1 50 | # - name: Build pi-pico example 51 | # working-directory: example-pi-pico 52 | # run: cargo build --verbose 53 | 54 | - name: Build std example 55 | working-directory: example-std 56 | run: cargo build --verbose 57 | 58 | - name: Run std example 59 | working-directory: example-std 60 | run: cargo run --verbose 61 | 62 | 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cargo resources and build artefacts 2 | **/target/ 3 | **/Cargo.lock 4 | 5 | # IDE's 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "defmt-serial" 3 | version = "0.10.0" 4 | edition = "2021" 5 | authors = [ "Gaute Hope " ] 6 | description = "Log defmt-messages over serial" 7 | license = "MIT" 8 | repository = "https://github.com/gauteh/defmt-serial" 9 | 10 | 11 | [dependencies] 12 | critical-section = "1.1" 13 | defmt = "0.3" 14 | embedded-io = "0.6.1" 15 | 16 | [dev-dependencies] 17 | cortex-m-rt = "0.7" 18 | panic-probe = { version = "0.3", features = [ "print-defmt" ] } 19 | cortex-m = "0.7" 20 | static_cell = "2" 21 | 22 | [dev-dependencies.ambiq-hal] 23 | version = "0.3" 24 | features = [ "ambiq-sdk", "sparkfun-redboard-nano", "rt"] 25 | git = "https://github.com/gauteh/ambiq-rs" 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/defmt-serial.svg)](https://crates.io/crates/defmt-serial) 2 | [![Documentation](https://docs.rs/defmt-serial/badge.svg)](https://docs.rs/defmt-serial/) 3 | [![tests](https://github.com/gauteh/defmt-serial/actions/workflows/rust.yml/badge.svg)](https://github.com/gauteh/defmt-serial/actions/workflows/rust.yml) 4 | 5 | # defmt-serial 6 | 7 | A [defmt](https://github.com/knurling-rs/defmt) target for logging over a serial 8 | port. Have a look at examples to see how to use library 9 | [example-artemis](example-artemis) or [example-pi-pico](example-pi-pico). You 10 | can also try it out in a hosted environment: [example-std](example-std). To 11 | parse the logs have a look at [parsing logs](#Parsing-logs). 12 | 13 | ```rust 14 | static SERIAL: StaticCell = StaticCell::new(); 15 | 16 | #[entry] 17 | fn main() -> ! { 18 | let mut dp = hal::pac::Peripherals::take().unwrap(); 19 | let pins = hal::gpio::Pins::new(dp.GPIO); 20 | 21 | // set up serial 22 | let serial = hal::uart::Uart0::new(dp.UART0, pins.tx0, pins.rx0); 23 | defmt_serial::defmt_serial(SERIAL.init(serial)); 24 | 25 | defmt::info!("Hello from defmt!"); 26 | 27 | loop { 28 | asm::wfi(); 29 | } 30 | } 31 | ``` 32 | 33 | Remember to set the `DEFMT_LOG` variable when testing, e.g.: 34 | 35 | ``` 36 | $ cd example-std/ 37 | $ DEFMT_LOG=debug cargo run 38 | ``` 39 | 40 | 41 | 42 | ## Parsing logs 43 | 44 | The easiest way to parse the logs is to use `socat` and `defmt-print` together. 45 | For example: 46 | ``` 47 | $ socat ${PORT},rawer,b${BAUDRATE} STDOUT | defmt-print -e ${ELF} 48 | ``` 49 | Just replace `${PORT}`, `${BAUDRATE}` and `${ELF}` with correct values. 50 | 51 | To install the tools on Ubuntu 22.04 run these commands: 52 | ``` 53 | $ apt install socat 54 | $ cargo install defmt-print 55 | ``` 56 | 57 | Note that on Mac OS it seems that `socat` is broken for this purpose. Use `stty` 58 | and `cat` in stead: 59 | 60 | ``` 61 | $ (stty speed 115200 >/dev/null && cat) = StaticCell::new(); 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let mut dp = hal::pac::Peripherals::take().unwrap(); 16 | let core = hal::pac::CorePeripherals::take().unwrap(); 17 | 18 | let mut delay = hal::delay::Delay::new(core.SYST, &mut dp.CLKGEN); 19 | 20 | let pins = hal::gpio::Pins::new(dp.GPIO); 21 | let mut led = pins.d19.into_push_pull_output(); 22 | 23 | // set up serial 24 | let serial = hal::uart::Uart0::new(dp.UART0, pins.tx0, pins.rx0); 25 | defmt_serial::defmt_serial(SERIAL.init(serial)); 26 | 27 | defmt::warn!("Hello!"); 28 | 29 | loop { 30 | delay.delay_ms(2000u32); 31 | defmt::info!("Loop!"); 32 | for i in 0..10 { 33 | led.toggle().unwrap(); 34 | delay.delay_ms(100u32); 35 | defmt::debug!("Inner loop: {}", i); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example-artemis/svl-defmt-wrapper.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -ep 4 | 5 | TARGET=${1} 6 | BIN="${TARGET}.bin" 7 | 8 | BOOTLOADER="/home/gauteh/dev/embedded/ambiq-rs/tools/svl/svl.py" 9 | 10 | echo "size:" 11 | arm-none-eabi-size "${TARGET}" 12 | 13 | echo "objcopy.." 14 | arm-none-eabi-objcopy -S -O binary "${TARGET}" "${BIN}" 15 | 16 | echo "flashing /dev/ttyUSB0.." 17 | python3 "${BOOTLOADER}" -f "${BIN}" /dev/ttyUSB0 -v 18 | 19 | 20 | echo "Attaching defmt-print.." 21 | 22 | socat /dev/ttyUSB0,b115200,raw,echo=0 STDOUT | defmt-print -e $1 23 | -------------------------------------------------------------------------------- /example-defmt-serial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauteh/defmt-serial/febbcf366c5cb39f9906e059408faca23327d25b/example-defmt-serial.png -------------------------------------------------------------------------------- /example-pi-pico/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Set the default target to match the Cortex-M0+ in the RP2040 3 | target = "thumbv6m-none-eabi" 4 | 5 | # Target specific options 6 | [target.thumbv6m-none-eabi] 7 | # Pass some extra options to rustc, some of which get passed on to the linker. 8 | # 9 | # * linker argument --nmagic turns off page alignment of sections (which saves 10 | # flash space) 11 | # * linker argument -Tlink.x tells the linker to use link.x as the linker 12 | # script. This is usually provided by the cortex-m-rt crate, and by default 13 | # the version in that crate will include a file called `memory.x` which 14 | # describes the particular memory layout for your specific chip. 15 | # * inline-threshold=5 makes the compiler more aggressive and inlining functions 16 | # * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't 17 | # have SIMD) 18 | rustflags = [ 19 | "-C", "link-arg=--nmagic", 20 | "-C", "link-arg=-Tlink.x", 21 | "-C", "link-arg=-Tdefmt.x", 22 | "-C", "inline-threshold=5", 23 | "-C", "no-vectorize-loops", 24 | ] 25 | # This runner will use openOCD to flash the pico directly from the raspbery pi and then 26 | # open serial0 for logging 27 | runner = "bash pico-pi4-openocd-defmt-wrapper.sh" 28 | 29 | # This runner will use uf2 to flash the pico and then open serial0 for logging. 30 | # Don't forget to replug and hit the bootsel button! 31 | #runner = "bash pico-elf2uf2-defmt-wrapper.sh" 32 | 33 | [env] 34 | DEFMT_LOG = "debug" -------------------------------------------------------------------------------- /example-pi-pico/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pico-defmt-serial" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cortex-m = { version = "0.7.6"} 8 | cortex-m-rt = "0.7" 9 | embedded-hal = { version = "1" } 10 | defmt = "0.3" 11 | panic-probe = { version = "0.3", features = ["print-defmt"] } 12 | rp-pico = "0.9" 13 | defmt-serial = { version = "*", path = "../" } 14 | panic-halt = "0.2.0" 15 | static_cell = "2.0.0" 16 | portable-atomic = { version = "1.6.0", features = ["unsafe-assume-single-core"] } 17 | -------------------------------------------------------------------------------- /example-pi-pico/README.md: -------------------------------------------------------------------------------- 1 | # Pi Pico defmt example 2 | 3 | ## Introduction 4 | This example shows you how to setup a Pi Pico to output defmt messages over serial. This is useful if you are not using a probe and defmt-rtt. 5 | 6 | ## Envrionment Setup 7 | This example relies on using `defmt-print` to decode the output sent over serial. This is a tool rather than a library so add it with 8 | ``` 9 | cargo install defmt-print 10 | ``` 11 | 12 | Next, check that `pico-elf2uf2-defmt-wrapper.sh` and `pico-elf2uf2-defmt-wrapper.sh` are executable with `chmod +x`. 13 | 14 | Finally, ensure that you have permissions to open the serial port without sudo access! 15 | 16 | Often, you need to be part of the dialout group 17 | ``` 18 | sudo adduser dialout 19 | ``` 20 | 21 | ## Raspberry Pi4 to Pico Setup 22 | The following diagram shows how connect a raspberry pi4 to the pico for UART serial connections: 23 | 24 | Image showing connections from pi to pico 25 | 26 | Source: [Getting Started With Pico](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf) 27 | 28 | ## Selecting a runner 29 | If you are not using elf binaries and openocd to flash the pico, open `.cargo/config.toml` and change the selected runner to `pico-elf2uf2-defmt-wrapper.sh`. Otherwise, you can leave the default as is. 30 | 31 | ## Running and Output 32 | To flash and run this example type: 33 | ``` 34 | cargo run 35 | ``` 36 | You should see an output like this: 37 | 38 | Showing the expected output when running the program 39 | 40 | ## Credits 41 | A lot of the code in this example was derived from the following, execellent tutorials: 42 | 1. [https://reltech.substack.com/p/getting-started-with-rust-on-a-raspberry](https://reltech.substack.com/p/getting-started-with-rust-on-a-raspberry) 43 | 2. [https://www.fullstacklabs.co/blog/rust-raspberry-pi-pico-blink](https://www.fullstacklabs.co/blog/rust-raspberry-pi-pico-blink) 44 | 3. [https://www.alexdwilson.dev/learning-in-public/how-to-program-a-raspberry-pi-pico](https://www.alexdwilson.dev/learning-in-public/how-to-program-a-raspberry-pi-pico) 45 | -------------------------------------------------------------------------------- /example-pi-pico/memory.x: -------------------------------------------------------------------------------- 1 | MEMORY { 2 | BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 3 | FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 4 | RAM : ORIGIN = 0x20000000, LENGTH = 256K 5 | } 6 | 7 | EXTERN(BOOT2_FIRMWARE) 8 | 9 | SECTIONS { 10 | /* ### Boot loader */ 11 | .boot2 ORIGIN(BOOT2) : 12 | { 13 | KEEP(*(.boot2)); 14 | } > BOOT2 15 | } INSERT BEFORE .text; 16 | -------------------------------------------------------------------------------- /example-pi-pico/pi2pico-serial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauteh/defmt-serial/febbcf366c5cb39f9906e059408faca23327d25b/example-pi-pico/pi2pico-serial.png -------------------------------------------------------------------------------- /example-pi-pico/pico-defmt-serial-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gauteh/defmt-serial/febbcf366c5cb39f9906e059408faca23327d25b/example-pi-pico/pico-defmt-serial-output.png -------------------------------------------------------------------------------- /example-pi-pico/pico-elf2uf2-defmt-wrapper.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | echo "flashing with elf2uf2-rs" 3 | elf2uf2-rs -d $1 4 | 5 | echo "Attaching defmt-print.." 6 | cat /dev/ttyS0 | defmt-print -e $1 7 | -------------------------------------------------------------------------------- /example-pi-pico/pico-pi4-openocd-defmt-wrapper.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | echo "flashing with openocd" 3 | openocd -f interface/raspberrypi-swd.cfg -f target/rp2040.cfg -c "program $1 verify reset exit" 4 | 5 | 6 | echo "Attaching defmt-print.." 7 | cat /dev/serial0 | defmt-print -e $1 -------------------------------------------------------------------------------- /example-pi-pico/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Blinks the LED on a Pico board 2 | //! 3 | //! This will blink an LED attached to GP25, which is the pin the Pico uses for the on-board LED. 4 | #![no_std] 5 | #![no_main] 6 | 7 | use bsp::entry; 8 | use defmt; 9 | use defmt_serial as _; 10 | use panic_probe as _; 11 | use static_cell::StaticCell; 12 | 13 | // Provide an alias for our BSP so we can switch targets quickly. 14 | // Uncomment the BSP you included in Cargo.toml, the rest of the code does not need to change. 15 | use rp_pico as bsp; 16 | // use sparkfun_pro_micro_rp2040 as bsp; 17 | 18 | use bsp::hal::{ 19 | clocks::{init_clocks_and_plls, Clock}, 20 | pac, 21 | sio::Sio, 22 | watchdog::Watchdog, 23 | }; 24 | 25 | #[entry] 26 | fn main() -> ! { 27 | let mut pac = pac::Peripherals::take().unwrap(); 28 | let core = pac::CorePeripherals::take().unwrap(); 29 | let mut watchdog = Watchdog::new(pac.WATCHDOG); 30 | let sio = Sio::new(pac.SIO); 31 | 32 | // External high-speed crystal on the pico board is 12Mhz 33 | let external_xtal_freq_hz = 12_000_000u32; 34 | let clocks = init_clocks_and_plls( 35 | external_xtal_freq_hz, 36 | pac.XOSC, 37 | pac.CLOCKS, 38 | pac.PLL_SYS, 39 | pac.PLL_USB, 40 | &mut pac.RESETS, 41 | &mut watchdog, 42 | ) 43 | .ok() 44 | .unwrap(); 45 | 46 | let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); 47 | 48 | let pins = bsp::Pins::new( 49 | pac.IO_BANK0, 50 | pac.PADS_BANK0, 51 | sio.gpio_bank0, 52 | &mut pac.RESETS, 53 | ); 54 | 55 | // Need to perform clock init before using UART or it will freeze. 56 | let uart = bsp::hal::uart::UartPeripheral::new( 57 | pac.UART0, 58 | (pins.gpio0.into_mode(), pins.gpio1.into_mode()), 59 | &mut pac.RESETS, 60 | ) 61 | .enable( 62 | bsp::hal::uart::UartConfig::default(), 63 | clocks.peripheral_clock.freq(), 64 | ) 65 | .unwrap(); 66 | 67 | static SERIAL: StaticCell< 68 | bsp::hal::uart::UartPeripheral< 69 | bsp::hal::uart::Enabled, 70 | pac::UART0, 71 | ( 72 | bsp::hal::gpio::Pin< 73 | bsp::hal::gpio::bank0::Gpio0, 74 | bsp::hal::gpio::Function, 75 | >, 76 | bsp::hal::gpio::Pin< 77 | bsp::hal::gpio::bank0::Gpio1, 78 | bsp::hal::gpio::Function, 79 | >, 80 | ), 81 | >, 82 | > = StaticCell::new(); 83 | 84 | defmt_serial::defmt_serial(SERIAL.init(uart)); 85 | defmt::warn!("Hello!"); 86 | 87 | loop { 88 | delay.delay_ms(2000u32); 89 | defmt::info!("Loop!"); 90 | for i in 0..10 { 91 | delay.delay_ms(100u32); 92 | defmt::debug!("Inner loop: {}", i); 93 | } 94 | } 95 | } 96 | // End of file 97 | -------------------------------------------------------------------------------- /example-std/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-linux-musl] 2 | runner = "bash defmt-print-runner.sh" 3 | 4 | rustflags = [ 5 | "-C", "target-feature=+crt-static", 6 | "-C", "relocation-model=static", 7 | "-C", "link-arg=-T/usr/lib/x86_64-linux-gnu/ldscripts/elf_x86_64.x", 8 | # "-C", "link-arg=-T/usr/x86_64-linux-musl/usr/lib/ldscripts/elf_x86_64.x", 9 | "-C", "link-arg=-Tdefmt.x" 10 | ] 11 | 12 | [build] 13 | target = "x86_64-unknown-linux-musl" 14 | -------------------------------------------------------------------------------- /example-std/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /example-std/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bitflags" 7 | version = "1.3.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 10 | 11 | [[package]] 12 | name = "critical-section" 13 | version = "1.1.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" 16 | 17 | [[package]] 18 | name = "defmt" 19 | version = "0.3.5" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "a8a2d011b2fee29fb7d659b83c43fce9a2cb4df453e16d441a51448e448f3f98" 22 | dependencies = [ 23 | "bitflags", 24 | "defmt-macros", 25 | ] 26 | 27 | [[package]] 28 | name = "defmt-macros" 29 | version = "0.3.6" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "54f0216f6c5acb5ae1a47050a6645024e6edafc2ee32d421955eccfef12ef92e" 32 | dependencies = [ 33 | "defmt-parser", 34 | "proc-macro-error", 35 | "proc-macro2", 36 | "quote", 37 | "syn 2.0.18", 38 | ] 39 | 40 | [[package]] 41 | name = "defmt-parser" 42 | version = "0.3.3" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "269924c02afd7f94bc4cecbfa5c379f6ffcf9766b3408fe63d22c728654eccd0" 45 | dependencies = [ 46 | "thiserror", 47 | ] 48 | 49 | [[package]] 50 | name = "defmt-serial" 51 | version = "0.9.0" 52 | dependencies = [ 53 | "critical-section", 54 | "defmt", 55 | "embedded-io", 56 | ] 57 | 58 | [[package]] 59 | name = "embedded-io" 60 | version = "0.6.1" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 63 | 64 | [[package]] 65 | name = "example-std" 66 | version = "0.1.0" 67 | dependencies = [ 68 | "critical-section", 69 | "defmt", 70 | "defmt-serial", 71 | "embedded-io", 72 | "nb", 73 | "static_cell", 74 | ] 75 | 76 | [[package]] 77 | name = "nb" 78 | version = "1.1.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 81 | 82 | [[package]] 83 | name = "portable-atomic" 84 | version = "1.6.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" 87 | 88 | [[package]] 89 | name = "proc-macro-error" 90 | version = "1.0.4" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 93 | dependencies = [ 94 | "proc-macro-error-attr", 95 | "proc-macro2", 96 | "quote", 97 | "syn 1.0.109", 98 | "version_check", 99 | ] 100 | 101 | [[package]] 102 | name = "proc-macro-error-attr" 103 | version = "1.0.4" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 106 | dependencies = [ 107 | "proc-macro2", 108 | "quote", 109 | "version_check", 110 | ] 111 | 112 | [[package]] 113 | name = "proc-macro2" 114 | version = "1.0.60" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" 117 | dependencies = [ 118 | "unicode-ident", 119 | ] 120 | 121 | [[package]] 122 | name = "quote" 123 | version = "1.0.28" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" 126 | dependencies = [ 127 | "proc-macro2", 128 | ] 129 | 130 | [[package]] 131 | name = "static_cell" 132 | version = "2.0.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "fa6ba4cf83bf80d3eb25f098ea5e790a0a1fcb5e357442259b231e412c2d3ca0" 135 | dependencies = [ 136 | "portable-atomic", 137 | ] 138 | 139 | [[package]] 140 | name = "syn" 141 | version = "1.0.109" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 144 | dependencies = [ 145 | "proc-macro2", 146 | "unicode-ident", 147 | ] 148 | 149 | [[package]] 150 | name = "syn" 151 | version = "2.0.18" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" 154 | dependencies = [ 155 | "proc-macro2", 156 | "quote", 157 | "unicode-ident", 158 | ] 159 | 160 | [[package]] 161 | name = "thiserror" 162 | version = "1.0.40" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" 165 | dependencies = [ 166 | "thiserror-impl", 167 | ] 168 | 169 | [[package]] 170 | name = "thiserror-impl" 171 | version = "1.0.40" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" 174 | dependencies = [ 175 | "proc-macro2", 176 | "quote", 177 | "syn 2.0.18", 178 | ] 179 | 180 | [[package]] 181 | name = "unicode-ident" 182 | version = "1.0.9" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" 185 | 186 | [[package]] 187 | name = "version_check" 188 | version = "0.9.4" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 191 | -------------------------------------------------------------------------------- /example-std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-std" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | embedded-io = "0.6" 8 | defmt = "0.3.2" 9 | defmt-serial = { version = "*", path = "../" } 10 | nb = "1.0.0" 11 | critical-section = { version = "1.1", features = ["std"]} 12 | static_cell = "2.0.0" 13 | -------------------------------------------------------------------------------- /example-std/defmt-print-runner.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -ep 4 | 5 | $1 | defmt-print -e $1 6 | -------------------------------------------------------------------------------- /example-std/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(never_type)] 2 | use core::convert::Infallible; 3 | use embedded_io::{Write, ErrorType}; 4 | use static_cell::StaticCell; 5 | use std::io::{self, Write as _}; 6 | 7 | struct StdoutSerial; 8 | 9 | impl ErrorType for StdoutSerial { 10 | type Error = Infallible; 11 | } 12 | 13 | impl Write for StdoutSerial { 14 | fn write(&mut self, word: &[u8]) -> Result { 15 | io::stdout().write(word).unwrap(); 16 | Ok(word.len()) 17 | } 18 | 19 | fn flush(&mut self) -> Result<(), Infallible> { 20 | io::stdout().flush().unwrap(); 21 | Ok(()) 22 | } 23 | } 24 | 25 | static SERIAL: StaticCell = StaticCell::new(); 26 | 27 | fn main() { 28 | eprintln!("Hello, world!"); 29 | 30 | let serial = StdoutSerial; 31 | let _ds = defmt_serial::defmt_serial(SERIAL.init(serial)); 32 | 33 | eprintln!("Logging to info with defmt.."); 34 | defmt::info!("Hello defmt-world!"); 35 | 36 | for i in 0..50 { 37 | defmt::debug!("Now at: {}", i); 38 | } 39 | 40 | defmt::warn!("Done!"); 41 | 42 | eprintln!("Good bye."); 43 | } 44 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | //! A defmt target for logging messages over a serial interface. The serial interface must 3 | //! implement [`embedded_hal::blocking::serial::Write`]. 4 | //! 5 | //! The received defmt-frames can be read using e.g. `socat` and `defmt-print`, so that you can set 6 | //! it up as `cargo run`ner. See the [example-artemis](https://github.com/gauteh/defmt-serial/tree/main/example-artemis) for how to do that. 7 | //! 8 | //! You can also use it to have defmt work on std/hosted OSes, see [example-std](https://github.com/gauteh/defmt-serial/tree/main/example-std). 9 | //! 10 | //! ```no_run 11 | //! #![no_std] 12 | //! #![no_main] 13 | //! 14 | //! 15 | //! use panic_probe as _; 16 | //! use cortex_m::asm; 17 | //! use cortex_m_rt::entry; 18 | //! use ambiq_hal::{self as hal, prelude::*}; 19 | //! 20 | //! use static_cell::StaticCell; 21 | //! use defmt; 22 | //! use defmt_serial as _; 23 | //! 24 | //! static SERIAL: StaticCell = StaticCell::new(); 25 | //! 26 | //! #[entry] 27 | //! fn main() -> ! { 28 | //! let mut dp = hal::pac::Peripherals::take().unwrap(); 29 | //! let pins = hal::gpio::Pins::new(dp.GPIO); 30 | //! 31 | //! // set up serial 32 | //! let serial = hal::uart::Uart0::new(dp.UART0, pins.tx0, pins.rx0); 33 | //! defmt_serial::defmt_serial(SERIAL.init(serial)); 34 | //! 35 | //! defmt::info!("Hello from defmt!"); 36 | //! 37 | //! loop { 38 | //! asm::wfi(); 39 | //! } 40 | //! } 41 | //! ``` 42 | 43 | use core::ptr::addr_of_mut; 44 | use core::sync::atomic::{AtomicBool, Ordering}; 45 | use defmt::global_logger; 46 | use embedded_io::Write; 47 | 48 | static mut ENCODER: defmt::Encoder = defmt::Encoder::new(); 49 | static TAKEN: AtomicBool = AtomicBool::new(false); 50 | static mut CS_RESTORE: critical_section::RestoreState = critical_section::RestoreState::invalid(); 51 | 52 | /// All of this nonsense is to try and erase the Error type of the `embedded_hal::serial::nb::Write` implementor. 53 | pub trait EraseWrite { 54 | fn write(&mut self, buf: &[u8]); 55 | fn flush(&mut self); 56 | } 57 | 58 | impl EraseWrite for T { 59 | fn write(&mut self, buf: &[u8]) { 60 | self.write_all(buf).ok(); 61 | } 62 | 63 | fn flush(&mut self) { 64 | self.flush().ok(); 65 | } 66 | } 67 | 68 | static mut ERASEDWRITE: Option<&mut dyn EraseWrite> = None; 69 | 70 | /// Assign a serial peripheral to receive defmt-messages. 71 | /// 72 | /// 73 | /// ```no_run 74 | /// static SERIAL: StaticCell = StaticCell::new(); 75 | /// let serial = hal::uart::Uart0::new(dp.UART0, pins.tx0, pins.rx0); 76 | /// defmt_serial::defmt_serial(SERIAL.init(serial)); 77 | /// 78 | /// defmt::info!("Hello from defmt!"); 79 | /// ``` 80 | /// 81 | /// The peripheral should implement the [`embedded_hal::blocking::serial::Write`] trait. If your HAL 82 | /// already has the non-blocking [`Write`](embedded_hal::serial::Write) implemented, it can opt-in 83 | /// to the [default implementation](embedded_hal::blocking::serial::write::Default). 84 | /// 85 | /// Will panic if assigned more than once. 86 | pub fn defmt_serial(serial: &'static mut T) { 87 | unsafe { 88 | critical_section::with(|_| { 89 | assert!( 90 | ERASEDWRITE.is_none(), 91 | "Tried to assign serial port when one was already assigned." 92 | ); 93 | ERASEDWRITE = Some(serial); 94 | }); 95 | } 96 | } 97 | 98 | /// Release the serial port from defmt. 99 | pub fn release() { 100 | unsafe { 101 | critical_section::with(|_| { 102 | if TAKEN.load(Ordering::Relaxed) { 103 | panic!("defmt logger taken reentrantly"); // I don't think this actually is 104 | // possible. 105 | } 106 | 107 | ERASEDWRITE = None; 108 | }); 109 | } 110 | } 111 | 112 | #[global_logger] 113 | struct GlobalSerialLogger; 114 | 115 | unsafe impl defmt::Logger for GlobalSerialLogger { 116 | fn acquire() { 117 | let restore = unsafe { critical_section::acquire() }; 118 | 119 | if TAKEN.load(Ordering::Relaxed) { 120 | panic!("defmt logger taken reentrantly"); 121 | } 122 | 123 | TAKEN.store(true, Ordering::Relaxed); 124 | 125 | unsafe { 126 | CS_RESTORE = restore; 127 | } 128 | 129 | unsafe { ENCODER.start_frame(write_serial) } 130 | } 131 | 132 | unsafe fn release() { 133 | ENCODER.end_frame(write_serial); 134 | TAKEN.store(false, Ordering::Relaxed); 135 | 136 | let restore = CS_RESTORE; 137 | critical_section::release(restore); 138 | } 139 | 140 | unsafe fn write(bytes: &[u8]) { 141 | ENCODER.write(bytes, write_serial); 142 | } 143 | 144 | unsafe fn flush() { 145 | if let Some(writer) = &mut *addr_of_mut!(ERASEDWRITE) { 146 | (*writer).flush(); 147 | } 148 | } 149 | } 150 | 151 | /// Write to serial using proxy function. We must ensure this function is not called 152 | /// several times in parallel. 153 | fn write_serial(remaining: &[u8]) { 154 | unsafe { 155 | if let Some(writer) = &mut *addr_of_mut!(ERASEDWRITE) { 156 | (*writer).write(remaining); 157 | } 158 | } 159 | } 160 | --------------------------------------------------------------------------------