Skip to content

Instantly share code, notes, and snippets.

@cjdell
Created May 29, 2026 13:57
Show Gist options
  • Select an option

  • Save cjdell/46f98dd217247a1c36907c9e6fe4cf80 to your computer and use it in GitHub Desktop.

Select an option

Save cjdell/46f98dd217247a1c36907c9e6fe4cf80 to your computer and use it in GitHub Desktop.
How to use `defmt` over TCP with Embassy
// Observe logs with: defmt-print -e target/thumbv8m.main-none-eabihf/release/YOUR_ELF tcp --host YOUR_IP
// Add the follow to your program...
// #[embassy_executor::main]
// async fn main(spawner: Spawner) {
// ...
// spawner.spawn(defmt_runner(stack).unwrap());
// ...
// }
//
// #[embassy_executor::task]
// async fn defmt_runner(stack: Stack<'static>) {
// TCP_SENDER.runner(stack).await;
// }
use alloc::vec::Vec;
use defmt::Encoder;
use embassy_net::{Stack, tcp::TcpSocket};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, rwlock::RwLock};
use embassy_time::Duration;
use embedded_io_async::Write as _;
const RTT_PORT: u16 = 19021;
#[defmt::global_logger]
struct TcpLogger;
unsafe impl defmt::Logger for TcpLogger {
fn acquire() {
TCP_SENDER.acquire();
}
unsafe fn flush() {
TCP_SENDER.flush();
}
unsafe fn release() {
TCP_SENDER.release();
}
unsafe fn write(bytes: &[u8]) {
TCP_SENDER.write(bytes);
}
}
pub static TCP_SENDER: TcpSender = TcpSender::new();
pub struct TcpSender {
channel: Channel<CriticalSectionRawMutex, Vec<u8>, 10000>,
encoder: RwLock<CriticalSectionRawMutex, Encoder>,
}
impl TcpSender {
const fn new() -> Self {
TcpSender {
channel: Channel::new(),
encoder: RwLock::new(Encoder::new()),
}
}
fn acquire(&self) {
let mut encoder = match self.encoder.try_write() {
Ok(encoder) => encoder,
Err(_) => return,
};
encoder.start_frame(|bytes| {
self.channel.try_send(bytes.to_vec()).ok();
});
}
fn flush(&self) {}
fn release(&self) {
let mut encoder = match self.encoder.try_write() {
Ok(encoder) => encoder,
Err(_) => return,
};
encoder.end_frame(|bytes| {
self.channel.try_send(bytes.to_vec()).ok();
});
}
fn write(&self, bytes: &[u8]) {
let mut encoder = match self.encoder.try_write() {
Ok(encoder) => encoder,
Err(_) => return,
};
encoder.write(bytes, |bytes| {
self.channel.try_send(bytes.to_vec()).ok();
});
}
pub async fn runner(&self, stack: Stack<'static>) {
let mut rx_buffer = [0; 4096];
let mut tx_buffer = [0; 4096];
loop {
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
socket.set_timeout(Some(Duration::from_secs(10)));
if let Err(_) = socket.accept(RTT_PORT).await {
continue;
}
loop {
let buf = self.channel.receive().await;
match socket.write_all(&buf).await {
Ok(()) => (),
Err(_) => break,
};
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment