Sunday, January 27, 2013

Controlling Smart Outlet with Arduino

Smart outlets are becoming all the rage. But nobody is opening up an api that can be used by programmers to automate things. I need a smart outlet that I can control with my arduino, so I am going to reverse engineer one of the smart outlets.

I found one on ebay:



Its called "Outdoor Wireless Remote Control AC Power Outlet Plug Switch" and I got mine from ebay seller 'emilyandlily'.

It comes with a remote:


I have connected the outlet to my surge protector and lamp:





When you press the on button on the remote the outlet turns on with a green led.



When you press off the outlet turns off with a red led.


What just happens to be convenient about this outlet for reverse engineering is that the remote runs at 433.92mhz ASK radio frequency. This is convenient because there are tons of cheap off the shelf parts for receiving and transmitting 433.92mhz signals.

We could butcher the remote and then attach it to the arduino frankenstein style but it's not very elegant. 

I'll see what the remote transmits by using an off the shelf 433.92mhz receiver and logic analyzer. Then I will duplicate the signal by writing a code for arduino that sends the same signal through an off the shelf 433.92mhz transmitter.


To see what the remote sends, I have setup a 433.92mhz receiver(Digikey part number AMHRR30-433-ND) on my breadboard.

Pin 1 is connected to 5v
Pin 2 is connected to GND
Pin 3 is left alone
Pin 4 is GND
Pin 5 is GND
Pin 7 is 5v
Pin 8 you can connect to an oscilloscope to see the signal if you want to. I haven't connected it to anything.
Pin 9 is where the signal that the receiver sees is outputted. This is where I will connect my logic analyzer.
Pin 10 is connected to 5v



For my logic analyzer I am using a "Saelae Logic", it has OSX compatibility, so I can connect it to my mac mini. I got the logic analyzer because I already have an oscilloscope, but if I were to start over again(no scope + no logic analyzer), I would get the "QA100 USB Oscilloscope." You can google them to see.

So below I have attached my logic analyzer yellow lead to the receiver's data out pin 9. I have attached the ground lead to GND. Also I have placed the rest of the wires away from the sensing wires because they can cause erroneous readings if they are too close.



So with the logic analyzer software running and the circuit powered, I press "ON" on the remote. Here is what comes up:



Here is what comes up when I press OFF:



So all I need to do is make my Arduino send the same signals to turn the outlet on and off. I will use another off the shelf transmitter to do this.

Where do you get the 433.92mhz transmitter? Well I got mine from ebay. Search "arduino 433mhz transmitter." They come in a pair, one receiver one transmitter.  Below are the ones I bought.


Here is a pic of the arduino connected to the transmitter. The transmitter has 5v, GND, then it's data pin is connected to arduino digital pin 2.




I need to note two things, first you need to use a 6.8inch antenna, it is the orange wire above. This makes the transmission so much more effective and reliable. The second is that the power to the transmitter needs to be clean, USB power is too noisy. You can see I am using a 9v ac/dc adapter for the arduino.

I already know what I want from looking at what the logic analyzer showed me, so I just programmed:


int transmitPin = 2; 

void setup()
{
  pinMode(transmitPin,OUTPUT);
  digitalWrite(transmitPin,LOW);
}

void loop()
{
     turnOn();
     turnOn();
     turnOn();
     turnOn();
     delay(1000);
     turnOff();
     turnOff();
     turnOff();
     turnOff();
     delay(1000);

  
}


void customDelay(unsigned long time) 
{
    unsigned long end_time = micros() + time;
    while(micros() < end_time);
}

//1000=1ms
void setStateWithDelay(int pin, int state,int delayTime)
{
  if(state==1)
    digitalWrite(pin,HIGH);
  else
    digitalWrite(pin,LOW);
    
  customDelay(delayTime);
}

void turnOn()
{
    
  setStateWithDelay(transmitPin,0,13000);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
   
   digitalWrite(transmitPin,LOW);
}

void turnOff()
{
  setStateWithDelay(transmitPin,0,13000);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,1270);
  setStateWithDelay(transmitPin,0,430);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
   setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
 
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
 
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
 
  setStateWithDelay(transmitPin,1,430);
  setStateWithDelay(transmitPin,0,1270);
 
  setStateWithDelay(transmitPin,1,430);
 
    digitalWrite(transmitPin,LOW);
 
  
}




This turns my lamp on and off. You may have noticed that I send turnOn() 4 times, it seems that the outlet wants 4 signals to turn on before it does. Same for the off signal.

Here is a video of the whole circuit working

I don't know if all of the outlets sold have a unique remote id or if they all have one command. I axed the ebay item seller if they know the answer to this question but haven't gotten a reply yet.

Here are the exported output signals

Here are the signals that you can open in the Saelae logic analyzer program.