X-10 Smart Macros Operating on Cheap Hardware

Copyright © 2002, Richard Cochran

Warning: this page describes a tinkering hobby project. Feel free to use some or all of my ideas, but be aware that this will consume a bit of time, and it probably won't work right at first. If you don't know or care which end of a soldering iron is hot, if you want to buy something with a pretty box, if you want something you can have working the day you receive it, or if you view your time as valuable, buy a commercially supported product. I'm not paid to support this, and I don't have much time to answer questions. Only do this if you view it as an interesting, challenging puzzle, and if you consider the time spent on it as educational and fun. And obviously, please take all relevant safety precations, particularly when dealing with a soldering iron or a powerline. All my work is done with very low-voltage, low-power circuits, but they're indirectly connected to the powerline, and they may trigger unexpected X-10 actions.

X-10, ActiveHome, and limitations of dumb macros

X-10 is an established technology for home automation, allowing remote control of lights, appliances, etc. Things can be controlled by simply having a human press buttons on a transmitter, but another way to control things is to let a computer turn devices on and off in response to button presses, sensor inputs, timers, etc. The popular and inexpensive CM11A computer interface, along with X-10's free ActiveHome software running under Windows, allows one to create timed events and macros. These allow you to do things like turn outdoor lights on at dusk and off at 10:00pm, or push one button on a remote to activate a macro that closes the drapes, turns some lights off, other lights on, and sets the dimming level of lights.

The CM11A is a small device that doesn't use much power. It has some memory inside, and can store timers and macros internally. Once the timers and macros are loaded in the interface, your big computer can be switched off.

The problem is that the macros which can be stored inside the interface are "dumb". You can say, for example, "When the porch motion detector goes off, turn on the outside lights, and then turn them off after five minutes". You can also say "Turn on the ouside lights at dusk, and turn them off at midnight". But if you program both of those actions simultaneously via ActiveHome, then when someone trips the motion detector just after dusk, the outside lights will be switched off five minutes later. It would be nice to say "When the porch motion detector goes off, if the lights aren't already on, turn them on and then off after five minutes". But the word "if" isn't in the CM11A's vocabulary. I found another problem when I programmed my porch motion detector to sound a chime indoors when it detected motion. The first night I tried this, the neighborhood cats decided to trip the detector at 2:30am. I still want the chime, but only during normal waking hours.

What I need is "smart macros", or "conditional macros". I want to write instructions that set variables and read variables, and read the time of day, to take varying actions depending on the current time and the current state of affairs. I want flexibility and programmability.

Solutions

There are a few solutions to the problem. I could run software on my PC, and keep it running 24 hours a day. With the right hardware, a PC can be set to "sleep" most of the time, only waking up when the interface detects an X-10 signal. But sleep or not, a PC is a big, power-hungry piece of hardware to keep running constantly just to babysit an RS-232 port. Another possibility is found in various specialized programmable devices that will manage X-10 communications. The "Ocelot" is one such device. These are closer to what I was looking for, but they still seem a bit big and expensive.

