基于STM32-ESP8266-阿里云-微信小程序的智慧舒适家庭控制系统项目



基于STM32-ESP8266-阿里云-微信小程序的智慧舒适家庭控制系统项目

本文参考了 B 站 U P 主 [ 画卿师兄 ] : \textcolor{red}{本文参考了B站UP主[画卿师兄]:} 本文参考了BUP[画卿师兄]:https://space.bilibili.com/382542366 的【挽救小白第一季】视频进行学习制作和开发。 \textcolor{red}{的【挽救小白第一季】视频进行学习制作和开发。} 的【挽救小白第一季】视频进行学习制作和开发。

一、项目背景

​ 随着物联网技术的发展,物联网技术在各个领域均起到作用。其中实现智慧家庭不在是什么难题,通过物联网技术可以很容易的对家庭各种系统进行远程控制和调节,以达到改善和提高人们家庭生活质量的需求。而智慧家庭系统通过各种传感器将采集到的数据经整合后上传至智能云平台经其分析处理后再传输给手机端等移动设备,就可以实现对家庭灯光照明、温度控制、自动开启换气等家庭系统进行远程实时监测和控制


二、项目介绍

1、项目名称及开发环境

  • 基于STM32F103C8T6的智慧舒适家庭监测系统。
  • stm32使用Keil5 MDK 5.14版本进行开发。
  • 小程序使用微信开发者工具1.06.228010。

2、系统框架

系统框架如下图:

在这里插入图片描述

3、功能简介

该设计的功能如下:

  1. 实时的采集周围环境的温度、湿度、光照值信息;
  2. 实时的显示温度、湿度、光照值信息;
  3. 实现温度大于30℃、湿度大于80%、光照值大于4000lux其中之一超过阈值则进行蜂鸣器报警;
  4. 实现微信小程序实时的显示温度、湿度、光照值信息;
  5. 实现微信小程序端控制小风扇(LED的代替)的开关进行通风换气;
  6. 实现微信小程序端控制警报的打开与解除;

4、控制核心

STM32C8T6(最小核心板)

5、外围模块

  • 温湿度模块:DHT11

  • 光照值模块:BH1750

  • 蜂鸣器模块:MH-FMD(低电平触发)

  • OLED显示模块:(GND开头)

  • 小风扇:LED代替(一个LED+一个10k上拉电阻)

  • WIFI模块:ESP8266-01S/ATK-ESP8266正点原子官方

5、串口和烧录模块

  • 烧录模块:ST-LINK V2

  • 串口模块:TTL转USB(CH340G)

6、上位机

阿里云服务器[物联网平台]:https://iot.console.aliyun.com/和微信小程序

在这里插入图片描述


三、系统各器件连线及总体运行效果图

1、元件连线清单(各元器件与于STM32最小核心板的连线)

元器件VCCGND与STM32端其他
DHT113.3VGNDDATA-PA8
BH17503.3VGNDSCL-PB6、SDA-PB7
BEEP3.3VGNDI/O-PA0
OLED3.3VGNDSCK-PA5、SDA-PA7
LED正极-3.3V(10K的上拉电阻)负极-PA1
ESPB2663.3VGNDTXD-PA3、RXD-PA2
CH340G5.0VGNDTXD-PA10、RXD-PA9
ST-LINK V23.3VGNDSWCLK-SWCLK、SWIO-SWDIO

2、实物连接图

在这里插入图片描述

3、运行效果图

(1)设备数据上传微信端运行图

在这里插入图片描述

(2)微信下发数据控制设备端运行图

蜂鸣器在温度大于30℃时开始报警,在微信端下发命令关闭蜂鸣器报警

在这里插入图片描述

微信端下发命令打开小风扇(用LED亮灯代替)

在这里插入图片描述

(3)成功实现对小风扇(LED亮)的控制实物效果图

在这里插入图片描述


四、各功能模块的软件实现

1、DHT11温湿度采集的实现与数据上传

(1)dht11.c(移植现有模板后稍作修改)

#include "dht11.h"  //DHT11头文件引用
#include "delay.h"  //延时函数头文件引用
//复位DHT11
void DHT11_Rst(void)	   
{                 
		DHT11_IO_OUT(); 	
    DHT11_DQ_OUT=0; 	
    delay_ms(20);    	
    DHT11_DQ_OUT=1; 	
		delay_us(30);     	
}

//等待DHT11回应
//返回1:未检测到DHT11
//返回0:存在
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();	 
    while (DHT11_DQ_IN&&retry<100)
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
    while (!DHT11_DQ_IN&&retry<100)
	{
		retry++;
		delay_us(1);
	};
	if(retry>=100)return 1;	    
	return 0;
}

//从DHT11读取一个值
//返回值:1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)
	{
		retry++;
		delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)
	{
		retry++;
		delay_us(1);
	}
	delay_us(40);
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}

//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
    }						    
    return dat;
}

//*********从DHT11读取一次数据***************//
//*********temp:温度值(范围:0~50℃)********//
//*********humi:湿度值(范围:20%~90%)********//
//*********返回值:0,正常;1,读取失败********//

u8 DHT11_Read_Data(u8 *humiH,u8 *humiL,u8 *tempH,u8 *tempL)    
{        
 	u8 buf[5];
	u8 i;
	DHT11_Rst();
	if(DHT11_Check()==0)
	{
		for(i=0;i<5;i++)//读取40位数据
		{
			buf[i]=DHT11_Read_Byte();
		}
		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
		{
			*humiH=buf[0];			
			*humiL=buf[1];			
			*tempH=buf[2];
			*tempL=buf[3];
				
		}
	}else return 1;
	return 0;	    
}

//初始化DHT11的IO口,DQ同时监测DHT11的存在
//返回1:不存在
//返回0:存在  	 
u8 DHT11_Init(void)
{	 
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PA端口时钟
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;				       //PA8端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		   //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);				         //初始化IO口
 	GPIO_SetBits(GPIOA,GPIO_Pin_8);						             //PA8输出高
			    
	DHT11_Rst();                                           //复位DHT11
	return DHT11_Check();                                  //等待DHT11的回应
} 

(2)dht11.h

#ifndef __DHT11_H
#define __DHT11_H	 
#include "sys.h"
 
//IO方向设置
#define DHT11_IO_IN()  {GPIOA->CRH&=0XFFFFFFF0;GPIOA->CRH|=8;}
#define DHT11_IO_OUT() {GPIOA->CRH&=0XFFFFFFF0;GPIOA->CRH|=3;}
//IO操作函数											   
#define	DHT11_DQ_OUT PAout(8) //数据端口 PA8输出方向
#define	DHT11_DQ_IN  PAin(8)  //数据端口 PA8输入方向


u8 DHT11_Init(void);//初始化DHT11
u8 DHT11_Read_Data(u8 *humiH,u8 *humiL,u8 *tempH,u8 *tempL);//读取温湿度
u8 DHT11_Read_Byte(void);//读出一个字节
u8 DHT11_Read_Bit(void);//读出一个位
u8 DHT11_Check(void);//监测是否存在DHT11
void DHT11_Rst(void);//复位 
		 				    
#endif

(3)在main.c中添加

//添加全局变量//
u8 humidityH;	                 //湿度整数
u8 humidityL;	                 //湿度小数
u8 temperatureH;                 //温度整数
u8 temperatureL;                 //温度小数
int main(void)
{
DHT11_Init();                    //DHT11初始化
		  DEBUG_LOG("DHT11初始化			OK"); //串口显示初始化成功消息
		  OLED_Refresh_Line("DHT11");
while(1)
{
    DHT11_Read_Data(&humidityH,&humidityL,&temperatureH,&temperatureL);  //读取温湿度信息
}
}

相应的串口输出等函数在 u s a r t . c 中 \textcolor{red}{相应的串口输出等函数在usart.c中} 相应的串口输出等函数在usart.c

2、BH1750光照值采集的实现与数据上传

(1)bh1750.c和bh1750.h

移植链接如下:

提取码: hm83

(2)main.c中添加

#include "bh1750.h"
int Light = 0;//添加全局变量光照值
int main(void)
{
BH1750_Init();//函数初始化
while(1)
{
    if (!i2c_CheckDevice(BH1750_Addr))             //光照值传感器获取数据
			{
				Light = LIght_Intensity();
			}
}
}

3、BEEP蜂鸣器自动报警功能的实现逻辑

(1)Beep.c(初始化PA0端口)

#include "beep.h"
		    
//BEEP IO初始化
void BEEP_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PA端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				 //BEEP-->PA.0 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHZ
 GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根据设定参数初始化GPIOA.0
 GPIO_SetBits(GPIOA,GPIO_Pin_0);						 //PA.0 初始化为高电平
}
 

(2)Beep.h

#ifndef __BEEP_H
#define __BEEP_H	 
#include "sys.h"

#define BEEP PAout(0)	// PA0	
void BEEP_Init(void);//初始化		 				    
#endif

(3)main.c

#include "beep.h"
#include "timer.h" //中断函数头文件
#include "onenet.h"//阿里云数据发布和接受头文件
#include "delay.h" //延迟函数

int main(void)
{
u8 alarmFlag = 0;              //是否报警的标志   

BEEP_Init();//蜂鸣器初始化
TIM3_Int_Init(2499,7199);//TIM3中断函数
//蜂鸣器提示接入成功
BEEP = 0;                                          
delay_ms(250);
BEEP = 1;

if((humidityH < 80)&&(temperatureH < 30) && (Light < 2000))
    alarmFlag = 0;
else alarmFlag = 1;//如果温湿度光照值超过阈值,蜂鸣器报警(中断3)    
}

(4)timer.c(中断)

#include "timer.h"
#include "stdio.h"
extern u8 alarmFlag;//是否报警标志


//通用定时器中断初始化
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值,计数到5000为500ms
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMX时钟频率除数的预分频值 10khz的技术频率 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS=TCK_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct重指定的参数初始化TIMX的时间基数单位
 
	TIM_ITConfig(  //使能或者失能指定的TIM中断
		TIM3, //TIM3
		TIM_IT_Update ,
		ENABLE  //使能
		);
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能TIMX外设
							 
}

void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM中断源
		{
			TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMX的中断处理位:TIM中断源
			if(alarmFlag)//如果alarmFlag=1就执行报警
			{
				BEEP=0;
			}
			else
			{
				BEEP=1;
			}
		}
}

4、OLED显示模块的实现

(1)oled.c与oled.h

移植地址:[正点原子OLED]:http://www.openedv.com/docs/modules/lcd/0.96-OLED-12864.html

(2)main.c

#include "oled.h"
#include "timer.h"
#include "delay.h"
extern char oledBuf[20];
int main(void)
{
  OLED_Init();  //OLED初始化
  OLED_ColorTurn(0);         //0正常显示,1反色显示
  OLED_DisplayTurn(0);       //0正常显示,1屏幕翻转显示                     
  OLED_Clear();
   
  TIM2_Int_Init(2499,7199);//定时器中断2
    
}

(3)timer.c

#include "timer.h"
#include "stdio.h"
char oledBuf[20];
extern u8 humidityH;	  
extern u8 humidityL;	  
extern u8 temperatureH;   
extern u8 temperatureL;   
extern int Light; //光照值

//通用定时器中断初始化
void TIM2_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值,计数到5000为500ms
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMX时钟频率除数的预分频值 10khz的技术频率 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS=TCK_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct重指定的参数初始化TIMX的时间基数单位
 
	TIM_ITConfig(  //使能或者失能指定的TIM中断
		TIM2, //TIM2
		TIM_IT_Update ,
		ENABLE  //使能
		);
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

	TIM_Cmd(TIM2, ENABLE);  //使能TIMX外设
							 
}


void TIM2_IRQHandler(void)   //TIM2中断
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM中断源´ 
		{
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update  ); //清除TIMX的中断处理位:TIM中断源
			
				sprintf(oledBuf,"Welcome");
				OLED_ShowString(32,0,(u8*)oledBuf,16);//8*16 "ABC"
			sprintf(oledBuf,"wendu:%d.%d %%",humidityH,humidityL);
				OLED_ShowString(0,16,(u8*)oledBuf,16);//8*16 "ABC"
			sprintf(oledBuf,"shidu:%d.%d C",temperatureH,temperatureL);
				OLED_ShowString(0,32,(u8*)oledBuf,16);//8*16 "ABC"
				sprintf(oledBuf,"Light:%d Lx",Light);
				OLED_ShowString(0,48,(u8*)oledBuf,16);//8*16 "ABC"
				OLED_Refresh();
			
		}
}

5、小风扇模块(LED代替)

(1)led.c

#include "led.h"
//初始化PA1、PC13为输出口,并使能这三个口的时钟
//LEDIO口初始化
void LED_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE);	 //使能PA端口时钟

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;				 //LED2-->PA1端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHZ
 GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根据设定参数初始化GPIOA.1
 GPIO_SetBits(GPIOA,GPIO_Pin_1);		
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;	    		 //PC13端口配置
 GPIO_Init(GPIOC, &GPIO_InitStructure);	  				 //推挽输出,IO口速度为50MHZ
 GPIO_SetBits(GPIOC,GPIO_Pin_13); 						 //PC.13输出高
}
 

(2)led.h

#ifndef __LED_H
#define __LED_H	 
#include "sys.h"

#define LED2 PAout(1)	// PA1
#define LED1 PCout(13)	// PC13	

void LED_Init(void);//初始化			    
#endif

(3)main.c

#include "led.h"
int main(void)
{
    LED_Init();	//LED初始化
}

6、WIFI通信模块(ESP8266)

(1)esp8266.c

[程序移植地址]:http://www.openedv.com/docs/modules/iot/atk-esp.html?highlight=esp8266

做如下修改:

  • 将模板中的服务器地址和改为自己所用的服务器地址(阿里云),将WIFI名称密码配置成自己的:
#define WIFI_SSID 		"nova4"	//	WIFI密码名称,必须为2.4G,名称中不能含有空格、中文
#define WIFI_PSWD 		"66666666"//WIFI密码

#define SERVER_HOST		"broker.emqx.io"			//	MQTT服务器域名或IP
#define SERVER_PORT		"1883"						//	MQTT服务器端口

#define ESP8266_WIFI_INFO		"AT+CWJAP=\"" WIFI_SSID "\",\"" WIFI_PSWD "\"\r\n"//WIFI配置
#define ESP8266_ONENET_INFO		"AT+CIPSTART=\"TCP\",\"a1aFm68O8XC.iot-as-mqtt.cn-shanghai.aliyuncs.com\",1883\r\n"                   //阿里云连接域名及端口号

unsigned char esp8266_buf[256];
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;

extern u8 ESP8266_INIT_OK;

  • 在void ESP8266_Init(void)函数中添加8266复位指令:

    由于STM32再重新烧录程序后会自动进行复位操作,然而ESP8266不会复位,此前处于连接状态,烧录后再次连接则会出现问题,无法连接,故必须先进行复位操作。

ESP8266_SendCmd("AT+RST\r\n","");
        delay_ms(500);
ESP8266_SendCmd("AT+CIPCLOSE\r\n","");
        delay_ms(500);

7、设备采集数据上传阿里云流程

Mqttkit.c的源码移植于如下链接:

提取码:l00d

https://pan.baidu.com/s/1x4HmJWm-JkJkEftLBHs72g

<OneNET-V3.2-OneNet-裸机-基础例程:MQTT-例程>

  • 添加全局变量:
char PUB_BUF[256];                                                 //上传数据的BUF
const char *devSubTopic[] ={"/a1aFm68O8XC/home/user/get"};         //订阅主题
const char devPubTopic[] = "/a1aFm68O8XC/home/user/update/error";  //发布主题
u8 ESP8266_INIT_OK = 0;                                            //ESP8266初始化完成标志
  • 串口初始化、ESP8266初始化以及接入阿里云(115200:波特率,串口调试助手应于其一致):
int main(void)
{
unsigned short timeCount = 0;//发送间隔变量 
Usart1_Init(115200); //串口初始化
ESP8266_Init();   //ESP8266初始化
while(OneNet_DevLink())
	{                                                  //接入阿里云
		delay_ms(500);
	}		
    
	BEEP = 0;                                          //蜂鸣器提示连接成功
	delay_ms(250);
	BEEP = 1;
	OneNet_Subscribe(devSubTopic, 1);                   //订阅主题
        
}
  • 连接阿里云需要设置登录信息:

在onenet.c中修改自己相应的三元组

#define PROID		"home&a1aFm68O8XC"                                                               //Username
#define AUTH_INFO	"9c54332a63ea97a799d94844f38d5617e4ba46b8628453b1aa1cf7e01abc01cc"               //Password
#define DEVID		"a1aFm68O8XC.home|securemode=2,signmethod=hmacsha256,timestamp=1660806540188|"   //ClientId
  • 采集和发布数据至阿里云端:
while(1)
{
 if(timeCount % 40 == 0)                       //1000ms/25=40 一秒执行一次
{			
 DHT11_Read_Data(&humidityH,&humidityL,&temperatureH,&temperatureL); //获取温湿度数据
if (!i2c_CheckDevice(BH1750_Addr))             //获取光照值数据
{
	Light = LIght_Intensity();
}
Led_Status = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);//读取PA1端口(小风扇的状态)
     
/********** 报警逻辑 **************/
if((humidityH < 80)&&(temperatureH < 30) && (Light < 2000))alarmFlag = 0;
     
else alarmFlag = 1;    //其中之一超过阈值即报警                           

/**********串口输出调试信息**************/
DEBUG_LOG(" | 温度 %d.%d d | 湿度 %d.%d %% | 光照值 %d lx |小风扇 %s | 报警器 %s |",temperatureH,temperatureL,humidityH,humidityL,Light,Led_Status?"关闭":"启动",alarmFlag?"启动":"停止");//此处用了条件运算符(Exp1 ? Exp2 : Exp3),Exp1为真,则返回Exp2,否则返回Exp3
}
		
 /***************上传发布数据**************/
if(++timeCount >= 200)	                     //5000ms/25=200  每五秒上传一次数据     
{
Led_Status = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);//读取PA1端口(小风扇的状态)
DEBUG_LOG("====================================================================");
DEBUG_LOG("发布数据 ----- aliyun_Publish");
sprintf(PUB_BUF,"{\"Temp\":%d,\"Hum\":%d,\"Light\":%d,\"Led\":%d,\"Beep\":%d}"
			,temperatureH,humidityH,Light,Led_Status?0:1,alarmFlag);//缓存数据	
OneNet_Publish(devPubTopic, PUB_BUF);                   //发布消息上传数据	
DEBUG_LOG("====================================================================");
timeCount = 0;
ESP8266_Clear();                                        //清空缓存
    
}

8、阿里云下发指令控制设备流程

  • main.c中收到数据并处理部分:
#include "onenet.h"
int main(void)
{
dataPtr = ESP8266_GetIPD(3);   //获取下发数据
if(dataPtr != NULL)            //若收到数据,开始进行数据处理--->数据解析
  OneNet_RevPro(dataPtr);
delay_ms(10);
}
  • 收到下发的数据并进行数据C.json解析:在onenet.c中
void OneNet_RevPro(unsigned char *cmd)
{
MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};			//协议包

char *req_payload = NULL;        //消息体
char *cmdid_topic = NULL;        //主题topic
	
unsigned short topic_len = 0;    //主题长度
unsigned short req_len = 0;      //消息体长度
unsigned char type = 0;          
unsigned char qos = 0;
	
static unsigned short pkt_id = 0;	
short result = 0;
char *dataPtr = NULL;
char numBuf[10];
int num = 0;
	
cJSON *json , *json_value,*json_value2;    //用来存放解析的数据
type = MQTT_UnPacketRecv(cmd);	
switch(type)
{			
case MQTT_PKT_PUBLISH:	//阿里云向设备发送的消息类型属于MQTT_PKT_PUBLISH,故进入此分支(其他分支这里不做阐述)
		
      result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);
      if(result == 0)
	{								
		UsartPrintf(USART_DEBUG,"topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",
		cmdid_topic, topic_len, req_payload, req_len);	//串口打印主题、主题长度、消息体、消息体长度			
		json = cJSON_Parse(req_payload);                //对消息体进行c.json解析
	}
		if (!json)
		 UsartPrintf(USART_DEBUG,"Error before: [%s]\n",cJSON_GetErrorPtr());//解析失败,打印错误提示
		else//解析成功
		{
		  json_value = cJSON_GetObjectItem(json , "Led"); //解析消息体中的"Led",使json_value来存放"Led"的真值 
           if(!json_value);                                                     
			 else//解析"Led"成功
			{	                                                                 
             if(json_value->valueint)           //若json_value为整型且大于0			
						 LED2 = 0;            //LED2点亮,代表小风扇启动(PA1输出低电平)	    
			else                                                 
						 LED2 = 1;            //LED2熄灭,代表小风扇关闭(PA1输出高电平)				
			}					
		json_value2 = cJSON_GetObjectItem(json , "Beep");//解析消息体中的"Beep",使json_value2来存放"Led"的value值
			if(!json_value2) ;//解析失败
		    else//解析成功
            {
			if(json_value2->valueint) alarmFlag=1;        //是否警报标志alarmFlag为1,则打开蜂鸣器报警
			else {    
                      alarmFlag=0;							   
				 }
			} 

				cJSON_Delete(json);                       //删包(不然会导致缓存不足,无法继续运行)
				MQTT_DeleteBuffer(&mqttPacket);  
			}
		break;

存在的问题:

由于我使用的是自定义主题,其消息格式为{“Led”:value,“Beep”:value},故只需要解析一层;如果使用物模型主题,存在嵌套解析的问题,此处需要注意!至于如何进行嵌套解析,请百度搜索,此处不过多阐述。当使用自定义topic时,不能使用阿里云提供的物理模型,只能自定义数据结构的类型,可以使消息体的数据结构变得更加简洁,更加方便地进行Json数据解析,因此我的程序在接下来都将使用自定义topic进行数据通信

9、云产品流转规则设置

设备一(home) 订阅:/a1aFm68O8XC/home/user/get 发布:/a1aFm68O8XC/home/user/update/error

设备二(weixin) 订阅:/a1aFm68O8XC/weixin/user/get 发布:/a1aFm68O8XC/weixin/user/update/error

在这里插入图片描述

在这里插入图片描述


五、微信小程序的开发

微信小程序使用老师提供的模板,并参考CSDN博主Stdonald对其进行开发和修改。

页面效果图如下:

在这里插入图片描述

当进入小程序页面后,会自动与阿里云服务器连接,并提示连接成功,此时就可以读取上传至云平台经云流转后的温湿度以及光照值信息,并且也可以实现指令的下发。所有的操作都会在数据面板显示相应的提示信息。

微信端的程序设计:index.js

  • 三元组以及主题的设置
aliyunInfo: 
{
 productKey: 'a1aFm68O8XC', //阿里云连接的三元组 ,请自己替代为自己的产品信息!!
 deviceName: 'weixin', //阿里云连接的三元组 ,请自己替代为自己的产品信息!!
 deviceSecret: 'dd51bbc32a9b397e4f7ac2e40fa4323b', //阿里云连接的三元组 ,请自己替代为自己的产品信息!!
 regionId: 'cn-shanghai', //阿里云连接的三元组 ,请自己替代为自己的产品信息!!
 pubTopic: '/a1aFm68O8XC/weixin/user/update/error', //发布消息的主题
 subTopic: '/a1aFm68O8XC/weixin/user/get', //订阅消息的主题
    },
  • 接收数据并解析
that.data.client.on("message", function (topic, payload)
{
console.log(" 收到 topic:" + topic + " , payload :" + payload)
let json_payload = JSON.parse(payload)

if (payload.indexOf("Temp") !== -1) { //温度 湿度 光照值
console.log("温度 " + json_payload.Temp)      
that.setData({
            temp: json_payload.Temp,
            })
      }
if (payload.indexOf("Hum") !== -1){
        console.log("湿度 " + json_payload.Hum)
        that.setData({       
          humi: json_payload.Hum,      
        })
      }

if (payload.indexOf("Light") !== -1){
        console.log("光照值 " + json_payload.Light)
        that.setData({       
          light: json_payload.Light,     
        })
      }
      if (payload.indexOf("Led") !== -1) { //小风扇状态
        console.log("小风扇 " + json_payload.Led)
        that.setData({
          light_status1: json_payload.Led,
        })
      }
      if (payload.indexOf("Beep") !== -1) { //报警器状态
        console.log("报警器 " + json_payload.Beep)
        that.setData({
          light_status2: json_payload.Beep,
        })
      }
  • 消息的发布
onClickOpen1()
{
  that.sendCommond(1, 'light1');
},      
sendCommond(data, light) {
    let sendData = null;
    if (light === 'light1') {
      console.log(1)
      sendData = 
         {       
          "Led": data,     
          };
}
  • 小程序页面布局
<!-- button -->
<view class="button_wrap">
		<view class="button1">
				<button class="button1_open" type="primary" size="mini" bindtap="onClickOpen1">开小风扇</button>
				<button class="button1_close" type="warn" size="mini" bindtap="onClickOff1">关小风扇</button>
				<text class="button1_text">状态:</text>
				<switch class="button1_state" checked="{{light_status1}}" disabled/>
		</view>
		<view class="button2">
				<button class="button2_open" type="primary" size="mini" bindtap="onClickOpen2">开报警器</button>
				<button class="button2_close" type="warn" size="mini" bindtap="onClickOff2">关报警器</button>
				<text class="button2_text">状态:</text>
				<switch class="button2_state" checked="{{light_status2}}" disabled/>
		</view>
</view>
<!-- temp humi -->
<view class="temp_humi_wrap">
	<view class="temp_wrap">
		<text class="temp_tag">温度:</text>
		<text class="temp_value">{{temp}}</text>
	</view>
	<view class="humi_wrap">
		<text class="humi_tag">湿度:</text>
		<text class="humi_value">{{humi}}</text>
	</view>
  <view class="temp_wrap">
		<text class="humi_tag">光照值:</text>
		<text class="humi_value">{{light}}</text>
	</view>
</view>

六、项目制作中遇到的问题以及解决方法

1、Keil 5向STM32中烧录.hex文件的问题:

会报连接错误,无法下载。

解决方法:

  • 首先,由于我购买的是ST-LINK V2,需要先行安装ST-LINK驱动,其次因为我安装的版本较低,需要升级。安装完成后打开keil工程,点击魔术棒,然后设置DeBug
    在这里插入图片描述

然后打开旁边的Settings,添加如下图所示,其次返回点击OK退出。连接上ST-LINK点击Download进行下载即可。
在这里插入图片描述

2、阿里云下发指令设备端收不到并在串口也无法显示的问题:

分析:应该是设备端对数据包的接收和解析部分出现了问题

解决方法:

  • 由于模板文件其发布接收消息的主题为自定义主题,消息体较为简单,故设置的缓存消息的BUF为128,然而如果使用平台的物模型主题格式,缓存消息的BUF太小,导致无法正常接收和处理下发消息,故做如下修改:

    (1)在esp8266.c中将esp8266_buf由128改为256;
    在这里插入图片描述

    (2)在startup_stm32f103x_md.c中将Stack_Size EQU改为0X00000800;

    ​ Heap_Size EQU改为0X00000400。
    在这里插入图片描述

3、平台端没有显示上传的物模型实时数据

问题分析:

  • 由于如果你使用了自定义主题,则在阿里云平台不会显示实时的物模型数据。

在这里插入图片描述

  • 若使用如下物模型通信Topic,则会正常显示。

在这里插入图片描述

  • 34
    点赞
  • 346
    收藏
    觉得还不错? 一键收藏
  • 19
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值