diff --git a/.gitignore b/.gitignore
index 754bc9f09a550be8eda2fe17f67ad5ce9f128085..ff456d6912b537e7a9fb03f28635b2d78c449818 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,3 @@
-obsw_user_code.py
-
 _build/*
 _dependencies/*
 _bin/*
diff --git a/.idea/runConfigurations/tmtcclient_Binary_Upload_Serial.xml b/.idea/runConfigurations/tmtcclient_Binary_Upload_Serial.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1ea009cce7889d056cdffd4c6141e644cdb073df
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Binary_Upload_Serial.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Binary Upload Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 5 -c 1" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Software_Serial.xml b/.idea/runConfigurations/tmtcclient_CLI.xml
similarity index 75%
rename from .idea/runConfigurations/tmtcclient_Software_Serial.xml
rename to .idea/runConfigurations/tmtcclient_CLI.xml
index 0dfd7b192a6af6da43bb59f26b4b1c33c7030405..3fff92f9f52936c715fe17aca61e15aac6db9efc 100644
--- a/.idea/runConfigurations/tmtcclient_Software_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_CLI.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Software Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient CLI" type="PythonConfigurationType" factoryName="Python">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,8 +12,8 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
-    <option name="PARAMETERS" value="-m 4 -c 1 --hk -t 2.5" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
     <option name="MODULE_MODE" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Clear_SD_Card_.xml b/.idea/runConfigurations/tmtcclient_Clear_SD_Card_.xml
new file mode 100644
index 0000000000000000000000000000000000000000..44da0a930bf8625b82da82554d98660f440a5520
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Clear_SD_Card_.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Clear SD-Card " type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s SD -o A20 -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Copy_Bootloader.xml b/.idea/runConfigurations/tmtcclient_Copy_Bootloader.xml
new file mode 100644
index 0000000000000000000000000000000000000000..736bcabfd2d44298567bb23488fc72e175261a20
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Copy_Bootloader.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Copy Bootloader" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s Img -o A11S -c 1 -t 4" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Copy_OBSW_Update.xml b/.idea/runConfigurations/tmtcclient_Copy_OBSW_Update.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5da1e90b560d3818ec94438c15516f7203be48b3
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Copy_OBSW_Update.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Copy OBSW Update" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s Img -o A4U -c 1 -t 4" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Create_File_Struct_AT91.xml b/.idea/runConfigurations/tmtcclient_Create_File_Struct_AT91.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c6f1d6803daa7b6e151b38e5659b81fa811dd892
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Create_File_Struct_AT91.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Create File Struct AT91" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s SD -o C0A -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Disable_Periodic_Print_.xml b/.idea/runConfigurations/tmtcclient_Disable_Periodic_Print_.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0e02490417cea00fc0c0d247dcf7eccd85b77b38
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Disable_Periodic_Print_.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Disable Periodic Print " type="PythonConfigurationType" factoryName="Python" folderName="Serial Utility">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s 17 -o 130 -c 1 -t 2.2" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Dummy_UDP.xml b/.idea/runConfigurations/tmtcclient_Dummy_UDP.xml
index f591aac987e2c7e3c3038ad565a68c9404b06266..8d3a1fc9ee91099739631f163b2f072d09c32ab8 100644
--- a/.idea/runConfigurations/tmtcclient_Dummy_UDP.xml
+++ b/.idea/runConfigurations/tmtcclient_Dummy_UDP.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 3 -c 3 -s Dummy -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Enable_Periodic_Print.xml b/.idea/runConfigurations/tmtcclient_Enable_Periodic_Print.xml
new file mode 100644
index 0000000000000000000000000000000000000000..73417998676784fcda6e5155a672f1e0211fce7c
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Enable_Periodic_Print.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Enable Periodic Print" type="PythonConfigurationType" factoryName="Python" folderName="Serial Utility">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s 17 -o 129 -c 1 -t 2.2" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Format_SD_Card_.xml b/.idea/runConfigurations/tmtcclient_Format_SD_Card_.xml
new file mode 100644
index 0000000000000000000000000000000000000000..455cfed9156e5b7daca84fa9b650634aa46df8c5
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Format_SD_Card_.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Format SD-Card " type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s SD -o A21 -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_GPS0_Serial.xml b/.idea/runConfigurations/tmtcclient_GPS0_Serial.xml
index 0f8cd5fea5235267554c57f05839890d0fdd1617..c7a506c644d571a404d22dca29e26e433951d5bf 100644
--- a/.idea/runConfigurations/tmtcclient_GPS0_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_GPS0_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient GPS0 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient GPS0 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s GPS0 -c 1 --hk -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_GPS1_Serial.xml b/.idea/runConfigurations/tmtcclient_GPS1_Serial.xml
index 1f8098a72d1b52a387c0256e35fa3cebdb4ccfae..a6623ad0098f1d2678150becbabca84296cf88a1 100644
--- a/.idea/runConfigurations/tmtcclient_GPS1_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_GPS1_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient GPS1 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient GPS1 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s GPS1 -c 1 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_GUI.xml b/.idea/runConfigurations/tmtcclient_GUI.xml
index 6ae7919b09fd2b1514488e8ab73c7b964ebf0c64..7dd7c34ab2e002427304aad205be2c0b137a0e44 100644
--- a/.idea/runConfigurations/tmtcclient_GUI.xml
+++ b/.idea/runConfigurations/tmtcclient_GUI.xml
@@ -12,8 +12,8 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
-    <option name="PARAMETERS" value="-m 0" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_gui.py" />
+    <option name="PARAMETERS" value="" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
     <option name="MODULE_MODE" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Hamming_from_FRAM.xml b/.idea/runConfigurations/tmtcclient_Hamming_from_FRAM.xml
new file mode 100644
index 0000000000000000000000000000000000000000..34f82294477bd8f4553ea425b1297a44699fc91e
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Hamming_from_FRAM.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Hamming from FRAM" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -c 1 -s Img -o P1 -t 3" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Hamming_from_SD.xml b/.idea/runConfigurations/tmtcclient_Hamming_from_SD.xml
new file mode 100644
index 0000000000000000000000000000000000000000..886cb5a0638f95667d240ce2ae51d2c3efc0fd51
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Hamming_from_SD.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Hamming from SD" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -c 1 -s Img -o P0 -t 3" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Help.xml b/.idea/runConfigurations/tmtcclient_Help.xml
index 6aece9026d223bfd963b60d15dc2fe90cc60130e..1ce63fbd7352d94774f10a0e4d7bd2cb4385e5c1 100644
--- a/.idea/runConfigurations/tmtcclient_Help.xml
+++ b/.idea/runConfigurations/tmtcclient_Help.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-h" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Listener_Serial.xml b/.idea/runConfigurations/tmtcclient_Listener_Serial.xml
index c59efdb836593429d3c211d1ed21f814c1a13177..caf110fdf15843a00e038d4ae9df69dab0104517 100644
--- a/.idea/runConfigurations/tmtcclient_Listener_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_Listener_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Listener Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Listener Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 1 --hk -c 1" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Lock_file.xml b/.idea/runConfigurations/tmtcclient_Lock_file.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d071692fbc812f1034f3a4b4553c7da72f1785a8
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Lock_file.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Lock file" type="PythonConfigurationType" factoryName="Python" folderName="Serial FileManagement">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s SD -o 5 -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Module_Test_Serial.xml b/.idea/runConfigurations/tmtcclient_Module_Test_Serial.xml
index 61aa71dc850e5caf9c5bc54cb29b45ff3b63c73e..5a7ccb64b8f6d2578ee686fb0d9eba74db8113b4 100644
--- a/.idea/runConfigurations/tmtcclient_Module_Test_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_Module_Test_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Module Test Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Module Test Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 6 -c 1 --hk" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_PowerCycle_OBC_.xml b/.idea/runConfigurations/tmtcclient_PowerCycle_OBC_.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b3b97842056ab569cc84d1da2d200123a03562d5
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_PowerCycle_OBC_.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient PowerCycle OBC " type="PythonConfigurationType" factoryName="Python" folderName="Serial Core">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s Core -o A11 -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Print_SD_Card.xml b/.idea/runConfigurations/tmtcclient_Print_SD_Card.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b88172c9db66aadba52a7de897399ddeabfdb037
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Print_SD_Card.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Print SD-Card" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s SD -o A2 -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Reset_OBC.xml b/.idea/runConfigurations/tmtcclient_Reset_OBC.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8faaee31ebf7848379248c44bf3d5551a332a9fe
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Reset_OBC.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Reset OBC" type="PythonConfigurationType" factoryName="Python" folderName="Serial Core">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s Core -o A10 -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Run_Time_Stats_OBC_.xml b/.idea/runConfigurations/tmtcclient_Run_Time_Stats_OBC_.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f35cbb6d2bc1329f5e9142483fbd4b054414ac25
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Run_Time_Stats_OBC_.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Run Time Stats OBC " type="PythonConfigurationType" factoryName="Python" folderName="Serial Core">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s Core -o A0 -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Service_17_QEMU.xml b/.idea/runConfigurations/tmtcclient_Service_17_QEMU.xml
index 394ad5de13110eb11529d1301784fb505a53da2f..7ea51c07201800ebcb05346185ebe4cb5c6902e0 100644
--- a/.idea/runConfigurations/tmtcclient_Service_17_QEMU.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_17_QEMU.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 17 -c 2 -t 2" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_17_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_17_Serial.xml
index 5145e3b7acd8d267540c2cc4214eb61b7c4c8f67..850e14b920d3f00f752d6faa00a4d4e7d8c39f14 100644
--- a/.idea/runConfigurations/tmtcclient_Service_17_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_17_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Service 17 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Service 17 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 17 -c 1 -t 2.2" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_17_UDP.xml b/.idea/runConfigurations/tmtcclient_Service_17_UDP.xml
index f8ab43f96a2a4f9497d0d2bd5d76fcd602e00c9b..20619467d64c2c7257e21a1fa282cde93912dcef 100644
--- a/.idea/runConfigurations/tmtcclient_Service_17_UDP.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_17_UDP.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 3 -c 3 -s 17 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_200_UDP.xml b/.idea/runConfigurations/tmtcclient_Service_200_UDP.xml
index cf246a41cd1d20f0cbe32274fb3b4f554e659b60..069fe98fb5550a3d7dc2de6556aea4bdbd5fb63e 100644
--- a/.idea/runConfigurations/tmtcclient_Service_200_UDP.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_200_UDP.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 3 -c 3 -s 200 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_20_QEMU___.xml b/.idea/runConfigurations/tmtcclient_Service_20_QEMU___.xml
index 506baaca1246070f05f4e7b72e07c2ad33ec0e9d..779ce5bf884a48d549e55de87b71580aab791da7 100644
--- a/.idea/runConfigurations/tmtcclient_Service_20_QEMU___.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_20_QEMU___.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 3 -s 20 -c 2 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_2_QEMU.xml b/.idea/runConfigurations/tmtcclient_Service_2_QEMU.xml
index 59d368399ee161ca806e6177a62aebb1c761bd33..dd374950dc37b895e686f27afa139b632f87c112 100644
--- a/.idea/runConfigurations/tmtcclient_Service_2_QEMU.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_2_QEMU.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 2 -c 2 -t 2.5" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_2_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_2_Serial.xml
index 38316e0359a813bff39ae6574bb14beb0430e97e..4d0d694a22fba38ee47e1221d9c93c58b3cd2a75 100644
--- a/.idea/runConfigurations/tmtcclient_Service_2_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_2_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Service 2 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Service 2 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 2 -c 1 -t 3.5" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_2_UDP.xml b/.idea/runConfigurations/tmtcclient_Service_2_UDP.xml
index d592060fa408a267fbe76d06d59580403fc484d3..4375154dbd140756c61b29ed461c2d444b80b07c 100644
--- a/.idea/runConfigurations/tmtcclient_Service_2_UDP.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_2_UDP.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 3 -c 3 -s 2 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_3_QEMU_.xml b/.idea/runConfigurations/tmtcclient_Service_3_QEMU_.xml
index 2ec6365acecc8cc4291b929e08f7bd85a6dca548..621b333828a97c3da5073d28191f71d6ff92e3f1 100644
--- a/.idea/runConfigurations/tmtcclient_Service_3_QEMU_.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_3_QEMU_.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 3 -c 2 -t 2 --hk" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_3_Serial_.xml b/.idea/runConfigurations/tmtcclient_Service_3_Serial_.xml
index 7b0db83d4daea21024c7ef99394582f529ca1486..15d51da4d1d1c373b763e2928324f7070bd1a14f 100644
--- a/.idea/runConfigurations/tmtcclient_Service_3_Serial_.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_3_Serial_.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Service 3 Serial " type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Service 3 Serial " type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 3 -c 1 -t 3 --hk" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_5_QEMU_.xml b/.idea/runConfigurations/tmtcclient_Service_5_QEMU_.xml
index 7669c7a025df306fa0658ef487b7c2e997ba833f..9d97d32114c666ee252cd8e255c451a9f10a902f 100644
--- a/.idea/runConfigurations/tmtcclient_Service_5_QEMU_.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_5_QEMU_.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 5 -c 2 -t 2" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_5_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_5_Serial.xml
index aadf325a5002b612eaf8c56d8652bb56d02a7288..f4def1c17b51f567888b60277830b9e0bd391011 100644
--- a/.idea/runConfigurations/tmtcclient_Service_5_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_5_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Service 5 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Service 5 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 5 -c 1 -t 2.0" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_8_QEMU__.xml b/.idea/runConfigurations/tmtcclient_Service_8_QEMU__.xml
index 90becd426bf2091b79d593abc446d5e6a3eaacb0..c6acd1116ef80ac6a796d560fc2a6c16256a48c9 100644
--- a/.idea/runConfigurations/tmtcclient_Service_8_QEMU__.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_8_QEMU__.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 8 -c 2 -t 2.5" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_8_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_8_Serial.xml
index 1c96bce8f9e66fec8a1b6be5948348f2928904c7..55402cb29bfedf316785073627623a1b5c9fb358 100644
--- a/.idea/runConfigurations/tmtcclient_Service_8_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_8_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Service 8 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Service 8 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 8 -c 1 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_8_UDP.xml b/.idea/runConfigurations/tmtcclient_Service_8_UDP.xml
index f7797b9f92b1467733968774111393c5b01dd13f..1da3af4e1ce31f7baa623f63dd69ae0308c97d0b 100644
--- a/.idea/runConfigurations/tmtcclient_Service_8_UDP.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_8_UDP.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 3 -c 3 -s 8" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_9_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_9_Serial.xml
index f755f02c30089f9d2bc2a6efb46f47f65ac9e3a7..952b164ef6a663285f3772b919e878c028a44abd 100644
--- a/.idea/runConfigurations/tmtcclient_Service_9_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_9_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Service 9 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Service 9 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 9 -c 1 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_Dummy_QEMU.xml b/.idea/runConfigurations/tmtcclient_Service_Dummy_QEMU.xml
index 78e091163340f8bd1a7597a8dbd58f3c338d3c53..f7d6feedef966405ee32a1e4e2154025a7cdee6d 100644
--- a/.idea/runConfigurations/tmtcclient_Service_Dummy_QEMU.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_Dummy_QEMU.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s Dummy -c 2 -t 2" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_GPS0_QEMU__.xml b/.idea/runConfigurations/tmtcclient_Service_GPS0_QEMU__.xml
index e859a0a53f4994efb589bf38808f27852b89581f..a1e01a69e621608c5dab37f395f2376f0d5cce2a 100644
--- a/.idea/runConfigurations/tmtcclient_Service_GPS0_QEMU__.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_GPS0_QEMU__.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s GPS0 -c 2 -t 2.5 --hk" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_GPS1_QEMU___.xml b/.idea/runConfigurations/tmtcclient_Service_GPS1_QEMU___.xml
index 8a100cfd5b5483a1c943a682f32482cbe0d3822b..74f6c148547b1779ec9c462ca16e58c0f4160f4b 100644
--- a/.idea/runConfigurations/tmtcclient_Service_GPS1_QEMU___.xml
+++ b/.idea/runConfigurations/tmtcclient_Service_GPS1_QEMU___.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s GPS1 -c 2 -t 2.5 --hk" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Single_Command.xml b/.idea/runConfigurations/tmtcclient_Single_Command.xml
index 46d240300da9f6c7eec267237729c2fd9c72737d..9a116c2a5c403abf9e9712ea5b205f437bd7c2a9 100644
--- a/.idea/runConfigurations/tmtcclient_Single_Command.xml
+++ b/.idea/runConfigurations/tmtcclient_Single_Command.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 2 -c 3 --boardIP=127.0.0.1" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Single_Command_Serial_.xml b/.idea/runConfigurations/tmtcclient_Single_Command_Serial.xml
similarity index 80%
rename from .idea/runConfigurations/tmtcclient_Single_Command_Serial_.xml
rename to .idea/runConfigurations/tmtcclient_Single_Command_Serial.xml
index c59814017c5419b0cfa0d262228897e93ae23e4f..abef5a62f8645002647d6ac3a6cfdc487d12d13f 100644
--- a/.idea/runConfigurations/tmtcclient_Single_Command_Serial_.xml
+++ b/.idea/runConfigurations/tmtcclient_Single_Command_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient Single Command Serial " type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient Single Command Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,8 +12,8 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
-    <option name="PARAMETERS" value="-m 2 -c 1 -t 5" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 2 -c 1 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
     <option name="MODULE_MODE" value="false" />
diff --git a/.idea/runConfigurations/tmtcclient_Software_QEMU_.xml b/.idea/runConfigurations/tmtcclient_Software_QEMU_.xml
index 49a2418a343c21a57464df1f2c30035460fb530d..12abc3e6fc029245c7ccecde450954847d2ee574 100644
--- a/.idea/runConfigurations/tmtcclient_Software_QEMU_.xml
+++ b/.idea/runConfigurations/tmtcclient_Software_QEMU_.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 4 -c 2 -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Trigger_Exceptions.xml b/.idea/runConfigurations/tmtcclient_Trigger_Exceptions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6bea4aeed0a664d610f0c8fb3ad9fbd87a9fe3f6
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Trigger_Exceptions.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Trigger Exceptions" type="PythonConfigurationType" factoryName="Python" folderName="Serial Utility">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s 17 -o 150 -c 1 -t 2.2" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Unittest_QEMU.xml b/.idea/runConfigurations/tmtcclient_Unittest_QEMU.xml
index 07cedcf92c05201048b69b1cadd88c498dff07ac..edae8f21dd310739cd449f1929d40989aed0997c 100644
--- a/.idea/runConfigurations/tmtcclient_Unittest_QEMU.xml
+++ b/.idea/runConfigurations/tmtcclient_Unittest_QEMU.xml
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/core/tmtc_client_core.py" />
     <option name="PARAMETERS" value="-m 5 -c 2 -t 5" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="true" />
diff --git a/.idea/runConfigurations/tmtcclient_Unlock_File.xml b/.idea/runConfigurations/tmtcclient_Unlock_File.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ce9839baac94e9bcd4d9ae5dee6dfa9060779881
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Unlock_File.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Unlock File" type="PythonConfigurationType" factoryName="Python" folderName="Serial FileManagement">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
+    <option name="PARAMETERS" value="-m 3 -s SD -o 6 -c 1 -t 2.5" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient__Service_200_Serial.xml b/.idea/runConfigurations/tmtcclient__Service_200_Serial.xml
index ec2175bfadade9795139b22a62d815dd0ffa752e..e6a5fc66113e81d283604b4062241a7a392f1156 100644
--- a/.idea/runConfigurations/tmtcclient__Service_200_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient__Service_200_Serial.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="tmtcclient  Service 200 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Communication">
+  <configuration default="false" name="tmtcclient  Service 200 Serial" type="PythonConfigurationType" factoryName="Python" folderName="Serial Service Test">
     <module name="tmtc" />
     <option name="INTERPRETER_OPTIONS" value="" />
     <option name="PARENT_ENVS" value="true" />
@@ -12,7 +12,7 @@
     <option name="ADD_CONTENT_ROOTS" value="true" />
     <option name="ADD_SOURCE_ROOTS" value="true" />
     <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
-    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/tmtc_client_cli.py" />
     <option name="PARAMETERS" value="-m 3 -s 200 -c 1 --hk -t 3" />
     <option name="SHOW_COMMAND_LINE" value="false" />
     <option name="EMULATE_TERMINAL" value="false" />
diff --git a/README.md b/README.md
index 518c9cc833847de56233ab94d5dd857f6089c5a6..0c0ec34d7c3969a442abbceaff67a769d5fc6e3a 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,6 @@ git clone https://git.ksat-stuttgart.de/source/tmtc.git
 Initiate the core components by initiating and updating the submodules
 ```sh
 git submodule init
-git submodule sync
 git submoduke update
 ```
 
@@ -27,7 +26,7 @@ pip install -r requirements.txt
 
 Now the script can be tested by running
 ```sh
-python3 obsw_tmtc_client.py -h
+python obsw_tmtc_client.py -h
 ```
 
 It is recommended to use and setup PyCharm to also use the preconfigured
@@ -137,4 +136,4 @@ a new PyCharm project in a new window.
  
 To add new configurations, go to Edit Configurations... 
 at the top right corner in the drop-down menu.
-Specify the new run configurations and set a tick at Share through VCS. 
\ No newline at end of file
+Specify the new run configurations and set a tick at Share through VCS. 
diff --git a/gui/obsw_backend_test.py b/archive/obsw_backend_test.py
similarity index 85%
rename from gui/obsw_backend_test.py
rename to archive/obsw_backend_test.py
index c21caeec5ea9d5dacbc119a6c49e66b27237083a..62a3c3e5904060ea253302aae872ec213707ea51 100644
--- a/gui/obsw_backend_test.py
+++ b/archive/obsw_backend_test.py
@@ -6,9 +6,10 @@ import logging
 
 LOGGER = get_logger()
 
+
 class TmTcBackend(Process):
     def __init__(self):
-        from obsw_tmtc_client import TmTcHandler
+        from core.tmtc_client_core import TmTcHandler
         super(TmTcBackend, self).__init__()
         self.address = ('localhost', 6000)     # family is deduced to be 'AF_INET'
         self.tmtc_backend = TmTcHandler()
@@ -20,7 +21,7 @@ class TmTcBackend(Process):
 
     def listen(self):
         self.conn = self.listener.accept()
-        LOGGER.info("TmTcBackend: Connection accepted from %s",str(self.listener.last_accepted))
+        LOGGER.info("TmTcBackend: Connection accepted from %s", str(self.listener.last_accepted))
         while True:
             msg = self.conn.recv()
             # do something with msg
diff --git a/archive/obsw_pus_tm_base.py b/archive/obsw_pus_tm_base.py
deleted file mode 100644
index 287c9cdf06a80daf381a63a58d6e20007e14f18b..0000000000000000000000000000000000000000
--- a/archive/obsw_pus_tm_base.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Wed Apr  4 11:44:48 2018
-Generic PUS packet class to deserialize raw PUS telemetry.
-@author: S. Gaisser
-"""
-
-import crcmod
-import datetime
-
-
-class ObswPusPacket:
-    def __init__(self, byte_array: bytearray):
-        self.__packet_raw = byte_array
-        self.PUSHeader = PUSPacketHeader(byte_array)
-        byte_array = byte_array[6:]
-        self.dataFieldHeader = OBSWPUSPacketDataFieldHeader(byte_array)
-        byte_array = byte_array[12:]
-        self.data = byte_array[:len(byte_array) - 2]
-        self.crc = byte_array[len(byte_array) - 2] << 8 | byte_array[len(byte_array) - 1]
-
-    def get_raw_packet(self) -> bytearray:
-        return self.__packet_raw
-
-    def append_pus_packet_header(self, array):
-        self.dataFieldHeader.printDataFieldHeader(array)
-        self.PUSHeader.printPusPacketHeader(array)
-
-    def append_pus_packet_header_column_headers(self, array):
-        self.dataFieldHeader.printDataFieldHeaderColumnHeader(array)
-        self.PUSHeader.printPusPacketHeaderColumnHeaders(array)
-
-    def getPacketSize(self):
-        # PusHeader Size + data size
-        size = PUSPacketHeader.headerSize + self.PUSHeader.length + 1
-        return size
-
-    def getService(self):
-        return self.dataFieldHeader.type
-
-    def getSubservice(self):
-        return self.dataFieldHeader.subtype
-
-    def getSSC(self):
-        return self.PUSHeader.sourceSequenceCount
-
-    def printData(self):
-        print(self.returnDataString())
-
-    def returnDataString(self):
-        strToPrint = "["
-        for byte in self.data:
-            strToPrint += str(hex(byte)) + " , "
-        strToPrint = strToPrint.rstrip(' , ')
-        strToPrint += ']'
-        return strToPrint
-
-
-
diff --git a/config/obsw_com_config.py b/config/obsw_com_config.py
index 3de6c2863cc55bd60f83dc150043fe31ff972d53..0d45593ee4d216993bc43b1dc0daf1f79d857681 100644
--- a/config/obsw_com_config.py
+++ b/config/obsw_com_config.py
@@ -25,12 +25,12 @@ def set_communication_interface(tmtc_printer: TmTcPrinter) -> Union[Communicatio
     :return: CommunicationInterface object
     """
     try:
-        if g.G_COM_IF == g.ComIF.Ethernet:
+        if g.G_COM_IF == g.ComInterfaces.Ethernet:
             communication_interface = EthernetComIF(
                 tmtc_printer=tmtc_printer, tm_timeout=g.G_TM_TIMEOUT,
                 tc_timeout_factor=g.G_TC_SEND_TIMEOUT_FACTOR, send_address=g.G_ETHERNET_SEND_ADDRESS,
                 receive_address=g.G_ETHERNET_RECV_ADDRESS)
-        elif g.G_COM_IF == g.ComIF.Serial:
+        elif g.G_COM_IF == g.ComInterfaces.Serial:
             serial_baudrate = g.G_SERIAL_BAUDRATE
             serial_timeout = g.G_SERIAL_TIMEOUT
             communication_interface = SerialComIF(
@@ -39,10 +39,14 @@ def set_communication_interface(tmtc_printer: TmTcPrinter) -> Union[Communicatio
                 ser_com_type=SerialCommunicationType.DLE_ENCODING)
             communication_interface.set_dle_settings(
                 g.G_SERIAL_DLE_MAX_QUEUE_LEN, g.G_SERIAL_DLE_MAX_FRAME_SIZE, serial_timeout)
-        elif g.G_COM_IF == g.ComIF.QEMU:
+        elif g.G_COM_IF == g.ComInterfaces.QEMU:
+            serial_timeout = g.G_SERIAL_TIMEOUT
             communication_interface = QEMUComIF(
-                tmtc_printer=tmtc_printer, tm_timeout=g.G_TM_TIMEOUT,
-                tc_timeout_factor=g.G_TC_SEND_TIMEOUT_FACTOR)
+                tmtc_printer=tmtc_printer,
+                serial_timeout=serial_timeout,
+                ser_com_type=SerialCommunicationType.DLE_ENCODING)
+            communication_interface.set_dle_settings(
+                g.G_SERIAL_DLE_MAX_QUEUE_LEN, g.G_SERIAL_DLE_MAX_FRAME_SIZE, serial_timeout)
         else:
             communication_interface = DummyComIF(tmtc_printer=tmtc_printer)
         if not communication_interface.valid:
diff --git a/config/obsw_config.py b/config/obsw_config.py
index e703586eed6d5560c1e3c9de539e14a23b59c4e4..60b761db14e45ba9a9f40b0534969dfa172b98fe 100644
--- a/config/obsw_config.py
+++ b/config/obsw_config.py
@@ -10,8 +10,7 @@
 import struct
 import pprint
 import logging
-from socket import INADDR_ANY
-from config.obsw_definitions import ModeList, ComIF
+from config.obsw_definitions import ModeList, ComInterfaces
 
 """
 Mission/Device specific information.
@@ -28,6 +27,15 @@ Other global variables
 # TMTC Client
 G_TMTC_LOGGER_NAME = "TMTC Logger"
 G_ERROR_LOG_FILE_NAME = "tmtc_error.log"
+G_PP = pprint.PrettyPrinter()
+LOGGER = logging.getLogger(G_TMTC_LOGGER_NAME)
+# General Settings
+G_SCRIPT_MODE = 1
+G_MODE_ID: ModeList = ModeList.ListenerMode
+G_SERVICE = 17
+G_OP_CODE = 0
+G_LISTENER_AFTER_OP = False
+G_DISPLAY_MODE = "long"
 
 # General TMTC Settings
 G_APID = 0x65  # Global APID for EIVE
@@ -44,6 +52,13 @@ G_TM_TIMEOUT = 6
 G_TC_SEND_TIMEOUT_FACTOR = 2.0
 
 # Serial communication
+# Binary Upload Settings
+G_MAX_BINARY_FRAME_LENGTH = 1500
+G_MAX_APP_DATA_LENGTH = G_MAX_BINARY_FRAME_LENGTH - 100
+
+G_COM_IF: ComInterfaces = ComInterfaces.QEMU
+# COM Port for serial communication
+G_COM_PORT = 'COM0'
 G_SERIAL_TIMEOUT = 0.01
 G_SERIAL_BAUDRATE = 230400
 G_SERIAL_FRAME_SIZE = 256
@@ -79,14 +94,13 @@ G_TM_LISTENER = None
 G_COM_INTERFACE = None
 G_TMTC_PRINTER = None
 
-
 # noinspection PyUnusedLocal
 def set_globals(args):
-    global G_ETHERNET_RECV_ADDRESS, G_ETHERNET_SEND_ADDRESS, G_SCRIPT_MODE, G_MODE_ID, G_SERVICE, G_DISPLAY_MODE,\
-        G_COM_IF, G_COM_PORT, G_SERIAL_TIMEOUT, G_TM_TIMEOUT, G_TC_SEND_TIMEOUT_FACTOR, \
-        G_PRINT_TO_FILE, G_PRINT_HK_DATA, G_PRINT_RAW_TM, G_PRINT_TM
-    if args.mode == 0:
-        LOGGER.info("GUI mode not implemented yet !")
+    global G_ETHERNET_RECV_ADDRESS, G_ETHERNET_SEND_ADDRESS, G_SCRIPT_MODE, G_MODE_ID, G_SERVICE, \
+        G_DISPLAY_MODE, G_COM_IF, G_COM_PORT, G_SERIAL_TIMEOUT, G_TM_TIMEOUT, \
+        G_TC_SEND_TIMEOUT_FACTOR, G_PRINT_TO_FILE, G_PRINT_HK_DATA, G_PRINT_RAW_TM, G_PRINT_TM, \
+        G_OP_CODE, G_RESEND_TC, G_LISTENER_AFTER_OP
+
     if args.shortDisplayMode:
         G_DISPLAY_MODE = "short"
     else:
@@ -108,20 +122,28 @@ def set_globals(args):
             G_MODE_ID = ModeList.UnitTest
     else:
         G_MODE_ID = ModeList[1]
-    if args.com_if == ComIF.Ethernet.value:
-        G_COM_IF = ComIF.Ethernet
-    elif args.com_if == ComIF.Serial.value:
-        G_COM_IF = ComIF.Serial
-    elif args.com_if == ComIF.QEMU.value:
-        G_COM_IF = ComIF.QEMU
+
+    if args.com_if == ComInterfaces.Ethernet.value:
+        G_COM_IF = ComInterfaces.Ethernet
+    elif args.com_if == ComInterfaces.Serial.value:
+        G_COM_IF = ComInterfaces.Serial
+    elif args.com_if == ComInterfaces.QEMU.value:
+        G_COM_IF = ComInterfaces.QEMU
     else:
-        G_COM_IF = ComIF.Dummy
+        G_COM_IF = ComInterfaces.Dummy
 
     G_SERVICE = str(args.service)
     if G_SERVICE.isdigit():
         G_SERVICE = int(args.service)
     else:
         G_SERVICE = args.service
+
+    G_OP_CODE = str(args.op_code)
+    if G_OP_CODE.isdigit():
+        G_OP_CODE = int(G_OP_CODE)
+    else:
+        G_OP_CODE = str(G_OP_CODE)
+
     G_MODE_ID = G_MODE_ID
     G_PRINT_HK_DATA = args.print_hk
     G_PRINT_TM = args.print_tm
@@ -130,6 +152,8 @@ def set_globals(args):
     G_COM_PORT = args.com_port
     G_TM_TIMEOUT = args.tm_timeout
     G_RESEND_TC = args.resend_tc
-    from obsw_user_code import global_setup_hook
+    G_LISTENER_AFTER_OP = args.listener
+
+    from config.obsw_user_code import global_setup_hook
     global_setup_hook()
 
diff --git a/config/obsw_definitions.py b/config/obsw_definitions.py
index 738119abdf4f43de91c60393b82652efb511962b..3aff14867aa39318c7bfa8907fbcf511c7ea4ea8 100644
--- a/config/obsw_definitions.py
+++ b/config/obsw_definitions.py
@@ -17,7 +17,7 @@ class ModeList(enum.Enum):
     PromptMode = 32
 
 
-class ComIF(enum.Enum):
+class ComInterfaces(enum.Enum):
     Dummy = 0
     Serial = 1
     QEMU = 2
diff --git a/obsw_user_code.py b/config/obsw_user_code.py
similarity index 100%
rename from obsw_user_code.py
rename to config/obsw_user_code.py
diff --git a/gui/__init__.py b/core/__init__.py
similarity index 100%
rename from gui/__init__.py
rename to core/__init__.py
diff --git a/obsw_tmtc_client.py b/core/tmtc_backend.py
old mode 100755
new mode 100644
similarity index 61%
rename from obsw_tmtc_client.py
rename to core/tmtc_backend.py
index d97dc3586a4c4345c9312a4803f0d644d7077a85..392a009f2fe156e6b7d05904005139e828668441
--- a/obsw_tmtc_client.py
+++ b/core/tmtc_backend.py
@@ -1,121 +1,78 @@
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
-"""
-@brief  This client was developed by KSat for the SOURCE project to test the on-board software.
-@details
-This client features multiple sender/receiver modes and has been designed
-to be extensible and easy to use. This clien is based on the PUS standard for the format
-of telecommands and telemetry. It can also send TMTC via different interfaces like the
-serial interface (USB port) or ethernet interface.
-
-@license
-Copyright 2020 KSat e.V. Stuttgart
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-@manual
-Run this file with the -h flag to display options.
-"""
 import atexit
 import time
 import logging
 import sys
+from multiprocessing import Process
 from collections import deque
 from typing import Tuple, Union
 
 from config import obsw_config as g
-from config.obsw_config import set_globals
-from config.obsw_com_config import set_communication_interface
+from config.obsw_definitions import ModeList
+from config.obsw_user_code import command_preparation_hook
 
+from tmtc_core.utility.obsw_logger import get_logger
 from tmtc_core.tc.obsw_pus_tc_base import PusTcInfo
 from tmtc_core.sendreceive.obsw_single_command_sender_receiver import SingleCommandSenderReceiver
 from tmtc_core.sendreceive.obsw_sequential_sender_receiver import SequentialCommandSenderReceiver
 from tmtc_core.sendreceive.obsw_tm_listener import TmListener
+from tmtc_core.comIF.obsw_com_interface import CommunicationInterface
 from tmtc_core.utility.obsw_tmtc_printer import TmTcPrinter
 from tmtc_core.utility.obsw_exit_handler import keyboard_interrupt_handler
-from tmtc_core.utility.obsw_logger import set_tmtc_logger, get_logger
 
+from tc.obsw_pus_tc_packer import ServiceQueuePacker, create_total_tc_queue
 from test.obsw_pus_service_test import run_selected_pus_tests
-from tc.obsw_pus_tc_packer import create_total_tc_queue, ServiceQueuePacker
-from utility.obsw_args_parser import parse_input_arguments
-from utility.obsw_binary_uploader import perform_binary_upload
-
-from gui.obsw_tmtc_gui import TmTcGUI
-from gui.obsw_backend_test import TmTcBackend
-from obsw_user_code import command_preparation_hook
+from config.obsw_com_config import set_communication_interface
+from utility.obsw_binary_uploader import BinaryFileUploader
 
 LOGGER = get_logger()
 
 
-def main():
+class TmTcHandler:
     """
-    Main method, reads input arguments, sets global variables and start TMTC handler.
+    This is the primary class which handles TMTC reception. This can be seen as the backend
+    in case a GUI or front-end is implemented.
     """
-    set_tmtc_logger()
-    LOGGER.info("Starting TMTC Client")
-
-    LOGGER.info("Parsing input arguments")
-    args = parse_input_arguments()
-
-    LOGGER.info("Setting global variables")
-    set_globals(args)
-
-    LOGGER.info("Starting TMTC Handler")
-
-    if g.G_MODE_ID == g.ModeList.GUIMode:
-        do_gui_test()
-
-    else:
-        tmtc_handler = TmTcHandler()
-        tmtc_handler.perform_operation()
+    def __init__(self, init_mode: ModeList = ModeList.ListenerMode):
+        self.mode = init_mode
+        self.com_if = g.G_COM_IF
+        # This flag could be used later to command the TMTC Client with a front-end
+        self.one_shot_operation = True
 
-    # At some later point, the program will run permanently and be able to take commands.
-    # For now we put a permanent loop here so the program
-    # doesn't exit automatically (TM Listener is daemonic)
-    while True:
-        pass
+        self.tmtc_printer: Union[None, TmTcPrinter] = None
+        self.communication_interface: Union[None, CommunicationInterface] = None
+        self.tm_listener: Union[None, TmListener] = None
 
+        self.single_command_package: Tuple[bytearray, Union[None, PusTcInfo]] = bytearray(), None
 
-def do_gui_test():
-    # Experimental
-    backend = TmTcBackend()
-    backend.start()
-    gui = TmTcGUI()
-    gui.start()
-    backend.join()
-    gui.join()
-    LOGGER.info("Both processes have closed")
-    sys.exit()
+    def set_one_shot_or_loop_handling(self, enable: bool):
+        """
+        Specify whether the perform_operation() call will only handle one action depending
+        on the mode or keep listening for replies after handling an operation.
+        """
+        self.one_shot_operation = enable
 
+    def set_mode(self, mode: ModeList):
+        """
+        Set the mode which will determine what perform_operation does.
+        """
+        self.mode = mode
 
-def command_preparation() -> Tuple[bytearray, Union[None, PusTcInfo]]:
-    """
-    Prepare command for single command testing
-    :return:
-    """
-    return command_preparation_hook()
+    @staticmethod
+    def prepare_tmtc_handler_start(init_mode: ModeList = g.ModeList.ListenerMode):
+        tmtc_handler = TmTcHandler(init_mode)
+        tmtc_task = Process(target=TmTcHandler.start_handler, args=(tmtc_handler, ))
+        return tmtc_task
 
+    @staticmethod
+    def start_handler(executed_handler):
+        executed_handler.initialize()
+        executed_handler.perform_operation()
 
-class TmTcHandler:
-    """
-    This is the primary class which handles TMTC reception. This can be seen as the backend
-    in case a GUI or front-end is implemented.
-    """
-    def __init__(self):
-        self.mode = g.G_MODE_ID
-        self.com_if = g.G_COM_IF
-        # This flag could be used later to command the TMTC Client with a front-end
-        self.command_received = True
+    def initialize(self):
+        """
+        Perform initialization steps which might be necessary after class construction.
+        This has to be called at some point before using the class!
+        """
         self.tmtc_printer = TmTcPrinter(g.G_DISPLAY_MODE, g.G_PRINT_TO_FILE, True)
         self.communication_interface = set_communication_interface(self.tmtc_printer)
         self.tm_listener = TmListener(
@@ -133,25 +90,16 @@ class TmTcHandler:
         """
         Periodic operation
         """
-        while True:
-            try:
-                if self.command_received:
-                    self.command_received = False
-                    self.handle_action()
-                if self.mode == g.ModeList.Idle:
-                    LOGGER.info("TMTC Client in idle mode")
-                    time.sleep(5)
-                if self.mode == g.ModeList.ListenerMode:
-                    time.sleep(1)
-            except KeyboardInterrupt:
-                LOGGER.info("Closing TMTC client.")
-                sys.exit()
-            except IOError as e:
-                LOGGER.exception(e)
-                LOGGER.info("Closing TMTC client.")
-                sys.exit()
-
-    def handle_action(self):
+        try:
+            self.__core_operation(self.one_shot_operation)
+        except KeyboardInterrupt:
+            LOGGER.info("Keyboard Interrupt.")
+            sys.exit()
+        except IOError as e:
+            LOGGER.error("IO Error occured!")
+            sys.exit()
+
+    def __handle_action(self):
         """
         Command handling.
         """
@@ -159,27 +107,31 @@ class TmTcHandler:
             self.prompt_mode()
 
         if self.mode == g.ModeList.ListenerMode:
-            if self.tm_listener.event_reply_received.is_set():
+            if self.tm_listener.reply_event():
                 LOGGER.info("TmTcHandler: Packets received.")
                 self.tmtc_printer.print_telemetry_queue(self.tm_listener.retrieve_tm_packet_queue())
                 self.tm_listener.clear_tm_packet_queue()
-                self.tm_listener.event_reply_received.clear()
-            self.command_received = True
+                self.tm_listener.clear_reply_event()
 
         elif self.mode == g.ModeList.SingleCommandMode:
-            pus_packet_tuple = command_preparation()
+            if self.single_command_package is None:
+                pus_packet_tuple = command_preparation()
+            else:
+                LOGGER.info("send package from gui")
+                pus_packet_tuple = self.single_command_package
             sender_and_receiver = SingleCommandSenderReceiver(
                 com_interface=self.communication_interface, tmtc_printer=self.tmtc_printer,
                 tm_listener=self.tm_listener)
             LOGGER.info("Performing single command operation")
             sender_and_receiver.send_single_tc_and_receive_tm(pus_packet_tuple=pus_packet_tuple)
-            self.command_received = True
             self.mode = g.ModeList.PromptMode
 
         elif self.mode == g.ModeList.ServiceTestMode:
             service_queue = deque()
             service_queue_packer = ServiceQueuePacker()
-            service_queue_packer.pack_service_queue(g.G_SERVICE, service_queue)
+            op_code = g.G_OP_CODE
+            service_queue_packer.pack_service_queue(
+                service=g.G_SERVICE, service_queue=service_queue, op_code=op_code)
             if not self.communication_interface.valid:
                 return
             LOGGER.info("Performing service command operation")
@@ -187,7 +139,6 @@ class TmTcHandler:
                 com_interface=self.communication_interface, tmtc_printer=self.tmtc_printer,
                 tm_listener=self.tm_listener, tc_queue=service_queue)
             sender_and_receiver.send_queue_tc_and_receive_tm_sequentially()
-            self.command_received = True
             self.mode = g.ModeList.ListenerMode
 
         elif self.mode == g.ModeList.SoftwareTestMode:
@@ -203,8 +154,9 @@ class TmTcHandler:
         elif self.mode == g.ModeList.BinaryUploadMode:
             # Upload binary, prompt user for input, in the end prompt for new mode and enter that
             # mode
-            perform_binary_upload()
-            self.command_received = True
+            file_uploader = BinaryFileUploader(self.communication_interface, self.tmtc_printer,
+                                               self.tm_listener)
+            file_uploader.perform_file_upload()
             self.mode = g.ModeList.ListenerMode
 
         elif self.mode == g.ModeList.UnitTest:
@@ -219,6 +171,19 @@ class TmTcHandler:
             logging.error("Unknown Mode, Configuration error !")
             sys.exit()
 
+
+    def __core_operation(self, one_shot):
+        if not one_shot:
+            while True:
+                self.__handle_action()
+                if self.mode == g.ModeList.Idle:
+                    LOGGER.info("TMTC Client in idle mode")
+                    time.sleep(5)
+                elif self.mode == g.ModeList.ListenerMode:
+                    time.sleep(1)
+        else:
+            self.__handle_action()
+
     def prompt_mode(self):
         next_mode = input("Please enter next mode (enter h for list of modes): ")
         if next_mode == 'h':
@@ -235,6 +200,22 @@ class TmTcHandler:
         else:
             self.mode = g.ModeList.ListenerMode
 
+    # These two will not be used for now.
+    @staticmethod
+    def prepare_tmtc_handler_start_in_process(init_mode: ModeList):
+        tmtc_handler_task = Process(target=TmTcHandler.start_tmtc_handler, args=(init_mode, ))
+        return tmtc_handler_task
+
+    @staticmethod
+    def start_tmtc_handler(handler_args: any):
+        tmtc_handler = TmTcHandler(handler_args)
+        tmtc_handler.initialize()
+        tmtc_handler.perform_operation()
+
 
-if __name__ == "__main__":
-    main()
+def command_preparation() -> Tuple[bytearray, Union[None, PusTcInfo]]:
+    """
+    Prepare command for single command testing
+    :return:
+    """
+    return command_preparation_hook()
diff --git a/core/tmtc_client_core.py b/core/tmtc_client_core.py
new file mode 100755
index 0000000000000000000000000000000000000000..08b93b1296755014784b019de34c275ae927ead7
--- /dev/null
+++ b/core/tmtc_client_core.py
@@ -0,0 +1,75 @@
+#!/usr/bin/python3
+"""
+@brief  This client was developed by KSat for the SOURCE project to test the on-board software.
+@details
+This client features multiple sender/receiver modes and has been designed
+to be extensible and easy to use. This clien is based on the PUS standard for the format
+of telecommands and telemetry. It can also send TMTC via different interfaces like the
+serial interface (USB port) or ethernet interface.
+
+@license
+Copyright 2020 KSat e.V. Stuttgart
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+@manual
+Run this file with the -h flag to display options.
+"""
+from multiprocessing import Process
+from tmtc_core.utility.obsw_logger import set_tmtc_logger, get_logger
+
+from config.obsw_config import set_globals
+import config.obsw_config as g
+from core.tmtc_backend import TmTcHandler
+from core.tmtc_frontend import TmTcFrontend
+from utility.obsw_args_parser import parse_input_arguments
+
+LOGGER = get_logger()
+
+def run_tmtc_client(use_gui: bool):
+    """
+    Main method, reads input arguments, sets global variables and start TMTC handler.
+    """
+    set_tmtc_logger()
+    LOGGER.info("Starting TMTC Client")
+
+    if not use_gui:
+        LOGGER.info("Parsing input arguments")
+        args = parse_input_arguments()
+
+        LOGGER.info("Setting global variables")
+        set_globals(args)
+
+    LOGGER.info("Starting TMTC Handler")
+
+    tmtc_frontend_task = Process
+
+    # Currently does not work, problems with QEMU / Asyncio
+    if not use_gui:
+        tmtc_handler = TmTcHandler(g.G_MODE_ID)
+        tmtc_handler.set_one_shot_or_loop_handling(g.G_LISTENER_AFTER_OP)
+        tmtc_handler.initialize()
+        tmtc_handler.perform_operation()
+    else:
+        tmtc_gui = TmTcFrontend()
+        tmtc_gui.start_ui()
+        # tmtc_handler_task = TmTcHandler.prepare_tmtc_handler_start()
+        # tmtc_frontend = TmTcFrontend()
+        # tmtc_frontend_task = tmtc_frontend.prepare_start(tmtc_frontend)
+        # tmtc_frontend_task.start()
+        # tmtc_handler_task.start()
+        # tmtc_handler_task.join()
+        # tmtc_frontend_task.join()
+
+
+
diff --git a/core/tmtc_frontend.py b/core/tmtc_frontend.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a5908a2cc24732126de974caf32f2df8df9480c
--- /dev/null
+++ b/core/tmtc_frontend.py
@@ -0,0 +1,356 @@
+#!/usr/bin/python3.7
+"""
+@file           tmtc_frontend.py
+@date           01.11.2019
+@brief          This is part of the TMTC client developed by the SOURCE project by KSat
+@description    GUI Testing for TMTC client
+@manual
+@author         R. Mueller, P. Scheurenbrand
+"""
+from multiprocessing import Process
+
+from PyQt5.QtWidgets import *
+from core.tmtc_backend import TmTcHandler
+from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand
+from tmtc_core.utility.obsw_logger import get_logger
+from config import obsw_config
+from config.obsw_definitions import ComInterfaces
+import threading
+
+LOGGER = get_logger()
+
+"""
+TODO: Make it look nicer. Add SOURCE or KSat logo.
+"""
+
+class TmTcFrontend:
+
+    # TODO: this list should probably be inside an enum in the obsw_config.py
+    serviceList = [ 2, 3, 5, 8, 9, 17, 20, 200, "Dummy", "GPS0", "GPS1" ] # , "Error"]
+
+    service_test_button: QPushButton
+    single_command_button: QPushButton
+    command_table: QTableWidget
+
+    single_command_service: int = 17
+    single_command_sub_service: int = 1
+    single_command_ssc: int = 20
+    single_command_data: bytearray = bytearray([])
+
+    is_busy: bool
+
+    def __init__(self):
+        self.tmtc_handler = TmTcHandler()
+        # TODO: Perform initialization on button press with specified ComIF
+        #       Also, when changing ComIF, ensure that old ComIF is closed (e.g. with printout)
+        #       Lock all other elements while ComIF is invalid.
+        self.tmtc_handler.initialize()
+        obsw_config.G_SERVICE = 17
+        obsw_config.G_COM_IF = obsw_config.ComInterfaces.QEMU
+
+    def prepare_start(self, args: any) -> Process:
+        return Process(target=self.start_ui)
+
+    def service_index_changed(self, index: int):
+        obsw_config.G_SERVICE = self.serviceList[index]
+        LOGGER.info("service_test_mode_selection updated: " + str(self.serviceList[index]))
+
+    def single_command_set_service(self, value):
+        self.single_command_service = value
+
+    def single_command_set_sub_service(self, value):
+        self.single_command_sub_service = value
+
+    def single_command_set_ssc(self, value):
+        self.single_command_ssc = value
+
+    def start_service_test_clicked(self):
+        LOGGER.info("start service test button pressed")
+        LOGGER.info("start testing service: " + str(obsw_config.G_SERVICE))
+        self.tmtc_handler.mode = obsw_config.ModeList.ServiceTestMode
+        # start the action in a new process
+        p = threading.Thread(target=self.handle_tm_tc_action)
+        p.start()
+
+    def send_single_command_clicked(self, table):
+        LOGGER.info("send single command pressed")
+
+        # parse the values from the table
+        #service = int(self.commandTable.item(0, 0).text())
+        #subservice = int(self.commandTable.item(0, 1).text())
+        #ssc = int(self.commandTable.item(0, 2).text())
+
+        LOGGER.info("service: " + str(self.single_command_service) +
+                    ", subservice: " + str(self.single_command_sub_service) +
+                    ", ssc: " + str(self.single_command_ssc))
+
+        # TODO: data needs to be parsed in a different way
+        # data = int(self.commandTable.item(0, 3).text())
+        # crc = int(self.commandTable.item(0, 4).text())
+
+        # create a command out of the parsed table
+        command = PusTelecommand(
+            service=self.single_command_service, subservice=self.single_command_sub_service,
+            ssc=self.single_command_ssc)
+        self.tmtc_handler.single_command_package = command.pack_command_tuple()
+
+        self.tmtc_handler.mode = obsw_config.ModeList.SingleCommandMode
+        # start the action in a new process
+        p = threading.Thread(target=self.handle_tm_tc_action)
+        p.start()
+
+    def handle_tm_tc_action(self):
+        LOGGER.info("start tmtc_handler.handle_action")
+        self.is_busy = True
+        self.set_send_buttons(False)
+        self.tmtc_handler.perform_operation()
+        self.is_busy = False
+        self.set_send_buttons(True)
+        LOGGER.info("finished tmtc_handler.handle_action")
+
+    def set_send_buttons(self, state: bool):
+        self.service_test_button.setEnabled(state)
+        self.single_command_button.setEnabled(state)
+
+    def start_ui(self):
+        app = QApplication([])
+
+        win = QWidget()
+        grid = QGridLayout()
+
+        row = 0
+        grid.addWidget(QLabel("Configuration:"), row, 0, 1, 2)
+        row += 1
+
+        checkbox_console = QCheckBox("print output to console")
+        checkbox_console.setChecked(obsw_config.G_PRINT_TM)
+        checkbox_console.stateChanged.connect(checkbox_console_print)
+
+        checkbox_log = QCheckBox("print output to log file")
+        checkbox_log.setChecked(obsw_config.G_PRINT_TO_FILE)
+        checkbox_log.stateChanged.connect(checkbox_log_print)
+
+        checkbox_raw_tm = QCheckBox("print all raw TM data directly")
+        checkbox_raw_tm.setChecked(obsw_config.G_PRINT_RAW_TM)
+        checkbox_raw_tm.stateChanged.connect(checkbox_print_raw_data)
+
+        checkbox_hk = QCheckBox("print hk data")
+        checkbox_hk.setChecked(obsw_config.G_PRINT_HK_DATA)
+        checkbox_hk.stateChanged.connect(checkbox_print_hk_data)
+
+        checkbox_short = QCheckBox("short display mode")
+        checkbox_short.setChecked(obsw_config.G_DISPLAY_MODE == "short")
+        checkbox_short.stateChanged.connect(checkbox_short_display_mode)
+
+        grid.addWidget(checkbox_log, row, 0, 1, 1)
+        grid.addWidget(checkbox_console, row, 1, 1, 1)
+        row += 1
+        grid.addWidget(checkbox_raw_tm, row, 0, 1, 1)
+        grid.addWidget(checkbox_hk, row, 1, 1, 1)
+        row += 1
+        grid.addWidget(checkbox_short, row, 0, 1, 1)
+        row += 1
+
+        grid.addWidget(QLabel("TM Timeout:"), row, 0, 1, 1)
+        grid.addWidget(QLabel("TM Timeout Factor:"), row, 1, 1, 1)
+        row += 1
+
+        spin_timeout = QDoubleSpinBox()
+        spin_timeout.setValue(4)
+        # TODO: set sensible min/max values
+        spin_timeout.setSingleStep(0.1)
+        spin_timeout.setMinimum(0.25)
+        spin_timeout.setMaximum(60)#
+        # https://youtrack.jetbrains.com/issue/PY-22908
+        # Ignore those warnings for now.
+        spin_timeout.valueChanged.connect(number_timeout)
+        grid.addWidget(spin_timeout, row, 0, 1, 1)
+
+        spin_timeout_factor = QDoubleSpinBox()
+        spin_timeout_factor.setValue(obsw_config.G_TC_SEND_TIMEOUT_FACTOR)
+        # TODO: set sensible min/max values
+        spin_timeout_factor.setSingleStep(0.1)
+        spin_timeout_factor.setMinimum(0.25)
+        spin_timeout_factor.setMaximum(10)
+        spin_timeout_factor.valueChanged.connect(number_timeout_factor)
+        grid.addWidget(spin_timeout_factor, row, 1, 1, 1)
+        row += 1
+
+        grid.addWidget(QLabel("Client IP:"), row, 0, 1, 1)
+        grid.addWidget(QLabel("Board IP:"), row, 1, 1, 1)
+        row += 1
+
+        spin_client_ip = QLineEdit()
+        # TODO: set sensible min/max values
+        spin_client_ip.setInputMask("000.000.000.000;_")
+        spin_client_ip.textChanged.connect(ip_change_client)
+        grid.addWidget(spin_client_ip, row, 0, 1, 1)
+
+        spin_board_ip = QLineEdit()
+        # TODO: set sensible min/max values
+        spin_board_ip.setInputMask("000.000.000.000;_")
+        spin_board_ip.textChanged.connect(ip_change_board)
+        #spin_board_ip.setText(obsw_config.G_SEND_ADDRESS[0])
+        grid.addWidget(spin_board_ip, row, 1, 1, 1)
+
+
+        row += 1
+        # com if configuration
+        grid.addWidget(QLabel("Communication Interface:"), row, 0, 1, 1)
+        com_if_comboBox = QComboBox()
+        # add all possible ComIFs to the comboBox
+        for comIf in obsw_config.ComInterfaces:
+            com_if_comboBox.addItem(comIf.name)
+        com_if_comboBox.setCurrentIndex(obsw_config.G_COM_IF.value)
+        com_if_comboBox.currentIndexChanged.connect(com_if_index_changed)
+        grid.addWidget(com_if_comboBox, row, 1, 1, 1)
+        row += 1
+
+        # service test mode gui
+        grid.addWidget(QLabel("Service Test Mode:"), row, 0, 1, 2)
+        row += 1
+
+        comboBox = QComboBox()
+        for service in self.serviceList:
+            comboBox.addItem("Service - " + str(service))
+        comboBox.setCurrentIndex(self.serviceList.index(obsw_config.G_SERVICE))
+        comboBox.currentIndexChanged.connect(self.service_index_changed)
+        grid.addWidget(comboBox, row, 0, 1, 1)
+
+        self.service_test_button = QPushButton()
+        self.service_test_button.setText("Start Service Test")
+        self.service_test_button.clicked.connect(self.start_service_test_clicked)
+        grid.addWidget(self.service_test_button, row, 1, 1, 1)
+        row += 1
+
+        # single command operation
+        grid.addWidget(QLabel("Single Command Operation:"), row, 0, 1, 1)
+        row += 1
+
+        single_command_grid = QGridLayout()
+        single_command_grid.setSpacing(5)
+
+        single_command_grid.addWidget(QLabel("Service:"), row, 0, 1, 1)
+        single_command_grid.addWidget(QLabel("SubService:"), row, 1, 1, 1)
+        single_command_grid.addWidget(QLabel("SSC:"), row, 2, 1, 1)
+
+        row += 1
+
+        spin_service = QSpinBox()
+        spin_service.setValue(self.single_command_service)
+        # TODO: set sensible min/max values
+        spin_service.setMinimum(0)
+        spin_service.setMaximum(99999)
+        spin_service.valueChanged.connect(self.single_command_set_service)
+        single_command_grid.addWidget(spin_service, row, 0, 1, 1)
+
+        spin_sub_service = QSpinBox()
+        spin_sub_service.setValue(self.single_command_sub_service)
+        # TODO: set sensible min/max values
+        spin_sub_service.setMinimum(0)
+        spin_sub_service.setMaximum(99999)
+        spin_sub_service.valueChanged.connect(self.single_command_set_sub_service)
+        single_command_grid.addWidget(spin_sub_service, row, 1, 1, 1)
+
+        spin_ssc = QSpinBox()
+        spin_ssc.setValue(self.single_command_ssc)
+        # TODO: set sensible min/max values
+        spin_ssc.setMinimum(0)
+        spin_ssc.setMaximum(99999)
+        spin_ssc.valueChanged.connect(self.single_command_set_ssc)
+        single_command_grid.addWidget(spin_ssc, row, 2, 1, 1)
+
+        row += 1
+
+        single_command_grid.addWidget(QLabel("Data:"), row, 0, 1, 3)
+
+        row += 1
+
+        # TODO: how should this be converted to the byte array?
+        single_command_data_box = QTextEdit()
+        single_command_grid.addWidget(single_command_data_box, row, 0, 1, 3)
+
+        grid.addItem(single_command_grid, row, 0, 1, 2)
+
+        row += 1
+
+        #self.commandTable = SingleCommandTable()
+        #grid.addWidget(self.commandTable, row, 0, 1, 2)
+        row += 1
+        self.single_command_button = QPushButton()
+        self.single_command_button.setText("Send single command")
+        self.single_command_button.clicked.connect(self.send_single_command_clicked)
+        grid.addWidget(self.single_command_button, row, 0, 1, 2)
+        row += 1
+
+        win.setLayout(grid)
+        win.resize(900, 800)
+        win.show()
+
+        # resize table columns to fill the window width
+        #for i in range(0, 5):
+        #    self.commandTable.setColumnWidth(i, int(self.commandTable.width() / 5) - 3)
+
+        app.exec_()
+
+class SingleCommandTable(QTableWidget):
+    def __init__(self):
+        super().__init__()
+        self.setRowCount(1)
+        self.setColumnCount(5)
+        self.setHorizontalHeaderItem(0, QTableWidgetItem("Service"))
+        self.setHorizontalHeaderItem(1, QTableWidgetItem("Subservice"))
+        self.setHorizontalHeaderItem(2, QTableWidgetItem("SSC"))
+        self.setHorizontalHeaderItem(3, QTableWidgetItem("Data"))
+        self.setHorizontalHeaderItem(4, QTableWidgetItem("CRC"))
+        self.setItem(0, 0, QTableWidgetItem("17"))
+        self.setItem(0, 1, QTableWidgetItem("1"))
+        self.setItem(0, 2, QTableWidgetItem("20"))
+
+def com_if_index_changed(index: int):
+    obsw_config.G_COM_IF = ComInterfaces(index)
+    LOGGER.info("com if updated: " + str(obsw_config.G_COM_IF))
+
+def checkbox_console_print(state: int):
+    LOGGER.info(["enabled", "disabled"][state == 0] + " console print")
+    obsw_config.G_PRINT_TM = state == 0
+
+
+def checkbox_log_print(state: int):
+    LOGGER.info(["enabled", "disabled"][state == 0] + " print to log")
+    obsw_config.G_PRINT_TO_FILE = state == 0
+
+
+def checkbox_print_raw_data(state: int):
+    LOGGER.info(["enabled", "disabled"][state == 0] + " printing of raw data")
+    obsw_config.G_PRINT_RAW_TM = state == 0
+
+
+def checkbox_print_hk_data(state: int):
+    LOGGER.info(["enabled", "disabled"][state == 0] + " printing of hk data")
+    obsw_config.G_PRINT_HK_DATA = state == 0
+
+
+def checkbox_short_display_mode(state: int):
+    LOGGER.info(["enabled", "disabled"][state == 0] + " short display mode")
+    obsw_config.G_DISPLAY_MODE = ["short", "long"][state == 0]
+
+
+def number_timeout(value: float):
+    LOGGER.info("tm timeout changed to: " + str(value))
+    obsw_config.G_TM_TIMEOUT = value
+
+
+def number_timeout_factor(value: float):
+    LOGGER.info("tm timeout factor changed to: " + str(value))
+    obsw_config.G_TC_SEND_TIMEOUT_FACTOR = value
+
+
+def ip_change_client(value):
+    LOGGER.info("client ip changed: " + value)
+    obsw_config.G_REC_ADDRESS = (value, 2008)
+
+
+def ip_change_board(value):
+    LOGGER.info("board ip changed: " + value)
+    obsw_config.G_SEND_ADDRESS = (value, 7)
\ No newline at end of file
diff --git a/gui/obsw_tmtc_gui.py b/gui/obsw_tmtc_gui.py
deleted file mode 100644
index 8c322d5e8e7f54f2ba7a23b5dd9b6209624db546..0000000000000000000000000000000000000000
--- a/gui/obsw_tmtc_gui.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/python3.7
-# -*- coding: utf-8 -*-
-"""
-@file
-    obsw_tmtc_gui.py
-@date
-    01.11.2019
-@brief
-    This is part of the TMTC client developed by the SOURCE project by KSat
-@description
-    GUI Testing for TMTC client
-@manual
-@author:
-    R. Mueller
-"""
-import tkinter as tk
-from multiprocessing.connection import Client
-from multiprocessing import Process
-from tmtc_core.utility.obsw_logger import get_logger
-import time
-
-LOGGER = get_logger()
-
-# A first simple version has drop down menus to chose all necessary options
-# which are normally handled by the args parser.
-# when pressing save, all chosen values get passed to the globals file (OBSW_Config)
-# To start the program, another button with start needs to be set up.
-# A third button to perform a keyboard interrupt should be implemented
-# include a really nice source badge and make it large !
-# plan this on paper first...
-# Step 1: Huge Mission Badge in Tkinter window because that is cool.
-# Step 2: Simple buttons to run servce tests around the badge.
-class TmTcGUI(Process):
-    def __init__(self):
-        super(TmTcGUI, self).__init__()
-        self.root = None
-        self.address = ('localhost', 6000)
-        self.conn = Client(self.address, authkey=None)
-        self.counter = 0
-
-    def run(self):
-        self.open()
-
-    def open(self):
-        self.root = tk.Tk()
-        self.root.title("Hallo Welt")
-        while True:
-            LOGGER.info("TmTcGUI: Sending test message")
-            self.conn.send("test")
-            self.root.update()
-            time.sleep(0.1)
-            self.counter = self.counter + 1
-            if self.counter == 50:
-                self.conn.send("close")
-                self.conn.close()
-                break
-        # self.window.mainloop()
-
-    def close(self):
-        pass
-        # self.window.quit()
diff --git a/requirements.txt b/requirements.txt
index 4a3f79dd0651dd1e85c8e72f74264a7f5a6cecde..78c3bdb95643e7298470cc47c523f99b3c2a4989 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,26 +1,4 @@
-aiohttp==3.6.2
-astroid==2.4.2
-async-timeout==3.0.1
-attrs==19.3.0
-chardet==3.0.4
-colorama==0.4.3
-cpplint==1.5.4
 crcmod>=1.7
-docopt==0.6.2
-future==0.18.2
-idna==2.10
-iso8601==0.1.12
-isort==5.4.2
-lazy-object-proxy==1.5.1
-mccabe==0.6.1
-multidict==4.7.6
-pylint>=2.5.3
-PyQt5>=5.15.0
-PyQt5-sip>=12.8.0
+PyQt5>=5.15.1
+PyQt5-stubs>=5.14.2.2
 pyserial>=3.4
-PyYAML==5.3.1
-six==1.15.0
-toml==0.10.1
-typing-extensions==3.7.4.2
-wrapt==1.11.2
-yarl==1.5.1
diff --git a/tc/obsw_image_handler.py b/tc/obsw_image_handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f3216861781687d30e6180c43cdb21eed8379a7
--- /dev/null
+++ b/tc/obsw_image_handler.py
@@ -0,0 +1,54 @@
+from typing import Union
+
+from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand, Deque
+from tc.obsw_tc_service8 import make_action_id
+from tc.obsw_tc_service20 import pack_boolean_parameter_setting
+from tmtc_core.utility.obsw_logger import get_logger
+from config.obsw_config import SW_IMAGE_HANDLER_ID
+
+LOGGER = get_logger()
+
+
+def generate_copy_bl_sdc_to_flash_packet(ssc: int, from_fram: bool = False,
+                                         object_id: bytearray = SW_IMAGE_HANDLER_ID):
+    app_data = bytearray(object_id)
+    app_data += make_action_id(11)
+    app_data.append(from_fram)
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
+
+
+# Slot values: 0 -> Slot 0, 1 -> Slot 1, 2 -> Update Slot
+def generate_copy_obsw_sdc_to_flash_packet(ssc: int, slot: int,
+                                           object_id: bytearray = SW_IMAGE_HANDLER_ID):
+    app_data = bytearray(object_id)
+    app_data += make_action_id(4)
+    app_data.append(slot)
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
+
+
+def generate_param_packet_hamming_from_sdcard(enable: bool, ssc: int,
+                                              object_id: bytearray = SW_IMAGE_HANDLER_ID):
+    return pack_boolean_parameter_setting(object_id=object_id, domain_id=0, unique_id=0,
+                                          parameter=enable, ssc=ssc)
+
+
+def generate_img_handler_packet(service_queue: Deque, op_code: Union[int, str]):
+    # Action ID 4 (Copy SDC to Flash, Software Update Image)
+    if op_code == "A4U":
+        service_queue.appendleft(("print", "Generating command to copy SDC OBSW to flash."))
+        command = generate_copy_obsw_sdc_to_flash_packet(ssc=0, slot=2)
+        service_queue.appendleft(command.pack_command_tuple())
+    # Action ID 11 (Copy bootloader from SDC to flash
+    elif op_code == "A11S":
+        service_queue.appendleft(("print", "Generating command to copy SDC bootloader to flash."))
+        command = generate_copy_bl_sdc_to_flash_packet(0)
+        service_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "P0":
+        service_queue.appendleft(("print", "Configuring hamming code to be taken from SD card"))
+        command = generate_param_packet_hamming_from_sdcard(enable=True, ssc=0)
+        service_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "P1":
+        service_queue.appendleft(("print", "Configuring hamming code to be taken from FRAM"))
+        command = generate_param_packet_hamming_from_sdcard(enable=False, ssc=0)
+        service_queue.appendleft(command.pack_command_tuple())
+
diff --git a/tc/obsw_pus_tc_frame_packer.py b/tc/obsw_pus_tc_frame_packer.py
index 71d045feb2fdf3a4565384d88afd0678ee3fa7e7..12447d34e9288af1fcff012337ac936c21d9e0ff 100644
--- a/tc/obsw_pus_tc_frame_packer.py
+++ b/tc/obsw_pus_tc_frame_packer.py
@@ -49,4 +49,4 @@ def pack_tc_info(tc_list: List[PusTelecommand]) -> List[PusTcInfo]:
     tc_info_list = list()
     for tc in tc_list:
         tc_info_list.append(tc.pack_information())
-    return tc_info_list
\ No newline at end of file
+    return tc_info_list
diff --git a/tc/obsw_pus_tc_packer.py b/tc/obsw_pus_tc_packer.py
index 23920defffab2162592f8143a8a002861c3f0126..c4d281ed5f67cd7d483a8e84c7430df55ea3bb4f 100644
--- a/tc/obsw_pus_tc_packer.py
+++ b/tc/obsw_pus_tc_packer.py
@@ -14,11 +14,14 @@ from tc.obsw_tc_service2 import pack_service2_test_into
 from tc.obsw_tc_service3 import pack_service3_test_into
 from tc.obsw_tc_service8 import pack_service8_test_into
 from tc.obsw_tc_service9 import pack_service9_test_into
-from tc.obsw_tc_service23 import pack_service23_test_into
+from tc.obsw_tc_service23_sdcard import pack_service23_commands_into
 from tc.obsw_tc_service20 import pack_service20_test_into
+from tc.obsw_tc_utility import pack_utility_command
 from tc.obsw_tc_service200 import pack_mode_data, pack_service200_test_into
 from tc.obsw_tc_service5_17 import pack_service5_test_into, pack_service17_test_into
+from tc.obsw_image_handler import generate_img_handler_packet
 from tc.obsw_tc_gps import pack_gps_test_into
+from tc.obsw_tc_core import pack_core_command
 
 from tmtc_core.utility.obsw_logger import get_logger
 import config.obsw_config as g
@@ -32,7 +35,8 @@ class ServiceQueuePacker:
     def __init__(self):
         pass
 
-    def pack_service_queue(self, service: Union[int, str], service_queue: TcQueueT):
+    @staticmethod
+    def pack_service_queue(service: Union[int, str], op_code: int, service_queue: TcQueueT):
         if service == 2:
             return pack_service2_test_into(service_queue)
         if service == 3:
@@ -44,24 +48,30 @@ class ServiceQueuePacker:
         if service == 9:
             return pack_service9_test_into(service_queue)
         if service == 17:
-            return pack_service17_test_into(service_queue)
+            return pack_service17_test_into(service_queue, op_code)
         if service == 20:
             return pack_service20_test_into(service_queue)
-        if service == 23:
-            return pack_service23_test_into(service_queue)
+        if service == 23 or service.lower() == "sd":
+            return pack_service23_commands_into(service_queue, op_code)
         if service == 200:
             return pack_service200_test_into(service_queue)
-        if service == "Dummy":
+        if service.lower() == "dummy":
             return pack_dummy_device_test_into(service_queue)
-        if service == "GPS0":
+        if service.lower() == "img":
+            return generate_img_handler_packet(service_queue, op_code)
+        if service.lower() == "core":
+            return pack_core_command(service_queue, op_code)
+        if service.lower() == "led":
+            return pack_utility_command(service_queue, op_code)
+        if service.lower() == "gps0":
             # Object ID: GPS Device
             object_id = g.GPS0_DEVICE_ID
             return pack_gps_test_into(object_id, service_queue)
-        if service == "GPS1":
+        if service.lower() == "gps1":
             # Object ID: GPS Device
             object_id = g.GPS1_DEVICE_ID
             return pack_gps_test_into(object_id, service_queue)
-        if service == "Error":
+        if service.lower() == "Error":
             return pack_error_testing_into(service_queue)
         LOGGER.warning("Invalid Service !")
 
@@ -77,6 +87,7 @@ def create_total_tc_queue() -> TcQueueT:
     tc_queue = pack_service8_test_into(tc_queue)
     tc_queue = pack_service9_test_into(tc_queue)
     tc_queue = pack_service17_test_into(tc_queue)
+    tc_queue = pack_service20_test_into(tc_queue)
     tc_queue = pack_service200_test_into(tc_queue)
     tc_queue = pack_dummy_device_test_into(tc_queue)
     object_id = g.GPS0_DEVICE_ID
diff --git a/tc/obsw_tc_core.py b/tc/obsw_tc_core.py
new file mode 100644
index 0000000000000000000000000000000000000000..be4a2fa3910f779bcdd995113cf4b6fff0a6287c
--- /dev/null
+++ b/tc/obsw_tc_core.py
@@ -0,0 +1,38 @@
+from typing import Deque
+
+from config.obsw_config import CORE_CONTROLLER_ID
+from tc.obsw_tc_service8 import make_action_id
+from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand
+
+
+def pack_core_command(tc_queue: Deque, op_code, ssc: int = 0):
+    if op_code.lower() == "a0":
+        tc_queue.appendleft(("print", "Generating OBC run time stats"))
+        command = generate_run_time_stats_generation_command(ssc=ssc)
+        tc_queue.appendleft(command.pack_command_tuple())
+    if op_code.lower() == "a10":
+        tc_queue.appendleft(("print", "Resetting OBC."))
+        command = generate_reset_obc_command(ssc=ssc)
+        tc_queue.appendleft(command.pack_command_tuple())
+    if op_code.lower() == "a11":
+        tc_queue.appendleft(("print", "Power cycling OBC."))
+        command = generate_power_cycle_obc_command(ssc=ssc)
+        tc_queue.appendleft(command.pack_command_tuple())
+
+
+def generate_reset_obc_command(ssc: int, object_id: bytearray = CORE_CONTROLLER_ID):
+    app_data = bytearray(object_id)
+    app_data += make_action_id(10)
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
+
+
+def generate_power_cycle_obc_command(ssc: int, object_id: bytearray = CORE_CONTROLLER_ID):
+    app_data = bytearray(object_id)
+    app_data += make_action_id(11)
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
+
+
+def generate_run_time_stats_generation_command(ssc: int, object_id: bytearray = CORE_CONTROLLER_ID):
+    app_data = bytearray(object_id)
+    app_data += make_action_id(0)
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
diff --git a/tc/obsw_tc_gps.py b/tc/obsw_tc_gps.py
index 9d9e1eb29cd230694aab8ed3129e640c7eaf2f4f..1a047bf304543f8268967c814f068419fd3bfc96 100644
--- a/tc/obsw_tc_gps.py
+++ b/tc/obsw_tc_gps.py
@@ -11,6 +11,7 @@ from tc.obsw_tc_service2 import pack_mode_data
 
 import config.obsw_config as g
 
+
 def pack_gps_test_into(object_id: bytearray, tc_queue: TcQueueT) -> TcQueueT:
     if object_id == g.GPS0_DEVICE_ID:
         gps_string = "GPS0"
diff --git a/tc/obsw_tc_service20.py b/tc/obsw_tc_service20.py
index 1b316d68e38b0f2750857c2e78392f9968dc1d7e..b0dedf027528d041fa0b87c3adb7aa26ed746abd 100644
--- a/tc/obsw_tc_service20.py
+++ b/tc/obsw_tc_service20.py
@@ -9,15 +9,52 @@ import struct
 from typing import Deque
 
 import config.obsw_config as g
-from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand
+from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand, TcQueueT
+from tmtc_core.utility.obsw_logger import get_logger
 from tc.obsw_tc_service200 import pack_mode_data
 
+LOGGER = get_logger()
+
+
+def pack_boolean_parameter_setting(object_id: bytearray, domain_id: int,
+                                   unique_id: int, parameter: bool, ssc: int):
+    """
+    Generic function to pack a telecommand to tweak a boolean parameter
+    @param object_id:
+    @param domain_id:
+    @param unique_id:
+    @param parameter:
+    @param ssc:
+    @return:
+    """
+    parameter_id = bytearray(4)
+    parameter_id[0] = domain_id
+    if unique_id > 255:
+        LOGGER.warning("Invalid unique ID, should be smaller than 255!")
+        return
+    parameter_id[1] = unique_id
+    parameter_id[2] = 0
+    parameter_id[3] = 0
+    data_to_pack = bytearray(object_id)
+    data_to_pack.extend(parameter_id)
+    # PTC and PFC for uint8_t according to CCSDS
+    ptc = 3
+    pfc = 4
+    rows = 1
+    columns = 1
+    data_to_pack.append(ptc)
+    data_to_pack.append(pfc)
+    data_to_pack.append(rows)
+    data_to_pack.append(columns)
+    data_to_pack.append(parameter)
+    return PusTelecommand(service=20, subservice=128, ssc=ssc, app_data=data_to_pack)
+
 
 def pack_service20_test_into(tc_queue: Deque, called_externally: bool = False) -> Deque:
-    #parameter IDs
+    # parameter IDs
     parameterID0 = 0
-    parameterID1 = 1
-    parameterID2 = 2
+    parameterID1 = 256
+    parameterID2 = 512
 
     if called_externally is False:
         tc_queue.appendleft(("print", "Testing Service 20"))
@@ -29,121 +66,68 @@ def pack_service20_test_into(tc_queue: Deque, called_externally: bool = False) -
     command = PusTelecommand(service=200, subservice=1, ssc=2000, app_data=mode_data)
     tc_queue.appendleft(command.pack_command_tuple())
 
-    #test invalid subservice
-    #use subservice 130 for invalid subservice check, as this is in use for dump reply
-    #(and therefore will never be a valid subservice)
-    #tc_queue.appendleft(("print", "Testing Service 20: Invalid subservice"))
-    #mode_data = pack_mode_data(object_id, 2, 0)
-    #command = PusTelecommand(service=20, subservice=130, ssc=810, app_data=mode_data)
-    #tc_queue.appendleft(command.pack_command_tuple())
-
-    #test invalid objectid //TODO: do we have an objectid known to be empty (even in future)?
-    #tc_queue.appendleft(("print", "Testing Service 20: Invalid object ID"))
-    #mode_data = pack_mode_data(object_id, 2, 0)
-    #command = PusTelecommand(service=20, subservice=128, ssc=810, app_data=mode_data)
-    #tc_queue.appendleft(command.pack_command_tuple())
-
-    #test invalid parameterID for load
-    #tc_queue.appendleft(("print", "Testing Service 20: Invalid parameter ID for load"))
-    #mode_data = pack_mode_data(object_id, 2, 0)
-    #command = PusTelecommand(service=20, subservice=128, ssc=810, app_data=mode_data)
-    #tc_queue.appendleft(command.pack_command_tuple())
-
-    #test invalid parameterID for dump
-    #tc_queue.appendleft(("print", "Testing Service 20: Invalid parameter ID for dump"))
-    #mode_data = pack_mode_data(object_id, 2, 0)
-    #command = PusTelecommand(service=20, subservice=129, ssc=810, app_data=mode_data)
-    #tc_queue.appendleft(command.pack_command_tuple())
-
-    #test checking Load for uint32_t
+    # test checking Load for uint32_t
     tc_queue.appendleft(("print", "Testing Service 20: Load uint32_t"))
     parameter_id = struct.pack(">I", parameterID0)
+    type_and_matrix_data = pack_type_and_matrix_data(3, 14, 1, 1)
     parameter_data = struct.pack(">I", 42)
-    payload = object_id + parameter_id + parameter_data
-    command = PusTelecommand(service=20, subservice=128, ssc=2001, app_data=payload)
+    payload = object_id + parameter_id+ type_and_matrix_data + parameter_data
+    command = PusTelecommand(service=20, subservice=128, ssc=2010, app_data=payload)
     tc_queue.appendleft(command.pack_command_tuple())
 
-    #test checking Dump for uint32_t
+    # test checking Dump for uint32_t
     tc_queue.appendleft(("print", "Testing Service 20: Dump uint32_t"))
     parameter_id = struct.pack(">I", parameterID0)
     payload = object_id + parameter_id
-    command = PusTelecommand(service=20, subservice=129, ssc=2001, app_data=payload)
+    command = PusTelecommand(service=20, subservice=129, ssc=2020, app_data=payload)
     tc_queue.appendleft(command.pack_command_tuple())
 
-    if called_externally is False:
-        tc_queue.appendleft(("export", "log/tmtc_log_service20.txt"))
-    return tc_queue
-
-
-"""
-    #test checking Load for int32_t
+    # test checking Load for int32_t
     tc_queue.appendleft(("print", "Testing Service 20: Load int32_t"))
-    mode_data = pack_mode_data(object_id, 2, 0)
-    command = PusTelecommand(service=20, subservice=128, ssc=2003, app_data=mode_data)
+    parameter_id = struct.pack(">I", parameterID1)
+    type_and_matrix_data = pack_type_and_matrix_data(4, 14, 1, 1)
+    parameter_data = struct.pack(">i", -42)
+    payload = object_id + parameter_id+ type_and_matrix_data + parameter_data
+    command = PusTelecommand(service=20, subservice=128, ssc=2030, app_data=payload)
     tc_queue.appendleft(command.pack_command_tuple())
 
-    #test checking Dump for int32_t
+    # test checking Dump for int32_t
     tc_queue.appendleft(("print", "Testing Service 20: Dump int32_t"))
-    mode_data = pack_mode_data(object_id, 2, 0)
-    command = PusTelecommand(service=20, subservice=129, ssc=2004, app_data=mode_data)
+    parameter_id = struct.pack(">I", parameterID1)
+    payload = object_id + parameter_id
+    command = PusTelecommand(service=20, subservice=129, ssc=2040, app_data=payload)
     tc_queue.appendleft(command.pack_command_tuple())
 
-    #test checking Load for float
+    # test checking Load for float
     tc_queue.appendleft(("print", "Testing Service 20: Load float"))
-    mode_data = pack_mode_data(object_id, 2, 0)
-    command = PusTelecommand(service=20, subservice=128, ssc=2005, app_data=mode_data)
+    parameter_id = struct.pack(">I", parameterID2)
+    type_and_matrix_data = pack_type_and_matrix_data(5, 1, 1, 1)
+    parameter_data = struct.pack(">f", 4.2)
+    payload = object_id + parameter_id+ type_and_matrix_data + parameter_data
+    command = PusTelecommand(service=20, subservice=128, ssc=2050, app_data=payload)
     tc_queue.appendleft(command.pack_command_tuple())
 
-    #test checking Dump for float
+    # test checking Dump for float
     tc_queue.appendleft(("print", "Testing Service 20: Dump float"))
-    mode_data = pack_mode_data(object_id, 2, 0)
-    command = PusTelecommand(service=20, subservice=129, ssc=2006, app_data=mode_data)
-    tc_queue.appendleft(command.pack_command_tuple())
-"""
-
-
-
-
-"""
-    # set mode on
-    tc_queue.appendleft(("print", "Testing Service 8: Set On Mode"))
-    mode_data = pack_mode_data(object_id, 1, 0)
-    command = PusTelecommand(service=200, subservice=1, ssc=800, app_data=mode_data)
+    parameter_id = struct.pack(">I", parameterID2)
+    payload = object_id + parameter_id
+    command = PusTelecommand(service=20, subservice=129, ssc=2060, app_data=payload)
     tc_queue.appendleft(command.pack_command_tuple())
 
-    # set mode normal
-    tc_queue.appendleft(("print", "Testing Service 8: Set Normal Mode"))
-    mode_data = pack_mode_data(object_id, 2, 0)
-    command = PusTelecommand(service=200, subservice=1, ssc=810, app_data=mode_data)
-    tc_queue.appendleft(command.pack_command_tuple())
+    if called_externally is False:
+        tc_queue.appendleft(("export", "log/tmtc_log_service20.txt"))
+    return tc_queue
 
-    # Direct command which triggers completion reply
-    tc_queue.appendleft(("print", "Testing Service 8: Trigger Completion Reply"))
-    action_id = g.DUMMY_COMMAND_1
-    direct_command = object_id + action_id
-    command = PusTelecommand(service=8, subservice=128, ssc=820, app_data=direct_command)
-    tc_queue.appendleft(command.pack_command_tuple())
 
-    # Direct command which triggers _tm_data reply
-    tc_queue.appendleft(("print", "Testing Service 8: Trigger Data Reply"))
-    action_id = g.DUMMY_COMMAND_2
-    command_param1 = g.DUMMY_COMMAND_2_PARAM_1
-    command_param2 = g.DUMMY_COMMAND_2_PARAM_2
-    direct_command = object_id + action_id + command_param1 + command_param2
-    command = PusTelecommand(service=8, subservice=128, ssc=830, app_data=direct_command)
-    tc_queue.appendleft(command.pack_command_tuple())
+def pack_type_and_matrix_data(ptc, pfc, column, row):
+    data = bytearray(4)
+    data[0] = ptc
+    data[1] = pfc
+    data[2] = column
+    data[3] = row
+    return data
 
-    # Direct command which triggers an additional step reply and one completion reply
-    tc_queue.appendleft(("print", "Testing Service 8: Trigger Step and Completion Reply"))
-    action_id = g.DUMMY_COMMAND_3
-    direct_command = object_id + action_id
-    command = PusTelecommand(service=8, subservice=128, ssc=840, app_data=direct_command)
-    tc_queue.appendleft(command.pack_command_tuple())
 
-    # set mode off
-    tc_queue.appendleft(("print", "Testing Service 8: Set Off Mode"))
-    mode_data = pack_mode_data(object_id, 0, 0)
-    command = PusTelecommand(service=200, subservice=1, ssc=800, app_data=mode_data)
-    tc_queue.appendleft(command.pack_command_tuple())
-    tc_queue.appendleft(("wait", 2))
-"""
+def pack_service23_commands_into(tc_queue: TcQueueT, op_code: int):
+    if op_code == 0:
+        pack_service20_test_into(tc_queue=tc_queue)
diff --git a/tc/obsw_tc_service23.py b/tc/obsw_tc_service23.py
deleted file mode 100644
index d0e09076441a3838e1db0e7958a7f3d2ec70863a..0000000000000000000000000000000000000000
--- a/tc/obsw_tc_service23.py
+++ /dev/null
@@ -1,255 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Created: 21.01.2020 07:48
-
-@author: Jakob Meier
-"""
-import math
-import config.obsw_config as g
-from typing import Deque
-
-from tc.obsw_pus_tc_packer import PusTelecommand
-
-
-class Service23WriteToFile:
-    """ This class generates the telecommand packet for the custom PUS service [23, 128]
-    @param objectID(bytearray): The objectID of the filesystem handler (e.g. SD_CARD_HANDLER_ID, PLOCHandler)
-    @param repository_path(str): The directory of the file
-    @param filename(str): The name of the file (e.g. boot.bin)
-    @param file_data(bytearray): The data to write to the file
-    """
-    def __init__(self, repository_path: str, filename: str, size_of_data_blocks: int,
-                 object_id: bytearray = bytearray([]), file_data: bytearray = bytearray([])):
-        """
-
-        @param repository_path:
-        @param filename:
-        @param object_id:
-        @param file_data:
-        """
-        self.file_data = []
-        self.number_of_packets = 0
-        self.file_data = file_data
-        self.data_to_pack = object_id
-        repository_path_length_h = len(bytearray(repository_path, 'utf-8')) >> 8
-        repository_path_length_l = 0x00FF & len(bytearray(repository_path, 'utf-8'))
-        self.data_to_pack.append(repository_path_length_h)
-        self.data_to_pack.append(repository_path_length_l)
-        if repository_path != "":
-            self.data_to_pack += bytearray(repository_path, 'utf-8')
-        self.data_to_pack.append(len(bytearray(filename, 'utf-8')))
-        self.data_to_pack += bytearray(filename, 'utf-8')
-        self.pus_packets = self.split_large_file(size_of_data_blocks)
-
-    def split_large_file(self, size_of_data_blocks):
-        """ This function splits a large file in multiple packets
-            This is necessary because the packet size is limited
-
-        :param size_of_data_blocks: The file is splitted in data blocks of this size
-        :return: List containing the PUS packets generated for each data block
-        """
-        self.number_of_packets = math.floor(len(self.file_data) / size_of_data_blocks)
-        packetNumber = 0
-
-        commands = []
-        for i in range(self.number_of_packets):
-            # append packet number
-            self.data_to_pack.append(size_of_data_blocks)
-            self.data_to_pack.append(packetNumber >> 8)
-            self.data_to_pack.append(0xFF & packetNumber)
-            self.data_to_pack += self.file_data[i * size_of_data_blocks:(i + 1) * size_of_data_blocks]
-            commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=self.data_to_pack))
-            # Last data block, packet number and data block size is removed to create new command with next data block,
-            # packet number and data block size
-            self.data_to_pack = self.data_to_pack[:len(self.data_to_pack) - size_of_data_blocks - 2 - 1]
-            packetNumber = packetNumber + 1
-        # Calculate remaining data bytes
-        remaining_data_size = len(self.file_data) - self.number_of_packets * size_of_data_blocks
-        self.data_to_pack.append(remaining_data_size)
-        self.data_to_pack.append(packetNumber >> 8)
-        self.data_to_pack.append(0xFF & packetNumber)
-        self.data_to_pack += self.file_data[self.number_of_packets * size_of_data_blocks:len(self.file_data)]
-        commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=self.data_to_pack))
-        return commands
-
-    def get_number_of_packets(self):
-        return self.number_of_packets
-
-# def sendSoftwareUpdate(comInterface, tmtcPrinter, tmListener, tmTimeout, tcTimeoutFactor, doPrintToFile):
-#     """ This function sends all packets to upload a new version of the software image
-#         The new image is assumed to be stored in the tmtc directory
-#     """
-#     image = open("sourceobsw-at91sam9g20_ek-sdram.bin", "rb").read()
-#     update = Service23WriteToFile("", "boot.bin", objectID=[0x4D, 0x00, 0x73, 0xAD], file_data=image)
-#     updateQueue = deque()
-#     updateQueue.appendleft(("print", "Service 23 Software Update"))
-#     i = 1
-#     for packet in update.pus_packets:
-#         updateQueue.appendleft(
-#             ("print", "Packet " + str(i) + "/" + str(len(update.pus_packets)) + " of software update sent"))
-#         updateQueue.appendleft(packet.packCommandTuple())
-#         i = i + 1
-#     updateQueue.appendleft(("print", "Transmission of software update complete"))
-#     SenderAndReceiver = SequentialCommandSenderReceiver(comInterface, tmtcPrinter, tmListener, tmTimeout, updateQueue,
-#                                                         tcTimeoutFactor, doPrintToFile)
-#     SenderAndReceiver.sendQueueTcAndReceiveTmSequentially()
-
-
-def generate_service23_subservice2_packet(filename: str, repositoryPath: str = "",
-                                          object_id=bytearray([])):
-    """ This function generates the application data field of a service 23/subservice 2 to
-        delete a file on a file-system-capable on-board memory.
-    : param filename: The name of the file to delete
-            repository_path: The path where the directory shall be created
-            objectID: The object ID of the memory handler which manages the file system
-    :return The TC[23,2] PUS packet
-    """
-    data_to_pack = object_id
-    data_to_pack += bytearray(repositoryPath, 'utf-8')
-    # Add string terminator to repository_path
-    data_to_pack.append(0)
-    data_to_pack += bytearray(filename, 'utf-8')
-    # Add string terminator to filename
-    data_to_pack.append(0)
-    return PusTelecommand(service=23, subservice=2, ssc=21, app_data=data_to_pack)
-
-
-def generate_service23_subservice9_packet(directory_name: str, repository_path: str = "",
-                                          object_id=bytearray([])):
-    """ This function generates the application data field of a service 23/subservice 9 packet.
-        This service can be used to create directories on file systems.
-    : param repository_path: The path where the directory shall be created
-            directoryName: The name of the directory to create
-            objectID: The object ID of the memory handler which manages the file system
-    :return
-    """
-    data_to_pack = object_id
-    data_to_pack += bytearray(repository_path, 'utf-8')
-    # Add string terminator to repository path
-    data_to_pack.append(0)
-    data_to_pack += bytearray(directory_name, 'utf-8')
-    # Add string terminator to directory name
-    data_to_pack.append(0)
-    return PusTelecommand(service=23, subservice=9, ssc=21, app_data=data_to_pack)
-
-
-def generateService23Subservice10Packet(directory_name: str, repository_path: str = "",
-                                        object_id=bytearray([])):
-    """ This function generates the application data field for a PUS packet with service
-        23 and subservie 10.
-        This service deletes the directory dirname.
-    @param directory_name: Name of the directory to delete
-    @param repository_path: Path to directory dirname
-    @param object_id: object ID of the memory handler (e.g. SD Card Handler)
-    @return The application data field of the (23,10) PUS packet
-    """
-    data_to_pack = object_id
-    data_to_pack += bytearray(repository_path, 'utf-8')
-    # Add string terminator of repository path
-    data_to_pack.append(0)
-    data_to_pack += bytearray(directory_name, 'utf-8')
-    # Add string terminator of directory name
-    data_to_pack.append(0)
-    return PusTelecommand(service=23, subservice=10, ssc=21, app_data=data_to_pack)
-
-
-def generate_service23_subservice128_packet(repository_path: str, filename: str,
-                                            object_id: bytearray = bytearray([]),
-                                            file_data: bytearray = bytearray([])):
-    """ This function generates the application data field for a PUS packet with service
-        23 and subservie 128. Subservice 128 is a custom service to write data in a file.
-        Additionally file is created if not already existing.
-    @param repository_path: The path of the target file
-    @param filename: Name of file from which the content shall be read
-    @param object_id: object ID of the memory handler (e.g. SD Card Handler)
-    @param file_data: The data to write in the file
-    @return: The application data field of the (23,128) PUS packet
-    """
-    data_to_pack = object_id
-    data_to_pack += bytearray(repository_path, 'utf-8')
-    # Add string terminator of repository path
-    data_to_pack.append(0)
-    data_to_pack += bytearray(filename, 'utf-8')
-    # Add string terminator of filename
-    data_to_pack.append(0)
-    return split_large_file(data_to_pack, 236, file_data)
-
-
-def split_large_file(data_to_pack: bytearray, size_of_data_blocks, data: bytearray):
-    """
-    This function splits a large file in multiple packets.
-    This is necessary because the packet size is limited.
-    @param data_to_pack: bytearray of data. data will be split and appended to this bytearray
-    @param size_of_data_blocks: The file is splitted in data blocks of this size
-    @param data: The data to pack in multiple packets
-    @return List containing the PUS packets generated for each data block
-    """
-    numberOfPackets = math.floor(len(data) / size_of_data_blocks)
-
-    packetNumber = 0
-
-    commands = []
-    for i in range(numberOfPackets):
-        data_to_pack.append(packetNumber >> 8)
-        data_to_pack.append(0xFF & packetNumber)
-        data_to_pack += data[i * size_of_data_blocks:(i + 1) * size_of_data_blocks]
-        commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=data_to_pack))
-        # Last data block, packet number and data block size is removed to create new command with
-        # next data block, packet number
-        data_to_pack = data_to_pack[:len(data_to_pack) - size_of_data_blocks - 2]
-        packetNumber = packetNumber + 1
-    data_to_pack.append(packetNumber >> 8)
-    data_to_pack.append(0xFF & packetNumber)
-    data_to_pack += data[numberOfPackets * size_of_data_blocks:len(data)]
-    commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=data_to_pack))
-    return commands
-
-
-def generate_service23_subservice129_packet(repository_path: str, filename: str,
-                                            object_id: bytearray = bytearray([])):
-    """
-    This function generates the application data field for a PUS packet with service
-    23 and subservie 129. Subservice 129 is a custom service to request the data of a file.
-    @param repository_path: The path of the target file
-    @param filename: Name of file from which the content shall be read
-    @param object_id: object ID of the memory handler (e.g. SD Card Handler)
-    @return: The application data field of the (23,129) PUS packet
-    """
-    data_to_pack = object_id
-    data_to_pack += bytearray(repository_path, 'utf-8')
-    # Add string terminator of repository paht
-    data_to_pack.append(0)
-    data_to_pack += bytearray(filename, 'utf-8')
-    # Add string terminator of filename
-    data_to_pack.append(0)
-    return PusTelecommand(service=23, subservice=129, ssc=21, app_data=data_to_pack)
-
-
-def pack_service23_test_into(tc_queue: Deque) -> Deque:
-    sd_handler_id = g.SD_CARD_HANDLER_ID
-    tc_queue.appendleft(("print", "Testing Service 23"))
-    tc_queue.append(("print", "Create directory 'test'"))
-    command = generate_service23_subservice9_packet(directory_name="test", object_id=sd_handler_id)
-    tc_queue.appendleft(command.pack_command_tuple())
-    # tc_queue.append(("print", "Create subdirectory 'subdir' in 'test'"))
-    # command = generate_service23_subservice9_packet(repository_path="test", directory_name="subdir",
-    #                                                 object_id=sd_handler_id)
-    # tc_queue.appendleft(command.pack_command_tuple())
-    # tc_queue.appendleft(("print", "Create and write in test.bin"))
-    # command = generate_service23_subservice128_packet(
-    #     "test/subdir", "test.bin", sd_handler_id, file_data=bytearray([0x01, 0x00, 0x01, 0x00]))
-    # tc_queue.appendleft(command[0].pack_command_tuple())
-    # tc_queue.appendleft(("print", "Read data of test.bin"))
-    # command = generate_service23_subservice129_packet("test/subdir", "test.bin", sd_handler_id)
-    # tc_queue.appendleft(command.pack_command_tuple())
-    # tc_queue.appendleft(("print", "Delete 'test.bin'"))
-    # command = generate_service23_subservice2_packet("test.bin", "test/subdir", object_id=sd_handler_id)
-    # tc_queue.appendleft(command.pack_command_tuple())
-    # tc_queue.appendleft(("print", "Delete 'subdir' directory"))
-    # command = generateService23Subservice10Packet("subdir", "test", object_id=sd_handler_id)
-    # tc_queue.appendleft(command.pack_command_tuple())
-    # tc_queue.appendleft(("print", "Delete 'test' directory"))
-    # command = generateService23Subservice10Packet(directory_name="test", object_id=sd_handler_id)
-    # tc_queue.appendleft(command.pack_command_tuple())
-    tc_queue.appendleft(("print", "\r"))
-    return tc_queue
diff --git a/tc/obsw_tc_service23_sdcard.py b/tc/obsw_tc_service23_sdcard.py
new file mode 100644
index 0000000000000000000000000000000000000000..b89ae1bfb3f10dc9cd95af6f9d92ed503f2e20a0
--- /dev/null
+++ b/tc/obsw_tc_service23_sdcard.py
@@ -0,0 +1,381 @@
+# -*- coding: utf-8 -*-
+"""
+Created: 21.01.2020 07:48
+
+@author: Jakob Meier
+"""
+import config.obsw_config as g
+from typing import Deque, Union
+
+from tc.obsw_pus_tc_packer import PusTelecommand, TcQueueT
+from tc.obsw_tc_service8 import make_action_id
+from tmtc_core.utility.obsw_logger import get_logger
+
+LOGGER = get_logger()
+
+
+def generate_print_sd_card_packet(
+        ssc: int, object_id: bytearray = g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    app_data = bytearray(object_id)
+    app_data += make_action_id(2)
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
+
+
+def generate_clear_sd_card_packet(
+        ssc: int, object_id: bytearray = g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    app_data = bytearray(object_id)
+    app_data += make_action_id(20)
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
+
+
+def generate_format_sd_card_packet(
+        ssc: int, object_id: bytearray = g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    app_data = bytearray(object_id)
+    app_data += make_action_id(21)
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
+
+
+def generate_generic_folder_structure(
+        tc_queue: Deque, init_ssc: int, object_id: bytearray = g.SD_CARD_HANDLER_ID,
+        iobc: bool = False):
+    tc_queue.appendleft(("print", "Creating TC folder"))
+    command = generate_mkdir_srv23_9_packet("TC", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    init_ssc += 1
+    command = generate_mkdir_srv23_9_packet(
+        repository_path="TC", directory_name="LARGE", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    init_ssc += 1
+    command = generate_mkdir_srv23_9_packet(
+        repository_path="TC", directory_name="SMALL", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    init_ssc += 1
+
+    tc_queue.appendleft(("print", "Creating TM folder"))
+    command = generate_mkdir_srv23_9_packet("TM", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    init_ssc += 1
+    command = generate_mkdir_srv23_9_packet(
+        repository_path="TM", directory_name="HK", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    init_ssc += 1
+    command = generate_mkdir_srv23_9_packet(
+        repository_path="TM", directory_name="SC", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    command = generate_mkdir_srv23_9_packet(
+        repository_path="TM/SC", directory_name="LARGE", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    init_ssc += 1
+    command = generate_mkdir_srv23_9_packet(
+        repository_path="TM/SC", directory_name="SMALL", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    init_ssc += 1
+
+    tc_queue.appendleft(("print", "Creating BIN folder"))
+    command = generate_mkdir_srv23_9_packet("BIN", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+    init_ssc += 1
+    if iobc:
+        command = generate_mkdir_srv23_9_packet(
+            repository_path="BIN", directory_name="IOBC", ssc=init_ssc, object_id=object_id)
+        tc_queue.appendleft(command.pack_command_tuple())
+        init_ssc += 1
+        command = generate_mkdir_srv23_9_packet(
+            repository_path="BIN/IOBC", directory_name="BL", ssc=init_ssc, object_id=object_id)
+        tc_queue.appendleft(command.pack_command_tuple())
+        init_ssc += 1
+        command = generate_mkdir_srv23_9_packet(
+            repository_path="BIN/IOBC", directory_name="OBSW", ssc=init_ssc, object_id=object_id)
+        tc_queue.appendleft(command.pack_command_tuple())
+        init_ssc += 1
+    else:
+        command = generate_mkdir_srv23_9_packet(
+            repository_path="BIN", directory_name="AT91", ssc=init_ssc, object_id=object_id)
+        tc_queue.appendleft(command.pack_command_tuple())
+        init_ssc += 1
+        command = generate_mkdir_srv23_9_packet(
+            repository_path="BIN/AT91", directory_name="BL", ssc=init_ssc, object_id=object_id)
+        tc_queue.appendleft(command.pack_command_tuple())
+        init_ssc += 1
+        command = generate_mkdir_srv23_9_packet(
+            repository_path="BIN/AT91", directory_name="OBSW", ssc=init_ssc, object_id=object_id)
+        tc_queue.appendleft(command.pack_command_tuple())
+        init_ssc += 1
+
+    tc_queue.appendleft(("print", "Creating MISC folder"))
+    command = generate_mkdir_srv23_9_packet("MISC", ssc=init_ssc, object_id=object_id)
+    tc_queue.appendleft(command.pack_command_tuple())
+
+
+def generate_create_file_srv23_1_packet(
+        filename: str, repository_path: str, ssc: int, max_size_of_app_data: int,
+        initial_data: bytearray = bytearray([]),
+        object_id: bytearray = g.SD_CARD_HANDLER_ID) -> Union[PusTelecommand, None]:
+    if len(initial_data) > calculate_allowed_file_data_size(
+            max_size_of_app_data, filename, repository_path):
+        LOGGER.error("generate_create_file_srv23_1_packet: Initial data too large!")
+        return None
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=filename)
+    data_to_pack += initial_data
+    return PusTelecommand(service=23, subservice=1, ssc=ssc, app_data=data_to_pack)
+
+
+def generate_rm_file_srv23_2_packet(filename: str, repository_path: str,
+                                    ssc: int, object_id=g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    """
+    This function generates a packet which is used to delete a file on a
+    file-system-capable on-board memory.
+    @param filename: The name of the file to delete
+    @param repository_path: The path where the directory shall be created
+    @param ssc: source sequence count
+    @param object_id: The object ID of the memory handler which manages the file system
+    @return The telecommand.
+    """
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=filename)
+    return PusTelecommand(service=23, subservice=2, ssc=ssc, app_data=data_to_pack)
+
+
+def generate_report_file_attr_srv23_3_packet(
+        filename: str, repository_path: str, ssc: int,
+        object_id=g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    """
+    @param filename: The name of the file to delete
+    @param repository_path: The path where the directory shall be created
+    @param ssc: source sequence count
+    @param object_id: The object ID of the memory handler which manages the file system
+    @return The telecommand.
+    """
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=filename)
+    return PusTelecommand(service=23, subservice=3, ssc=ssc, app_data=data_to_pack)
+
+
+def generate_mkdir_srv23_9_packet(directory_name: str, ssc: int, repository_path: str = "/",
+                                  object_id: bytearray = g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    """
+    This function generates a packet which is used to create directories on file systems.
+    @param directory_name: The path where the directory shall be created
+    @param repository_path: The name of the directory to create
+    @param ssc: source sequence count
+    @param object_id: The object ID of the memory handler which manages the file system
+    @return The telecommand.
+    """
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=directory_name)
+    return PusTelecommand(service=23, subservice=9, ssc=ssc, app_data=data_to_pack)
+
+
+def generate_rmdir_srv23_10_packet(directory_name: str, repository_path: str, ssc: int,
+                                   object_id: bytearray = g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    """
+    This function generates a packet which deletes the a directory at the specified repository path.
+    @param directory_name: Name of the directory to delete
+    @param repository_path: Path to directory dirname
+    @param ssc: source sequence count
+    @param object_id: object ID of the memory handler (e.g. SD Card Handler)
+    @return The telecommand.
+    """
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=directory_name)
+    return PusTelecommand(service=23, subservice=10, ssc=ssc, app_data=data_to_pack)
+
+
+def generate_append_to_file_srv23_130_packet(
+        filename: str, repository_path: str, packet_sequence_number: int,
+        ssc: int, file_data: bytearray = bytearray([]),
+        object_id: bytearray = g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=filename)
+    data_to_pack += file_data
+    data_to_pack.append(packet_sequence_number >> 8)
+    data_to_pack.append(packet_sequence_number & 0xff)
+    return PusTelecommand(service=23, subservice=130, ssc=ssc, app_data=data_to_pack)
+
+
+def generate_finish_append_to_file_srv23_131_packet(
+        filename: str, repository_path: str,  ssc: int, lock_file: bool = False,
+        object_id: bytearray = g.SD_CARD_HANDLER_ID):
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=filename)
+    data_to_pack.append(lock_file)
+    return PusTelecommand(service=23, subservice=131, ssc=ssc, app_data=data_to_pack)
+
+
+def generate_read_file_srv23_140_packet(
+        repository_path: str, filename: str, ssc: int,
+        object_id: bytearray = g.SD_CARD_HANDLER_ID) -> PusTelecommand:
+    """
+    This function generates the application data field for a PUS packet with service
+    23 and subservie 140. Subservice 140 is a custom service to request the data of a file.
+    @param repository_path: The path of the target file
+    @param filename: Name of file from which the content shall be read
+    @param ssc: source sequence count
+    @param object_id: object ID of the memory handler (e.g. SD Card Handler)
+    @return: The application data field of the (23,129) PUS packet
+    """
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=filename)
+    return PusTelecommand(service=23, subservice=140, ssc=ssc, app_data=data_to_pack)
+
+
+def generate_lock_file_srv23_5_6_packet(ssc: int, lock: bool, repository_path: str,
+                                        filename: str, object_id: bytearray = g.SD_CARD_HANDLER_ID):
+    data_to_pack = pack_generic_file_command_header(object_id=object_id, first_path=repository_path,
+                                                    second_path=filename)
+    if lock:
+        return PusTelecommand(service=23, subservice=5, ssc=ssc, app_data=data_to_pack)
+    else:
+        return PusTelecommand(service=23, subservice=6, ssc=ssc, app_data=data_to_pack)
+
+
+def pack_service23_commands_into(tc_queue: TcQueueT, op_code: int) -> Deque:
+    # sd_handler_id = g.SD_CARD_HANDLER_ID
+    if op_code == 0:
+        generate_generic_service_23_test(tc_queue)
+    elif op_code == "3" or op_code == 3:
+        LOGGER.info("Press h in the following input requests")
+        LOGGER.info("to send a command to display the folder structure instead")
+        (repo_path, filename) = prompt_for_repo_filename()
+        if repo_path == "" and filename == "h":
+            tc_queue.appendleft(generate_print_sd_card_packet(ssc=0).pack_command_tuple())
+        elif repo_path == "" and filename == "c":
+            return tc_queue
+        else:
+            tc_queue.append(("print", "Requesting file attributes"))
+            command = generate_report_file_attr_srv23_3_packet(
+                ssc=0, filename=filename, repository_path=repo_path)
+            tc_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "5" or op_code == 5:
+        LOGGER.info("Press h in the following input requests")
+        LOGGER.info("to send a command to display the folder structure instead")
+        (repo_path, filename) = prompt_for_repo_filename()
+        if repo_path == "" and filename == "h":
+            tc_queue.append(("print", "Printing active file system"))
+            tc_queue.appendleft(generate_print_sd_card_packet(ssc=0).pack_command_tuple())
+        elif repo_path == "" and filename == "c":
+            return tc_queue
+        else:
+            tc_queue.append(("print", "Locking file"))
+            command = generate_lock_file_srv23_5_6_packet(ssc=0, repository_path=repo_path,
+                                                          filename=filename, lock=True)
+            tc_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "6" or op_code == 6:
+        LOGGER.info("Press h in the following input requests to send a command to display the"
+                    "folder structure instead")
+        (repo_path, filename) = prompt_for_repo_filename()
+        if repo_path == "" and filename == "h":
+            tc_queue.append(("print", "Printing active file system"))
+            tc_queue.appendleft(generate_print_sd_card_packet(ssc=0).pack_command_tuple())
+        elif repo_path == "" and filename == "c":
+            return tc_queue
+        else:
+            tc_queue.append(("print", "Unlocking file"))
+            command = generate_lock_file_srv23_5_6_packet(ssc=0, repository_path=repo_path,
+                                                          filename=filename, lock=False)
+            tc_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "A2":
+        tc_queue.append(("print", "Printing active file system"))
+        command = generate_print_sd_card_packet(ssc=2300)
+        tc_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "A20":
+        tc_queue.append(("print", "Clearing active file system"))
+        command = generate_clear_sd_card_packet(ssc=2300)
+        tc_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "A21":
+        tc_queue.append(("print", "Formatting active file system"))
+        command = generate_format_sd_card_packet(ssc=2300)
+        tc_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "C0A":
+        tc_queue.append(("print", "Generating generic folder structure on AT91"))
+        generate_generic_folder_structure(tc_queue, init_ssc=0, iobc=False)
+    elif op_code == "C0I":
+        tc_queue.append(("print", "Generating generic folder structure on iOBC"))
+        generate_generic_folder_structure(tc_queue, init_ssc=0, iobc=True)
+
+    return tc_queue
+
+
+def generate_generic_service_23_test(tc_queue: TcQueueT):
+    tc_queue.appendleft(("print", "Testing Service 23"))
+
+    tc_queue.append(("print", "Create directory 'test'"))
+    command = generate_mkdir_srv23_9_packet(directory_name="test", repository_path="/", ssc=2300)
+    tc_queue.appendleft(command.pack_command_tuple())
+    tc_queue.append(("print", "Create subdirectory 'subdir' in 'test'"))
+    command = generate_mkdir_srv23_9_packet(repository_path="test", ssc=2301,
+                                            directory_name="subdir")
+    tc_queue.appendleft(command.pack_command_tuple())
+
+    tc_queue.appendleft(("print", "Create test.bin"))
+    command = generate_create_file_srv23_1_packet(
+        filename="test.bin", repository_path="test/subdir", ssc=2302,
+        initial_data=bytearray([0x01, 0x00, 0x01, 0x00]),
+        max_size_of_app_data=1024)
+    tc_queue.appendleft(command.pack_command_tuple())
+
+    tc_queue.appendleft(("print", "Printing file structure."))
+    command = generate_print_sd_card_packet(ssc=2300)
+    tc_queue.appendleft(command.pack_command_tuple())
+
+    tc_queue.appendleft(("print", "Clearing SD card"))
+    command = generate_clear_sd_card_packet(ssc=2301)
+    tc_queue.appendleft(command.pack_command_tuple())
+
+    tc_queue.appendleft(("print", "Printing file structure"))
+    command = generate_print_sd_card_packet(ssc=2302)
+    tc_queue.appendleft(command.pack_command_tuple())
+
+    # tc_queue.appendleft(("print", "Read data of test.bin"))
+    # command = generate_read_file_srv23_129_packet("test/subdir", "test.bin", ssc=2303)
+    # tc_queue.appendleft(command.pack_command_tuple())
+    tc_queue.appendleft(("print", "Delete 'test.bin'"))
+    command = generate_rm_file_srv23_2_packet(
+        filename="test.bin", repository_path="test/subdir", ssc=2304)
+    tc_queue.appendleft(command.pack_command_tuple())
+    tc_queue.appendleft(("print", "Delete 'subdir' directory"))
+    command = generate_rmdir_srv23_10_packet(directory_name="subdir", repository_path="test",
+                                             ssc=2305)
+    tc_queue.appendleft(command.pack_command_tuple())
+    tc_queue.appendleft(("print", "Delete 'test' directory"))
+    command = generate_rmdir_srv23_10_packet(directory_name="test", repository_path="/",
+                                             ssc=2306)
+    tc_queue.appendleft(command.pack_command_tuple())
+
+
+def calculate_allowed_file_data_size(max_app_data_size: int, filename: str, repository: str):
+    # Subtract two because of '\0' terminators
+    return max_app_data_size - len(filename) - len(repository) - 2
+
+
+def prompt_for_repo_filename():
+    input_confirmation = False
+    repo_path = ""
+    filename = ""
+    while not input_confirmation:
+        repo_path = input("Please type in repository path [c to cancel]: ")
+        if repo_path == "h":
+            return "", "h"
+        filename = input("Please type in filename path [c to cancel]: ")
+        print("Selection for repostiory path: " + str(repo_path))
+        print("Selection for filename: " + str(filename))
+        if repo_path == "c" or filename == "c":
+            return "", "c"
+        if repo_path == "h" or filename == "h":
+            return "", "h"
+        input_confirmation = input("Confirm selection [y/n]: ")
+        if input_confirmation in ['y', "yes", 1]:
+            input_confirmation = True
+    return repo_path, filename
+
+
+def pack_generic_file_command_header(object_id: bytearray, first_path: str, second_path: str):
+    data_to_pack = bytearray(object_id)
+    data_to_pack += first_path.encode('utf-8')
+    # Add string terminator of repository paht
+    data_to_pack.append(0)
+    data_to_pack += second_path.encode('utf-8')
+    # Add string terminator of filename
+    data_to_pack.append(0)
+    return data_to_pack
diff --git a/tc/obsw_tc_service5_17.py b/tc/obsw_tc_service5_17.py
index 9045f08e163c5778014c80d116ac8721ed4f7699..23dbd25701c81f31e544a658d3a617df6f281a03 100644
--- a/tc/obsw_tc_service5_17.py
+++ b/tc/obsw_tc_service5_17.py
@@ -36,23 +36,45 @@ def pack_service5_test_into(tc_queue: TcQueueT) -> TcQueueT:
     return tc_queue
 
 
-def pack_service17_test_into(tc_queue: TcQueueT) -> TcQueueT:
-    tc_queue.appendleft(("print", "Testing Service 17"))
-    # ping test
-    tc_queue.appendleft(("print", "Testing Service 17: Ping Test"))
-    command = PusTelecommand(service=17, subservice=1, ssc=1700)
-    tc_queue.appendleft(command.pack_command_tuple())
-    # enable event
-    tc_queue.appendleft(("print", "Testing Service 17: Enable Event"))
-    command = PusTelecommand(service=5, subservice=5, ssc=52)
-    tc_queue.appendleft(command.pack_command_tuple())
-    # test event
-    tc_queue.appendleft(("print", "Testing Service 17: Trigger event"))
-    command = PusTelecommand(service=17, subservice=128, ssc=1701)
+def pack_service17_test_into(tc_queue: TcQueueT, op_code: int = 0) -> TcQueueT:
+    if op_code == 0:
+        tc_queue.appendleft(("print", "Testing Service 17"))
+        # ping test
+        tc_queue.appendleft(("print", "Testing Service 17: Ping Test"))
+        command = PusTelecommand(service=17, subservice=1, ssc=1700)
+        tc_queue.appendleft(command.pack_command_tuple())
+        # enable event
+        tc_queue.appendleft(("print", "Testing Service 17: Enable Event"))
+        command = PusTelecommand(service=5, subservice=5, ssc=52)
+        tc_queue.appendleft(command.pack_command_tuple())
+        # test event
+        tc_queue.appendleft(("print", "Testing Service 17: Trigger event"))
+        command = PusTelecommand(service=17, subservice=128, ssc=1701)
+        tc_queue.appendleft(command.pack_command_tuple())
+        # invalid subservice
+        tc_queue.appendleft(("print", "Testing Service 17: Invalid subservice"))
+        command = PusTelecommand(service=17, subservice=243, ssc=1702)
+        tc_queue.appendleft(command.pack_command_tuple())
+        tc_queue.appendleft(("export", "log/tmtc_log_service17.txt"))
+    elif op_code == 129:
+        pack_enable_periodic_print_packet(tc_queue, True, 0)
+    elif op_code == 130:
+        pack_enable_periodic_print_packet(tc_queue, False, 0)
+    elif op_code == 150:
+        pack_trigger_exception_packet(tc_queue, 0)
+    return tc_queue
+
+
+def pack_enable_periodic_print_packet(tc_queue: TcQueueT, enable: bool, ssc: int):
+    tc_queue.appendleft(("print", "Enabling periodic printout"))
+    if enable:
+        command = PusTelecommand(service=17, subservice=129, ssc=ssc)
+    else:
+        command = PusTelecommand(service=17, subservice=130, ssc=ssc)
     tc_queue.appendleft(command.pack_command_tuple())
-    # invalid subservice
-    tc_queue.appendleft(("print", "Testing Service 17: Invalid subservice"))
-    command = PusTelecommand(service=17, subservice=243, ssc=1702)
+
+
+def pack_trigger_exception_packet(tc_queue: TcQueueT, ssc: int):
+    tc_queue.appendleft(("print", "Triggering software exception"))
+    command = PusTelecommand(service=17, subservice=150, ssc=ssc)
     tc_queue.appendleft(command.pack_command_tuple())
-    tc_queue.appendleft(("export", "log/tmtc_log_service17.txt"))
-    return tc_queue
diff --git a/tc/obsw_tc_service8.py b/tc/obsw_tc_service8.py
index 130bc4996265e30a73c82913165456293dc80e2e..6449fcf45f5599368fc0b30061cc7d68ef0ce8ea 100644
--- a/tc/obsw_tc_service8.py
+++ b/tc/obsw_tc_service8.py
@@ -5,6 +5,7 @@
 @author R. Mueller
 @date   01.11.2019
 """
+import struct
 from typing import Deque
 
 import config.obsw_config as g
@@ -62,3 +63,14 @@ def pack_service8_test_into(tc_queue: Deque, called_externally: bool = False) ->
     if called_externally is False:
         tc_queue.appendleft(("export", "log/tmtc_log_service8.txt"))
     return tc_queue
+
+
+def generate_action_command(object_id: bytearray, action_id: int, data: bytearray = bytearray([]),
+                            ssc: int = 0):
+    data_to_pack = bytearray(object_id)
+    data_to_pack += make_action_id(action_id) + data
+    return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=data_to_pack)
+
+
+def make_action_id(action_id: int) -> bytearray:
+    return bytearray(struct.pack('!I', action_id))
diff --git a/tc/obsw_tc_service9.py b/tc/obsw_tc_service9.py
index 507b57ffbba4723c9661970c4194ed97034a8aee..7970b0e5215faba74de4de94156f4a6236f16e29 100644
--- a/tc/obsw_tc_service9.py
+++ b/tc/obsw_tc_service9.py
@@ -36,4 +36,4 @@ def pack_service9_test_into(tc_queue: TcQueueT) -> TcQueueT:
     tc_queue.appendleft(command.pack_command_tuple())
     # TODO: Add other time formats here
     tc_queue.appendleft(("export", "log/tmtc_log_service9.txt"))
-    return tc_queue
\ No newline at end of file
+    return tc_queue
diff --git a/tc/obsw_tc_utility.py b/tc/obsw_tc_utility.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f21283dc0bdfdc34194b3e135432540233b7d13
--- /dev/null
+++ b/tc/obsw_tc_utility.py
@@ -0,0 +1,13 @@
+from typing import Union
+
+from tmtc_core.tc.obsw_pus_tc_base import TcQueueT
+from tc.obsw_tc_service8 import generate_action_command
+from config.obsw_config import LED_TASK_ID
+
+
+def pack_utility_command(service_queue: TcQueueT, op_code: Union[str, int]):
+    if op_code == "A0":
+        service_queue.appendleft(generate_action_command(LED_TASK_ID, 0x00).pack_command_tuple())
+    elif op_code == "A1":
+        service_queue.appendleft(generate_action_command(LED_TASK_ID, 0x01).pack_command_tuple())
+
diff --git a/test/obsw_module_test.py b/test/obsw_module_test.py
index 07b97cec3523a0a73e1c6d78fc63e88a33f2a511..fbdd1b609b3beed2f19d34c24108f6909c7bf47c 100644
--- a/test/obsw_module_test.py
+++ b/test/obsw_module_test.py
@@ -240,10 +240,12 @@ class TestService(unittest.TestCase):
             if current_tm_info[TmDictionaryKeys.SERVICE] == 1:
                 self.scan_for_respective_tc(current_tm_info)
             # Here, the desired event Id or RID can be specified
+
             elif current_tm_info[TmDictionaryKeys.SERVICE] == 5:
                 # TODO: hardcoded values.. should be in config file
-                if (current_tm_info[TmDictionaryKeys.EVENT_ID] == 8300 and
-                        current_tm_info[TmDictionaryKeys.REPORTER_ID] == 0x51001700):
+                if current_tm_info[TmDictionaryKeys.EVENT_ID] == 9700 and \
+                        current_tm_info[TmDictionaryKeys.REPORTER_ID] == \
+                        (struct.unpack('>I', g.PUS_SERVICE_17)[0]):
                     self.event_counter = self.event_counter + 1
             elif current_tm_info[TmDictionaryKeys.SERVICE] == 17:
                 self.misc_counter = self.misc_counter + 1
diff --git a/test/obsw_pus_service_test.py b/test/obsw_pus_service_test.py
index 1d8410c8075715b454453f8f7824a217c41f8077..16137a669cb53a9f0cfc0076a1c349c2ab771bcf 100644
--- a/test/obsw_pus_service_test.py
+++ b/test/obsw_pus_service_test.py
@@ -11,7 +11,7 @@ from typing import Deque
 from test.obsw_module_test import TestService, PusTmInfoQueueT, TmDictionaryKeys, AssertionDictKeys
 from tmtc_core.tc.obsw_pus_tc_base import PusTcInfoQueueT
 from tc.obsw_pus_tc_packer import pack_service17_test_into, pack_service5_test_into, \
-    pack_service2_test_into, pack_service8_test_into, pack_service200_test_into
+    pack_service2_test_into, pack_service8_test_into, pack_service200_test_into, pack_service20_test_into
 import config.obsw_config as g
 from tmtc_core.utility.obsw_logger import get_logger
 
diff --git a/tm/obsw_pus_tm_factory_hook.py b/tm/obsw_pus_tm_factory_hook.py
index 94899de4acb68c275049ca88e28cd29c6be22a96..94359cf02de06f448c37c09b620af0f6d13944a1 100644
--- a/tm/obsw_pus_tm_factory_hook.py
+++ b/tm/obsw_pus_tm_factory_hook.py
@@ -1,9 +1,13 @@
+import struct
+
 from tmtc_core.tm.obsw_pus_tm_base import PusTelemetry
 from tmtc_core.utility.obsw_logger import get_logger
+
 from tm.obsw_tm_service_1 import Service1TM
 from tm.obsw_tm_service_3 import Service3TM
 from tm.obsw_tm_service_5 import Service5TM
-import struct
+from tm.obsw_tm_service_20 import Service20TM
+from tm.obsw_tm_service_23 import Service23TM
 
 LOGGER = get_logger()
 
@@ -24,6 +28,8 @@ def tm_factory_hook(raw_tm_packet: bytearray) -> PusTelemetry:
         return Service17TM(raw_tm_packet)
     if service_type == 20:
         return Service20TM(raw_tm_packet)
+    if service_type == 23:
+        return Service23TM(raw_tm_packet)
     if service_type == 200:
         return Service200TM(raw_tm_packet)
     LOGGER.info("The service " + str(service_type) + " is not implemented in Telemetry Factory")
@@ -86,23 +92,6 @@ class Service17TM(PusTelemetry):
         return
 
 
-class Service20TM(PusTelemetry):
-    def __init__(self, byte_array):
-        super().__init__(byte_array)
-        self.parameter_id = struct.unpack('>I', self._tm_data[0:4])[0]
-        self.specify_packet_info("Functional Commanding Reply")
-
-    def append_telemetry_content(self, array):
-        super().append_telemetry_content(array)
-        array.append(self.parameter_id)
-        return
-
-    def append_telemetry_column_headers(self, array):
-        super().append_telemetry_column_headers(array)
-        array.append("param0_dump_repl")
-        return
-
-
 class Service200TM(PusTelemetry):
     def __init__(self, byte_array):
         super().__init__(byte_array)
@@ -141,4 +130,4 @@ class Service200TM(PusTelemetry):
         elif self.isModeReply:
             array.append("Mode")
             array.append("Submode")
-        return
\ No newline at end of file
+        return
diff --git a/tm/obsw_tm_service_20.py b/tm/obsw_tm_service_20.py
new file mode 100644
index 0000000000000000000000000000000000000000..48d4d630d126251cbd80976cf87fffcc78401c8f
--- /dev/null
+++ b/tm/obsw_tm_service_20.py
@@ -0,0 +1,62 @@
+import struct
+
+from tmtc_core.tm.obsw_pus_tm_base import PusTelemetry, TmDictionaryKeys, PusTmInfoT
+
+
+class Service20TM(PusTelemetry):
+    def __init__(self, byte_array):
+        super().__init__(byte_array)
+        datasize = len(self._tm_data)
+        self.objectId = struct.unpack('>I', self._tm_data[0:4])[0]
+        self.parameter_id = struct.unpack('>I', self._tm_data[4:8])[0]
+        if self.get_subservice() == 130:
+            self.type = struct.unpack('>H', self._tm_data[8:10])[0]
+            self.type_ptc = self._tm_data[8]
+            self.type_pfc = self._tm_data[9]
+            self.column = self._tm_data[10]
+            self.row = self._tm_data[11]
+            if self.type_ptc == 3 and self.type_pfc == 14:
+                self.param = struct.unpack('>I', self._tm_data[12:datasize])[0]
+            if self.type_ptc == 4 and self.type_pfc == 14:
+                self.param = struct.unpack('>i', self._tm_data[12:datasize])[0]
+            if self.type_ptc == 5 and self.type_pfc == 1:
+                self.param = struct.unpack('>f', self._tm_data[12:datasize])[0]
+        else:
+            logger.info("Error when receiving Pus Service 20 TM: subservice is != 130, but 130 is \
+                         only known subservice")
+        self.specify_packet_info("Functional Commanding Reply")
+
+    def append_telemetry_content(self, array):
+        super().append_telemetry_content(array)
+        array.append(hex(self.objectId))
+        array.append(self.parameter_id)
+        if self.get_subservice() == 130:
+            array.append("PTC: " + str(self.type_ptc) + " | PFC: " + str(self.type_pfc))
+            array.append(str(self.column))
+            array.append(str(self.row))
+            if self.type_ptc == 5 and self.type_pfc == 1:
+                array.append(str(float(self.param)))
+            else:
+                array.append(str(hex(self.param)))
+        return
+
+    def append_telemetry_column_headers(self, array):
+        super().append_telemetry_column_headers(array)
+        array.append("objectID")
+        array.append("parameterID")
+        if self.get_subservice() == 130:
+            array.append("type")
+            array.append("column")
+            array.append("row")
+            array.append("parameter")
+        return
+
+    def pack_tm_information(self) -> PusTmInfoT:
+        tm_information = super().pack_tm_information()
+        add_information = {
+            TmDictionaryKeys.REPORTER_ID: self.objectId,
+            TmDictionaryKeys.EVENT_ID: self.parameter_id,
+            TmDictionaryKeys.EVENT_PARAM_1: self.param
+        }
+        tm_information.update(add_information)
+        return tm_information
diff --git a/tm/obsw_tm_service_23.py b/tm/obsw_tm_service_23.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4668c332ff5c583f286129cbed23a6e5a5dafd3
--- /dev/null
+++ b/tm/obsw_tm_service_23.py
@@ -0,0 +1,79 @@
+import struct
+
+from tmtc_core.tm.obsw_pus_tm_base import PusTelemetry
+from tmtc_core.utility.obsw_logger import get_logger
+
+LOGGER = get_logger()
+
+
+class Service23TM(PusTelemetry):
+    MAX_REPOSITORY_LENGTH = 64
+    MAX_FILENAME_LENGTH = 12
+
+    def __init__(self, byte_array):
+        super().__init__(byte_array)
+        if len(self.get_tm_data()) < 4:
+            LOGGER.error("Service23TM: Invalid packet format!")
+            return
+        self.object_id = struct.unpack('!I', self._tm_data[0:4])[0]
+        self.repo_path = ""
+        self.filename = ""
+        self.file_size = 0
+        self.lock_status = False
+        self.data_start_idx = 0
+        if self.get_subservice() == 4:
+            self.unpack_repo_and_filename()
+            self.unpack_file_attributes()
+        elif self.get_subservice() == 132:
+            self.unpack_repo_and_filename()
+            pass
+
+    def unpack_repo_and_filename(self):
+        repo_path_found = False
+        path_idx_start = 0
+        max_len_to_scan = len(self.get_tm_data()) - 4
+        for idx in range(4, max_len_to_scan):
+            if not repo_path_found and self._tm_data[idx] == 0:
+                repo_bytes = self._tm_data[4:idx]
+                self.repo_path = repo_bytes.decode('utf-8')
+                path_idx_start = idx + 1
+                idx += 1
+                repo_path_found = True
+            if repo_path_found:
+                if self._tm_data[idx] == 0:
+                    filename_bytes = self._tm_data[path_idx_start:idx]
+                    self.filename = filename_bytes.decode('utf-8')
+                    self.data_start_idx = idx + 1
+                    break
+
+    def unpack_file_attributes(self):
+        # Size of file length (4) + lock status (1), adapt if more field are added!
+        print(len(self.get_tm_data()) - self.data_start_idx)
+        if len(self.get_tm_data()) - self.data_start_idx != 5:
+            LOGGER.error("Service23TM: Invalid lenght of file attributes data")
+            return
+        self.file_size = struct.unpack('!I', self.get_tm_data()[
+                                             self.data_start_idx: self.data_start_idx + 4])[0]
+        self.lock_status = self.get_tm_data()[self.data_start_idx + 4]
+
+    def append_telemetry_content(self, content_list: list):
+        super().append_telemetry_content(content_list)
+        content_list.append(hex(self.object_id))
+        content_list.append(self.repo_path)
+        content_list.append(self.filename)
+        if self.get_subservice() == 4:
+            content_list.append(self.file_size)
+            if self.lock_status == 0:
+                content_list.append("No")
+            else:
+                content_list.append("Yes")
+
+    def append_telemetry_column_headers(self, header_list: list):
+        super().append_telemetry_column_headers(header_list)
+        header_list.append("Object ID")
+        header_list.append("Repo Path")
+        header_list.append("File Name")
+        if self.get_subservice() == 4:
+            header_list.append("File Size")
+            header_list.append("Locked")
+
diff --git a/tm/obsw_tm_service_3.py b/tm/obsw_tm_service_3.py
index 96be1223fc42fdfb94298ffabd993dfdc1434ede..ddb4376333700671b40b2f244a48a4a14f8b691a 100644
--- a/tm/obsw_tm_service_3.py
+++ b/tm/obsw_tm_service_3.py
@@ -122,8 +122,8 @@ class Service3TM(PusTelemetry):
         # TODO: size check. sth not right with gps 0 test
         self.number_of_parameters = 9
         self.hk_header = ["Fix Mode", "SV in Fix", "GNSS Week", "Time of Week", "Latitude",
-                         "Longitude", "Mean Sea Altitude", "Position X", "Position Y", "Position Z",
-                         "Velocity X", "Velocity Y", "Velocity Z"]
+                          "Longitude", "Mean Sea Altitude", "Position X", "Position Y",
+                          "Position Z", "Velocity X", "Velocity Y", "Velocity Z"]
         fix_mode = self._tm_data[4]
         sv_in_fix = self._tm_data[5]
         gnss_week = struct.unpack('>H', self._tm_data[6:8])[0]
diff --git a/tmtc_client_cli.py b/tmtc_client_cli.py
new file mode 100644
index 0000000000000000000000000000000000000000..48c02fbe3b710e6d782bdb345d16760375c0aab8
--- /dev/null
+++ b/tmtc_client_cli.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python3
+"""
+@brief  This client was developed by KSat for the SOURCE project to test the on-board software.
+@details
+This client features multiple sender/receiver modes and has been designed
+to be extensible and easy to use. This clien is based on the PUS standard for the format
+of telecommands and telemetry. It can also send TMTC via different interfaces like the
+serial interface (USB port) or ethernet interface.
+
+@license
+Copyright 2020 KSat e.V. Stuttgart
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+@manual
+Run this file with the -h flag to display options.
+"""
+from core.tmtc_client_core import run_tmtc_client
+
+def main():
+    run_tmtc_client(False)
+
+if __name__ == "__main__":
+    main()
diff --git a/tmtc_client_gui.py b/tmtc_client_gui.py
new file mode 100644
index 0000000000000000000000000000000000000000..60722026def47208fcd2bef2eb3e07607f2ea440
--- /dev/null
+++ b/tmtc_client_gui.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python3
+"""
+@brief  This client was developed by KSat for the SOURCE project to test the on-board software.
+@details
+This client features multiple sender/receiver modes and has been designed
+to be extensible and easy to use. This clien is based on the PUS standard for the format
+of telecommands and telemetry. It can also send TMTC via different interfaces like the
+serial interface (USB port) or ethernet interface.
+
+@license
+Copyright 2020 KSat e.V. Stuttgart
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+@manual
+"""
+from core.tmtc_client_core import run_tmtc_client
+
+def main():
+    run_tmtc_client(True)
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/tmtc_core b/tmtc_core
index 4d37769e7c8254bf7e20a016cf076ae48db31e57..1e06be166b0bf47a2a921fd45eed49ea9f5782c3 160000
--- a/tmtc_core
+++ b/tmtc_core
@@ -1 +1 @@
-Subproject commit 4d37769e7c8254bf7e20a016cf076ae48db31e57
+Subproject commit 1e06be166b0bf47a2a921fd45eed49ea9f5782c3
diff --git a/utility/binary_writer.py b/utility/binary_writer.py
new file mode 100644
index 0000000000000000000000000000000000000000..c07ab66cdf39ec3d3a3e6aa5c6bdf129d0124079
--- /dev/null
+++ b/utility/binary_writer.py
@@ -0,0 +1,54 @@
+import os
+import struct
+from glob import glob
+
+
+def main():
+    """
+    Was written to assign sixth arm vector to binary size for bootloader.
+    @return:
+    """
+
+    # Parse for binaries
+    file_path = file_selector("../../_bin", '*.bin')
+    file_size = os.path.getsize(file_path)
+    print("File size: " + str(file_size))
+    size_uint32 = struct.pack("<I", file_size)
+    print("File size as uint32: " + ' '.join(map('{:02X}'.format, size_uint32)))
+    with open(file_path, "rb+") as file:
+        # Read the first seven ARM vectors. The sixth one will be replaced.
+        arm_vectors = file.read(4 * 7)
+        print("ARM vectors: ")
+        # print(''.join('{:02x}'.format(x) for x in arm_vectors))
+        for x in range(0, 28, 4):
+            print(' '.join(map('{:02X}'.format, arm_vectors[x:x + 4])))
+        file.seek(5*4)
+        file.write(size_uint32)
+        file.seek(0)
+        arm_vectors = file.read(4 * 7)
+        print("New ARM vector: ")
+        for x in range(0, 28, 4):
+            print(' '.join(map('{:02X}'.format, arm_vectors[x:x + 4])))
+        pass
+
+
+def file_selector(path: str, pattern: str) -> str:
+    result = [y for x in os.walk(path) for y in glob(os.path.join(x[0], pattern))]
+    print("Files found in _bin folder: ")
+    for idx, path in enumerate(result):
+        print("Selection " + str(idx) + ": " + str(path))
+    selection = input("Please enter desired selection [c to cancel]: ")
+    while True:
+        if selection == 'c':
+            return ""
+        if not selection.isdigit():
+            selection = input("Invalid input, try again [c to cancel]: ")
+        if selection.isdigit():
+            if int(selection) < len(result):
+                return result[int(selection)]
+            else:
+                selection = input("Invalid input, try again [c to cancel]: ")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/utility/obsw_args_parser.py b/utility/obsw_args_parser.py
index bef39878d7c7eb07d7c2766e11bb4d11da000167..47f2018ae2a2a64961d5f7e4e6b5eadc207e2ce3 100644
--- a/utility/obsw_args_parser.py
+++ b/utility/obsw_args_parser.py
@@ -18,16 +18,23 @@ def parse_input_arguments():
     arg_parser = argparse.ArgumentParser(description="TMTC Client Command Line Interface")
 
     arg_parser.add_argument(
-        '-m', '--mode', type=int, help='Target Mode. Default is 1(Listener Mode), '
-        '0: GUI Mode, 1:Listener Mode, 2: Single Command Mode, 3: Service Test Mode, '
-        '4: Software Test Mode, 5: Unit Test Mode ', default=0)
+        '-m', '--mode', type=int, help='Target Mode. Default is 1 (Listener Mode), '
+        '0: GUI Mode, 1: Listener Mode, 2: Single Command Mode, 3: Service Test Mode, '
+        '4: Software Test Mode, 5: Binary Upload Mode, 6: Unit Test Mode ', default=0)
     arg_parser.add_argument(
         '-c', '--com_if', type=int, help='Communication Interface. 0: Dummy Interface, 1: Serial, '
         '2: QEMU, 3: UDP', default=2)
+    arg_parser.add_argument(
+        '-o', '--op_code', help='Operation code, which is passed to the TC '
+        'packer functions', default=0)
     arg_parser.add_argument('--clientIP', help='Client(Computer) IP. Default:\'\'', default='')
     arg_parser.add_argument(
         '--boardIP', help='Board IP. Default: Localhost 127.0.0.1', default="127.0.0.1")
     arg_parser.add_argument('-s', '--service', help='Service to test. Default: 17', default=17)
+    arg_parser.add_argument(
+        '-l','--listener',  help='Determine whether the listener mode will be active after '
+                                 'performing the operation',
+        action='store_false')
     arg_parser.add_argument(
         '-t', '--tm_timeout', type=float, help='TM Timeout when listening to verification sequence.'
         ' Default: 5 seconds', default=5.0)
@@ -38,7 +45,7 @@ def parse_input_arguments():
         '--np', dest='print_tm', help='Supply --np to suppress print output to console.',
         action='store_false')
     arg_parser.add_argument(
-        '-o', '--tc_timeout_factor', type=float, help='TC Timeout Factor. Multiplied with '
+        '--tc_timeout_factor', type=float, help='TC Timeout Factor. Multiplied with '
         'TM Timeout, TC sent again after this time period. Default: 3.5', default=3.5)
     arg_parser.add_argument(
         '-r', '--rawDataPrint', help='Supply -r to print all raw TM data directly',
diff --git a/utility/obsw_binary_uploader.py b/utility/obsw_binary_uploader.py
index 5c9cc8bef3b166d2bb1979a55634df00c6aa990b..60c3de66a08d7a357d2ae58b547c3c0d61c3d09b 100644
--- a/utility/obsw_binary_uploader.py
+++ b/utility/obsw_binary_uploader.py
@@ -6,34 +6,241 @@ This module will be used to upload binaries to the OBC via a communication port,
 a supplied binary. The binary will be sent via the specified communication interface.
 It will be possible to encode the data (for example using DLE encoding)
 """
+import os
+import time
 import tkinter as tk
 from tkinter import filedialog
+from collections import deque
+from glob import glob
+from typing import Deque
 
+from tmtc_core.comIF.obsw_com_interface import CommunicationInterface
+from utility.obsw_file_transfer_helper import FileTransferHelper
 import config.obsw_config as g
+from tmtc_core.utility.obsw_tmtc_printer import TmTcPrinter, DisplayMode
+from tmtc_core.utility.obsw_logger import get_logger
+from tmtc_core.sendreceive.obsw_tm_listener import TmListener
 
+LOGGER = get_logger()
 
-def perform_binary_upload():
-    print("Please select file to upload")
-    root = tk.Tk()
-    root.withdraw()
-    root.wm_attributes('-topmost', 1)
-    file_path = filedialog.askopenfilename(parent=root)
-    print("File select: " + str(file_path))
-    calc_hamming_code = input("Calculate and send hamming code? [y/n]")
-    if calc_hamming_code in ['y', 'yes', 1]:
-        calc_hamming_code = True
-        print("Hamming code will be calculated and sent in tail packet")
-    else:
-        calc_hamming_code = False
-        print("Hamming code will not be calculated")
 
-    # Right now, the size of PUS packets is limited to 1024 bytes. Therefore, we split up the
-    # binary in 1000 byte packets
-    frame_length = g.G_MAX_BINARY_FRAME_LENGTH
+class BinaryFileUploader:
+    def __init__(self, com_if: CommunicationInterface, tmtc_printer: TmTcPrinter,
+                 tm_listener: TmListener):
+        """
+        Initializes the binary file uploader with the required components.
+        @param com_if:
+        @param tmtc_printer:
+        @param tm_listener:
+        """
+        self.com_if = com_if
+        self.tmtc_printer = tmtc_printer
+        self.tm_listener = tm_listener
+        self.iobc = False
+        self.send_interval = 1.0
 
-    if calc_hamming_code:
-        # now we calculate the hamming code
-        pass
+    def perform_file_upload(self):
+        gui_cl_prompt = input("GUI(0) or command line version (1)? [0/1]: ")
+        if gui_cl_prompt == 0:
+            gui_cl_prompt = True
+        else:
+            gui_cl_prompt = False
+        print("Please select file to upload: ")
+        file_path = ""
+        if gui_cl_prompt:
+            root = tk.Tk()
+            root.withdraw()
+            root.wm_attributes('-topmost', 1)
+            file_path = filedialog.askopenfilename(parent=root)
+            print("File select: " + str(file_path))
+            if file_path == ():
+                LOGGER.warning("Invalid file path, exiting binary upload mode.")
+                return
+        else:
+            result = [y for x in os.walk("../_bin") for y in glob(os.path.join(x[0], '*.bin'))]
+            print("Files found in _bin folder: ")
+            for idx, path in enumerate(result):
+                print("Selection " + str(idx) + ": " + str(path))
+            select_valid = False
+            selection = input("Please enter desired selection [c to cancel]: ")
+            while not select_valid:
+                if selection == 'c':
+                    print("Exiting binary upload mode..")
+                    return
+                if not selection.isdigit():
+                    selection = input("Invalid input, try again [c to cancel]: ")
+                if selection.isdigit():
+                    if int(selection) < len(result):
+                        file_path = result[int(selection)]
+                        select_valid = True
+                    else:
+                        selection = input("Invalid input, try again [c to cancel]: ")
 
-    # We have to split the binary here first
+        print_string = file_path.rsplit(os.path.sep, 1)[-1] + " was selected."
+        LOGGER.info(print_string)
+        calc_hamming_code = input("Calculate and send hamming code? [y/n]: ")
+        if calc_hamming_code in ['y', 'yes', 1]:
+            calc_hamming_code = True
+            print("Hamming code will be calculated and sent in tail packet")
+        else:
+            calc_hamming_code = False
+            print("Hamming code will not be calculated")
 
+        iobc_prompt = input("iOBC? [y/n]: ")
+        if iobc_prompt in ['y', 'yes', 1]:
+            self.iobc = True
+            self.send_interval = 0.8
+            iobc_prompt = True
+        else:
+            self.iobc = False
+            self.send_interval = 0.6
+            iobc_prompt = False
+
+        bootloader_prompt = input("Bootloader (0) or Software Image (1)? [0/1]: ")
+        if str(bootloader_prompt) == "0":
+            bootloader_prompt = True
+        else:
+            bootloader_prompt = False
+
+        prompt_lock = input("Lock file with last packet? [y/n]: ")
+        if prompt_lock in ['n', "no", 0]:
+            prompt_lock = False
+        else:
+            prompt_lock = True
+
+        if bootloader_prompt:
+            file_name = "bl.bin"
+        else:
+            file_name = "obsw_up.bin"
+
+        if iobc_prompt:
+            if bootloader_prompt:
+                repository_name = "BIN/IOBC/BL"
+            else:
+                repository_name = "BIN/IOBC/OBSW"
+        else:
+            if bootloader_prompt:
+                repository_name = "BIN/AT91/BL"
+            else:
+                repository_name = "BIN/AT91/OBSW"
+
+        if calc_hamming_code:
+            pass
+
+        # Right now, the size of PUS packets is limited to 1500 bytes which also limits the app
+        # data length.
+        frame_length = g.G_MAX_APP_DATA_LENGTH
+
+        if calc_hamming_code:
+            # now we calculate the hamming code
+            pass
+
+        tc_queue = deque()
+
+        # Delete existing binary file first, otherwise data might be appended to otherwise
+        # valid file which already exists.
+        file_transfer_helper = FileTransferHelper(
+            tc_queue=tc_queue, max_size_of_app_data=frame_length, target_repository=repository_name,
+            target_filename=file_name)
+
+        init_ssc = 0
+        self.tmtc_printer.set_display_mode(DisplayMode.SHORT)
+
+        # Configure file transfer helper
+        file_transfer_helper.set_data_from_file(file_path)
+        file_transfer_helper.set_to_delete_old_file()
+        if prompt_lock:
+            file_transfer_helper.set_to_lock_file(prompt_lock)
+        else:
+            file_transfer_helper.set_to_lock_file(prompt_lock)
+
+        # Generate the packets.
+        file_transfer_helper.generate_packets(init_ssc)
+
+        self.tm_listener.set_listener_mode(TmListener.ListenerModes.MANUAL)
+        print_string = "BinaryUploader: Detected file size: " + str(
+            file_transfer_helper.file_size())
+        LOGGER.info(print_string)
+        total_num_packets = file_transfer_helper.get_number_of_packets_generated()
+        print_string = "BinaryUploader: " + str(total_num_packets) + \
+                       " packets generated."
+        if prompt_lock:
+            print_string += " File will be locked."
+        else:
+            print_string += " File will not be locked."
+        LOGGER.info(print_string)
+
+        reception_deque = deque()
+        self.__perform_send_algorithm(tc_queue, total_num_packets, reception_deque)
+
+        print_string = "BinaryUploader: All binary packets were sent!"
+        LOGGER.info(print_string)
+        print_string = str(reception_deque.__len__()) + " replies received."
+
+        LOGGER.info(print_string)
+        time.sleep(15)
+        reception_deque.extend(self.tm_listener.retrieve_tm_packet_queue())
+        for tm_list in reception_deque:
+            for tm_packet in tm_list:
+                if tm_packet.get_service() == 23 and tm_packet.get_subservice() == 132:
+                    # tmtc_printer.print_telemetry(tm_packet)
+                    pass
+        self.tm_listener.clear_tm_packet_queue()
+        LOGGER.info("Transitioning back to listener mode..")
+
+    def __perform_send_algorithm(self, tc_queue: Deque, number_of_packets: int, reception_deque:
+                                 Deque):
+        last_check = time.time()
+        last_sent = time.time()
+        total_time = self.send_interval * number_of_packets
+        idx = 1
+        while tc_queue:
+            next_send = last_sent + self.send_interval
+            (tc_packet, tc_info) = tc_queue.pop()
+            if not isinstance(tc_packet, str):
+                # print_string = "Sending packet " + str(idx) + ".."
+                # LOGGER.info(print_string)
+                idx += 1
+                self.com_if.send_telecommand(tc_packet, tc_info)
+                self.tmtc_printer.print_telecommand(tc_packet, tc_info)
+            elif tc_packet == "print":
+                LOGGER.info(tc_info)
+            remaining_time_string = "Remaining time: " + \
+                                    str(round(total_time - (idx - 2) * self.send_interval, 2)) + \
+                                    " seconds"
+            print_progress_bar(idx - 2, number_of_packets, print_end="\n",
+                               suffix=remaining_time_string)
+            # sys.stdout.write("\033[F")  # Cursor up one line
+            packets_received = self.tm_listener.retrieve_tm_packet_queue()
+            reception_deque.extend(packets_received)
+            # Every 5 seconds, check whether any reply has been received. If not, cancel operation.
+            if time.time() - last_check > 5.0 and len(packets_received) == 0:
+                LOGGER.warning("No replies are being received, cancelling upload operation..")
+            time_to_sleep = next_send - time.time()
+            last_sent = next_send
+            time.sleep(time_to_sleep)
+
+
+# https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console
+# Thank you Greensticks :-)
+def print_progress_bar(iteration, total, prefix='', suffix='', decimals=1, length=100,
+                       fill='â–ˆ', print_end="\r"):
+    """
+    Call in a loop to create terminal progress bar
+    @params:
+        iteration   - Required  : current iteration (Int)
+        total       - Required  : total iterations (Int)
+        prefix      - Optional  : prefix string (Str)
+        suffix      - Optional  : suffix string (Str)
+        decimals    - Optional  : positive number of decimals in percent complete (Int)
+        length      - Optional  : character length of bar (Int)
+        fill        - Optional  : bar fill character (Str)
+        print_end    - Optional  : end character (e.g. "\r", "\r\n") (Str)
+    """
+    percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
+    filled_length = int(length * iteration // total)
+    bar = fill * filled_length + '-' * (length - filled_length)
+    print(f'\r{prefix} |{bar}| {percent}% {suffix}', end=print_end)
+    # Print New Line on Complete
+    if iteration == total:
+        print()
diff --git a/utility/obsw_file_transfer_helper.py b/utility/obsw_file_transfer_helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6efb90e7e15cf98b494e9c895f9137b88107c6f
--- /dev/null
+++ b/utility/obsw_file_transfer_helper.py
@@ -0,0 +1,241 @@
+from enum import Enum
+import math
+
+from config.obsw_config import SD_CARD_HANDLER_ID
+from tmtc_core.tc.obsw_pus_tc_base import TcQueueT, PusTelecommand
+from tc.obsw_tc_service23_sdcard import \
+    calculate_allowed_file_data_size, generate_rm_file_srv23_2_packet, \
+    generate_create_file_srv23_1_packet, generate_finish_append_to_file_srv23_131_packet, \
+    generate_lock_file_srv23_5_6_packet
+
+
+class FileTransferHelper:
+    """
+    This helper class fills the provided TC queue with appropriate PUS telecommands
+    to transfer a file.
+    There are three modes which determine which telecommands will be generated:
+    1. NORMAL:      Generate telecommand to create a new file and append data packets if
+                    the file data is too large. This will be the default mode.
+    2. DELETE_OLD:  Generate telecommand to delete old file and then perform same steps as the
+                    normal mode
+    3. RENAME_OLD:  Rename old file and then perform same steps as in normal mode.
+
+    Please note that the setter functions set_data have to be used to assign data, otherwise
+    an empty file will be created. The mode is set with setter commands as well.
+    """
+    class TransferMode(Enum):
+        # Normal mode
+        NORMAL = 1
+        # Generate a command to delete the old file first
+        DELETE_OLD = 2
+        # Generate a command to rename the old file first.
+        RENAME_OLD = 3
+
+    def __init__(self, tc_queue: TcQueueT, max_size_of_app_data: int,
+                 target_repository: str, target_filename: str,
+                 object_id=SD_CARD_HANDLER_ID):
+        """
+        @param tc_queue: TC queue which will be filled
+        @param max_size_of_app_data: Maximum allowed app data size. Number of generated packets
+        will depend on this value
+        @param target_repository: Repository path on target.
+        @param target_filename: Filename on target
+        @param object_id:
+        """
+        self.object_id = object_id
+        self.max_size_of_app_data = max_size_of_app_data
+        self.allowed_file_data_size = calculate_allowed_file_data_size(
+            max_size_of_app_data, target_filename, target_repository)
+
+        self.target_filename = target_filename
+        self.target_repository = target_repository
+
+        self.tc_queue = tc_queue
+
+        self.__transfer_mode = self.TransferMode.NORMAL
+        self.__max_file_data_size = 0
+        self.__renamed_name = self.target_filename + "old"
+
+        self.__large_file = False
+        self.__number_of_packets = 0
+        self.__number_of_append_packets = 0
+        self.__number_of_create_packets = 1
+        self.__number_of_delete_packets = 0
+        self.__number_of_finish_packets = 1
+
+        self.__current_ssc = 0
+        self.__lock_file = True
+        self.__local_filename = ""
+        self.__file_data = bytearray()
+        # This will generate a telecommand to delete the old file, if it exists
+        self.delete_old_file = False
+        # This will generater a telecommand to rename the old file, if it exists
+        self.rename_old_file = False
+
+    def set_data_from_file(self, local_filename: str):
+        with open(local_filename, 'rb') as file:
+            self.__file_data = file.read()
+
+    def set_data_raw(self, tc_data: bytearray):
+        self.__file_data = tc_data
+
+    def set_to_delete_old_file(self):
+        self.__transfer_mode = self.TransferMode.DELETE_OLD
+
+    def set_to_rename_old_file(self, renamed_name: str):
+        self.__transfer_mode = self.TransferMode.RENAME_OLD
+        self.__renamed_name = renamed_name
+
+    def set_to_lock_file(self, lock_file: bool):
+        """
+        Command will be sent to lock file after succesfull transfer
+        @param lock_file:
+        @return:
+        """
+        self.__lock_file = lock_file
+
+    def get_number_of_packets_generated(self):
+        return self.__number_of_packets
+
+    def set_max_file_data_size(self, max_file_data_size: int):
+        """
+        If this value is specified and the source file is large (larger than the maximum allowed
+        app data!), the file data size will be set to this value.
+        @param max_file_data_size:
+        @return:
+        """
+        self.__max_file_data_size = max_file_data_size
+
+    def file_size(self):
+        return len(self.__file_data)
+
+    def generate_packets(self, ssc: int):
+        """
+        Main function to generate all packets and fill them into the provided deque.
+        @param ssc:
+        @return:
+        """
+        self.__current_ssc = ssc
+        self.__handle_delete_packet_generation()
+        if self.__transfer_mode == self.TransferMode.RENAME_OLD:
+            # not implemented yet
+            pass
+        self.__handle_create_file_packet_generation()
+        self.__handle_finish_and_lock_packet_generation()
+        self.__number_of_packets = \
+            self.__number_of_create_packets + self.__number_of_append_packets + \
+            self.__number_of_delete_packets + self.__number_of_finish_packets
+
+    def __handle_delete_packet_generation(self):
+        if self.__transfer_mode == self.TransferMode.DELETE_OLD:
+            command = generate_rm_file_srv23_2_packet(
+                filename=self.target_filename, repository_path=self.target_repository,
+                ssc=self.__current_ssc, object_id=self.object_id)
+            self.__number_of_delete_packets = 1
+            self.__current_ssc += 1
+            self.tc_queue.appendleft(command.pack_command_tuple())
+
+    def __handle_create_file_packet_generation(self):
+        if len(self.__file_data) > self.allowed_file_data_size:
+            # Large file, create file with init_data
+            if self.__max_file_data_size > 0:
+                init_data = self.__file_data[0:self.__max_file_data_size]
+            else:
+                init_data = self.__file_data[0:self.allowed_file_data_size]
+            self.__large_file = True
+        else:
+            init_data = self.__file_data
+
+        # Create file.
+        command = generate_create_file_srv23_1_packet(
+            self.target_filename, self.target_repository, ssc=self.__current_ssc,
+            max_size_of_app_data=self.max_size_of_app_data, initial_data=init_data)
+        self.__current_ssc += 1
+        self.tc_queue.appendleft(command.pack_command_tuple())
+        if not self.__large_file:
+            return
+        rest_of_data = self.__file_data[self.allowed_file_data_size:]
+        # Generate the rest of the packets to write to large file
+        if self.__max_file_data_size > 0:
+            self.__generate_append_to_file_packets_automatically(
+                data=rest_of_data, target_repository=self.target_repository,
+                target_filename=self.target_filename, size_of_data_blocks=self.__max_file_data_size,
+                init_ssc=self.__current_ssc)
+        else:
+            self.__generate_append_to_file_packets_automatically(
+                data=rest_of_data, target_repository=self.target_repository,
+                target_filename=self.target_filename, size_of_data_blocks=self.max_size_of_app_data,
+                init_ssc=self.__current_ssc)
+        self.__current_ssc += 1
+
+    def __generate_append_to_file_packets_automatically(
+            self, data: bytearray, target_repository: str, target_filename: str,
+            size_of_data_blocks: int, init_ssc: int):
+        """
+        This function generates PUS packets which is used to write data in a file.
+        A new file will be created if not already existing. If the file already exists, this might
+        lead to
+
+        If the file data is larger than the maximum allowed size of application data, this function
+        will split the data into multiple packets and increment the initial SSC number by one for
+        each packet.
+        @param data: Data which will be split up.
+        @param init_ssc: First SSC, which will be incremented for each packet.
+        """
+        header = bytearray(self.object_id)
+        header += target_repository.encode('utf-8')
+        # Add string terminator of repository path
+        header.append(0)
+        header += target_filename.encode('utf-8')
+        # Add string terminator of filename
+        header.append(0)
+        self.__split_large_file(header, size_of_data_blocks, data, init_ssc)
+
+    def __split_large_file(self, header: bytearray, size_of_data_blocks: int,
+                           data: bytearray, init_ssc: int):
+        """
+        This function splits a large file in multiple packets and packs the generated packets
+        into the member deque. This is necessary because the packet size is limited.
+        @param header: Repository and file name which will always stay the same
+        @param size_of_data_blocks: The file data blocks will have this size
+        @param data: The data to pack in multiple packets
+        @param init_ssc: The ssc of the first command, will be incremented by one for each packet.
+        """
+        number_of_packets = math.floor(len(data) / size_of_data_blocks)
+        packet_sequence_number = 0
+
+        for i in range(number_of_packets):
+            header.append(packet_sequence_number >> 8)
+            header.append(0xFF & packet_sequence_number)
+            header += data[i * size_of_data_blocks:(i + 1) * size_of_data_blocks]
+
+            commands = PusTelecommand(service=23, subservice=130, ssc=init_ssc + i,
+                                      app_data=header)
+            self.tc_queue.appendleft(commands.pack_command_tuple())
+
+            # Remove everything except the header
+            header = header[:len(header) - size_of_data_blocks - 2]
+            packet_sequence_number = packet_sequence_number + 1
+        # Last packet will be subservice 131 to finish  the append operation
+        header.append(packet_sequence_number >> 8)
+        header.append(0xFF & packet_sequence_number)
+        self.__number_of_append_packets += number_of_packets
+        header += data[number_of_packets * size_of_data_blocks:len(data)]
+        commands = PusTelecommand(service=23, subservice=130, ssc=init_ssc + packet_sequence_number,
+                                  app_data=header)
+        self.tc_queue.appendleft(commands.pack_command_tuple())
+
+    def __handle_finish_and_lock_packet_generation(self):
+        if self.__large_file:
+            last_command = generate_finish_append_to_file_srv23_131_packet(
+                filename=self.target_filename, repository_path=self.target_repository,
+                ssc=self.__current_ssc, lock_file=self.__lock_file)
+        else:
+            if self.__lock_file:
+                last_command = generate_lock_file_srv23_5_6_packet(
+                    filename=self.target_filename, repository_path=self.target_repository,
+                    object_id=self.object_id, lock=True, ssc=self.__current_ssc)
+            else:
+                self.__number_of_finish_packets = 0
+                return
+        self.tc_queue.appendleft(last_command.pack_command_tuple())
\ No newline at end of file