I decided to create my own smart X-10 macro device. I'll call it "Smart Macros Operating on Cheap Hardware", or SMOOCH. All I need is a programmable device with an RS-232 port that can watch the port for X-10 activity from the CM11A, keep track of a few variables, and send X-10 commands to the CM11A. When it needs to know the time of day, it can ask the CM11A, so there's no need to design hardware to allow the user to set a real-time clock. I ought to be able to program it from my PC, but while it's running, it doesn't need any I/O at all, except the RS-232 port. A PIC16F628 microcontroller ought to have plenty of power to do this. It's available for under $4.00 in small quantities (much cheaper still if you're buying hundreds). It takes a few more dollars for the parts to make it run with an RS-232 port, but it still ought to be relatively inexpensive. Since the chips are so cheap, I buy a handful of them. I decide to get one big development board with an LCD, plus a few minimal tiny boards with nothing but a serial port. (I've got a couple of other ideas brewing for tiny serial port devices.)

The drawback to this approach is the software development effort. The PIC16F628 is programmable in several languages, but the only language that's free is assembler. Assembler is also the way to maximize speed and control over the processor. I had no experience with PIC assembler, but I've programmed several mainframes and microcomputers in various assembler languages years ago, so I bite the bullet and teach myself PIC assembler. Microchip provides a free detailed data sheet on the web, and this chip has by far the simplest assembler language I've ever used. Nevertheless, assembler at its simplest can still frustrate. (should that be BTFSC or BTFSS?) Fortunately, I view this as a hobby, so software development time is free. If I charged an hourly rate for software development, I would have been much better off buying a commercial product.

Development Hardware


Raw buffer received from CM11A shown in hexadecimal on top line, Bottom line shows human-readable interpretation "E2 On", and current time of day.
The PIC16F628 is the chip at the center, just below the display. The other chip to the right is a MAX232, to handle RS-232 voltage levels on the serial port.

I wanted to develop the sofware on a board that had a good way of displaying data for debugging purposes. I bought an melabs Lab-X3 development board from Vikon.com. This has an LCD display built in, which can display 2 lines of 20 characters each. In retrospect, I didn't need the full Lab-X3 -- any Hitachi-compatible LCD could be hooked up to a PIC with minimal effort (see the LAB-X3 schematic for details), but this is a very well-made product, and I didn't even have to solder anything to make it work! Later, I find an LCD in a parts bin at a local electronics store for $9.95. It's plug compatible with the Lab-X3, but it has two lines of 40 characters each, for more display space! Another nice thing about the LAB-X3 is that it has a connector to plug in a programmer, so you can program your chip without pulling it out of the circuit. I study the schematic, and add a header to my homebuilt PIC programmer to provide the right signals on the right pins of the connector. The assemble/program/test cycle goes MUCH faster when I don't have to plug or unplug any chips or connectors! If I had studied the LAB-X3 schematic, I could have duplicated this functionality on my prototype board using nothing but a few connectors, but the LAB-X3 makes it easy.

To develop software for a PIC, you also need a programming device. This transfers your program from your PC to the chip. There are lots of these, both commercial and DIY, available on the web, but I had already built the NOPPP programmer a couple of years ago. This is cheap to build, using a single transistor plus a few diodes, capacitors, and resistors. The software from the noppp site predates the PIC16f628, but I found fpp.exe, a generic flash pic programmer that can be configured to work with virtually any PIC chip and virtually any programming hardware, including the NOPPP.

Finally, you need a PC running Windows, with a Parallel port to support the programmer. You can download the free MPLAB software from Microchip. This is an assembler and editor that runs under Windows.

SMOOCH Hardware

 
You're looking at about $25.00 worth of parts that make up a SMOOCH board.

Once I've got the software developed, I want to run it on minimal hardware, freeing up my development hardware for another project. I use a PICProto18 board from melabs, selling for $9.95. It's a well-made 1.5"x3" board, with the circuitry laid out for a PIC, crystal oscillator, and voltage regulator. The remainder of the board (about 2/3s of the space) is prototyping area. Melabs also sells a parts package that contains a voltage regulator, 4MHz crystal, 2-22pF capacitors, 10uF and 1 uF capacitors, an LED and dropping resistor, all for $7.95. This is enough to get the PIC running, and it's all soldered to the top part of the board. I just need to add a levels converter for RS-232 voltages. I choose a MAX232 chip (under $2.00), because it's cheap, widely available, and bulletproof. It converts the 5 volt signals from the PIC to +/- 12V on the RS232 line, using only a 5V supply. The MAX232 chip requires four capacitors to function. I add connector for my RS-232 port, and I'm all set! The photos above show the raw board; since then, I've added two LEDs, and put the whole thing in a plastic enclosure. I've added a socket to the enclosure so I can power it with a small "wall wart" transformer instead of a 9V battery. It draws about 14mA, most of which goes toward the RS-232 chip. That's enough to drain a 9V transistor radio battery in a day or two, but it's a trivial load for a transformer to carry.

I haven't provided a schematic, but here's a description: The PicProto18 documentation shows how to hook up the voltage regulator and how to connect the crystal and two 22pF capacitors to the PIC. The datasheet for the PIC16F628 also tells how to hook the PIC to power and a crystal. The Max232 datasheet shows how to wire up power, ground, and capacitors to the Max232. The photo above shows how pins B1 and B2 from the PIC are wired to the MAX232's transmitter and receiver pins via the blue and purple wires, and how the MAX232's pins are wired to pins 2 and 3 of the RS-232 port via the red and orange wires. Pin 5 of the RS-232 port is hooked to ground, via the white wire. All other wires on the RS-232 port are left unconnected.

Of course you could do this on a different, cheaper prototype board if you've got something in your gadget box. I haven't measured the CM11a's voltage, but it wouldn't surprise me if you could get away with using +5/0 volts for serial port levels instead of +12/-12 volts that the RS232 spec calls for. However, you will at least need to invert the signals, since the PIC produces +5V for logic 1 and 0V for logic 0, while RS232 uses -12V for logic 1 and +12V for logic 0. Furthermore, you want to take care not to drive the PIC's input pins higher than Vdd or lower than Gnd, as it can cause evil things to happen to the PIC. Finally, the PIC pins can be very sensitive to static electricity, and the chip can be destroyed if a jolt of static hits one of the pins. The MAX232, while it couldn't withstand a direct hit of a lighting bolt, was designed to withstand ordinary static that might happen when touching the port pins. While there may be other ways of driving the serial port lines, the MAX232 chip handles all the issues very well.

Software

The .asm and .inc files for the software are available here.

I wrote the software in phases. I first wrote routines to display data on the LCD. Then I set about accessing the serial port. Only then did I write the software to display the output of a CM11A on the LCD. A fair bit of the software is devoted to producing a "human-readable" display of the X-10 action that the CM11A receives. I also wrote a routine to get the time from the CM11A, store it internally, and display it on the LCD. I call this every time the CM11A reports a command. At this point I had a nice device to monitor all X-10 activity and report it via the LCD

Finally I wrote a few routines and macros to send X-10 commands out to the powerline via the CM11A. I was able to transfer all of my ActiveHome macros to the SMOOCH at this time, and I could make the macros more intelligent. When the CM11A reports X-10 activity on the powerline, my software stores the comands in a buffer, and calls "domacros" to check the buffer for anything of interest, set variables, and issue any X-10 commands I want. I now have smart macros!

One issue I uncovered is that, when the CM11A executes a fast macro or timer, it does not report what X-10 action or timed event triggered the macro, nor does it report what command(s) the CM11A sent out as a result. The only thing it reports via its serial interface is the internal address in its EEPROM of the macro or timer it executed. This could be useful to the software which originally downloaded the macros/timers, but it is useless to me. Then again, you may not care so much -- fast macros from ActiveHome still work perfectly, it's just that my PIC can't know what's going on. I decide to move all my timers and macros to the SMOOCH, so that I can keep the CM11A's memory clear.

I wrote interrupt routines to maintain the real-time clock. Once per minute, I call the "dotimers" routine, which executes timed events. My LAB-X3 uses a ceramic resonator for its frequency standard, which isn't very precise. Mine gains about 15 seconds per hour. To keep things from getting too far out of sync, I update the SMOOCH's clock from the CM11A every time an x-10 action is reported, and every hour at 58 minutes past the hour. My SMOOCH hardware has a more stable crystal oscillator, and keeps time much better, but nevertheless, I keep the software in place to keep the clock synchronized with the CM11A's.

If power to the CM11A is interrupted and restored, the CM11A starts nagging the PC (or in my case, the SMOOCH) for the correct time. It won't speak to the SMOOCH until it receives the updated time from the serial port, so the system is stuck, and can't execute macros. I'm not sure I understand why it does this, since it has battery back-up for its clock. Anyway I wrote code to send the SMOOCH's clock to the CM11A in response to its nagging. Of course, if AC power was interrupted to the CM11A, it was probably also interrupted to the SMOOCH, so it's questionable whether I'm providing a correct time, but at least I'm getting things "unstuck". I finally solved the issue by providing a battery backup for the SMOOCH. This is just a 9V battery in parallel with the 12V wall-wart power supply. A diode in series with the battery prevents the wall-wart from inadvertantly charging the battery. The battery should be able to keep the PIC running smoothly and keeping perfect time for a power interruption of a day or so. The power, whether it's 9V from the battery or 12V from the wall-wart, goes through a 5V regulator before it gets to anything else on the circuit board, so the PIC and MAX232 see a nice continuous 5V supply.

This feature has proven itself; after a recent storm, my power was out for 17 hours. When power was restored, the home automation system worked perfectly, and its internal clock was still right.

My macros

Here's an informal English description of my macros.
  1. At dusk, turn the front walk lights on, and set a flag indicating that it's dark outside.
  2. At dawn, set the flag that indicates that it's light outside. Turn off all units on my default housecode (there normally shouldn't be any on, but just in case.)
  3. Turn the front walk lights off on the hour after dusk (if dusk was 7:18pm, turn them off at 8:00pm).
  4. When the porch motion detector detects motion, if it's dark outside, and if the lights aren't already on, turn them on, and set them to go off after five minutes.
  5. Also when the porch motion detector detects motion, ring the chime if it's a reasonable hour of the day. But if the detector detects several motion events in a row, only ring the chime on the first one (if I'm talking to someone at the door, I don't want the chime to keep sounding).
  6. When the porch motion detector says the motion events are over (This will happen one minute after it's detected the last motion), re-enable the chime for next time.
  7. If I push the special button in my car's garage door remote transmitter (that button isn't connected to the garage door), turn on the front walk lights, and turn them off after five minutes. This is similar to the porch motion detector, but if I go to the trouble of pushing the button, I want to do it regardless of time of day or dark/light conditions outside.
  8. My stereo/TV entertainment center is on a different housecode, so an "All Units Off" won't kill it, but I want to be able to turn it on or off via unit 7 of my default housecode.
  9. Turn my bedside radio on at 6:00am, and off at 9:00am every day.
  10. I've got a couple of radio remote transmitters that lack an "all on"/"all off" button, plus, I've got two sets of low-voltage lights on transformers that are controlled by appliance modules, which don't respond to "All lights on", anyway. So unit 8 is a macro to turn my backyard and deck lights on, plus two indoor lights on. The indoor lights should be dimmed to 30%. When I push the "off" button for unit 8, just send an "All units off" command.
  11. At 2:45am and 2:50am, turn all units of my default houscode off, and turn my stereo/TV off. These shouldn't have been left on, but stranger things have happened.
  12. On weekday mornings (not weekends) ring the chime at 8:15 and again at 8:20. Turn the deck lights on at 8:15 and off at 8:20, as well, because I might be out on the deck, soaking in the hot tub. The deck lights will let me know it's time to get ready for work in this case.
  13. I have two keychain remotes. One uses units K1 and K2, and the other uses units K5 and K6. K1 and K5 are programmed as "all lights on/ all units off" buttons. K2 is programmed to turn my stereo on and off, and K6 is programmed to turn my bedside radio on and off.
  14. Housecode "P" is reserved for changing the mode of the controller. (Originally "P" stood for "party", but maybe it now stands for "Programming".) P1 is "party mode". A P1 ON command will put the controller into party mode, meaning that it will never turn the outdoor lights off automatically via a timer. It will also turn ALL outdoor lights on at dusk (usually, I just turn the front walk lights on at dusk). P1 OFF cancels party mode. Also, party mode is always cancelled at dawn.
  15. P2 ON puts the controller into "Christmas light" mode. The outdoor Christmas lights are controlled through the same address as the front walk lights. "Christmas mode" just makes sure the outdoor lights aren't turned off before 8:00pm (they go on at dusk, as usual). P2 OFF cancels "Christmas light" mode.

    When either "P1 ON", "P1 OFF", "P2 ON", or "P2 OFF" is sensed, the interior lights in the family room are blinked on and off to acknowledge reception of the command.

