跳转至

腾讯云

一、简介

腾讯云物联网开发平台(IoT Explorer)为各行业的设备制造商、方案商及应用开发商提供一站式设备智能化服务。平台提供海量设备连接与管理能力及小程序应用开发能力,并打通腾讯云基础产品及 AI 能力,提升传统行业设备智能化的效率,降低用户的开发运维成本,助力用户业务发展。

二、演示功能概述

本教程教你如何用 Air724 开发板,如何接入腾讯云,并且会介绍如何进行腾讯云的上下行交互处理。

三、准备硬件环境

3.1 开发板准备

使用 EVB_Air724 开发板,如下图所示:

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

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

api:https://doc.openluat.com/wiki/21?wiki_page_id=2068

3.2 数据通信线

USB 数据线一根(micro USB)。

3.3 PC 电脑

WIN7 以及以上版本的 WINDOWS 系统。

3.4 SIM 卡

中国大陆环境下,可以上网的 SIM 卡。一般来说,使用移动,电信,联通的物联网卡或者手机卡都行。

3.5 组装硬件环境

USB 数据线插入 USB 口,另一端与电脑相连,拨码开关全部拨到 ON,串口切换开关选择 UART1,USB 供电的 4V 对应开关拨至 ON 档,SIM 卡放到 SIM 卡槽中锁紧,如下图所示。

四、准备软件环境

4.1 下载调试工具

使用说明参考:Luatools 下载和详细使用

4.2 源码及固件

1.底层 core 下载

下载底层固件,并解压

链接:https://docs.openluat.com/air724ug/luatos/firmware/

如下图所示,红框的是我们要使用到的。

2.本教程使用的 demo:

https://gitee.com/openLuat/LuatOS-Air724UG/tree/master/script_LuaTask/demo/txiot

4.3 下载固件和脚本到开发板中

打开 Luatools,开发板上电开机,如开机成功 Luatools 会打印如下信息。

点击项目管理测试选项。

进入管理界面,如下图所示。

  • 点击选择文件,选择底层固件,我的文件放在 D:\luatOS\Air724 路径中

  • 点击增加脚本或资源文件,选择程序源码,如下图所示。

  • 点击下载底层和脚本,下载完成如下图所示。

五、代码示例介绍

5.1 API 说明

模块功能:MQTT 客户端

5.1.1 mqtt.client(clientId, keepAlive, username, password, cleanSession, will, version)

创建一个 mqtt client 实例

  • 参数
名称
传入值类型
释义
clientId
string
确保设备唯一性
keepAlive
number
可选参数,默认为 300 心跳间隔(单位为秒),默认 300 秒
username
string
可选参数,默认为"" 用户名,用户名为空配置为""或者 nil
password
string
可选参数,默认为"" 密码,密码为空配置为""或者 nil
cleanSession
number
可选参数,默认为 1 1/0
will
table
可选参数,默认为 nil 遗嘱参数,格式为{qos=,retain=,topic=,payload=}
version
string
可选参数,默认为"3.1.1" MQTT 版本号,仅支持"3.1"和"3.1.1"
  • 返回值

table mqttc client 实例

  • 例子
mqttc = mqtt.client("clientid-123")
mqttc = mqtt.client("clientid-123",200)
mqttc = mqtt.client("clientid-123",nil,"user","password")
mqttc = mqtt.client("clientid-123",nil,"user","password",nil,{qos=0,retain=0,topic="willTopic",payload="willTopic"},"3.1")

5.1.2 mqttc:connect(host, port, transport, cert, timeout)

连接 mqtt 服务器

  • 参数
