这是用微信小程序遥控开门的系列文章,具体微信小程序连接物联网的代码在第三章提及。
微信小程序连接物联网(一):初始化ESP8266 NodeMCU
微信小程序连接物联网(二):NodeMCU Lua学习笔记
微信小程序连接物联网(三):微信小程序远程遥控宿舍开门 基于NodeMCU通过MQTT协议连接阿里云物联网平台
折腾宿舍远程开门的这两周里碰壁不少,还好功能实现了。我想还有很多和我有同一想法的人。所以记录过程下来供参考少走弯路吧。我不是电子专业的学生,且这个Nodemcu也没涉及到底层知识,文中有错误的地方欢迎指正。
收到很多做毕设的小伙伴私信,这里补上完整的项目源码:
https://gitee.com/koevas/IOT-wechat
官网:MQTT.fx
阿里云文档(使用MQTT.fx接入物联网平台):
https://help.aliyun.com/document_detail/140507.html
先在阿里云物联网平台创建一个产品,ProductKey为产品密钥
再创建两个设备
一个是宿舍门的被控端,用在nodemcu连接
一个是宿舍门的控制端,用在微信小程序连接
创建完设备获取到三元组,记录下来
在MQTT.fx上填写连接信息,用{}的变量请换成自己的三元组。
产品密钥:{ProductKey}
设备名字:{DeviceName}
设备密钥:{DeviceSecret}
Profile Name: Class_Door # 在MQTT.fx里的名字,随便写
Profile Type: MQTT Broker # 类型为MQTT代理
Broker Address: {ProductKey}.iot-as-mqtt.cn-shanghai.aliyuncs.com # 设备名 + 域名后缀
Broker Port: 1883 # MQTT协议的连接端口,默认为1883
Client ID: {DeviceName}|securemode=3,signmethod=hmacsha1|
用户名:
User Name: {DeviceName}&{ProductKey}
密码:
密码需经过HmacSHA1算法加密生成,可以用 在线加密解密工具 生成
加密所需的明文为:
clientId{DeviceName}deviceName{DeviceName}productKey{ProductKey}
加密所需的密钥为:
{DeviceSecret}
连接成功后订阅消息,然后在阿里云物联网在线调试上发送一条数据,测试有没有收到。
使用MG90S舵机,可以旋转角度为0-180°,产生的扭矩足够打开宿舍的门锁,电源接到esp8266的Vin、GND口。PWM信号线接到esp8266的D3口。
打开esplorer编写init.lua烧录进esp8266里
连接wifi代码
-- wifi config
station_cfg = {}
station_cfg.ssid = "gzta"
station_cfg.pwd = "12345678"
-- wifi connect
print('Setting up WIFI...')
wifi.setmode(wifi.STATION)
wifi.sta.config(station_cfg)
-- wifi eventmon
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T)
print("wifi connecting...")
end)
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T)
print("wifi connect:" .. wifi.sta.getip())
end)
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T)
print("wifi disconnect...")
end)
控制舵机旋转角度的代码,用于打开门锁,旋转3秒后恢复到原来的位置。
-- Open the door for 3 seconds
function DoorOpen()
pin = 3
-- 20 - 130
pwm.setup(pin, 50, 40)
pwm.start(pin)
print("start move position")
tmr.create():alarm(3000, tmr.ALARM_SINGLE, function()
print("recover position")
pwm.setduty(pin, 120)
pwm.stop(pin)
end)
end
完整代码如下:
更改自己的wifi配置、topic、物联网三元组即可
-- wifi config
station_cfg = {}
station_cfg.ssid = "gzta"
station_cfg.pwd = "12345678"
-- topic
topic0 = "/a1UlGaNjWAO/Class_Door/user/message"
-- aliyun IOT config
ProductKey = "换成自己的"
DeviceName = "换成自己的"
DeviceSecret = "换成自己的"
mqttAddress = ProductKey .. ".iot-as-mqtt.cn-shanghai.aliyuncs.com"
mqttPort = 1883
ClientID = DeviceName .. "|securemode=3,signmethod=hmacsha1|"
UserName = DeviceName .. "&" .. ProductKey
hmac_data = "clientId" .. DeviceName .. "deviceName" .. DeviceName .. "productKey" .. ProductKey
Password = crypto.toHex(crypto.hmac("sha1", hmac_data, DeviceSecret))
-- wifi connect
print('Setting up WIFI...')
wifi.setmode(wifi.STATION)
wifi.sta.config(station_cfg)
-- wifi eventmon
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T)
print("wifi connecting...")
end)
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T)
print("wifi connect:" .. wifi.sta.getip())
end)
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T)
print("wifi disconnect...")
end)
-- aliyun iot connect
MQTT_connect_Flag = 0
myMQTT = mqtt.Client(ClientID, 120, UserName, Password)
Connect_timer = tmr.create()
Connect_timer:alarm(1000, tmr.ALARM_SEMI, function()
if myMQTT ~= nil then
print("MQTT client connect...")
myMQTT:connect(mqttAddress, mqttPort, 0, mqtt_connect, mqtt_disconnect)
end
end)
function mqtt_connect(client)
print("mqtt connect success")
myMQTT = client
MQTT_connect_Flag = 1
client:subscribe(topic0, 0, function(client)
print("subscribe success")
end)
Connect_timer:stop()
end
function mqtt_disconnect(client, reason)
print("mqtt connect Fail:" .. reason)
MQTT_connect_Flag = 0
Connect_timer:start()
end
-- mqtt client offline event
myMQTT:on("offline", function(client)
print("mqtt client offline event")
Connect_timer:start()
end)
-- mqtt client message event
myMQTT:on("message", function(client, topic, data)
print(topic .. ":" .. data)
t = sjson.decode(data)
if t["method"] == "thing.service.DoorOpen" then
DoorOpen()
end
end)
-- Open the door for 3 seconds
function DoorOpen()
pin = 3
-- 20 - 130
pwm.setup(pin, 50, 40)
pwm.start(pin)
print("start move position")
tmr.create():alarm(3000, tmr.ALARM_SINGLE, function()
print("recover position")
pwm.setduty(pin, 120)
pwm.stop(pin)
end)
end
需要用到两个js库,分别是
主要流程:
核心的js代码参考了这篇博客,因为自己的宿舍小程序还有一些登录验证的代码,所以就不放出自己小程序完整的js代码了
var mqtt = require('../../utils/mqtt.min.js')
const crypto = require('../../utils/hex_hmac_sha1.js');
Page({
data: {
},
// 发送开门指令,仅供参考,使用时请修改自己的topic
command_opendoor(){
var connectText = '{ "method": "thing.service.DoorOpen"}'
client.publish('/a1UlGaNaaa/Door_Console/user/message', connectText, function (err){
if (!err) {
console.log('开门指令发送成功!');
}
})
},
onLoad: function () {
this.doConnect()
},
doConnect(){
const deviceConfig = {
productKey: "替换",
deviceName: "替换",
deviceSecret: "替换",
regionId: "cn-shanghai"
};
const options = this.initMqttOptions(deviceConfig);
console.log(options)
//替换productKey为你自己的产品的
const client = mqtt.connect('wxs://productKey.iot-as-mqtt.cn-shanghai.aliyuncs.com',options)
client.on('connect', function () {
console.log('连接服务器成功')
//订阅主题,替换productKey和deviceName(这里的主题可能会不一样,具体请查看后台设备Topic列表或使用自定义主题)
client.subscribe('/productKey/deviceName/get', function (err) {
if (!err) {
console.log('订阅成功!');
}
})
})
//接收消息监听
client.on('message', function (topic, message) {
// message is Buffer
console.log('收到消息:'+message.toString())
//关闭连接 client.end()
})
},
//IoT平台mqtt连接参数初始化
initMqttOptions(deviceConfig) {
const params = {
productKey: deviceConfig.productKey,
deviceName: deviceConfig.deviceName,
timestamp: Date.now(),
clientId: Math.random().toString(36).substr(2),
}
//CONNECT参数
const options = {
keepalive: 60, //60s
clean: true, //cleanSession不保持持久会话
protocolVersion: 4 //MQTT v3.1.1
}
//1.生成clientId,username,password
options.password = this.signHmacSha1(params, deviceConfig.deviceSecret);
options.clientId = `${params.clientId}|securemode=2,signmethod=hmacsha1,timestamp=${params.timestamp}|`;
options.username = `${params.deviceName}&${params.productKey}`;
return options;
},
/*
生成基于HmacSha1的password
参考文档:https://help.aliyun.com/document_detail/73742.html?#h2-url-1
*/
signHmacSha1(params, deviceSecret) {
let keys = Object.keys(params).sort();
// 按字典序排序
keys = keys.sort();
const list = [];
keys.map((key) => {
list.push(`${key}${params[key]}`);
});
const contentStr = list.join('');
return crypto.hex_hmac_sha1(deviceSecret, contentStr);
}
})
由于MQTT协议里同一ClientID只能有一个连接,如果是用同一个ClientID 在不同地方登录,会先把最先登录的ClientID踢下线。所以微信小程序和NodeMCU不能共用一个设备。 既然如此,就将微信小程序和NodeMCU划分为两个不同设备。使用规则转发的方式通信。当微信小程序设备端发布消息时,NodeMCU端也同样能收到消息。
阿里云物联网平台官方文档:
基于规则引擎的M2M设备间通信
云产品流转概述
新建规则查询语句:
将微信小程序端收到的数据同时完整地转发给Nodemcu端
SELECT * FROM "/a1UlGaNaaa/Door_Console/user/message"
远程宿舍开门的功能需求简单,又无其他业务需求,就不需要利用到个人服务器保存什么数据了。所有操作只与阿里云的物联网平台交互