├── LICENSE ├── README.md └── sinric.ino /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Box 2 | ======= 3 | 4 | 5 | ### If you have [issues](https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch/issues/7#issuecomment-355533848) discovering please consider using Sinric Pro Smart Home Skill. 6 | * [sinric.pro](https://sinric.pro) 7 | 8 | I just wanted to build a cheap switch that can be controlled by Amazon Alexa by emulating a delkin device. 9 | 10 | You can do this using WeMos D1-mini + Relay or use SonOff 11 | 12 | 13 | #### Using WeMos D1-mini + Relay 14 | 15 | 1. WeMos D1-mini ($4.00) http://www.aliexpress.com/store/product/D1-mini-Mini-NodeMcu-4M-bytes-Lua-WIFI-Internet-of-Things-development-board-based-ESP8266/1331105_32529101036.html 16 | 17 | 2. WeMos Relay ($1.90) http://www.aliexpress.com/store/product/Relay-Shield-for-WeMos-D1-mini-button/1331105_32596395175.html 18 | 19 | 3. AC-DC HLK-PM01 module voltage 220V to 5V (Optinal) to convert 220V to 5V for D1-mini 20 | 21 | Relay is connected to D1 22 | 23 | To controlled this switch via Amazon Alexa. 24 | 25 | 1. Download the code 26 | 2. Change the WI-FI settings. 27 | 3. Flash 28 | 4. Scan for new devices in Alexa 29 | 5. Say "turn on" box 30 | 31 | #### Do you want to turn on / off your TV, AirCon (AC) using Amazon Alexa? Checkout my latest project https://irdevkit.com/ 32 | 33 | #### Looking for a Cheap DIY Alexa Light Bulb? 34 | https://github.com/kakopappa/ble-light-bulb 35 | 36 | ##Using Sonoff## 37 | 38 | Sonoff - Thanks @joeman2116 (https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch/issues/6) 39 | 40 | Use a ftdi 3.3v as the programmer. 41 | I tried type as a generic 1meg flash but got reboot problems. 42 | So I used the nodemcu.9 choice. 43 | 44 | Change the ssid 45 | Change password 46 | Change device name to (your choice) 47 | 48 | Pins 49 | I use d6 for the relay and d7 for the led.(optional) 50 | 51 | If you want to control more than 1 switch checkout my other project 52 | https://github.com/kakopappa/arduino-esp8266-alexa-multiple-wemo-switch 53 | 54 | #### Articles about the Wemos switch 55 | * [monkeytypewritr](https://medium.com/@monkeytypewritr/amazon-echo-esp8266-iot-a42076daafa5#.oc4od1xa0) 56 | 57 | 58 | 59 | #### Credits 60 | 61 | - makermusings - [GitHub](https://github.com/makermusings/fauxmo) 62 | 63 | #### Developed By 64 | 65 | * Aruna Tennakoon 66 | * [paypal.me/arunat](http://paypal.me/arunat) 67 | * Bitcon ```1JnmhzTbW9MRb7FK7UReoWdUZ18MDn6aNv``` 68 | * 3 beers from [dougstrickland](https://github.com/dougstrickland) - Thanks 69 | * 2 beers from Michael Berna 70 | * 1 beer Keg from Phillip Ryals 71 | * 5 beers from Steven Boger 72 | * 5 Kegs from Robert Paradiso 73 | -------------------------------------------------------------------------------- /sinric.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void prepareIds(); 7 | boolean connectWifi(); 8 | boolean connectUDP(); 9 | void startHttpServer(); 10 | void turnOnRelay(); 11 | void turnOffRelay(); 12 | void sendRelayState(); 13 | 14 | const char* ssid = "************"; // CHANGE: Wifi name 15 | const char* password = "********"; // CHANGE: Wifi password 16 | String friendlyName = "tv"; // CHANGE: Alexa device name 17 | const int relayPin = 5; // D1 pin. More info: https://github.com/esp8266/Arduino/blob/master/variants/d1_mini/pins_arduino.h#L49-L61 18 | 19 | WiFiUDP UDP; 20 | IPAddress ipMulti(239, 255, 255, 250); 21 | ESP8266WebServer HTTP(80); 22 | boolean udpConnected = false; 23 | unsigned int portMulti = 1900; // local port to listen on 24 | unsigned int localPort = 1900; // local port to listen on 25 | boolean wifiConnected = false; 26 | boolean relayState = false; 27 | char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, 28 | String serial; 29 | String persistent_uuid; 30 | boolean cannotConnectToWifi = false; 31 | 32 | void setup() { 33 | Serial.begin(115200); 34 | 35 | // Setup Relay 36 | pinMode(relayPin, OUTPUT); 37 | 38 | prepareIds(); 39 | 40 | // Initialise wifi connection 41 | wifiConnected = connectWifi(); 42 | 43 | // only proceed if wifi connection successful 44 | if(wifiConnected){ 45 | Serial.println("Ask Alexa to discover devices"); 46 | udpConnected = connectUDP(); 47 | 48 | if (udpConnected){ 49 | // initialise pins if needed 50 | startHttpServer(); 51 | } 52 | } 53 | } 54 | 55 | void loop() { 56 | 57 | HTTP.handleClient(); 58 | delay(1); 59 | 60 | 61 | // if there's data available, read a packet 62 | // check if the WiFi and UDP connections were successful 63 | if(wifiConnected){ 64 | if(udpConnected){ 65 | // if there’s data available, read a packet 66 | int packetSize = UDP.parsePacket(); 67 | 68 | if(packetSize) { 69 | //Serial.println(""); 70 | //Serial.print("Received packet of size "); 71 | //Serial.println(packetSize); 72 | //Serial.print("From "); 73 | IPAddress remote = UDP.remoteIP(); 74 | 75 | for (int i =0; i < 4; i++) { 76 | Serial.print(remote[i], DEC); 77 | if (i < 3) { 78 | Serial.print("."); 79 | } 80 | } 81 | 82 | Serial.print(", port "); 83 | Serial.println(UDP.remotePort()); 84 | 85 | int len = UDP.read(packetBuffer, 255); 86 | 87 | if (len > 0) { 88 | packetBuffer[len] = 0; 89 | } 90 | 91 | String request = packetBuffer; 92 | //Serial.println("Request:"); 93 | //Serial.println(request); 94 | 95 | // Issue https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch/issues/24 fix 96 | if(request.indexOf("M-SEARCH") >= 0) { 97 | // Issue https://github.com/kakopappa/arduino-esp8266-alexa-multiple-wemo-switch/issues/22 fix 98 | //if(request.indexOf("urn:Belkin:device:**") > 0) { 99 | if((request.indexOf("urn:Belkin:device:**") > 0) || (request.indexOf("ssdp:all") > 0) || (request.indexOf("upnp:rootdevice") > 0)) { 100 | Serial.println("Responding to search request ..."); 101 | respondToSearch(); 102 | } 103 | } 104 | } 105 | 106 | delay(10); 107 | } 108 | } else { 109 | Serial.println("Cannot connect to Wifi"); 110 | } 111 | } 112 | 113 | void prepareIds() { 114 | uint32_t chipId = ESP.getChipId(); 115 | char uuid[64]; 116 | sprintf_P(uuid, PSTR("38323636-4558-4dda-9188-cda0e6%02x%02x%02x"), 117 | (uint16_t) ((chipId >> 16) & 0xff), 118 | (uint16_t) ((chipId >> 8) & 0xff), 119 | (uint16_t) chipId & 0xff); 120 | 121 | serial = String(uuid); 122 | persistent_uuid = "Socket-1_0-" + serial; 123 | } 124 | 125 | void respondToSearch() { 126 | Serial.println(""); 127 | Serial.print("Sending response to "); 128 | Serial.println(UDP.remoteIP()); 129 | Serial.print("Port : "); 130 | Serial.println(UDP.remotePort()); 131 | 132 | IPAddress localIP = WiFi.localIP(); 133 | char s[16]; 134 | sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); 135 | 136 | String response = 137 | "HTTP/1.1 200 OK\r\n" 138 | "CACHE-CONTROL: max-age=86400\r\n" 139 | "DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" 140 | "EXT:\r\n" 141 | "LOCATION: http://" + String(s) + ":80/setup.xml\r\n" 142 | "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" 143 | "01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" 144 | "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" 145 | "ST: urn:Belkin:device:**\r\n" 146 | "USN: uuid:" + persistent_uuid + "::urn:Belkin:device:**\r\n" 147 | "X-User-Agent: redsonic\r\n\r\n"; 148 | 149 | // Try changing to this if you have problems discovering 150 | /* https://github.com/kakopappa/arduino-esp8266-alexa-wemo-switch/issues/26 151 | String response = 152 | "HTTP/1.1 200 OK\r\n" 153 | "CACHE-CONTROL: max-age=86400\r\n" 154 | "DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" 155 | "EXT:\r\n" 156 | "LOCATION: http://" + String(s) + ":80/setup.xml\r\n" 157 | "OPT: "http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" 158 | "01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" 159 | "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" 160 | "ST: ssdp:all\r\n" 161 | "USN: uuid:" + persistent_uuid + "::upnp:rootdevice\r\n" 162 | "X-User-Agent: redsonic\r\n\r\n"; 163 | */ 164 | 165 | UDP.beginPacket(UDP.remoteIP(), UDP.remotePort()); 166 | UDP.write(response.c_str()); 167 | UDP.endPacket(); 168 | 169 | /* add yield to fix UDP sending response. For more informations : https://www.tabsoverspaces.com/233359-udp-packets-not-sent-from-esp-8266-solved */ 170 | yield(); 171 | 172 | Serial.println("Response sent !"); 173 | } 174 | 175 | void startHttpServer() { 176 | HTTP.on("/index.html", HTTP_GET, [](){ 177 | Serial.println("Got Request index.html ...\n"); 178 | HTTP.send(200, "text/plain", "Hello World!"); 179 | }); 180 | 181 | HTTP.on("/upnp/control/basicevent1", HTTP_POST, []() { 182 | Serial.println("########## Responding to /upnp/control/basicevent1 ... ##########"); 183 | 184 | //for (int x=0; x <= HTTP.args(); x++) { 185 | // Serial.println(HTTP.arg(x)); 186 | //} 187 | 188 | String request = HTTP.arg(0); 189 | Serial.print("request:"); 190 | Serial.println(request); 191 | 192 | if(request.indexOf("SetBinaryState") >= 0) { 193 | if(request.indexOf("1") >= 0) { 194 | Serial.println("Got Turn on request"); 195 | turnOnRelay(); 196 | } 197 | 198 | if(request.indexOf("0") >= 0) { 199 | Serial.println("Got Turn off request"); 200 | turnOffRelay(); 201 | } 202 | } 203 | 204 | if(request.indexOf("GetBinaryState") >= 0) { 205 | Serial.println("Got binary state request"); 206 | sendRelayState(); 207 | } 208 | 209 | HTTP.send(200, "text/plain", ""); 210 | }); 211 | 212 | HTTP.on("/eventservice.xml", HTTP_GET, [](){ 213 | Serial.println(" ########## Responding to eventservice.xml ... ########\n"); 214 | 215 | String eventservice_xml = "" 216 | "" 217 | "" 218 | "SetBinaryState" 219 | "" 220 | "" 221 | "" 222 | "BinaryState" 223 | "BinaryState" 224 | "in" 225 | "" 226 | "" 227 | "" 228 | "" 229 | "GetBinaryState" 230 | "" 231 | "" 232 | "" 233 | "BinaryState" 234 | "BinaryState" 235 | "out" 236 | "" 237 | "" 238 | "" 239 | "" 240 | "" 241 | "" 242 | "BinaryState" 243 | "Boolean" 244 | "0" 245 | "" 246 | "" 247 | "level" 248 | "string" 249 | "0" 250 | "" 251 | "" 252 | "\r\n" 253 | "\r\n"; 254 | 255 | HTTP.send(200, "text/plain", eventservice_xml.c_str()); 256 | }); 257 | 258 | HTTP.on("/setup.xml", HTTP_GET, [](){ 259 | Serial.println(" ########## Responding to setup.xml ... ########\n"); 260 | 261 | IPAddress localIP = WiFi.localIP(); 262 | char s[16]; 263 | sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); 264 | 265 | String setup_xml = "" 266 | "" 267 | "" 268 | "urn:Belkin:device:controllee:1" 269 | ""+ friendlyName +"" 270 | "Belkin International Inc." 271 | "Socket" 272 | "3.1415" 273 | "Belkin Plugin Socket 1.0\r\n" 274 | "uuid:"+ persistent_uuid +"" 275 | "221517K0101769" 276 | "0" 277 | "" 278 | "" 279 | "urn:Belkin:service:basicevent:1" 280 | "urn:Belkin:serviceId:basicevent1" 281 | "/upnp/control/basicevent1" 282 | "/upnp/event/basicevent1" 283 | "/eventservice.xml" 284 | "" 285 | "" 286 | "" 287 | "\r\n" 288 | "\r\n"; 289 | 290 | HTTP.send(200, "text/xml", setup_xml.c_str()); 291 | 292 | Serial.print("Sending :"); 293 | Serial.println(setup_xml); 294 | }); 295 | 296 | // openHAB support 297 | HTTP.on("/on.html", HTTP_GET, [](){ 298 | Serial.println("Got Turn on request"); 299 | HTTP.send(200, "text/plain", "turned on"); 300 | turnOnRelay(); 301 | }); 302 | 303 | HTTP.on("/off.html", HTTP_GET, [](){ 304 | Serial.println("Got Turn off request"); 305 | HTTP.send(200, "text/plain", "turned off"); 306 | turnOffRelay(); 307 | }); 308 | 309 | HTTP.on("/status.html", HTTP_GET, [](){ 310 | Serial.println("Got status request"); 311 | 312 | String statrespone = "0"; 313 | if (relayState) { 314 | statrespone = "1"; 315 | } 316 | HTTP.send(200, "text/plain", statrespone); 317 | 318 | }); 319 | 320 | HTTP.begin(); 321 | Serial.println("HTTP Server started .."); 322 | } 323 | 324 | // connect to wifi – returns true if successful or false if not 325 | boolean connectWifi(){ 326 | boolean state = true; 327 | int i = 0; 328 | 329 | WiFi.mode(WIFI_STA); 330 | WiFi.begin(ssid, password); 331 | Serial.println(""); 332 | Serial.println("Connecting to WiFi"); 333 | 334 | // Wait for connection 335 | Serial.print("Connecting ..."); 336 | while (WiFi.status() != WL_CONNECTED) { 337 | delay(500); 338 | Serial.print("."); 339 | if (i > 10){ 340 | state = false; 341 | break; 342 | } 343 | i++; 344 | } 345 | 346 | if (state){ 347 | Serial.println(""); 348 | Serial.print("Connected to "); 349 | Serial.println(ssid); 350 | Serial.print("IP address: "); 351 | Serial.println(WiFi.localIP()); 352 | } 353 | else { 354 | Serial.println(""); 355 | Serial.println("Connection failed."); 356 | } 357 | 358 | return state; 359 | } 360 | 361 | boolean connectUDP(){ 362 | boolean state = false; 363 | 364 | Serial.println(""); 365 | Serial.println("Connecting to UDP"); 366 | 367 | if(UDP.beginMulticast(WiFi.localIP(), ipMulti, portMulti)) { 368 | Serial.println("Connection successful"); 369 | state = true; 370 | } 371 | else{ 372 | Serial.println("Connection failed"); 373 | } 374 | 375 | return state; 376 | } 377 | 378 | void turnOnRelay() { 379 | digitalWrite(relayPin, HIGH); // turn on relay with voltage HIGH 380 | relayState = true; 381 | 382 | String body = 383 | "\r\n" 384 | "\r\n" 385 | "1\r\n" 386 | "\r\n" 387 | " "; 388 | 389 | HTTP.send(200, "text/xml", body.c_str()); 390 | 391 | Serial.print("Sending :"); 392 | Serial.println(body); 393 | } 394 | 395 | void turnOffRelay() { 396 | digitalWrite(relayPin, LOW); // turn off relay with voltage LOW 397 | relayState = false; 398 | 399 | String body = 400 | "\r\n" 401 | "\r\n" 402 | "0\r\n" 403 | "\r\n" 404 | " "; 405 | 406 | HTTP.send(200, "text/xml", body.c_str()); 407 | 408 | Serial.print("Sending :"); 409 | Serial.println(body); 410 | } 411 | 412 | void sendRelayState() { 413 | 414 | String body = 415 | "\r\n" 416 | "\r\n" 417 | ""; 418 | 419 | body += (relayState ? "1" : "0"); 420 | 421 | body += "\r\n" 422 | "\r\n" 423 | " \r\n"; 424 | 425 | HTTP.send(200, "text/xml", body.c_str()); 426 | } 427 | --------------------------------------------------------------------------------