diff --git a/.vscode/settings.json b/.vscode/settings.json
index d3abb169e8483de9d90cc454edb48266e06af854..fcde4c615a2f857fa3e7e9c86f802b52292760a0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -152,6 +152,7 @@
         "editor.defaultFormatter": "redhat.vscode-xml"
     },
     "cSpell.words": [
+        "cppcheck",
         "entrypoints",
         "Mavlink",
         "Plottables",
diff --git a/src/shared/Components/FilterSelector/FilterSelector.cpp b/src/shared/Components/FilterSelector/FilterSelector.cpp
index 6439081dcbd496352af3043dfb416f62df97db6c..969ce367236e590118ebb3e638b0e876da92ba29 100644
--- a/src/shared/Components/FilterSelector/FilterSelector.cpp
+++ b/src/shared/Components/FilterSelector/FilterSelector.cpp
@@ -2,6 +2,7 @@
 
 #include <Modules/Mavlink/MavlinkVersionHeader.h>
 
+#include <QDebug>
 #include <QLabel>
 #include <QPushButton>
 
@@ -12,26 +13,14 @@ void FilterSelector::setupUi()
 
     QVBoxLayout *layout = new QVBoxLayout;
 
-    QHBoxLayout *topicLayout = new QHBoxLayout;
+    QComboBox *topic = new QComboBox;
+    topic->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+    topic->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+    topic->addItems(messages.keys());
+    layout->addWidget(topic);
 
-    QLabel *mainTopic = new QLabel;
-    mainTopic->setText("Mav");
-    topicLayout->addWidget(mainTopic);
-
-    QLabel *topicSeparator = new QLabel;
-    topicSeparator->setText("/");
-    topicLayout->addWidget(topicSeparator);
-
-    QComboBox *subTopic = new QComboBox;
-    subTopic->setSizeAdjustPolicy(QComboBox::AdjustToContents);
-    subTopic->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
-    subTopic->addItems(messages.keys());
-    topicLayout->addWidget(subTopic, 1);
-
-    layout->addLayout(topicLayout);
-
-    fieldsLayout = new QVBoxLayout;
-    layout->addLayout(fieldsLayout);
+    fieldsWidget = new QListWidget;
+    layout->addWidget(fieldsWidget);
 
     QHBoxLayout *buttons = new QHBoxLayout;
     QPushButton *cancel  = new QPushButton("Cancel");
@@ -41,17 +30,32 @@ void FilterSelector::setupUi()
     layout->addLayout(buttons);
 
     // Set default message
-    currentMessage = messages.keys().at(0);
-    subTopic->setCurrentText(currentMessage);
-    fieldsLayout->addWidget(fields[currentMessage]);
+    if (filter.getTopic().toString() != Topic().toString())
+    {
+        currentMessage = filter.getTopic().toString();
+        topic->setCurrentText(filter.getTopic().toString());
+    }
+    else
+    {
+        currentMessage = messages.keys().at(0);
+        topic->setCurrentText(messages.keys().at(0));
+    }
 
-    connect(subTopic, &QComboBox::currentTextChanged, this,
+    connect(topic, &QComboBox::currentTextChanged, this,
             [=](QString key)
             {
                 if (messages.contains(key))
                 {
-                    fieldsLayout->removeWidget(fields[currentMessage]);
-                    fieldsLayout->addWidget(fields[key]);
+                    fieldsWidget->clear();
+
+                    fieldsWidget->addItems(messages[key]);
+                    for (int i = 0; i < fieldsWidget->count(); i++)
+                    {
+                        auto item = fieldsWidget->item(i);
+                        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
+                        item->setCheckState(Qt::Unchecked);
+                    }
+
                     currentMessage = key;
                 }
             });
@@ -59,8 +63,14 @@ void FilterSelector::setupUi()
     connect(select, &QPushButton::clicked,
             [=]()
             {
-                Filter filter(Topic("Mav/" + subTopic->currentText()));
-                filter.addField(fields[currentMessage]->currentText());
+                Filter filter(Topic(topic->currentText()));
+
+                for (int i = 0; i < fieldsWidget->count(); i++)
+                {
+                    auto item = fieldsWidget->item(i);
+                    if (item->checkState())
+                        filter.addField(item->text());
+                }
 
                 emit filterSelected(filter);
                 deleteLater();
@@ -110,15 +120,7 @@ void FilterSelector::parseMessagesList()
             for (unsigned int ii = 0; ii < messagesList[i].num_fields; ii++)
                 fields.append(messagesList[i].fields[ii].name);
 
-            messages[messagesList[i].name] = fields;
+            messages[QString("Mav/") + messagesList[i].name] = fields;
         }
     }
-
-    // Prepare fields combo boxes
-    for (auto messageName : messages.keys())
-    {
-        auto comboBox = new QComboBox();
-        comboBox->addItems(messages[messageName]);
-        fields[messageName] = comboBox;
-    }
 }
diff --git a/src/shared/Components/FilterSelector/FilterSelector.h b/src/shared/Components/FilterSelector/FilterSelector.h
index f1586d4c930ea85d6168dd1d5f1452b81066e58e..3b5ea4d122220596a7e3b6cc7617d0cdc5cf17dd 100644
--- a/src/shared/Components/FilterSelector/FilterSelector.h
+++ b/src/shared/Components/FilterSelector/FilterSelector.h
@@ -5,6 +5,7 @@
 #include <QBoxLayout>
 #include <QComboBox>
 #include <QList>
+#include <QListWidget>
 #include <QMap>
 #include <QString>
 
@@ -28,10 +29,9 @@ private:
     Filter filter;
 
     QString currentMessage;
-    QVBoxLayout* fieldsLayout;
+    QListWidget* fieldsWidget;
 
     QMap<QString, QList<QString>> messages;
-    QMap<QString, QComboBox*> fields;
 
 signals:
     void filterSelected(const Filter&);
diff --git a/src/shared/Components/SubscriptionsPanel/SubscriptionsPanel.cpp b/src/shared/Components/SubscriptionsPanel/SubscriptionsPanel.cpp
index 90def45996b75f8618169f18dd4ad58dc83a3edb..9d1b463e70b0806d19618facb518851fd852ae79 100644
--- a/src/shared/Components/SubscriptionsPanel/SubscriptionsPanel.cpp
+++ b/src/shared/Components/SubscriptionsPanel/SubscriptionsPanel.cpp
@@ -33,7 +33,7 @@ SubscriptionsPanel::SubscriptionsPanel(const QList<Filter>& filters)
 void SubscriptionsPanel::addFilter(const Filter& filter)
 {
     filtersList->addItem(new QListWidgetItem(filter.toString()));
-    emit topicAndFieldFilterAdded(filter);
+    emit filterAdded(filter);
 }
 
 void SubscriptionsPanel::setupUi()
@@ -41,6 +41,8 @@ void SubscriptionsPanel::setupUi()
     QVBoxLayout* layout = new QVBoxLayout;
 
     filtersList = new QListWidget;
+    filtersList->setSelectionBehavior(QAbstractItemView::SelectItems);
+    filtersList->setSelectionMode(QAbstractItemView::SingleSelection);
     layout->addWidget(filtersList);
 
     QHBoxLayout* buttonsLayout = new QHBoxLayout;
@@ -48,6 +50,9 @@ void SubscriptionsPanel::setupUi()
     QPushButton* addButton = new QPushButton;
     addButton->setText("Add filter");
     buttonsLayout->addWidget(addButton);
+    QPushButton* editButton = new QPushButton;
+    editButton->setText("Edit filter");
+    buttonsLayout->addWidget(editButton);
     QPushButton* removeButton = new QPushButton;
     removeButton->setText("Remove filter");
     buttonsLayout->addWidget(removeButton);
@@ -59,12 +64,27 @@ void SubscriptionsPanel::setupUi()
     connect(addButton, &QPushButton::clicked, this,
             [&]()
             {
-                FilterSelector::selectFilter(
-                    [&](const Filter& filter)
-                    {
-                        qDebug() << "New filter:" << filter.toString();
-                        addFilter(filter);
-                    });
+                FilterSelector::selectFilter([&](const Filter& filter)
+                                             { addFilter(filter); });
+            });
+    connect(editButton, &QPushButton::clicked, this,
+            [&]()
+            {
+                if (filtersList->selectedItems().size() > 0)
+                {
+                    auto item = filtersList->selectedItems().at(0);
+
+                    FilterSelector::selectFilter(
+                        Filter::fromString(item->text()),
+                        [=](const Filter& newFilter)
+                        {
+                            if (Filter::fromString(item->text()) != newFilter)
+                            {
+                                removeSubscription(item);
+                                addFilter(newFilter);
+                            }
+                        });
+                }
             });
     connect(removeButton, &QPushButton::clicked, this,
             [&]()
@@ -76,6 +96,6 @@ void SubscriptionsPanel::setupUi()
 
 void SubscriptionsPanel::removeSubscription(QListWidgetItem* item)
 {
-    emit topicAndFieldFilterRemoved(Filter::fromString(item->text()));
+    emit filterRemoved(Filter::fromString(item->text()));
     delete item;
 }
diff --git a/src/shared/Components/SubscriptionsPanel/SubscriptionsPanel.h b/src/shared/Components/SubscriptionsPanel/SubscriptionsPanel.h
index 1fdff87822a011688d54ff2506c4072671d8b584..f39b186758d5962c11e912c35a6d7536904808bd 100644
--- a/src/shared/Components/SubscriptionsPanel/SubscriptionsPanel.h
+++ b/src/shared/Components/SubscriptionsPanel/SubscriptionsPanel.h
@@ -17,8 +17,8 @@ public:
     void addFilter(const Filter& filter);
 
 signals:
-    void topicAndFieldFilterAdded(const Filter&);
-    void topicAndFieldFilterRemoved(const Filter&);
+    void filterAdded(const Filter& filter);
+    void filterRemoved(const Filter& filter);
 
 private:
     void setupUi();
diff --git a/src/shared/Core/Message/Field.cpp b/src/shared/Core/Message/Field.cpp
index 23f42935d26e1bf8bb5938b42165d1bef459f50a..8324a96219d621dc7f0d67c7ac1e177702500c5d 100644
--- a/src/shared/Core/Message/Field.cpp
+++ b/src/shared/Core/Message/Field.cpp
@@ -40,6 +40,10 @@ int64_t Field::getInteger() const
 {
     if (type == Type::INT)
         return signedInteger;
+    else if (type == Type::UINT)
+        return unsignedInteger;
+    else if (type == Type::DOUBLE)
+        return floatingPoint;
     else
         return 0;
 }
@@ -48,6 +52,10 @@ uint64_t Field::getUnsignedInteger() const
 {
     if (type == Type::UINT)
         return unsignedInteger;
+    else if (type == Type::INT)
+        return signedInteger;
+    else if (type == Type::DOUBLE)
+        return floatingPoint;
     else
         return 0;
 }
@@ -56,18 +64,14 @@ double Field::getDouble() const
 {
     if (type == Type::DOUBLE)
         return floatingPoint;
+    else if (type == Type::INT)
+        return signedInteger;
+    else if (type == Type::UINT)
+        return unsignedInteger;
     else
         return 0;
 }
 
-QString Field::getString() const
-{
-    if (type == Type::STRING)
-        return string;
-    else
-        return QString();
-}
-
 QString Field::toString() const
 {
     switch (type)
diff --git a/src/shared/Core/Message/Field.h b/src/shared/Core/Message/Field.h
index 50d0af6103534e40cf11204dadd9a65e430dfda6..b1210bb05ab6084d8a383232475f969ab48bebc7 100644
--- a/src/shared/Core/Message/Field.h
+++ b/src/shared/Core/Message/Field.h
@@ -26,18 +26,12 @@ public:
 
     Type getType() const;
 
-    /// @brief Returns value if the field is of type INT
     int64_t getInteger() const;
 
-    /// @brief Returns value if the field is of type UINT
     uint64_t getUnsignedInteger() const;
 
-    /// @brief Returns value if the field is of type DOUBLE
     double getDouble() const;
 
-    /// @brief Returns value if the field is of type STRING
-    QString getString() const;
-
     QString toString() const;
 
 private:
diff --git a/src/shared/Core/Message/Filter.cpp b/src/shared/Core/Message/Filter.cpp
index 85acceeedf25310474764ba2e8fd2a4adfd76d88..4bda55506ecd0915a2032621c2db71084c8632e4 100644
--- a/src/shared/Core/Message/Filter.cpp
+++ b/src/shared/Core/Message/Filter.cpp
@@ -10,14 +10,17 @@ Filter::Filter(Topic topic, QSet<QString> fields) : topic(topic), fields(fields)
 
 bool Filter::operator==(const Filter& toCompare) const
 {
-    return topic == toCompare.topic &&
-           ((fields.isEmpty() && toCompare.fields.isEmpty()) ||
-            (fields == toCompare.fields));
+    return toString() == toCompare.toString();
+}
+
+bool Filter::operator!=(const Filter& toCompare) const
+{
+    return !(*this == toCompare);
 }
 
 bool Filter::operator<(const Filter& toCompare) const
 {
-    return topic < toCompare.topic;
+    return toString() < toCompare.toString();
 }
 
 void Filter::setTopic(Topic topic) { this->topic = topic; }
diff --git a/src/shared/Core/Message/Filter.h b/src/shared/Core/Message/Filter.h
index c086b769a239f34a69e438f885a5d9f762a4c79b..d3f5d0c5c8ae230ac1738542c0da62a2394a331b 100644
--- a/src/shared/Core/Message/Filter.h
+++ b/src/shared/Core/Message/Filter.h
@@ -24,6 +24,7 @@ public:
     Filter(Topic topic, QSet<QString> fields);
 
     bool operator==(const Filter& toCompare) const;
+    bool operator!=(const Filter& toCompare) const;
     bool operator<(const Filter& toCompare) const;
 
     void setTopic(Topic topic);
diff --git a/src/shared/Core/Message/Message.cpp b/src/shared/Core/Message/Message.cpp
index 8da9c4efd3ab81a2308c615519064cd3784186f2..278ef8da323ddbafb17989e957900adadecc0272 100644
--- a/src/shared/Core/Message/Message.cpp
+++ b/src/shared/Core/Message/Message.cpp
@@ -10,7 +10,13 @@ Topic Message::getTopic() const { return topic; }
 
 void Message::setField(QString key, const Field& field) { fields[key] = field; }
 
-Field Message::getField(QString key) const { return fields[key]; }
+Field Message::getField(QString key) const
+{
+    if (fields.contains(key))
+        return fields[key];
+    else
+        return Field();
+}
 
 bool Message::removeField(QString key) { return fields.remove(key); }
 
diff --git a/src/shared/Core/MessageBroker/MessageBroker.cpp b/src/shared/Core/MessageBroker/MessageBroker.cpp
index 9aeb1ac78744b2145239126e1af5ab2d95818e17..226fa93902e6877e872ac97c2b2933be8634ae43 100644
--- a/src/shared/Core/MessageBroker/MessageBroker.cpp
+++ b/src/shared/Core/MessageBroker/MessageBroker.cpp
@@ -2,8 +2,6 @@
 
 #include <Core/Module/Module.h>
 
-#include <QDebug>
-
 void MessageBroker::subscribe(Filter filter, Module* observer,
                               Callback callback)
 {
@@ -13,8 +11,6 @@ void MessageBroker::subscribe(Filter filter, Module* observer,
     // subscriber
     connect(observer->getEventHandler(), &EventHandler::beforeDelete, this,
             &MessageBroker::onModuleDeleted);
-
-    qDebug() << "Added subscription: " << filter.toString();
 }
 
 void MessageBroker::unsubscribe(Filter filter, Module* module)
@@ -39,7 +35,7 @@ void MessageBroker::publish(const Message& message)
             {
                 auto copy = message;
                 filter.filterMessage(copy);
-                (*subscriber.second)(copy);
+                (*subscriber.second)(copy, filter);
             }
         }
     }
diff --git a/src/shared/Core/MessageBroker/MessageBroker.h b/src/shared/Core/MessageBroker/MessageBroker.h
index ada2a4fc94d7c64481c0c22ccc3127b4339dc058..eee290fbc51c007bc438808fd6d0172ada3edd16 100644
--- a/src/shared/Core/MessageBroker/MessageBroker.h
+++ b/src/shared/Core/MessageBroker/MessageBroker.h
@@ -12,7 +12,7 @@ class MessageBroker : public QObject
     Q_OBJECT
 
 public:
-    using Callback = std::function<void(const Message& msg)>;
+    using Callback = std::function<void(const Message&, const Filter&)>;
 
     void subscribe(Filter filter, Module* observer, Callback callback);
 
diff --git a/src/shared/Core/ModulesManager/ModulesManager.cpp b/src/shared/Core/ModulesManager/ModulesManager.cpp
index a4b809facd58ccee8e737d3f9026c8502cadb5c7..55533318d0c935980d1cdfd520929538f8048b1f 100644
--- a/src/shared/Core/ModulesManager/ModulesManager.cpp
+++ b/src/shared/Core/ModulesManager/ModulesManager.cpp
@@ -200,8 +200,6 @@ void ModulesManager::onReplaceMeWith(Module* sender, Module* newModule)
             pages[index] = newModule;
             connectModule(newModule);
             disconnectModule(sender);
-            // TODO: Understand well how the all this mess works
-            // delete sender;
         }
     }
 }
