【智能家居入门4】(FreeRTOS、MQTT服务器、MQTT协议、微信小程序)

前面已经发了智能家居入门的1、2、3了,在实际开发中一般都会使用到实时操作系统,这里就以FreeRTOS为例子,使用标准库。记录由裸机转到实时操作系统所遇到的问题以及总体流程。相较于裸机,系统实时性强了很多,小程序下发的指令基本立马执行,没有啥延迟,调整任务的挂起时间,可以进一步提高系统效率

    • 前言
    • 问题汇总
      • 1、内存不足
      • 2、延时函数
    • 一、项目总体介绍
    • 二、代码
      • 1、下位机
      • 2、微信小程序
    • 三、工程源码获取

前言

主控仍旧是STM32F103C8T6,实时操作系统选择的是FreeRTOS。
主要功能:
①环境信息采集并上传至微信小程序
②微信小程序下发指令控制家电
③由雨滴传感器和步进电机能够实现下雨自动收起衣服,停雨自动晒出衣服(由于驱动板和步进电机不在身边,这里代码中就用舵机来模拟,之前的项目中有用stm32来驱动步进电机,代码在另外的工程里,有需要的话可以关注我私聊,无偿)
前几篇博客:
环境信息采集(HTTP+ONENET)
智能家居(MQTT+ONENET)
智能家居(MQTT+MQTT服务器)

问题汇总

1、内存不足

将FreeRTOS移植到自己的工程项目里后(移植可以看上一篇博客),经过测试是可以用的,但是在我将之前的net文件夹移植过来之后编译就报错了,net文件夹里是与服务器通信的一些源文件(onenet.c、esp8266.c、MqttKit.c、cJSON.c),对net文件不了解的可以看这篇博客,有介绍,或者到文章最后下载源代码自己分析代码。下面贴出错误:
在这里插入图片描述
经过网上查找,这是由于RAM内存不够导致的,解决办法两种:
①更换开发板,换成STM32F103ZET6或者其他RAM大的开发板,亲测可以。
②修改FreeRTOSConfig.h文件:
在这里插入图片描述

2、延时函数

当我将dht11的驱动代码移植过来之后,编译没有报错,抱着侥幸心理以为没问题,运行之后发现串口没有调试信息输出,然后进入调试,发现任务函数2执行到DHT11_Read_Data()时就不往后走了,每次停止的时候都会卡在不同的函数里,还真是棘手,无从下手。经过长时间问题找寻,并没有在网上找到答案,我就一步一步调试,发现每次就是延时函数出问题:
在这里插入图片描述

然后追溯回延时函数实现,我怀疑是因为这个延时函数使用到了系统时钟中断,然后导致和freertos起冲突,不过我只是怀疑,并不知道具体原因,然后我就将延时函数的源文件和头文件移除,使用定时器来实现延时,成功解决。
用定时器实现的延时函数如下,用这两个函数替换dht11.c和esp8266.c中的延时函数即可:

void TIM3_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  ///使能TIM3时钟
	TIM_TimeBaseInitStructure.TIM_Period    = 50000-1; 	//自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 60-1;     //定时器分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
}
//微秒级延时
void TIM3_Delayus(u16 xus)
{
	TIM_Cmd(TIM3,ENABLE); //启动定时器
	while(TIM3->CNT < xus);
	TIM3->CNT = 0;
	TIM_Cmd(TIM3,DISABLE); //关闭定时器
}
//毫秒级延时
void TIM3_Delayms(u16 xms)
{
	int i;
	for(i=0;i<xms;i++)
	{
		TIM3_Delayus(1000);
	}
}

一、项目总体介绍

用到的设备:sg90舵机、MG946舵机、MQ-2、MQ-4、MQ-9传感器、雨滴传感器、火焰传感器、继电器、风扇、水泵、DHT11、USB转TTL、J-;ink、STM32F103C8T6、Esp8266-01s、led。
①标准库移植FreeRTOS参考之前的一篇博客:https://blog.csdn.net/m0_71523511/article/details/135956912
②两个舵机由定时器二的通道0、1输出PWM进行控制,这里不会的可以参考:https://blog.csdn.net/m0_71523511/article/details/135851583
③MQ系列传感器由adc三个通道进行采集,adc知识可以看这个视频,我的代码直接搬过来改的:https://www.bilibili.com/video/BV1th411z7sn/?p=22&spm_id_from=pageDriver&vd_source=2a10d30b8351190ea06d85c5d0bfcb2a
④继电器的使用和接线参考这篇博客:https://blog.csdn.net/m0_71523511/article/details/135926339
⑤雨滴传感器和火焰传感器都直接接收DO输出口,直接读取数字量,当然也可以接AO口,用adc读取模拟量,可以自己尝试。
⑥DHT11原理可以百度也可以参考这篇博客里面的:https://blog.csdn.net/m0_71523511/article/details/135892908
⑦新买的esp8266-01s如果没法用的话可以参考:https://blog.csdn.net/m0_71523511/article/details/135887108

二、代码

1、下位机

1、main.c:
创建了两个任务,高优先级的任务1(负责接收服务器下发指令),低优先级任务2(负责读取传感器数据并打包发送给服务器)。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

#include "FreeRTOS.h"//操作系统头文件
#include "task.h"

