diff --git a/config/obsw_config.py b/config/obsw_config.py
index db122b02ed89247983fbb65ea1fd40edac256b56..6b9ebc476b96ff9e0960a1a160456826cd4bce39 100644
--- a/config/obsw_config.py
+++ b/config/obsw_config.py
@@ -19,9 +19,10 @@ Mission/Device specific information.
 # TODO: Automate / Autofill this file with the MIB parser
 
 # Object IDs
-GPS0_ObjectId = bytearray([0x44, 0x10, 0x1F, 0x00])
-GPS1_ObjectId = bytearray([0x44, 0x20, 0x20, 0x00])
+GPS0_DEVICE_ID = bytearray([0x44, 0x10, 0x1F, 0x00])
+GPS1_DEVICE_ID = bytearray([0x44, 0x20, 0x20, 0x00])
 DUMMY_DEVICE_ID = bytearray([0x44, 0x00, 0xAF, 0xFE])
+SD_CARD_HANDLER_ID = bytearray([0x4D, 0x00, 0x73, 0xAD])
 
 # Commands
 DUMMY_COMMAND_1 = struct.pack(">I", 666)
diff --git a/tc/obsw_pus_tc_packer.py b/tc/obsw_pus_tc_packer.py
index 0bb610d448bc1951910a8d9e74d0ff692938ba34..23920defffab2162592f8143a8a002861c3f0126 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_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
@@ -46,17 +47,19 @@ class ServiceQueuePacker:
             return pack_service17_test_into(service_queue)
         if service == 20:
             return pack_service20_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":
             return pack_dummy_device_test_into(service_queue)
         if service == "GPS0":
             # Object ID: GPS Device
-            object_id = g.GPS0_ObjectId
+            object_id = g.GPS0_DEVICE_ID
             return pack_gps_test_into(object_id, service_queue)
         if service == "GPS1":
             # Object ID: GPS Device
-            object_id = g.GPS1_ObjectId
+            object_id = g.GPS1_DEVICE_ID
             return pack_gps_test_into(object_id, service_queue)
         if service == "Error":
             return pack_error_testing_into(service_queue)
@@ -76,7 +79,7 @@ def create_total_tc_queue() -> TcQueueT:
     tc_queue = pack_service17_test_into(tc_queue)
     tc_queue = pack_service200_test_into(tc_queue)
     tc_queue = pack_dummy_device_test_into(tc_queue)
-    object_id = g.GPS0_ObjectId
+    object_id = g.GPS0_DEVICE_ID
     tc_queue = pack_gps_test_into(object_id, tc_queue)
     return tc_queue
 
diff --git a/tc/obsw_tc_gps.py b/tc/obsw_tc_gps.py
index e7edd53747d5fda55524a80f99c6304ce3021e34..9d9e1eb29cd230694aab8ed3129e640c7eaf2f4f 100644
--- a/tc/obsw_tc_gps.py
+++ b/tc/obsw_tc_gps.py
@@ -12,9 +12,9 @@ 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_ObjectId:
+    if object_id == g.GPS0_DEVICE_ID:
         gps_string = "GPS0"
-    elif object_id == g.GPS1_ObjectId:
+    elif object_id == g.GPS1_DEVICE_ID:
         gps_string = "GPS1"
     else:
         gps_string = "unknown"
@@ -31,12 +31,12 @@ def pack_gps_test_into(object_id: bytearray, tc_queue: TcQueueT) -> TcQueueT:
     tc_queue.appendleft(command.pack_command_tuple())
     # Enable HK report
     sid_gps = 0
-    if object_id == g.GPS0_ObjectId:
+    if object_id == g.GPS0_DEVICE_ID:
         sid_gps = g.GPS0_SID
         tc_queue.appendleft(("print", "Testing " + gps_string + ": Enable HK Reporting"))
         command = PusTelecommand(service=3, subservice=5, ssc=13, app_data=sid_gps)
         tc_queue.appendleft(command.pack_command_tuple())
-    elif object_id == g.GPS1_ObjectId:
+    elif object_id == g.GPS1_DEVICE_ID:
         sid_gps = g.GPS1_SID
         tc_queue.appendleft(("print", "Testing " + gps_string + ": Enable HK Reporting"))
         command = PusTelecommand(service=3, subservice=5, ssc=14, app_data=sid_gps)
diff --git a/tc/obsw_tc_service23.py b/tc/obsw_tc_service23.py
new file mode 100644
index 0000000000000000000000000000000000000000..a7ed1a55e5b60cb2ca00cac4852864476b045c48
--- /dev/null
+++ b/tc/obsw_tc_service23.py
@@ -0,0 +1,271 @@
+# -*- coding: utf-8 -*-
+"""
+Created: 21.01.2020 07:48
+
+@author: Jakob Meier
+"""
+import math
+import config.obsw_config as g
+
+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. SD_CARD_HANDLER_ID, 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:
+    sd_handler_id = g.SD_CARD_HANDLER_ID
+    tcQueue.appendleft(("print", "Testing Service 23"))
+    tcQueue.append(("print", "Create directory 'test'"))
+    command = generateService23Subservice9Packet(directoryName="test", objectID=sd_handler_id)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.append(("print", "Create subdirectory 'subdir' in 'test'"))
+    command = generateService23Subservice9Packet(repositoryPath="test", directoryName="subdir",
+                                                 objectID=sd_handler_id)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "Create and write in test.bin"))
+    command = generateService23Subservice128Packet(
+        "test/subdir", "test.bin", sd_handler_id, 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", sd_handler_id)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "Delete 'test.bin'"))
+    command = generateService23Subservice2Packet("test.bin", "test/subdir", objectID=sd_handler_id)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "Delete 'subdir' directory"))
+    command = generateService23Subservice10Packet("subdir", "test", objectID=sd_handler_id)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "Delete 'test' directory"))
+    command = generateService23Subservice10Packet(dirname="test", objectID=sd_handler_id)
+    tcQueue.appendleft(command.pack_command_tuple())
+    tcQueue.appendleft(("print", "\r"))
+    return tcQueue
\ No newline at end of file