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

news2024/11/13 15:06:36

前面已经写了三篇博客关于智能家居的,服务器全都是使用ONENET中国移动,他最大的优点就是作为数据收发的中转站是免费的。本篇使用专门适配MQTT协议的MQTT服务器,有公用的,也可以自己搭建(应该要钱),项目源码在最后

    • 前言
    • 一、项目总览
    • 二、总体流程分析
      • 1、了解mqtt协议
      • 2、测试下位机与服务器的通信(mqtt.fx)
      • 3、搭建自己的MQTT服务器
    • 三、代码
      • 1、下位机:
      • 2、微信小程序:
    • 四、项目获取

前言

本篇博客实现的功能和之前的智能家居系列类似,仅仅是把服务器换成了公用的mqtt服务器,在经过实测之后,个人觉得智能家居这种场景还是比较适合使用mqtt协议,仅仅是个人感觉。其实功能这一块我想到一个比较有意思的,就是用雨滴传感器和步进电机来模拟自动晾晒衣服的功能,大家应该能想象出来是啥意思,就是下雨了通过步进电机将晒衣服的架子给缩回来,不下雨了又给推出去,这还蛮有意思的。
前几篇博客:
①http+onenet实现数据上传给微信小程序
②http+onenet实现小程序下发指令控制开发板
③mqtt+onenet

一、项目总览

由于面包板还没到,线接的很乱就不拍照了,找到了之前做类似项目的照片,用这个代替一下:
在这里插入图片描述
微信小程序:
在这里插入图片描述

基本功能首先是检测家庭内部环境信息,其次就是微信小程序控制外设动作。

上面的实物图中有一个sim900A模块,是以前用到的,可以用于拨打电话和发送短信,要是有兴趣可以评论或者私信,我单独出一期。
可以看到本次微信小程序大改动,不再是前几篇那样简陋了,但是实现的功能还是一样的,这里就不再介绍了,请移步前几篇博客。

二、总体流程分析

1、了解mqtt协议

mqtt协议我单独出了一篇博客,参考:https://blog.csdn.net/m0_71523511/article/details/135905690

2、测试下位机与服务器的通信(mqtt.fx)

在这里插入图片描述
可以看到免费的mqtt服务器的域名是:broker.emqx.io,下位机上传数据使用TCP端口:1883。
通常当下位机上传数据逻辑写好之后不会直接用微信小程序进行测试,而是使用软件连接服务器来观察数据是否成功上传给了服务器,如果成功即可开始微信小程序的代码编写,没成功就调试,还是比较方便的。
这里使用mqtt.fx来进行测试:
在网上已经不好找到这个软件的早些版本了,这里给出网盘资源,下载后直接安装即可:
我用夸克网盘分享了「mqttfx-1.7.1-windows-x64.exe」,点击链接即可保存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。
链接:https://pan.quark.cn/s/8013f5c2151c
安装完成之后按照下面步骤设置:
首先点击那个蓝色的设置按钮,按下面这样设置好:
在这里插入图片描述
然后订阅单片机向服务器发送数据的topic:/smarthome/pub
在这里插入图片描述
此时只要下载在本篇文章结尾开源的代码(记得修改esp8266.c中的wifi名称和密码,其他的不用修改)并烧录,一会儿就可以在mqtt.fx软件的右边那个框看到上传上去的数据了。由于是公用的服务器,所以可能会导致数据流紊乱。如果测试没问题就可以打开微信小程序愉快的调试了。

3、搭建自己的MQTT服务器

这里我还没有尝试,但是有一个比较靠谱的视频:https://b23.tv/p8RkhU5
一般来说自己搭建多少是要钱的,如果免费的能满足尽量不要自己搭建,或者可以淘宝租一个月玩玩。

三、代码

1、下位机:

代码结构没变,还是那四个文件组成网络通信部分的代码:
在这里插入图片描述
①上传数据也没啥可以说的,直接参照上一篇博客即可(这次代码写的更漂亮,有参考价值)。
②接收数据在这里做了比较大的改动,这里使用两个键值对的逻辑进行发送和解析:
小程序下发的指令:
在这里插入图片描述
实际上对应的就是:{target:“fen”,value:“1”}
下位机解析:
在这里插入图片描述
首先解析出前面这个键值对的值,后面的逻辑就是判断这个值是多少,比如是leds的话就再进行第二个键值对的解析,此时判断第二个键值对的值是0还是1以控制对应的外设。这种逻辑相较于前几次博客的项目而言是可以减少内存占用的,因为只需要初始化一个:cJSON *json , *json_value;

