├── 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 | 
22 |
23 |
24 | - Needs just an ESP8266 or ESP32, no need for extra hardware.
25 | - Easily integrates with Home Assistant's MQTT auto-discovery.
26 | - Supports update OverTheAir
27 |
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 |  |  | 
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 | 
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 | 
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 | 
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 | 
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 
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 | }
--------------------------------------------------------------------------------