#include "dht11.h"//外设头文件
#include "timer.h"
#include "usart.h"
#include "led.h"
#include "adc.h"
#include "Servo.h"//舵机
#include "PWM.h"
#include "jdq.h"

#include "esp8266.h"//网络相关头文件
#include "onenet.h"

#include <math.h>//数学公式头文件


TaskHandle_t mytask1;
TaskHandle_t mytask2;

u8 humidityH;	  //湿度整数部分
u8 humidityL;	  //湿度小数部分
u8 temperatureH;   //温度整数部分
u8 temperatureL;   //温度小数部分

u32 gas;//天然气adc读回的值
u32 ranqi;//可燃气体adc读回的值
u32 yanwu;//烟雾adc读回的值

float w2;//天然气浓度电压值
float w3;//可燃气体浓度电压值
float w4;//烟雾浓度电压值

u32 ppm2;//天然气浓度值
u32 ppm3;//可燃气体浓度值
u32 ppm4;//烟雾浓度值

unsigned char hy,yd;//火焰

char PUB_BUF[256];//上传数据的buf
const char *devSubTopic[] = {"/mysmarthome/sub"};
const char devPubTopic[] = "/smarthome/pub";
u8 ESP8266_INIT_OK = 0;//esp8266初始化完成标志

unsigned char *dataPtr = NULL;

//任务1用来接收云平台下发数据
void myTask1( void *arg )
{
	while(1)
	{
		dataPtr = ESP8266_GetIPD(10);
		DEBUG_LOG("收到小程序下发指令");
		if(dataPtr != NULL)
			OneNet_RevPro(dataPtr);
		vTaskDelay(200);
	}
}


//任务2用于采集传感器数据并上云
void myTask2( void *arg )
{
	while(1)
	{
			//读取火焰传感器数字io输出:PB7
			hy = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7);
			//读取雨滴传感器数字io输出:PB8
			yd = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8);
		
		
			//读取MQ传感器原始数据:PA6、PA7、PB0分别对应MQ-4、MQ-9、MQ-2
		  taskENTER_CRITICAL();
			gas = AD_GetValue(ADC_Channel_6);
		  taskEXIT_CRITICAL();
			w3 = gas*5.0/4095;
			ppm2 = pow((7.2495*353.134*w3)/(23.5-4.7*w3),0.6077);
			
		  taskENTER_CRITICAL();
		  ranqi =  AD_GetValue(ADC_Channel_7);
		  taskEXIT_CRITICAL();
			w2 = ranqi*5.0/4095;
			ppm3 = pow((24.5911*430.323*w2)/(50-10*w2),0.6934);
		
			taskENTER_CRITICAL();
		  yanwu =  AD_GetValue(ADC_Channel_8);
		  taskEXIT_CRITICAL();
			w3 = yanwu*5.0/4095;
			ppm4 = pow((24.5911*430.323*w3)/(50-10*w3),0.6934);
			
			//读取DHT11数据:PA8
		  taskENTER_CRITICAL();//关闭中断,防止执行读取温湿度时被任务1打断
			DHT11_Read_Data(&humidityH,&humidityL,&temperatureH,&temperatureL);
		  taskEXIT_CRITICAL();
			
			//发布数据
		  taskENTER_CRITICAL();
			DEBUG_LOG("==================================================================================");
			DEBUG_LOG("发布数据");
			sprintf(PUB_BUF,"{\"Humi\":%d.%d,\"Temp\":%d.%d,\"huoyan\":%d,\"xiayu\":%d,\"gas\":%d,\"ranqi\":%d,\"yanwu\":%d}",humidityH,humidityL,temperatureH,temperatureL,!hy,yd,ppm2,ppm3,ppm4);
			OneNet_Publish(devPubTopic, PUB_BUF);
			DEBUG_LOG("==================================================================================");
		  taskEXIT_CRITICAL();
			vTaskDelay(5000);
	}
}


int main(void)
{
	TIM3_Init();
	
	Usart1_Init(115200);
	DEBUG_LOG("串口1初始化完成\r\n");//串口打印调试信息:TX PA9 ; RX  PA10
	
	LED_Init();
	DEBUG_LOG("led初始化完成\r\n");
	
	Usart2_Init(115200);//stm32-esp8266通讯串口;TX PA2 ; RX  PA3
	DEBUG_LOG("串口2初始化完成\r\n");
	
	DHT11_Init();
	DEBUG_LOG("DHT11初始化完成\r\n");
	
	AD_Init();
	DEBUG_LOG("MQ-4、MQ-9初始化完成\r\n");
	
	Servo_Init();
	DEBUG_LOG("舵机初始化完成\r\n");//PA1
	
	Jdq_GPIO_Init();
	DEBUG_LOG("继电器初始化完成\r\n");//风扇、水泵:PB5、6
	
	HY_GPIO_Init();
	DEBUG_LOG("火焰传感器初始化完成\r\n");//火焰传感器:PB7
	
	YD_GPIO_Init();
	DEBUG_LOG("雨滴传感器初始化完成\r\n");//雨滴传感器:PB8
	
	DEBUG_LOG("初始化ESP8266 WIFI模块...");
	if(!ESP8266_INIT_OK){
		DEBUG_LOG("WIFI连接中...");
	}
	ESP8266_Init();					//初始化ESP8266
	
	DEBUG_LOG("服务器连接中...");
	while(OneNet_DevLink()){//接入OneNET
		TIM3_Delayms(500);
	}	
	
	OneNet_Subscribe(devSubTopic, 1);//订阅上位机下发数据Topic
	
	xTaskCreate(myTask1,"matask1",512,NULL,3,&mytask1);
	xTaskCreate(myTask2,"matask2",512,NULL,2,&mytask2);
	DEBUG_LOG("任务创建完成,开始任务调度\r\n");
	vTaskStartScheduler();//开始任务调度,这之后的代码不会执行
	
	while (1)
	{
	}
}