diff --git a/src/shared/Modules/FileStream/FileStreamModule.cpp b/src/shared/Modules/FileStream/FileStreamModule.cpp
index 609d61b7846dddcf892abc1cdcd40174199ae8eb..a1fcf669aa15c0ff2b7fab5823221d13b38a80e6 100644
--- a/src/shared/Modules/FileStream/FileStreamModule.cpp
+++ b/src/shared/Modules/FileStream/FileStreamModule.cpp
@@ -134,7 +134,8 @@ void FileStreamModule::onStartClicked()
     {
         getCore()->getMessageBroker()->subscribe(
             Filter::fromString(topicViewsList[i]->text().trimmed()), this,
-            [this](const Message& msg) { onMsgReceived(msg); });
+            [this](const Message& message, const Filter& filter)
+            { onMsgReceived(message); });
         topicViewsList[i]->setEnabled(false);
     }
 }
diff --git a/src/shared/Modules/Graph/Graph.cpp b/src/shared/Modules/Graph/Graph.cpp
index 17c56cbf1d1418d0985e1ca41477838c32c02ca3..e49e6598305d0a657d72add10450e864067674f6 100644
--- a/src/shared/Modules/Graph/Graph.cpp
+++ b/src/shared/Modules/Graph/Graph.cpp
@@ -4,6 +4,7 @@
 #include <Components/SubscriptionsPanel/SubscriptionsPanel.h>
 #include <Core/MessageBroker/MessageBroker.h>
 
