3

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发

 2 years ago
source link: https://blog.51cto.com/u_11822586/5043295
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发

原创

DS小龙哥 2022-02-24 09:48:14 博主文章分类:STM32项目开发 ©著作权

文章标签 数据 应用层 客户端 文章分类 嵌入式Linux 嵌入式 阅读数643

近几年随着科技的进步和智能化浪潮的到来,智能穿戴设备也在飞速火爆发展,各种健康智能手环,智能手表、智能跑鞋、智能眼镜纷纷上市,并出现了很多针对个人家庭的健康管理设备。比如: 智能血压计、智能心率检测、脂肪秤、智能体重秤等等,都带上了智能、健康各种标签。

可穿戴设备,即直接穿在身上,或是整合到用户的衣服或配件的一种便携式设备。可穿戴设备不仅仅是一种硬件设备,更是通过软件支持以及数据交互、云端交互来实现强大的功能,可穿戴设备将会对生活、感知带来很大的转变。

这篇文章就利用STM32加上各种外设传感器配合华为云IOT物联网平台设计一个健康管理设备,通过ESP8266+MQTT协议将数据传输导致华为云物联网平台,并通过华为云的应用侧完成应用层软件开发;设计本项目的目的就是,上手体验华为云物联网平台,并探究一下智能设备的实现原理。

当前设计的监控管理设备支持的功能有:
(1)人体温度测量
(2)运动监测、计步功能
(3)睡眠监测
(4)心率测量

STM32采集这些传感器数据之后,进行处理,在本地OLED显示屏上完成显示;再通过ESP8266将数据传递到华为云物联网平台,关联数据可视化大屏完成数据展示。

下面是示波器测量的心率显示

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据

设备运行效果:

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_02

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_03

二、硬件介绍

2.1 主控芯片

主控芯片采用STM32F103C8T6,它一款基于ARM Cortex-M 内核STM32系列的32位的微控制器,程序存储器容量是64KB,RAM空间是20K,工作电压2V~3.6V,运行速度72MHZ。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_04

2.2 体温测量

人体温度测量,采用非接触式红外测温芯片GY-MCU90615,工作电压 3-5v 功耗小,体积小。其工作原理, 是通过单片机读取红外温度度数据,串口(TTL 电平)通信方式输出。串口的波特率有 9600bps 与 115200bps有连续输出与询问输出两种方式,可适应不同的工作环境,与所有的单片机及电脑连接。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_05

2.3 心率测量

心率测量,采用PulseSensor传感器,这是一款用于脉搏心率测量的光电反射式模拟传感器,通过模拟输出口可将采集到的模拟信号传输给 STM32单片机用来转换为数字信号,再通过单片机简单计算后就可以得到心率数值。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_06

2.4 计步、睡眠监测功能

计步模块,睡眠监测,运动监测功能采用MUP6050陀螺仪实现,这是一款高性能三轴加速度+三轴陀螺仪的六轴传感器,该模块采用InvenSense 公司的 MPU6050 芯片作为核心, 该芯片内部整合了3轴陀螺仪和3轴加速度传感器,并可利用自带的数字运动处理器硬件加速引擎,通过主 IIC 接口,向应用端输出姿态解算后的数据。有了DMP,可以使用 InvenSense 公司提供的运动处理资料库,非常方便的实现姿态解算,降低了运动处理运算对操作系统的负荷,同时大大降低了开发难度。
MPU6050 模块具有:体积小、自带 DMP、 自带温度传感器、 支持 IIC 从机地址设置和中断、兼容 3.3V/5V 系统、使用方便等特点。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_07

(5)本地数据显示用的OLED显示屏采用0.96寸的SPI接口显示屏,分辨率为 128*64,主要是在本地显示采集的数据,时间等信息。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_08

(6)上网的模块采用ESP8266,ESP8266是物联网领域常见无线网卡芯片,支持AT指令,支持串口协议控制,只需要几个简单的AT指令就可以完成网络连接,数据传输。当前项目里,就是通过ESP8266将采集的数据传递到华为云IOT平台,实现数据展示。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_09

三、创建IOT产品、上云测试

3.1 创建产品

