Sensors

Enter Your Electronics & Design Project for a chance to win a $200 shopping cart!

Submit an EntrySubmit an Entry  Back to homepage
Project14 Home
Monthly Themes
Monthly Theme Poll

 

ARDUINO BLUETOOTH HEARTBEAT

MONITORING SYSTEM

 

 

 

 

 

 

 

 

Part 1: Introduction

Part 2: Hardware

Part 3: Software

Part 4: Interfacing

Part 5: Conclusion

 

 

Part 1: Introduction

 

 

             A pulse wave is a change in the volume of a blood vessel that occurs when the heart pumps blood, and a detector that monitors this volume change is called a pulse sensor. Heart rate can be measured in many different ways. The figure below shows the different ways of measuring the heart rate.

 

 

 

 

The two most common techniques are electrical and optical methods. In principle the heartbeat sensor is based on the principle of photoplethysmography, whereby the heart rate is measured by sensing the changes in blood flow through the index finger. The flow of blood volume is decided by the rate of heart pulses and since light is absorbed by the blood, the signals are equivalent to the heartbeat pulses. There are two types of photoplethysmography:

 

Transmission : Light emitted from the light-emitting device is transmitted through any vascular region of the body like the earlobe and received by the detector.

 

Reflection : Light emitted from the light-emitting device is reflected by the regions.

 

The below image illustrates how these two methods are applied.

 

 

A plot for this change recorded against time is named as photoplethysmographic (PPG) waveform.

 

 

 

 

 

     Generally, by looking at the period of fluctuation from the waveform obtained by measurements of the pulse wave sensor and observing the pulsation (variation) using the heart rate along with both red and infrared waves, it is possible to measure the arterial blood oxygen saturation (SpO2). Now let's look into how the heartbeat sensor works. The basic heartbeat sensor consists of a light-emitting diode and a light detecting sensor. The heartbeat pulse cause a variation in the flow of blood to different regions of the body. When tissue is illuminated with the light source, i.e. light emitted by the led, it either reflects or transmits the light. Some of the lights is absorbed by the blood and the transmitted or the reflected light is received by the light detector. The amount of light absorbed depends on the blood volume in that tissue. The detector output is in the form of the electrical signal and is proportional to the heartbeat rate, as shown in the above figure.

    

     This signal is a DC  signal relating to the tissues and the blood volume and the AC component synchronous with the heartbeat and caused by pulsatile changes in the arterial blood volume is superimposed on the DC signal. We will later look into more details of the pulse sensor in Part 2.

 


 

Part 2: Hardware

 

     The main sensor that we will be using in this project, is the pulse sensor.  Before going further, let's look into the details of this pulse sensor. Below is the figure of the pulse sensor

 

 

Front view of the pulse sensorRear view of the pulse sensor

 

 

 

 

And below is the schematic of the pulse sensor

 

 

 

 

 

 

     The schematic and PCB file is open source and can be downloaded from here. To explain more on the pulse sensor, on the front of the sensor is the heart logo. This is the side that makes contact with the skin. On the front, you see a small round hole, which is where the LED shines through from the back, and there is also a little square just under the LED. The square is an ambient light sensor, similar to the one used in laptops, tablets, and cellphones, to adjust the screen's brightness in different light conditions. The LED shines a light into the fingertip or other capillary tissue and the sensor reads the light that bounces back. The back of the sensor is where the rest of the parts are mounted. That's a very brief idea of the pulse sensor. This sensor can be directly connected to Arduino or any other microcontroller. This sensor comes with three pins. The figure shows the name of each pins.

 

 

 

 

And the spec of the sensor is as shown in the table below

 

 

 

 

 

 

 

That's about the pulse sensor. Next, we shall see our next component/device which is the microcontroller. For this project, i have decided to use the Arduino Nano. The reason for me to choose this model is due to its small form factor. The figure below shows the image of the Arduino Nano and its corresponding pin.

 

 

 

 

 

We will like to display the heartbeat in a cool way, hence decided to use the 1.44" tft display and their respective pin layout.

 

 

 

 

 

 

