
做太陽能板監控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換成