跳转至

ONENET

一、OneNET 简介

OneNET 是中国移动提供的物联网开放平台,支持设备接入、数据管理、应用开发、增值服务等能力,为城市、行业、生活等场景提供智能化解决方案。接下来

二、演示功能概述

本文为大家介绍 Air780EP 如何接入 OneNET,并且会介绍如何进行一次完整的 OneNET 的上下行交互处理。

三、准备硬件环境

参考:硬件环境清单第二章节内容,准备以及组装好硬件环境。。

四、软件环境

“凡事预则立,不预则废。”在详细阐述本功能示例之前,我们需先精心筹备好以下软件环境。

1. Luatools工具

2. 内核固件文件(底层core固件文件):LuatOS-SoC_V2002_Air780EP;参考项目使用的内核固件

3. luatos需要的脚本和资源文件

脚本和资源文件点我,查看demo链接

lib脚本文件:使用Luatools烧录时,勾选 添加默认lib 选项,使用默认lib脚本文件;

准备好软件环境之后,接下来查看如何烧录项目文件到Air780EP核心板,将本篇文章中演示使用的项目文件烧录到Air780EP核心板中。

五、oneNET 软硬件资料

oneNET 为云平台,所以不需要什么太多的硬件,只需要一个 Air780EP 开发板,一根 USB 数据传输线以及一张 sim 卡即可,主要是软件。接下来介绍 oneNET 所需要的软件库以及接口文档

5.1 iotcloud 库介绍

众所周知,市面上有很多云平台,阿里云、腾讯云、中移 onenet、华为云、百度云、华为云、Tlink 云等等......并且每家都有自己的协议,工程师要移植不同的 sdk 代码或基于各家的手册文档对接不同的协议,看着都头大!!!

所以 iotcloud 应运而生!iotcloud 是合宙专门为了合并 iot 平台而制作的库,意在使用统一且极简的代码接入各个云平台,轻松实现云功能。用户无需为那么多云平台的接入而头疼,只需要极简的通用 API 即可轻松上云!并且因为通用,所以云平台之间的迁移也十分方便。

iotcloud 库本质就是上层设计一套通用的 API 库来进行每个平台功能的对接。目前已经实现了各个平台的所有注册方式,其中自动注册会将相关验证信息保存 kv,随后使用此验证信息进行连接,通知针对每个平台添加了特有系统实现,比如设备上线通知,设备版本号上传,ota 功能等,用户无需管理这些只需要注意相关下发消息做应用逻辑即可。

5.2 API 接口介绍

本教程使用 api 接口为:iotcloud - iotcloud 云平台库

六、云平台配置与效果展示

6.1 登录与创建产品

注意:ONENET 物联网平台经过很多次更新,老版 ONENET(多协议接入)-> 新版 ONENET->ONENET Studio->OneNET 物联网开放平台,本次示例为最新的 OneNET 物联网开放平台最为演示平台

需要登录官网平台概览 - OneNET 物联网平台 (10086.cn)注册 OneNET 账号并创建一个产品,后面我们会在此项目中进行演示

如果你没找到 OneNET 的物联网平台在哪,可以点击 OneNET 官网右上角的开发者中心,

在完成一系列安全认证(绑定身份证并进行人脸验证)以后,将鼠标移动到左上角的"全部产品服务"即可看到物联网开放平台在哪,点击进入即可

在物联网管理平台,按下图三步,选择适合自己的产品定义

如果你的产品不在已经定义好的产品列表里,那么可以下拉选择框选择其他行业

选择智能化方式为"设备接入"

点击设备接入以后,创建产品,带* 的为必选项,接入协议必须选择"MQTT",数据协议可以选择"OneJson"也可以选择"自定义/透传",区别只是上传和下发数据的时候的格式,如果你选择了 OneJson,则上传数据和接受平台下发数据必须要按照 OneNET 定义的 josn 格式来上传/解析。

创建完成后我们一定要记住产品 ID,后面会用到

6.2 添加设备

产品创建好以后就该添加设备进对应的产品了。在产品列表中找到"设备管理",单击进去,设备名称选择所用模块的 IMEI,可以在模块屏蔽盖上扫码查看·

6.3 设备注册

ONENET 支持两种注册方式:动态注册和手动注册,其中手动注册设备支持一型一密和一机一密方式进行连接登录

