×

车库MC(监视器和控制器)开源分享

消耗积分:0 | 格式:zip | 大小:1.25 MB | 2022-11-16

李红

分享资料个

描述

我建造 Garage MC 是因为我想了解更多关于电子学的知识。作为奖励,我想下次我出去让车库门开着时,它会节省我的屁股。

*我在 2021 年 1 月 6 日添加了一个更新,其中包含 OTA 更新和 MQTT 到 Home Assistant(详情如下)*

我的目标

  • 能够监控车库门的状态(关闭/半开/打开)。
  • 能够用我的安卓手机(我的妻子用她的苹果手机)控制(打开/关闭)车库门。
  • 学习新东西。
  • 不花很多钱。

第 1 步:项目规划

在这个项目之前,我从亚马逊购买了一个兼容 Arduino 的入门套件。所以我是电子产品的初学者,只是学习绳索。但我确实受益于 15 年前做一些计算机编程,所以我能够在 Arduino IDE 上快速学习 C++ 编码。

车库开门器:首先,我必须确定我是否可以轻松地将微控制器与我的 Liftmaster 车库开门器连接起来。幸运的是,Liftmaster 使用两条线在常开(认为是打开的开关)状态下连接回壁挂式遥控器。当按下壁挂式遥控器上的按钮时,它会通过“短路”电线并触发门打开或关闭来短暂完成电路。哦,别忘了检查这些电线上的电压……Liftmaster 手册上说这些电线大约是 20 伏(不是 120 伏),但您的设备可能会有所不同。

微控制器:接下来,我必须决定如何将 Garage MC 连接到互联网。我最初考虑使用 ENC28J60 以太网板,虽然我更喜欢硬线连接,但将线路连接到我的路由器会比它的价值更麻烦。我研究了 wifi 板并决定使用 NodeMCU ESP8266,因为它们成本低且具有多个 GPIO 引脚。

手机应用程序:有多种方法可以通过手机连接到 NodeMCU,包括通过 SMS 消息、将 NodeMCU 用作 Web 服务器等。对我来说,使用 Blynk 应用程序是启动和运行最简单、最快捷的方式。

车库门监控:我决定使用两个霍尔效应传感器来监控车库门的位置。可能已经可以从 Liftmaster 单元获取门的位置甚至行进方向。但是,如果有人拉动紧急释放绳并手动打开门,传感器仍会通知我门已打开。

第 2 步:开发

NodeMCU:我首先将 NodeMCU 插入面包板并通过 USB 连接到我的计算机为其供电。下面的链接有一个很好的教程,用于在 Arduino IDE 上初始设置 NodeMCU:

Blynk :接下来,我将 NodeMCU 连接到互联网和手机上的 Blynk 应用程序。

面包板:是时候添加组件了。虽然我是在装上所有组件后制作了 Fritzing 电路图,但我在这里添加了它,以便更容易参考...

pYYBAGNy7hmAYpOfAAOkc328isA491.jpg
Garage MC - Fritzing 图
 

项目代码:我附上了我的项目的完整代码。我试图评论它,但如果有什么你不明白,或者我可以改进,请告诉我。我在教程中添加了一些小代码片段来强调一些事情,例如:

  • 我使我的代码非阻塞。这意味着它没有“延迟()”语句(好吧,设置中有一个等待串行端口)。相反,我依赖于基于“SimpleTimer”库的 Blynk 计时器。
poYBAGNy7h-AVugYAAqblz67eRE834.jpg
我的工作区
 

首先是继电器(控制器):我使用的继电器是一个单通道继电器,触发“LOW”以激活。继电器需要 5 伏电压,所以我使用 NodeMCU 上的 VIN 引脚为其供电。继电器有两个用途。

  • 首先是激活车库门开启器。这是通过将一根电线从车库门开启器连接到继电器上的公共 (COM) 引脚来完成的。第二根线连接到继电器上的 N/O(常开)引脚。当继电器被触发(300 毫秒)时,电路闭合。
  • 第二个目的是保护 NodeMCU 免受车库门开启器的 20 伏电线的影响,因为 NodeMCU 上的数字引脚只能承受 3.3 伏电压。

