陈拓 [email protected] 2019.10.6/2019.10.11
在自己写MQTT模拟器之前先从网上安装一个现成的体验一下,这可以先看看我之前写的文章《微信小程序MQTT模拟器阿里云物联网平台测试》,在下面的网址可以找到这篇文章:
https://mp.csdn.net/postedit/102216865
https://zhuanlan.zhihu.com/p/84810734
下面我们自己写一个MQTT模拟器实现这些功能。
填写你的AppID,新建。
在默认情况下,项目路径为C:\Users\Administrator\WeChatProjects。
放在pages下的images目录中。
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "我的MQTT模拟器",
"navigationBarTextStyle": "black"
},
"sitemapLocation": "sitemap.json"
}
只改“我的MQTT模拟器”这里。
https://github.com/mqttjs/MQTT.js
或者下载mqtt.min.js
https://unpkg.com/[email protected]/dist/mqtt.min.js
mqtt.min.js小一些。
https://github.com/xihu-fm/aliyun-iot-client-sdk/tree/master/lib
两个js文件都放在utils目录下:
<view>
<view class="main-center">
<image src="{{imageUrl}}" class="ledinfo-avatar">image>
<view class="ledinfo-values">
<text>湿度:text><text>{{humidity}}text><text>%text>
<text>温度:text><text>{{temperature}}text><text>℃text>
view>
view>
<text class='subheading'>设备身份三元组text>
<view style='margin-top: 20rpx;'>
<view class='connect-info background-white'>
<text class='text'>productKey:text>
<input class='input' name='productKey' placeholder='替换'
bindinput='productKeyInput'/>
view>
<view class='connect-info background-white'>
<text class='text'>deviceName:text>
<input class='input' name='deviceName' placeholder='替换'
bindinput='deviceNameInput'/>
view>
<view class='connect-info background-white'>
<text class='text'>deviceSecret:text>
<input class='input' name='deviceSecret' placeholder='替换'
bindinput='deviceSecretInput'/>
view>
view>
<view class="buttons">
<view class="button-container" bindtap='online'>
<text class="button">设备上线text>
view>
<view class="button-container" bindtap='publish'>
<text class="button">上报数据text>
view>
<view class="button-container" bindtap='event'>
<text class="button">告 警text>
view>
<view class="button-container" bindtap='service'>
<text class="button">订阅主题text>
view>
<view class="button-container" bindtap='offline'>
<text class="button">设备下线text>
view>
view>
<text class='subheading'>设备日志text>
<view style='margin-top: 20rpx;'>
<view class='deviceState background-white'>
<text class='text'>{{deviceState}}text>
view>
view>
<view class='devicelog'>
<text>{{deviceLog}}text>
view>
view>
/**index.wxss**/
page {
background-color: rgb(240, 240, 240);
font-size: 26rpx;
}
.main-center {
display: flex;
flex-direction: column;
align-items: center;
}
.connect-info {
display: flex;
flex-direction: row;
margin-top: 1rpx;
height: 60rpx;
}
.background-white {
background-color: #FFF;
}
.buttons {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items:center;
margin-left: 10rpx;
margin-right: 10rpx;
margin-top: 30rpx;
margin-bottom: 30rpx;
}
.button {
line-height: 60rpx;
}
.button-container {
margin-top: 0px;
border: 1px solid #aaa;
width: 140rpx;
height: 60rpx;
border-radius: 5px;
text-align: center;
}
.ledinfo-values {
color: #92ADF0;
margin: 20rpx;
}
.ledinfo-avatar {
width: 128rpx;
height: 128rpx;
margin: 20rpx;
}
.subheading {
color: rgb(128, 128, 128);
margin: 20rpx;
}
.text {
width: 220rpx;
height: 30rpx;
margin-left: 20rpx;
margin-top: 10rpx;
}
.input {
width: 100%;
height: 30rpx;
margin-left: 20rpx;
margin-top: 10rpx;
color: rgb(128, 128, 128);
}
.deviceState {
color: #1d953f;
}
.devicelog {
margin-top: 20rpx;
word-break:break-all;
color: #426ab3;
}
//index.js
// 设备身份三元组+区域
const deviceConfig = {
productKey: "替换",
deviceName: "替换",
deviceSecret: "替换",
regionId: "cn-shanghai"
};
function getPostData() {
const payloadJson = {
id: Date.now(),
params: {
temperature: Math.floor((Math.random() * 20) + 10),
humidity: Math.floor((Math.random() * 20) + 60)
},
method: "thing.event.property.post"
}
return JSON.stringify(payloadJson);
}
function getAlarmPostData() {
const payloadJson = {
id: Date.now(),
params: {
temperature: Math.floor((Math.random() * 20) + 10)
},
method: "thing.event.hotAlarm.post"
}
return JSON.stringify(payloadJson);
}
const util = require('../../utils/util.js')
var mqtt = require('../../utils/mqtt.min.js')
const crypto = require('../../utils/hex_hmac_sha1.js')
var client
Page({
data: {
temperature: '0',
humidity: '0',
imageUrl: '../images/iLED1.png',
deviceLog: '',
deviceState: ''
},
// 设备身份三元组输入框事件处理函数
productKeyInput: function (e) {
deviceConfig.productKey = e.detail.value
},
deviceNameInput: function (e) {
deviceConfig.deviceName = e.detail.value
},
deviceSecretInput: function (e) {
deviceConfig.deviceSecret = e.detail.value
},
// 设备上线 按钮点击事件
online: function (e) {
this.doConnect()
},
doConnect() {
var that = this;
const options = this.initMqttOptions(deviceConfig);
console.log(options)
client = mqtt.connect('wxs://productKey.iot-as-mqtt.cn-shanghai.aliyuncs.com', options)
client.on('connect', function () {
console.log('连接服务器成功')
let dateTime = util.formatTime(new Date());
that.setData({
deviceState: dateTime + ' Connect Success!'
})
})
},
//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);
},
// 上报数据 按钮点击事件
publish: function (e) {
var that = this;
let topic = `/sys/${deviceConfig.productKey}/${deviceConfig.deviceName}/thing/event/property/post`;
// 注意用`符号,不是' !!!!!
let JSONdata = getPostData()
console.log("===postData\n topic=" + topic)
console.log("payload=" + JSONdata)
client.publish(topic, JSONdata)
that.setData({
deviceLog: 'topic=' + topic + '\n' + 'payload=' + JSONdata
})
},
// 告警 按钮点击事件
event: function (e) {
var that = this;
let topic_alarm = `/sys/${deviceConfig.productKey}/${deviceConfig.deviceName}/thing/event/hotAlarm/post`;
let JSONdata = getAlarmPostData()
console.log("===postData\n topic=" + topic_alarm)
console.log("payload=" + JSONdata)
client.publish(topic_alarm, JSONdata)
that.setData({
deviceLog: 'topic=' + topic_alarm + '\n' + 'payload=' + JSONdata
})
},
// 订阅主题 按钮点击事件
service: function (e) {
var that = this;
that.setData({
deviceLog: '接收消息监听'
})
//接收消息监听
let topic = `/sys/${deviceConfig.productKey}/${deviceConfig.deviceName}/thing/event/property/post`;
client.on('message', function (topic, message) {
// message is Buffer
let messageStr = message.toString()
console.log('收到消息:' + messageStr)
that.setData({
deviceLog: '收到消息:\n' + messageStr
})
if (messageStr.indexOf('on') > 0) {
that.setData({
imageUrl: '../images/iLED2.png',
})
}
if (messageStr.indexOf('off') > 0) {
that.setData({
imageUrl: '../images/iLED1.png',
})
}
if (messageStr.indexOf('blue') > 0) {
that.setData({
imageUrl: '../images/iLED3.png',
})
}
if (messageStr.indexOf('green') > 0) {
that.setData({
imageUrl: '../images/iLED4.png',
})
}
})
},
// 设备下线 按钮点击事件
offline: function () {
var that = this;
client.end() // 关闭连接
console.log('服务器连接断开')
let dateTime = util.formatTime(new Date());
that.setData({
deviceState: dateTime + ' Disconnect!'
})
},
onLoad: function () {
var that = this
setInterval(function () {
that.setData({
temperature: Math.floor((Math.random() * 20) + 10),
humidity: Math.floor((Math.random() * 20) + 60)
})
}, 3000)
},
})
注册阿里云账号,获得三元组:PublicKey、DeviceName、DeviceSecret。
见《微信小程序MQTT模拟器阿里云物联网平台测试》一文。
https://mp.csdn.net/postedit/102216865
https://zhuanlan.zhihu.com/p/84810734
在微信开发者工具中打开模拟器。
点击F5刷新设备列表,可以看到设备状态已经是在线,查看小程序设备日志和设备列表页面中的最后上线时间,用模拟器测试慢一秒,用真机测试时间一致。
如果连接参数不正确,或遇到其他连接问题,可以在开发工具的Console中查看,例如productKey输入错误:
在设备上线时,点击“上报数据”,我们看到MQTT模拟器上报了当前湿度温度值。
上报的湿度73%,温度29℃。
和MQTT模拟器上报的数据一致。
在设备上线时,点击“告警”,就会生成一条事件告警,并上报当前的温度。
可以看到“温度过高报警”。
在实际情况下,报警温度的阈值在客户端设定,当温度超过阈值时发送报警,这里只是演示报警功能,不用在意温度值的大小。
等待接收消息。
1) 选择设备
2) 选择“调试真实设备”选项卡
3) 选择“服务调用”
4) 选中功能“开灯(switch)”
5) 输入参数{"status":"on"}
6) 点击“发送指令”
查看实时日志:
看,灯亮了!
还可以在物联网平台上:
1) 发送{"status":"off"},关灯
2) 发送{"status":"blue"},灯发蓝光
3) 发送{"status":" green"},灯发绿光
见《微信小程序MQTT模拟器阿里云物联网平台测试》一文。
https://mp.csdn.net/postedit/102216865
https://zhuanlan.zhihu.com/p/84810734
https://github.com/chentuo2000/MyMQTTsimulator