3. 节点角色分配:Friend节点与Low Power节点配置

高密度网络里,功耗和吞吐量永远是一对矛盾体。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。

避坑提醒: 我曾经在一个项目中,Friend节点同时服务了20个LPN,每个队列设了12条消息。运行三天后,Friend节点频繁重启。排查发现是消息堆积导致内存碎片化,最终触发了看门狗复位。后来我把队列统一降到6条,问题解决。

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,紧急情况需要低延迟
经验之谈: 我习惯在LPN里加一个动态唤醒策略。平时按5秒轮询,如果检测到事件(比如门磁被触发),立即切换到200ms轮询,持续30秒后再恢复。这样既省电,又保证了关键事件的实时性。

3.4 Friend节点与LPN的配对流程

配对流程看起来简单,但每一步都有讲究。

  1. LPN发送Friend请求:广播一条Friend Request消息
  2. FN回复Friend Offer:携带自己的能力信息(队列大小、接收窗口等)
  3. LPN选择FN:根据Offer信息选择一个最合适的
  4. LPN发送Friend Poll:周期性唤醒后发送Poll消息
  5. FN回复Friend Update:推送缓存消息或空包

这里有个细节:LPN可以收到多个Friend Offer。它怎么选?我一般看两个指标:接收窗口大小和信号强度。接收窗口大的FN,容错性更好;信号强的FN,通信更稳定。

关键参数: Friend节点在Offer中会携带Friend Queue Size和Receive Window。LPN应该优先选择Queue Size大且Receive Window适中的FN。Queue Size太小容易丢消息,Receive Window太大则LPN功耗高。

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个队列位置
}
注意: 动态调整不能太频繁。我建议每10分钟评估一次,避免频繁调整导致队列抖动。另外,调整前要确保有足够的空闲内存,否则直接拒绝调整请求。

3.6 知识体系图

下面这张图展示了Friend节点和LPN的核心关系与配置要点。

Friend节点与LPN配置关系图 Friend节点与LPN配置关系 Friend节点 LPN 1(传感器) LPN 2(开关) LPN N(报警) 接收窗口:80-120ms 队列大小:4-16 轮询超时:1-30s 说明:LPN通过Poll消息唤醒,Friend节点根据配置参数推送缓存消息

3.7 避坑总结

最后,我把这些年踩过的坑整理一下。

  • 队列溢出: 别贪心,一个Friend节点服务10个LPN就够了,超过15个就要考虑分层
  • 接收窗口过小: 低于50ms在干扰环境下基本不可用,我建议至少80ms
  • 轮询间隔不合理: 所有LPN用同一个轮询间隔是偷懒的做法,应该根据设备类型差异化配置
  • 忽略消息优先级: 报警消息和心跳消息混在一起排队,后果很严重
我的习惯: 每次部署前,我都会用抓包工具抓一轮Friend节点和LPN的交互日志。重点看三个指标:Poll成功率、消息延迟、队列占用率。这三个指标正常,网络基本不会出大问题。