3. 高密度网络拓扑与节点角色规划
高密度网络里,功耗和吞吐量永远是一对矛盾体。Low Power Node(LPN)想省电,Friend Node(FN)就得替它扛活。这个机制说白了就是「你睡我守,你醒我交」。我最早接触这个设计时,总觉得Friend节点就是个消息中转站,后来踩了坑才明白——配置不当,Friend节点反而会成为网络瓶颈。
3.1 核心角色定义
先理清两个角色的职责。LPN是电池供电设备,大部分时间处于休眠状态。FN是常电设备,替LPN缓存消息。当LPN醒来时,FN把缓存的消息一次性推过去。
| 角色 | 供电方式 | 主要职责 | 典型设备 |
|---|---|---|---|
| Low Power Node | 电池 | 周期性休眠,醒来拉取消息 | 传感器、门锁、开关 |
| Friend Node | 市电/大容量电池 | 缓存消息,响应拉取请求 | 网关、中继器、智能灯 |
这里有个关键点:LPN只能和一个Friend节点建立关系。但一个Friend节点可以同时服务多个LPN。我在一个智能照明项目里,一个Friend节点挂了16个LPN传感器,结果Friend节点内存直接爆了。嗯,这个后面细说。
3.2 Friend节点配置要点
配置Friend节点,核心是三个参数:接收窗口、缓存大小、轮询超时。
3.2.1 接收窗口(Receive Window)
LPN醒来后,会打开一个接收窗口等待Friend节点推送消息。这个窗口太短,消息可能丢;太长,LPN功耗就上去了。
// 接收窗口配置示例
#define RECEIVE_WINDOW_MS 100 // 典型值:100ms
#define RECEIVE_WINDOW_MIN 10 // 最小值:10ms
#define RECEIVE_WINDOW_MAX 255 // 最大值:255ms
我个人习惯把接收窗口设在80-120ms之间。低于50ms,在干扰大的环境里丢包率会明显上升。我曾经在一个工厂项目里试过30ms,结果LPN每次醒来都收不到消息,白白浪费了三次重试才成功。
3.2.2 缓存大小(Friend Queue Size)
Friend节点为每个LPN维护一个消息队列。队列满了,新消息就会丢弃。这个值怎么设?
| LPN数量 | 建议队列大小 | 内存占用估算 |
|---|---|---|
| 1-5 | 8-16 | ~2KB |
| 6-15 | 4-8 | ~4KB |
| 16+ | 2-4 | ~6KB |
为什么LPN多了反而要减小队列?因为Friend节点的总内存是有限的。每个LPN的队列独立分配,总队列数 = LPN数量 × 队列大小。我见过一个项目,Friend节点配了32个LPN,每个队列16条消息,结果Friend节点启动后直接OOM。
3.2.3 轮询超时(PollTimeout)
LPN两次唤醒之间的间隔。这个值直接影响功耗和实时性。
// 轮询超时配置示例
#define POLL_TIMEOUT_MS 5000 // 5秒轮询一次
#define POLL_TIMEOUT_MAX 96000 // 最大96秒
你想想看,如果LPN是温度传感器,5秒轮询一次完全够用。但如果是门锁,用户按了门铃,你总不能等5秒才响应吧?所以门锁这类设备,轮询间隔要短,或者干脆不用LPN模式。
3.3 Low Power节点配置要点
LPN的配置相对简单,但有几个坑容易踩。
3.3.1 订阅与发布模型
LPN通常只订阅少量组地址,减少唤醒后的处理量。我建议LPN的订阅地址不超过3个。
// LPN订阅配置示例
// 只订阅两个组地址:控制组和状态组
uint16_t subscribe_addrs[] = {0xC001, 0xC002};
uint8_t subscribe_count = 2;
3.3.2 唤醒间隔策略
唤醒间隔不是越短越好。太短,LPN和普通节点没区别;太长,消息延迟无法接受。
- 传感器类: 5-30秒,数据变化不频繁
- 开关类: 1-3秒,用户操作需要快速响应
- 报警类: 200-500ms,紧急情况需要低延迟
3.4 Friend节点与LPN的配对流程
配对流程看起来简单,但每一步都有讲究。
- LPN发送Friend请求:广播一条Friend Request消息
- FN回复Friend Offer:携带自己的能力信息(队列大小、接收窗口等)
- LPN选择FN:根据Offer信息选择一个最合适的
- LPN发送Friend Poll:周期性唤醒后发送Poll消息
- FN回复Friend Update:推送缓存消息或空包
这里有个细节:LPN可以收到多个Friend Offer。它怎么选?我一般看两个指标:接收窗口大小和信号强度。接收窗口大的FN,容错性更好;信号强的FN,通信更稳定。
3.5 高密度场景下的优化策略
当网络里LPN数量超过50个时,Friend节点就成了关键瓶颈。我总结了几条优化策略。
3.5.1 分层Friend架构
不要把所有LPN挂到一个Friend节点上。把网络分成多个Friend域,每个域独立管理。
// 分层Friend架构示例
// 域1:传感器LPN → Friend节点A
// 域2:开关LPN → Friend节点B
// 域3:报警LPN → Friend节点C
// Friend节点A/B/C之间通过Subnet通信
3.5.2 消息优先级管理
Friend节点内部应该对消息做优先级排序。紧急消息优先推送,普通消息排队处理。
| 优先级 | 消息类型 | 处理策略 |
|---|---|---|
| 高 | 报警、控制指令 | 立即推送,不排队 |
| 中 | 状态更新、配置变更 | 下次Poll时优先推送 |
| 低 | 心跳、日志 | 按FIFO顺序推送 |
3.5.3 动态队列调整
Friend节点可以根据LPN的活跃度动态调整队列大小。活跃度高的LPN分配更多队列空间,活跃度低的LPN减少队列空间。
// 动态队列调整伪代码
if (lpn_poll_count > THRESHOLD_HIGH) {
increase_queue(lpn_id, 2); // 增加2个队列位置
} else if (lpn_poll_count < THRESHOLD_LOW) {
decrease_queue(lpn_id, 1); // 减少1个队列位置
}
3.6 知识体系图
下面这张图展示了Friend节点和LPN的核心关系与配置要点。
3.7 避坑总结
最后,我把这些年踩过的坑整理一下。
- 队列溢出: 别贪心,一个Friend节点服务10个LPN就够了,超过15个就要考虑分层
- 接收窗口过小: 低于50ms在干扰环境下基本不可用,我建议至少80ms
- 轮询间隔不合理: 所有LPN用同一个轮询间隔是偷懒的做法,应该根据设备类型差异化配置
- 忽略消息优先级: 报警消息和心跳消息混在一起排队,后果很严重