diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9eb53ea82bae755ad23a03385aaf44e1a98ebf1e..0d90274bf9e0327fe706ffcfa337b49322e0bb3e 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"
@@ -15,7 +15,7 @@ stages:
   - deploy
 
 before_script:
-  - apt-get update -qq && apt-get -qq -y install python3-pip && python3 -m pip install --upgrade pip
+  - apt-get update -qq && apt-get -qq -y install libstdc++6 python3-pip && python3 -m pip install --upgrade pip
   - python3 -V
   - python3 -m pip --version
   - g++ --version
@@ -54,8 +54,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 +61,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 +68,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 +78,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 +91,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/CMakeLists.txt b/CMakeLists.txt
index 59845186d702025b9ab7ce607c58ce8affbd6093..d75c731545f27339a595ce38b1f89a33a99d7c42 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,10 +14,17 @@ add_custom_target(check
         COMMAND ./cppcheck-misra.sh
         WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/ci")
 
+# Allow the user to set the ECSS Configuration directory using the -DECSS_CONFIGURATION=... command line argument
+set(ECSS_CONFIGURATION "${PROJECT_SOURCE_DIR}/inc/Platform/x86" CACHE PATH
+        "The include directory for the platform-specific ECSS configuration headers"
+        )
+include_directories(${ECSS_CONFIGURATION})
+
 # Specify the .cpp files common across all targets
 add_library(common OBJECT
         src/Service.cpp
         src/ErrorHandler.cpp
+        src/Logger.cpp
         src/Message.cpp
         src/MessageParser.cpp
         src/ServicePool.cpp
@@ -40,7 +47,6 @@ add_library(common OBJECT
 # Specify the .cpp files for the executables
 file(GLOB x86_main_SRC "src/Platform/x86/*.cpp")
 add_executable(ecss_services
-        src/main.cpp
         $<TARGET_OBJECTS:common>
         ${x86_main_SRC}
         )
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..3ac4810e6a55343c491976dff0e11eac2a146689 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../inc/Platform/x86 \
+    -I/usr/include/c++/$GCCVERSION -I/usr/include/x86_64-linux-gnu/c++/$GCCVERSION \
+    -I/usr/include/c++/$GCCVERSION/$MACHTYPE
+
diff --git a/ci/cppcheck-misra.sh b/ci/cppcheck-misra.sh
index 737eb6f217aeb121635dd5cf225b4d3121f1c7e9..b1313b80c7e7cee69cd69e42c85dd2fee6424223 100755
--- a/ci/cppcheck-misra.sh
+++ b/ci/cppcheck-misra.sh
@@ -11,9 +11,12 @@
 cd "$(dirname "$0")/.."
 
 echo -e "\u001b[34;1mGetting prerequisites...\u001b[0m"
+# get the current cppcheck version
+CPCVERSION=`cppcheck --version | awk '{print $2}'`
+
 # grab the MISRA addon and the cppcheck addon interface from github
-curl https://raw.githubusercontent.com/danmar/cppcheck/master/addons/misra.py > ci/misra.py
-curl https://raw.githubusercontent.com/danmar/cppcheck/master/addons/cppcheckdata.py > ci/cppcheckdata.py
+curl https://raw.githubusercontent.com/danmar/cppcheck/$CPCVERSION/addons/misra.py > ci/misra.py
+curl https://raw.githubusercontent.com/danmar/cppcheck/$CPCVERSION/addons/cppcheckdata.py > ci/cppcheckdata.py
 
 # generate dump files (XML representations of AST etc.) for all headers, source files etc.
 echo -e "\u001b[34;1mGenerating dump files...\u001b[0m"
@@ -30,8 +33,13 @@ sed -i -r 's/(.*Script.*)|(.*Checking.*)|(.*MISRA.*)|(.*Undefined: .*)|(.* \(-\)
 # run the summarizer for a nice, clean summary of errors
 echo -e "\u001b[34;1mSummarizing results...\u001b[0m"
 python3 ci/summarizer.py --report ci/report.msr --suppress 3.1 5.1 5.2 5.3 12.3 13.4 14.4 15.5 16.3 18.4 18.8
+EXIT_CODE=$?
 
 # clean up old files
 echo -e "\u001b[34;1mRemoving dump files...\u001b[0m"
 echo > ci/report.msr # clear the report file
 find inc/ src/ -type f -name "*.dump" | xargs rm -rf
+
+# finally return the return value of the summarizer.py script
+exit $EXIT_CODE
+
diff --git a/ci/cppcheck.sh b/ci/cppcheck.sh
index 2179a051c88e90de9c07d7f9898422c4c38beb6f..d0be486ccf6049895d93a76f070687dcb5c2e775 100755
--- a/ci/cppcheck.sh
+++ b/ci/cppcheck.sh
@@ -13,4 +13,4 @@ echo -e "\u001b[34;1mRunning cppcheck with default checklist...\u001b[0m"
 
 cd "$(dirname "$0")/.."
 cppcheck --enable=all --suppress=unusedFunction --suppress=noExplicitConstructor \
-    --inline-suppr --error-exitcode=1 -I inc src test
+    --force --inline-suppr --error-exitcode=1 -I inc src test
diff --git a/ci/summarizer.py b/ci/summarizer.py
index 15e48d03c61bffd4caeb8c43804393abbf00016e..5139df77ab8d09d9c8acf4d90b3cd3471023f3dd 100755
--- a/ci/summarizer.py
+++ b/ci/summarizer.py
@@ -58,7 +58,7 @@ class Summarizer(object):
                 with open(os.path.abspath(file_name)) as code_file:
                     code_lines = code_file.readlines()  # Read the source code file
                     line_of_interest = code_lines[int(violation[0]) - 1]  # Get the desired violation line
-                if line_of_interest.find("// Ignore-MISRA") >= 0:
+                if line_of_interest.find("// Ignore-MISRA") >= 0 or line_of_interest.find("/* Ignore-MISRA */") >= 0:
                     continue
 
                 if file_name not in self.violations_map.keys():
diff --git a/ci/vera.sh b/ci/vera.sh
index 4481cfb2b2b49d904725a211d4c58b51fe4c4048..2ece81da50bfefdefba08effef4bbadc3f7d415f 100755
--- a/ci/vera.sh
+++ b/ci/vera.sh
@@ -10,4 +10,5 @@
 echo -e "\033[0;34mRunning vera++...\033[0m"
 
 cd "$(dirname "$0")/.."
-vera++ --error --parameter max-line-length=120 --profile custom `find src inc test -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'`
+vera++ --error --parameter max-line-length=120 --profile custom \
+    `find src inc test -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)' -not -wholename 'inc/Logger.hpp'`
diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp
index 184e969afa195bf6fc1003da4d745abd5255e4fd..c59a2624d5c4ba9b77f3dc68db1aa4284b0f4d2d 100644
--- a/inc/ECSS_Definitions.hpp
+++ b/inc/ECSS_Definitions.hpp
@@ -1,19 +1,66 @@
 #ifndef ECSS_SERVICES_ECSS_DEFINITIONS_H
 #define ECSS_SERVICES_ECSS_DEFINITIONS_H
 
-#define ECSS_MAX_MESSAGE_SIZE 1024u
+/**
+ * @defgroup ECSSDefinitions ECSS Defined Constants
+ *
+ * This file contains constant definitions that are used throughout the ECSS services. They often refer to maximum
+ * values and upper limits for the storage of data in the services.
+ *
+ * @todo All these constants need to be redefined and revised after the design and the requirements are finalized.
+ *
+ * @{
+ */
+
+/**
+ * @file
+ * This file contains constant definitions that are used throughout the ECSS services.
+ * @see ECSSDefinitions
+ */
+
+/**
+ * The maximum size of a regular ECSS message, in bytes
+ */
+#define ECSS_MAX_MESSAGE_SIZE 1024U
+
+
+/**
+ * The maximum size of a regular ECSS message, plus its headers and trailing data, in bytes
+ */
+#define CCSDS_MAX_MESSAGE_SIZE (ECSS_MAX_MESSAGE_SIZE + 6u + 6u + 2u)
 
-#define ECSS_MAX_STRING_SIZE 256u
+/**
+ * The maximum size of a string to be read or appended to a Message, in bytes
+ *
+ * This is used by the Message::appendString() and Message::readString() functions
+ */
+#define ECSS_MAX_STRING_SIZE 256U
+
+/**
+ * The maximum size of a string to be used by ST[13] \ref LargePacketTransferService, in bytes
+ *
+ * This is used by the Message::appendString() and Message::readString() functions
+ */
+#define ECSS_MAX_FIXED_OCTET_STRING_SIZE 256U
 
-#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 (10U * 20U)
 
-// 7.4.1
+/**
+ * The CCSDS packet version, as specified in section 7.4.1
+ */
 #define CCSDS_PACKET_VERSION 0
 
-// 7.4.4.1c
+/**
+ * The ECSS packet version, as specified in requirement 7.4.4.1c
+ */
 #define ECSS_PUS_VERSION 2U
 
-// 9.3.1a.1.e
+/**
+ * The CCSDS sequence flags have the constant value 0x3, as specified in section 7.4.1
+ */
 #define ECSS_SEQUENCE_FLAGS 0x3
 
 /**
@@ -24,7 +71,7 @@
  * their message request.
  * @attention This definition is probably dependent on the ECSS_TC_REQUEST_STRING_SIZE
  */
-#define ECSS_MAX_REQUEST_COUNT 20 // todo: Needs to be defined
+#define ECSS_MAX_REQUEST_COUNT 20
 
 /**
  * @brief Maximum length of a String converted TC packet message
@@ -33,25 +80,66 @@
  */
 #define ECSS_TC_REQUEST_STRING_SIZE 64
 
-// todo: Define the maximum number of activities
+/**
+ * The maximum number of activities that can be in the time-based schedule
+ * @see TimeBasedSchedulingService
+ */
 #define ECSS_MAX_NUMBER_OF_TIME_SCHED_ACTIVITIES 10
 
 /**
  * @brief Time margin used in the time based command scheduling service ST[11]
  * @details This defines the time margin in seconds, from the current rime, that an activity must
  * have in order
- * @todo Define the time margin for the command activation
+ * @see TimeBasedSchedulingService
  */
 #define ECSS_TIME_MARGIN_FOR_ACTIVATION 60
 
+/**
+ * @brief Maximum size of an event's auxiliary data
+ * @see EventReportService
+ */
+#define ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE 64
+
 /**
  * @brief Size of the multimap that holds every event-action definition
+ * @see EventActionService
  */
 #define ECSS_EVENT_ACTION_STRUCT_MAP_SIZE 256
 
-// todo: Define the maximum delta between the specified
+/**
+ * The maximum delta between the specified release time and the actual release time
+ * @see TimeBasedSchedulingService
+ */
 #define ECSS_MAX_DELTA_OF_RELEASE_TIME 60
-// release time and the actual release time
+
+/**
+ * The maximum number of stored parameters in the \ref ParameterService
+ */
+#define ECSS_MAX_PARAMETERS 5
+
+/**
+ * The number of functions supported by the \ref FunctionManagementService
+ */
+#define ECSS_FUNCTION_MAP_SIZE 5
+
+/**
+ * The maximum length of a function name, in bytes
+ * @see FunctionManagementService
+ */
+#define ECSS_FUNCTION_NAME_LENGTH 32
+
+/**
+ * The maximum length of the argument of a function
+ * @see FunctionManagementService
+ */
+#define ECSS_FUNCTION_MAX_ARG_LENGTH 32
+
+/** @} */
+
+/**
+ * @brief The maximum size of a log message
+ */
+#define LOGGER_MAX_MESSAGE_SIZE 512
 
 /**
  * @brief Size of the map holding the Parameter objects for the ST[20] parameter service
diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp
index 5dd1c6fb2064dd31a0ac893dda282493bf69b9a3..07510f25615205b3c3d46e226f10ed5bef065570 100644
--- a/inc/ErrorHandler.hpp
+++ b/inc/ErrorHandler.hpp
@@ -76,7 +76,11 @@ public:
 		/**
 		 * Attempt to overwrite an existing parameter (ST[20])
 		 */
-		ExistingParameterId = 11
+		ExistingParameterId = 11,
+		/**
+		 * A Message that is included within another message is too large
+		 */
+		NestedMessageTooLarge = 12
 	};
 
 	/**
@@ -229,11 +233,17 @@ public:
 	 * Reports a failure that occurred internally, not due to a failure of a received packet.
 	 *
 	 * Creates an error if \p condition is false. The created error is Internal.
+	 *
+	 * @param condition The condition to check. Throws an error if false.
+	 * @param errorCode The error code that is assigned to this error. One of the \ref ErrorHandler enum values.
+	 * @return Returns \p condition, i.e. true if the assertion is successful, false if not.
 	 */
-	static void assertInternal(bool condition, InternalErrorType errorCode) {
+	static bool assertInternal(bool condition, InternalErrorType errorCode) {
 		if (not condition) {
 			reportInternalError(errorCode);
 		}
+
+		return condition;
 	}
 
 	/**
@@ -242,12 +252,19 @@ public:
 	 * Reports a failure that occurred while processing a request, in any of the process phases.
 	 *
 	 * Creates an error if \p condition is false. The created error corresponds to a \p message.
+	 *
+	 * @param condition The condition to check. Throws an error if false.
+	 * @param message The message to associate with this error
+	 * @param errorCode The error code that is assigned to this error. One of the \ref ErrorHandler enum values.
+	 * @return Returns \p condition, i.e. true if the assertion is successful, false if not.
 	 */
 	template <typename ErrorType>
-	static void assertRequest(bool condition, const Message& message, ErrorType errorCode) {
+	static bool assertRequest(bool condition, const Message& message, ErrorType errorCode) {
 		if (not condition) {
 			reportError(message, errorCode);
 		}
+
+		return condition;
 	}
 
 	/**
diff --git a/inc/Helpers/TimeHelper.hpp b/inc/Helpers/TimeHelper.hpp
index 7cd1920d5343fa6b8186f3c377369462a1cc8a91..027e078e817bf6748ac693821d7527a9e5c7c811 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
@@ -33,7 +33,7 @@
  * decides what the time unit will be, so this is a subject for discussion. The recommended time unit from the
  * standard is the second and it is probably the best solution for accuracy.
  *
- * @notes
+ * @note
  * The defined epoch for both time formats is 1 January 1958 00:00:00
  *
  * Since CDS format is UTC-based, the leap second correction must be made. The leap seconds that
@@ -56,7 +56,7 @@ public:
 	/**
 	 * Convert UTC date to elapsed seconds since Unix epoch (1/1/1970 00:00:00).
 	 *
-	 * This is a reimplemented mktime() of <ctime> library in an embedded systems way
+	 * This is a reimplemented `mktime()` of the `<ctime>` library in an embedded compatible way
 	 *
 	 * @note
 	 * This function can convert UTC dates after 1 January 2019 to elapsed seconds since Unix epoch
@@ -71,7 +71,7 @@ public:
 	/**
 	 * Convert elapsed seconds since Unix epoch to UTC date.
 	 *
-	 * This is a reimplemented gmtime() of <ctime> library in an embedded systems way
+	 * This is a reimplemented `gmtime()` of the `<ctime>` library in an embedded compatible way
 	 *
 	 * @note
 	 * This function can convert elapsed seconds since Unix epoch to UTC dates after 1 January 2019
@@ -110,7 +110,7 @@ public:
 	 *
 	 * Converts a UTC date to CUC time format.
 	 *
-	 * @notes
+	 * @note
 	 * The T-field is specified for the seconds passed from the defined epoch 1 January 1958. We use 4 octets(32
 	 * bits) for the time unit (seconds) because 32 bits for the seconds are enough to count 136 years! But if we use 24
 	 * bits for the seconds then it will count 0,5 years and this isn't enough. Remember we can use only integers
diff --git a/inc/Logger.hpp b/inc/Logger.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5e4b7f21546360ce2d71194b6fc2c7eae4a7cd3
--- /dev/null
+++ b/inc/Logger.hpp
@@ -0,0 +1,225 @@
+#ifndef ECSS_SERVICES_LOGGER_HPP
+#define ECSS_SERVICES_LOGGER_HPP
+
+#include <cstdint>
+#include <etl/String.hpp>
+#include <etl/to_string.h>
+#include "ECSS_Configuration.hpp"
+#include "ECSS_Definitions.hpp"
+
+#if defined LOGLEVEL_TRACE
+#define LOGLEVEL Logger::trace // Ignore-MISRA
+#elif defined LOGLEVEL_DEBUG
+#define LOGLEVEL Logger::debug // Ignore-MISRA
+#elif defined LOGLEVEL_INFO
+#define LOGLEVEL Logger::info // Ignore-MISRA
+#elif defined LOGLEVEL_NOTICE
+#define LOGLEVEL Logger::notice // Ignore-MISRA
+#elif defined LOGLEVEL_WARNING
+#define LOGLEVEL Logger::warning // Ignore-MISRA
+#elif defined LOGLEVEL_ERROR
+#define LOGLEVEL Logger::error // Ignore-MISRA
+#elif defined LOGLEVEL_EMERGENCY
+#define LOGLEVEL Logger::emergency // Ignore-MISRA
+#elif !defined LOGLEVEL
+#define LOGLEVEL Logger::disabled // Ignore-MISRA
+#endif
+
+#define LOG_TRACE     (LOG<Logger::trace>())     ///< @see LOG @relates Logger
+#define LOG_DEBUG     (LOG<Logger::debug>())     ///< @see LOG @relates Logger
+#define LOG_INFO      (LOG<Logger::info>())      ///< @see LOG @relates Logger
+#define LOG_NOTICE    (LOG<Logger::notice>())    ///< @see LOG @relates Logger
+#define LOG_WARNING   (LOG<Logger::warning>())   ///< @see LOG @relates Logger
+#define LOG_ERROR     (LOG<Logger::error>())     ///< @see LOG @relates Logger
+#define LOG_EMERGENCY (LOG<Logger::emergency>()) ///< @see LOG @relates Logger
+
+/**
+ * A logging class for ECSS Services that supports ETL's String and is lightweight enough to be used in embedded
+ * development.
+ *
+ * @note Always use the \ref LOG function and its associated utility macros to log. Do not directly use the Logger
+ * class.
+ */
+class Logger {
+public:
+	/**
+	 * No need to instantiate a Logger object for now.
+	 */
+	Logger() = delete;
+
+	/**
+	 * The underlying type to be used for values of Logger::LogLevel.
+	 */
+	typedef uint8_t LogLevelType;
+
+	/**
+	 * ETL's string format specification, to be used for all logged messages
+	 */
+	static etl::format_spec format;
+
+	/**
+	 * Log levels supported by the logger. Each level represents a different severity of the logged Message,
+	 * and messages of lower severities can be filtered on top of more significant ones.
+	 *
+	 * Each severity is tied to a number. The higher the number, the higher the severity.
+	 */
+	enum LogLevel : LogLevelType {
+		trace = 32, ///< Very detailed information, useful for tracking the individual steps of an operation
+		debug = 64, ///< General debugging information
+		info = 96, ///< Noteworthy or periodical events
+		notice = 128, ///< Uncommon but expected events
+		warning = 160, ///< Unexpected events that do not compromise the operability of a function
+		error = 192, ///< Unexpected failure of an operation
+		emergency = 254, ///< Unexpected failure that renders the entire system unusable
+		disabled = 255, ///< Use this log level to disable logging entirely. No message should be logged as disabled.
+	};
+
+	/**
+	 * An empty enum representing a dummy log entry that will not be logged due to an insufficient level.
+	 *
+	 * @internal
+	 */
+	enum class NoLogEntry {};
+
+	/**
+	 * A class that defines a log message.
+	 *
+	 * Instead of using this class, prefer one of the above macros.
+	 * @see LOG
+	 * @internal
+	 */
+	struct LogEntry {
+		String<LOGGER_MAX_MESSAGE_SIZE> message = ""; ///< The current log message itself, starting from a blank slate
+		LogLevel level; ///< The log level of this message
+
+		explicit LogEntry(LogLevel level); ///< Create a new LogEntry
+
+		/**
+		 * The LogEntry destructor gets called whenever a log message is finalized, and ready to be shown to the
+		 * user. This function is responsible for calling the Logger::log function.
+		 *
+		 * According to the C++ standard, a variable used only within an expression will be immediately destroyed once
+		 * the processing of this expression is over. This allows a syntax such as `LogEntry(...) << "some" << "text"`,
+		 * where the destructor will be called strictly **after** all the `<<` operations have been completed. This
+		 * allows the destructor to send the finalized log entry for further processing.
+		 */
+		~LogEntry();
+
+		LogEntry(LogEntry const&) = delete; ///< Unimplemented copy constructor
+
+		/**
+		 * Stream operator to append new values to a log record
+		 *
+		 * @tparam T The type of value to append
+		 * @param value The new value to add
+         * @todo See if noexcept can be added here without triggering warnings
+		 * @return The current Logger::LogEntry where the value has been appended
+		 */
+		template <class T>
+		Logger::LogEntry& operator<<(const T value) {
+			etl::to_string(value, message, format, true);
+
+			return *this;
+		}
+
+		Logger::LogEntry& operator<<(const std::string& value);
+	};
+
+	/**
+	 * Returns whether a log entry of level \p level is logged, based on the compilation constants
+	 * @param level The level of the log entry
+	 * @return True if the logging is enabled for \p level, false if not
+	 */
+	static constexpr bool isLogged(LogLevelType level) {
+		return static_cast<LogLevelType>(LOGLEVEL) <= level;
+	}
+
+	/**
+	 * Store a new log message
+	 */
+	static void log(LogLevel level, etl::istring & message);
+};
+
+/**
+ * Create a stream to log a Message
+ *
+ * This functions appends one line to the Logs (which could be printed to screen, transferred via UART or stored for
+ * later use.)
+ *
+ * Examples of usage:
+ * @code
+ * LOG<Logger::debug>() << "Reached point of no return";
+ * LOG<Logger::error>() << "More than " << 50 << " dogs found!";
+ * @endcode
+ *
+ * You can also use one of the \ref LOG_TRACE, \ref LOG_DEBUG, \ref LOG_INFO, \ref LOG_NOTICE, \ref LOG_WARNING,
+ * \ref LOG_ERROR or \ref LOG_EMERGENCY defines, which avoid the need of explicitly passing the log level:
+ * @code
+ * LOG_DEBUG << "Reached point of no return";
+ * LOG_ERROR << "More than " << 50 << " dogs found!";
+ * @endcode
+ *
+ * See \ref Logger::LogLevel for an explanation of the different log levels.
+ *
+ * @par Implementation details
+ * Functions here are defined as `constexpr` in order to let them be optimized as soon as possible. The LOG()
+ * function returns an instance of \ref Logger::LogEntry if the level is high enough to be shown, or an instance of
+ * \ref Logger::NoLogEntry if the log entry will not be displayed. As this is a templated function, it is acceptable
+ * to support different return types using the `auto` keyword.
+ *
+ * @warning For messages that will not be logged, any calls to functions that contain **side effects will still take
+ * place**.
+ * @code
+ * LOG_DEBUG << "The temperature is: " << getTemperature();
+ * @endcode
+ * In the above example, if `getTemperature()` will cause a side effect (e.g. an I2C connection or a `std::cout` print),
+ * it will still be executed, even if the debug message will not be printed to the screen due to an insufficient
+ * LOGLEVEL.
+ *
+ * @section GlobalLogLevels Global log levels
+ * The **global log level** defines the minimum severity of events to be displayed. Log entries with a severity equal
+ * to or higher than the global log level will be shown. Log entries with a severity smaller than the global log level
+ * will not be shown.
+ *
+ * The global log level can be set by defining one of the following constants:
+ * - `LOGLEVEL_TRACE`
+ * - `LOGLEVEL_DEBUG`
+ * - `LOGLEVEL_INFO`
+ * - `LOGLEVEL_NOTICE`
+ * - `LOGLEVEL_WARNING`
+ * - `LOGLEVEL_ERROR`
+ * - `LOGLEVEL_EMERGENCY`
+ *
+ * @relates Logger
+ * @tparam level The log level. A value of \ref Logger::LogLevel
+ * @return Returns \ref Logger::LogEntry if the level is sufficient to be logged, or \ref Logger::NoLogEntry if the
+ * message will not be logged. This is determined at compile-time.
+ */
+template <Logger::LogLevel level>
+constexpr __attribute__((always_inline)) inline auto LOG() {
+	if constexpr (Logger::isLogged(level)) {
+		return Logger::LogEntry(level);
+	} else {
+		return Logger::NoLogEntry();
+	}
+};
+
+/**
+ * A no-op function that considers an empty log entry that will not be displayed, processed or stored.
+ *
+ * @warning Note that functions containing **side effects** will get properly executed. Only use functions that return
+ * plain values as parts of the log function, so they might be optimized away at compile time.
+ *
+ * @tparam T The type of the data that will be ignored
+ * @param noLogEntry A dummy no-op log entry
+ * @param value The data that will be ignored
+ * @return A dummy no-op log entry
+ * @see Logger::LogEntry::operator<<(const T value)
+ * @relates Logger::LogEntry
+ */
+template <typename T>
+[[maybe_unused]] constexpr Logger::NoLogEntry operator<<(const Logger::NoLogEntry noLogEntry, T value) {
+	return noLogEntry;
+}
+
+#endif //ECSS_SERVICES_LOGGER_HPP
diff --git a/inc/Message.hpp b/inc/Message.hpp
index 6cd0ef1d73f06c6a2dda9fad42d17c8f4c86f3e4..f94c31bea5cf52138caec588a6f2ec3803794459 100644
--- a/inc/Message.hpp
+++ b/inc/Message.hpp
@@ -33,29 +33,46 @@ 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
+	 * @param msg The message content to compare against
 	 * @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 {
-		TM = 0, // Telemetry
-		TC = 1 // Telecommand
+		TM = 0, ///< Telemetry
+		TC = 1 ///< Telecommand
 	};
 
 	// The service and message IDs are 8 bits (5.3.1b, 5.3.3.1d)
@@ -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();
@@ -140,8 +159,7 @@ public:
 	 *
 	 * @param string The string to insert
 	 */
-	template <const size_t SIZE>
-	void appendString(const String<SIZE>& string);
+	void appendString(const etl::istring& string);
 
 	/**
 	 * Appends a number of bytes to the message
@@ -156,8 +174,7 @@ public:
 	 *
 	 * @param string The string to insert
 	 */
-	template <const size_t SIZE>
-	void appendFixedString(const String<SIZE>& string);
+	void appendFixedString(const etl::istring& string);
 
 	/**
 	 * Reads the next \p numBits bits from the the message in a big-endian format
@@ -339,14 +356,18 @@ public:
 	 *
 	 * PTC = 7, PFC = 0
 	 */
-	template <const size_t SIZE>
-	void appendOctetString(const String<SIZE>& string) {
-		// Make sure that the string is large enough to count
-		ASSERT_INTERNAL(string.size() <= (std::numeric_limits<uint16_t>::max)(), ErrorHandler::StringTooLarge);
+	void appendOctetString(const etl::istring& string);
 
-		appendUint16(string.size());
-		appendString(string);
-	}
+	/**
+	 * Adds a nested TC or TM Message within the current Message
+	 *
+	 * As a design decision, nested TC & TM Messages always have a fixed width, specified in \ref ECSSDefinitions. This
+	 * reduces the uncertainty and complexity of having to parse the nested Message itself to see how long it is, at
+	 * the cost of more data to be transmitted.
+	 * @param message The message to append
+	 * @param size The fixed number of bytes that the message will take up. The empty last bytes are padded with 0s.
+	 */
+	void appendMessage(const Message & message, uint16_t size);
 
 	/**
 	 * Fetches a single-byte boolean value from the current position in the message
@@ -543,25 +564,4 @@ public:
 	}
 };
 
-template <const size_t SIZE>
-inline void Message::appendString(const String<SIZE>& string) {
-	ASSERT_INTERNAL(dataSize + string.size() < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge);
-	// TODO: Do we need to keep this check? How does etl::string handle it?
-	ASSERT_INTERNAL(string.size() < string.capacity(), ErrorHandler::StringTooLarge);
-
-	memcpy(data + dataSize, string.data(), string.size());
-
-	dataSize += string.size();
-}
-
-template <const size_t SIZE>
-inline void Message::appendFixedString(const String<SIZE>& string) {
-	ASSERT_INTERNAL((dataSize + SIZE) < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge);
-
-	memcpy(data + dataSize, string.data(), string.size()); // Append the bytes with content
-	(void) memset(data + dataSize + string.size(), 0, SIZE - string.size()); // The rest of the bytes is set to 0
-
-	dataSize += SIZE;
-}
-
 #endif // ECSS_SERVICES_PACKET_H
diff --git a/inc/MessageParser.hpp b/inc/MessageParser.hpp
index b3b1f0ded171bdcab16489dbcbc06282a9f7a655..82517e8203cd2eb0735e3625c82ef97b9eb0646f 100644
--- a/inc/MessageParser.hpp
+++ b/inc/MessageParser.hpp
@@ -7,17 +7,36 @@
 /**
  * A generic class responsible for the execution and the parsing of the incoming telemetry and telecommand
  * packets
+ *
+ * This class is responsible for converting Packets and Messages to and from the internal representation used in this
+ * project. The following hierarchy is used between the different layers on ecss-services:
+ *
+ * \code
+ *                                                       -------------------
+ *                                                       | User data field |
+ *                                                       -------------------
+ *                            ---------------------------                            Application Layer
+ *                            | Packet secondary header |
+ *                            |      (ECSS header)      |
+ *                            ---------------------------                    --------------------------------
+ *  -------------------------
+ *  | Packet primary header |
+ *  |     (CCSDS header)    |                                                          Network Layer
+ *  -------------------------
+ * \endcode
+ *
+ * The service data is encapsulated within the, **ECSS packet** which is encapsulated within the **CCSDS packet**.
+ * The MessageParser class is responsible for adding and processing both the ECSS and CCSDS headers. The target it uses
+ * for the internal representation of all received Telemetry (TM) and Telecommands (TC) is the \ref Message class.
  */
 
 class MessageParser {
 public:
 	/**
-     * This function takes as input TC packets and and calls the proper services' functions that have been
+     * This function takes as input TC packets and calls the proper services' functions that have been
 	 * implemented to handle TC packets.
 	 *
-	 * @param Message Contains the necessary parameters to call the suitable subservice
- 	 * @todo Implement the execute() in the upcoming services or generally in the upcoming
- 	 * activities
+	 * @param message Contains the necessary parameters to call the suitable subservice
 	 */
 	static void execute(Message& message);
 
@@ -30,34 +49,40 @@ public:
 	 * @param length The size of the message
 	 * @return A new object that represents the parsed message
 	 */
-	Message parse(uint8_t* data, uint32_t length);
+	static Message parse(uint8_t* data, uint32_t length);
 
 	/**
-	 * @todo: elaborate on this comment
-	 * Create a message so that a string can be parsed
+	 * Parse data that contains the ECSS packet header, without the CCSDS space packet header
 	 *
 	 * Note: conversion of char* to unsigned char* should flow without any problems according to
 	 * this great analysis:
 	 * stackoverflow.com/questions/15078638/can-i-turn-unsigned-char-into-char-and-vice-versa
 	 */
-	Message parseRequestTC(String<ECSS_TC_REQUEST_STRING_SIZE> data);
+	static Message parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data);
 
 	/**
-	 * @brief Overloaded version
+	 * @brief Overloaded version of \ref MessageParser::parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data)
 	 * @param data A uint8_t array of the TC packet data
 	 * @return Parsed message
 	 */
-	Message parseRequestTC(uint8_t* data);
+	static Message parseECSSTC(uint8_t* data);
 
 	/**
-	 * @brief Converts a TC packet of type Message to a String
-	 * @details Convert a parsed TC message to a string in order to be used by the services
+	 * @brief Converts a TC or TM message to a message string, appending just the ECSS header
+	 * @todo Add time reference, as soon as it is available and the format has been specified
 	 * @param message The Message object to be parsed to a String
-	 * @return A String class containing the parsed TC request
-	 * @attention The returned String has a fixed size, therefore the message size is considered
-	 * fixed and equal to the ECSS_TC_REQUEST_STRING_SIZE definition.
+	 * @param size The wanted size of the message (including the headers). Messages larger than \p size display an
+	 * error. Messages smaller than \p size are padded with zeros. When `size = 0`, there is no size limit.
+	 * @return A String class containing the parsed Message
 	 */
-	String<ECSS_TC_REQUEST_STRING_SIZE> convertTCToStr(Message& message);
+	static String<CCSDS_MAX_MESSAGE_SIZE> composeECSS(const Message& message, uint16_t size = 0u); // Ignore-MISRA
+
+	/**
+	 * @brief Converts a TC or TM message to a packet string, appending the ECSS and then the CCSDS header
+	 * @param message The Message object to be parsed to a String
+	 * @return A String class containing the parsed Message
+	 */
+	static String<CCSDS_MAX_MESSAGE_SIZE> compose(const Message& message);
 
 private:
 	/**
@@ -69,7 +94,7 @@ private:
 	 * @param length The size of the header
 	 * @param message The Message to modify based on the header
 	 */
-	void parseTC(const uint8_t* data, uint16_t length, Message& message);
+	static void parseECSSTCHeader(const uint8_t* data, uint16_t length, Message& message);
 
 	/**
 	 * Parse the ECSS Telemetry packet secondary header
@@ -80,7 +105,7 @@ private:
 	 * @param length The size of the header
 	 * @param message The Message to modify based on the header
 	 */
-	void parseTM(const uint8_t* data, uint16_t length, Message& message);
+	static void parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message);
 };
 
 #endif // ECSS_SERVICES_MESSAGEPARSER_HPP
diff --git a/inc/Platform/x86/ECSS_Configuration.hpp b/inc/Platform/x86/ECSS_Configuration.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a627d61ae8dcb1790d5c6db133c18096a1d6f7f4
--- /dev/null
+++ b/inc/Platform/x86/ECSS_Configuration.hpp
@@ -0,0 +1,40 @@
+#ifndef ECSS_SERVICES_ECSS_CONFIGURATION_HPP
+#define ECSS_SERVICES_ECSS_CONFIGURATION_HPP
+
+/**
+ * @file
+ * The configuration file for this ECSS Services platform.
+ *
+ * @see GlobalLogLevels Define the minimum level for logged messages
+ * @see ServiceDefinitions Define the service types that will be compiled
+ */
+
+// Log all levels of messages
+#define LOGLEVEL_TRACE
+
+/**
+ * @defgroup ServiceDefinitions Service compilation switches
+ * These preprocessor defines control whether the compilation of each ECSS service is enabled. By not defining one of
+ * those, the service will not be compiled, and no RAM or ROM will be spent storing it.
+ *
+ * Define these in the `ECSS_Configuration.hpp` file of your platform.
+ * @{
+ */
+
+#define SERVICE_ALL ///< Enables compilation of all the ECSS services
+
+#ifdef SERVICE_ALL
+#define SERVICE_EVENTACTION         ///<  Compile ST[19] event-action
+#define SERVICE_EVENTREPORT         ///<  Compile ST[05] event reporting
+#define SERVICE_FUNCTION            ///<  Compile ST[08] function management
+#define SERVICE_LARGEPACKET         ///<  Compile ST[13] large packet transfer
+#define SERVICE_MEMORY              ///<  Compile ST[06] memory management
+#define SERVICE_PARAMETER           ///<  Compile ST[20] parameter management
+#define SERVICE_REQUESTVERIFICATION ///<  Compile ST[01] request verification
+#define SERVICE_TEST                ///<  Compile ST[17] test
+#define SERVICE_TIME                ///<  Compile ST[09] time management
+#define SERVICE_TIMESCHEDULING      ///<  Compile ST[11] time-based scheduling
+#endif
+/** @} */
+
+#endif //ECSS_SERVICES_ECSS_CONFIGURATION_HPP
diff --git a/inc/Platform/x86/TimeGetter.hpp b/inc/Platform/x86/TimeGetter.hpp
index 7cb15d7389c5b3a7e3c9f69de719ca2ac0332c3e..d5da817d62de9548c355a0e757c8ebc275e40614 100644
--- a/inc/Platform/x86/TimeGetter.hpp
+++ b/inc/Platform/x86/TimeGetter.hpp
@@ -1,7 +1,6 @@
 #ifndef ECSS_SERVICES_TIMEGETTER_HPP
 #define ECSS_SERVICES_TIMEGETTER_HPP
 
-#include <iostream>
 #include <cstdint>
 #include <ctime>
 
diff --git a/inc/Service.hpp b/inc/Service.hpp
index 32e77e19387cd0b74236c082ffc271f3557535b7..53bf0bae49366814e9e8bff2a0d1436abbb877c8 100644
--- a/inc/Service.hpp
+++ b/inc/Service.hpp
@@ -3,17 +3,20 @@
 
 #include <cstdint>
 #include "Message.hpp"
-#include <iostream> // This file should be removed
 
 class ServicePool;
 
+/**
+ * @defgroup Services Services
+ * ECSS Services implementations, as defined in ECSS-E-ST-70-41C. These services receive TC Messages, and output TM
+ * Messages.
+ */
+
 /**
  * A spacecraft service, as defined in ECSS-E-ST-70-41C
  *
  * A member of the Service class should be used as a singleton, i.e. must be created only once in
  * the code
- *
- * @todo Disable copy constructor
  */
 class Service {
 private:
@@ -33,7 +36,6 @@ protected:
 	 *                    the TC[17,3] message has `messageType = 3`.
 	 * @todo See if the Message must be returned by reference
 	 * @todo Set the application ID to the current application
-	 * @todo Use the messageTypeCounter
 	 */
 	Message createTM(uint8_t messageType) {
 		return Message(serviceType, messageType, Message::TM, 0);
diff --git a/inc/ServicePool.hpp b/inc/ServicePool.hpp
index 97fbed7ff13ef836ad9de9da68f9c5d544050e7d..51c1fa201b856c3f1b0225113c1e60860f36ff1a 100644
--- a/inc/ServicePool.hpp
+++ b/inc/ServicePool.hpp
@@ -1,7 +1,8 @@
 #ifndef ECSS_SERVICES_SERVICEPOOL_HPP
 #define ECSS_SERVICES_SERVICEPOOL_HPP
 
-#include <Services/TimeBasedSchedulingService.hpp>
+#include "ECSS_Configuration.hpp"
+#include "Services/TimeBasedSchedulingService.hpp"
 #include "Services/LargePacketTransferService.hpp"
 #include "Services/RequestVerificationService.hpp"
 #include "Services/TimeManagementService.hpp"
@@ -16,21 +17,61 @@
  * Defines a class that contains instances of all Services.
  *
  * All Services should be stored here and should not be instantiated in a different way.
- *
- * @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;
+#ifdef SERVICE_EVENTACTION
+	EventActionService eventAction;
+#endif
+
+#ifdef SERVICE_EVENTREPORT
 	EventReportService eventReport;
+#endif
+
+#ifdef SERVICE_FUNCTION
+	FunctionManagementService functionManagement;
+#endif
+
+#ifdef SERVICE_LARGEPACKET
+	LargePacketTransferService largePacketTransferService;
+#endif
+
+#ifdef SERVICE_MEMORY
 	MemoryManagementService memoryManagement;
-	TimeManagementService timeManagement;
-	EventActionService eventAction;
-	TestService testService;
+#endif
+
+#ifdef SERVICE_PARAMETER
 	ParameterService parameterManagement;
-	LargePacketTransferService largePacketTransferService;
-	FunctionManagementService functionManagement;
+#endif
+
+#ifdef SERVICE_REQUESTVERIFICATION
+	RequestVerificationService requestVerification;
+#endif
+
+#ifdef SERVICE_TEST
+	TestService testService;
+#endif
+
+#ifdef SERVICE_TIME
+	TimeManagementService timeManagement;
+#endif
+
+#ifdef SERVICE_TIMESCHEDULING
 	TimeBasedSchedulingService timeBasedScheduling;
+#endif
 
 	/**
 	 * The default ServicePool constructor
@@ -44,6 +85,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/inc/Services/EventActionService.hpp b/inc/Services/EventActionService.hpp
index aac524c061e7494204350e03c6cb6455a27d2a38..799ae996939bd4747c2fceb878d1c9dc05fdce60 100644
--- a/inc/Services/EventActionService.hpp
+++ b/inc/Services/EventActionService.hpp
@@ -11,6 +11,7 @@
  *
  * ECSS 8.19 && 6.19
  *
+ * @ingroup Services
  * @note: Make sure to check the note in the addEventActionDefinition()
  * @note: A third variable was added, the eventActionDefinitionID. This was added for the purpose of identifying
  * various eventActionDefinitions that correspond to the same eventDefinitionID. The goal is to have multiple actions
diff --git a/inc/Services/EventReportService.hpp b/inc/Services/EventReportService.hpp
index d1eac70f4c3f2e77feb895ea1c91e9cc71e13ea3..376b35d4a5194cf56397c02dd1c606c6f7a96c15 100644
--- a/inc/Services/EventReportService.hpp
+++ b/inc/Services/EventReportService.hpp
@@ -7,13 +7,12 @@
 /**
  * Implementation of ST[05] event reporting service
  *
+ * @ingroup Services
  * @todo: add more enums event IDs
  * @todo: Make sure there isn't an event ID == 0, because there's a confliction with another service
  * Note: enum IDs are these just for test purposes
  *
  */
-#define CSS_EVENTS_MAX_COUNT 16
-#define ECSS_EVENTS_BITS 16
 
 class EventReportService : public Service {
 private:
@@ -101,9 +100,8 @@ public:
 	 *
 	 * @param eventID event definition ID
 	 * @param data the data of the report
-	 * @param length the length of the data
 	 */
-	void informativeEventReport(Event eventID, const String<64>& data);
+	void informativeEventReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data);
 
 	/**
 	 * TM[5,2] low severiity anomaly report
@@ -113,9 +111,8 @@ public:
 	 *
 	 * @param eventID event definition ID
 	 * @param data the data of the report
-	 * @param length the length of the data
 	 */
-	void lowSeverityAnomalyReport(Event eventID, const String<64>& data);
+	void lowSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data);
 
 	/**
 	 * TM[5,3] medium severity anomaly report
@@ -125,9 +122,8 @@ public:
 	 *
 	 * @param eventID event definition ID
 	 * @param data the data of the report
-	 * @param length the length of the data
 	 */
-	void mediumSeverityAnomalyReport(Event eventID, const String<64>& data);
+	void mediumSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data);
 
 	/**
 	 * TM[5,4] high severity anomaly report
@@ -137,9 +133,8 @@ public:
 	 *
 	 * @param eventID event definition ID
 	 * @param data the data of the report
-	 * @param length the length of the data
 	 */
-	void highSeverityAnomalyReport(Event eventID, const String<64>& data);
+	void highSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data);
 
 	/**
 	 * TC[5,5] request to enable report generation
@@ -164,7 +159,6 @@ public:
 	/**
 	 * TM[5,8] disabled event definitions report
 	 * Telemetry package of a report of the disabled event definitions
-	 * @param message
 	 */
 	void listOfDisabledEventsReport();
 
@@ -181,7 +175,7 @@ public:
 	 * is the ground station.
 	 *
 	 * @note This function is called from the main execute() that is defined in the file MessageParser.hpp
-	 * @param param Contains the necessary parameters to call the suitable subservice
+	 * @param message Contains the necessary parameters to call the suitable subservice
 	 */
 	void execute(Message& message);
 };
