diff --git a/.cargo/config.toml b/.cargo/config.toml
index 0687b20267f03d161ac856f7d088f1a2d8ae7956..de8eca6872180644052c69f949fd82647b52b12b 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,6 +1,7 @@
 [target.thumbv7em-none-eabihf]
 rustflags = [
   "-C", "link-arg=-Tlink.x",
+  "-C", "linker=flip-link",
 ]
 
 [build]
diff --git a/Cargo.lock b/Cargo.lock
index 0d7a2ae8ff2bf08d792c5e8ef6a2a4ee26b9e06d..0b0da39fe408847709c63c8435d0e34f3b1989dc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -306,7 +306,7 @@ dependencies = [
 [[package]]
 name = "ham-cats"
 version = "0.1.0"
-source = "git+https://gitlab.scd31.com/cats/ham-cats/#d32d0a72f3e5633a25cc6495b2fe7daed139fc84"
+source = "git+https://gitlab.scd31.com/cats/ham-cats/#97ff918c4e3aaa6e15dadbaa1ab9c12b1fdb5655"
 dependencies = [
  "arrayvec",
  "bitvec",
diff --git a/README.md b/README.md
index 4ee18fee291fe8fd7029a42208b79f58b4a70d42..c621ef79412dbe8d418640bdc5e294a2a63cc5a3 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,7 @@ git clone https://gitlab.scd31.com/cats/mobile-transceiver-software
 cd mobile-transceiver-software
 git pull # make sure we're up to date
 rustup target add thumbv7em-none-eabihf # add our board architecture, if needed
+cargo install flip-link # needed for building
 cargo install cargo-dfu # needed for flashing
 ```
 
diff --git a/src/main.rs b/src/main.rs
index c5be60081dcd059f57bc3c1392c561f41dece93f..eb29d54a2d075007b975194916016a140c2e2b4e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -28,6 +28,7 @@ mod app {
     };
     use half::f16;
     use ham_cats::{
+        buffer::Buffer,
         packet::Packet,
         whisker::{Gps, Route},
     };
@@ -263,7 +264,8 @@ mod app {
         };
 
         if should_transmit {
-            let mut cats: Packet<MAX_PACKET_LEN> = Packet::default();
+            let mut buf = [0; MAX_PACKET_LEN];
+            let mut cats = Packet::new(&mut buf);
 
             config.lock(|config| {
                 cats.add_identification(ham_cats::whisker::Identification {
@@ -292,18 +294,14 @@ mod app {
                 .unwrap();
             }
 
-            let x = cats.fully_encode().unwrap();
-
             let buf = ctx.local.buf;
-            buf[..x.len()].copy_from_slice(&x);
-
-            let tx_buf = &buf[..x.len()];
+            let mut x = Buffer::new_empty(buf);
+            cats.fully_encode(&mut x).unwrap();
 
-            ctx.shared.radio.lock(|radio| {
-                radio
-                    .as_mut()
+            (ctx.shared.radio, config).lock(|r, c| {
+                r.as_mut()
                     .unwrap()
-                    .tx(&mut ctx.shared.red, tx_buf)
+                    .tx(&mut ctx.shared.red, &x, Some((&c.callsign, c.ssid)))
                     .unwrap();
             });
         }
diff --git a/src/radio.rs b/src/radio.rs
index 4255333a4fe2c8c15fae575f93cf11c38bbdec26..0fc9d2b36962e9b72abe086d9ab37f2fe0d6d3fd 100644
--- a/src/radio.rs
+++ b/src/radio.rs
@@ -1,4 +1,5 @@
 use ham_cats::{
+    buffer::Buffer,
     packet::Packet,
     whisker::{Route, RouteNode},
 };
@@ -20,7 +21,7 @@ type MyDelay = Delay<TIM5, 1000000>;
 type Radio = Rf4463<Spi<SPI1>, SdnPin, CsPin, MyDelay>;
 
 pub struct RadioManager<'a> {
-    radio: Radio,
+    pub radio: Radio,
     buf: &'a mut [u8; MAX_PACKET_LEN],
     enable_digipeating: bool,
 }
@@ -39,8 +40,7 @@ impl<'a> RadioManager<'a> {
         // sets us up for the default CATS frequency, 430.500 MHz
         radio.set_channel(20);
 
-        // perpetual mode
-        radio.start_rx(None, true).ok()?;
+        radio.start_rx(None, false).ok()?;
 
         Some(Self {
             radio,
@@ -51,6 +51,8 @@ impl<'a> RadioManager<'a> {
 
     // call me every 20-ish ms
     // technically needs to be every 100ms, tops
+    // digipeats only if ident is Some,
+    // otherwise the packet is discarded
     pub fn tick<P: OutputPin, M: Mutex<T = P>>(
         &mut self,
         led: &mut M,
@@ -58,7 +60,7 @@ impl<'a> RadioManager<'a> {
     ) -> Result<(), TransferError<spi::Error>> {
         if self.radio.is_idle() {
             self.radio
-                .start_rx(None, true)
+                .start_rx(None, false)
                 .map_err(TransferError::SpiError)?;
         }
 
@@ -67,25 +69,33 @@ impl<'a> RadioManager<'a> {
         if let Some(data) = self.radio.finish_rx(self.buf).unwrap() {
             if self.enable_digipeating {
                 if let Some((callsign, ssid)) = ident {
-                    if let Ok(packet) = Packet::fully_decode(data.data()) {
+                    let mut buf = [0; MAX_PACKET_LEN];
+                    let p = Packet::fully_decode(data.data(), &mut buf);
+                    if let Ok(packet) = p {
                         self.handle_packet_rx(led, packet, callsign, ssid);
-                    };
+                    }
                 }
             }
 
-            // Only needed if the radio gets confused
             self.radio
-                .start_rx(None, true)
+                .start_rx(None, false)
                 .map_err(TransferError::SpiError)?;
         }
 
         Ok(())
     }
 
-    pub fn tx<P: OutputPin, M: Mutex<T = P>>(&mut self, led: &mut M, data: &[u8]) -> Option<()> {
+    // digipeats only if ident is Some,
+    // otherwise the rx'd packet is discarded
+    pub fn tx<P: OutputPin, M: Mutex<T = P>>(
+        &mut self,
+        led: &mut M,
+        data: &[u8],
+        ident: Option<(&str, u8)>,
+    ) -> Option<()> {
         // don't want to transmit on top of a packet
         while self.radio.is_busy_rxing().ok()? {
-            self.tick(led, None).ok()?;
+            self.tick(led, ident).ok()?;
         }
 
         led.lock(|l| l.set_high().ok());
@@ -99,7 +109,6 @@ impl<'a> RadioManager<'a> {
         Some(())
     }
 
-    // Not great - lots of copies, and therefore stack usage here
     fn handle_packet_rx<P: OutputPin, M: Mutex<T = P>>(
         &mut self,
         led: &mut M,
@@ -112,8 +121,10 @@ impl<'a> RadioManager<'a> {
                 return;
             }
 
-            if let Ok(buf) = packet.fully_encode() {
-                self.tx(led, &buf);
+            let mut buf = [0; MAX_PACKET_LEN];
+            let mut buf = Buffer::new_empty(&mut buf);
+            if packet.fully_encode(&mut buf).is_ok() {
+                self.tx(led, &buf, None);
             }
         }
     }