در این پروژه میخواهیم یک سیستم تشخیص و نظارت بر ضربان قلب بسازیم که با استفاده از بورد آردوینو و ThingSpeak (یکی از پلتفرمهای اینترنت اشیا)، پیادهسازی میشود. این سیستم دادهها را با کمک سنسور تشخیص پالسی که در آن تعبیه میشود جمعآوری کرده و نتایج حاصل شده را با فرمت BPM (ضربان بر دقیقه) در LCD سیستم نمایش میدهد. علاوه بر آن این دادهها را با استفاده از ماژول وایفای ESP8266 که به آن متصل است، به سرور ThingSpeak نیز ارسال میکند و به این ترتیب ضربان قلب مذکور در هر نقطهای از دنیا از طریق اینترنت قابل ملاحظه و کنترل است.

اگر از قبل نام ThingSpeak را نشنیدهاید، باید بگوییم ThingSpeak یکی از بزرگترین پلتفرمها برای ارسال و دریافت آنلاین دادههاست که در هر زمان و هر مکانی که باشید میتوانید به آن دسترسی پیدا کنید.
قبلا هم یک پروژهی مانیتورینگ ضربان قلب را با هم پیادهسازی کردهایم با این تفاوت که در آنجا دادهها را روی ThingSpeak ارسال نمیکردیم و قابلیت دسترسی از طریق اینترنت را نداشت. اما این بار میخواهیم به قلمرو اینترنت اشیا قدم گذاشته و این قابلیت را نیز به پروژه اضافه کنیم.
آنچه که در ادامه نیاز خواهیم داشت.
- سنسور تشخیص ضربان
- ماژول وایفای ESP8266
- بورد Arduino Uno
- LCD
- برد بورد
- پتانسیومتر 10K
- مقاومت 1K
- تعدادی مقاومت ۲۲۰ اهم
- LED
- سیم رابط
نمودار مدار و توضیحات آن
ابتدا باید ماژول ESP8266 و بورد آردوینو را به هم متصل کنیم. این ماژول با ولتاژ ۳.۳ ولت کار میکند و اگر بخواهید ولتاژ ۵ ولت را از بورد آردوینو بگیرید و به آن متصل کنید، احتمال آنکه به درستی کار نکند و یا حتی آسیب ببیند وجود دارد. بنابراین VCC و CH_PD را به پین ۳.۳ ولت آردوینو متصل کنید.
پین RX ماژول وایفای با ۳.۳ ولت کار میکند. بنابراین اگر آن را مستقیما به آردوینو متصل کنید، کار نخواهد کرد. باید یک مدار مقسم ولتاژ را در میان آنها قرار دهیم که ولتاژ ۵ ولت را گرفته و ۳.۳ ولت آن را به پین RX بدهد. سادهترین مداری که میتوان در نظر گرفت، استفاده از سه مقاومت به صورت متوالی است. ما هم در بستن مدار از همین روش استفاده کردهایم.
پین TX ماژول وایفای را به پین شماره ۹ بورد آردوینو، و پین RX ماژول وایفای را به پین شماره ۱۰ بورد آردوینو متصل کنید.

وجود ماژول ارزان قیمت ESP8266 در سیستم شما موجب میشود که بتوانید به وایفای و اینترنت دسترسی داشته باشید. بنابراین بسیار آن را کاربردیتر خواهد ساخت. از دیگر قابلیتهای این ماژول این است میتواند به راحتی با اغلب میکروکنترلرها ارتباط برقرار کند و به همین علت به یکی از پرطرفدارترین ماژولهای وایفای در حوزهی IoT تبدیل شده است.
در قدم بعدی باید سنسور تشخیص ضربان را به بورد آردوینو متصل کنیم. خوشبختانه این سنسور تنها سه عدد پین دارد و متصل کردن آن کار بسیار راحتی است. کافیست پین ۵ ولت و پین زمین آن را به ترتیب به پینهای ۵ ولت و زمین بورد آردوینو متصل کنید و پین سیگنال آن را هم به پین A0 بورد.

