├── .gitignore ├── .travis.yml ├── README.md ├── lib └── readme.txt ├── platformio.ini └── src └── main.ino /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .clang_complete 3 | .gcc-flags.json 4 | .piolibdeps -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < http://docs.platformio.org/en/stable/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < http://docs.platformio.org/en/stable/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < http://docs.platformio.org/en/stable/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choice one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # 39 | # script: 40 | # - platformio run 41 | 42 | 43 | # 44 | # Template #2: The project is intended to by used as a library with examples 45 | # 46 | 47 | # language: python 48 | # python: 49 | # - "2.7" 50 | # 51 | # sudo: false 52 | # cache: 53 | # directories: 54 | # - "~/.platformio" 55 | # 56 | # env: 57 | # - PLATFORMIO_CI_SRC=path/to/test/file.c 58 | # - PLATFORMIO_CI_SRC=examples/file.ino 59 | # - PLATFORMIO_CI_SRC=path/to/test/directory 60 | # 61 | # install: 62 | # - pip install -U platformio 63 | # 64 | # script: 65 | # - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266 (NodeMCU) relay control 2 | 3 | This project was the first thing I did with NodeMCU. I hope that following this README will be as straightforward as possible. In case you have questions do not hesitate to contact me using [Issues](https://github.com/ArnieX/esp8266_relay_mqtt/issues). 4 | 5 | ## Check out my new repository with ESP8266 equipped HW - [SWIFITCH](https://github.com/ArnieX/swifitch) 6 | 7 | If you want to be able to change LED Strip brightness too follow my other project: [LED Strip Dimming using MOSFET](https://github.com/ArnieX/esp8266_dimmer_mqtt) 8 | 9 | ## Dependencies 10 | - Some MQTT server (If you have Raspberry Pi use Mosquitto) 11 | - Optional is [Homebridge](https://github.com/nfarina/homebridge) with [MQTT Plugin](https://github.com/cflurin/homebridge-mqtt) to control the relay from iDevices 12 | - Optional that will make your life easier with IoT is Node-RED, plus you can get decent dashboard with Node-RED Dashboard plugin 13 | - [PlatformIO](https://github.com/platformio/platformio) best Arduino IDE available, hacked from ATOM text editor 14 | 15 | - [WiFi Manager](https://github.com/tzapu/WiFiManager) 16 | - [PubSubClient (MQTT)](https://github.com/knolleary/pubsubclient) 17 | - ESP8266 WiFi library 18 | 19 | Install using PlatformIO Library Manager 20 | 21 | ``` 22 | pio lib -g install WiFiManager 23 | pio lib -g install PubSubClient 24 | pio lib -g install ESP8266wifi 25 | ``` 26 | 27 | ## Getting started 28 | 29 | Update main.ino with your custom preferences 30 | 31 | - [11] Set desired configuration AP name (This is used when ESP8266 isn't connected to your WiFi router to allow setup) 32 | - [12] Set password for configuration AP, so that noone else can access it in case your router is OFF 33 | - [13] Set your MQTT IP address 34 | - [14] Set your MQTT PORT 35 | 36 | - [17-21] Change MQTT topics (THIS IS OPTIONAL and I do not recommend to change it for first test of function) 37 | 38 | - [33] Set your OTA password, this will be used for secured OTA update using PlatformIO, change this respectively in platformio.ini too 39 | 40 | - [90 and 102] Uncomment and set your DATA pin which is connected to your relay 41 | 42 | - [123] Uncomment and set your DATA pin which is connected to your relay 43 | - [124] OPTIONAL: If you want your relay to turn on by default on boot, uncomment this line. 44 | 45 | Update platformio.ini with your custom preferences (Do not change unless you want to turn OTA ON) 46 | 47 | - [14 and 15] To enable OTA for next updates uncomment these lines and change values to reflect your enviroment 48 | 49 | To turn OTA OFF any time, just comment these lines again with ; 50 | 51 | ## Usage 52 | 53 | Send command through your MQTT server as such: 54 | 55 | |TOPIC|DESCRIPTION| 56 | |---|---| 57 | |home/room/relayone|Send 1/0 to turn ON/OFF| 58 | |home/pingall|Send whatever and you get response at topic home/pingallresponse| 59 | 60 | Receive back from your device: 61 | 62 | |TOPIC|DESCRIPTION| 63 | |---|---| 64 | |home/room/device_name/devicestatus|Will contain device status eg connected| 65 | |home/pingallresponse|This will contain status after you send pingall request and all devices should respond| 66 | |home/room/relayone/status|This listens for status change to set correct status eg in Homebridge| 67 | 68 | ## Result 69 | 70 | **Click the image to play video on YouTube:** 71 | 72 | [![Hey Siri! Hey ESP8266!](http://img.youtube.com/vi/LSd6auz77bI/0.jpg)](https://www.youtube.com/watch?v=LSd6auz77bI) 73 | -------------------------------------------------------------------------------- /lib/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for the project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link to executable file. 4 | 5 | The source code of each library should be placed in separate directory, like 6 | "lib/private_lib/[here are source files]". 7 | 8 | For example, see how can be organized `Foo` and `Bar` libraries: 9 | 10 | |--lib 11 | | |--Bar 12 | | | |--docs 13 | | | |--examples 14 | | | |--src 15 | | | |- Bar.c 16 | | | |- Bar.h 17 | | |--Foo 18 | | | |- Foo.c 19 | | | |- Foo.h 20 | | |- readme.txt --> THIS FILE 21 | |- platformio.ini 22 | |--src 23 | |- main.c 24 | 25 | Then in `src/main.c` you should use: 26 | 27 | #include 28 | #include 29 | 30 | // rest H/C/CPP code 31 | 32 | PlatformIO will find your libraries automatically, configure preprocessor's 33 | include paths and build them. 34 | 35 | More information about PlatformIO Library Dependency Finder 36 | - http://docs.platformio.org/en/stable/librarymanager/ldf.html 37 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter, extra scripting 4 | ; Upload options: custom port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; 7 | ; Please visit documentation for the other options and examples 8 | ; http://docs.platformio.org/en/stable/projectconf.html 9 | 10 | [env:nodemcuv2] 11 | platform = espressif8266 12 | board = nodemcuv2 13 | framework = arduino 14 | ;upload_flags = --port=8266 --auth=ESP8266_PASSWORD 15 | ;upload_port = IP (Set fixed IP in your router first!!!) -------------------------------------------------------------------------------- /src/main.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Constants 11 | const char* autoconf_ssid = "ESP8266_DEVICENAME"; //AP name for WiFi setup AP which your ESP will open when not able to connect to other WiFi 12 | const char* autoconf_pwd = "CONFIG_WIFI_PWD"; // AP password so noone else can connect to the ESP in case your router fails 13 | const char* mqtt_server = "XX.XX.XX.XX"; //MQTT Server IP, your home MQTT server eg Mosquitto on RPi, or some public MQTT 14 | const int mqtt_port = 1883; //MQTT Server PORT, default is 1883 but can be anything. 15 | 16 | // MQTT Constants 17 | const char* mqtt_devicestatus_set_topic = "home/room/device_name/devicestatus"; // Change device name, but you can completely change the topics to suit your needs 18 | const char* mqtt_pingallresponse_set_topic = "home/pingallresponse"; 19 | const char* mqtt_relayone_set_topic = "home/room/relayone/status"; 20 | const char* mqtt_relayone_get_topic = "home/room/relayone"; 21 | const char* mqtt_pingall_get_topic = "home/pingall"; 22 | 23 | // Global 24 | byte relayone_state; 25 | long lastReconnectAttempt = 0; 26 | 27 | WiFiClient espClient; 28 | PubSubClient client(espClient); 29 | 30 | void setup_ota() { 31 | 32 | // Set OTA Password, and change it in platformio.ini 33 | ArduinoOTA.setPassword("ESP8266_PASSWORD"); 34 | ArduinoOTA.onStart([]() {}); 35 | ArduinoOTA.onEnd([]() {}); 36 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {}); 37 | ArduinoOTA.onError([](ota_error_t error) { 38 | if (error == OTA_AUTH_ERROR); // Auth failed 39 | else if (error == OTA_BEGIN_ERROR); // Begin failed 40 | else if (error == OTA_CONNECT_ERROR); // Connect failed 41 | else if (error == OTA_RECEIVE_ERROR); // Receive failed 42 | else if (error == OTA_END_ERROR); // End failed 43 | }); 44 | ArduinoOTA.begin(); 45 | 46 | } 47 | 48 | boolean reconnect() { 49 | 50 | // MQTT reconnection function 51 | 52 | // Create a random client ID 53 | String clientId = "ESP8266Client-"; 54 | clientId += String(random(0xffff), HEX); 55 | 56 | // Attempt to connect 57 | if (client.connect(clientId.c_str(),mqtt_devicestatus_set_topic,0,false,"disconnected")) { 58 | 59 | // Once connected, publish an announcement... 60 | client.publish(mqtt_devicestatus_set_topic, "connected"); 61 | // ... and resubscribe 62 | client.subscribe(mqtt_relayone_get_topic); 63 | client.subscribe(mqtt_pingall_get_topic); 64 | 65 | } 66 | 67 | return client.connected(); 68 | 69 | } 70 | 71 | void callback(char* topic, byte* payload, unsigned int length) { 72 | 73 | char c_payload[length]; 74 | memcpy(c_payload, payload, length); 75 | c_payload[length] = '\0'; 76 | 77 | String s_topic = String(topic); // Topic 78 | String s_payload = String(c_payload); // Message content 79 | 80 | // Handling incoming MQTT messages 81 | 82 | if ( s_topic == mqtt_relayone_get_topic ) { 83 | 84 | if (s_payload == "1") { 85 | 86 | if (relayone_state != 1) { 87 | 88 | // Turn relay ON, set HIGH/LOW depending on your relay 89 | 90 | // digitalWrite(Dx,HIGH); 91 | client.publish(mqtt_relayone_set_topic, "1"); 92 | relayone_state = 1; 93 | 94 | } 95 | 96 | } else if (s_payload == "0") { 97 | 98 | if (relayone_state != 0) { 99 | 100 | // Turn relay OFF, set HIGH/LOW depending on your relay 101 | 102 | // digitalWrite(Dx,LOW); 103 | client.publish(mqtt_relayone_set_topic, "0"); 104 | relayone_state = 0; 105 | 106 | } 107 | 108 | } 109 | 110 | } else if ( s_topic == mqtt_pingall_get_topic ) { 111 | 112 | client.publish(mqtt_pingallresponse_set_topic, "{\"device_name\":\"connected\"}"); 113 | client.publish(mqtt_devicestatus_set_topic, "connected"); 114 | 115 | } 116 | 117 | 118 | } 119 | 120 | void setup() { 121 | 122 | //Relay setup 123 | //pinMode(Dx,OUTPUT); 124 | //digitalWrite(Dx,HIGH); // Turn ON by default ca. 0,5s after power-up 125 | 126 | pinMode(BUILTIN_LED, OUTPUT); //Initialize the BUILTIN_LED pin as an output 127 | Serial.begin(115200); 128 | WiFiManager wifiManager; 129 | wifiManager.autoConnect(autoconf_ssid,autoconf_pwd); 130 | setup_ota(); 131 | MDNS.begin("esp-relay"); 132 | client.setServer(mqtt_server, mqtt_port); 133 | client.setCallback(callback); 134 | lastReconnectAttempt = 0; 135 | digitalWrite(BUILTIN_LED, HIGH); //Turn off LED as default, also signal that setup is over 136 | 137 | } 138 | 139 | void loop() { 140 | 141 | if (!client.connected()) { 142 | long now = millis(); 143 | if (now - lastReconnectAttempt > 5000) { 144 | lastReconnectAttempt = now; 145 | // Attempt to reconnect 146 | if (reconnect()) { 147 | lastReconnectAttempt = 0; 148 | } 149 | } 150 | } else { 151 | // Client connected 152 | client.loop(); 153 | } 154 | ArduinoOTA.handle(); 155 | 156 | 157 | } --------------------------------------------------------------------------------