diff --git a/src/calibrate.rs b/src/calibrate.rs
deleted file mode 100644
index 0decbca..0000000
--- a/src/calibrate.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use defmt::Format;
-use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pubsub::Subscriber};
-use embassy_time::Timer;
-
-use crate::{gcc_hid::GcReport, input::CHANNEL_GCC_STATE};
-
-const CALIBRATION_ENTRY_COMBO: [AwaitableButtons; 4] = [
-    AwaitableButtons::Start,
-    AwaitableButtons::A,
-    AwaitableButtons::X,
-    AwaitableButtons::Y,
-];
-
-/// This doesn't need to be super fast, since it's only used
-/// in calibration mode.
-const BUTTON_POLL_INTERVAL_MILLIS: u64 = 20;
-
-#[derive(Clone, Copy, Debug, Format)]
-enum AwaitableButtons {
-    A,
-    B,
-    X,
-    Y,
-    Up,
-    Down,
-    Left,
-    Right,
-    Start,
-    L,
-    R,
-}
-
-trait WaitForButtonPress {
-    /// Wait for a single button press.
-    async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons);
-
-    /// Wait for multiple buttons to be pressed simultaneously.
-    async fn wait_for_simultaneous_button_presses<const N: usize>(
-        &mut self,
-        buttons_to_wait_for: &[AwaitableButtons; N],
-    );
-
-    /// Wait for a single button press of specified buttons, and return the button that was pressed.
-    async fn wait_and_filter_button_press<const N: usize>(
-        &mut self,
-        buttons_to_wait_for: &[AwaitableButtons; N],
-    ) -> AwaitableButtons;
-
-    /// Wait for multiple possible button combinations to be pressed simultaneously, and return the index of the combination that was pressed.
-    async fn wait_and_filter_simultaneous_button_presses<const N: usize, const M: usize>(
-        &mut self,
-        buttons_to_wait_for: &[[AwaitableButtons; N]; M],
-    ) -> usize;
-}
-
-impl<'a, const I: usize, const J: usize, const K: usize> WaitForButtonPress
-    for Subscriber<'a, CriticalSectionRawMutex, GcReport, I, J, K>
-{
-    async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) {
-        loop {
-            if match self.next_message_pure().await {
-                report => is_awaitable_button_pressed(&report, button_to_wait_for),
-            } {
-                break;
-            }
-            Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
-        }
-    }
-
-    async fn wait_for_simultaneous_button_presses<const N: usize>(
-        &mut self,
-        buttons_to_wait_for: &[AwaitableButtons; N],
-    ) {
-        loop {
-            if match self.next_message_pure().await {
-                report => buttons_to_wait_for
-                    .iter()
-                    .all(|button| is_awaitable_button_pressed(&report, button)),
-            } {
-                break;
-            }
-            Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
-        }
-    }
-
-    async fn wait_and_filter_button_press<const N: usize>(
-        &mut self,
-        buttons_to_wait_for: &[AwaitableButtons; N],
-    ) -> AwaitableButtons {
-        loop {
-            match self.next_message_pure().await {
-                report => {
-                    for button in buttons_to_wait_for {
-                        if is_awaitable_button_pressed(&report, button) {
-                            return *button;
-                        }
-                    }
-                }
-            }
-            Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
-        }
-    }
-
-    async fn wait_and_filter_simultaneous_button_presses<const N: usize, const M: usize>(
-        &mut self,
-        buttons_to_wait_for: &[[AwaitableButtons; N]; M],
-    ) -> usize {
-        loop {
-            match self.next_message_pure().await {
-                report => {
-                    for (i, buttons) in buttons_to_wait_for.iter().enumerate() {
-                        if buttons
-                            .iter()
-                            .all(|button| is_awaitable_button_pressed(&report, button))
-                        {
-                            return i;
-                        }
-                    }
-                }
-            }
-            Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
-        }
-    }
-}
-
-fn is_awaitable_button_pressed(report: &GcReport, button_to_wait_for: &AwaitableButtons) -> bool {
-    match button_to_wait_for {
-        AwaitableButtons::A => report.buttons_1.button_a,
-        AwaitableButtons::B => report.buttons_1.button_b,
-        AwaitableButtons::X => report.buttons_1.button_x,
-        AwaitableButtons::Y => report.buttons_1.button_y,
-        AwaitableButtons::Up => report.buttons_1.dpad_up,
-        AwaitableButtons::Down => report.buttons_1.dpad_down,
-        AwaitableButtons::Left => report.buttons_1.dpad_left,
-        AwaitableButtons::Right => report.buttons_1.dpad_right,
-        AwaitableButtons::Start => report.buttons_2.button_start,
-        AwaitableButtons::L => report.buttons_2.button_l,
-        AwaitableButtons::R => report.buttons_2.button_r,
-    }
-}
-
-pub async fn calibration_loop() {
-    let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
-
-    loop {
-        gcc_subscriber
-            .wait_for_simultaneous_button_presses(&CALIBRATION_ENTRY_COMBO)
-            .await;
-    }
-}
diff --git a/src/config.rs b/src/config.rs
index da95f57..8c917de 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,5 +1,6 @@
 /**
  *  Storage for controller configuration, including helper functions & types, as well as sane defaults.
+ *  Also includes necessary logic for configuring the controller & calibrating the sticks.
  */
 use core::f32::consts::PI;
 