Keeping the clock perfectly synced

I'm using the SMOOCH as my alarm clock, among other things. The CM11A and the SMOOCH board both have reasonably accurate quartz crystal clocks, but they're not perfect. After a few months, I'd notice things were a minute or so off. So I used to have to periodically reset the CM11a's clock, when the time inaccuracy became annoying. To do this, I'd unplug the CM11a from the SMOOCH, and plug it into my PC. After setting my PC's clock precisely, I'd fire up ActiveHome to set the CM11a's clock from the PC. Then, I'd reconnect the CM11a with the SMOOCH, and the CM11a would update the SMOOCH's clock.

In the grand scheme of things, this isn't such an awful task, but I also have a radio controlled clock that automatically keeps itself synchronized to the National Institute of Standards and Technology master clock in Colorado. I get really spoiled by having an accurate clock that stays perfectly accurate with no intervention on my part needed to keep it that way. I started thinking about ways to synchronize my SMOOCH with the accurate clock. It doesn't necessarily have to be precise to the second, but I don't want it to drift so far off that it needs to be reset periodically. I figure if I set the alarm clock to go off once per day at a predetermined time, the SMOOCH can resync itself once per day, and that ought to be more than accurate enough for my purposes.

I considered hooking up the clock's alarm signal to an X-10 transmitter, so that it would send an X-10 signal at a precise time every day. But I decided it was just as simple to hard-wire the clock directly to the SMOOCH. I installed an earphone jack into the alarm portion of the clock, wired just like an earphone jack on a radio. When nothing's plugged in, it works as normal, but when something's plugged into the jack, the alarm signal is sent out through the plug, and the alarm speaker is silent. So now the alarm clock sends an audio signal through that cable at precisely the same time every day.