+#include <QDebug>
 #include <QTimer>
 #include <algorithm>
 
@@ -15,14 +16,8 @@ Graph::Graph(QWidget* parent) : DefaultModule(parent)
     connect(&updaterTimer, &QTimer::timeout, this, &Graph::onUpdateTimerTick);
     updaterTimer.setSingleShot(false);
     updaterTimer.start(updatePeriod);
-
-    getCore()->getMessageBroker()->subscribe(Filter::fromString("*"), this,
-                                             [this](const Message& msg)
-                                             { onMsgReceived(msg); });
 }
 
-Graph::~Graph() {}
-
 QWidget* Graph::toWidget() { return this; }
 
 XmlObject Graph::toXmlObject()
@@ -36,10 +31,10 @@ XmlObject Graph::toXmlObject()
     obj.addAttribute("x_lower_range", (float)plot->xAxis->range().lower);
     obj.addAttribute("x_upper_range", (float)plot->xAxis->range().upper);
 
-    for (int i = 0; i < filters.count(); i++)
+    for (auto filter : lines.keys())
     {
         XmlObject element("subscription");
-        element.addAttribute("filter", filters[i].toString());
+        element.addAttribute("filter", filter.toString());
         obj.addChild(element);
     }
 
@@ -71,7 +66,7 @@ void Graph::fromXmlObject(const XmlObject& obj)
             if (child.getObjectName() == "subscription")
             {
                 auto filter = Filter::fromString(child.getAttribute("filter"));
-                onSubscriptionAdded(filter);
+                onFilterAdded(filter);
             }
         }
     }
