Skip to content

Instantly share code, notes, and snippets.

@kakopappa
Last active January 14, 2019 01:53
Show Gist options
  • Save kakopappa/02299a8ceec0aaaad3ea3bd7f2af8066 to your computer and use it in GitHub Desktop.
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());
}
@RafVerzet
Copy link

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!

@Stuckeymax
Copy link

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

@peteiwenzel
Copy link

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 ..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment