【嵌入式实时操作系统开发】智能家居入门4(FreeRTOS、MQTT服务器、MQTT协议、STM32、微信小程序)

news2024/11/25 19:21:47

前面已经发了智能家居入门的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-Link、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.coloradmin.cn/o/2195166.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaSE——面向对象9.1:代码块详解

目录 一、静态代码块 二、构造代码块 三、局部代码块 四、总结 一、静态代码块 在《JavaSE——面向对象9&#xff1a;static、final关键字、代码块、单例模式》这篇文章中已经介绍过&#xff0c;静态代码块随着类的加载而加载&#xff0c;且只加载一次&#xff0c;这里不再…

【优选算法】---分治 归并排序

分治 归并排序 一、排序数组 / 归并排序的复习1、题目解析2、算法原理3、代码 二、逆序对的总数1、题目解析2、算法原理3、代码 三、计算右侧小于当前元素的个数1、题目解析2、算法原理3、代码 四、翻转对1、题目解析2、算法原理3、代码 一、排序数组 / 归并排序的复习 归并排…

Windows下Jenkins控制台中文乱码

问题描述 问题情况如下图&#xff1a; 环境信息 Windows 11 家庭中文版java 21.0.4 2024-07-16 LTSJenkins 2.452.3 解决方法 增加系统JAVA_TOOL_OPTIONS&#xff0c;并设置值为-Dfile.encodingGBK。 打开设置方法&#xff1a;桌面上右键点击“此电脑”图标&#xff0c;选…

软考高级之系统架构师之计算机硬件基础

概述 局部性原理是指在指定时间内&#xff0c;程序趋于在有限的内存区域内重复访问。通常将局部性分为空间局部性和时间局部性。空间局部性是指已访问过的内存地址附近的位置很可能被连续访问。时间局部性是指已访问过的内存地址在较短的时间内还可能被多次访问。 计算机执行…

请散户股民看过来,密切关注两件大事

明天股市要开市&#xff0c;不仅散户股民期盼节后股市大涨&#xff0c;上面也同样想在节后来上一个“开门红”。 为此&#xff0c;上面没休假&#xff0c;关起门来办了两件大事&#xff0c;这两天发布消息已提前预热了。 两件大事如下&#xff1a; 一是&#xff0c;上交所10…

柯桥生活口语学习之在化妆品店可以用到的韩语句子

화장품을 사고 싶어요. 我想买化妆品。 어떤 화장품을 원하세요? 您想买什么化妆品。 스킨로션을 찾고 있어요. 我想买化妆水&#xff0c;乳液。 피부 타입은 어떠세요? 您是什么皮肤类型&#xff1f; 민감성 피부예요. 我是敏感性皮肤。 평소에 쓰시는 제품은 뭐예…

【判断推理】逻辑基础

1.1 命题 用语言、符号或者式子表达的&#xff0c;可以判断真假的陈述句称为命题&#xff0c;一般写为 若p&#xff0c;则q 真命题&#xff1a;判断为真的语句假命题&#xff1a;判断为假的语句 eg1&#xff1a;小张是中国人&#xff08;若是小张&#xff0c;则是中国人&#…

【传感器技术】【9 温度测量,热电偶传感器,膨胀式温度传感器,压力测量,弹性式压力表】

上理考研周导师的哔哩哔哩频道 我在频道里讲课哦 目录 9.1、 温度概述 1&#xff0e; 温度与温标 2&#xff0e; 温度测量的主要方法和分类 9.2、 膨胀式温度传感器 1&#xff0e; 液体膨胀式 2&#xff0e; 固体膨胀式 3&#xff0e; 气体膨胀式 9.3、 热电偶传感器 1. …

Cesium的一些神奇概念及技术流程(1)

近期要深度研究Cesium。关于Cesium的用法、渲染流程等方面我看很多人都写过。我就写写其中一些可能平时用不到但是比较有趣的内容。因为边研究边写&#xff0c;所以会陆续出几集&#xff0c;然后合并在一起&#xff0c;欢迎大家跟踪。 我的这些文章不打算把一些基本概念展开解…