官网地址:  https://www.huaweicloud.com/s/JeeJqeiBlOe9kSU

选择IOTDA进入,选择免费试用。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_10

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_11

在产品页面,选择右上角创建产品。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_12

根据提示,填入对应参数。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_13

创建好之后,查看产品详情,进入属性配置页面。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_14

选择自定义模型。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_15

添加服务。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_16

接下来就添加属性,属性就是传感器上传的数据类型,需要展示的数据;根据自己传感器的数量、类型自己设置即可。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_17

添加心率传感器数据属性。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_18

添加体温传感器数据属性。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_19

添加计步功能的数据属性。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_20

创建成功:

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_21

3.2 注册设备

打开设备页面,点击右上角注册设备按钮,根据提示和产品的信息填入;创建完保存得到的信息。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_22

点击确定之后,创建成功效果如下;目前设备还未激活,需要设备登录一次服务器即可激活;接下来就是如何登录了。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_23

3.3 设备上云测试

完成产品、设备创建之后,接下来采用MQTT客户端模拟设备,测试是否可以正常上华为云。

连接协议使用MQTT协议,MQTT协议登录服务器,就像QQ登录一样,需要输入账号、密码等一些信息;下面先利用华为云的小工具完成这些数据的创建。

华为云提供的MQTT账户信息生成在线小工具:  https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

前面两行填入的数据,在创建设备成功时提示下载的文件里有,照着填写即可。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_24

我的设备生成的数据如下:

ClientId   61df9a6bc7fb24029b0c160d_1126626497_0_0_2022011303
Username   61df9a6bc7fb24029b0c160d_1126626497
Password   20618c172eb24418e0910804889c7d2074a5847e9e7205a41a8bf5adeec399f9

华为云IOT平台的MQTT服务器地址信息如下:

端口: 1883
域名: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
IP地址: 121.36.42.100

华为云IOT平台MQTT协议订阅主题的格式:

格式: $oc/devices/{device_id}/sys/messages/down
//订阅主题: 平台下发消息给设备
$oc/devices/61df9a6bc7fb24029b0c160d_1126626497/sys/messages/down

华为云IOT平台MQTT协议上报主题的格式:

格式: $oc/devices/{device_id}/sys/properties/report
//设备上报主题请求
$oc/devices/61df9a6bc7fb24029b0c160d_1126626497/sys/properties/report


//上报的数据格式如下
{"services": [{"service_id": "healthy","properties":{"HeartRate":127}},{"service_id": "healthy","properties":{"motion":2000}},{"service_id": "healthy","properties":{"temperature":36.2}}]}

打开MQTT客户端,填入对应数据,连接华为云物联网平台:

如需使用和我一样的同款软件,打开百度搜索MQTT客户端_v2.4(协议3.1.1).exe 即可找到下载地址。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_25

登录成功后,查看华为云页面,可以看到设备已经在线,并且上传的数据已经展示出来。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_26

四、应用侧软件开发

4.1 功能介绍

为了更方便的展示设备数据,与设备完成交互,还需要开发一个配套的上位机,官方提供了应用侧开发的API接口、SDK接口,为了方便通用一点,我这里采用了API接口完成数据交互,上位机软件采用QT开发。

帮助文档地址:  https://support.huaweicloud.com/usermanual-iothub/iot_01_0045.html

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_27

4.2 查询设备属性接口

设备属性就是设备上传的传感器状态数据信息,应用侧提供了API接口,可以主动向设备端下发请求指令;设备端收到指令之后需要按照约定的数据格式上报数据;所以,要实现应用层与设备端的数据交互,需要应用层与设备端配合才能完成。

下面分别介绍应用测和设备测的实现流程。

(1)应用层下发的指令

帮助文档地址:  https://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html

接口的在线调试地址:  https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=IoTDA&api=ListProperties

如果请求参数和返回值不清楚,写代码前,先使用在线调试接口体验一下,验证数据交互是否OK。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_客户端_28

请求参数里比较总要的两个必填参数,是设备ID和服务ID,这两个参数在第3章节就介绍过如何获取了,在产品页面创建自定义属性时可以看到服务ID。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_29

请求接口总结:

