跳转至

一、MQTT 介绍

MQTT 是一种低开销、低带宽占用的即时通讯协议,可以用极少的代码和带宽为远程设备提供实时可靠的消息服务。它适用于硬件性能低下的设备以及网络状况不佳的环境,因此在物联网(IoT)小型设备和移动应用等方面有广泛应用。

MQTT 采用发布/订阅通信模型,客户端可以发布消息到主题(Topic),也可以订阅主题来接收消息。这种模式解耦了消息的发送者和接收者。

MQTT 的消息传递质量分为三种:最多一次(QoS 0)不保证交付,至少一次(QoS 1)确保至少到达但可能重复,只有一次(QoS 2)确保仅到达一次。

二、演示功能概述

本 demo 通过使用 Air724UG 开发板,带你快速体验通过 MQTT 协议进行数据接收与发送。

三、准备硬件环境

3.1 Air724UG 开发板

准备一块 Air724UG-NFM 开发板,如下图所示:

点击链接购买:Air724UG-NFM 开发板淘宝购买链接

此开发板的详细使用说明参考:Air724UG 产品手册 中的《EVB_Air724UG_AXX 开发板使用说明》,写这篇文章时最新版本的使用说明为:《EVB_Air724UG_A14 开发板使用说明》;开发板使用过程中遇到任何问题,可以直接参考这份使用说明文档。

3.2 SIM 卡

请准备一张可以正常上网的 SIM 卡,该卡可以是物联网卡或者您的个人手机卡。

特别提醒:请确保 SIM 卡未欠费且网络功能正常,以便顺利进行后续操作。

3.3 PC 电脑

请准备一台配备 USB 接口且能够正常上网的 windows7 以及以上系统的电脑。

3.4 数据通信线

请准备一根用于连接 EVB_Air724UG_A14 开发板和 PC 电脑的数据线,该数据线将实现业务逻辑的控制与交互。

  • USB 数据线:此数据线不仅用于为测试板供电,还用于查看数据日志。其一端为 Micro-B 接口(俗称老安卓口),用于连接 EVB_Air724UG_A14 开发板;另一端为标准 USB 接口,连接 PC 电脑。

3.5 组装硬件环境

3.5.1 请按照 SIM 卡槽上的指示方向正确插入 SIM 卡,务必确保插入方向正确,避免插反导致损坏!

通常,插入 SIM 卡的步骤如下:

  • 将 SIM 卡槽的卡扣向 OPEN 的方向推,然后抬起。
  • 将 SIM 卡对准卡槽形状,SIM 卡金属接触面朝下,再将卡口合上,朝 CLOSE 方向推,能够关上就说明 SIM 卡已经安装到位。

3.5.2 USB 数据线,连接电脑和 Air724UG 开发板:

1. 如下图,位于上方的 USB 接口(上方红框)直接用 micro USB 数据线连接 PC 供电。

2. 如下图,使用 USB 线缆,插入左侧的 USB 端口,将开发板左侧的电源拨动开关拨至“ON”。长按开机按钮 3 秒钟。

注意:A 所指示的框图区域,拨码开关 1 和 2 的位置需拨至上方与下图保持一致,以确保开发板能够正常工作。

3. 如下图,此时位置 1 的电源红灯会处于常亮,如果开发板是 AT 版本的固件,位置 2 的绿灯会闪烁(LUA 固件不带脚本时绿灯不亮)

四、准备软件环境

注:以下软件下载链接,不能点击下载的请复制后,粘贴到浏览器 URL 地址栏进行下载;

4.1 PC 上 MQTT 客户端

MQTT 下载链接 https://www.emqx.com/zh

4.2 Luatools

下载地址:Luatools v3 下载调试工具

4.3 core 固件和源码脚本

底层 core 下载地址:Air724UG SDK&Demo

烧录脚本以及 lib 脚本下载地址:Air724UG SDK&Demo

4.4 安装驱动程序

1. 如果发现启动设备后,打开电脑的设备管理器,出现下图情况,需要安装驱动程序,Air724UG_USB 驱动下载地址:点击此处

