diff --git a/tc/obsw_pus_tc_packer.py b/tc/obsw_pus_tc_packer.py
index af185553ca87016c31d7cffbc6bad8809a612d85..1beef55662423d3e3e352a7f3b9a626fa8700050 100644
--- a/tc/obsw_pus_tc_packer.py
+++ b/tc/obsw_pus_tc_packer.py
@@ -14,6 +14,7 @@ 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_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_tc_gps import pack_gps_test_into
@@ -38,6 +39,8 @@ def pack_service_queue(service: Union[int, str], service_queue: TcQueueT):
         return pack_service9_test_into(service_queue)
     if service == 17:
         return pack_service17_test_into(service_queue)
+    if service == 23:
+        return pack_service23_test_into(service_queue)
     if service == 200:
         return pack_service200_test_into(service_queue)
     if service == "Dummy":
diff --git a/tc/obsw_tc_service23.py b/tc/obsw_tc_service23.py
new file mode 100644
index 0000000000000000000000000000000000000000..636eb319612d529ce4bd62c27872ea0a491fc474
--- /dev/null
+++ b/tc/obsw_tc_service23.py
@@ -0,0 +1,272 @@
+# -*- coding: utf-8 -*-
+"""
+Created: 21.01.2020 07:48
+
+@author: Jakob Meier
+"""
+import math
+from collections import deque
+import time
+
+from tc.obsw_pus_tc_packer import TcQueueT, 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. SDCardHandler, PLOCHandler)
+            repositoryPath(str): The directory of the file
+            filename(str): The name of the file (e.g. boot.bin)
+            fileData(bytearray): The data to write to the file
+
+    """
+
+    fileData = bytearray()
+
+    def __init__(self, repositoryPath, filename, objectID=bytearray([]), fileData=bytearray([])):
+        self.fileData = fileData
+        self.dataToPack = bytearray()
+        self.dataToPack.append(objectID[0])
+        self.dataToPack.append(objectID[1])
+        self.dataToPack.append(objectID[2])
+        self.dataToPack.append(objectID[3])
+        repositoryPathLength_H = len(bytearray(repositoryPath, 'utf-8')) >> 8
+        repositoryPathLength_L = 0x00FF & len(bytearray(repositoryPath, 'utf-8'))
+        self.dataToPack.append(repositoryPathLength_H)
+        self.dataToPack.append(repositoryPathLength_L)
+        if(repositoryPath != ""):
+            self.dataToPack += bytearray(repositoryPath, 'utf-8')
+        self.dataToPack.append(len(bytearray(filename, 'utf-8')))
+        self.dataToPack += bytearray(filename, 'utf-8')
+        self.pusPackets = self.splitLargeFile(236)
+
+    def splitLargeFile(self, sizeOfDataBlocks):
+        """ This function splits a large file in multiple packets
+            This is necessary because the packet size is limited
+
+        :param sizeOfDataBlocks: The file is splitted in data blocks of this size
+        :return: List containing the PUS packets generated for each data block
+        """
+        self.numberOfPackets = math.floor(len(self.fileData) / sizeOfDataBlocks)
+
+        packetNumber = 0;
+
+        commands = []
+        for i in range(self.numberOfPackets):
+            # append packet number
+            self.dataToPack.append(sizeOfDataBlocks)
+            self.dataToPack.append(packetNumber >> 8)
+            self.dataToPack.append(0xFF & packetNumber)
+            self.dataToPack += self.fileData[i * sizeOfDataBlocks:(i + 1) * sizeOfDataBlocks]
+            commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=self.dataToPack))
+            # 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.dataToPack = self.dataToPack[:len(self.dataToPack) - sizeOfDataBlocks - 2 - 1]
+            packetNumber = packetNumber + 1;
+        # Calculate remaining data bytes
+        remainingDataSize = len(self.fileData) - self.numberOfPackets * sizeOfDataBlocks
+        self.dataToPack.append(remainingDataSize)
+        self.dataToPack.append(packetNumber >> 8)
+        self.dataToPack.append(0xFF & packetNumber)
+        self.dataToPack += self.fileData[self.numberOfPackets * sizeOfDataBlocks:len(self.fileData)]
+        commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=self.dataToPack))
+        return commands
+
+    def getNumberOfPackets(self):
+        return self.numberOfPackets
+
+# 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], fileData=image)
+#     updateQueue = deque()
+#     updateQueue.appendleft(("print", "Service 23 Software Update"))
+#     i = 1
+#     for packet in update.pusPackets:
+#         updateQueue.appendleft(
+#             ("print", "Packet " + str(i) + "/" + str(len(update.pusPackets)) + " 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 generateService23Subservice2Packet(filename, repositoryPath="",  objectID=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
+            repositoryPath: 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
+    """
+    dataToPack = bytearray()
+    dataToPack.append(objectID[0])
+    dataToPack.append(objectID[1])
+    dataToPack.append(objectID[2])
+    dataToPack.append(objectID[3])
+    dataToPack += bytearray(repositoryPath, 'utf-8')
+    # Add string terminator to repositoryPath
+    dataToPack.append(0)
+    dataToPack += bytearray(filename, 'utf-8')
+    # Add string terminator to filename
+    dataToPack.append(0)
+    return PusTelecommand(service=23, subservice=2, ssc=21, app_data=dataToPack)
+
+
+def generateService23Subservice9Packet(directoryName, repositoryPath="",  objectID=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 repositoryPath: 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
+    """
+    dataToPack = bytearray()
+    dataToPack.append(objectID[0])
+    dataToPack.append(objectID[1])
+    dataToPack.append(objectID[2])
+    dataToPack.append(objectID[3])
+    dataToPack += bytearray(repositoryPath, 'utf-8')
+    # Add string terminator to repository path
+    dataToPack.append(0)
+    dataToPack += bytearray(directoryName, 'utf-8')
+    # Add string terminator to directory name
+    dataToPack.append(0)
+    return PusTelecommand(service=23, subservice=9, ssc=21, app_data=dataToPack)
+
+
+def generateService23Subservice10Packet(dirname, repositoryPath="", objectID=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 dirname: Name of the directory to delete
+           repositoryPath: Path to directory dirname
+           objectID: object ID of the memory handler (e.g. SD Card Handler)
+
+    :return: The application data field of the (23,10) PUS packet
+    """
+    dataToPack = bytearray()
+    dataToPack.append(objectID[0])
+    dataToPack.append(objectID[1])
+    dataToPack.append(objectID[2])
+    dataToPack.append(objectID[3])
+    dataToPack += bytearray(repositoryPath, 'utf-8')
+    # Add string terminator of repository path
+    dataToPack.append(0);
+    dataToPack += bytearray(dirname, 'utf-8')
+    # Add string terminator of directory name
+    dataToPack.append(0);
+    return PusTelecommand(service=23, subservice=10, ssc=21, app_data=dataToPack)
+
+
+def generateService23Subservice128Packet(repositoryPath, filename, objectID=bytearray([]), fileData=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 repositoryPath: The path of the target file
+           filename: Name of file from which the content shall be read
+           objectID: object ID of the memory handler (e.g. SD Card Handler)
+           fileData: The data to write in the file
+
+    :return: The application data field of the (23,128) PUS packet
+    """
+    dataToPack = bytearray()
+    dataToPack.append(objectID[0])
+    dataToPack.append(objectID[1])
+    dataToPack.append(objectID[2])
+    dataToPack.append(objectID[3])
+    dataToPack += bytearray(repositoryPath, 'utf-8')
+    # Add string terminator of repository path
+    dataToPack.append(0);
+    dataToPack += bytearray(filename, 'utf-8')
+    # Add string terminator of filename
+    dataToPack.append(0);
+    return splitLargeFile(dataToPack, 236, fileData)
+
+
+def splitLargeFile(dataToPack, sizeOfDataBlocks, data):
+    """ This function splits a large file in multiple packets
+        This is necessary because the packet size is limited
+
+    :param sizeOfDataBlocks: The file is splitted in data blocks of this size
+           data: The data to pack in multiple packets
+    :return: List containing the PUS packets generated for each data block
+    """
+    numberOfPackets = math.floor(len(data) / sizeOfDataBlocks)
+
+    packetNumber = 0;
+
+    commands = []
+    for i in range(numberOfPackets):
+        dataToPack.append(packetNumber >> 8)
+        dataToPack.append(0xFF & packetNumber)
+        dataToPack += data[i * sizeOfDataBlocks:(i + 1) * sizeOfDataBlocks]
+        commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=dataToPack))
+        # Last data block, packet number and data block size is removed to create new command with next data block,
+        # packet number
+        dataToPack = dataToPack[:len(dataToPack) - sizeOfDataBlocks - 2]
+        packetNumber = packetNumber + 1;
+    dataToPack.append(packetNumber >> 8)
+    dataToPack.append(0xFF & packetNumber)
+    dataToPack += data[numberOfPackets * sizeOfDataBlocks:len(data)]
+    commands.append(PusTelecommand(service=23, subservice=128, ssc=21, app_data=dataToPack))
+    return commands
+
+
+def generateService23Subservice129Packet(repositoryPath, filename, objectID=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 repositoryPath: The path of the target file
+           filename: Name of file from which the content shall be read
+           objectID: object ID of the memory handler (e.g. SD Card Handler)
+
+    :return: The application data field of the (23,129) PUS packet
+    """
+    dataToPack = bytearray()
+    dataToPack.append(objectID[0])
+    dataToPack.append(objectID[1])
+    dataToPack.append(objectID[2])
+    dataToPack.append(objectID[3])
+    dataToPack += bytearray(repositoryPath, 'utf-8')
+    # Add string terminator of repository paht
+    dataToPack.append(0);
+    dataToPack += bytearray(filename, 'utf-8')
+    # Add string terminator of filename
+    dataToPack.append(0);
+    return PusTelecommand(service=23, subservice=129, ssc=21, app_data=dataToPack)
+
+
+def pack_service23_test_into(tcQueue: TcQueueT) -> TcQueueT:
+    SDCardHandler = [0x4D, 0x00, 0x73, 0xAD]
+    tcQueue.appendleft(("print", "Testing Service 23"))
+    tcQueue.append(("print", "Create directory 'test'"))
+    command = generateService23Subservice9Packet(directoryName="test", objectID=SDCardHandler)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.append(("print", "Create subdirectory 'subdir' in 'test'"))
+    command = generateService23Subservice9Packet(repositoryPath="test", directoryName="subdir",
+                                                 objectID=SDCardHandler)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "Create and write in test.bin"))
+    command = generateService23Subservice128Packet("test/subdir", "test.bin", SDCardHandler,
+                                         fileData=bytearray([0x01, 0x00, 0x01, 0x00]))
+    tcQueue.appendleft(command[0].pack_command_tuple())
+    tcQueue.appendleft(("print", "Read data of test.bin"))
+    command = generateService23Subservice129Packet("test/subdir", "test.bin", SDCardHandler)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "Delete 'test.bin'"))
+    command = generateService23Subservice2Packet("test.bin", "test/subdir", objectID=SDCardHandler)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "Delete 'subdir' directory"))
+    command = generateService23Subservice10Packet("subdir", "test", objectID=SDCardHandler)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "Delete 'test' directory"))
+    command = generateService23Subservice10Packet(dirname="test", objectID=SDCardHandler)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "\r"))
+    return tcQueue
\ No newline at end of file
diff --git a/utility/obsw_tmtc_printer.py b/utility/obsw_tmtc_printer.py
index 563b3555f4f5fa27c93541baee2e5a38441f6fa6..c6453a4c2e1164869c7dac428d632d908fb2517d 100644
--- a/utility/obsw_tmtc_printer.py
+++ b/utility/obsw_tmtc_printer.py
@@ -74,6 +74,8 @@ class TmTcPrinter:
         if packet.get_service() == 3 and \
                 (packet.get_subservice() == 10 or packet.get_subservice() == 12):
             self.__handle_hk_definition_print(packet)