@@ -79,19 +74,23 @@ void Graph::fromXmlObject(const XmlObject& obj)
 
 void Graph::onSubscribeClicked()
 {
-    SubscriptionsPanel* panel = new SubscriptionsPanel(filters);
+    SubscriptionsPanel* panel = new SubscriptionsPanel(lines.keys());
     panel->setWindowTitle("Graph subscriptions");
-    connect(panel, &SubscriptionsPanel::topicAndFieldFilterAdded, this,
-            &Graph::onSubscriptionAdded);
-    connect(panel, &SubscriptionsPanel::topicAndFieldFilterRemoved, this,
-            &Graph::onSubscriptionRemoved);
+    connect(panel, &SubscriptionsPanel::filterAdded, this,
+            &Graph::onFilterAdded);
+    connect(panel, &SubscriptionsPanel::filterRemoved, this,
+            &Graph::onFilterRemoved);
     panel->show();
 }
 
 void Graph::onClearClicked()
 {
-    for (auto graph : graphs)
-        graph->data()->clear();
+    for (auto filter : lines.keys())
+    {
+        for (auto field : lines[filter].first.keys())
+            lines[filter].first[field].graph->data()->clear();
+    }
+
     plot->replot();
 }
 
@@ -107,38 +106,86 @@ void Graph::onStopClicked(bool checked)
 
 void Graph::onFollowClicked(bool checked) { following = checked; }
 
-void Graph::onSubscriptionAdded(const Filter& filter)
+void Graph::onFilterAdded(const Filter& filter)
 {
-    if (!filters.contains(filter))
+    qDebug() << "[Graph] On filter added";
+
+    // Fail if the filter is already in the list
+    if (lines.keys().contains(filter))
     {
+        qDebug() << "[Graph] Filter already in the list";
+        return;
+    }
+
+    // Add a new line for each field of the filter
+    for (auto field : filter.getFields())
+    {
+        // Create the graph
         QCPGraph* graph = plot->addGraph();
         graph->setPen(
             QPen(QColor(rand() % 255, rand() % 255, rand() % 255), 2));
         graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssNone, 2));
         graph->setSelectable(QCP::stNone);