The other end of the cable is connected to the SMOOCH, so that it toggles pin RB7 of the PIC. I used a single NPN transistor and resistor as a straightforward inverting amplifier. The transistor's emitter is connected to ground, the alarm clock signal goes through a resistor across the base-emitter junction of the transistor, and pin RB7 is connected to the transistor's collector. I programmed the PIC to provide weak internal pull-up on this pin, and the transistor will pull the pin's voltage down when the alarm sounds.

The rest of the work is done in software. The PIC can be configured to generate an interrupt any time the RB7 pin changes state. The alarm clock continues sounding for a couple of minutes after its initial time, but I want to ignore all except the first interrupt. I set the alarm clock to go off at 3:34am every day (an allusion to the '70s rock hit "Twenty-Five or Six to Four" by Chicago, plus it's a time when nothing else is likely to be happening). At 2:30am, I enable the PIC's interrupt, telling it to watch pin RB7 and generate an interrupt when its state changes. When the first interrupt is generated, the interrupt handling routine disables any further interrupts on RB7, so we should only get one per day. Of course, the interrupt routine also sets the SMOOCH's clock and the CM11a's clock precisely.

A side effect of doing this this way is that, since the alarm clock automatically switches back and forth for daylight savings time, my SMOOCH does, too! That's why I start watching over an hour before I expect the alarm clock to go off. That's also why I do the synchronization in the wee hours of the morning.

