├── doc └── images │ ├── easy.png │ ├── ex1.png │ ├── ex2.png │ ├── ex3.png │ ├── logo.png │ ├── ok.png │ ├── safe.png │ ├── schema.png │ └── setup.png ├── .gitignore ├── .vscode └── extensions.json ├── test └── README ├── src ├── config.h └── main.cpp ├── platformio.ini ├── LICENSE └── README.md /doc/images/easy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/easy.png -------------------------------------------------------------------------------- /doc/images/ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/ex1.png -------------------------------------------------------------------------------- /doc/images/ex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/ex2.png -------------------------------------------------------------------------------- /doc/images/ex3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/ex3.png -------------------------------------------------------------------------------- /doc/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/logo.png -------------------------------------------------------------------------------- /doc/images/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/ok.png -------------------------------------------------------------------------------- /doc/images/safe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/safe.png -------------------------------------------------------------------------------- /doc/images/schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/schema.png -------------------------------------------------------------------------------- /doc/images/setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raomin/ESPGate/HEAD/doc/images/setup.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | private 7 | private/* -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Configuration file for ESP Gate 3 | */ 4 | 5 | // Replace with your network credentials 6 | const char* ssid = "YOURSSID"; 7 | const char* password = "PASSWORD"; 8 | 9 | const int outputPin = 4; //<<-- The GPIO on which is connected the gate switch 10 | 11 | const bool pullup = 1; //<<-- Put 1 if the port should be normally at 5V-HIGH (5v nothing, GND to Open) 12 | // Put 0 if the port should be normally at GND-LOW (GND nothing, 5V to Open) 13 | 14 | // MQTT Server address 15 | const char* mqtt_server = "192.168.1.46";//<<-- Your MQTT server IP -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp8266_d1_mini] 12 | platform = espressif8266 13 | board = d1_mini 14 | framework = arduino 15 | monitor_speed = 115200 16 | ;upload_port = 192.168.1.86 17 | lib_deps = knolleary/PubSubClient@^2.8 18 | 19 | 20 | [env:esp32dev] 21 | platform = espressif32 22 | board = esp32dev 23 | framework = arduino 24 | monitor_speed = 115200 25 | lib_deps = knolleary/PubSubClient@^2.8 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Raomin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |

5 | 6 |   7 | 8 |   9 | 10 |   11 | 12 |

13 | 14 |
15 | 16 |

ESPGate is a solution to control your electric gate with HomeAssistant using just an ESP8266 or ESP32.