-        graph->setName(filter.toString());
+        graph->setName(field);
 
-        // Add the filter and the graph along with new buffers
-        filters.append(filter);
-        graphs.append(graph);
-        buffersX.append(QVector<double>());
-        buffersY.append(QVector<double>());
+        // Add the filter and the graph along with new buffers to the lists
+        lines[filter].first[field] = {graph, QVector<double>(),
+                                      QVector<double>()};
     }
+
+    getCore()->getMessageBroker()->subscribe(
+        filter, this,
+        [&](const Message& message, const Filter& filter)
+        {
+            qDebug() << "[Graph] Received new message:" << message.toString();
+
+            if (stopped && !lines.contains(filter))
+                return;
+
+            for (auto field : filter.getFields())
+            {
+                auto& line = lines[filter].first[field];
+
+                QVector<double>& bufferX = line.bufferX;
+                QVector<double>& bufferY = line.bufferY;
+
+                double x =
+                    message.getField("timestamp").getUnsignedInteger() / 1e6;
+                double y = message.getField(field).getDouble();
+
+                // Check if the timestamp resets
+                if (bufferX.last() < x)
+                {
+                    bufferX.append(x);
+                    bufferY.append(y);
+                }
+                else
+                {
+                    bufferX.clear();
+                    bufferY.clear();
+
+                    line.graph->data()->clear();
+                }
+            }
+
+            // Flag the data as updated
+            lines[filter].second = true;
+        });
 }
 
-void Graph::onSubscriptionRemoved(const Filter& filter)
+void Graph::onFilterRemoved(const Filter& filter)
 {
-    if (filters.contains(filter))
+    if (lines.contains(filter))
     {
-        int index = filters.indexOf(filter);
+        for (auto field : lines[filter].first.keys())
+        {
+            auto line = lines[filter].first[field];
 
-        plot->removeGraph(graphs[index]);
-        plot->replot();
+            plot->removeGraph(line.graph);
+            plot->replot();
+        }
 
-        filters.removeAt(index);
-        graphs.removeAt(index);
-        buffersX.removeAt(index);
-        buffersY.removeAt(index);
+        lines.remove(filter);
     }
 }
 
@@ -148,29 +195,33 @@ void Graph::onUpdateTimerTick()
     double maxX  = 0;
 
     // Check if new data items are available and redraw
-    for (int i = 0; i < filters.size(); i++)
+    for (auto filter : lines.keys())
     {
-        QCPGraph* graph          = graphs[i];
-        QVector<double>& bufferX = buffersX[i];
-
-        if (bufferX.size() > 0)
+        // Check if the data have been updated
+        if (lines[filter].second)
         {
-            QVector<double>& bufferY = buffersY[i];
+            for (auto field : lines[filter].first.keys())
+            {
+                auto& line = lines[filter].first[field];
 
-            newData = true;
+                if (line.bufferX.size() > 0)
+                {
+                    newData = true;
 
-            graph->addData(bufferX, bufferY);
+                    line.graph->addData(line.bufferX, line.bufferY);
 
-            for (auto tmp : bufferX)
-                if (tmp > maxX)
-                    // cppcheck-suppress useStlAlgorithm
-                    maxX = tmp;
+                    for (auto tmp : line.bufferX)
+                        if (tmp > maxX)
+                            // cppcheck-suppress useStlAlgorithm
+                            maxX = tmp;
 
-            bufferX.clear();
-            bufferY.clear();
+                    line.bufferX.clear();
+                    line.bufferY.clear();
 
-            // Check if there are too many data points
-            graph->data()->removeBefore(maxX - MAX_DATA_AGE);
+                    // Check if there are too many data points
+                    line.graph->data()->removeBefore(maxX - MAX_DATA_AGE);
+                }
+            }
         }
     }
 
@@ -179,47 +230,8 @@ void Graph::onUpdateTimerTick()
         plot->replot();
 
         if (following)
-        {
-            double size = plot->xAxis->range().size();
-            plot->xAxis->setRange(maxX, size, Qt::AlignmentFlag::AlignRight);
-        }
-    }
-}
-
-void Graph::onMsgReceived(const Message& msg)
-{
-    if (stopped)
-        return;
-
-    // TODO
-
-    for (auto filter : filters)
-    {
-        if (filter.match(msg))
-        {
-            int index = filters.indexOf(filter);
-
-            QVector<double>& bufferX = buffersX[index];
-            QVector<double>& bufferY = buffersY[index];
-
-            double x = msg.getField("timestamp").getUnsignedInteger() / 1e6;
-            double y = 2;  // TODO
-
-            // Check if the timestamp resets
-            if (bufferX.last() < x)
-            {
-                bufferX.append(x);
-                bufferY.append(y);
-            }
-            else
-            {
-                bufferX.clear();
-                bufferY.clear();
-
-                auto graph = graphs[index];
-                graph->data()->clear();
-            }
-        }
+            plot->xAxis->setRange(maxX, plot->xAxis->range().size(),
+                                  Qt::AlignmentFlag::AlignRight);
     }
 }
 
@@ -256,7 +268,7 @@ void Graph::onCustomContextMenuRequested(const QPoint& pos)
     QMenu menu;
 
     // Subscribe -> Edit graph series