My radio controlled alarm clock was made by Oregon Scientific, and I purchased it for $30.00 at a local mall.

Miscellaneous usage notes and "Gotchas"

To use the SMOOCH:

  1. Decide what timers and macros need more "smarts". You can still use ActiveHome to manage simple macros and timers if you like, but realize that, if you've got an ActiveHome fast macro triggered by "A2 On", then the CM11A will never let the SMOOCH know when an "A2 On" event happens. Also, the SMOOCH can't trigger the CM11A's fast macros. Other than these limitation, you can freely mix ActiveHome fast macros/timers and SMOOCH smart macros/timers. ActiveHome macros are generally much simpler to write and debug.
  2. Write desired timers and macros into the .asm file (dotimers and domacros functions), and assemble the .asm file into a .hex file using MPLAB.
  3. Start the fpp.exe programming software, and load the .hex file that was produced in the previous step.
  4. Connect the programming hardware, and click on the "program" button in fpp. Click "Verify" to make sure the chip programmed OK.
  5. Remove the chip from the programmer, and insert it into its socket on the SMOOCH board.
  6. Use ActiveHome software to set the clock on the CM11A, if it's not already set properly.
  7. Connect the serial cable between the CM11A and the SMOOCH, and apply power.
Voila! You now have smart macros operating on cheap hardware!