2、微信小程序:

代码风格跟之前的完全不一样,如果想详细了解的话可以学一学javascript,或者直接gpt问各行代码的意思。本设计用微信开发者工具原生框架进行小程序设计,用到了多个不同的文件来设计小程序,其中app.json文件是对小程序进行全局配置的,可以配置顶部文字和底部tabBar栏;以.wxml为后缀的文件用来设置小程序的整个页面及页面各部分内部的文字、数据、图片等;以wxss为后缀的文件用来在.wxml文件构建好的部分中设置规则,比如字体大小、颜色等;以.js为后缀的文件使用JavaScript语言处理小程序和用户的交互(与单片机的数据接收和发送)
①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
  },

 //下发指令: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('成功下发指令关闭水泵')
        }
      })
    }
  },
   //显示接受到的数据
  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,
           })


      } catch (error) {
        console.log('JSON解析失败',error)
      }
    })
  },
})

②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/gas.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/yanwu.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/huoyan.png" />
    <view class="data-card__text">
      <view class="data-card__title">
        火焰
      </view>
      <view class="data-card__value">
        <view wx:if="{{huoyan == 1}}">
          无
        </view>
        <view wx:elif="{{huoyan == 0}}">
          有
        </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==1}}">
          下雨
        </view>
        <view wx:elif="{{xiayu==0}}">
          未下雨
        </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>
  </view>
  </view>

③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;
}

至此整个项目的关键点介绍完毕,可以想象出裸机还是可能会漏消息的,下一篇就是使用freertos来进行编程,使得整个系统的实时性再一次提高。

四、项目获取

我用夸克网盘分享了「智能家居(mqtt服务器+mqtt协议).rar」,点击链接即可保存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。
链接:https://pan.quark.cn/s/42127dfa3d3a
提取码:2Nmg

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

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

相关文章

GMT绘图笔记

(1)图框设置。在利用GMT绘制图件时&#xff0c;需要设置边框的类型&#xff0c;字体的大小&#xff0c;标记距离边框的距离。主要涉及的参数有&#xff1a; gmt set MAP_FRAME_TYPE plain/fancy 可以调整边框为火车轨道或者线段。 (2)调整图框的粗细&#xff1a;主要是包含有…

c语言二叉树的创建,三种遍历方式,销毁

二叉树的创建 typedef char datatype; typedef struct Node {datatype data;struct Node *left_child;struct Node *right_child;}*Btree; //二叉树的创建 Btree create_node() {Btree t(Btree)malloc(sizeof(struct Node));if(NULLt)return NULL;t->data0;t->left_chil…

计算机网络_1.6.2 计算机网络体系结构分层的必要性

1.6.2 计算机网络体系结构分层的必要性 一、五层原理体系结构每层各自主要解决什么问题1、物理层2、数据链路层3、网络层4、运输层5、应用层 二、总结三、练习 笔记来源&#xff1a; B站 《深入浅出计算机网络》课程 本节主要介绍实现计算机网络需要解决哪些问题&#xff1f;以…

SpringBoot数据访问复习

SpringBoot数据访问复习 数据访问准备 引入jdbc所需要的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId></dependency> 原理分析 导入的启动器引入了两个…

Vue.js设计与实现(霍春阳)

Vue.js设计与实现 (霍春阳) 电子版获取链接&#xff1a;Vue.js设计与实现(霍春阳) 编辑推荐 适读人群 &#xff1a;1.对Vue.js 2/3具有上手经验&#xff0c;且希望进一步理解Vue.js框架设计原理的开发人员&#xff1b; 2.没有使用过Vue.js&#xff0c;但对Vue.js框架设计感兴趣…

CentOS下安装vlc

一、引言 vlc是一跨多媒体播放器&#xff0c;可以播放本地媒体文件和网络串流&#xff0c;帮助我们排查音视频开发过程中遇到的问题。大部分情况下&#xff0c;我们只需要在Windows系统下安装vlc就可以了。但有一种情况是需要在Linux下安装vlc的&#xff1a;我们的音视频拉流软…

Java 数据结构 二叉树(一)二叉查询树

目录 树的种类 二叉树 二叉查找树 满二叉树 ​编辑 完全二叉树 二叉树的数据存储 链式存储 数组存储 寻址方式&#xff1a; 二叉树的遍历&#xff08;了解即可&#xff09; ​编辑 二叉查询树缺点 前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满…

JVM之Java内存区域

