-
Notifications
You must be signed in to change notification settings - Fork 1
/
02SPO2_BPM.ino
207 lines (189 loc) · 9.17 KB
/
02SPO2_BPM.ino
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
//https://youtu.be/ghTtpUTSc4o
//安裝程式庫及版本:1.Adafruit SSD1306(2.4.6版)、2.Adafruit GFX(1.10.12版)、3.MAX30105(不限)、4.ESP32Servo(不限版本)
#include <Adafruit_GFX.h> //OLED libraries
#include <Adafruit_SSD1306.h> //OLED libraries
#include "MAX30105.h" //MAX3010x library
#include "heartRate.h" //Heart rate calculating algorithm
//#include "ESP32Servo.h"
MAX30105 particleSensor;
//計算心跳用變數
const byte RATE_SIZE = 4; //多少平均數量
byte rates[RATE_SIZE]; //心跳陣列
byte rateSpot = 0;
long lastBeat = 0; //Time at which the last beat occurred
float beatsPerMinute;
int beatAvg;
//計算血氧用變數
double avered = 0;
double aveir = 0;
double sumirrms = 0;
double sumredrms = 0;
double SpO2 = 0;
double ESpO2 = 60.0;//初始值
double FSpO2 = 0.7; //filter factor for estimated SpO2
double frate = 0.95; //low pass filter for IR/red LED value to eliminate AC component
int i = 0;
int Num = 30;//取樣30次才計算1次
#define FINGER_ON 7000 //紅外線最小量(判斷手指有沒有上)
#define MINIMUM_SPO2 60.0 //血氧最小量
//OLED設定
#define SCREEN_WIDTH 128 //OLED寬度
#define SCREEN_HEIGHT 64 //OLED高度
#define OLED_RESET -1 //Reset pin
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //Declaring the display name (display)
//心跳小圖
static const unsigned char PROGMEM logo2_bmp[] =
{ 0x03, 0xC0, 0xF0, 0x06, 0x71, 0x8C, 0x0C, 0x1B, 0x06, 0x18, 0x0E, 0x02, 0x10, 0x0C, 0x03, 0x10,
0x04, 0x01, 0x10, 0x04, 0x01, 0x10, 0x40, 0x01, 0x10, 0x40, 0x01, 0x10, 0xC0, 0x03, 0x08, 0x88,
0x02, 0x08, 0xB8, 0x04, 0xFF, 0x37, 0x08, 0x01, 0x30, 0x18, 0x01, 0x90, 0x30, 0x00, 0xC0, 0x60,
0x00, 0x60, 0xC0, 0x00, 0x31, 0x80, 0x00, 0x1B, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x04, 0x00,
};
//心跳大圖
static const unsigned char PROGMEM logo3_bmp[] =
{ 0x01, 0xF0, 0x0F, 0x80, 0x06, 0x1C, 0x38, 0x60, 0x18, 0x06, 0x60, 0x18, 0x10, 0x01, 0x80, 0x08,
0x20, 0x01, 0x80, 0x04, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0xC0, 0x00, 0x08, 0x03,
0x80, 0x00, 0x08, 0x01, 0x80, 0x00, 0x18, 0x01, 0x80, 0x00, 0x1C, 0x01, 0x80, 0x00, 0x14, 0x00,
0x80, 0x00, 0x14, 0x00, 0x80, 0x00, 0x14, 0x00, 0x40, 0x10, 0x12, 0x00, 0x40, 0x10, 0x12, 0x00,
0x7E, 0x1F, 0x23, 0xFE, 0x03, 0x31, 0xA0, 0x04, 0x01, 0xA0, 0xA0, 0x0C, 0x00, 0xA0, 0xA0, 0x08,
0x00, 0x60, 0xE0, 0x10, 0x00, 0x20, 0x60, 0x20, 0x06, 0x00, 0x40, 0x60, 0x03, 0x00, 0x40, 0xC0,
0x01, 0x80, 0x01, 0x80, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x30, 0x0C, 0x00,
0x00, 0x08, 0x10, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00
};
//氧氣圖示
static const unsigned char PROGMEM O2_bmp[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x3f, 0xc3, 0xf8, 0x00, 0xff, 0xf3, 0xfc,
0x03, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xfe, 0x0f, 0xff, 0xff, 0xfe, 0x0f, 0xff, 0xff, 0x7e,
0x1f, 0x80, 0xff, 0xfc, 0x1f, 0x00, 0x7f, 0xb8, 0x3e, 0x3e, 0x3f, 0xb0, 0x3e, 0x3f, 0x3f, 0xc0,
0x3e, 0x3f, 0x1f, 0xc0, 0x3e, 0x3f, 0x1f, 0xc0, 0x3e, 0x3f, 0x1f, 0xc0, 0x3e, 0x3e, 0x2f, 0xc0,
0x3e, 0x3f, 0x0f, 0x80, 0x1f, 0x1c, 0x2f, 0x80, 0x1f, 0x80, 0xcf, 0x80, 0x1f, 0xe3, 0x9f, 0x00,
0x0f, 0xff, 0x3f, 0x00, 0x07, 0xfe, 0xfe, 0x00, 0x0b, 0xfe, 0x0c, 0x00, 0x1d, 0xff, 0xf8, 0x00,
0x1e, 0xff, 0xe0, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00,
0x0f, 0xe0, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
int Tonepin = 4; //蜂鳴器腳位
void setup() {
Serial.begin(115200);
Serial.println("System Start");
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Start the OLED display
display.display();
delay(3000);
//檢查
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
{
Serial.println("找不到MAX30102");
while (1);
}
//以下選項可自行調整
byte ledBrightness = 0x7F; //亮度建議=127, Options: 0=Off to 255=50mA
byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
byte ledMode = 2; //Options: 1 = Red only(心跳), 2 = Red + IR(血氧)
int sampleRate = 800; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
int pulseWidth = 215; //Options: 69, 118, 215, 411
int adcRange = 16384; //Options: 2048, 4096, 8192, 16384
// Set up the wanted parameters
particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
particleSensor.enableDIETEMPRDY();
particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running
particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED
}
void loop() {
long irValue = particleSensor.getIR(); //Reading the IR value it will permit us to know if there's a finger on the sensor or not
//是否有放手指
if (irValue > FINGER_ON ) {
//檢查是否有心跳,測量心跳
if (checkForBeat(irValue) == true) {
//心跳動畫
display.clearDisplay();//清除螢幕
display.drawBitmap(0, 0, logo3_bmp, 32, 32, WHITE);//顯示大的心跳圖示
display.setTextSize(2);//設定文字大小
display.setTextColor(WHITE);//文字顏色
display.setCursor(42, 10);//設定游標位置
//display.print(beatAvg); display.println(" BPM");//顯示心跳數值
display.drawBitmap(0, 35, O2_bmp, 32, 32, WHITE);//顯示氧氣圖示
display.setCursor(42, 40);//設定游標位置
//顯示血氧數值
if (beatAvg > 30) display.print(String(ESpO2) + "%");
else display.print("---- %" );
display.display();//顯示螢幕
//tone(Tonepin, 1000);//發出聲音
//delay(10);
//noTone(Tonepin);//停止聲音
//Serial.print("Bpm="); Serial.println(beatAvg);//將心跳顯示到序列
long delta = millis() - lastBeat;//計算心跳差
lastBeat = millis();
beatsPerMinute = 60 / (delta / 1000.0);//計算平均心跳
if (beatsPerMinute < 255 && beatsPerMinute > 20) {
//心跳必須再20-255之間
rates[rateSpot++] = (byte)beatsPerMinute; //儲存心跳數值陣列
rateSpot %= RATE_SIZE;
beatAvg = 0;//計算平均值
for (byte x = 0 ; x < RATE_SIZE ; x++) beatAvg += rates[x];
beatAvg /= RATE_SIZE;
}
}
//測量血氧
uint32_t ir, red ;
double fred, fir;
particleSensor.check(); //Check the sensor, read up to 3 samples
if (particleSensor.available()) {
i++;
ir = particleSensor.getFIFOIR(); //讀取紅外線
red = particleSensor.getFIFORed(); //讀取紅光
//Serial.println("red=" + String(red) + ",IR=" + String(ir) + ",i=" + String(i));
fir = (double)ir;//轉double
fred = (double)red;//轉double
aveir = aveir * frate + (double)ir * (1.0 - frate); //average IR level by low pass filter
avered = avered * frate + (double)red * (1.0 - frate);//average red level by low pass filter
sumirrms += (fir - aveir) * (fir - aveir);//square sum of alternate component of IR level
sumredrms += (fred - avered) * (fred - avered); //square sum of alternate component of red level
if ((i % Num) == 0) {
double R = (sqrt(sumirrms) / aveir) / (sqrt(sumredrms) / avered);
SpO2 = -23.3 * (R - 0.4) + 120;
ESpO2 = FSpO2 * ESpO2 + (1.0 - FSpO2) * SpO2;//low pass filter
if (ESpO2 <= MINIMUM_SPO2) ESpO2 = MINIMUM_SPO2; //indicator for finger detached
if (ESpO2 > 100) ESpO2 = 99.9;
//Serial.print(",SPO2="); Serial.println(ESpO2);
sumredrms = 0.0; sumirrms = 0.0; SpO2 = 0;
i = 0;
}
particleSensor.nextSample(); //We're finished with this sample so move to next sample
}
//將數據顯示到序列
Serial.print("Bpm:" + String(beatAvg));
//顯示血氧數值,避免誤測,規定心跳超過30才能顯示血氧
if (beatAvg > 30) Serial.println(",SPO2:" + String(ESpO2));
else Serial.println(",SPO2:" + String(ESpO2));
//將數據顯示到OLED顯示器
display.clearDisplay();//清除螢幕
display.drawBitmap(5, 5, logo2_bmp, 24, 21, WHITE);//顯示小的心跳圖示
display.setTextSize(2);//設定文字大小
display.setTextColor(WHITE);//文字顏色
display.setCursor(42, 10);//設定游標位置
display.print(beatAvg); display.println(" BPM");//顯示心跳數值
display.drawBitmap(0, 35, O2_bmp, 32, 32, WHITE);//顯示氧氣圖示
display.setCursor(42, 40);//設定游標位置
//顯示血氧數值,避免誤測,規定心跳超過30才能顯示血氧
if (beatAvg > 30) display.print(String(ESpO2) + "%");
else display.print("---- %" );
display.display();//顯示螢幕
}
//沒偵測到手指,清除所有數據及螢幕內容顯示"Finger Please"
else {
//清除心跳數據
for (byte rx = 0 ; rx < RATE_SIZE ; rx++) rates[rx] = 0;
beatAvg = 0; rateSpot = 0; lastBeat = 0;
//清除血氧數據
avered = 0; aveir = 0; sumirrms = 0; sumredrms = 0;
SpO2 = 0; ESpO2 = 90.0;
//顯示Finger Please
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(30, 5);
display.println("Finger");
display.setCursor(30, 35);
display.println("Please");
display.display();
//noTone(Tonepin);
}
}