去年五月左右发布过这样一篇博客:

由于当时时间非常紧,加上写完文章验收完成果后就去忙别的,导致留下了一堆问题。最近刚好又有这方面的需求(指能够实现公网远程控制床头灯开关状态,目前仅支持局域网+网页控制),遂作此文记录过程。

目前的床上智能家居控制页面

材料

(好耶!是以前从来都不写的材料清单)

ESP8266开发板一块

ESP8266开发板图示

3v继电器若干

3v继电器图示

公对母杜邦线若干

公对母杜邦线图示

可传输数据的安卓线一条

可传输数据的安卓线图示

LED灯若干

LED灯图示

配比:一般一块ESP8266带2个继电器的情况下至少需要6根公对母杜邦线和2个LED灯(其实是通用教程,继电器的用电器端可以接任何用电器。举个例子,可以拆一个普通插排,将火线剪开分别正确接入继电器的用电器端即可实现普通插排智能化)(继电器用电器端支持10A 250VAC接入)(但不推荐,毕竟是高压电,没有做好保护的话容易凉凉)

搭建EMQX服务端

此处不再赘述,详见上一篇文章,除版本更新之外几乎没有任何差别。

此处贴出下载地址:点击直达

补充:可以使用反向代理功能实现域名访问及https,Docker部署好后只需要反向代理http://127.0.0.1:18083即可。具体见图

反向代理配置图示

EMQX后台配置

打开浏览器,输入配置好的EMQX域名。默认用户名为admin,密码为public。首次登录会强制要求修改密码。

现在最新版EMQX已经更新到了5.0.20版本,界面相较之前EMQX 4可以说好看了许多

EMQX后台图示

(但由于前面就没怎么熟悉上一版,外加界面大改版,刚装好时候就跟全新上手一样困难,抱着官方文档研究了好一会)

我们首先点击左下角露出左侧图标对应的选项名称,然后依次点击访问控制 - 认证,如图所示

认证页面图示

点击右上角的创建,新建一个认证方式

认证方式选择Password-Based,选好后点击下一步

认证方式图示,此处选择Password-Based

此处数据源选择Built-in Database,选择其它数据库需要填写对应数据库信息

配置参数保持默认即可。点击创建完成创建

配置参数图示

完成添加后如图

点击用户管理 - 添加,输入用户名和密码分别添加一个普通用户和一个超级用户,如图所示

添加用户图示1,超级用户需勾选“是否为超级用户”选项
添加用户图示2,此时分别添加了一个普通用户和一个超级用户

注:此处添加的普通用户为ESP8266开发板认证使用,超级用户为使用EMQX连接调试开发板使用。

到此后台暂时配置完成,下面进入编译器安装、接线和代码部分。

Arduino的安装和配置

不再赘述,参考上一篇文章。

注意:截至本文创作之时,Arduino最新版已经更新至2.0.4,本文使用的Arduino为1.8.19,进入官网往下滑动即可找到。

或者直接点击直达

接线部分

由于我已经接好线路并使用近一个月,此处接线图以绘制图代替。

(Visio还挺好用的)

开发板代码

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// GPIO 5 D1
#define LED1 D0
#define LED2 D1
#define LED_ LED_BUILTIN

// WiFi
const char *ssid = "WiFi名称,仅支持2.4GHz WiFi"; // Enter your WiFi name
const char *password = "WiFi密码";  // Enter WiFi password

// MQTT Broker
const char *mqtt_broker = "EMQX服务器IP地址/域名";
const char *topic = "esp8266_bedled1";  // 此处可以保持不动,或者自己起一个别的名字
const char *mqtt_username = "普通用户用户名";
const char *mqtt_password = "普通用户密码";
const int mqtt_port = 1883;

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
    // Set software serial baud to 115200;
    Serial.begin(115200);
    // connecting to a WiFi network
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi..");
    }
    Serial.println("Connected to the WiFi network");
    //connecting to a mqtt broker
    client.setServer(mqtt_broker, mqtt_port);
    client.setCallback(callback);
    while (!client.connected()) {
        String client_id = "esp8266-client-";
        client_id += String(WiFi.macAddress());
        Serial.printf("The client %s connects to the public mqtt broker\n", client_id.c_str());
        if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
            Serial.println("Public emqx mqtt broker connected");
        } else {
            Serial.print("failed with state ");
            Serial.print(client.state());
            delay(2000);
        }
    }
    // publish and subscribe
    client.publish(topic, "hello emqx");
    client.subscribe(topic);
}

