【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水!

时间:2024-03-28 19:23:49

【微信小程序控制硬件①】 全网首发,借助 emq 消息服务器带你如何搭建微信小程序的mqtt服务器,轻松控制智能硬件!
【微信小程序控制硬件②】 开始微信小程序之旅,导入小程序Mqtt客户端源码,实现简单的验证和通讯于服务器!
【微信小程序控制硬件③】 从软件到硬件搭建一个微信小程序控制esp8266的项目,自定义通讯协议,为面试职位和比赛项目加分!
【微信小程序控制硬件④】 深度剖析微信公众号配网 Airkiss 原理与过程,esp8266如何自定义回调参数给微信,实现绑定设备第一步!
【微信小程序控制硬件⑤ 进阶篇 】理清接下来必须走的架构思想,学习下 JavaScript 的观察者模式,在微信小程序多页面同时接收到设备推送事件!
【微信小程序控制硬件⑥ 进阶篇 】服务器如何集成七牛云存储SDK,把用户自定义设备图片存储在第三方服务器!
【微信小程序控制硬件⑦ 进阶篇 】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为自己心里全栈工程师梦想浇水!!



一、前言;


           经过几趟折磨,过年后也就把这个框架弄好了,一直没时间写篇文章分享下!今天有空之余分享下心得!前面说了,要实现一个微信小程序控制自己的硬件(包括esp8266、esp32或者流行的树莓派)都需要一个服务器,而我前面几个分享给大家的是走私有服务器(非微信服务器)

           而前端开发与设备通过Mqtt协议连接的交互信息,无非是通过订阅和发送不同的主题实现控制。
           所以,大多情况下,这种思想是固定的!发送主题,订阅主题!前端拿到设备列表的各个主题去订阅,一个主题就是一个设备!发送的主题就是控制一个设备。所以,我就大胆地设计了这么一个框架,并且分享出来!


【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水!


二、涉及的技术点;


  1. JavaScript观察者模式:因为各个页面发送和接收设备信息和主链接MQTT服务器是一个订阅观察的关系!由此我选用了onFire框架!
  2. weui框架使用:因为有些界面毕竟涉及到了美观,所以我就采用了微信开源的ui框架 ----- weui框架!
  3. MQTT协议的了解:这个协议是必须要熟悉的!MQTT协议已经作为物联网认知的一个协议标准了!
  4. 其他要了解的是微信小程序的基本开发流程,从下载开发工具和后台配置以及对微信小程序工程结构的了解!

【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水!


三、框架的运行原理;


【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水!

  • 再多的文字解释,也不够一个图片的归纳的好。上面是我归纳的;可以看到主要是分为三个模块:

  • 连接服务器主线程:实现与服务器的信息交互!不会直接和各个界面进行联系,只需要和封装库联系!

  • 封装库:作为服务器和用户交互的桥梁!内部封装好 主题订阅、发送消息和接收消息的工作。

  • 用户交互界面:这是一个很广的总结。交互界面有设备控制界面、设备列表界面或者其他分组界面等。但是还是只需要关心你的当前操作设备的订阅主题和发送主题,或者这个设备的图片和设备类型。


四、框架代码流程;


4.1 主线程;


            主线程是与mqtt服务器的连接,这个操作无非肯定是在app.js经行的!
            doConnect()函数涉及了连接、订阅主题以及和本地的封装库的逻辑!


doConnect: function() {
    var that = this;
    if (that.data.client && that.data.client.isConnected()) {
      console.log('不要重复连接');
      return
    }
    var client = new Client(this.globalData.server_domain, this.globalData.clientId());
    client.connect({
      userName: this.globalData.userName,
      password: this.globalData.password,
      useSSL: true,
      reconnect: true, //设置断线重连,默认是断线不重连
      cleanSession: true,
      keepAliveInterval: this.globalData.keepAliveInterval,
      onFailure: function(errorCode) {
        mDeviceClouds.notifyConnectEvent(false)
        //console.log("connect failed code:" + errorCode.errorCode)
        //console.log("connect failed message:" + errorCode.errorMessage)
      },
      onSuccess: function() {
        that.data.client = client
        client.onMessageArrived = function(msg) {
          if (typeof that.data.onMessageArrived === 'function') {
            return that.data.onMessageArrived(msg)
          }
          //  console.log("onMessageArrived topic:" + msg.destinationName)
          //  console.log("onMessageArrived payload:" + msg.payloadString)
          mDeviceClouds.notifyDeviceStatusEvent(msg.destinationName, msg.payloadString)
        }
        client.onConnectionLost = function(responseObject) {
          if (typeof that.data.onConnectionLost === 'function') {
            return that.data.onConnectionLost(responseObject)
          }
          if (responseObject.errorCode !== 0) {
            //console.log("onConnectionLost:" + responseObject.errorMessage);
          }
        }
        //console.log("connect success..")
        //连接成功mqtt服务器回调
        mDeviceClouds.notifyConnectEvent(true)
      }
    });
  },
  onLaunch: function() {

    //延迟链接,以防后面的收不到链接成功回调
    var that = this
    setTimeout(function() {
      that.doConnect();
    }, 800)

    // 订阅某个设备推送状态函数:参数 device对象
    mDeviceClouds.listenSubDeviceTopicEvent(true, function(device) {
      var client = that.data.client;
      if (client && client.isConnected()) {
        //console.log('订阅成功');
        return client.subscribe(device, {
          qos: 1
        });
      }
      //console.log('订阅失败');
    })
    // 发送消息给设备
    mDeviceClouds.listenWriteDeviceEvent(true, function(device, message, qos = 1, retained = false) {
      var client = that.data.client;
      if (client && client.isConnected()) {
        var message = new Message(message);
        message.destinationName = device;
        message.qos = qos;
        message.retained = retained;
        console.log('发送ok');
        return client.send(message);
      }
    })
  },

4.2 获取设备列表显示设备,以及订阅在线的设备;


            问题来了,从哪里获取设备列表,这个可以从自己的服务器,但是我的框架给出的是本地模拟的数据;这个可以根据小伙伴的业务来定了!可以看到下面,我的是很详细的,有设备名字、设备类型、设备图片、已经设备的订阅主题和发送主题!

//模拟数据
var localData = [{
  'alias': '书房灯',
  'type': 'light',
  'img': 'http://qiniu.xuhongv.com/device_list_light.png',
  'online': true,
  'deviceSubTopic': 'light/29e90c3d0/Sub',
  'devicePubTopic': 'light/29e90c3d0/Pub',
}, {
  'alias': '抽风机插座',
  'type': 'outlet',
  'img': 'http://qiniu.xuhongv.com/device_list_outlet.png',
  'online': true,
  'deviceSubTopic': 'outlet/59e90c3d6/Sub',
  'devicePubTopic': 'outlet/59e90c3d6/Pub',
}, {
  'alias': '卧室灯',
  'type': 'outlet',
  'img': 'http://qiniu.xuhongv.com/device_list_light.png',
  'online': false,
  'deviceSubTopic': 'light/m9e90c396/Sub',
  'devicePubTopic': 'outlet/m9e90c396/Pub',
}, ]

            拿到设备列表之后,自己做一个设备列表把!下面是我的设备列表页面代码!

            根据设备列表数据的是否在线而显示不同的颜色!加载不一样的文字显示!

<view class="page">
  <view wx:for="{{cloudsDevices}}" wx:key="this" data-index='{{index}}' wx:for-item='itemObj' 
   bindtap='jumpDeviceControl'>
    <view class='item-deviceList'>

      <!--设备图片-->
      <image src='{{itemObj.img}}' class='deviceIcon'></image>

      <!--如果设备在线设备名字颜色蓝色,否则就灰色-->
      <text wx:if='{{itemObj.online}}' class='deviceName'>{{itemObj.alias}}</text>
      <text wx:else class='deviceName' style='color:#a1afc9'>{{itemObj.alias}}</text>

      <!--如果设备离线则加载不一样的显示状态-->
      <text wx:if='{{itemObj.online}}' class="deviceStatusLine">在线</text>
      <text wx:else class="deviceStatusLine" style='color:#a1afc9'>离线</text>

      <!--如果设备离线则不显示箭头-->
      <image wx:if='{{itemObj.online}}' class='deviceInto' />

    </view>
    <view class='line'></view>
  </view>
  <view class="weui-footer weui-footer_fixed-bottom">
    <view class="weui-footer__links">
      <navigator url="https://github.com/xuhongv" class="weui-footer__link">WeUI+Mqtt+onfire</navigator>
    </view>
    <view class="weui-footer__text">Copyright ©半颗心脏 https://github.com/xuhongv</view>
  </view>

