Best approach for transfering files from LittleFS to BLE central

Hello,

I have implemented an application for the nRF52832 that uses peripheral NUS with LittleFS. The application receives sensor data via UART and stores it to a file using LittleFS. The central can then send a command via NUS which triggers reading the file with sensor data. All the file operations like reading, writting, erasing, and listing directory are implemented in a workqueue like recommended by Littlefs thread safe to make it thread safe.

Here is the file read handler:

static void littleFS_read_handler()
{
	int err;
	struct fs_dirent entry;
	size_t pos = 0;
	int bytes_read = 0;
	char inChar;
	char file_read_buf[100];

	err = fs_stat("/lfs/test.txt", &entry);
	if (err)
	{
		bt_nus_send(NULL, "No file to read\n", 16);
		return;
	}

	err = fs_open(&myFile, "/lfs/test.txt", FS_O_READ);
	if (err < 0)
	{
		LOG_INF("Error opening file [%d]\n", err);
		return;
	}

	bt_nus_send(NULL, "---Reading File---", strlen("---Reading File---"));

	while (pos < 100 - 1)
	{
		bytes_read = fs_read(&myFile, &inChar, 1);

		if (bytes_read == 0)
		{
			break;
		}

		if (inChar == '\n')
		{
			file_read_buf[pos] = '\0';

			bt_nus_send(NULL, file_read_buf, strlen(file_read_buf));

			pos = 0;
			file_read_buf[pos] = '\0';
		}

		file_read_buf[pos++] = inChar;
	}

	bt_nus_send(NULL, "---Closing File---", strlen("---Closing File---"));

	fs_close(&myFile);
}

The sensor data is stored in lines separated by '\n' in the file (Note: no line is expected to exceed 50 chars), so I read each line and send it via NUS. I have allocated a 256 kB storage partition in the nRF52832 NVM, so this line-by-line NUS send approach becomes more time consuming as data fills up. It takes around 1 minute and 30 seconds to transfer a full file from LittleFS through NUS. 

Is there a better approach for transfering the data from LittleFS to the BLE central? I want to keep NUS functionality for this application, but I know it has a 251 char limit per transfer, so I am also open to adding another service. 

Thank you!

Parents
  • Hello,

    I want to keep NUS functionality for this application, but I know it has a 251 char limit per transfer, so I am also open to adding another service. 

    This is a bit up to you, how you want to do it. A simple way would be to just read out 247 bytes, and send it using the Nordic UART service. Perhaps you could have one special character first, indicating to the receiver that this is a message from the file system. Then the receiver can reassemble the entire file. 

    Another option is, as you say, to transfer one line at the time. This is probably even simpler, because it is very easy to re-assemble the data on the other end.

    Then you can choose whether you want to use the NUS service, or if you want to set up an additional service for this. If you are using the NUS, and want to separate these messages from the rest, you can use e.g. a special character first indicating that this message contains a sample from the file. 

    It takes around 1 minute and 30 seconds to transfer a full file from LittleFS through NUS

    I don't know if this is an issue or not. The theoretical max throughput in a BLE link using 2MBPS on the radio is 1.3Mbps. This means that it is possible to do it in a couple of seconds. To acheive this throughput, you need to have the maximum MTU, use a long connection interval, and send data-packets as fast as you can. You can refer to the throughput sample:

    NCS\nrf\samples\bluetoot\throughput

    Whether you get the maximum throughput depends on the connected device. Does it allow 2MBPS phy? Will it give maximum MTU? Does it allow Data Length Extension (allowing to use more of the connection interval for data transfer), and so on.

    But I thought that 1m30s was a bit long. Do you wait for the previous packet to be acked before you send the next one?

    Best regards,

    Edvin

  • Hello Edvin,

    Your first suggestion worked for me! I modified my file read handler as follows:

    static void littleFS_read_handler()
    {
    	int err;
    	struct fs_dirent entry;
    	int bytes_read = 0;
    	char file_read_buf[200];
    
    	err = fs_stat("/lfs/test.txt", &entry);
    	if (err)
    	{
    		bt_nus_send(NULL, "No file to read\n", 16);
    		return;
    	}
    
    	err = fs_open(&myFile, "/lfs/test.txt", FS_O_READ);
    	if (err < 0)
    	{
    		LOG_INF("Error opening file [%d]\n", err);
    		return;
    	}
    
    	bt_nus_send(NULL, "---Reading File---", strlen("---Reading File---"));
    
    	while (1)
    	{
    		bytes_read = fs_read(&myFile, file_read_buf, 200);
    
    		if (bytes_read == 0)
    		{
    			break;
    		}
    
    		bt_nus_send(NULL, file_read_buf, strlen(file_read_buf));
    
    		memset(file_read_buf, 0, sizeof(file_read_buf));
    	}
    
    	bt_nus_send(NULL, "---Closing File---", strlen("---Closing File---"));
    
    	fs_close(&myFile);
    }

    Instead of sending line-by-line, I just send 200 chars at a time. This approach is much quicker, only taking 7 seconds to transfer a full file of like 250 kB. As for my previous approach, its obvious to me now that it was so slow because of a combination of parsing the data char-by-char looking for '\n' and only sending about < 50 chars per NUS transfer.

    Thank you! 

Reply
  • Hello Edvin,

    Your first suggestion worked for me! I modified my file read handler as follows:

    static void littleFS_read_handler()
    {
    	int err;
    	struct fs_dirent entry;
    	int bytes_read = 0;
    	char file_read_buf[200];
    
    	err = fs_stat("/lfs/test.txt", &entry);
    	if (err)
    	{
    		bt_nus_send(NULL, "No file to read\n", 16);
    		return;
    	}
    
    	err = fs_open(&myFile, "/lfs/test.txt", FS_O_READ);
    	if (err < 0)
    	{
    		LOG_INF("Error opening file [%d]\n", err);
    		return;
    	}
    
    	bt_nus_send(NULL, "---Reading File---", strlen("---Reading File---"));
    
    	while (1)
    	{
    		bytes_read = fs_read(&myFile, file_read_buf, 200);
    
    		if (bytes_read == 0)
    		{
    			break;
    		}
    
    		bt_nus_send(NULL, file_read_buf, strlen(file_read_buf));
    
    		memset(file_read_buf, 0, sizeof(file_read_buf));
    	}
    
    	bt_nus_send(NULL, "---Closing File---", strlen("---Closing File---"));
    
    	fs_close(&myFile);
    }

    Instead of sending line-by-line, I just send 200 chars at a time. This approach is much quicker, only taking 7 seconds to transfer a full file of like 250 kB. As for my previous approach, its obvious to me now that it was so slow because of a combination of parsing the data char-by-char looking for '\n' and only sending about < 50 chars per NUS transfer.

    Thank you! 

Children
No Data
Related