Skip to content

Commit 794c8f2

Browse files
authored
Sketch for Arduino R4 WIFI over WIFI (#1096)
//2025 This sketch enables a Blutezeit 1400-lb gate opener to OPEN/CLOSE with limit switches via WIFI. The WIFI address is dynamic; the PORT is 7624. //It was built with a Arduino Uno R4 WIFI and the Arduino 4-Relay Shield (bought directly from Arduino) using the NO ports attached to OPEN/CLOSE/STOP and the COMMON ports all attached to GND. //Limit switches were ME8108 from amazon utilizing NO contacts. 2 wires required/one is attached to a ground with a 10k resistor the other contact is attached to D2 (OPENED) and D3 (CLOSED) //It also enables updating sketches via Wifi... connect with the ARDUINO IDE on the SAME network with the IP address and Port. //Designed to integrate with the Rolloffino driver in INDI ///////////////////////////////////////////////////////////////////////
1 parent 4e69058 commit 794c8f2

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
//2025 This sketch enables a Blutezeit 1400-lb gate opener to OPEN/CLOSE with limit switches via WIFI. The WIFI address is dynamic; the PORT is 7624.
2+
//It was built with a Arduino Uno R4 WIFI and the Arduino 4-Relay Shield (bought directly from Arduino) using the NO ports attached to OPEN/CLOSE/STOP and the COMMON ports all attached to GND.
3+
//Limit switches were ME8108 from amazon utilizing NO contacts. 2 wires required/one is attached to a ground with a 10k resistor the other contact is attached to D2 (OPENED) and D3 (CLOSED)
4+
//It also enables updating sketches via Wifi... connect with the ARDUINO IDE on the SAME network with the IP address and Port.
5+
//Designed to integrate with the Rolloffino driver in INDI
6+
///////////////////////////////////////////////////////////////////////
7+
#include "secrets.h" // Must come before any use of WIFI_SSID / WIFI_PASSWORD
8+
#include <WiFiS3.h>
9+
#include <OTAUpdate.h>
10+
11+
// —— Configuration ———————————————————————————————————————
12+
// Relay shield pins
13+
const int RELAY_OPEN = 4;
14+
const int RELAY_CLOSE = 7;
15+
const int RELAY_ABORT = 8;
16+
const int RELAY_UNUSED = 12;
17+
18+
// Limit switches (active LOW)
19+
const int LIMIT_OPENED = 2;
20+
const int LIMIT_CLOSED = 3;
21+
22+
// If you wire C→NO instead of C→NC, set this to true
23+
const bool INVERT_RELAY_LOGIC = true;
24+
25+
// Helper macros: respect the invert flag
26+
#define RELAY_ON(pin) digitalWrite(pin, INVERT_RELAY_LOGIC ? HIGH : LOW)
27+
#define RELAY_OFF(pin) digitalWrite(pin, INVERT_RELAY_LOGIC ? LOW : HIGH)
28+
29+
// Pulse duration (ms)
30+
const unsigned long RELAY_DURATION = 2000;
31+
32+
// TCP server
33+
WiFiServer server(7624);
34+
WiFiClient client;
35+
36+
// Roof states
37+
enum RoofState { IDLE, OPENING, CLOSING, ABORTING };
38+
RoofState roofState = IDLE;
39+
unsigned long relayStartTime = 0;
40+
41+
// Parser buffers
42+
String incomingCommand;
43+
bool inCommand = false;
44+
45+
// Forward declarations
46+
void processCommand(const String &cmd);
47+
void sendMsg(const String &msg);
48+
49+
// Helpers
50+
bool isRoofFullyOpen() { return digitalRead(LIMIT_OPENED) == LOW; }
51+
bool isRoofFullyClosed(){ return digitalRead(LIMIT_CLOSED) == LOW; }
52+
53+
void setup() {
54+
Serial.begin(115200);
55+
while (!Serial);
56+
57+
// — Relays all off (NC closed, NO open)
58+
pinMode(RELAY_OPEN, OUTPUT); RELAY_OFF(RELAY_OPEN);
59+
pinMode(RELAY_CLOSE, OUTPUT); RELAY_OFF(RELAY_CLOSE);
60+
pinMode(RELAY_ABORT, OUTPUT); RELAY_OFF(RELAY_ABORT);
61+
pinMode(RELAY_UNUSED, OUTPUT); RELAY_OFF(RELAY_UNUSED);
62+
63+
// Limit switches
64+
pinMode(LIMIT_OPENED, INPUT_PULLUP);
65+
pinMode(LIMIT_CLOSED, INPUT_PULLUP);
66+
67+
// Connect to Wi-Fi
68+
Serial.print("Connecting to WiFi ");
69+
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
70+
while (WiFi.status() != WL_CONNECTED) {
71+
delay(500);
72+
Serial.print('.');
73+
}
74+
Serial.println("\nConnected, IP = " + WiFi.localIP().toString());
75+
76+
// Start TCP server
77+
server.begin();
78+
Serial.println("Server listening on port 7624");
79+
}
80+
81+
void loop() {
82+
// Handle OTA uploads (note: poll() not handle())
83+
OTAUpdate();
84+
85+
// Clean up disconnected client
86+
if (client && !client.connected()) {
87+
client.stop();
88+
Serial.println(">> Client disconnected");
89+
roofState = IDLE;
90+
}
91+
92+
// Accept new connection
93+
if (!client || !client.connected()) {
94+
WiFiClient nc = server.available();
95+
if (nc) {
96+
client = nc;
97+
inCommand = false;
98+
Serial.println(">> Client Connected!");
99+
}
100+
}
101+
102+
// Read incoming characters
103+
if (client && client.connected()) {
104+
while (client.available()) {
105+
char c = client.read();
106+
Serial.print("‹in:› "); Serial.println(c);
107+
if (c == '(') {
108+
inCommand = true;
109+
incomingCommand = "";
110+
}
111+
if (inCommand) {
112+
incomingCommand += c;
113+
if (c == ')') {
114+
processCommand(incomingCommand);
115+
inCommand = false;
116+
}
117+
}
118+
}
119+
}
120+
121+
// Auto-turn-off after pulse
122+
if ((roofState == OPENING || roofState == CLOSING || roofState == ABORTING)
123+
&& (millis() - relayStartTime >= RELAY_DURATION)) {
124+
RELAY_OFF(RELAY_OPEN);
125+
RELAY_OFF(RELAY_CLOSE);
126+
RELAY_OFF(RELAY_ABORT);
127+
roofState = IDLE;
128+
Serial.println("Action complete, IDLE");
129+
}
130+
}
131+
132+
// Send a response over TCP + newline
133+
void sendMsg(const String &msg) {
134+
if (client && client.connected()) {
135+
client.print(msg);
136+
client.print('\n');
137+
Serial.println("Sent: " + msg);
138+
}
139+
}
140+
141+
// Parse and execute full commands "(...)"
142+
void processCommand(const String &rawCmd) {
143+
String cmd = rawCmd;
144+
cmd.trim();
145+
if (cmd.startsWith("(") && cmd.endsWith(")"))
146+
cmd = cmd.substring(1, cmd.length() - 1);
147+
148+
Serial.println("Received: " + cmd);
149+
150+
int p1 = cmd.indexOf(':');
151+
String command = (p1 < 0 ? cmd : cmd.substring(0, p1));
152+
if (command == "CON") {
153+
sendMsg("(ACK:0:V1.3)");
154+
return;
155+
}
156+
157+
int p2 = cmd.indexOf(':', p1 + 1);
158+
String part1 = (p1 < 0 ? "" : cmd.substring(p1 + 1, p2));
159+
String part2 = (p2 < 0 ? "" : cmd.substring(p2 + 1));
160+
161+
if (command == "GET") {
162+
if (part1 == "OPENED")
163+
sendMsg("(ACK:OPENED:" + String(isRoofFullyOpen() ? "ON":"OFF") + ")");
164+
else if (part1 == "CLOSED")
165+
sendMsg("(ACK:CLOSED:" + String(isRoofFullyClosed() ? "ON":"OFF") + ")");
166+
else if (part1 == "LOCKED")
167+
sendMsg("(ACK:LOCKED:OFF)");
168+
else if (part1 == "AUXSTATE")
169+
sendMsg("(ACK:AUXSTATE:OFF)");
170+
else
171+
sendMsg("(NAK:ERROR:BadGET)");
172+
return;
173+
}
174+
175+
if (command == "SET") {
176+
if (part1 == "OPEN") {
177+
if (part2 == "ON") {
178+
RELAY_ON(RELAY_OPEN);
179+
relayStartTime = millis(); roofState = OPENING;
180+
sendMsg("(ACK:OPEN:ON)");
181+
} else {
182+
RELAY_OFF(RELAY_OPEN); roofState = IDLE;
183+
sendMsg("(ACK:OPEN:OFF)");
184+
}
185+
}
186+
else if (part1 == "CLOSE") {
187+
if (part2 == "ON") {
188+
RELAY_ON(RELAY_CLOSE);
189+
relayStartTime = millis(); roofState = CLOSING;
190+
sendMsg("(ACK:CLOSE:ON)");
191+
} else {
192+
RELAY_OFF(RELAY_CLOSE); roofState = IDLE;
193+
sendMsg("(ACK:CLOSE:OFF)");
194+
}
195+
}
196+
else if (part1 == "ABORT") {
197+
RELAY_ON(RELAY_ABORT);
198+
relayStartTime = millis(); roofState = ABORTING;
199+
sendMsg("(ACK:ABORT:ON)");
200+
}
201+
else {
202+
sendMsg("(NAK:ERROR:BadSET)");
203+
}
204+
return;
205+
}
206+
207+
// Unknown command
208+
sendMsg("(NAK:ERROR:UnknownCmd)");
209+
}

0 commit comments

Comments
 (0)