X10 - CM11a Receive/Monitor Powerline Commands

Greetings!

I thought I'd share a Proof of Concept for receiving powerline commands from an X10 CM11a serial interface. What it does is recieve commands via CM11a <> TTL shifter <> software serial and print commands to Arduino hardware serial.

This could be modified to control or display commands with your arduino. The CM11a is an RS232 device and there's no need for the "zero-crossing" based PSC05/TW523 module.

You MUST use a ttl level shifter, do NOT connect an RS232 device directly to your arduino.

I hope someone likes this, I could not find any other code to receive commands with CM11a. I'm a hobbyist; the code is what it is. I hope this might inspire someone to make it better.

Thanks, Dan G.

Credits:

Arduino IDE 1.0.3


/*
Proof of Concept: CM11a Powerline Command Monitor 
Author: Dan G.

Recieve commands via CM11a <> TTL shifter <>
software serial and print commands to Arduino hardware serial.

Since the PSC05/TW523 modules are no longer made.  CM11a can be used to receive PLC.
They are much cheaper and available.

Do NOT connect CM11a directly to Arduino, you MUST use TTL level shifter.

Search for "cm11a_protocol.txt" for more details.

Credits:
- Arduino folks, of course!
- "LJRob" for the initial sketch for sending commands via CM11a
  see: http://arduino.cc/forum/index.php?topic=136227.0
- BroHogan - Parse code at http://playground.arduino.cc/X10/ReceiveX10
*/

#define ON   B10000000  // Bitshifted ON
#define OFF  B11000000  // Bitshifted OFF

byte lookup[16] = {  // Lookup table for House and Unit Code
  //Binary  House	Unit
  B0110,  // A		1
  B1110,  // B		2
  B0010,  // C		3
  B1010,  // D		4
  B0001,  // E		5
  B1001,  // F		6
  B0101,  // G		7
  B1101,  // H		8
  B0111,  // I		9
  B1111,  // J		10
  B0011,  // K		11
  B1011,  // L		12
  B0000,  // M		13
  B1000,  // N		14
  B0100,  // O		15
  B1100,  // P		16
};

#include <SoftwareSerial.h>	// For the TTL to RS232
SoftwareSerial X10Serial(2, 3); // RX, TX to TTL RS232 



byte incomingByte; // Controller status
byte Hc;           // Housecode
char chrHc = Hc;   // Housecode to char
byte U;            // Unit
byte Cmd;          // ON or OFF

//Serial buffer
byte serIn; // var that will hold the bytes-in read from the serialBuffer
byte serInString[9]; // CM11a can send up to 10 bytes (program only handles 3 bytes)
int serInIndx = 0; // index of serInString[] in which to insert the next incoming byte
int serOutIndx = 0; // index of the outgoing serInString[] array;

void setup() {

  Serial.begin(57600);
  X10Serial.begin(4800);    // X10 Baud 4800 8,NONE,1
  Serial.println("x10 CM11a Ready:");
}

void loop(){
  
  CheckX10controller();  // Check CM11a for data or power failure.
  parseX10SerialString();
  
}

// Read from X10 controller
void CheckX10controller() {

  if (X10Serial.available() > 0) {
    incomingByte = X10Serial.read();

    switch (incomingByte) {
      
      case ((byte)0xA5): // Polling: This happens the first time you plug the X10 controller in the socket or after a power cut
      delay(100);  // CM11a is slow; don't write too fast, won't work!
      X10Serial.write((byte)0x9B); // Acknowledge / Clear, This must be cleared or it will not except any other x10 commands
      delay(60);
      Serial.println("Power Supply Restored");
      break;

      case ((byte)0x5A): // Polling: When powerline command received, CM11a send this byte. 
      delay(100); // CM11a is slow; don't write too fast, won't work!
      X10Serial.write((byte)0xC3);   // Send byte, initiate the data transfer.
      delay(60);
      ReadData();   
      break;
      //default:  //Used for testing only
      //ReadData(); //Used for testing only
    }
  }
}

void ReadData() {

  int sb;
  if(X10Serial.available()) {
    while (X10Serial.available()){
      sb = X10Serial.read();
      serInString[serInIndx] = sb;
      serInIndx++;
    }
  }
}

void parseX10SerialString(){
  if( serInIndx > 0) {
    //debug Serial.print("Bytes Received: ");
    //debug Serial.println(serInString[0]);
    if (serInString[1] == 0) {
      //debug Serial.print("X10 Address 2nd byte is: ");
      //debug Serial.println(serInString[1], HEX);
      //debug Serial.print("HcU in BIN: ");
      //debug Serial.println(serInString[2], BIN);
      Hc = serInString[2] >> 4;  // Gets Housecode binary
      //debug Serial.print("Hc in BIN: ");
      //debug Serial.println(Hc, BIN);
      for (byte i=0; i<16; i++){ // use lookup table for Housecode
        if (lookup[i] == Hc){
          Hc = i+65;   // this gives int House 'A' - 'P'
          char chrHc = Hc;  // Convert int Hc to Char for Serial print
          Serial.print("Housecode: ");
          Serial.println(chrHc);
          break;            // stop search when found!
        }
      }
      U = serInString[2] << 4;   // Bitshift for Unit
      U = U >> 4; // Final "bitdance" for Unit
      //debug Serial.print("U in BIN: ");
      //debug Serial.println(U, BIN);
      for (byte i=0; i<16; i++){  // use lookup table for Unit
        if (lookup[i] == U){
          U = i+1;        // this gives Unit 1-16
          Serial.print("Unit: ");
          Serial.println(U);
          break;          // stop search when found!
        }
      }
    }
    if (serInString[1] == 1) {
      //debug Serial.print("X10 Function 2nd byte is: ");
      //debug Serial.println(serInString[1], BIN);
      //debug Serial.print("Function: ");
      Cmd = serInString[2] << 6; // Bitshift out redundant Housecode
      Serial.print("Command: ");
      if(Cmd == B10000000)Serial.println("ON ");
      if(Cmd == B11000000)Serial.println("OFF ");
      //debug Serial.print("binary: ");
      //debug Serial.println(Cmd, BIN);
    }
  }
  //reset all the functions to be able to fill the string back with content
  serOutIndx = 0;
  serInIndx = 0;
}