OJ在线评测系统 微服务 OpenFeign调整后端上 内部调用Client代码书写 实现客户端接口 配置服务参数 接口文档

OpenFeign内部调用上 我们的代码已经搬运完毕了 但是我们的服务之间是无法相互调用的 我们可以使用OpenFeign进行远程调用 一个http调用客户端 提供了更方便的方式让你远程调用其他的服务 Nacos注册中心获取服务的调用地址 如果没有实现OpenFeign 也能实现跨服务的调用 …

Windows 11 version 24H2 LTSC 2024 中文版、英文版 (x64、ARM64) 下载 (updated Oct 2024)

Windows 11 version 24H2 & LTSC 2024 中文版、英文版 (x64、ARM64) 下载 (updated Oct 2024) Windows 11, version 24H2&#xff0c;企业版 arm64 x64 请访问原文链接&#xff1a;https://sysin.org/blog/windows-11/ 查看最新版。原创作品&#xff0c;转载请保留出处。…

出国必备神器!这5款中英翻译工具让你秒变外语达人

在这个全球化的时代&#xff0c;中英互译已然成为我们日常生活和工作中不可或缺的一环。面对众多的翻译工具&#xff0c;如何选择一款既高效又人性化的翻译助手呢&#xff1f;今天&#xff0c;就让我为大家揭秘几款热门的中英互译工具&#xff0c;并分享我的使用感受。 一、福昕…

【数据结构与算法】线性表

文章目录 一.什么是线性表&#xff1f;二.线性表如何存储&#xff1f;三.线性表的类型 我们知道从应用中抽象出共性的逻辑结构和基本操作就是抽象数据类型&#xff0c;然后实现其存储结构和基本操作。下面我们依然按这个思路来认识线性表 一.什么是线性表&#xff1f; 定义 线性…

【项目记录】大模型基于llama.cpp在Qemu-riscv64向量扩展指令下的部署

概述 本文在qemu-riscv64平台上&#xff0c;利用向量扩展指令加速运行基于llama.cpp构建的大模型。 参考博客链接&#xff1a; Accelerating llama.cpp with RISC-V Vector Extension 基于RVV的llama.cpp在Banana Pi F3 RISCV开发板上的演示 llama.cpp工程 Llama.cpp是一个基…

C语言-指针变量,常量与数组名的细微区别辨析

本节根据两个选择题进行展开辨析 一、例1 本题答案&#xff1a;C 解析&#xff1a;强干扰选项是B&#xff0c;我相信大多数同学都会在B&#xff0c;C之间犹豫好久&#xff0c;那么为什么答案会最终选择C呢&#xff1f;因为本题在定义函数&#xff0c;所以a首先是一个数组名&a…

【学习笔记】一种使用多项式快速计算 sin 和 cos 近似值的方法

一种使用多项式快速计算 sin 和 cos 近似值的方法 在嵌入式开发、游戏开发或其他需要快速数学计算的领域&#xff0c;sin 和 cos 函数的计算时间可能会影响程序的整体性能。特别是在对时间敏感、精度要求不高的场景中&#xff0c;传统的 sin 和 cos 函数由于依赖复杂的数值方法…

SOMEIP_ETS_168: SD_TestFieldUINT8Reliable

测试目的&#xff1a; 验证DUT能够通过Getter和Setter方法正确地发送和接收TestFieldUINT8Reliable字段的值&#xff0c;并且这些操作是可靠的。 描述 本测试用例旨在确保DUT的ETS能够响应Tester的请求&#xff0c;正确地使用Getter方法获取TestFieldUINT8Reliable的值&…

【MySQL必知会】事务

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 事务概念 &#x1f4c1; 事务操作 &#x1f4c1; 事务提交方式 &#x1f4c1; 隔离级别 &#x1f4c1; MVCC &#x1f4c2; 3个隐藏列字段 &#x1f4c2; undo日志 &#x1f4c2; Read View视图 &#x1f4c1; RR和R…

分治算法(5)_归并排序_排序数组

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 分治算法(5)_归并排序_排序数组 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 …