Ninety99
Published

Small COVID medi-kit (SPO2 + Heart Rate + ECG) with MATLAB

A small COVID medical kit that measures SPO2, Heart Rate and ECG. ECG readings are analyzed and shared via Matlab on to ThingSpeak.

BeginnerFull instructions provided19,490
Small COVID medi-kit (SPO2 + Heart Rate + ECG) with MATLAB

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
SparkFun Single Lead Heart Rate Monitor - AD8232
SparkFun Single Lead Heart Rate Monitor - AD8232
×1
Adafruit 0.91″ I2C/IIC Serial 4-Pin OLED Display Module
×1
Maxim Integrated MAX30100 Pulse Oximeter Heart Rate Sensor Module
×1
Breadboard (generic)
Breadboard (generic)
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE
MATLAB
MATLAB
Signal Processing Toolbox, Wavelet Toolbox, Arduino Package needed
ThingSpeak API
ThingSpeak API

Story

Read more

Schematics

Circuit Schematic

Code

Arduino IDE code

Arduino
#include <Adafruit_GFX.h>       
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#include "MAX30100.h"

#define REPORTING_PERIOD_MS     1000
#define SAMPLING_RATE                       MAX30100_SAMPRATE_100HZ

#define IR_LED_CURRENT                      MAX30100_LED_CURR_50MA
#define RED_LED_CURRENT                     MAX30100_LED_CURR_7_6MA

#define PULSE_WIDTH                         MAX30100_SPC_PW_1600US_16BITS
#define HIGHRES_MODE                        true

MAX30100 sensor;
PulseOximeter pox;

uint32_t tsLastReport = 0;

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //Declaring the display name (display)

static const unsigned char PROGMEM logo2_bmp[] =
{ 0x03, 0xC0, 0xF0, 0x06, 0x71, 0x8C, 0x0C, 0x1B, 0x06, 0x18, 0x0E, 0x02, 0x10, 0x0C, 0x03, 0x10,              //Logo2 and Logo3 are two bmp pictures that display on the OLED if called
0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x40, 0x01, 0x10, 0x40, 0x01, 0x10, 0xC0, 0x03, 0x08, 0x88,
0x02, 0x08, 0xB8, 0x04, 0xFF, 0x37, 0x08, 0x01, 0x30, 0x18, 0x01, 0x90, 0x30, 0x00, 0xC0, 0x60,
0x00, 0x60, 0xC0, 0x00, 0x31, 0x80, 0x00, 0x1B, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x04, 0x00,  };

static const unsigned char PROGMEM logo3_bmp[] =
{ 0x01, 0xF0, 0x0F, 0x80, 0x06, 0x1C, 0x38, 0x60, 0x18, 0x06, 0x60, 0x18, 0x10, 0x01, 0x80, 0x08,
0x20, 0x01, 0x80, 0x04, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0xC0, 0x00, 0x08, 0x03,
0x80, 0x00, 0x08, 0x01, 0x80, 0x00, 0x18, 0x01, 0x80, 0x00, 0x1C, 0x01, 0x80, 0x00, 0x14, 0x00,
0x80, 0x00, 0x14, 0x00, 0x80, 0x00, 0x14, 0x00, 0x40, 0x10, 0x12, 0x00, 0x40, 0x10, 0x12, 0x00,
0x7E, 0x1F, 0x23, 0xFE, 0x03, 0x31, 0xA0, 0x04, 0x01, 0xA0, 0xA0, 0x0C, 0x00, 0xA0, 0xA0, 0x08,
0x00, 0x60, 0xE0, 0x10, 0x00, 0x20, 0x60, 0x20, 0x06, 0x00, 0x40, 0x60, 0x03, 0x00, 0x40, 0xC0,
0x01, 0x80, 0x01, 0x80, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x30, 0x0C, 0x00,
0x00, 0x08, 0x10, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00  };

void onBeatDetected()
{
        display.clearDisplay();                                //Clear the display
        display.drawBitmap(0, 0, logo3_bmp, 32, 32, WHITE);    //Draw the second picture (bigger heart) 
        display.setTextSize(1);                                //And still displays the average BPM
        display.setTextColor(WHITE);             
        display.setCursor(50,0);                
        display.println("BPM");             
        display.setCursor(80,0);                
        display.println(pox.getHeartRate()); 
        display.setCursor(50,18);
        display.println("SpO2");
        display.setCursor(80,18);
        display.println(pox.getSpO2());
        display.setCursor(95,18);
        display.println("%");
        display.display();
}

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

    display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Start the OLED display
    display.display();
    delay(3000);
    pinMode(10, INPUT); //for L0+
    pinMode(11, INPUT); //for L0-
    
    sensor.begin(); //Get raw values
    sensor.setMode(MAX30100_MODE_SPO2_HR);
    sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT);
    sensor.setLedsPulseWidth(PULSE_WIDTH);
    sensor.setSamplingRate(SAMPLING_RATE);
    sensor.setHighresModeEnabled(HIGHRES_MODE);
    
    pox.begin();
 // pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);
    
}