-    QAction* subscribe = new QAction("Subscribe");
+    QAction* subscribe = new QAction("Manage subscriptions");
     connect(subscribe, &QAction::triggered, this, &Graph::onSubscribeClicked);
     menu.addAction(subscribe);
 
diff --git a/src/shared/Modules/Graph/Graph.h b/src/shared/Modules/Graph/Graph.h
index edb741816d2b0416d6fcb517702bc260e6c59f7b..4b0c551d2d72633335291f7ddcd92e371c2da606 100644
--- a/src/shared/Modules/Graph/Graph.h
+++ b/src/shared/Modules/Graph/Graph.h
@@ -1,8 +1,6 @@
 #pragma once
 
 #include <Core/Message/Filter.h>
-#include <Core/Message/Message.h>
-#include <Core/Module/Module.h>
 #include <Core/QCustomPlot/QCustomPlot.h>
 #include <Modules/DefaultModule/DefaultModule.h>
 
@@ -18,7 +16,6 @@ class Graph : public DefaultModule
 
 public:
     explicit Graph(QWidget* parent = nullptr);
-    ~Graph();
 
     QWidget* toWidget() override;
     XmlObject toXmlObject() override;
@@ -29,10 +26,11 @@ private slots:
     void onClearClicked();
     void onStopClicked(bool checked);
     void onFollowClicked(bool checked);
-    void onSubscriptionAdded(const Filter& filter);
-    void onSubscriptionRemoved(const Filter& filter);
+
+    void onFilterAdded(const Filter& filter);
+    void onFilterRemoved(const Filter& filter);
+
     void onUpdateTimerTick();
-    void onMsgReceived(const Message& msg);
 
 private:
     void setupUi();
@@ -41,10 +39,14 @@ private:
 
     QCustomPlot* plot;
 
-    QList<Filter> filters;
-    QList<QCPGraph*> graphs;
-    QList<QVector<double>> buffersX;
-    QList<QVector<double>> buffersY;
+    struct Line
+    {
+        QCPGraph* graph;
+        QVector<double> bufferX;
+        QVector<double> bufferY;
+    };
+
+    QMap<Filter, QPair<QMap<QString, Line>, bool>> lines;
 
     QTimer updaterTimer;
     int updatePeriod = 1000 / 5;  // 5fps
diff --git a/src/shared/Modules/IncomingMessagesViewer/IncomingMessagesViewerModule.cpp b/src/shared/Modules/IncomingMessagesViewer/IncomingMessagesViewerModule.cpp
index c43042b8e5117b753f4b12cfdff80638ec8411bf..1281433534887e5336cfbc2af558fbe67a15a875 100644
--- a/src/shared/Modules/IncomingMessagesViewer/IncomingMessagesViewerModule.cpp
+++ b/src/shared/Modules/IncomingMessagesViewer/IncomingMessagesViewerModule.cpp
@@ -48,18 +48,30 @@ void IncomingMessagesViewerModule::fromXmlObject(const XmlObject& xmlObject)
     {
         XmlObject child = xmlObject.childAt(i);
         if (child.getObjectName() == "subscription")
-            addSubscription(Filter::fromString(child.getAttribute("filter")));
+            onFilterAdded(Filter::fromString(child.getAttribute("filter")));
     }
 }
 
-void IncomingMessagesViewerModule::addSubscription(const Filter& filter)
+void IncomingMessagesViewerModule::onSubscribeClicked()
+{
+    SubscriptionsPanel* panel = new SubscriptionsPanel(filters);
+    panel->setWindowTitle("Graph subscriptions");
+    connect(panel, &SubscriptionsPanel::filterAdded, this,
+            &IncomingMessagesViewerModule::onFilterAdded);
+    connect(panel, &SubscriptionsPanel::filterRemoved, this,
+            &IncomingMessagesViewerModule::onFilterRemoved);
+    panel->show();
+}
+
+void IncomingMessagesViewerModule::onFilterAdded(const Filter& filter)
 {
-    qDebug() << "addSubscription";
     filters.append(filter);
     getCore()->getMessageBroker()->subscribe(
         filter, this,
-        [&](const Message& msg)
+        [&](const Message& message, const Filter& filter)
         {
+            qDebug() << "[Graph] Received new message:" << message.toString();
+
             QString oldText = "";
             QString time    = "";
 
@@ -70,7 +82,7 @@ void IncomingMessagesViewerModule::addSubscription(const Filter& filter)
             if (useTimestamp)
             {
                 // If available  show the timestamp
-                auto timestamp = msg.getField("timestamp");
+                auto timestamp = message.getField("timestamp");
                 time += "[";
                 time += QString::number(
                     (float)timestamp.getUnsignedInteger() / 1e6, 'f', 1);
@@ -82,36 +94,38 @@ void IncomingMessagesViewerModule::addSubscription(const Filter& filter)
             }
 
             // Updated the content
-            edit->setText(time + msg.toString() + "\n" + oldText);
+            edit->setText(time + message.toString() + "\n" + oldText);
         });
 }
 
-void IncomingMessagesViewerModule::removeSubscription(const Filter& filter)
+void IncomingMessagesViewerModule::onFilterRemoved(const Filter& filter)
 {
     filters.removeAll(filter);
     getCore()->getMessageBroker()->unsubscribe(filter, this);
 }
 