2、onenet.c中的OneNet_RevPro()函数:
这个函数负责解析微信小程序发送给服务器,又由服务器转发给下位机的指令:

void OneNet_RevPro(unsigned char *cmd)
{
	
	MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};								//协议包
	
	char *req_payload = NULL;
	char *cmdid_topic = NULL;
	
	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;
	
	type = MQTT_UnPacketRecv(cmd);
	switch(type)
	{
		case MQTT_PKT_CMD:															//命令下发
			
			result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len);	//解出topic和消息体
			if(result == 0)
			{
				DEBUG_LOG("cmdid: %s, req: %s, req_len: %d\r\n", cmdid_topic, req_payload, req_len);

				MQTT_DeleteBuffer(&mqttPacket);									//删包
			}
		break;
			
		case MQTT_PKT_PUBLISH:														//接收MQTT服务器转发的小程序下发数据
		
			result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);//解析MQTT数据包
			if(result == 0)
			{
				DEBUG_LOG("topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",
																	cmdid_topic, topic_len, req_payload, req_len);
				// 对数据包内的req_payload(消息体)进行JSON格式解析
				json = cJSON_Parse(req_payload);
				if (!json)
					UsartPrintf(USART_DEBUG,"Error before: [%s]\n",cJSON_GetErrorPtr());
				else
				{
					json_value = cJSON_GetObjectItem(json , "target");   //在JSON对象json中查找“target”这个键的值,并返回对应的cjson指针              //{'target':'leds'},这就是键值对
					//串口打印键值对                                                                            
					UsartPrintf(USART_DEBUG,"json_string: [%s]\n",json_value->string);//string-键
					UsartPrintf(USART_DEBUG,"json_value: [%s]\n",json_value->valuestring);//valuestring-值
					//strstr是标准字符串判断函数,判断前面查找到的“target”这个键的值是否等于leds
					if(strstr(json_value->valuestring,"leds") != NULL)
					{
						json_value = cJSON_GetObjectItem(json , "value");//进行第二次get,这样获取的就是value:1
						if(json_value->valueint)
							GPIO_SetBits(GPIOC,GPIO_Pin_13);
						else 
							GPIO_ResetBits(GPIOC,GPIO_Pin_13);
					}
					else if(strstr(json_value->valuestring,"men") != NULL)
					{
						json_value = cJSON_GetObjectItem(json , "value");//进行第二次get,这样获取的就是value:1
						if(json_value->valueint)
						{
								Servo_SetAngle(180);
								TIM3_Delayms(20);
						}
						else
						{
								Servo_SetAngle(0);
							  TIM3_Delayms(20);
						}
					}
					else if(strstr(json_value->valuestring,"beep") != NULL)//窗帘一档
					{
						json_value = cJSON_GetObjectItem(json,"value");
						if(json_value->valueint)
						{
							Servo_SetAngle2(60);
							TIM3_Delayms(20);
						}
						else
						{
							Servo_SetAngle2(60);
							TIM3_Delayms(20);
						}
					}
					else if(strstr(json_value->valuestring,"sandang") != NULL)//窗帘三档
					{
						json_value = cJSON_GetObjectItem(json,"value");
						if(json_value->valueint)
						{
							Servo_SetAngle2(180);
							TIM3_Delayms(20);
						}
						else
						{
							Servo_SetAngle2(180);
							TIM3_Delayms(20);
						}
					}
					else if(strstr(json_value->valuestring,"erdang") != NULL)//窗帘二档
					{
						json_value = cJSON_GetObjectItem(json,"value");
						if(json_value->valueint)
						{
							Servo_SetAngle2(120);
							TIM3_Delayms(20);
						}
						else
						{
               Servo_SetAngle2(120);
							TIM3_Delayms(20);
						}
					}
					else if(strstr(json_value->valuestring,"guanchuang") != NULL)//关闭窗帘
					{
						json_value = cJSON_GetObjectItem(json,"value");
						if(json_value->valueint)
						{
								Servo_SetAngle2(0);
						}
						else
						{
							  Servo_SetAngle2(0);
						}
					}
					else if(strstr(json_value->valuestring,"fen") != NULL)//小程序下发开风扇指令
					{
						json_value = cJSON_GetObjectItem(json,"value");
						if(json_value->valueint)
						{
							GPIO_SetBits(GPIOB,GPIO_Pin_5);			
							
						}
						else
						{
						  GPIO_ResetBits(GPIOB,GPIO_Pin_5);			
						}
					}
					else if(strstr(json_value->valuestring,"water") != NULL)//小程序下发开风扇指令
					{
						json_value = cJSON_GetObjectItem(json,"value");//小程序下发开水阀指令
						if(json_value->valueint)
						{
							GPIO_SetBits(GPIOB,GPIO_Pin_6);	
							
						}
						else
						{
							GPIO_ResetBits(GPIOB,GPIO_Pin_6);			
						}
					}
					else
					{
						
					}
				}
				cJSON_Delete(json);
			}
		break;
			
		case MQTT_PKT_PUBACK:														//发送Publish消息,平台回复的Ack,这里只有消息服务质量等级升高时才会用到(qos)
		
			if(MQTT_UnPacketPublishAck(cmd) == 0)
				DEBUG_LOG("Tips:	MQTT Publish Send OK\r\n");
			
		break;
			
		case MQTT_PKT_PUBREC:														//发送Publish消息,平台回复的Rec,设备需回复Rel消息
		
			if(MQTT_UnPacketPublishRec(cmd) == 0)
			{
				DEBUG_LOG("Tips:	Rev PublishRec\r\n");
				if(MQTT_PacketPublishRel(MQTT_PUBLISH_ID, &mqttPacket) == 0)
				{
					DEBUG_LOG("Tips:	Send PublishRel\r\n");
					ESP8266_SendData(mqttPacket._data, mqttPacket._len);
					MQTT_DeleteBuffer(&mqttPacket);
				}
			}
		
		break;
			
		case MQTT_PKT_PUBREL:														//收到Publish消息,设备回复Rec后,平台回复的Rel,设备需再回复Comp
			
			if(MQTT_UnPacketPublishRel(cmd, pkt_id) == 0)
			{
				DEBUG_LOG("Tips:	Rev PublishRel\r\n");
				if(MQTT_PacketPublishComp(MQTT_PUBLISH_ID, &mqttPacket) == 0)
				{
					DEBUG_LOG("Tips:	Send PublishComp\r\n");
					ESP8266_SendData(mqttPacket._data, mqttPacket._len);
					MQTT_DeleteBuffer(&mqttPacket);
				}
			}
		
		break;
		
		case MQTT_PKT_PUBCOMP:														//发送Publish消息,平台返回Rec,设备回复Rel,平台再返回的Comp
		
			if(MQTT_UnPacketPublishComp(cmd) == 0)
			{
				DEBUG_LOG("Tips:	Rev PublishComp\r\n");
			}
		
		break;
			
		case MQTT_PKT_SUBACK:														//发送Subscribe消息的Ack
		
			if(MQTT_UnPacketSubscribe(cmd) == 0)
				DEBUG_LOG("Tips:	MQTT Subscribe OK\r\n");
			else
				DEBUG_LOG("Tips:	MQTT Subscribe Err\r\n");
		
		break;
			
		case MQTT_PKT_UNSUBACK:														//发送UnSubscribe消息的Ack
		
			if(MQTT_UnPacketUnSubscribe(cmd) == 0)
				DEBUG_LOG("Tips:	MQTT UnSubscribe OK\r\n");
			else
				DEBUG_LOG("Tips:	MQTT UnSubscribe Err\r\n");
		
		break;
		
		default:
			result = -1;
		break;
	}
	
	ESP8266_Clear();									//清空缓存
	
	if(result == -1)
		return;
	
	dataPtr = strchr(req_payload, '}');					//搜索'}'

	if(dataPtr != NULL && result != -1)					//如果找到了
	{
		dataPtr++;
		
		while(*dataPtr >= '0' && *dataPtr <= '9')		//判断是否是下发的命令控制数据
		{
			numBuf[num++] = *dataPtr++;
		}
		
		num = atoi((const char *)numBuf);				//转为数值形式
		
	}

	if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH)
	{
		MQTT_FreeBuffer(cmdid_topic);
		MQTT_FreeBuffer(req_payload);
	}

}

