简单整理了自己在本学期学校的“物联网技术与应用”课程的课程大作业中所实现的一个小原型。
该原型实现了利用esp8266自带的WIFI模块,借助Node MCU开发板读取pulse sensor心率传感器的数据,通过MQTT通信协议,在手机上查看心率值,并且显示在OLED屏上。同时通过MQTT协议,连接了开发板的传感器、EMQ服务器、手机之间可以相互订阅、发布相关topic的消息。
需要用到:
esp8266开发板
杜邦线、USB线
Pulse Sensor心率传感器
0.96寸OLED显示屏
Arduino IDE
EMQ (MQTT消息服务器)
一、传感器相关准备
用杜邦线将传感器按照以下的对应关系连接起来:
Pulse Sensor心率传感器 esp8266
S A0引脚(ADC)
+ 3.3V
- GND
Pulse Sensor心率传感器如下图所示:
网上关于Pulse Sensor心率传感器也可以搜到一些,这里就不再多说啦,可以参考这个博客:史上最全脉搏心率传感器PulseSensor资料(电路图+中文说明书+最全源代码)
Pulse Sensor官方的github库: PulseSensorPlayground
在Arduino->管理库->库管理器 搜索PulseSensor Playground下载并安装,安装成功后在 Arduino->文件->示例 可以看到如下图所示的官方示例代码。
连接好:
二、OLED相关准备
使用的是0.96寸的OLED屏:
将OLED屏也连接到esp8266开发板上:
三、Arduino下载安装相关库
在Arduino库管理器里搜索并安装以下的将会用到的库
四、MQTT相关知识
MQTT菜鸟教程:MQTT 入门介绍
五、搭建EMQ
EMQ官网:https://www.emqx.io/cn/products/broker
官网安装配置文档:https://docs.emqx.io/broker/v3/cn/
搭建成功后,在浏览器输入控制台地址:http://127.0.0.1:18083
输入账号密码即可进入EMQ控制台:
六、Arduino部分
Arduino->工具里开发板要选NodeMCU1.0,且端口要选对
实现代码 (计算心率算法+MQTT+OLED控制):
extern "C" {
#include "user_interface.h"
}
#include
#include
#include
// Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h"
// legacy include: `#include "SSD1306.h"`
SSD1306Wire display(0x3c, D4, D5);
//esp8266通过传感器采集脉搏模拟信号,经过AD转换为数字电压 并计算心率值
//Signal,信号,持有原始模拟输入数据引脚0,每2ms更新一次 int
//IBI: 相邻两次脉搏的时间间隔(单位:ms) int
//BPM(beats per minute):心率,一分钟内的心跳次数 int
//(且BPM = 60 / IBI)是根据之前 10个IBI值的 平均值
//Pulse脉冲,当检测到心跳时 为true,其他时候为false。它控制LED引脚13。 boolean
//QS 当找到Pulse并更新BPM时 为true。必须重置。 boolean
const char* ssid = "";//热点名
const char* password = "";//热点密码
const char* mqtt_server = "";//MQTT服务器ip
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[8];
int value = 0;
#define N 10
volatile int BPM; // 保持引脚0,每2ms更新一次
volatile int Signal; // 保存原始数据
volatile int IBI = 600; // 相邻两次心跳之间的时间间隔是唯一确定的
volatile boolean Pulse = false; // 脉冲,当监测到一个心跳时为"True",否则为"False"
volatile boolean QS = false; // 当Arduoino监测到心跳时为true
volatile int Rate[N]; // 设置数组保存最后10次IBI数据
volatile unsigned long CurrBeatTime = 0; // 用于确定脉冲时间
volatile unsigned long LastBeatTime = 0; // 用于监测IBI
volatile int P = 500; // 用于寻找脉搏波峰值
volatile int T = 500; // 用于发现脉搏波中的波谷
volatile int Threshold = 512; //设置阀值
volatile int Amplifier = 100; //放大器
int PulseSensorPin = 17;
int FadePin = 4;
int FadeRate = 0;
String tmp="";
void setupTimer(int m /* msec */) {
timer0_isr_init();
timer0_attachInterrupt(timer0_ISR);
timer0_write(ESP.getCycleCount() + 80000L * m); // 80MHz/1000 == 1msec
}
unsigned long getCurrentTime() {
return ESP.getCycleCount() / 80000L;
}
void timer0_ISR(void) {
noInterrupts();
Signal = system_adc_read();
CurrBeatTime = getCurrentTime(); // msec
unsigned long interval = CurrBeatTime - LastBeatTime;
// 找出脉搏波的波谷
if ((Signal < Threshold) && (interval >(IBI * 3) / 5)) { // 通过等待3/5的IBI,以避免二色噪声
if (Signal < T) { // T 是波谷
T = Signal; // 脉搏波最低点跟踪
//Serial.println("T:" + String(T));
}
}
//找出脉搏波的波峰
if (Signal > Threshold && Signal > P) { // 阈值条件有助于避免噪音
P = Signal; // P 是波峰
//Serial.println("P:" + String(P));
}
// 开始寻找心跳
// 每当有脉冲时,信号就会增值。
if (interval > 250 /* ms */) { // 避免高频率噪音
//检查信号是否超过阀值
if ((Signal > Threshold) && !Pulse && (interval > (IBI * 3) / 5)) {
Pulse = true; // 当感觉这有心跳时设Pulse flag
IBI = interval;
if (Rate[0] < 0) { // first time 第一次
Rate[0] = 0;
LastBeatTime = getCurrentTime();
setupTimer(10);
noInterrupts();
return;
}
else if (Rate[0] == 0) { // second time 第二次
for (int i = 0; i < N; ++i) { // 以获得启动时实际的BPM
Rate[i] = IBI;
}
}
// 保持最后10个IBI值的累计总数
word running_total = 0; // 清零runningTotal
for (int i = 0; i < N - 1; ++i) { // 心率数组中的移位数据
Rate[i] = Rate[i + 1]; // 降低最早的IBI值
running_total += Rate[i]; // 加上9个最早的IBI值 一共10个,平均值得到BPM
}
Rate[N - 1] = IBI; // 将最新的IBI添加到心率数组中
running_total += IBI; // 将最新的IBI添加到运行总数runningTotal中
running_total /= N; // 取10个IBI值的平均值=BPM
BPM = 60000 / running_total; // 一分钟内的心跳次数就是BPM
QS = true; // 设置量化标记Quantified Self flag
LastBeatTime = getCurrentTime();
}
}
// 检查信号是否在阀值以下
if ((Signal < Threshold) && Pulse) {
Pulse = false;
Amplifier = P - T;
Threshold = Amplifier / 2 + T; // 修改阀值
P = Threshold;
T = Threshold;
}
// 如果没有信号超过2.5秒 检查
if (interval > 2500 /* ms */) {
Threshold = 512;
P = 500;
T = 500;
LastBeatTime = getCurrentTime();
for (int i = 0; i < N; ++i) {
Rate[i] = -1;
}
}
setupTimer(10);
interrupts();
}
void setup() {
pinMode(BUILTIN_LED, OUTPUT);
pinMode(A0, INPUT);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
pinMode(FadePin, OUTPUT);
analogWriteRange(255);
noInterrupts();
setupTimer(10);
interrupts();
LastBeatTime = getCurrentTime(); // msec
Serial.println();
display.init();
display.clear();
}
void setup_wifi() {
delay(10);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
tmp+=(char)payload[i];
}
Serial.println();
displayData();
tmp="";
}
void displayData(){
display.clear();
display.drawString(0,0,"[receive]:");
display.drawString(0,20,tmp);
display.display();
delay(1000);
}
void reconnect() {
while (!client.connected()) {
Serial.print("MQTT connection...");
if (client.connect("ESP8266Client")) {
client.subscribe("Topic"); //订阅主题Topic
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
if (QS) {
FadeRate = 255;
Serial.print("BPM: ");
Serial.println(BPM);
snprintf(msg, 8, "BPM:%d", BPM);
client.publish("Topic", msg);//发布主题Topic
QS = false;
}
FadeRate -= 15;
FadeRate = constrain(FadeRate, 0, 255);
analogWrite(FadePin, FadeRate);
delay(20);
}
程序写好后,将开发板上电,并连接到电脑上,将程序烧制进去,保持手指放在心率传感器的正面,打开串口监视器(波特率选择115200),当成功连接到MQTT服务器时,就可以看到串口输出以下数据,并可以在OLED上显示(程序订阅和发布的主题都是Topic,所以自己能收到自己发的)
此时可以打开EMQ的控制台,利用EMQ的WebSocket工具,订阅主题Topic便可以查看收到的消息数据,也可以发送Topic主题的消息,其可以显示在OLED屏上。
七、iOS/Android手机端MQTTClient
相关MQTTClient的应用各大应用商城都可以搜到很多,可以选择下载一个进行调试,查看效果;也可以自己写一个简单的安卓MQTTClient程序,实现在安卓手机端查看心率值,前提是手机、开发板、部署了MQTT服务器的电脑在同一网络下。 以下是自己写的一个简单的安卓MQTTClient客户端的demo,可以订阅、发布Topic主题的消息,在安卓手机上以Toast的形式显示接收到的心率值。
安卓MQTTClient部分代码: