ARTICLE DETAIL

资讯详情

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

基于ESP32与MQTT的太阳能板监控系统搭建实战

基于ESP32与MQTT的太阳能板监控系统搭建实战 1. 项目背景与核心需求为什么要自己动手做一个太阳能板监控系统如果你搞过光伏发电一定经历过这种尴尬太阳能板装了逆变器也跑了但每天到底发了多少电、板子表面温度正不正常、今天阴天效率掉了多少统统只能靠手机App里那个不太准的估算值。更麻烦的是板子有没有被遮挡、灰尘积累到什么程度、哪块板子开始老化了这些问题在现有商用方案里要么不开放数据接口要么传感器精度低得离谱。我自己在老家屋顶装了一套 3kW 的光伏系统最开始用的是厂家配套的监控盒子。用了小半年发现一个问题厂家云平台只给发电量和简单的状态信息板子温度、光照强度、每块组件的独立电压一个都看不到。后来我干脆自己动手用 ESP32 加几个传感器搭了一套IoT-Based Solar Panel Monitor也就是物联网太阳能板监控系统。这套系统把所有关键数据都握在自己手里不受厂家云平台限制数据可以随时导出接入自己的可视化面板甚至能设置告警规则。这套系统适合谁我觉得有三类人特别值得参考第一类是已经装了光伏但想拿到更细粒度数据的人第二类是搞硬件开发或者物联网想找个真实场景练手的人第三类是给小型离网系统、太阳能水泵、房车太阳能做监控的玩家。整个项目不需要太高门槛ESP32 开发板、几个传感器、一块面包板就能起步软件这边用开源的 MQTT Broker 加图形化面板就能跑起来后面我会把每一步怎么操作、怎么避坑都写清楚。2. 物联网太阳能监控系统整体架构与关键技术选型2.1 系统架构分层从传感器到云端的完整数据链路在动手之前得先把整体架构想明白。一个完整的 IoT 监控系统本质上就是一条数据链路物理世界 → 传感器 → 采集端 → 网络 → 服务器 → 展示端。每一项都有多个可选方案选型直接决定后面开发的顺利程度和系统的稳定性。我这套系统的数据链路是这样的感知层电压传感器、电流传感器、温度传感器NTC 热敏电阻或者 DS18B20、光照强度传感器BH1750、湿度传感器DHT22 可选。采集层ESP32 开发板负责读取传感器数据并做初步处理通过 ADC 引脚采集模拟量通过 I2C 或单总线读取数字传感器。传输层Wi-Fi 连接家庭路由器通过 MQTT 协议把数据发布到 Broker。平台层本地用 Raspberry Pi 跑 Mosquitto 作为 MQTT Broker同时跑 Node-RED 做数据解析和转发数据存入 InfluxDB 时序数据库。展示层用 Grafana 做可视化面板实现实时数据查看、历史趋势分析、告警规则配置。为什么不用厂家云平台核心原因是数据主权和灵活性。商用光伏监控平台的数据格式是封闭的拿不到原始采样值也没法做自定义分析。自己搭这套系统从头到尾数据都是自己的想怎么处理都行。2.2 核心器件选型ESP32 为什么是性价比之王采集端我选了 ESP32 而不是 Arduino Uno有几个关键原因首先ESP32 自带 Wi-Fi 和蓝牙功能不需要额外挂一块 ESP8266 或者网卡模块电路设计简单很多。Arduino Uno 要连网必须叠一个网络扩展板体积大、功耗高、调试也麻烦。其次ESP32 的 ADC 是 12 位分辨率也就是 0 到 4095 的采样精度而 Arduino Uno 的 ADC 只有 10 位0 到 1023。做电压采集时12 位意味着同样的参考电压下能分辨出更小的变化这对精确监测太阳能板输出电压意义很大。第三ESP32 支持双核处理器主频最高 240MHz可以在一个核心上处理传感器采集另一个核心处理网络通信避免采集和上报互相阻塞。传感器方面我最后定下来用的是传感器型号接口测量内容选型理由电压采集电阻分压 ADC模拟量0-30V 板端电压成本极低精度够用电流采集ACS712 或 INA219模拟量/I2C输出电流INA219 更准带高侧检测板温监测DS18B20单总线组件温度防水封装适合户外光照强度BH1750I2C辐照度勒克斯数字输出不用校准环境温湿度DHT22单总线温湿度经济实惠做环境参考电压采集这里我要特别多说一句。ESP32 的 ADC 输入电压范围是 0 到 3.3V太阳能板空载电压通常在 20V 到 45V 之间取决于板型直接接会烧掉开发板。正确做法是用电阻分压把 0-30V 的电压映射到 0-2.5V 左右的安全范围。分压电阻的选型有讲究后面第三节我给出具体计算过程。2.3 通信协议选择MQTT 在物联网场景的优势数据从 ESP32 传到服务器通信协议我用了 MQTT而不是 HTTP。为什么MQTT 是一种基于发布/订阅模式的轻量级消息传输协议特别适合物联网设备。它有几个优点在光伏监控场景里非常关键一是带宽占用低。消息头可以压缩到 2 字节而 HTTP 每个请求光头部就要几百字节。太阳能监控数据是高频小数据包用 MQTT 效率高得多。二是支持离线消息缓存。MQTT Broker 可以为订阅者保存最后一条消息retained message设备断线重连后能立即拿到最新状态这对长时间运行的监控系统很有用。三是 QoS 机制可以保证消息可靠到达。QoS 0 最多一次QoS 1 至少一次QoS 2 恰好一次。监控场景不需要所有数据都精确一次QoS 0 或 1 就够了。四是天然支持多端订阅。手机、电脑、大屏可以同时订阅同一个主题不需要开发独立的推送逻辑。我实测下来ESP32 通过 MQTT 上报一条包含电压、电流、温度、光照四个数据的 JSON 消息从采集到 Broker 收到局域网内延迟在 10 毫秒以内云端也不过 200 毫秒左右完全满足秒级监控需求。3. 系统搭建实操硬件连接、代码实现与数据通路配置3.1 硬件组装与分压电路计算先说硬件部分。我把系统分成三个模块传感器采集模块、ESP32 主控模块、供电模块。如果你只是做原型验证用面包板搭就行如果是长期户外运行建议画一块 PCB、做防水盒封装。电压分压电路是这里最容易出错的地方。假设太阳能板最大输出电压是 30V我要把它分压到 ESP32 ADC 能安全读取的范围3.3V 以内。分压公式很简单Vout Vin × R2 / (R1 R2)我希望 30V 输入时输出 2.5V留出安全余量。设 R2 10kΩ2.5 30 × R2 / (R1 R2)R1 (30 × R2 / 2.5) - R2 (30 × 10 / 2.5) - 10 110kΩ所以取 R1 110kΩ可以用 100kΩ 10kΩ 串联得到R2 10kΩ这样 30V 对应 2.5VADC 完全安全。同时注意分压电阻上要并联一个 0.1μF 的陶瓷电容做滤波防止高频噪声干扰 ADC 采样。接线顺序我建议是先接电源和地再接 I2C 传感器BH1750、INA219再接 DS18B20最后接分压电路。每接完一个模块就用万用表确认一次供电电压避免短路烧板。DS18B20 的数据线和 VCC 之间必须接一个 4.7kΩ 上拉电阻否则读不到数据这是单总线协议的老规矩。3.2 ESP32 固件编写传感器读取与 MQTT 上报固件我用了 Arduino 框架配合 PlatformIO 做项目管理。为什么不用 ESP-IDF因为 ESP-IDF 写起来太底层配个 Wi-Fi 都要写一堆事件循环回调做原型阶段没必要。Arduino 框架生态好、库多、社区资料丰富踩坑时好搜。核心代码分三部分传感器读取、Wi-Fi 连接、MQTT 发布。传感器读取部分DS18B20 用 OneWire 和 DallasTemperature 库BH1750 用现成的库INA219 也有 Adafruit 的库。ADC 读取电压原始值后要做一个软件校准因为 ESP32 的 ADC 在高阻源驱动下会有非线性。#include WiFi.h #include PubSubClient.h #include Wire.h #include BH1750.h #include DallasTemperature.h #include OneWire.h #include Adafruit_INA219.h // Wi-Fi 和 MQTT 配置 const char* ssid YOUR_WIFI_SSID; const char* password YOUR_WIFI_PASSWORD; const char* mqtt_server 192.168.1.100; // Raspberry Pi 的局域网地址 const int mqtt_port 1883; // 引脚定义 #define ONE_WIRE_BUS 4 #define VOLTAGE_PIN 34 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature ds18b20(oneWire); BH1750 lightMeter; Adafruit_INA219 ina219; WiFiClient espClient; PubSubClient mqttClient(espClient); // ADC 校准参数需要实测标定 float adc_vref 3.12; // 实际参考电压用万用表实测 float voltage_divider_ratio 12.0; // (R1R2)/R2 float adc_real_scale 4095.0 / adc_vref; float readBatteryVoltage() { int raw analogRead(VOLTAGE_PIN); float vout raw / adc_real_scale; float vin vout * voltage_divider_ratio; return vin; } void setup() { Serial.begin(115200); Wire.begin(21, 22); lightMeter.begin(); ds18b20.begin(); ina219.begin(); WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } mqttClient.setServer(mqtt_server, mqtt_port); } void loop() { if (!mqttClient.connected()) { reconnectMQTT(); } mqttClient.loop(); // 采集传感器数据 ds18b20.requestTemperatures(); float panelTemp ds18b20.getTempCByIndex(0); float lux lightMeter.readLightLevel(); float current ina219.getCurrent_mA(); float voltage readBatteryVoltage(); // 构造 JSON 消息 char payload[256]; snprintf(payload, sizeof(payload), {\voltage\:%.2f,\current\:%.2f,\power\:%.2f,\temp\:%.2f,\lux\:%.1f}, voltage, current/1000.0, voltage * current/1000.0, panelTemp, lux); mqttClient.publish(solar/monitor/data, payload); delay(5000); // 5秒采集一次 }有几个细节要提醒第一analogRead在 ESP32 上默认 12 位精度但参考电压不是标准的 3.3V必须用万用表实测 VCC 引脚的实际电压填入adc_vref否则电压测量误差可能达到 10% 以上。第二DS18B20 的读取要等转换完成requestTemperatures()之后不要立刻getTempCByIndex()最好间隔 750ms12 位精度时需要所以我上面代码在实际运行中会加一个delay(750)或者用异步方式处理。第三MQTT QoS 在 PubSubClient 里默认是 0实测已经足够没必要为了可靠性牺牲实时性。3.3 服务端环境搭建Mosquitto、Node-RED 与 InfluxDB 联动服务端我跑在一台树莓派 4B 上2GB 版本就够了操作系统用的 Raspberry Pi OS Lite64 位版本。整个安装流程一行命令的事情sudo apt update sudo apt install -y mosquitto mosquitto-clients node-red influxdb grafana这里有个坑Debian 系的 APT 源里带的 Mosquitto 默认配置只监听本地回环地址外部设备连不上。装完必须改配置文件/etc/mosquitto/mosquitto.conflistener 1883 0.0.0.0 allow_anonymous true如果在意安全可以设置用户名密码后面第五节我会专门讲安全策略。开发阶段先用匿名模式通了再收紧。Node-RED 在这里扮演的是数据总线角色它订阅 MQTT 主题把 JSON 消息转成 InfluxDB 的数据点写入。为什么要中间加一层 Node-RED而不是让 ESP32 直接写入 InfluxDB因为 InfluxDB 的写入端口8086如果直接暴露到局域网设备多的时候并发控制会变得很麻烦而且 ESP32 的 HTTP 客户端在异常场景下容易挂起。用 MQTT 解耦后数据先到 Broker再由 Node-RED 统一入库链路清晰也方便后续接多个设备。Node-RED 里的流程很简单MQTT In 节点订阅solar/monitor/data后面挂一个 Function 节点做字段拆分再连 InfluxDB Out 节点写入时序数据库。Function 节点里可以顺便做数据清洗比如电压小于 0 的脏数据直接丢弃温度超过 100 摄氏度的异常值标记为 NaN。InfluxDB 的数据库要提前建好我用的 1.8 版本APT 源里默认如果没有就先用一个最简单的 measurement 结构CREATE DATABASE solar_monitor3.4 Grafana 可视化面板配置流程数据进库之后用 Grafana 展示。安装完成后第一次访问http://树莓派IP:3000默认账号密码都是 admin登录后第一件事改密码。添加数据源Configuration → Data Sources → Add data source → 选择 InfluxDB填 URLhttp://localhost:8086、数据库名solar_monitor、用户密码默认空。然后创建 Dashboard我建了三个核心 Panel第一个是发电功率实时曲线用 Gauge 类型显示当前功率阈值设置为 0 到 3000W颜色分三段一眼就能看出当前板子有没有在出力。第二个是电压和电流历史趋势用 Time series 类型横轴时间、纵轴数值电压和电流用不同颜色标注。这样的好处是能同时观察负载变化时电压电流的联动关系判断系统是否正常工作。第三个是环境温度与板温对比同样是 Time series可以在多云天气看到板温比环境温度高多少度。实测在阳光直射下板温通常比环境温度高 20 到 30 度这个数据对评估光伏板散热设计很有用。Grafana 的查询语句用 InfluxQL 写比如功率曲线就是SELECT mean(power) FROM solar_data WHERE $timeFilter GROUP BY time($__interval) fill(null)4. 告警规则、异常检测与数据价值挖掘4.1 梯度告警机制设计监控系统如果只是看看数据价值就少了一半。真正有用的是告警。我自己设计了一套基于梯度的告警规则用 Node-RED 的 Function 节点判断然后走 Telegram Bot 推送。为什么用 Telegram因为它的 Bot API 免费、稳定、支持群组推送而且能在手机电脑同时收到。告警分三个级别级别触发条件响应动作警告功率低于预期值 30% 持续 10 分钟发送通知标记为白蚁排查严重电压超过安全阈值例如 32V立即推送同时断开负载继电器紧急温度超过 85 摄氏度推送 蜂鸣器响 记录日志这套分级逻辑的核心思路是不是所有异常都值得打扰用户只有影响系统安全或持续低效的才需要人工介入。比如瞬时云朵遮住阳光导致发电功率骤降这是正常现象不应该触发告警但如果功率持续偏低可能就是板子脏了、遮挡了、或者个别电池片热斑这时候人工检查才有意义。4.2 异常检测从“看数据”到“看懂数据”装完系统跑了三个月我积累了一组典型数据。把这组数据做成基线之后异常检测就变得容易了。最简单的方法是基于历史均值判断。比如早晨 9 点到下午 3 点之间的理论峰值功率阴天时会降到晴天的 20%-40%但如果晴天时功率只有同条件历史的 60%那说明有问题。另一个是电压-电流特性曲线分析。太阳能板输出符合 I-V 特性正常情况下电压在某个区间内缓慢变化电流随光照线性变化。如果电压突然掉得很厉害但电流没变大概率是板子内部有旁路二极管导通如果电流衰减但电压稳定可能是一块或几块电池片被遮住了。我实测过一个典型场景一块树叶落在板角系统显示功率从 200W 掉到 130W但电压只降了 2V电流降了 0.8A。如果没有监控系统这个故障可能要等月底看电费单才能发现。有了数据记录一眼就能看出问题。4.3 数据价值延伸发电效率趋势与衰减分析数据积累多了之后还能做一件很有价值的事情长期衰减分析。光伏板的输出会随时间逐渐衰减标准质保是 25 年不低于 80% 额定功率。但每一块板子实际衰减率不一样如果能长期记录发电数据就能绘制出衰减曲线及时发现个别衰减过快的组件。我目前的做法是每周计算一次“日均发电量/日均光照度”得到一个效率系数。把这个效率系数绘制成月趋势图如果斜率异常陡峭就说明系统出现了增量衰减。这个方法不要求专业辐照度计用 BH1750 的光照数据做归一化精度虽然不如专业设备但趋势判断是够用的。5. 安全加固与长期运行维护心得5.1 MQTT 认证授权配置开发阶段匿名访问没问题但系统跑起来面向局域网甚至公网时裸奔会出事。我见过有人把光储监控系统暴露在公网结果被人控制逆变器更改参数的事故虽然是极端案例但安全意识不能少。Mosquitto 的认证配置很简单sudo mosquitto_passwd -c /etc/mosquitto/passwd solar_client sudo mosquitto_passwd /etc/mosquitto/passwd admin然后修改配置文件listener 1883 0.0.0.0 allow_anonymous false password_file /etc/mosquitto/passwdESP32 固件里连接 MQTT 时加上用户名密码mqttClient.connect(solar_node_01, solar_client, 你的密码);记住密码不要硬编码在源码里提交到 GitHub我吃过这个亏代码传上去第二天就有国外 IP 尝试连接密码被扫了。正确做法是用 PlatformIO 的secrets.h文件并把它加入.gitignore。5.2 数据备份与设备断线重连长时间运行的监控系统最怕的不是数据不准而是数据丢失。ESP32 偶尔会死机、Wi-Fi 会断、树莓派会重启任何一个环节出问题都会造成数据空洞。我的处理策略是三层防护第一层ESP32 固件里加看门狗定时器ESP32 的 TaskWatchdog主循环超过 30 秒没有执行就强制重启。写一个简单的健康检查任务每 10 秒喂一次狗。第二层数据双链路冗余。ESP32 在发布 MQTT 的同时把最近 1000 条数据缓存到 SD 卡模块。当 MQTT Broker 不可达时本地缓存不丢Broker 恢复后ESP32 再补传缓存数据。这样即使断网 12 小时数据也能等到恢复后自动补齐。第三层InfluxDB 定期备份到 NAS。树莓派上跑一个 cron 任务每周把 InfluxDB 的数据库目录打包传到局域网 NAS。0 3 * * 0 influxd backup -portable /mnt/backup/solar_$(date \%Y\%m\%d)5.3 户外长期运行的防护经验如果你跟我一样把传感器放在屋顶或户外有几个防护细节必须重视接线端子用防水的航空插头不要用杜邦线直接裸露在外面雨淋几次就会氧化接触不良。所有 PCB 板刷三防漆尤其是传感器接口区域防潮防腐蚀。DS18B20 买防水探头版本不锈钢封装的不是那种裸铜的。采集频率不要太高。我最初设的是 1 秒一次持续跑了三天发现 ESP32 温度过高因为 ADC 连续采样发热后来改成 5 秒一次稳定多了。太阳能板的热惯性很大5 秒采一次完全够用不需要 1 秒级。供电方案上ESP32 的功耗大约 80mAWi-Fi 开启时如果用 5V 太阳能充电板搭配 18650 电池供电一块 2000mAh 的电池能撑 20 小时以上足以过夜。我的方案是直接从光伏系统的 12V 蓄电池取电加一个 DC-DC 降压模块降到 5V稳定可靠。6. 常见问题与排查技巧实录6.1 ESP32 连不上 Wi-Fi这个问题出现频率最高。排查五步走确认 SSID 和密码没错注意大小写和特殊字符。如果密码里有#、$之类的字符在 C 字符串里要转义。确认路由器是 2.4GHz 网络。ESP32 不支持 5GHz Wi-Fi很多双频路由器默认把 IoT 设备踢到访客网络如果访客网络是 5GHz 那就死活连不上。增加重连逻辑。WiFi.begin()之后要做一个阻塞等待加超时重试而不是只执行一次。我一般设 20 秒超时超时后重启设备。检查天线区域。如果 ESP32 被放在金属外壳里Wi-Fi 信号会被屏蔽输出功率不够。我的做法是把 2.4GHz 天线端露在盒子外面。终极方案换一个 ESP32 模块。有些低质量的 ESP32 模组天线焊接不良Wi-Fi 功率异常换一个模块立竿见影。6.2 电压读数漂移严重ADC 读数不稳定有两个常见原因一是 ESP32 ADC 本身的非线性问题。在 ADC 引脚加一个 100pF 到 1μF 的电容做硬件滤波同时在代码里做软件滤波。我用的是滑动平均滤波器取最近 10 次采样的平均值效果很明显。二是参考电压不稳定。ESP32 的 ADC 参考电压不是精确的 3.3V而且会随温度漂移。要精确的话可以使用内部 Tension Sensortlos校准或者干脆外接一个 TL431 基准电压芯片把基准电压值也作为一个通道采集每次计算时用实际基准值校正。6.3 MQTT 数据断线后无法恢复最常见的原因是保留消息retained message行为导致的问题。如果 Broker 保留了最后一条旧消息新节点上线后立刻收到的是旧数据展示端会把时间戳对不上看起来像“假数据”。处理方法ESP32 发布时把 retained 标志设为 falseNode-RED 端收到数据时不立即写库而是解析时间戳、超过 10 秒的数据直接丢弃。6.4 数据断断续续图表有空洞检查三个点一是 ESP32 是否在高负载时重启看Serial日志里的重启原因rst:0x0二是树莓派上的 Mosquitto 是否内存不足free -h看下内存如果不足就调低log_type减少日志写入频率三是无线网络拥塞家里 Wi-Fi 干扰严重时可以把 ESP32 的路由器绑定到 1、6、11 这些不重叠信道。6.5 温度传感器读数异常-127 或其他乱值DS18B20 报 -127 意味着通信失败。大概率是上拉电阻没接或者接触不良。先量一下数据线对地的电阻应该是 4.7kΩ再确认没有把 VCC 和数据线短接。还有一个坑多个 DS18B20 挂在同一条总线上时如果其中一根线接触不良整条总线都会瘫痪。7. 项目复盘与优化空间系统从最初面包板原型到现在的稳定运行版本我一直在这个项目上打磨了很多轮。第一批坑集中在硬件层面分压电阻没算准差点烧了 ADC、DS18B20 没加上拉电阻导致半小时排查、户外防水没做好遇到连续雨天数据中断。第二批坑在软件层面ESP32 死机没有看门狗、MQTT 断线重连逻辑不完善、InfluxDB 数据库增长导致磁盘满。现在系统稳定运行了大半年每天的发电量、板温、光照数据都能自动记录手机随时打开 Grafana 看一眼数据树叶遮挡、板子脏了、逆变器异常都能第一时间发现。如果继续扩展我觉得有三个方向值得尝试一是接入更多传感器比如风向风速、降雨量做清洗周期预测二是部署轻量级机器学习模型在边缘节点做光伏板故障诊断三是联动智能家居平台比如检测到连续阴天但蓄电池电量低时自动调整负载策略优先保障关键设备。最后分享一个小经验不要一开始就想把系统做到完美先跑通最小闭环再逐步迭代。我第一次做的时候花了三周折腾硬件接线和传感器校准后来发现先把一个电压数据跑通整个链路剩下的都是顺水推舟。项目这东西能跑起来才是第一步跑得久才是真功夫。
返回列表