在可再生能源領域,太陽能處于最前沿,因為利用太陽能發電是可再生能源最簡單、商業上可行的方式。說到太陽能電池板,需要監控太陽能電池板輸出的輸出功率,以便從電池板獲得最佳功率輸出。這就是為什么需要實時監控系統的原因。在大型太陽能發電廠中,它還可用于監控每個面板的功率輸出,這有助于識別灰塵堆積。它還可以防止運行期間出現任何故障情況。在這個項目中,我們將通過結合基于MPPT (最大功率點跟蹤器)的電池充電技術來制作基于物聯網的太陽能監測系統,這將有助于減少充電時間并提高效率。此外,我們將測量面板溫度、輸出電壓和電流,以提高電路的安全性。最后,最重要的是,我們將使用 ThingSpeak 云服務來監控來自世界各地的輸出數據。
為支持物聯網的太陽能監視器選擇合適的組件
使用太陽能監視器,監視和檢測任何太陽能系統中的故障變得非常容易。這就是為什么在設計這樣的系統時組件選擇成為一個非常重要的部分。下面給出了我們使用的部件列表。
ESP32 開發板
MPPT電路(可以是任何太陽能電路)
分流電阻器(例如 1 Ohm 1 watt - 適用于高達 1A 的電流)
鋰電池(首選 7.4v)。
有效的 Wi-Fi 連接
太陽能電池板的溫度傳感器
分壓器電路(見說明)
Esp32 開發板:
對于支持物聯網的應用程序,必須選擇正確類型的開發板,該開發板能夠處理來自其模擬引腳的數據并通過任何類型的連接協議(如 Wi-Fi 或云)發送數據服務器。我們特別選擇了 ESP32,因為它是一款具有大量功能的低成本微控制器。此外,它有一個內置的 Wi-Fi 收音機,我們可以通過它非常輕松地連接到互聯網。
太陽能電路:
太陽能充電電路是從太陽能電池板獲得更高電壓并將其轉換為充電電壓的電路,以便它可以有效地為電池充電。對于這個項目,我們將使用基于 LT3562 的 MPPT 充電控制器電路板,我們已經在之前的一個項目中制作了該電路板。但是如果你想嵌入這個物聯網啟用監控,你可以使用任何類型的太陽能電路。我們選擇此板是因為該電路配備了最大功率點跟蹤 (MPPT),這對低功率太陽能電池板項目非常有利。這是從太陽能電池板為小型鋰電池充電的有效方法。
分流電阻:
任何電阻器都遵循歐姆定律,這意味著如果一定量的電流流過電阻器,就會出現一定量的電壓降。分流電阻器也不例外,它專門用于測量電流。但是,根據流過太陽能電池板的標稱電流,選擇一個分流電阻器,該電阻器將產生足夠的電壓,該電壓可由微控制器單元測量。但是,與此同時,電阻的瓦數也很重要。分流電阻功率的選擇也很重要。
可以使用下面給出的公式計算電壓降。這被稱為歐姆定律——
V = I x R
V 是在“I”期間將產生的電壓,即流過電阻“R”的電流量。例如,1歐姆的電阻在1A的電流流過時會產生1V的壓降。
對于電阻器的瓦數,可以使用下面給出的公式 -
P=I 2 R
其中 I 是最大電流,R 是電阻值。對于具有 1 歐姆電阻的 1A 電流,1 瓦的功率耗散就足夠了。然而,這對小型太陽能電池板項目很有用,但根本不適合與太陽能電網相關的應用。在這種情況下,實際上需要使用非侵入式電流測量技術。在這種情況下,可以精確測量電流,其中可以測量非常低的電流量以及非常高的電流量。
鋰電池:
鋰電池的選擇是任何涉及太陽能電池板的項目的重要組成部分。因為始終保持開啟并不斷檢查和提交數據的微控制器單元需要至少一百毫安的電流才能穩定運行。
當由于季風而沒有陽光照射時,電池容量應該可以為微控制器供電至少 4-5 天。從電池的角度來看,充電電流必須大于負載電流也很重要。如果有人將 100mA 的負載與電池連接并提供小于該值的充電電流,這是很不尋常的。為了安全起見,我們的充電電流至少應該是負載電流的 5 倍。
另一方面,電池電壓需要高于微控制器所需的任何常用穩壓器輸入電壓。例如,一個 7.4V 鋰電池可以連接在 3.3V 和 5.0V 線性穩壓器上(因為線性穩壓器需要比 LDO 和開關更高的壓差電壓。)
在我們的項目中,我們使用了額定電壓為 7.4V 的 4000mAH 電池。我們使用了一個 5.0V 穩壓器,可為 ESP32 提供足夠的電流和電壓輸出。
分壓器:
分壓器是太陽能電池板電壓測量的重要組成部分。應該選擇一個分壓器,根據微控制器 I/O 電壓輸入來分壓。
選擇上述電阻,使分壓器輸出電壓不應超過微控制器最大 I/O 電壓(ESP32 為 3.3V)。但是,建議使用電位器,因為它可以靈活地選擇任何更高或更低額定電壓的太陽能電池板,并且可以使用萬用表輕松設置電壓。
在我們的例子中,我們在 MPPT 板電路中有一個電位計,用作分壓器。我們設置分壓器的分壓系數為 6V。我們接了兩只萬用表,一只在鍋的輸入端,一只在鍋的輸出端,并設置當輸入電壓為18V時輸出為3V,因為太陽能電池板的標稱輸出電壓為18V。
太陽能電池板的溫度傳感器:
太陽能電池板的功率輸出與太陽能電池板的溫度直接相關。為什么?因為隨著太陽能電池板的溫度開始增加,太陽能電池板的輸出電流呈指數增長,而電壓輸出開始呈線性下降。
根據功率公式,瓦數等于電壓乘以電流 (W = V x A),即使電流增加,降低輸出電壓也會降低太陽能電池板的輸出功率。現在,我們想到的下一個問題是,如何測量太陽溫度?好吧,這很有趣,因為太陽能電池板通常暴露在熱環境中,因為它暴露在陽光直射下,原因很明顯。測量太陽能電池板溫度的最佳方法是使用平面溫度傳感器。還建議使用直接放置在太陽能電池板中的 K 型熱電偶。
對于我們的應用,我們使用了基于熱敏電阻的溫度傳感器模塊,如下所示。
基于物聯網的太陽能監測電路圖
啟用 IoT 的太陽能監視器的完整電路圖如下所示。原理圖很簡單。紅色點劃線板是我們在這個項目中使用的 MPPT 板。
設置 ThingSpeak
使用 ThingSpeak 創建一個帳戶并轉到“我的頻道”選項,然后單擊新頻道。
使用字段名稱創建一個新通道。
現在設置字段后,轉到可以使用 Write API Key的API Keys字段。需要在代碼中提供此密鑰以及通道 ID。
ThingSpeak 地址可在同一頁面上找到。
通過上述步驟,您可以非常輕松地設置 ThingSpeak。如果您想了解有關ThingSpeak 及其設置過程的更多信息,您可以查看我們之前關于該主題的文章。
使用 ESP32 進行太陽能監測的 Arduino 代碼
完整的 ESP32 太陽能監控代碼可以在本頁底部找到。代碼首先定義您的 SSID、密碼和一些其他常量參數,如下所示。 ??
?
// 為上行鏈路定義 WiFi SSID 和 PWD。 #define WLAN_SSID "xxxx" #define WLAN_PASS "xxxxxxxxxx"
?
在此字段中,需要設置 SSID 和密碼。
?
// 25 攝氏度時的電阻 #define THERMISTORNOMINAL 10000 // 溫度。對于標稱電阻(幾乎總是 25 C) #define TEMPERATURENOMINAL 25 // 熱敏電阻的 beta 系數(通常為 3000-4000) #define BCOEFFICIENT 3950 //“其他”電阻的值 #define SERIESRESISTOR 10000
?
熱敏電阻標稱歐姆是在標稱溫度下提供的。根據熱敏電阻的數據表設置此值。放熱敏電阻的Beta系數和串聯電阻值。
?
// 為電流和電壓定義模擬 const int curr_an_pin = 35; 常量 int volt_an_pin = 34; 常量 int ntc_temp_an_pin = 33;
?
PIN 在這里定義。
?
#define thingSpeakAddress "xxxxxxxx" #define channelID xxxxx #define writeFeedAPIKey "xxxxxxx" #define readFeedAPIKey "xxxxxxx" #define readFieldAPIKey "xxxxxxxx" #define readStatusAPIKey "xxxxxxx"
?
放入thingSpeakAddress、channelID、Write Feed API Key。其余的東西不是必需的,但如果需要從 Web 接收數據,它們仍然有用。
?
void setup() { // 將您的設置代碼放在這里,運行一次: // 將串口設置為 115200 Serial.begin(115200); //初始化串行 延遲(1000); WiFi.mode(WIFI_STA); ThingSpeak.begin(客戶端); // 初始化 ThingSpeak // todo: 創建一個任務來讀取引腳以獲取電流和電壓并計算太陽能電池板的瓦特和溫度 xTaskCreate( wifi_task, /* 任務函數。*/ "wifi_task", /* 名稱為的字符串task. */ 1024 * 2, /* 堆棧大小(以字節為單位)。 */ NULL, /* 作為任務輸入傳遞的參數 */ 5, /* 任務的優先級。*/ 空);/* 任務句柄。*/ Serial.print("數據讀取。"); }
?
在上面的代碼中,初始化了 ThingSpeak 服務器并創建了一個任務,該任務將獲取與太陽能電池板相關的數據。
在主回路中,太陽能電流和電壓通過模擬引腳進行檢測,并進行平均。
?
浮動太陽能curr_adc_val = 0; 浮動太陽能伏特adc_val = 0; for (i = 0; i < NUMSAMPLES; i++) { curr_samples[i] = analogRead(curr_an_pin); volt_samples[i] = 模擬讀取(volt_an_pin); temp_samples[i] = 模擬讀取(ntc_temp_an_pin); 延遲(10); } // 平均所有樣本 float curr_avg = 0; 浮動 volt_avg = 0; 浮動 temp_avg = 0; for (i = 0; i < NUMSAMPLES; i++) { curr_avg += curr_samples[i]; volt_avg += volt_samples[i]; temp_avg += temp_samples[i]; } curr_avg /= NUM??SAMPLES; volt_avg /= NUM??SAMPLES; temp_avg /= NUM??SAMPLES; //Serial.print("ADC 值 = "); //Serial.println(ADC_VALUE); // 將 adc 值轉換為電壓以獲得實際電流和電壓。 float solar_curr = (curr_avg * 3.3) / (4095); 浮動太陽能伏特=(伏特平均* 3.3)/(4095); //通過使用分壓器,我們降低了實際電壓。 //因此,我們將 6 乘以平均電壓,得到太陽能電池板的實際電壓。 太陽能電壓 *= 6;
?
太陽能電壓通過乘以 6 得到,因為我們創建了將輸入電壓除以 6 倍的分壓器。
使用對數形式從熱敏電阻產生溫度。
?
// 將該值轉換為電阻 temp_avg = 4095 / temp_avg - 1; temp_avg = SERIESRESISTOR / temp_avg; //Serial.print("熱敏電阻"); //Serial.println(temp_avg); 浮動斯坦哈特; steinhart = temp_avg / THERMISTORNOMINAL;// (R/Ro) steinhart = log(steinhart); // ln(R/Ro) steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro) steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To) steinhart = 1.0 / steinhart; // 反轉 steinhart -= 273.15; // 將絕對溫度轉換為 C
?
每 15 秒讀取一次數據。
?
延遲(1000); 計數++; Serial.print("."); 如果(計數 >= 15){ 計數 = 0; Serial.println("============================================== ============================="); Serial.print("太陽能電壓 = "); Serial.println(solar_volt); Serial.print("太陽能電流 = "); Serial.println(solar_curr); 浮動太陽能瓦特=太陽能伏特*太陽能電流; Serial.print("太陽能瓦特 = "); Serial.println(solar_watt); Serial.print("太陽溫度 = "); Serial.println(steinhart); Serial.println("============================================== =============================");
?
使用函數Thing.Speak.setField() 傳輸各個字段的數據;連接 WiFi 時。
?
if (WiFi.status() == WL_CONNECTED) { ThingSpeak.setField(1, solar_volt); ThingSpeak.setField(2, solar_curr); ThingSpeak.setField(3, solar_watt); ThingSpeak.setField(4, steinhart); // 寫入 ThingSpeak 通道 int x = ThingSpeak.writeFields(channelID, writeFeedAPIKey); if (x == 200) { Serial.println("頻道更新成功。"); } else { Serial.println("更新頻道有問題。HTTP錯誤碼" + String(x)); } } 其他 { Serial.println("\r\n######################################### ###################"); Serial.println("更新數據到thingSpeak服務器失敗。"); Serial.println("WiFi 未連接..."); Serial.println("############################################ ###############\r\n"); } Serial.print("數據讀取。"); } }
?
在以下代碼片段中創建的 Wi-Fi 任務 -
?
void wifi_task( void * parameter ) { while (1) { if (WiFi.status() != WL_CONNECTED) { Serial.print("正在嘗試連接到 SSID:"); Serial.println(WLAN_SSID); 而(WiFi.status()!= WL_CONNECTED){ WiFi.begin(WLAN_SSID,WLAN_PASS);// 連接到 WPA/WPA2 網絡。如果使用開放或 WEP 網絡,請更改此行 Serial.print("."); 延遲(5000); } Serial.println("\n已連接。"); 序列號.println(); Serial.println("WiFi 連接"); Serial.println("IP地址:"); Serial.println(WiFi.localIP()); } vTaskDelay(1000/portTICK_PERIOD_MS); } vTaskDelete(NULL); }
?
測試和監控數據
將太陽能電池板與電路連接并放置在陽光下進行測試,如下圖所示。
下面的視頻演示了完整的工作。我們的電路能夠從面板讀取輸出電壓、電流和功率,并在 thingspeak 頻道上實時更新,如下所示。
如我們所見,上圖中顯示了 15 分鐘的數據。由于這是一個戶外操作項目,因此需要使用適當的 PCB 以及一個封閉的盒子。外殼的制造方式需要使電路在雨中保持防水。
#include
#include "ThingSpeak.h"?
#include
#include
#include
// 為上行鏈路定義 WiFi SSID 和 PWD。
#define WLAN_SSID "xxxx"?
#define WLAN_PASS "xxxxxxxxxx"?
#define NUMSAMPLES 5?
int curr_samples[NUMSAMPLES];?
int volt_samples[NUMSAMPLES];?
int temp_samples[NUMSAMPLES];?
WiFiClient客戶端;
// 25 攝氏度時的電阻
#define THERMISTORNOMINAL 10000? ? ? ? ??
// 溫度。
#define SERIESRESISTOR 10000? ? ?
// 為電流和電壓定義模擬
const int curr_an_pin = 35;?
常量 int volt_an_pin = 34;?
常量 int ntc_temp_an_pin = 33;?
整數計數 = 0;
// thingSpeak Details?
#define thingSpeakAddress "xxxxxxxx"? ??
#define channelID xxxxx? ? ? ? ? ? ? ? ? ? ??
#define writeFeedAPIKey "xxxxxxx"? ? ? ? ? ? ? ? ??
#define readFeedAPIKey "xxxxxxx"? ? ? ? ? ? ? ? ? ?
#define readFieldAPIKey "xxxxxxxx"? ? ? ? ? ? ? ? ?
#define readStatusAPIKey "xxxxxxx"? ? ? ? ? ? ? ? ?
void setup() {?
? // 把你的設置代碼放在這里, 運行一次:
? // 將串口設置為 115200?
? Serial.begin(115200);
? WiFi.mode(WIFI_STA);?
? ThingSpeak.begin(客戶端); // 初始化 ThingSpeak?
? // todo: 創建一個任務來讀取引腳以獲取電流和電壓并計算太陽能電池板的瓦特和溫度
? xTaskCreate(?
? ? ? ? ? ? ? ? wifi_task, /* 任務函數。*/?
? ? ? ? ? ? ? ? "wifi_task", /* 名稱為的字符串task. */?
? ? ? ? ? ? ? ? 1024 * 2, /* 以字節為單位的堆棧大小。*/?
? ? ? ? ? ? ? ? NULL, /* 作為任務輸入傳遞的參數 */?
? ? ? ? ? ? ? ? 5, /* 任務的優先級。*/?
? ? ? ? ? ? ? ? NULL); /* 任務句柄。*/?
? Serial.print("數據讀取。");?
}
無效循環() {
? // 把你的主要代碼放在這里,重復運行:
? int i=0;?
? 浮動太陽能curr_adc_val = 0;
? 浮動太陽能伏特adc_val = 0;
? for (i = 0; i < NUMSAMPLES; i++) {?
? ? ? ? ? ? ? ? curr_samples[i] = analogRead(curr_an_pin);?
? ? ? ? ? ? ? ? volt_samples[i] = 模擬讀取(volt_an_pin);
? ? ? ? ? ? ? ? temp_samples[i] = 模擬讀取(ntc_temp_an_pin);
? ? ? ? ? ? ? ? 延遲(10);
? }?
? // 平均所有樣本
? float curr_avg = 0;?
? 浮動 volt_avg = 0;?
? 浮動 temp_avg = 0;?
? for (i = 0; i < NUMSAMPLES; i++) {?
? ? ? ? ? ? ? ? curr_avg += curr_samples[i];?
? ? ? ? ? ? ? ? volt_avg += volt_samples[i];?
? ? ? ? ? ? ? ? temp_avg += temp_samples[i];
? }?
? curr_avg /= NUM??SAMPLES;?
? volt_avg /= NUM??SAMPLES;?
? temp_avg /= NUM??SAMPLES;?
? //Serial.print("ADC 值 = ");?
? //Serial.println(ADC_VALUE);?
? // 將 adc 值轉換為電壓以獲得實際電流和電壓。
? float solar_curr = (curr_avg * 3.3) / (4095);?
? 浮動太陽能伏特=(伏特平均* 3.3)/(4095);
? //通過使用分壓器,我們降低了實際電壓。
? //因此,我們將 6 乘以平均電壓,得到太陽能電池板的實際電壓。
? 太陽能電壓 *= 6;
? // 將該值轉換為電阻
? temp_avg = 4095 / temp_avg - 1;?
? temp_avg = SERIESRESISTOR / temp_avg;
? //Serial.print("熱敏電阻");
? //Serial.println(temp_avg);?
? 浮動斯坦哈特;
? steinhart = temp_avg / THERMISTORNOMINAL;// (R/Ro)?
? steinhart = log(steinhart); // ln(R/Ro)?
? steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)?
? steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)?
? steinhart = 1.0 / steinhart; // 反轉
? steinhart -= 273.15; // 將絕對溫度轉換為 C
? 延遲(1000);
? 計數++;
? Serial.print(".");?
? 如果(計數 >= 15){
? ? ? ? ? ? ? ? 計數 = 0;?
? ? ? ? ? ? ? ? Serial.println("============================================== =============================");
? ? ? ? ? ? ? ? Serial.print("太陽能電壓 = ");?
? ? ? ? ? ? ? ? Serial.println(solar_volt);?
? ? ? ? ? ? ? ? Serial.print("太陽能電流 = ");?
? ? ? ? ? ? ? ? Serial.println(solar_curr);?
? ? ? ? ? ? ? ? 浮動太陽能瓦特=太陽能伏特*太陽能電流;
? ? ? ? ? ? ? ? Serial.print("太陽能瓦特 = ");?
? ? ? ? ? ? ? ? Serial.println(solar_watt);?
? ? ? ? ? ? ? ? Serial.print("太陽溫度 = ");?
? ? ? ? ? ? ? ? Serial.println(steinhart);?
? ? ? ? ? ? ? ? Serial.println("============================================== =============================");?
? ? ? ? ? ? ? ? if (WiFi.status() == WL_CONNECTED) {?
? ? ? ? ? ? ? ? ThingSpeak.setField(1, solar_volt);?
? ? ? ? ? ? ? ? 物語。
? ? ? ? ? ? ? ? ThingSpeak.setField(3, solar_watt);?
? ? ? ? ? ? ? ? ThingSpeak.setField(4, steinhart);?
? ? ? ? ? ? ? ? // 寫入 ThingSpeak 通道
? ? ? ? ? ? ? ? int x = ThingSpeak.writeFields(channelID, writeFeedAPIKey);?
? ? ? ? ? ? ? ? if (x == 200) {?
? ? ? ? ? ? ? ? Serial.println("頻道更新成功。");?
? ? ? ? ? ? ? ? }?
? ? ? ? ? ? ? ? else {?
? ? ? ? ? ? ? ? Serial.println("更新頻道有問題。HTTP錯誤碼" + String(x));?
? ? ? ? ? ? ? ? }?
? ? ? ? ? ? ? ? } else {?
? ? ? ? ? ? ? ? Serial.println("\r\n#################################### #######################");?
? ? ? ? ? ? ? ? Serial.println("更新數據到thingSpeak服務器失敗。");
? ? ? ? ? ? ? ? Serial.println("WiFi 未連接...");?
? ? ? ? ? ? ? ? Serial.println("############################################ ###############\r\n");?
? ? ? ? ? ? ? ? }?
? Serial.print("數據讀取。");?
? }?
}?
void wifi_task( void * parameter ) {?
? while (1) {?
? ? ? ? ? ? ? ? if (WiFi.status() != WL_CONNECTED) {?
? ? ? ? ? ? ? ? Serial.print("正在嘗試連接到 SSID:");?
? ? ? ? ? ? ? ? Serial.println(WLAN_SSID);?
? ? ? ? ? ? ? ? 而(WiFi.status()!= WL_CONNECTED){?
? ? ? ? ? ? ? ? WiFi.begin(WLAN_SSID,WLAN_PASS);// 連接到 WPA/WPA2 網絡。如果使用開放或 WEP 網絡,請更改此行
? ? ? ? ? ? ? ? Serial.print(".");
? ? ? ? ? ? ? ? }?
? ? ? ? ? ? ? ? Serial.println("\n已連接。");?
? ? ? ? ? ? ? ? 序列號.println();?
? ? ? ? ? ? ? ? Serial.println("WiFi 連接");?
? ? ? ? ? ? ? ? Serial.println("IP地址:");?
? ? ? ? ? ? ? ? Serial.println(WiFi.localIP());?
? ? ? ? ? ? ? ? }?
? ? ? ? ? ? ? ? vTaskDelay(1000/portTICK_PERIOD_MS);?
? }?
? vTaskDelete(NULL);?
}
評論
查看更多