├── .gitignore ├── .travis.yml ├── README.md ├── images └── DimmingModule.jpg ├── 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) dimmer control 2 | 3 | This project is next step of my [relay control project](https://github.com/ArnieX/esp8266_relay_mqtt) where I controled only ON/OFF state of the LED Strip. 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_dimmer_mqtt/issues). 4 | 5 | ## Dependencies 6 | - Some MQTT server (If you have Raspberry Pi use Mosquitto) 7 | - Optional is [Homebridge](https://github.com/nfarina/homebridge) with [MQTT Plugin](https://github.com/cflurin/homebridge-mqtt) to control the relay from iDevices 8 | - Optional that will make your life easier with IoT is Node-RED, plus you can get decent dashboard with Node-RED Dashboard plugin 9 | - [PlatformIO](https://github.com/platformio/platformio) best Arduino IDE available, hacked from ATOM text editor 10 | 11 | - [WiFi Manager](https://github.com/tzapu/WiFiManager) 12 | - [PubSubClient (MQTT)](https://github.com/knolleary/pubsubclient) 13 | - ESP8266 WiFi library 14 | 15 | Install using PlatformIO Library Manager 16 | 17 | ``` 18 | pio lib -g install WiFiManager 19 | pio lib -g install PubSubClient 20 | pio lib -g install ESP8266wifi 21 | ``` 22 | 23 | - Dimming module (Make the one attached, it was made by my neighbour) or buy some cheap on AliExpress/eBay and change the code if needed 24 | 25 | ![Dimming module scheme](images/DimmingModule.jpg?raw=true) 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-23] Change MQTT topics (THIS IS OPTIONAL and I do not recommend to change it for first test of function) 37 | 38 | [35] Set your OTA password, this will be used for secured OTA update using PlatformIO, change this respectively in platformio.ini too 39 | 40 | [192-194] Pin setup, PWM range, and turn off by default. Change PIN number if needed and make sure you change it everywhere. 41 | 42 | Update platformio.ini with your custom preferences (Do not change unless you want to turn OTA ON) 43 | 44 | [14 and 15] To enable OTA for next updates uncomment these lines and change values to reflect your enviroment 45 | 46 | To turn OTA OFF any time, just comment these lines again with ; 47 | 48 | ## Usage 49 | 50 | Send command through your MQTT server as such: 51 | 52 | |TOPIC|DESCRIPTION| 53 | |---|---| 54 | |home/room/ledstrip|Turn LED Strip ON/OFF send 1/0| 55 | |home/room/ledstrip/brightness|Set brightness 0% to 100% so send 0-100 number| 56 | |home/pingall|Send whatever and you get response at topic home/pingallresponse| 57 | 58 | Receive back from your device: 59 | 60 | |TOPIC|DESCRIPTION| 61 | |---|---| 62 | |home/room/device_name/devicestatus|Will contain device status eg connected| 63 | |home/room/ledstrip/status|Current ON/OFF status 1/0| 64 | |home/room/ledstrip/brightness/status|Current brightness level, number 0-100| 65 | |home/pingallresponse|This will contain status after you send pingall request and all devices should respond| 66 | 67 | ## Result 68 | 69 | **Click the image to play video on YouTube:** 70 | 71 | [![LED Strip dimming with Siri, Homebridge and NodeMCU (ESP8266)](http://img.youtube.com/vi/KB4Ermphibo/0.jpg)](https://www.youtube.com/watch?v=KB4Ermphibo) 72 | -------------------------------------------------------------------------------- /images/DimmingModule.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArnieX/esp8266_dimmer_mqtt/8fc194f33c5ed17238ca8e873bbf57d7e2ce1ff0/images/DimmingModule.jpg -------------------------------------------------------------------------------- /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"; 18 | const char* mqtt_dimlightstatus_set_topic = "home/room/ledstrip/status"; 19 | const char* mqtt_dimlightbrightnessstatus_set_topic = "home/room/ledstrip/brightness/status"; 20 | const char* mqtt_pingallresponse_set_topic = "home/pingallresponse"; 21 | const char* mqtt_dimlight_get_topic = "home/room/ledstrip"; 22 | const char* mqtt_dimlightbrightness_get_topic = "home/room/ledstrip/brightness"; 23 | const char* mqtt_pingall_get_topic = "home/pingall"; 24 | 25 | // Global 26 | int current_brightness = 100; // LED STRIP OFF (100), LED STRIP ON (0) My dimmer module is driven LOW so 100% is 0, 0% is 100 27 | byte state = 1; 28 | 29 | WiFiClient espClient; 30 | PubSubClient client(espClient); 31 | 32 | void setup_ota() { 33 | 34 | // Set OTA Password, and change it in platformio.ini 35 | ArduinoOTA.setPassword("ESP8266_PASSWORD"); 36 | ArduinoOTA.onStart([]() {}); 37 | ArduinoOTA.onEnd([]() {}); 38 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {}); 39 | ArduinoOTA.onError([](ota_error_t error) { 40 | if (error == OTA_AUTH_ERROR); // Auth failed 41 | else if (error == OTA_BEGIN_ERROR); // Begin failed 42 | else if (error == OTA_CONNECT_ERROR); // Connect failed 43 | else if (error == OTA_RECEIVE_ERROR); // Receive failed 44 | else if (error == OTA_END_ERROR); // End failed 45 | }); 46 | ArduinoOTA.begin(); 47 | 48 | } 49 | 50 | void reconnect() { 51 | 52 | // Loop until we're reconnected 53 | while (!client.connected()) { 54 | 55 | // Create a random client ID 56 | String clientId = "ESP8266Client-"; 57 | clientId += String(random(0xffff), HEX); 58 | // Attempt to connect 59 | 60 | if (client.connect(clientId.c_str())) { 61 | 62 | // Once connected, publish an announcement... 63 | client.publish(mqtt_devicestatus_set_topic, "connected"); 64 | // ... and resubscribe 65 | client.subscribe(mqtt_dimlight_get_topic); 66 | client.subscribe(mqtt_dimlightbrightness_get_topic); 67 | client.subscribe(mqtt_pingall_get_topic); 68 | 69 | } else { 70 | 71 | // Wait 5 seconds before retrying 72 | delay(5000); 73 | 74 | } 75 | 76 | } 77 | 78 | } 79 | 80 | void callback(char* topic, byte* payload, unsigned int length) { 81 | 82 | char c_payload[length]; 83 | memcpy(c_payload, payload, length); 84 | c_payload[length] = '\0'; 85 | 86 | String s_topic = String(topic); 87 | String s_payload = String(c_payload); 88 | 89 | // Handling incoming messages 90 | 91 | if ( s_topic == mqtt_dimlight_get_topic ) { 92 | 93 | if (s_payload == "1") { 94 | 95 | if (state != 1) { 96 | 97 | // Turn ON function will set last known brightness 98 | 99 | client.publish(mqtt_dimlightstatus_set_topic, "1"); 100 | state = 1; 101 | blink(); 102 | 103 | setBrightness(current_brightness); 104 | 105 | 106 | } 107 | 108 | } else if (s_payload == "0") { 109 | 110 | if (state != 0) { 111 | 112 | // Turn OFF function 113 | 114 | client.publish(mqtt_dimlightstatus_set_topic, "0"); 115 | state = 0; 116 | blink(); 117 | 118 | setBrightness(100); 119 | 120 | 121 | } 122 | 123 | } 124 | 125 | } else if ( s_topic == mqtt_dimlightbrightness_get_topic ) { 126 | 127 | int receivedBrightness = s_payload.toInt(); 128 | 129 | if (receivedBrightness <= 100) { 130 | 131 | int brightness = 100-receivedBrightness; // Invert brightness received to reflect that the module I have is driven LOW 132 | 133 | setBrightness(brightness); 134 | client.publish(mqtt_dimlightbrightnessstatus_set_topic,c_payload); 135 | 136 | } 137 | 138 | } else if ( s_topic == mqtt_pingall_get_topic ) { 139 | 140 | client.publish(mqtt_pingallresponse_set_topic, "{\"livingroom_ledstrip\":\"connected\"}"); 141 | blink(); 142 | 143 | } 144 | 145 | 146 | } 147 | 148 | void setBrightness(int newbrightness) { 149 | 150 | // This function will animate brightness change from last known brightness to the new one 151 | // It takes already inverted value so 100 is OFF, 0 is full brightness 152 | 153 | if (newbrightness > current_brightness) { 154 | 155 | for (int i=current_brightness; newbrightness>i; i++) { 156 | 157 | analogWrite(D7, i); 158 | delay(10); 159 | current_brightness = i; 160 | 161 | } 162 | 163 | } else if (newbrightness < current_brightness) { 164 | 165 | for (int i=current_brightness; newbrightness