简介:这份《OneNET平台NB-IOT接入开发文档》由中移物联网有限公司编写,面向使用OneNET平台进行NB-IoT设备接入与第三方应用开发的工程师及物联网学习者,帮助解决从终端SDK移植、设备登录到平台数据交互全流程中的技术疑问。资源包为1个docx文档,大小约1.73MB,内容按章节组织,涵盖设备接入流程综述、SDK移植到MCU与NB模组、创建产品设备、登录订阅、数据推送与API接口调用、接入实例,以及LWM2M与CoAP协议解析和常见问题。文档还提供数据推送SDK、NB-IoT API与开发板资料下载指引,目录结构清晰,便于按模块查阅。目前已有1083人学习下载,适合需要系统掌握OneNET平台NB-IoT接入方法、对照API接口与协议细节进行开发调试的读者参考。
1. 从一份 2018 年的 NB-IoT 接入文档说起:它到底能帮你省掉哪些弯路
如果你手里正捏着一块 NB 模组,想把它连上 OneNET 平台,却卡在“SDK 往哪塞”“设备怎么在平台上冒出来”“第三方应用怎么收数据”这几个问题上,那这份《OneNET平台NB-IOT接入开发文档》就是给你准备的。它不是那种只讲概念的 PPT 式材料,而是一份从终端设备接入到北向 API 调用的全流程开发指南,覆盖了 SDK 移植、产品设备创建、LWM2M 资源模型、数据推送、API 接口调用以及 CoAP 协议细节。适合两类人:一类是做嵌入式侧开发的,需要把 NB 模组或 NB 芯片跟平台打通;另一类是做应用侧开发的,需要调 API 拿数据、下发命令、配触发器。文档里最值钱的部分不是那些流程图,而是它把“设备怎么在平台上从灰色变绿色”这件事拆成了可执行的步骤,并且给出了资源模型的具体 ID 和类型定义。你照着走一遍,至少能少踩三天的坑。
2. 终端接入前的 SDK 移植:三种架构怎么选、往哪塞
2.1 MCU+模组架构下,SDK 放 MCU 还是放模组
这是第一个要做的选型决策,选错了后面全是返工。文档里把终端分成了三类:SDK 移植到 MCU、SDK 移植到 NB 通信模组、SDK 移植到 NB 芯片。先说最常见的“MCU+NB 通信模组”架构,这里有两个分支。
如果 SDK 移植到 MCU,那 NB 模组只当透传通道用。MCU 里要干的事包括:移植基础通信套件 SDK、写设备应用程序、实现抽象接口、搞定底层操作系统和驱动。好处是控制权全在你手里,坏处是 MCU 的 Flash 和 RAM 得够,LWM2M 协议栈加上 CoAP 那套东西不是省油的灯。我一般会先算一下 MCU 的资源余量,如果 Flash 剩不到 128KB,就别硬塞了。
如果 SDK 移植到 NB 通信模组,MCU 侧只需要写应用逻辑,通过 AT 指令跟模组交互。文档里列了一批已经完成移植的模组型号,比如 M5310、N700-CN、BC95、ML5330、SIM7000C-N、IM2209。选这条路的前提是你用的模组在列表里,或者模组厂商愿意帮你移植。不在列表里的模组,得走商务渠道去推,周期不好控。
提示:模组侧移植的 SDK 版本要和平台侧文档版本对齐,2018 年初版文档对应的 SDK 跟后期版本在资源模型支持上有差异,别混用。
2.2 NB 芯片 SOC 架构的移植边界
NB 芯片架构就是把 SDK 直接塞进芯片里,芯片自己跑应用、跑协议栈、跑驱动。文档里列了 RoseFinch7100、MDM9206、MT2625、Boudica120、Boudica150 这几款。这条路适合产品形态极度紧凑、成本敏感的场景,比如模组都嫌大的那种。但坑在于:芯片厂商的 SDK 移植进度参差不齐,有的只给了半成品,抽象接口实现得你自己补。我见过一个项目,芯片厂给的 SDK 里 CoAP 块传输没实现完整,结果设备上传超过 1KB 的数据就断,查了一周才定位到。
选型逻辑很简单:MCU 资源够、想自己控协议栈,就选 MCU 侧移植;模组在认证列表里、想快速出原型,就选模组侧移植;产品对体积和成本有硬约束,再考虑芯片侧。别为了省一颗 MCU 去选芯片方案,除非你确认芯片厂的 SDK 已经有人量产验证过。
2.3 移植完先别急着连平台,本地自检做这三步
SDK 移植完,别直接上平台。先做本地自检:第一步,确认 SDK 的抽象接口都实现了,特别是网络收发、定时器、内存分配这三个,缺一个后面都跑不起来。第二步,用串口把 SDK 的日志级别开到 Debug,看初始化流程有没有报错。第三步,模拟一个 CoAP 请求发到本地回环,确认协议栈能正常打包和解包。
// SDK 初始化后,检查关键接口是否返回成功 int ret = lwm2m_init(&ctx, &config); if (ret != 0) { printf("lwm2m_init failed: %d\n", ret); // 常见原因:内存池不够、回调函数未注册 return -1; } // 注册设备读写执行回调,缺了平台下发命令时无法响应 lwm2m_register_callbacks(&ctx, read_cb, write_cb, exec_cb);这段代码里,lwm2m_init的返回值必须查,很多人直接忽略,结果设备登录不上平台还找不到原因。config里一般要配 endpoint name、lifetime、服务器地址。lwm2m_register_callbacks注册的三个回调,读回调负责平台读资源时返回当前值,写回调负责平台写资源时更新本地状态,执行回调负责平台下发命令时触发动作。少注册一个,平台侧就会显示设备在线但资源不可操作。
3. 平台侧创建产品与设备:资源模型和鉴权参数怎么填
3.1 创建产品时三个选项不能选错
进入开发者中心后,点“创建产品”,有三个选项直接决定后面能不能走通:设备接入方式选“公开协议”,联网方式选“NB-IoT”,设备接入协议选“LWM2M”。这三个选错了,后面设备登录会一直失败,而且报错信息不会告诉你选错了协议。
产品创建完,点“立即添加设备”,填设备名称、IMEI、IMSI。这里有个关键选项:“是否开启自动订阅”。如果选“是”,终端设备创建的资源信息会自动同步到平台侧虚拟设备的资源列表里。如果选“否”,你得手动在平台上配资源,或者通过 API 去写。我一般建议开自动订阅,省事,但前提是终端侧的资源模型定义得跟平台侧预期一致。
注意:IMEI 和 IMSI 要跟终端设备里配的完全一致,平台侧靠这两个做鉴权。填反了或者填错一位,设备登录请求会被平台拒掉,日志里只显示“注册码验证失败”,不会告诉你具体哪个参数错了。
3.2 LWM2M 资源模型:Object/Instance/Resource 三层怎么落地
OneNET 平台支持 LWM2M 和 IPSO 定义的资源模型,结构是 Object/Instance/Resource 三层。Object 表示某类传感器,比如 3311 是 Light Control,3200 是 Digital Input。Instance 是同一类传感器的数量,比如三个灯就是 Instance 0/1/2。Resource 是传感器具体属性,比如 5851 是 Dimmer(整数 0-100,可读可写),5850 是 On/Off(布尔,可读可写)。
终端侧创建设备后,要按这个结构去创建 Object、Instance、Resource。平台登录成功后会下发 Observer 和 Discover 消息,SDK 自动处理,你不需要手动回。但资源 ID 和类型必须跟平台侧预期一致,否则平台侧显示的资源列表会是空的或者类型不对。
| Resource ID | 描述 | 类型 | 访问权限 |
|---|---|---|---|
| 5851 | Dimmer | Integer 0-100 | R,W |
| 5850 | On/Off | Boolean | R,W |
这张表是文档里给的示例,实际项目里你得根据传感器类型去 IPSO 或 LWM2M 标准里查对应的 Object ID 和 Resource ID。别自己编 ID,平台侧不认。
3.3 设备登录成功的标志:灰色变绿色
终端侧完成 SDK 初始化、设备创建、资源创建后,向平台发登录请求。平台验证注册码里的参数,通过就返回登录成功,设备名称左边的圆形图标从灰色变绿色。如果参数错误或超时,返回登录失败,图标保持灰色。
登录成功后,平台会向设备下发 Observer 和 Discover 消息。Observer 是观测请求,SDK 收到后自动维护观测记录。Discover 是通知 SDK 获取指定 Object 的属性,SDK 自动反馈。你不需要写代码处理这两条消息,但得确认 SDK 版本支持自动处理,早期版本可能需要手动回。
# 用 AT 指令查模组侧 SDK 版本,确认支持自动处理 Observer/Discover AT+ONETVER? # 返回示例:+ONETVER: 1.2.0 # 如果版本低于 1.1.0,Observer 消息可能需要手动回这个命令因模组厂商而异,有的用AT+CGMR查固件版本,有的用厂商自定义指令。查不到就翻模组手册,别瞎试。
4. 第三方应用开发:数据推送和 API 调用怎么配
4.1 数据推送的 URL&Token 验证流程
第三方应用要跟 OneNET 平台交互,先得建立连接。流程是:平台向第三方应用的 URL 发验证请求,第三方应用验证 Token 后返回验证消息,平台收到后连接建立。之后平台收到设备数据,就推送到这个 URL。
平台侧配置界面要填 URL、Token、消息加解密方式。Token 可以自定义,用于握手验证。加解密方式有明文模式和安全模式,安全模式下要配 AESKey,应用程序里用这个 Key 解密消息。推送时间限制和推送数据限制用来做批量推送,减少服务器压力。不设就实时推。
文档里提到平台提供 Java、PHP、Nodejs、C# 四个版本的接入验证 SDK。我一般用 Nodejs 版做快速验证,因为起一个 HTTP 服务最快。
// Nodejs 接入验证示例:校验 Token 并返回验证消息 const crypto = require('crypto'); const express = require('express'); const app = express(); app.use(express.json()); const TOKEN = 'your_custom_token'; const AES_KEY = 'your_aes_key_32_bytes'; app.post('/onenet/callback', (req, res) => { const { msg, nonce, signature } = req.body; // 验证签名:sha1(sort(TOKEN, nonce, msg)) const raw = [TOKEN, nonce, msg].sort().join(''); const calcSig = crypto.createHash('sha1').update(raw).digest('hex'); if (calcSig !== signature) { return res.status(401).send('invalid signature'); } // 验证通过,返回 msg 原文 res.send({ msg }); }); app.listen(3000, () => console.log('callback server running on 3000'));这段代码里,signature的算法是平台侧定的,文档里写的是sha1(sort(TOKEN, nonce, msg)),但不同版本可能有差异,以你拿到的 SDK 为准。AES_KEY在安全模式下用来解密消息体,明文模式下不需要。返回的时候要把msg原样返回,平台侧校验通过后才算连接建立。
4.2 API 接口调用:创建设备、读写资源、下发命令
平台提供了一套 HTTPS API,第三方应用通过调这些接口完成设备管理和数据读写。文档里列了创建设备、查看单个设备信息、删除设备、读设备资源、写设备资源、下发命令、获取资源列表、订阅、离线命令、触发器、批量查询设备状态、批量查询设备最新数据、查看数据点等接口。
调 API 之前先拿 API Key,在开发者中心里生成。请求头里带api-key,请求体按接口文档拼 JSON。
# 查看单个设备信息 curl -X GET "https://api.heclouds.com/devices/{device_id}" \ -H "api-key: your_api_key" # 写设备资源:向 3311/0/5851 写值 50 curl -X POST "https://api.heclouds.com/devices/{device_id}/datapoints" \ -H "api-key: your_api_key" \ -H "Content-Type: application/json" \ -d '{"datastreams":[{"id":"3311_0_5851","datapoints":[{"value":50}]}]}'第一个命令查设备信息,返回里包含在线状态、资源列表、最后活跃时间。第二个命令写资源,datastreams里的id格式是ObjectID_InstanceID_ResourceID,值按 Resource 类型填。写成功后平台会向设备下发写请求,设备侧写回调触发,更新本地状态后返回。
提示:API 调用频率有限制,批量查询接口就是用来降低调用频次的。别在循环里单条查设备状态,用批量接口一次查一批。
4.3 触发器:让平台主动告诉你设备异常
触发器是平台侧的条件判断,当数据流满足条件时,平台主动推送通知到第三方应用。比如温度超过 80 度就推一条告警。配置触发器要选数据流、设阈值、选触发方式(大于、小于、等于等),然后配通知地址。
触发器适合做告警和联动,但别把它当实时控制用。平台侧触发到推送有延迟,秒级到分钟级不等,取决于推送配置。实时控制还是走 API 下发命令。
5. 避坑与排查:设备登录不上、资源不显示、数据推不过来
5.1 设备一直灰色,登录请求被拒
现象:终端侧发登录请求,平台侧设备图标一直灰色,日志显示“注册码验证失败”。
原因:IMEI 或 IMSI 跟平台侧填的不一致,或者 endpoint name 格式不对。还有一种情况是 SDK 里的服务器地址配错了,请求根本没到 OneNET。
解决:先核对平台侧设备的 IMEI/IMSI 跟终端侧代码里配的是否完全一致,包括大小写。再抓包看登录请求有没有发出去,目标地址是不是 OneNET 的接入地址。如果地址对、参数对,还是失败,查 SDK 版本跟平台文档版本是否匹配。
5.2 设备在线但资源列表是空的
现象:设备登录成功,图标绿色,但平台侧资源列表里什么都没有。
原因:终端侧创建 Object/Instance/Resource 时用的 ID 不在平台支持的模型里,或者自动订阅没开,平台没收到资源上报。
解决:先确认平台侧产品创建时“是否开启自动订阅”选了“是”。再查终端侧资源 ID 是不是 LWM2M 或 IPSO 标准里定义的,别自己编。如果都对了,看 SDK 日志里 Discover 消息有没有正常回,回的内容里资源列表是不是空的。
5.3 数据推送收不到,回调 URL 没反应
现象:平台侧配置了推送 URL,设备也有数据上报,但第三方应用收不到推送。
原因:URL&Token 验证没通过,平台侧连接没建立。或者推送时间限制和推送数据限制设得太高,数据攒着没推。
解决:先看平台侧配置界面连接状态是不是“已连接”。如果不是,检查 Token 是否一致、验证程序返回的 msg 是否正确。如果是“已连接”但收不到数据,把推送时间限制和推送数据限制设小一点,或者不设,让平台实时推。再不行就在回调入口打日志,看请求有没有进来。
5.4 写资源成功但设备侧没反应
现象:调 API 写资源返回成功,但设备侧状态没变。
原因:设备侧的写回调没注册,或者注册了但没处理对应的 Resource ID。
解决:查 SDK 初始化时lwm2m_register_callbacks有没有传写回调。再查写回调里有没有按 Resource ID 分支处理,别只处理了一个 ID 就返回。平台下发的写请求里带 Resource ID,回调里要根据 ID 去更新对应的本地变量。
5.5 离线命令下发后设备上线没执行
现象:设备离线时通过 API 下发了命令,设备上线后没执行。
原因:离线命令有有效期,过期了平台就不推了。或者设备上线后没有订阅对应的资源,平台不知道往哪推。
解决:查离线命令的有效期设置,别设太短。设备上线后确认订阅关系建立了,平台侧资源列表里能看到订阅标记。如果还不行,查 SDK 版本是否支持离线命令的缓存和重推。
6. 进阶技巧:用批量查询和触发器把运维成本降下来
设备量上来之后,单条 API 查询会把调用配额吃光。批量查询设备状态和批量查询设备最新数据这两个接口就是用来解决这个问题的。一次请求可以查一批设备,返回里包含在线状态和最新数据点。我一般会写一个定时任务,每 5 分钟批量查一次,把结果写进本地库,前端从本地库读,不直接打 OneNET API。
import requests import json API_KEY = 'your_api_key' DEVICE_IDS = ['dev1', 'dev2', 'dev3'] # 替换成实际设备 ID 列表 def batch_query_status(device_ids): url = 'https://api.heclouds.com/devices/status' headers = {'api-key': API_KEY} params = {'devIds': ','.join(device_ids)} resp = requests.get(url, headers=headers, params=params) if resp.status_code != 200: print(f'query failed: {resp.status_code}') return None return resp.json() def batch_query_latest(device_ids): url = 'https://api.heclouds.com/devices/datapoints' headers = {'api-key': API_KEY} params = {'devIds': ','.join(device_ids)} resp = requests.get(url, headers=headers, params=params) if resp.status_code != 200: print(f'query failed: {resp.status_code}') return None return resp.json() if __name__ == '__main__': status = batch_query_status(DEVICE_IDS) print(json.dumps(status, indent=2, ensure_ascii=False)) latest = batch_query_latest(DEVICE_IDS) print(json.dumps(latest, indent=2, ensure_ascii=False))这段代码里,devIds参数是逗号分隔的设备 ID 列表,一次别超过 100 个,超了平台会截断。返回的 JSON 里,status接口给在线状态和最后活跃时间,datapoints接口给每个设备的最新数据点。拿到之后写本地库,前端查本地库,响应快还不吃 API 配额。
触发器那边,我一般用来做两级告警:一级是设备数据超阈值,触发器直接推告警到应用;二级是设备离线超过 10 分钟,用批量查询定时任务发现后本地生成告警。这样平台侧触发器只负责数据异常,离线检测走本地逻辑,减少平台侧配置复杂度。
还有一个技巧是资源模型的复用。同一类传感器用同一个 Object ID,不同设备用不同 Instance,平台侧资源列表会自动按设备区分。别给每个设备建一套自定义 Object,后期维护会疯。
从那以后我每次接 NB 设备,都先把资源模型表拉出来对一遍,确认 Object/Instance/Resource 三层跟平台侧预期一致,再动手写代码。这个习惯帮我省掉了至少两次大规模返工。希望帮到你。
本文还有配套的精品资源,点击获取