Arduino uno R3作品秀:智能健康计
一、作品说明及使用场景 目前国内的老年化问题越来越突出,老年人的健康问题已经不容小视,如何借助快速发展的科技水平来解决老年人的看病难的问题,这在国内得到了广泛的关注和讨论。在这样的背景下智慧医疗的出现,希望能够为医疗行业的发展带来一次变革。何谓智能医疗?它是通过打造健康档案区域医疗信息平台,利用最先进的物联网技术,实现患者与医务人员、医疗机构、医疗设备之间的互动,逐步达到信息化。在不久的将来医疗行业将融入更多人工智慧、传感技术等高科技,使医疗服务走向真正意义的智能化,推动医疗事业的繁荣发展。随着智能硬件的发展,将极大的降低开发者的开发流程和开发门槛,届时任何人都能够DIY 出自己的作品。我的创意就是来自于中老年人一方面对于自身健康的重视,另一方面就是就医的难度增大(需要坐车去医院、排队挂号等等十分繁琐)。我想借助云服务的平台和DIY硬件打造一个集方便、快捷的本地检测、本地显示、云端查看等方式来将为中老年人的健康提供保障。让他们足不出户也能够实时的检测自己的心率、体重等数据,数据同时上传到医院患者的电子病历(假设)和子女的手机端显示,以及历史查询等功能。以期帮助中老年人打造健康的生活环境。二、作品所使用的BOM清单以及硬件部分 硬件清单: 两块arduino开发板; 一块gokit的扩展板; 一个心率脉搏传感器; 一个4针的OLED显示模块; 一个轻触式按键开关; 开发板的供电线两根; 面包板一块; 杜邦线若干。 http://club.gizwits.com/data/attachment/forum/201608/25/163901g7h6zamf44a4e464.png
工作原理:光电容积法的基本原理是利用人体组织在血管搏动时造成透光率不同来进行脉搏测量的。其使用的传感器由光源和光电变换器两部分组成,通过绑带或夹子固定在病人的手指或耳垂上。光源一般采用对动脉血中氧和血红蛋白有选择性的一定波长(500nm~700nm)的发光二极管。当光束透过人体外周血管,由于动脉搏动充血容积变化导致这束光的透光率发生改变,此时由光电变换器接收经人体组织反射的光线,转变为电信号并将其放大和输出。由于脉搏是随心脏的搏动而周期性变化的信号,动脉血管容积也周期性变化,因此光电变换器的电信号变化周期就是脉搏率。 这里先连接上实物电路:将gokit中下载好源码Pulse_arduino进行连接,这里选择的是arduino作为底板,然后将预留的A1引脚作为心率脉搏传感器的连接口:Pulse Sensor——>A1;
本地显示模块的连接,将Menulib源码下载到另一块的arduino中,将4针IICD的OLED显示模块按照以下方式连接:IICOLED: VCC ——VCC ArduinoGND ——GNDSCL ——SCLSDA ——SDAPulse Sensor S-pin——>A0; 之所以用到两块arduino是因为源码占用了很大一部分的内存,这里就将他们分开来执行相应的功能。在Pulse_arduino.zip\Pulse_arduino\libraries\GizWits\GizWits.h中修改下我们的pk(这里是我们的Pk 号:7fc4e47382514c518c44e4fc36f4fcd3,欢迎测试),然后将源码下载成功之后,长按key2进入AirLink模式,RGB灯绿色,进入到对应的配置步骤中,配置过程参照机智云的官网介绍,这里不必累述。 这里是我设定的三个数据点,它分别用来测量测试者周围的温湿度和使用者的心率(及脉搏,这里没有列出,它是可以测量脉搏的,后续大家可以添加试试,我自己还增加了称重模块、人体温度,这里不一一上传)。http://club.gizwits.com/data/attachment/forum/201608/25/162507f63eux7je7sfxxy6.png这里是显示模块的连接,连线之间略显有点乱,所以呢小伙伴呢要重点注意布线,当然小伙伴也可以向我一样散漫些昂:http://club.gizwits.com/data/attachment/forum/201608/25/164200tezmxllva516dm1n.pnghttp://club.gizwits.com/data/attachment/forum/201608/25/164417bfzeuz5im5emamq0.png
连线完成后我满简单的介绍下,这里我的显示模块有一个小小的菜单功能,为了实现这个功能占用了对于arduino来说自身很大的内存,这点我是比较心疼的,但是丝毫呢不影响它的处理。绿色的这个就是我们简单的心率脉搏传感器,我们将其固定在面包板上,这里原本我是打算做一个精巧的包装给它打扮一下,结果未能够使实现,动手能力强或者有一定基础的可以自己打印一个美美的外壳。因为考虑到它的输出端要供给两块板子,所以需要在面包板上跳线接到两块板子上。完成上述之后会发现如来它是如此简单,因此它也将会是非常时候自己DIY使用的。
给设备上电完成完成系统的初始化,可将中指或者食指轻放到Pulse Sensor 白色的心型部分,然后我们就可以通过本地模块以及手机端的APP进行测量者自身的心率等情况以及结合周围的环境温度给病人制定一个合理的解决方案。APP端实时显示界面:
系统初始化前: http://club.gizwits.com/data/attachment/forum/201608/25/165951f93cda5auznhn53a.png数据上传之后数据实时的显示: http://club.gizwits.com/data/attachment/forum/201608/25/165951ihw5hh1t1kkchz7z.png
本地显示:系统初始化: http://club.gizwits.com/data/attachment/forum/201608/25/170648v15leb3k3c5kk9y1.png按下按键之后接入到测量界面:http://club.gizwits.com/data/attachment/forum/201608/25/170653iuvyxglehsy3xilg.png实时的测量中: http://club.gizwits.com/data/attachment/forum/201608/25/170658jo3pfimffhhpwl74.png
原文转自:http://club.gizwits.com/thread-2646-1-1.html
三、代码分析以及功能分析
在Pulse_arduino.zip\Pulse_arduino\libraries\GizWits\GizWits.h中修改下我们的pk,这里可以使用我的PK号:7fc4e47382514c518c44e4fc36f4fcd3。这里重点的要说明下这部分的代码,其他的都是基于宠物屋的arduino版本进行修改的,不需要进行太多的说明。
这里是利用定时器每2ms产生一个中断信号,然后来检测Pulse Sensor传感器的数据引脚输出的电信号,按照光电容积的原理算法处理后得到人体的心率,并将其通过串口打印或者显示模块等方式来将数据信息显示出来。
interruptSetup()是使用arduino的定时器1,我发现宠物屋的源码中,定时器2已经被使用了,这时我们就需要其他定时器来做定时中断处理(arduinoUNO内部有3个定时器,time0不可被使用)。配置完定时器之后,我们需要在ISR( )函数接口中说明我们使用到了哪一个接口,这里在附录中列一些定时器1、2的地址和对应的ISR( )入口:
[*]
[*]
复制代码
[*]/*******************************************************************************
[*]* Function Name: interruptSetup
[*]* Description : Set Time1
[*]* Input : None
[*]* Output : None
[*]* Return : None
[*]* Attention :
[*]*******************************************************************************/
[*]void interruptSetup(){
[*] // Initializes Timer1 to throw an interrupt every 2mS.
[*] TCCR1A = 0x00; // DISABLE OUTPUTS AND PWM ON DIGITAL PINS 9 & 10
[*] TCCR1B = 0x11; // GO INTO 'PHASE AND FREQUENCY CORRECT' MODE, NO PRESCALER
[*] TCCR1C = 0x00; // DON'T FORCE COMPARE
[*] TIMSK1 = 0x01; // ENABLE OVERFLOW INTERRUPT (TOIE1)
[*] ICR1 = 16000;// TRIGGER TIMER INTERRUPT EVERY 2mS
[*] sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
[*]}
[*]
[*]/*******************************************************************************
[*]* Function Name: ISR(TIMER1_COMPA_vect)
[*]* Description : Callback function , Judge Wifi statue
[*]* Input : None
[*]* Output : None
[*]* Return : None
[*]* Attention :
[*]*******************************************************************************/
[*]ISR(TIMER1_OVF_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 with this variable
[*]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 we think there is a pulse
[*] digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
[*] IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
[*] lastBeatTime = sampleCounter; // keep track of time for next pulse
[*]
[*] if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
[*] secondBeat = false; // clear secondBeat flag
[*] for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
[*] rate = IBI;
[*] }
[*] }
[*]
[*] if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE
[*] firstBeat = false; // clear firstBeat flag
[*] secondBeat = true; // set the second beat flag
[*] sei(); // enable interrupts again
[*] return; // IBI value is unreliable so discard it
[*] }
[*]
[*]
[*] // keep a running total of the last 10 IBI values
[*] word runningTotal = 0; // clear the runningTotal variable
[*]
[*] for(int i=0; i<=8; i++){ // shift data in the rate array
[*] rate = rate; // and drop the oldest IBI value
[*] runningTotal += rate; // add up the 9 oldest IBI values
[*] }
[*]
[*] rate = IBI; // add the latest IBI to the rate array
[*] runningTotal += rate; // 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
复制代码
[*]<b><font size="4">附录:</font></b>
[*]
[*]Timer2
[*]
[*] Pulse Sensor Arduino UNO uses Timer2 by default.
[*] Use of Timer2 interferes with PWM on pins 3 and 11.
[*] There is also a conflict with the Tone library, so if you want tones, use Timer1 below.
[*]
[*] void interruptSetup(){
[*] // Initializes Timer2 to throw an interrupt every 2mS.
[*] 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
[*] }
[*]
[*] use the following interrupt vector with Timer2
[*]
[*]<font color="#ff0000"> ISR(TIMER2_COMPA_vect)</font>
[*]
[*]>> Timer1
[*]
[*] Use of Timer1 interferes with PWM on pins 9 and 10.
[*] The Servo library also uses Timer1, so if you want servos, use Timer2 above.
[*]
[*] void interruptSetup(){
[*] // Initializes Timer1 to throw an interrupt every 2mS.
[*] TCCR1A = 0x00; // DISABLE OUTPUTS AND PWM ON DIGITAL PINS 9 & 10
[*] TCCR1B = 0x11; // GO INTO 'PHASE AND FREQUENCY CORRECT' MODE, NO PRESCALER
[*] TCCR1C = 0x00; // DON'T FORCE COMPARE
[*] TIMSK1 = 0x01; // ENABLE OVERFLOW INTERRUPT (TOIE1)
[*] ICR1 = 16000;// TRIGGER TIMER INTERRUPT EVERY 2mS
[*] sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
[*] }
[*]
[*] Use the following ISR vector for the Timer1 setup above
[*]
[*]<font color="#ff0000"> ISR(TIMER1_OVF_vect)</font>
复制代码
除此之外细心的小伙伴还会发现在我的源码中还定义了一个int blinkPin = 13; 这个引脚可以接一个小灯,它会随着信号值的改变而随着改变,通过它你也能够直观的看到自己的心跳哦~
心率有算法直接用吗?还是自己写的 你的心率测量准吗,有使用相应的滤波算法吗
页:
[1]