├── library.json ├── examples ├── x10_blink │ └── x10_blink.ino ├── x10_fade │ └── x10_fade.ino ├── x10_multi │ └── x10_multi.ino └── x10_receive │ └── x10_receive.ino ├── README.txt ├── psc05.h ├── keywords.txt ├── x10constants.h ├── x10.h └── x10.cpp /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "X10", 3 | "keywords": "x10, protocol, ac", 4 | "description": "Sending X10 signals over AC power lines (PL513, TW523 and etc)", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/DougC/arduino-x10.git" 9 | }, 10 | "exclude": "examples/*/applet", 11 | "frameworks": "arduino", 12 | "platforms": "atmelavr" 13 | } 14 | -------------------------------------------------------------------------------- /examples/x10_blink/x10_blink.ino: -------------------------------------------------------------------------------- 1 | /* 2 | X10 blink 3 | 4 | Blinks an lamp on an X10 lamp module. 5 | Example was built using a PL513 6 | X10 One-Way Interface Module from http://www.smarthome.com 7 | as the modem, and a Powerhouse X10 Lamp Module from Smarthome 8 | to plug the lamp in. 9 | 10 | created 15 June 2007 11 | by Tom Igoe 12 | 13 | v4 demo, creatrope, uses HOUSE_A instead of A 14 | 15 | */ 16 | #include 17 | #include 18 | 19 | #define zcPin 2 20 | #define dataPin 3 21 | 22 | // set up a new x10 instance: 23 | x10 myHouse; 24 | 25 | void setup() { 26 | Serial.begin(57600); 27 | myHouse.init(zcPin, dataPin); 28 | Serial.println(myHouse.version()); 29 | } 30 | 31 | void loop() { 32 | Serial.println("Lights on:"); 33 | // send a "lights on" command 3 times: 34 | myHouse.write(HOUSE_A, ON,3); 35 | 36 | delay(500); 37 | Serial.println("Lights off:"); 38 | // send a "lights off" command 3 times: 39 | myHouse.write(HOUSE_A, OFF,3); 40 | delay(500); 41 | } 42 | -------------------------------------------------------------------------------- /examples/x10_fade/x10_fade.ino: -------------------------------------------------------------------------------- 1 | /* 2 | X10 dimmer 3 | 4 | Dims and brightens an incandescent lamp on an X10 5 | lamp module. Example was built using a PL513 6 | X10 One-Way Interface Module from http://www.smarthome.com 7 | as the modem, and a Powerhouse X10 Lamp Module from Smarthome 8 | to plug the lamp in. 9 | 10 | created 15 June 2007 11 | by Tom Igoe 12 | 13 | v4 demo, creatrope, uses HOUSE_A instead of A 14 | 15 | */ 16 | 17 | 18 | #include 19 | #include 20 | 21 | #define zcPin 2 22 | #define dataPin 3 23 | 24 | // set up a new x10 instance: 25 | x10 myHouse; 26 | 27 | void setup() { 28 | Serial.begin(57600); 29 | myHouse.init(zcPin, dataPin); 30 | Serial.println(myHouse.version()); 31 | // send a "Lights ON" command 3 times: 32 | myHouse.write(HOUSE_A, ON,3); 33 | } 34 | 35 | void loop() { 36 | Serial.println("Lights up:"); 37 | // send a "lights BRIGHT" command 19 times. 38 | // it takes 19 BRIGHT or DIM commands to get 39 | // an incandescent lamp dim or bright. 40 | myHouse.write(HOUSE_A, BRIGHT,19); 41 | 42 | delay(500); 43 | Serial.println("Lights down:"); 44 | // send a "lights DIM" command 19 times: 45 | myHouse.write(HOUSE_A, DIM,19); 46 | delay(500); 47 | } 48 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | I've forked this from Creatrope's code at https://docs.google.com/file/d/0B5Sg6E9g_zOXMzQxZmVkYjktNjQwZi00MjgxLTk4YzQtNGIwYzI0ZjA0Njg3/edit?pli=1. There doesn't seem to have been any work done on this since 2010 and some of the Arduino API has been updated since then. 2 | 3 | -- Doug 4 | 5 | 6 | V4 X10 Send/Receive Code 7 | by creatrope 8 | 9 | Place this library in your libraries folder. It has been tested with Arduino 0018. 10 | 11 | It is an upgraded version of the X10 library that incorporates the original X10 send code, with Brohogan's X10 receive code. 12 | 13 | This preliminary version is the most minimal integration possible - no attempt has been made to combine similar structures internally for maximal efficiency. 14 | 15 | It is not quite upward compatible with the original v3 X10 send library. 16 | 17 | The single letter X10 house codes #defines, which caused me lots of problems, have been replaced with #defines like HOUSE_A, HOUSE_B, rather than just A or B. 18 | 19 | There are some new return functions, one that returns the largely unintelligible 'binary' house and unit codes, which are used for input in the original v3 library, and easily readable/printable ascii house codes and integer units codes. 20 | 21 | The indicator LED pin is now settable. 22 | 23 | There are several constructors, one of which retains the simple two field zCross and data output pin, and additional constructors that take one or both of the input pin and LED pin. 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /psc05.h: -------------------------------------------------------------------------------- 1 | /* 2 | 2017-APR-17 Richard Hughes Version 0.5 3 | 4 | - Added timing definitions for 50Hz mains frequency. 5 | */ 6 | 7 | #ifndef LPCS05 8 | #define LPCS05 9 | // Defines and constants for PSC05 receiving 10 | 11 | #define OFFSET_DELAY 500 // uS from zero cross to center of bit (sugg 500-700 us) 12 | #define HALF_CYCLE_DELAY 8334 // Calculated 8334 uS between bit repeats in a half-cycle 13 | 14 | // Timings for 50Hz mains frequency 15 | #define OFFSET_DELAY_50 800 // Microseconds from zero cross to center of bit 16 | #define HALF_CYCLE_DELAY_50 10000 // Microseconds between bit repeats in a half-cycle 17 | 18 | #ifndef ON // use same defines from x10constants.h for rcvd cmnds 19 | #define ON B00101 // these are examples 20 | #endif 21 | #ifndef OFF 22 | #define OFF B00111 23 | #endif 24 | 25 | byte House[16] = { // Lookup table for House Code 26 | B0110, // A 27 | B1110, // B 28 | B0010, // C 29 | B1010, // D 30 | B0001, // E 31 | B1001, // F 32 | B0101, // G 33 | B1101, // H 34 | B0111, // I 35 | B1111, // J 36 | B0011, // K 37 | B1011, // L 38 | B0000, // M 39 | B1000, // N 40 | B0100, // O 41 | B1100, // P 42 | }; 43 | 44 | byte Unit[16] = { // Lookup table for Unit Code 45 | B01100, // 1 46 | B11100, // 2 47 | B00100, // 3 48 | B10100, // 4 49 | B00010, // 5 50 | B10010, // 6 51 | B01010, // 7 52 | B11010, // 8 53 | B01110, // 9 54 | B11110, // 10 55 | B00110, // 11 56 | B10110, // 12 57 | B00000, // 13 58 | B10000, // 14 59 | B01000, // 15 60 | B11000, // 16 61 | }; 62 | #endif 63 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Test 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | x10 KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | write KEYWORD2 16 | sendBits KEYWORD2 17 | waitForZeroCross KEYWORD2 18 | version KEYWORD2 19 | 20 | ###################################### 21 | # Instances (KEYWORD2) 22 | ####################################### 23 | 24 | 25 | ####################################### 26 | # Constants (LITERAL1) 27 | ####################################### 28 | HOUSE_A LITERAL1 29 | HOUSE_B LITERAL1 30 | HOUSE_C LITERAL1 31 | HOUSE_D LITERAL1 32 | HOUSE_E LITERAL1 33 | HOUSE_F LITERAL1 34 | HOUSE_G LITERAL1 35 | HOUSE_H LITERAL1 36 | HOUSE_I LITERAL1 37 | HOUSE_J LITERAL1 38 | HOUSE_K LITERAL1 39 | HOUSE_L LITERAL1 40 | HOUSE_M LITERAL1 41 | HOUSE_N LITERAL1 42 | HOUSE_O LITERAL1 43 | HOUSE_P LITERAL1 44 | 45 | UNIT_1 LITERAL1 46 | UNIT_2 LITERAL1 47 | UNIT_3 LITERAL1 48 | UNIT_4 LITERAL1 49 | UNIT_5 LITERAL1 50 | UNIT_6 LITERAL1 51 | UNIT_7 LITERAL1 52 | UNIT_8 LITERAL1 53 | UNIT_9 LITERAL1 54 | UNIT_10 LITERAL1 55 | UNIT_11 LITERAL1 56 | UNIT_12 LITERAL1 57 | UNIT_13 LITERAL1 58 | UNIT_14 LITERAL1 59 | UNIT_15 LITERAL1 60 | UNIT_16 LITERAL1 61 | 62 | ALL_UNITS_OFF LITERAL1 63 | ALL_LIGHTS_ON LITERAL1 64 | ON LITERAL1 65 | OFF LITERAL1 66 | DIM LITERAL1 67 | BRIGHT LITERAL1 68 | ALL_LIGHTS_OFF LITERAL1 69 | EXTENDED_CODE LITERAL1 70 | HAIL_REQUEST LITERAL1 71 | HAIL_ACKNOWLEDGE LITERAL1 72 | PRE_SET_DIM LITERAL1 73 | EXTENDED_DATA LITERAL1 74 | STATUS_ON LITERAL1 75 | STATUS_OFF LITERAL1 76 | STATUS_REQUEST LITERAL1 77 | 78 | -------------------------------------------------------------------------------- /examples/x10_multi/x10_multi.ino: -------------------------------------------------------------------------------- 1 | /* 2 | X10 multi 3 | 4 | Turns on and off AC appliances. Example was built using a PL513 5 | X10 One-Way Interface Module from http://www.smarthome.com 6 | as the modem, and two Powerhouse X10 Lamp Module from Smarthome 7 | to plug the lamps in. 8 | 9 | Set module 1 to house code A, unit code 1, and module 2 10 | to house code A, unit code 2. 11 | 12 | created 17 June 2007 13 | by Tom Igoe 14 | 15 | v4 demo, creatrope, uses HOUSE_A instead of A 16 | 17 | */ 18 | 19 | // include the X10 library files: 20 | #include 21 | #include 22 | 23 | #define zcPin 2 // the zero crossing detect pin 24 | #define dataPin 3 // the X10 data out pin 25 | #define repeatTimes 3 // how many times each X10 message should repeat 26 | // in an electrically noisy environment, you 27 | // can set this higher. 28 | 29 | 30 | // set up a new x10 library instance: 31 | x10 myHouse; 32 | 33 | void setup() { 34 | // begin serial: 35 | Serial.begin(57600); 36 | myHouse.init(zcPin, dataPin); 37 | Serial.println(myHouse.version()); 38 | // Turn off all lights: 39 | myHouse.write(HOUSE_A, ALL_UNITS_OFF,repeatTimes); 40 | } 41 | 42 | void loop() { 43 | // Turn on first module: 44 | myHouse.write(HOUSE_A, UNIT_1,repeatTimes); 45 | myHouse.write(HOUSE_A, ON,repeatTimes); 46 | // Turn off second module: 47 | myHouse.write(HOUSE_A, UNIT_2,repeatTimes); 48 | myHouse.write(HOUSE_A, OFF,repeatTimes); 49 | delay(500); 50 | // Turn off first module: 51 | myHouse.write(HOUSE_A, UNIT_1,repeatTimes); 52 | myHouse.write(HOUSE_A, OFF,repeatTimes); 53 | // turn on second module: 54 | myHouse.write(HOUSE_A, UNIT_2,repeatTimes); 55 | myHouse.write(HOUSE_A, ON,repeatTimes); 56 | delay(500); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /examples/x10_receive/x10_receive.ino: -------------------------------------------------------------------------------- 1 | /* Arduino Interface to the PSC05 X10 Receiver. BroHogan 3/24/09 2 | * SETUP: X10 PSC05/TW523 RJ11 to Arduino (timing for 60Hz) 3 | * - RJ11 pin 1 (BLK) -> Pin 2 (Interrupt 0) = Zero Crossing 4 | * - RJ11 pin 2 (RED) -> GND 5 | * - RJ11 pin 3 (GRN) -> Pin 4 = Arduino receive 6 | * - RJ11 pin 4 (YEL) -> Pin 5 = Arduino transmit (via X10 Lib) 7 | * NOTES: 8 | * - Must detach interrup when transmitting with X10 Lib 9 | */ 10 | 11 | #include // X10 lib is used for transmitting X10 12 | #include // X10 Lib constants 13 | #define RPT_SEND 2 14 | 15 | #define ZCROSS_PIN 2 // BLK pin 1 of PSC05 16 | #define RCVE_PIN 4 // GRN pin 3 of PSC05 17 | #define TRANS_PIN 5 // YEL pin 4 of PSC05 18 | #define LED_PIN 13 // for testing 19 | 20 | x10 SX10;// set up a x10 library instance: 21 | 22 | void setup() { 23 | Serial.begin(57600); 24 | SX10.init(ZCROSS_PIN,TRANS_PIN,RCVE_PIN,LED_PIN); 25 | Serial.println(SX10.version()); 26 | delay(500); 27 | Serial.print("x10 receive/send test"); 28 | } 29 | 30 | // A simple test program that demonstrates integrated send/receive 31 | // prints X10 input, then sets D5 on/off if unit code on input was 1 32 | void loop(){ 33 | if (SX10.received()) { // received a new command 34 | SX10.debug(); // print out the received command 35 | SX10.reset(); 36 | if (SX10.unitCode() == 1){ 37 | byte cmndCode = SX10.cmndCode(); 38 | SX10.write(HOUSE_D,UNIT_5,RPT_SEND); 39 | if(cmndCode == ON) SX10.write(HOUSE_D,ON,RPT_SEND); 40 | if(cmndCode == OFF) SX10.write(HOUSE_D,OFF,RPT_SEND); 41 | } 42 | } 43 | } 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /x10constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | 2017-APR-17 Richard Hughes Version 0.5 3 | 4 | - Added timing definitions for 50Hz mains frequency measured with 5 | a logic analyser when running on Arduino Nano. 6 | */ 7 | 8 | #ifndef HX10C 9 | #define HX10C 10 | 11 | #define HIGH 0x1 12 | #define LOW 0x0 13 | 14 | #define BIT_DELAY 1778 // 1778 us between bit repeats in a half-cycle 15 | #define BIT_LENGTH 800 // each bit is slightly less than 1ms long 16 | 17 | 18 | // Timings for 50Hz mains frequency 19 | #define BIT_DELAY_50 2300 // Microseconds between bit repeats in a half-cycle for 3 phase mains. 20 | #define BIT_LENGTH_50 1000 // Each bit shoud be 1ms long but Arduino delay is not precise. 21 | 22 | 23 | #define HOUSE_A B0110 24 | #define HOUSE_B B1110 25 | #define HOUSE_C B0010 26 | #define HOUSE_D B1010 27 | #define HOUSE_E B0001 28 | #define HOUSE_F B1001 29 | #define HOUSE_G B0101 30 | #define HOUSE_H B1101 31 | #define HOUSE_I B0111 32 | #define HOUSE_J B1111 33 | #define HOUSE_K B0011 34 | #define HOUSE_L B1011 35 | #define HOUSE_M B0000 36 | #define HOUSE_N B1000 37 | #define HOUSE_O B0100 38 | #define HOUSE_P B1100 39 | 40 | #define UNIT_1 B01100 41 | #define UNIT_2 B11100 42 | #define UNIT_3 B00100 43 | #define UNIT_4 B10100 44 | #define UNIT_5 B00010 45 | #define UNIT_6 B10010 46 | #define UNIT_7 B01010 47 | #define UNIT_8 B11010 48 | #define UNIT_9 B01110 49 | #define UNIT_10 B11110 50 | #define UNIT_11 B00110 51 | #define UNIT_12 B10110 52 | #define UNIT_13 B00000 53 | #define UNIT_14 B10000 54 | #define UNIT_15 B01000 55 | #define UNIT_16 B11000 56 | 57 | #define ALL_UNITS_OFF B00001 58 | #define ALL_LIGHTS_ON B00011 59 | #define ON B00101 60 | #define OFF B00111 61 | #define DIM B01001 62 | #define BRIGHT B01011 63 | #define ALL_LIGHTS_OFF B01101 64 | #define EXTENDED_CODE B01111 65 | #define HAIL_REQUEST B10001 66 | #define HAIL_ACKNOWLEDGE B10011 67 | #define PRE_SET_DIM B10101 68 | #define EXTENDED_DATA B11001 69 | #define STATUS_ON B11011 70 | #define STATUS_OFF B11101 71 | #define STATUS_REQUEST B11111 72 | #endif 73 | -------------------------------------------------------------------------------- /x10.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stepper.h - - Stepper library for Wiring/Arduino - Version 0.4 3 | 4 | Original library (0.1) by Tom Igoe. 5 | Timing bug fixes (0.2) " " " 6 | 7 | Sends X10 commands. 8 | 9 | 2017-APR-17 Richard Hughes Version 0.5 10 | 11 | - Added timing varaibles for bitLength, bitDelay, offsetDelay and 12 | halfCycleDelay so that timings can be selected and runtime. 13 | - Moved definition of receive pin and LED pin from x10.cpp 14 | - Created new constructor without parameters and moved the init 15 | function to be public so that the class can be created and 16 | initialised seperately. 17 | 18 | */ 19 | 20 | // ensure this library description is only included once 21 | #ifndef x10_h 22 | #define x10_h 23 | 24 | // include types & constants of Wiring core API 25 | #include 26 | #include "Arduino.h" 27 | #include "pins_arduino.h" 28 | 29 | // library interface description 30 | class x10 { 31 | public: 32 | // constructors: 33 | x10(int zeroCrossingPin, int dataPin, int rp, int led); 34 | x10(int zeroCrossingPin, int dataPin, int rp); 35 | x10(int zeroCrossingPin, int dataPin); 36 | x10(); 37 | // write command method: 38 | void write(byte houseCode, byte numberCode, int numRepeats); 39 | int version(void); 40 | boolean received(void); 41 | byte unitCode(void); // returns integer unit code 42 | byte houseCode(void); // returns ascii A-P human readable house code 43 | byte uc(void); // returns binary unit code (x10constants.h) 44 | byte hc(void); // returns binary house code (x10constants.h) 45 | byte cmndCode(void); 46 | void reset(void); 47 | void debug(void); 48 | void Check_Rcvr(); 49 | void Parse_Frame(); 50 | void attach(); 51 | void detach(); 52 | void init(int zeroCrossingPin, int dataPin, int rp, int led); 53 | void init(int zeroCrossingPin, int dataPin, int rp); 54 | void init(int zeroCrossingPin, int dataPin); 55 | void detectMainsFreq(); 56 | // Timing varaibles. 57 | float mainsFrequency; 58 | long bitDelay; 59 | long bitLength; 60 | long offsetDelay; 61 | long halfCycleDelay; 62 | private: 63 | int zeroCrossingPin; // AC zero crossing pin 64 | int dataPin; // data out pin 65 | int recvPin; // Receive data pin 66 | int ledPin; // LED pin 67 | // sends the individual bits of the commands: 68 | void sendBits(byte cmd, byte numBits, byte isStartCode); 69 | // checks for AC zero crossing 70 | void waitForZeroCross(int pin, int howManyTimes); 71 | }; 72 | 73 | #endif 74 | 75 | -------------------------------------------------------------------------------- /x10.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | x10.cpp - X10 transmission library for Arduino version 0.4 3 | 4 | Original library (0.1) by Tom Igoe. 5 | Timing bug fixes (0.2) " " " 6 | #include bug fixes for 0012 (0.3) " " " 7 | Integrates brohogan.blogspot.com X10 receive code from Arduino Playground (0.4) 8 | 9 | Zero crossing algorithms borrowed from David Mellis' shiftOut command 10 | for Arduino. 11 | 12 | The circuits can be found at http://www.arduino.cc/en/Tutorial/x10 13 | 14 | 2017-APR-17 Richard Hughes Version 0.5 15 | 16 | - Introduced new function detectMainsFreq() called during initialisation 17 | to make library work with both 50Hz and 60Hz mains frequencies without 18 | user code changes giving the ability for devices using this library to 19 | work with 50Hz or 60Hz mains frequencies without modification. 20 | - Moved definition for receive pin and LED pin to x10.h and removed the 21 | zcross_in definition as this was already defined in x10.h and so duplicated. 22 | - Enchanced the version function to additionally print the selected mains 23 | frequency to serial. 24 | - Replaced hard coded interrupt number with digitalPinToInterrupt function 25 | so that interrupt number is correctly set if a zero crossing pin other than 26 | 2 is selected. May also help this code to run on ESP8266 although this 27 | has not been tested. 28 | 29 | 2018-FEB-18 Richard Hughes Version 0.6 30 | 31 | - Added delay(0) in loop within waitForZeroCross to prevent WDT reseting 32 | ESP8266. 33 | 34 | */ 35 | 36 | #include 37 | #include "Arduino.h" 38 | #include "x10.h" 39 | #include "x10constants.h" 40 | #include "psc05.h" 41 | 42 | volatile unsigned long mask; // MSB first - bit 12 - bit 0 43 | volatile unsigned int X10BitCnt; // counts bit sequence in frame 44 | volatile unsigned int ZCrossCnt; // counts Z crossings in frame 45 | volatile unsigned long rcveBuff; // holds the 13 bits received in a frame 46 | volatile boolean X10rcvd; // true if a new frame has been received 47 | boolean _newX10; // both the unit frame and the command frame received 48 | byte _houseCode,_unitCode,_cmndCode; // 49 | byte _hc,_uc; 50 | byte startCode; 51 | bool autoDetectFreq; 52 | 53 | x10 *object; 54 | 55 | //static void Check_Rcvr(); 56 | //static void Parse_Frame(); 57 | 58 | void x10_Check_Rcvr_wrapper() { 59 | object->Check_Rcvr(); 60 | } 61 | 62 | void x10_Parse_Frame_wrapper() { 63 | object->Parse_Frame(); 64 | } 65 | 66 | 67 | // Initialise instance of X10 object 68 | void x10::init(int zeroCrossingPin, int dataPin, int rp, int led) 69 | { 70 | object = this; // Need to populate object for *_wrapper() 71 | 72 | this->zeroCrossingPin = zeroCrossingPin; // the zero crossing pin 73 | this->dataPin = dataPin; // the output data pin 74 | this->recvPin = rp; 75 | this->ledPin = led; 76 | 77 | // Set I/O modes: 78 | pinMode(this->zeroCrossingPin, INPUT_PULLUP); // set 20K pullup (low active signal) 79 | pinMode(this->dataPin, OUTPUT); 80 | if (this->ledPin>0) { pinMode(this->ledPin, OUTPUT); } 81 | 82 | // If we have a receive pin specified. 83 | if (this->recvPin>0) { 84 | pinMode(this->recvPin,INPUT_PULLUP); // receive X10 commands - low = 1 - INPUT_PULLUP sets 20K pullup (low active signal) 85 | attachInterrupt(digitalPinToInterrupt(this->zeroCrossingPin),x10_Check_Rcvr_wrapper,CHANGE);// trigger zero cross 86 | X10BitCnt=0; // counts bit sequence in frame 87 | ZCrossCnt=0; // counts Z crossings in frame 88 | X10rcvd=false; // true if a new frame has been received 89 | _newX10=false; // both the unit frame and the command frame received 90 | } 91 | 92 | // Detect mains frequency. 93 | if(autoDetectFreq) { 94 | detectMainsFreq(); 95 | } else { 96 | // Default to 60Hz timings. 97 | this->mainsFrequency = 60; 98 | this->bitDelay = BIT_DELAY; 99 | this->bitLength = BIT_LENGTH; 100 | this->offsetDelay = OFFSET_DELAY; 101 | this->halfCycleDelay = HALF_CYCLE_DELAY; 102 | } 103 | 104 | } 105 | 106 | void x10::init(int zeroCrossingPin, int dataPin, int rp) { 107 | init(zeroCrossingPin,dataPin,rp,0); 108 | } 109 | 110 | void x10::init(int zeroCrossingPin, int dataPin) 111 | { 112 | init(zeroCrossingPin,dataPin,0,0); 113 | } 114 | 115 | x10::x10(int zeroCrossingPin, int dataPin, int rp, int led) 116 | { 117 | autoDetectFreq = false; 118 | init(zeroCrossingPin,dataPin,rp,led); 119 | autoDetectFreq = true; 120 | } 121 | x10::x10(int zeroCrossingPin, int dataPin, int rp) 122 | { 123 | autoDetectFreq = false; 124 | init(zeroCrossingPin,dataPin,rp,0); 125 | autoDetectFreq = true; 126 | } 127 | x10::x10(int zeroCrossingPin, int dataPin) 128 | { 129 | autoDetectFreq = false; 130 | init(zeroCrossingPin,dataPin,0,0); 131 | autoDetectFreq = true; 132 | } 133 | x10::x10() 134 | { 135 | autoDetectFreq = true; 136 | } 137 | 138 | 139 | /* 140 | Establish mains frequency and set appropriate parameters 141 | */ 142 | void x10::detectMainsFreq() { 143 | Serial.println("Detecting mains frequency ..."); 144 | // Default parameters for 60Hz 145 | this->bitDelay = BIT_DELAY; 146 | this->bitLength = BIT_LENGTH; 147 | this->offsetDelay = OFFSET_DELAY; 148 | this->halfCycleDelay = HALF_CYCLE_DELAY; 149 | // Wait for zero crossing before we start counting 150 | waitForZeroCross(this->zeroCrossingPin,1); 151 | float startFreqCount = micros(); 152 | // Wait for 200 half cycles to pass to get an average timing from 100 cycles. 153 | waitForZeroCross(this->zeroCrossingPin,200); 154 | float stopFreqCount = micros(); 155 | // Calculate frequency. 156 | this->mainsFrequency = 100000000 / (stopFreqCount - startFreqCount); 157 | // If frequency is below 54 cycles then we assume 50Hz which allows for a 60Hz 158 | // waveform to be 10% below specification. 159 | if(this->mainsFrequency < 54) { 160 | this->bitDelay = BIT_DELAY_50; 161 | this->bitLength = BIT_LENGTH_50; 162 | this->offsetDelay = OFFSET_DELAY_50; 163 | this->halfCycleDelay = HALF_CYCLE_DELAY_50; 164 | } 165 | } 166 | 167 | /* 168 | Writes an X10 command out to the X10 modem 169 | */ 170 | void x10::write(byte houseCode, byte numberCode, int numRepeats) { 171 | byte startCode = B1110; // every X10 command starts with this 172 | if (this->recvPin>0) { detachInterrupt(digitalPinToInterrupt(this->zeroCrossingPin)); } 173 | // repeat as many times as requested: 174 | for (int i = 0; i < numRepeats; i++) { 175 | // send the three parts of the command: 176 | sendBits(startCode, 4, true); 177 | sendBits(houseCode, 4, false); 178 | sendBits(numberCode, 5, false); 179 | } 180 | // if this isn't a bright or dim command, it should be followed by 181 | // a delay of 3 power cycles (or 6 zero crossings): 182 | if ((numberCode != BRIGHT) && (numberCode != DIM)) { 183 | waitForZeroCross(this->zeroCrossingPin, 6); 184 | } 185 | if (this->recvPin>0) { attachInterrupt(digitalPinToInterrupt(this->zeroCrossingPin),x10_Check_Rcvr_wrapper,CHANGE); } // trigger zero cross 186 | } 187 | /* 188 | Writes a sequence of bits out. If the sequence is not a start code, 189 | it repeats the bits, inverting them. 190 | */ 191 | 192 | void x10::sendBits(byte cmd, byte numBits, byte isStartCode) { 193 | byte thisBit; // copy of command so we can shift bits 194 | 195 | // iterate the number of bits to be shifted: 196 | for(int i=1; i<=numBits; i++) { 197 | // wait for a zero crossing change: 198 | waitForZeroCross(this->zeroCrossingPin, 1); 199 | // shift off the last bit of the command: 200 | thisBit = !!(cmd & (1 << (numBits - i))); 201 | 202 | // repeat once for each phase: 203 | for (int phase = 0; phase < 3; phase++) { 204 | // set the data Pin: 205 | digitalWrite(this->dataPin, thisBit); 206 | delayMicroseconds(this->bitLength); 207 | // clear the data pin: 208 | digitalWrite(this->dataPin, LOW); 209 | // Only delay between first to phases as final delay is what ever is left before zero cross. 210 | // This was we can be a bit more accurate (slighlty more delay between phases) without missing zero cross. 211 | if(phase < 2) { delayMicroseconds(this->bitDelay); } 212 | } 213 | 214 | // if this command is a start code, don't 215 | // send its complement. Otherwise do: 216 | if(!isStartCode) { 217 | // wait for zero crossing: 218 | waitForZeroCross(zeroCrossingPin, 1); 219 | for (int phase = 0; phase < 3; phase++) { 220 | // set the data pin: 221 | digitalWrite(this->dataPin, !thisBit); 222 | delayMicroseconds(this->bitLength); 223 | // clear the data pin: 224 | digitalWrite(dataPin, LOW); 225 | // Only delay between first to phases as final delay is what ever is left before zero cross. 226 | // This was we can be a bit more accurate (slighlty more delay between phases) without missing zero cross. 227 | if(phase < 2) { delayMicroseconds(this->bitDelay); } 228 | } 229 | } 230 | } 231 | } 232 | 233 | 234 | /* 235 | waits for a the zero crossing pin to cross zero 236 | 237 | */ 238 | void x10::waitForZeroCross(int pin, int howManyTimes) { 239 | unsigned long cycleTime = 0; 240 | 241 | // cache the port and bit of the pin in order to speed up the 242 | // pulse width measuring loop and achieve finer resolution. calling 243 | // digitalRead() instead yields much coarser resolution. 244 | uint8_t bit = digitalPinToBitMask(pin); 245 | uint8_t port = digitalPinToPort(pin); 246 | 247 | for (int i = 0; i < howManyTimes; i++) { 248 | // wait for pin to change: 249 | if((*portInputRegister(port) & bit)) 250 | while((*portInputRegister(port) & bit)) { 251 | cycleTime++; 252 | // Yield to prevent WDT reset 253 | delay(0); 254 | } 255 | else 256 | while(!(*portInputRegister(port) & bit)) { 257 | cycleTime++; 258 | // Yield to prevent WDT reset 259 | delay(0); 260 | } 261 | } 262 | } 263 | 264 | 265 | /* 266 | version() returns the version of the library: 267 | */ 268 | int x10::version(void) 269 | { 270 | int ver = 6; 271 | Serial.print("Zero Crossing Pin: "); 272 | Serial.println(this->zeroCrossingPin); 273 | Serial.print("Transmit Pin : "); 274 | Serial.println(this->dataPin); 275 | Serial.print("Receive Pin : "); 276 | Serial.println(this->recvPin); 277 | Serial.print("LED Indicator Pin: "); 278 | Serial.println(this->ledPin); 279 | Serial.print("Mains Frequency : "); 280 | Serial.println(this->mainsFrequency); 281 | Serial.print("Bit Length : "); 282 | Serial.println(this->bitLength); 283 | Serial.print("Bit Delay : "); 284 | Serial.println(this->bitDelay); 285 | Serial.print("Offset Delat : "); 286 | Serial.println(this->offsetDelay); 287 | Serial.print("Half Cycle Delay : "); 288 | Serial.println(this->halfCycleDelay); 289 | Serial.print("Version : "); 290 | return ver; 291 | } 292 | 293 | boolean x10::received(void) 294 | { 295 | return _newX10; 296 | } 297 | 298 | void x10::reset(void) 299 | { 300 | _newX10 = false; 301 | } 302 | 303 | byte x10::unitCode(void) 304 | { 305 | return _unitCode; 306 | } 307 | byte x10::houseCode(void) 308 | { 309 | return _houseCode; 310 | } 311 | byte x10::uc(void) 312 | { 313 | return _uc; 314 | } 315 | 316 | byte x10::hc(void) 317 | { 318 | return _hc; 319 | } 320 | byte x10::cmndCode(void) 321 | { 322 | return _cmndCode; 323 | } 324 | 325 | void x10::Check_Rcvr(){ // ISR - called when zero crossing (on CHANGE) 326 | if (X10BitCnt == 0) { // looking for new frame 327 | delayMicroseconds(this->offsetDelay); // wait for bit 328 | if(digitalRead(this->recvPin)) return; // still high - no start bit - get out 329 | if (this->ledPin>0) { digitalWrite(this->ledPin, HIGH); } // indicate you got something 330 | rcveBuff = 0; 331 | mask = 0x1000; // bitmask with bit 12 set 332 | rcveBuff = rcveBuff | mask; // sets bit 12 (highest) 333 | mask = mask >> 1; // move bit down in bit mask 334 | X10BitCnt = 1; // inc the bit count 335 | ZCrossCnt = 1; // need to count zero crossings too 336 | return; 337 | } 338 | // Begins here if NOT the first bit . . . 339 | ZCrossCnt++; // inc the zero crossing count 340 | // after SC (first 4 bits) ignore the pariety bits - so only read odd crossings 341 | if (X10BitCnt < 5 || (ZCrossCnt & 0x01)){ // if it's an odd # zero crossing 342 | delayMicroseconds(this->offsetDelay); // wait for bit 343 | if(!digitalRead(this->recvPin)) rcveBuff = rcveBuff | mask; // got a 1 set the bit, else skip and leave it 0 344 | mask = mask >> 1; // move bit down in bit mask 345 | X10BitCnt++; 346 | 347 | if(X10BitCnt == 13){ // done with frame after 13 bits 348 | for (byte i=0;i<5;i++)delayMicroseconds(this->halfCycleDelay); // need this 349 | X10rcvd = true; // a new frame has been received 350 | if (this->ledPin>0) { digitalWrite(this->ledPin, LOW); } // indicate you got something 351 | X10BitCnt = 0; 352 | x10_Parse_Frame_wrapper(); // parse out the house & unit code and command 353 | } 354 | } 355 | } 356 | 357 | #if 1 358 | void x10::Parse_Frame() { // parses the receive buffer to get House, Unit, and Cmnd 359 | if(rcveBuff & 0x1){ // last bit set so it's a command 360 | _cmndCode = rcveBuff & 0x1F; // mask 5 bits 0 - 4 to get the command 361 | _newX10 = true; // now have complete pair of frames 362 | } 363 | else { // last bit not set so it's a unit 364 | _unitCode = rcveBuff & 0x1F; // mask 5 bits 0 - 4 to get the unit 365 | _uc = _unitCode; 366 | _newX10 = false; // now wait for the command 367 | for (byte i=0; i<16; i++){ // use lookup table to get the actual unit # 368 | if (Unit[i] == _unitCode){ 369 | _unitCode = i+1; // this gives Unit 1-16 370 | break; // stop search when found! 371 | } 372 | } 373 | } 374 | rcveBuff = rcveBuff >> 5; // shift the house code down to LSB 375 | _houseCode = rcveBuff & 0x0F; // mask the last 4 bits to get the house code 376 | _hc = _houseCode; 377 | for (byte i=0; i<16; i++){ // use lookup table to get the actual command # 378 | if (House[i] == _houseCode){ 379 | _houseCode = i+65; // this gives House 'A' - 'P' 380 | break; // stop search when found! 381 | } 382 | } 383 | rcveBuff = rcveBuff >> 4; // shift the start code down to LSB 384 | startCode = rcveBuff & 0x0F; // mask the last 4 bits to get the start code 385 | X10rcvd = false; // reset status 386 | } 387 | #else 388 | // NEW VERSION - better error checking 389 | void Parse_Frame() { // parses the receive buffer to get House, Unit, and Cmnd 390 | // Remember this is called by ISR - so STILL IN ISR! - no delays or prints! 391 | byte rawCmndUnit; // Command or Unit portion of rcveBuff 392 | byte rawHouse; // House portion of rcveBuff 393 | byte foundMatch = false; // set when a match is found 394 | 395 | // break the rcveBuff into it's parts . . . 396 | if(rcveBuff & 0x1) procCmnd = true; // last bit set so frame is a command (2nd) 397 | else procCmnd = false; // else frame is a unit (1st) 398 | rawCmndUnit = rcveBuff & 0x1F; // mask 5 bits 0 - 4 get the unit / command 399 | rcveBuff = rcveBuff >> 5; // shift the house code down to LSB 400 | rawHouse = rcveBuff & 0x0F; // mask the last 4 bits to get the house code 401 | rcveBuff = rcveBuff >> 4; // shift the start code down to LSB 402 | X10.Start = rcveBuff & 0x0F; // mask the last 4 bits to get the start code 403 | 404 | // start with the House code - SB the same for both frames 405 | for (byte i=0; i<16; i++){ // use lookup table to get the actual command # 406 | if (House[i] == rawHouse){ 407 | X10.House = i+65; // this gives House 'A' - 'P' 408 | foundMatch = true; 409 | break; // stop search when found! 410 | } 411 | } 412 | // if no match, wipe out the start byte to abort the command 413 | if (foundMatch == false) X10.Start = 0; 414 | // if it's the 1st frame, save off the house 415 | if (foundMatch == true && procCmnd == false) firstHouse = X10.House; 416 | // if it's the 2nd frame, compare the house code for the 2 frames 417 | if (foundMatch == true && procCmnd == true){ 418 | if (X10.House != firstHouse) X10.Start = 0; // abort if not the same 419 | } 420 | // If it's the 1st frame process the Unit code . . . 421 | if (procCmnd == false){ // last bit not set so it's a unit 422 | _newX10 = false; // now wait for the command 423 | foundMatch = false; // reset this before search 424 | for (byte i=0; i<16; i++){ // use lookup table to get the actual unit # 425 | if (Unit[i] == rawCmndUnit){ 426 | X10.Unit = i+1; // this gives Unit 1-16 427 | foundMatch = true; 428 | break; // stop search when found! 429 | } 430 | } 431 | if (foundMatch == false) X10.Start = 0; // wipe out the start byte if no match 432 | } 433 | 434 | // If it's the 2nd frame process the Command code . . . 435 | if (procCmnd == true){ // last bit set so it's a command 436 | if (rawCmndUnit == ON) X10.Cmnd=on; // convert commands to generic commands 437 | else if (rawCmndUnit == OFF) X10.Cmnd=off; 438 | else if (rawCmndUnit == DIM) X10.Cmnd=dim; 439 | else if (rawCmndUnit == BRIGHT) X10.Cmnd=bright; 440 | else X10.Start = 0; // wipe out the start byte if no match 441 | _newX10 = true; // now have complete pair of frames 442 | } 443 | 444 | // if the command was set as invalid, it's not a new command 445 | if (X10.Start != B1110) _newX10 = false; // final error check here 446 | //if (gParam[NO_P16]){ // optionally stop phantom P-16 commands 447 | // if (X10.House == 'P' && X10.Unit == 16) _newX10 = false; // Fix for phantom P-16 comand 448 | //} 449 | } 450 | #endif 451 | 452 | void x10::attach(void) 453 | { 454 | attachInterrupt(digitalPinToInterrupt(this->zeroCrossingPin),x10_Check_Rcvr_wrapper,CHANGE);// (pin 2) trigger zero cross 455 | } 456 | void x10::detach(void) 457 | { 458 | detachInterrupt(digitalPinToInterrupt(this->zeroCrossingPin)); // must detach interrupt before sending 459 | } 460 | 461 | void x10::debug(void){ 462 | Serial.print("SC-"); 463 | Serial.print(startCode,BIN); 464 | Serial.print(" HOUSE-"); 465 | Serial.print(_houseCode); 466 | Serial.print(" UNIT-"); 467 | Serial.print(_unitCode,DEC); 468 | Serial.print(" CMND"); 469 | Serial.print(_cmndCode,DEC); 470 | if(_cmndCode == ON)Serial.print(" (ON)"); 471 | if(_cmndCode == OFF)Serial.print(" (OFF)"); 472 | Serial.println(""); 473 | } 474 | --------------------------------------------------------------------------------