2、微信小程序

1、index.js

// index.js
// 获取应用实例
const app = getApp()

const{ connect } = require('../../utils/mqtt')

const mqttHost = 'broker.emqx.io'
const mqttPort = 8084

const deviceSub = '/mysmarthome/sub' //小程序发布指令的Topic
const devicePub = '/smarthome/pub' //小程序接收数据的Topic

const mpSubTopic = devicePub //mp指小程序(这样更直观)
const mpPubTopic = deviceSub //小程序发布指令的Topic

Page({
  data: {
    client: null,
    Humi:0,
    Temp:0,
    huoyan:1,
    gas:0,
    leds:false,
    ranqi:0,
    fen:false,
    water:false,
    men:false
  },

 //下发指令:led
  onledsChange(event){
    const that = this
    console.log(event.detail.value);
    const sw = event.detail.value
    that.setData({leds:sw})

    if(sw){
       that.data.client.publish(mpPubTopic,JSON.stringify({
         target:"leds",
         value:1
       }),function (err) {
         if(!err){
           console.log('成功下发指令打开led')
           console.log('{target:"leds",value:1}')
         }
       })
    }else{
      that.data.client.publish(mpPubTopic,JSON.stringify({
        target:"leds",
        value:0
      }),function (err) {
        if(!err){
          console.log('成功下发指令关闭led')
          console.log('{target:"leds",value:0}')
        }
      })
    }
  },
 //下发指令:风扇
  onfenChange(event){
    const that = this
    console.log(event.detail.value);
    const sw = event.detail.value
    that.setData({fen:sw})

    if(sw){
       that.data.client.publish(mpPubTopic,JSON.stringify({
         target:"fen",
         value:1
       }),function (err) {
         if(!err){
           console.log('成功下发指令打开排气扇')
         }
       })
    }else{
      that.data.client.publish(mpPubTopic,JSON.stringify({
        target:"fen",
        value:0
      }),function (err) {
        if(!err){
          console.log('成功下发指令关闭排气扇')
        }
      })
    }
  },
   //下发指令:水泵
   onwaterChange(event){
    const that = this
    console.log(event.detail.value);
    const sw = event.detail.value
    that.setData({water:sw})

    if(sw){
       that.data.client.publish(mpPubTopic,JSON.stringify({
         target:"water",
         value:1
       }),function (err) {
         if(!err){
           console.log('成功下发指令打开水泵')
         }
       })
    }else{
      that.data.client.publish(mpPubTopic,JSON.stringify({
        target:"water",
        value:0
      }),function (err) {
        if(!err){
          console.log('成功下发指令关闭水泵')
        }
      })
    }
  },

     //下发指令:门
     onmenChange(event){
      const that = this
      console.log(event.detail.value);
      const sw = event.detail.value
      that.setData({men:sw})
  
      if(sw){
         that.data.client.publish(mpPubTopic,JSON.stringify({
           target:"men",
           value:1
         }),function (err) {
           if(!err){
             console.log('成功下发指令打开门')
           }
         })
      }else{
        that.data.client.publish(mpPubTopic,JSON.stringify({
          target:"men",
          value:0
        }),function (err) {
          if(!err){
            console.log('成功下发指令关闭门')
          }
        })
      }
    },
    
    //舵机逻辑
    //下发指令:窗帘一档
    goToPage1(event){
      const that = this
      that.setData({cnt1:this.data.cnt1 + 1})
  
      if(this.data.cnt1 > 0){
         that.data.client.publish(mpPubTopic,JSON.stringify({
           target:"beep",
           value:1
         }),function (err) {
           if(!err){
             console.log('成功下发指令打开窗帘一档')
           }
         })
      }else{
        that.data.client.publish(mpPubTopic,JSON.stringify({
          target:"beep",
          value:1
        }),function (err) {
          if(!err){
            console.log('成功下发指令关闭窗帘一档')
          }
        })
      }
    },
  //下发指令:二档
  goToPage2(event){
    const that = this
    that.setData({cnt2:this.data.cnt2 + 1})

    if(this.data.cnt2 >= 0){
       that.data.client.publish(mpPubTopic,JSON.stringify({
         target:"erdang",
         value:1
       }),function (err) {
         if(!err){
           console.log('成功下发指令打开窗帘二档')
         }
       })
    }else{
      that.data.client.publish(mpPubTopic,JSON.stringify({
        target:"erdang",
        value:1
      }),function (err) {
        if(!err){
          console.log('成功下发指令关闭窗帘二档')
        }
      })
    }
  },
  //下发指令:三档
  goToPage3(event){
    const that = this
    that.setData({cnt3:this.data.cnt3 + 1})

    if(this.data.cnt3 >= 0){
       that.data.client.publish(mpPubTopic,JSON.stringify({
         target:"sandang",
         value:1
       }),function (err) {
         if(!err){
           console.log('成功下发指令打开窗帘三档')
         }
       })
    }else{
      that.data.client.publish(mpPubTopic,JSON.stringify({
        target:"sandang",
        value:1
      }),function (err) {
        if(!err){
          console.log('成功下发指令关闭窗帘s档')
        }
      })
    }
  },
  goToPage4(event){
    const that = this
    that.setData({cnt4:this.data.cnt4 + 1})

    if(this.data.cnt4 >= 0){
       that.data.client.publish(mpPubTopic,JSON.stringify({
         target:"guanchuang",
         value:1
       }),function (err) {
         if(!err){
           console.log('成功下发指令关闭窗帘')
         }
       })
    }else{
      that.data.client.publish(mpPubTopic,JSON.stringify({
        target:"guanchuang",
        value:1
      }),function (err) {
        if(!err){
          console.log('成功下发指令关闭窗帘')
        }
      })
    }
  },

 //显示接受到的数据
  onShow(){
    const that = this
    that.setData({
      client:connect(`wxs://${mqttHost}:${mqttPort}/mqtt`)
    })
    that.data.client.on('connect',function (params) {
      console.log('成功连接到mqtt服务器')
      wx.showToast({
        title: '连接成功',
        icon:'success',
        mask:true
      })
      that.data.client.subscribe(mpSubTopic,function (err) {
        if(!err){
          console.log('成功订阅设备上行数据Topic')
        }
      })
    })

    that.data.client.on('message',function (topic,message) {
      console.log(topic);
      let dataFromDev = {}
      try {
           dataFromDev = JSON.parse(message)
           console.log(dataFromDev);
           that.setData({
             Temp:dataFromDev.Temp,
             Humi:dataFromDev.Humi,
             huoyan:dataFromDev.huoyan,
             gas:dataFromDev.gas,
             ranqi:dataFromDev.ranqi,
             xiayu:dataFromDev.xiayu,
             yanwu:dataFromDev.yanwu,
           })
      } catch (error) {
        console.log('JSON解析失败',error)
      }
    })
  },
})