请求方法 GET
URI地址  /v5/iot/{project_id}/devices/{device_id}/properties
传输协议 HTTPS

拼接好的地址: 
https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/0e5957be8a00f53c2fa7c0045e4d8fbf/devices/61df9a6bc7fb24029b0c160d_1126626497/properties?service_id=1126626497

其中的project_id和device_id需要根据自己的设备信息修改。


请求头:
{
 "User-Agent": "API Explorer",
 "X-Auth-Token": "******",   这个是鉴权用的token
 "Content-Type": "application/json"
}


响应体(设备上传的数据)
{
 "response": {
  "services": [
   {
    "service_id": "healthy",
    "properties": {
     "HeartRate": 127
    }
   },
   {
    "service_id": "healthy",
    "properties": {
     "motion": 2000
    }
   },
   {
    "service_id": "healthy",
    "properties": {
     "temperature": 36.2
    }
   }
  ]
 }
}

请求头里需要填X-Subject-Token参数,这个参数只要是访问任何华为云都需要填,获取具体的流程可以看这里。 https://bbs.huaweicloud.com/blogs/317759 翻到第3小节。

(2)设备上传数据

应用层向设备端请求查询设备属性时,设备端会收到如下的消息:

$oc/devices/61df9a6bc7fb24029b0c160d_1126626497/sys/properties/get/request_id=336bcb57-0e0a-44d0-90f7-31386cb54a3c{"service_id":"1126626497"}

这个消息里有一个主要参数request_id请求ID,设备端需要解析出这个参数,给应用层响应数据时,需要带上这个ID。

这个请求属性详细帮助文档看这里:  https://support.huaweicloud.com/api-iothub/iot_06_v5_3011.html

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_30

设备响应的数据格式:

主题格式: $oc/devices/{device_id}/sys/properties/get/response/request_id={request_id}

示    例:
$oc/devices/61df9a6bc7fb24029b0c160d_1126626497/sys/properties/get/response/request_id=336bcb57-0e0a-44d0-90f7-31386cb54a3c

响应的数据格式:
{"services": [{"service_id": "healthy","properties":{"HeartRate":127}},{"service_id": "healthy","properties":{"motion":2000}},{"service_id": "healthy","properties":{"temperature":36.2}}]}

响应的数据格式可以看这里的介绍:  https://support.huaweicloud.com/api-iothub/iot_06_v5_3010.html

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_31

4.3 在线API调试结合设备模拟

下面使用MQTT客户端与在线API接口联合模拟一下接口效果:

(1)先打开调试页面:  https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=IoTDA&api=ListProperties

然后填好设备DI和服务ID:

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_32

(2)、打开MQTT客户端,登录华为云物联网平台(也就是模拟设备上线):

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_33

(3)、打开在线API调试页面,点击调试: 点击后可以看到页面上已经在等待客户端的响应了。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_34

(4)、MQTT客户端响应详细

按照前面说的响应格式,拼接好接口,数据。然后发布主题。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_数据_35

(5)、应用层收到客户端响应,调试成功

调试成功后,响应体里收到的就是设备端上传的设备属性数据。

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_36

4.4 应用层核心代码

/*
功能: 获取token
*/
void Widget::GetToken()
{
    //表示获取token
    function_select=3;

    QString requestUrl;
    QNetworkRequest request;

    //设置请求地址
    QUrl url;

    //获取token请求地址
    requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens")
                 .arg(SERVER_ID);

    //自己创建的TCP服务器,测试用
    //requestUrl="http://10.0.0.6:8080";

    //设置数据提交格式
    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));

    //构造请求
    url.setUrl(requestUrl);

    request.setUrl(url);

    QString text =QString("{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":"
    "{\"user\":{\"domain\": {"
    "\"name\":\"%1\"},\"name\": \"%2\",\"password\": \"%3\"}}},"
    "\"scope\":{\"project\":{\"name\":\"%4\"}}}}")
            .arg(MAIN_USER)
            .arg(IAM_USER)
            .arg(IAM_PASSWORD)
            .arg(SERVER_ID);

    //发送请求
    manager->post(request, text.toUtf8());
}