@@ -16,6 +17,22 @@ use crate::{
     ADDR_OFFSET, FLASH_SIZE,
 };
 
+use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pubsub::Subscriber};
+use embassy_time::Timer;
+
+use crate::{gcc_hid::GcReport, input::CHANNEL_GCC_STATE};
+
+const CONFIG_MODE_ENTRY_COMBO: [AwaitableButtons; 4] = [
+    AwaitableButtons::Start,
+    AwaitableButtons::A,
+    AwaitableButtons::X,
+    AwaitableButtons::Y,
+];
+
+/// This doesn't need to be super fast, since it's only used
+/// in config mode.
+const BUTTON_POLL_INTERVAL_MILLIS: u64 = 20;
+
 /// This needs to be incremented for ANY change to ControllerConfig
 /// else we risk loading uninitialized memory.
 pub const CONTROLLER_CONFIG_REVISION: u8 = 2;
@@ -98,6 +115,21 @@ const DEFAULT_ANGLES: [f32; NO_OF_NOTCHES] = [
     PI * 15. / 8.,
 ];
 
+#[derive(Clone, Copy, Debug, Format)]
+enum AwaitableButtons {
+    A,
+    B,
+    X,
+    Y,
+    Up,
+    Down,
+    Left,
+    Right,
+    Start,
+    L,
+    R,
+}
+
 #[derive(Debug, Clone, Format, PackedStruct)]
 #[packed_struct(endian = "msb")]
 pub struct StickConfig {
@@ -199,3 +231,123 @@ impl ControllerConfig {
         Ok(())
     }
 }
