在前三篇文章了解了三个外接模块的使用方法之后,我们开始将三个模块整合起来。
头文件、宏定义与变量声明
头文件
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_MLX90614.h>
#include "stdlib.h"
宏定义
#define SCREEN_WIDTH 128 // OLED 屏幕宽, 单位像素点数
#define SCREEN_HEIGHT 64 // OLED 屏幕长, 单位像素点数
#define OLED_RESET -1 // 重置引脚
#define pin 35 //心跳传感器引脚
变量声明
int counter = 0;//时间计数器
int Y[128];//屏幕横坐标缓存数组
int maxid=0;//后续心跳传感器使用
int second=0;//后续心跳传感器使用
double bpm;//每分钟心跳数
double Data[50];//来自心跳传感器的数据缓存
double temperature;//温度
//实例化SSD1306显示屏
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//实例化MLX90614传感器
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
//这是一帧BMP格式的图片,可以在OLED上看看是什么
const uint8_t frame0 [] = {
0x00,0x00,0x00,0x80,0x01,0x00,0xC0,0x00,0x00,0x60,0x02,0x00,0x30,0x01,0x00,0x90,
0x04,0x00,0x58,0x02,0x00,0x58,0x09,0x00,0x58,0x05,0x00,0x58,0x35,0x00,0x58,0x35,
0x00,0x58,0x05,0x00,0x58,0x09,0x00,0x58,0x02,0x00,0x90,0x04,0x00,0x30,0x01,0x00,
0x60,0x02,0x00,0xC0,0x00,0x00,0x80,0x01,0x00,0x00,0x00,0x00
};
初始化函数
void setup() {
Serial.begin(9600);
//SSD1306配置
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.display();
display.clearDisplay();
//MLX90614配置
mlx.begin();
for(int i=0;i<128;i++)
{
Y[i]=0;
}
pinMode( pin, INPUT);//设置管脚为输入
}
循环体函数
void loop() {
temperature=mlx.readObjectTempC();
for(int j=0;j<50;j++)
{
Data[j]=analogRead(pin);
delay(10);
}
for(int j=0;j<50;j++)
{
if(Data[j]>Data[maxid])
{
maxid=j;
}
}
for(int j=0;j<50;j++)
{
if(Data[j]>Data[second]&&Data[j]<=Data[maxid]&&abs(j-maxid)>5)
{
second=j;
}
}
if((maxid-second!=0)&&(3000/abs(maxid-second)<200))
bpm=3000/abs(maxid-second);
else
bpm=bpm;
Serial.println(Data[0]);
for(int i=31;i>0;i--)
{
Y[i]=Y[i-1];
Y[0]=bpm-70;//64*(Data[0]-2700)/(100);
}
display.clearDisplay();
//display.drawString(0,0, "Counter: " + String(counter));
display.drawLine(96,0,96,64,SSD1306_WHITE);//y
display.drawLine(0,32,96,32,SSD1306_WHITE);
display.drawLine(96,45,128,45,SSD1306_WHITE);
//x坐标轴绘制
for(int i=0;i<96;i++)
{
int count=i/4;
int res=i%4;
if(res==0)
{
display.drawLine(i,Y[count],i+4,Y[count+1],SSD1306_WHITE);//画线
}
else
{
}
}
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(100,5);
display.println(int(temperature));
display.setCursor(115,1);
display.println("o");
display.setCursor(120,5);
display.println("C");
display.setCursor(100,22);
display.println(int(bpm));
display.setCursor(100,30);
display.println("BPM");
//if (Blynk.getstate())
{
display.drawBitmap(105,48,frame0,16,16,SSD1306_WHITE);
}
display.fillCircle(counter,32,2,SSD1306_WHITE);
display.display();
if(counter<96)
{
counter=counter+4;
}
else
{
counter=0;
}
delay(200);
}
下面针对循环体函数进行简单的说明:
temperature=mlx.readObjectTempC();
for(int j=0;j<50;j++)
{
Data[j]=analogRead(pin);
delay(10);
}
这几行是从传感器端读取数据。温度传感器由于使用了库函数,所以读出来的temperature值为摄氏度数值,这个很方便。
而心跳传感器就比较麻烦了,在此处,心跳传感器的读出值为电压。在此略带提一下心跳传感器的工作原理。本质上它是一个光电传感器,通过指尖脉搏时光通过率的不同,输出不同电压,进而计算出脉搏数。由于没有合适的库,计算的过程只能我们自己来了。
计算的思想很简单,我们先读取若干个数据,从这些数据里找出两个电压最大值,这两个最大值就对应着心脏收缩的最大时刻输出电压,然后求得这两个电压最大值之间的时间间隔,从而可以计算出脉搏频率。
下面是计算过程:
for(int j=0;j<50;j++)
{
if(Data[j]>Data[maxid])
{
maxid=j;
}
}
for(int j=0;j<50;j++)
{
if(Data[j]>Data[second]&&Data[j]<=Data[maxid]&&abs(j-maxid)>5)
{
second=j;
}
}
上述语句块,我们在读取的50个心跳电压值中,找出最大值和次大值。为了减少噪声误差,简单的将时间间隔拉开(不写这句话会造成次大值一直在最大值的相邻位):
abs(j-maxid)>5
之后计算脉搏数值
if((maxid-second!=0)&&(3000/abs(maxid-second)<200))
bpm=3000/abs(maxid-second);
else
bpm=bpm;
我们设置一个采样处理周期是500ms,所以一分钟的心跳数需要乘上3000。
之后是在OLED上显示心跳频率
for(int i=31;i>0;i--)
{
Y[i]=Y[i-1];
Y[0]=bpm-70;//64*(Data[0]-2700)/(100);
}
display.clearDisplay();
//display.drawString(0,0, "Counter: " + String(counter));
display.drawLine(96,0,96,64,SSD1306_WHITE);//y
display.drawLine(0,32,96,32,SSD1306_WHITE);
display.drawLine(96,45,128,45,SSD1306_WHITE);
//x坐标轴绘制
for(int i=0;i<96;i++)
{
int count=i/4;
int res=i%4;
if(res==0)
{
display.drawLine(i,Y[count],i+4,Y[count+1],SSD1306_WHITE);//画线
}
else
{
}
}
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(100,5);
display.println(int(temperature));
display.setCursor(115,1);
display.println("o");
display.setCursor(120,5);
display.println("C");
display.setCursor(100,22);
display.println(int(bpm));
display.setCursor(100,30);
display.println("BPM");
//if (Blynk.getstate())
{
display.drawBitmap(105,48,frame0,16,16,SSD1306_WHITE);
}
display.fillCircle(counter,32,2,SSD1306_WHITE);
display.display();
if(counter<96)
{
counter=counter+4;
}
else
{
counter=0;
}
下面这句话是把测出来的心跳值平移下来,因为心跳数值会比64稍大,溢出了屏幕,所以需要进行向下平移。
for(int i=31;i>0;i--)
{
Y[i]=Y[i-1];
Y[0]=bpm-70;//64*(Data[0]-2700)/(100);
}
之后的语句都是画线的部分了,有兴趣的朋友可以自己改改。最后的显示效果如下图所示:
本地端的配置就算完成了,之后我们要将本地端的数据上传到Blynk服务器上,从而实现在手机上监测心跳和体温。