├── .gitignore ├── ESP RGB LED.fzz ├── ESP RGB LED_bb.png ├── LICENSE ├── DEPRECATED ├── mqtt_esp8266_brightness │ ├── config-sample.h │ └── mqtt_esp8266_brightness.ino ├── mqtt_esp8266_rgb │ ├── config-sample.h │ └── mqtt_esp8266_rgb.ino └── mqtt_esp8266_rgbw │ ├── config-sample.h │ └── mqtt_esp8266_rgbw.ino ├── mqtt_esp8266_light ├── config-sample.h └── mqtt_esp8266_light.ino └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | config.h 2 | -------------------------------------------------------------------------------- /ESP RGB LED.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbanmailloux/esp-mqtt-rgb-led/HEAD/ESP RGB LED.fzz -------------------------------------------------------------------------------- /ESP RGB LED_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corbanmailloux/esp-mqtt-rgb-led/HEAD/ESP RGB LED_bb.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Corban Mailloux 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 | -------------------------------------------------------------------------------- /DEPRECATED/mqtt_esp8266_brightness/config-sample.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a sample configuration file for the "mqtt_esp8266_brightness" light. 3 | * 4 | * Change the settings below and save the file as "config.h" 5 | * You can then upload the code using the Arduino IDE. 6 | */ 7 | 8 | // Pins 9 | #define CONFIG_PIN_LIGHT 0 10 | 11 | // WiFi 12 | #define CONFIG_WIFI_SSID "{WIFI-SSID}" 13 | #define CONFIG_WIFI_PASS "{WIFI-PASSWORD}" 14 | 15 | // MQTT 16 | #define CONFIG_MQTT_HOST "{MQTT-SERVER}" 17 | #define CONFIG_MQTT_USER "{MQTT-USERNAME}" 18 | #define CONFIG_MQTT_PASS "{MQTT-PASSWORD}" 19 | 20 | #define CONFIG_MQTT_CLIENT_ID "ESPBrightnessLED" // Must be unique on the MQTT network 21 | 22 | // MQTT Topics 23 | #define CONFIG_MQTT_TOPIC_STATE "home/brightness1" 24 | #define CONFIG_MQTT_TOPIC_SET "home/brightness1/set" 25 | 26 | #define CONFIG_MQTT_PAYLOAD_ON "ON" 27 | #define CONFIG_MQTT_PAYLOAD_OFF "OFF" 28 | 29 | // Miscellaneous 30 | // Default number of flashes if no value was given 31 | #define CONFIG_DEFAULT_FLASH_LENGTH 2 32 | 33 | // Reverse the LED logic 34 | // false: 0 (off) - 255 (bright) 35 | // true: 255 (off) - 0 (bright) 36 | #define CONFIG_INVERT_LED_LOGIC false 37 | 38 | // Enables Serial and print statements 39 | #define CONFIG_DEBUG false 40 | -------------------------------------------------------------------------------- /DEPRECATED/mqtt_esp8266_rgb/config-sample.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a sample configuration file for the "mqtt_esp8266_rgb" light. 3 | * 4 | * Change the settings below and save the file as "config.h" 5 | * You can then upload the code using the Arduino IDE. 6 | */ 7 | 8 | // Pins 9 | #define CONFIG_PIN_RED 0 10 | #define CONFIG_PIN_GREEN 2 11 | #define CONFIG_PIN_BLUE 3 12 | 13 | // WiFi 14 | #define CONFIG_WIFI_SSID "{WIFI-SSID}" 15 | #define CONFIG_WIFI_PASS "{WIFI-PASSWORD}" 16 | 17 | // MQTT 18 | #define CONFIG_MQTT_HOST "{MQTT-SERVER}" 19 | #define CONFIG_MQTT_USER "{MQTT-USERNAME}" 20 | #define CONFIG_MQTT_PASS "{MQTT-PASSWORD}" 21 | 22 | #define CONFIG_MQTT_CLIENT_ID "ESPRGBLED" // Must be unique on the MQTT network 23 | 24 | // MQTT Topics 25 | #define CONFIG_MQTT_TOPIC_STATE "home/rgb1" 26 | #define CONFIG_MQTT_TOPIC_SET "home/rgb1/set" 27 | 28 | #define CONFIG_MQTT_PAYLOAD_ON "ON" 29 | #define CONFIG_MQTT_PAYLOAD_OFF "OFF" 30 | 31 | // Miscellaneous 32 | // Default number of flashes if no value was given 33 | #define CONFIG_DEFAULT_FLASH_LENGTH 2 34 | // Number of seconds for one transition in colorfade mode 35 | #define CONFIG_COLORFADE_TIME_SLOW 10 36 | #define CONFIG_COLORFADE_TIME_FAST 3 37 | 38 | // Reverse the LED logic 39 | // false: 0 (off) - 255 (bright) 40 | // true: 255 (off) - 0 (bright) 41 | #define CONFIG_INVERT_LED_LOGIC false 42 | 43 | // Enables Serial and print statements 44 | #define CONFIG_DEBUG false 45 | -------------------------------------------------------------------------------- /DEPRECATED/mqtt_esp8266_rgbw/config-sample.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a sample configuration file for the "mqtt_esp8266_rgbw" light. 3 | * 4 | * Change the settings below and save the file as "config.h" 5 | * You can then upload the code using the Arduino IDE. 6 | */ 7 | 8 | // Pins 9 | #define CONFIG_PIN_RED 0 10 | #define CONFIG_PIN_GREEN 2 11 | #define CONFIG_PIN_BLUE 3 12 | #define CONFIG_PIN_WHITE 4 13 | 14 | // WiFi 15 | #define CONFIG_WIFI_SSID "{WIFI-SSID}" 16 | #define CONFIG_WIFI_PASS "{WIFI-PASSWORD}" 17 | 18 | // MQTT 19 | #define CONFIG_MQTT_HOST "{MQTT-SERVER}" 20 | #define CONFIG_MQTT_USER "{MQTT-USERNAME}" 21 | #define CONFIG_MQTT_PASS "{MQTT-PASSWORD}" 22 | 23 | #define CONFIG_MQTT_CLIENT_ID "ESPRGBWLED" // Must be unique on the MQTT network 24 | 25 | // MQTT Topics 26 | #define CONFIG_MQTT_TOPIC_STATE "home/rgbw1" 27 | #define CONFIG_MQTT_TOPIC_SET "home/rgbw1/set" 28 | 29 | #define CONFIG_MQTT_PAYLOAD_ON "ON" 30 | #define CONFIG_MQTT_PAYLOAD_OFF "OFF" 31 | 32 | // Miscellaneous 33 | // Default number of flashes if no value was given 34 | #define CONFIG_DEFAULT_FLASH_LENGTH 2 35 | // Number of seconds for one transition in colorfade mode 36 | #define CONFIG_COLORFADE_TIME_SLOW 10 37 | #define CONFIG_COLORFADE_TIME_FAST 3 38 | 39 | // Reverse the LED logic 40 | // false: 0 (off) - 255 (bright) 41 | // true: 255 (off) - 0 (bright) 42 | #define CONFIG_INVERT_LED_LOGIC false 43 | 44 | // Enables Serial and print statements 45 | #define CONFIG_DEBUG false 46 | -------------------------------------------------------------------------------- /mqtt_esp8266_light/config-sample.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a sample configuration file for the "mqtt_esp8266" light. 3 | * 4 | * Change the settings below and save the file as "config.h" 5 | * You can then upload the code using the Arduino IDE. 6 | */ 7 | 8 | // Leave this here. These are the choices for CONFIG_STRIP below. 9 | enum strip { 10 | BRIGHTNESS, // only one color/only white 11 | RGB, // RGB LEDs 12 | RGBW // RGB LEDs with an extra white LED per LED 13 | }; 14 | 15 | #define CONFIG_STRIP RGB // Choose one of the options from above. 16 | 17 | // Pins 18 | // In case of BRIGHTNESS: only WHITE is used 19 | // In case of RGB(W): red, green, blue(, white) is used 20 | // All values need to be present, if they are not needed, set to -1, 21 | // it will be ignored. 22 | #define CONFIG_PIN_RED 0 // For RGB(W) 23 | #define CONFIG_PIN_GREEN 2 // For RGB(W) 24 | #define CONFIG_PIN_BLUE 3 // For RGB(W) 25 | #define CONFIG_PIN_WHITE -1 // For BRIGHTNESS and RGBW 26 | 27 | // WiFi 28 | #define CONFIG_WIFI_SSID "{WIFI-SSID}" 29 | #define CONFIG_WIFI_PASS "{WIFI-PASSWORD}" 30 | 31 | // MQTT 32 | #define CONFIG_MQTT_HOST "{MQTT-SERVER}" 33 | #define CONFIG_MQTT_PORT 1883 // Usually 1883 34 | #define CONFIG_MQTT_USER "{MQTT-USERNAME}" 35 | #define CONFIG_MQTT_PASS "{MQTT-PASSWORD}" 36 | #define CONFIG_MQTT_CLIENT_ID "ESP_LED" // Must be unique on the MQTT network 37 | 38 | #define CONFIG_DEFAULT_TRANSITION_TIME 1 39 | 40 | // MQTT Topics 41 | #define CONFIG_MQTT_TOPIC_STATE "home/ESP_LED" 42 | #define CONFIG_MQTT_TOPIC_SET "home/ESP_LED/set" 43 | 44 | #define CONFIG_MQTT_PAYLOAD_ON "ON" 45 | #define CONFIG_MQTT_PAYLOAD_OFF "OFF" 46 | 47 | // Miscellaneous 48 | // Default number of flashes if no value was given 49 | #define CONFIG_DEFAULT_FLASH_LENGTH 2 50 | // Number of seconds for one transition in colorfade mode 51 | #define CONFIG_COLORFADE_TIME_SLOW 10 52 | #define CONFIG_COLORFADE_TIME_FAST 3 53 | 54 | // Reverse the LED logic 55 | // false: 0 (off) - 255 (bright) 56 | // true: 255 (off) - 0 (bright) 57 | #define CONFIG_INVERT_LED_LOGIC false 58 | 59 | // Set the mode for the built-in LED on some boards. 60 | // -1 = Do nothing. Leave the pin in its default state. 61 | // 0 = Explicitly set the LED_BUILTIN to LOW. 62 | // 1 = Explicitly set the LED_BUILTIN to HIGH. (Off for Wemos D1 Mini) 63 | #define CONFIG_LED_BUILTIN_MODE -1 64 | 65 | // Enables Serial and print statements 66 | #define CONFIG_DEBUG false 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecation Notice 2 | This project served a need originally, but I no longer use or maintain it. I use and highly recommend [ESPHome](https://esphome.io/), which supports everything this project can handle and much, much more. 3 | ESPHome has a local API with a native integration with Home Assistant, but can also be used separately from Home Assistant using [the MQTT component](https://esphome.io/components/mqtt). 4 | 5 | As an example, this is an initial configuration you could use in ESPHome to replace this whole project for an RGB light using MQTT: 6 | ```yaml 7 | esphome: 8 | name: rgb-light # Update this. 9 | 10 | esp8266: 11 | # Update to match your board. See: https://esphome.io/components/esp8266.html 12 | board: d1_mini 13 | 14 | wifi: 15 | ssid: wifi_ssid # Update this (ideally using !secrets) 16 | password: wifi_password # Update this (ideally using !secrets) 17 | 18 | mqtt: 19 | broker: 192.168.1.2 # Update this. 20 | username: username # Update this (ideally using !secrets) 21 | password: password # Update this (ideally using !secrets) 22 | 23 | logger: 24 | ota: 25 | 26 | output: 27 | - platform: esp8266_pwm 28 | id: red_pin 29 | pin: GPIO0 # Update this. 30 | - platform: esp8266_pwm 31 | id: green_pin 32 | pin: GPIO2 # Update this. 33 | - platform: esp8266_pwm 34 | id: blue_pin 35 | pin: GPIO3 # Update this. 36 | 37 | light: 38 | - platform: rgb 39 | name: "RGB Light" 40 | red: red_pin 41 | green: green_pin 42 | blue: blue_pin 43 | ``` 44 | 45 | See the relevant light components for more help: 46 | - [Monochromatic Light](https://esphome.io/components/light/monochromatic) 47 | - [RGB Light](https://esphome.io/components/light/rgb) 48 | - [RGBW Light](https://esphome.io/components/light/rgbw) 49 | 50 | Thank you for using this repo and providing feedback. Going forward, I've moved some of my development attention to supporting ESPHome. 51 | 52 | Previous README below. 53 | 54 | ---- 55 | 56 | # ESP8266 MQTT RGB(W) Lights Using JSON for Home Assistant 57 | 58 | This project adds an easy way to create DIY lighting for [Home Assistant](https://home-assistant.io/), an amazing, extensible, open-source home automation system. 59 | 60 | I was frustrated that the built-in MQTT light didn't support transitions (fading between colors/brightnesses), and that it uses multiple separate calls to set the different values (state (on/off), brightness, color), so I decided to make my own version. As of version 0.26, the [MQTT JSON light platform](https://home-assistant.io/components/light.mqtt_json/) has been merged into Home Assistant. 61 | 62 | By sending a JSON payload (in an MQTT message), Home Assistant can include whichever fields are necessary, reducing the round trips from 3 to 1. For example, this is a sample payload including most of the fields: 63 | ```json 64 | { 65 | "state": "ON", 66 | "brightness": 120, 67 | "color": { 68 | "r": 255, 69 | "g": 100, 70 | "b": 100 71 | }, 72 | "transition": 5 73 | } 74 | ``` 75 | 76 | ## Installation/Configuration 77 | 78 | To set this system up, you need to configure the [MQTT light](https://www.home-assistant.io/components/light.mqtt/#json-schema) component in Home Assistant and set up a light to control. This guide assumes that you already have Home Assistant set up and running. If not, see the installation guides [here](https://home-assistant.io/getting-started/). 79 | 80 | ### The Home Assistant Side 81 | 1. In your `configuration.yaml`, add the following, depending on the supported features of the light: 82 | 83 | ```yaml 84 | # Only one color: 85 | light: 86 | - platform: mqtt 87 | schema: json 88 | name: mqtt_json_light_1 89 | state_topic: "home/json_brightness" 90 | command_topic: "home/json_brightness/set" 91 | brightness: true 92 | effect: true 93 | effect_list: [flash] 94 | optimistic: false 95 | qos: 0 96 | 97 | # RGB: 98 | light: 99 | - platform: mqtt 100 | schema: json 101 | name: mqtt_json_light_2 102 | state_topic: "home/rgb1" 103 | command_topic: "home/rgb1/set" 104 | brightness: true 105 | rgb: true 106 | effect: true 107 | effect_list: [colorfade_slow, colorfade_fast, flash] 108 | optimistic: false 109 | qos: 0 110 | 111 | # RGBW: 112 | light: 113 | - platform: mqtt 114 | schema: json 115 | name: mqtt_json_light_3 116 | state_topic: "home/rgbw1" 117 | command_topic: "home/rgbw1/set" 118 | brightness: true 119 | rgb: true 120 | white_value: true 121 | effect: true 122 | effect_list: [colorfade_slow, colorfade_fast, flash] 123 | optimistic: false 124 | qos: 0 125 | ``` 126 | 2. Set the `name`, `state_topic`, and `command_topic` to values that make sense for you. 127 | 3. Restart Home Assistant. Depending on how you installed it, the process differs. For a Raspberry Pi All-in-One install, use `sudo systemctl restart home-assistant.service` (or just restart the Pi). 128 | 129 | ### The Light Side 130 | I'm using ESP8266-01 microcontrollers for my lights because they are so cheap and small. The downside of the size and price is that programming them can be a bit of a hassle. There are many sites that go into detail, so I won't do it here. You'll need an ESP set up to work with the Arduino IDE. See the readme [here](https://github.com/esp8266/Arduino) for instructions. Another good device to work with is the [Wemos D1 Mini](https://wiki.wemos.cc/products:d1:d1_mini), which has a built-in micro-USB port and is much easier to program. 131 | 132 | 1. Using the Library Manager in the Arduino IDE, install [ArduinoJSON](https://github.com/bblanchon/ArduinoJson/) and [PubSubClient](http://pubsubclient.knolleary.net/). You can find the Library Manager in the "Sketch" menu under "Include Library" -> "Manage Libraries..." 133 | * **NOTE:**: At the moment, this project only supports ArduinoJSON version 5. The default is now version 6, so you'll have to specify the version to install in the dropdown in the Library Manager window. 134 | 2. Open the `mqtt_esp8266_light` project in the Arduino IDE. 135 | 3. Update the `config-sample.h` file with your settings for LED type, pin numbers, WiFi settings, and MQTT settings. 136 | 1. Review the comments to help with the options. For the `CONFIG_STRIP` option, choose one of `BRIGHTNESS`, `RGB`, or `RGBW`. 137 | 2. Ensure that the `CONFIG_MQTT_CLIENT_ID` setting is a unique value for your network. 138 | 3. Set `CONFIG_MQTT_TOPIC_STATE` and `CONFIG_MQTT_TOPIC_SET` to match the values you put in your `configuration.yaml`. 139 | 4. Save the configuration file as `config.h`. 140 | 5. Open the `.ino` file in the Arduino IDE and upload to an ESP with the correct connections. 141 | 142 | #### About the `DEPRECATED` Folder 143 | Originally, there were separate Arduino sketches for each of the supported light types. This quickly became redundant and hard-to-maintain, so the new, unified sketch was created. 144 | 145 | Please use this (`mqtt_esp8266_light`) sketch going forward. The deprecated sketches will not receive new features and will be removed from the project in the future. 146 | 147 | #### Wiring 148 | For an RGB LED strip using N-MOSFETs for control, you'll want to wire it similar to this: 149 | ![RGB Wiring](https://raw.githubusercontent.com/corbanmailloux/esp-mqtt-rgb-led/master/ESP%20RGB%20LED_bb.png) 150 | 151 | Note that the MOSFETs have pull-**up** resistors in this setup. This means that the lights may flash on when the module resets, but it was necessary to keep the ESP's pins in the right start state. 152 | -------------------------------------------------------------------------------- /DEPRECATED/mqtt_esp8266_brightness/mqtt_esp8266_brightness.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * ESP8266 MQTT Lights for Home Assistant. 3 | * 4 | * This file is for single-color lights. 5 | * 6 | * See https://github.com/corbanmailloux/esp-mqtt-rgb-led 7 | */ 8 | 9 | // Set configuration options for pins, WiFi, and MQTT in the following file: 10 | #include "config.h" 11 | 12 | // https://github.com/bblanchon/ArduinoJson 13 | #include 14 | 15 | #include 16 | 17 | // http://pubsubclient.knolleary.net/ 18 | #include 19 | 20 | const bool debug_mode = CONFIG_DEBUG; 21 | const bool led_invert = CONFIG_INVERT_LED_LOGIC; 22 | 23 | const int redPin = CONFIG_PIN_LIGHT; 24 | const int txPin = BUILTIN_LED; // On-board blue LED 25 | // const int greenPin = 2; 26 | // const int bluePin = 3; 27 | 28 | const char* ssid = CONFIG_WIFI_SSID; 29 | const char* password = CONFIG_WIFI_PASS; 30 | 31 | const char* mqtt_server = CONFIG_MQTT_HOST; 32 | const char* mqtt_username = CONFIG_MQTT_USER; 33 | const char* mqtt_password = CONFIG_MQTT_PASS; 34 | const char* client_id = CONFIG_MQTT_CLIENT_ID; 35 | 36 | // Topics 37 | const char* light_state_topic = CONFIG_MQTT_TOPIC_STATE; 38 | const char* light_set_topic = CONFIG_MQTT_TOPIC_SET; 39 | 40 | const char* on_cmd = CONFIG_MQTT_PAYLOAD_ON; 41 | const char* off_cmd = CONFIG_MQTT_PAYLOAD_OFF; 42 | 43 | const int BUFFER_SIZE = JSON_OBJECT_SIZE(8); 44 | 45 | // Maintained state for reporting to HA 46 | byte red = 255; 47 | // byte green = 255; 48 | // byte blue = 255; 49 | byte brightness = 255; 50 | 51 | // Real values to write to the LEDs (ex. including brightness and state) 52 | byte realRed = 0; 53 | // byte realGreen = 0; 54 | // byte realBlue = 0; 55 | 56 | bool stateOn = false; 57 | 58 | // Globals for fade/transitions 59 | bool startFade = false; 60 | unsigned long lastLoop = 0; 61 | int transitionTime = 0; 62 | bool inFade = false; 63 | int loopCount = 0; 64 | int stepR; //, stepG, stepB; 65 | int redVal; //, grnVal, bluVal; 66 | 67 | // Globals for flash 68 | bool flash = false; 69 | bool startFlash = false; 70 | int flashLength = 0; 71 | unsigned long flashStartTime = 0; 72 | byte flashRed = red; 73 | // byte flashGreen = green; 74 | // byte flashBlue = blue; 75 | byte flashBrightness = brightness; 76 | 77 | WiFiClient espClient; 78 | PubSubClient client(espClient); 79 | 80 | void setup() { 81 | pinMode(redPin, OUTPUT); 82 | // pinMode(greenPin, OUTPUT); 83 | // pinMode(bluePin, OUTPUT); 84 | 85 | pinMode(txPin, OUTPUT); 86 | digitalWrite(txPin, HIGH); // Turn off the on-board LED 87 | 88 | analogWriteRange(255); 89 | 90 | if (debug_mode) { 91 | Serial.begin(115200); 92 | } 93 | 94 | setup_wifi(); 95 | client.setServer(mqtt_server, 1883); 96 | client.setCallback(callback); 97 | } 98 | 99 | void setup_wifi() { 100 | delay(10); 101 | // We start by connecting to a WiFi network 102 | Serial.println(); 103 | Serial.print("Connecting to "); 104 | Serial.println(ssid); 105 | 106 | WiFi.mode(WIFI_STA); 107 | WiFi.begin(ssid, password); 108 | 109 | while (WiFi.status() != WL_CONNECTED) { 110 | delay(500); 111 | Serial.print("."); 112 | } 113 | 114 | Serial.println(""); 115 | Serial.println("WiFi connected"); 116 | Serial.println("IP address: "); 117 | Serial.println(WiFi.localIP()); 118 | } 119 | 120 | /* 121 | SAMPLE PAYLOAD: 122 | { 123 | "brightness": 120, 124 | "flash": 2, 125 | "transition": 5, 126 | "state": "ON" 127 | } 128 | */ 129 | void callback(char* topic, byte* payload, unsigned int length) { 130 | Serial.print("Message arrived ["); 131 | Serial.print(topic); 132 | Serial.print("] "); 133 | 134 | char message[length + 1]; 135 | for (int i = 0; i < length; i++) { 136 | message[i] = (char)payload[i]; 137 | } 138 | message[length] = '\0'; 139 | Serial.println(message); 140 | 141 | if (!processJson(message)) { 142 | return; 143 | } 144 | 145 | if (stateOn) { 146 | // Update lights 147 | realRed = map(red, 0, 255, 0, brightness); 148 | // realGreen = map(green, 0, 255, 0, brightness); 149 | // realBlue = map(blue, 0, 255, 0, brightness); 150 | } 151 | else { 152 | realRed = 0; 153 | // realGreen = 0; 154 | // realBlue = 0; 155 | } 156 | 157 | startFade = true; 158 | inFade = false; // Kill the current fade 159 | 160 | sendState(); 161 | } 162 | 163 | bool processJson(char* message) { 164 | StaticJsonBuffer jsonBuffer; 165 | 166 | JsonObject& root = jsonBuffer.parseObject(message); 167 | 168 | if (!root.success()) { 169 | Serial.println("parseObject() failed"); 170 | return false; 171 | } 172 | 173 | if (root.containsKey("state")) { 174 | if (strcmp(root["state"], on_cmd) == 0) { 175 | stateOn = true; 176 | } 177 | else if (strcmp(root["state"], off_cmd) == 0) { 178 | stateOn = false; 179 | } 180 | } 181 | 182 | // If "flash" is included, treat RGB and brightness differently 183 | if (root.containsKey("flash") || 184 | (root.containsKey("effect") && strcmp(root["effect"], "flash") == 0)) { 185 | 186 | if (root.containsKey("flash")) { 187 | flashLength = (int)root["flash"] * 1000; 188 | } else { 189 | flashLength = CONFIG_DEFAULT_FLASH_LENGTH * 1000; 190 | } 191 | 192 | if (root.containsKey("brightness")) { 193 | flashBrightness = root["brightness"]; 194 | } 195 | else { 196 | flashBrightness = brightness; 197 | } 198 | 199 | // if (root.containsKey("color")) { 200 | // flashRed = root["color"]["r"]; 201 | // flashGreen = root["color"]["g"]; 202 | // flashBlue = root["color"]["b"]; 203 | // } 204 | // else { 205 | // flashRed = red; 206 | // flashGreen = green; 207 | // flashBlue = blue; 208 | // } 209 | 210 | flashRed = map(flashRed, 0, 255, 0, flashBrightness); 211 | // flashGreen = map(flashGreen, 0, 255, 0, flashBrightness); 212 | // flashBlue = map(flashBlue, 0, 255, 0, flashBrightness); 213 | 214 | flash = true; 215 | startFlash = true; 216 | } 217 | else { // Not flashing 218 | flash = false; 219 | 220 | // if (root.containsKey("color")) { 221 | // red = root["color"]["r"]; 222 | // green = root["color"]["g"]; 223 | // blue = root["color"]["b"]; 224 | // } 225 | 226 | if (root.containsKey("brightness")) { 227 | brightness = root["brightness"]; 228 | } 229 | 230 | if (root.containsKey("transition")) { 231 | transitionTime = root["transition"]; 232 | } 233 | else { 234 | transitionTime = 0; 235 | } 236 | } 237 | 238 | return true; 239 | } 240 | 241 | void sendState() { 242 | StaticJsonBuffer jsonBuffer; 243 | 244 | JsonObject& root = jsonBuffer.createObject(); 245 | 246 | root["state"] = (stateOn) ? on_cmd : off_cmd; 247 | // JsonObject& color = root.createNestedObject("color"); 248 | // color["r"] = red; 249 | // color["g"] = green; 250 | // color["b"] = blue; 251 | 252 | root["brightness"] = brightness; 253 | 254 | char buffer[root.measureLength() + 1]; 255 | root.printTo(buffer, sizeof(buffer)); 256 | 257 | client.publish(light_state_topic, buffer, true); 258 | } 259 | 260 | void reconnect() { 261 | // Loop until we're reconnected 262 | while (!client.connected()) { 263 | Serial.print("Attempting MQTT connection..."); 264 | // Attempt to connect 265 | if (client.connect(client_id, mqtt_username, mqtt_password)) { 266 | Serial.println("connected"); 267 | client.subscribe(light_set_topic); 268 | } else { 269 | Serial.print("failed, rc="); 270 | Serial.print(client.state()); 271 | Serial.println(" try again in 5 seconds"); 272 | // Wait 5 seconds before retrying 273 | delay(5000); 274 | } 275 | } 276 | } 277 | 278 | void setColor(int inR) { //, int inG, int inB) { 279 | if (led_invert) { 280 | inR = (255 - inR); 281 | } 282 | 283 | analogWrite(redPin, inR); 284 | // analogWrite(greenPin, inG); 285 | // analogWrite(bluePin, inB); 286 | 287 | Serial.println("Setting LEDs:"); 288 | // Serial.print("r: "); 289 | Serial.println(inR); 290 | // Serial.print(", g: "); 291 | // Serial.print(inG); 292 | // Serial.print(", b: "); 293 | // Serial.println(inB); 294 | } 295 | 296 | void loop() { 297 | 298 | if (!client.connected()) { 299 | reconnect(); 300 | } 301 | client.loop(); 302 | 303 | if (flash) { 304 | if (startFlash) { 305 | startFlash = false; 306 | flashStartTime = millis(); 307 | } 308 | 309 | if ((millis() - flashStartTime) <= flashLength) { 310 | if ((millis() - flashStartTime) % 1000 <= 500) { 311 | setColor(flashRed); //, flashGreen, flashBlue); 312 | } 313 | else { 314 | setColor(0); //, 0, 0); 315 | // If you'd prefer the flashing to happen "on top of" 316 | // the current color, uncomment the next line. 317 | // setColor(realRed, realGreen, realBlue); 318 | } 319 | } 320 | else { 321 | flash = false; 322 | setColor(realRed); //, realGreen, realBlue); 323 | } 324 | } 325 | 326 | if (startFade) { 327 | // If we don't want to fade, skip it. 328 | if (transitionTime == 0) { 329 | setColor(realRed); //, realGreen, realBlue); 330 | 331 | redVal = realRed; 332 | // grnVal = realGreen; 333 | // bluVal = realBlue; 334 | 335 | startFade = false; 336 | } 337 | else { 338 | loopCount = 0; 339 | stepR = calculateStep(redVal, realRed); 340 | // stepG = calculateStep(grnVal, realGreen); 341 | // stepB = calculateStep(bluVal, realBlue); 342 | 343 | inFade = true; 344 | } 345 | } 346 | 347 | if (inFade) { 348 | startFade = false; 349 | unsigned long now = millis(); 350 | if (now - lastLoop > transitionTime) { 351 | if (loopCount <= 1020) { 352 | lastLoop = now; 353 | 354 | redVal = calculateVal(stepR, redVal, loopCount); 355 | // grnVal = calculateVal(stepG, grnVal, loopCount); 356 | // bluVal = calculateVal(stepB, bluVal, loopCount); 357 | 358 | setColor(redVal); //, grnVal, bluVal); // Write current values to LED pins 359 | 360 | Serial.print("Loop count: "); 361 | Serial.println(loopCount); 362 | loopCount++; 363 | } 364 | else { 365 | inFade = false; 366 | } 367 | } 368 | } 369 | } 370 | 371 | // From https://www.arduino.cc/en/Tutorial/ColorCrossfader 372 | /* BELOW THIS LINE IS THE MATH -- YOU SHOULDN'T NEED TO CHANGE THIS FOR THE BASICS 373 | * 374 | * The program works like this: 375 | * Imagine a crossfade that moves the red LED from 0-10, 376 | * the green from 0-5, and the blue from 10 to 7, in 377 | * ten steps. 378 | * We'd want to count the 10 steps and increase or 379 | * decrease color values in evenly stepped increments. 380 | * Imagine a + indicates raising a value by 1, and a - 381 | * equals lowering it. Our 10 step fade would look like: 382 | * 383 | * 1 2 3 4 5 6 7 8 9 10 384 | * R + + + + + + + + + + 385 | * G + + + + + 386 | * B - - - 387 | * 388 | * The red rises from 0 to 10 in ten steps, the green from 389 | * 0-5 in 5 steps, and the blue falls from 10 to 7 in three steps. 390 | * 391 | * In the real program, the color percentages are converted to 392 | * 0-255 values, and there are 1020 steps (255*4). 393 | * 394 | * To figure out how big a step there should be between one up- or 395 | * down-tick of one of the LED values, we call calculateStep(), 396 | * which calculates the absolute gap between the start and end values, 397 | * and then divides that gap by 1020 to determine the size of the step 398 | * between adjustments in the value. 399 | */ 400 | int calculateStep(int prevValue, int endValue) { 401 | int step = endValue - prevValue; // What's the overall gap? 402 | if (step) { // If its non-zero, 403 | step = 1020/step; // divide by 1020 404 | } 405 | 406 | return step; 407 | } 408 | 409 | /* The next function is calculateVal. When the loop value, i, 410 | * reaches the step size appropriate for one of the 411 | * colors, it increases or decreases the value of that color by 1. 412 | * (R, G, and B are each calculated separately.) 413 | */ 414 | int calculateVal(int step, int val, int i) { 415 | if ((step) && i % step == 0) { // If step is non-zero and its time to change a value, 416 | if (step > 0) { // increment the value if step is positive... 417 | val += 1; 418 | } 419 | else if (step < 0) { // ...or decrement it if step is negative 420 | val -= 1; 421 | } 422 | } 423 | 424 | // Defensive driving: make sure val stays in the range 0-255 425 | if (val > 255) { 426 | val = 255; 427 | } 428 | else if (val < 0) { 429 | val = 0; 430 | } 431 | 432 | return val; 433 | } 434 | -------------------------------------------------------------------------------- /DEPRECATED/mqtt_esp8266_rgb/mqtt_esp8266_rgb.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * ESP8266 MQTT Lights for Home Assistant. 3 | * 4 | * This file is for RGB (red, green, and blue) lights. 5 | * 6 | * See https://github.com/corbanmailloux/esp-mqtt-rgb-led 7 | */ 8 | 9 | // Set configuration options for pins, WiFi, and MQTT in the following file: 10 | #include "config.h" 11 | 12 | // https://github.com/bblanchon/ArduinoJson 13 | #include 14 | 15 | #include 16 | 17 | // http://pubsubclient.knolleary.net/ 18 | #include 19 | 20 | const bool debug_mode = CONFIG_DEBUG; 21 | const bool led_invert = CONFIG_INVERT_LED_LOGIC; 22 | 23 | const int redPin = CONFIG_PIN_RED; 24 | const int txPin = BUILTIN_LED; // On-board blue LED 25 | const int greenPin = CONFIG_PIN_GREEN; 26 | const int bluePin = CONFIG_PIN_BLUE; 27 | 28 | const char* ssid = CONFIG_WIFI_SSID; 29 | const char* password = CONFIG_WIFI_PASS; 30 | 31 | const char* mqtt_server = CONFIG_MQTT_HOST; 32 | const char* mqtt_username = CONFIG_MQTT_USER; 33 | const char* mqtt_password = CONFIG_MQTT_PASS; 34 | const char* client_id = CONFIG_MQTT_CLIENT_ID; 35 | 36 | // Topics 37 | const char* light_state_topic = CONFIG_MQTT_TOPIC_STATE; 38 | const char* light_set_topic = CONFIG_MQTT_TOPIC_SET; 39 | 40 | const char* on_cmd = CONFIG_MQTT_PAYLOAD_ON; 41 | const char* off_cmd = CONFIG_MQTT_PAYLOAD_OFF; 42 | 43 | const int BUFFER_SIZE = JSON_OBJECT_SIZE(15); 44 | 45 | // Maintained state for reporting to HA 46 | byte red = 255; 47 | byte green = 255; 48 | byte blue = 255; 49 | byte brightness = 255; 50 | 51 | // Real values to write to the LEDs (ex. including brightness and state) 52 | byte realRed = 0; 53 | byte realGreen = 0; 54 | byte realBlue = 0; 55 | 56 | bool stateOn = false; 57 | 58 | // Globals for fade/transitions 59 | bool startFade = false; 60 | unsigned long lastLoop = 0; 61 | int transitionTime = 0; 62 | bool inFade = false; 63 | int loopCount = 0; 64 | int stepR, stepG, stepB; 65 | int redVal, grnVal, bluVal; 66 | 67 | // Globals for flash 68 | bool flash = false; 69 | bool startFlash = false; 70 | int flashLength = 0; 71 | unsigned long flashStartTime = 0; 72 | byte flashRed = red; 73 | byte flashGreen = green; 74 | byte flashBlue = blue; 75 | byte flashBrightness = brightness; 76 | 77 | // Globals for colorfade 78 | bool colorfade = false; 79 | int currentColor = 0; 80 | // {red, grn, blu} 81 | const byte colors[][3] = { 82 | {255, 0, 0}, 83 | {0, 255, 0}, 84 | {0, 0, 255}, 85 | {255, 80, 0}, 86 | {163, 0, 255}, 87 | {0, 255, 255}, 88 | {255, 255, 0} 89 | }; 90 | const int numColors = 7; 91 | 92 | WiFiClient espClient; 93 | PubSubClient client(espClient); 94 | 95 | void setup() { 96 | pinMode(redPin, OUTPUT); 97 | pinMode(greenPin, OUTPUT); 98 | pinMode(bluePin, OUTPUT); 99 | 100 | pinMode(txPin, OUTPUT); 101 | digitalWrite(txPin, HIGH); // Turn off the on-board LED 102 | 103 | analogWriteRange(255); 104 | 105 | if (debug_mode) { 106 | Serial.begin(115200); 107 | } 108 | 109 | setup_wifi(); 110 | client.setServer(mqtt_server, 1883); 111 | client.setCallback(callback); 112 | } 113 | 114 | void setup_wifi() { 115 | 116 | delay(10); 117 | // We start by connecting to a WiFi network 118 | Serial.println(); 119 | Serial.print("Connecting to "); 120 | Serial.println(ssid); 121 | 122 | WiFi.mode(WIFI_STA); 123 | WiFi.begin(ssid, password); 124 | 125 | while (WiFi.status() != WL_CONNECTED) { 126 | delay(500); 127 | Serial.print("."); 128 | } 129 | 130 | Serial.println(""); 131 | Serial.println("WiFi connected"); 132 | Serial.println("IP address: "); 133 | Serial.println(WiFi.localIP()); 134 | } 135 | 136 | /* 137 | SAMPLE PAYLOAD: 138 | { 139 | "brightness": 120, 140 | "color": { 141 | "r": 255, 142 | "g": 100, 143 | "b": 100 144 | }, 145 | "flash": 2, 146 | "transition": 5, 147 | "state": "ON", 148 | "effect": "colorfade_fast" 149 | } 150 | */ 151 | void callback(char* topic, byte* payload, unsigned int length) { 152 | Serial.print("Message arrived ["); 153 | Serial.print(topic); 154 | Serial.print("] "); 155 | 156 | char message[length + 1]; 157 | for (int i = 0; i < length; i++) { 158 | message[i] = (char)payload[i]; 159 | } 160 | message[length] = '\0'; 161 | Serial.println(message); 162 | 163 | if (!processJson(message)) { 164 | return; 165 | } 166 | 167 | if (stateOn) { 168 | // Update lights 169 | realRed = map(red, 0, 255, 0, brightness); 170 | realGreen = map(green, 0, 255, 0, brightness); 171 | realBlue = map(blue, 0, 255, 0, brightness); 172 | } 173 | else { 174 | realRed = 0; 175 | realGreen = 0; 176 | realBlue = 0; 177 | } 178 | 179 | startFade = true; 180 | inFade = false; // Kill the current fade 181 | 182 | sendState(); 183 | } 184 | 185 | bool processJson(char* message) { 186 | StaticJsonBuffer jsonBuffer; 187 | 188 | JsonObject& root = jsonBuffer.parseObject(message); 189 | 190 | if (!root.success()) { 191 | Serial.println("parseObject() failed"); 192 | return false; 193 | } 194 | 195 | if (root.containsKey("state")) { 196 | if (strcmp(root["state"], on_cmd) == 0) { 197 | stateOn = true; 198 | } 199 | else if (strcmp(root["state"], off_cmd) == 0) { 200 | stateOn = false; 201 | } 202 | } 203 | 204 | // If "flash" is included, treat RGB and brightness differently 205 | if (root.containsKey("flash") || 206 | (root.containsKey("effect") && strcmp(root["effect"], "flash") == 0)) { 207 | 208 | if (root.containsKey("flash")) { 209 | flashLength = (int)root["flash"] * 1000; 210 | } 211 | else { 212 | flashLength = CONFIG_DEFAULT_FLASH_LENGTH * 1000; 213 | } 214 | 215 | if (root.containsKey("brightness")) { 216 | flashBrightness = root["brightness"]; 217 | } 218 | else { 219 | flashBrightness = brightness; 220 | } 221 | 222 | if (root.containsKey("color")) { 223 | flashRed = root["color"]["r"]; 224 | flashGreen = root["color"]["g"]; 225 | flashBlue = root["color"]["b"]; 226 | } 227 | else { 228 | flashRed = red; 229 | flashGreen = green; 230 | flashBlue = blue; 231 | } 232 | 233 | flashRed = map(flashRed, 0, 255, 0, flashBrightness); 234 | flashGreen = map(flashGreen, 0, 255, 0, flashBrightness); 235 | flashBlue = map(flashBlue, 0, 255, 0, flashBrightness); 236 | 237 | flash = true; 238 | startFlash = true; 239 | } 240 | else if (root.containsKey("effect") && 241 | (strcmp(root["effect"], "colorfade_slow") == 0 || strcmp(root["effect"], "colorfade_fast") == 0)) { 242 | flash = false; 243 | colorfade = true; 244 | currentColor = 0; 245 | if (strcmp(root["effect"], "colorfade_slow") == 0) { 246 | transitionTime = CONFIG_COLORFADE_TIME_SLOW; 247 | } 248 | else { 249 | transitionTime = CONFIG_COLORFADE_TIME_FAST; 250 | } 251 | } 252 | else if (colorfade && !root.containsKey("color") && root.containsKey("brightness")) { 253 | // Adjust brightness during colorfade 254 | // (will be applied when fading to the next color) 255 | brightness = root["brightness"]; 256 | } 257 | else { // No effect 258 | flash = false; 259 | colorfade = false; 260 | 261 | if (root.containsKey("color")) { 262 | red = root["color"]["r"]; 263 | green = root["color"]["g"]; 264 | blue = root["color"]["b"]; 265 | } 266 | 267 | if (root.containsKey("brightness")) { 268 | brightness = root["brightness"]; 269 | } 270 | 271 | if (root.containsKey("transition")) { 272 | transitionTime = root["transition"]; 273 | } 274 | else { 275 | transitionTime = 0; 276 | } 277 | } 278 | 279 | return true; 280 | } 281 | 282 | void sendState() { 283 | StaticJsonBuffer jsonBuffer; 284 | 285 | JsonObject& root = jsonBuffer.createObject(); 286 | 287 | root["state"] = (stateOn) ? on_cmd : off_cmd; 288 | JsonObject& color = root.createNestedObject("color"); 289 | color["r"] = red; 290 | color["g"] = green; 291 | color["b"] = blue; 292 | 293 | root["brightness"] = brightness; 294 | 295 | if (colorfade) { 296 | if (transitionTime == CONFIG_COLORFADE_TIME_SLOW) { 297 | root["effect"] = "colorfade_slow"; 298 | } 299 | else { 300 | root["effect"] = "colorfade_fast"; 301 | } 302 | } 303 | else { 304 | root["effect"] = "null"; 305 | } 306 | 307 | char buffer[root.measureLength() + 1]; 308 | root.printTo(buffer, sizeof(buffer)); 309 | 310 | client.publish(light_state_topic, buffer, true); 311 | } 312 | 313 | void reconnect() { 314 | // Loop until we're reconnected 315 | while (!client.connected()) { 316 | Serial.print("Attempting MQTT connection..."); 317 | // Attempt to connect 318 | if (client.connect(client_id, mqtt_username, mqtt_password)) { 319 | Serial.println("connected"); 320 | client.subscribe(light_set_topic); 321 | } else { 322 | Serial.print("failed, rc="); 323 | Serial.print(client.state()); 324 | Serial.println(" try again in 5 seconds"); 325 | // Wait 5 seconds before retrying 326 | delay(5000); 327 | } 328 | } 329 | } 330 | 331 | void setColor(int inR, int inG, int inB) { 332 | if (led_invert) { 333 | inR = (255 - inR); 334 | inG = (255 - inG); 335 | inB = (255 - inB); 336 | } 337 | 338 | analogWrite(redPin, inR); 339 | analogWrite(greenPin, inG); 340 | analogWrite(bluePin, inB); 341 | 342 | Serial.println("Setting LEDs:"); 343 | Serial.print("r: "); 344 | Serial.print(inR); 345 | Serial.print(", g: "); 346 | Serial.print(inG); 347 | Serial.print(", b: "); 348 | Serial.println(inB); 349 | } 350 | 351 | void loop() { 352 | 353 | if (!client.connected()) { 354 | reconnect(); 355 | } 356 | client.loop(); 357 | 358 | if (flash) { 359 | if (startFlash) { 360 | startFlash = false; 361 | flashStartTime = millis(); 362 | } 363 | 364 | if ((millis() - flashStartTime) <= flashLength) { 365 | if ((millis() - flashStartTime) % 1000 <= 500) { 366 | setColor(flashRed, flashGreen, flashBlue); 367 | } 368 | else { 369 | setColor(0, 0, 0); 370 | // If you'd prefer the flashing to happen "on top of" 371 | // the current color, uncomment the next line. 372 | // setColor(realRed, realGreen, realBlue); 373 | } 374 | } 375 | else { 376 | flash = false; 377 | setColor(realRed, realGreen, realBlue); 378 | } 379 | } 380 | else if (colorfade && !inFade) { 381 | realRed = map(colors[currentColor][0], 0, 255, 0, brightness); 382 | realGreen = map(colors[currentColor][1], 0, 255, 0, brightness); 383 | realBlue = map(colors[currentColor][2], 0, 255, 0, brightness); 384 | currentColor = (currentColor + 1) % numColors; 385 | startFade = true; 386 | } 387 | 388 | if (startFade) { 389 | // If we don't want to fade, skip it. 390 | if (transitionTime == 0) { 391 | setColor(realRed, realGreen, realBlue); 392 | 393 | redVal = realRed; 394 | grnVal = realGreen; 395 | bluVal = realBlue; 396 | 397 | startFade = false; 398 | } 399 | else { 400 | loopCount = 0; 401 | stepR = calculateStep(redVal, realRed); 402 | stepG = calculateStep(grnVal, realGreen); 403 | stepB = calculateStep(bluVal, realBlue); 404 | 405 | inFade = true; 406 | } 407 | } 408 | 409 | if (inFade) { 410 | startFade = false; 411 | unsigned long now = millis(); 412 | if (now - lastLoop > transitionTime) { 413 | if (loopCount <= 1020) { 414 | lastLoop = now; 415 | 416 | redVal = calculateVal(stepR, redVal, loopCount); 417 | grnVal = calculateVal(stepG, grnVal, loopCount); 418 | bluVal = calculateVal(stepB, bluVal, loopCount); 419 | 420 | setColor(redVal, grnVal, bluVal); // Write current values to LED pins 421 | 422 | Serial.print("Loop count: "); 423 | Serial.println(loopCount); 424 | loopCount++; 425 | } 426 | else { 427 | inFade = false; 428 | } 429 | } 430 | } 431 | } 432 | 433 | // From https://www.arduino.cc/en/Tutorial/ColorCrossfader 434 | /* BELOW THIS LINE IS THE MATH -- YOU SHOULDN'T NEED TO CHANGE THIS FOR THE BASICS 435 | * 436 | * The program works like this: 437 | * Imagine a crossfade that moves the red LED from 0-10, 438 | * the green from 0-5, and the blue from 10 to 7, in 439 | * ten steps. 440 | * We'd want to count the 10 steps and increase or 441 | * decrease color values in evenly stepped increments. 442 | * Imagine a + indicates raising a value by 1, and a - 443 | * equals lowering it. Our 10 step fade would look like: 444 | * 445 | * 1 2 3 4 5 6 7 8 9 10 446 | * R + + + + + + + + + + 447 | * G + + + + + 448 | * B - - - 449 | * 450 | * The red rises from 0 to 10 in ten steps, the green from 451 | * 0-5 in 5 steps, and the blue falls from 10 to 7 in three steps. 452 | * 453 | * In the real program, the color percentages are converted to 454 | * 0-255 values, and there are 1020 steps (255*4). 455 | * 456 | * To figure out how big a step there should be between one up- or 457 | * down-tick of one of the LED values, we call calculateStep(), 458 | * which calculates the absolute gap between the start and end values, 459 | * and then divides that gap by 1020 to determine the size of the step 460 | * between adjustments in the value. 461 | */ 462 | int calculateStep(int prevValue, int endValue) { 463 | int step = endValue - prevValue; // What's the overall gap? 464 | if (step) { // If its non-zero, 465 | step = 1020/step; // divide by 1020 466 | } 467 | 468 | return step; 469 | } 470 | 471 | /* The next function is calculateVal. When the loop value, i, 472 | * reaches the step size appropriate for one of the 473 | * colors, it increases or decreases the value of that color by 1. 474 | * (R, G, and B are each calculated separately.) 475 | */ 476 | int calculateVal(int step, int val, int i) { 477 | if ((step) && i % step == 0) { // If step is non-zero and its time to change a value, 478 | if (step > 0) { // increment the value if step is positive... 479 | val += 1; 480 | } 481 | else if (step < 0) { // ...or decrement it if step is negative 482 | val -= 1; 483 | } 484 | } 485 | 486 | // Defensive driving: make sure val stays in the range 0-255 487 | if (val > 255) { 488 | val = 255; 489 | } 490 | else if (val < 0) { 491 | val = 0; 492 | } 493 | 494 | return val; 495 | } 496 | -------------------------------------------------------------------------------- /DEPRECATED/mqtt_esp8266_rgbw/mqtt_esp8266_rgbw.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * ESP8266 MQTT Lights for Home Assistant. 3 | * 4 | * This file is for RGBW (red, green, blue, and white) lights. 5 | * 6 | * See https://github.com/corbanmailloux/esp-mqtt-rgb-led 7 | */ 8 | 9 | // Set configuration options for pins, WiFi, and MQTT in the following file: 10 | #include "config.h" 11 | 12 | // https://github.com/bblanchon/ArduinoJson 13 | #include 14 | 15 | #include 16 | 17 | // http://pubsubclient.knolleary.net/ 18 | #include 19 | 20 | const bool debug_mode = CONFIG_DEBUG; 21 | const bool led_invert = CONFIG_INVERT_LED_LOGIC; 22 | 23 | const int redPin = CONFIG_PIN_RED; 24 | const int txPin = BUILTIN_LED; // On-board blue LED 25 | const int greenPin = CONFIG_PIN_GREEN; 26 | const int bluePin = CONFIG_PIN_BLUE; 27 | const int whitePin = CONFIG_PIN_WHITE; 28 | 29 | const char* ssid = CONFIG_WIFI_SSID; 30 | const char* password = CONFIG_WIFI_PASS; 31 | 32 | const char* mqtt_server = CONFIG_MQTT_HOST; 33 | const char* mqtt_username = CONFIG_MQTT_USER; 34 | const char* mqtt_password = CONFIG_MQTT_PASS; 35 | const char* client_id = CONFIG_MQTT_CLIENT_ID; 36 | 37 | // Topics 38 | const char* light_state_topic = CONFIG_MQTT_TOPIC_STATE; 39 | const char* light_set_topic = CONFIG_MQTT_TOPIC_SET; 40 | 41 | const char* on_cmd = CONFIG_MQTT_PAYLOAD_ON; 42 | const char* off_cmd = CONFIG_MQTT_PAYLOAD_OFF; 43 | 44 | const int BUFFER_SIZE = JSON_OBJECT_SIZE(20); 45 | 46 | // Maintained state for reporting to HA 47 | byte red = 255; 48 | byte green = 255; 49 | byte blue = 255; 50 | byte white = 255; 51 | byte brightness = 255; 52 | 53 | // Real values to write to the LEDs (ex. including brightness and state) 54 | byte realRed = 0; 55 | byte realGreen = 0; 56 | byte realBlue = 0; 57 | byte realWhite = 0; 58 | 59 | bool stateOn = false; 60 | 61 | // Globals for fade/transitions 62 | bool startFade = false; 63 | unsigned long lastLoop = 0; 64 | int transitionTime = 0; 65 | bool inFade = false; 66 | int loopCount = 0; 67 | int stepR, stepG, stepB, stepW; 68 | int redVal, grnVal, bluVal, whtVal; 69 | 70 | // Globals for flash 71 | bool flash = false; 72 | bool startFlash = false; 73 | int flashLength = 0; 74 | unsigned long flashStartTime = 0; 75 | byte flashRed = red; 76 | byte flashGreen = green; 77 | byte flashBlue = blue; 78 | byte flashWhite = white; 79 | byte flashBrightness = brightness; 80 | 81 | // Globals for colorfade 82 | bool colorfade = false; 83 | int currentColor = 0; 84 | // {red, grn, blu, wht} 85 | const byte colors[][4] = { 86 | {255, 0, 0, 0}, 87 | {0, 255, 0, 0}, 88 | {0, 0, 255, 0}, 89 | {255, 80, 0, 0}, 90 | {163, 0, 255, 0}, 91 | {0, 255, 255, 0}, 92 | {255, 255, 0, 0} 93 | }; 94 | const int numColors = 7; 95 | 96 | WiFiClient espClient; 97 | PubSubClient client(espClient); 98 | 99 | void setup() { 100 | pinMode(redPin, OUTPUT); 101 | pinMode(greenPin, OUTPUT); 102 | pinMode(bluePin, OUTPUT); 103 | pinMode(whitePin, OUTPUT); 104 | 105 | pinMode(txPin, OUTPUT); 106 | digitalWrite(txPin, HIGH); // Turn off the on-board LED 107 | 108 | analogWriteRange(255); 109 | 110 | if (debug_mode) { 111 | Serial.begin(115200); 112 | } 113 | 114 | setup_wifi(); 115 | client.setServer(mqtt_server, 1883); 116 | client.setCallback(callback); 117 | } 118 | 119 | void setup_wifi() { 120 | 121 | delay(10); 122 | // We start by connecting to a WiFi network 123 | Serial.println(); 124 | Serial.print("Connecting to "); 125 | Serial.println(ssid); 126 | 127 | WiFi.mode(WIFI_STA); 128 | WiFi.begin(ssid, password); 129 | 130 | while (WiFi.status() != WL_CONNECTED) { 131 | delay(500); 132 | Serial.print("."); 133 | } 134 | 135 | Serial.println(""); 136 | Serial.println("WiFi connected"); 137 | Serial.println("IP address: "); 138 | Serial.println(WiFi.localIP()); 139 | } 140 | 141 | /* 142 | SAMPLE PAYLOAD: 143 | { 144 | "brightness": 120, 145 | "color": { 146 | "r": 255, 147 | "g": 100, 148 | "b": 100 149 | }, 150 | "white_value": 255, 151 | "flash": 2, 152 | "transition": 5, 153 | "state": "ON", 154 | "effect": "colorfade_fast" 155 | } 156 | */ 157 | void callback(char* topic, byte* payload, unsigned int length) { 158 | Serial.print("Message arrived ["); 159 | Serial.print(topic); 160 | Serial.print("] "); 161 | 162 | char message[length + 1]; 163 | for (int i = 0; i < length; i++) { 164 | message[i] = (char)payload[i]; 165 | } 166 | message[length] = '\0'; 167 | Serial.println(message); 168 | 169 | if (!processJson(message)) { 170 | return; 171 | } 172 | 173 | if (stateOn) { 174 | // Update lights 175 | realRed = map(red, 0, 255, 0, brightness); 176 | realGreen = map(green, 0, 255, 0, brightness); 177 | realBlue = map(blue, 0, 255, 0, brightness); 178 | realWhite = map(white, 0, 255, 0, brightness); 179 | } 180 | else { 181 | realRed = 0; 182 | realGreen = 0; 183 | realBlue = 0; 184 | realWhite = 0; 185 | } 186 | 187 | startFade = true; 188 | inFade = false; // Kill the current fade 189 | 190 | sendState(); 191 | } 192 | 193 | bool processJson(char* message) { 194 | StaticJsonBuffer jsonBuffer; 195 | 196 | JsonObject& root = jsonBuffer.parseObject(message); 197 | 198 | if (!root.success()) { 199 | Serial.println("parseObject() failed"); 200 | return false; 201 | } 202 | 203 | if (root.containsKey("state")) { 204 | if (strcmp(root["state"], on_cmd) == 0) { 205 | stateOn = true; 206 | } 207 | else if (strcmp(root["state"], off_cmd) == 0) { 208 | stateOn = false; 209 | } 210 | } 211 | 212 | // If "flash" is included, treat RGB and brightness differently 213 | if (root.containsKey("flash") || 214 | (root.containsKey("effect") && strcmp(root["effect"], "flash") == 0)) { 215 | 216 | if (root.containsKey("flash")) { 217 | flashLength = (int)root["flash"] * 1000; 218 | } 219 | else { 220 | flashLength = CONFIG_DEFAULT_FLASH_LENGTH * 1000; 221 | } 222 | 223 | if (root.containsKey("brightness")) { 224 | flashBrightness = root["brightness"]; 225 | } 226 | else { 227 | flashBrightness = brightness; 228 | } 229 | 230 | if (root.containsKey("color")) { 231 | flashRed = root["color"]["r"]; 232 | flashGreen = root["color"]["g"]; 233 | flashBlue = root["color"]["b"]; 234 | } 235 | else { 236 | flashRed = red; 237 | flashGreen = green; 238 | flashBlue = blue; 239 | } 240 | 241 | if (root.containsKey("white_value")) { 242 | flashWhite = root["white_value"]; 243 | } 244 | else { 245 | flashWhite = white; 246 | } 247 | 248 | flashRed = map(flashRed, 0, 255, 0, flashBrightness); 249 | flashGreen = map(flashGreen, 0, 255, 0, flashBrightness); 250 | flashBlue = map(flashBlue, 0, 255, 0, flashBrightness); 251 | flashWhite = map(flashWhite, 0, 255, 0, flashBrightness); 252 | 253 | flash = true; 254 | startFlash = true; 255 | } 256 | else if (root.containsKey("effect") && 257 | (strcmp(root["effect"], "colorfade_slow") == 0 || strcmp(root["effect"], "colorfade_fast") == 0)) { 258 | flash = false; 259 | colorfade = true; 260 | currentColor = 0; 261 | if (strcmp(root["effect"], "colorfade_slow") == 0) { 262 | transitionTime = CONFIG_COLORFADE_TIME_SLOW; 263 | } 264 | else { 265 | transitionTime = CONFIG_COLORFADE_TIME_FAST; 266 | } 267 | } 268 | else if (colorfade && !root.containsKey("color") && root.containsKey("brightness")) { 269 | // Adjust brightness during colorfade 270 | // (will be applied when fading to the next color) 271 | brightness = root["brightness"]; 272 | } 273 | else { // No effect 274 | flash = false; 275 | colorfade = false; 276 | 277 | if (root.containsKey("color")) { 278 | red = root["color"]["r"]; 279 | green = root["color"]["g"]; 280 | blue = root["color"]["b"]; 281 | } 282 | 283 | if (root.containsKey("white_value")) { 284 | white = root["white_value"]; 285 | } 286 | 287 | if (root.containsKey("brightness")) { 288 | brightness = root["brightness"]; 289 | } 290 | 291 | if (root.containsKey("transition")) { 292 | transitionTime = root["transition"]; 293 | } 294 | else { 295 | transitionTime = 0; 296 | } 297 | } 298 | 299 | return true; 300 | } 301 | 302 | void sendState() { 303 | StaticJsonBuffer jsonBuffer; 304 | 305 | JsonObject& root = jsonBuffer.createObject(); 306 | 307 | root["state"] = (stateOn) ? on_cmd : off_cmd; 308 | JsonObject& color = root.createNestedObject("color"); 309 | color["r"] = red; 310 | color["g"] = green; 311 | color["b"] = blue; 312 | 313 | root["brightness"] = brightness; 314 | 315 | root["white_value"] = white; 316 | 317 | if (colorfade) { 318 | if (transitionTime == CONFIG_COLORFADE_TIME_SLOW) { 319 | root["effect"] = "colorfade_slow"; 320 | } 321 | else { 322 | root["effect"] = "colorfade_fast"; 323 | } 324 | } 325 | else { 326 | root["effect"] = "null"; 327 | } 328 | 329 | char buffer[root.measureLength() + 1]; 330 | root.printTo(buffer, sizeof(buffer)); 331 | 332 | client.publish(light_state_topic, buffer, true); 333 | } 334 | 335 | void reconnect() { 336 | // Loop until we're reconnected 337 | while (!client.connected()) { 338 | Serial.print("Attempting MQTT connection..."); 339 | // Attempt to connect 340 | if (client.connect(client_id, mqtt_username, mqtt_password)) { 341 | Serial.println("connected"); 342 | client.subscribe(light_set_topic); 343 | } else { 344 | Serial.print("failed, rc="); 345 | Serial.print(client.state()); 346 | Serial.println(" try again in 5 seconds"); 347 | // Wait 5 seconds before retrying 348 | delay(5000); 349 | } 350 | } 351 | } 352 | 353 | void setColor(int inR, int inG, int inB, int inW) { 354 | if (led_invert) { 355 | inR = (255 - inR); 356 | inG = (255 - inG); 357 | inB = (255 - inB); 358 | inW = (255 - inW); 359 | } 360 | 361 | analogWrite(redPin, inR); 362 | analogWrite(greenPin, inG); 363 | analogWrite(bluePin, inB); 364 | analogWrite(whitePin, inW); 365 | 366 | Serial.println("Setting LEDs:"); 367 | Serial.print("r: "); 368 | Serial.print(inR); 369 | Serial.print(", g: "); 370 | Serial.print(inG); 371 | Serial.print(", b: "); 372 | Serial.print(inB); 373 | Serial.print(", w: "); 374 | Serial.println(inW); 375 | } 376 | 377 | void loop() { 378 | 379 | if (!client.connected()) { 380 | reconnect(); 381 | } 382 | client.loop(); 383 | 384 | if (flash) { 385 | if (startFlash) { 386 | startFlash = false; 387 | flashStartTime = millis(); 388 | } 389 | 390 | if ((millis() - flashStartTime) <= flashLength) { 391 | if ((millis() - flashStartTime) % 1000 <= 500) { 392 | setColor(flashRed, flashGreen, flashBlue, flashWhite); 393 | } 394 | else { 395 | setColor(0, 0, 0, 0); 396 | // If you'd prefer the flashing to happen "on top of" 397 | // the current color, uncomment the next line. 398 | // setColor(realRed, realGreen, realBlue, realWhite); 399 | } 400 | } 401 | else { 402 | flash = false; 403 | setColor(realRed, realGreen, realBlue, realWhite); 404 | } 405 | } 406 | else if (colorfade && !inFade) { 407 | realRed = map(colors[currentColor][0], 0, 255, 0, brightness); 408 | realGreen = map(colors[currentColor][1], 0, 255, 0, brightness); 409 | realBlue = map(colors[currentColor][2], 0, 255, 0, brightness); 410 | realWhite = map(colors[currentColor][3], 0, 255, 0, brightness); 411 | currentColor = (currentColor + 1) % numColors; 412 | startFade = true; 413 | } 414 | 415 | if (startFade) { 416 | // If we don't want to fade, skip it. 417 | if (transitionTime == 0) { 418 | setColor(realRed, realGreen, realBlue, realWhite); 419 | 420 | redVal = realRed; 421 | grnVal = realGreen; 422 | bluVal = realBlue; 423 | whtVal = realWhite; 424 | 425 | startFade = false; 426 | } 427 | else { 428 | loopCount = 0; 429 | stepR = calculateStep(redVal, realRed); 430 | stepG = calculateStep(grnVal, realGreen); 431 | stepB = calculateStep(bluVal, realBlue); 432 | stepW = calculateStep(whtVal, realWhite); 433 | 434 | inFade = true; 435 | } 436 | } 437 | 438 | if (inFade) { 439 | startFade = false; 440 | unsigned long now = millis(); 441 | if (now - lastLoop > transitionTime) { 442 | if (loopCount <= 1020) { 443 | lastLoop = now; 444 | 445 | redVal = calculateVal(stepR, redVal, loopCount); 446 | grnVal = calculateVal(stepG, grnVal, loopCount); 447 | bluVal = calculateVal(stepB, bluVal, loopCount); 448 | whtVal = calculateVal(stepW, whtVal, loopCount); 449 | 450 | setColor(redVal, grnVal, bluVal, whtVal); // Write current values to LED pins 451 | 452 | Serial.print("Loop count: "); 453 | Serial.println(loopCount); 454 | loopCount++; 455 | } 456 | else { 457 | inFade = false; 458 | } 459 | } 460 | } 461 | } 462 | 463 | // From https://www.arduino.cc/en/Tutorial/ColorCrossfader 464 | /* BELOW THIS LINE IS THE MATH -- YOU SHOULDN'T NEED TO CHANGE THIS FOR THE BASICS 465 | * 466 | * The program works like this: 467 | * Imagine a crossfade that moves the red LED from 0-10, 468 | * the green from 0-5, and the blue from 10 to 7, in 469 | * ten steps. 470 | * We'd want to count the 10 steps and increase or 471 | * decrease color values in evenly stepped increments. 472 | * Imagine a + indicates raising a value by 1, and a - 473 | * equals lowering it. Our 10 step fade would look like: 474 | * 475 | * 1 2 3 4 5 6 7 8 9 10 476 | * R + + + + + + + + + + 477 | * G + + + + + 478 | * B - - - 479 | * 480 | * The red rises from 0 to 10 in ten steps, the green from 481 | * 0-5 in 5 steps, and the blue falls from 10 to 7 in three steps. 482 | * 483 | * In the real program, the color percentages are converted to 484 | * 0-255 values, and there are 1020 steps (255*4). 485 | * 486 | * To figure out how big a step there should be between one up- or 487 | * down-tick of one of the LED values, we call calculateStep(), 488 | * which calculates the absolute gap between the start and end values, 489 | * and then divides that gap by 1020 to determine the size of the step 490 | * between adjustments in the value. 491 | */ 492 | int calculateStep(int prevValue, int endValue) { 493 | int step = endValue - prevValue; // What's the overall gap? 494 | if (step) { // If its non-zero, 495 | step = 1020/step; // divide by 1020 496 | } 497 | 498 | return step; 499 | } 500 | 501 | /* The next function is calculateVal. When the loop value, i, 502 | * reaches the step size appropriate for one of the 503 | * colors, it increases or decreases the value of that color by 1. 504 | * (R, G, and B are each calculated separately.) 505 | */ 506 | int calculateVal(int step, int val, int i) { 507 | if ((step) && i % step == 0) { // If step is non-zero and its time to change a value, 508 | if (step > 0) { // increment the value if step is positive... 509 | val += 1; 510 | } 511 | else if (step < 0) { // ...or decrement it if step is negative 512 | val -= 1; 513 | } 514 | } 515 | 516 | // Defensive driving: make sure val stays in the range 0-255 517 | if (val > 255) { 518 | val = 255; 519 | } 520 | else if (val < 0) { 521 | val = 0; 522 | } 523 | 524 | return val; 525 | } 526 | -------------------------------------------------------------------------------- /mqtt_esp8266_light/mqtt_esp8266_light.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * ESP8266 MQTT Lights for Home Assistant. 3 | * 4 | * Created DIY lights for Home Assistant using MQTT and JSON. 5 | * This project supports single-color, RGB, and RGBW lights. 6 | * 7 | * Copy the included `config-sample.h` file to `config.h` and update 8 | * accordingly for your setup. 9 | * 10 | * See https://github.com/corbanmailloux/esp-mqtt-rgb-led for more information. 11 | */ 12 | 13 | // Set configuration options for LED type, pins, WiFi, and MQTT in the following file: 14 | #include "config.h" 15 | 16 | // https://github.com/bblanchon/ArduinoJson 17 | #include 18 | 19 | #include 20 | 21 | // http://pubsubclient.knolleary.net/ 22 | #include 23 | 24 | const bool rgb = (CONFIG_STRIP == RGB) || (CONFIG_STRIP == RGBW); 25 | const bool includeWhite = (CONFIG_STRIP == BRIGHTNESS) || (CONFIG_STRIP == RGBW); 26 | 27 | const int BUFFER_SIZE = JSON_OBJECT_SIZE(20); 28 | 29 | // Maintained state for reporting to HA 30 | byte red = 255; 31 | byte green = 255; 32 | byte blue = 255; 33 | byte white = 255; 34 | byte brightness = 255; 35 | 36 | // Real values to write to the LEDs (ex. including brightness and state) 37 | byte realRed = 0; 38 | byte realGreen = 0; 39 | byte realBlue = 0; 40 | byte realWhite = 0; 41 | 42 | bool stateOn = false; 43 | 44 | // Globals for fade/transitions 45 | bool startFade = false; 46 | unsigned long lastLoop = 0; 47 | int transitionTime = 0; 48 | bool inFade = false; 49 | int loopCount = 0; 50 | int stepR, stepG, stepB, stepW; 51 | int redVal, grnVal, bluVal, whtVal; 52 | 53 | // Globals for flash 54 | bool flash = false; 55 | bool startFlash = false; 56 | int flashLength = 0; 57 | unsigned long flashStartTime = 0; 58 | byte flashRed = red; 59 | byte flashGreen = green; 60 | byte flashBlue = blue; 61 | byte flashWhite = white; 62 | byte flashBrightness = brightness; 63 | 64 | // Globals for colorfade 65 | bool colorfade = false; 66 | int currentColor = 0; 67 | // {red, grn, blu, wht} 68 | const byte colors[][4] = { 69 | {255, 0, 0, 0}, 70 | {0, 255, 0, 0}, 71 | {0, 0, 255, 0}, 72 | {255, 80, 0, 0}, 73 | {163, 0, 255, 0}, 74 | {0, 255, 255, 0}, 75 | {255, 255, 0, 0} 76 | }; 77 | const int numColors = 7; 78 | 79 | WiFiClient espClient; 80 | PubSubClient client(espClient); 81 | 82 | void setup() { 83 | if (rgb) { 84 | pinMode(CONFIG_PIN_RED, OUTPUT); 85 | pinMode(CONFIG_PIN_GREEN, OUTPUT); 86 | pinMode(CONFIG_PIN_BLUE, OUTPUT); 87 | } 88 | if (includeWhite) { 89 | pinMode(CONFIG_PIN_WHITE, OUTPUT); 90 | } 91 | 92 | // Set the LED_BUILTIN based on the CONFIG_LED_BUILTIN_MODE 93 | switch (CONFIG_LED_BUILTIN_MODE) { 94 | case 0: 95 | pinMode(LED_BUILTIN, OUTPUT); 96 | digitalWrite(LED_BUILTIN, LOW); 97 | break; 98 | case 1: 99 | pinMode(LED_BUILTIN, OUTPUT); 100 | digitalWrite(LED_BUILTIN, HIGH); 101 | break; 102 | default: // Other options (like -1) are ignored. 103 | break; 104 | } 105 | 106 | analogWriteRange(255); 107 | 108 | if (CONFIG_DEBUG) { 109 | Serial.begin(115200); 110 | } 111 | 112 | setup_wifi(); 113 | client.setServer(CONFIG_MQTT_HOST, CONFIG_MQTT_PORT); 114 | client.setCallback(callback); 115 | } 116 | 117 | void setup_wifi() { 118 | delay(10); 119 | // We start by connecting to a WiFi network 120 | Serial.println(); 121 | Serial.print("Connecting to "); 122 | Serial.println(CONFIG_WIFI_SSID); 123 | 124 | WiFi.mode(WIFI_STA); // Disable the built-in WiFi access point. 125 | WiFi.begin(CONFIG_WIFI_SSID, CONFIG_WIFI_PASS); 126 | 127 | while (WiFi.status() != WL_CONNECTED) { 128 | delay(500); 129 | Serial.print("."); 130 | } 131 | 132 | Serial.println(""); 133 | Serial.println("WiFi connected"); 134 | Serial.println("IP address: "); 135 | Serial.println(WiFi.localIP()); 136 | } 137 | 138 | /* 139 | SAMPLE PAYLOAD (BRIGHTNESS): 140 | { 141 | "brightness": 120, 142 | "flash": 2, 143 | "transition": 5, 144 | "state": "ON" 145 | } 146 | 147 | SAMPLE PAYLOAD (RGBW): 148 | { 149 | "brightness": 120, 150 | "color": { 151 | "r": 255, 152 | "g": 100, 153 | "b": 100 154 | }, 155 | "white_value": 255, 156 | "flash": 2, 157 | "transition": 5, 158 | "state": "ON", 159 | "effect": "colorfade_fast" 160 | } 161 | */ 162 | void callback(char* topic, byte* payload, unsigned int length) { 163 | Serial.print("Message arrived ["); 164 | Serial.print(topic); 165 | Serial.print("] "); 166 | 167 | char message[length + 1]; 168 | for (int i = 0; i < length; i++) { 169 | message[i] = (char)payload[i]; 170 | } 171 | message[length] = '\0'; 172 | Serial.println(message); 173 | 174 | if (!processJson(message)) { 175 | return; 176 | } 177 | 178 | if (stateOn) { 179 | // Update lights 180 | realRed = map(red, 0, 255, 0, brightness); 181 | realGreen = map(green, 0, 255, 0, brightness); 182 | realBlue = map(blue, 0, 255, 0, brightness); 183 | realWhite = map(white, 0, 255, 0, brightness); 184 | } 185 | else { 186 | realRed = 0; 187 | realGreen = 0; 188 | realBlue = 0; 189 | realWhite = 0; 190 | } 191 | 192 | startFade = true; 193 | inFade = false; // Kill the current fade 194 | 195 | sendState(); 196 | } 197 | 198 | bool processJson(char* message) { 199 | StaticJsonBuffer jsonBuffer; 200 | 201 | JsonObject& root = jsonBuffer.parseObject(message); 202 | 203 | if (!root.success()) { 204 | Serial.println("parseObject() failed"); 205 | return false; 206 | } 207 | 208 | if (root.containsKey("state")) { 209 | if (strcmp(root["state"], CONFIG_MQTT_PAYLOAD_ON) == 0) { 210 | stateOn = true; 211 | } 212 | else if (strcmp(root["state"], CONFIG_MQTT_PAYLOAD_OFF) == 0) { 213 | stateOn = false; 214 | } 215 | } 216 | 217 | // If "flash" is included, treat RGB and brightness differently 218 | if (root.containsKey("flash") || 219 | (root.containsKey("effect") && strcmp(root["effect"], "flash") == 0)) { 220 | 221 | if (root.containsKey("flash")) { 222 | flashLength = (int)root["flash"] * 1000; 223 | } 224 | else { 225 | flashLength = CONFIG_DEFAULT_FLASH_LENGTH * 1000; 226 | } 227 | 228 | if (root.containsKey("brightness")) { 229 | flashBrightness = root["brightness"]; 230 | } 231 | else { 232 | flashBrightness = brightness; 233 | } 234 | 235 | if (rgb && root.containsKey("color")) { 236 | flashRed = root["color"]["r"]; 237 | flashGreen = root["color"]["g"]; 238 | flashBlue = root["color"]["b"]; 239 | } 240 | else { 241 | flashRed = red; 242 | flashGreen = green; 243 | flashBlue = blue; 244 | } 245 | 246 | if (includeWhite && root.containsKey("white_value")) { 247 | flashWhite = root["white_value"]; 248 | } 249 | else { 250 | flashWhite = white; 251 | } 252 | 253 | flashRed = map(flashRed, 0, 255, 0, flashBrightness); 254 | flashGreen = map(flashGreen, 0, 255, 0, flashBrightness); 255 | flashBlue = map(flashBlue, 0, 255, 0, flashBrightness); 256 | flashWhite = map(flashWhite, 0, 255, 0, flashBrightness); 257 | 258 | flash = true; 259 | startFlash = true; 260 | } 261 | else if (rgb && root.containsKey("effect") && 262 | (strcmp(root["effect"], "colorfade_slow") == 0 || strcmp(root["effect"], "colorfade_fast") == 0)) { 263 | flash = false; 264 | colorfade = true; 265 | currentColor = 0; 266 | if (strcmp(root["effect"], "colorfade_slow") == 0) { 267 | transitionTime = CONFIG_COLORFADE_TIME_SLOW; 268 | } 269 | else { 270 | transitionTime = CONFIG_COLORFADE_TIME_FAST; 271 | } 272 | } 273 | else if (colorfade && !root.containsKey("color") && root.containsKey("brightness")) { 274 | // Adjust brightness during colorfade 275 | // (will be applied when fading to the next color) 276 | brightness = root["brightness"]; 277 | } 278 | else { // No effect 279 | flash = false; 280 | colorfade = false; 281 | 282 | if (rgb && root.containsKey("color")) { 283 | red = root["color"]["r"]; 284 | green = root["color"]["g"]; 285 | blue = root["color"]["b"]; 286 | } 287 | 288 | if (includeWhite && root.containsKey("white_value")) { 289 | white = root["white_value"]; 290 | } 291 | 292 | if (root.containsKey("brightness")) { 293 | brightness = root["brightness"]; 294 | } 295 | 296 | if (root.containsKey("transition")) { 297 | transitionTime = root["transition"]; 298 | } 299 | else { 300 | transitionTime = CONFIG_DEFAULT_TRANSITION_TIME; 301 | } 302 | } 303 | 304 | return true; 305 | } 306 | 307 | void sendState() { 308 | StaticJsonBuffer jsonBuffer; 309 | 310 | JsonObject& root = jsonBuffer.createObject(); 311 | 312 | root["state"] = (stateOn) ? CONFIG_MQTT_PAYLOAD_ON : CONFIG_MQTT_PAYLOAD_OFF; 313 | if (rgb) { 314 | JsonObject& color = root.createNestedObject("color"); 315 | color["r"] = red; 316 | color["g"] = green; 317 | color["b"] = blue; 318 | } 319 | 320 | root["brightness"] = brightness; 321 | 322 | if (includeWhite) { 323 | root["white_value"] = white; 324 | } 325 | 326 | if (rgb && colorfade) { 327 | if (transitionTime == CONFIG_COLORFADE_TIME_SLOW) { 328 | root["effect"] = "colorfade_slow"; 329 | } 330 | else { 331 | root["effect"] = "colorfade_fast"; 332 | } 333 | } 334 | else { 335 | root["effect"] = "null"; 336 | } 337 | 338 | char buffer[root.measureLength() + 1]; 339 | root.printTo(buffer, sizeof(buffer)); 340 | 341 | client.publish(CONFIG_MQTT_TOPIC_STATE, buffer, true); 342 | } 343 | 344 | void reconnect() { 345 | // Loop until we're reconnected 346 | while (!client.connected()) { 347 | Serial.print("Attempting MQTT connection..."); 348 | // Attempt to connect 349 | if (client.connect(CONFIG_MQTT_CLIENT_ID, CONFIG_MQTT_USER, CONFIG_MQTT_PASS)) { 350 | Serial.println("connected"); 351 | client.subscribe(CONFIG_MQTT_TOPIC_SET); 352 | } else { 353 | Serial.print("failed, rc="); 354 | Serial.print(client.state()); 355 | Serial.println(" try again in 5 seconds"); 356 | // Wait 5 seconds before retrying 357 | delay(5000); 358 | } 359 | } 360 | } 361 | 362 | void setColor(int inR, int inG, int inB, int inW) { 363 | if (CONFIG_INVERT_LED_LOGIC) { 364 | inR = (255 - inR); 365 | inG = (255 - inG); 366 | inB = (255 - inB); 367 | inW = (255 - inW); 368 | } 369 | 370 | if (rgb) { 371 | analogWrite(CONFIG_PIN_RED, inR); 372 | analogWrite(CONFIG_PIN_GREEN, inG); 373 | analogWrite(CONFIG_PIN_BLUE, inB); 374 | } 375 | 376 | if (includeWhite) { 377 | analogWrite(CONFIG_PIN_WHITE, inW); 378 | } 379 | 380 | if (CONFIG_DEBUG) { 381 | Serial.print("Setting LEDs: {"); 382 | if (rgb) { 383 | Serial.print("r: "); 384 | Serial.print(inR); 385 | Serial.print(" , g: "); 386 | Serial.print(inG); 387 | Serial.print(" , b: "); 388 | Serial.print(inB); 389 | } 390 | 391 | if (includeWhite) { 392 | if (rgb) { 393 | Serial.print(", "); 394 | } 395 | Serial.print("w: "); 396 | Serial.print(inW); 397 | } 398 | 399 | Serial.println("}"); 400 | } 401 | } 402 | 403 | void loop() { 404 | if (!client.connected()) { 405 | reconnect(); 406 | } 407 | 408 | client.loop(); 409 | 410 | if (flash) { 411 | if (startFlash) { 412 | startFlash = false; 413 | flashStartTime = millis(); 414 | } 415 | 416 | if ((millis() - flashStartTime) <= flashLength) { 417 | if ((millis() - flashStartTime) % 1000 <= 500) { 418 | setColor(flashRed, flashGreen, flashBlue, flashWhite); 419 | } 420 | else { 421 | setColor(0, 0, 0, 0); 422 | // If you'd prefer the flashing to happen "on top of" 423 | // the current color, uncomment the next line. 424 | // setColor(realRed, realGreen, realBlue, realWhite); 425 | } 426 | } 427 | else { 428 | flash = false; 429 | setColor(realRed, realGreen, realBlue, realWhite); 430 | } 431 | } 432 | else if (rgb && colorfade && !inFade) { 433 | realRed = map(colors[currentColor][0], 0, 255, 0, brightness); 434 | realGreen = map(colors[currentColor][1], 0, 255, 0, brightness); 435 | realBlue = map(colors[currentColor][2], 0, 255, 0, brightness); 436 | realWhite = map(colors[currentColor][3], 0, 255, 0, brightness); 437 | currentColor = (currentColor + 1) % numColors; 438 | startFade = true; 439 | } 440 | 441 | if (startFade) { 442 | // If we don't want to fade, skip it. 443 | if (transitionTime == 0) { 444 | setColor(realRed, realGreen, realBlue, realWhite); 445 | 446 | redVal = realRed; 447 | grnVal = realGreen; 448 | bluVal = realBlue; 449 | whtVal = realWhite; 450 | 451 | startFade = false; 452 | } 453 | else { 454 | loopCount = 0; 455 | stepR = calculateStep(redVal, realRed); 456 | stepG = calculateStep(grnVal, realGreen); 457 | stepB = calculateStep(bluVal, realBlue); 458 | stepW = calculateStep(whtVal, realWhite); 459 | 460 | inFade = true; 461 | } 462 | } 463 | 464 | if (inFade) { 465 | startFade = false; 466 | unsigned long now = millis(); 467 | if (now - lastLoop > transitionTime) { 468 | if (loopCount <= 1020) { 469 | lastLoop = now; 470 | 471 | redVal = calculateVal(stepR, redVal, loopCount); 472 | grnVal = calculateVal(stepG, grnVal, loopCount); 473 | bluVal = calculateVal(stepB, bluVal, loopCount); 474 | whtVal = calculateVal(stepW, whtVal, loopCount); 475 | 476 | setColor(redVal, grnVal, bluVal, whtVal); // Write current values to LED pins 477 | 478 | Serial.print("Loop count: "); 479 | Serial.println(loopCount); 480 | loopCount++; 481 | } 482 | else { 483 | inFade = false; 484 | } 485 | } 486 | } 487 | } 488 | 489 | // From https://www.arduino.cc/en/Tutorial/ColorCrossfader 490 | /* BELOW THIS LINE IS THE MATH -- YOU SHOULDN'T NEED TO CHANGE THIS FOR THE BASICS 491 | * 492 | * The program works like this: 493 | * Imagine a crossfade that moves the red LED from 0-10, 494 | * the green from 0-5, and the blue from 10 to 7, in 495 | * ten steps. 496 | * We'd want to count the 10 steps and increase or 497 | * decrease color values in evenly stepped increments. 498 | * Imagine a + indicates raising a value by 1, and a - 499 | * equals lowering it. Our 10 step fade would look like: 500 | * 501 | * 1 2 3 4 5 6 7 8 9 10 502 | * R + + + + + + + + + + 503 | * G + + + + + 504 | * B - - - 505 | * 506 | * The red rises from 0 to 10 in ten steps, the green from 507 | * 0-5 in 5 steps, and the blue falls from 10 to 7 in three steps. 508 | * 509 | * In the real program, the color percentages are converted to 510 | * 0-255 values, and there are 1020 steps (255*4). 511 | * 512 | * To figure out how big a step there should be between one up- or 513 | * down-tick of one of the LED values, we call calculateStep(), 514 | * which calculates the absolute gap between the start and end values, 515 | * and then divides that gap by 1020 to determine the size of the step 516 | * between adjustments in the value. 517 | */ 518 | int calculateStep(int prevValue, int endValue) { 519 | int step = endValue - prevValue; // What's the overall gap? 520 | if (step) { // If its non-zero, 521 | step = 1020/step; // divide by 1020 522 | } 523 | 524 | return step; 525 | } 526 | 527 | /* The next function is calculateVal. When the loop value, i, 528 | * reaches the step size appropriate for one of the 529 | * colors, it increases or decreases the value of that color by 1. 530 | * (R, G, and B are each calculated separately.) 531 | */ 532 | int calculateVal(int step, int val, int i) { 533 | if ((step) && i % step == 0) { // If step is non-zero and its time to change a value, 534 | if (step > 0) { // increment the value if step is positive... 535 | val += 1; 536 | } 537 | else if (step < 0) { // ...or decrement it if step is negative 538 | val -= 1; 539 | } 540 | } 541 | 542 | // Defensive driving: make sure val stays in the range 0-255 543 | if (val > 255) { 544 | val = 255; 545 | } 546 | else if (val < 0) { 547 | val = 0; 548 | } 549 | 550 | return val; 551 | } 552 | --------------------------------------------------------------------------------