diff --git a/.idea/runConfigurations/tmtcclient_Hamming_from_FRAM.xml b/.idea/runConfigurations/tmtcclient_Hamming_from_FRAM.xml
new file mode 100644
index 0000000000000000000000000000000000000000..86e6dbb681b37ef6a8873140acc9b2a2350e3f0e
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Hamming_from_FRAM.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Hamming from FRAM" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="PARAMETERS" value="-m 3 -c 1 -s Img -o P1 -t 3" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/tmtcclient_Hamming_from_SD.xml b/.idea/runConfigurations/tmtcclient_Hamming_from_SD.xml
new file mode 100644
index 0000000000000000000000000000000000000000..43c2bce795ed4979b42d4bca37d72c9163c3dfcc
--- /dev/null
+++ b/.idea/runConfigurations/tmtcclient_Hamming_from_SD.xml
@@ -0,0 +1,24 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="tmtcclient Hamming from SD" type="PythonConfigurationType" factoryName="Python" folderName="Serial SD-Card and Image">
+    <module name="tmtc" />
+    <option name="INTERPRETER_OPTIONS" value="" />
+    <option name="PARENT_ENVS" value="true" />
+    <envs>
+      <env name="PYTHONUNBUFFERED" value="1" />
+    </envs>
+    <option name="SDK_HOME" value="" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+    <option name="IS_MODULE_SDK" value="true" />
+    <option name="ADD_CONTENT_ROOTS" value="true" />
+    <option name="ADD_SOURCE_ROOTS" value="true" />
+    <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
+    <option name="SCRIPT_NAME" value="$PROJECT_DIR$/obsw_tmtc_client.py" />
+    <option name="PARAMETERS" value="-m 3 -c 1 -s Img -o P0 -t 3" />
+    <option name="SHOW_COMMAND_LINE" value="false" />
+    <option name="EMULATE_TERMINAL" value="true" />
+    <option name="MODULE_MODE" value="false" />
+    <option name="REDIRECT_INPUT" value="false" />
+    <option name="INPUT_FILE" value="" />
+    <method v="2" />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/obsw_tmtc_client.py b/obsw_tmtc_client.py
index e502717fda8cd2ba2db5c963c394fec2eec3ea43..600db5aef0cd0e10ae9f4602259a7a36c595a72e 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_file_upload
+from utility.obsw_binary_uploader import BinaryFileUploader
 
 from gui.obsw_tmtc_gui import TmTcGUI
 from gui.obsw_backend_test import TmTcBackend
@@ -205,7 +205,9 @@ class TmTcHandler:
         elif self.mode == g.ModeList.BinaryUploadMode:
             # Upload binary, prompt user for input, in the end prompt for new mode and enter that
             # mode
-            perform_file_upload(self.communication_interface, self.tmtc_printer, self.tm_listener)
+            file_uploader = BinaryFileUploader(self.communication_interface, self.tmtc_printer,
+                                               self.tm_listener)
+            file_uploader.perform_file_upload()
             self.command_received = True
             self.mode = g.ModeList.ListenerMode
 
diff --git a/tc/obsw_image_handler.py b/tc/obsw_image_handler.py
index 5f96ad68b051046aea733aa89bb2321f9c792f3d..be5fd0b174ac405a9acc625997dd6f74273cc038 100644
--- a/tc/obsw_image_handler.py
+++ b/tc/obsw_image_handler.py
@@ -1,5 +1,6 @@
 from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand, Deque
 from tc.obsw_tc_service8 import make_action_id
+from tc.obsw_tc_service20 import pack_boolean_parameter_setting
 from tmtc_core.utility.obsw_logger import get_logger
 from config.obsw_config import SW_IMAGE_HANDLER_ID
 
