Hi,
I would like to read a large data file from SD card and send over BLE from my device to an Android smartphone. I have adapted the ble_app_uart example into my program, and I test it using the NRF Toolbox UART app. For a small file (~200 bytes), it worked perfectly. But then when I tried with larger files, the program would hang after transmitting a certain number of bytes (this was fairly repeatable). My approach was to:
- Setup the SD card
- Start sending packets in a loop, where we keep doing it until there is no more data to send or we see NRF_ERROR_RESOURCES (means TX buffer is full)
- If we are at the start of the SDC, read SDC_BUFF_SIZE bytes into buffer
- send a packet of 20 bytes via ble_nus_string_send until we see NRF_ERROR_RESOURCES
- At this point we break out of the loop, and wait for a BLE_NUS_EVT_TX_RDY event in nus_data_handler() before resending more packets
- otherwise, update the position of where we are in the SDC buffer and how much is left to send
- Continue sending packets
- Uninit the SDC
When I run this program with a larger SDC file, the program hangs (and fails to respond, even the WDT won't restart it) at the f_read() step of read_SDC(). I have noticed that how many SDC reads are successful and how many bytes are sent depends a lot on SDC_BUFF_SIZE . When it is 100, I can send ~500 bytes successfully (which requires 5 SDC reads) when the program hangs. When it is SDC_BUFF_SIZE > 500, I can send up to one full buffer when the program hangs. For example, if SDC_BUFF_SIZE is 500, I can send 500 bytes; if SDC_BUFF_SIZE is 2000, I can send 2000 bytes. I am verifying how many bytes are sent by checking the log on the UART app.
A separate approach I tried to fix/debug this: I tried to init and uninit the SDC before/after each packet send, but then the SDC init function would hang at the disk_initialize step (although maybe this is unrelated and may be a separate issue with interrupts and IRQ priorities?). Any suggestions on how to fix this would help a lot. Thanks!
Here are the main functions for this:
// Split up SDC into packets and keep sending
static void send_sdc_packets() {
NRF_LOG_INFO("Initial sdc_buff_current_pos: %d", sdc_buff_current_pos);
int cnt = 0;
uint16_t send_length = packet_length;
// Keep sending packets until buffers are full, then wait for completion
while (true)
{
// Read from SDC if starting from the beginning
if (bytes_remaining == 0) {
sdc_read_num++;
NRF_LOG_INFO("--READ SDC: sdc_read_num: %d", sdc_read_num);
NRF_LOG_INFO("f_tell(&file): %d", f_tell(&file));
bytes_remaining = read_SDC();
sdc_buff_current_pos = 0; // Reset to beginning
NRF_LOG_INFO("--AR");
NRF_LOG_INFO("bytes_remaining: %d", bytes_remaining);
if (bytes_remaining < SDC_BUFF_SIZE) {
NRF_LOG_INFO("done_reading_sdc: %d", done_reading_sdc);
done_reading_sdc = true;
}
}
// If near end of file, and we have less than a packet
if (bytes_remaining < packet_length) {
send_length = bytes_remaining;
} else {
send_length = packet_length;
}
NRF_LOG_INFO("-sdc_read_num: %d", sdc_read_num);
NRF_LOG_INFO("bytes_remaining: %d, packet_length: %d, send_length: %d", bytes_remaining, packet_length, send_length);
// Sending the data over BLE
err_code = ble_nus_string_send(&m_nus, &sdc_buff[sdc_buff_current_pos], &send_length);
if (err_code == NRF_ERROR_RESOURCES ||
err_code == NRF_ERROR_INVALID_STATE ||
err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING)
{
NRF_LOG_INFO("send_sdc_packets(), err_code: %d", err_code);
NRF_LOG_INFO("sdc_buff_current_pos: %d", sdc_buff_current_pos);
NRF_LOG_INFO("cnt: %d", cnt);
break;
}
else if (err_code != NRF_SUCCESS)
{
NRF_LOG_INFO("** ERROR send_sdc_packets(), err_code: %d", err_code);
APP_ERROR_HANDLER(err_code);
} else {
// If successfully sent, update position for next one
sdc_buff_current_pos += send_length;
bytes_remaining -= send_length;
cnt++;
// Check if we read all the SDC and sent the remaining data
if (done_reading_sdc && (bytes_remaining == 0) ) {
done_sending_sdc = true;
NRF_LOG_INFO("Final cnt: %d", cnt);
// Uninitialize here, since done with SDC
sd_uninit();
nrf_gpio_cfg_output(ADP1_PIN);
nrf_gpio_pin_clear(ADP1_PIN); // Enable HIGH
break;
}
}
}
}
// Set up SD card and start sending data over BLE
static void send_sdc_data() {
NRF_LOG_INFO("--ENTERED send_sdc_data()");
// reset flag so that it doesn't try to send again every time it wakes up
start_sending_sdc_data = false;
// Read the SD card and send the data
packet_length = BLE_TX_PACKET_SIZE; // global static variable
nrf_gpio_cfg_output(ADP1_PIN);
nrf_gpio_pin_set(ADP1_PIN); // Enable HIGH
sd_init();
sd_mount();
sd_open(BLE_TEST_FILE_NAME, FA_READ);
send_sdc_packets();
}
And here are some of the other relevant functions:
// Reads a large buffer block from the SDC
uint32_t read_SDC() {
uint32_t bytes_read;
ff_result = f_read(&file, sdc_buff, SDC_BUFF_SIZE, (UINT *) &bytes_read);
if (ff_result != FR_OK) {
NRF_LOG_INFO("** ERROR read_SDC(): read failed, ff_result: %d **", ff_result);
return ff_result;
}
return bytes_read;
}
// For BLE UART handler
static void nus_data_handler(ble_nus_evt_t * p_evt)
{
if (p_evt->type == BLE_NUS_EVT_TX_RDY) {
NRF_LOG_INFO("nus_data_handler(): Detected BLE_NUS_EVT_TX_RDY");
if (!done_sending_sdc) {
NRF_LOG_INFO("nus_data_handler(): Detected BLE_NUS_EVT_TX_RDY");
send_sdc_packets();
}
}
}