IR Heartbeat Display
Lab 2
Lab 2
In this lab, we will use our developed knowledge of I2C, Serial, and Bluetooth communication to develop a system that can detect our heartbeat through the use of an IR emitter and IR receiver. Once this circuit is established, we will move on to filter the output signals (our heart beat) with both analog and digital filters. Filters are introduced in this lab as they are a powerful tool in the world of signal processing as they allow you to literally filter out the signal you want while getting rid of unwanted "noise".
Then we will use this heart beat sensor to test different locations on our body that are optimal for producing the best signal. Different parts of the body have different effects on how the IR sensors detect heartbeat due to difference in skin thickness, blood flow, dense muscles, etc. Spoiler alert: turns out the wrist is actually not the ideal location for heart rate detection! (I wonder why so many wearables are strapped to the wrist?)
Finally, we finish the lab by creating a heartbeat display using the OLED used from the previous lab. However, we will also implement a Python script that can read data from the Arduino via Bluetooth and store/plot the data we receive. This will be accomplished through the use of two strong Python libraries commonly used in the industry: Numpy and MatPlotLib. Through the successful completion of this lab, you will not only further enhance your skills in Arduino/Python development, but have a functional heart rate sensor in your possession!
1.) First, we begin by setting up the circuit on the breadboard with the IR emitter and IR receiver. The circuit is setup using the diagram on Figure 1 & Figure 2 below:
Note the polarity on the IR emitter and receivers. The longer leg will be the positive end and the shorter leg will be the negative terminal. The same goes for the capacitor (1 uF). The result should look something like this:
2.) Next, we will sample analog signals from the Arduino's A1 pin, and use Arduino's built-in Serial Plotter function to display the results. The code is given below:
int sensorPin = A1;
int sensorValue = 0;
void setup() {
Serial.begin(9600);
while(!Serial) {};
pinMode(sensorPin, INPUT);
}
void loop() {
sensorValue = analogRead(A1);
Serial.println(sensorValue);
}
Once you copy and paste the code into your Arduino sketch, upload it to the board and run it. Once the code begins to run, open Serial Plotter (Arduino >> Tools >> Serial Plotter). Place your finger on top of the IR emitter and receiver to check your heartbeat. The results should resemble something like the picture below:
IR Signal Output Showing the Detection of Heartbeats
3.) Congratulations! You have just created an IR receiver/emitter circuit that can check your heartbeat. There are a few things to note about this objective:
1.) Now that we have a working heartbeat sensor, we must get a more consistent and smooth signal. This can be done using an Op-amp to amplify the analog circuit we receive from the IR receiver. Set up the Op-amp circuit according to Figure 3 and Figure 4 below:
2.) Note that not all the pins on the Op-amp need to be connected. Pins 1, 7, and 8 are not connected to anything. Your circuit should resemble something like this:
3.) Using the same code as Objective 1A, run the Arduino code and put your finger on top of the IR emitter and receiver to obtain a heart rate in Serial Plotter. You should notice a significantly smoother signal that resembles something like the one shown below:
IR Signal Output After Adding Op-Amp
4.) Congratulations! You have successfully amplified your heartbeat signal using the Op-amp to provide a much more consistent signal to the Serial Plotter. To explain how this feat was accomplished, it is important to note exactly what the Op-amp does.
1.) First, set up the LPF on your circuit using resistors and capacitors. You should always disconnect the Arduino prior to touching the circuit elements to prevent the chance of short-circuiting your entire setup. Reference Figure 5 and Figure 6 to assemble your new circuit:
The circuit you construct should resemble something like this:
2.) As before, upload the same Arduino code to your circuit and run it. Put your finger on top of the IR emitter and receiver, and on the Serial Plotter screen, you should notice a significant reduction in noise (signal jitter). The result should look similar to the following plot:
IR Signal Output After Adding LPF
Note that the heartbeat signal here has less jagged curves compared to the signal from the circuit without the LPF. In other words, adding the analog LPF to our circuit helped "smoothen" out our IR signal curves. The signal also appears to be more amplified. This is simply a result of the reduction of noise, which correspondingly adjusts our graph scale to better represent the amplitude of our wanted IR signal.
1.) Now we introduce the idea of digital filtering in this objective. To start off, we must add an extra output wire for the Op-amp to send a signal to the Arduino's A0 analog pin. This can be done by simply adding an extra wire as shown in Figure 7 and Figure 8.
The final circuit should resemble something like the one shown below (note that there are two wires, one going to pin A0 and another going to pin A1):
2.) Now that we have an extra signal from the Op-amp, we must update the Arduino code to display this extra signal as well. We will separate the values from the two signals (A0 and A1) by adding a tab character (\t) in between them when writing to Serial. The resulting code is below:
int sensorPin = A1;
int sensorValue = 0;
int sensorPin2 = A0;
int sensorValue2 = 0;
void setup() {
Serial.begin(9600);
while(!Serial) {};
pinMode(sensorPin, INPUT);
pinMode(sensorPin2, INPUT);
}
void loop() {
sensorValue = analogRead(A1);
sensorValue2 = analogRead(A0);
Serial.print(sensorValue);
Serial.print("\t");
Serial.println(sensorValue2);
}
The code will therefore print to the Serial plotter the sensor value from A1 followed by the sensor value from A0, which are separated by a tab character (\t).
3.) Upload and run the code to your Arduino. Open the Serial Plotter and place your finger on the IR receiver and emitter to check the heart rate again. You should notice two signals on the Serial Plotter (one blue and one red). The red signal is from the analog pin A0 and the blue signal is from pin A1. The result from checking your heart rate should resemble something like this:
IR Signal Outputs from Unfiltered Pin A0 (Red) and Filtered Pin A1 (Blue)
You can notice a significant difference between the A0 and A1 signals from the resulting graph. The red A0 signal output has more noise shown by the rough curves, whereas the blue A1 signal output has less noise shown by the smoother, more consistent curves. This is because the red A0 signal has only been amplified by the Op-amp, but no filters have been applied to it. The blue A1 signal has been both amplified by the Op-amp and filtered with the analog LPF on the circuit board.
Now we can manipulate the red A0 signal by adding a digital filter over it. This filter will be a digital LPF with a cutoff frequency of 2.0 Hz. The resulting curve should have less noise than the one depicted in the previous graph.
4.) In order to implement a digital filter, we must first install the digital filter library for the Arduino. The library can be found in The GitHub repository. Simply download the folder, unzip the folder, and add it to the Arduino "libraries" directory on your computer. Once this is accomplished, restart the Arduino IDE and verify that the library has been installed (Arduino >> Examples >> Examples from Custom Libraries).
Upload the following code to your Arduino and run it to verify that it works:
#include <Filters.h>
int sensorPin = A1;
int sensorValue = 0;
int sensorPin2 = A0;
int sensorValue2 = 0;
float filterFrequency = 2.0;
FilterOnePole lowpassFilter( LOWPASS, filterFrequency);
void setup() {
Serial.begin(9600);
while(!Serial) {};
pinMode(sensorPin, INPUT);
pinMode(sensorPin2, INPUT);
}
void loop() {
sensorValue = analogRead(A1);
sensorValue2 = lowpassFilter.input( analogRead(A0) );
Serial.print(sensorValue);
Serial.print("\t");
Serial.println(sensorValue2);
}
There are a few updates to the code from the previous one in order to get it to work properly. These updates include:
3.) Run the Arduino code and check for your heart rate once more. You will obtain a graph similar to the one shown below:
IR Signal Outputs from Digitally Filtered Pin A0 (Red) and Analog Filtered Pin A1 (Blue)
4.) Congratulations! You have successfully applied a digital LPF with a cutoff frequency of 2.0 Hz to your red A0 analog signal. You should notice that unlike the previous graph obtained, this graph reveals a red A0 signal that is considerably smoother than the blue A1 signal. This is due to the fact that the digitally filtered A0 signal does a better job at filtering noise due to its digital nature. The blue A1 signal has an analog LPF, which means that the actual resistors and capacitors are responsible for filtering the signal, and due to their physical nature, the resistors and capacitors are subject to signal loss (e.g. faulty wires, noise from the breadboard, etc.).
From this result, we can conclude that the digital filter does a better job at filtering the noise from the IR signals compared to the analog filter. The digital filter is also easier implement as it does not require any physical circuit components like the analog filter!
1.) The digital LPF successfully improved the quality of our signal, but to fix the issue of inconsistent amplitudes, we can apply a derivative to the signal as well. To implement a basic derivative, we can just take the difference between the current analog output and the previous analog output. This is done in the following code:
#include <Filters.h>
int sensorPin = A1;
int sensorValue = 0;
int sensorPin2 = A0;
int sensorValue2 = 0;
int derivative = 0;
float filterFrequency = 2.0;
FilterOnePole lowpassFilter( LOWPASS, filterFrequency);
void setup() {
Serial.begin(9600);
while(!Serial) {};
pinMode(sensorPin, INPUT);
pinMode(sensorPin2, INPUT);
}
void loop() {
sensorValue = sensorValue2;
sensorValue2 = lowpassFilter.input( analogRead(A0) );
derivative = sensorValue2 - sensorValue;
Serial.println(derivative);
}
Upload the code into your Arduino and run it. You should get an output in the Serial Plotter similar to the following graph:
Serial Plotter Graph of Transformed Signal #1: Derivative
2.) Next, we will apply an inverse to the output signal. This can be done by simply inverting the output of the digital LPF signal in the code:
#include <Filters.h>
int sensorPin = A1;
int sensorValue = 0;
int sensorPin2 = A0;
int sensorValue2 = 0;
int derivative = 0;
float filterFrequency = 2.0;
FilterOnePole lowpassFilter( LOWPASS, filterFrequency);
void setup() {
Serial.begin(9600);
while(!Serial) {};
pinMode(sensorPin, INPUT);
pinMode(sensorPin2, INPUT);
}
void loop() {
sensorValue = sensorValue2;
sensorValue2 = lowpassFilter.input( analogRead(sensorPin2) );
sensorValue2 = -sensorValue2;
Serial.println(sensorValue2);
}
An inverse filter simply shows the inverse of the original output. This can be useful when you need to visualize a signal's change as a decremental transformation, rather than an incremental one. In other words, this will allow us to see the heart beat as a dip, rather than a spike. The output signal on Serial Plotter should look like the following:
Serial Plotter Graph of Transformed Signal #2: Inverse
3.) Another transformation of the signal we can do is the bandpass filter. The bandpass filter is simply a combination of the lowpass and high-pass filters. Here we will implement a bandpass filter at the 2.0 Hz frequency (LPF) and 1.0 Hz (HPF):
#include <Filters.h>
int sensorPin = A1;
int sensorValue = 0;
int sensorPin2 = A0;
int sensorValue2 = 0;
int derivative = 0;
float filterFrequency = 2.0;
float freq = 5.0;
FilterOnePole lowpassFilter( LOWPASS, filterFrequency);
FilterOnePole highpassFilter ( HIGHPASS, freq );
void setup() {
Serial.begin(9600);
while(!Serial) {};
pinMode(sensorPin, INPUT);
pinMode(sensorPin2, INPUT);
}
void loop() {
sensorValue = highpassFilter.input( analogRead(sensorPin2) );
sensorValue2 = lowpassFilter.input( sensorValue );
Serial.println(sensorValue2);
}
Note that the HPF and the LPF are applied at different frequencies. This is so that the LPF will filter signals above 2.0 Hz and the HPF will filter signals below 1.0 Hz. By doing so, we can visualize the effects of what a band-pass filter will have on our output signal. Bandpass filters are extremely useful for filtering signals within a specific frequency range (in our case, 1.0~2.0 Hz). The output should resemble the following graph:
Serial Plotter Graph of Transformed Signal #3: Band-pass Filter
1.) In this objective, we will explore alternative locations on the body that can be used to measure heart rate. So far, we have been using our finger to measure the blood pulse, but there could be regions of the body that will allow easier/more accurate readings. Therefore, we need to first modify our circuit to be more flexible for movement. This can be done by following the diagram in Figure 9. An actual picture of the circuit assembled is also shown to the right below:
Example of Assembled IR Circuit
2.) With our circuit assembled, we can now check for heart beats in different regions of the body. First, we will measure the heart rate from the fingertip as before. Reusing the code from Objective 1, we obtain the following output:
Serial Plotter Screenshot of IR Signal on Fingertip
3.) Next we obtain the heart rate data from our wrist. Follow the same procedure of obtaining heart rate except we will place the IR emitter/receiver on our wrist. The following output was plotted on Serial Plotter:
Serial Plotter Screenshot of IR Signal on Wrist
4.) Lastly, we will repeat the same procedure but for our neck right next to the atom's apple. The following output plot was obtained from measuring heart rate via the neck:
Serial Plotter Screenshot of IR Signal on Neck
5.) Congratulations, you have successfully completed this objective. Before we move on to the next objective, you should notice that there were some significant advantages/disadvantages of each different region that we measured heart rate from.
The fingertip was easy to measure a consistent heart beat from when compared to the other locations, but a disadvantage was that it can often be difficult to keep your fingertip extremely still. Movement in the finger can easily disrupt the signal output.
The wrist outputted inconsistent signals for the heart rate. However, it was much easier to keep your wrist still than the finger.
The neck was chosen as a location as most people check the neck for a pulse when measuring their heart rate. The neck seemed to output a signal quality worse than that of the fingertip, but better than the wrist. The neck is also quite an easy region to keep still during the heart rate measuring period.
1.) First, we need to assemble our circuit so that the OLED display is added. Refer back to Lab 1 for the diagram on how to wire the OLED display. The circuit should look similar to the one below:
Assembled Circuit With OLED Display
2.) Next, we want to write an Arduino code that will translate the IR signal data to something that can be displayed on the OLED display. For this objective, we will display the heart beat as the Serial Plotter displayed it, but on the OLED. There are some things to keep in mind:
Scaling Factor = Maximum Height of OLED / Maximum Range of IR Signal
Scaling Factor = 32 / 100
This is done simply by taking the scaled y-value of the IR signal and subtracting it from the maximum OLED height:
y-value = (Maximum OLED height) - (scaled y-value of IR signal)
y-value = 32 - (scaled y-value of IR signal)
With these points in mind, we develop the Arduino code below:
#include <Filters.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCALING 32/100 //Ratio of OLED height to IR signal
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH 16
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
B00000001, B11000000,
B00000001, B11000000,
B00000011, B11100000,
B11110011, B11100000,
B11111110, B11111000,
B01111110, B11111111,
B00110011, B10011111,
B00011111, B11111100,
B00001101, B01110000,
B00011011, B10100000,
B00111111, B11100000,
B00111111, B11110000,
B01111100, B11110000,
B01110000, B01110000,
B00000000, B00110000 };
#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
// ---------- Global Variables ----------------------------------
int sensorPin = A1;
int sensorValue = 0;
int x = 0;
float filterFrequency = 2.0;
FilterOnePole lowpassFilter( LOWPASS, filterFrequency );
// ------------- Set up -----------------------------------
void setup() {
Serial.begin(9600);
while(!Serial) {};
pinMode(sensorPin, INPUT);
// Initializing OLED
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
}
// -------------------- Loop ----------------------------------
void loop() {
sensorValue = lowpassFilter.input(analogRead(sensorPin));
int result = valueMapping(sensorValue);
Serial.println(result);
if (x <= 127 && x >= 0) {
drawPulse(x, result);
display.display();
x++;
}
else {
x = 0;
display.clearDisplay();
}
}
uint16_t valueMapping( uint16_t IR_signal ) {
// this function will take IR sensor output as inputs and
// convert it to data usable for the drawPulse() function
// The IR_signal value will be from 475 to 575
if (IR_signal < 475) {
IR_signal = 0;
}
else if (IR_signal > 575) {
IR_signal = 100;
}
else {
IR_signal = IR_signal - 475;
}
// Now we need to scale IR_signal to be a value within OLED's
// height (X range[0:127] and Y-range[0:31])
IR_signal = IR_signal * SCALING;
// Return the y-value for drawPulse() to use as input
return IR_signal;
}
void drawPulse( uint16_t x, uint16_t y) {
// Orient the y-value so that it adheres to the OLED's coordinate system
y = 32 - y;
// Now we display the pixel
display.drawPixel(x, y, WHITE);
}
Make sure to read through the comments in the code so you understand what the functions and variables are doing!
Run the Arduino code on your board, and watch the OLED display your heart beat. The result should looking something like the picture below:
OLED Heartbeat Display
1.) First we need to establish a circuit so that the HM-10 BLE module will be properly set up. Similar to the previous lab, Lab 1, we will implement a button to make sure the BLE module can go to sleep and wake up based on the button press. Below is a picture of the circuit:
Picture of BLE Circuit With Button
2.) Now we need to modify the code from Objective 5A to work with our new circuit. The code is implemented below:
#include <Filters.h>
#include <AltSoftSerial.h>
#define buttonPin 4
#define sensorPin A1
// ---------- Global Variables ----------------------------------
int sensorValue = 0;
int current_state = 0;
int last_state = 0;
boolean do_nothing = true;
/* Sleep has 3 states: 0, 1, and 2.
* Sleep == 0: Neutral/Idle
* Sleep == 1: BLE is sleeping
* Sleep == 2: BLE is waking
*/
int sleep_state = 0;
char c = ' ';
char buf[10];
boolean NL = true;
AltSoftSerial BTserial;
float filterFrequency = 2.0;
FilterOnePole lowpassFilter( LOWPASS, filterFrequency );
// ------------- Set up -----------------------------------
void setup() {
// Serial.begin(9600);
// while(!Serial) {};
pinMode(sensorPin, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
BTserial.begin(9600);
// Serial.println("BTserial Initializing...");
delay(2000);
BTserial.write("AT");
delay(200);
BTserial.write("AT+CLEAR");
delay(200);
BTserial.write("AT+RENEW");
delay(200);
BTserial.write("AT+RESET");
delay(500);
// Serial.println("BTserial Ready");
// BLE_sleep();
// Serial.println("BLE_sleep activated");
// delay(2000);
// Serial.println("Waking up BLE");
// BLE_wakeup();
current_state = digitalRead(buttonPin);
}
// -------------------- Loop ----------------------------------
void loop() {
button( &last_state, ¤t_state, &sleep_state, &do_nothing);
if (!do_nothing) {
if (sleep_state == 1) {
BLE_sleep();
do_nothing = true;
}
if (sleep_state == 2) {
BLE_wakeup();
do_nothing = true;
}
}
// We are going to send IR data from analog to BLE
if (sleep_state == 0 && do_nothing) {
sensorValue = lowpassFilter.input( analogRead(sensorPin) );
dtostrf(sensorValue, 4, 2, buf);
BTserial.write("$");
BTserial.write(buf);
delay(100);
// BTserial.write("\n");
}
}
// ----------------------- Check for button push ----------------
void button( int *last_state, int *current_state, int *sleep_state,
boolean *do_nothing)
{
*last_state = *current_state;
*current_state = digitalRead(buttonPin);
if ((*current_state == 0) && (*last_state == 1))
{
*do_nothing = false;
if (*sleep_state == 0) {
*sleep_state = 1;
}
else if (*sleep_state == 1) {
*sleep_state = 2;
}
}
else if (*sleep_state == 2) {
*sleep_state = 0;
}
}
// --------------------- Put BLE to sleep --------------------
void BLE_sleep ( ) {
BTserial.write("#sleep");
delay(500);
BTserial.write("AT");
delay(200);
BTserial.write("AT+ADTY3");
delay(200);
BTserial.write("AT+RESET");
delay(500);
BTserial.write("AT");
delay(200);
BTserial.write("AT+SLEEP");
delay(200);
}
// --------------------- Wake up BLE ---------------------------
void BLE_wakeup ( ) {
for (int i = 0; i < 80; i++) {
BTserial.write("$");
}
delay(500);
BTserial.write("AT+ADTY0");
delay(200);
BTserial.write("AT+RESET");
delay(500);
BTserial.write("#wakeup");
delay(200);
}
This Arduino sketch simply combines various features of our previous labs and objectives, such as the button press and sleep/wake function, so make sure to read through it and understand the code! Upload the sketch and run it on the Arduino to make sure the functions work as intended.
You can also disconnect the Arduino from the USB port to your computer and connect it to a 9V battery like in Lab 1. The sleep function allows the Arduino to save battery while running on 9V battery power!
3.) Now we want to write a Python script to read data via Bluetooth from the Arduino for 20 seconds, store it in a file, and plot the data points. The code is displayed below:
#Import libraries
import serial
import sys
import requests
import time
from time import sleep
import matplotlib.pyplot as plt
import numpy as np
# These variables are just defined commands for the BLE module
set_clear = "AT+CLEAR"
set_renew = "AT+RENEW"
set_reset = "AT+RESET"
set_role= "AT+ROLE1"
set_imme = "AT+IMME1"
set_adty = "AT+ADTY3"
set_AT = "AT"
# Make sure to change the MAC Address to your specific HM-10 Module's MAC Address
set_connect = "AT+CON3403DE349771"
# Reads Bluetooth input buffer and stores it into a string
def read_BLE (ser):
msg = ""
if (ser.in_waiting > 0):
msg = ser.readline(ser.in_waiting).decode('utf-8')
return msg
# Reads Bluetooth input buffer for 20 seconds and stores it into a string
def read_BLE_20seconds (ser):
msg = []
data = ""
t_end = time.time() + 20
while (time.time() < t_end):
if(ser.in_waiting > 0):
data = ser.readline(ser.in_waiting).decode('utf-8')
msg.append(data)
return msg
# Initializes the HM-10 Module to our preferred settings for this specific script
def initialize_BLE (ser):
ser.reset_input_buffer()
ser.write(set_AT.encode())
sleep(0.5)
print("> AT: " + read_BLE(ser))
ser.write(set_clear.encode())
sleep(0.5)
print("> CLEAR: " + read_BLE(ser))
ser.write(set_reset.encode())
sleep(0.5)
print("> RESET: " + read_BLE(ser))
sleep(2)
ser.write(set_renew.encode())
sleep(0.5)
print("> RENEW: " + read_BLE(ser))
sleep(2)
ser.write(set_reset.encode())
sleep(0.5)
print("> RESET: " + read_BLE(ser))
sleep(2)
ser.write(set_imme.encode())
sleep(0.5)
print("> IMME: " + read_BLE(ser))
ser.write(set_adty.encode())
sleep(0.5)
print("> ADTY: " + read_BLE(ser))
ser.write(set_role.encode())
sleep(0.5)
print("> ROLE: " + read_BLE(ser))
sleep(2)
ser.write(set_reset.encode())
sleep(0.5)
print("> RESET: " + read_BLE(ser))
sleep(2)
# Establish connection with HM-10 BLE Module
with serial.Serial(port='/dev/cu.usbserial', baudrate = 9600, timeout = 1) as ser:
sleep(2)
# Initialize the BLE module to our preferred settings
initialize_BLE(ser)
# Establishes a connection with the BLE module
ser.write(set_connect.encode())
sleep(0.5)
ble_in = read_BLE(ser)
print("> CONNECT: " + ble_in)
print("> Attempting to Connect...")
sleep(15)
ble_in = read_BLE(ser)
if ( ble_in.find('OK+CONNF') > -1 ):
print("Connection Failed")
connection_secure = False
else:
print("Connection Successful")
connection_secure = True
ble_in = ""
while (True):
# Once an initial BLE connection is established, this loop will execute until connection is lost
while (connection_secure):
# First read the BLE input buffer and determine if the Arduino's BLE has sent out the sleep flag: #sleep
ble_in = read_BLE(ser)
if (ble_in.find("#sleep") > -1 ):
connection_secure = False
ble_in = ""
print("> Connection Lost")
else :
ser.reset_input_buffer()
command = input("> 1.) Hit ENTER to write data into file for 20 second\n> 2.) Enter QUIT to exit:\t")
if (command == ""):
# Check again for the Arduino's BLE's flag for sleep: #sleep
ble_in = read_BLE(ser)
if (ble_in.find("#sleep") > -1 ):
connection_secure = False
ble_in = ""
print("> Connection Lost")
else :
# Read the BLE input buffer for 20 seconds, then write the data stored as a file
# named 'lab2_raw_data_jyjang.npy'
# Feel free to change the name of the file to whatever you like!
print("> Writing Data...")
data_list = read_BLE_20seconds(ser)
with open('lab2_raw_data_jyjang.npy', 'w') as my_file :
data_string = ''.join(data_list)
my_file.write(data_string)
# Once the files are done writing, we read the data into a new string and parse the
# string into a list of floats (We need float type numbers to plot the data)
print("> Done Writing...\n> Plotting Data...")
with open('lab2_raw_data_jyjang.npy', 'r') as my_file2 :
contents = my_file2.read()
data_list = contents.split('$')
data_list.remove('')
format_data_list = [float(i) for i in data_list]
plt.plot(format_data_list)
plt.ylabel('Heartbeat')
print("> Heartrate plot is being display...")
plt.show()
if (command == "QUIT" or command == "quit"):
print("> Exiting..")
sys.exit(0)
# This loop will execute until a connection has been reestablished
while (connection_secure == False):
ser.reset_input_buffer()
command = input("> 1.) Hit ENTER to try Reconnecting\n> 2.) Enter QUIT to exit:\t")
if (command == ""):
ser.write(set_connect.encode())
sleep(0.5)
ble_in = read_BLE(ser)
print("> " + ble_in)
print("> Attempting to Reconnect...")
sleep(15)
ble_in = read_BLE(ser)
print("> " + ble_in)
if ( ble_in.find('OK+CONNF') > -1 ):
print("> Reconnection Unsuccessful")
else:
print("> Reconnection Successful")
connection_secure = True
if (command == "QUIT" or command == "quit"):
sys.exit(0)
The code has been commented throughout for your ease of learning what the code actually does. Make sure to change the set_connect variable to your Arduino BLE module's MAC address, and the name of the output file to whatever you desire. By default, the file name is "lab2_raw_data_jyjang.npy". Run the Python script from Terminal and test the system together with the Arduino!
4.) Congratulations! You have successfully completed Objective 5B. The plot that you see from running the code should map out your heart beat, and it should look similar to the plot below:
Plot of Heartbeat Data From Python Data File