简介:《OneNET平台NB-IOT接入开发文档》面向使用中移物联网OneNET平台进行NB-IoT设备接入与第三方应用开发的嵌入式工程师、物联网开发者,提供从终端侧SDK移植到平台侧数据交互的系统性技术参考。压缩包内为1个docx文件,约1.73MB,内容按章节组织,涵盖接入总体流程、SDK移植到MCU/NB通信模组/NB芯片、创建产品与设备、登录订阅、API接口调用、明文与密文数据推送、MCU侧接入实例,以及LWM2M、CoAP协议栈与常见问题。已有1090人学习,适合在方案选型、联调对接和协议理解时对照查阅。文档对创建设备、读写资源、下发命令、离线命令、触发器、批量查询等接口均有展开,并给出数据推送SDK、NB-IoT API和开发板资料下载线索,可帮助读者快速搭建接入链路、定位平台配置与消息加密环节的问题。
1. 一块上电正常却始终离线的开发板
去年帮一个做智能照明的团队排查过一次 NB-IoT 接入:设备上电正常,模组 AT 指令回得干干净净,信号值也在正常区间,可 OneNET 平台侧的设备图标一直显示为离线状态。查到最后不是代码写错了,而是 SDK 的放置位置选错了——他们把基础通信套件放进了 MCU,却指望模组侧替他们处理 CoAP 报文。这类问题在 OneNET 平台 NB-IoT 接入中太常见了。这份 V0.1 版开发文档把流程拆得很细,真正卡人的其实就三件事:SDK 到底移植到哪一层、创建产品时那几个下拉框选什么、登录成功之后平台下发的 Observer 和 Discover 由谁来应答。装错一步,后面就要多调三天。下面按我实际跑通的顺序写,从架构选型、平台建产品、终端五步接入,一路写到联调避坑,适合手上已经有 NB 模组和开发板、准备把设备真正挂上平台的人。
2. SDK 移植与架构选型:三种落点决定后面所有工作量
动手前先把架构定下来,这一步选错,后面写的每一行代码基本都要返工。文档里把终端分成三类,判断标准只有一条:基础通信套件最终跑在哪颗芯片上。它跑在 MCU 上,模组就只是透传通道;跑在模组里,MCU 只负责调 AT 指令;跑在 NB 芯片里,那就是 SoC 方案,应用、协议栈、驱动全在一颗片子里。三种形态在平台侧看到的效果完全一样,但终端侧要写的代码量差三到五倍,所以先说清楚怎么选。
2.1 三种软硬件架构的对照与取舍
| 架构 | SDK 落点 | 终端侧要自己写的东西 | 典型场景 |
|---|---|---|---|
| MCU + NB 通信模组 | MCU | 设备应用、抽象接口实现、底层 OS 与驱动 | 已有成熟 MCU 代码,想复用外设驱动 |
| MCU + NB 通信模组 | NB 通信模组 | MCU 只写设备应用,通过 AT 指令操作资源 | 快速验证、小批量出货 |
| NB 芯片(SoC) | NB 芯片 | 应用、AT 封装、抽象接口、驱动全部在片内 | 成本敏感、体积敏感的量产 |
一个常见的误判是“反正模组支持 AT,那就让模组干活”。前提是这颗模组的固件里已经移植好接入平台的 SDK,并把资源操作封装成了 AT 指令;如果模组只提供标准的拨号与收发能力,那么 SDK 必须回到 MCU 侧,此时模组就是一根透传的串口线。反过来,如果 MCU 侧的外设驱动很重、Flash 也紧张,把 SDK 塞进 MCU 会挤掉应用空间,那就该选模组方案。
文档表 3-1 里列了一批已经完成 SDK 移植的模组型号,比如 BC95、N700-CN、M5310、ML5330、SIM7000C-N、IM2209 这类;表 3-2 里是已经完成移植的芯片,比如 RoseFinch7100、MDM9206、MT2625、Boudica120、Boudica150。挑型号时不要只看“支不支持”,还要确认模组出厂固件对应的 SDK 版本与文档描述的是同一套资源模型,否则你在平台侧建的 Object 和模组里写死的 Object 会对不上号,这是后面资源列表空白的头号原因。
注意:模组方案省掉的是协议栈移植工作,不是省掉联调。AT 指令的返回时序、超时重试、断线重连仍然要你在 MCU 侧兜住。
2.2 SDK 移植到 MCU:抽象接口和底层驱动得自己补齐
选 MCU 方案,工作量集中在四块:串口收发、底层 OS 适配、抽象接口实现、应用初始化。基础通信套件本身不关心你用的是什么 RTOS,它只要求你提供几个能力——单调递增的时间基准、内存分配、以及把一段字节发出去并收回来。我的习惯是先把串口链路跑通再动 SDK,不然后面出问题分不清是协议栈没跑起来还是串口根本没收着数。
先给一个移植顺序,按这个顺序做返工最少:串口能收到模组回显 → 能稳定发出 AT 并解析回包 → 把“发一包、收一包”封装成套件要求的发送接口 → 注册读、写、执行三个回调 → 最后跑创建设备和登录。
抽象接口这块,文档里明确要求实现读、写、执行三类回调,平台下发读资源、写资源、执行资源时,最终都落到这三个函数上。下面是 MCU 侧的骨架,函数名按你手上 SDK 头文件的实际命名替换,结构是一样的:
/* 1. 先把串口收发注册给基础通信套件,套件不关心底层是 UART 还是 SPI */ static int32_t port_send(const uint8_t *buf, uint32_t len) { /* 阻塞发送,超时时间要大于模组单包最大耗时,否则大包会被截断 */ return uart_write(buf, len, 3000); } /* 2. 读资源回调:平台来读 5851(Dimmer) 时被调用 */ static int32_t res_read_cb(uint16_t obj_id, uint16_t ins_id, uint16_t res_id, char *buf, uint32_t len) { if (obj_id == 3311 && res_id == 5851) { /* 把当前亮度值按文本写进 buf,返回实际写入长度 */ return snprintf(buf, len, "%d", g_light_level); } return -1; /* 不认识的资源一律返回错误,别静默返回 0 */ } /* 3. 写资源回调:平台下发开关指令时被调用,注意这里不要做耗时操作 */ static int32_t res_write_cb(uint16_t obj_id, uint16_t ins_id, uint16_t res_id, const char *buf, uint32_t len) { if (obj_id == 3311 && res_id == 5850) { g_switch = (buf[0] == '1' || buf[0] == 't'); return 0; } return -1; }这段代码里有两个参数值得多说一句。obj_id、ins_id、res_id三元组是资源在平台侧的唯一定位,回调里必须按三元组精确匹配,只判断res_id在单实例场景下能跑,一旦上了多实例(比如三个灯对应 Instance 0/1/2)就会串数据。另一处是返回长度,snprintf返回的是“本该写入的长度”,如果它大于len,说明缓冲区不够、内容被截断了,这种情况要返回错误让平台重读,而不是把截断的字符串当成功发出去。
底层 OS 与驱动部分,套件一般会要求一个毫秒级 tick,用于lifetime计时和重传超时。如果你用的是裸机主循环,就把 tick 挂在定时器中断里自增,别在回调里用delay死等——回调是在协议栈的上下文里被调用的,在里面阻塞会把整个 CoAP 收发卡住,表现出来就是“平台下发命令,设备半天不回,最后超时”。
2.3 SDK 移植到模组与芯片:AT 封装的边界在哪
模组方案里,MCU 侧只保留应用逻辑,所有资源操作都变成 AT 指令,典型形态是“创建设备 → 添加 Object/Instance/Resource → 登录 → 等平台下发”。这套东西的好处是 MCU 资源占用极小,几 KB RAM 就能跑;代价是排错时你只能看到 AT 的回显,看不到底层 CoAP 报文,出问题得靠模组厂商提供的日志口。我的做法是调试阶段务必把模组的协议栈日志打开,把 CoAP 的请求和应答打出来,等稳定了再关掉,不然一次登录失败能查一整天。
芯片方案更进一步,连 AT 指令都不用过,应用直接调 SDK 接口,抽象接口、驱动、协议栈都在片内完成。这条路前期最省事,后期最考验选型——一旦芯片侧的 SDK 版本跟不上平台侧的资源模型更新,你只能等厂商出新固件。所以走 SoC 路线的话,建议在项目启动阶段就把 SDK 版本号记录下来,后面平台侧行为有变化时第一时间能定位是不是版本对不上。
提示:三种架构在平台侧创建的设备参数完全一致,架构只影响终端侧代码。所以联调阶段可以先用模组方案把整条链路跑通,再决定量产要不要换 SoC。
3. 平台侧准备:创建产品、设备与资源模型
平台侧这几步看起来全是填表单,实际上每个选项都对应终端侧的一段代码,选错了不会立刻报错,只会在登录或者订阅阶段以一种很难查的方式失败。按文档的顺序走:注册账号、创建产品、添加设备、把鉴权参数抄回终端。
3.1 创建产品时的三个下拉框:公开协议、NB-IoT、LWM2M
注册账号这一步没什么技术含量,但有个细节值得提:文档建议规模化量产的产品以企业名义注册,个人版和企业版在功能上没有差异,差别在于后续的设备数量和商务流程。个人开发者验证阶段用个人账号完全够用。
创建产品时真正要盯的是三个选项:
| 选项 | 应选值 | 选错的后果 |
|---|---|---|
| 设备接入方式 | 公开协议 | 选私有协议后终端侧要走完全不同的鉴权流程,SDK 里的 LWM2M 逻辑用不上 |
| 联网方式 | NB-IoT | 影响平台对设备在线状态和离线命令的判定策略 |
| 设备接入协议 | LWM2M | 选了别的协议,平台不会下发 Observer 和 Discover,资源列表永远建不起来 |
这三个值在创建产品时就固定下来了,中途改不了,只能重建产品再把设备迁过去。我见过有人为了“先跑通再说”随手选了默认值,结果设备能登录、图标也变绿,但平台侧的资源列表一直是空的,回头重建产品、重新添加设备、重新把 IMEI 和 IMSI 写进终端固件,等于把接入流程重走一遍。所以创建产品时慢一点,把产品名称、行业、类别这些信息也填全,后面设备多了好管理。
3.2 添加设备:IMEI、IMSI 与自动订阅开关
产品建好后点“立即添加设备”,要填的字段不多,但每一个都会参与终端鉴权:
| 字段 | 说明 | 注意点 |
|---|---|---|
| 设备名称 | 平台侧显示名,仅用于识别 | 建议带上编号,和终端丝印对应 |
| IMEI | 模组的国际移动设备识别码 | 必须与终端实际上报的一致,一位不对就登录失败 |
| IMSI | 用户识别码 | 部分模组需要插卡后读取,别手抄错 |
| 是否开启自动订阅 | 终端创建的资源是否自动同步到平台 | 建议先开,联调期能少写不少代码 |
自动订阅这个开关值得单独说。开启之后,终端在本地创建的资源信息会自动更新到平台侧这台“虚拟设备”的资源列表下,你不用在应用里手工调订阅接口;关闭的话,平台只在收到终端主动上报时才更新资源,联调阶段很容易出现“终端明明建了 Object,平台侧看不见”的错觉。我的习惯是联调期一律开启,等资源模型稳定、要精确控制上报时机了再关掉手工管理。
添加完设备,还有一步容易被跳过:把平台侧这台虚拟设备的 IMEI 和 IMSI 回写到终端里,它们会作为登录时的 endpoint name 参与鉴权。文档里写得很直白,平台拿这两个值验证注册码里的参数,对不上就返回登录失败。所以终端固件里千万别把 IMEI 写死成某个常量,从模组读、或者从配置区读,量产时才不会一批设备共用一个身份。
3.3 资源模型:Object/Instance/Resource 三层怎么摆
资源模型是 NB-IoT 接入里最抽象的一块,但理解了就一层窗户纸。平台支持 LWM2M 和 IPSO 两套标准定义,结构都是三层:
- Object(对象):表示某一类传感器,比如灯控、数字输入
- Instance(实例):同一类传感器的数量,三个灯就是三个实例
- Resource(属性):传感器的具体特性,比如亮度值、开关状态
文档里给的例子是 Object ID=3311(IPSO Light Control),Instance=0/1/2 三个实例,每个实例下挂 Resource ID=5851(Dimmer)和 5850(On/Off)。这两个资源的定义如下:
| Resource ID | 描述 | 类型 | 访问方式 |
|---|---|---|---|
| 5851 | Dimmer(亮度) | Integer,取值 0-100 | 读、写 |
| 5850 | On/Off(开关) | Boolean | 读、写 |
另一类常见对象是 3200(IPSO Digital Input),用于开关量输入。选哪套对象不用纠结,先看你的传感器能不能在 IPSO 定义里找到对应的 Object ID,找得到就直接用标准 ID,平台侧和第三方应用侧都能直接识别;找不到再自定义,自定义时把 Object ID 段避开标准已占用的区间,免得后面和平台更新撞车。
这里有个坑要提前说:资源模型是终端和平台之间的约定,终端侧建了 3311/0/5851,平台侧就必须能查到同样的三元组。如果终端侧建的是自定义 ID,而平台侧按 IPSO 标准去解析,结果就是资源列表能看到对象、点进去没数据。联调时先把终端侧要建的三元组列成一张表,和平台侧的预期对一遍,比事后抓包快得多。
4. 终端接入五步:从初始化到平台订阅发现
平台侧准备完,回到终端。文档把接入拆成五步:软硬件初始化、创建设备及资源、登录平台、平台订阅、发现设备资源。后两步是平台主动发起的,终端只要正确应答,判断接入成功的标志就是收到平台下发的 Observer 和 Discover 消息。
4.1 上电初始化:硬件、软件、通信套件的顺序
硬件初始化包括各模块上电、处理器寄存器初始化、工作模式配置;软件初始化包括基础通信套件初始化、应用接口初始化、应用程序初始化。顺序不能乱,尤其是套件初始化必须在串口和外设就绪之后,否则套件内部注册的发送接口指向的是一个还没打开的串口,后面登录请求发不出去,日志上只显示“发送超时”,很容易误判成网络问题。
我的顺序是这样的:时钟与电源 → 串口与 GPIO → 模组上电并等待注网 → 基础通信套件初始化(注册发送接口、tick、内存分配)→ 应用接口初始化 → 进入创建设备流程。注网这一步别省,模组没注上网就发登录请求,CoAP 报文发不出去还会消耗重传次数,等注网成功了反而要等重传周期结束。
4.2 创建设备与资源:lifetime 和三个回调怎么填
终端在应用里创建设备(dev),要配置四样东西:接入机地址、endpoint name(也就是鉴权用的 IMEI、IMSI 这类信息)、lifetime、以及读/写/执行三个回调函数。设备创建完之后,还要在设备下创建 Object 以及对应的 Instance 和 Resource。下面这段是创建设备和资源的典型写法:
/* 1. 创建设备:lifetime 决定平台判定离线的宽限期,单位秒 */ dev_t *dev = onenet_dev_create(); onenet_dev_set_server(dev, ONENET_SERVER_ADDR); /* 接入机地址,按文档给的填 */ onenet_dev_set_endpoint(dev, g_imei, g_imsi); /* 鉴权三要素,与平台侧设备一致 */ onenet_dev_set_lifetime(dev, 86400); /* 建议不小于 3600,太短会频繁重登录 */ onenet_dev_set_callback(dev, res_read_cb, res_write_cb, res_exec_cb); /* 2. 在设备下挂对象、实例、资源,三元组要和平台侧对齐 */ obj_t *light = onenet_obj_create(dev, 3311); /* IPSO Light Control */ for (int i = 0; i < 3; i++) { ins_t *ins = onenet_ins_create(light, i); /* 三个实例,对应三路灯 */ onenet_res_create(ins, 5851, TYPE_INTEGER, ACC_RW); /* 亮度 */ onenet_res_create(ins, 5850, TYPE_BOOL, ACC_RW); /* 开关 */ } /* 3. 发起登录,返回值只代表请求是否发出,不代表平台已验证通过 */ onenet_dev_login(dev);参数里最需要斟酌的是lifetime。它决定平台在多长时间收不到终端消息后判定设备离线。设得太小,比如 60 秒,NB-IoT 的省电模式下终端可能长时间不发包,平台会反复把你踢成离线再等你重登录,日志上就是频繁的登录请求;设得太大,设备真的掉线了平台也发现不了,下发命令全部落到离线队列。文档里的接入实例没有强调这个值,我一般按业务上报周期来定,取上报间隔的 10 到 20 倍,常见区间是几小时到一天。
回调函数的三个参数前面已经写过,这里补一句执行回调:平台下发执行类资源操作时会走它,很多传感器用不上,但仍然要注册一个空实现并返回错误码,不要传 NULL——部分 SDK 内部不做空指针判断,登录阶段解析资源时会直接跑飞。
4.3 登录请求与平台下发 Observer/Discover
登录时终端把注册码上报给平台,平台验证注册码里的参数,参数错误或者超时都返回登录失败;验证通过返回登录成功。平台侧的直观表现就是设备名称左边的圆形图标从灰色变成绿色。这一步失败的原因九成集中在三处:endpoint name 和平台侧的 IMEI/IMSI 对不上、接入机地址填错、模组没注上网。排查就按这个顺序,先看 AT 的注网状态,再核对三要素,最后查地址。
登录成功后,平台会向设备下发 Observer 消息和 Discover 消息。Observer 是观测请求,套件收到后自动维护观测记录;Discover 是通知套件上报指定 Object 的属性,套件自动反馈。这两条消息由基础通信套件内部处理,应用层不用管,你只需要确认回调里没有拦截它们。我见过有人在底层发送接口里加了一段“只允许发送指定长度的报文”的过滤逻辑,结果把 Discover 的应答拦掉了,平台侧资源列表一直空白,查了两天才发现是自己写的过滤条件。
判断是否真的接入成功,不能只看图标变绿。完整的判断标准是三条同时成立:设备图标由灰变绿、平台侧资源列表出现你创建的 Object 及其属性、平台下发一次读资源操作后终端回调里有对应日志。少一条都说明链路只走通了一半。
5. 第三方应用接入与联调避坑:URL 验签、AESKey 与 API 调用顺序
设备能上报数据只是第一步,业务系统要拿到数据,还得把第三方应用接进来。这一章是整条链路上配置项最多、也最容易在细节上翻车的一段,我把接入验证、数据推送、API 调用分开写,最后集中列五个我实际踩过的坑。
5.1 接入验证程序:URL 和 Token 握手的三个必填项
第三方应用和平台之间走 HTTPS,前提是先建立连接。流程是三件事:平台上已经有一台能正常上报数据的 NB-IoT 设备、你已部署好验证接入的程序、平台侧正确配置了接收地址(URL)、Token 和其他参数。三者缺一不可,很多人卡在第二步——程序没部署到公网可达的地址,平台发起验证请求直接超时。
平台完成配置后会向第三方平台发起 URL 和 Token 验证,验证程序校验参数,通过后返回验证消息,平台收到就算握手成功。文档里提供了 Java、PHP、Nodejs、C# 四个版本的接入验证程序 SDK,可以直接拿来改。下面是 Nodejs 版本的校验逻辑骨架,参数名以你手上 SDK 示例为准:
// 平台带着 token、随机串和签名打过来,校验通过后回一段验证消息 const http = require('http'); const crypto = require('crypto'); const TOKEN = process.env.PUSH_TOKEN; // 与平台侧配置的 Token 完全一致,别带空格 http.createServer((req, res) => { const q = new URL(req.url, 'http://127.0.0.1').searchParams; const token = q.get('token'); // 平台带回的 Token const nonce = q.get('nonce'); // 随机串,参与签名 const sign = q.get('signature'); // 拼接顺序和摘要算法必须与 SDK 示例一致,改一个字符都验不过 const expect = crypto.createHash('md5') .update([TOKEN, nonce].sort().join('')) .digest('hex'); if (token === TOKEN && sign === expect) { res.end(nonce); // 原样返回随机串,平台据此判定握手成功 return; } res.statusCode = 401; res.end('verify failed'); }).listen(8080);这段代码里两个细节决定成败。一是拼接顺序,多数 SDK 的做法是把 Token 和随机串按字典序排序后拼接再摘要,顺序错了算出来的签名永远对不上;二是返回值,平台要的是原样回一段验证消息,不是回一个 JSON{"code":0}。第一次接的时候我就是回了 JSON,平台侧一直提示验证失败,日志里连个错误码都没有,纯靠对着 SDK 示例逐行比对才找出来。
5.2 数据推送:明文、安全模式与两个限流参数
平台侧的配置界面字段不多,但每一个都影响你后端的解析代码:
| 配置项 | 取值说明 | 影响 |
|---|---|---|
| URL | 第三方平台的接收地址 | 必须公网可达,且能处理平台发来的验证请求 |
| Token | 自定义字符串 | 参与握手校验,改完要同步改后端 |
| 消息加解密方式 | 明文模式 / 安全模式 | 安全模式需自定义 AESKey 或由平台随机生成 |
| 推送时间限制(秒) | 批量推送的时间窗口 | 不设则实时推送,设了会攒够时间再推 |
| 推送数据限制(个) | 批量推送的条数阈值 | 与时间限制配合,降低后端压力 |
推送时间限制和推送数据限制这两个参数是给高并发场景准备的。设备多、上报频繁的时候,实时推送会把后端打出一堆短连接,设上这两个值之后,平台会等时间或条数任一条件满足再批量推一次。代价是数据延迟变大,做实时控制类业务时不要开。我的经验是,联调阶段全部留空走实时推送,能最快看到数据;等设备量上来了再按后端的处理能力调这两个值,一般从 5 秒 / 100 条起步往上试。
安全模式下必须拿 AESKey 解密消息,这个 Key 要么自己填、要么平台随机生成,生成完要立刻抄走,页面刷新后不一定还能看到。密文消息的字段结构和明文是一致的,只是消息体被加密,所以别为了安全模式单独写一套解析逻辑,解密后复用明文那套就行。
5.3 API 接口的调用顺序与最小调用示例
平台给第三方应用封装了一批 API,覆盖设备管理和资源操作。文档 4.2 节列了十几个,常用的其实就这些:
| 接口 | 用途 | 什么时候用 |
|---|---|---|
| 创建设备 | 批量导入设备 | 量产前批量建号,别一台台手点 |
| 查看单个设备信息 | 查在线状态、鉴权信息 | 排障第一步 |
| 读 / 写设备资源 | 读写某个三元组的当前值 | 业务侧主动取值或下发 |
| 下发命令 | 向设备投递指令 | 立即执行类操作 |
| 离线命令 | 设备不在线时先缓存,上线后取 | 非实时场景 |
| 订阅 | 订阅资源变化 | 替代轮询 |
| 触发器 | 条件满足时触发动作 | 告警、联动 |
| 批量查询设备状态 / 最新数据 | 批量取状态和数据 | 大屏、看板 |
| 查看数据点 | 拉历史数据 | 报表、曲线 |
调用顺序上有个反直觉的地方:不要一上来就调“下发命令”。设备不在线时这个接口会失败或者落到离线队列里,你以为发出去了,其实设备根本没收到。正确顺序是先调“查看单个设备信息”确认在线,再决定走实时命令还是离线命令。下面是最小调用示例:
# 先确认设备在线,再读资源;域名和路径以文档 4.2 节为准 curl -s "https://{api-host}/devices/{device_id}" \ -H "api-key: {api-key}" # 读指定资源,注意三元组要拼全,只给 res_id 在某些接口上会返回全部资源 curl -s "https://{api-host}/devices/{device_id}/resources?obj_id=3311&obj_inst_id=0&res_id=5851" \ -H "api-key: {api-key}"api-key分账号级和设备级两种,账号级的权限大、能操作所有设备,设备级的只能操作单台。生产环境里给第三方应用配设备级 Key,别图省事全用账号级,一旦泄露影响面差一个数量级。另外这些接口都是 HTTPS,本地调试时不要图快改成 HTTP,平台侧不接受,报错信息还特别含糊。
5.4 五个让联调卡住的坑
第一个,平台点提交后一直提示验证失败。现象是平台侧配置页反复提示握手不成功,后端日志里能看到请求进来了。原因基本是验证程序返回的内容格式不对,或者签名算法和 SDK 示例有出入。解决办法是把 SDK 示例原样跑一遍,用假参数在本地打一次,确认能返回正确内容再换到真实环境;验证通过后再改代码,改动一次验一次。
第二个,设备图标变绿了,但资源列表是空的。现象是设备显示在线,点进去看不到 Object。原因有两个:平台侧添加设备时自动订阅没开,或者终端的 Discover 应答被吞了。先查开关,开关没问题就打开模组的 CoAP 日志,看平台下发 Discover 后终端有没有回 2.05 之类的成功响应,没有就是资源创建那一步的三元组和实际注册的不一致。
第三个,设备反复上下线。现象是平台侧图标一会儿绿一会儿灰,日志里频繁出现登录请求。原因是 lifetime 设得太短,或者模组在省电模式下长时间不发包被平台判超时。把 lifetime 调大,同时确认终端有没有按周期发更新消息,两者配合才能稳住在线状态。
第四个,平台收到推送但后端解不开。现象是 webhook 收到了请求,解析时报错或者拿到乱码。原因是平台侧开了安全模式,后端还按明文解析;或者 AESKey 抄漏了几位。先确认加解密方式,安全模式就老老实实解密,Key 从平台页面完整复制,前后空格都别留。
第五个,下发命令设备毫无反应。现象是接口返回成功,设备侧日志什么都没有。原因是命令进了离线队列,设备当时并不在线;或者设备在线但没订阅对应资源。先查在线状态,再确认订阅关系,最后才怀疑命令内容。这套顺序看着笨,但比一上来就怀疑协议栈省时间。
6. 装完之后:怎么确认这套接入是真的跑通了
接入这类环境最怕“看起来通了”。图标是绿的、日志有输出、接口返回 200,三样凑齐也可能只是半条链路在工作。我一般按下面这张清单逐项过,全部打勾才算收工。
| 验证项 | 操作 | 通过标准 |
|---|---|---|
| 设备在线 | 平台侧看设备图标 | 由灰变绿且稳定 10 分钟不掉 |
| 资源注册 | 展开设备资源列表 | 能看到 3311 下三个实例、每个实例下 5851 和 5850 |
| 平台读资源 | 平台侧发起读操作 | 终端回调有日志,平台侧显示当前值 |
| 平台写资源 | 平台侧写 5850 为开 | 终端设备实际动作,平台侧值同步更新 |
| 数据上报 | 终端主动上报一次 | 平台侧数据点里能查到这条记录 |
| 推送连通 | 本地起一个 HTTP 服务接收 | 收到平台推来的消息,字段完整 |
| API 取数 | 调查看数据点接口 | 能拉到刚才上报的那条数据 |
表格之外还有两个动作值得坚持做。一个是把整条链路断一次再恢复:拔掉模组电源,等平台把设备判成离线,再上电看它能不能自己重新登录、重新订阅,这一步能暴露大部分重连逻辑的缺陷。另一个是抓一次完整报文:从登录到订阅发现,把 CoAP 的请求应答全存下来,以后现场出问题,拿这份基线一对比就知道是哪一步变了。
血泪经验是,联调阶段千万别只在自己电脑上验证。我第一次跑通的时候,本地 curl 什么都对,结果第三方平台的服务器在另一个网段,平台的验证请求根本打不进去,白白多耗了半天。从那以后我每次接这类推送,都强制走一遍“外网地址 + 真实平台发起”的验证,本地能通不算数。这套流程跑顺之后,后面每加一台设备就是复制粘贴的事。希望帮到你。
本文还有配套的精品资源,点击获取