如果你一个项目中的设备总数不固定,随时可能会新添加设备或者不想每一台都需要想办法将不同的鉴权码传给模块,推荐使用动态注册

6.3.1 动态注册

此方式极为简单,无需创建设备,他可以实现统一代码使用时动态进行设备注册,只需要三个参数,产品 ID,用户 ID,用户 Accesskey

产品 ID 在上面创建产品的时候已经拿到了,接下来我们点击右上角头像下的访问权限就可以看到 用户 ID 和用户 Accesskey 了

接下来我们再来看下代码具体怎么用

-- 动态注册--
iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",userid = "xxx",userkey = "xxx"})

第一个参数表示我们使用的是 ONENET,第二个参数我们将上面得到的产品 ID,用户 ID,用户 Accesskey,非常的简单!

注:如果你用动态注册的话,千万不要使用"添加设备"的功能,如果已经添加过了设备,后续又用对应设备去做动态注册,动态注册会失败,直接影响后面的和服务器进行交互,已经添加过的设备可以直接走云平台上删除。

删除方式

6.3.2 手动注册(一型一密)

一型一密使用产品密钥作为校验凭据,我们点击产品开发

可以获取到产品 ID 和产品密钥,之后我们创建设备,点击设备管理-> 创建设备(推荐设备名称使用 IMEI 创建)

接下来我们看下代码具体怎么用

_-- 一型一密-- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",device_name = "xxx",product_secret = "xxx"})_

第一个参数表示我们使用的是 ONENET,第二个参数我们将上面得到的三个参数填写到 table 中即可

6.3.3 手动注册(一机一密)

一机一密使用设备密钥作为校验凭据

和上面一型一密的创建设备流程一样,创建之后我们再来看看设备信息

记住设备 ID 和设备密钥

接下来我们再来看下代码具体怎么用

--一机一密
iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",device_name = "xxx",device_secret = "xxx"})

第一个参数表示我们使用的是 ONENET,第二个参数我们将上面得到的产品 ID、设备 ID 和设备密钥,同样很简单~

6.4 效果演示

代码演示

好了,接下来我们看下完整代码和效果,这里以动态注册为例,此处 demo 是上面 demo 源码,删除了其他云平台以及无关代码以后的

-- LuaTools需要PROJECT和VERSION这两个信息_
PROJECT = "iotclouddemo"
VERSION = "1.0.0"
-- sys库是标配
_G.sys = require("sys")
--[[特别注意, 使用mqtt库需要下列语句]]_
_G.sysplus = require("sysplus")

local iotcloud = require("iotcloud")
sys.taskInit(function()-- 等待联网_
    sys.waitUntil("IP_READY")
--------    以下接入方式根据自己需要修改,相关参数修改为自己的    ----------- ONENET云
-- 动态注册
    iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",userid = "xxx",userkey = "xxx"})
    -- 一型一密-- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",device_name = "xxx",product_secret = "xxx"})_
    -- 一机一密-- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",device_name = "xxx",device_secret = "xxx"})_
    if iotcloudc then
        iotcloudc:connect()
    end
end)
sys.subscribe("iotcloud", function(cloudc,event,data,payload)
if event == iotcloud.CONNECT then 
-- 云平台联上了_
print("iotcloud","CONNECT", "云平台连接成功")
_-- iotcloud:subscribe("test") -- 定阅主题
elseif event == iotcloud.RECEIVE then
print("iotcloud","topic", data, "payload", payload)-- 用户处理代码_
elseif event ==  iotcloud.OTA then
    if data then
        rtos.reboot()
    end
elseif event == iotcloud.DISCONNECT then-- 云平台断开了_
            -- 用户处理代码
end)

-- 用户代码已结束----------------------------------------------- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

烧录过程

设备日志:

可以看到我们的设备打印了连接成功,证明自动注册 + 连接流程已经完成

6.5 云平台效果:

设备已经自动注册并且在线,接下来演示数据的上传和下发解析

6.6 数据上传和下发

所有的产品想要在 oneNET 中显示对应的数据,都需要有对应的物模型,物模型有两种,一种是 oneNET 已经定义好的,一种是用户自定义的,如果在最初创建产品的时候,我们选择的"产品类别",是 oneNET 里面已经有的产品类别,那么物模型已经有现成的,用户只需要按照 oneNET 规定的数据格式去上传数据即可,例如这个智能路灯

6.6.1 物模型的设置