diff --git a/inc/Services/FunctionManagementService.hpp b/inc/Services/FunctionManagementService.hpp
index 512ce3e39c71475915cfaf2bde38ee0a30d491fc..36f68d84ece877d7977d6f704a73f67337702d21 100644
--- a/inc/Services/FunctionManagementService.hpp
+++ b/inc/Services/FunctionManagementService.hpp
@@ -7,10 +7,6 @@
 #include "Service.hpp"
 #include "ErrorHandler.hpp"
 
-#define FUNC_MAP_SIZE 5 // size of the function map (number of elements)
-#define FUNC_NAME_LENGTH 32 // max length of the function name
-#define MAX_ARG_LENGTH 32 // maximum argument byte string length
-
 /**
  * Implementation of the ST[08] function management service
  *
@@ -25,6 +21,7 @@
  *
  * You have been warned.
  *
+ * @ingroup Services
  * @author Grigoris Pavlakis <grigpavl@ece.auth.gr>
  */
 
@@ -49,10 +46,11 @@
  * 		include(String<FUNC_NAME_LENGTH>("bar"), &bar);
  * 		include(String<FUNC_NAME_LENGTH>("baz"), &baz);
  * 	}
+ * @endcode
  */
 
-typedef String<FUNC_NAME_LENGTH> functionName;
-typedef etl::map<functionName, void (*)(String<MAX_ARG_LENGTH>), FUNC_MAP_SIZE> FunctionMap;
+typedef String<ECSS_FUNCTION_NAME_LENGTH> functionName;
+typedef etl::map<functionName, void (*)(String<ECSS_FUNCTION_MAX_ARG_LENGTH>), ECSS_FUNCTION_MAP_SIZE> FunctionMap;
 
 class FunctionManagementService : public Service {
 	/**
@@ -64,8 +62,6 @@ public:
 	/**
 	 * Constructs the function pointer index with all the necessary functions at initialization time
 	 * These functions need to be in scope. Un-default when needed.
-	 *
-	 * @param None
 	 */
 	FunctionManagementService() = default;
 
@@ -85,7 +81,7 @@ public:
 	 * @param ptr pointer to a function of void return type and a MAX_ARG_LENGTH-lengthed byte
 	 * string as argument (which contains the actual arguments of the function)
 	 */
-	void include(String<FUNC_NAME_LENGTH> funcName, void (*ptr)(String<MAX_ARG_LENGTH>));
+	void include(String<ECSS_FUNCTION_NAME_LENGTH> funcName, void (*ptr)(String<ECSS_FUNCTION_MAX_ARG_LENGTH>));
 
 	int getMapSize() {
 		return funcPtrIndex.size();
@@ -96,7 +92,7 @@ public:
 	 * is the ground station.
 	 *
 	 * @note This function is called from the main execute() that is defined in the file MessageParser.hpp
-	 * @param param Contains the necessary parameters to call the suitable subservice
+	 * @param message Contains the necessary parameters to call the suitable subservice
 	 */
 	void execute(Message& message);
 };
diff --git a/inc/Services/LargePacketTransferService.hpp b/inc/Services/LargePacketTransferService.hpp
index e3219472881dfb84e59541aafa3d7c3c48725511..fe91522b0e50c5cc6007bdb0a247bb35ff584c7a 100644
--- a/inc/Services/LargePacketTransferService.hpp
+++ b/inc/Services/LargePacketTransferService.hpp
@@ -10,6 +10,8 @@
  * maximum data size
  *
  * Note: More information can be found in the standards' manual, in p. 526-528 and in p. 229-236
+ *
+ * @ingroup Services
  */
 
 class LargePacketTransferService : public Service {
@@ -49,7 +51,7 @@ public:
 	                            const String<ECSS_MAX_FIXED_OCTET_STRING_SIZE>& string);
 
 	// The three uplink functions should handle a TC request to "upload" data. Since there is not
-	// a createTC function ready, I just return the given string.
+	// a composeECSS function ready, I just return the given string.
 	// @TODO: Modify these functions properly
 	/**
 	 * Function that handles the first part of the uplink request
@@ -72,7 +74,7 @@ public:
 
 	/**
 	 * Function that splits large messages
-	 * @param Message that is exceeds the standards and has to be split down
+	 * @param message that is exceeds the standards and has to be split down
 	 * @param largeMessageTransactionIdentifier that is a value we assign to this splitting of the large message
 	 */
 	void split(Message& message, uint16_t largeMessageTransactionIdentifier);
diff --git a/inc/Services/MemoryManagementService.hpp b/inc/Services/MemoryManagementService.hpp
index f61ebad717ed06dd868233268cb5516306f3b795..95fca1ce82f975d26e14a87276a7b581bcab229e 100644
--- a/inc/Services/MemoryManagementService.hpp
+++ b/inc/Services/MemoryManagementService.hpp
@@ -2,12 +2,14 @@
 #define ECSS_SERVICES_MEMMANGSERVICE_HPP
 
 #include <memory>
-#include <iostream>
 #include "Service.hpp"
 #include "Helpers/CRCHelper.hpp"
 #include "ErrorHandler.hpp"
 #include "Platform/STM32F7/MemoryAddressLimits.hpp"
 
+/**
+ * @ingroup Services
+ */
 class MemoryManagementService : public Service {
 public:
 	// Memory type ID's
@@ -77,7 +79,7 @@ public:
 	 * is the ground station.
 	 *
 	 * @note This function is called from the main execute() that is defined in the file MessageParser.hpp
-	 * @param param Contains the necessary parameters to call the suitable subservice
+	 * @param message Contains the necessary parameters to call the suitable subservice
 	 */
 	void execute(Message& message);
 
diff --git a/inc/Services/RequestVerificationService.hpp b/inc/Services/RequestVerificationService.hpp
index 2d091de4033fe185829726133a8958e1ebecd459..c790029a8bab4d463b86d19248b13e6be1ddf09b 100644
--- a/inc/Services/RequestVerificationService.hpp
+++ b/inc/Services/RequestVerificationService.hpp
@@ -15,6 +15,8 @@
  *
  * @todo See if the deduced data defined from the standard should still be ignored. This deduced
  * data exists only in reports that send failure signs(for example the TM[1,2])
+ *
+ * @ingroup Services
  */
 class RequestVerificationService : public Service {
 public:
diff --git a/inc/Services/TestService.hpp b/inc/Services/TestService.hpp
index 8ed5d917e9a0868901ee1fbcde7b0ec4936f6b95..bb344af81f3b2447fc5c879a5fa5f45a25c84d2d 100644
--- a/inc/Services/TestService.hpp
+++ b/inc/Services/TestService.hpp
@@ -5,6 +5,8 @@
 
 /**
  * Implementation of the ST[17] test service
+ *
+ * @ingroup Services
  */
 class TestService : public Service {
 public:
diff --git a/inc/Services/TimeBasedSchedulingService.hpp b/inc/Services/TimeBasedSchedulingService.hpp
index 14581131ef478aa517555425ed22398197da4b50..575b03595e94158a5dae3b4e219d903ba608f942 100644
--- a/inc/Services/TimeBasedSchedulingService.hpp
+++ b/inc/Services/TimeBasedSchedulingService.hpp
@@ -41,6 +41,8 @@ struct Tester;
  * ground.
  * @todo Define whether the parsed absolute release time is saved in the scheduled activity as an
  * uint32_t or in the time format specified by the time management service.
+ *
+ * @ingroup Services
  */
 class TimeBasedSchedulingService : public Service {
 private:
@@ -52,21 +54,16 @@ private:
 	 */
 	bool executionFunctionStatus = false; // True indicates "enabled" and False "disabled" state
 
-	MessageParser msgParser; // Parse TC packets
-
 	/**
 	 * @brief Request identifier of the received packet
 	 *
 	 * @details The request identifier consists of the application process ID, the packet
 	 * sequence count and the source ID, all defined in the ECSS standard.
-	 * @var applicationID Application process ID
-	 * @var sequenceCount Packet sequence count
-	 * @var sourceID Packet source ID
 	 */
 	struct RequestID {
-		uint16_t applicationID = 0;
-		uint16_t sequenceCount = 0;
-		uint8_t sourceID = 0;
+		uint16_t applicationID = 0; ///< Application process ID
+		uint16_t sequenceCount = 0; ///< Packet sequence count
+		uint8_t sourceID = 0; ///< Packet source ID
 
 		bool operator!=(const RequestID& rightSide) const {
 			return (sequenceCount != rightSide.sequenceCount) or (applicationID != rightSide.applicationID) or
@@ -79,16 +76,14 @@ private:
 	 *
 	 * @details All scheduled activities must contain the request they exist for, their release
 	 * time and the corresponding request identifier.
-	 * @var request Contains the received TC request
-	 * @var requestID Contains the unique request identifier for that activity
-	 * @var requestReleaseTime The absolute time is seconds of the request release
+	 *
+	 * @todo If we decide to use sub-schedules, the ID of that has to be defined
+	 * @todo If groups are used, then the group ID has to be defined here
 	 */
 	struct ScheduledActivity {
-		Message request; // Hold the received command request
-		RequestID requestID; // Request ID, characteristic of the definition
-		uint32_t requestReleaseTime = 0; // Keep the command release time
-		// todo: If we decide to use sub-schedules, the ID of that has to be defined
-		// todo: If groups are used, then the group ID has to be defined here
+		Message request; ///< Hold the received command request
+		RequestID requestID; ///< Request ID, characteristic of the definition
+		uint32_t requestReleaseTime = 0; ///< Keep the command release time
 	};
 
 	/**
@@ -138,7 +133,7 @@ public:
 	void enableScheduleExecution(Message& request);
 
 	/**
-	 * @breif TC[11,2] disable the time-based schedule execution function
+	 * @brief TC[11,2] disable the time-based schedule execution function
 	 *
 	 * @details Disables the time-based command execution scheduling
 	 * @param request Provide the received message as a parameter
diff --git a/inc/Services/TimeManagementService.hpp b/inc/Services/TimeManagementService.hpp
index 4b6663c172208f52dc8201f7187e7da510a159da..54617aaf1aaf10ae8308741b2ff459f16ba38b20 100644
--- a/inc/Services/TimeManagementService.hpp
+++ b/inc/Services/TimeManagementService.hpp
@@ -7,7 +7,8 @@
 /**
  * Implementation of the ST[09] time management.
  *
- * @notes
+ * @ingroup Services
+ * @note
  * There is a noticeable difference between setting the time using GPS and setting the time
  * using space packets from the ground segment. The GPS module sends the actual time of UTC (123519
  * is 12:35:19 UTC), while space packets, for time configuration, sends the elapsed time units
diff --git a/inc/etl/String.hpp b/inc/etl/String.hpp
index 11c04b6747c6c12e211fb5bdd30ba498169c07f6..7691f78323f636c5741a68b9cb2144e7c362fa86 100644
--- a/inc/etl/String.hpp
+++ b/inc/etl/String.hpp
@@ -56,6 +56,20 @@ public:
 	 */
 	String(const char* text) // NOLINTNEXTLINE(google-explicit-constructor)
 	    : etl::string<MAX_SIZE>(text) {}
+
+	using etl::istring::append; // Use the append function from the parent
+
+    /**
+     * Append a specified number of bytes from a uint8_t array to the String
+     * @details The array does NOT need to be null-terminated
+     * @param data The characters to append
+     * @param n The number of characters that \p data contains
+     * @return This string
+     */
+	String& append(const uint8_t* data, size_t n) {
+		etl::string<MAX_SIZE>::append(reinterpret_cast<const char*>(data), n);
+		return *this;
+	}
 };
 
 #endif // ECSS_SERVICES_ETL_STRING_HPP
diff --git a/lib/etl b/lib/etl
index f2f23395cde34704686233094d9619ad8d06453a..5a7e181100dae73659db133783ae964c7661437b 160000
--- a/lib/etl
+++ b/lib/etl
@@ -1 +1 @@
-Subproject commit f2f23395cde34704686233094d9619ad8d06453a
+Subproject commit 5a7e181100dae73659db133783ae964c7661437b
diff --git a/src/ErrorHandler.cpp b/src/ErrorHandler.cpp
index 1cba15ca835dca1022adb68c032bb6912592dd8f..e7bacfb0f1561985485542a48b29425cf8047d10 100644
--- a/src/ErrorHandler.cpp
+++ b/src/ErrorHandler.cpp
@@ -1,39 +1,48 @@
-#include <iostream>
-#include <cxxabi.h>
+#include "ECSS_Configuration.hpp"
 #include <ErrorHandler.hpp>
 #include <ServicePool.hpp>
 #include "Services/RequestVerificationService.hpp"
 
 template <>
 void ErrorHandler::reportError(const Message& message, AcceptanceErrorType errorCode) {
+#ifdef SERVICE_REQUESTVERIFICATION
 	Services.requestVerification.failAcceptanceVerification(message, errorCode);
+#endif
 
 	logError(message, errorCode);
 }
 
 template <>
 void ErrorHandler::reportError(const Message& message, ExecutionStartErrorType errorCode) {
+#ifdef SERVICE_REQUESTVERIFICATION
 	Services.requestVerification.failStartExecutionVerification(message, errorCode);
+#endif
 
 	logError(message, errorCode);
 }
 
 void ErrorHandler::reportProgressError(const Message& message, ExecutionProgressErrorType errorCode, uint8_t stepID) {
+#ifdef SERVICE_REQUESTVERIFICATION
 	Services.requestVerification.failProgressExecutionVerification(message, errorCode, stepID);
+#endif
 
 	logError(message, errorCode);
 }
 
 template <>
 void ErrorHandler::reportError(const Message& message, ExecutionCompletionErrorType errorCode) {
+#ifdef SERVICE_REQUESTVERIFICATION
 	Services.requestVerification.failCompletionExecutionVerification(message, errorCode);
+#endif
 
 	logError(message, errorCode);
 }
 
 template <>
 void ErrorHandler::reportError(const Message& message, RoutingErrorType errorCode) {
+#ifdef SERVICE_REQUESTVERIFICATION
 	Services.requestVerification.failRoutingVerification(message, errorCode);
+#endif
 
 	logError(message, errorCode);
 }
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/TimeAndDate.cpp b/src/Helpers/TimeAndDate.cpp
index b531d2274ce3917f79e865fd63d20c7e5d85c602..54a17b6b17f0ca2838f741222858fc97777f25ae 100644
--- a/src/Helpers/TimeAndDate.cpp
+++ b/src/Helpers/TimeAndDate.cpp
@@ -15,8 +15,9 @@ TimeAndDate::TimeAndDate(uint16_t year, uint8_t month, uint8_t day, uint8_t hour
 	ASSERT_INTERNAL(2019 <= year, ErrorHandler::InternalErrorType::InvalidDate);
 	ASSERT_INTERNAL((1 <= month) && (month <= 12), ErrorHandler::InternalErrorType::InvalidDate);
 	ASSERT_INTERNAL((1 <= day) && (day <= 31), ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL(hour <= 24, ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL(minute <= 60, ErrorHandler::InternalErrorType::InvalidDate);
+	ASSERT_INTERNAL(hour < 24, ErrorHandler::InternalErrorType::InvalidDate);
+	ASSERT_INTERNAL(minute < 60, ErrorHandler::InternalErrorType::InvalidDate);
+	// Seconds can be equal to 60, to account for leap seconds.
 	ASSERT_INTERNAL(second <= 60, ErrorHandler::InternalErrorType::InvalidDate);
 
 	this->year = year;
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/Logger.cpp b/src/Logger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..76e36d59af7a58a5b9c34cd9fa3aca3592fee4b1
--- /dev/null
+++ b/src/Logger.cpp
@@ -0,0 +1,24 @@
+#include <Logger.hpp>
+
+etl::format_spec Logger::format;
+
+// Reimplementation of the function for variable C strings
+template <>
+Logger::LogEntry& Logger::LogEntry::operator<<(char* value) {
+	message.append(value);
+	return *this;
+}
+
+// Reimplementation of the function for C strings
+template <>
+Logger::LogEntry& Logger::LogEntry::operator<<(const char* value) {
+	message.append(value);
+	return *this;
+}
+
+Logger::LogEntry::LogEntry(LogLevel level) : level(level) {}
+
+Logger::LogEntry::~LogEntry() {
+	// When the destructor is called, the log message is fully "designed". Now we can finally "display" it to the user.
+	Logger::log(level, message);
+}
diff --git a/src/Message.cpp b/src/Message.cpp
index efedb65c16b2388fc6a87326cb0ffbcd35f2fac9..0333117ed92f6dc8b352268f94ff98d51eae9d14 100644
--- a/src/Message.cpp
+++ b/src/Message.cpp
@@ -2,6 +2,8 @@
 #include "macros.hpp"
 #include <cstring>
 #include <ErrorHandler.hpp>
+#include <ServicePool.hpp>
+#include <MessageParser.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 +38,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 +90,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);
 
@@ -156,3 +162,38 @@ void Message::resetRead() {
 	readPosition = 0;
 	currentBit = 0;
 }
+
+void Message::appendMessage(const Message& message, uint16_t size) {
+	appendString(MessageParser::composeECSS(message, size));
+}
+
+void Message::appendString(const etl::istring& string) {
+	ASSERT_INTERNAL(dataSize + string.size() <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge);
+	// TODO: Do we need to keep this check? How does etl::string handle it?
+	ASSERT_INTERNAL(string.size() <= string.capacity(), ErrorHandler::StringTooLarge);
+
+	memcpy(data + dataSize, string.data(), string.size());
+
+	dataSize += string.size();
+}
+
+void Message::appendFixedString(const etl::istring& string) {
+	ASSERT_INTERNAL((dataSize + string.max_size()) < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge);
+
+	// Append the bytes with content
+	memcpy(data + dataSize, string.data(), string.size());
+	// The rest of the bytes is set to 0
+	(void) memset(data + dataSize + string.size(), 0, string.max_size() - string.size());
+
+	dataSize += string.max_size();
+}
+
+void Message::appendOctetString(const etl::istring& string) {
+	// Make sure that the string is large enough to count
+	ASSERT_INTERNAL(string.size() <= (std::numeric_limits<uint16_t>::max)(), ErrorHandler::StringTooLarge);
+	// Redundant check to make sure we fail before appending string.size()
+	ASSERT_INTERNAL(dataSize + 2 + string.size() < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge);
+
+	appendUint16(string.size());
+	appendString(string);
+}
diff --git a/src/MessageParser.cpp b/src/MessageParser.cpp
index 29300cdbf7c0b761f443c7ee189f6f59bfb4589b..b4eb3953d2e6ecba5f4115759ba0ac062fb2847e 100644
--- a/src/MessageParser.cpp
+++ b/src/MessageParser.cpp
@@ -8,30 +8,54 @@
 
 void MessageParser::execute(Message& message) {
 	switch (message.serviceType) {
+#ifdef SERVICE_EVENTREPORT
 		case 5:
 			Services.eventReport.execute(message); // ST[05]
 			break;
+#endif
+
+#ifdef SERVICE_MEMORY
 		case 6:
 			Services.memoryManagement.execute(message); // ST[06]
 			break;
+#endif
+
+#ifdef SERVICE_FUNCTION
 		case 8:
 			Services.functionManagement.execute(message); // ST[08]
 			break;
+#endif
+
+#ifdef SERVICE_TIME
 		case 9:
 			Services.timeManagement.execute(message); // ST[09]
 			break;
+#endif
+
+#ifdef SERVICE_TIMESCHEDULING
 		case 11:
 			Services.timeBasedScheduling.execute(message); // ST[11]
 			break;
+#endif
+
+#ifdef SERVICE_TEST
 		case 17:
 			Services.testService.execute(message); // ST[17]
 			break;
+#endif
+
+#ifdef SERVICE_EVENTACTION
 		case 19:
 			Services.eventAction.execute(message); // ST[19]
 			break;
+#endif
+
+#ifdef SERVICE_PARAMETER
 		case 20:
 			Services.parameterManagement.execute(message); // ST[20]
 			break;
+#endif
+
 		default:
 			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
 	}
@@ -47,28 +71,30 @@ Message MessageParser::parse(uint8_t* data, uint32_t length) {
 	// Individual fields of the CCSDS Space Packet primary header
 	uint8_t versionNumber = data[0] >> 5;
 	Message::PacketType packetType = ((data[0] & 0x10) == 0) ? Message::TM : Message::TC;
-	uint8_t secondaryHeaderFlag = data[0] & static_cast<uint8_t>(0x08);
+	bool secondaryHeaderFlag = (data[0] & 0x08U) != 0U;
 	uint16_t APID = packetHeaderIdentification & static_cast<uint16_t>(0x07ff);
 	auto sequenceFlags = static_cast<uint8_t>(packetSequenceControl >> 14);
+	uint16_t packetSequenceCount = packetSequenceControl & (~ 0xc000U); // keep last 14 bits
 
 	// 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, ErrorHandler::UnacceptablePacket);
+	ASSERT_INTERNAL(sequenceFlags == 0x3U, ErrorHandler::UnacceptablePacket);
+	ASSERT_INTERNAL(packetDataLength == (length - 6U), ErrorHandler::UnacceptablePacket);
 
 	Message message(0, 0, packetType, APID);
+	message.packetSequenceCount = packetSequenceCount;
 
 	if (packetType == Message::TC) {
-		parseTC(data + 6, packetDataLength, message);
+		parseECSSTCHeader(data + 6, packetDataLength, message);
 	} else {
-		parseTM(data + 6, packetDataLength, message);
+		parseECSSTMHeader(data + 6, packetDataLength, message);
 	}
 
 	return message;
 }
 
-void MessageParser::parseTC(const uint8_t* data, uint16_t length, Message& message) {
+void MessageParser::parseECSSTCHeader(const uint8_t* data, uint16_t length, Message& message) {
 	ErrorHandler::assertRequest(length >= 5, message, ErrorHandler::UnacceptableMessage);
 
 	// Individual fields of the TC header
@@ -76,9 +102,7 @@ void MessageParser::parseTC(const uint8_t* data, uint16_t length, Message& messa
 	uint8_t serviceType = data[1];
 	uint8_t messageType = data[2];
 
-	// 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;
@@ -91,34 +115,90 @@ void MessageParser::parseTC(const uint8_t* data, uint16_t length, Message& messa
 	message.dataSize = length;
 }
 
-Message MessageParser::parseRequestTC(String<ECSS_TC_REQUEST_STRING_SIZE> data) {
+Message MessageParser::parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data) {
 	Message message;
 	auto* dataInt = reinterpret_cast<uint8_t*>(data.data());
 	message.packetType = Message::TC;
-	parseTC(dataInt, ECSS_TC_REQUEST_STRING_SIZE, message);
+	parseECSSTCHeader(dataInt, ECSS_TC_REQUEST_STRING_SIZE, message);
 	return message;
 }
 
-Message MessageParser::parseRequestTC(uint8_t* data) {
+Message MessageParser::parseECSSTC(uint8_t* data) {
 	Message message;
 	message.packetType = Message::TC;
-	parseTC(data, ECSS_TC_REQUEST_STRING_SIZE, message);
+	parseECSSTCHeader(data, ECSS_TC_REQUEST_STRING_SIZE, message);
 	return message;
 }
 
-String<ECSS_TC_REQUEST_STRING_SIZE> MessageParser::convertTCToStr(Message& message) {
-	uint8_t tempString[ECSS_TC_REQUEST_STRING_SIZE] = {0};
+String<CCSDS_MAX_MESSAGE_SIZE> MessageParser::composeECSS(const Message& message, uint16_t size) {
+	uint8_t header[5];
 
-	tempString[0] = ECSS_PUS_VERSION << 4; // Assign the pusVersion = 2
-	tempString[1] = message.serviceType;
-	tempString[2] = message.messageType;
-	memcpy(tempString + 5, message.data, ECSS_TC_REQUEST_STRING_SIZE - 5);
-	String<ECSS_TC_REQUEST_STRING_SIZE> dataString(tempString);
+	if (message.packetType == Message::TC) {
+		header[0] = ECSS_PUS_VERSION << 4U; // Assign the pusVersion = 2
+		header[1] = message.serviceType;
+		header[2] = message.messageType;
+		header[3] = 0;
+		header[4] = 0;
+	} else {
+		header[0] = ECSS_PUS_VERSION << 4U; // Assign the pusVersion = 2
+		header[1] = message.serviceType;
+		header[2] = message.messageType;
+		header[3] = static_cast<uint8_t>(message.messageTypeCounter >> 8U);
+		header[4] = static_cast<uint8_t>(message.messageTypeCounter & 0xffU);
+	}
+
+	String<CCSDS_MAX_MESSAGE_SIZE> dataString(header, 5);
+	dataString.append(message.data, message.dataSize);
+
+	// Make sure to reach the requested size
+	if (size != 0) {
+		if (dataString.size() > size) {
+			// Message overflow
+			ErrorHandler::reportInternalError(ErrorHandler::NestedMessageTooLarge);
+		} else if (dataString.size() < size) {
+			// Append some 0s
+			dataString.append(size - dataString.size(), 0);
+		} else {
+			// The message has an equal size to the requested one - do nothing
+		}
+	}
 
 	return dataString;
 }
 
-void MessageParser::parseTM(const uint8_t* data, uint16_t length, Message& message) {
+String<CCSDS_MAX_MESSAGE_SIZE> MessageParser::compose(const Message& message) {
+	uint8_t header[6];
+
+	// First, compose the ECSS part
+	String<CCSDS_MAX_MESSAGE_SIZE> ecssMessage = MessageParser::composeECSS(message);
+
+	// Sanity check that there is enough space for the string
+	ASSERT_INTERNAL((ecssMessage.size() + 6U) <= CCSDS_MAX_MESSAGE_SIZE, ErrorHandler::StringTooLarge);
+
+	// Parts of the header
+	uint16_t packetId = message.applicationId;
+	packetId |= (1U << 11U); // Secondary header flag
+	packetId |= (message.packetType == Message::TC) ? (1U << 12U) : (0U); // Ignore-MISRA
+	uint16_t packetSequenceControl = message.packetSequenceCount | (3U << 14U);
+	uint16_t packetDataLength = ecssMessage.size();
+
+	// Compile the header
+	header[0] = packetId >> 8U;
+	header[1] = packetId & 0xffU;
+	header[2] = packetSequenceControl >> 8U;
+	header[3] = packetSequenceControl & 0xffU;
+	header[4] = packetDataLength >> 8U;
+	header[5] = packetDataLength & 0xffU;
+
+	// Compile the final message by appending the header
+	String<CCSDS_MAX_MESSAGE_SIZE> ccsdsMessage(header, 6);
+	ccsdsMessage.append(ecssMessage);
+
+	return ccsdsMessage;
+}
+
+
+void MessageParser::parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message) {
 	ErrorHandler::assertRequest(length >= 5, message, ErrorHandler::UnacceptableMessage);
 
 	// Individual fields of the TM header
@@ -126,7 +206,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/ErrorHandler.cpp b/src/Platform/x86/ErrorHandler.cpp
index a59e36514ddf1a7047ab4e2e2a47f05dfd4cfd89..ffddffd2d0501e688779674251ae1da83aa035d7 100644
--- a/src/Platform/x86/ErrorHandler.cpp
+++ b/src/Platform/x86/ErrorHandler.cpp
@@ -7,6 +7,7 @@
 #include <cxxabi.h>
 #include <ErrorHandler.hpp>
 #include <Message.hpp>
+#include <Logger.hpp>
 
 // TODO: Find a way to reduce the number of copies of this chunk
 template void ErrorHandler::logError(const Message&, ErrorHandler::AcceptanceErrorType);
@@ -18,23 +19,22 @@ template void ErrorHandler::logError(ErrorHandler::InternalErrorType);
 
 template <typename ErrorType>
 void ErrorHandler::logError(const Message& message, ErrorType errorType) {
-	std::cerr
+	LOG_ERROR
 	    /*
 	     * Gets the error class name from the template
 	     * Note: This is g++-dependent code and should only be used for debugging.
 	     */
 	    << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error "
 	    << "[" << static_cast<uint16_t>(message.serviceType) << "," << static_cast<uint16_t>(message.messageType)
-	    << "]: " << errorType << std::endl;
+	    << "]: " << errorType;
 }
 
 template <typename ErrorType>
 void ErrorHandler::logError(ErrorType errorType) {
-	std::cerr
+	LOG_ERROR
 	    /*
 	     * Gets the error class name from the template
 	     * Note: This is g++-dependent code and should only be used for debugging.
 	     */
-	    << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error: " << errorType
-	    << std::endl;
+	    << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error: " << errorType;
 }
diff --git a/src/Platform/x86/Logger.cpp b/src/Platform/x86/Logger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..85d0797656a4831c718e5b64740eb977ddb98c1d
--- /dev/null
+++ b/src/Platform/x86/Logger.cpp
@@ -0,0 +1,68 @@
+#include <Logger.hpp>
+#include <etl/String.hpp>
+#include <iostream>
+#include <ECSS_Definitions.hpp>
+
+#include <chrono>
+#include <iomanip>
+
+// The implementation of this function appends ANSI codes that should add colours to a compatible terminal
+void Logger::log(Logger::LogLevel level, etl::istring & message) {
+	// Get the current time & date
+	std::time_t t = std::time(nullptr);
+	std::tm tm = *std::localtime(&t);
+
+	// Get the log level and its colour
+	std::string name;
+	std::string colour;
+	bool keepColour = false; // Whether to keep the colour in the rest of the message
+
+	if (level <= Logger::trace) {
+		name = "trace";
+		colour = "90"; // bright black
+		keepColour = true;
+	} else if (level <= Logger::debug) {
+		name = "debug";
+		colour = "90"; // bright black
+	} else if (level <= Logger::info) {
+		name = "info";
+		colour = "32"; // green
+	} else if (level <= Logger::notice) {
+		name = "notice";
+		colour = "36"; // cyan
+	} else if (level <= Logger::warning) {
+		name = "warning";
+		colour = "33"; // yellow
+	} else if (level <= Logger::error) {
+		name = "error";
+		colour = "31"; // red
+	} else {
+		name = "emergency";
+		colour = "31"; // red
+		keepColour = true;
+	}
+
+	std::ostringstream ss; // A string stream to create the log message
+	ss << "\033" "[0;90m" << std::put_time(&tm, "%FT%T%z") << "\033" "[0m "; // The date
+	ss << "[\033" "[1;" << colour << "m" << std::setfill(' ') << std::setw(7) << std::right // Ignore-MISRA
+		<< name << std::setw(0) << "\033" "[0m] "; // The log level // Ignore-MISRA
+
+	if (keepColour) {
+		ss << "\033" "[0;" << colour << "m"; // Ignore-MISRA
+	}
+	ss << message.c_str(); // The message itself
+	if (keepColour) {
+		ss << "\033" "[0m";
+	}
+
+	ss << "\n";
+	std::cerr << ss.str();
+}
+
+// Reimplementation of the log function for C++ strings
+// This is kept in the Platform files, since we don't want to mess with std::strings in the microcontroller
+Logger::LogEntry& Logger::LogEntry::operator<<(const std::string & value) {
+	message.append(value.c_str());
+
+	return *this;
+}
diff --git a/src/Platform/x86/Service.cpp b/src/Platform/x86/Service.cpp
index 7c20d2f90bea925cd43754c62a231c25bc2c5648..60786fc650c32da32cb98860886291537e378fce 100644
--- a/src/Platform/x86/Service.cpp
+++ b/src/Platform/x86/Service.cpp
@@ -1,21 +1,25 @@
 #include <iostream>
 #include <iomanip>
+#include <Logger.hpp>
 #include "Service.hpp"
 
 void Service::storeMessage(Message& message) {
 	// appends the remaining bits to complete a byte
 	message.finalize();
 
+	// Create a new stream to display the packet
+	std::ostringstream ss;
+
 	// Just print it to the screen
-	std::cout << "New " << ((message.packetType == Message::TM) ? "TM" : "TC") << "["
-	          << std::hex
-	          // << std::dec
-	          << static_cast<int>(message.serviceType) << "," << static_cast<int>(message.messageType)
-	          << "] message!\n";
-	// std::cout << std::hex << std::setfill('0') << std::setw(2);
-	for (int i = 0; i < message.dataSize; i++) {
-		std::cout << static_cast<int>(message.data[i]);
-		std::cout << " ";
+	ss << "New " << ((message.packetType == Message::TM) ? "TM" : "TC") << "["
+	   << std::hex
+	   << static_cast<int>(message.serviceType) << "," // Ignore-MISRA
+	   << static_cast<int>(message.messageType) // Ignore-MISRA
+	   << "] message! ";
+
+	for (unsigned int i = 0; i < message.dataSize; i++) {
+		ss << static_cast<int>(message.data[i]) << " "; // Ignore-MISRA
 	}
-	std::cout << std::endl;
+
+	LOG_DEBUG << ss.str();
 }
diff --git a/src/main.cpp b/src/Platform/x86/main.cpp
similarity index 96%
rename from src/main.cpp
rename to src/Platform/x86/main.cpp
index aed43a835568e32cffa5c32ba04f5dcbcb8ce5a6..391780a790d452b7d5e0fd46a3aec0c280882818 100644
--- a/src/main.cpp
+++ b/src/Platform/x86/main.cpp
@@ -1,5 +1,6 @@
 #include <iostream>
 #include <ServicePool.hpp>
+#include <Logger.hpp>
 #include "Helpers/CRCHelper.hpp"
 #include "Helpers/TimeHelper.hpp"
 #include "Services/TestService.hpp"
@@ -20,9 +21,11 @@
 #include "etl/String.hpp"
 
 int main() {
+	LOG_NOTICE << "ECSS Services test application";
+
 	Message packet = Message(0, 0, Message::TC, 1);
 
-	packet.appendString<5>("hello");
+	packet.appendString(String<5>("hello"));
 	packet.appendBits(15, 0x28a8);
 	packet.appendBits(1, 1);
 	packet.appendFloat(5.7);
@@ -298,13 +301,13 @@ int main() {
 
 	// ST[11] test
 	TimeBasedSchedulingService timeBasedSchedulingService;
-	MessageParser msgParser;
 	auto currentTime = static_cast<uint32_t>(time(nullptr)); // Get the current system time
 	std::cout << "\n\nST[11] service is running";
 	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
 
@@ -314,11 +317,11 @@ int main() {
 	receivedMsg = Message(11, 4, Message::TC, 1);
 	receivedMsg.appendUint16(2); // Total number of requests
 
-	receivedMsg.appendUint32(currentTime + 1556435u);
-	receivedMsg.appendString(msgParser.convertTCToStr(testMessage1));
+	receivedMsg.appendUint32(currentTime + 1556435U);
+	receivedMsg.appendString(MessageParser::composeECSS(testMessage1));
 
-	receivedMsg.appendUint32(currentTime + 1957232u);
-	receivedMsg.appendString(msgParser.convertTCToStr(testMessage2));
+	receivedMsg.appendUint32(currentTime + 1957232U);
+	receivedMsg.appendString(MessageParser::composeECSS(testMessage2));
 	timeBasedSchedulingService.insertActivities(receivedMsg);
 
 	// Time shift activities
@@ -335,5 +338,6 @@ int main() {
 	receivedMsg = Message(11, 12, Message::TC, 1);
 	timeBasedSchedulingService.summaryReportActivitiesByID(receivedMsg);
 
+	LOG_NOTICE << "ECSS Services test complete";
 	return 0;
 }
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..f42079794019ef1d4fca34b4ed176e7d40e5c18c 100644
--- a/src/Services/EventActionService.cpp
+++ b/src/Services/EventActionService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_EVENTACTION
+
 #include "Services/EventActionService.hpp"
 #include "Message.hpp"
 #include "MessageParser.hpp"
@@ -77,7 +80,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 +113,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();
@@ -180,8 +183,7 @@ void EventActionService::executeAction(uint16_t eventID) {
 		auto range = eventActionDefinitionMap.equal_range(eventID);
 		for (auto& element = range.first; element != range.second; ++element) {
 			if (element->second.enabled) {
-				MessageParser messageParser;
-				Message message = messageParser.parseRequestTC(element->second.request);
+				Message message = MessageParser::parseECSSTC(element->second.request);
 				MessageParser::execute(message);
 			}
 		}
@@ -218,3 +220,5 @@ void EventActionService::execute(Message& message) {
 			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
 	}
 }
+
+#endif
\ No newline at end of file
diff --git a/src/Services/EventReportService.cpp b/src/Services/EventReportService.cpp
index cbf8ee72ae08d669e65537d6cd6d08314ef28c41..ce97f874a51aa795556b313e901448237c68c13b 100644
--- a/src/Services/EventReportService.cpp
+++ b/src/Services/EventReportService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_EVENTREPORT
+
 #include <Services/EventReportService.hpp>
 #include <Services/EventActionService.hpp>
 #include "Message.hpp"
@@ -6,7 +9,7 @@
  * @todo: Add message type in TCs
  * @todo: this code is error prone, depending on parameters given, add fail safes (probably?)
  */
-void EventReportService::informativeEventReport(Event eventID, const String<64>& data) {
+void EventReportService::informativeEventReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data) {
 	// TM[5,1]
 	if (stateOfEvents[static_cast<uint16_t>(eventID)]) {
 		Message report = createTM(1);
@@ -19,7 +22,8 @@ void EventReportService::informativeEventReport(Event eventID, const String<64>&
 	}
 }
 
-void EventReportService::lowSeverityAnomalyReport(Event eventID, const String<64>& data) {
+void
+EventReportService::lowSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data) {
 	lowSeverityEventCount++;
 	// TM[5,2]
 	if (stateOfEvents[static_cast<uint16_t>(eventID)]) {
@@ -35,7 +39,8 @@ void EventReportService::lowSeverityAnomalyReport(Event eventID, const String<64
 	}
 }
 
-void EventReportService::mediumSeverityAnomalyReport(Event eventID, const String<64>& data) {
+void
+EventReportService::mediumSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data) {
 	mediumSeverityEventCount++;
 	// TM[5,3]
 	if (stateOfEvents[static_cast<uint16_t>(eventID)]) {
@@ -51,7 +56,8 @@ void EventReportService::mediumSeverityAnomalyReport(Event eventID, const String
 	}
 }
 
-void EventReportService::highSeverityAnomalyReport(Event eventID, const String<64>& data) {
+void
+EventReportService::highSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data) {
 	highSeverityEventCount++;
 	// TM[5,4]
 	if (stateOfEvents[static_cast<uint16_t>(eventID)]) {
@@ -120,7 +126,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);
 		}
@@ -141,3 +147,5 @@ void EventReportService::execute(Message& message) {
 			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
 	}
 }
+
+#endif
diff --git a/src/Services/FunctionManagementService.cpp b/src/Services/FunctionManagementService.cpp
index b6d5638b6efd990136f8173beced1618be85f247..5400f038bb6efb3193712b8a800881478c89a338 100644
--- a/src/Services/FunctionManagementService.cpp
+++ b/src/Services/FunctionManagementService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_FUNCTION
+
 #include "Services/FunctionManagementService.hpp"
 
 void FunctionManagementService::call(Message& msg) {
@@ -7,13 +10,13 @@ void FunctionManagementService::call(Message& msg) {
 	ErrorHandler::assertRequest(msg.messageType == 1, msg, ErrorHandler::AcceptanceErrorType::UnacceptableMessage);
 	ErrorHandler::assertRequest(msg.serviceType == 8, msg, ErrorHandler::AcceptanceErrorType::UnacceptableMessage);
 
-	uint8_t funcName[FUNC_NAME_LENGTH] = { 0 }; // the function's name
-	uint8_t funcArgs[MAX_ARG_LENGTH] = { 0 }; // arguments for the function
+	uint8_t funcName[ECSS_FUNCTION_NAME_LENGTH] = { 0 }; // the function's name
+	uint8_t funcArgs[ECSS_FUNCTION_MAX_ARG_LENGTH] = { 0 }; // arguments for the function
 
-	msg.readString(funcName, FUNC_NAME_LENGTH);
-	msg.readString(funcArgs, MAX_ARG_LENGTH);
+	msg.readString(funcName, ECSS_FUNCTION_NAME_LENGTH);
+	msg.readString(funcArgs, ECSS_FUNCTION_MAX_ARG_LENGTH);
 
-	if (msg.dataSize > (FUNC_NAME_LENGTH + MAX_ARG_LENGTH)) {
+	if (msg.dataSize > (ECSS_FUNCTION_NAME_LENGTH + ECSS_FUNCTION_MAX_ARG_LENGTH)) {
 		ErrorHandler::reportError(msg,
 		                          ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError); // report failed
 		// start of execution as requested by the standard
@@ -21,9 +24,9 @@ void FunctionManagementService::call(Message& msg) {
 	}
 
 	// locate the appropriate function pointer
-	String<FUNC_NAME_LENGTH> name(funcName);
+	String<ECSS_FUNCTION_NAME_LENGTH> name(funcName);
 	FunctionMap::iterator iter = funcPtrIndex.find(name);
-	void (*selected)(String<MAX_ARG_LENGTH>);
+	void (*selected)(String<ECSS_FUNCTION_MAX_ARG_LENGTH>);
 
 	if (iter != funcPtrIndex.end()) {
 		selected = *iter->second;
@@ -36,10 +39,11 @@ void FunctionManagementService::call(Message& msg) {
 	selected(funcArgs);
 }
 
-void FunctionManagementService::include(String<FUNC_NAME_LENGTH> funcName, void (*ptr)(String<MAX_ARG_LENGTH>)) {
+void FunctionManagementService::include(String<ECSS_FUNCTION_NAME_LENGTH> funcName,
+	void (* ptr)(String<ECSS_FUNCTION_MAX_ARG_LENGTH>)) {
 	if (not funcPtrIndex.full()) { // CAUTION: etl::map won't check by itself if it's full
 		// before attempting to insert a key-value pair, causing segmentation faults. Check first!
-		funcName.append(FUNC_NAME_LENGTH - funcName.length(), 0);
+		funcName.append(ECSS_FUNCTION_NAME_LENGTH - funcName.length(), 0);
 		funcPtrIndex.insert(std::make_pair(funcName, ptr));
 	} else {
 		ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::MapFull);
@@ -56,3 +60,5 @@ void FunctionManagementService::execute(Message& message) {
 			break;
 	}
 }
+
+#endif
diff --git a/src/Services/LargePacketTransferService.cpp b/src/Services/LargePacketTransferService.cpp
index 15b0e3c3609c41f242a6f6830f570b5598a30f9a..38a0a666b533eb7f7123389a4ad08a7c67a903c9 100644
--- a/src/Services/LargePacketTransferService.cpp
+++ b/src/Services/LargePacketTransferService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_LARGEPACKET
+
 #include <Services/LargePacketTransferService.hpp>
 #include "Message.hpp"
 #include <etl/String.hpp>
@@ -69,7 +72,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 +89,7 @@ void LargePacketTransferService::split(Message& message, uint16_t largeMessageTr
 		positionCounter++;
 	}
 	stringPart = dataPart;
-	lastDownlinkPartReport(largeMessageTransactionIdentifier, (parts - 1u), stringPart);
+	lastDownlinkPartReport(largeMessageTransactionIdentifier, (parts - 1U), stringPart);
 }
+
+#endif
diff --git a/src/Services/MemoryManagementService.cpp b/src/Services/MemoryManagementService.cpp
index d592da573bbc7de0b99d86abcc02241b228afb84..e14fce311ad01d1d9f7a2d3cfd46476c52042f41 100644
--- a/src/Services/MemoryManagementService.cpp
+++ b/src/Services/MemoryManagementService.cpp
@@ -1,5 +1,7 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_MEMORY
+
 #include "Services/MemoryManagementService.hpp"
-#include <iostream>
 #include <cerrno>
 #include <etl/String.hpp>
 
@@ -236,3 +238,5 @@ void MemoryManagementService::execute(Message& message) {
 			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
 	}
 }
+
+#endif
diff --git a/src/Services/ParameterService.cpp b/src/Services/ParameterService.cpp
index a6cb189f271e8694b259931e76ed3f24ece29f86..aef2fd05b0d9662892394d5bab5f306a89ac209d 100644
--- a/src/Services/ParameterService.cpp
+++ b/src/Services/ParameterService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_PARAMETER
+
 #include "Services/ParameterService.hpp"
 #include "Services/Parameter.hpp"
 
@@ -114,3 +117,5 @@ void ParameterService::execute(Message& message) {
 			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
 	}
 }
+
+#endif
diff --git a/src/Services/RequestVerificationService.cpp b/src/Services/RequestVerificationService.cpp
index fdab0b0cc3f85a808ef84cc1db546b7c17ff5537..56bcdf07e3d72f9334f9f82c6cb81c98e7f546e0 100644
--- a/src/Services/RequestVerificationService.cpp
+++ b/src/Services/RequestVerificationService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_REQUESTVERIFICATION
+
 #include "Services/RequestVerificationService.hpp"
 
 void RequestVerificationService::successAcceptanceVerification(const Message& request) {
@@ -7,10 +10,10 @@ void RequestVerificationService::successAcceptanceVerification(const Message& re
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count(not implemented)
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 
 	storeMessage(report);
 }
@@ -23,10 +26,10 @@ void RequestVerificationService::failAcceptanceVerification(const Message& reque
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count(not implemented)
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 	report.appendEnum16(errorCode); // error code
 
 	storeMessage(report);
@@ -39,10 +42,10 @@ void RequestVerificationService::successStartExecutionVerification(const Message
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 
 	storeMessage(report);
 }
@@ -55,10 +58,10 @@ void RequestVerificationService::failStartExecutionVerification(const Message& r
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count(not implemented)
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 	report.appendEnum16(errorCode); // error code
 
 	storeMessage(report);
@@ -71,10 +74,10 @@ void RequestVerificationService::successProgressExecutionVerification(const Mess
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count(not implemented)
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 	report.appendByte(stepID); // step ID
 
 	storeMessage(report);
@@ -89,10 +92,10 @@ void RequestVerificationService::failProgressExecutionVerification(const Message
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count(not implemented)
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 	report.appendByte(stepID); // step ID
 	report.appendEnum16(errorCode); // error code
 
@@ -106,10 +109,10 @@ void RequestVerificationService::successCompletionExecutionVerification(const Me
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count(not implemented)
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 
 	storeMessage(report);
 }
@@ -122,10 +125,10 @@ void RequestVerificationService::failCompletionExecutionVerification(
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count(not implemented)
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 	report.appendEnum16(errorCode); // error code
 
 	storeMessage(report);
@@ -139,11 +142,13 @@ void RequestVerificationService::failRoutingVerification(const Message& request,
 
 	report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number
 	report.appendEnumerated(1, request.packetType); // packet type
-	report.appendBits(1, 0); // secondary header flag(not implemented)
+	report.appendBits(1, 1); // secondary header flag
 	report.appendEnumerated(11, request.applicationId); // application process ID
 	report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags
-	report.appendBits(14, 0); // packet sequence count(not implemented)
+	report.appendBits(14, request.packetSequenceCount); // packet sequence count
 	report.appendEnum16(errorCode); // error code
 
 	storeMessage(report);
 }
+
+#endif
diff --git a/src/Services/TestService.cpp b/src/Services/TestService.cpp
index 001726da6eee06412b6fff685f9114610c05aaa8..53a42b10a2ee0644be2c610ac09ba05a0e5ae110 100644
--- a/src/Services/TestService.cpp
+++ b/src/Services/TestService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_TEST
+
 #include "Services/TestService.hpp"
 
 void TestService::areYouAlive(Message& request) {
@@ -30,3 +33,5 @@ void TestService::execute(Message& message) {
 			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
 	}
 }
+
+#endif
diff --git a/src/Services/TimeBasedSchedulingService.cpp b/src/Services/TimeBasedSchedulingService.cpp
index 170f9c93f6141ea06d15a6ad6b3e4339896756b2..b88e04ec06ee58b32a282269b6d7ac610a0d3d9c 100644
--- a/src/Services/TimeBasedSchedulingService.cpp
+++ b/src/Services/TimeBasedSchedulingService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_TIMESCHEDULING
+
 #include "Services/TimeBasedSchedulingService.hpp"
 
 TimeBasedSchedulingService::TimeBasedSchedulingService() {
@@ -49,7 +52,7 @@ void TimeBasedSchedulingService::insertActivities(Message& request) {
 			// Get the TC packet request
 			uint8_t requestData[ECSS_TC_REQUEST_STRING_SIZE] = {0};
 			request.readString(requestData, ECSS_TC_REQUEST_STRING_SIZE);
-			Message receivedTCPacket = msgParser.parseRequestTC(requestData);
+			Message receivedTCPacket = MessageParser::parseECSSTC(requestData);
 			ScheduledActivity newActivity; // Create the new activity
 
 			// Assign the attributes to the newly created activity
@@ -168,7 +171,7 @@ void TimeBasedSchedulingService::detailReportAllActivities(Message& request) {
 		// todo: append sub-schedule and group ID if they are defined
 
 		report.appendUint32(activity.requestReleaseTime);
-		report.appendString(msgParser.convertTCToStr(activity.request));
+		report.appendString(MessageParser::composeECSS(activity.request));
 	}
 	storeMessage(report); // Save the report
 }
@@ -209,7 +212,7 @@ void TimeBasedSchedulingService::detailReportActivitiesByID(Message& request) {
 	report.appendUint16(static_cast<uint16_t>(matchedActivities.size()));
 	for (auto& match : matchedActivities) {
 		report.appendUint32(match.requestReleaseTime); // todo: Replace with the time parser
-		report.appendString(msgParser.convertTCToStr(match.request));
+		report.appendString(MessageParser::composeECSS(match.request));
 	}
 	storeMessage(report); // Save the report
 }
@@ -293,3 +296,5 @@ void TimeBasedSchedulingService::execute(Message& message) {
 			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
 	}
 }
+
+#endif
diff --git a/src/Services/TimeManagementService.cpp b/src/Services/TimeManagementService.cpp
index 9e281450768a3978df10b857c7d763fcd21cbd78..d12061958917c080c0bda02b9aae15f839b4fec7 100644
--- a/src/Services/TimeManagementService.cpp
+++ b/src/Services/TimeManagementService.cpp
@@ -1,3 +1,6 @@
+#include "ECSS_Configuration.hpp"
+#ifdef SERVICE_TIME
+
 #include "Services/TimeManagementService.hpp"
 
 void TimeManagementService::cdsTimeReport(TimeAndDate& TimeInfo) {
@@ -35,3 +38,5 @@ void TimeManagementService::execute(Message& message) {
 			break;
 	}
 }
+
+#endif
diff --git a/test/ErrorHandler.cpp b/test/ErrorHandler.cpp
index c0dc1393a8a513b024a8a2ea597feda54bcd0b71..804657b3d95151ab4f2be4cdd98293d08df614e1 100644
--- a/test/ErrorHandler.cpp
+++ b/test/ErrorHandler.cpp
@@ -7,6 +7,7 @@ TEST_CASE("Error: Failed Acceptance", "[errors]") {
 	ErrorHandler::reportError(failedMessage, ErrorHandler::MessageTooShort);
 
 	REQUIRE(ServiceTests::hasOneMessage());
+	CHECK(ServiceTests::thrownError(ErrorHandler::MessageTooShort));
 	Message report = ServiceTests::get(0);
 
 	// Check that a TM[1,2] message was returned
@@ -17,7 +18,7 @@ TEST_CASE("Error: Failed Acceptance", "[errors]") {
 
 	CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
 	CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
-	CHECK(report.readBits(1) == 0);
+	CHECK(report.readBits(1) == 1);
 	CHECK(report.readBits(11) == 47);
 	CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
 	CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
@@ -29,6 +30,7 @@ TEST_CASE("Error: Failed Execution Start", "[errors]") {
 	ErrorHandler::reportError(failedMessage, ErrorHandler::UnknownExecutionStartError);
 
 	REQUIRE(ServiceTests::hasOneMessage());
+	CHECK(ServiceTests::thrownError(ErrorHandler::UnknownExecutionStartError));
 	Message report = ServiceTests::get(0);
 
 	// Check that a TM[1,3] message was returned
@@ -39,7 +41,7 @@ TEST_CASE("Error: Failed Execution Start", "[errors]") {
 
 	CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
 	CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
-	CHECK(report.readBits(1) == 0);
+	CHECK(report.readBits(1) == 1);
 	CHECK(report.readBits(11) == 56);
 	CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
 	CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
@@ -51,6 +53,7 @@ TEST_CASE("Error: Failed Execution Progress", "[errors]") {
 	ErrorHandler::reportProgressError(failedMessage, ErrorHandler::UnknownExecutionProgressError, 0);
 
 	REQUIRE(ServiceTests::hasOneMessage());
+	CHECK(ServiceTests::thrownError(ErrorHandler::UnknownExecutionProgressError));
 	Message report = ServiceTests::get(0);
 
 	// Check that a TM[1,6] message was returned
@@ -61,7 +64,7 @@ TEST_CASE("Error: Failed Execution Progress", "[errors]") {
 
 	CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
 	CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
-	CHECK(report.readBits(1) == 0);
+	CHECK(report.readBits(1) == 1);
 	CHECK(report.readBits(11) == 56);
 	CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
 	CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
@@ -74,6 +77,7 @@ TEST_CASE("Error: Failed Execution Completion", "[errors]") {
 	ErrorHandler::reportError(failedMessage, ErrorHandler::UnknownExecutionCompletionError);
 
 	REQUIRE(ServiceTests::hasOneMessage());
+	CHECK(ServiceTests::thrownError(ErrorHandler::UnknownExecutionCompletionError));
 	Message report = ServiceTests::get(0);
 
 	// Check that a TM[1,8] message was returned
@@ -84,7 +88,7 @@ TEST_CASE("Error: Failed Execution Completion", "[errors]") {
 
 	CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
 	CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
-	CHECK(report.readBits(1) == 0);
+	CHECK(report.readBits(1) == 1);
 	CHECK(report.readBits(11) == 56);
 	CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
 	CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
@@ -96,6 +100,7 @@ TEST_CASE("Error: Failed Routing", "[errors]") {
 	ErrorHandler::reportError(failedMessage, ErrorHandler::UnknownRoutingError);
 
 	REQUIRE(ServiceTests::hasOneMessage());
+	CHECK(ServiceTests::thrownError(ErrorHandler::UnknownRoutingError));
 	Message report = ServiceTests::get(0);
 
 	// Check that a TM[1,8] message was returned
@@ -106,7 +111,7 @@ TEST_CASE("Error: Failed Routing", "[errors]") {
 
 	CHECK(report.readBits(3) == CCSDS_PACKET_VERSION);
 	CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC));
-	CHECK(report.readBits(1) == 0);
+	CHECK(report.readBits(1) == 1);
 	CHECK(report.readBits(11) == 71);
 	CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS);
 	CHECK(report.readBits(14) == failedMessage.packetSequenceCount);
diff --git a/test/Helpers/TimeAndDate.cpp b/test/Helpers/TimeAndDate.cpp
index f5bc35fe51a660fe403847e65eda1b1c21d4b9b3..13866f29ebff1432aa88562fdb7f4020690fc37a 100644
--- a/test/Helpers/TimeAndDate.cpp
+++ b/test/Helpers/TimeAndDate.cpp
@@ -1,5 +1,6 @@
 #include "catch2/catch.hpp"
 #include "Helpers/TimeAndDate.hpp"
+#include "../Services/ServiceTests.hpp"
 
 TEST_CASE("Date comparison", "[operands]") {
 	SECTION("Invalid date") {
@@ -9,6 +10,9 @@ TEST_CASE("Date comparison", "[operands]") {
 		TimeAndDate InvalidDate3(2030, 2, 2, 74, 5, 6); // error in hour
 		TimeAndDate InvalidDate4(2030, 2, 2, 4, 75, 6); // error in minute
 		TimeAndDate InvalidDate5(2030, 2, 2, 4, 5, 76); // error in seconds
+
+		CHECK(ServiceTests::countErrors() == 6);
+		CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate));
 	}
 
 	SECTION("Different year") {
diff --git a/test/Helpers/TimeHelper.cpp b/test/Helpers/TimeHelper.cpp
index 00577f65203e1260d1d210e50df4b0f1c71ba4e2..cca057be3459f519e781c4c758d53483e4a1d32b 100644
--- a/test/Helpers/TimeHelper.cpp
+++ b/test/Helpers/TimeHelper.cpp
@@ -1,5 +1,6 @@
 #include "catch2/catch.hpp"
 #include "Helpers/TimeHelper.hpp"
+#include "../Services/ServiceTests.hpp"
 
 TEST_CASE("Time format implementation for CDS", "[CDS]") {
 	SECTION("Invalid date") {
@@ -16,7 +17,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") {
 		TimeHelper::utcToSeconds(TimeInfo);
 
 		// invalid month
-		TimeInfo.year = 2018;
+		TimeInfo.year = 2019;
 		TimeInfo.month = 60;
 		TimeInfo.day = 10;
 		TimeInfo.hour = 10;
@@ -26,7 +27,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") {
 		TimeHelper::utcToSeconds(TimeInfo);
 
 		// invalid day
-		TimeInfo.year = 2018;
+		TimeInfo.year = 2019;
 		TimeInfo.month = 4;
 		TimeInfo.day = 35;
 		TimeInfo.hour = 10;
@@ -36,7 +37,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") {
 		TimeHelper::utcToSeconds(TimeInfo);
 
 		// invalid hour
-		TimeInfo.year = 2018;
+		TimeInfo.year = 2019;
 		TimeInfo.month = 4;
 		TimeInfo.day = 10;
 		TimeInfo.hour = 100;
@@ -46,7 +47,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") {
 		TimeHelper::utcToSeconds(TimeInfo);
 
 		// invalid minute
-		TimeInfo.year = 2018;
+		TimeInfo.year = 2019;
 		TimeInfo.month = 4;
 		TimeInfo.day = 10;
 		TimeInfo.hour = 10;
@@ -56,7 +57,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") {
 		TimeHelper::utcToSeconds(TimeInfo);
 
 		// invalid second
-		TimeInfo.year = 2018;
+		TimeInfo.year = 2019;
 		TimeInfo.month = 4;
 		TimeInfo.day = 10;
 		TimeInfo.hour = 10;
@@ -64,6 +65,9 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") {
 		TimeInfo.second = 122;
 
 		TimeHelper::utcToSeconds(TimeInfo);
+
+		CHECK(ServiceTests::countErrors() == 6);
+		CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate));
 	}
 
 	SECTION("Convert UTC date to elapsed seconds since Unix epoch") {
diff --git a/test/Message.cpp b/test/Message.cpp
index 25f098322d48d4ec9b80f8fce62937653b22b9ac..a13c29e2b7b59298188b77042069ac60b83af8f8 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);
@@ -155,7 +156,7 @@ TEST_CASE("Requirement 7.3.6 (Real)", "[message][ecss]") {
 TEST_CASE("Requirement 7.3.8 (Octet-string)", "[message][ecss]") {
 	Message message(0, 0, Message::TC, 0);
 
-	message.appendString<4>("test");
+	message.appendString(String<4>("test"));
 
 	REQUIRE(message.dataSize == 4);
 
@@ -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/MessageParser.cpp b/test/MessageParser.cpp
index d11d42ab54e4bfd796f1d726855bda45489458aa..c416f1b5d6cdecf2c0b19f311bde1963540b3716 100644
--- a/test/MessageParser.cpp
+++ b/test/MessageParser.cpp
@@ -9,30 +9,68 @@
 
 
 TEST_CASE("TC message parsing", "[MessageParser]") {
-	MessageParser messageParser;
-	uint8_t packet[] = {0x18, 0x07, 0xc0, 0x4d, 0x00, 0x0a, 0x20, 0x81, 0x1f, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f};
+	uint8_t packet[] = {0x18, 0x07, 0xe0, 0x07, 0x00, 0x0a, 0x20, 0x81, 0x1f, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f};
 
-	Message message = messageParser.parse(packet, 16);
+	Message message = MessageParser::parse(packet, 16);
 	CHECK(message.packetType == Message::TC);
 	CHECK(message.applicationId == 7);
+	CHECK(message.packetSequenceCount == 8199);
 	CHECK(message.dataSize == 5);
 	CHECK(message.serviceType == 129);
 	CHECK(message.messageType == 31);
 	CHECK(memcmp(message.data, "hello", 5) == 0);
 }
 
-TEST_CASE("TC data parsing into a message", "[MessageParser]") {}
+TEST_CASE("TC Message parsing into a string", "[MessageParser]") {
+	uint8_t wantedPacket[] = {0x18, 0x07, 0xe0, 0x07, 0x00, 0x0a, 0x20, 0x81, 0x1f, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c,
+		0x6f};
+
+	Message message;
+	message.packetType = Message::TC;
+	message.applicationId = 7;
+	message.serviceType = 129;
+	message.messageType = 31;
+	message.packetSequenceCount = 8199;
+	memcpy(message.data, "hello", 5);
+	message.dataSize = 5;
+
+	String<CCSDS_MAX_MESSAGE_SIZE> createdPacket = MessageParser::compose(message);
+
+	CHECK(createdPacket.size() == 16);
+	// The two parentheses are necessary so that Catch2 doesn't try to parse the strings here
+	CHECK((createdPacket == String<16>(wantedPacket)));
+}
 
 TEST_CASE("TM message parsing", "[MessageParser]") {
-	MessageParser messageParser;
 	uint8_t packet[] = {0x08, 0x02, 0xc0, 0x4d, 0x00, 0x0c, 0x20, 0x16, 0x11,
-	                    0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69};
+		0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69};
 
-	Message message = messageParser.parse(packet, 18);
+	Message message = MessageParser::parse(packet, 18);
 	CHECK(message.packetType == Message::TM);
 	CHECK(message.applicationId == 2);
+	CHECK(message.packetSequenceCount == 77);
 	CHECK(message.dataSize == 7);
 	CHECK(message.serviceType == 22);
 	CHECK(message.messageType == 17);
 	CHECK(memcmp(message.data, "hellohi", 7) == 0);
 }
+
+TEST_CASE("TM Message parsing into a string", "[MessageParser]") {
+	uint8_t wantedPacket[] = {0x08, 0x02, 0xc0, 0x4d, 0x00, 0x0c, 0x20, 0x16, 0x11,
+		0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69};
+
+	Message message;
+	message.packetType = Message::TM;
+	message.applicationId = 2;
+	message.packetSequenceCount = 77;
+	message.serviceType = 22;
+	message.messageType = 17;
+	memcpy(message.data, "hellohi", 7);
+	message.dataSize = 7;
+
+	String<CCSDS_MAX_MESSAGE_SIZE> createdPacket = MessageParser::compose(message);
+
+	CHECK(createdPacket.size() == 18);
+	// The two parentheses are necessary so that Catch2 doesn't try to parse the strings here
+	CHECK((createdPacket == String<18>(wantedPacket)));
+}
diff --git a/test/Services/FunctionManagementService.cpp b/test/Services/FunctionManagementService.cpp
index 64683103f21ee59c80db329077d148ca6e335266..a793c3e9035ed37eb4aa9351d235fe1774f866a6 100644
--- a/test/Services/FunctionManagementService.cpp
+++ b/test/Services/FunctionManagementService.cpp
@@ -8,7 +8,7 @@ FunctionManagementService& fms = Services.functionManagement;
 
 uint8_t globalVariable = 10;
 
-void test(String<MAX_ARG_LENGTH> a) {
+void test(String<ECSS_FUNCTION_MAX_ARG_LENGTH> a) {
 	globalVariable = a[0];
 }
 
@@ -17,10 +17,10 @@ TEST_CASE("ST[08] - Call Tests") {
 		ServiceTests::reset();
 		globalVariable = 10;
 
-		fms.include(String<FUNC_NAME_LENGTH>("test"), &test);
+		fms.include(String<ECSS_FUNCTION_NAME_LENGTH>("test"), &test);
 		Message msg(8, 1, Message::TC, 1);
 
-		msg.appendFixedString(String<FUNC_NAME_LENGTH>("test"));
+		msg.appendFixedString(String<ECSS_FUNCTION_NAME_LENGTH>("test"));
 		msg.appendByte(199);
 		MessageParser::execute(msg);
 
@@ -32,9 +32,9 @@ TEST_CASE("ST[08] - Call Tests") {
 		ServiceTests::reset();
 		globalVariable = 10;
 
-		fms.include(String<FUNC_NAME_LENGTH>("test"), &test);
+		fms.include(String<ECSS_FUNCTION_NAME_LENGTH>("test"), &test);
 		Message msg(8, 1, Message::TC, 1);
-		msg.appendFixedString(String<FUNC_NAME_LENGTH>("t3st"));
+		msg.appendFixedString(String<ECSS_FUNCTION_NAME_LENGTH>("t3st"));
 		MessageParser::execute(msg);
 
 		CHECK(ServiceTests::get(0).messageType == 4);
@@ -47,16 +47,16 @@ TEST_CASE("ST[08] - Call Tests") {
 		ServiceTests::reset();
 		globalVariable = 10;
 
-		fms.include(String<FUNC_NAME_LENGTH>("test"), &test);
+		fms.include(String<ECSS_FUNCTION_NAME_LENGTH>("test"), &test);
 		Message msg(8, 1, Message::TC, 1);
-		msg.appendFixedString(String<FUNC_NAME_LENGTH>("test"));
+		msg.appendFixedString(String<ECSS_FUNCTION_NAME_LENGTH>("test"));
 		msg.appendString(String<65>
 		    ("eqrhjweghjhwqgthjkrghthjkdsfhgsdfhjsdjsfdhgkjdfsghfjdgkdfsgdfgsgd"));
 		MessageParser::execute(msg);
 
 		CHECK(ServiceTests::get(0).messageType == 4);
 		CHECK(ServiceTests::get(0).serviceType == 1);
-		CHECK(ServiceTests::countErrors() == 2);
+		CHECK(ServiceTests::countErrors() == 1);
 		CHECK(globalVariable == 10);
 	}
 }
@@ -67,9 +67,9 @@ TEST_CASE("ST[08] - Insert Tests") {
 		ServiceTests::reset();
 		std::string name = "test"; // FOR TESTING ONLY!
 
-		for (int i = 0; i < FUNC_MAP_SIZE + 1; i++) {
+		for (int i = 0; i < ECSS_FUNCTION_MAP_SIZE + 1; i++) {
 			name += std::to_string(i); // different names to fill up the map
-			fms.include(String<FUNC_NAME_LENGTH>(name.c_str()), &test);
+			fms.include(String<ECSS_FUNCTION_NAME_LENGTH>(name.c_str()), &test);
 		}
 		CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::MapFull));
 	}
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/RequestVerificationService.cpp b/test/Services/RequestVerificationService.cpp
index 6576bdbaa5b2183432e747a1a76b25788f646fda..df0ea7e26c31c971b940c391f453d9908fa15696 100644
--- a/test/Services/RequestVerificationService.cpp
+++ b/test/Services/RequestVerificationService.cpp
@@ -20,7 +20,7 @@ TEST_CASE("TM[1,1]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
@@ -41,7 +41,7 @@ TEST_CASE("TM[1,2]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
@@ -63,7 +63,7 @@ TEST_CASE("TM[1,3]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
@@ -84,7 +84,7 @@ TEST_CASE("TM[1,4]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
@@ -106,7 +106,7 @@ TEST_CASE("TM[1,5]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
@@ -128,7 +128,7 @@ TEST_CASE("TM[1,6]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
@@ -151,7 +151,7 @@ TEST_CASE("TM[1,7]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
@@ -171,7 +171,7 @@ TEST_CASE("TM[1,8]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
@@ -193,7 +193,7 @@ TEST_CASE("TM[1,10]", "[service][st01]") {
 	// Check for the value that is stored in <<data>> array(data-member of object response)
 	CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number
 	CHECK(response.readEnumerated(1) == Message::TC); // packet type
-	CHECK(response.readBits(1) == 0); // secondary header flag
+	CHECK(response.readBits(1) == 1); // secondary header flag
 	CHECK(response.readEnumerated(11) == 3); // application process ID
 	CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags
 	CHECK(response.readBits(14) == 0); // packet sequence count
diff --git a/test/Services/TimeBasedSchedulingService.cpp b/test/Services/TimeBasedSchedulingService.cpp
index 56a7267df7c9b2859a9d813bcd4ed780f01430e2..e74ff765734bd695d8c0a8930f36158d5e2d4b20 100644
--- a/test/Services/TimeBasedSchedulingService.cpp
+++ b/test/Services/TimeBasedSchedulingService.cpp
@@ -33,7 +33,6 @@ namespace unit_test
 } // namespace unit_test
 
 Message testMessage1, testMessage2, testMessage3, testMessage4;
-MessageParser msgParser;
 auto currentTime = static_cast<uint32_t>(time(nullptr)); // Get the current system time
 bool messagesPopulated = false; // Indicate whether the test messages are initialized
 
@@ -71,19 +70,19 @@ auto activityInsertion(TimeBasedSchedulingService& timeService) {
 
 	// Test activity 1
 	receivedMessage.appendUint32(currentTime + 1556435);
-	receivedMessage.appendString(msgParser.convertTCToStr(testMessage1));
+	receivedMessage.appendMessage(testMessage1, ECSS_TC_REQUEST_STRING_SIZE);
 
 	// Test activity 2
 	receivedMessage.appendUint32(currentTime + 1957232);
-	receivedMessage.appendString(msgParser.convertTCToStr(testMessage2));
+	receivedMessage.appendMessage(testMessage2, ECSS_TC_REQUEST_STRING_SIZE);
 
 	// Test activity 3
 	receivedMessage.appendUint32(currentTime + 1726435);
-	receivedMessage.appendString(msgParser.convertTCToStr(testMessage3));
+	receivedMessage.appendMessage(testMessage3, ECSS_TC_REQUEST_STRING_SIZE);
 
 	// Test activity 4
 	receivedMessage.appendUint32(currentTime + 17248435);
-	receivedMessage.appendString(msgParser.convertTCToStr(testMessage4));
+	receivedMessage.appendMessage(testMessage4, ECSS_TC_REQUEST_STRING_SIZE);
 
 	// Insert activities in the schedule. They have to be inserted sorted
 	timeService.insertActivities(receivedMessage);
@@ -115,14 +114,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 +198,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 +213,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") {
@@ -275,7 +275,7 @@ TEST_CASE("TC[11,9] Detail report scheduled activities by ID", "[service][st11]"
 			Message receivedTCPacket;
 			uint8_t receivedDataStr[ECSS_TC_REQUEST_STRING_SIZE];
 			response.readString(receivedDataStr, ECSS_TC_REQUEST_STRING_SIZE);
-			receivedTCPacket = msgParser.parseRequestTC(receivedDataStr);
+			receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr);
 
 			if (i == 0) {
 				REQUIRE(receivedReleaseTime == scheduledActivities.at(0)->requestReleaseTime);
@@ -379,10 +379,10 @@ TEST_CASE("TC[11,16] Detail report all scheduled activities", "[service][st11]")
 		Message receivedTCPacket;
 		uint8_t receivedDataStr[ECSS_TC_REQUEST_STRING_SIZE];
 		response.readString(receivedDataStr, ECSS_TC_REQUEST_STRING_SIZE);
-		receivedTCPacket = msgParser.parseRequestTC(receivedDataStr);
+		receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr);
 
 		REQUIRE(receivedReleaseTime == scheduledActivities.at(i)->requestReleaseTime);
-		REQUIRE(scheduledActivities.at(i)->request == receivedTCPacket);
+		REQUIRE(receivedTCPacket.bytesEqualWith(scheduledActivities.at(i)->request));
 	}
 }
 
@@ -408,7 +408,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") {
diff --git a/test/TestPlatform.cpp b/test/TestPlatform.cpp
index a4cbee4d07452934aec010d06061b4479cb2d775..6833d3edeb9222d5d106f0ddf5dcac0e4f7ad5a7 100644
--- a/test/TestPlatform.cpp
+++ b/test/TestPlatform.cpp
@@ -3,6 +3,7 @@
 #include <catch2/catch.hpp>
 #include <Message.hpp>
 #include <Service.hpp>
+#include <Logger.hpp>
 #include "Services/ServiceTests.hpp"
 
 // Explicit template specializations for the logError() function
@@ -34,6 +35,10 @@ void ErrorHandler::logError(ErrorType errorType) {
 	ServiceTests::addError(ErrorHandler::findErrorSource(errorType), errorType);
 }
 
+void Logger::log(Logger::LogLevel level, etl::istring & message) {
+	// Logs while testing are completely ignored
+}
+
 struct ServiceTestsListener : Catch::TestEventListenerBase {
 	using TestEventListenerBase::TestEventListenerBase; // inherit constructor
 
@@ -43,8 +48,7 @@ struct ServiceTestsListener : Catch::TestEventListenerBase {
 			// An Error was thrown with this Message. If you expected this to happen, please call a
 			// corresponding assertion function from ServiceTests to silence this message.
 
-			// TODO: Uncomment the following line as soon as Issue #19 is closed
-			// CHECK(ServiceTests::hasNoErrors());
+			CHECK(ServiceTests::hasNoErrors());
 		}
 	}