STM32+ESP01S连接阿里云(上报温湿度(DHT11)与心率血样数据(惊帆JF141模组))

前两篇博客,我介绍了我给血氧模块写的驱动,和如何将官方的MQTT例程更改来实现连接阿里云并上传数据的过程。

那么有了这些我们就可以来实现一个完整的上报数据的功能了!

STM32+ESP01S连接阿里云(上报温湿度(DHT11)与心率血样数据(惊帆JF141模组))_第1张图片

在做这个东西之前我的想法是:

通过ESP8266,在联网成功后第一时间获取当前时间,并反馈给单片机,单片机利用自身的RTC功能继续走时,提供当前时间给程序使用,尽量不要利用定时器,在定时器里面上传数据(数据通过串口发送给WIFI模块),主要是怕影响血氧采集,每过30秒,LED灯亮2秒左右,代表上传数据,按下按键1,代表使用者想要测量心率和血氧,此时延时5s左右开始播报语音,请开始测量,打开血氧传感器模块,并采集数据,等待RTC时间,每经过1s记录一次数据,共记录15次数据,然后求平均值,最后一次数据接收完成后,播报测量结束语音,将测量值显示在LED屏上面,并播报数据上传进度。数据上传完成恢复温湿度检测模式

但是在做的过程中,发现联网获取当前时间,虽然理论上也是可行的,但是获取到时间之后如何让这个时间在本地继续走时,让我感觉到很不好实现,因为我想着如果用RTC来走时的话,如何才能将实时的时间显示到OLED屏上呢?第一种方法肯定就是利用RTC的定时中断,在定时中断中,实现走时的功能,但是基本上只能精确到分钟级,因为秒级会导致频繁进入中断,可能会影响主程序进程,因为要用OLED显示。不过后来想了想可以考虑用DMA+硬件I2C自动传输数据实现,回头我试试!

接下来就回到正题,我在做之前的想法是那样的,但是做之后,就把从网络获取时间,和显示时间的功能给去掉了,因为首先是感觉实现起来比较困难,另一个原因就是,OLED有点小,没办法显示那么多内容。所以我的这个东西的整体思路就是:
开机》开始连接WIFI和阿里云》连接成功就置连接成功标志位,失败(超时退出,防止程序卡死)同理》RTC定时5分钟中断通过置标志位的方式实现获取并上报温湿度数据》利用外部中断实现按键更新标志位为采集心率和血氧数据并上传的程序》两种采集上传程序执行一次之后均接收进入睡眠模式。模式切换采用switch语句实现,因为结构比较清晰,并且切换模式比较方便。

在我的程序里面,最初的时候我是没有选择去做如果没有网络时的应对方法,后来觉得连不上网络就卡死,确实不太好,所以加入了是否连接成功的标志位,方便程序自信判断,当前的网络状态。

并且,再没有网络的时候,积极的重连网络,当然此处就按照大家的需求自行修改了,比如我是想,让这个东西能够及时并且正确的上传数据,所以我的策略就是有网的时候正确传输数据,传输失败或者没有网络的时候尽快重连。

下面是用到的变量名和标志位

    uint8_t Link_No=1;//连云端是否成功标志位,0:连接失败;1:连接成功
	bool Sign;//上传状态标志位
	uint8_t Sign_Bit=0;//模式标志位
	uint8_t count=0;//超时计次
	uint8_t Temp= 0;//温度取值范围/0-99
	uint8_t Humidity = 0;//湿度取值范围/0-99
	uint8_t HeartRate = 0;//心率取值范围/1-150
	uint8_t Blood_oxygen = 0;//血氧取值范围/50-99

主函数开始对需要的各种外设初始化,并进行第一次连接云端

	MyRTC_Init(); 
	delay_init();
	ESP8266_Init(115200);
	OLED_Init();
	MyDHT11_Init();
	Voice_Init();
	XU_Yang_Init();//语音模块JR6001模块
	Key_Init();
	MyRTC_ReadTime();//从RTC中获取日期用于OLED显示日期
	RTC_Alarm_Init();//RTC闹钟中断初始化
	RTC_Alarm_Set(200);//RTC闹钟中断时间间隔设置
	Link_No = ESP8266_STA_MQTTClient_Test();//MQTT通讯连接阿里云初始化
	delay_ms(1000);//显示信息,防止信息刷新过快
	OLED_ShowString(2, 1, "                ");//清楚显示
	OLED_ShowString(3, 1, "                ");

采集与上传温湿度数据的程序(模式0)

整体思路比较简单:

更新日期

获取数据

成功,继续进行上传程序

