-
-
Save kakopappa/02299a8ceec0aaaad3ea3bd7f2af8066 to your computer and use it in GitHub Desktop.
#include <ESP8266WiFi.h> | |
#include <ESP8266WebServer.h> | |
#include <WiFiUdp.h> | |
#include <functional> | |
void prepareIds(); | |
boolean connectWifi(); | |
boolean connectUDP(); | |
void startHttpServer(); | |
void turnOnRelay(); | |
void turnOffRelay(); | |
void sendRelayState(); | |
const char* ssid = "************"; | |
const char* password = "********"; | |
String friendlyName = "box"; // Alexa device name | |
unsigned int localPort = 1900; // local port to listen on | |
WiFiUDP UDP; | |
boolean udpConnected = false; | |
IPAddress ipMulti(239, 255, 255, 250); | |
unsigned int portMulti = 1900; // local port to listen on | |
ESP8266WebServer HTTP(80); | |
boolean wifiConnected = false; | |
boolean relayState = false; | |
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, | |
String serial; | |
String persistent_uuid; | |
const int relayPin = D1; | |
boolean cannotConnectToWifi = false; | |
void setup() { | |
Serial.begin(115200); | |
// Setup Relay | |
pinMode(relayPin, OUTPUT); | |
prepareIds(); | |
// Initialise wifi connection | |
wifiConnected = connectWifi(); | |
// only proceed if wifi connection successful | |
if(wifiConnected){ | |
Serial.println("Ask Alexa to discover devices"); | |
udpConnected = connectUDP(); | |
if (udpConnected){ | |
// initialise pins if needed | |
startHttpServer(); | |
} | |
} | |
} | |
void loop() { | |
HTTP.handleClient(); | |
delay(1); | |
// if there's data available, read a packet | |
// check if the WiFi and UDP connections were successful | |
if(wifiConnected){ | |
if(udpConnected){ | |
// if there’s data available, read a packet | |
int packetSize = UDP.parsePacket(); | |
if(packetSize) { | |
//Serial.println(""); | |
//Serial.print("Received packet of size "); | |
//Serial.println(packetSize); | |
//Serial.print("From "); | |
IPAddress remote = UDP.remoteIP(); | |
for (int i =0; i < 4; i++) { | |
Serial.print(remote[i], DEC); | |
if (i < 3) { | |
Serial.print("."); | |
} | |
} | |
Serial.print(", port "); | |
Serial.println(UDP.remotePort()); | |
int len = UDP.read(packetBuffer, 255); | |
if (len > 0) { | |
packetBuffer[len] = 0; | |
} | |
String request = packetBuffer; | |
//Serial.println("Request:"); | |
//Serial.println(request); | |
if(request.indexOf('M-SEARCH') > 0) { | |
if(request.indexOf("urn:Belkin:device:**") > 0) { | |
Serial.println("Responding to search request ..."); | |
respondToSearch(); | |
} | |
} | |
} | |
delay(10); | |
} | |
} else { | |
// Turn on/off to indicate cannot connect .. | |
} | |
} | |
void prepareIds() { | |
uint32_t chipId = ESP.getChipId(); | |
char uuid[64]; | |
sprintf_P(uuid, PSTR("38323636-4558-4dda-9188-cda0e6%02x%02x%02x"), | |
(uint16_t) ((chipId >> 16) & 0xff), | |
(uint16_t) ((chipId >> 8) & 0xff), | |
(uint16_t) chipId & 0xff); | |
serial = String(uuid); | |
persistent_uuid = "Socket-1_0-" + serial; | |
} | |
void respondToSearch() { | |
Serial.println(""); | |
Serial.print("Sending response to "); | |
Serial.println(UDP.remoteIP()); | |
Serial.print("Port : "); | |
Serial.println(UDP.remotePort()); | |
IPAddress localIP = WiFi.localIP(); | |
char s[16]; | |
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); | |
String response = | |
"HTTP/1.1 200 OK\r\n" | |
"CACHE-CONTROL: max-age=86400\r\n" | |
"DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" | |
"EXT:\r\n" | |
"LOCATION: http://" + String(s) + ":80/setup.xml\r\n" | |
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" | |
"01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" | |
"SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" | |
"ST: urn:Belkin:device:**\r\n" | |
"USN: uuid:" + persistent_uuid + "::urn:Belkin:device:**\r\n" | |
"X-User-Agent: redsonic\r\n\r\n"; | |
UDP.beginPacket(UDP.remoteIP(), UDP.remotePort()); | |
UDP.write(response.c_str()); | |
UDP.endPacket(); | |
Serial.println("Response sent !"); | |
} | |
void startHttpServer() { | |
HTTP.on("/index.html", HTTP_GET, [](){ | |
Serial.println("Got Request index.html ...\n"); | |
HTTP.send(200, "text/plain", "Hello World!"); | |
}); | |
HTTP.on("/upnp/control/basicevent1", HTTP_POST, []() { | |
Serial.println("########## Responding to /upnp/control/basicevent1 ... ##########"); | |
//for (int x=0; x <= HTTP.args(); x++) { | |
// Serial.println(HTTP.arg(x)); | |
//} | |
String request = HTTP.arg(0); | |
Serial.print("request:"); | |
Serial.println(request); | |
if(request.indexOf("SetBinaryState") >= 0) { | |
if(request.indexOf("<BinaryState>1</BinaryState>") >= 0) { | |
Serial.println("Got Turn on request"); | |
turnOnRelay(); | |
} | |
if(request.indexOf("<BinaryState>0</BinaryState>") >= 0) { | |
Serial.println("Got Turn off request"); | |
turnOffRelay(); | |
} | |
} | |
if(request.indexOf("GetBinaryState") >= 0) { | |
Serial.println("Got binary state request"); | |
sendRelayState(); | |
} | |
HTTP.send(200, "text/plain", ""); | |
}); | |
HTTP.on("/eventservice.xml", HTTP_GET, [](){ | |
Serial.println(" ########## Responding to eventservice.xml ... ########\n"); | |
String eventservice_xml = "<scpd xmlns=\"urn:Belkin:service-1-0\">" | |
"<actionList>" | |
"<action>" | |
"<name>SetBinaryState</name>" | |
"<argumentList>" | |
"<argument>" | |
"<retval/>" | |
"<name>BinaryState</name>" | |
"<relatedStateVariable>BinaryState</relatedStateVariable>" | |
"<direction>in</direction>" | |
"</argument>" | |
"</argumentList>" | |
"</action>" | |
"<action>" | |
"<name>GetBinaryState</name>" | |
"<argumentList>" | |
"<argument>" | |
"<retval/>" | |
"<name>BinaryState</name>" | |
"<relatedStateVariable>BinaryState</relatedStateVariable>" | |
"<direction>out</direction>" | |
"</argument>" | |
"</argumentList>" | |
"</action>" | |
"</actionList>" | |
"<serviceStateTable>" | |
"<stateVariable sendEvents=\"yes\">" | |
"<name>BinaryState</name>" | |
"<dataType>Boolean</dataType>" | |
"<defaultValue>0</defaultValue>" | |
"</stateVariable>" | |
"<stateVariable sendEvents=\"yes\">" | |
"<name>level</name>" | |
"<dataType>string</dataType>" | |
"<defaultValue>0</defaultValue>" | |
"</stateVariable>" | |
"</serviceStateTable>" | |
"</scpd>\r\n" | |
"\r\n"; | |
HTTP.send(200, "text/plain", eventservice_xml.c_str()); | |
}); | |
HTTP.on("/setup.xml", HTTP_GET, [](){ | |
Serial.println(" ########## Responding to setup.xml ... ########\n"); | |
IPAddress localIP = WiFi.localIP(); | |
char s[16]; | |
sprintf(s, "%d.%d.%d.%d", localIP[0], localIP[1], localIP[2], localIP[3]); | |
String setup_xml = "<?xml version=\"1.0\"?>" | |
"<root>" | |
"<device>" | |
"<deviceType>urn:Belkin:device:controllee:1</deviceType>" | |
"<friendlyName>"+ friendlyName +"</friendlyName>" | |
"<manufacturer>Belkin International Inc.</manufacturer>" | |
"<modelName>Socket</modelName>" | |
"<modelNumber>3.1415</modelNumber>" | |
"<modelDescription>Belkin Plugin Socket 1.0</modelDescription>\r\n" | |
"<UDN>uuid:"+ persistent_uuid +"</UDN>" | |
"<serialNumber>221517K0101769</serialNumber>" | |
"<binaryState>0</binaryState>" | |
"<serviceList>" | |
"<service>" | |
"<serviceType>urn:Belkin:service:basicevent:1</serviceType>" | |
"<serviceId>urn:Belkin:serviceId:basicevent1</serviceId>" | |
"<controlURL>/upnp/control/basicevent1</controlURL>" | |
"<eventSubURL>/upnp/event/basicevent1</eventSubURL>" | |
"<SCPDURL>/eventservice.xml</SCPDURL>" | |
"</service>" | |
"</serviceList>" | |
"</device>" | |
"</root>\r\n" | |
"\r\n"; | |
HTTP.send(200, "text/xml", setup_xml.c_str()); | |
Serial.print("Sending :"); | |
Serial.println(setup_xml); | |
}); | |
HTTP.begin(); | |
Serial.println("HTTP Server started .."); | |
} | |
// connect to wifi – returns true if successful or false if not | |
boolean connectWifi(){ | |
boolean state = true; | |
int i = 0; | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(ssid, password); | |
Serial.println(""); | |
Serial.println("Connecting to WiFi"); | |
// Wait for connection | |
Serial.print("Connecting ..."); | |
while (WiFi.status() != WL_CONNECTED) { | |
delay(500); | |
Serial.print("."); | |
if (i > 10){ | |
state = false; | |
break; | |
} | |
i++; | |
} | |
if (state){ | |
Serial.println(""); | |
Serial.print("Connected to "); | |
Serial.println(ssid); | |
Serial.print("IP address: "); | |
Serial.println(WiFi.localIP()); | |
} | |
else { | |
Serial.println(""); | |
Serial.println("Connection failed."); | |
} | |
return state; | |
} | |
boolean connectUDP(){ | |
boolean state = false; | |
Serial.println(""); | |
Serial.println("Connecting to UDP"); | |
if(UDP.beginMulticast(WiFi.localIP(), ipMulti, portMulti)) { | |
Serial.println("Connection successful"); | |
state = true; | |
} | |
else{ | |
Serial.println("Connection failed"); | |
} | |
return state; | |
} | |
void turnOnRelay() { | |
digitalWrite(relayPin, HIGH); // turn on relay with voltage HIGH | |
relayState = true; | |
String body = | |
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body>\r\n" | |
"<u:SetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">\r\n" | |
"<BinaryState>1</BinaryState>\r\n" | |
"</u:SetBinaryStateResponse>\r\n" | |
"</s:Body> </s:Envelope>"; | |
HTTP.send(200, "text/xml", body.c_str()); | |
Serial.print("Sending :"); | |
Serial.println(body); | |
} | |
void turnOffRelay() { | |
digitalWrite(relayPin, LOW); // turn off relay with voltage LOW | |
relayState = false; | |
String body = | |
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body>\r\n" | |
"<u:SetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">\r\n" | |
"<BinaryState>0</BinaryState>\r\n" | |
"</u:SetBinaryStateResponse>\r\n" | |
"</s:Body> </s:Envelope>"; | |
HTTP.send(200, "text/xml", body.c_str()); | |
Serial.print("Sending :"); | |
Serial.println(body); | |
} | |
void sendRelayState() { | |
String body = | |
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body>\r\n" | |
"<u:GetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">\r\n" | |
"<BinaryState>"; | |
body += (relayState ? "1" : "0"); | |
body += "</BinaryState>\r\n" | |
"</u:GetBinaryStateResponse>\r\n" | |
"</s:Body> </s:Envelope>\r\n"; | |
HTTP.send(200, "text/xml", body.c_str()); | |
} |
Hello kakopappa,
The code is working perfectly! However, which parts of the code do I have to change/add in order to control more than 1 relay? Maybe just an overview would help. Thanks!
Hello Kakopappa and all members,
thanks for your code, so far it is the only one I found that works with my Echo Dot v2. But like other members I would like to add more devices to the code. Does anyone have any idea how to add multiple devices? I am using a WeMos D1 lite mini with 9 DO so it would be a petty not to use them all :-)
Thanks for your help!
Hello kakopappa. I adapted your code to press 3 buttons on an RTS Somfy handheld motorized drapery controller, see somfydevkit.ino. Thanks for the hard work. I'm using a NodeMCU v1.0 board, and aside from the occasional crash (perhaps 1 per week or less), it has been working great.
Best regards ;)
Stuckeymax
Hi kakopappa - I've used your code but unfortunately my device is not discover-able by Alexa. Can you suggest what troubleshooting I should do? Below is the serial output. As an FYI it seems that I cannot get any code to work (e.g. FauxmoESP, etc). I am using NodeMCU ESP-12E, ESP8266 v2.4.2, and Arduino IDE 1.8.8.
19:44:43.678 -> ⸮
19:44:43.678 -> Connecting to WiFi
19:44:43.678 -> Connecting ......
19:44:45.184 -> Connected to PetaPepe 2.4
19:44:45.184 -> IP address: 192.168.1.147
19:44:45.184 -> Ask Alexa to discover devices
19:44:45.184 ->
19:44:45.184 -> Connecting to UDP
19:44:45.184 -> Connection successful
19:44:45.184 -> HTTP Server started ..
hi Zaxper95
plz upload code..,my alexa is not discovering NodeMCU
plz hlp