由于产品当初创建的时候,在"产品类别"里,我们没有选择任何已有类别,所以需要自己去添加自己想要的数据当作物模型

这里假设我们演示的类别为 4G 远程继电器(GPIO6 为继电器控制开关),"当前 GPIO 状态"(高电平/低电平)、"当前 CPU 温度"、"当前经纬度(基站定位所得)"这三个数据为我们想时刻了解的数据,那么就可以定义如下物模型

{"gpio_state":1, "cpu_temp":"50","lbs_lat":"","lbs_lng":""}

6.6.2 创建物模型

点击左边的"产品开发",再点击右边对应产品的"产品开发"

在刷新出的界面中点击"设置物模型"

选择"添加自定义功能点",在弹出的界面中按每一个需要的数据依次填入对应的值

上图演示的是当前 GPIO 状态(gpio_state),后面的 CPU 温度 cpu_temp 、基站定位经纬度 lbs_lat/lbs_lng

也是一样的设置方法

设置完后点击"保存" "确定"即可完成物模型的设置

如果你发现自己物模型设置错了,比如上图基站定位经纬度应该是浮点数,不应该是整数,则可以重新点击"设置物模型"--"编辑"重新编辑对应的物模型

6.6.3 查看物模型

选择"查看/导出物模型"--"精简物模型"

6.6.4 topic 设置

上述物模型设置完毕以后,我们需要知道模块上传/下发的主题,往对应的主题发送物模型格式的数据,才能在 oneNET 中看到对应的数据,根据 oneNET 的文档可知,如果需要给 oneNET 的物模型中上传我们需要的数据,则发布的主题为:$sys/{pid}/{device-name}/thing/property/post,云平台响应该发布的主题(即我们订阅的主题为):$sys/{pid}/{device-name}/thing/property/post/reply

想订阅和发布其他主题可以看 oneNET 通讯主题

看清楚操作权限,记录下这些主题,然后在代码里订阅对应的主题

6.6.5 代码示例

接下来演示定时 5 分钟发送一次上文定义的数据给 oneNET

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "iotclouddemo"
VERSION = "1.0.0"

-- sys库是标配
_G.sys = require("sys")
--[[特别注意, 使用mqtt库需要下列语句]]
_G.sysplus = require("sysplus")
lbsLoc2 = require("lbsLoc2")
local iotcloud = require("iotcloud")
-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "iotclouddemo"
VERSION = "1.0.0"

-- sys库是标配
_G.sys = require("sys")
--[[特别注意, 使用mqtt库需要下列语句]]
_G.sysplus = require("sysplus")
lbsLoc2 = require("lbsLoc2")
local iotcloud = require("iotcloud")

--自动切换sim卡1和sim卡2,如果只有卡一,可以注释掉这句话,会影响开机驻网的时间
mobile.simid(2, true)

-- Air780EP的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
    pm.power(pm.PWK_MODE, false)
end

--这里填用户自己的,如果不填会导致设备注册不上oneNET
local produt_id = ""
local userid = ""
local userkey = ""
local device_name = mobile.imei()

local send_data_time = 5 * 60 * 1000 -- 定时发送数据的时间,单位ms

-- 统一联网函数
sys.taskInit(function()
    local device_id = mcu.unique_id():toHex()

    -- 默认都等到联网成功
    sys.waitUntil("IP_READY")
    sys.publish("net_ready", device_id)
end)

sys.taskInit(function()
    -- 等待联网
    local ret, device_id = sys.waitUntil("net_ready")

    --------    以下接入方式根据自己需要修改,相关参数修改为自己的    ---------

    -- ONENET云
    -- 动态注册
    iotcloudc = iotcloud.new(iotcloud.ONENET, {
        device_name = device_name,
        produt_id = produt_id,
        userid = userid,
        userkey = userkey
    })
    -- 一型一密
    -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",product_secret = "xxx"})
    -- 一机一密
    -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",device_name = "xxx",device_secret = "xxx"})

    if iotcloudc then
        iotcloudc:connect()
    end

end)

-- 发布和订阅的主题

local oneNET_sub = "$sys/" .. produt_id .. "/" .. device_name .. "/thing/property/post/reply"

local oneNET_pub = "$sys/" .. produt_id .. "/" .. device_name .. "/thing/property/post"