+
+trait WaitForButtonPress {
+    /// Wait for a single button press.
+    async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons);
+
+    /// Wait for multiple buttons to be pressed simultaneously.
+    async fn wait_for_simultaneous_button_presses<const N: usize>(
+        &mut self,
+        buttons_to_wait_for: &[AwaitableButtons; N],
+    );
+
+    /// Wait for a single button press of specified buttons, and return the button that was pressed.
+    async fn wait_and_filter_button_press<const N: usize>(
+        &mut self,
+        buttons_to_wait_for: &[AwaitableButtons; N],
+    ) -> AwaitableButtons;
+
+    /// Wait for multiple possible button combinations to be pressed simultaneously, and return the index of the combination that was pressed.
+    async fn wait_and_filter_simultaneous_button_presses<const N: usize, const M: usize>(
+        &mut self,
+        buttons_to_wait_for: &[[AwaitableButtons; N]; M],
+    ) -> usize;
+}
+
+impl<'a, const I: usize, const J: usize, const K: usize> WaitForButtonPress
+    for Subscriber<'a, CriticalSectionRawMutex, GcReport, I, J, K>
+{
+    async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) {
+        loop {
+            if match self.next_message_pure().await {
+                report => is_awaitable_button_pressed(&report, button_to_wait_for),
+            } {
+                break;
+            }
+            Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
+        }
+    }
+
+    async fn wait_for_simultaneous_button_presses<const N: usize>(
+        &mut self,
+        buttons_to_wait_for: &[AwaitableButtons; N],
+    ) {
+        loop {
+            if match self.next_message_pure().await {
+                report => buttons_to_wait_for
+                    .iter()
+                    .all(|button| is_awaitable_button_pressed(&report, button)),
+            } {
+                break;
+            }
+            Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
+        }
+    }
+
+    async fn wait_and_filter_button_press<const N: usize>(
+        &mut self,
+        buttons_to_wait_for: &[AwaitableButtons; N],
+    ) -> AwaitableButtons {
+        loop {
+            match self.next_message_pure().await {
+                report => {
+                    for button in buttons_to_wait_for {
+                        if is_awaitable_button_pressed(&report, button) {
+                            return *button;
+                        }
+                    }
+                }
+            }
+            Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
+        }
+    }
+
+    async fn wait_and_filter_simultaneous_button_presses<const N: usize, const M: usize>(
+        &mut self,
+        buttons_to_wait_for: &[[AwaitableButtons; N]; M],
+    ) -> usize {
+        loop {
+            match self.next_message_pure().await {
+                report => {
+                    for (i, buttons) in buttons_to_wait_for.iter().enumerate() {
+                        if buttons
+                            .iter()
+                            .all(|button| is_awaitable_button_pressed(&report, button))
+                        {
+                            return i;
+                        }
+                    }
+                }
+            }
+            Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
+        }
+    }
+}
+
+fn is_awaitable_button_pressed(report: &GcReport, button_to_wait_for: &AwaitableButtons) -> bool {
+    match button_to_wait_for {
+        AwaitableButtons::A => report.buttons_1.button_a,
+        AwaitableButtons::B => report.buttons_1.button_b,
+        AwaitableButtons::X => report.buttons_1.button_x,
+        AwaitableButtons::Y => report.buttons_1.button_y,
+        AwaitableButtons::Up => report.buttons_1.dpad_up,
+        AwaitableButtons::Down => report.buttons_1.dpad_down,
+        AwaitableButtons::Left => report.buttons_1.dpad_left,
+        AwaitableButtons::Right => report.buttons_1.dpad_right,
+        AwaitableButtons::Start => report.buttons_2.button_start,
+        AwaitableButtons::L => report.buttons_2.button_l,
+        AwaitableButtons::R => report.buttons_2.button_r,
+    }
+}
+
+#[embassy_executor::task]
+pub async fn config_task() {
+    let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
+
+    loop {
+        gcc_subscriber
+            .wait_for_simultaneous_button_presses(&CONFIG_MODE_ENTRY_COMBO)
+            .await;
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index e49a382..4974835 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,6 @@
 
 #![no_std]
 #![no_main]
-mod calibrate;
 mod config;
 mod filter;
 mod gcc_hid;
@@ -12,7 +11,7 @@ mod helpers;
 mod input;
 mod stick;
 
-use calibrate::calibration_loop;
+use config::config_task;
 use config::ControllerConfig;
 use defmt::{debug, info};
 use embassy_executor::Executor;
@@ -119,6 +118,7 @@ fn main() -> ! {
     // pwm_brake.set_counter(255);
 
     executor0.run(|spawner| {
+        spawner.spawn(config_task()).unwrap();
         spawner
             .spawn(update_stick_states_task(
                 spi,