The CM11A comes with a cable that connects its RJ-11 jack to a DE-9 male connector, to plug into a PC's standard DE-9 female serial port. The LAB-X3 has a DE-9 male connector, so it can connect to a PC using a straight-through connector. If you've been reading carefully, you realize that this means the CM11A's cable can't be plugged directly into the LAB-X3. You could make your own cable, but I happened to have a "reverse null modem" male-to-male gender changer with pins 2 & 3 swapped. I built the SMOOCH board with a header, connected to a male DE-9 connector via a short ribbon cable, so it can use the CM11A's cable directly. You could build a SMOOCH board to use an RJ-11 jack instead of the DE-9 connector, so you could connect the SMOOCH and the CM11A using an ordinary modular phone cord.

ActiveHome has some issues regarding its handling of daylight savings time. Different versions behave differently, but at least some versions try to keep the CM11A on standard time year-round, while displaying the time adjusted an hour for daylight savings time, as appropriate. I've found the easiest thing to do is to lie to the ActiveHome software, and tell it my area does not use DST. This causes ActiveHome to always set the CM11A to wall-clock time. This used to mean I had to reset the CM11A's clock twice a year, and it would probably confuse ActiveHome's dawn/dusk calculations. But now, I've solved the DST issue by synchronizing the SMOOCH using a radio controlled alarm clock that knows about DST. I never used ActiveHome's dusk/dawn calculations, since I rely on a a Hawkeye motion/daylight sensor to detect dawn and dusk.

I wrote the software to operate two LEDs as follows: One LED blinks briefly every four seconds, as a "heartbeat" to show things are working. A second LED lights up when it's waiting for the CM11A to acknowledge a transmission, so it blinks irregularly when X-10 activity is received or sent. If power has been interrupted and the system clock is questionable, the "hearbeat" starts racing, making the LED blink twice per second. It'll go back to normal when it receives a synchronization alarm from the alarm clock (as described above), or when power is removed and restored from the SMOOCH.

Although I haven't done rigorous testing, SMOOCH macros seem to be every bit as fast as ActiveHome macros that do similar X-10 functions. The major bottleneck always seems to be sending the X-10 data across the limited-bandwith AC powerline. In a single half-cycle (1/120 of a second), the X-10 system can send one BIT of data, or the SMOOCH and CM11A can exchange 4 bytes of data at 4800 baud, or the PIC can execute over 8,300 instructions! So the PIC is easily fast enough to keep up with the X-10 data stream. Obviously, the SMOOCH doesn't take any time to wake up or spin up a disk drive, a possible advantage over some PC hardware.

Future possibilities

I'm always thinking of small changes to make to macros and timed events, or I'm adding lights or other devices to be controlled. That will continue indefinitely.

I think I'll soon get around to storing the "Christmas mode" setting in the PIC's EEPROM memory, so that it will remember whether it was in Christmas mode when power is lost, and even when the chip is reprogrammed! (The EEPROM memory is separate from the program memory, so reprogramming the chip can still preserve EEPROM data).

There are several unused I/O ports left on the PIC, so this could be a basis for hooking up all sorts of other devices. The first additional device I connected was the alarm clock (see discussion about keeping the clock perfectly synced, above). There may be more someday.

I've used about half the code space on the PIC. Most of the code is "housekeeping" stuff to interface to the LCD, display human-readable interpretations of X-10 buffers, manage the clock, etc. I haven't taken any extraordinary efforts to make the code "tight". I've got LOTS of room for more complex timers and macros. I could tighten up the "dlay" macro, or eliminate some LCD display routines if space were at a premium, but it's not.

Someday I'm going to use a second copy of this same hardware, plus an old 2400 baud modem I have lying around, to create a device that watches my phone line, listening for rings. When it hears the phone ring, it counts the rings, waits a few minutes for the answering machine to take a message, and then dials my pager, telling me my phone rang, and reporting how many rings it heard. If I get ambitious, I may wire it to the answering machine's blinking light, so it can tell me whether or not the calling party left a message. Everything else can be done with just a serial port.

Links and References


Copyright © 2002, Richard Cochran