Skip to content

Update getFileBlock to match PR 40 in the SARA-R5 Library #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 104 additions & 60 deletions src/sfe_ublox_cellular.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4682,76 +4682,120 @@ UBX_CELL_error_t SparkFun_ublox_Cellular::getFileContents(String filename, char
return err;
}

UBX_CELL_error_t SparkFun_ublox_Cellular::getFileBlock(const String &filename, char *buffer, size_t offset, size_t requested_length,
size_t &bytes_read)
{
bytes_read = 0;
if (filename.length() < 1 || buffer == nullptr || requested_length < 1)
UBX_CELL_error_t SparkFun_ublox_Cellular::getFileBlock(const String &filename, char *buffer, size_t offset, size_t requestedLength,
size_t &bytesRead)
{
UBX_CELL_error_t err;
char *command;
char *response;

bytesRead = 0;
if (filename.length() < 1 || buffer == nullptr || requestedLength < 1)
{
return UBX_CELL_ERROR_UNEXPECTED_PARAM;
}

// trying to get a byte at a time does not seem to be reliable so this method must use
// a real UART.
if (_hardSerial == nullptr)
{
if (_printDebug == true)
{
return UBX_CELL_ERROR_UNEXPECTED_PARAM;
_debugPort->println(F("getFileBlock: only works with a hardware UART"));
}
return UBX_CELL_ERROR_INVALID;
}

command = ubx_cell_calloc_char(strlen(UBX_CELL_FILE_SYSTEM_READ_BLOCK) + filename.length() + 28);
if (command == nullptr)
{
return UBX_CELL_ERROR_OUT_OF_MEMORY;
}
sprintf(command, "%s=\"%s\",%lu,%lu", UBX_CELL_FILE_SYSTEM_READ_BLOCK, filename.c_str(), (unsigned long) offset, (unsigned long) requestedLength);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use snprintf() instead of sprintf()

Comment on lines +4709 to +4714
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure strlen(UBX_CELL_FILE_SYSTEM_READ_BLOCK) + filename.length() + 28 is big enough for what can go into command. After strlen(UBX_CELL_FILE_SYSTEM_READ_BLOCK) + filename.length(), you have the characters ="",, (5 characters), plus 2x long integers, which are 64-bit on ESP32 (up to 20 digits each, I believe). So replace 28 with 45, and I think this should be able to handle any input (and will be memory safe if snprintf() is used).


// trying to get a byte at a time does not seem to be reliable so this method must use
// a real UART.
if (_hardSerial == nullptr)
response = ubx_cell_calloc_char(minimumResponseAllocation);
if (response == nullptr)
{
if (_printDebug == true)
{
if (_printDebug == true)
{
_debugPort->println(F("getFileBlock: only works with a hardware UART"));
}
return UBX_CELL_ERROR_INVALID;
_debugPort->print(F("getFileBlock: response alloc failed: "));
_debugPort->println(minimumResponseAllocation);
}

size_t cmdLen = filename.length() + 32;
char *cmd = ubx_cell_calloc_char(cmdLen);
if (cmd == nullptr)
return UBX_CELL_ERROR_OUT_OF_MEMORY;
snprintf(cmd, cmdLen, "at+urdblock=\"%s\",%zu,%zu\r\n", filename.c_str(), offset, requested_length);
sendCommand(cmd, false);

int ich;
char ch;
int quote_count = 0;
size_t comma_idx = 0;

while (quote_count < 3)
free(command);
return UBX_CELL_ERROR_OUT_OF_MEMORY;
}
Comment on lines +4716 to +4726
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response is a fixed size, so don't use ubx_cell_calloc_char(), just create it as an array:

char response[minimumResponseAllocation];


// Send command and wait for some response
// Response format: \r\n+URDBLOCK: "filename",64000,"these bytes are the data of the file block"\r\n\r\nOK\r\n
sendCommand(command, true);
err = waitForResponse(UBX_CELL_FILE_SYSTEM_READ_BLOCK, UBX_CELL_RESPONSE_ERROR, 5 * UBX_CELL_STANDARD_RESPONSE_TIMEOUT);
if (err != UBX_CELL_ERROR_SUCCESS)
{
if (_printDebug == true)
{
ich = _hardSerial->read();
if (ich < 0)
{
continue;
}
ch = (char)(ich & 0xFF);
cmd[bytes_read++] = ch;
if (ch == '"')
{
quote_count++;
}
else if (ch == ',' && comma_idx == 0)
{
comma_idx = bytes_read;
}
_debugPort->print(F("getFileBlock: waitForResponse returned err "));
_debugPort->println(err);
}

cmd[bytes_read] = 0;
cmd[bytes_read - 2] = 0;

// Example response:
// +URDBLOCK: "wombat.bin",64000,"<data starts here>... "<cr><lf>
size_t data_length = strtoul(&cmd[comma_idx], nullptr, 10);
free(cmd);

bytes_read = 0;
size_t bytes_remaining = data_length;
while (bytes_read < data_length)
free(command);
free(response);
return err;
}

// Skip the filename in quotes and get the data length index
int ich;
char ch;
int quoteCount = 0;
size_t lengthIndex = 0;
while (quoteCount < 3 && bytesRead < minimumResponseAllocation)
{
ich = _hardSerial->read();
if (ich < 0)
{
continue;
}
ch = (char)(ich & 0xFF);
response[bytesRead++] = ch;
if (ch == '"')
{
quoteCount++;
}
else if (ch == ',' && lengthIndex == 0 && quoteCount == 2)
{
lengthIndex = bytesRead;
}
}
Comment on lines +4749 to +4766
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should lengthIndex be checked after this loop? Just thinking what happens if the response is malformed, should try to detect that.

response[bytesRead] = 0; // Make response a null-terminated string
response[bytesRead - 2] = 0; // Terminate response string right after block length
size_t data_length = strtoul(&response[lengthIndex], nullptr, 10);

// Read file block data directly into supplied buffer
bytesRead = 0;
size_t bytesRemaining = data_length;
while (bytesRead < data_length)
{
// This method seems more reliable than reading a byte at a time.
size_t rc = _hardSerial->readBytes(&buffer[bytesRead], bytesRemaining);
bytesRead += rc;
bytesRemaining -= rc;
}
Comment on lines +4774 to +4780
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for _hardSerial->readBytes() to return zeros forever? If so, this loop will never exit, so it'd be good to have a timeout


// Read rest of response until \r\nOK\r\n
err = waitForResponse(UBX_CELL_RESPONSE_OK, UBX_CELL_RESPONSE_ERROR, UBX_CELL_STANDARD_RESPONSE_TIMEOUT);
if (err != UBX_CELL_ERROR_SUCCESS)
{
if (_printDebug == true)
{
// This method seems more reliable than reading a byte at a time.
size_t rc = _hardSerial->readBytes(&buffer[bytes_read], bytes_remaining);
bytes_read += rc;
bytes_remaining -= rc;
_debugPort->print(F("getFileBlock: waitForResponse returned err "));
_debugPort->println(err);
}
free(command);
free(response);
return err;
}

return UBX_CELL_ERROR_SUCCESS;
free(command);
free(response);
return err;
}

UBX_CELL_error_t SparkFun_ublox_Cellular::getFileSize(String filename, int *size)
Expand Down
4 changes: 2 additions & 2 deletions src/sfe_ublox_cellular.h
Original file line number Diff line number Diff line change
Expand Up @@ -1043,8 +1043,8 @@ class SparkFun_ublox_Cellular : public Print
char *contents); // OK for binary files. Make sure contents can hold the entire
// file. Get the size first with getFileSize.
UBX_CELL_error_t getFileBlock(
const String &filename, char *buffer, size_t offset, size_t length,
size_t &bytes_read); // OK for binary files. Make sure buffer can hold the requested block size.
const String &filename, char *buffer, size_t offset, size_t requestedLength,
size_t &bytesRead); // OK for binary files. Make sure buffer can hold the requested block size.

// Append data to a file, delete file first to not appends the data.
UBX_CELL_error_t appendFileContents(String filename, String str);
Expand Down