@@ -23,15 +24,29 @@ def generate_copy_obsw_sdc_to_flash_packet(ssc: int, slot: int,
     return PusTelecommand(service=8, subservice=128, ssc=ssc, app_data=app_data)
 
 
+def generate_param_packet_hamming_from_sdcard(enable: bool, ssc: int,
+                                              object_id: bytearray = SW_IMAGE_HANDLER_ID):
+    return pack_boolean_parameter_setting(object_id=object_id, domain_id=0, unique_id=0,
+                                          parameter=enable, ssc=ssc)
+
+
 def generate_img_handler_packet(service_queue: Deque, op_code: 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())
+    elif op_code == "P0":
+        service_queue.appendleft(("print", "Configuring hamming code to be taken from SD card"))
+        command = generate_param_packet_hamming_from_sdcard(enable=True, ssc=0)
+        service_queue.appendleft(command.pack_command_tuple())
+    elif op_code == "P1":
+        service_queue.appendleft(("print", "Configuring hamming code to be taken from FRAM"))
+        command = generate_param_packet_hamming_from_sdcard(enable=False, ssc=0)
+        service_queue.appendleft(command.pack_command_tuple())
+
diff --git a/tc/obsw_tc_service20.py b/tc/obsw_tc_service20.py
index 24ce024e069c49f1b22d4b9c8dfb86ab6bbfa12e..34bdd32cdd17e2fedac76da8436c449e1a2c6623 100644
--- a/tc/obsw_tc_service20.py
+++ b/tc/obsw_tc_service20.py
@@ -9,9 +9,46 @@ import struct
 from typing import Deque
 
 import config.obsw_config as g
-from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand
+from tmtc_core.tc.obsw_pus_tc_base import PusTelecommand, TcQueueT
+from tmtc_core.utility.obsw_logger import get_logger
 from tc.obsw_tc_service200 import pack_mode_data
 
+LOGGER = get_logger()
+
+
+def pack_boolean_parameter_setting(object_id: bytearray, domain_id: int,
+                                   unique_id: int, parameter: bool, ssc: int):
+    """
+    Generic function to pack a telecommand to tweak a boolean parameter
+    @param object_id:
+    @param domain_id:
+    @param unique_id:
+    @param parameter:
+    @param ssc:
+    @return:
+    """
+    parameter_id = bytearray(4)
+    parameter_id[0] = domain_id
+    if unique_id > 255:
+        LOGGER.warning("Invalid unique ID, should be smaller than 255!")
+        return
+    parameter_id[1] = unique_id
+    parameter_id[2] = 0
+    parameter_id[3] = 0
+    data_to_pack = bytearray(object_id)
+    data_to_pack.extend(parameter_id)
+    # PTC and PFC for uint8_t according to CCSDS
+    ptc = 3
+    pfc = 4
+    rows = 1
+    columns = 1
+    data_to_pack.append(ptc)
+    data_to_pack.append(pfc)
+    data_to_pack.append(rows)
+    data_to_pack.append(columns)
+    data_to_pack.append(parameter)
+    return PusTelecommand(service=20, subservice=128, ssc=ssc, app_data=data_to_pack)
+
 
 def pack_service20_test_into(tc_queue: Deque, called_externally: bool = False) -> Deque:
     # parameter IDs
@@ -75,6 +112,11 @@ def pack_service20_test_into(tc_queue: Deque, called_externally: bool = False) -
     return tc_queue
 
 
+def pack_service23_commands_into(tc_queue: TcQueueT, op_code: int):
+    if op_code == 0:
+        pack_service20_test_into(tc_queue=tc_queue)
+
+
 """
     #test checking Load for int32_t
     tc_queue.appendleft(("print", "Testing Service 20: Load int32_t"))
diff --git a/tc/obsw_tc_service23_sdcard.py b/tc/obsw_tc_service23_sdcard.py
index ccd07d808c1b253838c2d5e3e4b9322b88c0fdbf..b89ae1bfb3f10dc9cd95af6f9d92ed503f2e20a0 100644
--- a/tc/obsw_tc_service23_sdcard.py
+++ b/tc/obsw_tc_service23_sdcard.py
@@ -4,9 +4,6 @@ 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
 
@@ -17,206 +14,6 @@ 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)
diff --git a/tmtc_core b/tmtc_core
index 233383842fdaca1480d25ce4568da54e8305ffaa..228d57103721942918f38b54e0b5e84f3acae427 160000
--- a/tmtc_core
+++ b/tmtc_core
@@ -1 +1 @@
-Subproject commit 233383842fdaca1480d25ce4568da54e8305ffaa
+Subproject commit 228d57103721942918f38b54e0b5e84f3acae427
diff --git a/utility/obsw_binary_uploader.py b/utility/obsw_binary_uploader.py
index 7f27a9e3f6255ef4cac7671785d2af0ee00f9e31..add1696883cd2dad5bd42d1069432505234b3205 100644
--- a/utility/obsw_binary_uploader.py
+++ b/utility/obsw_binary_uploader.py
@@ -12,183 +12,208 @@ import tkinter as tk
 from tkinter import filedialog
 from collections import deque
 from glob import glob
+from typing import Deque
 
 from tmtc_core.comIF.obsw_com_interface import CommunicationInterface
-from tc.obsw_tc_service23_sdcard import FileTransferHelper
+from utility.obsw_file_transfer_helper import FileTransferHelper
 import config.obsw_config as g
 from tmtc_core.utility.obsw_tmtc_printer import TmTcPrinter, DisplayMode
 from tmtc_core.utility.obsw_logger import get_logger
 from tmtc_core.sendreceive.obsw_tm_listener import TmListener
-from tmtc_core.tm.obsw_pus_tm_factory import PusTmQueueT
 
 LOGGER = get_logger()
 
 
-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..")
+class BinaryFileUploader:
+    def __init__(self, com_if: CommunicationInterface, tmtc_printer: TmTcPrinter,
+                 tm_listener: TmListener):
+        """
+        Initializes the binary file uploader with the required components.
+        @param com_if:
+        @param tmtc_printer:
+        @param tm_listener:
+        """
+        self.com_if = com_if
+        self.tmtc_printer = tmtc_printer
+        self.tm_listener = tm_listener
+
+    def perform_file_upload(self):
+        gui_cl_prompt = input("GUI(0) or command line version (1)? [0/1]: ")
+        if gui_cl_prompt == 0:
+            gui_cl_prompt = True
+        else:
+            gui_cl_prompt = False
+        print("Please select file to upload: ")
+        file_path = ""
+        if gui_cl_prompt:
+            root = tk.Tk()
+            root.withdraw()
+            root.wm_attributes('-topmost', 1)
+            file_path = filedialog.askopenfilename(parent=root)
+            print("File select: " + str(file_path))
+            if file_path == ():
+                LOGGER.warning("Invalid file path, exiting binary upload mode.")
                 return
-            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:
+        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")
+        else:
+            calc_hamming_code = False
+            print("Hamming code will not be calculated")
 
-    print_string = file_path.rsplit(os.path.sep, 1)[-1] + " was selected."
-    LOGGER.info(print_string)
-    calc_hamming_code = input("Calculate and send hamming code? [y/n]: ")
-    if calc_hamming_code in ['y', 'yes', 1]:
-        calc_hamming_code = True
-        print("Hamming code will be calculated and sent in tail packet")
-    else:
-        calc_hamming_code = False
-        print("Hamming code will not be calculated")
-
-    iobc_prompt = input("iOBC? [y/n]: ")
-    if iobc_prompt in ['y', 'yes', 1]:
-        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"
+        iobc_prompt = input("iOBC? [y/n]: ")
+        if iobc_prompt in ['y', 'yes', 1]:
+            iobc_prompt = True
         else:
-            repository_name = "BIN/IOBC/OBSW"
-    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:
-            repository_name = "BIN/AT91/BL"
+            file_name = "bl.bin"
+        else:
+            file_name = "obsw_up.bin"
+
+        if iobc_prompt:
+            if bootloader_prompt:
+                repository_name = "BIN/IOBC/BL"
+            else:
+                repository_name = "BIN/IOBC/OBSW"
+        else:
+            if bootloader_prompt:
+                repository_name = "BIN/AT91/BL"
+            else:
+                repository_name = "BIN/AT91/OBSW"
+
+        if calc_hamming_code:
+            pass
+
+        # Right now, the size of PUS packets is limited to 1500 bytes which also limits the app
+        # data length.
+        frame_length = g.G_MAX_APP_DATA_LENGTH
+
+        if calc_hamming_code:
+            # now we calculate the hamming code
+            pass
+
+        tc_queue = deque()
+
+        # Delete existing binary file first, otherwise data might be appended to otherwise
+        # valid file which already exists.
+        file_transfer_helper = FileTransferHelper(
+            tc_queue=tc_queue, max_size_of_app_data=frame_length, target_repository=repository_name,
+            target_filename=file_name)
+
+        init_ssc = 0
+        self.tmtc_printer.set_display_mode(DisplayMode.SHORT)
+
+        # Configure file transfer helper
+        file_transfer_helper.set_data_from_file(file_path)
+        file_transfer_helper.set_to_delete_old_file()
+        if prompt_lock:
+            file_transfer_helper.set_to_lock_file(prompt_lock)
+        else:
+            file_transfer_helper.set_to_lock_file(prompt_lock)
+
+        # Generate the packets.
+        file_transfer_helper.generate_packets(init_ssc)
+
+        self.tm_listener.set_listener_mode(TmListener.ListenerModes.MANUAL)
+        print_string = "BinaryUploader: Detected file size: " + str(
+            file_transfer_helper.file_size())
+        LOGGER.info(print_string)
+        total_num_packets = file_transfer_helper.get_number_of_packets_generated()
+        print_string = "BinaryUploader: " + str(total_num_packets) + \
+                       " packets generated."
+        if prompt_lock:
+            print_string += " File will be locked."
         else:
-            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
-
-    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..")
+            print_string += " File will not be locked."
+        LOGGER.info(print_string)
+
+        reception_deque = deque()
+        self.__perform_send_algorithm(tc_queue, total_num_packets, reception_deque)
+
+        print_string = "BinaryUploader: All binary packets were sent!"
+        LOGGER.info(print_string)
+        print_string = str(reception_deque.__len__()) + " replies received."
+
+        LOGGER.info(print_string)
+        time.sleep(15)
+        reception_deque.extend(self.tm_listener.retrieve_tm_packet_queue())
+        for tm_list in reception_deque:
+            for tm_packet in tm_list:
+                if tm_packet.get_service() == 23 and tm_packet.get_subservice() == 132:
+                    # tmtc_printer.print_telemetry(tm_packet)
+                    pass
+        self.tm_listener.clear_tm_packet_queue()
+        LOGGER.info("Transitioning back to listener mode..")
+
+    def __perform_send_algorithm(self, tc_queue: Deque, number_of_packets: int, reception_deque:
+                                 Deque):
+        interval = 0.6
+        last_check = time.time()
+        last_sent = time.time()
+        total_time = interval * number_of_packets
+        idx = 1
+        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
+                self.com_if.send_telecommand(tc_packet, tc_info)
+                self.tmtc_printer.print_telecommand(tc_packet, tc_info)
+            elif tc_packet == "print":
+                LOGGER.info(tc_info)
+            remaining_time_string = "Remaining time: " + \
+                                    str(round(total_time - (idx - 2) * interval, 2)) + \
+                                    " seconds"
+            print_progress_bar(idx - 2, number_of_packets, print_end="\n",
+                               suffix=remaining_time_string)
+            # sys.stdout.write("\033[F")  # Cursor up one line
+            packets_received = self.tm_listener.retrieve_tm_packet_queue()
+            reception_deque.extend(packets_received)
+            # Every 5 seconds, check whether any reply has been received. If not, cancel operation.
+            if time.time() - last_check > 5.0 and len(packets_received) == 0:
+                LOGGER.warning("No replies are being received, cancelling upload operation..")
+            time_to_sleep = next_send - time.time()
+            last_sent = next_send
+            time.sleep(time_to_sleep)
 
 
 # https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console
diff --git a/utility/obsw_file_transfer_helper.py b/utility/obsw_file_transfer_helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6efb90e7e15cf98b494e9c895f9137b88107c6f
--- /dev/null
+++ b/utility/obsw_file_transfer_helper.py
@@ -0,0 +1,241 @@
+from enum import Enum
+import math
+
+from config.obsw_config import SD_CARD_HANDLER_ID
+from tmtc_core.tc.obsw_pus_tc_base import TcQueueT, PusTelecommand
+from tc.obsw_tc_service23_sdcard import \
+    calculate_allowed_file_data_size, generate_rm_file_srv23_2_packet, \
+    generate_create_file_srv23_1_packet, generate_finish_append_to_file_srv23_131_packet, \
+    generate_lock_file_srv23_5_6_packet
+
+
+class FileTransferHelper:
+    """
+    This helper class fills the provided TC queue with appropriate PUS telecommands
+    to transfer a file.
+    There are three modes which determine which telecommands will be generated:
+    1. NORMAL:      Generate telecommand to create a new file and append data packets if
+                    the file data is too large. This will be the default mode.
+    2. DELETE_OLD:  Generate telecommand to delete old file and then perform same steps as the
+                    normal mode
+    3. RENAME_OLD:  Rename old file and then perform same steps as in normal mode.
+
+    Please note that the setter functions set_data have to be used to assign data, otherwise
+    an empty file will be created. The mode is set with setter commands as well.
+    """
+    class TransferMode(Enum):
+        # Normal mode
+        NORMAL = 1
+        # Generate a command to delete the old file first
+        DELETE_OLD = 2
+        # Generate a command to rename the old file first.
+        RENAME_OLD = 3
+
+    def __init__(self, tc_queue: TcQueueT, max_size_of_app_data: int,
+                 target_repository: str, target_filename: str,
+                 object_id=SD_CARD_HANDLER_ID):
+        """
+        @param tc_queue: TC queue which will be filled
+        @param max_size_of_app_data: Maximum allowed app data size. Number of generated packets
+        will depend on this value
+        @param target_repository: Repository path on target.
+        @param target_filename: Filename on target
+        @param object_id:
+        """
+        self.object_id = object_id
+        self.max_size_of_app_data = max_size_of_app_data
+        self.allowed_file_data_size = calculate_allowed_file_data_size(
+            max_size_of_app_data, target_filename, target_repository)
+
+        self.target_filename = target_filename
+        self.target_repository = target_repository
+
+        self.tc_queue = tc_queue
+
+        self.__transfer_mode = self.TransferMode.NORMAL
+        self.__max_file_data_size = 0
+        self.__renamed_name = self.target_filename + "old"
+
+        self.__large_file = False
+        self.__number_of_packets = 0
+        self.__number_of_append_packets = 0
+        self.__number_of_create_packets = 1
+        self.__number_of_delete_packets = 0
+        self.__number_of_finish_packets = 1
+
+        self.__current_ssc = 0
+        self.__lock_file = True
+        self.__local_filename = ""
+        self.__file_data = bytearray()
+        # This will generate a telecommand to delete the old file, if it exists
+        self.delete_old_file = False
+        # This will generater a telecommand to rename the old file, if it exists
+        self.rename_old_file = False
+
+    def set_data_from_file(self, local_filename: str):
+        with open(local_filename, 'rb') as file:
+            self.__file_data = file.read()
+
+    def set_data_raw(self, tc_data: bytearray):
+        self.__file_data = tc_data
+
+    def set_to_delete_old_file(self):
+        self.__transfer_mode = self.TransferMode.DELETE_OLD
+
+    def set_to_rename_old_file(self, renamed_name: str):
+        self.__transfer_mode = self.TransferMode.RENAME_OLD
+        self.__renamed_name = renamed_name
+
+    def set_to_lock_file(self, lock_file: bool):
+        """
+        Command will be sent to lock file after succesfull transfer
+        @param lock_file:
+        @return:
+        """
+        self.__lock_file = lock_file
+
+    def get_number_of_packets_generated(self):
+        return self.__number_of_packets
+
+    def set_max_file_data_size(self, max_file_data_size: int):
+        """
+        If this value is specified and the source file is large (larger than the maximum allowed
+        app data!), the file data size will be set to this value.
+        @param max_file_data_size:
+        @return:
+        """
+        self.__max_file_data_size = max_file_data_size
+
+    def file_size(self):
+        return len(self.__file_data)
+
+    def generate_packets(self, ssc: int):
+        """
+        Main function to generate all packets and fill them into the provided deque.
+        @param ssc:
+        @return:
+        """
+        self.__current_ssc = ssc
+        self.__handle_delete_packet_generation()
+        if self.__transfer_mode == self.TransferMode.RENAME_OLD:
+            # not implemented yet
+            pass
+        self.__handle_create_file_packet_generation()
+        self.__handle_finish_and_lock_packet_generation()
+        self.__number_of_packets = \
+            self.__number_of_create_packets + self.__number_of_append_packets + \
+            self.__number_of_delete_packets + self.__number_of_finish_packets
+
+    def __handle_delete_packet_generation(self):
+        if self.__transfer_mode == self.TransferMode.DELETE_OLD:
+            command = generate_rm_file_srv23_2_packet(
+                filename=self.target_filename, repository_path=self.target_repository,
+                ssc=self.__current_ssc, object_id=self.object_id)
+            self.__number_of_delete_packets = 1
+            self.__current_ssc += 1
+            self.tc_queue.appendleft(command.pack_command_tuple())
+
+    def __handle_create_file_packet_generation(self):
+        if len(self.__file_data) > self.allowed_file_data_size:
+            # Large file, create file with init_data
+            if self.__max_file_data_size > 0:
+                init_data = self.__file_data[0:self.__max_file_data_size]
+            else:
+                init_data = self.__file_data[0:self.allowed_file_data_size]
+            self.__large_file = True
+        else:
+            init_data = self.__file_data
+
+        # Create file.
+        command = generate_create_file_srv23_1_packet(
+            self.target_filename, self.target_repository, ssc=self.__current_ssc,
+            max_size_of_app_data=self.max_size_of_app_data, initial_data=init_data)
+        self.__current_ssc += 1
+        self.tc_queue.appendleft(command.pack_command_tuple())
+        if not self.__large_file:
+            return
+        rest_of_data = self.__file_data[self.allowed_file_data_size:]
+        # Generate the rest of the packets to write to large file
+        if self.__max_file_data_size > 0:
+            self.__generate_append_to_file_packets_automatically(
+                data=rest_of_data, target_repository=self.target_repository,
+                target_filename=self.target_filename, size_of_data_blocks=self.__max_file_data_size,
+                init_ssc=self.__current_ssc)
+        else:
+            self.__generate_append_to_file_packets_automatically(
+                data=rest_of_data, target_repository=self.target_repository,
+                target_filename=self.target_filename, size_of_data_blocks=self.max_size_of_app_data,
+                init_ssc=self.__current_ssc)
+        self.__current_ssc += 1
+
+    def __generate_append_to_file_packets_automatically(
+            self, data: bytearray, target_repository: str, target_filename: str,
+            size_of_data_blocks: int, init_ssc: int):
+        """
+        This function generates PUS packets which is used to write data in a file.
+        A new file will be created if not already existing. If the file already exists, this might
+        lead to
+
+        If the file data is larger than the maximum allowed size of application data, this function
+        will split the data into multiple packets and increment the initial SSC number by one for
+        each packet.
+        @param data: Data which will be split up.
+        @param init_ssc: First SSC, which will be incremented for each packet.
+        """
+        header = bytearray(self.object_id)
+        header += target_repository.encode('utf-8')
+        # Add string terminator of repository path
+        header.append(0)
+        header += target_filename.encode('utf-8')
+        # Add string terminator of filename
+        header.append(0)
+        self.__split_large_file(header, size_of_data_blocks, data, init_ssc)
+
+    def __split_large_file(self, header: bytearray, size_of_data_blocks: int,
+                           data: bytearray, init_ssc: int):
+        """
+        This function splits a large file in multiple packets and packs the generated packets
+        into the member deque. This is necessary because the packet size is limited.
+        @param header: Repository and file name which will always stay the same
+        @param size_of_data_blocks: The file data blocks will have this size
+        @param data: The data to pack in multiple packets
+        @param init_ssc: The ssc of the first command, will be incremented by one for each packet.
+        """
+        number_of_packets = math.floor(len(data) / size_of_data_blocks)
+        packet_sequence_number = 0
+
+        for i in range(number_of_packets):
+            header.append(packet_sequence_number >> 8)
+            header.append(0xFF & packet_sequence_number)
+            header += data[i * size_of_data_blocks:(i + 1) * size_of_data_blocks]
+
+            commands = PusTelecommand(service=23, subservice=130, ssc=init_ssc + i,
+                                      app_data=header)
+            self.tc_queue.appendleft(commands.pack_command_tuple())
+
+            # Remove everything except the header
+            header = header[:len(header) - size_of_data_blocks - 2]
+            packet_sequence_number = packet_sequence_number + 1
+        # Last packet will be subservice 131 to finish  the append operation
+        header.append(packet_sequence_number >> 8)
+        header.append(0xFF & packet_sequence_number)
+        self.__number_of_append_packets += number_of_packets
+        header += data[number_of_packets * size_of_data_blocks:len(data)]
+        commands = PusTelecommand(service=23, subservice=130, ssc=init_ssc + packet_sequence_number,
+                                  app_data=header)
+        self.tc_queue.appendleft(commands.pack_command_tuple())
+
+    def __handle_finish_and_lock_packet_generation(self):
+        if self.__large_file:
+            last_command = generate_finish_append_to_file_srv23_131_packet(
+                filename=self.target_filename, repository_path=self.target_repository,
+                ssc=self.__current_ssc, lock_file=self.__lock_file)
+        else:
+            if self.__lock_file:
+                last_command = generate_lock_file_srv23_5_6_packet(
+                    filename=self.target_filename, repository_path=self.target_repository,
+                    object_id=self.object_id, lock=True, ssc=self.__current_ssc)
+            else:
+                self.__number_of_finish_packets = 0
+                return
+        self.tc_queue.appendleft(last_command.pack_command_tuple())
\ No newline at end of file