diff --git a/.idea/runConfigurations/tmtcclient_Software_Serial.xml b/.idea/runConfigurations/tmtcclient_Binary_Upload_Serial.xml
similarity index 77%
rename from .idea/runConfigurations/tmtcclient_Software_Serial.xml
rename to .idea/runConfigurations/tmtcclient_Binary_Upload_Serial.xml
index 0dfd7b192a6af6da43bb59f26b4b1c33c7030405..0f71641f824fd4d2ef324b83f038e96cf4a6d467 100644
--- a/.idea/runConfigurations/tmtcclient_Software_Serial.xml
+++ b/.idea/runConfigurations/tmtcclient_Binary_Upload_Serial.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 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" />
@@ -13,9 +13,9 @@
     <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="PARAMETERS" value="-m 5 -c 1" />
     <option name="SHOW_COMMAND_LINE" value="false" />
-    <option name="EMULATE_TERMINAL" 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="" />
diff --git a/.idea/runConfigurations/tmtcclient_Clear_SD_Card_.xml b/.idea/runConfigurations/tmtcclient_Clear_SD_Card_.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0625478e1096c8e4741c5a679e7f1a5106657c74
--- /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$/obsw_tmtc_client.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..d91be384b7b6603e6fbb013d2d72767e64c340c3
--- /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$/obsw_tmtc_client.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..df307b80bceb31890f70e8a330843cce7d4412be
--- /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$/obsw_tmtc_client.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..cc27520bd0fab79801e5e6cb318253da46476c3c
--- /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$/obsw_tmtc_client.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..b6645926453ca505e2732cd9a09225e1f7b733cc
--- /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$/obsw_tmtc_client.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_Enable_Periodic_Print.xml b/.idea/runConfigurations/tmtcclient_Enable_Periodic_Print.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b7394cbce13ae7c4f410959e820edcf2c31248c5
--- /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$/obsw_tmtc_client.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..050cb977f81f9aebbc5e2509619b6e5bf3a54821
--- /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$/obsw_tmtc_client.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..7aa42a29328053467a3364d0c8fd1e5623643b94 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" />
diff --git a/.idea/runConfigurations/tmtcclient_GPS1_Serial.xml b/.idea/runConfigurations/tmtcclient_GPS1_Serial.xml
index 1f8098a72d1b52a387c0256e35fa3cebdb4ccfae..2e914ff6b1e6f74ba631fb5d3a019074c29bfe49 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" />
diff --git a/.idea/runConfigurations/tmtcclient_Listener_Serial.xml b/.idea/runConfigurations/tmtcclient_Listener_Serial.xml
index c59efdb836593429d3c211d1ed21f814c1a13177..8655adedcf593f6bc9146582d876f95a7b5a29f6 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" />
diff --git a/.idea/runConfigurations/tmtcclient_Lock_file.xml b/.idea/runConfigurations/tmtcclient_Lock_file.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dc7e019138759e7d48e4bbaa6bcf32dc3dd89849
--- /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$/obsw_tmtc_client.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..317ef3ef1694004f58e6eca515834aa7bc1f6525 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" />
diff --git a/.idea/runConfigurations/tmtcclient_PowerCycle_OBC_.xml b/.idea/runConfigurations/tmtcclient_PowerCycle_OBC_.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b443130565f1120cc74f261a940059def5685f68
--- /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$/obsw_tmtc_client.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..5c359f03920f2de984dfbd0496d6ca80534ebfb2
--- /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$/obsw_tmtc_client.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..3e529385b41396062e99870991fcde125cffd9be
--- /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$/obsw_tmtc_client.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..8e3e5851b3f02f90adb088bc83c11d659719c433
--- /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$/obsw_tmtc_client.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_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_17_Serial.xml
index 5145e3b7acd8d267540c2cc4214eb61b7c4c8f67..9abd7b47887bd512e9ab8324697489c8d241f991 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" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_2_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_2_Serial.xml
index 38316e0359a813bff39ae6574bb14beb0430e97e..1ffb46d6a63706b548933833b4bd1e4575723b3e 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" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_3_Serial_.xml b/.idea/runConfigurations/tmtcclient_Service_3_Serial_.xml
index 7b0db83d4daea21024c7ef99394582f529ca1486..92741a34eaf48f82d59302d0c44c0b4d8e918556 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" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_5_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_5_Serial.xml
index aadf325a5002b612eaf8c56d8652bb56d02a7288..58a794a73e1b785409f4b477af84e3355a96967c 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" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_8_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_8_Serial.xml
index 1c96bce8f9e66fec8a1b6be5948348f2928904c7..7d9695d2c9c2e0c3ea98b767c37b8cf11948c2df 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" />
diff --git a/.idea/runConfigurations/tmtcclient_Service_9_Serial.xml b/.idea/runConfigurations/tmtcclient_Service_9_Serial.xml
index f755f02c30089f9d2bc2a6efb46f47f65ac9e3a7..781448c3ff0319a1b8f5c887741b7b837010a29d 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" />
diff --git a/.idea/runConfigurations/tmtcclient_Single_Command_Serial_.xml b/.idea/runConfigurations/tmtcclient_Single_Command_Serial.xml
similarity index 87%
rename from .idea/runConfigurations/tmtcclient_Single_Command_Serial_.xml
rename to .idea/runConfigurations/tmtcclient_Single_Command_Serial.xml
index c59814017c5419b0cfa0d262228897e93ae23e4f..aa1a2f4b2ae631f47148b51f094fe3d17986b6e4 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" />
@@ -13,7 +13,7 @@
     <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="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_Trigger_Exceptions.xml b/.idea/runConfigurations/tmtcclient_Trigger_Exceptions.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d8483e91e0c5ee927bf310001149083e5b92467e
