
简介这份基于STM32的简易智能电表项目面向电子爱好者、嵌入式学习者及电力监测方向开发者解决家庭用电参数采集与计量问题。项目以STM32为主控通过电压电流传感器采集信号经ADC转换后计算功率、用电量及功率因数并涉及定时器、I2C、USART等外设的配合使用。压缩包内共440个文件约199.59MB以C/H源码、Keil工程文件、原理图与PCB设计、PDF文档、编译输出文件及日志为主还包含部分演示文稿和Excel数据表能完整呈现从硬件连接到软件实现的工程全貌。目前已有1357人学习下载。资源不仅包含STM32初始化配置、ADC采样、电力参数计算等核心代码还提供原理图和PCB布局便于读者对照理解系统架构。对想深入STM32开发或智能电表设计的人来说这是一份具有实践参考价值的完整案例。 把自制STM32智能电表装进家里的配电箱那天我比预想中要兴奋得多。OLED屏幕亮起来首先跳出219.8V接着电流、功率、累计电量一组组稳定刷新——原本只在示波器上见过的“220V市电”变成了一串能让我看懂的数字。这个基于STM32的简易智能电表项目成本不到80块测电压、电流、有功功率、累计电能还能通过ESP8266把数据推送到手机上看。这类项目特别适合三类人正在找STM32毕业设计题目的学生、想给家里老电箱加个监测功能的电子爱好者、以及想认真搞懂交流采样、有效值计算、电能计量这些底层原理的嵌入式入门者。它不会像成品智能电表那样精准到千分位但做完之后你对“电”的理解绝对会上一个台阶而且对付日常用电监测绰绰有余。1. 系统架构与器件选型做一个“简易但可靠”的采样前端1.1 核心器件清单其实不用太多东西先看我用到的物料都是容易买到且不贵的常用件模块/器件型号/规格作用主控芯片STM32F103C8T6蓝丸核心板即可采集、计算、显示、通信电流互感器SCT013100A/50mA开口式非接触测量火线电流电压互感器ZMPT101B2mA/2mA隔离采样220V电压显示屏0.96寸OLEDI2C接口128x64本地显示数据无线模块ESP8266AT固件连接Wi-Fi上报数据电源220V转5V AC-DC模块 AMS1117-3.3V系统供电辅助器件运放、电阻电容、TVS/二极管、接线端子等信号调理与保护MCU选STM32F103C8T6的原因很简单72MHz主频、12位ADC、丰富的UART/I2C/SPI接口做这种工频交流采样绰绰有余。而且这门芯片的资料和例程在中文互联网上极其丰富遇到问题基本都能搜到答案。真没必要上F407或H7系列属于杀鸡用牛刀。电流互感器选SCT013这颗100A/50mA开口式最大好处是安装方便——把火线直接穿过中间的圆孔就能测不需要断开原有线路安全得多也更接近真实钳形表的使用逻辑。测家里的进户主线100A量程足够长期过载也不容易烧。1.2 为什么不直接用专用计量芯片学习价值和灵活性很多入坑的朋友第一反应是既然有HLW8032、RN8209这类专用电能计量芯片人家内部都算好了接上串口直接读寄存器不香吗确实香精度比你自己写算法高得多但我不建议把它当作这个“简易智能电表”项目的核心。原因有两个。第一用STM32 ADC直接采样你才能真真切切理解交流信号的负半周怎么处理、直流偏置怎么加、有效值怎么算、功率和电能是积分出来的而不是“读出来的”。这些概念是后面做任何电力电子、电源、电机控制的基础用计量芯片等于把最精华的部分外包了。第二自研采样通路可以灵活扩展——以后想加谐波分析、多回路监测、功率因数实时补偿在同一套ADC采集框架上改就行芯片方案做不到这么自由。当然如果你目标是“产品化”或者强需求精度建议MD卡直接用专用计量芯片再用STM32做数据处理和通信。但作为学习项目和毕设ADC直接采样是更值得走的一条路。1.3 方案对比三种电压/电流采集思路做一个直观的对比采集方案成本精度安全性学习价值电阻分压电流采样电阻直接进ADC最低一般且不隔离差有触电风险中STM32 ADC 互感器信号调理电路低校准后误差1%~3%好互感器隔离高专用计量芯片 STM32中等高好低最终我选了第二条路线互感器隔离输入 运放调理 STM32 ADC内部计算。这是综合成本、安全、学习价值三方面后最均衡的方案也是国内外很多开源的“电能监测仪”项目采用的主流做法。2. 硬件电路设计中的关键取舍信号调理、安全与电源2.1 信号调理电路为什么不能把互感器输出直接接ADC这是整个硬件部分最值得说道的环节。很多人第一次抄电路时容易踩坑直接把互感器二次侧输出引到STM32的PA0引脚结果读出来的波形乱七八糟负半周全是0甚至芯片偶尔烧掉。原因很简单STM32的ADC只能采集0~3.3V的单极性电压而互感器输出的是交流小信号有正有负。一次性信号怼进去负半周被二极管钳位在0V附近正半周可能超过3.3V既不准确也不安全。正确的调理电路要做三件事直流偏置把整个交流信号整体抬高1.65V让它的“零点”落在ADC量程的中间。常见做法是用两个精密电阻从3.3V和GND分压出1.65V参考再通过运放跟随器隔离后给信号做叠加。限幅保护在ADC引脚前并联两个二极管一个接到3.3V一个接到GND把电压钳位在-0.3V到3.6V之间。万一互感器异常或接线松动产生尖峰不会击穿MCU引脚。低通滤波信号路径上串联一个1kΩ电阻对地并联一个100nF电容构成截止频率约1.6kHz的低通滤波器滤掉高频开关噪声和空间辐射干扰。这里截止频率不用刻意压低因为我们关心的信号只有50Hz而STM32的ADC采样率也不需要太高。我第一版电路就吃过亏偷懒把SCT013的输出电阻选得太大导致信号摆幅只有0.1V左右有效值计算分辨率很低功率显示飘忽不定。后来把采样电阻改成把额定50mA输出转成约0.5V~1V的交流电压摆幅再配调理电路数据一下就稳了。所以调电路时记住一个原则尽量让有效信号占据ADC量程的50%~80%不然分辨率不够后面算法再好也是白搭。2.2 接线安全与共地问题做电表项目安全永远是第一位的。我的建议是火线和零线进线直接用2.5平方的铜导线接到端子排然后用SCT013的互感器圆孔穿过火线电压采样用ZMPT101B的输入端并联到火线和零线之间。整个过程中所有220V侧的接线必须保持足够爬电距离焊接点要热缩管包好板子要放在绝缘外壳里。非专业人士千万不要带电操作强烈建议装好后先用隔离变压器或接好线再上电。还有一个嵌入式新手特别容易忽略的问题共地。OLED、ESP8266、STM32、互感器二次侧、ZMPT101B的输出参考地全部要连到同一个GND网络。之前有人给我发来代码说采样值总跳排查半天发现ESP8266的GND没和STM32共地串口通信时电流回路走错了路径数据自然不稳。2.3 电源设计干净的地和干净的VDDA是精度的起点STM32F103C8T6没有独立的VREF引脚它的ADC参考电压是内部接到VDDA上的。因此想要ADC读数稳定3.3V电源的纹波必须控制好。我这里建议分两级前级用质量好一点的220V转5V AC-DC模块输出电流至少500mA。后级用AMS1117-3.3把5V稳到3.3V给MCU和传感器供电。关键点在VDDA的处理C8T6的VDDA和VDD虽然是两个引脚但内部是相通的外部建议在VDDA引脚前串联一个10Ω电阻再对地接1μF陶瓷电容。这就构成一个简单的LC/RC滤波能有效隔离数字电路开关噪声对模拟部分的污染。实测下来不加这个滤波和使用者垃圾充电器供电ADC结果能差出几十个码值足够让最后显示的电压多出0.5V误差。3. 核心算法与固件实现从ADC裸数据到可读电量3.1 采样策略我选了“定时器触发DMA搬运”而不是过零采样交流采样有个设计决策要做是等过零中断来触发采样还是用定时器固定频率采样我两种都试过最终选了定时器DMA方案。过零触发的好处是能精确跟住一个工频周期20ms内的200个点便于算相位但缺点也很明显市电频率不是恒定的可能在49.8Hz到50.2Hz之间漂移过零检测电路本身也有噪声抖动导致采样周期不稳定反而引入误差。定时器触发DMA的好处是简单可靠配置一个定时器比如输出10kHz的更新事件触发ADC规则采样DMA自动把转换结果搬到内存数组里CPU几乎不参与。每1秒累积200个周期数据200点/周期然后统一计算一次效率高代码也干净。具体参数PWM/定时器主频72MHz分频后产生10kHz触发频率即每个50Hz工频周期采集200个点。这已经远高于奈奎斯特要求足以覆盖50Hz基波及前几十次谐波的分析需求。ADC采样时间拉长一点比如239.5周期确保采样电容充分充电提高精度。DMA工作在循环模式缓存区大小设为200个uint16_t。3.2 RMS有效值和有功功率的计算思路拿到200个ADC原始值后不能直接拿一个点的值去当有效值必须走完整的计算链路去直流偏置每个采样点减去标定好的零点偏置即输入为0时ADC应该输出的值理论上是2048附近但实际会偏。算有效值把去偏置后的瞬时值平方、累加、求平均、开根号得到AD域的有效值。电压/电流校准用标准的万用表读数做一次拟合得到AD_RMS到工程值V/A的线性系数。算有功功率把同一时刻的电压瞬时值和电流瞬时值相乘然后在一个周期内求平均。这个平均值就是有功功率P (1/N) * Σ u(n) * i(n)。功率因数PF P / (Urms * Irms)非常自然地算出来了。有人问为什么不用“电压有效值×电流有效值”算功率这是因为在非纯阻性负载下电压和电流之间存在相位差直接相乘得到的是视在功率不是实际消耗的有功功率。用瞬时值相乘再平均才能正确把相位差体现出来这个细节也是很多入门项目做不准的根源。累计电能的公式也简单把每个采样周期的有功功率乘以时间间隔累计起来。为了省内存和高精度我统一用uint32_t保存毫瓦时mWh显示时再转成kWh保留3位小数。这里有个小技巧每次累加完如果超过1000mWh就把它转成1Wh加进小时累计变量避免大数溢出。3.3 核心代码一个可运行的最小实现下面是我裁剪后的核心逻辑基于STM32 HAL库ADC1的CH0和CH1分别采电压、电流两路DMA搬运。#define SAMPLE_NUM 200 #define SAMPLE_FREQ 10000 // 10kHz uint16_t adc_u_buf[SAMPLE_NUM]; uint16_t adc_i_buf[SAMPLE_NUM]; volatile uint8_t sample_ready 0; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { sample_ready 1; // 一轮采样完成 } void calc_power(void) { float sum_u2 0, sum_i2 0, sum_p 0; for (int i 0; i SAMPLE_NUM; i) { float u (adc_u_buf[i] / 4096.0f * 3.3f) - offset_u; float v (adc_i_buf[i] / 4096.0f * 3.3f) - offset_i; sum_u2 u * u; sum_i2 v * v; sum_p u * v; } float urms sqrtf(sum_u2 / SAMPLE_NUM); float irms sqrtf(sum_i2 / SAMPLE_NUM); float p sum_p / SAMPLE_NUM; voltage urms * ku; // ku由校准得到 current irms * ki; power p * ku * ki; // 电压、电流系数分别乘回去 pf (urms * irms 0.1f) ? (p / (urms * irms)) : 0.0f; }主循环里每一秒做一次计算并把电能累计值写入Flash备份if (sample_ready) { sample_ready 0; calc_power(); energy_mwh (uint32_t)(power * 1000.0f / 3600.0f); // 每秒对应功率折算成mWh update_display(); update_uart_report(); }3.4 掉电保存Flash写入不能太频繁家用环境随时可能断电累计电量如果只存在RAM里断电就清零那这个电表就失去了“计量”的意义。STM32F103内部Flash可以存数据但擦写寿命大约1万次不能每秒钟都写。我的策略是在内存中维护一个energy_mwh变量正常情况下每秒更新同时记录上次写Flash的值。只有当累计值比上次写入的值多出10Wh以上时才执行一次Flash页擦除和写入。页擦除前还要把原来存储的其他参数读出来一并重写防止误删。顺带说一句Flash写入时要先解锁、再擦除、再写入整个过程要关中断防止被打断。操作完成后重新上电程序启动时先从Flash读回上次保存的电量再接着累计这样掉电丢失的误差最多就是最后的10Wh。4. 人机交互与数据上报OLED菜单和Wi-Fi节点4.1 OLED显示页面设计三页足够日常使用0.96寸OLED虽然分辨率只有128x64但做电表显示绰绰有余。我用软件I2C驱动不依赖硬件I2C因为STM32F103的硬件I2C在部分固件库版本上存在兼容性小坑而软件I2C对0.96寸这种低速设备完全够用且日后换芯片平台直接平移代码。页面设计分了三页靠一个按键循环切换页面1实时电压、电流、有功功率。这是最常用的页面一眼看出当前负载情况。页面2本月累计电量、昨天用量、按默认电价估算的电费。电费计算直接按电量kWh * 电价电价常量可以在代码头部改成自己家的阶梯电价。页面3功率因数、峰值电流记录自开机以来最大电流、系统运行时长。我习惯把“峰值电流”做个简单的RAM记录开机自动清零方便观察家里同时开大功率电器的瞬时冲击电流也挺有意思。4.2 ESP8266上报从AT指令到MQTTESP8266在这个项目里做两件事连接家里的Wi-Fi然后把电表数据定时推到物联网平台。最简单的方式是用官方AT固件串口直连STM32发AT指令控制。核心流程ATCWMODE1设为Station模式。ATCWJAPSSID,password连接路由器。ATCIPSTARTTCP,xxx.xxx.xxx.xxx,端口建立TCP连接。周期发送HTTP POST或MQTT报文。如果不想自己搭服务器可以直接用国内一些物联网平台它们大多提供MQTT接入地址。ESP8266通过AT指令连接MQTT服务器然后ATMQTTPUB发布主题数据格式我用最简单的JSON{voltage:220.3,current:2.15,power:473.6,energy:12.34}手机端写个简单的MQTT订阅就能实时看到。这里有个经验上报周期别设太短10~60秒比较合适。太频繁会占用串口和MCU资源电表本身是7x24小时运行的设备没必要秒级上报而且ESP8266偶尔会抽风断连必须做一个状态机连接失败就延时5秒重试重试3次后先复位Wi-Fi模块再重新初始化不能死循环在某个AT上。4.3 本地Modbus RTU预留一步之遥的工业通信能力既然做了电表我很建议在硬件上预留一个485接口MAX485芯片走Modbus RTU从站协议。这样这套系统不仅自己看还能对接组态屏、PLC或者上位机软件直接变身一个简易的能源管理节点。Modbus从站原理不复杂STM32的USART接收中断解析帧校验CRC16识别功能码0x03读保持寄存器从预定义寄存器表里取数据返回。寄存器表可以这样规划寄存器地址内容数据类型0x0000电压 (0.1V)uint160x0001电流 (0.001A)uint160x0002有功功率 (0.1W)uint160x0003累计电量 (0.001kWh)uint160x0004功率因数 (0.001)uint16Modbus这部分我建议放在项目验收或者功能扩展阶段再做先把基础功能跑通再考虑这个“可连工业总线”的亮点B格和实用性都能拉满。5. 校准、精度分析与避坑实录5.1 在家怎么校准一台万用表加一只电水壶就够了校准是整个项目里最容易被忽略但又至关重要的一步。不校准也能“显示电压”但显示的是某个可疑的数字。校准流程其实不难校准电压上电后先用万用表测一下家里插座的实际电压把这个值和电表读到的电压值对齐反推电压校准系数Ku。比如万用表显示220.5V电表显示“未校准”状态下的AD_RMS是1.65V按比例算出Ku133.6左右。校准电流找一个纯阻性负载白炽灯、电水壶都行功率因数接近1是理想的标定源。万用表置于交流电流挡串联进火线和电表显示的电流对比反推Ki。校准相位/功率因数纯阻性负载下功率因数应该在1左右如果电表显示0.95或更低说明电压和电流通道本身的相位差还没补偿。处理方法是在功率计算里对电流序列做软件移相比如向前移动1个采样点1/200周期看功率因数是否更接近1做几次迭代就能找到最佳移相量。这里有个细节市电电压本身也在波动校准应该分几次采样取平均值并且尽量在负载稳定的时候校准避免多次读数误差叠加。5.2 实测精度哪些指标够用哪些指标勉强我实测的一组数据供参考家用场景普通万用表精度±0.5%为基准测量项万用表/标准值本电表读数相对误差电压220.0V220.4V0.18%电压213.5V213.8V0.14%电流0.51A0.50A-1.96%电流4.57A4.61A0.87%功率约1000W1012W1.2%电压通道精度很好因为ZMPT101B线性度不错校准后轻松做到0.5%以内。电流通道在小电流段误差偏大原因是SCT013在小电流下输出信号弱ADC量化噪声占比变大以及互感器本身的非线性。如果你需要小电流也准可以考虑换5A量程的互感器或者用更高分辨率的ADC芯片比如外部16位ADS1115但那就不是“简易”范畴了属于加强版的路线。5.3 几个让人抓狂的坑提前帮你趟平这个项目调试过程中我踩了不少坑挑几个印象最深的说说采样值周期性跳变现象是OLED上的功率数字每隔几秒就跳一下。排查后发现是220V转5V的开关电源纹波通过3.3V串进了偏置参考电压。解决方法是给偏置参考电压加一个大电容100μF并且用稳压管或者LDO单独供另外把互感器信号线换成了屏蔽双绞线。OLED屏偶尔显示乱码/花屏多半是I2C总线过长或者上拉电阻缺失。0.96寸OLED模块有些出厂自带4.7kΩ上拉有些不带。排查方法很简单在SCL/SDA上对地或对3.3V补上拉电阻能解决大部分显示异常。ESP8266上报偶尔卡死AT指令模式下容易出现“串口忙等待”死循环。我的经验是给每个AT指令设置超时和最大重试次数超时后先发ATRST复位模块而不是盲目退出整个上报逻辑放在状态机里不阻塞主循环这样即使Wi-Fi挂了电表本地的显示和计量功能还能正常跑。Flash写坏导致程序卡死一开始直接在主循环里频繁调Flash写函数导致芯片几小时后程序变卡。后来改成“电量变化超过10Wh且时间超过1分钟才写”配合关中断操作稳定运行了很久。5.4 做完之后可以往哪些方向延伸这个项目留的扩展空间非常大想玩实时系统可以把裸机轮询改成FreeRTOS把采集、显示、通信拆成三个任务想玩大数据分析可以在ESP8266端接入Home Assistant做出全屋用电曲线想更贴近工业再把Modbus从站调通485接进PLC或者组态软件做一个车间级能耗监测点位。对我来说做这个电表最大的收获是终于不用再根据“供电局账单除以30天”来猜家里每天用多少电而是可以看着屏幕上的数字知道此刻是空调在吃电还是电热水壶在偷跑。如果你正准备上手STM32这确实是一个既能练硬件、又能练算法、还能出成果的好项目。本文还有配套的精品资源点击获取