JVM-Java内存区域 Java内存区域是Java虚拟机&#xff08;JVM&#xff09;管理的内存资源的逻辑划分&#xff0c;用于存储程序运行时所需的数据。Java内存区域的合理划分和管理对于程序的性能和稳定性具有重要影响。本文将深入探讨Java内存区域的各个部分&#xff0c;包括方法区…

C语言第十八弹---指针(二)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、const修饰指针 1.1、const修饰变量 1.2、const修饰指针变量 2、指针运算 2.1、指针- 整数 2.2、指针-指针 2.3、指针的关系运算 3、野指针 3.1、…

由于误删了node依赖,导致这后面的一系列操作

文章目录 1. 事发原因&#xff1a;Delete select files2. Delete select files引起的cross-env报错3. cross-env是node_modules的依赖工具4. 那么Delete selected files到底是什么操作5. 重装node_modules依赖包&#xff0c;也报错6. 报错&#xff1a;cb() never called!7. 算了…

Transformer 自然语言处理(三)

原文&#xff1a;Natural Language Processing with Transformers 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第八章&#xff1a;使 transformers 在生产中更高效 在之前的章节中&#xff0c;您已经看到了 transformers 如何被微调以在各种任务上产生出色的结果。…

【Mysql】整理

Mysql整理与总结 整理Mysql的基本内容供回顾。 参考&#xff1a; [1]. 掘金.MySQL三大日志(binlog,redolog,undolog)详解 [2]. Javaguide.MySQL三大日志(binlog、redo log和undo log)详解

从一个小故事讲解观察者模式~

定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 什么是观察者模式&#xff1f; 观察者模式在我们的日常生活中极其常见。 先来看看观察者模式的定义&#xff1a; 观察者模式定义了对象之间…

MySQL进阶入门指南

文章目录 MySQL如何定位慢查询找到了这个执行慢的SQL语句&#xff0c;如何分析 MySQL的引擎InnoDBMyISAM 索引什么是索引&#xff1f;索引的优缺点索引底层数据结构索引的分类MySQL超大分页怎么处理 什么是最左匹配原则创建索引的原则索引失效的场景 事务什么是事务&#xff1f…

jsp网格环境变化系统Myeclipse开发mysql数据库web结构java编程计算机网页项目百度地图

一、源码特点 JSP 网格环境变化系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

ChatGPT实战100例 - (15) 还不会写 Stable Diffusion (SD) 绘画提示词?没关系,ChatGPT帮你搞定

文章目录 ChatGPT实战100例 - (15) 还不会写 Stable Diffusion (SD) 绘画提示词&#xff1f;没关系&#xff0c;ChatGPT帮你搞定一、把场景描述转为镜头语言二、把镜头语言转换为Prompt三、把Prompt转换为图片 ChatGPT实战100例 - (15) 还不会写 Stable Diffusion (SD) 绘画提示…

利用jmeter完成简单的压力测试

Jmeter是一个非常好用的压力测试工具。Jmeter用来做轻量级的压力测试&#xff0c;非常合适&#xff0c;只需要十几分钟&#xff0c;就能把压力测试需要的脚本写好。 1、什么是压力测试 顾名思义&#xff1a;压力测试&#xff0c;就是 被测试的系统&#xff0c;在一定的访问压…

C++集群聊天服务器 数据模块+业务模块+CMake构建项目 笔记 (上)

跟着施磊老师做C项目&#xff0c;施磊老师_腾讯课堂 (qq.com) 本文在此篇博客的基础上继续实现数据模块和业务模块代码&#xff1a; C集群聊天服务器 网络模块业务模块CMake构建项目 笔记 &#xff08;上&#xff09;-CSDN博客https://blog.csdn.net/weixin_41987016/article…

Matplotlib箱形图的绘制与高级技巧解析【第57篇—python:Matplotlib箱形图】

文章目录 Matplotlib箱形图的绘制与高级技巧解析1. 水平箱形图2. 带缺口箱形图3. 群组箱形图4. 堆叠箱形图5. 核密度箱形图6. 小提琴箱形图7. 组合箱形图8. 多子图中的炫酷箱形图9. 动态箱形图与交互式可视化9.1 动态箱形图9.2 交互式小提琴箱形图 总结 Matplotlib箱形图的绘制…

爬虫(二)

1.同步获取短视频 1.只要播放地址对Json数据解析&#xff0c;先把列表找出&#xff1a; 2.只想要所有的播放地址&#xff0c;通过列表表达式循环遍历这个列表拿到每个对象&#xff0c;再从一个个对象里面找到Video,再从Video里面找到播放地址(play_addr),再从播放地址找到播放…