17 | 18 | _If this project has any value for you, please consider [buying me a 🍺](https://www.buymeacoffee.com/raomin)!. I don't do this for money but it feels good to get some support! Thanks :)_ 19 | 20 | ## Features 21 | ![](doc/images/schema.png) 22 | 23 | 28 | 29 | # Prerequisites 30 | 31 | ## Hardware 32 | 33 | - An electric gate you want to control over Home Assistant. 34 | - An ESP8266 or ESP32 35 | - *Optional: one relay module.* 36 | - *Optional: a 5v regulator / step down.* 37 | 38 | ## Software 39 | 40 | - Platformio 41 | 42 | # Getting started 43 | 44 | ## Step 1: Determining the connections 45 | 46 | ### Principles 47 | 1. Open your gate main board and localize with the manual the connection for the Open/Stop/Close switch. This should look like this: 48 | 49 | Example 1 | Example 2 | Example 3 50 | :-------------------------:|:-------------------------:|:-------------------------: 51 | ![](doc/images/ex1.png) | ![](doc/images/ex2.png) | ![](doc/images/ex3.png) 52 | 53 | 2. Determine if the connection is pulled UP or DOWN with a multimeter: Measure the voltage between COM (black gnd probe) and the O/S/C pin (red/vcc probe). If the multimeter reads 5V you have pulled UP, meaning that the OSC pin needs to be put to the GND to activate. If you read -5v the OSC pin is pulled DOWN, meaning that the OSC pin acts when receiving 5v. 54 | 55 | 3. Find some 5v for your ESP. Usually, your microcontrollers on the board will work with 5v and your ESP needs 5V to work. So probe around to find some 5V connection. Note that some 5V lines might note have enough current to power the ESP. I found mine in a serial connector on the board. 56 | 57 | ### The easy way 58 | 59 | ![](doc/images/easy.png) 60 | 61 | ### The safe way 62 | 63 | If your voltage reading is in the range of +5v to -5v you should be able to connect your ESP directly. If you have a different value, or if you want to play safe, use a relay module (eg something like [this](https://www.aliexpress.com/item/1005001567474787.html)). 64 | 65 | ![](doc/images/safe.png) 66 | 67 | ### The still ok way 68 | 69 | If you cannot find a suitable 5V, you'll most probably find 12V (eg for the photosensors) that you'll have to regulate with a 12V -> 5V regulator (that old USB car charger that you kept somewhere, or a DC-DC 5V step down...) 70 | 71 | ![](doc/images/ok.png) 72 | 73 | ## Step 2: The soft part 74 | 75 | 1. Edit `platformio.ini` to set the right environment and board. 76 | 77 | 2. Edit the file `src/config.h` as follows: 78 | - Enter your wifi and mqtt settings 79 | - Select the GPIO output you'll use 80 | - Set `pullup` to 1 or 0 depending whether your OSC is PulledUP (1) or Pulled Down (0). 81 | 82 | 3. Upload. A new switch called `ESP Gate` should appear in Home Assistant. 83 | 84 | 4. Go connect your ESP, press on the switch in HA, tadaaa! 85 | 86 | # How it looks 87 | 88 | ## On an SCS Sentinel - Opengate 2 89 | 90 | ![](doc/images/setup.png) 91 | 92 | # FAQ 93 | 94 | ### How can I determine if the gate is opened or closed? 95 | 96 | There is no easy way of doing this. I'm still wondering myself... 97 | 98 | 99 | ### Is it safe? Can I break my opener? 100 | 101 | It is as safe as interacting with a device can be. Pretty safe if you are a bit careful. Play it safe with a relay and a DC regulator if you prefer. Use is entirely at your own risk. No liability. 102 | 103 | # How can I contribute? 104 | 105 | Every contribution to this project is highly appreciated! Don't fear to create issues to report possible bugs or feature request. Pull requests which enhance or fix ESPGate are also greatly appreciated for everybody! 106 | 107 | If this project is useful to you, and if you want, [you can buy me a beer](https://www.buymeacoffee.com/raomin)! It feels good and really helps improving ESPGate. Thanks :) 108 | 109 | # License 110 | ESPGate is licensed under ![MIT Licence](https://img.shields.io/github/license/raomin/ESPGate.svg?style=for-the-badge) 111 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /********* 2 | * ESPGate by Raomin - June 2021 3 | * 4 | * Control your electric gate from home assistant. 5 | * 6 | *********/ 7 | 8 | #include "config.h" 9 | 10 | #ifdef ESP32 11 | #include 12 | #include 13 | #elif ESP8266 14 | #include 15 | #include 16 | #endif 17 | #include 18 | #include 19 | #include 20 | 21 | WiFiClient espClient; 22 | PubSubClient client(espClient); 23 | 24 | char devicename[32]; 25 | 26 | bool action; 27 | bool stdby; 28 | unsigned long first=0; 29 | 30 | bool gateClosed; 31 | 32 | 33 | void doAction(){ 34 | digitalWrite(outputPin, action); 35 | 36 | 37 | 38 | if (gateClosed) 39 | client.publish("home/gated/state", "opening"); 40 | else 41 | client.publish("home/gated/state", "closing"); 42 | client.publish("home/gate/state", "ON"); 43 | 44 | delay(500); 45 | digitalWrite(outputPin, stdby); 46 | client.publish("home/gate/state", "OFF"); 47 | 48 | } 49 | 50 | void callback(char *topic, byte *payload, unsigned int length) 51 | { 52 | char message[length + 1]; 53 | strncpy(message, (char *)payload, length); 54 | message[length] = '\0'; 55 | 56 | client.publish("home/gate/log", message); 57 | 58 | // check if payload is "OPEN" or "CLOSE" or "STOP" 59 | if (strcmp(message, "OPEN") == 0) 60 | { 61 | // action = HIGH; 62 | // stdby = LOW; 63 | doAction(); 64 | } 65 | else if (strcmp(message, "CLOSE") == 0) 66 | { 67 | // action = LOW; 68 | // stdby = HIGH; 69 | doAction(); 70 | } 71 | else if (strcmp(message, "STOP") == 0) 72 | { 73 | doAction(); 74 | } 75 | else if (strcmp(message, "ON") == 0) 76 | { 77 | doAction(); 78 | } 79 | else if (strcmp(message, "OFF") == 0) 80 | { 81 | doAction(); 82 | } 83 | 84 | 85 | } 86 | 87 | void setup() 88 | { 89 | Serial.begin(115200); 90 | 91 | sprintf(devicename,"espGate-%d",ESP.getChipId()); 92 | ESP.getChipId(); 93 | 94 | // Initialize the output variables as outputs 95 | if (pullup) 96 | { 97 | action = LOW; 98 | stdby = HIGH; 99 | } 100 | else 101 | { 102 | action = HIGH; 103 | stdby = LOW; 104 | } 105 | pinMode(D0, INPUT); 106 | gateClosed = false; 107 | pinMode(outputPin, OUTPUT); 108 | digitalWrite(outputPin, stdby); 109 | 110 | 111 | // Connect to Wi-Fi network with SSID and password 112 | Serial.print("Connecting to "); 113 | Serial.println(ssid); 114 | WiFi.begin(ssid, password); 115 | while (WiFi.status() != WL_CONNECTED) 116 | { 117 | delay(500); 118 | Serial.print("."); 119 | } 120 | // Print local IP address and start web server 121 | Serial.println(""); 122 | Serial.println("WiFi connected."); 123 | Serial.println("IP address: "); 124 | Serial.println(WiFi.localIP()); 125 | ArduinoOTA.begin(); 126 | client.setServer(mqtt_server, 1883); 127 | client.setCallback(callback); 128 | 129 | #ifdef ESP32 130 | mdns_hostname_set("espgate"); 131 | #elif ESP8266 132 | if (!MDNS.begin(devicename)) 133 | { 134 | Serial.println("Error starting mDNS"); 135 | } 136 | #endif 137 | } 138 | 139 | void reconnect() 140 | { 141 | // Loop until we're reconnected 142 | int tries = 0; 143 | while (!client.connected()) 144 | { 145 | Serial.print("Attempting MQTT connection..."); 146 | // Attempt to connect 147 | if (client.connect(devicename, "", "", "home/gate/LWT", 0, true, "offline")) 148 | { 149 | // Once connected, publish an announcement... 150 | 151 | client.publish("homeassistant/cover/gated/config", "{\"name\": \"ESP Gated\",\"~\":\"home/gated\",\"uniq_id\":\"gated\",\"dev_cla\":\"door\",\"avty_t\":\"~/LWT\",\"command_topic\": \"~/set\", \"stat_t\": \"~/state\"}", true); 152 | client.publish("homeassistant/switch/gate/config", "{\"name\": \"ESP Gate\",\"~\":\"home/gate\",\"uniq_id\":\"gate\",\"avty_t\":\"~/LWT\",\"command_topic\": \"~/set\", \"stat_t\": \"~/state\"}", true); 153 | client.publish("home/gate/LWT", "online", true); 154 | client.publish("home/gated/LWT", "online", true); 155 | client.subscribe("home/gate/set"); 156 | client.subscribe("home/gated/set"); 157 | Serial.println("Connected!"); 158 | bool status = digitalRead(D0)==HIGH; 159 | client.publish("home/gate/log", status ? "HIGH" : "LOW"); 160 | } 161 | else 162 | { 163 | Serial.print("failed, rc="); 164 | Serial.print(client.state()); 165 | Serial.println(" try again in 5 seconds"); 166 | // Wait 5 seconds before retrying 167 | unsigned long start = millis(); 168 | while (millis() < (start + 5000L)) 169 | { 170 | ArduinoOTA.handle(); 171 | } 172 | if (tries++ == 10) 173 | { 174 | #ifdef ESP32 175 | esp_restart(); 176 | #elif ESP8266 177 | Serial.println("10 failures, restart"); 178 | ESP.reset(); 179 | #endif 180 | } 181 | } 182 | } 183 | } 184 | 185 | 186 | bool hasGateChanged(bool update=false)//return true if gate state has changed 187 | { 188 | bool status = digitalRead(D0)==HIGH; 189 | if (status != gateClosed) 190 | { 191 | if (update) 192 | gateClosed = status; 193 | return true; 194 | } 195 | return false; 196 | } 197 | 198 | uint16 last = 0; 199 | 200 | void loop() 201 | { 202 | 203 | if (hasGateChanged()) 204 | { 205 | //debounce 206 | delay(500); 207 | hasGateChanged(true); 208 | client.publish("home/gated/state", gateClosed ? "closed" : "open"); 209 | client.publish("home/gate/log", gateClosed ? "Gate is closed" : "Gate is open"); 210 | } 211 | 212 | 213 | if (digitalRead(D0) == LOW && gateClosed) 214 | { 215 | gateClosed = false; 216 | client.publish("home/gated/state", "open"); 217 | client.publish("home/gate/log", "Gate is open"); 218 | } 219 | else if (digitalRead(D0) == HIGH && !gateClosed) 220 | { 221 | client.publish("home/gate/log", "Gate is closed"); 222 | gateClosed = true; 223 | client.publish("home/gated/state", "closed"); 224 | } 225 | 226 | 227 | 228 | ArduinoOTA.handle(); 229 | if (!client.connected()) 230 | { 231 | Serial.println("Not connected, reconnecting"); 232 | reconnect(); 233 | } 234 | client.loop(); 235 | } --------------------------------------------------------------------------------