diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake
index 94e87cbbc5a59e48ce8b63e9cece67a186dd1d04..1e77239ce7e8cf8273640307e8f2b78a99e37daa 100644
--- a/cmake/dependencies.cmake
+++ b/cmake/dependencies.cmake
@@ -122,4 +122,5 @@ set(GS_COMPUTER
     src/boards/Gs/Ports/Serial.cpp
     src/boards/Gs/Radio/Radio.cpp
     src/boards/Gs/Radio/RadioStatus.cpp
+    src/boards/Gs/Hub.cpp
 )
\ No newline at end of file
diff --git a/src/boards/Gs/Hub.cpp b/src/boards/Gs/Hub.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..da8f1b649c761a6f1699cf6011311708549cb9c4
--- /dev/null
+++ b/src/boards/Gs/Hub.cpp
@@ -0,0 +1,55 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Davide Mor
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "Hub.h"
+
+#include <Gs/Radio/Radio.h>
+#include <Gs/Radio/RadioStatus.h>
+#include <Gs/Ports/Serial.h>
+
+using namespace Gs;
+using namespace Boardcore;
+
+void Hub::dispatchOutgoingMsg(const mavlink_message_t& msg) {
+    RadioStatus *status = ModuleManager::getInstance().get<RadioStatus>();
+
+    // TODO: Dispatch to correct radio using mavlink ids
+
+    if (status->isMainRadioPresent())
+    {
+        RadioMain *radio = ModuleManager::getInstance().get<RadioMain>();
+        radio->sendMsg(msg);
+    }
+
+    if (status->isPayloadRadioPresent())
+    {
+        RadioPayload *radio = ModuleManager::getInstance().get<RadioPayload>();
+        radio->sendMsg(msg);
+    }
+}
+
+void Hub::dispatchIncomingMsg(const mavlink_message_t& msg) {
+    Serial* serial = ModuleManager::getInstance().get<Serial>();
+    serial->sendMsg(msg);
+
+    // TODO: Add UDP dispatch
+}
\ No newline at end of file
diff --git a/src/boards/Gs/Hub.h b/src/boards/Gs/Hub.h
new file mode 100644
index 0000000000000000000000000000000000000000..d229b05b4fbf1e5076c638f44931113bbc33834a
--- /dev/null
+++ b/src/boards/Gs/Hub.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2023 Skyward Experimental Rocketry
+ * Author: Davide Mor
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <utils/ModuleManager/ModuleManager.hpp>
+#include <common/Mavlink.h>
+
+namespace Gs {
+
+/**
+ * @brief Central hub connecting all outgoing and ingoing modules.
+*/
+class Hub : public Boardcore::Module {
+public:
+    Hub() {}
+
+    /**
+     * @brief Dispatch to the correct interface and outgoing packet (gs -> rocket).
+    */
+    void dispatchOutgoingMsg(const mavlink_message_t& msg);
+
+    /**
+     * @brief Dispatch to the correct interface and incoming packet (rocket -> gs).
+    */
+    void dispatchIncomingMsg(const mavlink_message_t& msg);
+};
+
+}
\ No newline at end of file
diff --git a/src/boards/Gs/Ports/Serial.cpp b/src/boards/Gs/Ports/Serial.cpp
index e8ea16ca04fe76634f35a94e54e9d6f0cd6f199f..470de562736485720e2fe871b7b7ced7eb77a0ff 100644
--- a/src/boards/Gs/Ports/Serial.cpp
+++ b/src/boards/Gs/Ports/Serial.cpp
@@ -22,6 +22,7 @@
 
 #include "Serial.h"
 
+#include <Gs/Hub.h>
 #include <Gs/Radio/Radio.h>
 #include <Gs/Radio/RadioStatus.h>
 
@@ -47,24 +48,6 @@ void Serial::sendMsg(const mavlink_message_t &msg)
     serial->writeBlock(msg_buf, msg_len, 0);
 }
 