--- /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$/obsw_tmtc_client.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_Unlock_File.xml b/.idea/runConfigurations/tmtcclient_Unlock_File.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d484de6c0b86bb59356bcc78423c8267138bebed
--- /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$/obsw_tmtc_client.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..8613b8d723259f83a52b98fc2a01f8fe8253d24c 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" />
diff --git a/config/obsw_com_config.py b/config/obsw_com_config.py
index 66e5da5837784e1577612a39e0f19a1f4a01843a..a425ea64333295e7ff5af1a36c8999fdb6800384 100644
--- a/config/obsw_com_config.py
+++ b/config/obsw_com_config.py
@@ -40,9 +40,7 @@ def set_communication_interface(tmtc_printer: TmTcPrinter) -> Union[Communicatio
             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:
-            serial_timeout = g.G_SERIAL_TIMEOUT
-            communication_interface = QEMUComIF(
-                tmtc_printer=tmtc_printer, serial_timeout=serial_timeout)
+            communication_interface = QEMUComIF(tmtc_printer=tmtc_printer, serial_timeout=g.G_TM_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 7adbb6d2d0c9375c2fea57b8709141e91be630cc..30dbd15fd492fcdb6d0caac7b3fb3b5ca6c1d5ed 100644
--- a/config/obsw_config.py
+++ b/config/obsw_config.py
@@ -11,7 +11,6 @@ import struct
 import pprint
 import logging
 from socket import INADDR_ANY
-
 from config.obsw_definitions import ModeList, ComIF
 
 """
@@ -21,6 +20,9 @@ Mission/Device specific information.
 # TODO: Automate / Autofill this file with the MIB parser
 
 # Object IDs
+CORE_CONTROLLER_ID = bytearray([0x40, 0x00, 0x10, 0x00])
+SW_IMAGE_HANDLER_ID = bytearray([0x4D, 0x00, 0x90, 0x00])
+PUS_SERVICE_17 = bytearray([0x53, 0x00, 0x00, 0x17])
 GPS0_DEVICE_ID = bytearray([0x44, 0x10, 0x1F, 0x00])
 GPS1_DEVICE_ID = bytearray([0x44, 0x20, 0x20, 0x00])
 DUMMY_DEVICE_ID = bytearray([0x44, 0x00, 0xAF, 0xFE])
@@ -62,6 +64,7 @@ LOGGER = logging.getLogger(G_TMTC_LOGGER_NAME)
 G_SCRIPT_MODE = 1
 G_MODE_ID = 0
 G_SERVICE = 17
+G_OP_CODE = 0
 G_DISPLAY_MODE = "long"
 
 # General TMTC Settings
@@ -69,6 +72,7 @@ G_APID = 0x73
 
 # Binary Upload Settings
 G_MAX_BINARY_FRAME_LENGTH = 1500
+G_MAX_APP_DATA_LENGTH = G_MAX_BINARY_FRAME_LENGTH - 100
 
 G_COM_IF = 2
 # COM Port for serial communication
@@ -111,9 +115,11 @@ 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
+    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
+
     if args.shortDisplayMode:
         G_DISPLAY_MODE = "short"
     else:
@@ -149,6 +155,13 @@ def set_globals(args):
         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
@@ -157,6 +170,7 @@ 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
     global_setup_hook()
 
diff --git a/gui/obsw_backend_test.py b/gui/obsw_backend_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9e6c488a10a2a9c5050d4dd33f328f01d548abb
--- /dev/null
+++ b/gui/obsw_backend_test.py
@@ -0,0 +1,37 @@
+from multiprocessing.connection import Listener
+from multiprocessing import Process
+from tmtc_core.utility.obsw_logger import get_logger
+import logging
+
+
+LOGGER = get_logger()
+
+
+class TmTcBackend(Process):
+    def __init__(self):
+        from obsw_tmtc_client import TmTcHandler
+        super(TmTcBackend, self).__init__()
+        self.address = ('localhost', 6000)     # family is deduced to be 'AF_INET'
+        self.tmtc_backend = TmTcHandler()
+        self.listener = Listener(self.address, authkey=None)
+        self.conn = 0
+
+    def run(self):
+        self.listen()
+
+    def listen(self):
+        self.conn = self.listener.accept()
+        LOGGER.info("TmTcBackend: Connection accepted from %s", str(self.listener.last_accepted))
+        while True:
+            msg = self.conn.recv()
+            # do something with msg
+            # here, the core client could be called to perform operations based on received message
+            if msg == 'test':
+                LOGGER.info("TmTcBackend: Hallo Welt !")
+            elif msg == 'close':
+                try:
+                    self.conn.close()
+                    break
+                except OSError:
+                    logging.exception("Error: ")
+        self.listener.close()
diff --git a/gui/obsw_tmtc_gui.py b/gui/obsw_tmtc_gui.py
index ba7985fc3334f6542fb8189b500634622214dfb0..ae29984ce4108a75a6bad1437319a08cf624390b 100644
--- a/gui/obsw_tmtc_gui.py
+++ b/gui/obsw_tmtc_gui.py
@@ -22,6 +22,7 @@ import threading
 
 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)
diff --git a/obsw_tmtc_client.py b/obsw_tmtc_client.py
index dbeb01f8a83d69673fcd341a69df26f2d316d954..379a3cd280c22d56aac8e8177ec7bfa92525d4ba 100755
--- a/obsw_tmtc_client.py
+++ b/obsw_tmtc_client.py
@@ -48,7 +48,7 @@ from tmtc_core.utility.obsw_logger import set_tmtc_logger, get_logger
 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 utility.obsw_binary_uploader import perform_file_upload
 
 from obsw_user_code import command_preparation_hook
 import gui.obsw_tmtc_gui
@@ -151,11 +151,11 @@ 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.tm_listener.clear_reply_event()
             self.command_received = True
 
         elif self.mode == g.ModeList.SingleCommandMode:
@@ -176,7 +176,9 @@ class TmTcHandler:
         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")
@@ -200,7 +202,7 @@ 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()
+            perform_file_upload(self.communication_interface, self.tmtc_printer, self.tm_listener)
             self.command_received = True
             self.mode = g.ModeList.ListenerMode
 
diff --git a/requirements.txt b/requirements.txt
index 4a3f79dd0651dd1e85c8e72f74264a7f5a6cecde..4ac62a18eeebe75e7db2b6cc16666ea926ccad88 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
 aiohttp==3.6.2
 astroid==2.4.2
 async-timeout==3.0.1
-attrs==19.3.0
+attrs==20.2.0
 chardet==3.0.4
 colorama==0.4.3
 cpplint==1.5.4
@@ -9,18 +9,18 @@ crcmod>=1.7
 docopt==0.6.2
 future==0.18.2
 idna==2.10
-iso8601==0.1.12
-isort==5.4.2
+iso8601==0.1.13
+isort==5.6.3
 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
+pylint>=2.6.0
+PyQt5>=5.15.1
+PyQt5-sip>=12.8.1
 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
+typing-extensions==3.7.4.3
+wrapt==1.12.1
+yarl==1.6.0
diff --git a/tc/obsw_image_handler.py b/tc/obsw_image_handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f96ad68b051046aea733aa89bb2321f9c792f3d
--- /dev/null
+++ b/tc/obsw_image_handler.py
@@ -0,0 +1,37 @@
+from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand, Deque
+from tc.obsw_tc_service8 import make_action_id
+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_img_handler_packet(service_queue: Deque, op_code: int):
+    # 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())
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..52152688dad6933afa6fa76d717a9be147d9f736 100644
--- a/tc/obsw_pus_tc_packer.py
+++ b/tc/obsw_pus_tc_packer.py
@@ -14,11 +14,13 @@ 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_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 +34,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 +47,28 @@ 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() == "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 !")
 
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..24ce024e069c49f1b22d4b9c8dfb86ab6bbfa12e 100644
--- a/tc/obsw_tc_service20.py
+++ b/tc/obsw_tc_service20.py
@@ -14,7 +14,7 @@ from tc.obsw_tc_service200 import pack_mode_data
 
 
 def pack_service20_test_into(tc_queue: Deque, called_externally: bool = False) -> Deque:
-    #parameter IDs
+    # parameter IDs
     parameterID0 = 0
     parameterID1 = 1
     parameterID2 = 2
@@ -29,33 +29,33 @@ 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 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
     tc_queue.appendleft(("print", "Testing Service 20: Load uint32_t"))
     parameter_id = struct.pack(">I", parameterID0)
     parameter_data = struct.pack(">I", 42)
@@ -63,7 +63,7 @@ def pack_service20_test_into(tc_queue: Deque, called_externally: bool = False) -
     command = PusTelecommand(service=20, subservice=128, ssc=2001, 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
@@ -101,9 +101,6 @@ def pack_service20_test_into(tc_queue: Deque, called_externally: bool = False) -
     tc_queue.appendleft(command.pack_command_tuple())
 """
 
-
-
-
 """
     # set mode on
     tc_queue.appendleft(("print", "Testing Service 8: Set On Mode"))
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..ccd07d808c1b253838c2d5e3e4b9322b88c0fdbf
--- /dev/null
+++ b/tc/obsw_tc_service23_sdcard.py
@@ -0,0 +1,584 @@
+# -*- coding: utf-8 -*-
+"""
+Created: 21.01.2020 07:48
+
+@author: Jakob Meier
+"""
+import math
+from enum import Enum
+
+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()
+
+
+class FileTransferHelper:
+    """
+    This helper class fills the provided TC queue with appropriate PUS telecommands
+    to transfer a large 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=g.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.__transfer_mode = self.TransferMode.NORMAL
+        self.max_size_of_app_data = max_size_of_app_data
+        self.__max_file_data_size = 0
+        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.__renamed_name = self.target_filename + "old"
+        self.object_id = object_id
+        self.tc_queue = tc_queue
+
+        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.__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):
+        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):
+        if self.__transfer_mode == self.TransferMode.DELETE_OLD:
+            command = generate_rm_file_srv23_2_packet(self.target_filename, self.target_repository,
+                                                      ssc, self.object_id)
+            self.__number_of_delete_packets = 1
+            ssc += 1
+            self.tc_queue.appendleft(command.pack_command_tuple())
+        elif self.__transfer_mode == self.TransferMode.RENAME_OLD:
+            # not implemented yet
+            pass
+        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]
+        else:
+            # Small file, one packet for file creation sufficient
+            command = generate_create_file_srv23_1_packet(
+                self.target_filename, self.target_repository, ssc, self.max_size_of_app_data,
+                self.__file_data)
+            ssc += 1
+            self.tc_queue.appendleft(command.pack_command_tuple())
+            return
+
+        # Create large file.
+        command = generate_create_file_srv23_1_packet(
+            self.target_filename, self.target_repository, ssc, self.max_size_of_app_data,
+            init_data)
+        ssc += 1
+        self.tc_queue.appendleft(command.pack_command_tuple())
+        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=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=ssc)
+        ssc += 1
+        last_command = generate_finish_append_to_file_srv23_131_packet(
+            filename=self.target_filename, repository_path=self.target_repository,
+            ssc=ssc, lock_file=self.__lock_file)
+        self.tc_queue.appendleft(last_command.pack_command_tuple())
+        self.__number_of_packets = \
+            self.__number_of_append_packets + self.__number_of_create_packets + \
+            self.__number_of_delete_packets + 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 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..9166214399eed0fa485d9d471cf1d6ee358da9bb 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,7 @@ 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 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/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/tm/obsw_pus_tm_factory_hook.py b/tm/obsw_pus_tm_factory_hook.py
index 94899de4acb68c275049ca88e28cd29c6be22a96..6effae10a4852b37511e9a0d243dc7351e9e5dcf 100644
--- a/tm/obsw_pus_tm_factory_hook.py
+++ b/tm/obsw_pus_tm_factory_hook.py
@@ -3,6 +3,7 @@ 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
+from tm.obsw_tm_service_23 import Service23TM
 import struct
 
 LOGGER = get_logger()
