Skip to main content.

X10 CM15A USB ActiveHome Controller

This little piece of hardware is fun. It allows for two-way communications between a PC and an X10 network. It supports both powerline and RF-based X10 communications. Bundled with it is a proprietary program known as ActiveHome Pro. I bought the box, hoping that the SDK that was advertised would be worth its salt.

That SDK consists of an OCX that lets you send UNIT ON and UNIT OFF commands to the network. It does not let you take advantage of the fact that the CM15A talks to the PC at all. I wanted to set up software actions to react to X10 commands. This just wouldn't do.

Goal

My goal was to develop enough knowledge to understand all of the signals coming over the powerline and all of the signals coming over the radio, and to be able to write a program that would react to these signals and take some sort of software action (turning music on or off, changing the playlist, changing the volume, etc).

Note that my goal involved developing read capabilities only. I never actually used the ActiveHome Pro software while developing this all, and personally don't have any documentation on how to send to the network yet.

Resources

Procedure

I followed the following procedure to get this protocol description:

  1. Fired up Snoopy Pro
  2. Attached it to the CM15A
  3. Hit a button on my X10 remote and verified that it incremented the captured packet count
  4. Began writing down the hex dumps of the packets I saw while iterating through each unit code from 1 to 16, making sure to capture both an ON and an OFF packet
  5. Froze the unit code and rotated between house codes from A to P, capturing data as I went
  6. Looked at my data and compared packets to see which bits changed in reaction to the house code changing and which reacted to the unit code changing
  7. Realized that the bits designating the unit and house code were shuffled throughout the packet, and found a way to put them back together
  8. Implemented a test driver using libusb to test that my findings worked

Protocol Description

The protocol differentiates between X10 commands received via powerline, X10 commands received via RF, X10 commands sent, and several controller-specific items (macros, memory dumps, clock signals, and the like). This is an incomplete description of the protocol and only features the reception and transmission of X10 commands. I haven't played with controller-specific items, and don't particularily care to--if you figure those out, feel free to contact me if you'd like me to post your information.

General Commands (opcode 0x5a)

The general commands were described in detail by Eclipse Home Automation's PDF on the CM15a prior to the writing of this article. As such, credit should be given to them for this section, since they figured it out first. You may see some similarities, as I have kept their convention for designating which nibble belongs to a house code, a unit code, or a function code.

Format: 5a sz pt [data] ...
where sz is the number of bytes following sz, including tt and onward;
where pt is the type of the packet, to be described below

Encoded House, Unit, and Function Codes

Either the CM15a or the X10 network does some encoding on the unit, house, and function codes before sending them. They're bit-scrambled and I wound up brute-forcing them, so here's a lookup table that describes them.

Encoding Table

Src. Dec.Src. HexFunctionHouseUnitDst. Dec.Dst. Hex
0 0x00 ALL_UNITS_OFF A1 6 0x06
1 0x01 ALL_LIGHTS_ON B2 14 0x0e
2 0x02 ON C3 2 0x02
3 0x03 OFF D4 10 0x0a
4 0x04 DIM E5 1 0x01
5 0x05 BRIGHT F6 9 0x09
6 0x06 ALL_LIGHTS_OFF G7 5 0x05
7 0x07 EXTENDED_CODE H8 13 0x0d
8 0x08 HAIL_REQ I9 7 0x07
9 0x09 HAIL_ACK J10 15 0x0f
10 0x0a PRESET_DIM_LOW K11 3 0x03
11 0x0b PRESET_DIM_HIGHL12 11 0x0b
12 0x0c EXTENDED_DATA M13 0 0x00
13 0x0d STATUS_ON N14 8 0x08
14 0x0e STATUS_OFF O15 4 0x04
15 0x0f STATUS_REQ P16 12 0x0c

Decoding Table

Dst. Dec.Dst. HexFunctionHouseUnitSrc. Dec.Src. Hex
12 0x0c EXTENDED_DATA M13 0 0x00
4 0x04 DIM E5 1 0x01
2 0x02 ON C3 2 0x02
10 0x0a PRESET_DIM_LOW K11 3 0x03
14 0x0e STATUS_OFF O15 4 0x04
6 0x06 ALL_LIGHTS_OFF G7 5 0x05
0 0x00 ALL_UNITS_OFF A1 6 0x06
8 0x08 HAIL_REQ I9 7 0x07
13 0x0d STATUS_ON N14 8 0x08
5 0x05 BRIGHT F6 9 0x09
3 0x03 OFF D4 10 0x0a
11 0x0b PRESET_DIM_HIGHL12 11 0x0b
15 0x0f STATUS_REQ P16 12 0x0c
7 0x07 EXTENDED_CODE H8 13 0x0d
1 0x01 ALL_LIGHTS_ON B2 14 0x0e
9 0x09 HAIL_ACK J10 15 0x0f

X10 Commands Received from Powerline

The way in which the RF transceiver I own operates is a two-step process, involving packet types 0x00, 0x01, and 0x02, as follows.

Step 1 Format: 0x5a 02 00 hd
where h is an encoded house code nibble;
where d is an encoded unit code nibble

This step selects the device about to be commanded.

Step 2 Format: 0x5a 02 01 hf
where h is an encoded house code nibble;
where f is an encoded function code nibble

This step actually executes a function upon the previously selected unit. Two other variants of this command supposedly exist (one with sz = 0x03, and one with sz = 0x04), but were not seen on my network. The Eclipse Home Automation document describes these.

These two commands are all you need to be aware of to receive commands from a MiniPad remote and the basic X10 receiver.

X10 Commands Received over Radio

Format: 0x5d 20 hb ~hb ua ub
where hb is a house code designated by a scrambling format for which a map follows;
where ~hb is the result of a binary NOT on hh;
where ua ub is a two-byte function specification (containing both unit code and function code), to be described

hb Format

b is a nibble describing whether we're looking at a high bank or a low bank. It is equal to 0x04 if we're on a high bank (units 9-16), and is equal to 0x00 if we're using a low bank (unit codes 1-8). It may be a bit-field containing other flags that I haven't discovered.

h is a nibble describing the house code. It is not encoded in the same way as powerline signals. This time, it's governed by either an algorithm I failed to reverse-engineer, or just arbitrary shuffling. Either way, an encoding table follows.

h Encoding Table

HouseNumberCoded Nibble
A 0 0x6
B 1 0x7
C 2 0x4
D 3 0x5
E 4 0x8
F 5 0x9
G 6 0xa
H 7 0xb
I 8 0xe
J 9 0xf
K 10 0xc
L 11 0xd
M 12 0x0
N 13 0x1
O 14 0x2
P 15 0x3

h Decoding Table

HouseNumberCoded Nibble
M 12 0x0
N 13 0x1
O 14 0x2
P 15 0x3
C 2 0x4
D 3 0x5
A 0 0x6
B 1 0x7
E 4 0x8
F 5 0x9
G 6 0xa
H 7 0xb
K 10 0xc
L 11 0xd
I 8 0xe
J 9 0xf

ua ub Format

ua ub appears to always equal 0x88 77 for "BRIGHT" and 0x98 67 for "DIM".

For all other commands, take apart the unit code you wish to address, down to the binary level. For instance, unit code 16 is a decimal 15, so in binary it is 1111. Similarly, unit code 8 would equal 0111. We'll consider the rightmost bit to be B1, and the leftmost to be B4.

The following is a description of each bit in a ON or OFF command. ON will equal 1 if we're turning a device on, and will equal 0 if we're turning one off. ~B1 will equal the NOT of B1.

ua Format: [constant 0] [B3] [~ON] [B1] [B2] [constant 0] [constant 0] [constant 0]

ub Format: [constant 1] [~B3] [ON] [~B1] [~B2] [~B4] [constant 1] [constant 1]

You'll notice that the second byte is the NOT of the first one, with one exception: B4 does not appear in the first byte. I don't know why this occurs. Either way, I've also attached an encoding table for this one.

ua ub Encoding Table

Unitua ub for ONua ub for OFF
10x00 ff0x20 df
20x10 ef0x30 cf
30x08 f70x28 d7
40x18 e70x38 c7
50x40 bf0x60 9f
60x50 af0x70 8f
70x48 b70x68 97
80x58 a70x78 87
90x00 fb0x20 db
100x10 eb0x30 cb
110x08 f30x28 d3
120x18 e30x38 c3
130x40 bb0x60 9b
140x50 ab0x70 8b
150x48 b30x68 93
160x58 a30x78 83