我使用了两个函数来触发我的继电器,使其无阻塞。在我的 Blynk 应用程序中按下按钮时,它会调用一个函数来激活门。请注意,“ActivateDoor()”仅在按下按钮时(上升 = 1)很重要,而不是在释放按钮时,否则您将激活门两次。ActivateDoorRelay 函数将继电器引脚设置为“LOW”,然后设置一个 300 毫秒的计时器,而不是使用“delay()”。然后定时器触发第二个功能将引脚重置为“HIGH”以关闭继电器。我正在检查该州的原因

BLYNK_WRITE(V10) {
    if (param.asInt()) {  // only do it on button "rising=1"
        ActivateDoor();     // trigger the door
    }
}

void ActivateDoor() {
    activateState = garageState; // get garage state when the button was pressed
    long songLength = PlaySong(); // Play a song
    // Wait for the song to finish, then activate the relay
    myTimer.setTimeout(songLength, ActivateDoorRelay);
}

void ActivateDoorRelay() {
    // only trigger the relay if the state hasn't changed since the button press
    if (garageState == activateState) {
        digitalWrite(RELAY1_PIN, LOW); // trigger the relay to activate the door
        myTimer.setTimeout(300, ActivateDoorRelayReset);  // Wait 300ms
    }
}

void ActivateDoorRelayReset() {
    digitalWrite(RELAY1_PIN, HIGH);  // Deactivate the door relay
    Blynk.virtualWrite(V10, 0);  // Reset the Blynk app switch
}

霍尔效应传感器(监视器):霍尔效应传感器监控车库门的位置。传感器是非锁定的并且是数字的(它可以感应或不感应磁铁;模拟输出传感器将测量磁铁的强度)。我在车库门上使用了两个钕磁铁(一起使它们更坚固)来触发传感器。我有一些 CAT6 电缆,所以我将它从车库天花板上的 NodeMCU 连接到每个霍尔传感器。车库门可以处于三种状态之一(因为我不在乎门的移动方向):

  • 关闭:门关闭位置的霍尔效应传感器由磁铁激活。
  • 打开:门打开位置的霍尔效应传感器由磁铁激活。
  • 半开:霍尔效应传感器均未激活。因此,门可以在打开位置移动或停止。
pYYBAGNy7iOAPr5qAAOaMxMu5Gk722.jpg
霍尔效应传感器(距磁铁约 2 厘米)
 

我不喜欢轮询事件的发生。因此,霍尔传感器使用“中断”来指示状态变化(例如门关闭移动)。中断就是当霍尔传感器发生状态变化时,它会中断程序代码以立即运行一个短函数。

// Interrupt Declarations
// ESP boards need ICACHE_RAM_ATTR included
void ICACHE_RAM_ATTR InterruptDoorClosed();
void ICACHE_RAM_ATTR InterruptDoorOpen();

// this is only a snippet from the "setup" function
void setup() {
  // Interrupts set for the two hall sensors activated on a CHANGE of state
  // the Hall sensors require a pullup resistor, I used the internal pullup
  pinMode(HALL_CLOSED_PIN,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(HALL_CLOSED_PIN), InterruptDoorClosed, CHANGE);
  pinMode(HALL_OPEN_PIN,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(HALL_OPEN_PIN), InterruptDoorOpen, CHANGE);
}

// Interrupt for the Hall Sensor at the Door CLOSED point
// activated on CHANGE and Sets a timer to immediately CheckGarageState
void InterruptDoorClosed() {
    hallClosedState = digitalRead(HALL_CLOSED_PIN); // get the sensor state
    myTimer.setTimeout(10, GarageStateChanged); // run the function in 10ms
}
// Interrupt for the Hall Sensor at the Door OPEN point
// activated on CHANGE and Sets a timer to immediately CheckGarageState
void InterruptDoorOpen() {
    hallOpenState = digitalRead(HALL_OPEN_PIN); // get the sensor state
    myTimer.setTimeout(10, GarageStateChanged); // run the function in 10ms
}

被动压电蜂鸣器:当门被远程激活(使用 Blynk 应用程序)时,我想提醒车库里的任何人门即将移动。所以我加了一个蜂鸣器来播放一首短歌。我在电路中添加了一个 100 欧姆的电阻,尽管我不确定是否有必要。我还让这首歌不阻塞。尽管这需要我获得歌曲的长度,所以我在完成之前不会激活门。

