Bluetooth Communication between Arduino and Matlab

Hello,

I want to send data from multiple sensors from Arduino per Bluetooth and plot it in real time in Matlab.

My approach is to concatenate the values of the different sensors into a string using letters as delimiters to show which sensor the data is coming from. A typical string looks like this: 'I234H45V1S57W0E345X654Y765Z467'. I am using SoftwareSerial.print() to send the string to HC-06 Bluetooth module. Later in Matlab I am extracting only the sensor values, transforming them to numbers and doing some processing including plotting.

My problem is that I cannot achieve a data rate higher than 10-12Hz, which in my case is not enough. I am using vital parameters sensors like ECG, PPG and an accelerometer. My Matlab code is sending a specific character when ready to read and process new data. When this character is received on the Arduino side, value of analog pins is being read and sent via print(). Meanwhile both the Matlab and the Arduino loops need some time to execute which results in the low data rate.

Actualy my first approach was that Arduino routine was measuring and sending data all the time without waiting for a request. Matlab would then read the data when ready. The problem in this case is obvious - Matlab loop takes more time than the one in Arduino, resulting in the data to be stacked in the outgoing buffer (not sure about the right term). This leads to delays in the plot.

Is there any way of avoiding the buffer or reading only the last value that was stored there? I think this would increase the data rate as both loops would execute independently.

Any other ideas for incresing the data rate would be appreciated.

Thanks in advance.

Any other ideas for incresing the data rate would be appreciated.

Without seeing your code, we can't tell you why it is so slow.

Hi Paul,

here is my code. It is a bit too long but I think all of it matters for the case. Most of the Arduino code is copied from the MAXREFDES117# firmware given from MAXIM INTEGRATED. It can be found here:

Inside the while(1) loop I integrated the readings from the analog inputs and the printing of the data if 'S' is received.

Arduino Code:

#include <Arduino.h>
#include "algorithm.h"
#include "max30102.h"

//Bluetooth Transmission
#include <SoftwareSerial.h>
SoftwareSerial blueSerial(11, 12); //RX, TX
char doi;

#define MAX_BRIGHTNESS 255

uint16_t aun_ir_buffer[100]; //infrared LED sensor data
uint16_t aun_red_buffer[100];  //red LED sensor data
int32_t n_ir_buffer_length; //data length
int32_t n_spo2;  //SPO2 value
int8_t ch_spo2_valid;  //indicator to show if the SPO2 calculation is valid
int32_t n_heart_rate; //heart rate value
int8_t  ch_hr_valid;  //indicator to show if the heart rate calculation is valid
uint8_t uch_dummy;

// the setup routine runs once when you press reset:
void setup() {
  pinMode(13, OUTPUT);  //LED output pin 13 on Arduino

  maxim_max30102_reset(); //resets the MAX30102
  // initialize serial communication at 115200 bits per second:
  Serial.begin(115200);
  // initialize Bluetooth communication at baudrate 57600 
  blueSerial.begin(57600);
  pinMode(10, INPUT);  //pin D10 connects to the interrupt output pin of the MAX30102
  delay(1000);
  maxim_max30102_read_reg(REG_INTR_STATUS_1,&uch_dummy);  //Reads/clears the interrupt status register
    delay(10000);   // Give enough time to open the serial monitor for debugging purposes
    Serial.println(F("Waiting for connection..."));
    delay(1000);
  maxim_max30102_init();  //initialize the MAX30102
}