+        if packet.get_service() == 23 and packet.get_subservice()  == 130:
+            self.service23ReadFileReply(packet)
         if g.G_PRINT_RAW_TM:
             self.__print_buffer = "TM Data:" + "\n" + self.return_data_string(packet.get_tm_data())
             LOGGER.info(self.__print_buffer)
@@ -132,6 +134,20 @@ class TmTcPrinter:
             self.__print_buffer = self.__print_buffer + str(hex(tm_packet.sid)) + " :"
             self.__print_hk(tm_packet)
 
+    def service23ReadFileReply(self, packet):
+        data = packet.get_tm_data()
+        objectIdLen = 4
+        objectIdOutput = "Object Id of memory handler: " + self.return_data_string(data[:4])
+        print(objectIdOutput)
+        repoPathEnd = data.find(0, objectIdLen)
+        pathOutput = "Repository path: " + data[objectIdLen:repoPathEnd].decode("utf-8")
+        print(pathOutput)
+        filenameEnd = data.find(0, repoPathEnd + 1)
+        filenameOutput = "Filename: " + data[repoPathEnd + 1:filenameEnd].decode("utf-8")
+        print(filenameOutput)
+        filecontentOutput = "File content: " + self.return_data_string(data[filenameEnd + 1:])
+        print(filecontentOutput)
+
     def __print_hk(self, tm_packet: Service3TM):
         """
         :param tm_packet: