Friday, May 28, 2010

Pratical Arduino Cool Projects for Open Source Hardware--Appliance Remote Control

From http://www.elechouse.com/elechouse/bbs/viewforum.php?f=12&sid=11735986b17c0c66bee5db61762f99b6

One of the basic tasks in many home automation systems is controlling power to appliances. These could be lights, a heater, an exhaust fan, or just about anything else that runs on mains power. The problem, of course, is that it’s dangerous to mess with mains-level power directly and you may even be in violation of your local building code if you don’t have the necessary qualifications. This project uses a general-purpose appliance remote control that can be obtained from a local hardware store. It can be easily modified to link it to an Arduino for software control of devices around your house, without having to touch any mains-level wiring.

This technique isn’t limited to just controlling appliances, though, and is a great way to modify just about any device with a remote control so that it can be linked to an Arduino. Any device with push-button control can be modified so that an Arduino can simulate button presses and have the device respond as if you’d pressed the buttons yourself. You could do the same thing with a TV remote control or a garage door opener. One of the authors has even done it with the temperature preset buttons on a gas-powered continuous hot-water service and on the control panel for his electric curtain tracks. You can see the parts needed in Figure 2-1 and the complete schematic in Figure 2-2.

Parts Required

1x Arduino Duemilanove, Arduino Mega-ATmega1280, Arduino Pro, Seeeduino, or equivalent
1x RF appliance remote control
1x Prototyping shield or Arduino Mega Prototype Shield (for Arduino Mega) or Arduino Prototype Shield (Arduino Diecimila)
4x 5V Reed relay
4x 1N4001 power diode or equivalent
4x PCB-mount male connectors
4x line-mount female connectors
Break Away Male Headers(Optional)
4x 10cm ribbon cable



Source code available from www.practicalarduino.com/projects/appliance-remote-control.



Figure 2-1. Parts required for Applicance Remote Control



Figure 2-2. Complete schematic for Appliance Remote Control


Instructions


Test and Investigate Appliance Remote

Plug the appliance remote control receiver into a handy power outlet and then plug an appliance (a small lamp is ideal when testing) into the socket on the receiver. Test that the unit works correctly in factory form by using the remote control to turn the appliance on and off. There’s no point doing a lot of work modifying something if it doesn’t work as intended in the first place!

Also pay attention to how long you need to hold the button down for the transmitter to operate correctly. Some systems require you to hold the button for half a second or so, and others will operate if you stab at it very briefly. You may find you need to adjust the button press time variable in the example programs that follow if your remote control has unusual characteristics.

Some remotes also perform different functions depending on whether you hold the button down or not, such as toggling a lamp on a brief press or fading it up/down on a long press.

The photo in Figure 2-3 shows two different appliance remote control sets. The set on the left is designed for Australian power sockets running at 240V and has four sets of on/off buttons, and can switch between four different ranges to control a total of 16 devices from one remote control. The receiver unit has a visual indication of status and is designed to be plugged into a wall socket, then an appliance plugged into it. It also supports a ground connection so it’s suitable for many types of appliances.

Figure 2-3. Appliance remote controls

The set on the right is designed for U.S. power sockets running at 110V, and has a much smaller transmitter, but it can only turn one appliance on or off. This particular model doesn’t have a groundconnection, so it’s suitable only for double-insulated appliances that don’t require a ground pin, such as most lamps.There are many other types of appliance remote controls available and you can often pick up sets containing one remote control and three receivers for $30 to $50.

Once you’ve tested your remote control on an appliance such as a desk lamp, take the battery out of the transmitter module and open it up by either removing the screws or (if it clips together) forcing the shell apart with a screwdriver to gain access to the circuit inside. You will probably find everything on a single PCB with a wire antenna connected at one end and the battery clip connected at the other. The buttons on the front of the remote control are usually just plastic or rubber covers that mount over the top of the actual buttons mounted on the circuit board, so locate the relevant “appliance on” and “appliance off” buttons on the circuit board and then turn it over to find the soldered connections to the back of the buttons.

Figure 2-4 shows the location of the solder pads on the back of the buttons for a typical remote control.


Figure 2-4. PCB of appliance remote control transmitter

If each button has only two connections as in the particular remote control shown in Figure 2-4, you’re on easy street, but many PCB-mount buttons have four pins to provide them with a strong mechanical mount even though they only have two actual connections so it’s not always obvious which ones you need to use. The four pins are usually arranged in two pairs that are joined together internally so that when the button is pressed the pairs are shorted together. What you need to do is find a pair of pins that are normally open but then short together when the button is pressed.

There are several ways of doing this: you could use a multimeter to measure the resistance between pairs of pins and see if it changes from very high to very low when the button is pressed, or you could even just put the battery back in the transmitter and then touch a short piece of wire across a pair of pins and see what happens. It doesn’t matter if you get it wrong because the matching pins are joined together inside the switch anyway. If you get it right, the remote control will act as if you pressed the button and send a signal to the receiver and turn your test appliance on or off depending on which button you shorted out.Every four-pin pushbutton we have ever seen joins connections together internally along two opposite sides, so if you pick two diagonally opposite
pins it will almost certainly work no matter what the orientation of the button may be.

Once you’ve found an operational pair of pins behind each button, use a felt-tip marker to put a dot next to each one to make things easier for yourself when it comes time to connect to the Arduino.

Assemble Reed Relay Shield

To link your Arduino to the appliance remote control you need to make sure the two devices are electrically isolated. The simplest way to do this is with one 5V reed relay for each button you want to control. A reed relay is a very low-power electromechanical switch that allows a low current to control a higher current. Modern 5V reed relays require only about 20mA to operate. That’s
low enough that you can drive it directly from an Arduino output without requiring any other buffer circuitry at all, so the next step is to mount some reed relays on a prototyping shield and connect them to Arduino outputs.

A regular Arduino prototyping shield will comfortably fit four reed relays plus connectors. If you only want to operate a single device, you probably only need two relays (one to connect to the “on” button, one for “off”) but it can be handy having more outputs so I installed four.

Start by fitting the relays and male PCB-mount headers to the shield as shown in Figure 2-5.


Figure 2-5. Reed relays and male headers fitted to PCB

The reed relays have their inputs (coil connections) on the bottom center pair of pins when aligned with the notch to the left, as in Figure 2-5, and their outputs on the outer pairs of pins.

Turn the shield over and solder the relays and connectors in place. You only need to solder the pins that will actually be used, but you can solder all the pins if you prefer (see Figure 2-6).


Figure 2-6. Reed relay connections soldered to shield

The particular prototyping shield shown in Figure 2-6 has a strip of ground connections down one side and +5V down the other. Use some short lengths of wire to join a coil connection from each relay together and then to ground. Also connect the PCB-mount plug pins to the adjacent outer connections (outputs) on the reed relays and fit the breakaway headers that allow the shield to plug into an Arduino.

You’ll notice in the photo shown in Figure 2-7 that we only fitted three of the four breakaway headers. This may seem odd, but we didn’t need to make a connection to any of the analog input pins so there’s no need to put that connector on the board. The shield will still mount very firmly with
three connectors and having the fourth connector installed would just make it a little bit harder to align and insert into an Arduino for no benefit.

Turn the board back over and install jumper leads from the other coil connection on each relay to the matching Arduino digital output. In this project we started with digital output 5 for the first relay, 6 for the second, and so on (see Figure 2-8).


Figure 2-7. Connections to +5V and ground


Figure 2-8. Connections from digital outputs to relay inputs

Electro-mechanical relays operate by running current through a coil to generate a magnetic field and pull output contacts together, thus completing a circuit. When power to the relay is turned off, the magnetic field collapses and the contacts release, but while it is collapsing the field generates a “reverse spike” or “back-EMF”—a brief high voltage of opposite polarity to the original voltage. What this means is that the Arduino output that was previously supplying +5V to hold the relay on is temporarily subjected to a big blast of negative voltage, and if the spike is big enough the output can actually be damaged or destroyed.

The simple solution is to fit a “reverse biased” diode across the relay coil so that as soon as the reverse spike begins it will be shorted out by the diode and the Arduino output will be protected. Diodes only pass current in one direction, so by fitting it backward across the relay, it won’t do anything in normal operation and will appear as an open circuit, but will easily conduct the reverse-voltage spike while the relay’s magnetic field collapses. This will protect your Arduino from the reverse voltage.Given the extremely low power involved in a small reed relay it’s highly unlikely that it will blow an I/O pin right away, so it can be tempting to leave off the protection diode and hope everything will be all right.

However, damage to I/O pins can be subtle and cumulative and putting a diode in now is much cheaper than replacing an Arduino later so it’s better to play it safe.Because the voltages and currents involved are very small, you can use just about any power diode or signal diode you may have available. We used 1N4004 diodes, which are commonly available for only a few cents each—we buy them by the hundred so that we always have plenty around when we need them.

Fit a diode across each relay coil with the “anode” lead going to the pin connected to ground, and the “cathode” lead (the end with the stripe) going to the relay pin connected to the Arduino output as shown in Figure 2-9.


Figure 2-9. Protection diodes connected across relay coils

That’s the shield all done, so next you need to assemble a couple of small wiring harnesses.


Figure 2-10. Connections to link shield with remote control

Separate pairs of wires from the ribbon cable by nipping between the ends and pulling it apart with your fingers, then solder one end of each pair of wires to a female line connector and assemble the connector to make a wiring harness that you can plug into the shield as shown in Figure 2-10.

You now have a useful general-purpose relay shield that you can connect to any low-voltage device that you want to control simply by soldering the ends of the wiring harnesses into place.

Connect Reed Relay Shield to Remote Control

Solder the pair of wires from each wiring harness across the back of the buttons in the remote control using the connections you marked on the PCB earlier, as shown in Figure 2-11.

Mount the reed relay shield on your Arduino and plug each wiring harness into the connectors on the reed relay shield, as shown in Figure 2-12.

If your appliance remote control runs on 3V you can optionally remove the battery from the remote control and solder a pair of wires across the (+) and (–) battery terminals, then connect them to +3.3V and GND, respectively, on your Arduino. The remote control will then draw its power from the Arduino and you never need to worry about its battery going flat. In our case, though, the remote
control uses a little “A23” type 12V battery so we left it in place in the transmitter.

Now all the hardware is done, so on to the software!


Figure 2-11. Remote control transmitter with connections in place


Figure 2-12. Remote control linked to shield

Create Reed Relay Control Program

When you use the appliance remote control manually you only press the buttons momentarily and don’t hold them down indefinitely, so to simulate a button press we’ll pulse a reed relay on for 250ms (1/4 of a second) and then turn it off again. That should be plenty of time for the appliance remote to detect the virtual “button press” and then send the appropriate signal to the receiver.

There are two versions of the code available for download from the Practical Arduino web site. The first version, called ApplianceRemoteControl, is designed to be as conceptually simple as possible to make it easy for a beginner to follow what it does. The second version, called ApplianceRemoteControlCompact, is functionally identical but uses some more advanced concepts to make the code smaller. The second version is harder for a beginner to understand, but comparing the two programs is a great way to see how more advanced concepts (such as arrays)
can make your code much smaller. Since the Arduino has very limited memory capacity it is important to know how to minimize your code size.

When you compile (“Verify”) a program in the Arduino IDE, the size of the resulting program is shown in bytes in the bottom left of the window. It’s good to get into the habit of looking at the size of the programs you create to get a feel for how much memory your software will take up. With larger
programs it can become quite a juggling act to squeeze all the features you want into the few kilobytes of available space.
We’ll start by working through the longer, but conceptually simpler, ApplianceRemoteControl program.

ApplianceRemoteControl

The code starts by defining some basic values such as which digital I/O lines need to be used as outputs and how long to hold each “button press” on for.

// Use pins 5 through 12 as the digital outputs
int output1 = 5;
int output2 = 6;
int output3 = 7;
int output4 = 8;
int output5 = 9;
int output6 = 10;
int output7 = 11;
int output8 = 12;
int buttonPressTime = 250; // Number of milliseconds to hold outputs on

You’ll notice that it defines eight outputs, not just the two or four we use in this project. The extra outputs don’t matter if we don’t use them, but by defining them now you can plug in a shield with
up to eight relays or even an eight-way opto-isolator later on and use exactly the same program with no modification required. This would come in very handy if you want to connect up all eight on/off buttons on an eight-way appliance remote control.
However, keep in mind that the ATMega CPU in an Arduino can only supply a limited amount of current from each pin and that the total output
current is also limited. If you try turning on eight reed relays at once on eight outputs you’d probably exceed the chip’s current supply rating. With the example programs shown here it’s not a problem because they only ever allow one output to be turned on at a time, but if you use different code to turn outputs on and off independently you need to be careful of the total current draw.

The program then runs the setup function that tells the CPU to switch each of those digital pins into output mode, then forces them to an initial low state so all the relays are turned off when the program starts running. It also opens the serial port (USB on a Duemilanove) for communication with the host computer at a speed of 38400bps.

void setup()
{
// Open the serial connection to listen for commands from the host
Serial.begin(38400);
// Set up the pins as outputs
pinMode(output1, OUTPUT);
pinMode(output2, OUTPUT);
pinMode(output3, OUTPUT);
pinMode(output4, OUTPUT);
pinMode(output5, OUTPUT);
pinMode(output6, OUTPUT);
pinMode(output7, OUTPUT);
pinMode(output8, OUTPUT);
// Make sure the outputs are all set LOW initally
digitalWrite(output1, LOW);
digitalWrite(output2, LOW);
digitalWrite(output3, LOW);
digitalWrite(output4, LOW);
digitalWrite(output5, LOW);
digitalWrite(output6, LOW);
digitalWrite(output7, LOW);
digitalWrite(output8, LOW);
}

The program then enters the main loop, which is where the real action happens.

The main loop watches the serial port for data being sent to it and examines any characters to see if they match the ID of a known output. If there is a match it pushes that output high to turn on the relay connected to it, waits 250ms, and then takes it low again to turn off the relay before going
back to the start of the loop to listen for the next command. The outputs are labelled “1” through “8,” corresponding to the eight output pins defined previously.

void loop()
{
byte val;
// Check if a value has been sent by the host
if(Serial.available()) {
val = Serial.read();
if(val == '1') {
// Pulse the 1st button
Serial.println("Output 1 ON");
digitalWrite(output1, HIGH);
delay(buttonPressTime);
digitalWrite(output1, LOW);
Serial.println("Output 1 OFF"); 28
} else if(val == '2') {
// Pulse the 2nd button
Serial.println("Output 2 ON");
digitalWrite(output2, HIGH);
delay(buttonPressTime);
digitalWrite(output2, LOW);
Serial.println("Output 2 OFF");
} else if(val == '3') {
// Pulse the 3rd button
Serial.println("Output 3 ON");
digitalWrite(output3, HIGH);
delay(buttonPressTime);
digitalWrite(output3, LOW);
Serial.println("Output 3 OFF");
} else if(val == '4') {
// Pulse the 4th button
Serial.println("Output 4 ON");
digitalWrite(output4, HIGH);
delay(buttonPressTime);
digitalWrite(output4, LOW);
Serial.println("Output 4 OFF");
} else if(val == '5') {
// Pulse the 5th button
Serial.println("Output 5 ON");
digitalWrite(output5, HIGH);
delay(buttonPressTime);
digitalWrite(output5, LOW);
Serial.println("Output 5 OFF");
} else if(val == '6') {
// Pulse the 6th button
Serial.println("Output 6 ON");
digitalWrite(output6, HIGH);
delay(buttonPressTime);
digitalWrite(output6, LOW);
Serial.println("Output 6 OFF");
} else if(val == '7') {
// Pulse the 7th button
Serial.println("Output 7 ON");
digitalWrite(output7, HIGH);
delay(buttonPressTime);
digitalWrite(output7, LOW);
Serial.println("Output 7 OFF");
} else if(val == '8') {
// Pulse the 8th button
Serial.println("Output 8 ON");
digitalWrite(output8, HIGH);
delay(buttonPressTime);
digitalWrite(output8, LOW);
Serial.println("Output 8 OFF");
}
}
}