void loop()
{
    Serial.println(analogRead(A0));
    uint16_t irValue, red;

    sensor.update();
    pox.update();
    
    sensor.getRawValues(&irValue, &red);
    if (irValue < 7000){       //If no finger is detected it inform the user and put the average BPM to 0 or it will be stored for the next measure
     display.clearDisplay();
     display.setTextSize(1);                    
     display.setTextColor(WHITE);             
     display.setCursor(30,5);                
     display.println("Please Place "); 
     display.setCursor(30,15);
     display.println("your finger ");  
     display.display();
    }
        
    if (millis() - tsLastReport > REPORTING_PERIOD_MS) {

          if(irValue > 7000){                                           //If a finger is detected
        display.clearDisplay();                                   //Clear the display
        display.drawBitmap(5, 5, logo2_bmp, 24, 21, WHITE);       //Draw the first bmp picture (little heart)
        display.setTextSize(1);                                //And still displays the average BPM
        display.setTextColor(WHITE);             
        display.setCursor(50,0);                
        display.println("BPM");             
        display.setCursor(80,0);                
        display.println(pox.getHeartRate()); 
        display.setCursor(50,18);
        display.println("SpO2");
        display.setCursor(80,18);
        display.println(pox.getSpO2());
        display.setCursor(95,18);
        display.println("%");
        display.display();
        
        pox.setOnBeatDetectedCallback(onBeatDetected);                        //If a heart beat is detected
          
        Serial.print("\t Heart rate:");
        Serial.print(pox.getHeartRate());
        Serial.print("bpm / SpO2:");
        Serial.print(pox.getSpO2());
        Serial.println("%");
        tsLastReport = millis();
      }//inner if for finger detect
  }//outer if for Report
}// loop end

Main Matlab Code

MATLAB
clc; 
clear all;
close all;

%arduino set and variables
a=arduino();
ecg=0;

for k=1:500
    b=readVoltage(a,'A0');
    ecg=[ecg,b];
    plot(ecg);
    grid on;
    drawnow;
end
% ecg7500=load('BEAT_12-2-2016 17.53.13.txt');
% for k=1:500
%     ecg(k)=ecg7500(k);
%     plot(ecg);
%     drawnow;
% end

%R wave calculation
wt = modwt(ecg,5);
wtrec = zeros(size(wt));
wtrec(4:5,:) = wt(4:5,:);
y = imodwt(wtrec,'sym4');
tm = 0:1:499;
y = abs(y).^2;
[qrspeaks,locs] = findpeaks(y,tm,'MinPeakHeight',0.35,'MinPeakDistance',0.150);
figure,
plot(tm,y);
hold on
plot(locs,qrspeaks,'ro')
xlabel('Seconds')
title('R Peaks Localized by Wavelet Transform with Automatic Annotations')
grid on

distanceBetweenFirstAndLastPeaks = locs(length(locs))-locs(1);
averageDistanceBetweenPeaks = distanceBetweenFirstAndLastPeaks/length(locs);
averageHeartRate = averageDistanceBetweenPeaks*10/3;
disp('Average Heart Rate = ');
disp(averageHeartRate);

%THINGSPEAK SETUP------------------------------
channelID = ; %%add your channel ID here
writeKey = ' '; %%add your write API key here
UserApikey = ' '; %%add youe User API key here
url = sprintf('https://api.thingspeak.com/channels/%s/feeds.json?api_key=%s',num2str(channelID),UserApikey);
response = webwrite(url, weboptions('RequestMethod','delete'));

%Creating timestamp----------------------------
tStamps = [datetime('now')-seconds(499):seconds(1):datetime('now')]';
dataTable = timetable(tStamps, ecg',y');
thingSpeakWrite(channelID,dataTable,'WriteKey',writeKey);

ECG filter

MATLAB
clc; 
clear all;
close all;

%ARDUINO SET and TIME AXIS-----------------------------
a = arduino(); %'COM6', 'Nano3', 'ForceBuildOn', true
ecg=0;
f_s=250;
N=180;
t=[0:N-1]/f_s;

%loop to take data-------------------------------------
for k=1:180
    b=readVoltage(a,'A0');
    ecg=[ecg,b];
    plot(ecg);
    grid on;
    drawnow;
end

%NOTCH FILTER ECG------------------------------
w=50/(250/2);
bw=w;
[num,den]=iirnotch(w,bw); % notch filter implementation 
ecg_notch=filter(num,den,ecg);

%PLOT FILTERED ECG-----------------------------
figure, %%1
N1=length(ecg_notch);
t1=[0:N1-1]/f_s;
plot(t1,ecg_notch,'r'); title('Filtered ECG signal ')             
xlabel('time')
ylabel('amplitude')

