在可再生能源领域,太阳能处于最前沿,因为利用太阳能发电是可再生能源最简单、商业上可行的方式。说到太阳能电池板,需要监控太阳能电池板输出的输出功率,以便从电池板获得最佳功率输出。这就是为什么需要实时监控系统的原因。在大型太阳能发电厂中,它还可用于监控每个面板的功率输出,这有助于识别灰尘堆积。它还可以防止运行期间出现任何故障情况。在这个项目中,我们将通过结合基于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 /= NUMSAMPLES; volt_avg /= NUMSAMPLES; temp_avg /= NUMSAMPLES; //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 /= NUMSAMPLES;
volt_avg /= NUMSAMPLES;
temp_avg /= NUMSAMPLES;
//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);
}
评论
查看更多