2. 安装成功后打开设备管理器,会出现如下界面:

五、代码示例介绍

5.1 MQTT 配置

5.1.1 MQTT 的 4 个重要配置

  • "lbsmqtt.airm2m.com"(MQTT 服务器地址,这里使用合宙提供公用测试服务器)
  • 1884(MQTT 端口)
  • "user"(MQTT 服务器登录用户名)
  • "password"(MQTT 服务器登录密码)

5.1.2 MQTT 的 2 个重要主题

  • /qos0topic(设备发布主题,可在 mqttOutMsg.lua 源码 18 行,可自行修改)
  • /event0(设备订阅主题,可在 mqttTask.lua 源码 39 行,可自行修改)

5.2 完整程序清单

1. mqttOutMsg.lua 是 MQTT 客户端数据发送处理

module(...,package.seeall)

--数据发送的消息队列
local msgQueue = {}

--这个函数用于将MQTT消息插入到一个消息队列
local function insertMsg(topic,payload,qos,user)
    table.insert(msgQueue,{t=topic,p=payload,q=qos,user=user})
    sys.publish("APP_SOCKET_SEND_DATA")
end
--这是发布QoS0消息后的回调函数,用于处理发布结果
local function pubQos0TestCb(result)
    log.info("mqttOutMsg.pubQos0TestCb",result)
    if result then sys.timerStart(pubQos0Test,10000) end
end
--发布一个QoS0的MQTT消息
function pubQos0Test()
    insertMsg("/qos0topic","Hello mqtt",0,{cb=pubQos0TestCb})
end
--这是发布QoS1消息后的回调函数,用于处理发布结果
local function pubQos1TestCb(result)
    log.info("mqttOutMsg.pubQos1TestCb",result)
    if result then sys.timerStart(pubQos1Test,20000) end
end
--发布一个中文QoS0的MQTT消息
function pubQos1Test()
    insertMsg("/中文qos1topic","中文qos1data",1,{cb=pubQos1TestCb})
end

--- 初始化“MQTT客户端数据发送”
-- @return 无
-- @usage mqttOutMsg.init()
function init()
    pubQos0Test()
    pubQos1Test()
end

--- 去初始化“MQTT客户端数据发送”
-- @return 无
-- @usage mqttOutMsg.unInit()
function unInit()
    sys.timerStop(pubQos0Test)
    sys.timerStop(pubQos1Test)
    while #msgQueue>0 do
        local outMsg = table.remove(msgQueue,1)
        if outMsg.user and outMsg.user.cb then outMsg.user.cb(false,outMsg.user.para) end
    end
end


--- MQTT客户端数据发送处理
-- @param mqttClient,MQTT客户端对象
-- @return 处理成功返回true,处理出错返回false
-- @usage mqttOutMsg.proc(mqttClient)
function proc(mqttClient)
    while #msgQueue>0 do
        local outMsg = table.remove(msgQueue,1)
        local result = mqttClient:publish(outMsg.t,outMsg.p,outMsg.q)
        if outMsg.user and outMsg.user.cb then outMsg.user.cb(result,outMsg.user.para) end
        if not result then return end
    end
    return true
end

2. mqttInMsg.lua 是 MQTT 客户端数据接收处理

module(...,package.seeall)

--- MQTT客户端数据接收处理
-- @param mqttClient,MQTT客户端对象
-- @return 处理成功返回true,处理出错返回false
-- @usage mqttInMsg.proc(mqttClient)
function proc(mqttClient)
    local result,data
    while true do
        result,data = mqttClient:receive(60000,"APP_SOCKET_SEND_DATA")
        --接收到数据
        if result then
            log.info("mqttInMsg.proc",data.topic,string.toHex(data.payload))

            --TODO:根据需求自行处理data.payload
        else
            break
        end
    end

    return result or data=="timeout" or data=="APP_SOCKET_SEND_DATA"
end

3. mqttTask.lua 是 MQTT 客户端处理框架

module(...,package.seeall)

require"misc"
require"mqtt"
require"mqttOutMsg"
require"mqttInMsg"

local ready = false

