در این پروژه میخواهیم یک سیستم تشخیص و نظارت بر ضربان قلب بسازیم که با استفاده از بورد آردوینو و 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
امیدواریم که این مطلب برای شما مفید واقع شده باشد و بتوانید با ترکیب اینترنت اشیا و پروژههای آردوینو گجتهای کاربری را بسازید که زندگی انسانها را هوشمندتر کند. کامنت یادتون نره:)
اگر این نوشته برایتان مفید بود لطفا کامنت بنویسید.
عالی
خیلی عالی بود
کاش پی دی اف هم برای دانلود نوشته ها میزاشتید
عالی بود موفق باشید
این کد براتون ارورنداشت ؟
سلام. یه سوال دارم. چرا از وی ماس دی وان مینی پرو استفاده نکرده؟
اخه این دستگاه، ماژول وای فای رو هم داره.