├── Fader.h ├── LedFunction.h ├── LedStates.h ├── PinStates.h ├── README.md ├── RF.h ├── RainbowFunction.h ├── SimpleRGBFunction.h ├── WaveFunction.h └── bitluniHomeAutomation.ino /Fader.h: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | template 11 | class Fader 12 | { 13 | public: 14 | bool active = false; 15 | long startTime = 0; 16 | long duration = 0; 17 | T &from; 18 | T &to; 19 | 20 | Fader(T &fromState, T &toState) 21 | :from(fromState) 22 | ,to(toState) 23 | { 24 | } 25 | 26 | bool start(int duration) 27 | { 28 | if(active) 29 | return false; 30 | active = true; 31 | startTime = millis(); 32 | this->duration = duration; 33 | } 34 | 35 | bool fade() 36 | { 37 | if(!active) 38 | return false; 39 | long t = millis() - startTime; 40 | if(t >= duration || t < 0 || duration == 0) 41 | { 42 | from.setValues(to); 43 | active = false; 44 | return false; 45 | } 46 | float f1 = (float)t / duration; 47 | float f0 = 1 - f1; 48 | from.fade(to, long(f0 * 0x10000), long(f1 * 0x10000)); 49 | return true; 50 | } 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /LedFunction.h: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | #include 11 | #ifndef LED_FUNCTION_H 12 | #define LED_FUNCTION_H 13 | 14 | const char *rgbNames[] = {"r", "g", "b"}; 15 | 16 | class LedStates; 17 | class LedFunction 18 | { 19 | public: 20 | LedStates *state; 21 | LedFunction() 22 | { 23 | } 24 | 25 | virtual bool init(ESP8266WebServer &server) 26 | { 27 | } 28 | 29 | bool loadRGBValues(ESP8266WebServer &server, uint8_t *rgb) 30 | { 31 | bool valid = false; 32 | for(int i = 0; i < 3; i++) 33 | if(server.hasArg(rgbNames[i])) 34 | { 35 | int value = server.arg(rgbNames[i]).toInt(); 36 | if(value < 0 || value > 255) 37 | { 38 | String message = "Illegal value:\n\n"; 39 | message += rgbNames[i]; 40 | message += "="; 41 | message += server.arg(rgbNames[i]); 42 | message += "\nOnly values 0 to 255 are allowed. 0 is off and 255 is 100% lit."; 43 | server.send(200, "text/plain", message); 44 | return false; 45 | } 46 | rgb[i] = value; 47 | } 48 | return true; 49 | } 50 | 51 | virtual void render() = 0; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /LedStates.h: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | #include 11 | #include "LedFunction.h" 12 | 13 | const int MAX_LED_COUNT = 300; 14 | 15 | class LedStates 16 | { 17 | public: 18 | uint8_t values[MAX_LED_COUNT][3] = {{0}}; 19 | int count = 0; 20 | bool dirty = false; 21 | Adafruit_NeoPixel &pixels; 22 | LedFunction *function = 0; 23 | 24 | LedStates(Adafruit_NeoPixel &ledPixels) 25 | :pixels(ledPixels) 26 | { 27 | count = pixels.numPixels(); 28 | } 29 | 30 | void setFunction(LedFunction *newFunction) 31 | { 32 | if(function) 33 | delete function; 34 | function = newFunction; 35 | if(!function) 36 | return; 37 | function->state = this; 38 | } 39 | 40 | void setRgb(int i, uint8_t r, uint8_t g, uint8_t b) 41 | { 42 | values[i][0] = r; 43 | values[i][1] = g; 44 | values[i][2] = b; 45 | dirty = true; 46 | } 47 | 48 | virtual void render() 49 | { 50 | if(function) 51 | function->render(); 52 | } 53 | 54 | void setValues(LedStates &to) 55 | { 56 | for(int i = 0; i < count; i++) 57 | for(int j = 0; j < 3; j++) 58 | values[i][j] = to.values[i][j]; 59 | setFunction(to.function); 60 | to.function = 0; 61 | dirty = true; 62 | } 63 | 64 | void commit() 65 | { 66 | if(!dirty) 67 | return; 68 | for(int i = 0; i < count; i++) 69 | pixels.setPixelColor(i, pixels.Color(values[i][0], values[i][1], values[i][2])); 70 | pixels.show(); 71 | dirty = false; 72 | } 73 | 74 | void fade(LedStates &to, long f0, long f1) 75 | { 76 | for(int i = 0; i < count; i++) 77 | pixels.setPixelColor(i, pixels.Color((values[i][0] * f0 + to.values[i][0] * f1) >> 16, (values[i][1] * f0 + to.values[i][1] * f1) >> 16, (values[i][2] * f0 + to.values[i][2] * f1) >> 16)); 78 | pixels.show(); 79 | dirty = true; 80 | } 81 | }; 82 | 83 | -------------------------------------------------------------------------------- /PinStates.h: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | #include 11 | 12 | //If you encounter compile error in this file 13 | //you have to select the board WeMos D1 R2 Mini in the tools 14 | //If you are using this code elswhere use the wemos D1 mini pin configuration 15 | //from this comment 16 | /*const int D0 = 16; 17 | const int D1 = 5; 18 | const int D2 = 4; 19 | const int D3 = 0; 20 | const int D4 = 2; 21 | const int D5 = 14; 22 | const int D6 = 12; 23 | const int D7 = 13; 24 | const int D8 = 15;*/ 25 | 26 | const int PIN_COUNT = 9; 27 | const char *pinNames[PIN_COUNT] = {"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8"}; 28 | const int pinNumbers[PIN_COUNT] = {D0, D1, D2, D3, D4, D5, D6, D7, D8}; 29 | 30 | class PinStates 31 | { 32 | public: 33 | int values[PIN_COUNT] = {-1}; 34 | 35 | PinStates() 36 | { 37 | } 38 | 39 | bool loadValues(ESP8266WebServer &server) 40 | { 41 | for(int i = 0; i < PIN_COUNT; i++) 42 | if(server.hasArg(pinNames[i])) 43 | { 44 | int value = server.arg(pinNames[i]).toInt(); 45 | if(value < 0 || value > 1023) 46 | { 47 | String message = "Illegal value:\n\n"; 48 | message += pinNames[i]; 49 | message += "="; 50 | message += server.arg(pinNames[i]); 51 | message += "\nOnly values 0 to 1023 are allowed. 0 is off and 1023 is on."; 52 | server.send(200, "text/plain", message); 53 | return false; 54 | } 55 | values[i] = value; 56 | } 57 | return true; 58 | } 59 | 60 | void commit() 61 | { 62 | for(int i = 0; i < PIN_COUNT; i++) 63 | if(values[i] > -1) 64 | analogWrite(pinNumbers[i], values[i]); 65 | } 66 | 67 | void setAllTo(int value) 68 | { 69 | for(int i = 0; i < PIN_COUNT; i++) 70 | if(values[i] > -1) 71 | values[i] = value; 72 | } 73 | 74 | void toggle() 75 | { 76 | for(int i = 0; i < PIN_COUNT; i++) 77 | if(values[i] > -1) 78 | values[i] = values[i] ^ 1023; 79 | } 80 | 81 | void setValues(PinStates &to) 82 | { 83 | for(int i = 0; i < PIN_COUNT; i++) 84 | values[i] = to.values[i]; 85 | } 86 | 87 | void fade(PinStates &to, long f0, long f1) 88 | { 89 | for(int i = 0; i < PIN_COUNT; i++) 90 | if(values[i] > -1) 91 | analogWrite(pinNumbers[i], ((to.values[i] * f1) + (values[i] * f0)) >> 16); 92 | } 93 | }; 94 | 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bitluniHomeAutomation 2 | WeMos ESP8266 Arduino sketch to control pins and WS2812 led strips. 3 | 4 | This is the sketch to the corresponding tutorial video: 5 | https://www.youtube.com/watch?v=7Dv70ci-MOw 6 | 7 | TODOs: 8 | - fading jumps to taget state when called while prevoius fading still active 9 | - servo support 10 | - leds optional 11 | - provide configuration page 12 | - when one specific pin is high on startup, provide AP to access configuration page 13 | - add authentication 14 | 15 | Find more projects: 16 | https://youtube.com/bitlunislab 17 | -------------------------------------------------------------------------------- /RF.h: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | //Works with BH9938 with t=200 11 | 12 | inline void rfPreamble(int pin, int t) 13 | { 14 | int m = micros(); 15 | digitalWrite(pin, 1); 16 | while(micros() - m < t); 17 | digitalWrite(pin, 0); 18 | while(micros() - m < t * 32); 19 | } 20 | 21 | inline void rfWriteBit(int pin, int t, int b) 22 | { 23 | int m = micros(); 24 | if(b) 25 | { 26 | digitalWrite(pin, 1); 27 | while(micros() - m < t * 3); 28 | digitalWrite(pin, 0); 29 | while(micros() - m < t * 4); 30 | } 31 | else 32 | { 33 | digitalWrite(pin, 1); 34 | while(micros() - m < t); 35 | digitalWrite(pin, 0); 36 | while(micros() - m < t * 4); 37 | } 38 | } 39 | 40 | void rfWriteCode(int pin, int t, int code, int data) 41 | { 42 | rfPreamble(pin, t); 43 | for(int i = 0; i < 20; i++) 44 | { 45 | rfWriteBit(pin, t, code & 1); 46 | code >>= 1; 47 | } 48 | for(int i = 0; i < 4; i++) 49 | { 50 | rfWriteBit(pin, t, data & 1); 51 | data >>= 1; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /RainbowFunction.h: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | #include "LedFunction.h" 11 | 12 | class RainbowFunction: public LedFunction 13 | { 14 | public: 15 | RainbowFunction() 16 | { 17 | } 18 | 19 | virtual void render() 20 | { 21 | int j = (millis() >> 4) & 255; 22 | for(int i = 0; i < state->count; i++) 23 | { 24 | int ij = 255 - ((i + j) & 255); 25 | if(ij < 85) 26 | state->setRgb(i, 255 - ij * 3, 0, ij * 3); 27 | else 28 | if(ij < 170) 29 | state->setRgb(i, 0, (ij - 85) * 3, 255 - (ij - 85) * 3); 30 | else 31 | state->setRgb(i, (ij -170) * 3, 255 - (ij -170) * 3, 0); 32 | } 33 | } 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /SimpleRGBFunction.h: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | #include "LedFunction.h" 11 | 12 | class SimpleRGBFunction: public LedFunction 13 | { 14 | public: 15 | uint8_t rgb[3] = {0, 0, 0}; 16 | bool set = false; 17 | 18 | SimpleRGBFunction() 19 | { 20 | } 21 | 22 | virtual bool init(ESP8266WebServer &server) 23 | { 24 | if(!loadRGBValues(server, rgb)) 25 | return false; 26 | } 27 | 28 | virtual void render() 29 | { 30 | if(set) 31 | return; 32 | for(int i = 0; i < state->count; i++) 33 | state->setRgb(i, rgb[0], rgb[1], rgb[2]); 34 | set = true; 35 | } 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /WaveFunction.h: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | #include 11 | #include "LedFunction.h" 12 | 13 | class WaveFunction: public LedFunction 14 | { 15 | public: 16 | long sinTab[256]; 17 | uint8_t rgb[3] = {0, 0, 0}; 18 | int start; 19 | WaveFunction() 20 | { 21 | for(int i = 0; i < 256; i++) 22 | sinTab[i] = sin(3.1415 / 128 * i) * 0x7fff + 0x8000; 23 | start = millis(); 24 | } 25 | 26 | virtual bool init(ESP8266WebServer &server) 27 | { 28 | if(!loadRGBValues(server, rgb)) 29 | return false; 30 | } 31 | 32 | virtual void render() 33 | { 34 | int j = ((millis() - start) / 63) & 255; 35 | int k = ((millis() - start) / 71) & 255; 36 | for(int i = 0; i < state->count; i++) 37 | { 38 | long s = (sinTab[(i*3 + j) & 255] >> 8) * (sinTab[-(i*4 + k) & 255] >> 8); 39 | state->setRgb(i, (rgb[0] * s) >> 16, (rgb[1] * s) >> 16, (rgb[2] * s) >> 16); 40 | } 41 | } 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /bitluniHomeAutomation.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Released under Creative Commons Attribution 4.0 3 | by bitluni 2016 4 | https://creativecommons.org/licenses/by/4.0/ 5 | Attribution means you can use it however you like as long you 6 | mention that it's base on my stuff. 7 | I'll be pleased if you'd do it by sharing http://youtube.com/bitlunislab 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "PinStates.h" 18 | #include "LedStates.h" 19 | #include "Fader.h" 20 | #include "RainbowFunction.h" 21 | #include "SimpleRGBFunction.h" 22 | #include "WaveFunction.h" 23 | #include "RF.h" 24 | 25 | const char* ssid = "..."; 26 | const char* password = "..."; 27 | 28 | ESP8266WebServer server(80); 29 | 30 | const int LED_PIN = D4; 31 | const int LED_COUNT = 300; 32 | 33 | const int RF_OSC = 200; 34 | 35 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); 36 | 37 | LedStates currentLedStates(strip); 38 | LedStates targetLedStates(strip); 39 | Fader ledFader(currentLedStates, targetLedStates); 40 | PinStates currentPinStates; 41 | PinStates targetPinStates; 42 | Fader pinFader(currentPinStates, targetPinStates); 43 | 44 | void handleRoot() { 45 | String message = "Following functions are available:

"; 46 | message += "/rainbow a rainbow animation on LEDs
"; 47 | message += "/wave a slow wave animation on LED on base color specified by arguments: r=<0..255> g=<0..255> b=<0..255>
"; 48 | message += "/setleds sets LEDs to the color from arguments: r=<0..255> g=<0..255> b=<0..255>
"; 49 | message += "/ledsoff turns off LEDs
"; 50 | message += "/setpins sets to any of the in arguments specified pins (D0..D8) to their PWM values (0..1023). To use them digital: 0=off, 1023=on
"; 51 | message += "/togglepins inverts all pin values form pins used before.
"; 52 | message += "/rf sends a rf code from arguments: D=<0..8> t=<0..1000> id=<0..1048575> channel=<0..2> on=<0..1>. Dx is the pin, t is the optional signal clock(default is 200, works for me)

"; 53 | message += "All functions except togglepins and rf support the argument 'fade' which specifies the milliseconds it takes to fade to the new specified state. ...nice blending ;-)
"; 54 | message += "
Syntax is as follows: http://<ip>/<command>?<argument1>=<value1>&<argument2>=<value2>&...
"; 55 | message += "You can click on each link to see an example.

