diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp
index 2eecf34c200ed0dc09649dc0eba09ba21cb118fa..43a363c6cfcdc851364bad9a2a70127081c2a651 100644
--- a/inc/ECSS_Definitions.hpp
+++ b/inc/ECSS_Definitions.hpp
@@ -7,6 +7,11 @@
 
 #define ECSS_MAX_FIXED_OCTET_STRING_SIZE 256U // For the ST13 large packet transfer service
 
+/**
+ * The total number of different message types that can be handled by this project
+ */
+#define ECSS_TOTAL_MESSAGE_TYPES (10 * 20)
+
 // 7.4.1
 #define CCSDS_PACKET_VERSION 0
 
diff --git a/inc/Message.hpp b/inc/Message.hpp
index 6cd0ef1d73f06c6a2dda9fad42d17c8f4c86f3e4..904c8f67b6c9ea07f367441f0c767025ff437a38 100644
--- a/inc/Message.hpp
+++ b/inc/Message.hpp
@@ -34,23 +34,40 @@ public:
 	 * @brief Overload the equality operator to compare messages
 	 * @details Compare two @ref ::Message objects, based on their contents and type
 	 * @param The message content to compare against
-	 * @todo Activate the dataSize check when the Message object data field is defined with a
-	 * fixed size
 	 * @return The result of comparison
 	 */
 	bool operator==(const Message& msg) const {
-		// todo: Enable the following check when the message data field has a fixed size padded
-		//  with zeros. At the moment the data array is not padded with zeros to fulfil the
-		//  maximum set number of a TC request message.
-		// if (this->dataSize != msg.dataSize) return false;
-
-		for (uint16_t i = 0; i < ECSS_MAX_MESSAGE_SIZE; i++) {
-			if (this->data[i] != msg.data[i]) {
-				return false;
-			}
+		if (dataSize != msg.dataSize) {
+			return false;
 		}
-		return (this->packetType == msg.packetType) && (this->messageType == msg.messageType) &&
-		       (this->serviceType == msg.serviceType);
+
+		if (not isSameType(*this, msg)) {
+			return false;
+		}
+
+		return std::equal(data, data + dataSize, msg.data);
+	}
+
+	/**
+	 * Checks the first \ref Message::dataSize bytes of \p msg for equality
+	 *
+	 * This performs an equality check for the first `[0, this->dataSize)` bytes of two messages. Useful to compare
+	 * two messages that have the same content, but one of which does not know its length.
+	 *
+	 * @param msg The message to check. Its `dataSize` must be smaller than the object calling the function
+	 * @return False if the messages are not of the same type, if `msg.dataSize < this->dataSize`, or if the first
+	 * `this->dataSize` bytes are not equal between the two messages.
+	 */
+	bool bytesEqualWith(const Message& msg) const {
+		if (msg.dataSize < dataSize) {
+			return false;
+		}
+
+		if (not isSameType(*this, msg)) {
+			return false;
+		}
+
+		return std::equal(data, data + dataSize, msg.data);
 	}
 
 	enum PacketType {
@@ -84,7 +101,9 @@ public:
 	// Pointer to the contents of the message (excluding the PUS header)
 	// We allocate this data statically, in order to make sure there is predictability in the
 	// handling and storage of messages
-	// TODO: Is it a good idea to not initialise this to 0?
+	//
+	// @note This is initialized to 0 in order to prevent any mishaps with non-properly initialized values. \ref
+	// Message::appendBits() relies on this in order to easily OR the requested bits.
 	uint8_t data[ECSS_MAX_MESSAGE_SIZE] = { 0 };
 
 	// private:
@@ -108,7 +127,7 @@ public:
 	 * multiple of the padding word size declared for the application process
 	 * @todo Confirm that the overall packet size is an integer multiple of the padding word size
 	 * declared for every application process
-	 * @todo check if wee need to define the spare field for the telemetry and telecommand
+	 * @todo check if we need to define the spare field for the telemetry and telecommand
 	 * secondary headers
 	 */
 	void finalize();
diff --git a/inc/ServicePool.hpp b/inc/ServicePool.hpp
index 97fbed7ff13ef836ad9de9da68f9c5d544050e7d..f6a7ea23ecc2e1d865c9ff3f454249e0b0520f94 100644
--- a/inc/ServicePool.hpp
+++ b/inc/ServicePool.hpp
@@ -20,6 +20,19 @@
  * @todo Find a way to disable services which are not used
  */
 class ServicePool {
+	/**
+	 * A counter for messages
+	 *
+	 * Each key-value pair corresponds to one MessageType within a Service. For the key, the most significant 8 bits are
+     * the number of the service, while the least significant 8 bits are the number of the Message. The value is the
+     * counter of each MessageType.
+	 */
+	etl::map<uint16_t, uint16_t, ECSS_TOTAL_MESSAGE_TYPES> messageTypeCounter;
+
+	/**
+	 * A counter for messages that corresponds to the total number of TM packets sent from an APID
+	 */
+	uint16_t packetSequenceCounter = 0;
 public:
 	RequestVerificationService requestVerification;
 	EventReportService eventReport;
@@ -44,6 +57,27 @@ public:
 	 * Services already stored as values will point to the "new" Services after a reset.
 	 */
 	void reset();
+
+	/**
+	 * Get and increase the "message type counter" for the next message of a type
+	 *
+	 * The message type counter counts the type of generated messages per destination, according to requirement
+	 * 5.4.2.1j. If the value reaches its max, it is wrapped back to 0.
+	 *
+	 * @param serviceType The service type ID
+	 * @param messageType The message type ID
+	 * @return The message type count
+	 */
+	uint16_t getAndUpdateMessageTypeCounter(uint8_t serviceType, uint8_t messageType);
+
+	/**
+	 * Get and increase the "packet sequence count" for the next message
+	 *
+	 * The packet sequence count is incremented each time a packet is released, with a maximum value of 2^14 - 1
+	 *
+	 * @return The packet sequence count
+	 */
+	uint16_t getAndUpdatePacketSequenceCounter();
 };
 
 /**
diff --git a/src/Message.cpp b/src/Message.cpp
index 7ff70f30a13882c56b626f4c0b071b8275549c0c..55e9d49d0cb91e099201e41afb451e681421cc78 100644
--- a/src/Message.cpp
+++ b/src/Message.cpp
@@ -2,6 +2,7 @@
 #include "macros.hpp"
 #include <cstring>
 #include <ErrorHandler.hpp>
+#include <ServicePool.hpp>
 
 Message::Message(uint8_t serviceType, uint8_t messageType, Message::PacketType packetType, uint16_t applicationId)
     : serviceType(serviceType), messageType(messageType), packetType(packetType), applicationId(applicationId) {}
@@ -36,11 +37,15 @@ void Message::appendBits(uint8_t numBits, uint16_t data) {
 
 void Message::finalize() {
 	// Define the spare field in telemetry and telecommand user data field (7.4.3.2.c and 7.4.4.2.c)
-
 	if (currentBit != 0) {
 		currentBit = 0;
 		dataSize++;
 	}
+
+	if (packetType == PacketType::TM) {
+		messageTypeCounter = Services.getAndUpdateMessageTypeCounter(serviceType, messageType);
+		packetSequenceCount = Services.getAndUpdatePacketSequenceCounter();
+	}
 }
 
 void Message::appendByte(uint8_t value) {
diff --git a/src/ServicePool.cpp b/src/ServicePool.cpp
index f76e2ee77d066b475c0678baec2159ad3dc1a246..b8e7c963ca5af536baf4f0b52456a9432452bbb7 100644
--- a/src/ServicePool.cpp
+++ b/src/ServicePool.cpp
@@ -12,3 +12,19 @@ void ServicePool::reset() {
 	// statically allocated from before.
 	new (this) ServicePool();
 }
+
+uint16_t ServicePool::getAndUpdateMessageTypeCounter(uint8_t serviceType, uint8_t messageType) {
+	uint16_t key = (serviceType << 8U) | messageType; // Create the key of the map
+	return (messageTypeCounter[key])++; // Fetch and increase the value
+}
+
+uint16_t ServicePool::getAndUpdatePacketSequenceCounter() {
+	uint16_t value = packetSequenceCounter;
+
+	// Increase the value
+	if ((++packetSequenceCounter) >= (1U << 14U)) { // The value of the packet sequence counter is <= (2^14 - 1)
+		packetSequenceCounter = 0;
+	}
+
+	return value;
+}
diff --git a/test/Message.cpp b/test/Message.cpp
index 25f098322d48d4ec9b80f8fce62937653b22b9ac..2c92a13ed98fd4954d78634502a2a24a1fe7559c 100644
--- a/test/Message.cpp
+++ b/test/Message.cpp
@@ -1,5 +1,6 @@
 #include <catch2/catch.hpp>
 #include <Message.hpp>
+#include <ServicePool.hpp>
 
 TEST_CASE("Message is usable", "[message]") {
 	Message message(5, 17, Message::TC, 3);
@@ -177,17 +178,19 @@ TEST_CASE("Spare field", "[message]") {
 
 	message1.appendByte(1);
 	message1.appendHalfword(2);
-	message1.appendBits(1, 5);
+	message1.appendBits(1, 1);
 	message1.finalize();
 
+	CHECK(message1.data[3] == 0b10000000);
 	CHECK(message1.dataSize == 4);
 
 	Message message2(0, 0, Message::TM, 0);
 	message2.appendByte(1);
 	message2.appendHalfword(2);
-	message2.appendBits(2, 5);
+	message2.appendBits(2, 3);
 	message2.finalize();
 
+	CHECK(message2.data[3] == 0b11000000);
 	CHECK(message2.dataSize == 4);
 
 	Message message3(0, 0, Message::TM, 0);
@@ -197,6 +200,7 @@ TEST_CASE("Spare field", "[message]") {
 	message3.appendBits(3, 5);
 	message3.finalize();
 
+	CHECK(message3.data[3] == 0b10100000);
 	CHECK(message3.dataSize == 4);
 
 	Message message4(0, 0, Message::TM, 0);
@@ -206,6 +210,7 @@ TEST_CASE("Spare field", "[message]") {
 	message4.appendBits(4, 5);
 	message4.finalize();
 
+	CHECK(message4.data[3] == 0b01010000);
 	CHECK(message4.dataSize == 4);
 
 	Message message5(0, 0, Message::TM, 0);
@@ -215,6 +220,7 @@ TEST_CASE("Spare field", "[message]") {
 	message5.appendBits(5, 5);
 	message5.finalize();
 
+	CHECK(message5.data[3] == 0b00101000);
 	CHECK(message5.dataSize == 4);
 
 	Message message6(0, 0, Message::TM, 0);
@@ -224,6 +230,7 @@ TEST_CASE("Spare field", "[message]") {
 	message6.appendBits(6, 5);
 	message6.finalize();
 
+	CHECK(message6.data[3] == 0b00010100);
 	CHECK(message6.dataSize == 4);
 
 	Message message7(0, 0, Message::TM, 0);
@@ -233,6 +240,7 @@ TEST_CASE("Spare field", "[message]") {
 	message7.appendBits(7, 5);
 	message7.finalize();
 
+	CHECK(message7.data[3] == 0b00001010);
 	CHECK(message7.dataSize == 4);
 
 	Message message8(0, 0, Message::TM, 0);
@@ -242,6 +250,7 @@ TEST_CASE("Spare field", "[message]") {
 	message8.appendBits(8, 5);
 	message8.finalize();
 
+	CHECK(message8.data[3] == 0b00000101);
 	CHECK(message8.dataSize == 4);
 
 	Message message9(0, 0, Message::TM, 0);
@@ -253,3 +262,74 @@ TEST_CASE("Spare field", "[message]") {
 
 	CHECK(message9.dataSize == 3);
 }
+
+TEST_CASE("Message type counter", "[message]") {
+	SECTION("Message counting") {
+		Message message1(0, 0, Message::TM, 0);
+		message1.finalize();
+		CHECK(message1.messageTypeCounter == 0);
+
+		Message message2(0, 0, Message::TM, 0);
+		message2.finalize();
+		CHECK(message2.messageTypeCounter == 1);
+	}
+
+	SECTION("Different message types") {
+		Message message1(0, 1, Message::TM, 0);
+		message1.finalize();
+		CHECK(message1.messageTypeCounter == 0);
+
+		Message message2(0, 2, Message::TM, 0);
+		message2.finalize();
+		CHECK(message2.messageTypeCounter == 0);
+	}
+
+	SECTION("Message counter overflow") {
+		for (int i = 0; i <= 65534; i++) {
+			Message message(0, 3, Message::TM, 0);
+			message.finalize();
+		}
+
+		Message message1(0, 3, Message::TM, 0);
+		message1.finalize();
+		CHECK(message1.messageTypeCounter == 65535);
+
+		Message message2(0, 3, Message::TM, 0);
+		message2.finalize();
+		CHECK(message2.messageTypeCounter == 0);
+	}
+}
+
+TEST_CASE("Packet sequence counter", "[message]") {
+	SECTION("Packet counting") {
+		Message message1(0, 0, Message::TM, 0);
+		message1.finalize();
+		CHECK(message1.packetSequenceCount == 0);
+
+		Message message2(0, 0, Message::TM, 0);
+		message2.finalize();
+		CHECK(message2.packetSequenceCount == 1);
+
+		// Different message type check
+		Message message3(1, 2, Message::TM, 0);
+		message3.finalize();
+		CHECK(message3.packetSequenceCount == 2);
+	}
+
+	SECTION("Packet counter overflow") {
+		Services.reset();
+
+		for (int i = 0; i <= 16382; i++) {
+			Message message(0, 3, Message::TM, 0);
+			message.finalize();
+		}
+
+		Message message1(0, 3, Message::TM, 0);
+		message1.finalize();
+		CHECK(message1.packetSequenceCount == 16383);
+
+		Message message2(0, 3, Message::TM, 0);
+		message2.finalize();
+		CHECK(message2.packetSequenceCount == 0);
+	}
+}
diff --git a/test/Services/LargePacketTransferService.cpp b/test/Services/LargePacketTransferService.cpp
index 4d75409acdf09ea4a9a41c973c31423b77c2b575..be5c9fdf9baf89153f22b32e9f15f29e6dee67b3 100644
--- a/test/Services/LargePacketTransferService.cpp
+++ b/test/Services/LargePacketTransferService.cpp
@@ -86,5 +86,6 @@ TEST_CASE("Split function", "[no][service]") {
 			message5.appendUint8(ServiceTests::get(i).readUint8());
 		}
 	}
-	CHECK(message == message5);
+
+	CHECK(message.bytesEqualWith(message5));
 }
diff --git a/test/Services/TimeBasedSchedulingService.cpp b/test/Services/TimeBasedSchedulingService.cpp
index 56a7267df7c9b2859a9d813bcd4ed780f01430e2..b56a8bd3a7475d3e59c02e49d1f5e2deeb15cbc7 100644
--- a/test/Services/TimeBasedSchedulingService.cpp
+++ b/test/Services/TimeBasedSchedulingService.cpp
@@ -115,14 +115,15 @@ TEST_CASE("TC[11,4] Activity Insertion", "[service][st11]") {
 	auto scheduledActivities = activityInsertion(timeBasedService);
 
 	REQUIRE(scheduledActivities.size() == 4);
+
 	REQUIRE(scheduledActivities.at(0)->requestReleaseTime == currentTime + 1556435);
 	REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1726435);
 	REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 1957232);
 	REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 17248435);
-	REQUIRE(scheduledActivities.at(0)->request == testMessage1);
-	REQUIRE(scheduledActivities.at(1)->request == testMessage3);
-	REQUIRE(scheduledActivities.at(2)->request == testMessage2);
-	REQUIRE(scheduledActivities.at(3)->request == testMessage4);
+	REQUIRE(testMessage1.bytesEqualWith(scheduledActivities.at(0)->request));
+	REQUIRE(testMessage3.bytesEqualWith(scheduledActivities.at(1)->request));
+	REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(2)->request));
+	REQUIRE(testMessage4.bytesEqualWith(scheduledActivities.at(3)->request));
 
 	SECTION("Error throw test") {
 		Message receivedMessage(11, 4, Message::TC, 1);
@@ -198,7 +199,7 @@ TEST_CASE("TC[11,7] Time shift activities by ID", "[service][st11]") {
 
 		// Make sure the new value is inserted sorted
 		REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 1957232 + timeShift);
-		REQUIRE(scheduledActivities.at(3)->request == testMessage2);
+		REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(3)->request));
 	}
 
 	SECTION("Negative Shift") {
@@ -213,7 +214,7 @@ TEST_CASE("TC[11,7] Time shift activities by ID", "[service][st11]") {
 
 		// Output should be sorted
 		REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1957232 - 250000);
-		REQUIRE(scheduledActivities.at(1)->request == testMessage2);
+		REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(1)->request));
 	}
 
 	SECTION("Error throw on wrong request ID") {
@@ -382,7 +383,7 @@ TEST_CASE("TC[11,16] Detail report all scheduled activities", "[service][st11]")
 		receivedTCPacket = msgParser.parseRequestTC(receivedDataStr);
 
 		REQUIRE(receivedReleaseTime == scheduledActivities.at(i)->requestReleaseTime);
-		REQUIRE(scheduledActivities.at(i)->request == receivedTCPacket);
+		REQUIRE(receivedTCPacket.bytesEqualWith(scheduledActivities.at(i)->request));
 	}
 }
 
@@ -408,7 +409,7 @@ TEST_CASE("TC[11,5] Activity deletion by ID", "[service][st11]") {
 
 		REQUIRE(scheduledActivities.size() == 3);
 		REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 17248435);
-		REQUIRE(scheduledActivities.at(2)->request == testMessage4);
+		REQUIRE(testMessage4.bytesEqualWith(scheduledActivities.at(2)->request));
 	}
 
 	SECTION("Error throw on wrong request ID") {