//查询设备属性
void Widget::Get_device_properties()
{
    //表示获取token
    function_select=0;

    QString requestUrl;
    QNetworkRequest request;

    //设置请求地址
    QUrl url;

    //获取token请求地址
    requestUrl = QString("https://iotda.%1.myhuaweicloud.com/v5/iot/%2/devices/%3/properties?service_id=%4")
                 .arg(SERVER_ID)
            .arg(PROJECT_ID)
            .arg(device_id)
            .arg(service_id);

    //自己创建的TCP服务器,测试用
    //requestUrl="http://10.0.0.6:8080";

    //设置数据提交格式
    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));

    //设置token
    request.setRawHeader("X-Auth-Token",Token);

    //构造请求
    url.setUrl(requestUrl);

    request.setUrl(url);

    //发送请求
    manager->get(request);
}

#yyds干货盘点# 基于STM32+ESP8266+华为云IoT设计的健康管理系统并完成应用侧开发_应用层_37

五、设备底层开发

下面列出STM32设备底层端的一些传感器核心处理代码。

5.1 心率采集计算算法

int BPM;                   			 // 用于保存脉冲速率
int Signal;               			 // 持有的原始数据
int IBI = 600;            			 
unsigned char Pulse = false;    
unsigned char QS = false;        
int rate[10];                    
unsigned long sampleCounter = 0; 
unsigned long lastBeatTime = 0;  
int P =512;                      
int T = 512;                     
int thresh = 512;               
int amp = 100;                  
unsigned char firstBeat = true;  
unsigned char secondBeat = false;
/*
	定时器2中断服务函数 用于周期性采集心率值
*/
void TIM2_IRQHandler(void)
{
	uint16_t runningTotal=0;
	uint8_t i;
	uint16_t Num;
	
	if(TIM2->SR&1<<0)
	{
			//读取到的值右移2位,12位-->10位
		Signal = Get_AdcCHx_DATA(1)>>2;     	
		sampleCounter += 2;                          
		Num = sampleCounter - lastBeatTime; 

		//发现脉冲波的波峰和波谷
		//  find the peak and trough of the pulse wave
		if(Signal < thresh && Num > (IBI/5)*3)
		{   
			if (Signal < T)
			{                       			 				
				T = Signal;  
			}
		}

		if(Signal > thresh && Signal > P)
		{ 
			P = Signal; 
		}  

		//开始寻找心跳
		//当脉冲来临的时候,signal的值会上升
		if (Num > 250)
		{                            
			if ( (Signal > thresh) && (Pulse == false) && (Num > (IBI/5)*3) )
			{        
				Pulse = true;                         
				//LED0(0); 
				IBI = sampleCounter - lastBeatTime;       
				lastBeatTime = sampleCounter;              

				if(secondBeat)
				{                       
					secondBeat = false;             
					for(i=0; i<=9; i++)
					{               
						rate[i] = IBI;              
					}
				}

				if(firstBeat)
				{                         						
					firstBeat = false;                   	
					secondBeat = true;                   	
					return;                              	
				}   

				for(i=0; i<=8; i++)
				{                    							
					rate[i] = rate[i+1];                
					runningTotal += rate[i];           
				}

				rate[9] = IBI;                       
				runningTotal += rate[9];                
				runningTotal /= 10;                     
				BPM = 60000/runningTotal;               
				QS = true;                            
			}                       
		}

		//脉冲开始下降
		if (Signal < thresh && Pulse == true)
		{
			Pulse = false;                        
			amp = P - T;                          
			thresh = amp/2 + T;                    
			P = thresh;                         
			T = thresh;
		}

		//没有检测到脉冲,设置默认值
		if (Num > 2500)
		{                       
			thresh = 512;                       
			P = 512;                              
			T = 512;                              
			lastBeatTime = sampleCounter;               
			firstBeat = true;                      
			secondBeat = false;                    
		}   
	}
	TIM2->SR&=0x0; //清中断标志
}

5.2 OLED关键代码