LED :最后,我添加了两个 LED,一个绿色和一个红色。每个 LED 在其电路中都需要一个 220 欧姆的电阻器。绿色 LED 表示 Garage MC 已连接到 Blynk 服务器。红色 LED 表示 Garage MC 已失去连接。

我的编程流程:如果您通常不在代码中使用计时器,我的程序流程可能难以掌握。它基本上是这样工作的:

  • setup() 函数将计时器设置为:1。检查互联网connection.2。检查车库门的状态 (CheckGarageState),如果门打开时间过长,它会更新 Blynk 应用程序并向 Blynk 发送通知。
  • loop() 函数使计时器保持运行。
  • 当车库门的状态发生变化(关闭到打开)时,会触发中断。中断标记状态更改,然后设置即时计时器(10 毫秒)来检查车库门的状态 (CheckGarageState),从而更新 Blynk 应用程序。

第 3 组:是时候把它放在一起并连接起来了

我希望 Garage MC 看起来是半专业的,所以我决定将所有组件安装在 Perfboard 上(带有预钻孔的单个铜垫)。有了这个,我必须学习如何焊接。这不一定是困难的,但可能很难做好。

案例:一台 3D 打印机我儿子的圣诞清单上。希望圣诞老人能通过,因为我也想使用它。事实证明,我有一个旧的 Chamberlain myQ(现在是“Merlin”),我将它拆开、切割和钻孔,然后把它变成一个项目案例。我认为结果没问题,即使继电器伸出底部。

Perfboard :根据机箱的大小,我选择了适合机箱的 perfboard,并且几乎适合 NodeMCU。正如您在我的图表中看到的那样,每侧的顶部引脚不适合(D0 和 A0),但无论如何我都没有使用它们。我没有删除它们,它们只是没有连接到任何东西。

poYBAGNy7iqANR9jAAI9QIxLka0609.jpg
穿孔板布局
 

我在 Perfboard 上焊接了两个插头引脚(16 个引脚长,但 NodeMCU 每侧有 15 个引脚)。因此,NodeMCU 位于底部的 15 个引脚上。我主要在 Perfboard 底部焊接连接线(有几个例外)和顶部的组件。长话短说……

并将其安装在车库门开启器上方的车库中(黄色 CAT6 电缆通过门轨连接到霍尔传感器)..

电源:天花板上有一个用于 Liftmaster 开启器的电源插座。所以我决定让事情变得简单。我插入了旧的黑莓手机充电器并使用旧的 USB 电缆为 Garage MC 供电。

我会做些什么不同的事情?

我实际上对我的最终结果很满意。但是我会做一些改变:

  • 我将霍尔效应传感器的螺丝连接器彼此相对放置,认为我很聪明并且节省空间。这使得将 CAT6 电缆的电线完美弯曲到位变得困难。下次我肯定会采取直接的方法,并将连接器并排放置。
  • 完成 - 我正在考虑添加通过无线 (OTA) 更新我的代码的功能,这样我就不必拿出我的梯子来进行更改。

V1.1 更新

在 Covid 呆在家里的圣诞假期期间,我开始学习和设置 Home Assistant。因此,我决定让 Garage MC 报告回来并由 Home Assistant 控制。因此,我决定对我的代码进行以下更改:添加 OTA 更新,并添加 MQTT 以与 Home Assistant 通信。

OTA 更新

这是我自 10 月以来对 Garage MC 所做的第一次更新,因为坦率地说,它不需要更新。但是,爬梯子进行更新很痛苦,所以我实施了 OTA 更新。

poYBAGNy7jiAH2OSAAC6hFMSydc445.png
Blynk 应用程序的小更新以打开/关闭 OTA 更新
 

我在我的 Blynk 应用程序中添加了一个开关,以便打开和关闭 OTA 更新,因为我不希望它一直运行。接下来,我添加了#include 库。

我添加了一个 MD5 散列密码,一个超时(关闭更新过程以防我忘记),并让 LED 闪烁,这样我就知道 GarageMC 处于“更新模式”。全局变量如下:

// OTA variables
#define CLIENT_NAME   "GarageMC"  // For MQTT topics, MQTT Client ID, and ArduinoOTA
const char ota_pass[]           = SECRET_OTA_PWD;
const unsigned long OTA_TIMEOUT = 600000;  // Turn off OTA after 10 min. if no update
unsigned long otaTimeout        = 0;        // End time for OTA = millis()+OTA_TIMEOUT
bool otaOn                      = false;    // set to "true" to turn on OTA updates
const int OTA_BLINK_DELAY       = 300;      // blink the leds while OTA is ON
unsigned long otaBlinkTimer     = 0;        // next led blink time

