diff --git a/CMakeLists.txt b/CMakeLists.txt
index dc328c0cfd1743968caf820770e1d8298a78a9ce..64a4c4a4bde7bc9bcdddddf8ba69416bfde075d8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -86,6 +86,7 @@ add_executable(groundstation
     src/shared/Modules/ValuesConverterViewer/ValuesViewerConfigPanel.cpp
     src/shared/Modules/ModuleInfo.cpp
     src/shared/Modules/ModulesList.cpp
+    src/shared/Modules/ValvesViewer/ValvesViewer.cpp
     src/entrypoints/groundstation/application.qrc
     src/entrypoints/groundstation/main.cpp
 )
diff --git a/SkywardHub.pro b/SkywardHub.pro
index 479c81743a6f0a41859150636461780bfebdb5a6..ba8d17d250db489ab7a22b771b5bb0af64ef8008 100644
--- a/SkywardHub.pro
+++ b/SkywardHub.pro
@@ -48,6 +48,7 @@ SOURCES += \
     src/shared/Modules/Mavlink/MavlinkReader.cpp \
     src/shared/Modules/Mavlink/MavlinkCommandAdapter.cpp \
     src/shared/Modules/Mavlink/MavlinkModule.cpp \
+    src/shared/Modules/ValvesViewer/ValvesViewer.cpp \
     src/shared/Core/EventHandler/EventHandler.cpp \
     src/shared/Core/XmlObject.cpp \
     src/shared/Core/QCustomPlot/QCustomPlot.cpp \
@@ -102,6 +103,8 @@ HEADERS += \
     src/shared/Modules/Mavlink/MavlinkModule.h \
     src/shared/Modules/Mavlink/MavlinkCommandAdapter.h \
     src/shared/Modules/ModulesList.h \
+    src/shared/Modules/ValvesViewer/ValvesViewer.h \
+    src/shared/Modules/ValvesViewer/ValvesList.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/ModuleInfo.h b/src/shared/Modules/ModuleInfo.h
index f300c0acb3eaf602ea42e66a8537a593ef9265b8..7590775044e4ec6fa5d4df6f4556f6a1f76d68df 100644
--- a/src/shared/Modules/ModuleInfo.h
+++ b/src/shared/Modules/ModuleInfo.h
@@ -41,6 +41,7 @@ enum ModuleId
     MODULETEST,
     TABS,
     FILTER_SELECTOR,
+    VALVESVIEWER,
 };
 
 enum ModuleCategory
diff --git a/src/shared/Modules/ModulesList.cpp b/src/shared/Modules/ModulesList.cpp
index d32058bd97cd0c686595a7f70135463fff2dc7a4..666fadd812c9e13d86d99dbd806118073d1c8dcc 100644
--- a/src/shared/Modules/ModulesList.cpp
+++ b/src/shared/Modules/ModulesList.cpp
@@ -35,6 +35,7 @@
 #include <Modules/Test/TestModule.h>
 #include <Modules/TimerController/TimerControllerModule.h>
 #include <Modules/ValuesConverterViewer/ValuesConverterViewerModule.h>
+#include <Modules/ValvesViewer/ValvesViewer.h>
 
 void ModulesList::createModuleList()
 {
@@ -130,6 +131,12 @@ void ModulesList::createModuleList()
     ModuleInfo tabs(ModuleId::TABS, "Tabs", ModuleCategory::UTILITY);
     tabs.setFactory([]() { return new TabsModule(); });
     addModuleInfo(tabs);
+
+    ModuleInfo valvesViewer(ModuleId::VALVESVIEWER, "ValvesViewer",
+                            ModuleCategory::DATAVISUAL);
+    valvesViewer.setFactory([]() { return new ValvesViewer(); });
+    valvesViewer.addModuleSourceFiles("Modules/ValvesViewer/");
+    addModuleInfo(valvesViewer);
 }
 
 // ____________________________________________________________________________________________
diff --git a/src/shared/Modules/ValvesViewer/ValvesList.h b/src/shared/Modules/ValvesViewer/ValvesList.h
new file mode 100644
index 0000000000000000000000000000000000000000..16fe15efa5b171177872a611b563822cd316ce9e
--- /dev/null
+++ b/src/shared/Modules/ValvesViewer/ValvesList.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 <QList>
+#include <QMap>
+#include <QString>
+
+namespace ValvesList
+{
+
+// Valves
+enum class Valve : int
+{
+    FILLING = 0,
+    RELEASE,
+    VENTING,
+    MAIN,
+};
+
+// Groundstation labels
+static const QList<QString> valvesLabels{
+    "FILLING",
+    "RELEASE",
+    "VENTING",
+    "MAIN",
+};
+
+}  // namespace ValvesList
\ No newline at end of file
diff --git a/src/shared/Modules/ValvesViewer/ValvesViewer.cpp b/src/shared/Modules/ValvesViewer/ValvesViewer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..71169955cafc25b042e34414f97448a9fd6a6df2
--- /dev/null
+++ b/src/shared/Modules/ValvesViewer/ValvesViewer.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 "ValvesViewer.h"
+
+#include <Components/FilterSelector/FilterSelector.h>
+#include <Core/MessageBroker/MessageBroker.h>
+
+ValvesViewer::ValvesViewer(QWidget* parent) : DefaultModule(parent)
+{
+    setupUi();
+    defaultContextMenuSetup();
+}
+
+ValvesViewer::~ValvesViewer()
+{
+    getCore()->getMessageBroker()->unsubscribe(filter, this);
+}
+
+QWidget* ValvesViewer::toWidget() { return this; }
+
+XmlObject ValvesViewer::toXmlObject()
+{
+    XmlObject obj(getName(ModuleId::VALVESVIEWER));
+
+    obj.addAttribute("filter", filter.toString());
+
+    return obj;
+}
+
+void ValvesViewer::fromXmlObject(const XmlObject& xmlObject)
+{
+    if (xmlObject.getObjectName() == getName(ModuleId::VALVESVIEWER))
+    {
+        auto filter = Filter::fromString(xmlObject.getAttribute("filter"));
+        setFilter(filter);
+    }
+}
+
+void ValvesViewer::setupUi()
+{
+    outerLayout = new QHBoxLayout;
+    outerLayout->setContentsMargins(0, 0, 0, 0);
+    outerLayout->setSpacing(0);
+
+    for (auto labelText : ValvesList::valvesLabels)
+    {
+        QLabel* label = new QLabel;
+        label->setText(labelText);
+        label->setAlignment(Qt::AlignCenter);
+        label->setContentsMargins(4, 4, 4, 4);
+        outerLayout->addWidget(label);
+    }
+
+    setLayout(outerLayout);
+}
+
+void ValvesViewer::addCustomActionsToMenu()
+{
+    QAction* action = new QAction("Choose topic and field");
+    connect(action, &QAction::triggered, this,
+            &ValvesViewer::onConfigureClicked);
+
+    addActionToMenu(action);
+}
+
+void ValvesViewer::onConfigureClicked()
+{
+    FilterSelector::selectFilter(
+        filter, [this](const Filter& newFilter) { setFilter(newFilter); });
+}
+
+void ValvesViewer::setFilter(const Filter& newFilter)
+{
+    getCore()->getMessageBroker()->unsubscribe(filter, this);
+    getCore()->getMessageBroker()->subscribe(
+        newFilter, this,
+        [this](const Message& message, const Filter& filter)
+        { onMsgReceived(message); });
+    filter = newFilter;
+}
+
+void ValvesViewer::onMsgReceived(const Message& msg)
+{
+    // Couple the valves with their own state
+    QMap<ValvesList::Valve, uint64_t> map{
+        {ValvesList::Valve::FILLING,
+         msg.getField("filling_valve_state").getUnsignedInteger()},
+        {ValvesList::Valve::MAIN,
+         msg.getField("main_valve_state").getUnsignedInteger()},
+        {ValvesList::Valve::RELEASE,
+         msg.getField("release_valve_state").getUnsignedInteger()},
+        {ValvesList::Valve::VENTING,
+         msg.getField("venting_valve_state").getUnsignedInteger()}};
+
+    // Define the "active" and the "not active" look
+    QString baseStyle =
+        "border-bottom-width:1px;border-left-width:1px;border-top-width:"
+        "1px;border-radius:0;";
+    QString completedStyle = "background-color:green;" + baseStyle;
+    QString errorStyle     = "background-color:red;" + baseStyle;
+
+    // Iterate over the valves to assign them the correct look
+    for (auto i = map.cbegin(), end = map.cend(); i != end; i++)
+    {
+        if (i.value() == 1)
+        {
+            // The valve is opened
+            auto label =
+                outerLayout->itemAt(static_cast<int>(i.key()))->widget();
+            label->setStyleSheet(completedStyle);
+        }
+        else
+        {
+            // The valve is closed
+            auto label =
+                outerLayout->itemAt(static_cast<int>(i.key()))->widget();
+            label->setStyleSheet(errorStyle);
+        }
+    }
+}
diff --git a/src/shared/Modules/ValvesViewer/ValvesViewer.h b/src/shared/Modules/ValvesViewer/ValvesViewer.h
new file mode 100644
index 0000000000000000000000000000000000000000..89fa9e7a0860ce837f0c0ae03d013641fcae836f
--- /dev/null
+++ b/src/shared/Modules/ValvesViewer/ValvesViewer.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <Core/Message/Filter.h>
+#include <Core/Message/Message.h>
+#include <Modules/DefaultModule/DefaultModule.h>
+
+#include <QLabel>
+#include <QWidget>
+
+#include "ValvesList.h"
+
+class ValvesViewer : public DefaultModule
+{
+    Q_OBJECT
+
+public:
+    explicit ValvesViewer(QWidget* parent = nullptr);
+    ~ValvesViewer();
+
+    QWidget* toWidget() override;
+
+    XmlObject toXmlObject() override;
+    void fromXmlObject(const XmlObject& xmlObject) override;
+
+private:
+    void setupUi();
+    void addCustomActionsToMenu() override;
+    void onConfigureClicked();
+    void setFilter(const Filter& filter);
+    void onMsgReceived(const Message& msg);
+
+    QHBoxLayout* outerLayout;
+
+    Filter filter;
+    ValvesList::Valve currentState;
+};