Go Down

Topic: Arduino Sketch to Connect to Belkin Wemo Switches (Read 6053 times) previous topic - next topic

CatweazleNZ

Nov 01, 2015, 04:16 am Last Edit: Nov 01, 2015, 04:26 am by CatweazleNZ
All

A few weeks ago I discovered Belkin Wemo Switches and decided to try and integrate them into my Arduino home automation system.

Wemo switches are just mains power socket adaptors that connect to your home WIFI local area network and can be used to switch lights, heaters, etc off and on within your house and remotely using a free smartphone Wemo app. Without building anything you can use Wemo to turn on and off mains power system appliances - but I wanted to integrate these WIFI devices into my Arduino home automation system so that I could control a 230 volt fan system and my Arduino system's UPS power supply according to my application's needs. (This avoids the cost of using professional electricians to integrate Arduino and mains power devices.)



I researched the subject on the internet and found some examples and the http soap code to switch these devices and extract their current switching status.

Initially I developed a test application that I could operate using the Arduino serial monitor function - once I had a quality code solution I integrated the code into my Arduino home automation system and now it is switching my fan system and UPS power supply on and off as required.

I have placed the Arduino test application in the PUBLIC folder on the SD Card web page of my Arduino home automation system at http://www.2wg.co.nz/ - look for the file WEMOTEST.INO

The Wemo switch testing code is well written and with lots of comments - you should not have much trouble getting it running.

Wemo switches have been around for a couple of years - but I was surprised that very little can be found about them within the Arduino community. So I have tried to fill that gap.

Note that much of the information available on the internet suggests that Wemo switches randomly operate on a variety of internet ports starting at port 49153 - which in turn makes it difficult to build http interfaces to them. My efforts do not confirm that - my two Wemo switches seem to run continuously on port 49153. (Well for more that a week so far.) My code recognises the possibility that Wemo switches may change to alternate ports and will search for them if necessary.

In a following post I will put up a sample serial monitor conversation with the Wemo test program to demonstrate how to use the WEMOTEST.INO sketch to switch Wemo switches on and off from an Arduino sketch.

Cheers

Catweazle NZ


CatweazleNZ

#1
Nov 01, 2015, 04:21 am Last Edit: Nov 01, 2015, 04:22 am by CatweazleNZ
All

Here is a sample serial monitor conversation I had with the two Wemo switches on my network. The two switches were given the name LIGHT and HEATER in the WEMOTEST.INO sketch. Lines prefixed by ">>" are serial monitor data entry commands typed in by me.

Code: [Select]
Arduino/Wemo Switch Integration Test Sketch
Setup ...
Ethernet Begin OK
LIGHT
Current Port: 49153
HEATER
Current Port: 49153
Setup Complete

Please enter <DEVICE> <ON|OFF|STATUS> Commands ...
>> LIGHT STATUS
STATUS: OFF
>> LIGHT ON
Done
>> HEATER STATUS
STATUS: ON
>> HEATER OFF
Done
>> LIGHT OFF
Done
>> LIGHT STATUS
STATUS: OFF
>> HEATER ON
Done
>> HEATER STATUS
STATUS: ON
>> HEATR STATUS
Device Not Found!
>> LIHT STATUS
Device Not Found!
>> LIGHT STATUS
STATUS: OFF
>> HEATER STATUS
STATUS: ON
>> HEATER STATE
Action Must Be ON, OFF or STATUS!
>> HEATER STATUS
STATUS: ON

Cheers

Catweazle NZ

wayt

Excellent sketch, CatweazleNZ!

I've adapted some of your functions to control a Wemo switch over WiFi, using an Arduino Uno attached to Sparkfun's ESP8266 shield (and the library Sparkfun provides for it).

My code is below. It works, but weirdly it takes about 20 seconds to turn the switch on and off. To trace the orgin of the problem, I wired up an LED and added code to flash itas each client.println() executes. I noitced that these statements fire off very slowly--like one every 5 seconds. So the issue seems to be slowness in transmitting the commands to the Wemo.

Is this an issue over Ethernet as well?

If not, do you have any idea what might be causing the slow transmission or suggestions for speeding it up?

Code: [Select]

#include <FreqCounter.h>
#include <SoftwareSerial.h>
#include <SparkFunESP8266WiFi.h>

// Replace these two character strings with the name and
// password of your WiFi network.
const char mySSID[] = "Dangermouse";        // SSID of the WiFi network the system should connect to
const char myPSK[] = "********";            // Pre-shared key for the WiFi network
boolean wifiUp = false;                  // flag for WiFi connection; true = up
const byte switchIPadd[] = {192,168,4,100}; // IP address of the WeMo switch
const IPAddress switchIP = switchIPadd;     // convert type to IPAddress
const unsigned int switchPort = 49153;      // port for WeMO HTTP server
int returnedValue = 0;
int loop_cnt = 0;


 // Define hardware connections
//#define PIN_GATE_IN 2
//#define IRQ_GATE_IN  0

#define PIN_LED_OUT 13
#define PIN_ANALOG_IN A0

//  end global definitions and initialization

//----------------------------------------------------------------------------------------------

void setup()
{
  Serial.begin(9600);

  // Configure LED pin as output
  pinMode(PIN_LED_OUT, OUTPUT);

  while (!wifiUp)
  {
    initializeESP8266();
   
    connectESP8266();    // connects to the defined WiFi network.
  }

  displayConnectInfo();
} // end setup()

//----------------------------------------------------------------------------------------------

void loop()
{
  static int volume = 0;

  //Set threshhold volume; alarm chirp should register louder than this volume
  static int threshhold = 25;               //envelope volume above which is counted as a beep

  static int count = 0;                     // running count of beeps
  static int beepCount = 10;                // number of beeps before slave siren is activated
  static boolean sirenOn = false;           //flag for siren state
  static unsigned long beepStart = 0;       //time when volume last rose above threshhold
  static unsigned long beepEnd = 0;         //time when volume last fell below threshhold
  static int beepFreq;
  static int beepFreqLowerLimit = 100;
  static int beepFreqUpperLimit = 300;
  static unsigned long interval;            //time elapsed between beeps
  static unsigned long beepInterval = 50;   //time (in ms) that must elapse between loud events to be considered a beep
  // end variable initialization

  // Measure frequency of incoming sound
  FreqCounter::f_comp=10;                   // Cal Value / Calibrate with professional Freq Counter
  FreqCounter::start(100);                  // 100 ms Gate Time
  while (FreqCounter::f_ready == 0)
  beepFreq = FreqCounter::f_freq;
 
  // Check the envelope input (i.e., amplitude of sound at microphone)
  volume = analogRead(PIN_ANALOG_IN);

  if(volume > 20)
  {
    Serial.print(F("Level: "));
    Serial.print(volume);
    Serial.print(F(" | Freq: "));
    Serial.println(beepFreq);
    digitalWrite(PIN_LED_OUT,HIGH);
  }
   
  if((volume > threshhold) && (beepFreq < beepFreqUpperLimit) && (beepFreq >= beepFreqLowerLimit))
  {
    beepEnd = beepStart;
    beepStart = millis();
    interval = beepStart - beepEnd;
 
    Serial.print(F("Level: "));
    Serial.print(volume);
    Serial.print(F(" | Gap: "));
    Serial.print(interval);
    Serial.print(F(" | Freq: "));
    Serial.println(beepFreq);

    if((interval < 3*beepInterval) && (interval >= beepInterval))
    {
      count++;
      Serial.print(F(" | Count: "));
      Serial.println(count);   
    }
   
    //light up LED
    digitalWrite(PIN_LED_OUT,HIGH);
  }

  if(volume < threshhold)
  {
    //turn off LED
    digitalWrite(PIN_LED_OUT,LOW);   
    if(((millis() - beepStart) > 3*beepInterval) && sirenOn)
    {
      count = 0;  //reset counter if loud sound goes on too long
      Serial.println(count);

      //silence the siren if on
      if(sirenOn)
        WemoSignal("OFF");

      sirenOn = false;
    }
  }

  if (count >= beepCount)
  {
    Serial.println(count);

    //sound the siren if not on already
    if(!sirenOn)
      WemoSignal("ON");

    sirenOn = true;
  }
  // pause for 0.05 second
  delay(50);
  loop_cnt++;
  if (loop_cnt >= 1000) // every 1,000 times through the loop, verify that we're still connected to WiFi and reset if not
  {
    if (!check_wifi())
      setup();
    loop_cnt = 0;
  }
   
}


//----------------------------------------------------------------------------------------------


// WemoSignal() connects to a WeMo switch and then sends whatever signal is passed to this function

void WemoSignal(const String &action)
{
  ESP8266Client wifiClient;
  if (wifiClient.connect(switchIP, switchPort) < 0)
  {
    Serial.println(F("Failed to connect to switch."));
    return;
  }

  Serial.println(F("Connected to switch."));

  WemoSwitchCommand(wifiClient,action);

  delay(1000);  // give the WeMo time to process the action
 
  wifiClient.stop();
  if (action != "STATUS")
    Serial.println(F("Done"));
} // end WemoSignal()

//----------------------------------------------------------------------------------------------

// WemoSwitchCommand() transmits an ON or OFF command to the WeMo switch
// p_wifiClient should be an active connection to the WeMo, p_action must be ON, OFF, or STATUS

void WemoSwitchCommand(ESP8266Client p_wifiClient, const String &p_action)
{
  if (p_action == "ON")
    Serial.println(F("Switching ON..."));
  else if (p_action == "OFF")
    Serial.println(F("Switching OFF..."));
  else
    Serial.println(F("Getting status..."));

  p_wifiClient.println(F("POST /upnp/control/basicevent1 HTTP/1.1"));
  digitalWrite(PIN_LED_OUT,HIGH);

  p_wifiClient.println(F("Content-Type: text/xml; charset=utf-8"));
  digitalWrite(PIN_LED_OUT,LOW);

  if (p_action != "STATUS") //ON or OFF  (Set)
    p_wifiClient.println(F("SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\""));
  else //STATUS (Get)
    p_wifiClient.println(F("SOAPACTION: \"urn:Belkin:service:basicevent:1#GetBinaryState\""));
  digitalWrite(PIN_LED_OUT,HIGH);

  p_wifiClient.println(F("Connection: keep-alive"));
  digitalWrite(PIN_LED_OUT,LOW);

  p_wifiClient.print(F("Content-Length: "));
  digitalWrite(PIN_LED_OUT,HIGH);

  p_wifiClient.println(243+1+55); //data1.length()); - pre, request, post
  p_wifiClient.println();
  digitalWrite(PIN_LED_OUT,LOW);

  //<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
  p_wifiClient.print(F("<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "));
  digitalWrite(PIN_LED_OUT,HIGH);

  //s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>
  p_wifiClient.print(F("s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body>"));
  digitalWrite(PIN_LED_OUT,LOW);

  //<u:SetBinaryState xmlns:u="urn:Belkin:service:basicevent:1"><BinaryState>
  if (p_action != "STATUS") //ON or OFF (Set)
    p_wifiClient.print(F("<u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>"));
  else //STATUS (Get)
    p_wifiClient.print(F("<u:GetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\"><BinaryState>"));
  digitalWrite(PIN_LED_OUT,HIGH);

  if (p_action != "OFF")
    p_wifiClient.print(F("1")); //ON or STATUS request
  else
    p_wifiClient.print(F("0")); //OFF
  digitalWrite(PIN_LED_OUT,LOW);

  //</BinaryState></u:SetBinaryState></s:Body></s:Envelope>
  if (p_action != "STATUS") //ON or OFF (Set)
    p_wifiClient.print(F("</BinaryState></u:SetBinaryState></s:Body></s:Envelope>"));
  else //STATUS (Get)
    p_wifiClient.print(F("</BinaryState></u:GetBinaryState></s:Body></s:Envelope>"));

  p_wifiClient.println();
} // end WemoSwitchCommand()


wayt

Using a BananaPi Pro I have on the same network as my Arduino and Wemo switch, I altered my code to send the on and off messages to the Pi instead of the Wemo and ran
Code: [Select]
sudo nc -l -p 49153
there so that I could see what the Arduino is actually transmitting.

The results are disappointing: the Arduino sends its data at roughly human typing speed. And in every third or fourth test, a character or two are dropped (at random positions in the message) or the message simply halts partway through.

It seems as though something is very wrong with the client.println() function in the library for the WiFi shield. Or could it be a hardware problem of some kind?

I'd appreciate any suggestions on workarounds or further tests I could do to isolate this problem.

wayt

By pairing down the code to bare bones, I tracked the source of the slow transmission issue: it is the "F()" function used inside the client.println() function. That F() function ensures that the long strings of data are stored in flash memory rather than SRAM, which is very helpful for conserving global variable space.

Unfortunately, there is a huge tradeoff in speed: apparently reading these data strings from flash is veeeerrry sloooow--and worse than that, unreliable, or in any case causes sync or buffering issues that result in bad/incomplete data being sent over the network.

So now my challenge is to rewrite other portions of my code to free up enough global variable space that I can store the data in SRAM as normally declared const char arrays.

CatweazleNZ

#5
Nov 20, 2015, 08:37 pm Last Edit: Nov 20, 2015, 09:10 pm by CatweazleNZ
Hi

Over in this thread you will find a solution to slow F() string processing.

http://forum.arduino.cc/index.php?topic=353860.msg2439384#msg2439384

Even if you reduce the buffer size from 128 bytes to 16 bytes you should still see huge performance benefits.

Cheers

CatweazleNZ

wayt

Thanks, CatWeazleNZ, for that fix--I've replied to your other thread requesting elaboration on how to apply that patch.

In my particular case, I didn't need it because I was able to fit the outbound data in SRAM with enough space left over that the program operates stably. For the benefit of others, my final sketch is attached.

raxpa

Hi guys,
I hope someone can help me 'cause I'm not that clever :)
I'm trying to control a fan depends on air humidity and basically now I need to switch on or off a led depending on my real wemo status

I've managed to connect to my wemo sending commands via shell script ... but I can't  figure how to get wemo status ... I'm stucked any help?

Code: [Select]

void loop() {

  lcd.setCursor(0, 0);
  String dataString;
  dataString += getDateStamp();
  lcd.print(dataString);

  float temperature = dht.readTemperature();  //LEGGO TEMPERATURA DEL SENSORE
  float humidity = dht.readHumidity();        //LEGGO UMIDITÀ DEL SENSORE

  if (humidity > setPoint + isteresi) {
    if (! fSopra) {
      // Non hai ancora mandato l'eMail

      Process p;
      p.runShellCommand("/usr/bin/uta_off.sh");
      statoUTA = "UTA OFF";
      statoU = 0;

      fSopra = true;
    }
    fSotto = false;
  }
  else if (humidity < setPoint - isteresi) {
    if (! fSotto) {
      // Non hai ancora mandato l'eMail per < 68

      Process p;
      p.runShellCommand("/usr/bin/uta_on.sh");


      statoUTA = "UTA ON";
      statoU = 1;

      fSotto = true;
    }
    fSopra = false;
  }

  //********************
  //LEGGO I VALORI DEL POtENZIOMETRO E LI STAMPO A SCHERMO
  //********************
  pot = analogRead(potentiometerPin); // LEGGO IL VALORE DEL PIN A0
  lcd.setCursor(0, 1);                // MI POSIZIONO SULLA SECONDA RIGA DELL'LCD
  lcd.print("                     "); // CANCELLO TUTTA LA RIGA PER EVITARE SOVRAPPOSIZIONE DI CARATTERI
  lcd.setCursor(0, 1);                // MI POSIZIONO SULLA SECONDA RIGA DELL'LCD
  lcd.print("P: ");
  lcd.print(pot);                     // SCRIVO IL VALORE DEL POTENZIOMETRO
  setPoint = ceil (pot / 7.29);       // CALCOLO IL VALORE DEL SETPOINT IN SCALA 1-100
  lcd.print(" S:");
  lcd.print(setPoint);                // SCRIVO IL VALORE DEL SETPOINT
  lcd.print(" ");
  lcd.print(statoUTA);                // SCRIVO IL VALORE DELLO STATO UTA

  lcd.setCursor(0, 2);                // MI POSIZIONO SULLA TERZA RIGA DELL'LCD
  lcd.print("Temp : ");

  lcd.print(temperature);                     //SCRIVO TEMPERATURA DEL SENSORE
  lcd.print(" deg.  ");

  lcd.setCursor(0, 3);                // MI POSIZIONO SULLA QUARTA RIGA DELL'LCD
  lcd.print("Umid : ");

  lcd.print(humidity);                //SCRIVO UMIDITÀ DEL SENSORE
  lcd.print(" %  ");


  float vol[ARRAY_SIZE];
  vol[0] = temperature;
  vol[1] = humidity;
  vol[2] = statoU;
  vol[3] = setPoint;
  postToThingSpeak("XXXX", vol); //ACCOUNT


  delay(10000);                      //ASPETTO PRIMA DI RICOMINCIARE
}


notinswitzerland

All

A few weeks ago I discovered Belkin Wemo Switches and decided to try and integrate them into my Arduino home automation system.

[...]

Cheers

Catweazle NZ


Catweazle & wayt I can't thank you enough!!
I've modified your codes to fit my application which is a wemo controlled via arduino with a cc3000 chip!
I'm a novice but with your help I've gotten quite far!

But I'm a bit stuck at the moment and I was wondering if you can help? When I attempt connection to my wemo it replies as follows:

HTTP/1.1 500 Internal Server Error
CONTENT-LENGTH: 407
CONTENT-TYPE: text/xml; charset="utf-8"
DATE: Sun, 05 Jun 2016 20:44:47 GMT
EXT:
SERVER: Unspecified, UPnP/1.0, Unspecified
X-User-Agent: redsonic


What is it trying to tell me?

Also the intense size of the strings is messing with the Arduino's memory and I need to somehow deal with that, I'd be very grateful for suggestions!!

I've attached the current sketch in case it can help anyone!

Thank you both!!!

Go Up