"; 56 | message += "have fun -bitluni"; 57 | server.send(200, "text/html", message); 58 | } 59 | 60 | void handleNotFound(){ 61 | String message = "File Not Found\n\n"; 62 | message += "URI: "; 63 | message += server.uri(); 64 | message += "\nMethod: "; 65 | message += (server.method() == HTTP_GET)?"GET":"POST"; 66 | message += "\nArguments: "; 67 | message += server.args(); 68 | message += "\n"; 69 | for (uint8_t i=0; i -1) 87 | { 88 | targetLedStates.setFunction(f); 89 | ledFader.start(fade); 90 | } 91 | else 92 | currentLedStates.setFunction(f); 93 | } 94 | 95 | void handleRf() 96 | { 97 | const int pinNumbers[] = {D0, D1, D2, D3, D4, D5, D6, D7, D8}; 98 | int pin = getArgValue("D"); 99 | int t = getArgValue("t"); 100 | if(t == -1) t = RF_OSC; 101 | int id = getArgValue("id"); 102 | int ch = getArgValue("channel"); 103 | int on = getArgValue("on"); 104 | String out = "rf D"; 105 | out += pin; 106 | out += " "; 107 | out += t; 108 | out += " "; 109 | out += id; 110 | out += " "; 111 | out += ch; 112 | out += " "; 113 | out += on; 114 | pinMode(pinNumbers[pin], OUTPUT); 115 | for(int i = 0; i < 5; i++) 116 | rfWriteCode(pinNumbers[pin], t, id, (1 << (ch + 1)) | (on > 0? 1: 0)); 117 | server.send(200, "text/plain", out); 118 | } 119 | 120 | void setup(void){ 121 | Serial.begin(115200); 122 | WiFi.begin(ssid, password); 123 | Serial.println(""); 124 | 125 | // Wait for connection 126 | while (WiFi.status() != WL_CONNECTED) { 127 | delay(500); 128 | Serial.print("."); 129 | } 130 | Serial.println(""); 131 | Serial.print("Connected to "); 132 | Serial.println(ssid); 133 | Serial.print("IP address: "); 134 | Serial.println(WiFi.localIP()); 135 | 136 | //find it as http://lights.local 137 | /*if (MDNS.begin("lights")) 138 | { 139 | Serial.println("MDNS responder started"); 140 | }*/ 141 | 142 | server.on("/", handleRoot); 143 | 144 | server.on("/rainbow", [](){ 145 | server.send(200, "text/plain", "rainbow"); 146 | checkFadeAndSetLedFunction(new RainbowFunction()); 147 | }); 148 | 149 | server.on("/wave", [](){ 150 | server.send(200, "text/plain", "wave"); 151 | WaveFunction *f = new WaveFunction(); 152 | f->init(server); 153 | checkFadeAndSetLedFunction(f); 154 | }); 155 | 156 | server.on("/setleds", [](){ 157 | server.send(200, "text/plain", "setleds"); 158 | SimpleRGBFunction *f = new SimpleRGBFunction(); 159 | f->init(server); 160 | checkFadeAndSetLedFunction(f); 161 | }); 162 | 163 | server.on("/ledsoff", [](){ 164 | server.send(200, "text/plain", "ledsoff"); 165 | checkFadeAndSetLedFunction(new SimpleRGBFunction()); 166 | }); 167 | 168 | server.on("/togglepins", [](){ 169 | server.send(200, "text/plain", "togglepins"); 170 | currentPinStates.toggle(); 171 | currentPinStates.commit(); 172 | }); 173 | 174 | server.on("/setpins", [](){ 175 | server.send(200, "text/plain", "setpins"); 176 | int fade = getArgValue("fade"); 177 | if(fade > -1) 178 | { 179 | targetPinStates.loadValues(server); 180 | pinFader.start(fade); 181 | } 182 | else 183 | { 184 | currentPinStates.loadValues(server); 185 | currentPinStates.commit(); 186 | } 187 | }); 188 | 189 | server.on("/pinsoff", [](){ 190 | server.send(200, "text/plain", "pinsoff"); 191 | currentPinStates.setAllTo(0); 192 | currentPinStates.commit(); 193 | }); 194 | 195 | server.on("/rf", handleRf); 196 | 197 | server.onNotFound(handleNotFound); 198 | 199 | server.begin(); 200 | Serial.println("HTTP server started"); 201 | 202 | strip.begin(); 203 | strip.show(); // Initialize all pixels to 'off' 204 | } 205 | 206 | void loop(void) 207 | { 208 | server.handleClient(); 209 | //MDNS.update(); 210 | currentLedStates.render(); 211 | if(ledFader.active) 212 | targetLedStates.render(); 213 | if(!ledFader.fade()) 214 | currentLedStates.commit(); 215 | pinFader.fade(); 216 | } 217 | --------------------------------------------------------------------------------