nRF52 BLE Code optimization and increase the transmission speed

General project help for Adafruit customers

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Adafruitree
 
Posts: 4
Joined: Mon Apr 04, 2022 2:46 pm

nRF52 BLE Code optimization and increase the transmission speed

Post by Adafruitree »

I am using adafruit feather nrf52840 sense with a customized EMG amplifier (Analog input), and Mic (Analog input). And, I want to plot the data on the mobile application. But, the maximum sampling rate calculated by "millis()" was only 630 Hz.

Could you please check my code and give me some comments to increase the sampling rate? I want to get about a 1 kHz sampling rate. And, also could you please recommend a mobile application to plot and save the received data? Now, I used Bluefruit LE Connect but there is no data save function.

Thank you.

Code: Select all

#include <bluefruit.h>
#include <Adafruit_LittleFS.h>
#include <InternalFileSystem.h>

// BLE Service
BLEDfu  bledfu;  // OTA DFU service
BLEDis  bledis;  // device information
BLEUart bleuart; // uart over ble
BLEBas  blebas;  // battery

bool BLEisConnected = false;

unsigned long time_start = 0;

void setup()
{
  Serial.begin(115200);

#if CFG_DEBUG
  // Blocking wait for connection when debug mode is enabled via IDE
  while ( !Serial ) yield();
#endif
  
  Serial.println("Bluefruit52 BLEUART Example");
  Serial.println("---------------------------\n");

  // Setup the BLE LED to be enabled on CONNECT
  // Note: This is actually the default behavior, but provided
  // here in case you want to control this LED manually via PIN 19
  Bluefruit.autoConnLed(true);

  // Config the peripheral connection with maximum bandwidth 
  // more SRAM required by SoftDevice
  // Note: All config***() function must be called before begin()
  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);

  Bluefruit.begin();
  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values
  //Bluefruit.setName(getMcuUniqueID()); // useful testing with multiple central connections
  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  // To be consistent OTA DFU should be added first if it exists
  bledfu.begin();

  // Configure and Start Device Information Service
  bledis.setManufacturer("Adafruit Industries");
  bledis.setModel("Bluefruit Feather52");
  bledis.begin();

  // Configure and Start BLE Uart Service
  bleuart.begin();

  // Start BLE Battery Service
  blebas.begin();
  blebas.write(100);

  // Set up and start advertising
  startAdv();

  Serial.println("Please use Adafruit's Bluefruit LE app to connect in UART mode");
  Serial.println("Once connected, enter character(s) that you wish to send");
}

void startAdv(void)
{
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();

  // Include bleuart 128-bit uuid
  Bluefruit.Advertising.addService(bleuart);

  // Secondary Scan Response packet (optional)
  // Since there is no room for 'Name' in Advertising packet
  Bluefruit.ScanResponse.addName();
  
  /* Start Advertising
   * - Enable auto advertising if disconnected
   * - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
   * - Timeout for fast mode is 30 seconds
   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
   * 
   * For recommended advertising interval
   * https://developer.apple.com/library/content/qa/qa1931/_index.html   
   */
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 32);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds  
}

void loop()
{
  // Forward data from HW Serial to BLEUART
  while (BLEisConnected)
  {

  // Delay to wait for enough input, since we have a limited transmission buffer
  //delay(500);
  char buf[1500];
  
  String value ="";

  for (int i = 0; i<=100; i++)
  {

  time_start = millis();

    
// Read value from pin A0 of the nRF52832
  analogReadResolution(8); // 0-255
  uint8_t num = analogRead(A1); // unsigned char: 0-255 ( 1 byte)
  uint8_t num2 = analogRead(A5); // unsigned char: 0-255 ( 1 byte)

  value += String(time_start);
  value += ",";
  value += String(num);
  value += ",";
  value += String(num2);
  value += "\n";
  }

  // Convert String into an array of characters that will be stored in "buf"
  value.toCharArray(buf, 1500);
  
  // Send the buf (character array) via BLE
  bleuart.write( buf, strlen(buf) );
    
  }
}

// callback invoked when central connects
void connect_callback(uint16_t conn_handle)
{
  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);

  char central_name[32] = { 0 };
  connection->getPeerName(central_name, sizeof(central_name));

  Serial.print("Connected to ");
  Serial.println(central_name);
  BLEisConnected = true;
}

/**
 * Callback invoked when a connection is dropped
 * @param conn_handle connection where this event happens
 * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
 */
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
  (void) conn_handle;
  (void) reason;

  Serial.println();
  Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
  BLEisConnected = false;
}


User avatar
adafruit_support_mike
 
Posts: 67391
Joined: Thu Feb 11, 2010 2:51 pm

Re: nRF52 BLE Code optimization and increase the transmission speed

Post by adafruit_support_mike »

This will sound harsh, but suggestions for optimization based on 'looking at the code' are useless.

There's a huge body of evidence to support the fact that, in general, programmers have no idea what effect a given optimization will have. Experts with a deep understanding of their own code and the system it's running on will be completely wrong more often than not.

The -only- useful source of information for optimization is profiling data from the code running in its intended context.

That data also proves that a good 80% of valid optimizations aren't worth doing: making a function 90% faster isn't useful if it only accounts for 0.2% of the total runtime. Almost every program spends most of its runtime in a small fraction of the overall code. Profiling tells you where that code is, and how much time is consumed by each statement in that section.

Most microcontrollers don't support full profiling very well, but you can collect the information in a stepwise manner: start by collecting a timestamp every time loop() begins. Then collect a pair of timestamps before and after each loop. If the code outside the loops still consumes any significant amount of time, collect another timestamp 50% of the way through that code, counting by lines. If one half is slower than the other, move the timestamp to find the lines that are slow.

The goal is to build a map of where the code spends most of its time. Loops are major suspects, but you need actual measurements to decide how bad they are.

As you go, you'll find sections that consume very little time. Those are the sections you can ignore, no matter how the code is written. Once you know how long your loops take overall, start collecting timestamps inside the loop to see which sections are slowest. The same is true for function calls.. get before-and-after timestamps to see how long the function takes. Ignore the fast ones and dig into the slow ones to see where they consume the most time.

First-order optimization is simply to find and remove bottlenecks. Profiling often shows that some innocuous-looking piece of non-critical code is much slower than you expected. In many cases the correct solution is just to get rid of that code because it isn't all that important. If you do want its effect, you can look for ways to do it faster.

Second-order optimization is to move as much code as possible out of the slowest loop. Again, it's common to find small and unimportant things that add up to a significant waste of time when you do them over and over. Taking them out of the loop gives you reasonable improvement if you can do it. One common time-sink is to create local variables over and over. Declaring them once outside the loop can often save a useful amount of time.

Only after you've done that basic housekeeping does it become useful to look for the kind of thing people usually think of as 'optimization'. And by the time you get there you should have a solid, data-based understanding of just how much time every line of code costs.


Sometimes profiling will show you that the slowest chunks are the ones you can't improve, like communication latency when talking to other devices. Even so, it's worth knowing that cost so you can think about ways to minimize it.

Locked
Please be positive and constructive with your questions and comments.

Return to “General Project help”