And the next device/component that we would like to see is the Bluetooth device. We would like to send the data from the pulse sensor to mobile devices. The type of bluetooth devices which we have decided to use is the HC05. The figure illustrates the HC05

 

 

 

 

 

 

These are the three main components of this project. Other than this we have used miscellaneous components such as the trimmer pot, and others as well. All the components used are covered in the BOM list. Now let's move on to designing the circuit for this project. For this, I have used Eagle. The figure below shows the schematic.
And below is the BOM of the schematic
I have decided to build this project on a PCB, below is the PCB layout of this schematic
The figure below shows the component populated on the board.
Once that we have our board ready, we have one last thing to get done before we can start to program our board. We need to get our pulse sensor ready for we can start to calibrate or put out the sensor into action. There are many ways to get the pulse sensor attached to the finger. One example is to use the velcro strap or any other method as well. In this project, I have decided to 3d print the pulse sensor holder. The figure below shows the 3d printed case for the pulse sensor.
Header 1Header 2Header 3

 

The file for the 3d print can be downloaded here. Once all are set, next we move on the coding part.

Part 3: Software

 

In this part, we shall look into the software that we use to build our project. Definitely we use the Arduino IDE, and apart from that, we shall use other software as well. Firstly we would like to calibrate our sensor. For this, we will use the sample code and view it in the serial monitor. First step is to ensure that we have installed the pulse sensor library. If not installed yet, please follow the steps below

 

 

 

 

Once the library is installed as shown in figure above, we can try to run the first example as shown in the figure below

 

 

 

As can be seen from the figure above there are a lot of examples provided within the library. The image below shows a better view of the examples

 

 

We will first run the "Getting_BPM_to_Monitor". Below is the snapshot of the code.

 

 

 

Basically, the code is from the pulse sensor example. The pin number for the pulse wire is changed from A0 to A6 to suit our design. Next, we upload the code to the board and see the serial output.

 

 

And the video below shows the heartbeat in the serial plotter.

 

 

The default threshold value is 500. And it can be changed to suit our sensor based on the reading we get through the serial monitor. Next, we shall move on to design the display for the TFT display. As we have seen in part 3, we are using a 1.44" TFT display. In order to get this running, we will be using the Adafruit_GFX.h and TFT_ILI9163C.h library. And to test that our display is working fine and wired properly we will try to run the sample code as shown below

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
/***************************************************  This is our GFX example for the Adafruit ILI9341 Breakout and Shield  ----> http://www.adafruit.com/products/1651  Check out the links above for our tutorials and wiring diagrams  These displays use SPI to communicate,
4 or 5 pins are required to
  interface (RST is optional)  Adafruit invests time and resources providing this open source code, 
please support Adafruit and open-source hardware by purchasing
  products from Adafruit!  Written by Limor Fried/Ladyada for Adafruit Industries.  MIT license, all text above must be included in any redistribution
****************************************************/ #include "SPI.h"#include "Adafruit_GFX.h"#include "Adafruit_ILI9341.h" #define __CS 10#define __DC 8#define Buzzer 4TFT_ILI9163C tft = TFT_ILI9163C(__CS, 9, __DC); // If using the breakout, change pins as desired//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);void setup() {   Serial.begin(9600);   Serial.println("ILI9341 Test!");   tft.begin();   // read diagnostics (optional but can help debug problems)  uint8_t x = tft.readcommand8(ILI9341_RDMODE);   Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);   x = tft.readcommand8(ILI9341_RDMADCTL);   Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);   x = tft.readcommand8(ILI9341_RDPIXFMT);   Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);   x = tft.readcommand8(ILI9341_RDIMGFMT);   Serial.print("Image Format: 0x"); Serial.println(x, HEX);   x = tft.readcommand8(ILI9341_RDSELFDIAG);   Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX);     Serial.println(F("Benchmark                Time (microseconds)"));   delay(10);   Serial.print(F("Screen fill              "));   Serial.println(testFillScreen());   delay(500);   Serial.print(F("Text                     "));   Serial.println(testText());   delay(3000);   Serial.print(F("Lines                    "));   Serial.println(testLines(ILI9341_CYAN));   delay(500);   Serial.print(F("Horiz/Vert Lines         "));   Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE));   delay(500);   Serial.print(F("Rectangles (outline)     "));   Serial.println(testRects(ILI9341_GREEN));   delay(500);   Serial.print(F("Rectangles (filled)      "));   Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA));   delay(500);   Serial.print(F("Circles (filled)         "));   Serial.println(testFilledCircles(10, ILI9341_MAGENTA));   Serial.print(F("Circles (outline)        "));   Serial.println(testCircles(10, ILI9341_WHITE));   delay(500);   Serial.print(F("Triangles (outline)      "));   Serial.println(testTriangles());   delay(500);   Serial.print(F("Triangles (filled)       "));   Serial.println(testFilledTriangles());   delay(500);   Serial.print(F("Rounded rects (outline)  "));   Serial.println(testRoundRects());   delay(500);   Serial.print(F("Rounded rects (filled)   "));   Serial.println(testFilledRoundRects());   delay(500);   Serial.println(F("Done!")); } void loop(void) {   for(uint8_t rotation=0; rotation<4; rotation++) {     tft.setRotation(rotation);     testText();     delay(1000);   } } unsigned long testFillScreen() {   unsigned long start = micros();   tft.fillScreen(ILI9341_BLACK);   yield();   tft.fillScreen(ILI9341_RED);   yield();   tft.fillScreen(ILI9341_GREEN);   yield();   tft.fillScreen(ILI9341_BLUE);   yield();   tft.fillScreen(ILI9341_BLACK);   yield();   return micros() - start; } unsigned long testText() {   tft.fillScreen(ILI9341_BLACK);   unsigned long start = micros();   tft.setCursor(0, 0);   tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);   tft.println("Hello World!");   tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2);   tft.println(1234.56);   tft.setTextColor(ILI9341_RED);    tft.setTextSize(3);   tft.println(0xDEADBEEF, HEX);   tft.println();   tft.setTextColor(ILI9341_GREEN);   tft.setTextSize(5);   tft.println("Groop");   tft.setTextSize(2);   tft.println("I implore thee,");   tft.setTextSize(1);   tft.println("my foonting turlingdromes.");   tft.println("And hooptiously drangle me");   tft.println("with crinkly bindlewurdles,");   tft.println("Or I will rend thee");   tft.println("in the gobberwarts");   tft.println("with my blurglecruncheon,");   tft.println("see if I don't!");   return micros() - start; } unsigned long testLines(uint16_t color) {   unsigned long start, t;   int           x1, y1, x2, y2,                 w = tft.width(),                 h = tft.height();   tft.fillScreen(ILI9341_BLACK);   yield();     x1 = y1 = 0;   y2    = h - 1;   start = micros();   for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);   x2    = w - 1;   for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);   t     = micros() - start; // fillScreen doesn't count against timing  yield();   tft.fillScreen(ILI9341_BLACK);   yield();   x1    = w - 1;   y1    = 0;   y2    = h - 1;   start = micros();   for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);   x2    = 0;   for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);   t    += micros() - start;   yield();   tft.fillScreen(ILI9341_BLACK);   yield();   x1    = 0;   y1    = h - 1;   y2    = 0;   start = micros();   for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);   x2    = w - 1;   for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);   t    += micros() - start;   yield();   tft.fillScreen(ILI9341_BLACK);   yield();   x1    = w - 1;   y1    = h - 1;   y2    = 0;   start = micros();   for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);   x2    = 0;   for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);   yield();   return micros() - start; } unsigned long testFastLines(uint16_t color1, uint16_t color2) {   unsigned long start;   int           x, y, w = tft.width(), h = tft.height();   tft.fillScreen(ILI9341_BLACK);   start = micros();   for(y=0; y<h; y+=5) tft.drawFastHLine(0, y, w, color1);   for(x=0; x<w; x+=5) tft.drawFastVLine(x, 0, h, color2);   return micros() - start; } unsigned long testRects(uint16_t color) {   unsigned long start;   int           n, i, i2,                 cx = tft.width()  / 2,                 cy = tft.height() / 2;   tft.fillScreen(ILI9341_BLACK);   n     = min(tft.width(), tft.height());   start = micros();   for(i=2; i<n; i+=6) {     i2 = i / 2;     tft.drawRect(cx-i2, cy-i2, i, i, color);   }   return micros() - start; } unsigned long testFilledRects(uint16_t color1, uint16_t color2) {   unsigned long start, t = 0;   int           n, i, i2,                 cx = tft.width()  / 2 - 1,                 cy = tft.height() / 2 - 1;   tft.fillScreen(ILI9341_BLACK);   n = min(tft.width(), tft.height());   for(i=n; i>0; i-=6) {     i2    = i / 2;     start = micros();     tft.fillRect(cx-i2, cy-i2, i, i, color1);     t    += micros() - start;     // Outlines are not included in timing results    tft.drawRect(cx-i2, cy-i2, i, i, color2);     yield();   }   return t; } unsigned long testFilledCircles(uint8_t radius, uint16_t color) {   unsigned long start;   int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2;   tft.fillScreen(ILI9341_BLACK);   start = micros();   for(x=radius; x<w; x+=r2) {     for(y=radius; y<h; y+=r2) {       tft.fillCircle(x, y, radius, color);     }   }   return micros() - start; } unsigned long testCircles(uint8_t radius, uint16_t color) {   unsigned long start;   int           x, y, r2 = radius * 2,                 w = tft.width()  + radius,                 h = tft.height() + radius;   // Screen is not cleared for this one -- this is  // intentional and does not affect the reported time.  start = micros();   for(x=0; x<w; x+=r2) {     for(y=0; y<h; y+=r2) {       tft.drawCircle(x, y, radius, color);     }   }   return micros() - start; } unsigned long testTriangles() {   unsigned long start;   int           n, i, cx = tft.width()  / 2 - 1,                       cy = tft.height() / 2 - 1;   tft.fillScreen(ILI9341_BLACK);   n     = min(cx, cy);   start = micros();   for(i=0; i<n; i+=5) {     tft.drawTriangle(       cx    , cy - i, // peak      cx - i, cy + i, // bottom left      cx + i, cy + i, // bottom right      tft.color565(i, i, i));   }   return micros() - start; } unsigned long testFilledTriangles() {   unsigned long start, t = 0;   int           i, cx = tft.width()  / 2 - 1,                    cy = tft.height() / 2 - 1;   tft.fillScreen(ILI9341_BLACK);   start = micros();   for(i=min(cx,cy); i>10; i-=5) {     start = micros();     tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,       tft.color565(0, i*10, i*10));     t += micros() - start;     tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,       tft.color565(i*10, i*10, 0));     yield();   }   return t; } unsigned long testRoundRects() {   unsigned long start;   int           w, i, i2,                 cx = tft.width()  / 2 - 1,                 cy = tft.height() / 2 - 1;   tft.fillScreen(ILI9341_BLACK);   w     = min(tft.width(), tft.height());   start = micros();   for(i=0; i<w; i+=6) {     i2 = i / 2;     tft.drawRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(i, 0, 0));   }   return micros() - start; } unsigned long testFilledRoundRects() {   unsigned long start;   int           i, i2,                 cx = tft.width()  / 2 - 1,                 cy = tft.height() / 2 - 1;   tft.fillScreen(ILI9341_BLACK);   start = micros();   for(i=min(tft.width(), tft.height()); i>20; i-=6) {     i2 = i / 2;     tft.fillRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(0, i, 0));     yield();   }   return micros() - start; }

 

Once the code is successfully compiled and uploaded to our board, the display should display as shown in the video below

 

 

Next, we shall move on to design the display which portrays the heartbeat on the TFT LCD. For this project, we will display the same value which is displayed in the serial monitor on the screen.

 

Part 4: Interfacing

 

In this part, we shall see how to integrate the software and the hardware which we have developed previously and of course with some modification. Below is the video of the result of the interfacing between the hardware and the software.

 

 

 

 

 

 

 

Part 5: Conclusion

 

Currently, this is the progress of this project. As for the Bluetooth part, I am still working on it and will update it the soonest time. Hope you enjoy this blog.

 

Thank you.