尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

uniapp + Vue2 + OneNET:MQTT接入与跨端打包完整实践

uniapp + Vue2 + OneNET:MQTT接入与跨端打包完整实践 简介面向物联网应用开发的完整实战示例基于uniappVue2框架接入中国移动OneNet平台帮助开发者解决跨平台App与设备云端通信的关键难题适用于课程设计、毕业设计或自学入门。资源包共103个文件压缩后48.34MB内容涵盖Vue页面与业务逻辑的js、vue源码用于界面展示的png、svg素材可安装的apk以及json、scss等配置文件覆盖从界面搭建到设备数据互通的完整链路便于直接运行、对照学习与二次开发。已有452人学习。除了可安装的安卓包还附有前端工程代码、证书数据及辅助文件可对照代码逐步理解设备接入流程、实时数据上报与拉取、消息推送、状态管理、异常处理和通信安全性等物联网应用中的常见环节完整展现了uniapp跨端开发与OneNet平台融合时的项目组织方式适合刚入门uniapp或打算将业务快速接入OneNet的开发者下载参考。 最近在给一套自制的温湿度传感器做配套App硬件端已经用OneNET上报数据跑了好几天结果卡在了移动端这步。网上搜“uniappvue2OneNET”要么是只讲能连上的Demo要么是后台截图配一句“看我做出来了”真正把MQTT连接细节、token计算、跨端打包的坑讲透的文章太少。这篇就把我从HBuilderX建项目到App上架前的完整流程掰开讲一遍适合正在用OneNET做物联网项目、想用uniapp快速出App或者小程序端的人参考。1. 为什么这个组合能省掉一半工作量先说结论这套组合不是技术最前沿的但它在物联网App这个场景里几乎是投入产出比最高的选择。我自己对比过几个方案如果你也卡在“硬件能上云手机端没人写”的阶段看完这章就能少走弯路。1.1 硬件端已经上车OneNET移动端就别换道了设备数据已经通过OneNET往上传了移动端继续选OneNET是最顺的路。OneNET是中国移动的物联网开放平台支持MQTT、HTTP、CoAP多种设备接入协议设备端SDK很成熟论坛和文档资料也攒了不少年。移动端要干的事其实就是“以合法身份订阅设备数据”OneNET的MQTT接口把这件事封得很干净你只需要按规则生成token、组topic剩下的长连接、心跳、消息推拉都由MQTT协议本身解决。反过来想移动端如果选了和OneNET没有直接打通的第三方云就得自己写设备数据转发层或者用HTTP API轮询。数据量小的时候看不出问题一旦设备数量上来轮询延迟、请求频率限制、服务器成本全来了。所以只要硬件端定了OneNET移动端就没有换平台的必要。1.2 uniappvue2的边界在哪里uniapp的卖点是一套代码编译到iOS、Android、微信小程序、H5。物联网App恰恰最需要这种跨端能力你自己调试用App给客户演示时可能得用小程序后台管理页面顺手做成H5如果每个端都单独写一套维护成本直接翻倍。vue2在2023年之后看着确实旧了但这个项目选vue2有明确的现实原因团队里最熟的版本就是vue2项目用到的组件库、日期插件、图表库基本都是基于vue2的改成vue3意味着这些生态要重新验证。OneNET接入本身不挑框架Vue2在这条链路里足够稳定。不是vue3不好而是“技术选型最贵的是切换成本”你把MQTT长连接这种核心逻辑封装好之后框架差异的影响真的不大。2. OneNET设备接入参数动手之前先理清这三样接入OneNET的第一步不是写代码是去控制台把产品、设备、数据流这三样概念理清楚然后把参数拿到手。我见过不少人在这一步把ProductID和DeviceName搞混后面调一整天“为什么连不上”。2.1 产品ID、设备名、设备密钥哪个对应哪个在OneNET控制台创建产品后你会得到一个ProductID这是产品级别的唯一标识相当于你App的“应用ID”。产品下面再创建设备每个设备有DeviceName和DeviceSecret。DeviceName在产品内唯一DeviceSecret是设备的“密码”后面生成MQTT签名令牌时要用它做HMAC-SHA1的密钥。创建产品的时候OneNET会让你选接入协议和数据加密方式项目里选MQTT协议、明文传输就行。设备名称建议用有意义且不含中文的名字比如dev_temp_001。原因很简单MQTT的clientId用的是设备名称某些物联网设备TCP栈对中文clientId处理并不友好直接避开能省很多通信层的奇奇怪怪问题。提示同一个产品下可以建多个设备。你做了几十个同型号硬件不需要重复建产品每个硬件对应一个设备即可ProductID相同DeviceName和DeviceSecret不同。2.2 数据流和topic的对应关系OneNET的MQTT接入里topic规则第一眼有点绕但记住一条主线就够了设备上报数据走“dp/post”相关主题平台下发命令走“cmd/request”相关主题。项目里的温度、湿度这类数据在OneNET里叫“数据流”你在控制台创建数据流时可以只定义名称也可以指定数据类型。设备上报数据之后平台会通过$sys/{productId}/{deviceName}/dp/post/json/accepted返回成功状态通过rejected返回失败原因。调试的时候一定要把这两个响应主题都订阅上数据没上去到底是因为token过期、JSON格式不对还是数据流不存在rejected报文的payload里都写得很清楚。3. MQTT连接的核心代码token签名和报文细节这一节是整个项目里最容易翻车的地方。OneNET的MQTT接入不是简单把用户名密码填进去它的password字段要放一个用设备密钥生成的签名令牌这个令牌有时效性。很多新手第一次连接失败不是网络不通就是令牌字符串没拼对。3.1 签名算法res、et、sign分别解决什么问题OneNET采用的是签名版token鉴权生成规则不复杂但每个字段的用途得清楚res访问资源标识格式是products/{产品ID}/devices/{设备名称}告诉OneNET你想访问哪个设备。et过期时间Unix时间戳。一般取当前时间加上86400秒过期后需要重新生成。sign用HMAC-SHA1算法对res \n et做签名密钥就是设备的DeviceSecret。version固定值2018-10-31代表签名协议版本。最后拼成一个查询字符串放在MQTT的password字段里。代码实现如下// utils/oneNetToken.js import CryptoJS from crypto-js export function generateToken(productId, deviceName, deviceSecret, expire 86400) { const res products/${productId}/devices/${deviceName} const et Math.round(Date.now() / 1000) expire const str ${res}\n${et} const sign CryptoJS.HmacSHA1(str, deviceSecret).toString(CryptoJS.enc.Base64) return version2018-10-31res${encodeURIComponent(res)}et${et}methodsha1sign${encodeURIComponent(sign)} }这个token建议每次连接前都重新生成一次不要复用旧值。我在实际开发中踩过坑把token写死在本地配置里第二天打开App连接一直失败折腾半天才反应过来是et过期了。另外如果你硬件端也用OneNET接入可以复用同一套签名逻辑只是换一门语言实现而已。3.2 mqtt.js连接参数怎么填uniapp里引入mqtt.js很直接先装依赖npm install mqtt --save然后写一个连接模块。注意App端用的是ws://或wss://协议头微信小程序端要用wxs://协议头用错的话连接会一直停在connecting状态不报错也不成功非常迷惑。// utils/mqtt.js import mqtt from mqtt import { generateToken } from ./oneNetToken let client null export function connectOneNet({ productId, deviceName, deviceSecret }) { const token generateToken(productId, deviceName, deviceSecret) const url wss://183.230.40.39:8084/mqtt // 以OneNET控制台实际接入点为准 client mqtt.connect(url, { clientId: deviceName, username: productId, password: token, clean: false, reconnectPeriod: 5000, connectTimeout: 10000, protocolVersion: 4 }) client.on(connect, () { console.log(MQTT连接成功收到CONNACK) }) client.on(reconnect, () { console.log(正在断线重连) }) client.on(error, (err) { console.log(MQTT错误, err) }) return client }clean: false这个参数值得说一句。clean session设为false表示断开重连后保留服务端的会话状态这样订阅关系不会丢。如果你的App会频繁切后台、切网络建议保持false能减少重连后的重新订阅操作。注意clientId填DeviceNameusername填ProductIDpassword填签名token。这三个字段填错任何一个OneNET都会在握手阶段直接断开连接控制台可能只看到一次“连接异常”根本定位不到具体是哪个字段错了。3.3 订阅/发布topic的报文长什么样连接建立后订阅方向和发布方向各有一组核心topic。我整理成表格方便对照方向主题格式说明设备上报数据$sys/{pid}/{deviceName}/dp/post/json向平台发布数据点JSON平台响应$sys/{pid}/{deviceName}/dp/post/json/accepted上报成功后平台的回执平台响应$sys/{pid}/{deviceName}/dp/post/json/rejected上报失败payload里带原因命令下发$sys/{pid}/{deviceName}/cmd/request/{cmdId}订阅后接收平台下发的指令命令应答$sys/{pid}/{deviceName}/cmd/response/{cmdId}设备执行完指令后回复结果订阅代码const pid 你的产品ID const deviceName 你的设备名称 client.subscribe($sys/${pid}/${deviceName}/dp/post/json/accepted) client.subscribe($sys/${pid}/${deviceName}/dp/post/json/rejected) client.subscribe($sys/${pid}/${deviceName}/cmd/request/) client.on(message, (topic, payload) { console.log(收到消息主题, topic) console.log(收到消息内容, payload.toString()) })发布数据点的payload格式是OneNET特有的{ datastreams: [ { id: temperature, datapoints: [ { value: 26.5 } ] }, { id: humidity, datapoints: [ { value: 60 } ] } ] }即使你只上报一个值datastreams也要用数组包一层datapoints同样要包数组。这是OneNET的固定格式少一层或多一层都会收到rejected回执。4. 页面和数据流MQTT消息怎么变成UI上的刷新能连上MQTT只是第一步App最终要给用户看实时数据、按按钮控制设备。这里的关键设计是不要让每个页面直接操作mqtt实例而是把MQTT封装成全局模块通过Vuex统一管理连接状态和消息分发。4.1 用Vuex管理连接状态和设备数据我推荐在Vuex里单独建一个mqtt模块统一维护连接状态、最新设备数据。这样做的好处是页面退出再回来时连接状态还在多个页面共享同一份实时数据不会各自建连接。代码结构大概长这样// store/modules/mqtt.js const state { status: disconnected, // disconnected / connecting / connected deviceData: { temperature: null, humidity: null }, lastMessage: null } const mutations { SET_STATUS(state, status) { state.status status }, SET_DEVICE_DATA(state, data) { state.deviceData data }, SET_LAST_MESSAGE(state, msg) { state.lastMessage msg } } const actions { init({ commit }, config) { const client connectOneNet(config) client.on(connect, () commit(SET_STATUS, connected)) client.on(message, (topic, payload) { const body JSON.parse(payload.toString()) if (body.datastreams) { const data {} body.datastreams.forEach(item { const last item.datapoints[item.datapoints.length - 1] data[item.id] last.value }) commit(SET_DEVICE_DATA, { ...state.deviceData, ...data }) } }) } }页面里只需要用mapState拿到deviceData配合computed属性MQTT消息一到就会触发视图更新。比如首页展示温度和湿度卡片数据变化时卡片上的数字自动刷新用户感知不到底层是MQTT还是HTTP轮询体验很顺滑。4.2 指令下发的完整链路远程控制本质上就是把用户操作转成一条MQTT消息发布到命令topic。OneNET收到App下发的指令后会把指令推给硬件端硬件端执行完再把结果通过命令应答topic传回来。// 指令下发 function sendCommand(productId, deviceName, cmdId, payload) { const topic $sys/${productId}/${deviceName}/cmd/request/${cmdId} client.publish(topic, JSON.stringify(payload), { qos: 0 }) }我在做继电器控制时就是按钮点击事件里调用这个函数把{ switch: on }通过cmd/request/relay001发出去。硬件端收到后执行再往cmd/response/relay001上报执行结果。整个过程链路清晰用MQTTX桌面工具模拟硬件端时还能直接看到App下发的指令报文。这里有一个细节命令请求topic的{cmdId}是用作标识的同一个指令多次发送时cmdId要区分开否则硬件端可能因为消息去重而忽略后续指令。我在实际测试中就让cmdId带上时间戳类似relay001_1690000000能确保每次指令都是独立的消息。5. 换端就翻车的三个坑App和小程序的适配细节如果你用HBuilderX直接“运行到小程序模拟器”大概率会在连接阶段卡住。这不是代码问题是跨端环境的差异。我踩过的坑主要集中在三个地方提前配好能省一晚上。5.1 小程序webSocket协议和合法域名微信小程序里不能随便连WebSocket地址必须在小程序后台配置socket合法域名而且域名必须是WSS开头不能用IP直连。OneNET的MQTT如果走TLS连接的就是wss协议地址。开发调试时可以在微信开发者工具里勾选“不校验合法域名”但真机预览和发布后配置没配上连接还是失败。另外一个对应关系的细节小程序端的连接地址要写成wxs://协议头。这个专门给小程序用的协议头不是拼写错误是mqtt.js针对小程序的WebSocket实现。App端用wss://小程序端改用wxs://同一套其余代码不用动。5.2 App后台切换后的断线重连App切到后台超过一定时间系统会断开WebSocket连接。回到前台时如果不处理界面就一直停在“已断开”状态。我的做法是在App.vue的onShow生命周期里检查MQTT连接状态如果status不是connected就重新走一遍connectOneNet流程。同时把reconnectPeriod设成5000毫秒配合mqtt.js自带的断线重连机制基本能做到无感恢复。实际测试中发现Android和iOS对后台WebSocket的回收策略不一样iOS更激进。所以不要只依赖前台的onShow钩子最好在全局的onHide时主动记下时间戳onShow时判断离开超过30秒就强制重连这样能避免“看似连着但实际收不到消息”的假连接状态。5.3 下拉刷新和页面滚动的冲突处理这个问题和MQTT没直接关系但页面有实时数据看板时几乎必踩。热搜里那句“下拉如何触动滚动屏而不触发页面下拉刷新”说的就是它。我的做法是页面配置里不要全局开启enablePullDownRefresh改用scroll-view的refresher-enabled属性单独实现下拉刷新通过refresherrefresh事件去重新拉一次数据。这样滚动容器自己滚动不会触发页面级的下拉刷新体验稳定很多。6. 打包上架前的配置检查清单项目开发完最后一公里是打包。uniapp用HBuilderX云打包算是最省心的路径但几个配置不提前做打包后会莫名其妙出问题。6.1 HBuilderX云打包和证书App端打包前在manifest.json里确认App模块勾选了网络、WebSocket能力。Android上架需要准备签名证书可以用Android Studio生成keystore也可以直接用HBuilderX的云端证书功能。iOS第一次打测试包时流程比较绕建议先用云打包的“测试证书”模式跑通安装包再换正式证书。要注意的是云打包时选择的“打包类型”要和你的实际需求匹配测试包用自定义基座就够了正式发布才需要生成正式包。离线打包SDK方案如果要用务必确认离线SDK版本号和HBuilderX版本号一致否则会出现编译错误或者原生能力失效。6.2 小程序和H5端的额外配置如果你同时发布微信小程序版本记得在小程序管理后台的“开发设置-服务器域名”里把socket合法域名配成OneNET的wss接入地址。域名配置生效有几分钟延迟配完别急着重试。H5端则需要注意跨域限制浏览器环境里跨域WebSocket一般没问题但如果是生产环境最好用HTTPS页面避免混合内容被浏览器拦截。跑通整个链路后我的体会是uniappvue2OneNET这套组合每个环节都不是最“前沿”的但胜在文档全、能搜到的坑多、团队上手快。如果你只是要把设备数据搬到手机上看再加一点远程控制这套方案已经足够覆盖大部分需求。最后分享一个调试习惯所有MQTT问题先在电脑上用MQTTX这样的桌面工具连一次OneNET确认参数没问题再回到uniapp里查代码。这一步能帮你把“平台问题”和“代码问题”快速区分开省下大量时间。本文还有配套的精品资源点击获取
返回列表