// the loop routine 
void loop() {
  uint32_t un_min, un_max, un_prev_data, un_brightness;  //variables to calculate the on-board LED brightness that reflects the heartbeats
  float f_temp;  
  un_brightness=0;
  un_min=0x3FFFF;
  un_max=0;
  int isample=0;  // Samples counter for debugging purposes
  //char str_buff[50];

  char doi; int val, xval, yval, zval;
  
  n_ir_buffer_length=100;  // buffer length of 100 stores 4 seconds of samples running at 25sps
  //read the first 100 samples, and determine the signal range
  for(i=0;i<n_ir_buffer_length;i++)
  {
    while(digitalRead(10)==1);  //wait until the interrupt pin asserts
    maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));  //read from MAX30102 FIFO
    
    if(un_min>aun_red_buffer[i])
      un_min=aun_red_buffer[i];  //update signal min
    if(un_max<aun_red_buffer[i])
      un_max=aun_red_buffer[i];  //update signal max
  }
  un_prev_data=aun_red_buffer[i];
  //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
  maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_spo2, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); 

  //Continuously taking samples from MAX30102.  Heart rate and SpO2 are calculated every 1 second
  
  while(1)
  {
    i=0;
    un_min=0x3FFFF;
    un_max=0;

    //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
    for(i=25;i<100;i++)
    {
      aun_red_buffer[i-25]=aun_red_buffer[i];
      aun_ir_buffer[i-25]=aun_ir_buffer[i];

      //update the signal min and max
      if(un_min>aun_red_buffer[i])
        un_min=aun_red_buffer[i];
      if(un_max<aun_red_buffer[i])
        un_max=aun_red_buffer[i];
    }

    //take 25 sets of samples before calculating the heart rate.
    for(i=75;i<100;i++)
    {
      un_prev_data=aun_red_buffer[i-1];
      while(digitalRead(10)==1);
      digitalWrite(9, !digitalRead(9));
      maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));

        if(blueSerial.available()){
          doi = (char)blueSerial.read();
          if(doi == 'S'){                                //if processing program requests a new measurement
         
        val = analogRead(A2);  // Read from ECG sensor
        xval = analogRead(A3);  // Read value for X axis of accelerometer
        yval = analogRead(A4);  // Read value for Y axis of accelerometer
        zval = analogRead(A5);  // Read value for Z axis of accelerometer
    
        //send samples and calculation result to terminal program through UART
       // PPG sensor
        Serial.print(isample);       //  Print sample number and value
        Serial.print("  "); 
        Serial.println(aun_ir_buffer[i], DEC);  // Value of infrared LED of PPG sensor

        blueSerial.print("R");   // Identifier for the value of red LED of PPG sensor
        blueSerial.print(aun_red_buffer[i], DEC);   // Value of red LED of PPG sensor
        blueSerial.print("I");   // Identifier for the value of infrared LED of PPG sensor
        blueSerial.print(aun_ir_buffer[i], DEC);  // Value of infrared LED of PPG sensor
        blueSerial.print("H"); // Identifier for the value of calculated heart rate from PPG sensor
        blueSerial.print(n_heart_rate, DEC);  // Value of calculated heart rate from PPG sensor
        blueSerial.print("V");  // Identifier if calculated heart rate from PPG sensor is valid
        blueSerial.print(ch_hr_valid, DEC);  // 1 - value is valid; 0 - value is not valid
        blueSerial.print("S");  // Identifier for the value of calculated SpO2
        blueSerial.print(n_spo2, DEC);  // Value of calculated Sp02
        blueSerial.print("W");  // Identifier if calculated SpO2 value from PPG sensor is valid
        blueSerial.print(ch_spo2_valid, DEC);  // 1 - value is valid; 0 - value is not valid

        //ECG sensor
        blueSerial.print("E"); // Identifier for the value of ECG sensor
        blueSerial.print(val); // Value of ECG sensor

        //Accelerometer
        blueSerial.print("X"); // Identifier for the value of X axis of the accelerometer
        blueSerial.print(xval); // Value of X axis of the accelerometer
        blueSerial.print("Y"); // Identifier for the value of Y axis of the accelerometer
        blueSerial.print(yval); // Value of Y axis of the accelerometer
        blueSerial.print("Z"); // Identifier for the value of Z axis of the accelerometer
        blueSerial.println(zval); // Value of Z axis of the accelerometer
        isample+=1;
          }
          }
        }
    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_spo2, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);  
  }
}

Matlab code:

%% Setup Bluetooth connection
instrhwinfo('Bluetooth','TexMatITA')
bt = Bluetooth('TexMatITA', 1)
fopen(bt)
set(bt, 'TimeOut', .5);

figure(1);
v=0;
xlabel('Time');
ylabel('PPG');
 
g = animatedline;
g.Color = 'red';  %EKG
ax = gca;
s=0;
x_axis_sec = datenum(seconds(8));
t_num = 0;

