
一、这个项目到底解决了什么问题做光伏也好做离网供电也罢绝大多数人最开始关心的问题是“今天发了多少电”但实际上真正该关心的是“这块板子是不是一直处在健康工作状态”。一块太阳能面板从出厂到实际使用会经历热斑效应、隐裂、灰尘遮挡、线缆氧化、控制器误动作等各种状况而这些都不是看一眼电压表就能发现的。物理层面的故障往往先表现为电气参数异常开路电压偏低、最大功率点漂移、电流突然跌零、温度异常升高。监控面板本质上就是在收集这些电气特征量让我们在故障真正酿成损失之前就能看到问题。我做的“IoT-Based Solar Panel Monitor”核心就是一套把太阳能板的电压、电流、功率、温度等关键参数实时采集上来再通过物联网通道上报到远端可视化平台的系统。它能用数据告诉你这块板子上午十点被云遮了三分钟下午两点有一阵电流尖峰连续阴雨天之后开路电压降到多少哪块板的输出在某时刻明显掉队。所有这些信息过去需要拿着万用表爬到屋顶逐块测量现在只需要打开手机或浏览器就能看到。这个项目尤其适合三类人一是家里装了离网光伏或屋顶光伏、想搞清自己发电系统到底健不健康的业主二是做户外设备供电、基站、农用监测、野外传感器供电方案的技术人员三是正在学习嵌入式物联网方向、想找一个能同时练到传感器、通信协议、前端可视化的综合项目的学生或转行者。它的门槛并不高我尽量用成熟模块和现成库来搭但麻雀虽小五脏俱全该涉及的指标采集、数据上云、异常告警、故障排查一个都不会少。二、整体设计与技术选型思路2.1 先想清楚监控系统到底要采哪些参数在动手开板子之前我建议先把要测的物理量和精度范围列出来。太阳能板的输出特性是典型的非线性电源主要指标有物理量为什么重要一般量程开路电压Voc反映面板是否正常隐裂/断栅会显著拉低0~50V视板型工作电压Vmp判断控制器是否工作在最大功率点0~40V工作电流Imp判断受光面积、遮挡、脏污情况0~15A视功率输出功率电压电流相乘是能量产出的直接指标0~500W电池板温度热斑效应和高温衰减的关键指标-20~100℃我的面板是12V/50W的离网板Voc约为22V短路电流约2.9A所以电压采样按0~30V设计电流采样按0~5A留余量。如果是100W以上的板子电流量程和分流电阻的功率都要重新算这一点千万别偷懒。把参数定下来后面选型和电路设计就有了依据不会盲目追求高精度或者大范围也不会出现“量到一半爆量程”的尴尬情况。2.2 主控选型为什么是ESP32而不是Arduino或STM32主控方面我用了ESP32。原因很简单它自带Wi-Fi和蓝牙一颗芯片就把采集端和联网端都干了不用外挂ESP8266或者网口模块整体体积和功耗都更好控制。Arduino Uno虽然上手容易但搞物联网还得加通信模块而且Flash和RAM都很紧张存不了什么离线数据。STM32的采集性能确实更稳但对于这个项目来说有点杀鸡用牛刀而且联网同样是个麻烦事。ESP32还有一个隐形的优势ADC采样可以做到12位分辨率在3.3V参考电压下能分辨到0.8mV左右做低电压信号采样完全够用。当然它的ADC线性度一般正经做采集我还是推荐走独立ADC或者用I2C接口的专用测量芯片而不是直接依赖ESP32内置ADC。这一点我在后面故障排查部分会展开说。2.3 电流电压方案INA226比ADC更靠谱我最初图省事想用两个分压电阻加上ESP32的ADC去测电压再用ACS712霍尔传感器测电流。后来实测发现两个坑第一ESP32的ADC参考电压虽标称3.3V但实际会随芯片温度和供电电压漂移如果不做校准误差能到2%左右第二ACS712虽然是现成的电流传感器模块但零漂问题严重标称输出在零电流时经常偏出几十毫伏在小电流场景下读数完全没法看。所以第二版我直接换成了TI的INA226。这是一颗I2C接口的电流/电压/功率监测芯片内置16位ADC实测精度比单片机自带ADC高一个数量级。它采用电流分流法外部接一个毫欧级采样电阻就能高精度采集电流同时还能直接测总线电压折算功率。关键是它把采样、换算、校准都做在芯片内部寄存器里主控只需要通过I2C读寄存器开发量小很多读数也稳定很多。2.4 通信协议MQTT几乎是为这类场景写的系统要联网不能只是把数据从设备推到服务器就完事。现场的设备可能因为断网、断电反复上下线数据流是间歇性的且一次上报的数据量很小几字节到几十字节。这种场景下MQTT比HTTP更合适。MQTT的最大优势是发布订阅模型设备只管往主题Topic里发布数据谁感兴趣谁去订阅天然解耦了生产者和消费者。而且它有QoS等级控制可以选择“至多一次”“至少一次”“恰好一次”三种可靠性级别配合遗嘱消息Last Will还能在设备异常下线时通知服务器。对于太阳能板这种经常处于弱网、断网环境的采集节点MQTT的断线重连和小报文特性非常实用。整体架构也就顺理成章ESP32采集INA226和温度传感器数据 → 整理成JSON → 通过MQTT发布到Broker → 服务端订阅并入库 → 前端仪表盘展示。如果你想降低对外部平台的依赖也可以直接把Mosquitto跑在一台内网机器上甚至在家里用树莓派搭一套自闭环的平台所有东西都在自己手里。三、硬件搭建与核心电路3.1 元器件清单与采购建议这个项目的硬件成本其实不高我采购的元器件加起来不到一百块钱不含太阳能板清单如下元件型号/规格数量备注主控板ESP32 DevKitC V41选择Flash不小于8MB的版本电流电压芯片INA226模块1买带采样电阻的成品模块省心采样电阻0.01Ω/3W或0.001Ω/5W1视电流量程选型温度传感器DS18B20防水探头1单总线可有多点分压电阻30kΩ 7.5kΩ等2~3用于扩展电压量程稳压模块MP1584或AMS11171从太阳能/电池取电给ESP32连接线接插件、纯铜线若干电流检测线要用粗线一些细节经验INA226模块尽量不要买那种不带采样电阻的裸板因为采样电阻的选型和焊接都会影响精度买成品模块可以直接用。采样电阻选型的关键是压降和功率。额定电流3A时如果选0.001Ω电阻压降只有3mV电流信号很容易被噪声淹没如果选0.01Ω压降达到30mV对系统的电压损耗又有点大。我的原则是压降控制在50mV以内电阻功率按1.5倍余量选。ESP32的GPIO输出能力有限如果后续要驱动蜂鸣器、继电器、屏幕一定要加三极管或MOS管驱动不要直接怼在GPIO上。3.2 电压采样电路设计INA226本身能测总线电压但它的输入电压范围要看模块设计很多模块参考的是INA226的VBus引脚量程上限一般在36V左右。我的面板Voc约22V直接接VBus是安全的。但如果你用的是两串或三串的板子Voc可能到60V甚至更高就必须先用分压电阻降压再接VBus。分压电路的原理很简单在VBus和地之间串联两个电阻取中间节点接到INA226的测量输入。计算时注意两个要点中点电压不得高于INA226允许的最大输入一般不超过5V分压电阻的阻值不能太小否则会额外消耗系统功率也不能太大否则采样电路输入阻抗会显著影响精度。以量程0~100V为例我选R195kΩ、R25kΩ分压比是20:1。中点最高电压为100V×(5/100)5V刚好贴着安全上限。这个分压比值在固件配置里设置好后续读取时乘回去就行。如果你用INA226的默认寄存器配置本身就有校准寄存器可以直接把分压系数写进去让芯片输出的就是实际电压值。3.3 电流采样INA226配置流程INA226的电流测量是量测采样电阻两端的差分电压。芯片内部有一个连续累加器会自动把采样电压乘以某个系数再通过校准寄存器输出实际电流值。校准公式如下Current_LSB Max_Expected_Current / 32768 Current_LSB × R_shunt 压降系数 Shunt_Voltage_LSB 2.5µV芯片固定 Current_Register_Value Shunt_Voltage_Reading / Current_LSB我用的模块采样电阻是0.002Ω最大预期电流5A所以Current_LSB为152.6µA/bit左右。每次刷新时读取电流寄存器和功率寄存器再除以对应LSB就能得到实际电流和功率。这个转换过程看起来复杂但实际用Arduino库只要几行代码#include INA226_WE.h INA226_WE ina226(0x40); void setup() { Wire.begin(); ina226.init(); ina226.setAverage(AVERAGE_64); ina226.setVoltageConversionTime(BIT_TIME_1100); ina226.setCurrentConversionTime(BIT_TIME_1100); ina226.setCalibration(5.0f, 0.002f); // 5A量程, 0.002Ω采样电阻 } void loop() { float busVoltage ina226.getBusVoltage_V(); float current ina226.getCurrent_A(); float power ina226.getBusPower(); float shuntVoltage ina226.getShuntVoltage_mV(); delay(1000); }注意setCalibration里的两个参数一个是预期最大电流一个是实际使用的采样电阻阻值必须跟硬件严格对应否则读出来的电流会是错的。3.4 接线顺序与自供电设计接线方面最容易出问题的是电流采样电阻的接入位置。做高边电流采样时采样电阻要串联在电源正极和负载之间INA226的VIN接电源侧VIN-接负载侧。如果反接读出的电流会是负数虽然不会立刻损坏芯片但会给后续逻辑判断带来很大困扰。供电方面这个系统通常部署在户外不能指望拉一个220V适配器过去。我的做法是把太阳能板输出先接到一个MP1584降压模块上将电压降到5V左右再通过AMS1117稳压到3.3V给ESP32供电。注意MP1584的输入耐压值要覆盖面板开路电压否则天晴时面板端电压飙升容易把降压模块打坏。一个需要特别留意的点太阳能板在强光下的短路电流可达标称值的1.2倍以上接线端子的接触电阻稍大就会发热。所有大电流回路的导线尽量用14AWG或更粗的纯铜线端子压接好后最好用热缩管保护别用焊锡直接焊在细线上糊弄事。四、物联网接入与核心代码实现4.1 MQTT主题设计主题设计是整个系统通信的基础。规模小的时候随便写没关系一旦面板数量上到几十块主题规划不合理就会让人崩溃。我的推荐是采用三层结构solar/{site_id}/{panel_id}/telemetry solar/{site_id}/{panel_id}/alarm solar/{site_id}/{panel_id}/status其中{site_id}是电站或设备站点编号{panel_id}是面板编号。telemetry主题用来上报电压、电流、功率等周期性数据alarm主题用来上报异常事件status主题用来上报设备在线状态和遗嘱消息。一个面板一秒钟发一条数据大规模部署时的主题数量会非常大所以务必在主题里带上站点号和面板号方便服务端按前缀订阅或按通配符处理。如果用通配符注意匹配单层#匹配多层不要搞混。4.2 ESP32端代码骨架ESP32端程序的主要任务就是读取传感器数据 → 组装JSON → 通过MQTT上报 → 进入低功耗或延时。为了防止长时间运行后出现死机我一般会在主循环里加看门狗并且每次上报成功后更新一个上次在线时间戳方便远端判断节点是否存活。#include WiFi.h #include PubSubClient.h #include ArduinoJson.h #include INA226_WE.h #define WIFI_SSID your_wifi #define WIFI_PASS your_password #define MQTT_HOST your.broker.com #define MQTT_PORT 1883 #define PANEL_ID panel_001 #define SITE_ID site_a INA226_WE ina226(0x40); WiFiClient espClient; PubSubClient mqtt(espClient); unsigned long lastPub 0; const int PUB_INTERVAL_MS 5000; void connectWiFi() { WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() ! WL_CONNECTED) { delay(500); } } void connectMQTT() { while (!mqtt.connected()) { if (mqtt.connect(esp32_solar_panel_001)) { // 遗嘱消息节点异常离线时通知服务端 mqtt.publish(solar/ SITE_ID / PANEL_ID /status, offline); } else { delay(2000); } } } void publishTelemetry() { float voltage ina226.getBusVoltage_V(); float current ina226.getCurrent_A(); float power ina226.getBusPower(); StaticJsonDocument256 doc; doc[panel_id] PANEL_ID; doc[voltage] voltage; doc[current] current; doc[power] power; doc[ts] millis(); char buffer[256]; serializeJson(doc, buffer); mqtt.publish(solar/ SITE_ID / PANEL_ID /telemetry, buffer); } void setup() { Serial.begin(115200); Wire.begin(); ina226.init(); ina226.setCalibration(5.0f, 0.002f); connectWiFi(); mqtt.setServer(MQTT_HOST, MQTT_PORT); mqtt.setKeepAlive(60); connectMQTT(); } void loop() { if (!mqtt.connected()) { connectMQTT(); } mqtt.loop(); if (millis() - lastPub PUB_INTERVAL_MS) { publishTelemetry(); lastPub millis(); } }这段代码没有任何花哨的东西就是确保不丢包、不频繁断线。需要注意mqtt.setKeepAlive(60)的数值如果设置为30秒但网络状态波动导致无法在30秒内完成握手Broker就会主动断开连接。对于太阳能板这种数据更新频率不高的传感器keep alive设置60秒甚至90秒都行减少无效报文。4.3 JSON格式与数据压缩上传的数据体我用的是JSON当然也可以用更紧凑的二进制格式但JSON的通用性和调试便利性太高了处理异常时随便抓一条报文就能看出问题在哪。唯一要留意的是ArduinoJson库的缓冲区大小太小会截断数据太大浪费内存。我在ESP32里声明StaticJsonDocument256它会在栈上分配256字节足够容纳面板信息加三项浮点数和时间戳。如果字段再多建议换成DynamicJsonDocument按需在堆上申请。发布JSON的数量级虽小但长期传输还是要考虑流量成本。面板每5秒上报一次一天产生的数据量约为每次 120 字节 × (86400 / 5 秒) ≈ 2.07 MB/天一年接近756MB如果使用运营商的物联网卡流量套餐这个量级还负担得起但再增加数据字段或者提高采样频率流量费用就会明显上升。必要时可以改成“变化上报”策略只有当电压或功率变化超过阈值时才上报这样夜间大段时间几乎不产生流量。4.4 设备安全与OTA升级讨论物联网如果不涉及安全等于把设备裸奔在公网里。我见过太多太阳能采集节点用的是默认Wi-Fi密码、明文MQTT消息数据被谁订阅走了都不知道。虽然光伏面板监测数据本身敏感度不高但如果有人恶意伪造消息把告警关掉或者发送篡改的功率值就会给运维人员造成极大误导。所以我的惯例是用WPA2/WPA3保护Wi-Fi网络不要用WEP和开放网络MQTT启用TLS加密这需要Broker端配置证书客户端代码里也要内置CA证书设置MQTT用户名和密码尽量不用allow anonymous生产环境做好设备证书指纹校验防止中间人攻击。OTA升级也值得做。设备部署到屋顶之后不可能为了改一个小参数就爬上爬下插串口。ESP32的Arduino生态自带Update库配合MQTT可以做一个“收到升级命令→从HTTP服务器拉取固件→写入Flash→重启”的流程。具体实现不展开但提醒一句OTA固件写入失败会直接把设备变砖所以一定要用双分区方式OTA App 0 / OTA App 1让设备回退到上一个可用版本千万别只留一个分区。五、数据存储、可视化与告警5.1 数据接入层如何设计数据到了Broker之后不能只靠人眼盯着看还需要一条自动化管道把它落库。我的选择是Node-RED因为它自带MQTT输入节点可以非常方便地订阅主题、解析JSON、再写入时序数据库。流程大概是MQTT in → JSON parse → 数据清洗过滤非法值 → 存入InfluxDB如果你不喜欢用Node-RED这种重工具也可以写一个简单的Python服务用paho-mqtt订阅主题然后批量写入数据库。Python方案的好处是可以直接写自定义逻辑比如阈值判断、异常修复、多设备聚合。缺点是得自己处理断线重连和重复消息工作量会大一点。5.2 可视化仪表盘怎么搭可视化方面我比较推荐Grafana加InfluxDB的组合。Grafana对时序数据支持非常好可以自动生成电压、电流、功率曲线还能做日发电量、月发电量对比也能方便地设置告警通知。如果你的目标平台是Home Assistant那更简单HA本身就有MQTT集成把主题配置成sensor实体然后直接在前端面板里拖拽卡片就能展示。无论选哪种方案仪表盘至少要能展示当前实时功率和当日累计发电量电压、电流、功率的实时曲线近一小时/一天/一周可切换面板板温曲线便于发现热斑风险设备离线/在线状态灯。我在做曲线图时踩过一个坑Grafana默认对NaN值不显示但时序数据库如果采集空白期太长曲线会出现莫名其妙的横线或空白看起来就像设备故障。解决方式是设置明确的fill策略和数据保留周期把缺失区间标记为null而不是0。5.3 分钟级聚合和日发电量统计单条原始数据只能反映瞬时状态真正做运维决策要看日累计、月累计。这里不建议直接在Grafana里对原始数据做sum聚合因为数据是5秒一个点999个点里只要缺几个sum结果就不准。更稳妥的做法是在写入数据库的同时用定时任务做分钟级聚合每分钟把该分钟内的原始数据求平均或求和再写另一张聚合表。我的经验是原始表保留7天以上用于观察瞬时故障分钟聚合表保留90天用于趋势分析小时/日聚合表保留一年以上用于发电量统计。这样存储开销可控查询响应也快。太阳能板监测不像股票交易那样需要毫秒级实时性分钟级聚合完全够用。5.4 告警规则怎么定告警是该系统的灵魂。如果只看曲线人不可能24小时盯着看除非异常已经影响到用电设备否则人工发现都会滞后。我的告警规则设计如下告警项判定条件等级可能的物理原因电压异常开路电压低于正常值的80%持续5分钟严重面板损坏、连接线断开、被严重遮挡电流接近零白天光照正常但电流连续10分钟接近0严重接线松动、控制器故障、负载断线功率突变功率在1分钟内下降超过50%且未恢复提示云层遮挡、树叶掉落、飞鸟停留板温过高面板温度超过80℃提示通风不良、热斑风险设备离线MQTT遗嘱消息触发严重断电、网络故障、设备死机第三项“功率突变”尤其重要因为一开始我也没设置它后来发现面板被鸟粪盖住一角曲线一直稳稳当当直到发电量数据出现明显下滑才被发现。加了这个规则之后系统自动捕获了两次短时遮挡事件这种“不可见的小问题”才是监控系统真正该抓的东西。告警推送我选了企业微信机器人接口代码里只需发一个HTTP POST请求。运行至今验证下来推送成功率非常稳定速度和成本都很好。六、常见问题与故障排查实录6.1 采集读数明显偏移怎么办这几乎是每个新手都会遇到的第一道坎。我第一版系统跑起来后电流读数一直比万用表高0.3A左右电压高1V多排查了老半天。先说明一点分压电阻精度是电压漂移的主要来源。普通贴片电阻精度5%温度系数可能到几百ppm/℃。太阳能板在阳光下外壳温度能到60-70℃分压电阻阻值一变采样系数就漂了。所以我后来把关键分压电阻全部换成了0.1%精度、25ppm/℃的金属膜电阻电压读数立刻稳定了很多。电流漂移则更多是采样电阻公差问题。INA226有一个校准寄存器它设置的电流LSB基于采样电阻的标称值但实际电阻阻值有偏差。比如0.002Ω的电阻实测可能是0.0021Ω那读出的电流就会偏高5%。要修正很简单用高精度万用表测出实际采样电阻值再把setCalibration里的第二个参数填实测值而不是标称值。还有一个高概率坑是接触电阻。采样电阻两端如果用的是排针或面包板连接每次插拔都会改变接触电阻导致读数来回跳。我的解决办法是直接把采样电阻焊死在模块上连线用压接端子不用插座。6.2 MQTT频繁掉线、数据丢失怎么排查设备在线状态像心跳一样起起伏伏是最让人头疼的。我总结出的排查路径是先确认是不是Wi-Fi信号问题。把ESP32放在室内信号满格稳得很放到太阳能板旁边金属框架一挡信号直接少20dB此时很容易掉线。解决方法是把天线尽量远离金属板或者使用带外置天线的ESP32模块。再确认Broker端的超时设置。如果客户端设置keep alive为60秒正常情况下每45秒左右发送一次PINGREQBroker如果在四个半keep alive周期内没收到任何包就会断开。无线环境差时PINGREQ丢失很正常这时要把keep alive适当调大。检查是否存在重复ClientID。如果在同一个Broker上跑了两个相同ClientID的设备后登录的会把先登录的踢下线。这个问题在部署多块面板时尤其容易踩坑一定要在ClientID里加上面板编号。最后看供电质量。ESP32的Wi-Fi射频对电源纹波比较敏感如果供电电压抖动厉害射频部分会频繁重连。我给ESP32的3.3V端加了一个100µF的电解电容和0.1µF的陶瓷电容掉线频率降了很多。6.3 断电续传与重启后的状态恢复太阳能电站最典型的场景是夜里断电、早上来电或者连续阴雨天电池放空后自动关机。如果设备意外断电内存里缓存的数据会全部丢失这是所有物联网采集设备共同的问题。我的方案是增加一块RTC和一个小容量的EEPROM或使用ESP32内置的NVS每次上报成功后将最新数据写进NVS设备重启后先读NVS恢复上次状态再继续采集。同时启动时主动发送一条status消息告知服务端“设备已上线并恢复运行”服务端就能根据这个时间点和最后一条telemetry判断出断电时间段。多说一句如果项目后续要扩展到几十上百块面板建议在所有设备上统一上次在线时间戳格式并定期做设备时间同步NTP或校时否则时序数据入库后会出现严重的时间错位。6.4 整个系统跑不起来的通用确认序列遇到任何不顺我基本按下面顺序排查90%的问题都能定位上电看串口日志确认ESP32是否启动、是否连上Wi-Fi、是否成功连上MQTT用MQTT Box或命令行工具手动订阅主题看Broker是否收到消息确认服务端的订阅规则和存储逻辑有没有bug别上来就怀疑硬件检查数据库保留策略确认数据是没写入还是写入了但被删了再看仪表盘查询语句很多“数据丢失”其实是SQL写错把时间范围过滤错了。这套流程适用于绝大多数物联网采集项目可以当成通用排查手册。七、项目总结与扩展思路整套系统下来我最深的感受是物联网采集项目真正难的不是硬件也不是软件而是把数据链路从“采集裸值”贯通到“决策可用的信息”。一个读数的漂移可能是传感器问题可能是供电问题也可能是传输问题一个掉线可能是网络问题也可能是设备状态问题。这种层层递进的排查能力光看文档是学不会的必须靠实际烧几块板子、掉几次线才能建立感觉。如果你想把这个项目继续往深做以下几个方向都很有价值从单面板扩展到多面板阵列加入面板汇流箱级别的数据聚合和拓扑自动发现引入历史发电量预测模型结合当地日照数据做发电预期偏差判断把告警系统接入运维工单系统实现异常自动派单硬件上进一步优化低功耗让节点仅靠太阳能就能全年不间断运行。这个项目完全可以作为智能光伏运维、分布式能源监控、农业物联网供电监测等多种场景的原型它麻雀虽小但核心链路和技术栈都是非常典型的物联网应用模板。如果你想做实验建议从一块小功率面板和第二块旧手机电池开始成本低、改造成本也小先把数据流跑通再逐步增加东西项目的乐趣会大得多。