local function oneNET_send_data()
    log.info("oneNET 链接成功,准备开始发送数据")
    while 1 do
        -- 没有mobile库就没有基站定位
        mobile.reqCellInfo(15)
        -- 由于基站定位需要等待扫描周围基站,推荐扫描时间为15S
        sys.waitUntil("CELL_INFO_UPDATE", 15000)
        local lat, lng, t = lbsLoc2.request(5000)
        log.info("lbsLoc2", lat, lng, (json.encode(t or {})))
        -- 如果没扫描到基站则给lat和lng赋值为0
        if lat and lng then
            log.info("扫描到了,有位置信息")
        else
            lat = "0"
            lng = "0"
        end
        -- 读取CPU温度, 单位为0.001摄氏度, 是内部温度, 非环境温度
        adc.open(adc.CH_CPU)
        local cpu_temp = adc.get(adc.CH_CPU)
        adc.close(adc.CH_CPU)
        local gpio_pin = 6 -- GPIO编号
        local gpio_state = gpio.get(gpio_pin)
        local send_data = {
            id = "123",
            verson = VERSION,
            params = {
                gpio_state = {
                    value = gpio_state
                },
                cpu_temp = {
                    value = cpu_temp / 1000
                },
                lbs_lat = {
                    value = tonumber(lat)
                },
                lbs_lng = {
                    value = tonumber(lng)
                    -- value = lng
                }
            }
        }
        local send_data = json.encode(send_data)
        log.info("发送的数据为", send_data)
        -- 正式发布数据
        iotcloudc:publish(oneNET_pub, send_data)
        -- 循环发送数据的定时时间
        sys.wait(send_data_time)
    end

end

local con = 0
--oneNET断开后的处理函数,
local function oneNET_DISCONNECT()
    log.info("云平台断开了,隔一分钟重连一次,如果10次都没有连上则重启设备")
    while con < 10 do
        sys.wait(60*1000)
        log.info("oneNET reconnection",con)
        iotcloudc:connect()
    end
    pm.reboot()
end
sys.subscribe("iotcloud", function(cloudc, event, data, payload)
    -- 注意,此处不是协程内,复杂操作发消息给协程内进行处理
    if event == iotcloud.CONNECT then -- 云平台联上了
        log.info("iotcloud", "CONNECT", "oneNET平台连接成功")
        iotcloudc:subscribe({
            [oneNET_sub] = 1
        }) -- 订阅服务器下发数据的主题
        -- 链接成功,启动一个task专门用来定时发消息
        sys.taskInit(oneNET_send_data)

    elseif event == iotcloud.RECEIVE then
        log.info("收到服务器下发的数据")
        log.info("iotcloud", "topic", data, "payload", payload)

        -- 用户处理代码
        if payload then
            payload = json.decode(payload)
            if payload["code"] == 200 then
                log.info("服务器收到了刚刚上传的数据", payload["msg"])
            else
                log.info("服务器接收数据有误", "错误码为", payload["code"], "错误信息为",
                    payload["msg"])
            end
        end

    elseif event == iotcloud.SEND then
        log.info("发送数据成功")

    elseif event == iotcloud.DISCONNECT then -- 云平台断开了
       sys.taskInit(oneNET_DISCONNECT)
    end
end)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
6.6.6 云平台效果展示

七、常见问题:

  • 如果已经烧录过一次自动注册的代码,第二次烧录的时候勾选了"EC 方案清除 KV 分区"和"EC 方案清除 FS 分区",则无法链接云平台,如图所示

这是因为 iotcolud 会在 FS 区保存用户的多个 key,如果你下载的时候清除掉了,则会根据 oneNET 规定的算法重新生成一组 key,但是在 oneNET 上,还是保持的原来的 key,没有更新,所以当前模块的状态就变成了链接不上云平台,只有在 oneNET 里删除了原来的设备才能重新注册上去,删除设备的方法如下图所示

  • 如果发送数据成功,但是服务器回复的 code 不是 200(验证成功)怎么办,如下图所示

可以根据错误信息打印查看原因,如上图,错误信息为 lbs_lng 这个数据发送错误,类型不对,应该是双精度浮点型。可以检查代码下代码,看看 lbs_lng 是否转化为了双精度浮点型数据,如果没有,可以使用 tonumber()函数,强制转化为 numer 类型

给读者的话

本篇文章由 黄何 开发;

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

请先登录合宙技术交流论坛,然后点击右边链接文档找错赢奖金-Air780EP-LuatOS-软件指南-网络应用-oneNET

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

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

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