-void Serial::handleMsg(const mavlink_message_t &msg)
-{
-    // TODO:
-    RadioStatus *status = ModuleManager::getInstance().get<RadioStatus>();
-
-    if (status->isMainRadioPresent())
-    {
-        RadioMain *radio = ModuleManager::getInstance().get<RadioMain>();
-        radio->sendMsg(msg);
-    }
-
-    if (status->isPayloadRadioPresent())
-    {
-        RadioPayload *radio = ModuleManager::getInstance().get<RadioPayload>();
-        radio->sendMsg(msg);
-    }
-}
-
 void Serial::run()
 {
     mavlink_message_t msg;
@@ -83,7 +66,9 @@ void Serial::run()
 
             if (parse_result == 1)
             {
-                handleMsg(msg);
+                // Dispatch the message through the hub.
+                ModuleManager::getInstance().get<Hub>()->dispatchOutgoingMsg(
+                    msg);
             }
         }
     }
diff --git a/src/boards/Gs/Ports/Serial.h b/src/boards/Gs/Ports/Serial.h
index ef2413b7b74910c4c5c8fcd0609876857a758f8a..f9aeded0585e225226b70920af36f9fd25c88b00 100644
--- a/src/boards/Gs/Ports/Serial.h
+++ b/src/boards/Gs/Ports/Serial.h
@@ -32,6 +32,9 @@
 namespace Gs
 {
 
+/**
+ * @brief Class responsible for UART communication.
+*/
 class Serial : public Boardcore::Module, private Boardcore::ActiveObject
 {
 public:
@@ -51,11 +54,6 @@ protected:
     void run() override;
 
 private:
-    /**
-     * @brief Called internally when a message is received.
-     */
-    void handleMsg(const mavlink_message_t& msg);
-
     miosix::FastMutex mutex;
 };
 
diff --git a/src/boards/Gs/Radio/Radio.cpp b/src/boards/Gs/Radio/Radio.cpp
index 5ae0252bafa27811521a13bc6d4b440166951a58..68840e52e15df7d1d10c179a0bee10c6b4109322 100644
--- a/src/boards/Gs/Radio/Radio.cpp
+++ b/src/boards/Gs/Radio/Radio.cpp
@@ -26,6 +26,7 @@
 #include <Gs/Ports/Serial.h>
 #include <Gs/Radio/RadioStatus.h>
 #include <radio/SX1278/SX1278Frontends.h>
+#include <Gs/Hub.h>
 
 using namespace miosix;
 using namespace Gs;
@@ -173,10 +174,8 @@ bool RadioPayload::start()
 
 void RadioBase::handleMsg(const mavlink_message_t& msg)
 {
-    // TODO:
-
-    Serial* serial = ModuleManager::getInstance().get<Serial>();
-    serial->sendMsg(msg);
+    // Dispatch the message through the hub.
+    ModuleManager::getInstance().get<Hub>()->dispatchIncomingMsg(msg);
 
     if (isEndOfTransmissionPacket(msg))
     {
diff --git a/src/boards/Gs/Radio/RadioStatus.h b/src/boards/Gs/Radio/RadioStatus.h
index 90364a7e1b755127ff75eda98d773d24cf6e5ad0..6a38a9d32f69078823fa48bf575c028ceec5e372 100644
--- a/src/boards/Gs/Radio/RadioStatus.h
+++ b/src/boards/Gs/Radio/RadioStatus.h
@@ -27,12 +27,22 @@
 namespace Gs
 {
 
+/**
+ * @brief Class responsible for keeping track of radio status and metrics.
+*/
 class RadioStatus : public Boardcore::Module
 {
 public:
     RadioStatus() {}
 
+    /**
+     * @brief Check wether the main radio was found during boot.
+    */
     bool isMainRadioPresent();
+
+    /**
+     * @brief Check wether the payload radio was found during boot.
+    */
     bool isPayloadRadioPresent();
 
     void setMainRadioPresent(bool present);