十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

自制太阳能板IoT监控系统:从ESP32到Grafana全链路解析

自制太阳能板IoT监控系统:从ESP32到Grafana全链路解析 做太阳能板监控Solar Panel Monitor这个项目起因其实很朴素作为IoT爱好者家里装了两块光伏板之后我特别想知道每天到底发了多少电、什么时候发电效率最高、要不要擦板子。市面上的成品监控要么绑定逆变器品牌要么只能看到电表数据始终没法看到板子层面的实时电压、电流和温度。于是就有了这套完全自建的IoT监控方案前后花了大概两周硬件成本不到200元却把光伏系统从“黑盒”变成了“可视化设备”。这篇文章我把完整链路拆开讲从硬件选型、接线校准到固件、数据管线和实际分析结果适合有小块太阳能板或者想练手IoT采集的同学参考。1. 先说说为什么要给自己的光伏板配一套IoT监测1.1 没装监控之前我到底错过了什么大多数家用光伏系统的用户日常能看到的只有逆变器液晶屏上的累计电量和瞬时功率。顶部瞬时功率是一个数字无法反映一天内变化的曲线。除非每天同一时间记录否则很难判断板子是否被树叶遮挡、是否布满灰尘、背板温度是否过高。更麻烦的是当发电量下降时故障可能已经持续几天甚至几周等到电费单出来才发现。损耗的电量无法补救。我最初安装的是12V小功率离网系统板子输出直接进MPPT控制器给蓄电池充电控制器带一个简易屏幕能看到电压电流但这些数据不联网也没有历史记录。某天我发现充电电流比平时低了30%排查好久才发现是连接器内部氧化。如果当时有持续监控这个问题当天就能发现。等到自己动手做这套IoT监控后我再也没有靠“感觉”维护过光伏系统。1.2 这个系统最终能监测什么我设计的这套监控指标如下表监测项传感器/方式量程/精度说明组件输出电压INA2260-36V精度±0.1%直接读Bus Voltage组件输出电流INA2260-2A或0-20A取决于分流电阻电流通过分流电阻换算实时功率软件计算由电压×电流得到单位W背板温度DS18B20防水探头-55~125°C精度±0.5°C贴在背板中心附近环境温度/湿度SHT30温度±0.3°C湿度±2%RH用于与背板温度对比累计发电量数据库聚合对功率做时间积分单位kWh设备在线状态MQTT Last Will实时掉线自动告警采样频率这里要特别说明传感器采集频率不等于上报频率。我建议设备端内部以1秒间隔做中值滤波然后每5秒通过MQTT上报一次如果不需要精细曲线1分钟一次也可以。存储方面时序数据库里保留原始数据Grafana展示时按时间聚合。如果你后面还想做辐照度、风速、倾角等扩展项I2C总线上还能继续挂传感器整体架构不用推倒重来。1.3 谁会需要这样一套东西如果你只有一块小板子给手机充电可能用不上。但如果你有完整的离网电源系统、屋顶光伏阵列或者正在做便携电站这套监控能帮你回答很多实际问题板子朝向换一下发电量差多少、梅雨季节要不要清洗、电池充满后怎么主动降低充电电流。而且它本身也是一个极好的IoT教学项目覆盖了传感器采集、无线通信、服务端数据管道和可视化告警几乎把常见物联网场景全串起来了。我后来做工业数据采集项目时很多思路都是从这个小项目里迁移过去的。2. 硬件选型与总体架构我为什么选了ESP32 INA2262.1 系统分层设计这个监控系统的架构可以分为三层。感知层由电流电压采样、温度探头组成负责把物理量变成电信号边缘层用ESP32完成数据读取、滤波、协议转换和上报应用层跑在局域网内的一台小服务器上包含MQTT Broker、时序数据库和可视化看板。之所以没有选“设备直接上云”的方案是因为很多光伏系统部署在没有外网的环境而且数据留在本地更安全后期接Home Assistant也方便。这种分层设计的好处是每一层都能独立替换。传感器坏了只换传感器MQTT服务挂了不影响采集端数据库想从InfluxDB迁到TimescaleDB也不改动设备固件。后面的选型逻辑都围绕“稳定、低成本、好维护”这三个关键词展开适合个人项目也适合小规模的边缘采集场景。2.2 主控ESP32是个人项目里的性价比之王主控的选择其实纠结过一段时间。Arduino Uno很容易上手但需要外接ESP8266等模块才能联网而且Flash小做个JSON解析都勉强树莓派性能强可以直接跑Python甚至数据库但成本高、启动慢、功耗大放在户外盒子里还要担心SD卡损坏。STM32性能不错但面对WiFi协议栈开发门槛明显更高。ESP32几乎是为这种场景设计的。双核240MHz跑WiFi和传感器采集不会互相拖累内置ADC、I2C、SPI、UART支持Arduino框架、ESP-IDF、MicroPython价格在20元左右模块拆坏了也不心疼。实际测试下来它同时处理INA226轮询、DS18B20读取和MQTT发布CPU占用率很低还有余量做本地平均滤波和异常检测。主控联网方式开发成本优点缺点Arduino Uno需外接ESP8266/ENC28J60低入门简单Flash小内存少协议栈弱ESP32内置WiFi/BLE中低双核、外设丰富、成本低ADC精度一般Raspberry Pi有线/WiFi高可跑服务端成本高、功耗大、启动慢STM32WiFi模块外挂模块高稳定、性能强开发周期长我的结论很明确如果是单点采集、数据量不大ESP32是最合适的。等将来节点多了再考虑换成带以太网的高性能网关。2.3 电流电压采样INA226比纯ADC方案靠谱在哪很多人第一步会想直接用ESP32的ADC读分压电阻测电压再配一个霍尔电流传感器。这个方案不是不行但误差会让你怀疑人生。ESP32自带的ADC在中等电压范围内线性度一般而且参考电压会随温度变化分压电阻的精度和温漂也会贡献几个百分点的误差。对于太阳能板这种波动本来就很大的电源最后数据只能看个趋势没法作为决策依据。INA226是一颗I2C接口的16位电流/电压监控芯片常见模块价格十几块钱。它内部带ADC能同时测母线电压VBUS和分流电阻两端电压VSHUNT通过校准寄存器配置好分流电阻阻值和预期量程后可以直接读出电流、电压还能额外算功率。最关键的一点是它的测量链路不经过MCU的ADC噪声和误差都小得多。如果你对精度有要求我强烈建议用INA226而不是ACS712。ACS712是霍尔型适合隔离大电流但零电流输出不是0温度漂移明显小电流下误差很大。太阳能板监控属于小功率直流场景用INA226串联采样更合适。选模块时要注意两个参数一是模块上的分流电阻阻值常见的有0.1Ω、0.002Ω、0.01Ω几种。0.1Ω适合小电流如0-2A压降大但分辨率高0.002Ω适合大电流如0-50A发热小但微弱电流测不准。二是芯片的共模电压范围。INA226的VBUS最高能到36V也有60V版本如果你的太阳能板开路电压超过这个值就要用分压电阻或者换更高耐压的芯片。2.4 背板温度和环境温湿度传感器背板温度我用了一根DS18B20防水探头直接贴在太阳能板背面铝边框和背板交接处。DS18B20是单总线数字传感器三根线就能搞定多个探头还能挂到同一条总线上每个探头有唯一64位地址。需要注意的是贴合时要涂抹导热硅脂不然测的是探头旁边空气的温度不是板子温度。环境温湿度我选的是SHT30因为DHT11/DHT22精度太差读取时序又严格换SHT30后读数稳定多了价格也才几块钱。这两个传感器都接在ESP32的GPIO上I2C共用一个总线单总线单独一个引脚。如果以后想加辐照度传感器辐照计I2C总线上还能继续挂。3. 接线、校准与防坑这步最容易被低估3.1 接线拓扑与电源方案接线是整个项目里最容易出问题的一步。我先说总体结构太阳能板正极输出先接到一个直流保险丝然后进入INA226模块的采样回路再从模块出来接到MPPT控制器或负载的输入端。也就是说电流采样电阻串联在太阳能板到控制器之间的正极线上。同时模块需要供电我直接用了5V的独立电源USB充电头给ESP32和传感器供电INA226模块本身用I2C的3.3V供电但它的采样输入是与供电隔离的所以采样电压可以达到几十伏。这里有个坑如果你用同一个电源给ESP32供电同时太阳能板又通过控制器给同一个电池充电地线需要共地。否则INA226读出的电压和电流会出现跳变或漂移。我用的是独立USB电源地和太阳能板负极共地这样就避免了浮动电压问题。实际操作时建议先用万用表确认太阳能板正负极然后先接控制器的电池端再接太阳能板避免控制器在无电池状态下空载。保险丝和直流断路器不能省。太阳能板只有在短路时才会有大电流但一旦接线错误可能瞬间烧毁控制器和模块。我在正极线上串了一个额定电流1.5倍于板子Isc的保险丝又在模块输入端并联了一个TVS管用来吸收开关瞬态高压。这些保护元件加起来不到十块钱但能避免很多“莫名其妙”的损坏。3.2 校准流程不校准的INA226数据只能当参考INA226虽然精度高但出厂默认校准寄存器并不能直接用于任意分流电阻。拿到模块后必须先做两件事确认分流电阻阻值以及写入正确的校准值。如果模块只印着0.1Ω没有给出精确阻值就用万用表电阻档实测。随后用官方INA226计算表出一个校准值。以0.1Ω分流电阻、预期最大电流2A为例CurrentLSB 2A / 32768 ≈ 0.000061A/bit取整到0.0001A/bit对应Cal 0.00512 / (0.0001 × 0.1) 512。写入Cal寄存器后电流寄存器LSB为0.1mA读取值就是电流。电压寄存器不需要校准它是绝对值测量但要注意VBUS寄存器是14位LSB为1.25mV直接乘1.25就是毫伏。校准的作用是让芯片内部的计算结果和真实电流一致。如果你不写Cal寄存器电流读值大概率是错的。另一个坑是零点偏移。芯片本身有offset系统在空载时可能读到几十mA的电流。我在软件里加入了开机空载校准设备启动后读取10次电流取平均作为之后每帧数据的减数。这样即使分流电阻有轻微温漂也能通过定期校准减小影响。要得到进一步精确的校准可以用一块高精度电子负载或一个已知阻值的功率电阻通入固定电流然后调整Cal值直到读数匹配。不要用电机这类波动负载校准。校准完成后把参数写到EEPROM或者代码的配置头文件里方便下次刷机恢复。3.3 常见接线错误和症状列几个我实际遇到的症状和原因电压读数正常电流一直是0大概率分流电阻两端的采样线接反了或者INA226模块的V-端没有串进主回路。电压、电流读数来回跳采样线接触不良或地线没有共地。设备一接太阳能板就重启太阳能板在强光下的瞬时电压超过了供电电路的耐压或共地噪声耦合到ESP32的复位引脚。温度读数明显偏高DS18B20防水探头没贴紧背板被太阳晒到了。排查这些问题时最好先用实验室直流电源代替太阳能板设置一个固定的12V电压和1A电流限制确认采集端读数无误后再接真实板子。这样可以把电源问题与通信问题隔离。4. 固件逻辑从采集到上报的完整链路4.1 采集流程设计固件的任务可以拆成四块初始化、循环采集、组包上报、异常处理。系统上电后先初始化Wire、INA226、DS18B20、DHT30再连WiFi和MQTT。如果WiFi一直连不上我采用“不阻塞启动”策略先开始本地采集后台异步重连而不是卡死在connect函数里。这样即使网络坏了设备仍然在本地运行重连后能拿到接近实时的数据。设备端缓存最近的上报值Mqtt连接建立后先补发一帧状态。采集循环里我以1秒为周期读取全部传感器。电压、电流各读10次去掉最大最小值后取平均把平均值存进一个环形缓冲。每5秒计算一次5秒窗口的移动平均作为上报数据。移动平均的作用是滤掉太阳能板由于云层遮挡或开关电源引起的毛刺避免Grafana上的曲线变成锯齿。4.2 关键代码段说明我用Arduino框架开发核心代码大概如下。INA226库选用Rob Tillaart的INA226库MQTT用PubSubClient。代码逻辑很直接需要注意的点我写在注释里。#include Wire.h #include INA226.h #include WiFi.h #include PubSubClient.h #include ArduinoJson.h INA226 ina(Wire, 0x40); // 默认地址 I2C 0x40 const float shuntResistor 0.1f; // 根据模块实际测量写入 float currentLSB 0.0001f; // 例子最大2A / 32768 ≈ 0.000061取0.0001 uint16_t calValue (uint16_t)(0.00512f / (currentLSB * shuntResistor)); void setup() { Wire.begin(21, 22); // ESP32 I2C引脚 if (!ina.begin()) { Serial.println(INA226 not found); } ina.setCalibration(calValue); // 其他传感器初始化... }上面的calValue计算出来是512对于0.1Ω和0.0001A/bit正好是512。如果你换用0.01Ω和2A量程Cal值就会变成5120。这个值不能乱填否则电流寄存器读出来的数和真实电流差很多倍。读取一帧数据并组包的部分我贴一个片段String buildPayload() { float v ina.getBusVoltage(); // 单位V float i ina.getCurrent(); // 单位A经过Cal寄存器换算后 float p v * i; float temp readDs18b20(); GlobalJsonDoc.clear(); JsonObject obj GlobalJsonDoc.toJsonObject(); obj[device] solar_panel_1; obj[v] serialized(String(v, 3)); obj[i] serialized(String(i, 3)); obj[p] serialized(String(p, 3)); obj[t] serialized(String(temp, 1)); // 时间戳由MQTT broker侧或数据库侧补充设备端不依赖NTP String out; GlobalJsonDoc.serializeTo(out); return out; }这里有个小设计设备端不在payload里写ts字段而是让服务端收到消息时按当前时间打标。原因是ESP32在断网后系统时间会不准如果以设备时间为准恢复网络前的时间戳就错乱了。如果一定要设备端时间戳就要通过NTP同步并定期校准。我用ArduinoJson库而不是手动拼字符串主要是为了避免特殊字符转义和浮点数格式问题。ESP32的Flash足够放这份JSON库序列化速度也很快。发布消息时把retained标志设成false避免旧数据被新订阅者当成最新状态。4.3 上报频率、QoS与数据量估算太阳能板的电气特征变化不会特别剧烈1秒采集、5秒发布已经足够。如果发布太频繁ESP32和MQTT broker都会白白耗电而且长时间运行会积攒大量碎片数据。每秒发布一个JSON一天就是86400条虽然InfluxDB能扛住但查询和告警没必要。我的经验是采集1秒、发布5秒保留原始数据一个月Grafana按1分钟聚合展示。如果后续需要做更多分析可以用InfluxDB的连续查询把数据降采样到1分钟明细再保留更长时间。MQTT QoS一般选1。QoS 0可能丢失QoS 2会引入两轮握手对5秒一条的物联网数据来说是浪费。Topic的设计也要注意我用的是sensor/solar_panel_1/data如果以后有多个板子可以直接扩展sensor/solar_panel_2/data。服务端订阅通配符sensor//data就能收到全部数据。一条典型的MQTT消息长这样{device:solar_panel_1,v:30.123,i:1.356,p:40.84,t:42.5}在InfluxDB里存下后查询时按设备标签和字段筛选很方便。如果未来想加入多台板子只需要改固件里的device字段服务端不用动。4.4 断线重连与看门狗WiFi在任何户外环境都不可能永远稳定。我遇到最典型的情况是路由器重启后ESP32的WiFi库会自动重连但MQTT连接并不会自动恢复必须监听MqttClient.connected()并在断开时主动调用reconnect()。在重试逻辑里加上随机退避比如每次重试间隔增加100-500ms避免所有设备同时重连瞬间把broker打挂。同时启用ESP32的Task Watchdog把采集主循环喂狗放在loop()开头。如果一个I2C读取卡死超过5秒看门狗就强制重启。这种方式看起来粗暴但非常有效能避免无人值守设备“假死”一星期。另外我把上次上报时间存在RTC内存里重启后如果发现距离上次上报超过阈值会立刻补发一帧方便服务端做在线状态判断。5. 数据落地MQTT、InfluxDB、Grafana三件套怎么串5.1 为什么选Mosquitto InfluxDB Grafana服务端我一开始打算用Node-RED直接收MQTT再写数据库后来发现对纯监控场景来说Node-RED有点重。改用Telegraf订阅MQTT直接写InfluxDB配合Grafana看板整个链路就是三个独立小服务哪个挂了都容易单独处理。Mosquitto是Eclipse社区的老牌MQTT Broker资源占用小配置文件简单Linux上一条命令就能装。InfluxDB是时序数据库专门处理这种“每隔几秒一条带时间戳的数据”。Grafana则负责把数据变成图表和告警。这三件套在物联网监控圈几乎成了默认组合我遇到问题的时候搜资料也特别方便。如果你愿意折腾也可以把InfluxDB换成
返回列表