diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index c42fa1138..e1d32a311 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -236,6 +236,48 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe
     }
 }
 
+impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead
+    for BufferedUarte<'d, U, T>
+{
+    type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>>
+    where
+        Self: 'a;
+
+    fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
+        poll_fn(move |cx| {
+            self.inner.with(|state| {
+                compiler_fence(Ordering::SeqCst);
+                trace!("fill_buf");
+
+                // We have data ready in buffer? Return it.
+                let buf = state.rx.pop_buf();
+                if !buf.is_empty() {
+                    trace!("  got {:?} {:?}", buf.as_ptr() as u32, buf.len());
+                    let buf: &[u8] = buf;
+                    // Safety: buffer lives as long as uart
+                    let buf: &[u8] = unsafe { core::mem::transmute(buf) };
+                    return Poll::Ready(Ok(buf));
+                }
+
+                trace!("  empty");
+                state.rx_waker.register(cx.waker());
+                Poll::<Result<&[u8], Self::Error>>::Pending
+            })
+        })
+    }
+
+    fn consume(&mut self, amt: usize) {
+        let signal = self.inner.with(|state| {
+            let full = state.rx.is_full();
+            state.rx.pop(amt);
+            full
+        });
+        if signal {
+            self.inner.pend();
+        }
+    }
+}
+
 impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write
     for BufferedUarte<'d, U, T>
 {
diff --git a/examples/nrf/src/bin/buffered_uart.rs b/examples/nrf/src/bin/buffered_uart.rs
index a64c5821b..782c39499 100644
--- a/examples/nrf/src/bin/buffered_uart.rs
+++ b/examples/nrf/src/bin/buffered_uart.rs
@@ -6,7 +6,7 @@ use defmt::*;
 use embassy::executor::Spawner;
 use embassy_nrf::buffered_uarte::State;
 use embassy_nrf::{buffered_uarte::BufferedUarte, interrupt, uarte, Peripherals};
-use embedded_io::asynch::{Read, Write};
+use embedded_io::asynch::{BufRead, Write};
 use futures::pin_mut;
 
 use defmt_rtt as _; // global logger
@@ -46,23 +46,13 @@ async fn main(_spawner: Spawner, p: Peripherals) {
     unwrap!(u.write_all(b"Hello!\r\n").await);
     info!("wrote hello in uart!");
 
-    // Simple demo, reading 8-char chunks and echoing them back reversed.
     loop {
         info!("reading...");
-        let mut buf = [0u8; 8];
-        unwrap!(u.read_exact(&mut buf).await);
+        let buf = unwrap!(u.fill_buf().await);
         info!("read done, got {}", buf);
 
-        // Reverse buf
-        for i in 0..4 {
-            buf.swap(i, 7 - i);
-        }
-
-        info!("writing...");
-        unwrap!(u.write_all(&buf).await);
-        info!("write done");
-
-        // Wait until the bytes are actually finished being transmitted
-        unwrap!(u.flush().await);
+        // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again
+        let n = buf.len();
+        u.consume(n);
     }
 }