double ppg_red;
double ppg_ir;
double ppg_hr;
double ppg_hr_v;
double ppg_spo2;
double ppg_spo2_v;
double ecg_val;
double acc_x;
double acc_y;
double acc_z;

del = {'R', 'I', 'H', 'V', 'S', 'W','E','X','Y','Z'};  %delimiters to split the incoming string
char temp;   %variable to store the incoming string
char split_temp[];    %variable to store split string
double num_temp[];  %variable to store split string converted to numbers

stop = false;
startTime = datetime('now');
t_start_num = datenum(startTime);  % Convert to num for faster calculations

fprintf(bt,'S');  % Request first value

while ~stop;
     try   
      temp = fgetl(bt);    % Read value
      fprintf(bt,'S');   % Request new value until processing the current one
      split_temp = strsplit(temp, del);   % Split incoming string
      num_temp = str2double(convertCharsToStrings(split_temp));  % Convert to numbers
      ppg_red = num_temp(2);
      ppg_ir = num_temp(3);
      ppg_hr = num_temp(4);
      ppg_hr_v = num_temp(5);
      ppg_spo2 = num_temp(6);
      ppg_spo2_v = num_temp(7);
      ecg_val = num_temp(8);
      acc_x = num_temp(9);
      acc_y = num_temp(10);
      acc_z = num_temp(11);

     % Get current times
     t_num = datenum(datetime('now')) - t_start_num;

    % Add points to animation – in this case only for PPG
    addpoints(g, t_num, ppg_ir);   % PPG infrared LED

       % Update axes
    ax.XLim = [t_num - x_axis_sec, t_num];
%      datetick('x','keeplimits');
     drawnow
  catch
  end
end

Plotting of only one of the sensor values (in this case the one of the infrared LED of the PPG sensor) is given in the code. I would like to implement more processing in the loop if possible.

I can not, for the life of me, understand why the reading of data from the bluetooth device is mixed in with the code for reading from the maxim sensor. Those feel like two completely independent activities. Please explain.

Sorry, I forgot to say that I am a newbie to both microcontrollers and programming.

The for loop for sampling from the sensor takes 1 second to execute. It samples with 25 Hz which is then used to calculate the heart rate and SpO2 value every 1 second. If I only send data outside of this loop, I cannot achieve frequency higher than 1 Hz. Or am I completely wrong?

If I only send data outside of this loop, I cannot achieve frequency higher than 1 Hz. Or am I completely wrong?

If it takes one second to collect all the meaningful data, it is impossible to send the meaningful data more than once per second.

I do not understand why sending any data, meaningful or not, relies on having received data. Waiting until you have received something (whatever it is that triggers sending data) is NOT the way to achieve high rates of data transfer.

You MIGHT want to not send data until the other end has said it is ready, but receiving THAT information should NOT be happening in the loop to get data, and should NOT happen more than once.

Ok. The sensor collects 25 samples of what the photodetector captured from red LED and ifrared LED. Based on this, every 1 sec the HR and SpO2 is computet. I want to send both raw photodetector data (updating every 40ms to plot signal in real time) and HR and SpO2 data (updating every 1 sec). I could probably be able to send only raw data and do the calculations on the other end. Though I am afraid that this would make the Matlab loop even slower.

What do you mean by saying that receiving information that the other end is ready should not happen more than once?

If I only use this to initiate the sending of the first measurement data, then the synchronization is getting lost. Arduino loop is executing fast and stacking data into the buffer. Matlab loop is executing slower and getting from the buffer the data that entered there first even if it was stacked there few seconds ago. This is no more real time.

Is there a way to avoid this buffer or reduce its size to only store one sample - the most recent one?

I would be really thankful if you give me an example for how I can correct my code because I am not comming up with a better idea...

What do you mean by saying that receiving information that the other end is ready should not happen more than once?

When you call someone on the phone, how often do you expect them to say "Hello"? Once, to indicate that they are ready to listen to you, or once each time they are ready?

Hello.
Hello.
Hello. Is anyone there?
Hello?

I got your point but how can I implement this in the code without losing synchro?

s7iVuR:
I got your point but how can I implement this in the code without losing synchro?

Since it is the attempt to synchronize data that is causing the slow data transfer, I guess you need to decide whether synchronization is more, or less, important than speed.