LED را هم به پین ۱۳ بورد آردوینو متصل کنید. دقت داشته باشید که پین شماره ۱۳ خود دارای یک مقاومت درونی است و نیازی نیست که شما برای وصل کردن LED به آن یک مقاومت هم در مسیر قرار دهید.
و در نهایت برای اتصال LCD و بورد به یکدیگر مراحل زیر را انجام دهید.
- پین ۱ (VEE) را به زمین متصل کنید.
- پین شماره ۲ (VDD یا VCC) را به ۵ ولت متصل کنید.
- پین شماره ۳ (V0) را به پین وسطی پتانسیومتر متصل کنید و دو پایهی دیگر پتانسیومتر را به زمین و ۵ ولت وصل کنید. نقش پتانسیومتر در تنظیم کنتراست صفحهی LCD است بنابراین اگر پتانسیومتری با مقادیر غیر از 10K داشته باشید هم احتمالا قابل استفاده هستند.
- پین شماره ۴ (RS) را به پین شماره ۱۲ بورد آردوینو متصل کنید.
- پین شماره ۵ (Read/Write) را که از آن استفادهای نمیکنیم؛ به زمین وصل کنید.
- پین شماره ۶ (E) را هم به پین شماره ۱۱ آردوینو متصل کنید. پینهای RS و E پینهای کنترلی هستند و برای ارسال داده و کاراکتر از آنها استفاده میشود.
- این ۴ پین هم پینهای تبادل اطلاعات هستند که باید به این ترتیب متصل شوند.
– پین ۱۱ (D4) به پین شماره ۵ آردوینو
– پین ۱۲ (D5) به پین شماره ۴ آردوینو
– پین ۱۳ (D6) به پین شماره ۳ آردوینو
– پین ۱۴ (D7) به پین شماره ۲ آردوینو
- پین شماره ۱۵ را از طریق مقاومت ۲۲۰ اهمی به VCC وصل کنید. این مقاومت برای تنظیم شدت نور صفحهی نمایشگر است. اگر از مقاومتهای بزرگتری استفاده کنید صفحه تاریکتر خواهد شد.
- پین ۱۶ را به زمین وصل کنید.

تنظیمات مربوط به ThingSpeak
همانطور که ابتدای این مطلب هم گفتیم، ThingSpeak ابزار بسیار مناسب و پرطرفداری در اجرای پروژهها و سیستمهای مربوط به اینترنت اشیا محسوب میشود. با استفاده از سایت ThingSpeak، میتوانیم دادههای خود را از طریق اینترنت ببینیم و کنترل کنیم. در حقیقت ThingSpeak بستری است که دادهها را از سنسورهایی که به آن متصل شدهاند دریافت و جمعآوری کرده، آنها را آنالیز و دستهبندی میکند و در صورت نیاز میتواند پاسخهایی را نیز با توجه به دادههای دریافت شده ارسال کند. در ادامه مختصری در رابطه با چگونگی کار کردن این ابزار توضیح خواهیم داد.
در ابتدا هر کاربر باید یک حساب کاربری بر روی سایت ThingSpeak.com ایجاد کند. پس از آن وارد حساب خود شده و Get Started را انتخاب میکنیم.

در حساب کاربری خود به قسمت channels رفته و create a new channel را انتخاب میکنیم. نامها را انتخاب کرده و در قسمت پایین صفحه، تیک Make Public را میزنیم و در نهایت save میکنیم.
حالا کانال اختصاصی ما ساخته شده است.

پس از آن به قسمت API keys رفته و کلیدی که در آنجا میبینید را کپی کنید. از این کلید در زمان نوشتن کد استفاده خواهیم کرد. (کد را به صورت کامل در انتهای آموزش برایتان قرار دادهایم)

توضیح چگونگی عملکرد
ابتدا باید سنسور تشخیص ضربان را به قسمتی از بدن که امکان تشخیص ضربان وجود داشته باشد (مثلا به سر انگشتها) وصل کنیم.
این سنسور تغییرات حجم خون عبوری از رگها را اندازهگیری میکند. میدانیم که در هر بار پمپاژ شدن خون به بدن توسط قلب، حجم خون عبوری تغییر میکند. بنابراین این تغییر حجم نمایانگر ضربان قلب خواهد بود. اگر دقیقتر بخواهیم بگوییم؛ تغییر حجم خون موجب تغییر شدت نور ساطع شده از رگ شده و سنسور قادر است این تغییر شدت نور را تشخیص داده و به آن عنوان ضربان ثبت کند. آردوینو این داده را دریافت کرده و آن را به تعداد ضربان بر دقیقه (BPM) تبدیل میکند و این مقدار بر روی LCD نمایش داده میشود. LED متصل به بورد هم با هر بار رسیدن ضربان یک بار چشمک میزند.
از طرفی ماژول ESP8266 که به بورد آردوینو متصل است، دادهی تولیدی آن را دریافت کرده و از طریق اینترنت آن را به ThingSpeak ارسال میکند. اتصال این ماژول به شبکه، به واسطهی روتری است که در کد برنامه مشخص میکنیم.
دادههای دریافت شده در ThingSpeak ذخیره شده و در هر لحظه به صورت نموداری قابل دسترس هستند؛ چه دادههای مربوط به آن لحظه و چه دادههای لحظات قبل.