+void IncomingMessagesViewerModule::setupUi()
+{
+    edit = new QTextEdit();
+    edit->setReadOnly(true);
+    edit->setContextMenuPolicy(Qt::ContextMenuPolicy::NoContextMenu);
+
+    QHBoxLayout* layout = new QHBoxLayout();
+    layout->setContentsMargins(0, 0, 0, 0);
+    layout->addWidget(edit);
+    setLayout(layout);
+}
+
 void IncomingMessagesViewerModule::addCustomActionsToMenu()
 {
     QAction* clear = new QAction("Clear");
     connect(clear, &QAction::triggered, this, [this]() { edit->clear(); });
     addActionToMenu(clear);
 
-    QAction* manage = new QAction("Manage subscriptions");
-    connect(manage, &QAction::triggered, this,
-            [this]()
-            {
-                auto* panel = new SubscriptionsPanel(filters);
-                panel->setWindowTitle("IncomingMessagesViewer subscriptions");
-                connect(panel, &SubscriptionsPanel::topicAndFieldFilterAdded,
-                        this, &IncomingMessagesViewerModule::addSubscription);
-                connect(panel, &SubscriptionsPanel::topicAndFieldFilterRemoved,
-                        this,
-                        &IncomingMessagesViewerModule::removeSubscription);
-                panel->show();
-            });
-    addActionToMenu(manage);
+    QAction* subscriptions = new QAction("Manage subscriptions");
+    connect(subscriptions, &QAction::triggered, this,
+            &IncomingMessagesViewerModule::onSubscribeClicked);
+    addActionToMenu(subscriptions);
 
     keepOnlyLastMessageAction = new QAction("Keep only last message");
     keepOnlyLastMessageAction->setCheckable(true);
@@ -125,15 +139,3 @@ void IncomingMessagesViewerModule::addCustomActionsToMenu()
             [=](bool value) { useTimestamp = value; });
     addActionToMenu(useTimestampAction);
 }
-
-void IncomingMessagesViewerModule::setupUi()
-{
-    edit = new QTextEdit();
-    edit->setReadOnly(true);
-    edit->setContextMenuPolicy(Qt::ContextMenuPolicy::NoContextMenu);
-
-    QHBoxLayout* layout = new QHBoxLayout();
-    layout->setContentsMargins(0, 0, 0, 0);
-    layout->addWidget(edit);
-    setLayout(layout);
-}
diff --git a/src/shared/Modules/IncomingMessagesViewer/IncomingMessagesViewerModule.h b/src/shared/Modules/IncomingMessagesViewer/IncomingMessagesViewerModule.h
index 4333f0a54ee78aef64710df14039032cf4735161..9b951e3ecbbc250666f8f24d0dd4aa36b17b1ab8 100644
--- a/src/shared/Modules/IncomingMessagesViewer/IncomingMessagesViewerModule.h
+++ b/src/shared/Modules/IncomingMessagesViewer/IncomingMessagesViewerModule.h
@@ -15,15 +15,16 @@ public:
 
     XmlObject toXmlObject() override;
     void fromXmlObject(const XmlObject& xmlObject) override;
-    void addCustomActionsToMenu() override;
 
-public slots:
-    void addSubscription(const Filter& filter);
-    void removeSubscription(const Filter& filter);
+private slots:
+    void onSubscribeClicked();
+    void onFilterAdded(const Filter& filter);
+    void onFilterRemoved(const Filter& filter);
 
 private:
     QTextEdit* edit;
     void setupUi();
+    void addCustomActionsToMenu() override;
 
     QList<Filter> filters;
 
diff --git a/src/shared/Modules/Mavlink/MavlinkModule.cpp b/src/shared/Modules/Mavlink/MavlinkModule.cpp
index ecdc16fb85dfb5d6fd8394360466263e1733950c..7dccbb99fccfd5a69ec4b07de7a9c11719d9f152 100644
--- a/src/shared/Modules/Mavlink/MavlinkModule.cpp
+++ b/src/shared/Modules/Mavlink/MavlinkModule.cpp
@@ -24,7 +24,8 @@ MavlinkModule::MavlinkModule(QWidget *parent)
 
     getCore()->getMessageBroker()->subscribe(
         Filter::fromString(SkywardHubStrings::commandsTopic + "/*"), this,
-        [this](const Message &msg) { onCommandReceived(msg); });
+        [this](const Message &message, const Filter &filter)
+        { onCommandReceived(message); });
 }
 
 MavlinkModule::~MavlinkModule() { onStopClicked(); }
diff --git a/src/shared/Modules/OrientationVisualizer/OrientationVisualizer.cpp b/src/shared/Modules/OrientationVisualizer/OrientationVisualizer.cpp
index 1aea11c59d7dc272371477f8a31ee03800bfd579..5b14d10255aff1da81b5e800b0a3bc5c805de9b4 100644
--- a/src/shared/Modules/OrientationVisualizer/OrientationVisualizer.cpp
+++ b/src/shared/Modules/OrientationVisualizer/OrientationVisualizer.cpp
@@ -30,12 +30,12 @@ OrientationVisualizer::OrientationVisualizer(QWidget *parent)
 
     getCore()->getMessageBroker()->subscribe(
         Filter::fromString("Mav/PAYLOAD_FLIGHT_TM"), this,
-        [this](const Message &msg)
+        [this](const Message &message, const Filter &filter)
         {
-            updateOrientation(msg.getField("nas_qx").getDouble(),
-                              msg.getField("nas_qy").getDouble(),
-                              msg.getField("nas_qz").getDouble(),
-                              msg.getField("nas_qw").getDouble());
+            updateOrientation(message.getField("nas_qx").getDouble(),
+                              message.getField("nas_qy").getDouble(),
+                              message.getField("nas_qz").getDouble(),
+                              message.getField("nas_qw").getDouble());
         });
 }
 
