diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9eb53ea82bae755ad23a03385aaf44e1a98ebf1e..54c80cca042fc224c4cb450888ad45a4c9ea4547 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: lycantropos/cmake
+image: lightspot21/acubesat-ci:latest
 
 variables:
   PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
@@ -14,14 +14,6 @@ stages:
   - test
   - deploy
 
-before_script:
-  - apt-get update -qq && apt-get -qq -y install python3-pip && python3 -m pip install --upgrade pip
-  - python3 -V
-  - python3 -m pip --version
-  - g++ --version
-  - cat /etc/*-release
-  - python3 -m pip install gcovr
-
 build:
   stage: build
   variables:
@@ -54,8 +46,6 @@ tests:
 cppcheck:
   stage: build
   before_script:
-    - echo deb http://deb.debian.org/debian testing main > /etc/apt/sources.list
-    - apt-get update -qq && apt-get -t testing install -y cppcheck
     - cppcheck --version
   script:
     - ci/cppcheck.sh
@@ -63,9 +53,6 @@ cppcheck:
 cppcheck-misra:
   stage: build
   before_script:
-  # install cppcheck from the testing repos in order to get the latest version
-    - echo deb http://deb.debian.org/debian testing main > /etc/apt/sources.list
-    - apt-get update -qq && apt-get -t testing install -y cppcheck && apt-get -t testing install -y python3
     - cppcheck --version
   script:
     - ci/cppcheck-misra.sh
@@ -73,7 +60,6 @@ cppcheck-misra:
 vera:
   stage: build
   before_script:
-    - apt-get update -qq && apt-get install -y -qq vera++
     - vera++ --version
     - cp ci/vera.profile /usr/lib/vera++/profiles/custom
   script:
@@ -84,14 +70,8 @@ clang-tidy:
   variables:
     GIT_SUBMODULE_STRATEGY: normal
     TERM: xterm-color
-  before_script:
-    # Installing the `sid` repository to get the latest version of clang
-    - echo deb http://deb.debian.org/debian sid main > /etc/apt/sources.list
-    - apt-get update -qq && apt-get -t sid install -y -qq clang-tidy-7
-    - clang-tidy-7 --version
   script:
-    # Running with `script` to give clang a tty so that it outputs colours
-    - script -c "bash -x ci/clang-tidy.sh"
+     - ./ci/clang-tidy.sh
 
 pages:
   stage: deploy
@@ -103,8 +83,6 @@ pages:
   variables:
     GIT_SUBMODULE_STRATEGY: normal
   script:
-    - apt-get install -qq -y doxygen graphviz lcov
-  after_script:
     - ./ci/pages_deploy.sh
     - echo -e "\e[1;36mPublic directory contents\e[0m" && ls -l public/coverage  # Print directory contents for debugging
   artifacts:
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..5d89f392d3d1e9e841f887a9f80e9c4d6350792f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,38 @@
+# This is the Dockerfile for the lightspot21/acubesat-ci:latest
+# Docker image used at the pipeline. Please take care to generate 
+# and push a new image to the lightspot21/acubesat-ci repo every 
+# 1-2 weeks in order to ensure that the tools are at the latest version.
+# 
+# P.S. Tag properly your images with --tag lightspot21/acubesat-ci when
+# building.
+
+FROM alpine:latest
+
+# Set a new work directory. DO NOT DELETE THE LINE BELOW
+WORKDIR /root/
+
+# Set up clang-tidy version 8
+RUN echo "@testing http://dl-cdn.alpinelinux.org/alpine/edge/testing" \
+    >> /etc/apk/repositories && apk update && \
+    apk add --no-cache --virtual git-deps git && \
+    apk add --no-cache build-base cmake && \
+    apk add --no-cache python3 && \
+    git clone --depth=1 https://github.com/llvm/llvm-project.git -b release/8.x && \
+    cmake \
+    -DLLVM_ENABLE_PROJECTS="clang-tools-extra;clang" \
+    -DCMAKE_BUILD_TYPE=MinSizeRel \
+    -DLLVM_TARGETS_TO_BUILD="host" \
+    -G "Unix Makefiles" ./llvm-project/llvm && \
+    make -j$(nproc) clang-tidy && mv bin/clang-tidy /usr/bin/clang-tidy && \
+    rm -rf * && apk del git-deps
+
+# Update package lists and install cmake, cppcheck, doxygen, vera++, 
+# gcc and lcov with their dependencies
+RUN apk add --no-cache findutils python3-dev \
+    cppcheck doxygen vera++@testing lcov@testing
+
+# Install gcovr
+RUN python3 -m pip install gcovr
+
+# Start a new shell
+ENTRYPOINT ["/bin/sh", "-c"]
diff --git a/ci/.clang-tidy b/ci/.clang-tidy
index 38beddf8e90c12aa96ede94f6d80bf27cc253aef..478e052795977bbfe87cab6a8dc73eeeee60e665 100644
--- a/ci/.clang-tidy
+++ b/ci/.clang-tidy
@@ -4,7 +4,7 @@ Checks:          >
   clang-analyzer-*,
   bugprone-*,
   cert-*,
-  cppcoreguidelines-*,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-bounds-constant-array-index,
+  cppcoreguidelines-*,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-avoid-c-arrays,
   misc-*,-misc-non-private-member-variables-in-classes,
   fuchsia-multiple-inheritance,
   google-*,-google-readability-todo,
@@ -17,7 +17,7 @@ Checks:          >
   misc-*,
   -misc-non-private-member-variables-in-classes,
   performance-*,
-  readability-*,
+  readability-*,-readability-magic-numbers,
   zircon-*
 WarningsAsErrors: '*,-misc-unused-parameters,-llvm-header-guard,-cppcoreguidelines-pro-type-member-init,-google-runtime-references,-clang-diagnostic-tautological-constant-out-of-range-compare,-readability-redundant-declaration,-modernize-use-equals-default,-fuchsia-statically-constructed-objects,-hicpp-signed-bitwise,-cert-err58-cpp,-clang-diagnostic-error,-misc-noexcept-move-constructor'
 HeaderFilterRegex: 'ecss-services\/((?!lib\/).)*$'
diff --git a/ci/clang-tidy.sh b/ci/clang-tidy.sh
index bdff8166981a4312ab0a2c28423a5359af80ad59..bf45a2eb46252b15df6fd7474720622429c86cc1 100755
--- a/ci/clang-tidy.sh
+++ b/ci/clang-tidy.sh
@@ -10,6 +10,10 @@
 echo -e "\033[0;34mRunning clang-tidy...\033[0m"
 
 cd "$(dirname "$0")"
-clang-tidy-7 `find ../src/ -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` \
-    -extra-arg=-fcolor-diagnostics  -- -std=c++17 -I../inc \
-    -I/usr/include/c++/7/ -I/usr/include/x86_64-linux-gnu/c++/7 -I../lib/etl/include
+GCCVERSION=`g++ -dumpversion`
+
+clang-tidy `find ../src/ -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` \
+    -extra-arg=-fcolor-diagnostics -- -std=c++17 -I../inc -I../lib/etl/include \
+    -I/usr/include/c++/$GCCVERSION -I/usr/include/x86_64-linux-gnu/c++/$GCCVERSION \
+    -I/usr/include/c++/$GCCVERSION/$MACHTYPE
+
diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp
index c129487243e458ba0fd119b92fa73d286e569f02..32b69187d174171fcaee147d8b5973a3145e3de2 100644
--- a/inc/ECSS_Definitions.hpp
+++ b/inc/ECSS_Definitions.hpp
@@ -1,11 +1,16 @@
 #ifndef ECSS_SERVICES_ECSS_DEFINITIONS_H
 #define ECSS_SERVICES_ECSS_DEFINITIONS_H
 
-#define ECSS_MAX_MESSAGE_SIZE 1024u
+#define ECSS_MAX_MESSAGE_SIZE 1024U
 
-#define ECSS_MAX_STRING_SIZE 256u
+#define ECSS_MAX_STRING_SIZE 256U
 
-#define ECSS_MAX_FIXED_OCTET_STRING_SIZE 256u // For the ST13 large packet transfer service
+#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/Helpers/TimeHelper.hpp b/inc/Helpers/TimeHelper.hpp
index 7cd1920d5343fa6b8186f3c377369462a1cc8a91..526b80333fbc584e87edbd3115ca3cf413766030 100644
--- a/inc/Helpers/TimeHelper.hpp
+++ b/inc/Helpers/TimeHelper.hpp
@@ -5,9 +5,9 @@
 #include <Message.hpp>
 #include "TimeAndDate.hpp"
 
-#define SECONDS_PER_MINUTE 60u
-#define SECONDS_PER_HOUR 3600u
-#define SECONDS_PER_DAY 86400u
+#define SECONDS_PER_MINUTE 60U
+#define SECONDS_PER_HOUR 3600U
+#define SECONDS_PER_DAY 86400U
 
 /**
  * @todo If we use CUC time format then we should keep leap seconds up to date. Leap seconds are added in undefined
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/Helpers/CRCHelper.cpp b/src/Helpers/CRCHelper.cpp
index dda87aa34d4a7be7c539ab5642ee48cc81fc6f71..1e6a28fd4c3808bcf5459a6378cd21e4f7d7ef52 100644
--- a/src/Helpers/CRCHelper.cpp
+++ b/src/Helpers/CRCHelper.cpp
@@ -4,23 +4,23 @@
 
 uint16_t CRCHelper::calculateCRC(const uint8_t* message, uint32_t length) {
 	// shift register contains all 1's initially (ECSS-E-ST-70-41C, Annex B - CRC and ISO checksum)
-	uint16_t shiftReg = 0xFFFFu;
+	uint16_t shiftReg = 0xFFFFU;
 
 	// CRC16-CCITT generator polynomial (as specified in standard)
-	uint16_t polynomial = 0x1021u;
+	uint16_t polynomial = 0x1021U;
 
 	for (uint32_t i = 0; i < length; i++) {
 		// "copy" (XOR w/ existing contents) the current msg bits into the MSB of the shift register
-		shiftReg ^= (message[i] << 8u);
+		shiftReg ^= (message[i] << 8U);
 
 		for (int j = 0; j < 8; j++) {
 			// if the MSB is set, the bitwise AND gives 1
-			if ((shiftReg & 0x8000u) != 0u) {
+			if ((shiftReg & 0x8000U) != 0U) {
 				// toss out of the register the MSB and divide (XOR) its content with the generator
-				shiftReg = ((shiftReg << 1u) ^ polynomial);
+				shiftReg = ((shiftReg << 1U) ^ polynomial);
 			} else {
 				// just toss out the MSB and make room for a new bit
-				shiftReg <<= 1u;
+				shiftReg <<= 1U;
 			}
 		}
 	}
diff --git a/src/Helpers/TimeHelper.cpp b/src/Helpers/TimeHelper.cpp
index 16b6e16229b6469d6e0c554385be6e0f0ec665e7..04792af7c90b0be0153d8212aa3af4262673870b 100644
--- a/src/Helpers/TimeHelper.cpp
+++ b/src/Helpers/TimeHelper.cpp
@@ -25,8 +25,8 @@ uint32_t TimeHelper::utcToSeconds(TimeAndDate& TimeInfo) {
 		secs += (IsLeapYear(y) ? 366 : 365) * SECONDS_PER_DAY;
 	}
 	for (uint16_t m = 1; m < TimeInfo.month; ++m) {
-		secs += DaysOfMonth[m - 1u] * SECONDS_PER_DAY;
-		if ((m == 2u) && IsLeapYear(TimeInfo.year)) {
+		secs += DaysOfMonth[m - 1U] * SECONDS_PER_DAY;
+		if ((m == 2U) && IsLeapYear(TimeInfo.year)) {
 			secs += SECONDS_PER_DAY;
 		}
 	}
@@ -62,7 +62,7 @@ struct TimeAndDate TimeHelper::secondsToUTC(uint32_t seconds) {
 		TimeInfo.month++;
 		seconds -= (DaysOfMonth[i] * SECONDS_PER_DAY);
 		i++;
-		if ((i == 1u) && IsLeapYear(TimeInfo.year)) {
+		if ((i == 1U) && IsLeapYear(TimeInfo.year)) {
 			if (seconds <= (28 * SECONDS_PER_DAY)) {
 				break;
 			}
@@ -121,7 +121,7 @@ TimeAndDate TimeHelper::parseCDStimeFormat(const uint8_t* data) {
 	uint32_t msOfDay = ((static_cast<uint32_t>(data[2])) << 24) | ((static_cast<uint32_t>(data[3]))) << 16 |
 	                   ((static_cast<uint32_t>(data[4]))) << 8 | (static_cast<uint32_t>(data[5]));
 
-	uint32_t seconds = (elapsedDays * SECONDS_PER_DAY) + (msOfDay / 1000u);
+	uint32_t seconds = (elapsedDays * SECONDS_PER_DAY) + (msOfDay / 1000U);
 
 	return secondsToUTC(seconds);
 }
diff --git a/src/Message.cpp b/src/Message.cpp
index efedb65c16b2388fc6a87326cb0ffbcd35f2fac9..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) {
@@ -84,7 +89,7 @@ uint16_t Message::readBits(uint8_t numBits) {
 		if ((currentBit + numBits) >= 8) {
 			auto bitsToAddNow = static_cast<uint8_t>(8 - currentBit);
 
-			uint8_t mask = ((1u << bitsToAddNow) - 1u);
+			uint8_t mask = ((1U << bitsToAddNow) - 1U);
 			uint8_t maskedData = data[readPosition] & mask;
 			value |= maskedData << (numBits - bitsToAddNow);
 
diff --git a/src/MessageParser.cpp b/src/MessageParser.cpp
index 29300cdbf7c0b761f443c7ee189f6f59bfb4589b..656e0756734d5ec788aae0a043e138a2f231451c 100644
--- a/src/MessageParser.cpp
+++ b/src/MessageParser.cpp
@@ -52,10 +52,10 @@ Message MessageParser::parse(uint8_t* data, uint32_t length) {
 	auto sequenceFlags = static_cast<uint8_t>(packetSequenceControl >> 14);
 
 	// Returning an internal error, since the Message is not available yet
-	ASSERT_INTERNAL(versionNumber == 0u, ErrorHandler::UnacceptablePacket);
-	ASSERT_INTERNAL(secondaryHeaderFlag == 1u, ErrorHandler::UnacceptablePacket);
-	ASSERT_INTERNAL(sequenceFlags == 0x3u, ErrorHandler::UnacceptablePacket);
-	ASSERT_INTERNAL(packetDataLength == (length - 6u), ErrorHandler::UnacceptablePacket);
+	ASSERT_INTERNAL(versionNumber == 0U, ErrorHandler::UnacceptablePacket);
+	ASSERT_INTERNAL(secondaryHeaderFlag == 1U, ErrorHandler::UnacceptablePacket);
+	ASSERT_INTERNAL(sequenceFlags == 0x3U, ErrorHandler::UnacceptablePacket);
+	ASSERT_INTERNAL(packetDataLength == (length - 6U), ErrorHandler::UnacceptablePacket);
 
 	Message message(0, 0, packetType, APID);
 
@@ -78,7 +78,7 @@ void MessageParser::parseTC(const uint8_t* data, uint16_t length, Message& messa
 
 	// todo: Fix this parsing function, because it assumes PUS header in data, which is not true
 	//  with the current implementation
-	ErrorHandler::assertRequest(pusVersion == 2u, message, ErrorHandler::UnacceptableMessage);
+	ErrorHandler::assertRequest(pusVersion == 2U, message, ErrorHandler::UnacceptableMessage);
 
 	// Remove the length of the header
 	length -= 5;
@@ -126,7 +126,7 @@ void MessageParser::parseTM(const uint8_t* data, uint16_t length, Message& messa
 	uint8_t serviceType = data[1];
 	uint8_t messageType = data[2];
 
-	ErrorHandler::assertRequest(pusVersion == 2u, message, ErrorHandler::UnacceptableMessage);
+	ErrorHandler::assertRequest(pusVersion == 2U, message, ErrorHandler::UnacceptableMessage);
 
 	// Remove the length of the header
 	length -= 5;
diff --git a/src/Platform/x86/main.cpp b/src/Platform/x86/main.cpp
index 2e15a3afd40df45d1dbfda53d9b624ddfab7542e..57ad74b65072e65d5b56ada6adafe1bc03a5ca6e 100644
--- a/src/Platform/x86/main.cpp
+++ b/src/Platform/x86/main.cpp
@@ -307,7 +307,8 @@ int main() {
 	std::cout << "\nCurrent time in seconds (UNIX epoch): " << currentTime << std::endl;
 
 	Message receivedMsg = Message(11, 1, Message::TC, 1);
-	Message testMessage1(6, 5, Message::TC, 1), testMessage2(4, 5, Message::TC, 1);
+	Message testMessage1(6, 5, Message::TC, 1);
+	Message testMessage2(4, 5, Message::TC, 1);
 	testMessage1.appendUint16(4253); // Append dummy data
 	testMessage2.appendUint16(45667); // Append dummy data
 
@@ -317,10 +318,10 @@ int main() {
 	receivedMsg = Message(11, 4, Message::TC, 1);
 	receivedMsg.appendUint16(2); // Total number of requests
 
-	receivedMsg.appendUint32(currentTime + 1556435u);
+	receivedMsg.appendUint32(currentTime + 1556435U);
 	receivedMsg.appendString(msgParser.convertTCToStr(testMessage1));
 
-	receivedMsg.appendUint32(currentTime + 1957232u);
+	receivedMsg.appendUint32(currentTime + 1957232U);
 	receivedMsg.appendString(msgParser.convertTCToStr(testMessage2));
 	timeBasedSchedulingService.insertActivities(receivedMsg);
 
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/src/Services/EventActionService.cpp b/src/Services/EventActionService.cpp
index e3788002c83a8c7b175e8c150a91880919c8f4f7..9ed4f9b6213752afc9b74efc49cf30fff61b1819 100644
--- a/src/Services/EventActionService.cpp
+++ b/src/Services/EventActionService.cpp
@@ -77,7 +77,7 @@ void EventActionService::enableEventActionDefinitions(Message& message) {
 	// TC[19,4]
 	message.assertTC(19, 4);
 	uint16_t numberOfEventActionDefinitions = message.readUint16();
-	if (numberOfEventActionDefinitions != 0u) {
+	if (numberOfEventActionDefinitions != 0U) {
 		for (uint16_t i = 0; i < numberOfEventActionDefinitions; i++) {
 			message.skipBytes(2); // Skips reading the application ID
 			uint16_t eventDefinitionID = message.readEnum16();
@@ -110,7 +110,7 @@ void EventActionService::disableEventActionDefinitions(Message& message) {
 	// TC[19,5]
 	message.assertTC(19, 5);
 	uint16_t numberOfEventActionDefinitions = message.readUint16();
-	if (numberOfEventActionDefinitions != 0u) {
+	if (numberOfEventActionDefinitions != 0U) {
 		for (uint16_t i = 0; i < numberOfEventActionDefinitions; i++) {
 			message.skipBytes(2); // Skips reading applicationID
 			uint16_t eventDefinitionID = message.readEnum16();
diff --git a/src/Services/EventReportService.cpp b/src/Services/EventReportService.cpp
index cbf8ee72ae08d669e65537d6cd6d08314ef28c41..baf2ba484347c417a6400cbf73f990baee8798bc 100644
--- a/src/Services/EventReportService.cpp
+++ b/src/Services/EventReportService.cpp
@@ -120,7 +120,7 @@ void EventReportService::listOfDisabledEventsReport() {
 
 	uint16_t numberOfDisabledEvents = stateOfEvents.size() - stateOfEvents.count();
 	report.appendHalfword(numberOfDisabledEvents);
-	for (uint16_t i = 0; i < stateOfEvents.size(); i++) {
+	for (size_t i = 0; i < stateOfEvents.size(); i++) {
 		if (not stateOfEvents[i]) {
 			report.appendEnum16(i);
 		}
diff --git a/src/Services/LargePacketTransferService.cpp b/src/Services/LargePacketTransferService.cpp
index 15b0e3c3609c41f242a6f6830f570b5598a30f9a..95b3cafafd1e89b51999563dd8473bd2c989275c 100644
--- a/src/Services/LargePacketTransferService.cpp
+++ b/src/Services/LargePacketTransferService.cpp
@@ -69,7 +69,7 @@ void LargePacketTransferService::split(Message& message, uint16_t largeMessageTr
 	stringPart = dataPart;
 	firstDownlinkPartReport(largeMessageTransactionIdentifier, 0, stringPart);
 
-	for (uint16_t part = 1; part < (parts - 1u); part++){
+	for (uint16_t part = 1; part < (parts - 1U); part++){
 		for (uint16_t i = 0; i < ECSS_MAX_FIXED_OCTET_STRING_SIZE; i++){
 			dataPart[i] = message.data[positionCounter];
 			positionCounter++;
@@ -86,5 +86,5 @@ void LargePacketTransferService::split(Message& message, uint16_t largeMessageTr
 		positionCounter++;
 	}
 	stringPart = dataPart;
-	lastDownlinkPartReport(largeMessageTransactionIdentifier, (parts - 1u), stringPart);
+	lastDownlinkPartReport(largeMessageTransactionIdentifier, (parts - 1U), stringPart);
 }
diff --git a/src/Services/ParameterService.cpp b/src/Services/ParameterService.cpp
index 44d766820f6eac6e67de2e502a52db608c7b5eac..018de09f2a197bc8f87a66b518d5618e5b16ee26 100644
--- a/src/Services/ParameterService.cpp
+++ b/src/Services/ParameterService.cpp
@@ -12,12 +12,12 @@ ParameterService::ParameterService() {
 	time_t currTime = time(nullptr);
 	struct tm* today = localtime(&currTime);
 
-	Parameter test1, test2;
-
+	Parameter test1;
 	test1.settingData = today->tm_hour; // the current hour
 	test1.ptc = 3; // unsigned int
 	test1.pfc = 14; // 32 bits
 
+	Parameter test2;
 	test2.settingData = today->tm_min; // the current minute
 	test2.ptc = 3; // unsigned int
 	test2.pfc = 14; // 32 bits
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") {