@@ -24,6 +25,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")
@@ -141,4 +144,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_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/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..d226d02405b4af3d6c655357e3467b7c63683d8c 100644
--- a/utility/obsw_args_parser.py
+++ b/utility/obsw_args_parser.py
@@ -18,12 +18,15 @@ 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")
@@ -38,7 +41,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..7f27a9e3f6255ef4cac7671785d2af0ee00f9e31 100644
--- a/utility/obsw_binary_uploader.py
+++ b/utility/obsw_binary_uploader.py
@@ -6,20 +6,67 @@ 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 tmtc_core.comIF.obsw_com_interface import CommunicationInterface
+from tc.obsw_tc_service23_sdcard 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
+from tmtc_core.tm.obsw_pus_tm_factory import PusTmQueueT
 
+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]")
+
+def perform_file_upload(com_if: CommunicationInterface, tmtc_printer: TmTcPrinter,
+                        tm_listener: TmListener):
+    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
+    # TODO: prompt whether this is a binary upload or a normal file upload. Or use op code
+    #       two different commands to achieve the same.
+    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]: ")
+
+    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")
@@ -27,13 +74,143 @@ def perform_binary_upload():
         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
+    iobc_prompt = input("iOBC? [y/n]: ")
+    if iobc_prompt in ['y', 'yes', 1]:
+        iobc_prompt = True
+    else:
+        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"
+
+    # 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
 
-    # We have to split the binary here first
+    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
+    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)
+
+    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)
+    interval = 0.6
+    last_sent = time.time()
+    total_time = interval * total_num_packets
+    idx = 1
+    reception_deque: PusTmQueueT = deque()
+    while tc_queue:
+        next_send = last_sent + 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
+            com_if.send_telecommand(tc_packet, tc_info)
+            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) * interval, 2)) + \
+                                " seconds"
+        print_progress_bar(idx - 2, total_num_packets, print_end="\n", suffix=remaining_time_string)
+        # sys.stdout.write("\033[F")  # Cursor up one line
+        reception_deque.extend(tm_listener.retrieve_tm_packet_queue())
+        time_to_sleep = next_send - time.time()
+        last_sent = next_send
+        time.sleep(time_to_sleep)
+
+    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(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
+    tm_listener.clear_tm_packet_queue()
+    LOGGER.info("Transitioning back to listener mode..")
+
 
+# 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()