2、index.wxml

<!--index.wxml-->
<view class="page-container">
<!--头部部分-->
  <view class="header-container">
  <view class="header-one">
    <view>
      战损版
   </view>
  </view>
  <view class="header-two">
    <view>
      智能家居
    </view>
  </view>
  </view>
<!--数据部分-->
<view class="data-container">
  <!--温度-->
  <view class="data-card">
    <image class="data-card__icon" src="/static/wendu.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        温度
      </view>
      <view class="data-card__value">
        {{ Temp }} ℃
      </view>
    </view>
  </view>
 <!--湿度-->
  <view class="data-card">
    <image class="data-card__icon" src="/static/shidu.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        湿度
      </view>
      <view class="data-card__value">
        {{ Humi }}%rh
      </view>
    </view>
  </view>
  <!--可燃气体浓度-->
  <view class="data-card">
    <image class="data-card__icon" src="/static/co.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        可燃气浓度
      </view>
      <view class="data-card__value">
        {{ ranqi }} ppm
      </view>
    </view>
  </view>
  

  <!--天然气浓度-->
  <view class="data-card">
    <image class="data-card__icon" src="/static/gas.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        天然气浓度
      </view>
      <view class="data-card__value">
        {{ gas }} ppm
      </view>
    </view>
  </view>

    <!--烟雾浓度-->
    <view class="data-card">
    <image class="data-card__icon" src="/static/yanwu.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        烟雾浓度
      </view>
      <view class="data-card__value">
        {{ yanwu }} ppm
      </view>
    </view>
  </view>

  <view class="data-card">
    <image class="data-card__icon" src="/static/huoyan.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        火焰
      </view>
      <view class="data-card__value">
        <view wx:if="{{huoyan == 0}}">
          无
        </view>
        <view wx:elif="{{huoyan == 1}}">
          有
        </view>
      </view>
    </view>
  </view>


  <!--雨水-->
  <view class="data-card">
    <image class="data-card__icon" src="/static/yudi.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        雨
      </view>
      <view class="data-card__value">
        <view wx:if="{{xiayu==0}}">
          下雨
        </view>
        <view wx:elif="{{xiayu==1}}">
          未下雨
        </view>
      </view>
    </view>
  </view>

  <!--开启or关闭排气扇-->
  <view class="data-card">
    <image class="data-card__icon" src="/static/fen.png"/>
    <view class="data-card__text">
      <view class="data-card__title">
        排气扇
      </view>
      <view class="data-card__value">
        <switch checked="{{fen}}" bindchange="onfenChange" color="#3d7ef9"/>
      </view>
    </view>
  </view>

    <!--开启or关闭水泵-->
    <view class="data-card">
    <image class="data-card__icon" src="/static/water.png"/>
    <view class="data-card__text">
      <view class="data-card__title">
        水泵
      </view>
      <view class="data-card__value">
        <switch checked="{{water}}" bindchange="onwaterChange" color="#3d7ef9"/>
      </view>
    </view>
  </view>

    <!--开启or关闭卧室灯-->
    <view class="data-card">
    <image class="data-card__icon" src="/static/leds.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        卧室灯
      </view>
      <view class="data-card__value">
      <switch checked="{{ leds }}" bindchange="onledsChange" color="#3d7ef9"/>
      </view>
    </view>
  </view>

    <!--开启or关闭卧室灯-->
    <view class="data-card">
    <image class="data-card__icon" src="/static/door-open.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        门
      </view>
      <view class="data-card__value">
      <switch checked="{{ men }}" bindchange="onmenChange" color="#3d7ef9"/>
      </view>
    </view>
  </view>

