Repository: CalgaryToSpace/CTS-SAT-1-OBC-Firmware
Pull request #312: Create telecommand: `fs_write_file_hex`
Author: login: KaleF07
Labels: name: pr-awaiting-changes
Created: 2025-03-26T05:44:41Z | Merged: 2025-04-07T04:59:37Z

Resolves #266 

Summary: Creates telecommand `fs_write_file_hex` that allows for storing hex files into a littlefs file. Works by sending a filename, offset to start writing at within the file, and the hex values to store.

Initial testing shows the function working properly, as well as proper error cases when sending the telecommand. 

Linked issue #266: Telecommand: `fs_write_file_hex`
* Similar to `fs_write_file_str` telecommand.
* Must support offsets within the file (i.e., to write a file larger than the max size of the uplink telecommand).

### Useful for:
* Flashing firmware onto MPI
* Over-the-air upgrades for OBC firmware (if implemented)


Diff:
diff --git a/firmware/Core/Inc/littlefs/littlefs_helper.h b/firmware/Core/Inc/littlefs/littlefs_helper.h
index a753f9f93..58f6ed027 100644
--- a/firmware/Core/Inc/littlefs/littlefs_helper.h
+++ b/firmware/Core/Inc/littlefs/littlefs_helper.h
@@ -32,6 +32,7 @@ int8_t LFS_list_directory(const char root_directory[], uint16_t offset, int16_t
 int8_t LFS_make_directory(const char dir_name[]);
 int8_t LFS_delete_file(const char file_name[]);
 int8_t LFS_write_file(const char file_name[], uint8_t *write_buffer, uint32_t write_buffer_len);
+int8_t LFS_write_file_with_offset(const char file_name[], lfs_soff_t offset, uint8_t *write_buffer, uint32_t write_buffer_len);
 int8_t LFS_append_file(const char file_name[], uint8_t *write_buffer, uint32_t write_buffer_len);
 lfs_ssize_t LFS_read_file(const char file_name[], lfs_soff_t offset, uint8_t *read_buffer, uint32_t read_buffer_len);
 lfs_ssize_t LFS_file_size(const char file_name[]);
diff --git a/firmware/Core/Inc/telecommands/lfs_telecommand_defs.h b/firmware/Core/Inc/telecommands/lfs_telecommand_defs.h
index 3368877dc..fd9fc7425 100644
--- a/firmware/Core/Inc/telecommands/lfs_telecommand_defs.h
+++ b/firmware/Core/Inc/telecommands/lfs_telecommand_defs.h
@@ -23,6 +23,9 @@ uint8_t TCMDEXEC_fs_make_directory(const char *args_str, TCMD_TelecommandChannel
 uint8_t TCMDEXEC_fs_write_file_str(const char *args_str, TCMD_TelecommandChannel_enum_t tcmd_channel,
                         char *response_output_buf, uint16_t response_output_buf_len);
 
+uint8_t TCMDEXEC_fs_write_file_hex(const char *args_str, TCMD_TelecommandChannel_enum_t tcmd_channel,
+                        char *response_output_buf, uint16_t response_output_buf_len);
+
 uint8_t TCMDEXEC_fs_delete_file(const char *args_str, TCMD_TelecommandChannel_enum_t tcmd_channel,
                         char *response_output_buf, uint16_t response_output_buf_len);
                         
diff --git a/firmware/Core/Src/littlefs/littlefs_helper.c b/firmware/Core/Src/littlefs/littlefs_helper.c
index 43fa361bf..e6f687caf 100644
--- a/firmware/Core/Src/littlefs/littlefs_helper.c
+++ b/firmware/Core/Src/littlefs/littlefs_helper.c
@@ -354,6 +354,128 @@ int8_t LFS_append_file(const char file_name[], uint8_t *write_buffer, uint32_t w
     return 0;
 }
 
+/// @brief Creates / Opens LittleFS File and writes data at a specific offset
+/// @param file_name - Pointer to cstring holding the file name to create or open
+/// @param offset - Position within the file to write the data
+/// @param write_buffer - Pointer to buffer holding the data to write
+/// @param write_buffer_len - Size of the data to write
+/// @retval 0 on success, 1 if LFS is unmounted, negative LFS error codes on failure
+int8_t LFS_write_file_with_offset(const char file_name[], lfs_soff_t offset, uint8_t *write_buffer, uint32_t write_buffer_len)
+{
+    if (!LFS_is_lfs_mounted)
+    {
+        LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_CRITICAL, LOG_all_sinks_except(LOG_SINK_FILE), "LittleFS not mounted");
+        return 1;
+    }
+
+    // Open the file with read-write access, create if it doesn't exist
+    lfs_file_t file;
+    const int8_t open_result = lfs_file_opencfg(&LFS_filesystem, &file, file_name, LFS_O_RDWR | LFS_O_CREAT, &LFS_file_cfg);
+    if (open_result < 0)
+    {
+        LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_WARNING, LOG_all_sinks_except(LOG_SINK_FILE), 
+                   "Error opening file: %s (error: %d)", file_name, open_result);
+        return open_result;
+    }
+    
+    LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_NORMAL, LOG_all_sinks_except(LOG_SINK_FILE), 
+               "Opened file for writing at offset: %s", file_name);
+
+    // Get the current file size to determine if we need to extend it
+    lfs_soff_t current_size = lfs_file_size(&LFS_filesystem, &file);
+    if (current_size < 0)
+    {
+        LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_WARNING, LOG_all_sinks_except(LOG_SINK_FILE), 
+                   "Error getting file size: %s (error: %ld)", file_name, current_size);
+        lfs_file_close(&LFS_filesystem, &file);
+        return current_size;
+    }
+
+    // Check if we need to extend the file with zeros for a gap
+    if (offset > current_size)
+    {
+        // Calculate the gap size between current file end and desired offset
+        uint32_t gap_size = offset - current_size;
+        
+        // Fill the gap with zeros if needed
+        if (gap_size > 0) 
+        {
+            // For efficiency, write zeros in chunks if the gap is large
+            uint8_t zero_buffer[64] = {0}; // Buffer of zeros to write
+            uint32_t chunk_size = sizeof(zero_buffer);
+            
+            LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_NORMAL, LOG_all_sinks_except(LOG_SINK_FILE), 
+                       "Extending file from %ld to %ld bytes", current_size, offset);
+
+            // Start writing zeros from the current file size to the desired offset
+            const lfs_soff_t seek_result_1 = lfs_file_seek(&LFS_filesystem, &file, current_size, LFS_SEEK_SET);
+            if (seek_result_1 < 0)
+            {
+                LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_WARNING, LOG_all_sinks_except(LOG_SINK_FILE), 
+                           "Error seeking to offset %ld in file: %s (error: %ld)", current_size, file_name, seek_result_1);
+                lfs_file_close(&LFS_filesystem, &file);
+                return seek_result_1;
+            }
+            
+            while (gap_size > 0) 
+            {
+                uint32_t bytes_to_write = (gap_size > chunk_size) ? chunk_size : gap_size;
+                
+                const lfs_ssize_t write_zeros_result = lfs_file_write(&LFS_filesystem, &file, zero_buffer, bytes_to_write);
+                if (write_zeros_result < 0) 
+                {
+                    LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_WARNING, LOG_all_sinks_except(LOG_SINK_FILE), 
+                               "Error extending file: %s (error: %ld)", file_name, write_zeros_result);
+                    lfs_file_close(&LFS_filesystem, &file);
+                    return write_zeros_result;
+                }
+                
+                gap_size -= bytes_to_write;
+            }
+        }
+    }
+    else 
+    {
+        // Seek to the specified offset
+        const lfs_soff_t seek_result_2 = lfs_file_seek(&LFS_filesystem, &file, offset, LFS_SEEK_SET);
+        if (seek_result_2 < 0)
+        {
+            LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_WARNING, LOG_all_sinks_except(LOG_SINK_FILE), 
+                       "Error seeking to offset %ld in file: %s (error: %ld)", offset, file_name, seek_result_2);
+            
+            // Close the file before returning
+            lfs_file_close(&LFS_filesystem, &file);
+            return seek_result_2;
+        }
+    }
+
+    // Write data to file at the specified offset
+    const lfs_ssize_t write_result = lfs_file_write(&LFS_filesystem, &file, write_buffer, write_buffer_len);
+    if (write_result < 0)
+    {
+        LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_WARNING, LOG_all_sinks_except(LOG_SINK_FILE), 
+                   "Error writing to file: %s at offset %ld (error: %ld)", file_name, offset, write_result);
+        
+        // Close the file before returning
+        lfs_file_close(&LFS_filesystem, &file);
+        return write_result;
+    }
+
+    // Close the File, the storage is not updated until the file is closed successfully
+    const int8_t close_result = lfs_file_close(&LFS_filesystem, &file);
+    if (close_result < 0)
+    {
+        LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_WARNING, LOG_all_sinks_except(LOG_SINK_FILE), 
+                   "Error closing file: %s (error: %d)", file_name, close_result);
+        return close_result;
+    }
+    
+    LOG_message(LOG_SYSTEM_LFS, LOG_SEVERITY_NORMAL, LOG_all_sinks_except(LOG_SINK_FILE), 
+               "Successfully wrote %lu bytes to file: %s at offset %ld", write_buffer_len, file_name, offset);
+
+    return 0;
+}
+
 /// @brief Opens LittleFS File to read from the Memory Module
 /// @param file_name - Pointer to buffer holding the file name to open
 /// @param offset - position within the file to read from