</view>

  • 效果如下:
    【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水!

4.3 点击某设备如何实现携带此设备信息到控制界面;


            这个问题其实原理很简答的!点击某个设备,获取其索引!然后根据这个索引来从设备列表的中查找到这个设备,然后就可以把这个设备的某些信息去跳转到下一个设备了,比如设备的名字和类型,最主要的是设备的订阅主题,因为设备一直在接收此主题的消息。下面是点击一项设备时候触发的方法:

jumpDeviceControl: function(e) {
    //拿到当前点击设备的设备对象
    var device = this.data.devicesCloudsDatas["listData"][e.currentTarget.dataset.index];
    //设备是否在线?如果在线则可以跳转
    if (device.online)
      //判断设备类型
      switch (device.type) {
        case 'light':
         //携带设备订阅主题和名字跳转给下一个界面
          wx.navigateTo({
            url: "../deviceLight/deviceLight?devicePubTopic=" + device.devicePubTopic + "&deviceSubTopic=" + device.deviceSubTopic + "&alias=" + device.alias
          })
          break;
        case 'outlet':
           //携带设备订阅主题和名字跳转给下一个界面
          wx.navigateTo({
            url: "../deviceSocket/deviceSocket?devicePubTopic=" + device.devicePubTopic + "&deviceSubTopic=" + device.deviceSubTopic + "&alias=" + device.alias
          })
          break;
        default:
          break;
      }
  }

4.4 控制界面如何获取上个界面传过来的信息;


            首先你得明白,onLoad()方法里面的参数就是上个界面传来的!因此,我们拿到了参数之后,要向我们的本地的订阅中心订阅发来所有信息,判断是否当前设备的发来的;如下代码:

 callBackDeviceStatus: function (topic, payload) {
    //此处判断服务器的下发主题和此设备的主题是否一致!
    if (this.data.devicePubTopic === topic) {
      console.log("设备状态回调:" + payload);
      //这里处理同步ui工作的代码逻辑处理
    }
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {

    console.log("拿到上个界面传来的设备信息:" + options.devicePubTopic);

    //根据设备名字,设置标题
    wx.setNavigationBarTitle({
      title: options.alias
    })
    this.setData({
      devicePubTopic: options.devicePubTopic,
      deviceSubTopic: options.deviceSubTopic
    })

    //设置监听函数
    mDeviceClouds.listenDeviceStatusEvent(true, this.callBackDeviceStatus);

    //控制设备,此函数任意地方调用!第一个参数是topic,第二个是payload
    //mDeviceClouds.notifyWriteDeviceEvent(options.deviceSubTopic, "hello world , i am from wechat");
  },

            既然我们拿到了设备的订阅主题,那么我们就可以做自己协议来控制了,比如点击开关灯以及控制亮度灯!看我下面的一个按键回调如何把定协议发送给设备:

 //按键触发
  onSwitch: function (e) {
    console.log("onSwitch success :" + e.detail.value);
    //同步数据
    if (e.detail.value)
      this.setData({
        lightValue: 100,
        valuePic: this.data.pathLightOpen
      })
    else this.setData({
      lightValue: 0,
      valuePic: this.data.pathLightOff
    })
    //开始构造json数据
    var jsonObj = new Object();
    jsonObj.change = "power";
    jsonObj.value = "" + e.detail.value + "";
    //开始发布消息
    //mDeviceClouds.notifyWriteDeviceEvent(this.data.deviceSubTopic, JSON.stringify(jsonObj));
  }

五、其他;


5.1 特性

  • 支持断线重连!重新订阅主题!
  • 拓展性强,与具体的通讯协议没有任何耦合!开发者可以根据自己业务来做协议!

5.2 如何修改信息;


  • 修改连接域名等连接信息!
    【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水!

  • 修改模拟的设备列表数据,小伙伴也可以从自己的服务器获取(修改格式时候要对应在index.js做适配):
    【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水!

  • 工程框架结构:
    【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水!


六、下载;