diff --git a/Core/Messages/messagefield.cpp b/Core/Messages/messagefield.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0fab7dfa84bc16a2a87b655ccc8cec61ee4b698d
--- /dev/null
+++ b/Core/Messages/messagefield.cpp
@@ -0,0 +1,95 @@
+#include "messagefield.h"
+
+MessageField::MessageField() : type(Type::EMPTY) { }
+
+MessageField::MessageField(int64_t _i64) : type(Type::INTEGER), i64(_i64) {}
+MessageField::MessageField(uint64_t _u64) : type(Type::UINTEGER), u64(_u64) {}
+MessageField::MessageField(double _d) : type(Type::DOUBLE), d(_d) {}
+MessageField::MessageField(char _c) : type(Type::CHAR), c(_c) {}
+MessageField::MessageField(QString _s) : type(Type::STRING), s(_s) {}
+
+int64_t MessageField::getInteger(int64_t defaultValue) const {
+    if(type == Type::INTEGER) {
+        return i64;
+    } else {
+        return defaultValue;
+    }
+}
+
+uint64_t MessageField::getUInteger(uint64_t defaultValue)  const {
+    if(type == Type::UINTEGER) {
+        return u64;
+    } else {
+        return defaultValue;
+    }
+}
+
+double MessageField::getDouble(double defaultValue) const {
+    if(type == Type::DOUBLE) {
+        return d;
+    } else {
+        return defaultValue;
+    }
+}
+
+char MessageField::getChar(char defaultValue) const {
+    if(type == Type::CHAR) {
+        return c;
+    } else {
+        return defaultValue;
+    }
+}
+
+QString MessageField::getString()  const {
+    switch(type) {
+        case Type::EMPTY:
+            return QString();
+        case Type::INTEGER:
+            return QString::number(i64);
+        case Type::UINTEGER:
+            return QString::number(u64);
+        case Type::DOUBLE:
+            return QString::number(d);
+        case Type::CHAR:
+            return QString(c);
+        case Type::STRING:
+            return s;
+    }
+}
+
+void MessageField::set(int64_t value) {
+    type = Type::INTEGER;
+    i64 = value;
+}
+
+void MessageField::set(uint64_t value) {
+    type = Type::UINTEGER;
+    u64 = value;
+}
+
+void MessageField::set(double value) {
+    type = Type::DOUBLE;
+    d = value;
+}
+
+void MessageField::set(char value) {
+    type = Type::CHAR;
+    c = value;
+}
+
+void MessageField::set(QString value) {
+    type = Type::STRING;
+    s = value;
+}
+
+void MessageField::set() {
+    if(type == Type::STRING) {
+        s.clear();
+    }
+
+    type = Type::EMPTY;
+}
+
+MessageField::Type MessageField::getType() const {
+    return type;
+}
diff --git a/Core/Messages/messagefield.h b/Core/Messages/messagefield.h
new file mode 100644
index 0000000000000000000000000000000000000000..c00dd65064c41a1616b652f27cf614768930e19c
--- /dev/null
+++ b/Core/Messages/messagefield.h
@@ -0,0 +1,52 @@
+#ifndef MESSAGEFIELD_H
+#define MESSAGEFIELD_H
+
+#include <QString>
+#include <cstdint>
+
+class MessageField {
+  public:
+    enum class Type {
+        EMPTY,
+        INTEGER,
+        UINTEGER,
+        DOUBLE,
+        CHAR,
+        STRING
+    };
+
+    MessageField();
+    MessageField(int64_t _i64);
+    MessageField(uint64_t _u64);
+    MessageField(double _d);
+    MessageField(char _c);
+    MessageField(QString _s);
+
+    int64_t getInteger(int64_t defaultValue) const;
+    uint64_t getUInteger(uint64_t defaultValue) const;
+    double getDouble(double defaultValue) const;
+    char getChar(char defaultValue) const;
+    QString getString() const;
+
+    void set(int64_t value);
+    void set(uint64_t value);
+    void set(double value);
+    void set(char value);
+    void set(QString value);
+    void set();
+
+    Type getType() const;
+
+  private:
+    Type type;
+    QString s;
+
+    union {
+        int64_t i64;
+        uint64_t u64;
+        double d;
+        char c;
+    };
+};
+
+#endif // MESSAGEFIELD_H
diff --git a/Core/Messages/topic.cpp b/Core/Messages/topic.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7fee1b57267dfb5221c29be6ef064d290bd7e219
--- /dev/null
+++ b/Core/Messages/topic.cpp
@@ -0,0 +1,40 @@
+#include "topic.h"
+
+#include <QRegExp>
+
+Topic::Topic(QString str) {
+    if(str == "*" || str.contains(QRegExp("^[A-Za-z0-9$_]+(/[A-Za-z0-9$_]+)*(/*)?$"))) {
+        repr = str;
+    } else {
+        str = "Invalid";
+    }
+}
+
+bool Topic::match(Topic prefix) const {
+    if(prefix.repr.endsWith("*")) {
+        int prefixSize = prefix.repr.size() - 2;
+        return repr.size() >= prefixSize && repr.at(prefixSize - 1) == '/' && repr.startsWith(prefix.repr.leftRef(prefixSize));
+    } else {
+        return repr == prefix.repr;
+    }
+}
+
+Topic Topic::getParent() const {
+    int idx = repr.lastIndexOf('/');
+    return idx == -1 ? Topic(repr) : Topic(repr.left(idx));
+}
+
+Topic Topic::getRoot() const {
+    int idx = repr.indexOf('/');
+    return idx == -1 ? Topic(repr) : Topic(repr.left(idx));
+}
+
+QString Topic::toString() const {
+    return repr;
+}
+
+bool Topic::isWildcard() const {
+    return repr.contains('*');
+}
+
+static const Topic Any("*");
diff --git a/Core/Messages/topic.h b/Core/Messages/topic.h
new file mode 100644
index 0000000000000000000000000000000000000000..efd658519cc27f833ec4bbb5488cda81321bbed9
--- /dev/null
+++ b/Core/Messages/topic.h
@@ -0,0 +1,40 @@
+#ifndef TOPIC_H
+#define TOPIC_H
+
+#include <QString>
+
+/*
+ * This class defines topics for GS messages.
+ *
+ * Topics are strings made of alphanumerical characters and slashes '/'. For example,
+ * "Prefix/Section2/Message" and "SomeMessage" are valid topics. I'll call the parts
+ * between slashes tokens.
+ *
+ * When a Module subscribes to a topic, it is allowed to specify a wildcard topic, which
+ * uses as last token a *, so that it will match against all topics with the same prefix.
+ *
+ * For example: "Prefix/*" is a valid topic that matches against "Prefix", "Prefix/AMessage" and
+ * "Prefix/Section2/Message". Similarly, "*" will match against anything.
+ *
+*/
+
+class Topic {
+  private:
+    QString repr;
+
+  public:
+    Topic(QString str);
+
+    bool match(Topic prefix) const;
+    bool match(QString prefix) const;
+
+    Topic getParent() const;
+    Topic getRoot() const;
+
+    QString toString() const;
+    bool isWildcard() const;
+
+    static const Topic Any;
+};
+
+#endif // TOPIC_H
diff --git a/Core/modulemessage.h b/Core/modulemessage.h
index ef0d923248297549bc59a59955ea25cd8227d3ef..a5f5f14dcc8d9cf7128cc956c38d61adad0f0a0c 100644
--- a/Core/modulemessage.h
+++ b/Core/modulemessage.h
@@ -9,49 +9,49 @@
 class ModuleMessage {
   public:
     ModuleMessage();
-    ModuleMessage(const QString &topic);
-    ModuleMessage(const QString &_topic, const QString &payload);
-    ModuleMessage(const QString &_topic, const QString &payload, const QTime &time);
-    ModuleMessage(const ModuleMessage &other);
+    ModuleMessage(const QString& topic);
+    ModuleMessage(const QString& _topic, const QString& payload);
+    ModuleMessage(const QString& _topic, const QString& payload, const QTime& time);
+    ModuleMessage(const ModuleMessage& other);
 
-    ModuleMessage operator=(const ModuleMessage &other);
+    ModuleMessage operator=(const ModuleMessage& other);
 
     QString payload() const;
-    void setPayload(const QString &value);
+    void setPayload(const QString& value);
     void setPayload(int value);
 
     QString topic() const;
-    void setTopic(const QString &value);
+    void setTopic(const QString& value);
 
     QTime timestamp() const;
-    void setTimestamp(const QTime &timestamp);
+    void setTimestamp(const QTime& timestamp);
 
     QString getHigherHierarchyTopic() const;
 
     QString originalTopic() const;
-    void setOriginalTopic(const QString &originalTopic);
+    void setOriginalTopic(const QString& originalTopic);
 
     /*
      * Returns an XmlObject containing some options
      */
     XmlObject getOptions() const;
-    QString getOption(const QString &optionName) const;
-    bool getIntOption(const QString &optionName, int &val) const;
-    void setOptions(const XmlObject &value);
-    void addOption(const QString &optionName, const QString &value);
-    void addOption(const QString &optionName, int value);
+    QString getOption(const QString& optionName) const;
+    bool getIntOption(const QString& optionName, int& val) const;
+    void setOptions(const XmlObject& value);
+    void addOption(const QString& optionName, const QString& value);
+    void addOption(const QString& optionName, int value);
 
     /*
      * Get the value of "value" if the payload exist and is an integer
      */
-    bool getIntPayload(int &value) const;
+    bool getIntPayload(int& value) const;
 
-    bool getFloatPayload(float &value) const;
+    bool getFloatPayload(float& value) const;
 
     QString toString() const;
 
   private:
-    void copy(const ModuleMessage &msg);
+    void copy(const ModuleMessage& msg);
 
     QString _payload;
     QString _topic;
diff --git a/SkywardHub.pro b/SkywardHub.pro
index b6275d7484efdba0f2001ec20a40c6abd150db7b..38fe58297b12d224dace02eddd1d2647f7cc3ee9 100644
--- a/SkywardHub.pro
+++ b/SkywardHub.pro
@@ -17,6 +17,8 @@ SOURCES += \
     Components/ToggleButton/togglebutton.cpp \
     Components/ErrorDisplayer/error.cpp \
     Components/ErrorDisplayer/errordisplayer.cpp \
+    Core/Messages/messagefield.cpp \
+    Core/Messages/topic.cpp \
     Core/module.cpp \
     Core/moduleeventshandler.cpp \
     Core/modulemessage.cpp \
@@ -73,6 +75,8 @@ HEADERS += \
     Components/ToggleButton/togglebutton.h \
     Components/ErrorDisplayer/error.h \
     Components/ErrorDisplayer/errordisplayer.h \
+    Core/Messages/messagefield.h \
+    Core/Messages/topic.h \
     Core/module.h \
     Core/moduleeventshandler.h \
     Core/modulemessage.h \