diff --git a/firmware/Core/Src/telecommands/lfs_telecommand_defs.c b/firmware/Core/Src/telecommands/lfs_telecommand_defs.c
index f24a2b11b..685ffaf01 100644
--- a/firmware/Core/Src/telecommands/lfs_telecommand_defs.c
+++ b/firmware/Core/Src/telecommands/lfs_telecommand_defs.c
@@ -175,7 +175,67 @@ uint8_t TCMDEXEC_fs_write_file_str(const char *args_str, TCMD_TelecommandChannel
     return 0;
 }
 
-// TODO: Add a `fs_write_file_hex` telecommand, which supports offsets within the file. (Issue #266)
+/// @brief Telecommand: Write hex data to a file in LittleFS with offset support
+/// @param args_str
+/// - Arg 0: File path as string
+/// - Arg 1: Offset within the file to start writing (uint64)
+/// - Arg 2: Hex string to write to file (e.g., "DEADBEEF" or "DE AD BE EF")
+/// @note The maximum number of bytes that can be written is 105 bytes
+uint8_t TCMDEXEC_fs_write_file_hex(const char *args_str, TCMD_TelecommandChannel_enum_t tcmd_channel,
+                        char *response_output_buf, uint16_t response_output_buf_len) {
+    char arg_file_name[LFS_MAX_PATH_LENGTH];
+    const uint8_t parse_file_name_result = TCMD_extract_string_arg(args_str, 0, arg_file_name, sizeof(arg_file_name));
+    if (parse_file_name_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing file name arg: Error %d", parse_file_name_result);
+        return 1;
+    }
+
+    // Extract the offset parameter
+    uint64_t file_offset = 0;
+    const uint8_t parse_offset_result = TCMD_extract_uint64_arg(args_str, strlen(args_str), 1, &file_offset);
+    if (parse_offset_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing offset arg: Error %d", parse_offset_result);
+        return 2;
+    }
+
+    // Buffer to hold the converted hex data
+    uint8_t binary_data[105] = {0};
+    uint16_t binary_data_length = 0;
+    
+    // Extract and convert hex string to binary data
+    const uint8_t parse_hex_result = TCMD_extract_hex_array_arg(
+        args_str, 2, binary_data, sizeof(binary_data), &binary_data_length
+    );
+    
+    if (parse_hex_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing hex data arg: Error %d", parse_hex_result);
+        return 3;
+    }
+
+    // Use our new helper function to write the data at the specified offset
+    const int8_t result = LFS_write_file_with_offset(arg_file_name, (lfs_soff_t)file_offset, binary_data, binary_data_length);
+    if (result != 0) {
+        snprintf(response_output_buf, response_output_buf_len, "LittleFS Writing Error: %d", result);
+        return 4;
+    }
+    
+    snprintf(response_output_buf, response_output_buf_len, 
+             "LittleFS Successfully Wrote %d bytes of hex data!", 
+             binary_data_length);
+    return 0;
+}
 
 /// @brief Telecommand: Deletes a specified file in LittleFS
 /// @param args_str