失败,重启模块上传上一次的数据(此处改为上传一个明显错误的数据来说明设备端出现问题好像也挺好的)

判断连接云端是否成功(连云端是否成功标志位)

成功,上传数据

失败,尝试重新连接云端(置连云端是否成功标志位)

清除标志位

				//更新日期
				MyRTC_ReadTime();
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(2,3," Get T + H");
				delay_ms(500);
				if(MyDHT11_Read_Data(&Temp,&Humidity))
				{//获取数据失败,等待下一次获取,并重启模块
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3,"  Get NO");
					MyDHT11_Rst();//
					delay_ms(500);
					break;
				}else
				{
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3,"  Get Yes");
				}
				OLED_ShowString(4,1,"                ");
				OLED_ShowString(4,1,"Temp:00");
				OLED_ShowString(4,8," Humi:00%");
				OLED_ShowNum(4,6,Temp,2);
				OLED_ShowNum(4,14,Humidity,2);				
				if(Link_No==1)//网络没有连接成功,就不发数据了
				{
					Sign = MQTT_SendString(User_ESP8266_MQTTServer_Topic_send,"Temp",Temp);//发送温度数据到MQTT服务器
					Sign = MQTT_SendString(User_ESP8266_MQTTServer_Topic_send,"Humidity",Humidity);//发送湿度数据到MQTT服务器
				}else
				{
					Sign=false;//初始化,不成功进入不联网模式,此时可以保留基本功能,不至于程序直接卡死,并在每次采集数据时积极尝试启动WIFI模块
				}
				if(Sign==true)//MQTT_SendString函数返回值有问题,如果采用实际返回值,就一直是失败,所以只能在程序中不论是否成功都显示成功
				{
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3," Cloud Yes");
					delay_ms(1000);
				}else
				{//防止当采集到数据,但不能上传时刷新界面过快
					delay_ms(1000);
					//上传数据失败,等待下一次上传,并重连
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3," Cloud No");
					Link_No = ESP8266_STA_MQTTClient_Test();
					OLED_ShowString(2,1,"                ");
					OLED_ShowString(4,1,"                ");
					OLED_ShowString(3,1,"                ");
					if(Sign_Bit==0)//只清除本模式的标志位,防止干扰其他模式的执行
					{
						Sign_Bit = 2;
					}//防止在运行过程中,按下按键又因为清除标志位导致无法进入模式1					
					break;
				}
				OLED_ShowString(4,1,"                ");
				OLED_ShowString(3,1,"                ");
				OLED_ShowString(2,1,"                ");
				if(Sign_Bit==0)//只清除本模式的标志位,防止干扰其他模式的执行
				{
					Sign_Bit = 2;
				}//防止在运行过程中,按下按键又因为清除标志位导致无法进入模式1

采集与上传心率血氧数据的程序(模式1)

整体思路也比较简单:

更新日期

发送血氧模块开始采集指令并且发出请放置手指语音指令,通过判断语音模块的busy位来查看语音播放是否完成。

//JR6001模块的简介大家可以看这一篇

https://blog.csdn.net/m0_56051805/article/details/125116764?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169493075916777224497848%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169493075916777224497848&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-125116764-null-null.142^v94^insert_down1&utm_term=jr6001%E8%AF%AD%E9%9F%B3%E6%A8%A1%E5%9D%97%E7%BB%93%E5%90%88stm32&spm=1018.2226.3001.4187icon-default.png?t=N7T8https://blog.csdn.net/m0_56051805/article/details/125116764?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169493075916777224497848%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169493075916777224497848&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-125116764-null-null.142^v94^insert_down1&utm_term=jr6001%E8%AF%AD%E9%9F%B3%E6%A8%A1%E5%9D%97%E7%BB%93%E5%90%88stm32&spm=1018.2226.3001.4187

开始采集数据

成功,继续进行上传程序

此处默认死循环等待防止手指采集数据,因为不想对放置手指的时间限制太死,大家也可以加入一下超时退出机制,让程序没那么容易卡死,所以此处我的程序只有采集成功这一个选项

STM32+ESP01S连接阿里云(上报温湿度(DHT11)与心率血样数据(惊帆JF141模组))_第2张图片

判断连接云端是否成功(连云端是否成功标志位)

成功,上传数据

失败,尝试重新连接云端(置连云端是否成功标志位)

