这是一个通过 433MHz 无线电信号控制中央供暖系统的单元。通过 Blynk 应用程序控制加热。
带有 Blynk 应用程序的 433MHz 无线电控制加热控制器
这是一个使用 433MHz 无线电模块控制锅炉的项目。我使用了简单且便宜的 OOK 无线电模块。它们有一个用于数据的引脚,将此引脚设为高电平或低电平将使无线电模块能够发送高电平或低电平信号。我还使用 ESP8266 (Wemos D1 Mini) 连接到互联网和 Blynk 应用程序,这样我就可以使用手机或平板电脑控制供暖,即使在外面也是如此。我连接了一个温度传感器,可以根据我选择的目标温度发送开/关信号。
将传感器上的 I2C 引脚连接到 Arduino 上的 I2C 引脚。这个特殊的传感器在 3.3v 下运行。幸运的是,我使用的 Wemos D1 Mini 也有 3.3v 逻辑。对于 433Mhz 无线电模块,连接 VCC 和 GND 引脚,将数据引脚连接到 D3,将 164 毫米线连接到天线引脚。
第 1 步
第一项工作是破译来自我的无线恒温器的开/关信号。我猜恒温器使用的是 433Mhz,因为大多数人都这样做,所以我使用 SDR 加密狗在该频率的 AM 频段上收听,然后在恒温器上上下调节温度设置,直到我可以看到正在传输的信号。
如果您没有 SDR 加密狗,那么您可以使用便宜的 433MHz 接收器模块和声卡。
所以,一旦我有一个干净的信号,我就录制了音频,然后将文件导入 Audacity。通过放大脉冲,我可以看到开/关信号被发送了 3 次。编码显然是曼彻斯特编码,脉冲长 500 微秒,3 个信号之间有 1000 毫秒的间隙。
然后我尝试了 Arduino 和 433MHz 无线电模块(经过大量试验和错误),直到我能够打开和关闭锅炉。我在下面有一张我复制的信号的图像,它实际上比原来的要干净得多。
第 2 步
现在我已经控制了锅炉,我决定使用 Blynk 创建一个电话应用程序来远程控制加热。
这是我在 Blynk 应用程序中创建的用于控制加热的界面图像:
我有一个大的启用/禁用按钮,用于打开和关闭系统。一旦系统启用,加热将根据我设置的目标温度和当前温度打开和关闭。如果当前温度低于目标温度,则打开锅炉,房屋将开始加热。一旦房屋温度超过目标温度,它将关闭。
有显示当前温度和湿度的显示器,显示历史数据的图表,用于调整目标温度的滑块,最后是目标温度本身。
此 UI 由 Blynk 应用程序中的 7 个小部件创建。顶部小部件是一个大按钮。这被设置为一个开关并在虚拟引脚 V0 上。
接下来是一个大的标记值小部件,用于显示系统状态。这设置为虚拟引脚 V1。文本居中对齐。
接下来是一个小标签值框,用于显示温度。这设置为虚拟引脚 V4。文本居中对齐并格式化为 /pin.#/°C 以显示温度,小数点后有 1 位,小数点后显示°C。
第 3 步
接下来是一个小标签值框,用于显示湿度。这设置为虚拟引脚 V5。文本居中对齐,格式设置为 /pin.#/% 以显示小数点后一位数字,然后显示 % 符号。
接下来是历史图表。这设置为显示虚拟引脚 V4 和 V5(温度和湿度)。该图显示了过去 1 小时、6 小时、1 天、1 周、1 个月和 3 个月的数据。
接下来是一个小滑块控件来调节温度。我将最低和最高温度设置为 10°C 和 30°C。
第 4 步
最后,我们有另一个小标签值框来显示选定的温度。再次,居中对齐并格式化为 /pin.#/°C
软件
这两个定义只是为了使代码中的内容更清晰。
#define ON 1
#define OFF 0
将库导入之后,我用带有HTU21D I2C 温度和湿度传感器的 ESP8266(具体来说是 Wemos D1 Mini)板。我还将在 EEPROM 中保存数据,因此我也导入了该库。
#include <Wire.h>
#include "SparkFunHTU21D.h"
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <EEPROM.h>
无线电发射器的数据引脚是引脚 D3。
#define dataPin D3
然后我设置了一个 HTU21D 对象来与传感器通信。
HTU21D myHumidity;
输入您的验证码(来自您的 Blynk 帐户)以及您的 WiFi 的 SSID 和密码。
char auth[] = "xxx";
char ssid[] = "xxx";
char pass[] = "xxx";
必须每 5 分钟重新传输一次开/关信号以停止锅炉关闭(故障安全模式),因此我将存储上次传输代码的时间(每 5 分钟)的毫秒值,最后一次我们更新了 UI(每秒)和我们上次检查当前温度是否达到目标温度的时间(每 60 秒)。
unsigned long lastTransmit;
unsigned long lastUpdate;
unsigned long lastTempCheck;
接下来我们声明并初始化一些变量。
temp 是当前温度,enabledState 根据按钮的状态或系统的最后已知状态设置为 1 或 0,requiredTemp 将存储目标温度。
float temp;
int enabledState;
int requiredTemp;
现在进入 setup() 函数。
void setup()
{
EEPROM.begin(3);
myHumidity.begin();
//EEPROM.write(2, 0);
//EEPROM.commit();
pinMode(dataPin, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Serial.begin(115200);
Blynk.begin(auth, ssid, pass);
Serial.println("Initialising....");
lastTransmit = millis();
lastUpdate = millis();
lastTempCheck = millis();
enabledState = EEPROM.read(1);
Serial.println("EEPROM: System is " + String(enabledState ? "ENABLED" : "DISABLED"));
requiredTemp = EEPROM.read(2);
Serial.println("EEPROM: Target Temperature is " + String(requiredTemp) + "°C");
}
我们会将系统的最后已知状态(启用或禁用)以及目标温度存储在 EEPROM 中,以防系统重置。我们留出 3 个字节来使用。
EEPROM.begin(3);
第 5 步
然后我们启动 HTU21D 传感器。
myHumidity.begin();
如果您是第一次运行代码,系统状态将不会存储在 EEPROM 中,因此请删除以下 2 行中的注释栏,将存储状态设置为 0(禁用)。不要忘记重新注释这两行,然后再次重新上传您的代码。
//EEPROM.write(2, 0);
//EEPROM.commit();
无线电发射器数据线和内部 LED 的引脚都设置为输出。LED 关闭。
pinMode(dataPin, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
串行通信用于调试目的,因此我们初始化串行线路,然后使用 Blynk 身份验证代码、WiFi SSID 和密码初始化 Blynk 库。
Serial.begin(115200);
Blynk.begin(auth, ssid, pass);
Serial.println("Initialising....");
然后存储当前的 millis() 值以供以后使用。
lastTransmit = millis();
lastUpdate = millis();
lastTempCheck = millis();
从 EEPROM 以及目标温度中检索 enabledState。这些被打印到串行监视器窗口。
enabledState = EEPROM.read(1);
Serial.println("EEPROM: System is " + String(enabledState ? "ENABLED" : "DISABLED"));
requiredTemp = EEPROM.read(2);
Serial.println("EEPROM: Target Temperature is " + String(requiredTemp) + "°C");
一旦 Blynk 库连接到 Blynk 云服务器,就需要执行一些管理命令。首先将滑块和目标温度框设置为存储的设置。
BLYNK_CONNECTED() {
Serial.println("CONNECTED");
Blynk.virtualWrite(V2, requiredTemp);
Blynk.virtualWrite(V3, requiredTemp);
然后将该按钮设置为启用或禁用。它的颜色被适当地设置为红色或绿色。
Blynk.virtualWrite(V0, enabledState);
Blynk.setProperty(V0, "color", (enabledState ? "#00FF00" : "#FF0000"));
然后状态框变为“就绪”和绿色。
Blynk.virtualWrite(V1, "Ready");
Blynk.setProperty(V1, "color", "#00FF00");
然后使用该getTemps() 功能从传感器中检索当前的温度和湿度读数。
getTemps();
启用/禁用按钮在 Blynk 应用程序中设置为 Virtual Pin V0,因此无论何时按下该BLYNK_WRITE(V0) 功能都会激活该功能。该按钮将在启用和禁用之间切换系统状态,然后将其存储在 EEPROM 中,以防我们断电或重置。
BLYNK_WRITE(V0)
{
enabledState = !enabledState;
EEPROM.write(1, enabledState);
EEPROM.commit();
我们将系统状态打印到串行监视器窗口,然后更改按钮状态和颜色。
Serial.println("System is " + String(enabledState ? "ENABLED" : "DISABLED"));
Blynk.virtualWrite(V0, enabledState);
Blynk.setProperty(V0, "color", enabledState ? "#00FF00" : "#FF0000");
每次在 Virtual Pin V2 上移动滑块时,BLYNK_WRITE(V2) 都会触发该功能。我们首先从滑块中以整数形式检索温度并将其存储在requiredTemp 变量中。
BLYNK_WRITE(V2)
{
requiredTemp = param.asInt();
然后 Virtual Pin V3 上的目标温度框会使用滑块值更新,并将该值存储在 EEPROM 中。
Blynk.virtualWrite(V3, requiredTemp); Blynk.run();
EEPROM.write(2, requiredTemp);
EEPROM.commit();
Serial.println("Target Temperature is " + String(requiredTemp));
}
第 6 步
下一个功能是我为向我的锅炉发送 ON 或 OFF 信号的适当脉冲而设计的。您可以使用完全相同的功能来控制您的功能,但您需要调整二进制代码和时间以适合您的系统。此代码仅适用于我的锅炉,但您可以使用 433MHz 无线电接收器或 SDR 加密狗(推荐)轻松找出您的代码。
void heatingControl(boolean onOff)
{
Serial.print(millis());
Serial.println(" Transmitting " + String(onOff ? "ON signal" : "OFF signal"));
int on[] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1};
int off[] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1};
for (int repeat = 1; repeat <= 3; repeat++)
{
for (int i = 0; i < sizeof( onOff ? on : off ) / sizeof( int ); i++) {
switch (onOff ? on[i] : off[i])
{
case 0:
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
digitalWrite(dataPin, HIGH);
delayMicroseconds(500);
break;
case 1:
digitalWrite(dataPin, HIGH);
delayMicroseconds(500);
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
break;
case 2:
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
break;
}
}
int waitTime = millis();
if ((millis() - waitTime) < 1000) {
Blynk.run();
}
}
Blynk.virtualWrite(V1, String(onOff ? "Heating ON" : "Heating OFF"));
Blynk.setProperty(V1, "color", String(onOff ? "#00FF00" : "#FF0000"));
lastTransmit = millis();
}
该函数希望您在调用时将布尔值传递给它。真值将打开加热,假值将其关闭。
void heatingControl(boolean onOff)
{
Serial.print(millis());
Serial.println(" Transmitting " + String(onOff ? "ON signal" : "OFF signal"));
ON 和 OFF 信号的二进制代码存储在整数数组中。1 位的脉冲由 500 微秒的高电平和 500 微秒的低电平组成。0 位是一个 500 微秒的低电平,后跟一个 500 微秒的高电平。信号为曼彻斯特 IEE 802.3,如下所示:
奇怪的是,尽管 ON/OFF 信号中的每一位都被正确编码,但由于某种原因,第 9 位在其后有一个额外的 500 微秒的低电平。因此,我不得不将它作为 2 存储在数组中。我不知道为什么它是这样编码的,但是没有它信号就无法工作。
int on[] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1};
int off[] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1};
信号重复 3 次,中间有 1000 毫秒的延迟。它们被发送 3 次,以确保接收器在存在干扰时有很好的机会获得 ON/OFF 信号。然后我们简单地通过适当的阵列并为所选信号发送正确定时的脉冲。
for (int repeat = 1; repeat <= 3; repeat++)
{
for (int i = 0; i < sizeof( onOff ? on : off ) / sizeof( int ); i++) {
switch (onOff ? on[i] : off[i])
{
case 0:
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
digitalWrite(dataPin, HIGH);
delayMicroseconds(500);
break;
case 1:
digitalWrite(dataPin, HIGH);
delayMicroseconds(500);
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
break;
case 2:
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
break;
}
}
Blynk.run() 必须连续运行该命令才能使 Blynk 库正常工作。如果几百毫秒没有握手,系统将断开连接并重新连接。Blynk.run() 因此,我在可能存在延迟的代码中随意粘贴了命令,并且 3 个脉冲后的 1000 毫秒延迟包含对Blynk.run() 函数的连续重复调用以防止这种情况发生。
int waitTime = millis();
if ((millis() - waitTime) < 1000) {
Blynk.run();
}
发送 ON 或 OFF 信号后,状态框(虚拟引脚 V1)变为“加热开启”或“加热关闭”,颜色变为红色或绿色。
Blynk.virtualWrite(V1, String(onOff ? "Heating ON" : "Heating OFF"));
Blynk.setProperty(V1, "color", String(onOff ? "#00FF00" : "#FF0000"));
最后,我们要确保我们不会过于频繁地传输 ON/OFF 信号,因此将存储 millis() 的当前值,这样我们就可以确保它们仅每 60 秒发送一次并且不少于(以防止损坏锅炉)。
lastTransmit = millis();
第 7 步
接下来是getTemps() 从 HTU21D 传感器获取温度和湿度读数并存储它们的短函数。然后将这些值写入 Blynk 应用程序上的虚拟引脚 V4 和 V5 的标签框。因此函数将每 1000 毫秒调用一次。
getTemps()
{
float humd = myHumidity.readHumidity();
temp = myHumidity.readTemperature();
Blynk.virtualWrite(V4, temp);
Blynk.virtualWrite(V5, humd);
}
我们在主循环之前的最后一个函数是关键的。此函数每 60 秒调用一次。它检查当前温度以查看它是否高于或低于所需温度,如果是,则系统启用,然后将 ON 或 OFF 信号发送到锅炉。这将确保房屋内保持所需的温度。
void checkTemp()
{
if ((temp > requiredTemp) && (enabledState == ON))
{
heatingControl(OFF);
}
if ((temp < requiredTemp) && (enabledState == ON))
{
heatingControl(ON);
}
lastTempCheck = millis();
}
我们现在已经达到了主循环功能。重复调用该Blynk.run() 命令以启用 Blynk 应用程序。
void loop()
{
Blynk.run();
然后我们检查自上次发送信号以来是否已经过去了 60 秒(60000 毫秒)。如果有,则重新发送 ON 或 OFF 信号。这是为了确保锅炉每 5 分钟收到一次信号,以防止其进入故障安全模式。仅当系统启用时才会发送 ON 信号。
if ((millis() - lastTransmit) > 60000)
{
if ((temp < requiredTemp) && (enabledState == ON)) heatingControl(ON);
else heatingControl(OFF);
}
每 1 秒我们获取传感器读数,更新显示,闪烁内置 LED(以显示系统正在运行)并存储 millis() 的当前值。
if ((millis() - lastUpdate) > 1000)
{
getTemps();
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
lastUpdate = millis();
}
最后,我们将每 60 秒将当前温度与目标温度进行比较,并在必要时打开或关闭加热。
if ((millis() - lastTempCheck) > 60000)
{
checkTemp();
}
结论
拥有这样一个真正的物联网项目之后,你就可以使用手机或平板电脑控制我家的供暖。我可以远程检查温度并从屋外打开或关闭暖气。
433MHz Heating Controller using BlynkArduino:
#define ON 1
#define OFF 0
#include <Wire.h>
#include "SparkFunHTU21D.h"
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <EEPROM.h>
#define dataPin D3
#define BLYNK_PRINT Serial
HTU21D myHumidity;
char auth[] = "xxx";
char ssid[] = "xxx";
char pass[] = "xxx";
unsigned long lastTransmit;
unsigned long lastUpdate;
unsigned long lastTempCheck;
float temp;
int enabledState;
int requiredTemp;
void setup()
{
EEPROM.begin(3);
myHumidity.begin();
//EEPROM.write(2, 0);
//EEPROM.commit();
pinMode(dataPin, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Serial.begin(115200);
Blynk.begin(auth, ssid, pass);
Serial.println("Initialising....");
lastTransmit = millis();
lastUpdate = millis();
lastTempCheck = millis();
enabledState = EEPROM.read(1);
Serial.println("EEPROM: System is " + String(enabledState ? "ENABLED" : "DISABLED"));
requiredTemp = EEPROM.read(2);
Serial.println("EEPROM: Target Temperature is " + String(requiredTemp) + "°C");
}
BLYNK_CONNECTED() {
Serial.println("CONNECTED"); Blynk.run();
Blynk.virtualWrite(V2, requiredTemp); Blynk.run();
Blynk.virtualWrite(V3, requiredTemp); Blynk.run();
Blynk.virtualWrite(V0, enabledState);
Blynk.setProperty(V0, "color", (enabledState ? "#00FF00" : "#FF0000")); Blynk.run();
Blynk.virtualWrite(V1, "Ready"); Blynk.run();
Blynk.setProperty(V1, "color", "#00FF00"); Blynk.run();
getTemps();
}
BLYNK_WRITE(V0)
{
enabledState = !enabledState;
EEPROM.write(1, enabledState);
EEPROM.commit();
Serial.println("System is " + String(enabledState ? "ENABLED" : "DISABLED"));
Blynk.virtualWrite(V0, enabledState);
Blynk.setProperty(V0, "color", enabledState ? "#00FF00" : "#FF0000"); Blynk.run();
}
BLYNK_WRITE(V2)
{
requiredTemp = param.asInt(); Blynk.run();
Blynk.virtualWrite(V3, requiredTemp); Blynk.run();
EEPROM.write(2, requiredTemp);
EEPROM.commit();
Serial.println("Target Temperature is " + String(requiredTemp));
}
void heatingControl(boolean onOff)
{
Serial.print(millis());
Serial.println(" Transmitting " + String(onOff ? "ON signal" : "OFF signal"));
int on[] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1};
int off[] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1};
for (int repeat = 1; repeat <= 3; repeat++)
{
for (int i = 0; i < sizeof( onOff ? on : off ) / sizeof( int ); i++) {
switch (onOff ? on[i] : off[i])
{
case 0:
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
digitalWrite(dataPin, HIGH);
delayMicroseconds(500);
break;
case 1:
digitalWrite(dataPin, HIGH);
delayMicroseconds(500);
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
break;
case 2:
digitalWrite(dataPin, LOW);
delayMicroseconds(500);
break;
}
}
int waitTime = millis();
if ((millis() - waitTime) < 1000) {
Blynk.run();
}
}
Blynk.virtualWrite(V1, String(onOff ? "Heating ON" : "Heating OFF")); Blynk.run();
Blynk.setProperty(V1, "color", String(onOff ? "#00FF00" : "#FF0000")); Blynk.run();
lastTransmit = millis();
}
void getTemps()
{
float humd = myHumidity.readHumidity(); Blynk.run();
temp = myHumidity.readTemperature(); Blynk.run();
Blynk.virtualWrite(V4, temp); Blynk.run();
Blynk.virtualWrite(V5, humd); Blynk.run();
}
void checkTemp()
{
if ((temp > requiredTemp) && (enabledState == ON)) heatingControl(OFF);
if ((temp < requiredTemp) && (enabledState == ON) heatingControl(ON);
lastTempCheck = millis();
}
void loop()
{
Blynk.run();
if ((millis() - lastTransmit) > 60000)
{
if ((temp < requiredTemp) && (enabledState == ON)) heatingControl(ON);
else heatingControl(OFF);
}
if ((millis() - lastUpdate) > 1000)
{
getTemps();
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
lastUpdate = millis();
}
if ((millis() - lastTempCheck) > 60000) checkTemp();
}
如果您对此项目有任何想法、意见或问题,请在下方留言。
以上内容翻译自网络,原作者:Mike McRoberts,如涉及侵权,可联系删除。