在 setup() 中,我按如下方式初始化 OTA:

ArduinoOTA.setHostname(CLIENT_NAME);
ArduinoOTA.setPasswordHash(ota_pass);
ArduinoOTA.onStart([]() {
    DEBUG_PRINTLN("OTA Starting update");
    digitalWrite(WIFI_ON_PIN, true);
    digitalWrite(WIFI_OFF_PIN, true);
});
ArduinoOTA.onEnd([]() {
    DEBUG_PRINTLN("OTA Finished");
});

我的主循环已更新,因此如果打开 OTA 更新,其他一切都会停止(我发现如果 Blynk 仍在运行,更新会很慢或失败)。

if (otaOn) {
    ArduinoOTA.handle();
    if (millis() > otaTimeout) {  // timeout if no upload, then restart
        ESP.restart();
    }
    // blink the red and green leds while OTA is on
    if (millis() > otaBlinkTimer) {
        SetWifiLeds(!digitalRead(WIFI_ON_PIN));
        otaBlinkTimer = millis() + OTA_BLINK_DELAY;
    }
} else {
    if (Blynk.connected()) {Blynk.run();} // run Blynk if it's connected
    myTimer.run();                        // Blynk Events Timer (run either way)
    // MQTT - loop or reconnect if needed
    mqttLoop();
}

最后,当我在 Blynk 应用程序中拨动开关时,它会向虚拟引脚 V0 发送一条消息(off=1 和 on =2,但我希望它是一个布尔值):

BLYNK_WRITE(V0) {
    otaOn = param.asInt() - 1;
    DEBUG_PRINTLN((String)"V0: " + otaOn);
    if (otaOn) {                            // OTA turned ON
        otaTimeout = millis() + OTA_TIMEOUT;  // Timeout end time
        ArduinoOTA.begin();                   // Start the OTA service
    } else {                                // OTA turned OFF
        ESP.restart();              // Restart to turn stop ArduinoOTA service
    }
}

从 Arduino IDE 处理 OTA 更新所需的一切。我喜欢这样一个事实,除非我通过拨动 Blynk 中的开关来采取行动,否则它不会运行。

MQTT 到家庭助理

我不会进入家庭助理,因为我只是在学习它。但我建议在 YouTube 上搜索 JuanMTech。而且我只是几乎不了解 MQTT,所以在那里也不会有太大帮助。我使用了流行的#include 库。详细信息可以在我的完整代码中找到。但基本上,作为客户端,您连接到 Home Assistant 上的 MQTT 代理。您将更新“发布”到 MQTT 代理,并“订阅”您希望通过“回调”函数接收更新的主题。

最简单的方法是连接到 MQTT 代理:

// subscribe to the control topic which can "activate" the garage door (open/close)
const char topicGarageControl[] = CLIENT_NAME"/control";
// publish to the state topics, the state of the door
const char topicGarageState[]   = CLIENT_NAME"/state";

WiFiClient espClient;
PubSubClient client(espClient);    // client for connection to MQTT broker
client.setServer(mqtt_server, mqtt_port);    // server to connect to
client.setCallback(callback);    // the "callback" function for subscribed topics
client.connect(mqtt_client_name, mqtt_user, mqtt_pass);    // connect to MQTT broker

// after connecting, subscribe to topics you want
client.subscribe(topicGarageControl);

当车库门状态发生变化(open->ajar->closed)时,我将更改发布到 Home Assistant:

// topic = CLIENT_NAME/state
// payload = door state (open/ajar/closed)
client.publish(topic.c_str(), payload.c_str(), retain);

到目前为止,一切都很好。现在我只需要找出 Home Assistant 来处理数据!

最后的评论

我故意没有详细介绍设置 Blynk 应用程序和控件的具体细节。虽然我添加了原理图,但我也没有详细介绍我的生产布线等。我相信我们自己尝试会学到更多。话虽如此,如果您遇到困难或需要帮助才能使项目正常运行(或想知道我为什么要做某事),请给我发消息,我很乐意提供帮助。

 


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !