diff --git a/README.md b/README.md index fbfdd7c..a8be36c 100644 --- a/README.md +++ b/README.md @@ -1 +1,78 @@ -# EasyESPRealy \ No newline at end of file +[![MIT License](https://img.shields.io/github/license/serega404/EasyESPRealy)](https://github.com/serega404/EasyESPRealy/blob/master/LICENSE) +![IDE](https://img.shields.io/badge/Work%20in-Arduino%20IDE-green) + +## About The Project + +This project was designed to facilitate deployment of a Wi-Fi relay. Advanced users can also extend its functionality with ease. + +### Example device +![alt text](images/example_device.jpg "example") + +### Installation + +1. Open .ino file in Arduino IDE + +2. Select COM port and Board in the tools + +3. Change the following constants: + + - `WIFI_SSID` - SSID of your Wi-Fi network, + - `WIFI_PASSWORD` - Wi-Fi password, + - `MQTT_HOST`, `MQTT_LOGIN`, `MQTT_PASSWORD` - as required to connect to your MQTT Broker. + + It is also recommended to change your `OTAUSER` and `OTAPASSWORD`. + +3. Flash the firmware to the ESP. + +## Usage + +The MQTT Protocol is used in many smart home projects, and this repository is no exception. A server with a MQTT Brocker installed and a client that can handle requests are required. + +To enable or disable the relay, send `1` or `0` to `setStateTopic`. +Сurrent status of the relay can be obtained in `stateTopic`. + +### Home Assistant template config +``` +light: + - platform: mqtt + name: "Light 1" + command_topic: "dvor/light1/state/set" + state_topic: "dvor/light1/state" + payload_on: "1" + payload_off: "0" + qos: 1 +``` + +## MQTT Topics + +* `ipTopic` - returns the IP address after entering boot mode +* `bootTopic` - required for HTTP updater +* `setStateTopic` - necessary to get information from MQTT broker +* `stateTopic` - checks whether the relay is enabled + +## OTA Update + +1. Send `1` in `bootTopic` to enable firmware update mode +2. Get relay's IP address from `ipTopic` (this can be done with [MQTT-Explorer](https://mqtt-explorer.com/)) +3. Go to `http://{ip}/firmware` in your Internet browser +4. Enter your username and password (Default: `admin` / `admin`) +5. Select the file and click *Update Firmware* + +## Libraries + +* [ESP8266WiFi](https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WiFi) +* [AsyncMqttClient](https://github.com/marvinroger/async-mqtt-client) +* [ESP8266HTTPUpdateServer](https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266HTTPUpdateServer) +* [Ticker](https://github.com/esp8266/Arduino/tree/master/libraries/Ticker) + +## Contributing + +Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Feel free to contribute to this project. + +## License + +Distributed under the MIT License. See `LICENSE` for more information. + +## Contact + +*Sergey Karmanov* - [@serega404](https://t.me/serega404) \ No newline at end of file diff --git a/images/example_device.jpg b/images/example_device.jpg new file mode 100644 index 0000000..c29b4ad Binary files /dev/null and b/images/example_device.jpg differ diff --git a/src/src.ino b/src/src.ino new file mode 100644 index 0000000..6716e5f --- /dev/null +++ b/src/src.ino @@ -0,0 +1,172 @@ + /* +|-------------------------------------------| + -|By serega404|- + SmartRelay +|-------------------------------------------| +*/ + +//------Libraries------ +#include +#include +#include +#include +//------Libraries------ + +//------Const------ +#define RELAY 3 // relay connected to GPIO3, dont use GPIO2 in ESP-01 + +#define WIFI_SSID "SSID" +#define WIFI_PASSWORD "PASSWORD" + +#define OTAUSER "admin" +#define OTAPASSWORD "admin" +#define OTAPATH "/firmware" +#define SERVERPORT 80 + +#define MQTT_HOST IPAddress(192, 168, 0, 2) +#define MQTT_PORT 1883 +#define MQTT_LOGIN "user" +#define MQTT_PASSWORD "password" + +// 0 - off | 1 - on +const bool defaultState = 1; // if wifi or mqtt is not available then the relay will be automatically turned on or off +const int reconnectTime = 10; + +// MQTT Topics +const char* ipTopic = "dvor/light1/ip"; // this topic will return the ip address after entering the boot mode +const char* bootTopic = "dvor/light1/boot"; // this topic is required for http updater +const char* setStateTopic = "dvor/light1/state/set"; // this topic is necessary in order to get information from mqqt broker +const char* stateTopic = "dvor/light1/state"; // this topic is necessary to find out whether the relay is enabled or disabled +//------Const------ + +//------Variables------ +bool bootMode = false; +//------Variables------ + +//------Initialization------ +AsyncMqttClient mqttClient; +Ticker reconnectTimer; + +ESP8266WebServer HttpServer(SERVERPORT); +ESP8266HTTPUpdateServer httpUpdater; +//------Initialization------ + +void setup() +{ + pinMode(RELAY,OUTPUT); + + // If you use the RX TX pins for relay, you need to disable UART + if (RELAY != 3 && RELAY != 1) + Serial.begin(9600); + + Serial.println(); + + mqttClient.onConnect(onMqttConnect); + mqttClient.onDisconnect(onMqttDisconnect); + mqttClient.onMessage(onMqttMessage); + mqttClient.setServer(MQTT_HOST, MQTT_PORT); + mqttClient.setCredentials(MQTT_LOGIN, MQTT_PASSWORD); + + httpUpdater.setup(&HttpServer, OTAPATH, OTAUSER, OTAPASSWORD); + HttpServer.onNotFound(handleNotFound); + HttpServer.begin(); + + wifiConnect(); + + if (WiFi.status() != WL_CONNECTED) { + Serial.println("Couldn't connect to Wi Fi!"); + defaultState ? digitalWrite(RELAY, HIGH) : digitalWrite(RELAY, LOW); + } else if (WiFi.status() == WL_CONNECTED && !mqttClient.connected()) { + Serial.println("Connecting to MQTT..."); + mqttClient.connect(); + } +} + +void loop() +{ + if (bootMode) { + HttpServer.handleClient(); + } +} + +void wifiConnect() { + unsigned long ConnectStart = millis(); + WiFi.mode(WIFI_STA); + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + + Serial.print("Connecting to Wi-Fi"); + + while (millis() - ConnectStart < 10000 && WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); + if (WiFi.status() == WL_CONNECTED) { + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } +} + +void reconnectFunc() { + Serial.println("Reconnect..."); + if (WiFi.status() == WL_CONNECTED && !mqttClient.connected()) { + Serial.println("Connecting to MQTT..."); + mqttClient.connect(); + } else if (mqttClient.connected()) { + reconnectTimer.detach(); + } +} + +// MQTT Callback start + +void onMqttConnect(bool sessionPresent) { + Serial.println("Connected to MQTT. "); + + mqttClient.subscribe(setStateTopic, 1); + Serial.println("Subscribing at setStateTopic"); + + mqttClient.subscribe(bootTopic, 2); + Serial.println("Subscribing at bootTopic"); + + mqttClient.publish(stateTopic, 1, true, "1"); + Serial.println("Publishing default state"); +} + +void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) { + Serial.println("Disconnected from MQTT."); + defaultState ? digitalWrite(RELAY, HIGH) : digitalWrite(RELAY, LOW); + if (!mqttClient.connected()) { + reconnectTimer.attach(reconnectTime, reconnectFunc); + } +} + +void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { + Serial.println("New message: \"" + String(payload) + "\" in topic: " + String(topic)); + if (String(topic).substring(0, 21) == setStateTopic) { + if (String(payload).substring(0, 1) == "1") { + Serial.println("RELAY: ON"); + digitalWrite(RELAY, HIGH); + mqttClient.publish(stateTopic, 1, true, "1"); + } else if (String(payload).substring(0, 1) == "0") { + Serial.println("RELAY: OFF"); + digitalWrite(RELAY, LOW); + mqttClient.publish(stateTopic, 1, true, "0"); + } + } else if(String(topic).substring(0, 16) == bootTopic) { + if (String(payload).substring(0, 1) == "1") { + Serial.println("BOOT: ON"); + mqttClient.publish(ipTopic, 1, true, WiFi.localIP().toString().c_str()); + bootMode = true; + } else if (String(payload).substring(0, 1) == "0") { + Serial.println("BOOT: OFF"); + bootMode = false; + } + } +} + +// MQTT Callback end + +void handleNotFound() { + HttpServer.send(404, "text/plain", "404: Not found"); +}