//向SSD1106写入一个字节。
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 cmd)
{	
	u8 i;			  
	if(cmd)
	  OLED_DC_Set();
	else 
	  OLED_DC_Clr();		  
	OLED_CS_Clr();
	for(i=0;i<8;i++)
	{			  
		OLED_SCLK_Clr();
		if(dat&0x80)
		   OLED_SDIN_Set();
		else 
		   OLED_SDIN_Clr();
		OLED_SCLK_Set();
		dat<<=1;   
	}				 		  
	OLED_CS_Set();
	OLED_DC_Set();   	  
} 

//设置坐标的位置(x范围: 0~127   ,   y的范围:0~63)
//注意: 8 行为一页,共 64 行即 8 页
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 
	OLED_WR_Byte(0xb0+y,OLED_CMD);
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD); 
}

5.3 体温采集换算

u8 Receive_ok;
u8 rebuf[20]={0};
void RxTempInfo(void)
{
	 static uint8_t i=0;
	if(USART2->SR&1<<5)     //判断接收标志
	{
		rebuf[i++]=USART2->DR;//读取串口数据,同时清接收标志
		if(rebuf[0]!=0x5a)    //帧头不对
			i=0;	
		if((i==2)&&(rebuf[1]!=0x5a))//帧头不对
			i=0;
	
		if(i>3)//i等于4时,已经接收到数据量字节rebuf[3]
		{
			if(i!=(rebuf[3]+5))//判断是否接收一帧数据完毕
				return ;	
			switch(rebuf[2])   //接收完毕后处理
			{
				case 0x45:
					if(!Receive_ok)//当数据处理完成后才接收新的数据
					{
						 Receive_ok=1;//接收完成标志
					}
					break;
				case 0x15:break;
				case 0x35:break;
			}
			i=0;//缓存清0
		}
	}
}

void GetTempInfo(void)
{
	float TO=0,TA=0;
    u8 sum=0,i=0;
	for(sum=0,i=0;i<(rebuf[3]+4);i++)
	{
	  	sum+=rebuf[i];  
	}
	if(sum==rebuf[i])//校验和判断
	{
		TO=(float)((rebuf[4]<<8)|rebuf[5])/100;  //得到真实温度
		TA=(float)((rebuf[6]<<8)|rebuf[7])/100;  //得到真实温度	
	}
	printf("TO: %f\r\n",TO);
	printf("TA: %f\r\n",TA);
}

5.4 运动计步算法

/*******************************************************************************
* LOCAL VARIABLES
*/
//存放三轴数据  
float oriValues[3] = {0};    
//用于存放计算阈值的波峰波谷差值  
float tempValue[VALUE_NUM] ={0};  
int tempCount = 0;  
//是否上升的标志位  
u8 isDirectionUp = FALSE;  
//持续上升次数  
int continueUpCount = 0;  
//上一点的持续上升的次数,为了记录波峰的上升次数  
int continueUpFormerCount = 0;  
//上一点的状态,上升还是下降  
u8 lastStatus = FALSE;  
//波峰值  
float peakOfWave = 0;  
//波谷值  
float valleyOfWave = 0;  
//此次波峰的时间  
long timeOfThisPeak = 0;  
//上次波峰的时间  
long timeOfLastPeak = 0;  
//当前的时间  
long timeOfNow = 0;  
//当前传感器的值  
float gravityNew = 0;  
//上次传感器的值  
float gravityOld = 0;  
//动态阈值需要动态的数据,这个值用于这些动态数据的阈值  
float initialValue = (float) 1.3;  
//初始阈值  
float ThreadValue = (float) 2.0;
//三轴轴值
accValue_t accValue;
//行走信息:卡路里、里程、步数
static sportsInfo_t sportsInfo;
//计步缓存
static u8 stepTempCount =0;


