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

news2024/11/25 0:09:15

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

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

前言

本篇博客实现的功能和之前的智能家居系列类似,把ONENET服务器换成了公用的mqtt服务器,在经过实测之后,个人觉得智能家居这种场景还是比较适合使用mqtt协议,仅仅是个人感觉。其实功能这一块我想到一个比较有意思的,就是用雨滴传感器和步进电机来模拟自动晾晒衣服的功能,大家应该能想象出来是啥意思,就是下雨了通过步进电机将晒衣服的架子给缩回来,不下雨了又给推出去,这还蛮有意思的。
前几篇博客:
①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。本设计用微信开发者工具原生框架进行小程序设计,用到了多个不同的文件来设计小程序,其中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来进行编程,使得整个系统的实时性再一次提高。

四、项目获取

关注私聊

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

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

相关文章

海外合规|新加坡推出智慧国2.0计划 设新网络安全与保障机构

智慧国2.0计划&#xff1a;政府将成立新机构杜绝网络伤害和援助受害者。政府将成立新机构&#xff0c;并制定新法令&#xff0c;以杜绝网络伤害行为和为受害者提供更多援助与保障。新加坡总理兼财政部长黄循财星期二&#xff08;10月1日&#xff09;在推介晚宴上&#xff0c;宣…

基于单片机的花色可调跑马灯设计

**基于单片机频率花色可调跑马灯 文章目录 前言概要设计思路 软件设计效果图 程序文章目录 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/A…

51c自动驾驶~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/11466109 #HTCL 超过所有视觉方案&#xff01;HTCL&#xff1a;分层时间上下文问鼎OCC 本文是对ECCV2024接受的文章 HTCL: 的介绍&#xff0c;HTCL在SemanticKITTI基准测试中超过了所有基于相机的方法&#xff0c;甚至在和…

在pycharm中使用PySpark 出现Java gateway process exited before sending its port number.

# 原因是没有下载Java&#xff08;jdk&#xff09; 程序出现下面错误&#xff1a; 解决办法&#xff1a; 1、2、3、先点击“” &#xff0c;添加这一行&#xff0c;点击确定即可。再次之前先判断你电脑上没有jdk&#xff0c;有的话&#xff0c;直接添加&#xff0c;也可以手动…

国庆节快乐前端(HTML+CSS+JavaScript+BootStrap.min.css)

一、效果展示 二、制作缘由 最近&#xff0c;到了国庆节&#xff0c;自己呆在学校当守校人&#xff0c;太无聊了&#xff0c;顺便做一个小demo帮祖国目前庆生&#xff01;&#xff01;&#xff01; 三、项目目录结构 四、准备工作 (1)新建好对应的文件目录 为了方便&#xff…

【超详细】Python、JDK、vscode安装

Python 下载 首先去Python官网下载安装程序Python官网&#xff0c;鼠标悬浮到Download后选择推荐的Python版本(笔者为Windows系统故选择Windows版本安装程序) 之后点击打开文件&#xff0c;或者点击文件的图标打开下载的目录&#xff0c;打开下载好的安装程序 安装 首先勾…

测试-----BUG篇

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 软件测试的生命周期bug的概念描述bugbug的级别bug的生命周期 软件测试的生命周期 软件测试贯穿与软件的整个生命周期&#xff0c;它的具体流程是: 1.需求分析 2.测…

【初阶数据结构】排序——归并排序

目录 前言归并排序归并排序的非递归写法计数排序排序总结 前言 对于常见的排序算法有以下几种&#xff1a; 前面我们已经学习了&#xff1a; 【初阶数据结构】排序——插入排序【初阶数据结构】排序——选择排序【初阶数据结构】排序——交换排序 下面这节我们来看最后一个…

基于SSM的O20兼职系统的设计与实现(源码+定制+文档)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

P1010 [NOIP1998 普及组] 幂次方 Python题解