One subtlety that may not be apparent on first inspection of the code above is what sort of data is being examined in the series of “if” comparisons. You’ll notice that the variable that holds the data from the serial port is of type “byte,” which is not a numeric type: it could actually be any character at all. Each “if” condition is comparing the data in the variable “val” with a string
in quotes, such as '1', or '2', not with the actual number 1 or 2.

If that’s beyond you right now, don’t worry about it. Things like variable types will become second nature as you spend more time working with Arduino.

Once you’ve loaded the sketch in the Arduino IDE, plug your Arduino into the USB port, select the port from Tools -->Serial Port, select your Arduino board type from Tools--> Boards, click “verify” to compile the sketch, and if there were no errors, click “upload” to push it across to the Arduino.

ApplianceRemoteControlCompact

One thing you’ll notice from the version of the preceding program is that although it is conceptually quite simple there is a lot of repetition in it. There are eight nearly identical lines defining output pins, and eight nearly identical lines forcing them low. There are also eight nearly identical blocks of code in the main loop, which makes it very long for such a simple program.

The compact version of the program uses a number of techniques to reduce the repetition in the code.

The button press definition doesn’t change, but the definition of the output pins is much shorter because it lists them in an array rather than defining each of them as a separate variable. We also need to know how many outputs are listed in the array. We could have done that manually by simply counting them ourselves and assigning that value to a variable, but then we would have to remember to change the value if we changed the array. To avoid that problem we do a little trick that gets the total size of the array and then divides it by the size of a single element
to get the number of elements. It doesn’t actually matter which element we use as the divisor, because every element takes up the same number of bytes in memory. We’ll just use the first element (element 0) in this case.

// Use pins 5 through 12 as the digital outputs
int pinMap[] = {5, 6, 7, 8, 9, 10, 11, 12};
byte pinCount = sizeof(pinMap) / sizeof(pinMap[0];

As before ,we also set a variable to specify how long to pulse each button for.

//Number of milliseconds to hold the outputs on
int buttonPressTime = 250;

An array is a list of values with the positions in the array numbered from 0. What that means is that the first entry is position 0 and has value 5, the second entry is position 1 and has value 6, the third entry is position 2 and has value 7, and so on.

What this allows us to do is simplify the setup function because instead of listing every single pin and setting it as an output and forcing it low, we can loop through the elements in the array and use each one in turn.

void setup()
{
// Open the serial connection to listen for commands from the host Serial.begin(38400);
int count = 0; // Variable to store current array position
// Set up the pins as outputs and force them LOW
for(count; count < pinCount; count++) {
pinMode(outputArray[count], OUTPUT);
digitalWrite(outputArray[count], LOW);
}
}

The for loop uses a simple counter that starts at 0 to read the first position in the array, then increments up through the positions to read each in turn.The biggest change is in the main program loop which no longer has to check for every possible value explicitly, but can just check that it falls within an acceptable range.

void loop()
{
byte val; // The raw character read from the serial port
int channel; // Integer version of channel ID
// Check if a value has been sent by the host
if(Serial.available()) {
val = Serial.read();
channel = (int)val - 48; // Convert ASCII value to digit
if(channel > 0 && channel <= pinCount) {
pulseOutput(channel); // Pulse the appropriate button
}
}
}

That’s certainly a much shorter function than in the first version! There are a few things to pay careful attention to in the new version of the loop, though.

You’ll notice that we have a new variable called “channel,” which is an integer. While reading the serial port there is a cryptic line that sets the value of “channel” by taking the integer value of the “val” variable and subtracting 48 from it.

What’s going on here? The byte received from the serial port is not actually a number, as you would generally expect. It’s an ASCII value that represents a character, and that character may (or may not) be a number. The ASCII code for the character “1” is 49, and the ASCII code for the character “2” is 50, and so on. So when we receive a value of “1” from the serial port, the ASCII code that is transmitted (and loaded into the variable “val”) is “49.”What that line does is “cast” (convert) the value of “val” into an integer using the (int) prefix, then subtract 48 from
it to convert it to the equivalent number. If the value sent via the serial port is “1” it will come through as ASCII code 49, then have 48 subtracted from it, and end up as the integer 1. The end result of all this trickery is that you send “1” at one end and get “1” out at the other end, but unfortunately it’s not as simple as you might expect it to be!

After converting the received value, “val,” to an integer value, “channel,” it is then tested to see if it falls inside the acceptable range from 1 to “pinCount,” which is the number of pins defined in the array.

Finally, if that test is met, the loop calls another function called pulseOutput(channel) which is where the actual work of firing the relay takes place.

void pulseOutput(int channel)
{
Serial.print("Output ");
Serial.print(channel); 31
Serial.println(" ON");
digitalWrite(outputArray[channel - 1], HIGH); // Channel number is 1 higher than
array position
delay(buttonPressTime);
digitalWrite(outputArray[channel - 1], LOW);
Serial.print("Output ");
Serial.print(channel);
Serial.println(" OFF");
}

The pulseOutput function accepts a single integer value passed to it from the main program loop, and then sends notification via the serial port that it is about to turn on that channel. It then looks up the array listing the output pins (outputArray) to find the pin number that corresponds to the requested output. Because arrays are numbered starting from 0, while our output channels are numbered starting from 1, we have to subtract 1 from the requested channel to access the correct position in the array: output channel 1 is array position 0, and so on.The function then pauses briefly, turns the appropriate relay back off, and sends notification that it’s all done.

As you can see the compact version of the program is much shorter than the original. Once you’re used to the way things such as arrays and functions work, you’ll find it much less clumsy working with programs structured to remove repetition using techniques such as these.

Test Reed Relay Shield and Sketch

Your Arduino should now be connected to the appliance remote control transmitter using the reed relay shield, the transmitter should have its battery in place, and your Arduino will be listening on the serial port for an instruction to “press” a button.

Click the “monitor” button in the IDE to switch to serial-monitor mode where you can see values being sent to you by the Arduino and also send values to it. Select 38400 from the baud rate drop-down box (see Figure 2-13) to match the value we set the Arduino to in the setup function.


Figure 2-13. Serial monitor in Arduino IDE

Now for the moment of truth! Enter the value 1 into the text input area on the right and click Send or press Enter, and you should immediately see your Arduino send you a response saying that it received the command and is activating output 1, followed almost immediately by notification that it is turning the output off again. If everything is working as expected your appliance should turn on, and sending a value of 2 to the Arduino should turn it off again. You can see this at work in Figure 2-14.


Figure 2-14. Arduino and shield using an appliance remote control to activate a
lamp

Variations

Wireless Link

Rather than having the Arduino tethered to a computer, you could replace the USB connection with a wireless link such as an XBee or 433MHz wireless module or even with an Ethernet shield to provide you with a web-services interface to your appliances. WiFi and Ethernet connectivity are discussed in later projects.

Automatic Trigger

The example programs rely on messages being sent to the Arduino via a serial connection. By connecting an Arduino I/O line as an input and checking the status of a device, such as a motion detector, it could instead trigger outputs based on events such as a motion detector being triggered, a shop door-minder beam being broken, or a window being opened. Connecting to various devices including security sensors is covered later in the book.

Socket Connections

Using the serial monitor in the Arduino IDE is fine for testing, but to make this project useful you probably want to be able to control devices from a scripting language such as Python, Perl, or PHP so that events can be triggered automatically rather than manually through the IDE. A utility such as ser2net (on Linux) or serproxy (on MacOS and Windows) will take a serial connection and expose it as a network socket so that it can be accessed over a network. This is also a huge help when working with scripts running on the computer directly connected to the Arduino because most scripting languages are great at making socket connections but terrible at
connecting to serial ports. Using a serial-to-network proxy allows you to use any scripting language that can open a network socket and have it talk to your Arduino via the USB connection as easily as if it were a network service.

There is more information on serial-to-network proxies on the Arduino web site at www.arduino.cc/playground/Interfacing/Flash.

No comments:

Post a Comment