In part 1 we showed you how to build a wireless power switch that would fit inside an outlet box and could be controlled remotely anywhere with an internet connection. Now we’ll show you how to control the outlet from anywhere you have access to the internet.
In order to implement the control of the outlet you’ll need:
1. The Compact 30A Wireless Power Outlet built in part 1
2. A web server
3. An Arduino Uno or equivalent
4. Two XBees (one configured as a coordinator and the other as a router, both in AT mode)
Before I get started I should give credit to Robert Faludi and his book Building Wireless Sensor Networks from where I gleaned much of the information in this post, particularly the XBee communications and PHP code. Unless you’re already very familiar with XBee communications, this book is a must.
The first thing I wanted to do was to generate a simple on/off switch on a webpage. Since I design websites for other Financial Advisors and CPAs, I already had a hosted website I could use as a server. I just created a subdirectory for the power switch pages below. Initially, I used a bit of PHP code for the on/off switch and put it into a stand-alone webpage. But I build my websites using WordPress, so I decided to make the on/off switch part of a ‘widget’ instead. I found a WordPress plugin called PHP Code Widget. It allows me to execute PHP code inside of a WordPress widget.
Here is the code you can use as a stand-alone page form or inside of a WordPress widget:
<html>
<head>
<title>XBee Internet Controlled Power Outlet</title>
</head>
<body>
<div>
<?php
// this code creates a form that can set an 'On' or 'Off' to
// control an XBee triggered power outlet.
// it creates a text file on the server called powerSetting.txt
// this file is typically read using power_setting_read.php
$myFile = "powerSetting.txt"; // define a variable for the data file
$fh = fopen($myFile, 'r') or die("can't open file"); // open the data file
$powerFlag = fread($fh, filesize($myFile)); // read the current setting from the data file
fclose($fh); // close the data file
// if the form has not yet been submitted
if(!isset($_POST['send'])) {
echo "Power was last: ".$powerFlag."<br />"; // show the previous setting
}
// when the form is submitted
else if(isset($_POST['send'])) {
echo "Power is now: ".$_POST['power'] . "<br />"; // update the power state
$value = $_POST['power']; // create a variable that holds the power state
$fh = fopen($myFile, 'w') or die("can't open file"); // open the file "powerSetting.txt" for writing
fwrite($fh, $value); // write the power state to the data file
fclose($fh); // clean up after yourself!
}
?>
<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
<select name="power">
<option value="Off" selected="selected">select</option>
<option value="On">On</option>
<option value="Off">Off</option>
</select>
<input type="submit" value="Submit" name="send"/>
</form>
</div>
________________________________
</body>
</html>
The next step was to create an empty file called powerSetting.txt on the server. We write to this file using the form created above and read from this form using another PHP file called Power_setting_read.php. In Power_setting_read.php we read the file powerSetting.txt and generate a 1 or 0 preceded by an “*” depending on whether we have selected on or off. We’ll be using the “*” as a start byte later when the Arduino code reads the webpage. The code in Power_setting_read.php should read as follows:
<?php
// this code responds with a '1' or '0' in ASCII when the page is loaded
// it is dependent upon reading a text file called powerSetting.txt
// that can be created by using Power Switch Form Widget
$myFile = "powerSetting.txt"; // define a variable for the data file
$fh = fopen($myFile, 'r') or die("can't open file"); // open the data file
// read the current power setting from the data file
// if “On” print *1, if “Off” print *0. The “*” is used as a start byte
$powerFlag = fread($fh, filesize($myFile));
if ($powerFlag == "On") {
print "*";
print "1";
}
else if ($powerFlag == "Off") {
print "*";
print "0";
}
fclose($fh); // close the data file
?>
At this point it can all be tested. When you select “On” or “Off” on the PHP generated on/off webpage form or widget, you should be able to go to http://<your-webserver>.com/powerSetting.txt and see the status of the power switch. You should also see the status change in the power switch on/off form itself. Lastly, you can go to http://<your-webserver>.com/Power_setting_read.php and see either a *0 or *1 depending on the on/off selection of the power switch form.
Now we need to generate an Arduino sketch that reads the webpage and forwards a command through the XBees to turn the power on or off. First you’ll need to create an Ethernet or WiFi WebClient so you can access the webpage on the server. Here we’re using WiFi. We’re also using another serial port (Serial1) using the Arduino SoftSerial library to communicate with the XBee Coordinator so we can use the USB serial port to monitor the data for debugging purposes.
We use a function called httpRequest(); that makes an HTTP connection to the server and reads the webpage, http://www.<your-webserver>.com/Power_setting_read.php, using an HTTP GET command. We then parse the webpage data in the main loop. Because our webpage includes header information, we want to skip that header information and just get the status of our power switch form. We do this by first looking for an “*” then we read the next character which is the byte corresponding to the status of the power switch form.
Finally we send the information to the XBee using the setRemoteState(); function. This sends a Remote AT Command Request packet to the router XBee telling it to engage or disengage the power outlet control relay. Sending a 0x05 engages the relay. Sending a 0x04 disengages the relay.
Below is the Arduino sketch. I’ve left out some of the WiFi connect functions to simplify the code.
// Arduino code used to read Power Switch Form Widget and send command to the wireless
// power outlet through a pair of XBees. Use either Ethernet or WiFi to connect to the internet
// here we are using WiFi
//
#include <WiFi.h>
#include <WiFiClient.h>
#include <SoftwareSerial.h> // Using a soft serial port to talk to the XBee
WiFiClient client;
SoftwareSerial Serial1 (2,3); // RX, TX
char servername[] = "www.<your-webserver>.com"; // remote server we will connect to
unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds
const unsigned long postingInterval = 1L * 300L; // delay between updates, in milliseconds
void setup() {
Serial.begin(19200); // Serial monitor
Serial1.begin(19200); // Serial for Xbee
}
void loop() {
char c;
// Read the website until we find an "*"
// then read the next byte
while (client.available()) {
c = client.read();
if (c == '*') {
c = client.read(); // look for 1 or 0
Serial.write(c); // for debug only
if(c=='1') {
setRemoteState(0x05); // Call Xbee actuator function = "ON"
}
if(c=='0') {
setRemoteState(0x04); // Call Xbee actuator function = "OFF"
}
}
}
// if ten seconds have passed since your last connection,
// then connect again and send data:
if (millis() - lastConnectionTime > postingInterval) {
httpRequest();
}
}
// makes an HTTP connection to the server:
void httpRequest() {
client.stop(); // close any connection before send a new request to free the socket on the WiFi shield
// if there's a successful connection:
if (client.connect(servername, 80)) {
client.println("GET /power_setting_read.php HTTP/1.1"); // send the HTTP GET request:
client.println("Host: www.<your_server>.com");
client.println("User-Agent: ArduinoWiFi/1.1");
client.println("Connection: close");
client.println();
lastConnectionTime = millis(); // note the time that the connection was made
}
else {
// if you couldn't make a connection:
Serial.println("connection failed");
}
}
// Remote AT Command Request sent to XBee controlling power outlet
void setRemoteState(int value) { // pass either a 0x4 or 0x5 to turn the pin on/off
Serial1.write(0x7E); // start byte
Serial1.write((byte)0x0); // high part of length (always zero)
Serial1.write(0x10); // low part of length (the number of bytes that follow, not including checksum)
Serial1.write(0x17); // 0x17 is a remote AT command
Serial1.write((byte)0x0); // frame id set to zero for no reply
// ID of XBee recipient, or use 0xFFFF for broadcast
Serial1.write((byte)0);
Serial1.write(0x13);
Serial1.write(0xA2);
Serial1.write((byte)0);
Serial1.write(0x40);
Serial1.write(0xBD);
Serial1.write(0x2F); // 0xFF for broadcast
Serial1.write(0xD7); // 0xFF for broadcast
// 16 bit of recipient or 0xFFFE if unknown
Serial1.write(0xFF);
Serial1.write(0xFE);
Serial1.write(0x02); // 0x02 to apply changes immediately on remote
// command name in ASCII characters
Serial1.write('D');
Serial1.write('1');
// command data in as many bytes as needed
Serial1.write(value);
Serial.println(value, HEX); //for debug only
// checksum is all bytes after length bytes
long sum = 0x17 + 0x13 + 0xA2 + 0x40 + 0xBD + 0x2F+ 0xD7 + 0xFF + 0xFE + 0x02 + 'D' + '1' + value;
Serial1.write(0xFF - ( sum & 0xFF)); // calculate the proper checksum
delay(10); // safety pause to avoid overwhelming the
// serial port (if this function is not implemented properly)
}
There are more elegant ways to do a lot of this. For instance you can run the setRemoteState(); function only when there is a change of state, but this will give you a basic idea of how to control a power outlet from the internet. Your suggestions and feedback are welcome.