[NOIP1998 普及组] 幂次方 题目描述 任何一个正整数都可以用 2 2 2 的幂次方表示。例如 137 2 7 2 3 2 0 1372^7 2^3 2^0 137272320。 同时约定次方用括号来表示&#xff0c;即 a b a^b ab 可表示为 a ( b ) a(b) a(b)。 由此可知&#xff0c; 137 137 137 可表示…

光通信——PON系统典型组网

PON系统可同时承载包括Internet上网、VoIP语音、IPTV视频、TDM数据专线、以太网专线、基站回传等业务在内的多种业务&#xff0c;实现全业务承载&#xff0c;此外&#xff0c;还可通过三波方案承载CATV业务&#xff0c;实现“三网融合” 。 CATV&#xff08;Community Antenna…

数字控制系统

目录 两轮自平衡式机器人的控制器设计 TMS320F28069 芯片特点 处理器引脚及功能 首先分析模型来确定控制原理 速度控制器 方向控制器 系统控制框架 智能平衡移动机器人PID控制 两轮自平衡式机器人的控制器设计 由于两轮自平衡式机器人的自不稳定性&#xff0c;控制起来…

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)2.7-2.8

目录 第四门课 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第二周 深度卷积网络&#xff1a;实例探究&#xff08;Deep convolutional models: case studies&#xff09;2.7 Inception 网络&#xff08;Inception network&#xff09;2.8 使 用 开 源 …

C++容器之list基本使用

目录 前言 一、list的介绍&#xff1f; 二、使用 1.list的构造 2.list iterator的使用 3.list capacity &#x1f947; empty &#x1f947;size 4.list element access &#x1f947; front &#x1f947; back 5.list modifiers &#x1f947; push_front &#x1f947; po…

Apache POI 2024/10/2

导入Apache POI的maven坐标 通过POI向Excel文件写入文件内容 package com.sky.test;import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.File; import java.…

在Linux系统安装Nginx

注意&#xff1a;Nginx端口号是80(云服务器要放行) 我的是基于yum源安装 安装yum源(下面这4步就好了) YUM源 1、将源文件备份 cd /etc/yum.repos.d/ && mkdir backup && mv *repo backup/ 2、下载阿里源文件 curl -o /etc/yum.repos.d/CentOS-Base.repo ht…

java OOP基础:类与对象(万字长文)

目录 类与对象 自定义类 对象的内存模型 “”赋值 对象引用&#xff1a;this 作为常量的对象变量:final 判等操作 1. “重写&#xff08;override&#xff09;”基类的equals()方法 2. “重载&#xff08;overload&#xff09;”equals()方法 对象的构造 有参构造与无…

腾讯云轻量服务器+宝塔面板+基于springboot的web网页部署

经历了一段时间的折磨&#xff0c;近期也在看数据挖掘&#xff0c;还有最优化算法&#xff0c;现在基于我上一篇的的问题上&#xff0c;现在你的情况是不是&#xff1a;本地已经存在一个Springboot的项目&#xff0c;在本地能够良好运行。现在你要做的是把自己的项目部署到网上…

B树、B+树

前言 B树和B树都是平衡的多路搜索树&#xff0c;它们在数据库和文件系统中广泛使用&#xff0c;用于存储和检索数据。B是指balance&#xff0c;也就是平衡的意思。那这俩与平衡二叉树有啥区别&#xff1f;首先要知道AVL树与B树、B树他们都是自平衡搜索树。 ALV的子树间高度不会…

知识图谱入门——4:Protégé 5.6.4安装和主要功能介绍、常用插件(2024年10月2日):知识图谱构建的利器

Protg 是斯坦福大学开发的一款开放源代码的本体编辑工具。它为构建、共享和管理本体&#xff08;Ontologies&#xff09;提供了一个强大的平台&#xff0c;广泛应用于语义网、知识管理、自然语言处理等领域&#xff0c;特别是知识图谱的开发和管理。Protg 支持 OWL&#xff08;…