diff --git a/CMakeLists.txt b/CMakeLists.txt
index fb561a7167901794677f8bd21dcc4ecec0c20a7d..2d60013b3ee2fb8f46558d46b891e11be189d1c8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,6 @@ include_directories("${PROJECT_SOURCE_DIR}/inc" "${PROJECT_SOURCE_DIR}/lib/etl/i
 
 add_custom_target(check
         COMMAND ./cppcheck.sh
-        COMMAND ./vera.sh
         COMMAND ./clang-tidy.sh
         COMMAND ./cppcheck-misra.sh
         WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/ci")
diff --git a/inc/Message.hpp b/inc/Message.hpp
index b37d4ac2d866fed774af516443d5863b2106c2aa..2ee8d3c85f0d302c6d2e71ff73d7cb3d110f2d43 100644
--- a/inc/Message.hpp
+++ b/inc/Message.hpp
@@ -358,6 +358,20 @@ public:
 	 */
 	void appendOctetString(const etl::istring& string);
 
+	/**
+	 * Generic function to append any type of data to the message. The amount of bytes appended is equal to the size of
+	 * the @ref T value.
+	 *
+	 * The data is appended on the current write position (deduced by @ref dataSize)
+	 *
+	 * Calling this or any of the other `append...` functions for equivalent types is exactly the same.
+	 *
+	 * @tparam T The type of the value to be appended
+	 * @return The value to append
+	 */
+	template <typename T>
+	void append(const T& value);
+
 	/**
 	 * Adds a nested TC or TM Message within the current Message
 	 *
@@ -514,6 +528,42 @@ public:
 		return size; // Return the string size
 	}
 
+	/**
+	 * Fetches an N-byte string from the current position in the message. The string can be at most MAX_SIZE long.
+	 *
+	 * @note This function was not implemented as Message::read() due to an inherent C++ limitation, see
+	 * https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/
+	 * @tparam MAX_SIZE The memory size of the string in bytes, which corresponds to the max string size
+	 */
+	template<const size_t MAX_SIZE>
+	String<MAX_SIZE> readOctetString() {
+		String<MAX_SIZE> string("");
+
+		uint16_t length = readUint16();
+		ASSERT_REQUEST(length <= string.max_size(), ErrorHandler::StringTooShort);
+		ASSERT_REQUEST((readPosition + length) <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooShort);
+
+		string.append(data + readPosition, length);
+		readPosition += length;
+
+		return std::move(string);
+	}
+
+	/**
+	 * Generic function to read any type of data from the message. The amount of bytes read is equal to the size of
+	 * the @ref T value.
+	 *
+	 * After the data is read, the message pointer @ref readPosition moves forward so that the next amount of data
+	 * can be read.
+	 *
+	 * Calling this or any of the other `read...` functions for equivalent types is exactly the same.
+	 *
+	 * @tparam T The type to be read
+	 * @return The value that has been read from the string
+	 */
+	template <typename T>
+	T read();
+
 	/**
 	 * @brief Skip read bytes in the read string
 	 * @details Skips the provided number of bytes, by incrementing the readPosition and this is
@@ -564,4 +614,37 @@ public:
 	}
 };
 
+template<> inline void Message::append(const uint8_t& value) { appendUint8(value); }
+template<> inline void Message::append(const uint16_t& value) { appendUint16(value); }
+template<> inline void Message::append(const uint32_t& value) { appendUint32(value); }
+template<> inline void Message::append(const uint64_t& value) { appendUint64(value); }
+
+template<> inline void Message::append(const int8_t& value) { appendSint8(value); }
+template<> inline void Message::append(const int16_t& value) { appendSint16(value); }
+template<> inline void Message::append(const int32_t& value) { appendSint32(value); }
+
+template<> inline void Message::append(const bool& value) { appendBoolean(value); }
+template<> inline void Message::append(const char& value) { appendByte(value); }
+template<> inline void Message::append(const float& value) { appendFloat(value); }
+
+/**
+ * Appends an ETL string to the message. ETL strings are handled as ECSS octet strings, meaning that the string size
+ * is appended as a byte before the string itself. To append other string sequences, see the Message::appendString()
+ * functions
+ */
+template<> inline void Message::append(const etl::istring& value) { appendOctetString(value); }
+
+template<> inline uint8_t Message::read() { return readUint8(); }
+template<> inline uint16_t Message::read() { return readUint16(); }
+template<> inline uint32_t Message::read() { return readUint32(); }
+template<> inline uint64_t Message::read() { return readUint64(); }
+
+template<> inline int8_t Message::read() { return readSint8(); }
+template<> inline int16_t Message::read() { return readSint16(); }
+template<> inline int32_t Message::read() { return readSint32(); }
+
+template<> inline bool Message::read<bool>() { return readBoolean(); }
+template<> inline char Message::read() { return readByte(); }
+template<> inline float Message::read() { return readFloat(); }
+
 #endif // ECSS_SERVICES_PACKET_H
diff --git a/inc/Services/Parameter.hpp b/inc/Services/Parameter.hpp
index 932d450f5389fb6cb67c9979f52106aef00d9b4c..d2fbe07053aa21e514b3782cd14c109151b02116 100644
--- a/inc/Services/Parameter.hpp
+++ b/inc/Services/Parameter.hpp
@@ -47,9 +47,7 @@ private:
 	DataType currentValue;
 
 public:
-	Parameter(DataType initialValue) {
-		currentValue = initialValue;
-	}
+	explicit Parameter(DataType initialValue) : currentValue(initialValue) {}
 
 	inline void setValue(DataType value) {
 		currentValue = value;
@@ -59,31 +57,19 @@ public:
 		return currentValue;
 	}
 
-	inline void setValueFromMessage(Message& message) override;
+	/**
+	 * Given an ECSS message that contains this parameter as its first input, this loads the value from that paremeter
+	 */
+	inline void setValueFromMessage(Message& message) override {
+		currentValue = message.read<DataType>();
+	};
 
-	inline void appendValueToMessage(Message& message) override;
+	/**
+	 * Appends the parameter as an ECSS value to an ECSS Message
+	 */
+	inline void appendValueToMessage(Message& message) override {
+		message.append<DataType>(currentValue);
+	};
 };
 
-template<> inline void Parameter<uint8_t>::setValueFromMessage(Message& message) {
-	currentValue = message.readUint8();
-}
-template<> inline void Parameter<uint16_t>::setValueFromMessage(Message& message) {
-	currentValue = message.readUint16();
-}
-
-template<> inline void Parameter<uint32_t>::setValueFromMessage(Message& message) {
-	currentValue = message.readUint32();
-}
-
-template<> inline void Parameter<uint8_t>::appendValueToMessage(Message& message) {
-	message.appendUint8(this->currentValue);
-}
-
-template<> inline void Parameter<uint16_t>::appendValueToMessage(Message& message) {
-	message.appendUint16(this->currentValue);
-}
-
-template<> inline void Parameter<uint32_t>::appendValueToMessage(Message& message) {
-	message.appendUint32(this->currentValue);
-}
 #endif //ECSS_SERVICES_PARAMETER_HPP
diff --git a/test/Message.cpp b/test/Message.cpp
index 8d356b6eafc9ea886067d3c84b5c73f6dcf041e1..4e1d12f56280bd21d2e122bd3544d42e716d03ea 100644
--- a/test/Message.cpp
+++ b/test/Message.cpp
@@ -1,6 +1,7 @@
 #include <catch2/catch.hpp>
 #include <Message.hpp>
 #include <ServicePool.hpp>
+#include "etl/String.hpp"
 #include "Services/EventReportService.hpp"
 
 TEST_CASE("Message is usable", "[message]") {
@@ -63,13 +64,13 @@ TEST_CASE("Requirement 5.3.1", "[message][ecss]") {
 TEST_CASE("Requirement 7.3.2 (Boolean)", "[message][ecss]") {
 	Message message(0, 0, Message::TC, 0);
 
-	message.appendBoolean(false);
-	message.appendBoolean(true);
+	message.append<bool>(false);
+	message.append<bool>(true);
 
 	REQUIRE(message.dataSize == 2);
 
-	CHECK_FALSE(message.readBoolean());
-	CHECK(message.readBoolean());
+	CHECK_FALSE(message.read<bool>());
+	CHECK(message.read<bool>());
 }
 
 TEST_CASE("Requirement 7.3.3 (Enumerated)", "[message][ecss]") {
@@ -93,17 +94,19 @@ TEST_CASE("Requirement 7.3.3 (Enumerated)", "[message][ecss]") {
 TEST_CASE("Requirement 7.3.4 (Unsigned integer)", "[message][ecss]") {
 	Message message(0, 0, Message::TC, 0);
 
-	message.appendUint8(230);
-	message.appendUint16(15933);
-	message.appendUint32(2000001);
-	message.appendUint64(12446744073709551615ULL);
+	message.append<char>(110);
+	message.append<uint8_t>(230);
+	message.append<uint16_t>(15933);
+	message.append<uint32_t>(2000001);
+	message.append<uint64_t>(12446744073709551615ULL);
 
-	REQUIRE(message.dataSize == 1 + 2 + 4 + 8);
+	REQUIRE(message.dataSize == 1 + 1 + 2 + 4 + 8);
 
-	CHECK(message.readUint8() == 230);
-	CHECK(message.readUint16() == 15933);
-	CHECK(message.readUint32() == 2000001);
-	CHECK(message.readUint64() == 12446744073709551615ULL);
+	CHECK(message.read<char>() == 110);
+	CHECK(message.read<uint8_t>() == 230);
+	CHECK(message.read<uint16_t>() == 15933);
+	CHECK(message.read<uint32_t>() == 2000001);
+	CHECK(message.read<uint64_t>() == 12446744073709551615ULL);
 
 	SECTION("7.4.3") {
 		/**
@@ -112,25 +115,25 @@ TEST_CASE("Requirement 7.3.4 (Unsigned integer)", "[message][ecss]") {
 		 * processors store data in little endian format. As a result, special care needs to be
 		 * taken for compliance.
 		 */
-		CHECK(message.data[1] == 0x3e);
-		CHECK(message.data[2] == 0x3d);
+		CHECK(message.data[2] == 0x3e);
+		CHECK(message.data[3] == 0x3d);
 	}
 }
 
 TEST_CASE("Requirement 7.3.5 (Signed integer)", "[message][ecss]") {
 	Message message(0, 0, Message::TC, 0);
 
-	message.appendSint8(-16);
-	message.appendSint16(-7009);
-	message.appendSint32(-2000001);
-	message.appendSint32(15839011);
+	message.append<int8_t>(-16);
+	message.append<int16_t>(-7009);
+	message.append<int32_t>(-2000001);
+	message.append<int32_t>(15839011);
 
 	REQUIRE(message.dataSize == 1 + 2 + 4 + 4);
 
-	CHECK(message.readSint8() == -16);
-	CHECK(message.readSint16() == -7009);
-	CHECK(message.readSint32() == -2000001);
-	CHECK(message.readSint32() == 15839011);
+	CHECK(message.read<int8_t>() == -16);
+	CHECK(message.read<int16_t>() == -7009);
+	CHECK(message.read<int32_t>() == -2000001);
+	CHECK(message.read<int32_t>() == 15839011);
 
 	SECTION("7.4.3") {
 		// Make sure the endianness of the message data is correct
@@ -145,25 +148,29 @@ TEST_CASE("Requirement 7.3.5 (Signed integer)", "[message][ecss]") {
 TEST_CASE("Requirement 7.3.6 (Real)", "[message][ecss]") {
 	Message message(0, 0, Message::TC, 0);
 
-	message.appendFloat(7.209f);
-	message.appendFloat(-9003.53135f);
+	message.append<float>(7.209f);
+	message.append<float>(-9003.53135f);
 
 	REQUIRE(message.dataSize == 8);
 
-	CHECK(message.readFloat() == 7.209f);
-	CHECK(message.readFloat() == -9003.53135f);
+	CHECK(message.read<float>() == 7.209f);
+	CHECK(message.read<float>() == -9003.53135f);
 }
 
 TEST_CASE("Requirement 7.3.8 (Octet-string)", "[message][ecss]") {
 	Message message(0, 0, Message::TC, 0);
 
 	message.appendString(String<4>("test"));
+	message.append<etl::istring>(String<4>("gaus"));
 
-	REQUIRE(message.dataSize == 4);
+	REQUIRE(message.dataSize == 4 + 6);
 
 	char string[5];
 	message.readCString(string, 4);
 	CHECK_THAT(string, Catch::Matchers::Equals("test"));
+
+	auto output = message.readOctetString<10>();
+	CHECK_THAT(output.c_str(), Catch::Matchers::Equals("gaus"));
 }
 
 TEST_CASE("Requirement 7.3.13 (Packet)", "[message][ecss]") {
diff --git a/test/Services/Parameter.cpp b/test/Services/Parameter.cpp
index 3a9b8efec4994e60d7b21a473df4c50c2a7e3787..837be7643c39cb5021a261723bde9e5bb9d1488f 100644
--- a/test/Services/Parameter.cpp
+++ b/test/Services/Parameter.cpp
@@ -6,9 +6,9 @@
 TEST_CASE("Parameter Append") {
 	SECTION("Check correct appending") {
 		Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1);
-		Parameter<uint8_t> parameter1 = Parameter<uint8_t>(1);
-		Parameter<uint16_t> parameter2 = Parameter<uint16_t>(500);
-		Parameter<uint32_t> parameter3 = Parameter<uint32_t>(70000);
+		auto parameter1 = Parameter<uint8_t>(1);
+		auto parameter2 = Parameter<uint16_t>(500);
+		auto parameter3 = Parameter<uint32_t>(70000);
 
 		parameter1.appendValueToMessage(request);
 		parameter2.appendValueToMessage(request);
@@ -23,9 +23,9 @@ TEST_CASE("Parameter Append") {
 TEST_CASE("Parameter Set") {
 	SECTION("Check correct setting") {
 		Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1);
-		Parameter<uint8_t> parameter1 = Parameter<uint8_t>(1);
-		Parameter<uint16_t> parameter2 = Parameter<uint16_t>(500);
-		Parameter<uint32_t> parameter3 = Parameter<uint32_t>(70000);
+		auto parameter1 = Parameter<uint8_t>(1);
+		auto parameter2 = Parameter<uint16_t>(500);
+		auto parameter3 = Parameter<uint32_t>(70000);
 
 		request.appendUint8(10);
 		request.appendUint16(1000);