/*******************************************************************************
* 函数名:DetectorNewStep
* 功能描述: 
*		  步伐更新:如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步 		
*		  阀值更新:符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中 		
* 参数说明:  
输入:
values:经过处理的G-sensor数据
timeStamp_p:时间戳
* 返回值说明:
* 修改记录:sportsInfo_t *onSensorChanged(accValue_t *pAccValue,timeStamp_t *timeStamp_p,personInfo_t * personInfo)
*******************************************************************************/
sportsInfo_t *DetectorNewStep(float values,timeStamp_t *timeStamp_p,personInfo_t * personInfo) 
{  
  static u32 time_old;
  personInfo_t *userInfo = personInfo;
  static u32 step_per_2_second;  //每两秒所走的步数
  float step_lenth,walk_speed,walk_distance,Calories;//步长
  u32 time_now;
  timeStamp_t *time_p = timeStamp_p;
  if (gravityOld == 0) 
  {  
    gravityOld = values;  
  } 
  else 
  {  
    if (DetectorPeak(values, gravityOld))//检测到波峰
    {  
      timeOfLastPeak = timeOfThisPeak;//更新上次波峰的时间  
      //将时间戳转换为以毫秒ms为单位
      time_now = timeOfNow = ((time_p->hour*60+time_p->minute)*60+time_p->second)*1000+time_p->twentyMsCount*20; //获取时间 ,并转化为毫秒
      //如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步 
      if (  (timeOfNow - timeOfLastPeak >= 250 )//Jahol Fan 修改为300,防止轻微动都也会检测步子
          //&& (timeOfNow - timeOfLastPeak <= 2000)
          &&(peakOfWave - valleyOfWave >= ThreadValue)
            )
      {  
        timeOfThisPeak = timeOfNow; //更新此次波峰时间 
        
        
       	stepTempCount++;//Jahol:加1为两步
        step_per_2_second ++;
        //Jahol:这样计算卡路里,不能滤除人为的误操作,导致的结果是:里程和卡路里偏大
        if((time_now - time_old) >= 2000 )    //如果时间过了2秒
        {

          if( 1 == step_per_2_second )				   
          {
            step_lenth = userInfo->height/5;
          }
          else if( 2 == step_per_2_second )
          {
            step_lenth = userInfo->height/4;
          }
          else if( 3 == step_per_2_second )
          {
            step_lenth = userInfo->height/3;
          }
          else if( 4 == step_per_2_second )
          {
            step_lenth = userInfo->height/2;
          }
          else if(5 == step_per_2_second)			  //Jahol:为了使计步准确,设置上限值为5步,牺牲卡路里准确性
          {
            step_lenth = userInfo->height/1.2f;
          }
          else if( 7 == step_per_2_second )
          {
            step_lenth = userInfo->height;
          }
          else if(step_per_2_second >= 8)				//		step_diff>8
          {
            step_lenth = userInfo->height*1.2f;
          }
          else 
          {
            step_lenth = 0;
          }
          walk_speed = step_per_2_second*step_lenth/2;   //速度 ,单位:米/秒
          walk_distance  = step_per_2_second*step_lenth; //行走距离,单位:米
          Calories = 4.5f*walk_speed*(userInfo->weight/2)/1800;  //Jahol:weight是以kg为单位
          sportsInfo.calories  += Calories;
          sportsInfo.distance  += walk_distance;		
          time_old = time_now;         //更新时间
          step_per_2_second = 0;
          
        }   
        else 
        {
          //do nothing
        }		
        /* 
        * 处理无效运动: 
        * 1.连续记录5才开始计步 
        * 2.例如记录的步用户停住超过3秒,则前面的记录失效,下次从头开始 
        * 3.连续4记录了步用户还在运动,之前的数据才有效 
        * */  				
        if ((stepTempCount< 5 )&&(timeOfNow - timeOfLastPeak >= 3000))          
        {
          stepTempCount = 0;
        }
        else if((stepTempCount>= 5)&&(timeOfNow - timeOfLastPeak <= 3000))
        {
          sportsInfo.stepCount += stepTempCount;          
          stepTempCount         = 0; 				
        }
        else
        {
          //do nothing
        }
        
        
      }  
      //Jahol:更新阀值,问题:阀值不会一直变大,不能变小?
      if (timeOfNow - timeOfLastPeak >= 250  
          && (peakOfWave - valleyOfWave >= initialValue)) 
      {  
        timeOfThisPeak = timeOfNow;  
        ThreadValue = Peak_Valley_Thread(peakOfWave - valleyOfWave);//更新阀值  
      }  
    }  
  }  
  gravityOld = values;  
  return &sportsInfo;
}



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK