Wemo Socket IoT

 

        This project shows you how to take control of your Wemo Socket (http://www.belkin.com/us/p/P-F7C027/). This is important if you want to mix sensors and actuators from different providers. Wemo products are very nice but.like any other system they are part of a relatively closed system. You cannot add your own devices from other third parties into a single application. Also you have the limited functionality provided with the Wemo App. One more reason to connect this devices to IC2Cloud.

        In essece this project is a "hub" that will communicate with your Wemo switch and control it based on commands sent from your own App.

    Complexity

 
Project  Wemo Socket
Time to build  1 hour
Cost $60
Arduino Code download
Cloud Device Code N/A
Mobile App Code download

 

       We have opted to use an Arduino Due in this project because it has more memory and CPU power compared to an Arduino Uno. A very good option would have been a Raspberry Pi which would have been more cost effective. But we prefered to use an Arduino board to keep consistent with the other applications and in preparations for more complex system where we will add our own sensors. At that point a Raspberry Pi might have not been the best option anyway. Still the exact same functionality can be implemented with any Raspberry Pi. The code is straight forward and for whoever has the knowledge to program the Raspberry Pi it can be done relatively simple.

 

    Hardware

        Here you have the list of components required to build the hardware. We assume you have a computer, a router and a network cable. If you purchase the Arduino board from the specified source it comes with the USB cable. Otherwise you might need to provide a USB cable. You can find better options for hardware. Be careful what components you buy. For Ethernet boards there are many versions with different chipsets but only the standard Arduino Shield or equivalent works with the software provided here. The prices are just a guide. These are the values we found when we created this project.

  
Picture Name Price Source

Arduino Due

 $50 Link
Arduino Ethernet Shield $10 Link

 

        Here you have a picture with the components we used:

 

 

 

     Specifications

       This section shows the pinout of the components or provide a link to the specifications of the component.

 

Component Name Pins
Arduino Due See: http://arduino.cc/en/Main/arduinoBoardDue
Arduino Ethernet See: http://arduino.cc/en/Main/ArduinoEthernetShield

 

     Wiring

        This section shows you how to connect the components together. First you have a pin-out connection and below there are pictures showing how it's done on the real boards.

 
Arduino Ethernet Arduino Due
Just insert the Ethernet Shield into Arduino Due
 

       Here you have pictures with the wiring

 

 

 

        There is no particular schematic for this project. Details of what exactly is connected from the Ethernet board to the Arduino Due board can be found on the www.arduino.cc.

 

    Software

        To run the system you need two software components. One is the software to run the hardware (Arduino Software) and the other one is the mobile App (Mobile App).

 

     Arduino Software

        To run the software you need the Arduino development environment. THERE IS A VERY IMPORTANT TRICK HERE. The .default Ethernet library does not support UDP multicast. We had to modify 2 files in the Arduino Ethernet library.

        In the folder: C:\Program Files (x86)\Arduino\libraries\Ethernet\src (assuming you insatlled Arduino in Windows in C:\Program Files (x86)\Arduino) you need to open the file  EthernetUdp.cpp and add next method at the end of the file

uint8_t EthernetUDP::beginMulti(IPAddress ip, uint16_t port) {
  if (_sock != MAX_SOCK_NUM)
    return 0;

  for (int i = 0; i < MAX_SOCK_NUM; i++) {
    uint8_t s = W5100.readSnSR(i);
    if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) {
      _sock = i;
      break;
    }
  }

  if (_sock == MAX_SOCK_NUM)
    return 0;
 
 
  // Calculate MAC address from Multicast IP Address
  byte mac[] = {  0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 };
 
  mac[3] = ip[1] & 0x7F;
  mac[4] = ip[2];
  mac[5] = ip[3];

  W5100.writeSnDIPR(_sock, rawIPAddress(ip));   //239.255.0.1
  W5100.writeSnDPORT(_sock, port);
  W5100.writeSnDHAR(_sock,mac);
 
  _remaining = 0;

  socket(_sock, SnMR::UDP, port, SnMR::MULTI);

  return 1;
}

 

        You have to add this new method to the Udp class. For that open the file: EthernetUdp.h and add next line immediately after the constructor

   virtual uint8_t beginMulti(IPAddress, uint16_t);

 

        You should end with something like:

 

public:
  EthernetUDP();  // Constructor
  virtual uint8_t beginMulti(IPAddress, uint16_t);

 

 

        You have to load the software provided here and configure it. These are the values you need to change in the code.

 

Field Description
device_id

This is a unique id you have to create for your hardware device to identify it from others in the cloud. Use this www.guidgenerator.com to generate GUID (Global Unique ID)

String devId      = "b5ebf7a7-e63a-4660-8f83-cf1e5d39cf5b";
auth

This is the authentication string requred for basic authentication of the http requests. Create a string from <email_address>:<API_Password> and encode it base64. The sample here "dXNlcjpwYXNzd29yZA==" is the encoding for "user:password"

String auth    = "dXNlcjpwYXNzd29yZA==";

 

       And here you have the sketch (download)

 

#include <SPI.h>
#include <Ethernet.h>
#include <Udp.h>

#define MAX_BUFF 1024                                             // maximum buffer size to read UDP packets
#define MX_SEARCH 10000                                           // Maximum time to wait for a response to an UPnP search request. 

byte mac[]     = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

char server[]  = "www.ic2pro.com";                          // Wiring Cloud host name
int port       = 80;
String devId   = "111-222-333";                             // Device ID. CREATE YOUR OWN GUID; Use this http://www.guidgenerator.com/
String auth    = "dXNlcjpwYXNzd29yZA==";                    // Authentication credentials Create a string from <email_address>:<API_Password> and encode it base64
                                                            // The sample here "dXNlcjpwYXNzd29yZA==" is the encoding for "user:password"

String swuuid  = "";                                              // Wemo switch ID (printed on the device). Set in the cloud

IPAddress ip(192, 168, 1, 50);                                    // fallback IP address in case DHCP fails
IPAddress upnp_ip(239, 255, 255, 250);                            // UPnP IP address to send the multicast search UPnP

String       switchIP;                                            // the IP for the Wemo switch
unsigned int switchPort;                                          // the port for the Wemo switch

String switchCurrentState;                                        // current state of the switch
String switchState;                                               // new state retrieved from server

EthernetClient eth;                                               // For HTTP requests 
EthernetUDP Udp;                                                  // For UDP connection

char buff[MAX_BUFF];                                              // buffer for the UDP packets

//  Read the response data from an HTTP request
String readData()
{
  while (eth.connected() && !eth.available()) delay(1);          // wait for data

  String buff = "";                                              // read data in the variable buff
  while (eth.connected() || eth.available()) {
    char c = eth.read();
    buff += c;
    Serial.print(c);
  }
  eth.stop();
  Serial.println();
  return buff;
}

//  Request the control signals from server
void getControl()
{
  Serial.println("====> Control");
  if (eth.connect(server, port)) {
    eth.println("GET http://" + String(server) + ":" + String(port) + "/Wire/connector/get?id=" + devId + "&SW&WEMOID HTTP/1.1");   // just get the switch status
    eth.println("Authorization: Basic " + auth);
    eth.println("Connection: close");
    eth.println();
  }
  else {
    Serial.println("CON ERR");
  }
}


//  Update state for switch (Wemo Switch)
void updateSwitch()
{
  Serial.println("SW-" + switchState);

  char txt[20];
  switchIP.toCharArray(txt, 20);
  if (eth.connect(txt, switchPort)) {
    eth.println("POST /upnp/control/basicevent1 HTTP/1.1");
    eth.println("Host: " + switchIP);
    eth.println("SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\"");
    eth.println("Content-type: text/xml; charset=\"utf-8\"");
    eth.println("Connection: close");
    eth.print("Content-Length: ");
    eth.println(262);
    eth.println();
    eth.print("<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>");
    eth.print(switchState);
    eth.println("</BinaryState></u:SetBinaryState></s:Body></s:Envelope>");
  }
  else {
    Serial.println("CC ERR");
  }
}

// Extract values from an HTTP response message 
void extractValues(String msg)
{
  String paramName;
  String paramValue;
  int pos1, pos2;

  if (msg != "")
  {
    // extract fields from received message
    pos2 = 0;
    pos1 = msg.indexOf("SW,");
    if(pos1 > 0) {
      pos2 = msg.indexOf("\n", pos1);
      paramValue = msg.substring(pos1, pos2);                           // extract value for parameter
      Serial.println(">"+paramValue +"<");
      if(paramValue.indexOf("ON") > 0)
        switchState = "1";
      else
        switchState = "0";
    }

    pos1 = msg.indexOf("WEMOID,");
    if(pos1 > 0) {
      pos2 = msg.indexOf("\n", pos1);
      paramValue = msg.substring(pos1+7, pos2);                           // extract value for parameter
      Serial.println(">"+paramValue +"<");
      swuuid = paramValue;
    }
  }
}

// Send a UPnP request to find the Wemo switch
void searchUPnP()
{
  Udp.beginMulti(upnp_ip, 1900);         // Open multicast UDP connection

  String msg = "M-SEARCH * HTTP/1.1\nHOST: 239.255.255.250:1900\nMAN: \"ssdp:discover\"\nMX: 10\nST: ssdp:all\n\n";
  msg.getBytes((byte*)buff, MAX_BUFF);
  buff[msg.length()] = 0;

  Udp.beginPacket(upnp_ip, 1900);        // send request
  Udp.write(buff, msg.length());
  Udp.endPacket();

  Udp.stop();                            // close Multicast UDP. To get response we have to switch to unicast
}

// Read responses from the UPnP search request
void readUPnP()
{
  int packetSize;;
  long start = millis();
  String msg;
  String wemoIP = "";
  String wemoPort = "";

  Udp.begin(1900);                                                           // Start a UDP unicast connection 
  while ((millis() < start + MX_SEARCH) && ((wemoIP == "")  || (wemoPort == "")))
  {
    packetSize = Udp.parsePacket();
    while (( packetSize > 0 ) && ((wemoIP == "")  || (wemoPort == "")))
    {
      Udp.read(buff, MAX_BUFF);                                              // We've received a packet, read the data from it
      buff[packetSize] = 0;
      msg = String(buff);
      if (msg.indexOf(swuuid) > 0 && msg.indexOf("Belkin") > 0)              // check if the packet is received from a Belkin device with the UUID
      {
        // extract IP & port
        int ipIdx = msg.indexOf("LOCATION: ");                               // find "LOCATION:" line in the packet
        if (ipIdx > 0)
        {
          ipIdx = ipIdx + 17;                                                // move index beyond Length("LOCATION: http://") = 17
          int ipIdxEnd = msg.indexOf(":", ipIdx);
          if (ipIdxEnd > 0)
          {
            wemoIP = msg.substring(ipIdx, ipIdxEnd);                         // extract Wemo IP
            int portIdxEnd = msg.indexOf("/", ipIdxEnd + 1);
            if (portIdxEnd > 0)
              wemoPort = msg.substring(ipIdxEnd + 1, portIdxEnd);            // extract port
          }
        }
      }
      packetSize = Udp.parsePacket();
    }
  }
  Udp.stop();                                                                // stop UDP connection
  switchIP = wemoIP;                                                         
  switchPort = wemoPort.toInt();
  Serial.println("[" + wemoIP + "]");
  Serial.println("[" + wemoPort + "]");
}

// Find the Wemo Switch
void findWemo()
{
  searchUPnP();      // send search request
  readUPnP();        // read response and extract IP & Port
}

void setup() {
  Serial.begin(115200);                               // Initialize debuging

  Serial.println("GO");
  if (Ethernet.begin(mac) == 0) {                   // Initialize Ethernet shield
    Serial.println("ETH ERR");
    Ethernet.begin(mac, ip);                        // If DHCP fails try fix IP
  }
  delay(1000);
  Serial.println(Ethernet.localIP());

  switchCurrentState = "";                          // Set last known state for switch
}

void loop()
{
  String data;
  getControl();                                     // Check on/off "buttons"
  data = readData();
  Serial.println("[" + data + "]");
  if(data != "") {
    extractValues(data);
    if (switchCurrentState != switchState)  {      // check if switch status was changed
      findWemo();                                  // find the wemo switch Ip & Port
      updateSwitch();                              // update switch
      readData();
      switchCurrentState = switchState;
    }
  }
  delay(1000);
}

     Mobile App

 

        The mobile application to control the security system is built with "App Inventor" (ai2.appinventor.mit.edu). If you just want to import the .aia file you can download it here. Otherwise you have to create next components.

 

Component Name Description
Label SwitchMsg A text that shows thestate of the switch (On or Off)
Button OnButton A button that will be used to turn the switch On.
Button OffButtomn A button that will be used to turn the switch off.
Web APIGetSwitchState

A Web component with next URL to retrieve the state of the switch

http://www.ic2pro.com/Wire/connector/get?id=<device_id>&SW

don't forget to replace device_id with the value you set in the Arduino Code

Web APISetOn

A web component to set the switch On

http://www.ic2pro.com/Wire/connector/set?id=<device_id>&SW=ON

don't forget to replace device_id with the value you set in the Arduino Code

Web APISetOff

A web component to set the switch Off

http://www.ic2pro.com/Wire/connector/set?id=<device_id>&SW=OFF

don't forget to replace device_id with the value you set in the Arduino Code

text Box DeviceIdTxt A text component to input the Wemo serial number
Button SetBttn A button to save the the wemo serial number in the cloud

 

        In the code you have to set a proper authentication string. Replace the string "Basic dXNlcjpwYXNzd29yZA==" with your authentication string. Create a string from <email_address>:<API_Password> and encode it base64. The sample here "dXNlcjpwYXNzd29yZA==" is the encoding for "user:password". Make sure you append "Basic " at the begining of it.

        And this is the logic you have to create in App Inventor. Once that is done you just build the APK and install it on your Android phone. We have created two screens. One for controling the socket and the other as a "Settings" section to set the Wemo serial number.

 

 

 

 

    Final Product

       Here you have a picture of the "final" product (basically the Arduino Due with an Ethernet snapped on it), hardware and screenshots from App.

 

 

 

 

        For the system to work you have to set the serial number for the wemo using the "Settings" screen on the App. Be aware that first time you go there you might get an HTTP error but once you set it first time then the error will disepear. To set the Device ID (Wemo Serial NUmber) look on the back of the device for the number shown in the picure below, type it in the text field and save it.