基于ESP32与Linky电表打造三相智能电力负荷管理器
1. 项目概述:基于Linky智能电表的家庭电力负荷管理器
如果你家也和我一样,从传统的机械电表换成了法国电力公司(EDF)推广的Linky智能电表,那你可能已经注意到了这个绿色小盒子带来的新可能性。它不再只是一个默默计费的设备,其内置的通信接口为我们这些喜欢动手折腾的“家庭电工”打开了一扇窗。我家的用电情况有点复杂:三相供电,家里有泳池泵、空调、热泵,还有一台电动汽车充电桩。这些“电老虎”一起开动时,总功率轻松就能突破供电合同的限制,导致主断路器跳闸,全家断电。更头疼的是,在三相系统中,如果负载在各相之间分配不均,还会造成额外的线路损耗,甚至影响供电质量。
传统的解决方案是安装一个“电力负荷管理器”(Délesteur),它监测总功耗,在接近上限时,按预设优先级切断一些非必要负载(比如关掉热水器,暂停泳池泵),等负荷降下来再恢复供电。但市售的成品要么功能单一,要么价格昂贵,而且往往无法与Linky电表直接“对话”,获取实时、精确的数据。于是,我决定自己动手,利用Linky电表标准配置的历史数据输出接口,打造一个完全定制化、可通过网页远程管理、且成本可控的智能负荷管理器。
这个项目的核心是实时监测三相电流和视在功率,计算并记录峰值,并能在功率超过设定阈值时,自动控制继电器断开预设的非关键负载。所有数据通过一个本地网页服务器呈现,你可以在手机或电脑上随时查看用电情况、设置参数,甚至远程手动控制负载。对于像我这样使用三相电且有大功率电器的家庭来说,它不仅能防止意外跳闸,更是优化用电、平衡三相负载的得力工具。下面,我就把从硬件选型、软件实现到调试安装的完整过程,以及我踩过的那些“坑”,毫无保留地分享出来。
2. 核心硬件选型与设计思路
2.1 为什么选择ESP32与Automator模块?
市面上主流的物联网开发板很多,比如Arduino、树莓派Pico等。我最终选择了ESP32作为主控芯片,并基于Elektor Automator模块进行开发,主要基于以下几点考量:
双核处理与无线连接能力:ESP32拥有两个处理核心,这允许我将一个核心专用于与Linky电表的高速、定时串口通信和数据解析,另一个核心则负责运行Web服务器、处理用户界面和逻辑控制。两者互不干扰,保证了系统的实时性和响应速度。其内置的Wi-Fi和蓝牙模块,使得实现远程网页控制变得轻而易举,无需额外模块。
电气隔离的安全性:与电表通信,安全是第一位的。Linky电表的数据输出接口(通常是红外或串行通信)是弱电信号,但我们必须假设其与强电系统在物理上可能存在潜在联系。直接连接非常危险。Elektor Automator模块的一个关键优势是,它板载了光耦隔离器。这意味着我的ESP32电路与Linky电表之间通过光信号传递数据,实现了完全的电气隔离,彻底杜绝了强电串入弱电控制部分的风险,这是DIY家庭电力项目不可妥协的底线。
丰富的扩展性与成熟的生态:Automator模块提供了标准的扩展总线,方便我后续增加更多的继电器输出通道(例如,管理多个可卸载的负载)。ESP32的Arduino核心拥有极其庞大的开源库和社区支持,无论是Web服务器、NTP对时还是JSON处理,都有成熟稳定的库可用,大大缩短了开发周期。
注意:如果你找不到或不想使用Automator模块,也可以使用通用的ESP32开发板(如ESP32 DevKitC)配合一个独立的光耦隔离模块(如6N137或PC817)来实现串口隔离。但务必确保光耦两侧使用独立的电源供电,才能真正实现隔离。
2.2 Linky电表通信接口解析
Linky电表提供了多种数据输出方式,最常见的是通过红外窗口(Teleinfo模式)或一个专用的串行接口(通常位于电表侧面,需要打开一个保护盖)。我使用的是串行接口,因为它更稳定,不受环境光影响,也方便布线。
Linky电表通过这个接口以异步串行通信方式,持续发送遵循法国Teleinfo标准的帧数据。关键参数如下:
- 波特率:1200 baud(这是标准值,早期电表可能是9600,但新Linky统一为1200)。
- 数据格式:7个数据位,偶校验,1个停止位(7E1)。这是一个比较特殊的格式,在配置串口时需要特别注意。
- 帧结构:数据以文本形式发送,每行是一个数据项,格式如
ADCO 012345678901 X,其中ADCO是电表标识符,012345678901是值,X是校验和。帧之间大约每1到3秒发送一次,包含了瞬时电流、功率、电价时段等数十个信息。
2.3 外围设备与电路设计
除了核心的主控和隔离部分,整个系统还需要以下部件:
OLED显示屏:我选用了一款0.96英寸的I2C接口OLED屏。它的作用是本地显示最关键的信息:三相的瞬时电流(A)和总视在功率(VA),以及它们的今日峰值。在调试初期,没有网络的情况下,这块屏幕是确认系统是否正常工作的唯一窗口。选择I2C接口是因为它接线简单(仅需4根线),且ESP32的I2C资源充足。
继电器模块:用于执行“卸载”动作。我选择了一个带光耦隔离和晶体管驱动的单路继电器模块,可以控制250VAC/10A的负载,足以应对大多数家用热水器或泳池泵。继电器的控制引脚连接到ESP32的一个GPIO口,通过一个简单的
digitalWrite函数即可控制其通断。按钮与LED指示灯:
- 一个常闭型自复位按钮:连接在ESP32的某个GPIO和地之间,并启用内部上拉电阻。这个按钮有三个功能:长按(>5秒)进入Wi-Fi配置模式;短按用于点亮/关闭OLED屏幕(为了保护屏幕寿命);在Web界面中也可以将其配置为手动触发继电器的紧急按钮。
- 两颗LED:一颗蓝色LED连接到与Linky通信的串口RX引脚附近,每当成功接收到一帧完整数据时就闪烁一下,作为“心跳”指示。另一颗红色LED连接到控制继电器的GPIO,继电器吸合时点亮,直观显示系统正处于“卸载”状态。
电源:整个系统需要稳定的5V或3.3V直流供电。我使用了一个高质量的5V/2A的USB电源适配器,并经过一个低压差线性稳压器(LDO)为ESP32和OLED屏提供稳定的3.3V。继电器模块则直接使用5V供电。确保电源有足够的余量,特别是在继电器动作时可能会产生电流尖峰。
3. 软件架构与核心功能实现
3.1 数据解析与实时计算逻辑
软件的核心是一个状态机,它持续监听来自Linky电表串口的数据流。以下是关键步骤的代码逻辑概述:
// 伪代码示例,说明核心逻辑 void handleTeleinfoData() { static String buffer; // 存储接收到的字符 while (serialLinky.available()) { char c = serialLinky.read(); if (c == '\n') { // 一帧结束 processLine(buffer); // 解析一行数据 buffer = ""; } else if (c != '\r') { // 忽略回车符 buffer += c; } } } void processLine(String &line) { // 示例:解析电流行 "IINST1 015 A" if (line.startsWith("IINST1")) { int currentPhase1 = line.substring(7, line.indexOf(' ')).toInt(); // 提取数值“015” updateCurrent(0, currentPhase1); // 更新第一相电流 digitalWrite(BLUE_LED_PIN, HIGH); // 闪烁蓝灯 delay(50); digitalWrite(BLUE_LED_PIN, LOW); } // 类似地解析 IINST2, IINST3, PAPP (视在功率) } void updateCurrent(int phase, int value) { currents[phase] = value; // 计算总视在功率(假设功率因数已知或从PAPP获取) // 更新今日峰值 if (value > maxCurrents[phase]) { maxCurrents[phase] = value; maxCurrentsTime[phase] = now(); // 记录峰值发生时间 } }峰值管理与午夜清零:系统为每相电流和总功率维护一个“今日最大值”变量。这些值在每天午夜00:00:00自动重置为零。时间的准确性至关重要,因此我集成了NTP(网络时间协议)客户端。ESP32在连接Wi-Fi后,会自动从互联网时间服务器获取精确时间,并自动处理夏令时/冬令时切换。重置逻辑很简单,在每次数据更新时检查当前时间,如果日期已变更,则执行重置。
通信超时处理:电力监控系统必须可靠。如果因为线路故障、电表重启等原因导致超过60秒没有收到任何有效数据,系统会在OLED屏幕和Web界面上显示“Linky Timeout!”警告。这提醒我需要检查物理连接。同时,负荷管理功能会进入安全模式——通常我会编程让继电器在通信丢失时自动释放(恢复供电),防止在未知状态下错误地切断负载。
3.2 网页服务器与用户界面设计
我使用ESPAsyncWebServer库来构建异步Web服务器,它性能好,能同时处理多个连接。服务器提供以下几个页面:
主仪表板(Dashboard):这是默认首页。以清晰的大字体和柱状图形式展示三相的瞬时电流、今日电流峰值及发生时间、总视在功率及其峰值。当系统因功率超限而激活卸载功能时,对应相的数据会高亮为红色,非常醒目。页面每3秒通过AJAX自动刷新数据,无需手动刷新。
历史峰值页面:除了今日峰值,我还利用Linky电表每天发送的“昨日最大功率”数据(
PPOT标签),在页面上显示前一天的峰值功率。这对于分析长期用电习惯、验证负载调整效果非常有用。正是通过对比连续几天的数据,我发现电动汽车充电时导致某一相负载过重,从而决定将其充电桩的电源线改接到另一相上。参数配置页面:这是系统的“大脑”。所有设置通过一个表单提交,保存到ESP32的非易失性存储(NVS)中。可配置项包括:
- 网络设置:Wi-Fi的SSID和密码、静态IP地址(可选)、网关和DNS。
- 卸载参数:这是核心逻辑。你需要为每一相(或总功率)设置两个阈值:
- 激活阈值(Activation):当功率/电流超过此值,继电器断开(卸载负载)。
- 恢复阈值(Deactivation):当功率/电流回落至此值以下,继电器重新闭合(恢复供电)。恢复阈值必须低于激活阈值,形成一个“迟滞区间”,防止继电器在临界点附近频繁跳动(称为“继电器震颤”),这会严重损害继电器和负载。我通常设置至少10%的差值。
- 屏幕保护设置:可以设置OLED屏幕常亮,或仅在按钮按下后点亮5分钟。
系统操作页面:提供一键“恢复出厂设置”按钮(将NVS中的参数重置为代码中编译的默认值),以及手动控制继电器的开关按钮,用于测试。
3.3 负载管理与卸载逻辑实现
卸载逻辑是项目的最终执行层。它作为一个独立的任务运行,定期(例如每秒)检查当前数据。
void loadSheddingTask(void *parameter) { for (;;) { int totalPower = calculateTotalPower(); // 计算当前总功率 bool overload = false; // 检查是否任何一相或总功率超过激活阈值 for (int i=0; i<3; i++) { if (currents[i] > activationThreshold[i]) { overload = true; overloadPhase = i; // 记录是哪一相超了 break; } } if (totalPower > totalActivationThreshold) { overload = true; overloadPhase = -1; // 表示是总功率超限 } if (overload && !relayState) { // 超限且继电器当前为断开(负载通电)状态 -> 执行卸载 digitalWrite(RELAY_PIN, HIGH); // 断开继电器(根据模块逻辑可能是LOW) relayState = true; logEvent("Load shed activated due to overload on phase " + String(overloadPhase)); } else if (!overload && relayState) { // 功率已恢复,且继电器当前为吸合(负载断电)状态 -> 恢复供电 // 需要额外检查是否所有值都低于恢复阈值 bool safeToRestore = true; for (int i=0; i<3; i++) { if (currents[i] > deactivationThreshold[i]) safeToRestore = false; } if (totalPower > totalDeactivationThreshold) safeToRestore = false; if (safeToRestore) { digitalWrite(RELAY_PIN, LOW); relayState = false; logEvent("Load restored."); } } vTaskDelay(1000 / portTICK_PERIOD_MS); // 每秒检查一次 } }关键设计点:逻辑判断必须放在非中断服务程序中。串口接收数据是在中断中处理的,但卸载逻辑涉及复杂的比较和状态控制,应放在一个独立的、低优先级的任务中,避免阻塞通信。同时,所有对共享数据(如currents[]数组)的访问,都需要使用信号量或互斥锁进行保护,防止数据在读取过程中被串口中断更新而导致错误。
4. 组装、配置与安装实操指南
4.1 硬件组装与接线步骤
准备与规划:在开始焊接或接线前,绘制一张简单的接线图。明确ESP32/Automator模块的每个引脚(3.3V, GND, GPIOxx, RX, TX, SDA, SCL)需要连接到哪里。这将极大减少错误。
焊接与连接:
- 将OLED显示屏的VCC、GND、SDA、SCL分别连接到ESP32的3.3V、GND、GPIO21(默认SDA)、GPIO22(默认SCL)。
- 将继电器模块的VCC、GND、IN引脚连接到ESP32的5V(或外部5V电源正极)、GND、以及一个GPIO口(例如GPIO23)。
- 将按钮一端连接到你选择的GPIO(例如GPIO4),另一端接地。在代码中启用该GPIO的内部上拉电阻。
- 将蓝色LED通过一个220欧姆的限流电阻连接到另一个GPIO(例如GPIO2),用于通信指示。
- 最关键的一步:连接Linky电表。使用一根双绞线或屏蔽线,一端连接Linky电表的串行输出接口(通常是两个端子:Data和GND),另一端连接到Automator模块的光耦隔离输入侧。务必对照Automator模块和Linky电表的说明书,确认正负极和信号线。接反可能无法通信甚至损坏光耦。
电源连接:使用万用表确认你的5V电源适配器输出正常。先将电源连接到继电器模块(如果需要5V驱动),然后通过LDO或DC-DC降压模块为ESP32和OLED提供稳定的3.3V。在上电前,反复检查所有电源线(特别是3.3V和5V)没有短路,正负极没有接反。
4.2 首次上电与Wi-Fi配置
进入配置模式:按住电路板上的配置按钮不放,然后给系统上电。保持按住约5秒,直到OLED屏幕显示“AP Mode”或类似信息,蓝色LED可能呈现快速闪烁模式。这表明ESP32已启动为接入点(AP)模式。
连接配置网络:用手机或电脑的Wi-Fi搜索一个名为“ESP32_Linky_”的网络(名称可能在代码中自定义),连接它。这个网络通常没有密码,或者初始密码是“12345678”。
访问配置页面:打开浏览器,输入地址
http://192.168.4.1。你将看到Web配置界面。首先进入“参数”页面。设置网络参数:
- 在“Wi-Fi设置”部分,填入你家庭路由器的SSID和密码。
- (可选)如果你希望设备使用固定IP,在此处设置IP地址、网关和子网掩码。使用DHCP(自动获取)通常更简单。
- 在“卸载设置”部分,暂时将激活阈值设为一个很高的值(比如9999),禁用自动卸载功能,等测试完成再调整。
- 点击“保存并重启”。
切换至工作模式:设备将重启,并尝试连接到你指定的Wi-Fi。连接成功后,OLED屏幕会显示获取到的IP地址(例如
192.168.1.123)。现在,你可以通过这个IP地址在家庭网络内的任何设备上访问它的Web界面了。
4.3 安装到配电箱与负载连接
安全警告:以下操作涉及家庭强电电路。如果你不具备电工资质或足够的经验和信心,请务必聘请专业电工完成。操作前必须断开总电源开关,并使用电笔确认无电后再进行。
选择被控负载:确定你想要管理哪个电器。通常选择热水器、空调外机、泳池泵、电动汽车充电桩(调低充电电流)等非即时性、可短暂中断的负载。切勿用于冰箱、照明、安防系统等关键负载。
继电器接入:
- 在配电箱中找到控制目标负载的电路断路器(空气开关)。
- 将该断路器的输出线(火线)切断。将来自电源侧的一端接入继电器模块的公共端(COM),将通往负载的一端接入继电器模块的常开端(NO)。这样,当继电器吸合时,电路断开;释放时,电路接通(这是最安全的断电逻辑)。
- 将继电器模块的控制电路(低压部分)与你之前做好的ESP32控制板连接好。
固定设备:将ESP32控制板安装在配电箱内一个安全、干燥、远离强电线缆和发热元件的位置。可以使用导轨安装壳或绝缘胶固定。
最终测试:
- 合上总闸,恢复供电。
- 观察OLED屏幕,应能看到来自Linky电表的实时电流和功率数据在变化。
- 通过Web界面,手动控制继电器开关,测试负载是否能被正确切断和恢复。
- 最后,在Web界面上设置合理的激活和恢复阈值。一个保守的起始点可以是:激活阈值设为供电合同最大电流的80%,恢复阈值设为70%。例如,对于12kW三相电(每相约17.3A),可以设置激活阈值为14A,恢复阈值为12A。
5. 调试心得与常见问题排查
在开发和安装过程中,我遇到了不少问题,这里总结一下,希望能帮你少走弯路。
5.1 通信问题:收不到Linky数据
这是最常见的问题。现象是OLED屏幕数据不更新,蓝灯不闪。
- 检查接线:这是第一步。确认Linky电表输出端到光耦输入端的线连接正确且牢固。尝试交换Data和GND线(在断电情况下操作)。
- 确认串口参数:在代码中,初始化串口的参数必须严格匹配:
Serial.begin(1200, SERIAL_7E1, RX_PIN, TX_PIN);。7E1(7位数据、偶校验、1位停止位)是关键,用8N1绝对收不到正确数据。 - 检查电表输出:最可靠的方法是用一个USB转TTL串口模块(如FT232RL),直接连接Linky电表输出端,在电脑上用串口调试助手(如Putty、Arduino IDE串口监视器)查看原始数据。设置正确的波特率和格式,如果能收到类似
ADCO ...的文本,证明电表输出正常。这能帮你快速定位是电表问题还是你的电路问题。 - 电源干扰:如果使用开关电源为ESP32供电,其噪声可能干扰串口通信。尝试在串口信号线上加一个100欧姆的电阻和100pF的电容组成低通滤波器,或者换用线性稳压电源测试。
5.2 Wi-Fi连接不稳定或Web界面无法访问
- 信号强度:配电箱往往是金属外壳,对Wi-Fi信号屏蔽严重。确保你的路由器信号能良好覆盖安装位置。可以考虑使用Wi-Fi中继器,或者将ESP32的天线部分引出配电箱(注意绝缘和安全)。
- IP冲突:如果你设置了静态IP,确保该IP不在路由器的DHCP分配范围内,否则会导致冲突。建议初期使用DHCP,在路由器后台查看ESP32获取到的IP,并将其设置为“静态地址分配”(或DHCP保留),这样既能固定IP,又避免冲突。
- Web服务器库:确保使用稳定的
ESPAsyncWebServer和AsyncTCP库版本。过旧或过新的版本可能存在兼容性问题。
5.3 卸载逻辑误动作或不动作
- 阈值设置不当:恢复阈值必须低于激活阈值。如果两者相等或太接近,负载功率的微小波动就会导致继电器频繁通断。将迟滞区间拉大,例如设置2-3A的差值。
- 数据抖动:Linky电表发送的瞬时电流值本身可能有1-2A的瞬时跳动。在软件中可以加入简单的软件滤波,例如对电流值进行移动平均计算,用过去几次采样的平均值来做判断,而不是用瞬时值。
// 简单的移动平均滤波示例 #define FILTER_SIZE 5 int currentSamples[FILTER_SIZE]; int sampleIndex = 0; int getFilteredCurrent(int newSample) { currentSamples[sampleIndex] = newSample; sampleIndex = (sampleIndex + 1) % FILTER_SIZE; long sum = 0; for (int i=0; i<FILTER_SIZE; i++) sum += currentSamples[i]; return sum / FILTER_SIZE; } - 继电器类型:确认你使用的是常开(NO)型继电器,并且接线正确(COM接电源进线,NO接负载线)。这样在系统断电或ESP32重启时,继电器会处于断开状态(负载断电),符合故障安全原则。
5.4 OLED屏幕寿命问题
OLED屏幕有烧屏风险,长期显示静态内容会损坏像素。这就是我加入屏幕保护模式的原因。在Web界面中启用“屏幕省电模式”后,屏幕默认关闭,只有短按按钮才会点亮5分钟。这个功能极大地延长了屏幕寿命。如果屏幕已经出现残影,尝试长时间关闭或显示全白/全黑图像进行一定程度的恢复。
这个项目从构思到稳定运行,花费了我不少周末的时间,但带来的价值是巨大的。它不仅让我家再也没有因过载而跳闸,更让我对家庭的用电模式有了前所未有的清晰认识。通过分析历史数据,我优化了大型电器的使用时间,成功平衡了三相负载,理论上还能节省一些电费。最重要的是,这个自己搭建的系统完全在我的控制之下,可以根据需求随时调整逻辑或增加功能(比如接入Home Assistant实现智能联动)。如果你也有类似的用电管理需求,并且喜欢动手创造,那么基于Linky电表打造一个属于自己的智能负荷管理器,绝对是一个充满乐趣和成就感的项目。
