1. 为什么今天还要聊传统节电很多人一提到Wi-Fi省电第一反应就是现在都Wi-Fi 6/7了谁还关心Legacy Power Save。但我可以很负责任地说只要你还在做物联网设备、低功耗传感器、电池供电的摄像头、电子价签或者你手上有一堆老款手机、老款IoT模组要兼容传统节电机制就是你绕不过去的一道坎。它不像TWTTarget Wake Time那样光鲜也不像WMM-PS那样被厂商重点宣传但它是整个802.11节电体系的地基——后面所有的节电机制本质上都是在Legacy PS的框架上做加法。这篇文章我想把Legacy Power Save这件事从头到尾讲透。它是什么、AP和STA之间怎么协商、beacon里的TIM/DTIM到底怎么读、PS-Poll怎么发、为什么你的设备明明开了省电还是耗电、为什么有些AP下设备休眠后收不到包。这些内容在标准文档里都有但标准文档不会告诉你实际抓包时哪里容易看错、厂商实现有哪些坑、参数怎么调才不翻车。我做过几个低功耗Wi-Fi模组的项目踩过的坑足够写一篇长文这里一次性整理出来。先说清楚适用人群如果你是做IoT固件、Wi-Fi驱动、AP侧省电策略、或者网络协议分析的这篇内容可以直接拿去对照抓包如果你是刚接触802.11的开发者建议先把基础帧格式过一遍再来看不然TIM字段那部分会有点吃力。全文围绕Legacy PS的核心机制展开涉及原理、参数、实操、排查四个层面尽量做到看完就能上手验证。2. Legacy Power Save的整体设计与思路拆解2.1 它到底解决什么问题Wi-Fi有个天然的矛盾STA要省电就得关射频但关了射频就收不到包。Legacy PS的核心思路非常朴素——让STA在不需要收发包的时候把射频关掉AP帮它把下行包缓存起来等STA醒来再通知它来取。这个缓存通知的模型是整个机制的灵魂。具体来说STA通过发送一个特殊的管理帧告诉AP我要睡了然后关闭收发器进入doze状态。AP收到这个通知后会把发给这个STA的单播帧先存到缓存队列里不直接发。同时AP在每个beacon帧里维护一个叫TIMTraffic Indication Map的字段用位图的方式标记哪些关联的STA有缓存数据。STA周期性醒来听beacon看TIM里自己对应的bit有没有置1有就发PS-Poll去取没有就继续睡。这个设计的高明之处在于AP承担了缓存和通知的成本STA只需要周期性醒来听一个beacon。beacon是广播帧本来就一直在发STA听beacon的代价远小于一直开着射频等单播包。用一个生活类比你住酒店前台AP帮你收快递缓存下行帧你STA不用一直守在大堂只要每隔一段时间去前台看一眼公告板beacon里的TIM有你的快递就去取PS-Poll没有就回房间继续休息。2.2 为什么是传统节电叫它传统Legacy是相对于后来的增强机制而言的。Legacy PS有两个明显的局限一是只对单播帧有效组播和广播帧的处理是另一套逻辑靠DTIM二是唤醒粒度粗STA只能整体开关射频没法精细控制什么时候醒。后来的WMM-PSU-APSD引入了触发帧和传递帧的概念TWT更是让STA和AP约定具体的唤醒时间窗口本质上都是在Legacy PS的基础上做优化。但正因为它是基础所有Wi-Fi芯片都必须实现它所有AP都必须支持它。你在一个Wi-Fi 6 AP下抓包依然能看到STA发Null帧带PM1进入省电模式依然能看到PS-Poll。这就是为什么我说它是地基——上层机制可以不用但底层这套协商流程永远在跑。2.3 关键角色与状态机Legacy PS涉及两个状态Active模式和PS模式。STA通过数据帧或Null帧的Frame Control字段里的**Power Management位PM位**来切换状态。PM0表示activePM1表示进入PS。这里有个容易被忽略的细节PM位是每个帧都带的不是只在切换时带。STA发的每一个帧都在声明自己当前想处于什么状态。AP根据收到的帧的PM位来更新对这个STA的状态记录。所以你会看到STA进入PS后发的每个帧PM都还是1直到它想退出PS才发PM0的帧。AP侧维护的是一个关联STA列表每个STA有个AIDAssociation ID范围1到2007。TIM位图就是用AID做索引的第N个bit对应AIDN的STA。这个映射关系是理解TIM的前提后面讲TIM结构时会反复用到。3. 核心细节解析TIM、DTIM与PS-Poll3.1 TIM字段的结构与位图逻辑TIM是beacon帧里的一个信息元素Element ID 5结构如下字段长度含义Element ID1字节固定为5Length1字节后续长度DTIM Count1字节距离下一个DTIM还有几个beaconDTIM Period1字节DTIM周期几个beacon一个DTIMBitmap Control1字节bit0是组播指示bit1-7是bitmap offsetPartial Virtual Bitmap变长虚拟位图按offset偏移这里最绕的是Partial Virtual Bitmap。标准里TIM的完整位图是251字节对应2007个AID但beacon帧空间宝贵不可能每次都发251字节。所以标准允许只发有置位的部分用Bitmap Control里的offset字段告诉接收方这段位图从哪个AID开始。举个例子假设只有AID5和AID100有缓存数据。完整位图里bit5和bit100置1但中间bit6到bit99全是0。AP可以只发从AID5开始到AID100结束的这一段offset设为5实际编码是offset/2因为位图按字节对齐标准里offset字段的单位是2个AID。STA收到后用自己的AID减去offset算出自己对应位图里的第几个bit。注意Bitmap Control的offset字段编码的是偏移量除以2因为位图必须字节对齐。所以实际起始AID offset × 2。这个细节抓包时特别容易算错我第一次分析TIM时就在这里栽过算出来的bit位置总是差一位。3.2 DTIM组播和广播的唤醒信号单播帧靠TIM通知组播和广播帧靠DTIM。DTIM是TIM字段里的一个计数器DTIM Count从DTIM Period-1递减到0减到0的那个beacon就是DTIM beacon。AP在DTIM beacon之后会立即发送缓存的组播/广播帧所有处于PS模式的STA必须在DTIM beacon时醒来接收。这里的关键参数是DTIM Period它决定了组播帧的最大延迟。如果DTIM Period3意味着每3个beacon有一个DTIM组播帧最多延迟3个beacon间隔。beacon间隔通常是100ms所以最大延迟300ms。对于实时性要求高的组播应用比如某些音视频同步场景DTIM Period要设小对于纯省电场景设大一点能让STA睡更久。我见过一个典型翻车案例某低功耗传感器用组播接收配置下发DTIM Period被AP设成了10结果配置下发延迟接近1秒用户以为设备死机了。后来把DTIM Period调到2延迟降到200ms以内问题解决。DTIM Period是省电和实时性的直接权衡杠杆调这个参数前一定要想清楚业务对组播延迟的容忍度。3.3 PS-Poll帧取缓存的取件码STA在beacon里看到自己的TIM bit置1后要发PS-Poll帧去AP取缓存。PS-Poll是个控制帧Control Frame subtype是10结构很简单RA接收地址就是AP的MAC、TA发送地址STA自己的MAC、AID12位低12位是AID高4位保留。AP收到PS-Poll后有两种响应方式立即响应直接回一个缓存的帧More Data位1表示还有更多缓存。延迟响应先回ACK稍后再发数据帧。标准允许两种方式但实际实现里大多数AP用第一种。STA收到数据帧后看More Data位如果1就继续发PS-Poll取下一个直到More Data0。实操心得抓包时如果看到STA连续发多个PS-Poll但AP只回一个数据帧别急着怀疑抓包工具先看More Data位。很多AP实现是一个PS-Poll换一个帧STA要循环发。也有AP实现是一个PS-Poll把队列清空这时候More Data会在最后一个帧置0。两种都合规但行为不同调试时要区分。3.4 唤醒时机与beacon同步STA怎么知道什么时候该醒来听beacon靠TSF定时器同步。STA在关联时从AP的beacon或probe response里获取TSF值之后本地维护一个TSF定时器按beacon间隔推算下一个beacon的到达时间。STA在预计beacon到达前提前醒来提前量取决于晶振精度和射频启动时间听完beacon再决定继续睡还是发PS-Poll。这个提前量很关键。晶振精度差的模组比如便宜的32.768kHz晶振精度可能只有±50ppm在100ms的beacon间隔下累积误差可能达到几十微秒加上射频启动时间典型几百微秒到几毫秒提前量设小了会漏beacon设大了白耗电。一般建议提前量设在1到3个beacon间隔的1%到2%具体要实测。4. 实操过程从进入PS到收包的完整链路4.1 进入PS模式的帧交互假设一个STA已经关联上AP现在想进入PS模式。完整流程如下STA发送一个Null帧或QoS Null帧PM位设为1。Null帧没有数据载荷纯粹用来携带PM位通知AP状态变化。AP收到后回ACK并在内部把这个STA标记为PS状态。STA关闭射频进入doze。为什么用Null帧而不是数据帧因为数据帧可能触发AP的响应逻辑比如回数据或ACK后的其他动作而Null帧语义最干净——我没数据要发只是告诉你我要睡了。当然如果STA正好有数据要发也可以直接在数据帧里把PM置1发完就睡省一个帧。抓包验证时你在Wireshark里过滤wlan.fc.pwrmgt 1就能看到所有PM1的帧。正常情况下应该能看到STA发的Null帧Frame Control里Power Management位是1。4.2 醒来听beacon与TIM解析STA按预定时间醒来打开射频等待beacon。收到beacon后解析TIM元素读DTIM Count如果0说明这是DTIM beacon要准备接收组播帧。读Bitmap Control的offset算出位图起始AID。用自己的AID减去起始AID得到在位图里的bit位置。检查该bit是否为1。如果bit1发PS-Poll如果bit0继续睡到下一个beacon。这里有个实操细节STA醒来后不只听一个beacon通常要听完整的一整个beacon帧因为TIM元素可能在beacon的靠后位置。有些低功耗实现为了省电只解调beacon的前半部分就判断这是不对的——TIM元素的位置不固定必须完整接收。4.3 PS-Poll取包与More Data处理发PS-Poll后STA等待AP的响应。响应可能是数据帧直接携带缓存的数据看More Data位决定是否继续。ACKAP先确认稍后发数据。STA要保持在active状态等数据到达。More Data位在Frame Control里和PM位是同一个字节的不同bit。STA收到数据帧后如果More Data1说明AP还有缓存要再发PS-Poll如果0说明缓存清空可以回去睡了。注意More Data位在AP发的帧里才有意义STA发的帧里这个位是保留的。抓包时别把STA帧的More Data位当有效信息看。4.4 退出PS模式STA想退出PS发一个PM0的帧Null帧或数据帧都行AP收到后把STA标记为active之后的下行帧直接发不再缓存。这个切换是即时的不需要额外握手。有个容易混淆的点STA退出PS后之前缓存的帧怎么办标准规定AP在STA退出PS时应该把缓存帧发出去因为STA现在能收了。但实际实现里有些AP会等STA发PS-Poll才发有些AP直接发。这个行为差异在调试退出PS后收不到缓存包的问题时要注意。4.5 一个完整的抓包时序示例下面是我在一次实测中抓到的典型时序时间轴单位ms时间方向帧类型PM位说明0STA→APNull1进入PS0.5AP→STAACK-确认100AP→广播Beacon-TIM中STA对应bit0200AP→广播Beacon-TIM中STA对应bit1200.3STA→APPS-Poll-取缓存200.8AP→STAData-More Data0300AP→广播Beacon-TIM bit0STA继续睡这个时序里STA在t0进入PSt100醒来听beacon发现没数据继续睡t200发现bit1发PS-Poll取包取完继续睡。整个过程STA的射频只在t100和t200附近短暂开启占空比很低省电效果明显。5. 常见问题与排查技巧实录5.1 设备开了省电反而更耗电这是最常见的抱怨。原因通常有几个beacon间隔太短。如果AP的beacon间隔是100msSTA每个beacon都要醒来听醒来一次的开销射频启动解调解析可能就要几毫秒占空比下不来。这种情况下省电效果有限。解决办法是让AP把beacon间隔调大比如200ms或300ms但要注意这会影响所有STA的发现延迟。DTIM Period设得太小。DTIM Period1意味着每个beacon都是DTIMSTA每个beacon都要醒来处理组播等于没省。检查AP的DTIM配置非必要场景设成3到5。TIM位图频繁置位。如果AP有大量下行单播流量比如服务器频繁推送STA的TIM bit一直置1每个beacon都要发PS-Poll省电无从谈起。这时候要考虑应用层做聚合减少下行帧频率。模组射频启动时间过长。有些低成本模组从doze唤醒到能收包要几毫秒这个固定开销在beacon间隔短时占比很高。选型时要看datasheet里的wake-up time参数。5.2 休眠后收不到下行包排查顺序建议这样确认STA真的进入了PS。抓包看有没有PM1的Null帧AP有没有回ACK。确认AP在缓存。看beacon的TIM里STA对应bit有没有置1。如果一直不置1说明AP没缓存可能是AP没识别到STA的PS状态或者AP的缓存队列满了丢包。确认STA醒来时机对。如果STA的TSF同步漂移了可能错过beacon。检查STA的beacon间隔配置和AP是否一致。确认PS-Poll格式对。AID字段填错比如填了关联时的AID但AP那边记录变了会导致AP不响应。我遇到过一个案例某模组的PS-Poll里AID字段填的是0AP直接丢弃不响应。原因是驱动里AID变量在重新关联后没更新。这种问题抓包一看PS-Poll的AID字段就能定位。5.3 TIM解析错误导致误判前面提过offset编码的坑。再补充一个Bitmap Control的bit0是组播指示位不是位图的一部分。解析时要把bit0单独拿出来看1表示有组播缓存位图从bit1开始算。有些开发者把整个Bitmap Control字节当位图用结果全错。还有一个Partial Virtual Bitmap的长度是可变的不是固定251字节。解析时要按Length字段来不能假设长度。Wireshark会自动解析但自己写解析代码时要注意。5.4 常见问题速查表现象可能原因排查方法省电无效耗电高beacon间隔短/DTIM Period小/下行流量大抓包统计唤醒次数检查AP配置休眠后丢包AP未缓存/STA错过beacon/PS-Poll错误看TIM bit、TSF同步、PS-Poll AID组播收不到DTIM时未醒来/DTIM Period配置错抓包看DTIM beacon时刻STA是否在收退出PS后延迟大AP缓存未及时清空看退出PS后AP是否主动发缓存帧TIM解析错位offset计算错/bit0误用对照标准逐字节解析验证5.5 几个实测避坑技巧技巧一用固定AID做测试。调试TIM解析时让测试STA关联后固定用一个已知AID比如通过AP配置指定这样位图位置可预测容易验证解析逻辑对不对。技巧二抓包时同时抓空口和AP侧。空口抓包能看到帧交互AP侧日志能看到缓存队列状态两边对照能快速定位是STA没发PS-Poll还是AP没缓存。技巧三beacon间隔和DTIM Period一起调。单独调一个往往效果不好两个参数要配合业务场景一起优化。比如传感器场景可以beacon 200ms DTIM Period 5实时性要求高的场景beacon 100ms DTIM Period 2。技巧四注意厂商私有扩展。有些AP厂商在Legacy PS基础上做了私有优化比如动态调整缓存策略抓包时可能看到不符合标准时序的行为。遇到这种情况先查厂商文档别硬套标准。6. 参数调优与场景适配6.1 beacon间隔的选择逻辑beacon间隔直接决定STA的最小唤醒周期。间隔越短STA发现下行数据越快但唤醒越频繁越耗电。标准默认100ms实际部署要根据场景调实时性优先比如语音、控制指令100ms甚至更短接受较高功耗。省电优先比如传感器上报200ms到400ms牺牲一点延迟换续航。极端省电比如电池供电的电子价签可以到1秒但要注意标准上限和AP兼容性。计算省电效果可以用一个简单模型假设每次唤醒开销T_wake射频启动解调解析典型2到5msbeacon间隔T_bi则占空比约T_wake/T_bi。100ms间隔下占空比2%到5%400ms间隔下降到0.5%到1.25%。这个粗略估算能帮你快速判断参数是否合理。6.2 DTIM Period与组播延迟的权衡DTIM PeriodN意味着组播最大延迟N×T_bi。设N3、T_bi100ms最大延迟300ms。如果业务能容忍300ms就设3不能容忍就设1或2但省电效果打折。有个折中方案把组播业务改成单播。如果应用层能把配置下发从组播改成针对每个STA的单播就不受DTIM约束STA可以睡更久。代价是AP要发多份但低功耗场景下STA数量通常不多这个代价可以接受。6.3 不同场景的推荐配置场景beacon间隔DTIM Period说明电池传感器200-400ms3-5省电优先容忍秒级延迟智能门锁100-200ms2-3兼顾响应和续航语音设备100ms1-2实时性优先电子价签500ms-1s5-10极端省电天级更新这些是起点值实际要结合模组功耗曲线和业务SLA实测调整。我一般会做一个简单的功耗测试固定业务流量扫beacon间隔和DTIM Period的组合测平均电流画个表找最优点。7. 写在最后的几点实操体会Legacy PS这套机制看起来简单但真正做低功耗产品时细节决定成败。我个人最大的体会是省电不是靠一个参数调出来的是整条链路配合出来的。STA的射频启动时间、AP的缓存策略、beacon间隔、DTIM Period、业务流量模式任何一个环节没对齐省电效果都会大打折扣。还有一个容易被忽视的点测试环境要和真实环境一致。实验室里AP就在旁边信号好STA醒来一次就能收到beacon真实部署时信号弱可能要重传或错过beacon实际功耗比实验室高不少。做功耗评估时一定要在弱信号下也测一遍。最后分享一个小技巧如果你在调一个低功耗模组先把所有省电机制关掉测一个baseline功耗然后逐个打开先Legacy PS再WMM-PS再TWT每开一个测一次这样能清楚看到每个机制的贡献和副作用。这个方法论比直接上全套配置再调参高效得多。
阅读完成 · 觉得有帮助?