1. 前言
近几年随着科技的进步和智能化浪潮的到来,智能穿戴设备也在飞速火爆发展,各种健康智能手环,智能手表、智能跑鞋、智能眼镜纷纷上市,并出现了很多针对个人家庭的健康管理设备。比如: 智能血压计、智能心率检测、脂肪秤、智能体重秤等等,都带上了智能、健康各种标签。
可穿戴设备,即直接穿在身上,或是整合到用户的衣服或配件的一种便携式设备。可穿戴设备不仅仅是一种硬件设备,更是通过软件支持以及数据交互、云端交互来实现强大的功能,可穿戴设备将会对生活、感知带来很大的转变。
当前采用STM32加上各种外设传感器配合物联网平台设计一个健康管理设备,通过ESP8266+MQTT协议将数据传输到物联网平台,实现云端健康管理。
物联网平台采用阿里云,下面是设计的界面与设备的整体效果:
2. 设计需求
设备需要支持以下功能:
(1)人体温度测量 (2)运动监测、计步功能 (3)睡眠监测 (4)心率测量
STM32采集这些传感器数据之后,进行处理,在本地OLED显示屏上完成显示;再通过ESP8266将数据传递到物联网平台,完成云端数据展示。
人体温度测量超过规定阈值时、心率测量超过规定阈值、睡眠监测睡眠时长如超出8h,云端以及本地0led屏发出文字提醒并且蜂鸣器报警。
3. 硬件选型
3.1 MAX30102血氧传感器
MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了一个红光LED和一个红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。MAX30102采用一个1.8V电源和一个独立的5.0V用于内部LED的电源,应用于可穿戴设备进行心率和血氧采集检测,佩戴于手指、耳垂和手腕等处。标准的I2C兼容的通信接口可以将采集到的数值传输给Arduino、KL25Z、51、STM32等单片机进行心率和血氧计算。此外,该芯片还可通过软件关断模块,待机电流接近为零,实现电源始终维持供电状态。
接线如下:
产品名称 MAX30102心率模块
LED峰值波长 660nm/880nm
LED供电电压 3.3~5V
检测信号类型 光反射信号(PPG)
输出信号接口 I2C接口
通信接口电压 1.8~3.3V5V(可选)
原理说明:
光溶积法: 利用人体组织在血管搏动时造成透光率不同来进行脉搏和血氧饱和度测量的;
光源: 采用对动脉血中氧合血红蛋白(Hb02)-和血红蛋白(Hb)有选择性的特定波长的发光极管透光率转化为电信号:动脉搏动充血容积委化导致够束光的透光率发生改变,此时由光电变换接收经人体组织反身光线,转变为电信号并将其放大输出。
引脚说明:
VIN:主电源电源输入端,1.8V-5V 3位焊盘:选择总线上的拉电平,取决于引脚主控电压,可选1.8V端或者3.3v端(此端包含3.3V及以上)
SCL:接IIC总线的时钟
SDA:接IIC总线的数据
NT: MAX30102芯片的中断引脚
RD: MAX30102芯片的 RED LED接地端,一般不接
IRD: MAX30102芯片的RLED接地端,一般不接
GND:接地线
PulseSensor 是一款用于脉搏心率测量的光电反射式模拟传感器。将其佩戴于手指、耳垂等处,通过导线连接可将采集到的模拟信号传输给 Arduino 等单片机用来转换为数字信号,再通过 arduino 等单片机简单计算后就可以得到心率数值,此外还可将脉搏波形通过串口上传到电脑显示波形。 PulseSensor 是一款开源硬件, 目前国外官网上已有其对应的 arduino 程序和上位机 Processing 程序, 其适用于心率方面的科学研究和教学演示,也非常适合用于二次开发。 特别提醒:传感器背面是电子元件,请不要用手指直接接触, 以免静电或汗液造成背面器件损坏。 可以在背面粘贴黑色粘扣, 正面粘贴透明膜来保护传感器。 传感器的接口一共 3 个, 如上图红框内所示。 请大家千万不要根据线的颜色来自行推测, 而要根据电路板的背面标识来分辨。 红框中的 3 根线,标有 S 的为模拟信号输出线(最左边) ; 标有+的为电源输入线(中间);标有-的为地线(最右边) 。 总结一下: S → 脉搏信号输出(要接单片机 AD 接口)
3.2 MPU6050
MPU6050特点:
(1)高性能三轴加速度+三轴陀螺仪的六轴传感器模块MPU6050芯片;
(2)可利用自带的数字运动处理器(DMP)硬件加速引擎,通过主IKC接口,向应用端输出姿态解算后的数据,使用InvenSense公司提供的运动处理资料库,实现姿态解算,降低了运动处理运算对操作系统的负荷同时大大降低了开发难度;
(3)体积小,自带温度传感器;
(4)支持IIC从机地址设置和中断;
(5)兼容3.3V/5V系统;
3.3 STM32开发板
STM32F103RCT6的芯体规格是32位,速度是72MHz,程序存储器容量是256KB,程序存储器类型是FLASH,RAM容量是48K。
3.4 ESP8266 WIFI
ESP8266是一款物联网WiFi芯片,基于ESP8266可以开发物联网串口WiFi模块,像SKYLAB的WG219/WG229专为移动设备和物联网应用设计,可将用户的物理设备连接到WiFi无线网络上,进行互联网或局域网通信,实现联网功能。
ESP8266,专为移动设备、可穿戴电子产品和物联网应用而设计,通过多项专有技术实现了超低功耗。ESP8266EX 具有的省电模式适用于各种低功耗应用场景。
ESP8266内置超低功耗 Tensilica L106 32 位 RISC 处理器,CPU 时钟速度最高可达 160 MHz,支持实时操作系统 (RTOS) 和 Wi-Fi 协议栈,可将高达 80% 的处理能力留给应用编程和开发。
3.5 GY-MCU90615V2体温传感器
通信协议:
串口发送命令字节:
(1)、串口通信参数(默认波特率值9600 bps,可通过软件设定)
波特率:9600 bps 校验位:N 数据位:8 停止位:1
波特率:115200 bps 校验位:N 数据位:8 停止位:1
(2)、模块输入命令,由外部控制器发送至GY-MCU90615模块(十六进制)
1、帧头:0xa5
指令格式:帧头+指令+校验和(8bit)(如自动读取温度指令=0xA5+0x45+0xEA)
2、命令指令:
连续输出指令:
0xA5+0x45+0xEA----------------温度数据(模块返回数据类型为0x45)
查询输出指令:
0xA5+0x15+0xBA ---------------温度数据(模块返回数据类型为0x45)
配置指令:(掉电重启后生效)
波特率配置:
0xA5+0xAE+0x53 ---------------9600(默认)
0xA5+0xAF+0x54 ---------------115200
上电是否自动发送温度数据配置:
0xA5+0x51+0xF6---------------上电后自动输出温度数据(默认)
0xA5+0x52+0xF7---------------上电后不自动输出温度数据
通信协议
串口接收:
(1)、串口通信参数(默认波特率值9600 bps,可通过软件设定)
波特率:9600 bps 校验位:N 数据位:8 停止位:1
波特率:115200 bps 校验位:N 数据位:8 停止位:1
(2)、模块输出格式,每帧包含9个字节(十六进制):
①.Byte0: 0x5A 帧头标志
②.Byte1: 0x5A 帧头标志
③.Byte2: 0X45 本帧数据类型(0X45:温度数据)
④.Byte3: 0x04 数据量(以下4个数据2组为例)
⑤.Byte4: 0x00~0xFF 数据1高8位
⑥.Byte5: 0x00~0xFF 数据1低8位
⑦.Byte6: 0x00~0xFF 数据2高8位
⑧.Byte7: 0x00~0xFF 数据2低8位
⑨.Byte8: 0x00~0xFF 校验和(前面数据累加和,仅留低8位)
(3)、数据计算方法
温度计算方法 :
温度= 高8位<<8 低8位(结果为实际角度乘以100)
例:发送指令:A5 45 EA ,接收到一帧数据:
<5A- 5A- 45- 04- 0C- 78- 0D- 19- A7 >
表示TO(有符号16bit,表示目标温度):TO=0x0C78/100=31.92 ℃
表示TA(有符号16bit,表示环境温度):TO=0x0D19/100=33.53 ℃
使用方法
该模块为串口输出数据,使用者通过串口连接后,发送输出指令,例如0xA5+0x45+0xEA给模块,模块将连续输出温度数据;如想通过查询输出可发送0xA5+0x15+0xBA给模块,每发送一次,模块将返回一次温度数据,查询频率应低于10hz,如需高于10hz请使用连续输出模式,即发送0xA5+0x45+0xEA指令;
3.6 OLED显示屏
特点:
OLED是有机发光二极管又称为有机激光显示、OLED显示技术具有自发光的特性、采用非常薄的有机材料涂层、和玻璃基板、当有电流通过时、这些有机材料就会发光、而且OLED显示屏幕可视角大、功耗低、OLED由于同时具备自发光、不需背光源(只是供电不会亮的、驱动程序和接线正确才会点亮)对比度高、厚度薄视角广、反应速度快、可用于挠曲面板、使用温度范围广、结构及制程等优异之特性、先接触的1286屏都是LCD的、需要背光、功耗较高、而OLED的功耗低、更加适合小系统、由于两者发光材料的不同、在不同的环境中、OLED的显示效果好、模块供电可以是3.3v也可以是5V、不需要修改模块电路、OLED屏具有多个控制指令、可以控制oLED的亮度、对比度、开关升压电路等指令、操作方便、功能丰富、可显示汉字、ASCH、图案等、同时为了方便应用在产品上、预留4个M3固定孔、方便用户固在机壳上。
3.7 母对母杜邦线
三、阿里云物联网云平台
3.1 创建产品
官网地址: https://iot.console.aliyun.com/lk/summary/new
没有账号需要先注册账号,实名认证之后登录。
(1)选择公共实例
(2)创建产品
(3)配置产品信息
3.2 创建设备
(1)点击添加设备
产品下可以创建多个具体设备,设备也可以使用接口让设备端自动创建,下面采用网页手动添加的方式,添加设备。
(2)点击添加设备
(3)填入设备的信息
(4)保存设备信息
创建成功后复制设备证书信息,方便后续生成MQTT登录参数使用。
{
"ProductKey": "a1cMlEwEwjg",
"DeviceName": "healthy_iot_dev",
"DeviceSecret": "9203ea85a963b40ace7686d01046598f"
}
(5)创建成功
(6)查看设备证书信息
如果刚才没有复制保存,可以在设备详情页面查看设备证书。
3.3 创建自定义属性
云端的产品、设备创建完毕之后,那么本地具体的设备肯定会向云端设备上传数据,那上传的数据用什么字段保存,数据是什么类型,这些需要提前创建,这样约定好之后,设备端才可以正常的上传数据上来。
(1)找到功能定义选项页面
(2)添加自定义功能
根据自己传感器的数据属性添加即可。
(3)添加心率属性
(4)添加血氧饱和度属性
(5)添加人体体温属性
(5)添加行走的步数
(6)添加完毕发布上线
(4)完成
3.4 查看物模型
接下来设备端的数据上报就要参考物模型的格式来完成数据拼接。
(1)完整物模型
{
"schema": "https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json",
"profile": {
"version": "1.0",
"productKey": "a1cMlEwEwjg"
},
"properties": [
{
"identifier": "HeartRate",
"name": "心率",
"accessMode": "r",
"desc": "心率",
"required": false,
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "500",
"step": "1"
}
}
},
{
"identifier": "BloodOxygen",
"name": "血氧饱和度",
"accessMode": "rw",
"desc": "血氧饱和度",
"required": false,
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100",
"unit": "%",
"unitName": "百分比",
"step": "1"
}
}
},
{
"identifier": "temperature",
"name": "人体体温",
"accessMode": "r",
"desc": "人体体温",
"required": false,
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100",
"unit": "°C",
"unitName": "摄氏度",
"step": "1"
}
}
},
{
"identifier": "steps",
"name": "行走步数",
"accessMode": "r",
"desc": "行走步数",
"required": false,
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100000",
"unit": "stepCount",
"unitName": "步",
"step": "1"
}
}
}
],
"events": [
{
"identifier": "post",
"name": "post",
"type": "info",
"required": true,
"desc": "属性上报",
"method": "thing.event.property.post",
"outputData": [
{
"identifier": "HeartRate",
"name": "心率",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "500",
"step": "1"
}
}
},
{
"identifier": "BloodOxygen",
"name": "血氧饱和度",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100",
"unit": "%",
"unitName": "百分比",
"step": "1"
}
}
},
{
"identifier": "temperature",
"name": "人体体温",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100",
"unit": "°C",
"unitName": "摄氏度",
"step": "1"
}
}
},
{
"identifier": "steps",
"name": "行走步数",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100000",
"unit": "stepCount",
"unitName": "步",
"step": "1"
}
}
}
]
}
],
"services": [
{
"identifier": "set",
"name": "set",
"required": true,
"callType": "async",
"desc": "属性设置",
"method": "thing.service.property.set",
"inputData": [
{
"identifier": "BloodOxygen",
"name": "血氧饱和度",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100",
"unit": "%",
"unitName": "百分比",
"step": "1"
}
}
}
],
"outputData": []
},
{
"identifier": "get",
"name": "get",
"required": true,
"callType": "async",
"desc": "属性获取",
"method": "thing.service.property.get",
"inputData": [
"HeartRate",
"BloodOxygen",
"temperature",
"steps"
],
"outputData": [
{
"identifier": "HeartRate",
"name": "心率",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "500",
"step": "1"
}
}
},
{
"identifier": "BloodOxygen",
"name": "血氧饱和度",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100",
"unit": "%",
"unitName": "百分比",
"step": "1"
}
}
},
{
"identifier": "temperature",
"name": "人体体温",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100",
"unit": "°C",
"unitName": "摄氏度",
"step": "1"
}
}
},
{
"identifier": "steps",
"name": "行走步数",
"dataType": {
"type": "int",
"specs": {
"min": "1",
"max": "100000",
"unit": "stepCount",
"unitName": "步",
"step": "1"
}
}
}
]
}
]
}
(2)精简物模型
{
"properties": [
{
"identifier": "HeartRate",
"dataType": {
"type": "int"
}
},
{
"identifier": "BloodOxygen",
"dataType": {
"type": "int"
}
},
{
"identifier": "temperature",
"dataType": {
"type": "int"
}
},
{
"identifier": "steps",
"dataType": {
"type": "int"
}
}
]
}
3.5 创建web可视化界面
地址: https://iot.console.aliyun.com/lk/related-services
(1)找到web应用开发入口
(2)新建项目
(3)查看项目
(4)新建web应用
(5)配置数据源
以仪表盘为例,拖一个仪表盘组件到页面,选中仪表盘,配置数据源。
关联产品
关联自己创建的产品。
关联成功
回到刚才的数据源页面,继续配置,选中设备,配置设备。
关联成功。
回到刚才配置数据源页面,刷新设备列表,继续配置。
根据自己仪表盘要显示的数据进行关联。
(6)完成页面配置
根据自己需求,配置页面样式,选择数据源。
因为目前刚创建完设备还没有上传数据激活,所以页面上选上数据源错误,这是正常。
(7)发布应用
3.6 生成MQTT设备登录信息
(1)MQTT登录地址
关于MQTT协议登录所需要的参数官方说明文档: https://help.aliyun.com/document_detail/140507.html?spm=a2c4g.11186623.6.571.1e417544OGPj2y
MQTT登录域名的格式:
${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com
其中:
${YourProductKey}:请替换为设备所属产品的ProductKey
${YourRegionId}:请替换为物联网平台设备所在地域代码。
下面是阿里云国内的服务器地域和可用区详情:
地域名称 所在城市 Region ID 可用区数量
华北 1 青岛 cn-qingdao 2
华北 2 北京 cn-beijing 10
华北 3 张家口 cn-zhangjiakou 3
华北 5 呼和浩特 cn-huhehaote 2
华北 6 乌兰察布 cn-wulanchabu 3
华东 1 杭州 cn-hangzhou 8
华东 2 上海 cn-shanghai 8
华南 1 深圳 cn-shenzhen 6
华南 2 河源 cn-heyuan 2
华南 3 广州 cn-guangzhou 2
西南 1 成都 cn-chengdu 2
端口号是:1883
经过上面的格式解释,我的阿里云服务器登录的域名就是(选择的是上海服务器):a1cMlEwEwjg.iot-as-mqtt.cn-shanghai.aliyuncs.com
域名对应的IP地址(动态解析出来的): 101.133.196.97
在线解析域名网站:https:
(2)MQTT登录参数
(1) MQTT_ClientID
固定格式:${ClientID}|securemode=${Mode},signmethod=${SignMethod}|。
参数说明:
${ClientId}: 设备ID,一般填设备的硬件编号。我这里就直接填当前的设备名称,后面的密码里也要填这个ID,必须一样就行。(设备名称就是创建设备的时候复制出来3个参数里的设备名称)
securemode=3:TCP直连模式,无需设置SSL/TLS信息。
securemode=2:TLS直连模式,需要设置SSL/TLS信息。
${SignMethod}:算法类型,支持hmacmd5和hmacsha1。
示例:
当前我的设备名称是:healthy_iot_dev ,选择TCP直连模式,选择hmacsha1算法类型。
那么我的ClientID就是:
healthy_iot_dev|securemode=3,signmethod=hmacsha1|
(2) MQTT_UserName
固定格式:${DeviceName}&${ProductKey}
参数解释:
${DeviceName} 是设备的名称(就是创建设备的时候复制出来3个参数里的设备名称)
${ProductKey} 是设备的ProductKey(就是创建设备的时候复制出来3个参数里的ProductKey)
示例:
当前我的设备名称是:healthy_iot_dev ,我的ProductKey是:a1cMlEwEwjg
那么我的UserName就是:
healthy_iot_dev&a1cMlEwEwjg
(3) MQTT_PassWord
下载密码生成小工具:https:
计算MQTT签名-密码:https://help.aliyun.com/document_detail/292635.htm?spm=a2c4g.11186623.0.0.5aaf3686v36VUs#section-jx3-u57-pmm
根据设备证书信息,填入参数,然后生成MQTT登录密匙。
生成的信息如下:这3个数据就是接下来,MQTT登录使用的密匙信息
mqttClientId: healthy_iot_dev|securemode=2,signmethod=hmacsha1,timestamp=1648185853325|
username: healthy_iot_dev&a1cMlEwEwjg
password: F4F3DA2461A8CD6BF5ACF024D065BB77A75DFFCA
(3)主题订阅、发布测试
在产品页面可以看到主题格式:https://iot.console.aliyun.com/product/productDetail/a1cMlEwEwjg/func?current=2
发布主题:
/sys/a1cMlEwEwjg/healthy_iot_dev/thing/event/property/post
上报属性消息的格式:
{"method":"thing.event.property.post","params":{"HeartRate":60,"BloodOxygen":80,"temperature":37,"steps":12345}}
订阅主题:
/sys/a1cMlEwEwjg/healthy_iot_dev/thing/service/property/set
3.7 软件模拟设备登录
(1)设备登录
软件下载地址:https://download.csdn.net/download/xiaolong1126626497/18784012
填入设备的登录信息。
(2)查看设备状态
打开网页的设备页面查看,设备已经在线了。
(3)查看上报的数据
物模型页面查看设备上传的数据。
(4)查看网页数据
项目主页:https://studio.iot.aliyun.com/p/a123qInUN2Bpr14w/project/detail
四、设备端开发
如果需要完整的项目资料可以去这里下载: https://download.csdn.net/download/xiaolong1126626497/85895120
4.1 设备运行效果
4.2 设备硬件接线说明
这里列出当前设备使用到的硬件连线说明。
(1)MAX30102--心率与血氧采集模块:(测量心率与血氧饱和度)-IIC协议
VCC------3.3V
GND------GND
SCL------PC0
SDA------PC1
IM-------PC2
(2)0.96寸SPI接口的OLED显示屏:
VCC----------3.3V
GND----------GND
D0-SCL-------PC8
D1-SDA-------PC9
RES-RST------PC10
DC-----------PB7
CS-----------PB8
(3)MPU6050 六轴陀螺仪(计步、睡眠监测)
SCL----------PC3
SDA----------PC4
AD0----------PC6
VCC----------3.3V
GND----------GND
(4)GY-MCU90615--红外测温模块
VCC----------3.3V
GND----------GND
PA2-TX-------RX
PA3-RX-------TX
(5)ESP8266-WIFI (连接阿里云物联网服务器)
ATK-ESP8266串口WIFI模块与STM32的串口3相连接。
PA10(TX)--RXD 模块接收脚
PA11(RX)--TXD 模块发送脚
GND---GND 地
VCC---VCC 电源(3.3V~5.0V)
(6)板载LED灯接线
LED1---PA8
LED2---PD2
BEEP---PA6 --------需要外接蜂鸣器
(7)板载按键接线
K0---PA0
K1---PC5
K2---PA15
4.3 keil工程布局
在mian.c里首先完成了各个传感器的初始化配置,然后进入到while循环,在循环不断的检测按键,如果有按键按下就切换显示页面,查看其他页面的数据。在while里固定1秒钟的频率向MQTT物联网服务器上传一次数据。
4.4 设备main.c核心代码
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#include <stdio.h>
#include "exti.h"
#include "timer.h"
#include "oled.h"
#include "rtc.h"
#include "temperature.h"
#include "mpu6050.h"
#include "stepAlgorithm.h"
#include "BMP.h"
#include <stdio.h>
#include <sys.h>
#include "esp8266.h"
#include "mqtt.h"
#include "max30102.h"
#include "myiic.h"
#include "algorithm.h"
#define MQTT_ClientID "healthy_iot_dev|securemode=2,signmethod=hmacsha1,timestamp=1648185853325|"
#define MQTT_UserName "healthy_iot_dev&a1cMlEwEwjg"
#define MQTT_PassWord "F4F3DA2461A8CD6BF5ACF024D065BB77A75DFFCA"
#define SET_TOPIC "/sys/a1cMlEwEwjg/healthy_iot_dev/thing/service/property/set"
#define POST_TOPIC "/sys/a1cMlEwEwjg/healthy_iot_dev/thing/event/property/post"
#define CONNECT_WIFI "Xiaomi_meizi6"
#define CONNECT_PASS "12170307yu"
#define CONNECT_SERVER_IP "a1cMlEwEwjg.iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define CONNECT_SERVER_PORT 1883
char mqtt_message[200];
u32 PagCnt=0;
void App_calStepHandler(void);
void OLEDPag1(void);
void OLEDPag2(void);
void OLEDPag3(void);
void OLEDPag4(void);
int HeartRate=60;
int BloodOxygen=66;
float temperature=37.8;
int steps=88;
float GY_MCU90615V2_buff[2];
uint32_t aun_ir_buffer[500];
int32_t n_ir_buffer_length;
uint32_t aun_red_buffer[500];
int32_t n_sp02;
int8_t ch_spo2_valid;
int32_t n_heart_rate;
int8_t ch_hr_valid;
uint8_t uch_dummy;
#define MAX_BRIGHTNESS 255
void dis_DrawCurve(u32* data,u8 x);
int main()
{
uint32_t un_min, un_max, un_prev_data;
u32 time_cnt=0;
u8 esp8266_state=0;
u32 i;
u8 temp[6];
int Tmp,stat=0;
int32_t n_brightness;
float f_temp;
u8 dis_hr=0,dis_spo2=0;
u8 str[100];
u8 cnt=1;
BeepInit();
JTAG_Set(SWD_ENABLE);
LedInit();
KeyInit();
Usart1Init(72,115200);
USART3_Init(115200);
TIMER3_Init(72,20000);
OLED_Init();
OLEDPag1();
TemPeratureInit();
RTC_Init();
stat=MPU_Init();
printf("MPU6050=%d\n",stat);
WatchInfo_init();
max30102_init();
Timer4Init(2500,720);
OLEDPag1();
printf("系统正常!\r\n");
for(i=0;i<5;i++)
{
if(ESP8266_Init()==0)
{
esp8266_state=1;
break;
}
else
{
esp8266_state=0;
printf("ESP8266硬件检测错误.\n");
}
}
if(esp8266_state)
{
printf("准备连接服务器....\r\n");
printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode(CONNECT_WIFI,CONNECT_PASS,CONNECT_SERVER_IP,CONNECT_SERVER_PORT,1));
MQTT_Init();
for(i=0;i<5;i++)
{
if(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord)==0)
{
esp8266_state=1;
break;
}
esp8266_state=0;
printf("服务器连接失败,正在重试...\n");
delay_ms(500);
}
printf("服务器连接成功.\n");
if(MQTT_SubscribeTopic(SET_TOPIC,0,1))
{
printf("主题订阅失败.\n");
}
else
{
printf("主题订阅成功.\n");
}
}
un_min=0x3FFFF;
un_max=0;
n_ir_buffer_length=500;
for(i=0;i<n_ir_buffer_length;i++)
{
while(MAX30102_INT==1);
max30102_FIFO_ReadBytes(REG_FIFO_DATA,temp);
aun_red_buffer[i] = (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2];
aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03)<<16) |(long)temp[4]<<8 | (long)temp[5];
if(un_min>aun_red_buffer[i])
un_min=aun_red_buffer[i];
if(un_max<aun_red_buffer[i])
un_max=aun_red_buffer[i];
}
un_prev_data=aun_red_buffer[i];
maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
while(1)
{
Tmp=GetKeyVal(0);
if(Tmp)
{
printf("按键按下:%d\r\n",Tmp);
if(cnt>=4)cnt=0;
cnt++;
OLED_Clear();
}
if(cnt==1)
{
OLEDPag1();
}
if(cnt==2)
{
TIM2->CR1|=1<<0;
OLEDPag2();
}
else
{
TIM2->CR1&=~(1<<0);
}
if(cnt==3)
{
OLEDPag3();
}
if(cnt==4)
{
OLEDPag4();
}
if(USART3_RX_FLAG)
{
USART3_RX_BUFFER[USART3_RX_CNT]='\0';
printf("UART3收到数据.....\r\n");
for(i=0;i<USART3_RX_CNT;i++)
{
printf("%c",USART3_RX_BUFFER[i]);
}
}
un_min=0x3FFFF;
un_max=0;
for(i=100;i<500;i++)
{
aun_red_buffer[i-100]=aun_red_buffer[i];
aun_ir_buffer[i-100]=aun_ir_buffer[i];
if(un_min>aun_red_buffer[i])
un_min=aun_red_buffer[i];
if(un_max<aun_red_buffer[i])
un_max=aun_red_buffer[i];
}
for(i=400;i<500;i++)
{
un_prev_data=aun_red_buffer[i-1];
while(MAX30102_INT==1);
max30102_FIFO_ReadBytes(REG_FIFO_DATA,temp);
aun_red_buffer[i] = (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2];
aun_ir_buffer[i] = (long)((long)((long)temp[3] & 0x03)<<16) |(long)temp[4]<<8 | (long)temp[5];
if(aun_red_buffer[i]>un_prev_data)
{
f_temp=aun_red_buffer[i]-un_prev_data;
f_temp/=(un_max-un_min);
f_temp*=MAX_BRIGHTNESS;
n_brightness-=(int)f_temp;
if(n_brightness<0)
n_brightness=0;
}
else
{
f_temp=un_prev_data-aun_red_buffer[i];
f_temp/=(un_max-un_min);
f_temp*=MAX_BRIGHTNESS;
n_brightness+=(int)f_temp;
if(n_brightness>MAX_BRIGHTNESS)
n_brightness=MAX_BRIGHTNESS;
}
if(ch_hr_valid == 1 && n_heart_rate<120)
{
dis_hr = n_heart_rate;
dis_spo2 = n_sp02;
}
else
{
dis_hr = 0;
dis_spo2 = 0;
}
}
maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
if(dis_hr == 0 && dis_spo2 == 0)
{
sprintf((char *)str,"HR:--- SpO2:--- ");
}
else
{
sprintf((char *)str,"HR:%3d SpO2:%3d ",dis_hr,dis_spo2);
}
HeartRate=dis_hr;
BloodOxygen=dis_spo2;
GetTemInfo(GY_MCU90615V2_buff);
temperature=GY_MCU90615V2_buff[0];
steps=userSportsInfo.stepCount/5.0;
if(HeartRate>200)
{
BEEP=1;
printf("警告心率超过正常阀值.\r\n");
}
else
{
BEEP=0;
}
if(temperature>40)
{
BEEP=1;
printf("警告体温超过正常阀值.\r\n");
}
else
{
BEEP=0;
}
if(HeartRate>50 && BloodOxygen>30)
{
}
time_cnt++;
delay_ms(10);
if(time_cnt>100)
{
time_cnt=0;
LED1=!LED1;
sprintf(mqtt_message,"{\"method\":\"thing.event.property.post\",\"id\":\"1234567890\",\"params\":{\"HeartRate\":%d,\"BloodOxygen\":%d,\"temperature\":%f,\"steps\":%d},\"version\":\"1.1.1\"}",
HeartRate,BloodOxygen,temperature,steps);
MQTT_PublishData(POST_TOPIC,mqtt_message,0);
sprintf(mqtt_message,"HeartRate:%d,BloodOxygen:%d,temperature:%f,steps:%d\r\n",
HeartRate,BloodOxygen,temperature,steps);
printf("%s\r\n",mqtt_message);
}
}
}
void OLEDPag1(void)
{
OLED_DrawBMP(0,0,11,1,SignalTower);
OLED_DrawBMP(111,0,128,1,Battery);
OLED_ShowCHinese(28,2,0);
OLED_ShowCHinese(46,2,1);
OLED_ShowCHinese(64,2,2);
OLED_ShowCHinese(82,2,3);
OLED_ShowNum(8*(0+3),4,RTC_Timer.year,4,16);
OLED_ShowChar(8*(4+3),4,'-');
OLED_ShowNum(8*(5+3),4,RTC_Timer.month,2,16);
OLED_ShowChar(8*(7+3),4,'-');
OLED_ShowNum(8*(8+3),4,RTC_Timer.day,2,16);
OLED_ShowNum(8*0,6,RTC_Timer.hour,2,16);
OLED_ShowChar(8*2,6,':');
OLED_ShowNum(8*3,6,RTC_Timer.minute,2,16);
OLED_ShowChar(8*5,6,':');
OLED_ShowNum(8*6,6,RTC_Timer.sec,2,16);
OLED_ShowString(8*9,6,"Week:");
OLED_ShowNum(8*14,6,RTC_Timer.week,1,16);
}
void OLEDPag2(void)
{
OLED_DrawBMP(0,0,11,1,SignalTower);
OLED_DrawBMP(111,0,128,1,Battery);
OLED_ShowCHinese(8*9,2,9);
OLED_ShowCHinese(8*11,2,10);
OLED_ShowNum(8*9,5,HeartRate,3,16);
OLED_DrawBMP(8*3,2,8*(3+4),6,Heart);
}
void OLEDPag3(void)
{
OLED_DrawBMP(0,0,11,1,SignalTower);
OLED_DrawBMP(111,0,128,1,Battery);
OLED_ShowCHinese(8*5,2,16);
OLED_ShowCHinese(8*7,2,4);
OLED_ShowCHinese(8*9,2,17);
u8 buff_b[10];
sprintf((char*)buff_b,"%d",userSportsInfo.stepCount/5);
strcat((char *)buff_b,"step");
OLED_ShowString(8*6,4,buff_b);
u8 buff_m[10];
sprintf((char*)buff_m,"%0.1f",userSportsInfo.distance/5);
strcat((char *)buff_m,"m");
OLED_ShowString(8*6,6,buff_m);
PagCnt++;
DelayMs(1);
if(PagCnt==300)
{
OLED_DrawBMP(0,2,18,6,OnFoot_1);
}
if(PagCnt==600)
{
OLED_DrawBMP(0,2,18,6,Nothing);
OLED_DrawBMP(0,2,17,6,OnFoot_2);
PagCnt=0;
}
}
void OLEDPag4(void)
{
u32 a,b;
OLED_DrawBMP(0,0,11,1,SignalTower);
OLED_DrawBMP(111,0,128,1,Battery);
OLED_DrawBMP(0*8,2,8*(0+4),6,Temperature);
OLED_ShowCHinese(8*6,0,6);
OLED_ShowCHinese(8*8,0,7);
if(tem_flag)
{
tem_flag=0;
a=GY_MCU90615V2_buff[0];
b=(((float)GY_MCU90615V2_buff[0]-a)*100);
OLED_ShowString(8*5,2,"T0:");
OLED_ShowString(8*5,4,"T1:");
OLED_ShowNum(8*8,2,a,2,16);
OLED_ShowChar(8*10,2,'.');
OLED_ShowNum(8*11,2,b,2,16);
OLED_ShowString(8*13,2,"T");
a=GY_MCU90615V2_buff[1];
b=(((float)GY_MCU90615V2_buff[1]-a)*100);
OLED_ShowNum(8*8,4,a,2,16);
OLED_ShowChar(8*10,4,'.');
OLED_ShowNum(8*11,4,b,2,16);
OLED_ShowString(8*13,4,"T");
}
}
void App_calStepHandler(void)
{
u8 err;
accValue_t accValue;
static u32 step_count_old_Val;
static u8 time_cnt;
static timeStamp_t timeStamp;
short aacx,aacy,aacz;
static u8 tempSecond;
Timer *rtcTime;
MPU_Get_Accelerometer(&aacx,&aacy,&aacz);
rtcTime = &RTC_Timer;
if(tempSecond != timeStamp.second)
{
tempSecond = timeStamp.second;
timeStamp.twentyMsCount = 0 ;
}
else
{
timeStamp.twentyMsCount ++;
}
timeStamp.hour = rtcTime->hour;
timeStamp.minute = rtcTime->minute;
timeStamp.second = rtcTime->sec;
accValue.accX = ((float)(int)aacx/16384) *10;
accValue.accY = ((float)(int)aacy/16384) *10;
accValue.accZ = ((float)(int)aacz/16384) *10;
step_count_old_Val = userSportsInfo.stepCount;
userSportsInfo = *onSensorChanged(&accValue,&timeStamp,WatchInfo_getUserInfo(&err));
if(step_count_old_Val != userSportsInfo.stepCount)
{
time_cnt++;
}
if(time_cnt == 1)
{
time_cnt = 0;
}
}
|