diff --git a/firmware/Core/Src/telecommands/telecommand_definitions.c b/firmware/Core/Src/telecommands/telecommand_definitions.c
index 10287a157..8e2722ce7 100644
--- a/firmware/Core/Src/telecommands/telecommand_definitions.c
+++ b/firmware/Core/Src/telecommands/telecommand_definitions.c
@@ -287,6 +287,12 @@ const TCMD_TelecommandDefinition_t TCMD_telecommand_definitions[] = {
         .number_of_args = 2,
         .readiness_level = TCMD_READINESS_LEVEL_FOR_OPERATION,
     },
+    {
+        .tcmd_name = "fs_write_file_hex",
+        .tcmd_func = TCMDEXEC_fs_write_file_hex,
+        .number_of_args = 3,
+        .readiness_level = TCMD_READINESS_LEVEL_FOR_OPERATION,
+    },
     {
         .tcmd_name = "fs_delete_file",
         .tcmd_func = TCMDEXEC_fs_delete_file,


Reviews:
user: login: DeflateAwning
body: Looks good. Only issue was that it appeared to succeed when not mounted
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-06T23:56:32Z
user: login: DeflateAwning
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T00:13:25Z
user: login: DeflateAwning
body: Thank you :)
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T03:58:12Z

Review comments:
diff_hunk: @@ -175,7 +175,67 @@ uint8_t TCMDEXEC_fs_write_file_str(const char *args_str, TCMD_TelecommandChannel
     return 0;
 }
 
-// TODO: Add a `fs_write_file_hex` telecommand, which supports offsets within the file. (Issue #266)
+/// @brief Telecommand: Write hex data to a file in LittleFS with offset support
+/// @param args_str
+/// - Arg 0: File path as string
+/// - Arg 1: Offset within the file to start writing (uint64)
+/// - Arg 2: Hex string to write to file (e.g., "DEADBEEF" or "DE AD BE EF")
+/// @note The maximum number of bytes that can be written is 105 bytes
+uint8_t TCMDEXEC_fs_write_file_hex(const char *args_str, TCMD_TelecommandChannel_enum_t tcmd_channel,
+                        char *response_output_buf, uint16_t response_output_buf_len) {
+    char arg_file_name[LFS_MAX_PATH_LENGTH];
+    const uint8_t parse_file_name_result = TCMD_extract_string_arg(args_str, 0, arg_file_name, sizeof(arg_file_name));
+    if (parse_file_name_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing file name arg: Error %d", parse_file_name_result);
+        return 1;
+    }
+
+    // Extract the offset parameter
+    uint64_t file_offset = 0;
+    const uint8_t parse_offset_result = TCMD_extract_uint64_arg(args_str, strlen(args_str), 1, &file_offset);
+    if (parse_offset_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing offset arg: Error %d", parse_offset_result);
+        return 2;
+    }
+
+    // Buffer to hold the converted hex data
+    uint8_t binary_data[105] = {0};
+    uint16_t binary_data_length = 0;
+    
+    // Extract and convert hex string to binary data
+    const uint8_t parse_hex_result = TCMD_extract_hex_array_arg(
+        args_str, 2, binary_data, sizeof(binary_data), &binary_data_length
+    );
+    
+    if (parse_hex_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing hex data arg: Error %d", parse_hex_result);
+        return 3;
+    }
+
+    // Use our new helper function to write the data at the specified offset
+    const int8_t result = LFS_write_file_with_offset(arg_file_name, (lfs_soff_t)file_offset, binary_data, binary_data_length);
+    if (result < 0) {
path: firmware/Core/Src/telecommands/lfs_telecommand_defs.c
user: login: DeflateAwning
body: Should be `result != 0`. If LFS is not mounted, then this TCMD appears to succeed.
created_at: 2025-04-06T23:56:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -175,7 +175,67 @@ uint8_t TCMDEXEC_fs_write_file_str(const char *args_str, TCMD_TelecommandChannel
     return 0;
 }
 
-// TODO: Add a `fs_write_file_hex` telecommand, which supports offsets within the file. (Issue #266)
+/// @brief Telecommand: Write hex data to a file in LittleFS with offset support
+/// @param args_str
+/// - Arg 0: File path as string
+/// - Arg 1: Offset within the file to start writing (uint64)
+/// - Arg 2: Hex string to write to file (e.g., "DEADBEEF" or "DE AD BE EF")
+/// @note The maximum number of bytes that can be written is 105 bytes
+uint8_t TCMDEXEC_fs_write_file_hex(const char *args_str, TCMD_TelecommandChannel_enum_t tcmd_channel,
+                        char *response_output_buf, uint16_t response_output_buf_len) {
+    char arg_file_name[LFS_MAX_PATH_LENGTH];
+    const uint8_t parse_file_name_result = TCMD_extract_string_arg(args_str, 0, arg_file_name, sizeof(arg_file_name));
+    if (parse_file_name_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing file name arg: Error %d", parse_file_name_result);
+        return 1;
+    }
+
+    // Extract the offset parameter
+    uint64_t file_offset = 0;
+    const uint8_t parse_offset_result = TCMD_extract_uint64_arg(args_str, strlen(args_str), 1, &file_offset);
+    if (parse_offset_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing offset arg: Error %d", parse_offset_result);
+        return 2;
+    }
+
+    // Buffer to hold the converted hex data
+    uint8_t binary_data[105] = {0};
+    uint16_t binary_data_length = 0;
+    
+    // Extract and convert hex string to binary data
+    const uint8_t parse_hex_result = TCMD_extract_hex_array_arg(
+        args_str, 2, binary_data, sizeof(binary_data), &binary_data_length
+    );
+    
+    if (parse_hex_result != 0) {
+        // error parsing
+        snprintf(
+            response_output_buf,
+            response_output_buf_len,
+            "Error parsing hex data arg: Error %d", parse_hex_result);
+        return 3;
+    }
+
+    // Use our new helper function to write the data at the specified offset
+    const int8_t result = LFS_write_file_with_offset(arg_file_name, (lfs_soff_t)file_offset, binary_data, binary_data_length);
+    if (result < 0) {
path: firmware/Core/Src/telecommands/lfs_telecommand_defs.c
user: login: DeflateAwning
body: This issue existed in many other places in the codebase. I've fixed them in #322, so just worry about fixing it here and then we can merge it! 
created_at: 2025-04-07T00:13:25Z
author_association: CONTRIBUTOR

PR comments:
user: login: NuclearTea
created_at: 2025-04-02T00:12:44Z
body: ## Write Hello world as hex to offset of 0 :heavy_check_mark: 

```
FrontierSat time: 19700101T00:00:54.859, Uptime: 54859 ms

========================= UART Telecommand Received =========================

CTS1+fs_write_file_hex(hello_world.hex,0,48 65 6C 6C 6F 20 57 6F 72 6C 64)!

  

=========================

Parsed telecommand (len=77): 'CTS1+fs_write_file_hex(hello_world.hex,0,48 65 6C 6C 6F 20 57 6F 72 6C 64)!

'

0000000000000+0000055246_N [TELECOMMAND:NORMAL]: Executing telecommand from agenda slot 0, sent at tssent=0, scheduled for tsexec=0.

========================= Executing telecommand 'fs_write_file_hex'=========================

0000000000000+0000055375_N [LFS:NORMAL]: Opened file for writing at offset: hello_world.hex

0000000000000+0000055436_N [LFS:NORMAL]: Successfully wrote 11 bytes to file: hello_world.hex at offset 0

========================= Response (duration=180ms, err=0) =========================

LittleFS Successfully Wrote 11 bytes of hex data!

===========================================================================

FrontierSat time: 19700101T00:00:55.854, Uptime: 55854 ms
```
### Read from file
```
========================= UART Telecommand Received =========================

CTS1+fs_read_file_hex(hello_world.hex)!

  

=========================

Parsed telecommand (len=41): 'CTS1+fs_read_file_hex(hello_world.hex)!

'

0000000000000+0000101900_N [TELECOMMAND:NORMAL]: Executing telecommand from agenda slot 0, sent at tssent=0, scheduled for tsexec=0.

========================= Executing telecommand 'fs_read_file_hex'=========================

0000000000000+0000102045_N [LFS:NORMAL]: Successfully opened file: hello_world.hex

0000000000000+0000102053_N [LFS:NORMAL]: Successfully closed file: hello_world.hex

0000000000000+0000102185_N [LFS:NORMAL]: Opened file to read: hello_world.hex

0000000000000+0000102193_N [LFS:NORMAL]: Successfully read file: hello_world.hex

0000000000000+0000102200_N [LFS:NORMAL]: Successfully closed file: hello_world.hex

48 65 6C 6C 6F 20 57 6F 72 6C 64 TODO: send data to radio from TCMD_fs_read_file_hex()========================= Response (duration=296ms, err=0) =========================

LittleFS Successfully Read File 'hello_world.hex': 'Hello World'!

===========================================================================

FrontierSat time: 19700101T00:01:42.623, Uptime: 102623 ms
```
## Write Hello World as hex to offset of 100 to same above file
```
FrontierSat time: 19700101T00:02:36.362, Uptime: 156362 ms

========================= UART Telecommand Received =========================

CTS1+fs_write_file_hex(hello_world.hex,100,48 65 6C 6C 6F 20 57 6F 72 6C 64)!

  

=========================

Parsed telecommand (len=79): 'CTS1+fs_write_file_hex(hello_world.hex,100,48 65 6C 6C 6F 20 57 6F 72 6C 64)!

'

0000000000000+0000157260_N [TELECOMMAND:NORMAL]: Executing telecommand from agenda slot 0, sent at tssent=0, scheduled for tsexec=0.

========================= Executing telecommand 'fs_write_file_hex'=========================

0000000000000+0000157406_N [LFS:NORMAL]: Opened file for writing at offset: hello_world.hex

0000000000000+0000157414_N [LFS:NORMAL]: Extending file from 11 to 100 bytes

0000000000000+0000157474_N [LFS:NORMAL]: Successfully wrote 11 bytes to file: hello_world.hex at offset 100

========================= Response (duration=203ms, err=0) =========================

LittleFS Successfully Wrote 11 bytes of hex data!

===========================================================================

FrontierSat time: 19700101T00:02:37.503, Uptime: 157503 ms
```
### Read from File
```
========================= UART Telecommand Received =========================

CTS1+fs_read_file_hex(hello_world.hex)!

  

=========================

Parsed telecommand (len=41): 'CTS1+fs_read_file_hex(hello_world.hex)!

'

0000000000000+0000196220_N [TELECOMMAND:NORMAL]: Executing telecommand from agenda slot 0, sent at tssent=0, scheduled for tsexec=0.

========================= Executing telecommand 'fs_read_file_hex'=========================

0000000000000+0000196382_N [LFS:NORMAL]: Successfully opened file: hello_world.hex

0000000000000+0000196390_N [LFS:NORMAL]: Successfully closed file: hello_world.hex

0000000000000+0000196540_N [LFS:NORMAL]: Opened file to read: hello_world.hex

0000000000000+0000196547_N [LFS:NORMAL]: Successfully read file: hello_world.hex

0000000000000+0000196555_N [LFS:NORMAL]: Successfully closed file: hello_world.hex

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 48 65 6C 6C 6F 20 57 6F 72 6C 64 TODO: send data to radio from TCMD_fs_read_file_hex()========================= Response (duration=360ms, err=0) =========================

LittleFS Successfully Read File 'hello_world.hex': ''!

===========================================================================

FrontierSat time: 19700101T00:03:16.621, Uptime: 196621 ms
```
**Seems to clear the rest of the bytes**

## Write 512 Bytes of data
**Input string: ** 
```Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris nisl nunc, porta in ullamcorper a, condimentum ac est. Vestibulum sollicitudin, lacus ac aliquet venenatis, lacus enim consectetur odio, et bibendum tortor ligula at turpis. Donec pretium ante eros, sit amet tincidunt ex sollicitudin et. Nulla consequat velit non elit vulputate porttitor. Mauris eu arcu auctor, porttitor odio et, accumsan quam. Quisque consequat, ipsum sed ultrices placerat, massa quam interdum metus, ac sollicitudin velit est.
```

**Note: ** This fails because telecommand it self has a size limit. Find Size limit and set that as the limit.
```
FrontierSat time: 19700101T00:08:00.209, Uptime: 480209 ms

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

HAL_UART_RxCpltCallback() -> UART telecommand buffer is full

FrontierSat time: 19700101T00:08:01.204, Uptime: 481204 ms

========================= UART Telecommand Received =========================

psum.hex,0,4C 6F 72 65 6D 20 69 70 73 75 6D 20 64 6F 6C 6F 72 20 73 69 74 20 61 6D 65 74 2C 20 63 6F 6E 73 65 63 74 65 74 75 72 20 61 64 69 70 69 73 63 69 6E 67 20 65 6C 69 74 2E 20 4D 61 75 72 69 73 20 6E 69 73 6C 20 6E 75 6E 63 2C 20 5 9 6 6 7F6C74 1 0 2

=========================

Parsed telecommand (len=256): 'psum.hex,0,4C 6F 72 65 6D 20 69 70 73 75 6D 20 64 6F 6C 6F 72 20 73 69 74 20 61 6D 65 74 2C 20 63 6F 6E 73 65 63 74 65 74 75 72 20 61 64 69 70 69 73 63 69 6E 67 20 65 6C 69 74 2E 20 4D 61 75 72 69 73 20 6E 69 73 6C 20 6E 75 6E 63 2C 20 5 9 6 6 7F6C74 1 0 2'

Error: TCMD_parse_full_telecommand: str does not start with the correct prefix.
```
## Write Max
**Lorem Ipsum Bytes Generator:**
`https://www.lipsum.com/`

**Ascii to hex (make sure to make the delemeter none:** `https://www.rapidtables.com/convert/number/ascii-to-hex.html`
 
I tried a lot and was able to find the exact number of bytes I am able to send which is **105**. This is a limitation of the telecommand parser and not your code.

**Ascii Input:** `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin feugiat vel risus sed bibendum. Nulla nisi.`

**As Hex:** `4C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E73656374657475722061646970697363696E6720656C69742E2050726F696E20666575676961742076656C2072697375732073656420626962656E64756D2E204E756C6C61206E6973692E`

**Response:**
```
FrontierSat time: 19700101T00:09:39.386, Uptime: 579386 ms

========================= UART Telecommand Received =========================

CTS1+fs_write_file_hex(lorem_ipsum.hex,0,4C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E73656374657475722061646970697363696E6720656C69742E2050726F696E20666575676961742076656C2072697375732073656420626962656E64756D2E204E756C6C61206E697369)!

  

=========================

Parsed telecommand (len=255): 'CTS1+fs_write_file_hex(lorem_ipsum.hex,0,4C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E73656374657475722061646970697363696E6720656C69742E2050726F696E20666575676961742076656C2072697375732073656420626962656E64756D2E204E756C6C61206E697369)!

'

0000000000000+0000579734_N [TELECOMMAND:NORMAL]: Executing telecommand from agenda slot 0, sent at tssent=0, scheduled for tsexec=0.

========================= Executing telecommand 'fs_write_file_hex'=========================

0000000000000+0000579865_N [LFS:NORMAL]: Opened file for writing at offset: lorem_ipsum.hex

0000000000000+0000579926_N [LFS:NORMAL]: Successfully wrote 105 bytes to file: lorem_ipsum.hex at offset 0

========================= Response (duration=181ms, err=0) =========================

LittleFS Successfully Wrote 105 bytes of hex data!

===========================================================================
```

# Final Comments

Almost everything makes sense, the 1 thing that I think needs to be revised is the way the offset works. It should probably only write over starting from the offset. I think I said something before which was probably confusing so please disregard that. Other than that, looks good!

author_association: CONTRIBUTOR
user: login: KaleF07
created_at: 2025-04-04T02:48:10Z
body: Recent changes should fix the issue of erasing data when writing to an offset larger than the file size. I also added a note indicating that the maximum bytes when writing is 105 bytes. Let me know if there's anything else to change!
author_association: CONTRIBUTOR