跳转至

一、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 卡金属接触面朝下,再将卡口合上,朝 LOCK 方向推,能够关上就说明 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. 如图通过 Micro USB 线将开发板与电脑连接

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-通信

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

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

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