diff --git a/config.toml.example b/config.toml.example
index 109e6d26fa5e6417fb1d79cb97baf2aa95f5652d..afc4001fbfdcbb052b65dd6a1f3b8cb0d6a3af6d 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -14,6 +14,9 @@ callsign = "NOCALL"
 # Max image dimension. Comment this out to send full-sized images
 max_img_dimension = 1024
 
+# radio uart
+uart = "/dev/ttyAMA0"
+
 # Comment this section out if you're not using
 # APRS for control
 [control]
diff --git a/src/config.rs b/src/config.rs
index f9057d1311a3d90aad91706b3eeb0804e563038b..b4b8e2666ed490318a9d34fc4bce45574492f62c 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -14,6 +14,7 @@ pub struct ControlConfig {
 pub struct Config {
     pub paths: Vec<PathBuf>,
     pub callsign: String,
+    pub uart: String,
     pub max_img_dimension: Option<u32>,
 
     pub control: Option<ControlConfig>,
diff --git a/src/control.rs b/src/control.rs
index 34aa11d9215e2e9085e7e8364afbbd2aff544b50..c779c11df73818fa5cf3304ca2a769fbdfb198a1 100644
--- a/src/control.rs
+++ b/src/control.rs
@@ -33,7 +33,11 @@ impl Controller {
         let (telem_tx, telem_rx) = mpsc::channel();
         let (cmd_tx, cmd_rx) = mpsc::channel();
 
-        thread::spawn(|| Self::tx_thread(img_rx, telem_rx));
+        {
+            let uart = self.config.uart.clone();
+            thread::spawn(|| Self::tx_thread(uart, img_rx, telem_rx));
+        }
+
         {
             let config = self.config.clone();
             thread::spawn(|| Self::aprs_thread(config, cmd_tx, telem_tx));
@@ -81,10 +85,9 @@ impl Controller {
     }
 
     // manages our transceiver
-    fn tx_thread(image_rx: Receiver<FecPacket>, telem_rx: Receiver<Packet>) {
+    fn tx_thread(uart: String, image_rx: Receiver<FecPacket>, telem_rx: Receiver<Packet>) {
         let mut radio = loop {
-            // TODO this should be configurable
-            let r = UartRadio::new("/dev/ttyAMA0");
+            let r = UartRadio::new(&uart);
 
             match r {
                 Ok(r) => break r,