diff --git a/src/internal_state.rs b/src/internal_state.rs
index 3bdb772f50b7330dd79c4ec4a0823c9342d4c71c..ef64e3bcbe37dcc7a2b1bf565a1c987169528c36 100644
--- a/src/internal_state.rs
+++ b/src/internal_state.rs
@@ -1,15 +1,15 @@
 // Tracks the state of our instance, *not* the state of the radio.
 #[derive(Debug)]
-pub enum InternalState<const PACKET_LEN: usize> {
+pub enum InternalState<'a, const PACKET_LEN: usize> {
     Idle,
     Rx {
-        data: [u8; PACKET_LEN],
+        data: &'a mut [u8; PACKET_LEN],
         i: usize,
         received: bool,
         rssi: Option<f64>,
     },
     Tx {
-        data: [u8; PACKET_LEN],
+        data: &'a [u8],
         i: usize,
         len: usize,
     },
diff --git a/src/lib.rs b/src/lib.rs
index 187f549759e36ebb5a615f997e385f9830412ff2..d9b2afab09311b409c322df24cacb5a1f45b7c62 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -22,20 +22,21 @@ use state::State;
 
 use crate::internal_radio::InternalRadio;
 
-pub struct Rf4463<const MAX_PACKET_LEN: usize, Spi, SdnPin, CsPin, Delay> {
+pub struct Rf4463<'a, const MAX_PACKET_LEN: usize, Spi, SdnPin, CsPin, Delay> {
     radio: InternalRadio<Spi, SdnPin, CsPin, Delay>,
-    state: InternalState<MAX_PACKET_LEN>,
+    state: InternalState<'a, MAX_PACKET_LEN>,
     rx_forever: bool,
     channel: u8,
 }
 
 impl<
+        'a,
         const MAX_PACKET_LEN: usize,
         Spi: Transfer<u8>,
         SdnPin: OutputPin<Error = Infallible>,
         CsPin: OutputPin<Error = Infallible>,
         Delay: DelayUs<u16>,
-    > Rf4463<MAX_PACKET_LEN, Spi, SdnPin, CsPin, Delay>
+    > Rf4463<'a, MAX_PACKET_LEN, Spi, SdnPin, CsPin, Delay>
 where
     Spi::Error: Debug,
 {
@@ -91,14 +92,19 @@ where
     }
 
     // Len is none when using a variable-length packet
-    pub fn start_rx(&mut self, len: Option<usize>, rx_forever: bool) -> Result<(), Spi::Error> {
+    pub fn start_rx(
+        &mut self,
+        buf: &'a mut [u8; MAX_PACKET_LEN],
+        len: Option<usize>,
+        rx_forever: bool,
+    ) -> Result<(), Spi::Error> {
         self.radio.clear_fifo()?;
         self.radio.clear_ph_and_modem_interrupts()?;
 
         let len = len.unwrap_or(0);
 
         self.state = InternalState::Rx {
-            data: [0; MAX_PACKET_LEN],
+            data: buf,
             i: 0,
             received: false,
             rssi: None,
@@ -124,44 +130,58 @@ where
         Ok(())
     }
 
-    pub fn finish_rx(&mut self) -> Result<Option<RxPacket<MAX_PACKET_LEN>>, Spi::Error> {
-        let pkt = match self.state {
+    /// Panics if rx_forever is true and new_buf is None
+    pub fn finish_rx(
+        &mut self,
+        new_buf: Option<&'a mut [u8; MAX_PACKET_LEN]>,
+    ) -> Result<Option<RxPacket<MAX_PACKET_LEN>>, Spi::Error> {
+        let (set_idle, ret) = match &mut self.state {
             InternalState::Rx {
                 data,
                 received,
                 i,
                 rssi,
-            } if received => {
-                let rssi = match rssi {
+            } if *received => {
+                let ret_rssi = match *rssi {
                     Some(x) => x,
-                    None => self.get_rssi()?,
+                    None => self.radio.get_rssi()?,
                 };
 
-                let ret = Some(RxPacket::new(data, i, rssi));
+                let ret = if self.rx_forever {
+                    let new_buf = new_buf.expect("rx_forever=true and no replacement buffer given");
 
-                if self.rx_forever {
                     self.radio.clear_ph_and_modem_interrupts()?;
 
-                    self.state = InternalState::Rx {
-                        data: [0; MAX_PACKET_LEN],
-                        i: 0,
-                        received: false,
-                        rssi: None,
-                    };
+                    core::mem::swap(new_buf, data);
+                    *i = 0;
+                    *received = false;
+                    *rssi = None;
+
+                    (false, Some(RxPacket::new(new_buf, *i, ret_rssi)))
                 } else {
-                    self.state = InternalState::Idle;
-                }
+                    (true, Some(RxPacket::new(data, *i, ret_rssi)))
+                };
 
                 ret
             }
 
-            _ => None,
+            _ => (false, None),
         };
 
-        Ok(pkt)
+        if set_idle {
+            self.state = InternalState::Idle;
+        }
+
+        Ok(ret)
     }
 
-    pub fn start_tx(&mut self, data: &[u8]) -> Result<(), TxError<Spi::Error>> {
+    /// Panics if data length is > 8191
+    pub fn start_tx(&mut self, data: &'a [u8]) -> Result<(), TxError<Spi::Error>> {
+        assert!(
+            MAX_PACKET_LEN < 8192,
+            "Packet length cannot be above 8191 bytes"
+        );
+
         let len = data.len();
         if len > MAX_PACKET_LEN {
             return Err(TxError::TooMuchData);
@@ -194,13 +214,7 @@ where
             ])
             .txe()?;
 
-        let mut padded_data = [0; MAX_PACKET_LEN];
-        padded_data[0..len].copy_from_slice(data);
-        self.state = InternalState::Tx {
-            data: padded_data,
-            i,
-            len,
-        };
+        self.state = InternalState::Tx { data, i, len };
 
         Ok(())
     }
@@ -288,12 +302,12 @@ where
 
     fn tx_step(&mut self) -> Result<(), TransferError<Spi::Error>> {
         let (data, i) = match &mut self.state {
-            InternalState::Tx { data, i, len } => (&mut data[0..*len], i),
+            InternalState::Tx { data, i, len } => (&data[0..*len], i),
             _ => return Ok(()),
         };
 
         // only look at the slice we haven't sent yet
-        let data = &mut data[*i..];
+        let data = &data[*i..];
 
         let len: usize = self.radio.tx_fifo_space().te()?.into();
         let len = len.min(data.len());
@@ -301,13 +315,19 @@ where
             return Ok(());
         }
 
+        // we need a mutable reference on data for our transfer function
+        // so we copy that data into a temporary buffer
+        const FIFO_BUFFER_LEN: usize = 129;
+        let mut buf = [0; FIFO_BUFFER_LEN];
+        buf[..len].copy_from_slice(&data[..len]);
+
         if self.radio.fifo_underflow_pending().te()? {
             return Err(TransferError::FifoOverflow);
         }
 
         self.radio.with_cs(|s| {
             s.spi_transfer_byte(WRITE_TX_FIFO).te()?;
-            s.spi.transfer(&mut data[0..len]).te()?;
+            s.spi.transfer(&mut buf[0..len]).te()?;
 
             Ok(())
         })?;
diff --git a/src/rx.rs b/src/rx.rs
index 7677257b55524951e854a5222fb8619ba57d47fc..e7f99d6837fc89b2073b2573ef465fc3622c3583 100644
--- a/src/rx.rs
+++ b/src/rx.rs
@@ -1,12 +1,12 @@
-#[derive(Debug, Clone)]
-pub struct RxPacket<const N: usize> {
-    data: [u8; N],
+#[derive(Debug)]
+pub struct RxPacket<'a, const N: usize> {
+    data: &'a mut [u8; N],
     i: usize,
     rssi: f64,
 }
 
-impl<const N: usize> RxPacket<N> {
-    pub(crate) fn new(data: [u8; N], i: usize, rssi: f64) -> Self {
+impl<'a, const N: usize> RxPacket<'a, N> {
+    pub(crate) fn new(data: &'a mut [u8; N], i: usize, rssi: f64) -> Self {
         Self { data, i, rssi }
     }
 
@@ -17,4 +17,8 @@ impl<const N: usize> RxPacket<N> {
     pub fn rssi(&self) -> f64 {
         self.rssi
     }
+
+    fn into_buf(self) -> &'a mut [u8; N] {
+        self.data
+    }
 }