手把手教你用MicroPython给ESP32做个电量显示器(附OLED/ST7789驱动适配指南)

张开发
2026/5/23 2:03:21 15 分钟阅读
手把手教你用MicroPython给ESP32做个电量显示器(附OLED/ST7789驱动适配指南)
从零构建ESP32智能电量监测系统MicroPython实战指南项目背景与核心价值在物联网设备开发中电源管理一直是影响用户体验的关键因素。想象一下当你设计的便携式气象站突然断电或是智能花盆在关键时刻停止浇水——这些场景都凸显了电量可视化的重要性。本项目将带领开发者使用MicroPython为ESP32构建一套完整的电池监测解决方案重点解决三个核心问题如何准确采集电压信号、如何将原始数据转换为直观电量百分比以及如何适配不同显示模块实现可视化输出。不同于简单的代码示例堆砌本方案采用模块化设计思想包含硬件电路优化、软件滤波算法和显示驱动抽象层。我们将从电路设计开始逐步深入到MicroPython的ADC高级配置最终实现一个可复用的电池管理系统。这个系统特别适合需要长期运行的低功耗设备如环境监测传感器、可穿戴设备和移动机器人。1. 硬件设计与信号采集优化1.1 分压电路设计与计算ESP32的ADC输入范围限制在0-3.3V而典型锂电池的工作电压范围(3.0-4.2V)超出了这个范围。我们需要设计分压电路将电池电压降至安全测量范围电池电压测量电路 锂电池 → R1(100kΩ) → ADC引脚 ↓ R2(100kΩ) → GND电压分压公式为V_adc V_bat × (R2 / (R1 R2))实际编码时需要反向计算def read_voltage(adc_pin): raw adc_pin.read() v_adc (raw / 4095) * 3.3 # 12位ADC转换 return v_adc * 2 # 分压比为1/2因此乘以2得到实际电压注意电阻建议选用1%精度的金属膜电阻且并联0.1μF电容可有效抑制高频噪声1.2 ADC配置进阶技巧ESP32的ADC存在非线性问题通过以下配置可提升精度from machine import ADC, Pin adc ADC(Pin(34)) adc.atten(ADC.ATTN_11DB) # 设置0-3.3V量程 adc.width(ADC.WIDTH_12BIT) # 12位分辨率 # 软件校准参数需根据实际测量调整 CALIBRATION_OFFSET 0.02 CALIBRATION_GAIN 1.05 def calibrated_read(): raw adc.read() voltage (raw / 4095) * 3.3 * CALIBRATION_GAIN CALIBRATION_OFFSET return voltage * 2 # 考虑分压比ESP32 ADC使用黄金法则避免使用ADC2通道与WiFi冲突采样时短暂关闭WiFi可提高精度多次采样取中值可抑制突发噪声2. 电量算法与滤波处理2.1 电压-百分比转换模型锂电池放电曲线非线性特征明显我们采用分段线性插值法# 3.7V锂离子电池特征点 VOLTAGE_POINTS [ (3.00, 0), # 完全放电 (3.70, 20), # 典型工作电压 (3.90, 50), (4.10, 80), (4.20, 100) # 满电 ] def voltage_to_percent(voltage): for i in range(len(VOLTAGE_POINTS)-1): if VOLTAGE_POINTS[i][0] voltage VOLTAGE_POINTS[i1][0]: x1, y1 VOLTAGE_POINTS[i] x2, y2 VOLTAGE_POINTS[i1] return y1 (y2-y1)*(voltage-x1)/(x2-x1) return 0 if voltage VOLTAGE_POINTS[0][0] else 1002.2 软件滤波算法实现原始ADC数据通常包含噪声我们实现三级滤波移动平均滤波硬件友好class MovingAverage: def __init__(self, size5): self.size size self.buffer [] def update(self, value): self.buffer.append(value) if len(self.buffer) self.size: self.buffer.pop(0) return sum(self.buffer) / len(self.buffer)中值滤波抗脉冲干扰import uheapq class MedianFilter: def __init__(self, size5): self.size size self.window [] def update(self, value): self.window.append(value) if len(self.window) self.size: self.window.pop(0) return sorted(self.window)[len(self.window)//2]卡尔曼滤波动态系统适用class SimpleKalman: def __init__(self, process_noise0.01, measurement_noise0.1): self.Q process_noise self.R measurement_noise self.P 1.0 self.x 0 def update(self, z): # 预测 x_pred self.x P_pred self.P self.Q # 更新 K P_pred / (P_pred self.R) self.x x_pred K * (z - x_pred) self.P (1 - K) * P_pred return self.x3. 显示驱动抽象层设计3.1 统一显示接口为支持多种屏幕我们定义抽象基类class DisplayDriver: def __init__(self): pass def draw_battery(self, x, y, width, height, percent): raise NotImplementedError def show_text(self, text, x, y, color): raise NotImplementedError3.2 ST7789驱动实现针对240x240像素的ST7789屏幕import st7789 class ST7789Driver(DisplayDriver): def __init__(self): self.tft st7789.ST7789( spi_num1, width240, height240, dcPin(2), rstPin(3), csPin(5) ) def draw_battery(self, x, y, width, height, percent): # 绘制电池外框 self.tft.rect(x, y, width, height, st7789.WHITE) # 绘制电量填充 fill_width int((width - 4) * percent / 100) self.tft.fill_rect(x2, y2, fill_width, height-4, self._get_color(percent)) def _get_color(self, percent): if percent 60: return st7789.GREEN elif percent 20: return st7789.YELLOW else: return st7789.RED3.3 OLED驱动实现针对128x64的SSD1306 OLEDimport ssd1306 class OLEDDriver(DisplayDriver): def __init__(self): self.oled ssd1306.SSD1306_I2C(128, 64, I2C(0)) def draw_battery(self, x, y, width, height, percent): # OLED绘制简化版电量图标 self.oled.rect(x, y, width, height, 1) self.oled.fill_rect(x1, y1, int((width-2)*percent/100), height-2, 1)4. 系统集成与优化4.1 电源管理模块完整电池管理类的实现class BatteryMonitor: def __init__(self, adc_pin, display_driver): self.adc ADC(adc_pin) self.adc.atten(ADC.ATTN_11DB) self.filter SimpleKalman() self.display display_driver self.voltage_history [] def update(self): voltage self._read_voltage() percent voltage_to_percent(voltage) # 更新显示 self.display.draw_battery(200, 0, 30, 15, percent) self.display.show_text(f{percent:.0f}%, 170, 0) return percent def _read_voltage(self): raw self.adc.read() v_adc (raw / 4095) * 3.3 filtered self.filter.update(v_adc * 2) # 记录最近10次测量 self.voltage_history.append(filtered) if len(self.voltage_history) 10: self.voltage_history.pop(0) return sum(self.voltage_history) / len(self.voltage_history)4.2 低功耗优化策略采样频率动态调整def adaptive_sleep(current_percent): if current_percent 80: return 60000 # 高电量时每分钟采样一次 elif current_percent 30: return 30000 # 中等电量每30秒采样 else: return 10000 # 低电量每10秒采样深度睡眠模式import esp32 from machine import deepsleep def enter_deep_sleep(seconds): # 配置唤醒源定时唤醒或引脚触发 esp32.wake_on_ext0(pin Pin(0), level 0) deepsleep(seconds * 1000)5. 进阶功能扩展5.1 电量预测算法基于历史消耗率的预测模型class BatteryPredictor: def __init__(self): self.consumption_rates [] self.timestamps [] def update(self, current_percent): now time.time() if len(self.timestamps) 1: rate (self.percent_history[-1] - current_percent) / (now - self.timestamps[-1]) self.consumption_rates.append(rate) self.timestamps.append(now) self.percent_history.append(current_percent) def predict_remaining(self): if len(self.consumption_rates) 3: return None avg_rate sum(self.consumption_rates[-3:]) / 3 return self.percent_history[-1] / avg_rate / 3600 # 返回剩余小时数5.2 云端监控集成通过MQTT上报电量数据import umqtt.simple class CloudMonitor: def __init__(self, client_id, server): self.client umqtt.simple.MQTTClient(client_id, server) def connect(self): self.client.connect() def report_battery(self, percent, voltage): payload f{{percent:{percent},voltage:{voltage:.2f}}} self.client.publish(bdevice/battery, payload)6. 常见问题排查指南ADC读数不稳定检查电源是否干净建议使用LDO稳压器确保分压电阻连接可靠尝试增加软件滤波强度电量显示不准确重新校准电压分段点检查电池实际容量是否衰减验证分压电阻精度显示刷新闪烁降低刷新频率使用双缓冲显示技术检查SPI/I2C总线速度设置在最近的一个农业传感器项目中这套系统成功实现了6个月以上的稳定运行。关键发现是在高温环境下需要将电压下限从3.2V调整到3.3V以防止电池过早进入保护状态。

更多文章