2. 测量OTA下载能耗基线
任务目标
在标准Wi-Fi条件下,使用功耗分析仪测量IoT终端下载1MB固件所消耗的能量,记录峰值电流、平均电流、下载耗时与总能耗,建立OTA下载能耗基线数据,为后续优化提供对比基准。
测量环境与设备
- 被测设备:ESP32-S3开发板(Wi-Fi 802.11 b/g/n模式)
- 功耗分析仪:Nordic Power Profiler Kit 2(PPK2),或同等支持电流采样率≥100kSPS的设备
- 固件服务器:本地HTTP服务器(如Python http.server),确保Wi-Fi信号强度在-45~-55 dBm范围内
- 固件文件:1MB随机数据文件(firmware.bin),放置于服务器根目录
测量原理
功耗分析仪串联在设备供电回路中,以固定采样率(本案例设为100kSPS)连续记录电流与电压。通过积分电流×电压×时间得到总能耗。测量分为两个阶段:空闲基线(Wi-Fi连接保持但不传输数据,持续5秒)和下载阶段(从HTTP服务器拉取1MB固件)。两阶段能耗差值即为OTA下载净增量能耗。
关键完整代码
// main.c - ESP32-S3 OTA下载能耗测量固件
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_http_client.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#define FW_URL "http://192.168.1.100:8000/firmware.bin"
#define FW_SIZE (1024 * 1024) // 1MB
#define GPIO_TRIGGER GPIO_NUM_4 // 测量触发引脚,接功耗分析仪数字输入
static void wifi_init(void) {
nvs_flash_init();
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
wifi_config_t wc = {.sta = {.ssid = "TEST_AP", .password = "12345678"}};
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wc);
esp_wifi_start();
esp_wifi_connect();
vTaskDelay(pdMS_TO_TICKS(3000)); // 等待DHCP获取IP
}
static void http_download(void) {
esp_http_client_config_t cfg = {
.url = FW_URL,
.method = HTTP_METHOD_GET,
.timeout_ms = 30000,
};
esp_http_client_handle_t client = esp_http_client_init(&cfg);
esp_http_client_open(client, 0);
int len = esp_http_client_fetch_headers(client);
int total = 0;
char buf[1024];
gpio_set_level(GPIO_TRIGGER, 1); // 拉高触发引脚,标记下载开始
while (total < FW_SIZE) {
int r = esp_http_client_read(client, buf, sizeof(buf));
if (r <= 0) break;
total += r;
}
gpio_set_level(GPIO_TRIGGER, 0); // 下载完成,拉低触发引脚
esp_http_client_close(client);
esp_http_client_cleanup(client);
printf("Downloaded: %d bytes\n", total);
}
void app_main(void) {
gpio_config_t io = {.pin_bit_mask = 1ULL << GPIO_TRIGGER,
.mode = GPIO_MODE_OUTPUT};
gpio_config(&io);
gpio_set_level(GPIO_TRIGGER, 0);
wifi_init();
vTaskDelay(pdMS_TO_TICKS(2000)); // 等待Wi-Fi稳定
// 阶段1:空闲基线(5秒),功耗分析仪记录
printf("Idle phase start\n");
vTaskDelay(pdMS_TO_TICKS(5000));
printf("Idle phase end\n");
// 阶段2:下载1MB固件
printf("Download phase start\n");
http_download();
printf("Download phase end\n");
vTaskDelay(pdMS_TO_TICKS(1000)); // 等待数据落盘
}
测量步骤
- 硬件连接:将PPK2的VOUT接入ESP32-S3的3.3V供电端,GPIO4接PPK2的数字输入通道(用于事件标记)
- 启动服务器:在PC上执行
python3 -m http.server 8000,确保firmware.bin位于当前目录 - 配置PPK2:采样率设为100kSPS,量程选择合适档位(建议100mA或1A),开启数字输入记录
- 烧录并运行:
idf.py flash monitor,观察串口输出"Idle phase start/end"与"Download phase start/end" - 数据导出:在PPK2软件中标记两个阶段的时间区间,导出CSV数据
数据处理与基线计算
# analyze.py - 计算能耗基线
import csv
import sys
def calc_energy(csv_path, start_s, end_s):
"""计算指定时间段的能耗(单位:mWh)"""
energy = 0.0
peak_current = 0.0
count = 0
with open(csv_path) as f:
reader = csv.DictReader(f)
for row in reader:
t = float(row['time_s'])
if start_s <= t <= end_s:
v = float(row['voltage_V'])
i = float(row['current_A'])
dt = 1.0 / 100000 # 100kSPS
energy += v * i * dt / 3600 * 1000 # 转为mWh
peak_current = max(peak_current, i)
count += 1
return energy, peak_current, count / 100000 # 返回能耗、峰值电流、时长
# 使用示例(根据PPK2导出的实际时间戳填写)
# idle_energy, idle_peak, idle_time = calc_energy('dump.csv', 5.0, 10.0)
# dl_energy, dl_peak, dl_time = calc_energy('dump.csv', 10.2, 15.8)
# print(f"下载净能耗: {dl_energy - idle_energy:.3f} mWh")
验证方法与通过标准
- 功能验证:串口输出"Downloaded: 1048576 bytes",确认1MB固件完整下载
- 数据有效性:PPK2记录中GPIO4信号在下载阶段为高电平,且下载阶段时长与串口时间戳误差<0.5s
- 基线合理性:下载阶段平均电流应明显高于空闲阶段(通常3~10倍),总能耗在10~50mWh范围内(视Wi-Fi环境与设备功耗等级而定)
- 可重复性:连续测量3次,能耗值偏差不超过±15%
常见问题
- 下载失败:检查服务器IP与端口,确认ESP32能ping通服务器
- 电流超量程:PPK2量程调至1A档,或降低采样率至50kSPS
- GPIO信号毛刺:在GPIO4与GND之间并联100nF电容滤波