名称
传入值类型
释义
host
string
服务器地址
port
param
string 或者 number 类型,服务器端口
transport
string
可选参数,默认为"tcp" “tcp"或者"tcp_ssl”
cert
table
可选参数,默认为 nil table 或者 nil 类型,ssl 证书,当 transport 为"tcp_ssl"时,此参数才有意义。cert 格式如下:{caCert = “ca.crt”, --CA 证书文件(Base64 编码 X.509 格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验 clientCert = “client.crt”, --客户端证书文件(Base64 编码 X.509 格式),服务器对客户端的证书进行校验时会用到此参数 clientKey = “client.key”, --客户端私钥文件(Base64 编码 X.509 格式)clientPassword = “123456”, --客户端证书文件密码[可选]}
timeout
number
可选参数,默认为 120 可选参数,socket 连接超时时间,单位秒
  • 返回值

result true 表示成功,false 或者 nil 表示失败

  • 例子
mqttc = mqtt.client("clientid-123", nil, nil, false); mqttc:connect("mqttserver.com", 1883, "tcp", 5)

5.1.3 mqttc:subscribe(topic, qos)

订阅主题

  • 参数
名称
传入值类型
释义
topic
param
string 或者 table 类型,一个主题时为 string 类型,多个主题时为 table 类型,主题内容为 UTF8 编码
qos
param
可选参数,默认为 0 number 或者 nil,topic 为一个主题时,qos 为 number 类型(0/1/2,默认 0);topic 为多个主题时,qos 为 nil
  • 返回值

bool true 表示成功,false 或者 nil 表示失败

  • 例子
mqttc:subscribe("/abc", 0) _-- subscribe topic "/abc" with qos = 0_
mqttc:subscribe({["/topic1"] = 0, ["/topic2"] = 1, ["/topic3"] = 2}) _-- subscribe multi topic_

5.1.4 mqttc:unsubscribe(topic)

取消订阅主题

  • 参数
名称
传入值类型
释义
topic
param
string 或者 table 类型,一个主题时为 string 类型,多个主题时为 table 类型,主题内容为 UTF8 编码
  • 返回值

bool true 表示成功,false 或者 nil 表示失败

  • 例子
mqttc:unsubscribe("/abc") _-- unsubscribe topic "/abc"_
mqttc:unsubscribe({"/topic1", "/topic2", "/topic3"}) _-- unsubscribe multi topic_

5.1.5 mqttc:publish(topic, payload, qos, retain)

发布一条消息

  • 参数
名称
传入值类型
释义
topic
string
UTF8 编码的字符串
payload
string
用户自己控制 payload 的编码,mqtt.lua 不会对 payload 做任何编码转换
qos 0/1/2, default
number
可选参数,默认为 00
retain
number
可选参数,默认为 0 0 或者 1
  • 返回值

bool 发布成功返回 true,失败返回 false

  • 例子
mqttc = mqtt.client("clientid-123", nil, nil, false)
mqttc:connect("mqttserver.com", 1883, "tcp")
mqttc:publish("/topic", "publish from luat mqtt client", 0)

5.1.6 mqttc:receive(timeout, msg)

接收消息

  • 参数
名称
传入值类型
释义
timeout
number
接收超时时间,单位毫秒
msg
string
可选参数,默认为 nil 可选参数,控制 socket 所在的线程退出 recv 阻塞状态
  • 返回值

result 数据接收结果,true 表示成功,false 表示失败

data

如果 result 为 true,表示服务器发过来的 mqtt 包

如果 result 为 false,超时失败,data 为"timeout"

如果 result 为 false,msg 控制退出,data 为 msg 的字符串

如果 result 为 false,socket 连接被动断开控制退出,data 为"CLOSED"

如果 result 为 false,PDP 断开连接控制退出,data 为"IP_ERROR_IND"

如果 result 为 false,mqtt 不处于连接状态,data 为 nil

如果 result 为 false,收到了 PUBLISH 报文,发送 PUBACK 或者 PUBREC 报文失败,data 为 nil

如果 result 为 false,收到了 PUBREC 报文,发送 PUBREL 报文失败,data 为 nil

如果 result 为 false,收到了 PUBREL 报文,发送 PUBCOMP 报文失败,data 为 nil

如果 result 为 false,发送 PINGREQ 报文失败,data 为 nil

param 如果是 msg 控制退出,param 的值是 msg 的参数;其余情况无意义,为 nil

  • 例子
true, packet = mqttc:receive(2000)
false, error_message = mqttc:receive(2000)
false, msg, para = mqttc:receive(2000,"APP_SEND_DATA")

5.1.7 mqttc:disconnect()

断开与服务器的连接

  • 参数

  • 返回值

nil

  • 例子
mqttc = mqtt.client("clientid-123", nil, nil, false)
mqttc:connect("mqttserver.com", 1883, "tcp")
process data
mqttc:disconnect()

5.2 txiot.lua 代码

本文件主要功能是,实现了一个完整的设备注册、MQTT 连接、消息处理的流程,适合用于基于腾讯云 IoT 的设备开发。可以根据具体需求对消息处理逻辑进行扩展和修改。

将 ProductId 改为自己的产品 ID。

将 ProductSecret 改为自己的产品动态注册密钥。

--- 模块功能:腾讯云平台
-- @module txiot
-- @author Dozingfiretruck
-- @license MIT
-- @copyright OpenLuat.com
-- @release 2020.12.31

module(...,package.seeall)

require "ntp"
require "misc"
require "mqtt"
require "utils"
require "patch"
require "socket"
require "http"
require "common"

-- 产品ID和产品动态注册密钥
local ProductId = "5FCW79CXYD"
local ProductSecret = "XOBuiSs4EUCjmP5NcFWrwdOe"

local tx_mqttClient
--[[
函数名:getDeviceName
功能  :获取设备名称
参数  :无
返回值:设备名称
]]
local function getDeviceName()
    --默认使用设备的IMEI作为设备名称,用户可以根据项目需求自行修改
    return misc.getImei()
end

function txiot_publish()
    --sys.publish("APP_SOCKET_SEND_DATA")
    --mqtt发布主题
    tx_mqttClient:publish("$thing/up/property/"..ProductId.."/"..getDeviceName(), "publish from luat mqtt client", 0)
end

-- 无网络重启时间,飞行模式启动时间
local rstTim, flyTim = 600000, 300000
local enrol_end = false
local mqtt_ready = false
--- MQTT连接是否处于激活状态
-- @return 激活状态返回true,非激活状态返回false
-- @usage mqttTask.isReady()
function isReady()
    return mqtt_ready
end

--- MQTT客户端数据接收处理
-- @param mqttClient,MQTT客户端对象
-- @return 处理成功返回true,处理出错返回false
-- @usage mqttInMsg.proc(mqttClient)
local function proc(mqttClient)
    local result,data
    while true do
        result,data = mqttClient:receive(120000,"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

local function cbFnc(result,prompt,head,body)
    log.info("testHttp.cbFnc",result,prompt,head,body)
    local dat, result, errinfo = json.decode(body)
    if result then
        if dat.code==0 then
            io.writeFile("/txiot.dat", body)
            log.info("腾讯云注册设备成功:", body)
        else
            log.info("腾讯云设备注册失败:", body)
        end
        enrol_end = true
    end
end

local function device_enrol()
    local deviceName = getDeviceName()
    local nonce = math.random(1,100)
    local timestamp = os.time()
    local data = "deviceName="..deviceName.."&nonce="..nonce.."&productId="..ProductId.."&timestamp="..timestamp
    local hmac_sha1_data = crypto.hmac_sha1(data,#data,ProductSecret,#ProductSecret):lower()
    local signature = crypto.base64_encode(hmac_sha1_data,#hmac_sha1_data)
    local tx_body = {
        deviceName=deviceName,
        nonce=nonce,
        productId=ProductId,
        timestamp=timestamp,
        signature=signature,
    }
    local tx_body_json = json.encode(tx_body)
    http.request("POST","https://ap-guangzhou.gateway.tencentdevices.com/register/dev",nil,{["Content-Type"]="application/json; charset=UTF-8"},tx_body_json,30000,cbFnc)
end

local function tencent_iot()
    if not io.exists("/txiot.dat") then device_enrol() while not enrol_end do sys.wait(100) end end
    if not io.exists("/txiot.dat") then device_enrol() log.warn("设备注册失败或设备已注册") return end
    local dat = json.decode(io.readFile("/txiot.dat"))
    local clientid = ProductId .. getDeviceName()    --生成 MQTT 的 clientid 部分, 格式为 ${productid}${devicename}
    local connid = math.random(10000,99999)
    local expiry = tostring(os.time() + 3600)
    local username = string.format("%s;12010126;%s;%s", clientid, connid, expiry)   --生成 MQTT 的 username 部分, 格式为 ${clientid};${sdkappid};${connid};${expiry}
    local payload = json.decode(crypto.aes_decrypt("CBC","ZERO",crypto.base64_decode(dat.payload, #dat.payload),string.sub(ProductSecret,1,16),"0000000000000000"))
    local password
    if payload.encryptionType==2 then
        local raw_key = crypto.base64_decode(payload.psk, #payload.psk) --生成 MQTT 的 设备密钥 部分
        password = crypto.hmac_sha256(username, raw_key):lower() .. ";hmacsha256" --根据物联网通信平台规则生成 password 字段
    elseif payload.encryptionType==1 then
        io.writeFile("/client.crt", payload.clientCert)
        io.writeFile("/client.key", payload.clientKey)
    end
    while true do
        if not socket.isReady() and not sys.waitUntil("IP_READY_IND", rstTim) then sys.restart("网络初始化失败!") end
        --创建一个MQTT客户端
        tx_mqttClient = mqtt.client(clientid,300,username,password)
        --阻塞执行MQTT CONNECT动作,直至成功
        if payload.encryptionType==2 then
            while not tx_mqttClient:connect(ProductId..".iotcloud.tencentdevices.com",1883,"tcp",nil,2000) do sys.wait(2000) end
        elseif payload.encryptionType==1 then
            while not tx_mqttClient:connect(ProductId..".iotcloud.tencentdevices.com",8883,"tcp_ssl",{clientCert="/client.crt",clientKey="/client.key"},2000) do sys.wait(2000) end
        end
            log.info("mqtt连接成功")
            tx_mqttClient:publish("$thing/up/property/"..ProductId.."/"..getDeviceName(), "publish from luat mqtt client", 0)
            --mqtt订阅主题
            local txiot_subscribetopic = {
                ["$thing/down/property/"..ProductId.."/"..getDeviceName()]=0
            }
            --订阅主题
            if tx_mqttClient:subscribe(txiot_subscribetopic, nil) then
                log.info("mqtt订阅成功")
                --循环处理接收和发送的数据
                while true do
                    mqtt_ready = true
                    if not proc(tx_mqttClient) then log.error("mqttTask.mqttInMsg.proc error") break end
                end
            else
                log.info("mqtt订阅失败")
            end
            mqtt_ready = false
        --断开MQTT连接
        tx_mqttClient:disconnect()
    end
end

local function iot()
    ntp.timeSync()
    if not socket.isReady() and not sys.waitUntil("IP_READY_IND", rstTim) then sys.restart("网络初始化失败!") end
    while not ntp.isEnd() do sys.wait(1000) end
    tencent_iot()
end

net.switchFly(false)
-- NTP同步失败强制重启
local tid = sys.timerStart(function()
    net.switchFly(true)
    sys.timerStart(net.switchFly, 5000, false)
end, flyTim)
sys.subscribe("IP_READY_IND", function()
    sys.timerStop(tid)
    log.info("---------------------- 网络注册已成功 ----------------------")
end)

sys.taskInit(iot)

5.3 main.lua 代码

本代码为主程序脚本,系统启动后首先会对 4G 网络进行配置,等待网络连接成功,然后加载测试模块。

六、腾讯云操作

6.1 产品操作

打开腾讯云,进入控制台选择物联网开发平台进入。(记住是物联网开发平台不是物联网通信)物联网开放平台

切换到老版本(腾讯新出了个 v2 版本开发平台,V2 较 V1 版本去掉了证书校验,去掉了自动注册中自动创建设备功能,自动注册的自动创建设备改没了,自动注册要手动一个一个创建设备)

点击新建项目

6.2 设备操作

进入项目点击新建产品,认证方式可选密钥认证或证书认证

进入产品记住产品 ID

打开动态注册并记住 ProductSecret

创建完成后,自动跳转新建设备界面,点击设备调试,再点击新建设备,设备名称我们可以直接填写模组的 imei 号

这样我们就创建好了设备,我们点进去看下设备信息!

可以看到我们需要的数据已经被贴心的加了一键复制,我们要记录下 设备名称、产品 ID、设备密钥 这三个数据,使用这三个数据我们就可以上云了!

6.3 设备注册

腾讯云 v2 开发平台支持两种注册方式,分别为密钥校验(手动注册)和动态注册,本文使用的动态注册方式。

6.3.1 动态注册

开启动态注册后设备无需一一烧录设备证书/密钥,同一产品下的所有设备可烧录相同的产品密钥(ProductId 和 ProductSecret ),云端授权通过后下发设备证书/密钥,您可以根据业务需要选择是否开启。本例采用动态注册的方式进行注册并连接云平台。

注意:动态注册需要在产品开发界面打开动态注册按钮, 随后会生成动态注册上方的产品密钥,我们复制产品 ID 和产品密钥即可。

将上面记录的产品 ID 填入 ProductId 中,产品密钥填入 ProductSecret 中。

-- 产品ID和产品动态注册密钥
local ProductId = "8ZAWHGG8AU"
local ProductSecret = "B7l0lX3GeotzY2HnlYQ83Zli"

6.3.2 密钥校验(手动注册)

手动注册不需要在产品开发界面打开动态注册按钮,但是设备要先在平台上注册。将上面记录的产品 ID 填入 ProductId 中,产品密钥填入 ProductSecret 中。

-- 产品ID和产品手动注册密钥
local ProductId = "8ZAWHGG8AU"
local ProductSecret = "B7l0lX3GeotzY2HnlYQ83Zli"

七、开机调试

7.1 开发板开机

连接好硬件并下载固件后,启动 Luatools 软件,系统运行信息将显示在界面中。红框中为开发板连接到 PC 机后正常打印的信息,如下图所示。

7.2 功能调试

  • 使用设备的 IMEI 作为设备名称。
  • 通过 HTTP POST 请求向腾讯云注册设备,并生成所需的签名。
  • 创建 MQTT 客户端并连接到腾讯云物联网平台。

订阅 MQTT 主题,处理接收到的消息。

服务器下发属性测试

服务器发送属性界面:

7.3 腾讯云上查看设备状态

八、常见问题

8.1 腾讯云接入失败,怎么排查?

答:先检查设备配置,确保设备的 ID,名称,密钥等配置信息正确无误;同时也要检查下网络连接,确保 SIM 卡是正常入网状态,若还是不行,请再仔细看下教程,看是否有哪个步骤有疏漏。

8.2 MQTT 连接腾讯云 , 不断的重连

确认一下是不是时间戳不对导致的,腾讯云注册是需要时间戳的。查看时间戳代码如下。

local timestamp = os.time()
log.info("时间戳:", timestamp )

给读者的话

本篇文章由杨超开发;

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

请登录合宙技术交流论坛,点击文档找错赢奖金-Air724UG-LuatOS-软件指南-公有云对接-腾讯云

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

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

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