--- MQTT连接是否处于激活状态
-- @return 激活状态返回true,非激活状态返回false
-- @usage mqttTask.isReady()
function isReady()
    return ready
end

--启动MQTT客户端任务
sys.taskInit(
    function()
        local retryConnectCnt = 0
        while true do
            if not socket.isReady() then
                retryConnectCnt = 0
                --等待网络环境准备就绪,超时时间是5分钟
                sys.waitUntil("IP_READY_IND",300000)
            end

            if socket.isReady() then
                local imei = misc.getImei()
                --创建一个MQTT客户端
                local mqttClient = mqtt.client(imei,600,"user","password")
                --阻塞执行MQTT CONNECT动作,直至成功
                --如果使用ssl连接,打开mqttClient:connect("lbsmqtt.airm2m.com",1884,"tcp_ssl",{caCert="ca.crt"}),根据自己的需求配置
                --mqttClient:connect("lbsmqtt.airm2m.com",1884,"tcp_ssl",{caCert="ca.crt"})
                if mqttClient:connect("lbsmqtt.airm2m.com",1884,"tcp") then
                    retryConnectCnt = 0
                    ready = true
                    --订阅主题
                    if mqttClient:subscribe({["/event0"]=0, ["/中文event1"]=1}) then
                        mqttOutMsg.init()
                        --循环处理接收和发送的数据
                        while true do
                            if not mqttInMsg.proc(mqttClient) then log.error("mqttTask.mqttInMsg.proc error") break end
                            if not mqttOutMsg.proc(mqttClient) then log.error("mqttTask.mqttOutMsg proc error") break end
                        end
                        mqttOutMsg.unInit()
                    end
                    ready = false
                else
                    retryConnectCnt = retryConnectCnt+1
                end
                --断开MQTT连接
                mqttClient:disconnect()
                if retryConnectCnt>=5 then link.shut() retryConnectCnt=0 end
                sys.wait(5000)
            else
                --进入飞行模式,20秒之后,退出飞行模式
                net.switchFly(true)
                sys.wait(20000)
                net.switchFly(false)
            end
        end
    end
)

六、功能验证

6.1 开机

1. 如图通过 TYPE-C 线将开发板与电脑连接

2. 如果在设备管理器的端口栏下多出这几个 USB 设备,即代表开机成功!

6.2 打开 Luatool 软件工具并进入项目管理测试页面

如果提示更新,就点击更新后重启,确保你的 Luatools 的版本大于或者等于 3.0.6 版本

6.3 新建项目

底层 CORE 和脚本以及资源前面我们已经下载好了,按照下图的步骤操作就行

6.4 按如下步骤进行程序烧录

6.5 打开 MQTT 客户端应用程序并配置

6.5.1 MQTT 客户端基本配置

ProfileName:合宙(可修改为你想要的名称)

BrokerAddress:http://lbsmqtt.airm2m.com (合宙提供的免费测试服务器,也可修改为自己的服务器)

BrokerPort:1883 (端口号)

UserName:user

Password:password

6.5.2 两个重要主题

设备发布主题:/qos0topic (设备向服务器发送数据使用)

设备订阅主题:/event0 (接收服务器数据主题)

6.6 开发板发送数据给 MQTT 客户端

开发板发送的数据为"Hello mqtt"

6.6 MQTT 客户端给开发板发送数据

我们只需要将 string.toHex(data.payload)替换为 data.payload,这样 log 就会直接输出 payload 的原始字符串内容

总结

至此,我们已使用 Air724UG 开发板完成了 MQTT 通信的基本功能。

给读者的话

本篇文章由胡海江开发;

本篇文章描述的内容,如果有错误、细节缺失、细节不清晰或者其他任何问题,总之就是无法解决您遇到的问题;

请登录合宙技术交流论坛,点击文档找错赢奖金-Air724UG-LuatOS-网络驱动-MQTT-通信

用截图标注+文字描述的方式跟帖回复,记录清楚您发现的问题;

我们会迅速核实并且修改文档;

同时也会为您累计找错积分,您还可能赢取月度找错奖金!