Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Program hangs when sending large SD card file over BLE, using adapted ble_app_uart example

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();
		}

    }
}

Related