diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1d1628e52fc224afacf2d2e1ca08113e5db7ee4e..9c873aeb4447281ce43b7c82c2a5864b3853ea54 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,7 @@ add_executable(groundstation
     src/shared/Modules/Mavlink/MavlinkCodec.cpp
     src/shared/Modules/Mavlink/SerialMavlinkModule.cpp
     src/shared/Modules/Mavlink/UdpMavlinkModule.cpp
+    src/shared/Modules/MessageTester/MessageTester.cpp
     src/shared/Modules/OutgoingMessagesViewer/OutgoingMessagesViewerModule.cpp
     src/shared/Modules/Splitter/Splitter.cpp
     src/shared/Modules/OrientationVisualizer/OrientationVisualizer.cpp
diff --git a/SkywardHub.pro b/SkywardHub.pro
index 4ac360988926cc0f458ab24622966b477dced6e2..21d94b7df67b4edf1285a1f8653ce611f62d0483 100644
--- a/SkywardHub.pro
+++ b/SkywardHub.pro
@@ -56,6 +56,7 @@ SOURCES += \
     src/shared/Modules/Mavlink/SerialMavlinkModule.cpp \
     src/shared/Modules/Mavlink/UdpMavlinkModule.cpp \
     src/shared/Modules/ValvesViewer/ValvesViewer.cpp \
+    src/shared/Modules/MessageTester/MessageTester.cpp \
     src/shared/Core/EventHandler/EventHandler.cpp \
     src/shared/Core/XmlObject.cpp \
     src/shared/Core/QCustomPlot/QCustomPlot.cpp \
@@ -120,6 +121,7 @@ HEADERS += \
     src/shared/Modules/ModulesList.h \
     src/shared/Modules/ValvesViewer/ValvesViewer.h \
     src/shared/Modules/ValvesViewer/ValvesList.h \
+    src/shared/Modules/MessageTester/MessageTester.h \
     src/shared/Core/EventHandler/EventHandler.h \
     src/shared/Core/QCustomPlot/QCustomPlot.h \
     src/shared/Core/MessageBroker/MessageBroker.h \
diff --git a/src/shared/Modules/CommandPad/MessagesList.h b/src/shared/Modules/CommandPad/MessagesList.h
index c8f583a4c3e26c0db2fa64729d98eecc9600e9f7..f91ab9fcfa889f828e5ebb375e412ae66a447b1e 100644
--- a/src/shared/Modules/CommandPad/MessagesList.h
+++ b/src/shared/Modules/CommandPad/MessagesList.h
@@ -123,6 +123,8 @@ const QMap<QString, int> servosList{
     {"FILLING_VALVE", FILLING_VALVE},
     {"DISCONNECT_SERVO", DISCONNECT_SERVO}};
 
+const mavlink_message_info_t messageInfo[] = MAVLINK_MESSAGE_INFO;
+
 inline void fillMessagesMap(QMap<QString, MessageFormElement *> &formElements)
 {
     MessageFormElement *element;
diff --git a/src/shared/Modules/MessageTester/MessageTester.cpp b/src/shared/Modules/MessageTester/MessageTester.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bceb2b37c3623e35a2b94deece3aa99681705dd6
--- /dev/null
+++ b/src/shared/Modules/MessageTester/MessageTester.cpp
@@ -0,0 +1,136 @@
+/*
+ * This file is part of Skyward Hub.
+ *
+ * Skyward Hub is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * Skyward Hub is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * Skyward Hub. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "MessageTester.h"
+
+#include <Core/MessageBroker/MessageBroker.h>
+#include <Modules/CommandPad/MessagesList.h>
+
+#include <QDebug>
+#include <QPushButton>
+#include <QTime>
+#include <QVBoxLayout>
+#include <iostream>
+
+MessageTester::MessageTester(QWidget *parent) : DefaultModule(parent)
+{
+    setupUi();
+    defaultContextMenuSetup();
+}
+
+MessageTester::~MessageTester()
+{
+    delete messagesListComboBox;
+    for (auto key : formElements.keys())
+        delete formElements[key];
+}
+
+QWidget *MessageTester::toWidget() { return this; }
+
+XmlObject MessageTester::toXmlObject()
+{
+    XmlObject obj = XmlObject(getName(ModuleId::MESSAGE_TESTER));
+
+    auto key = messagesListComboBox->currentText();
+    if (formElements.contains(key))
+    {
+        obj.addAttribute("message_id", key);
+        formElements[key]->toXmlObject(obj);
+    }
+
+    return obj;
+}
+
+void MessageTester::fromXmlObject(const XmlObject &obj)
+{
+    if (obj.getObjectName() == getName(ModuleId::MESSAGE_TESTER))
+    {
+        auto key = obj.getAttribute("message_id");
+
+        if (formElements.contains(key))
+        {
+            formElements[key]->fromXmlObject(obj);
+            messagesListComboBox->setCurrentText(key);
+        }
+    }
+}
+
+void MessageTester::setupUi()
+{
+    QVBoxLayout *outerLayout = new QVBoxLayout;
+    outerLayout->addStretch();
+
+    messagesListComboBox = new QComboBox;
+    messagesListComboBox->setSizePolicy(
+        QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
+
+    const mavlink_message_info_t messages[] = MAVLINK_MESSAGE_INFO;
+    for (int i = 100; i < 256; i++)
+    {
+        mavlink_message_info_t message = messages[i];
+        if (strcmp(message.name, "EMPTY") != 0)
+        {
+            messagesListComboBox->addItem(message.name);
+        }
+    }
+
+    outerLayout->addWidget(messagesListComboBox);
+
+    QGridLayout *formGridLayout = new QGridLayout;
+    outerLayout->addLayout(formGridLayout);
+
+    QPushButton *sendButton = new QPushButton("Send");
+    outerLayout->addWidget(sendButton);
+
+    setLayout(outerLayout);
+
+    MessagesList::fillMessagesMap(formElements);
+
+    // Set default value
+    currentMessage = "ACK_TM";
+    messagesListComboBox->setCurrentText(currentMessage);
+    formElements[currentMessage]->applyToGridLayout(formGridLayout);
+
+
+    connect(messagesListComboBox, &QComboBox::currentTextChanged, this,
+            [=](QString key)
+            {
+                if (formElements.contains(key))
+                {
+                    formElements[currentMessage]->removeFromGridLayout(
+                        formGridLayout);
+                    formElements[key]->applyToGridLayout(formGridLayout);
+                    currentMessage = key;
+                }
+            });
+
+    connect(sendButton, &QPushButton::clicked,
+            [=]()
+            {
+                auto key = messagesListComboBox->currentText();
+                if (formElements.contains(key))
+                {
+                    auto message = formElements[key]->prepareMessage(key);
+                    message.setTopic(Topic(QString("Mav/") + key));
+                    auto timestamp = Field(
+                        (uint64_t)QTime::currentTime().msecsSinceStartOfDay());
+                    message.setField("timestamp", timestamp);
+                    getCore()->getMessageBroker()->publish(message);
+                }
+            });
+}
diff --git a/src/shared/Modules/MessageTester/MessageTester.h b/src/shared/Modules/MessageTester/MessageTester.h
new file mode 100644
index 0000000000000000000000000000000000000000..ebe6e6ced6d79f913ab72e05b33f2ec7d21e188e
--- /dev/null
+++ b/src/shared/Modules/MessageTester/MessageTester.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of Skyward Hub.
+ *
+ * Skyward Hub is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * Skyward Hub is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * Skyward Hub. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <Modules/CommandPad/MessageFormElement.h>
+#include <Modules/DefaultModule/DefaultModule.h>
+
+#include <tuple>
+
+class MessageTester : public DefaultModule
+{
+    Q_OBJECT
+
+public:
+    explicit MessageTester(QWidget* parent = nullptr);
+    ~MessageTester();
+
+    QWidget* toWidget() override;
+
+    XmlObject toXmlObject() override;
+    void fromXmlObject(const XmlObject& xmlObject) override;
+
+private:
+    void setupUi();
+
+    QString currentMessage;
+    QComboBox* messagesListComboBox;
+    QMap<QString, MessageFormElement*> formElements;
+};
diff --git a/src/shared/Modules/ModuleInfo.h b/src/shared/Modules/ModuleInfo.h
index 8c7aba6c097381cc535ecc26286ad4e8f7bbb0a5..be517f6e2c37a0c31ced7ca70d2a82aaad9b20f6 100644
--- a/src/shared/Modules/ModuleInfo.h
+++ b/src/shared/Modules/ModuleInfo.h
@@ -35,6 +35,7 @@ enum ModuleId
     INCOMINGMESSAGESVIEWER,
     SERIAL_MAVLINK,
     UDP_MAVLINK,
+    MESSAGE_TESTER,
     FILESTREAM,
     ORIENTATION_VISUALIZER,
     MAINSTATEVIEWER,
diff --git a/src/shared/Modules/ModulesList.cpp b/src/shared/Modules/ModulesList.cpp
index f5332061f37fe6b5a20d27baaca746f50b3231e7..103cf06f5d0e2356331141c75db1acd032c57ad0 100644
--- a/src/shared/Modules/ModulesList.cpp
+++ b/src/shared/Modules/ModulesList.cpp
@@ -29,6 +29,7 @@
 #include <Modules/MainStateViewer/MainStateViewer.h>
 #include <Modules/Mavlink/SerialMavlinkModule.h>
 #include <Modules/Mavlink/UdpMavlinkModule.h>
+#include <Modules/MessageTester/MessageTester.h>
 #include <Modules/OrientationVisualizer/OrientationVisualizer.h>
 #include <Modules/OutgoingMessagesViewer/OutgoingMessagesViewerModule.h>
 #include <Modules/PayloadStateViewer/PayloadStateViewer.h>
@@ -66,6 +67,12 @@ void ModulesList::createModuleList()
     commandPad.setFactory([]() { return new CommandPad(); });
     addModuleInfo(commandPad);
 
+    ModuleInfo messageTester(ModuleId::MESSAGE_TESTER, "MessageTester",
+                             ModuleCategory::UTILITY);
+    messageTester.setFactory([]() { return new MessageTester(); });
+    messageTester.addModuleSourceFiles("Modules/MessageTester/");
+    addModuleInfo(messageTester);
+
     ModuleInfo compactCommandPad(ModuleId::COMPACT_COMMAND_PAD,
                                  "compact_command_pad",
                                  ModuleCategory::UTILITY);