void callback(char *topic, byte *payload, unsigned int length) {
    Serial.print("Message arrived in topic: ");
    Serial.println(topic);
    Serial.print("Message:");
    String message;
    for (int i = 0; i < length; i++) {
        message = message + (char) payload[i];  // convert *byte to string
    }
    Serial.print(message);
    if (message == "打开床头灯") { digitalWrite(LED1, HIGH); }   // LED on
    if (message == "关闭床头灯") { digitalWrite(LED1, LOW); } // LED off
    if (message == "打开床尾灯") { digitalWrite(LED2, HIGH); } // LED off
    if (message == "关闭床尾灯") { digitalWrite(LED2, LOW); } // LED off
    Serial.println();
    Serial.println("-----------------------");
}

void loop() {
    client.loop();
}

将代码贴入Arduino,修改好前面的WiFi信息和EMQX信息(代码10 – 17行)后,点击左上角的上传按钮开始编译烧录,按钮位置如图

上传按钮图示,图中第二个向右的箭头即是上传按钮

至此我们已经可以通过EMQX来控制灯的开关了,只需在对应Topic中输入“打开/关闭床头/床尾灯”即可。但我们不能满足于此(毕竟从局域网+网页控制变成广域网+固定APP发送信息控制不能说毫无提升只能说降级行为),下面将介绍如何接入微信小程序。

微信小程序接入

点击此处下载安装微信开发者工具

点击此处申请微信小程序

EMQX后台 -用户管理 - 添加,添加一个普通用户用于微信小程序连接EMQX服务器

使用微信开发者工具新建一个微信小程序,不使用云开发,模板可选择JavaScript - 基础模板

点击此处下载MQTT.js,下载好后放入utils文件夹,文件名应为mqtt.min.js

mqtt.min.js

展开pages文件夹,对应文件名贴入如下代码

index/index.wxml:

<view class="container">
  <button type="primary" bindtap="Led1_status_convert" style="margin: 10px;">{{ Led1_status }}床头灯</button>
  <button type="primary" bindtap="Led2_status_convert" style="margin: 10px;">{{ Led2_status }}床尾灯</button>
</view>

index/index.js:

import mqtt from "../../utils/mqtt.min.js";

Page({
  data: {
    client: null,
    conenctBtnText: "连接",
    host: "EMQX服务器IP地址/域名",
    subTopic: "esp8266_bedled1",
    pubTopic: "esp8266_bedled1",
    Led1_status: "打开",
    Led2_status: "打开",
    mqttOptions: {
      username: "刚刚开的用于微信小程序的普通用户名",
      password: "密码",
      reconnectPeriod: 1000, // 1000毫秒,设置为 0 禁用自动重连,两次重新连接之间的间隔时间
      connectTimeout: 30 * 1000, // 30秒,连接超时时间
      // 更多参数请参阅 MQTT.js 官网文档:https://github.com/mqttjs/MQTT.js#mqttclientstreambuilder-options
      // 更多 EMQ 相关 MQTT 使用教程可在 EMQ 官方博客中进行搜索:https://www.emqx.com/zh/blog
    },
  },

  onLoad: function () {
    // MQTT-WebSocket 统一使用 /path 作为连接路径,连接时需指明,但在 EMQX Cloud 部署上使用的路径为 /mqtt
    // 因此不要忘了带上这个 /mqtt !!!
    // 微信小程序中需要将 wss 协议写为 wxs,且由于微信小程序出于安全限制,不支持 ws 协议
    try {
      this.setValue("conenctBtnText", "连接中...");
      const clientId = new Date().getTime();
      this.data.client = mqtt.connect(`wxs://${this.data.host}:8084/mqtt`, {
        ...this.data.mqttOptions,
        clientId,
      });

      this.data.client.on("connect", () => {
        wx.showToast({
          title: "连接成功",
        });
        this.setValue("conenctBtnText", "连接成功");

        this.data.client.on("error", (error) => {
          this.setValue("conenctBtnText", "连接");
          console.log("onError", error);
        });

        this.data.client.on("reconnect", () => {
          this.setValue("conenctBtnText", "连接");
          console.log("reconnecting...");
        });

        this.data.client.on("offline", () => {
          this.setValue("conenctBtnText", "连接");
          console.log("onOffline");
        });
        // 更多 MQTT.js 相关 API 请参阅 https://github.com/mqttjs/MQTT.js#api
      });

      if (this.data.client) {
        this.data.client.subscribe(this.data.subTopic);
        return;
      }
    } catch (error) {
      this.setValue("conenctBtnText", "连接");
      console.log("mqtt.connect error", error);
      wx.showToast({
        title: "连接失败,请检查相关配置!",
      });
    }
  },

  setValue(key, value) {
    this.setData({
      [key]: value,
    });
  },

  Led1_status_convert() {
    if (this.data.client) {
      if (this.data.Led1_status == "打开") {
        this.data.client.publish(this.data.pubTopic, "打开床头灯");
        this.setData({
          "Led1_status": "关闭",
        })
      } else {
        this.data.client.publish(this.data.pubTopic, "关闭床头灯");
        this.setData({
          "Led1_status": "打开",
        })
      }
      return;
    }
    wx.showToast({
      title: "会话已过期,请点击右上角重新进入小程序。",
      icon: "error",
    });
  },

  Led2_status_convert() {
    if (this.data.client) {
      if (this.data.Led2_status == "打开") {
        this.data.client.publish(this.data.pubTopic, "打开床尾灯");
        this.setData({
          "Led2_status": "关闭",
        })
      } else {
        this.data.client.publish(this.data.pubTopic, "关闭床尾灯");
        this.setData({
          "Led2_status": "打开",
        })
      }
      return;
    }
    wx.showToast({
      title: "会话已过期,请点击右上角重新进入小程序。",
      icon: "error",
    });
  },
});

