В данной статье мы рассмотрим устройство для мониторинга сердечного ритма (пульса) на основе платы Arduino через сеть интернет, которое будет измерять пульс с помощью датчика импульсов и показывать измеренное значение пульса в BPM (Beats Per Minute – число ударов сердца в минуту). Также измеренные значения пульса мы с помощью Wi-Fi модуля ESP8266 будем передавать на сервер ThingSpeak, на котором их можно будет посмотреть из любой точки мира. Сервис ThingSpeak отлично подходит для подобных проектов, ранее мы рассматривали работу с ним в статье про мониторинг температуры через интернет.
Также ранее на нашем сайте мы рассматривали простой монитор сердечного ритма на основе Arduino, без передачи данных в сеть интернет. Рассматриваемый же в данной статье проект можно отнести к так называемой категории интернета вещей (IOT – Internet of Things).
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- Датчик импульсов/пульса (Pulse sensor) (купить на AliExpress).
- Wi-Fi модуль ESP8266 (купить на AliExpress).
- ЖК дисплей 16х2 (купить на AliExpress).
- Потенциометр 10 кОм (купить на AliExpress).
- Резисторы 1 кОм (купить на AliExpress).
- Резисторы 220 Ом (купить на AliExpress).
- Светодиод (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
Работа схемы
Вначале нам необходимо подключить модуль ESP8266 к плате Arduino. Модуль ESP8266 работает от напряжения 3.3V, поэтому если его подключить к контакту 5V платы Arduino, то его можно повредить. Поэтому следует подсоединить контакты VCC и CH_PD модуля ESP8266 к контакту 3.3V платы Arduino. Контакт RX модуля ESP8266 также работает от 3.3V и он также не будет взаимодействовать с платой Arduino если его подключить к ней непосредственно. Поэтому в нашей схеме мы будем использовать делитель напряжения чтобы конвертировать 5V в 3.3V. Подобный делитель напряжения можно сделать с помощью последовательного соединения 3-х резисторов как показано на схеме ниже. Соедините контакт TX модуля ESP8266 к контакту 9 платы Arduino, а контакт RX модуля ESP8266 к контакту 10 платы Arduino при помощи резисторов.
Wi-Fi модуль ESP8266 обеспечивает нашему устройству доступ в сеть интернет. Это достаточно дешевое устройство, которое можно подключить практически к любому микроконтроллеру, поэтому его так часто используют в проектах интернета вещей.
Затем необходимо подключить датчик импульсов к плате Arduino. Датчик импульсов имеет три контакта. Подсоедините контакт 5V и землю датчика к контактам 5V и земли платы Arduino, а информационный (сигнальный) контакт датчика – к контакту A0 платы Arduino.
После этого подсоедините светодиод к контакту 13 платы Arduino. Вам не нужно в данном случае использовать резистор поскольку плата Arduino имеет встроенный резистор на контакте 13.
И, наконец, нам останется только подключить ЖК дисплей к плате Arduino. В схеме необходимо будет сделать следующие соединения ЖК дисплея с платой Arduino:
- pin 1 (VEE) – на землю.
- pin 2 (VDD или VCC) - к 5V.
- pin 3 (V0) – к среднему контакту потенциометра 10 кОм, а остальные 2 контакта потенциометра – к контактам VCC и GND. Потенциометр будет использоваться для управления контрастностью ЖК дисплея. Можно использовать потенциометр с сопротивлением больше чем 10 кОм.
- pin 4 (RS) - к pin 12 платы Arduino.
- pin 5 (Read/Write) – к земле платы Arduino. Он используется редко, поэтому мы и замыкаем его на землю.
- pin 6 (E) – к pin 11 платы Arduino. Контакты RS и E являются управляющими контактами ЖК дисплея и используются для передачи данных и символов.
- далее соединяем 4 контакта данных ЖК дисплея:
pin 11 (D4) - к pin 5 платы Arduino.
pin 12 (D5) - к pin 4 платы Arduino.
pin 13 (D6) - к pin 3 платы Arduino.
pin 14 (D7) - к pin 2 платы Arduino.
- pin 15 - к VCC при помощи резистора 220 Ом. Резистор используется для установки яркости черного цвета ЖК дисплея. Чем больше сопротивление резистора, тем более темным будет черный цвет.
- pin 16 – на землю.
Получившаяся схема устройства представлена на следующем рисунке.
Конфигурирование сервиса ThingSpeak
Сервис ThingSpeak является очень удобным для проектов из категории интернета вещей. С его помощью можно мониторить данные и управлять нашей системой через сеть интернет, используя каналы и веб-страницы, предоставляемые данным сервисом. Более подробно мы его уже рассматривали в статье про мониторинг состояния погоды через сеть интернет, здесь же ограничимся более кратким его описанием.
Для начала зайдите на ThingSpeak.com и создайте там себе аккаунт (Get Started).
После создания аккаунта идите на вкладку каналы (channels) и создайте там себе новый канал. Заполните имя канала другие необходимые поля. Также поставьте галочку напротив ‘Make Public’ (сделать публичным, общедоступным) и сохраните канал (Save the Channel). После этого ваш канал создан.
После этого переключитесь на вкладку API keys и скопируйте оттуда ваш API key – он вам потребуется при написании кода программы.
Принцип работы проекта
Сначала необходимо прикрепить датчик импульсов (Pulse Sensor) к любой части вашего тела в которой можно обнаружить пульс, например, к пальцу. После этого датчик импульсов будет измерять изменения в объеме крови, которые происходят во время каждого сокращения сердечной мышцы. Эти изменения в объеме крови приводят к изменению интенсивности света через эту часть тела. Затем с помощью платы Arduino необходимо всего лишь конвертировать эти изменения в BPM (число ударов сердца в минуту). Светодиод, подключенный к контакту 13, также будет мигать в соответствии с частотой сердечного ритма.
Далее плата Arduino с помощью модуля ESP8266 будет передавать эти данные на сервис ThingSpeak. На сервисе ThingSpeak эти данные будут отображаться в виде графика, который можно будет посмотреть из любой точки мира. Также измеренный пульс (в BPM) будет отображаться на ЖК дисплее нашего устройства.
Исходный код программы
Первым делом при написании программы необходимо подключить все используемые библиотеки. Библиотека последовательной связи (Software serial library) используется для задействования режима последовательной связи (то есть последовательного порта) на контактах 9 и 10 платы Arduino. По умолчанию в плате Arduino для последовательной связи используются контакты 0 и 1, но с помощью Software serial library последовательную связь можно организовать и на других контактах платы Arduino. Также необходимо подключить библиотеку для работы с ЖК дисплеем (LiquidCrystal.h) и указать, к каким контактам платы Arduino подключен ЖК дисплей.
#include <SoftwareSerial.h>
#define DEBUG true
SoftwareSerial esp8266(9,10);
#include <LiquidCrystal.h>
#include <stdlib.h>
LiquidCrystal lcd(12,11,5,4,3,2);
Затем необходимо ввести имя пользователя Wi-Fi, пароль и IP адрес модуля ESP8266. Затем введите API key, который вы получили на сервисе ThingSpeak.
#define SSID "Your Wifi Name"
#define PASS "Your Wifi Password"
#define IP "184.106.153.149"
String msg = "GET /update?key=9YS21NU0HY5YS1IKU";
Следующая часть кода инициализирует ЖК дисплей и устанавливает бодовую скорость передачи. Скорость передачи необходимо установить в соответствии с тем, какую скорость поддерживает ваш модуль ESP8266. Некоторые модули ESP8266 поддерживают скорость передачи 9600 бод/с, некоторые 115200, а некоторые – другую.
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 updatebeat() будет передавать данные по IP адресу, который вы ввели, а также устанавливать необходимые значения полей для пульса.
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 с сетью Wi-Fi, данные для которой вы ввели ранее. Затем эта Wi-Fi сеть будет использоваться для передачи данных на 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 (число ударов в минуту). Также она будет управлять миганием светодиода, подключенного к контакту 13, в соответствии с частотой сердечного ритма.
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;
... ....
...... ..
Далее приведен полный текст программы.
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 |
#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 |
Статья переведена с иностранного сайта, комментарии к основному тексту программы почти не переводил. Но если статья вызовет интерес (напишите об этом в комментариях здесь), то готов перевести их. Ну и если есть какие-либо вопросы по теме статьи, также готов помочь разобраться в них