跳转至

腾讯云

一、简介

腾讯云物联网开发平台(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 API

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 网络进行配置,等待网络连接成功,然后加载测试模块。

--必须在这个位置定义PROJECT和VERSION变量
--PROJECT:ascii string类型,可以随便定义,只要不使用,就行
--VERSION:ascii string类型,如果使用Luat物联云平台固件升级的功能,必须按照"X.X.X"定义,X表示1位数字;否则可随便定义
PROJECT = "HTTP"
VERSION = "2.0.0"

--加载日志功能模块,并且设置日志输出等级
--如果关闭调用log模块接口输出的日志,等级设置为log.LOG_SILENT即可
require "log"
LOG_LEVEL = log.LOGLEVEL_TRACE
--[[
如果使用UART输出日志,打开这行注释的代码"--log.openTrace(true,1,115200)"即可,根据自己的需求修改此接口的参数
如果要彻底关闭脚本中的输出日志(包括调用log模块接口和Lua标准print接口输出的日志),执行log.openTrace(false,第二个参数跟调用openTrace接口打开日志的第二个参数相同),例如:
1、没有调用过sys.opntrace配置日志输出端口或者最后一次是调用log.openTrace(true,nil,921600)配置日志输出端口,此时要关闭输出日志,直接调用log.openTrace(false)即可
2、最后一次是调用log.openTrace(true,1,115200)配置日志输出端口,此时要关闭输出日志,直接调用log.openTrace(false,1)即可
]]
--log.openTrace(true,1,115200)

require "sys"

require "net"
--每1分钟查询一次GSM信号强度
--每1分钟查询一次基站信息
net.startQueryAll(60000, 60000)

--此处关闭RNDIS网卡功能
--否则,模块通过USB连接电脑后,会在电脑的网络适配器中枚举一个RNDIS网卡,电脑默认使用此网卡上网,导致模块使用的sim卡流量流失
--如果项目中需要打开此功能,把ril.request("AT+RNDISCALL=0,1")修改为ril.request("AT+RNDISCALL=1,1")即可
--注意:core固件:V0030以及之后的版本、V3028以及之后的版本,才以稳定地支持此功能
ril.request("AT+RNDISCALL=0,1")

--加载控制台调试功能模块(此处代码配置的是uart2,波特率115200)
--此功能模块不是必须的,根据项目需求决定是否加载
--使用时注意:控制台使用的uart不要和其他功能使用的uart冲突
--使用说明参考demo/console下的《console功能使用说明.docx》
--require "console"
--console.setup(2, 115200)

--加载网络指示灯和LTE指示灯功能模块
--根据自己的项目需求和硬件配置决定:1、是否加载此功能模块;2、配置指示灯引脚
--合宙官方出售的Air720开发板上的网络指示灯引脚为pio.P2_0,LTE指示灯引脚为pio.P2_1
--require "netLed"
--netLed.setup(true,pio.P2_0,pio.P2_1)
--网络指示灯功能模块中,默认配置了各种工作状态下指示灯的闪烁规律,参考netLed.lua中ledBlinkTime配置的默认值
--如果默认值满足不了需求,此处调用netLed.updateBlinkTime去配置闪烁时长
--LTE指示灯功能模块中,配置的是注册上4G网络,灯就常亮,其余任何状态灯都会熄灭

--加载错误日志管理功能模块【强烈建议打开此功能】
--如下2行代码,只是简单的演示如何使用errDump功能,详情参考errDump的api
require "errDump"
errDump.request("udp://dev_msg1.openluat.com:12425", nil, true)

--加载远程升级功能模块【强烈建议打开此功能,如果使用了阿里云的OTA功能,可以不打开此功能】
--如下3行代码,只是简单的演示如何使用update功能,详情参考update的api以及demo/update
--PRODUCT_KEY = "v32xEAKsGTIEQxtqgwCldp5aPlcnPs3K"
--require "update"
--update.request()

--加载腾讯云平台功能测试模块
require "txiot"

--启动系统框架
sys.init(0, 0)
sys.run()

六、腾讯云操作

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 )