注:

  1. 需要自己修改对应参数(index/index/.js中7 – 9、13、14行)
  2. 需要去小程序后台 - 开发 - 开发管理 - 开发设置 - 服务器域名中添加域名为合法域名,或者在微信开发工具右上角详情 - 本地设置中勾选“不校验合法域名”选项
页面样式、文件结构如图

至此就已经完成开发板、微信小程序通过EMQX连接的过程。后面(可能如果)研究Android开发就把代码更新在这(或者再开一贴)

230320:后面又经过多方调试发现微信小程序如果要发布一个测试版需要使用wss访问(类似https,需要证书)。而默认的证书无法对应当前使用的域名。故特此更新记录更换域名教程。以宝塔面板为例,登录宝塔面板后台,点击左侧的Docker选项,找到emqx的容器,选择目录,会自动跳转到当前容器的文件目录。依次进入opt - emqx - etc - certs,新建一个文件夹用于存放我们自己的域名证书,如图所示

新建文件夹图示,此处新建了一个名为”my”的文件夹用于存放自己的证书文件

进入刚刚新建的文件夹,将自己用于访问emqx后台的域名的SSL证书文件(*.pem、*.key)上传至文件夹中,如图所示

对应的自己域名的证书文件图示

返回到前面的etc目录,双击打开emqx.conf文件,找到如下代码段(大约在51行附近):

listeners.wss.default {
  bind = "0.0.0.0:8084"
  max_connections = 512000
  websocket.mqtt_path = "/mqtt"
  ssl_options {
    keyfile = "etc/certs/key.pem"
    certfile = "etc/certs/cert.pem"
    cacertfile = "etc/certs/cacert.pem"
  }
}

将第6、7行分别替换为自己存放证书的位置,此处贴上我自己改好的样例:

listeners.wss.default {
  bind = "0.0.0.0:8084"
  max_connections = 512000
  websocket.mqtt_path = "/mqtt"
  ssl_options {
    keyfile = "etc/certs/my/privkey.key"
    certfile = "etc/certs/my/fullchain.pem"
    cacertfile = "etc/certs/cacert.pem"
  }
}

改好后保存重启容器即可。此时发布的测试版微信小程序便可以正常使用啦~

参考

ESP8266 + MQTT :如何实现 LED 灯的远程控制 | EMQ (emqx.com)

在微信小程序中打造 MQTT 连接测试工具 | EMQ (emqx.com)

说点什么
你是我一生只会遇见一次的惊喜...
支持Markdown语法
在"微信小程序 + EMQX打造个性化智能家居控制平台"已有2条评论
Loading...