%THINGSPEAK SETUP------------------------------
channelID = ; %%your channel ID here
writeKey = ''; %%your write API key here
UserApikey = ''; %%your User API key here
url = sprintf('https://api.thingspeak.com/channels/%s/feeds.json?api_key=%s',num2str(channelID),UserApikey);
response = webwrite(url, weboptions('RequestMethod','delete'));
%Creating timestamp----------------------------
tStamps = [datetime('now')-hours(179):hours(1):datetime('now')]';
%adding data with timestamp--------------------
for k=1:180
    ecg_180r(k) = round(ecg_notch(k));
    ecg_180(k) = ecg_notch(k);
end
dataTable = timetable(tStamps,ecg_180r');
thingSpeakWrite(channelID,dataTable,'WriteKey',writeKey);

%XLS DATA AND PLOT-----------------------------
t180 = 0:1:179;
xlsdata = [t180;ecg_180];
figure, %%2------------------------------------
plot(t180,ecg(1:180),'r'); title('Data plotting for 0 to 0.804 time frame')             
xlabel('time')
ylabel('amplitude')
hold on
plot(t180,ecg_180,'g');             
legend('ORIGINAL ECG SIGNAL',' Flitered ECG SIGNAL')
hold off
[Status, Message] =  xlswrite('ECG_FILTERED_THINGSPEAK.xlsx', xlsdata');

ThingSpeak Raw data ECG

MATLAB
% Enter your MATLAB code below
readChannelID = ; %%channel Id here
readAPIKey = ' '; %%read API key here
f_s=250;
ecg = thingSpeakRead(readChannelID,'Field',1,'NumPoints',500,'ReadKey',readAPIKey);

N1=length(ecg);
t1=[0:N1-1]/f_s;
plot(t1,ecg,'r'); title('Raw ECG Data')             
xlabel('time')
ylabel('amplitude')

ThingSpeak R peaks localized

MATLAB
% Enter your MATLAB code below
readChannelID = ; %%your channel ID here
readAPIKey = ' '; %%your read API here
y1 = thingSpeakRead(readChannelID,'Field',2,'NumPoints',500,'ReadKey',readAPIKey);
N = length(y1);
ls = size(y1);
fs = 250;% find the sampling rate or frequency
t = (0 : N-1) / fs;% sampling period
T = 1/fs;% sampling rate or frequency
plot(t,y1); title('R Peaks Localized by Wavelet Transform: Automatic Annotations')

ThingSpeak Frequency response of ECG

MATLAB
% Enter your MATLAB code below
readChannelID = ; %%channel ID here
readAPIKey = ' '; %%your read API key here
y1 = thingSpeakRead(readChannelID,'Field',1,'NumPoints',500,'ReadKey',readAPIKey);
fs = 250 % find the sampling rate or frequency
T = 1/fs;% sampling rate or frequency
window = 120; % 2 min 0r 120 second
N = 500;
t = (0 : N-1) / fs;% sampling period

%Compute the spectrum of the ECG and provide remarks on the spectral features of the ECG 
%DFT to describe the signal in the frequency
NFFT = 2 ^ nextpow2(N);
Y = fft(y1, NFFT) / N;
f = (fs / 2 * linspace(0, 1, NFFT / 2+1))'; % Vector containing frequencies in Hz
amp = ( 2 * abs(Y(1: NFFT / 2+1))); % Vector containing corresponding amplitudes
figure;
plot (f, amp);
title ('plot Frequency Response of ECG signal');
xlabel ('frequency (Hz)');
ylabel ('|y(f)|');
grid on;

ThingSpeak PSD of ECG signal

MATLAB
% Enter your MATLAB code below
readChannelID = ; %%your channel ID here
readAPIKey = ' '; %%your read API key here
y1 = thingSpeakRead(readChannelID,'Field',1,'NumPoints',500,'ReadKey',readAPIKey);
fs = 250 % find the sampling rate or frequency
T = 1/fs;% sampling rate or frequency
window = 120; % 2 min 0r 120 second
N = 500;
t = (0 : N-1) / fs;% sampling period

%Compute the spectrum of the ECG and provide remarks on the spectral features of the ECG 
%DFT to describe the signal in the frequency
NFFT = 2 ^ nextpow2(N);
Y = fft(y1, NFFT) / N;
f = (fs / 2 * linspace(0, 1, NFFT / 2+1))'; % Vector containing frequencies in Hz
amp = ( 2 * abs(Y(1: NFFT / 2+1))); % Vector containing corresponding amplitudes
figure;
plot (f, amp);
title ('plot Frequency Response of ECG signal');
xlabel ('frequency (Hz)');
ylabel ('|y(f)|');
grid on;

Credits

Ninety99

Ninety99

2 projects • 10 followers

Comments