清除标志位

				//偶尔会出现手指没有放上去,却显示采集成功,
				//更新日期
				MyRTC_ReadTime();
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(2,3,"Get HR+SaO2");
				XU_Yang_SendByte(0x8A);//开始采集
				delay_ms(400);
				//设置音量//范围0-30
				Voice_SendString("AF:10");
				delay_ms(200);//防止两条指令之间发生干扰
				//播报“请放置手指语音”
				OLED_ShowString(3,1,"                ");
				OLED_ShowString(3,1,"      Put");
				Voice_SendString("A8:02/00002*???");
				delay_ms(200);
				//判断语音模块BUSY标志位,查看语音是否播报完成
				while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==1)
				{//利用等待语音播报的时间读取血氧模块的数据,让血氧模块能够尽快达到稳定
					count++;
					if(count>5){count=0;break;}
					XU_Yang_Receive();
					//注意观察此处的break是否只是跳出while循环而不能跳出swtich
				}
				//此处如果一直不放手指在传感器上面,容易导致程序卡死,但若设置超时退出程序,又无法知道手指是什么时候放上去的
				while(XU_Yang_Receive()==0);
				HeartRate = XU_Yang_RxPacket[2];
				XU_Yang_RxPacket[2] = 0;//清零数据防止干扰下一次的测量
				Blood_oxygen = XU_Yang_RxPacket[3];
				XU_Yang_RxPacket[3] = 0;
				OLED_ShowString(3,1,"                ");
				OLED_ShowString(3,3,"  Get Yes");//此处由于下面的语句需要传数据所以相当于延时
				OLED_ShowString(4,1,"                ");
				OLED_ShowString(4,1,"HR:00 ");
				OLED_ShowString(4,8," SaO2:00%");
				OLED_ShowNum(4,4,HeartRate,3);
				OLED_ShowNum(4,14,Blood_oxygen,2);
				delay_ms(1000);
				OLED_ShowString(3,1,"                ");
				OLED_ShowString(3,1,"     Pick up");//此处由于下面的语句需要传数据所以相当于延时
				XU_Yang_SendByte(0x88);//结束采集
				delay_ms(600);
				if(Link_No==1)//网络没有连接成功,就不发数据了
				{
					Sign = MQTT_SendString(User_ESP8266_MQTTServer_Topic_send,"HeartRate",HeartRate);//发送心率数据到MQTT服务器
					Sign = MQTT_SendString(User_ESP8266_MQTTServer_Topic_send,"Blood_oxygen",Blood_oxygen);//发送血氧数据到MQTT服务器
				}else
				{
					Sign=false;//初始化,不成功进入不联网模式,此时可以保留基本功能,不至于程序直接卡死,并在每次采集数据时积极尝试启动WIFI模块
				}
				if(Sign)
				{
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3," Cloud Yes");
					delay_ms(1000);
				}else
				{//防止当采集到数据,但不能上传时刷新界面过快
					delay_ms(1000);
					//上传数据失败,等待下一次上传,并重连
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3," Cloud No");
					Link_No = ESP8266_STA_MQTTClient_Test();
					OLED_ShowString(2,1,"                ");
					OLED_ShowString(4,1,"                ");
					OLED_ShowString(3,1,"                ");
					Sign_Bit = 2;					
					break;
				}
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(4,1,"                ");
				OLED_ShowString(3,1,"                ");
				Sign_Bit = 2;
//此处并没有像模式0那样,只清除自己模式的标志位,因为我想着刚采集上传过心率血氧数据,就让单片机休息一下吧,毕竟环境的温湿度对于实时性基本上没啥要求

最后我就把我的完整的主函数放在这个上面了,供大家参考,大家有什么想法也可以互相交流一下

#include "stm32f10x.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "usart.h"
#include "stm32f10x_it.h"
#include "tcp.h"
#include "esp8266.h"
#include "OLED.h"
#include "Key.h"
#include "mqtt.h"
#include "MyDHT11.h"
#include "Voice.h"
#include "XU_Yang.h"
#include "MyRTC.h"
/*
 * Copyright (c) 2020-2021 AIThinker.yangbin All rights reserved.
 *
 * 本工程只是STM32对接ESP8266的驱动demo,仅供参考,不保证商用稳定性,
 * 本工程参考该作者进行移植,连接为https://blog.csdn.net/it_boy__/article/details/71975797
 * 最终解释权归深圳市安信可科技有限公司所有。
 *
 * author     Specter
 */
    uint8_t Link_No=1;//连云端是否成功标志位,0:连接失败;1:连接成功
	bool Sign;//上传状态标志位
	uint8_t Sign_Bit=0;//模式标志位
	uint8_t count=0;//超时计次
	uint8_t Temp= 0;//温度取值范围/0-99
	uint8_t Humidity = 0;//湿度取值范围/0-99
	uint8_t HeartRate = 0;//心率取值范围/1-150
	uint8_t Blood_oxygen = 0;//血氧取值范围/50-99
int main(void)
{
	MyRTC_Init(); 
	delay_init();
	ESP8266_Init(115200);
	OLED_Init();
	MyDHT11_Init();
	Voice_Init();
	XU_Yang_Init();
	Key_Init();
	MyRTC_ReadTime();
	RTC_Alarm_Init();
	RTC_Alarm_Set(200);
	Link_No = ESP8266_STA_MQTTClient_Test();//MQTT通讯连接阿里云初始化
	delay_ms(1000);
	OLED_ShowString(2, 1, "                ");
	OLED_ShowString(3, 1, "                ");
	while(1)
	{
		//问题:当联网过程中失败时,会让程序卡死在连接云端的函数中,进而导致整个程序卡死
		//想法,可以设置一个标志位,若联网失败则置0,反之置1;当联网失败时,在程序中,每次需要联网时,都进行联网初始化,尽最大可能启动联网设备,但联网失败,并不会影响程序本来的功能,只是丧失了上传功能
		/*
		通过RTC闹钟中断来改变标志位采集上传温湿度数据,并且可以防止在温湿度采集过程中按下按键导致传输温湿度意外终止的情况
		先决条件(温湿度采集与上传的最大时间间隔不能超过定时的间隔,否则会导致采集温湿度模式因为定时中断导致系统一直进入温湿度采集模式)
		*/
		//当模式1运行过程中,发生RTC中断导致模式标志位变更,但由于模式1程序最后又将模式恢复模式2,所以,模式0会在下一次RTC中断后进行,而不是紧跟在模式1之后进行
		//同样的情况如果是模式0在运行的时候,按下按键,并不会进入模式1,因为最后清除了一下标志位,但是可以通过选择性清除规避这种情况
		switch (Sign_Bit)
		{
			//采集并上传温湿度数据
			case 0:
			{
				//更新日期
				MyRTC_ReadTime();
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(2,3," Get T + H");
				delay_ms(500);
				if(MyDHT11_Read_Data(&Temp,&Humidity))
				{//获取数据失败,等待下一次获取,并重启模块
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3,"  Get NO");
					MyDHT11_Rst();//
					delay_ms(500);
					break;
				}else
				{
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3,"  Get Yes");
				}
				OLED_ShowString(4,1,"                ");
				OLED_ShowString(4,1,"Temp:00");
				OLED_ShowString(4,8," Humi:00%");
				OLED_ShowNum(4,6,Temp,2);
				OLED_ShowNum(4,14,Humidity,2);				
				if(Link_No==1)//网络没有连接成功,就不发数据了
				{
					Sign = MQTT_SendString(User_ESP8266_MQTTServer_Topic_send,"Temp",Temp);//发送温度数据到MQTT服务器
					Sign = MQTT_SendString(User_ESP8266_MQTTServer_Topic_send,"Humidity",Humidity);//发送湿度数据到MQTT服务器
				}else
				{
					Sign=false;//初始化,不成功进入不联网模式,此时可以保留基本功能,不至于程序直接卡死,并在每次采集数据时积极尝试启动WIFI模块
				}
				if(Sign==true)//MQTT_SendString函数返回值有问题,如果采用实际返回值,就一直是失败,所以只能在程序中不论是否成功都显示成功
				{
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3," Cloud Yes");
					delay_ms(1000);
				}else
				{//防止当采集到数据,但不能上传时刷新界面过快
					delay_ms(1000);
					//上传数据失败,等待下一次上传,并重连
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3," Cloud No");
					Link_No = ESP8266_STA_MQTTClient_Test();
					OLED_ShowString(2,1,"                ");
					OLED_ShowString(4,1,"                ");
					OLED_ShowString(3,1,"                ");
					if(Sign_Bit==0)//此时跳出switch若不执行下列语句,就会导致陷入模式0的死循环
					{
						Sign_Bit = 2;
					}//防止在运行过程中,按下按键又因为清除标志位导致无法进入模式1					
					break;
				}
				OLED_ShowString(4,1,"                ");
				OLED_ShowString(3,1,"                ");
				OLED_ShowString(2,1,"                ");
				if(Sign_Bit==0)
				{
					Sign_Bit = 2;
				}//防止在运行过程中,按下按键又因为清除标志位导致无法进入模式1
				break;
			}
			//测量并上传血氧和心率模式
			case 1:
			{
				//偶尔会出现手指没有放上去,却显示采集成功,
				//更新日期
				MyRTC_ReadTime();
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(2,3,"Get HR+SaO2");
				XU_Yang_SendByte(0x8A);//开始采集
				delay_ms(400);
				//设置音量//范围0-30
				Voice_SendString("AF:10");
				delay_ms(200);//防止两条指令之间发生干扰
				//播报“请放置手指语音”
				OLED_ShowString(3,1,"                ");
				OLED_ShowString(3,1,"      Put");
				Voice_SendString("A8:02/00002*???");
				delay_ms(200);
				//判断语音模块BUSY标志位,查看语音是否播报完成
				while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==1)
				{//利用等待语音播报的时间读取血氧模块的数据,让血氧模块能够尽快达到稳定
					count++;
					if(count>5){count=0;break;}
					XU_Yang_Receive();
					//注意观察此处的break是否只是跳出while循环而不能跳出swtich
				}
				//此处如果一直不放手指在传感器上面,容易导致程序卡死,但若设置超时退出程序,又无法知道手指是什么时候放上去的
				while(XU_Yang_Receive()==0);
				HeartRate = XU_Yang_RxPacket[2];
				XU_Yang_RxPacket[2] = 0;//清零数据防止干扰下一次的测量
				Blood_oxygen = XU_Yang_RxPacket[3];
				XU_Yang_RxPacket[3] = 0;
				OLED_ShowString(3,1,"                ");
				OLED_ShowString(3,3,"  Get Yes");//此处由于下面的语句需要传数据所以相当于延时
				OLED_ShowString(4,1,"                ");
				OLED_ShowString(4,1,"HR:00 ");
				OLED_ShowString(4,8," SaO2:00%");
				OLED_ShowNum(4,4,HeartRate,3);
				OLED_ShowNum(4,14,Blood_oxygen,2);
				delay_ms(1000);
				OLED_ShowString(3,1,"                ");
				OLED_ShowString(3,1,"     Pick up");//此处由于下面的语句需要传数据所以相当于延时
				XU_Yang_SendByte(0x88);//结束采集
				delay_ms(600);
				if(Link_No==1)//网络没有连接成功,就不发数据了
				{
					Sign = MQTT_SendString(User_ESP8266_MQTTServer_Topic_send,"HeartRate",HeartRate);//发送心率数据到MQTT服务器
					Sign = MQTT_SendString(User_ESP8266_MQTTServer_Topic_send,"Blood_oxygen",Blood_oxygen);//发送血氧数据到MQTT服务器
				}else
				{
					Sign=false;//初始化,不成功进入不联网模式,此时可以保留基本功能,不至于程序直接卡死,并在每次采集数据时积极尝试启动WIFI模块
				}
				if(Sign)
				{
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3," Cloud Yes");
					delay_ms(1000);
				}else
				{//防止当采集到数据,但不能上传时刷新界面过快
					delay_ms(1000);
					//上传数据失败,等待下一次上传,并重连
					OLED_ShowString(3,1,"                ");
					OLED_ShowString(3,3," Cloud No");
					Link_No = ESP8266_STA_MQTTClient_Test();
					OLED_ShowString(2,1,"                ");
					OLED_ShowString(4,1,"                ");
					OLED_ShowString(3,1,"                ");
					Sign_Bit = 2;					
					break;
				}
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(4,1,"                ");
				OLED_ShowString(3,1,"                ");
				Sign_Bit = 2;
				break;
			}
			//睡眠模式
			//进入睡眠模式后,若需要下载时需要按住复位键点击下载
			case 2:
			{
				OLED_Clear();
				__WFI();
			}
		}
	}
}
void RTC_IRQHandler(void){ //RTC时钟全局中断函数(名称固定不可修改)
	if(RTC_GetITStatus(RTC_IT_ALR) != RESET)
	{
		RTC_ClearITPendingBit(RTC_IT_ALR); 	//清Alarm 中断标志位(ALRF)
		Sign_Bit = 0;						//闹钟时间到开始执行温湿度采集与上传
		RTC_Alarm_Set(300);					//设定30s后闹钟//闹钟时间过短会导致一个模式一直死循环//因为模式本身的时长也很长,容易导致,还没进行完又将标志位记为进入模式
	}	
}
void EXTI1_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line1)==SET)//判断是否是对应输入的标志位触发中断
	{

		Sign_Bit = 1;						//按键按下,标志位变更为心率和血氧的采集和上传程序
		EXTI_ClearITPendingBit(EXTI_Line1);//消除标志位,如果不消除,会使程序卡死在中断中
	}

}

你可能感兴趣的:(stm32,嵌入式硬件,单片机,物联网,阿里云)