توضیحات کد پروژه
مانند همیشه در ابتدای کار، کتابخانههای مرتبط را اضافه میکنیم. کتابخانهی Software serial برای فعال کردن Rx و Tx بر روی پینهای شمارهی ۹ و ۱۰ است. دقت داشته باشید که پینهای پیشفرض برای Rx و Tx در آردوینو، پینهای شمارهی ۰ و ۱ هستند. اگر بخواهیم پینهای دیگری را به این منظور استفاده کنیم ، باید از این کتابخانه استفاده کنیم. کتابخانهی LiquidCrystal.h را هم برای اتصال و استفاده از LCD نیاز داریم. پس از آوردن آن پینهایی که قرار است LCD به آنها متصل شود را هم تعیین میکنیم.
#include <SoftwareSerial.h> #define DEBUG true SoftwareSerial esp8266(9,10); #include <LiquidCrystal.h> #include <stdlib.h> LiquidCrystal lcd(12,11,5,4,3,2);
نام شبکه وایفای، پسورد و آدرس آیپی ماژول ESP8266 را وارد میکنیم و سپس همان کلیدی که در مرحلهی قبل از ThingSpeak کپی کرده بودیم را هم وارد میکنیم.
#define SSID "Your Wifi Name" #define PASS "Your Wifi Password" #define IP "184.106.153.149" String msg = "GET /update?key=9YS21NU0HY5YS1IKU";
تکهی بعدی از کد، LCD را راهاندازی کرده و بادریت را تنظیم میکند. بادریت را با توجه به ماژول ESP8266 مشخص میکنیم. (هر کدام از ماژولهای ESP8266 بادریت مخصوص به خود را دارند. برخی از آنها ۹۶۰۰، برخی ۱۱۵۲۰۰ و تعدادی دیگر بادریتهای دیگری دارند)
void setup()
{
lcd.begin(16, 2);
lcd.print("circuitdigest.com");
delay(100);
lcd.setCursor(0,1);
lcd.print("Connecting...");
Serial.begin(9600); //or use default 115200.
esp8266.begin(9600);
Serial.println("AT");
esp8266.println("AT");
delay(5000);
if(esp8266.find("OK")){
connectWiFi();
}
interruptSetup();
}
تابع ()updatebeat، دادههای جمعآوری شده را به آدرس آیپی که مشخص کردیم ارسال کرده و همچنین داده را در همان فرمتی که برای ضربان قلب مشخص کردهایم تنظیم میکند.
void updatebeat(){
String cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += IP;
cmd += "\",80";
Serial.println(cmd);
esp8266.println(cmd);
delay(2000);
if(esp8266.find("Error")){
return;
}
cmd = msg ;
cmd += "&field1=";
cmd += BPM;
..... .....
...... .....
بخش بعدی کد ماژول ESP8266 را به شبکهای که تعیین کردهایم متصل کرده و دادهها را از طریق آن به سرورهای ThingSpeak ارسال میکند.
boolean connectWiFi(){
Serial.println("AT+CWMODE=1");
esp8266.println("AT+CWMODE=1");
delay(2000);
String cmd="AT+CWJAP=\"";
cmd+=SSID;
cmd+="\",\"";
cmd+=PASS;
cmd+="\"";
.... .....
..... .....
برای خواندن دادهها از سنسور و تبدیل آنها از مقادیر خام به مقادیر BPM و چشمک زدن LED با هر یک از ضربانهای دریافتی کد زیر را وارد کنید.
ISR(TIMER2_COMPA_vect){
cli();
Signal = analogRead(pulsePin);
sampleCounter += 2;
int N = sampleCounter - lastBeatTime;
if(Signal < thresh && N > (IBI/5)*3){
if (Signal < T){
T = Signal;
... ....
...... ..
بقیهی قسمتهای کد تقریبا به صورت کامل کامنتگذاری شدهاند و میتوانید به راحتی کارکرد بخشهای مختلف آن را کشف کنید.
کد
#include <SoftwareSerial.h>
#define DEBUG true
SoftwareSerial esp8266(9,10);
#include <LiquidCrystal.h>
#include <stdlib.h>
LiquidCrystal lcd(12,11,5,4,3,2);
#define SSID "Your Wifi Name" // "SSID-WiFiname"
#define PASS "Your Wifi Password" // "password"
#define IP "184.106.153.149"// thingspeak.com ip
String msg = "GET /update?key=9YS21NU0HY5YS1IKU"; //change it with your api key like "GET /update?key=Your Api Key"
//Variables
float temp;
int hum;
String tempC;
int error;
int pulsePin = 0; // Pulse Sensor purple wire connected to analog pin 0
int blinkPin = 13; // pin to blink led at each beat
int fadePin = 5;
int fadeRate = 0;
// Volatile Variables, used in the interrupt service routine!
volatile int BPM; // int that holds raw Analog in 0. updated every 2mS
volatile int Signal; // holds the incoming raw data
volatile int IBI = 600; // int that holds the time interval between beats! Must be seeded!
volatile boolean Pulse = false; // "True" when heartbeat is detected. "False" when not a "live beat".
volatile boolean QS = false; // becomes true when Arduino finds a beat.
// Regards Serial OutPut -- Set This Up to your needs
static boolean serialVisual = true; // Set to 'false' by Default. Re-set to 'true' to see Arduino Serial Monitor ASCII Visual Pulse
volatile int rate[10]; // array to hold last ten IBI values
volatile unsigned long sampleCounter = 0; // used to determine pulse timing
volatile unsigned long lastBeatTime = 0; // used to find IBI
volatile int P =512; // used to find peak in pulse wave, seeded
volatile int T = 512; // used to find trough in pulse wave, seeded
volatile int thresh = 525; // used to find instant moment of heart beat, seeded
volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM
void setup()
{
lcd.begin(16, 2);
lcd.print("circuitdigest.com");
delay(100);
lcd.setCursor(0,1);
lcd.print("Connecting...");
Serial.begin(9600); //or use default 115200.
esp8266.begin(9600);
Serial.println("AT");
esp8266.println("AT");
delay(5000);
if(esp8266.find("OK")){
connectWiFi();
}
interruptSetup();
}
void loop(){
lcd.clear();
start: //label
error=0;
lcd.setCursor(0, 0);
lcd.print("BPM = ");
lcd.print(BPM);
delay (100);
lcd.setCursor(0, 1); // set the cursor to column 0, line 2
delay(1000);
updatebeat();
//Resend if transmission is not completed
if (error==1){
goto start; //go to label "start"
}
delay(1000);
}
void updatebeat(){
String cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += IP;
cmd += "\",80";
Serial.println(cmd);
esp8266.println(cmd);
delay(2000);
if(esp8266.find("Error")){
return;
}
cmd = msg ;
cmd += "&field1=";
cmd += BPM;
cmd += "\r\n";
Serial.print("AT+CIPSEND=");
esp8266.print("AT+CIPSEND=");
Serial.println(cmd.length());
esp8266.println(cmd.length());
if(esp8266.find(">")){
Serial.print(cmd);
esp8266.print(cmd);
}
else{
Serial.println("AT+CIPCLOSE");
esp8266.println("AT+CIPCLOSE");
//Resend...
error=1;
}
}
boolean connectWiFi(){
Serial.println("AT+CWMODE=1");
esp8266.println("AT+CWMODE=1");
delay(2000);
String cmd="AT+CWJAP=\"";
cmd+=SSID;
cmd+="\",\"";
cmd+=PASS;
cmd+="\"";
Serial.println(cmd);
esp8266.println(cmd);
delay(5000);
if(esp8266.find("OK")){
Serial.println("OK");
return true;
}else{
return false;
}
}
void interruptSetup(){
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06; // DON'T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}
ISR(TIMER2_COMPA_vect){ // triggered when Timer2 counts to 124
cli(); // disable interrupts while we do this
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
// find the peak and trough of the pulse wave
if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T){ // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}
if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when there is a pulse
digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
IBI = sampleCounter - lastBeatTime; // time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse
if(secondBeat){ // if this is the second beat
secondBeat = false; // clear secondBeat flag
for(int i=0; i<=9; i++){ // seed the running total to get a realistic BPM at startup
rate[i] = IBI;
}
}
if(firstBeat){ // if it's the first time beat is found
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}
word runningTotal = 0; // clear the runningTotal variable
for(int i=0; i<=8; i++){ // shift data in the rate array
rate[i] = rate[i+1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}
rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM!
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS ISR
}
}
if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over
digitalWrite(blinkPin,LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}
if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 512; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
}
sei();
// enable interrupts when youre done!
}// end isr
ویدئو
منبع: ترجمه از سایت circuitdigest.com
امیدواریم که این مطلب برای شما مفید واقع شده باشد و بتوانید با ترکیب اینترنت اشیا و پروژههای آردوینو گجتهای کاربری را بسازید که زندگی انسانها را هوشمندتر کند. کامنت یادتون نره:)
اگر این نوشته برایتان مفید بود لطفا کامنت بنویسید.
آموزش طراحی و ساخت پروژه های الکترونیک و برنامه نویسی میکروکنترلر ها آموزش الکترونیک,آموزش رزبری پای,آموزش راه اندازی ماژول و سنسور,آموزش آردوینو,نرم افزار های الکترونیک, طراحیPCB,برنامه نویسی میکروکنترلرها ARM AVR PIC FPGA
عالی
خیلی عالی بود
کاش پی دی اف هم برای دانلود نوشته ها میزاشتید
عالی بود موفق باشید
این کد براتون ارورنداشت ؟
سلام. یه سوال دارم. چرا از وی ماس دی وان مینی پرو استفاده نکرده؟
اخه این دستگاه، ماژول وای فای رو هم داره.