diff --git a/src/shared/Modules/OutgoingMessagesViewer/OutgoingMessagesViewerModule.cpp b/src/shared/Modules/OutgoingMessagesViewer/OutgoingMessagesViewerModule.cpp
index 3174a00de00ef409611365cce43ccff45ade744b..b1af15a228b580251b75422df4d2b7a8094a906f 100644
--- a/src/shared/Modules/OutgoingMessagesViewer/OutgoingMessagesViewerModule.cpp
+++ b/src/shared/Modules/OutgoingMessagesViewer/OutgoingMessagesViewerModule.cpp
@@ -15,14 +15,19 @@ OutgoingMessagesViewerModule::OutgoingMessagesViewerModule(QWidget* parent)
 
     getCore()->getMessageBroker()->subscribe(
         Filter::fromString(SkywardHubStrings::logCommandsTopic), this,
-        [this](const Message& msg) { addMsgSent(msg); });
+        [this](const Message& message, const Filter& filter)
+        { addMsgSent(message); });
 
     getCore()->getMessageBroker()->subscribe(
         Filter::fromString(SkywardHubStrings::mavlink_received_msg_ACK_topic),
-        this, [this](const Message& msg) { handleAck(msg); });
+        this,
+        [this](const Message& message, const Filter& filter)
+        { handleAck(message); });
     getCore()->getMessageBroker()->subscribe(
         Filter::fromString(SkywardHubStrings::mavlink_received_msg_NACK_topic),
-        this, [this](const Message& msg) { handleNack(msg); });
+        this,
+        [this](const Message& message, const Filter& filter)
+        { handleNack(message); });
 }
 
 OutgoingMessagesViewerModule::~OutgoingMessagesViewerModule() { delete ui; }
@@ -54,7 +59,7 @@ void OutgoingMessagesViewerModule::addMsgSent(const Message& msg)
 
 QString OutgoingMessagesViewerModule::computeMsgName(const Message& msg)
 {
-    return msg.getField("name").getString();
+    return msg.getField("name").toString();
 }
 
 void OutgoingMessagesViewerModule::handleAck(const Message& ack)
diff --git a/src/shared/Modules/StateViewer/StateViewer.cpp b/src/shared/Modules/StateViewer/StateViewer.cpp
index f1f2cb2567db24133353b56acf64c4af07462224..870c2128bd5d6c6d70c8109190d8c067ad242a3f 100644
--- a/src/shared/Modules/StateViewer/StateViewer.cpp
+++ b/src/shared/Modules/StateViewer/StateViewer.cpp
@@ -71,7 +71,9 @@ void StateViewerModule::setFilter(const Filter& newFilter)
 {
     getCore()->getMessageBroker()->unsubscribe(filter, this);
     getCore()->getMessageBroker()->subscribe(
-        newFilter, this, [this](const Message& msg) { onMsgReceived(msg); });
+        newFilter, this,
+        [this](const Message& message, const Filter& filter)
+        { onMsgReceived(message); });
     filter = newFilter;
 }
 
diff --git a/src/shared/Modules/Test/TestModule.cpp b/src/shared/Modules/Test/TestModule.cpp
index 6e78e8e92990d731c0f9eb0a96fd41436bd034f7..e87a2bbedd475c8cc2b0590a8583b806d9b94687 100644
--- a/src/shared/Modules/Test/TestModule.cpp
+++ b/src/shared/Modules/Test/TestModule.cpp
@@ -35,25 +35,25 @@ void TestModule::on_publish_button_clicked()
 {
     QString topic   = ui->topic_lineEdit->text().trimmed();
     QString payload = ui->payload_lineEdit->text().trimmed();
-    Message msg(Topic{topic});
+    Message message(Topic{topic});
     auto valueField = Field(payload);
-    msg.setField("value", valueField);
+    message.setField("value", valueField);
     auto timestampField =
         Field((uint64_t)QTime::currentTime().msecsSinceStartOfDay());
-    msg.setField("timestamp", timestampField);
-    getCore()->getMessageBroker()->publish(msg);
+    message.setField("timestamp", timestampField);
+    getCore()->getMessageBroker()->publish(message);
 }
 
 void TestModule::on_subscribe_button_clicked()
 {
     getCore()->getMessageBroker()->subscribe(
         Filter::fromString(ui->subscribe_lineEdit->text().trimmed()), this,
-        [this](const Message& msg)
+        [this](const Message& message, const Filter& filter)
         {
             QString oldText = ui->textArea->toPlainText();
             QString line =
                 QDateTime::currentDateTime().toString("hh.mm.ss (zzz): ") +
-                msg.toString();
+                message.toString();
             QString newText(line + '\n' + oldText);
             ui->textArea->setPlainText(newText.mid(0, maxChar));
         });
diff --git a/src/shared/Modules/ValuesConverterViewer/ValuesConverterViewerModule.cpp b/src/shared/Modules/ValuesConverterViewer/ValuesConverterViewerModule.cpp
index 1109c5a8ad959e7d87d31daaa1ffb50fe56b6076..6a870b348516016e7b56424433735c908f06e44e 100644
--- a/src/shared/Modules/ValuesConverterViewer/ValuesConverterViewerModule.cpp
+++ b/src/shared/Modules/ValuesConverterViewer/ValuesConverterViewerModule.cpp
@@ -12,9 +12,10 @@ ValuesConverterViewerModule::ValuesConverterViewerModule(QWidget* parent)
     ui->setupUi(this);
     defaultContextMenuSetup();
 
-    getCore()->getMessageBroker()->subscribe(Filter::fromString("*"), this,
-                                             [this](const Message& msg)
-                                             { onMsgReceived(msg); });
+    getCore()->getMessageBroker()->subscribe(
+        Filter::fromString("*"), this,
+        [this](const Message& message, const Filter& filter)
+        { onMsgReceived(message); });
 }
 
 ValuesConverterViewerModule::~ValuesConverterViewerModule()