心跳是个人重要的生命体征之一(正常人平静时每分钟60到100次参考[1])。国产港澳影视剧男女主角都是直接按压颈部来判断被害人是否挂掉的。美剧中,男女主角更喜欢掰开眼睑,用手电筒晃一下(遇到强光瞳孔收缩,是另外一个非常重要的生命体征)。出现这样的场景时,被害人通常都凶多吉少。军训的时,我旁边的一个兄弟身体出现了问题,他的心跳只有一分钟四十几下,去享受我们非常羡慕的病号待遇了。最终的程序中,我使用3秒来作为接受一个心跳信号的Timeout上限。
有氧运动时只有当心率达到一定程度时才是有效的(参考[2]),从这个方面来说,靠“打扫房间”等做家务活动来完成健身的目的是完全不可能的。老婆很瘦,为了健康,我鼓励她晚上进行跑步锻炼。计划是1000米左右,跑了几次她总是抱怨心跳难受。为了衡量运动质量,鼓励她继续坚持同时也为了展示本人的动手能力(我们知道有一些人只会动口动手方面很差,比如:疯狂英语的李阳老师),几个月前答应给老婆做个心跳显示的装置。最初的方案使用下面的这个模块,结构简单价格很便宜,但是真正动手之后发现问题并没有那么简单。
从原理上解释,当有脉搏的时候,手指或者耳垂会有血液密度的变化,光穿透之后另一面出现亮暗的差别。iPhone有个心率检测的应用,使用了这个原理,用法是将手指轻轻的放在摄像头上,对准强光即可获得当前的心率参考[3]。
这个模块有两个元件,上面是红外LED,下面是红外光敏元件。这个入手这个模块之后迫不及待直接接入Arduino,始终无法稳定而成功使用,意思是说有时候一直不好用,但是有些时候又出现了很大的类似心跳的数值波动。
很多年来我一直从事x86的DEBUG工作,之所以热爱这样的工作很大程度上是因为无论多么奇怪与复杂的现象,经过抽丝剥茧总能找到根本原因。使用示波器直接检查输出管脚,下面就是一个比较“稳定”的波形。
可以看出有信号,40mv左右的幅度(5V供电)。在我看来,这样的信号是完全会被淹没在电源的噪音中。刚开始我使用笔记本的USB为它供电,电源导入的噪音可以说是惨不忍睹,周期性的脉冲会达到电压10%。后来我换成了干电池,惊奇的发现干电池电压并不会像想象中那样是一条平滑的直线,电压波动差不多是电压的1%左右。再后来直接用安捷伦的电压发生器,结果和电池一样。实验做到这里,我心凉了半截,一方面是结果说明需要放大电路才能达到要求,另一方面,手指对这个模块的接触会导致信号上的变化,在这样的测试下,为了急于看到结果,制作人员会情不自禁的按下和抬起来获得预期的结果。这也是为什么前面我测试结果“时好时坏”。换句话说,“时好”的情况下并非结果正常而是我自己得到“期望的结果”而已。查看资料也会发现那些商品化的心跳模块都是封闭式的,上面的模块是开放式的结构也可能受到光线的影响。
几个月过去了,我应承的这个东西仍然毫无进展。某天,我又在Taobao上搜索关于心跳的模块,除了各种心跳手表心跳监视设备外,有一个功能类似只是套在手上的Arduino设备映入眼帘(PulseSensor脉搏),原理和上面的类似,价格要120,如果买了恐怕会被老婆嘲笑不如直接买个成品;此外就是跑步机的配件了,价格要低得多(电路+手柄才45)。忽然我有了一个想法,经过改装我们是否可以将它和Arduino链接在一起?
购买之前询问了买家能否和单片机连接,买家是做跑步机的对此并不清楚。本着探索的精神入手一套自己实验。这是买到的跑步机心跳配件电路部分,背后是一块“牛屎”,此外还有一对手柄。
拿到之后,慎重起见,先上3V电压,示波器看输出结果。
调整示波器记录时间,握住手柄一段再松开。
示波器抓图结果如下:
从上面的结果来看,使用这套东西是完全能够实现心率测试。于是搭配 1602 I2C液晶,做了一个。请老婆出马做了个外壳。下面就是运行的结果,在实验中我用的是笔记本USB供电,计划后面的使用中直接使用USB充电宝供电。
硬件连接上非常简单。心跳模块有4个Pin,分别是 Vcc GND Output 和 Enable。前面两个不用说,EnablePin无需连接。Single 接入 Arduino 的Digital Pin 2。I2C的1602接口连接到对应的A4A5(Arduino UNO)即可。
最初的程序有使用一个Digital Pin 2 下降沿触发的中断,在Loop中延时10秒,这个时间内开启中断,得到的中断数量乘以6,就是一分钟内的心跳数量。这样的方案理论上没有问题,但在实际测试经常发显示不准的问题。最终的方案是根据指定的心跳数量花费的时间来反推一分钟会跳多少下。例如下面的程序是计算跳动10次花费了多少时间,然后用10次乘以60秒再除以这个时间就得到一分钟跳动的次数。此外,程序中还设计了一秒钟变动一次的字符,用来表示程序仍然在运行。如果加入自定义的心形符号来显示每次收到的心跳会更好,只是我没做。
#include
#include "LiquidCrystal_I2C.h"
#define beatPin 2 //Heart beat signle is send to digital Pin2
#define beatNumber 10 //Heart beat number
int i;
unsigned long showChar=0UL; //Current milli seconds
int nextChar; //Char for running loop in "* | / -"
unsigned long startTimer; //
char buf[32]; //Output buffer
LiquidCrystal_I2C lcd(0x27,16,2); //1602 I2C address
void setup()
{
pinMode(beatPin,INPUT); //Low for heart beat
Serial.begin(9600);
lcd.init(); // initialize the lcd
lcd.backlight();
lcd.print("Wish you health");
}
void loop()
{
TimeOut:
startTimer=millis();
for (i=0;i
{
lcd.setCursor(2,1);
if (millis()-showChar>1000UL) { //Chang running char every1000ms
switch (nextChar) {
case 0:
lcd.print("*");
break;
case 1:
lcd.print("|");
break;
case 2:
lcd.print("/");
break;
case 3:
lcd.print("-");
} // End of if
showChar=millis();
nextChar=(nextChar +1) % 4;
}
if (0==pulseIn(beatPin,3000)) { //If we can't get any signal, wewill show message
lcd.setCursor(4,1);
snprintf(buf, sizeof(buf), "BPM N/A");
lcd.print(buf);
goto TimeOut;
}
}
//Here means we have collect beatNumber heart beats
lcd.setCursor(4,1);
snprintf(buf, sizeof(buf), "BPM %d ",(beatNumber * 60000)/(millis()-startTimer));
lcd.print(buf);
}
最后,非常遗憾,上海正在经历着xx年不遇的雾霾“天灾”,老婆的健身计划不得不终止。下面是我从自家阳台拍摄
联系客服