<!--   开启or关闭门
  <view class="data-card">
    <image class="data-card__icon" src="/static/door-open.png"/>
    <view class="data-card__text">
      <view class="data-card__title">
        门
      </view>
    </view>
  </view> -->

<view class="choice">
  <view>窗帘</view>
</view>

<view class="s_view">
  <button class='btn1' hover-class="btn-class" style="width: 45vw;height: 8vh;"
  bindtap="goToPage1">一档</button>
  <button style="width: 45vw;height: 8vh;"
  class='btn2' hover-class="btn-class2" bindtap="goToPage2">二档</button>
  <button style="width: 45vw;height: 8vh;"
  class='btn3' hover-class="btn-class3" bindtap="goToPage3">三档</button>
  <button style="width: 45vw;height: 8vh;"
  class='btn4' hover-class="btn-class4" bindtap="goToPage4">关闭</button>
</view>
</view>
</view>

3、index.wxss

/**小程序整个页面的设置**/
.page-container{
  padding: 36rpx;
}
/**设置顶部大筐筐**/
.header-container{
  background-color: #fff;
  color: black;
  box-shadow: #d6d6d6 0 0 20rpx;
  border-radius: 36rpx;
  padding: 3rpx 3rpx;
}
/**设置顶部大筐筐内的第一行字**/
.header-container .header-one{
  display: flex;
  justify-content: center;
  padding: 10rpx;
  font-size: 50rpx;
  font: bold;
}
/**设置顶部大筐筐内的第二行字**/
.header-container .header-two{  
  display: flex;
  justify-content: center;
  padding: 10rpx;
  font-size: 40rpx;
  font: bold;
}


.choice {
  display: flex;
  flex-direction: row;
  font-size: 20px;
  justify-content: center;
  color: rgb(38, 38, 39);
  margin-top: 30rpx;
  margin-bottom: 20rpx;
  top: 3rpx;
  box-shadow:0px 0px 10px #bfbfc0 inset;
}

.btn1 {
  /* float:left;
  background-color: rgb(112, 107, 107);
  color: rgb(66, 63, 63); */
  float:left;
  top: 3rpx;
  box-shadow:0px 0px 8px #999 inset; 
}

.btn-class{
  float:left;
  top: 3rpx;
  box-shadow:0px 0px 10px #b8b9bd inset;
}

.btn2 {
   float:right; 
  top: 3rpx;
  box-shadow:0px 0px 8px #999 inset; 
}
.btn-class2{
   float:right;
  top: 3rpx;
  box-shadow:0px 0px 10px #b8b9bd inset;
}

.btn3 {
  float:left;
  top: 3rpx;
  box-shadow:0px 0px 8px #999 inset; 
}
.btn-class3{
  float:left;
  top: 3rpx;
  box-shadow:0px 0px 10px #b8b9bd inset;
}

.btn4 {
  float:right;
  top: 3rpx;
  box-shadow:0px 0px 8px #999 inset; 
}
.btn-class4{
   float:right;
  top: 3rpx;
  box-shadow:0px 0px 10px #b8b9bd inset;
}

.s_view{
  bottom: 0rpx; 
  width: 100%;
}




/**设置数据部分总体布局**/
.data-container{
  margin-top: 30rpx;
  display: grid;
  justify-content: center;
  grid-template-columns: repeat(auto-fill,300rpx);
  grid-gap: 30rpx;
}
/**设置数据部分小卡片内的格式**/
.data-container .data-card{
  position: relative;
  background-color: #fff;
  height: 140rpx;
  box-shadow: #d6d6d6 0 0 8rpx;
  border-radius: 36rpx;
  display: flex;
  justify-content: space-between;
  padding: 16rpx;
}
/**以下是小卡片内的文本、图片、标题、数值设置**/
.data-container .data-card .data-card__text{
  position: absolute;
  top: 20rpx;
  right: 24rpx;
  text-align: right;
  white-space: nowrap;
}

.data-container .data-card .data-card__icon{
  position: absolute;
  height: 72rpx;
  width: 72rpx;
  left: 32rpx;
  top: 16rpx;
}

.data-container .data-card .data-card__value{
  font-size: 40rpx;
  margin-top: 20rpx;
}

.data-container .data-card .data-card__title{
  font-size: 34rpx;
}

三、工程源码获取

关注俺,私聊私发无偿。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/389106.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

相机图像质量研究(17)常见问题总结:CMOS期间对成像的影响--靶面尺寸

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

用EL操作JAVABEAN属性

用EL操作JAVABEAN属性 问题陈述 Smart SoftWare Inc.想要开发一款维护雇员数据(例如姓、名字、职位)的Web应用程序。该组织决定将雇员数据存储在一个JavaBean中。另外,它还希望该Web应用程序能让用户从此JavaBean中检索数据并用JSP页面和EL显示。 解决方案 要解决上述问题…

.net和jar包windows服务部署

一.NetCore 1.创建启动脚本run_instal.bat,例如程序文件为ApiDoc.exe set serviceName"Apidoc Web 01" set serviceFilePath%~dp0ApiDoc.exe set serviceDescription"ApiDoc 动态接口服务 web 01"sc create %serviceName% BinPath%serviceFilePath% sc c…

【JavaEE】_文件与IO

目录 1.文件概述 1.1 文件的概念 1.2 文件的存储 1.3 文件的分类 1.4 目录结构 1.5 文件操作 1.5.1 文件系统操作 1.5.2 文件内容操作 2. Java文件系统操作 2.1 File类所处的包 2.2 构造方法 2.3 方法 2.3.1 与文件路径、文件名有关的方法 2.3.2 文件是否存在与普…

新时代异步 IO 框架:IO_URING 的原理、用法、业界示例分析

文章目录 IO_URING基本介绍常见 I/O 模型IO_URING 原理核心结构工作模式高级特性 用法APIliburing基本流程Demo 业界示例SeaStar / ScyllaDBCEPHRocksDBClickHouse IO_URING 基本介绍 常见 I/O 模型 当前 Linux 的几种 I/O 模型&#xff1a; I/O 模型 同步 I/O 是目前应用最…

2024.2.10 HCIA - Big Data笔记

1. 大数据发展趋势与鲲鹏大数据大数据时代大数据的应用领域企业所面临的挑战和机遇华为鲲鹏解决方案2. HDFS分布式文件系统和ZooKeeperHDFS分布式文件系统HDFS概述HDFS相关概念HDFS体系架构HDFS关键特性HDFS数据读写流程ZooKeeper分布式协调服务ZooKeeper概述ZooKeeper体系结构…

【C++】C++11上

C11上 1.C11简介2.统一的列表初始化2.1 {} 初始化2.2 initializer_list 3.变量类型推导3.1auto3.2decltype3.3nullptr 4.范围for循环5.final与override6.智能指针7. STL中一些变化8.右值引用和移动语义8.1左值引用和右值引用8.2左值引用与右值引用比较8.3右值引用使用场景和意义…

GPU独显下ubuntu屏幕亮度不能调节解决方法

GPU独显下屏幕亮度不能调节&#xff08;假设你已经安装了合适的nvidia显卡驱动&#xff09;&#xff0c;我试过修改 /etc/default/grub 的 GRUB_CMDLINE_LINUX_DEFAULT"quiet splash acpi_backlightvendor" &#xff0c;没用。修改和xorg.conf相关的文件&#xff0c;…

【python】类创建、实例化和调用类方法、子类、继承、私有属性、静态方法

一、类属性&#xff1a;定义在类中函数外的属性;self代表类的实例。 其中 number属于类属性&#xff0c;name、age属于实例属性&#xff1b;实例属性一般在初始函数中定义。 class people:number300 #类属性def __init__(self,name,age):#初始化方法 左右两个下划线self.nam…

就是民族的气节

我们拥有一个名字叫中国 - 张明敏 一把黄土塑成千万个你我 静脉是长城 动脉是黄河五千年的文化是生生不息的脉搏&#xff08;齐楚燕韩赵魏秦&#xff09;提醒你 提醒我我们拥有个名字叫中国&#xff08;中原地区为主体&#xff0c;河南&#xff0c;山东&#xff0c;安徽&…

语言与真实世界的关系(超级语言生成能力将促进世界深刻变化)

语言与真实世界之间存在着紧密且复杂的关系。在人类社会中&#xff0c;语言是认知、表达和交流现实世界的主要工具&#xff0c;它帮助我们构建并理解周围环境&#xff0c;并将我们的思维和经验概念化。 1. 符号与指代&#xff1a; 语言是一种符号系统&#xff0c;通过词汇、句…

【精选】Java面向对象进阶——接口

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

(11)Hive调优——explain执行计划

一、explain查询计划概述 explain将Hive SQL 语句的实现步骤、依赖关系进行解析&#xff0c;帮助用户理解一条HQL 语句在底层是如何实现数据的查询及处理&#xff0c;通过分析执行计划来达到Hive 调优&#xff0c;数据倾斜排查等目的。 官网指路&#xff1a; https://cwiki.ap…

第14讲投票帖子详情实现

投票帖子详情实现 后端,根据id查询投票帖子信息&#xff1a; /*** 根据id查询* param id* return*/ GetMapping("/{id}") public R findById(PathVariable(value "id")Integer id){Vote vote voteService.getById(id);WxUserInfo wxUserInfo wxUserInf…

【Go语言】第一个Go程序

第一个 Go 程序 1 安装 Go Go语言官网&#xff1a;Download and install - The Go Programming Language&#xff0c;提供了安装包以及引导流程。 以 Windows 为例&#xff0c;进入windows安装包下载地址&#xff1a;All releases - The Go Programming Language&#xff0c…

BDD - Python Behave 用户自定义配置文件

BDD - Python Behave 用户自定义配置文件 引言默认 behave.ini 配置文件自定义配置文件json 格式的配置文件ini 格式的配置文件 实例应用项目结构代码BDD/Features/user_data.feature 文件BDD/steps/user_data_steps.py 文件BDD/environment.py 文件默认配置文件 behave.ini自定…

构建智慧交通平台:架构设计与实现

随着城市交通的不断发展和智能化技术的迅速进步&#xff0c;智慧交通平台作为提升城市交通管理效率和水平的重要手段备受关注。本文将探讨如何设计和实现智慧交通平台的系统架构&#xff0c;以应对日益增长的城市交通需求&#xff0c;并提高交通管理的智能化水平。 ### 1. 智慧…

Spring 用法学习总结(二)之基于注解注入属性

Spring学习 5 基于注解方式创建对象6 基于注解注入属性 5 基于注解方式创建对象 注解是代码的特殊标记&#xff0c;可以简化xml配置&#xff0c;格式&#xff1a;注解名称(属性名称属性值&#xff09;&#xff0c;可以作用在类、方法、属性上 以下注解都可以创建bean实例 Com…

【Qt】qt常用控件之QIcon 以及 qrc机制设置图片路径(QtCreator)

文章目录 1. QIcon / windowIcon2. setIcon() 与 setwindowIcon()2.1 setIcon() 介绍与使用2.2 setWindowIcon 介绍与使用 3. 路径问题 & qrc机制的引入3.1 绝对路径 / 相对路径 的问题3.2 qrc机制3.3 在QtCreator下利用qrc机制引入图片 1. QIcon / windowIcon QIcon QIco…

Java学习第十四节之冒泡排序

冒泡排序 package array;import java.util.Arrays;//冒泡排序 //1.比较数组中&#xff0c;两个相邻的元素&#xff0c;如果第一个数比第二个数大&#xff0c;我们就交换他们的位置 //2.每一次比较&#xff0c;都会产生出一个最大&#xff0c;或者最小的数字 //3.下一轮则可以少…
最新文章