diff --git a/x10rf.cpp b/x10rf.cpp index 6c47c8c..a83d5b6 100644 --- a/x10rf.cpp +++ b/x10rf.cpp @@ -1,255 +1,328 @@ /* -x10rf.cpp +x10rf.cpp Library for sending x10 messages by RF. Created by Pieter Paul Baron (embedded [at] ppbaron.nl), November 2013. Released into the public domain. -Library to send x10 messages via a cheap 433Mhz OOK device. No X10 Firecracker (CMA17A) necessary. -Decoding messages is not implemented. -This library can emulate x10 switches and security devices and also RFXMeter and RFXSensor devices manufactured by RFXCom. (www.rfxcom.com) +Library to send x10 messages via a cheap 433Mhz OOK device. No X10 Firecracker +(CMA17A) necessary. Decoding messages is not implemented. This library can +emulate x10 switches and security devices and also RFXMeter and RFXSensor +devices manufactured by RFXCom. (www.rfxcom.com) -Tested on a TI Stellarpad (LM4F120H5QR) and Energia 0101E0010. This should also work on Arduino (small modifications) or other TI Launchpad devices. +Tested on a TI Stellaris Launchpad (LM4F120H5QR) and Energia 0101E0010. This +should also work on Arduino (small modifications) or other TI Launchpad devices. */ #include #if defined(ARDUINO) && ARDUINO >= 100 - #include "Arduino.h" +#include "Arduino.h" #elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific - #include "Energia.h" +#include "Energia.h" #else - #include "WProgram.h" +#include "WProgram.h" #endif #include "x10rf.h" -#define X10_RF_SB_LONG 8960 // Start burts (leader) = 9ms -#define X10_RF_SB_SHORT 4500 //Start silecence (leader) = 4,5 ms -#define X10_RF_BIT_LONG 1120 // Bit 1 pulse length -#define X10_RF_BIT_SHORT 560 // Bit 1 pulse length -#define X10_RF_GAP 40000 // Length between commands +#define X10_RF_SB_LONG 8960 // Start burts (leader) = 9ms +#define X10_RF_SB_SHORT 4500 // Start silecence (leader) = 4,5 ms +#define X10_RF_BIT_LONG 1120 // Bit 1 pulse length +#define X10_RF_BIT_SHORT 560 // Bit 1 pulse length +#define X10_RF_GAP 40000 // Length between commands -void x10rf::begin() -{ - pinMode(_tx_pin, OUTPUT); - if (_led_pin > 0) pinMode(_led_pin, OUTPUT); -} - -x10rf::x10rf(uint8_t tx_pin, uint8_t led_pin, uint8_t rf_repeats) -{ - _tx_pin = tx_pin; - _led_pin = led_pin; - _rf_repeats = rf_repeats; +void x10rf::begin() { + pinMode(_tx_pin, OUTPUT); + if (_led_pin > 0) + pinMode(_led_pin, OUTPUT); } -void x10rf::RFXmeter(uint8_t rfxm_address, uint8_t rfxm_packet_type, long rfxm_value){ - uint8_t x10buff[5]; // Set message buffer - x10buff[0] = rfxm_address; - x10buff[1] = (~x10buff[0] & 0xF0) + (x10buff[0] & 0xF); // Calculate byte1 (byte 1 complement upper nibble of byte0) - if (rfxm_value > 0xFFFFFF) rfxm_value = 0; // We only have 3 byte for data. Is overflowed set to 0 - // Packet type goed into MSB nibble of byte 5. Max 15 (B1111) allowed - // Use switch case to filter invalid data types - switch(rfxm_packet_type) { - case 0x00: //Normal. Put counter values in byte 4,2 and 3 - x10buff[4] = (uint8_t) ((rfxm_value >> 16) & 0xff); - x10buff[2] = (uint8_t) ((rfxm_value >> 8) & 0xff); - x10buff[3] = (uint8_t) (rfxm_value & 0xff); - break; - case 0x01: // New interval time set. Byte 2 should be filled with interval - switch(rfxm_value) { - case 0x01: break; // 30sec - case 0x02: break; // 01min - case 0x04: break; // 06min (RFXpower = 05min) - case 0x08: break; // 12min (RFXpower = 10min) - case 0x10: break; // 15min - case 0x20: break; // 30min - case 0x40: break; // 45min - case 0x80: break; // 60min - default: - rfxm_value = 0x01; // Set to 30 sec if no valid option is found - } - x10buff[2] = rfxm_value; - break; - case 0x02: // calibrate value in in µsec. - x10buff[4] = (uint8_t) ((rfxm_value >> 16) & 0xff); - x10buff[2] = (uint8_t) ((rfxm_value >> 8) & 0xff); - x10buff[3] = (uint8_t) (rfxm_value & 0xff); - break; - case 0x03: break;// new address set - case 0x04: break; // counter value reset to zero - case 0x0B: // counter value set - x10buff[4] = (uint8_t) ((rfxm_value >> 16) & 0xff); - x10buff[2] = (uint8_t) ((rfxm_value >> 8) & 0xff); - x10buff[3] = (uint8_t) (rfxm_value & 0xff); - break; - case 0x0C: break; // set interval mode within 5 seconds - case 0x0D: break; // calibration mode within 5 seconds - case 0x0E: break; // set address mode within 5 seconds - case 0x0F: // identification packet (byte 2 = address, byte 3 = interval) - switch(rfxm_value) { - case 0x01: break; //30sec - case 0x02: break; //01min - case 0x04: break; //06min (RFXpower = 05min) - case 0x08: break; //12min (RFXpower = 10min) - case 0x10: break; // 15min1 - case 0x20: break; // 30min - case 0x40: break; // 45min - case 0x80: break; // 60min - default: - rfxm_value = 0x01; // Set to 30 sec if no valid option is found - } - x10buff[2] = rfxm_address; - x10buff[3] = rfxm_value; - break; - default: //Unknown packet type. Set packet type to zero and set counter to rfxm_value - rfxm_packet_type = 0; - x10buff[4] = (uint8_t) ((rfxm_value >> 16) & 0xff); - x10buff[2] = (uint8_t) ((rfxm_value >> 8) & 0xff); - x10buff[3] = (uint8_t) (rfxm_value & 0xff); - } - x10buff[5] = (rfxm_packet_type << 4); // Packet type goes into byte 5's upper nibble. - // Calculate parity which - uint8_t parity = ~(((x10buff[0] & 0XF0) >> 4) + (x10buff[0] & 0XF) + ((x10buff[1] & 0XF0) >> 4) + (x10buff[1] & 0XF) + ((x10buff[2] & 0XF0) >> 4) + (x10buff[2] & 0XF) + ((x10buff[3] & 0XF0) >> 4) + (x10buff[3] & 0XF) + ((x10buff[4] & 0XF0) >> 4) + (x10buff[4] & 0XF) + ((x10buff[5] & 0XF0) >> 4)); - x10buff[5] = (x10buff[5] & 0xf0) + (parity & 0XF); - SendCommand(x10buff, sizeof(x10buff)); // Send byte to be broadcasted +x10rf::x10rf(uint8_t tx_pin, uint8_t led_pin, uint8_t rf_repeats) { + _tx_pin = tx_pin; + _led_pin = led_pin; + _rf_repeats = rf_repeats; } -void x10rf::RFXsensor(uint8_t rfxs_address,uint8_t rfxs_type, char rfxs_packet_type, uint8_t rfxs_value){ - uint8_t x10buff[3]; // Set message buffer 4 bytes - x10buff[0] = (rfxs_address << 2); - switch (rfxs_type) { - case 't': break; // Temperature (default) - case 'a': // A/D - x10buff[0] = x10buff[0] + B01; - break; - case 'm': // message - x10buff[0] = x10buff[0] + B11; - break; - case 'v': // voltage - x10buff[0] = x10buff[0] + B10; - break; - } - x10buff[1] = (~x10buff[0] & 0xF0) + (x10buff[0] & 0xF); // Calculate byte1 (byte 1 complement MSB nibble of byte0) - x10buff[2] = rfxs_value; - switch(rfxs_packet_type) { - case 't': //temperature sensor (MSB = 0.5 degrees bit off) - x10buff[3] = 0x00; - break; - case 'T': //emperature sensor (MSB = 0.5 degrees bit on) - x10buff[3] = 0x80; - break; - case 'h': //RFU (humidity sensor) - x10buff[3] = 0x20; - break; - case 'p': //RFU (pressure sensor) - x10buff[3] = 0x40; - break; - default: - x10buff[3] = 0x00; - } - uint8_t parity = ~(((x10buff[0] & 0XF0) >> 4) + (x10buff[0] & 0XF) + ((x10buff[1] & 0XF0) >> 4) + (x10buff[1] & 0XF) + ((x10buff[2] & 0XF0) >> 4) + (x10buff[2] & 0XF) + ((x10buff[3] & 0XF0) >> 4)); - x10buff[3] = (x10buff[3] & 0xf0) + (parity & 0XF); - SendCommand(x10buff, sizeof(x10buff)); +void x10rf::RFXmeter(uint8_t rfxm_address, uint8_t rfxm_packet_type, + long rfxm_value) { + + static const uint8_t BUFFER_SIZE = 6; + uint8_t x10buff[BUFFER_SIZE]; + x10buff[0] = rfxm_address; + x10buff[1] = + (~x10buff[0] & 0xF0) + + (x10buff[0] & + 0xF); // Calculate byte1 (byte 1 complement upper nibble of byte0) + if (rfxm_value > 0xFFFFFF) + rfxm_value = 0; // We only have 3 byte for data. Is overflowed set to 0 + // Packet type goed into MSB nibble of byte 5. Max 15 (B1111) allowed + // Use switch case to filter invalid data types + switch (rfxm_packet_type) { + case 0x00: // Normal. Put counter values in byte 4,2 and 3 + x10buff[4] = (uint8_t)((rfxm_value >> 16) & 0xff); + x10buff[2] = (uint8_t)((rfxm_value >> 8) & 0xff); + x10buff[3] = (uint8_t)(rfxm_value & 0xff); + break; + case 0x01: // New interval time set. Byte 2 should be filled with interval + switch (rfxm_value) { + case 0x01: + break; // 30sec + case 0x02: + break; // 01min + case 0x04: + break; // 06min (RFXpower = 05min) + case 0x08: + break; // 12min (RFXpower = 10min) + case 0x10: + break; // 15min + case 0x20: + break; // 30min + case 0x40: + break; // 45min + case 0x80: + break; // 60min + default: + rfxm_value = 0x01; // Set to 30 sec if no valid option is found + } + x10buff[2] = rfxm_value; + break; + case 0x02: // calibrate value in in µsec. + x10buff[4] = (uint8_t)((rfxm_value >> 16) & 0xff); + x10buff[2] = (uint8_t)((rfxm_value >> 8) & 0xff); + x10buff[3] = (uint8_t)(rfxm_value & 0xff); + break; + case 0x03: + break; // new address set + case 0x04: + break; // counter value reset to zero + case 0x0B: // counter value set + x10buff[4] = (uint8_t)((rfxm_value >> 16) & 0xff); + x10buff[2] = (uint8_t)((rfxm_value >> 8) & 0xff); + x10buff[3] = (uint8_t)(rfxm_value & 0xff); + break; + case 0x0C: + break; // set interval mode within 5 seconds + case 0x0D: + break; // calibration mode within 5 seconds + case 0x0E: + break; // set address mode within 5 seconds + case 0x0F: // identification packet (byte 2 = address, byte 3 = interval) + switch (rfxm_value) { + case 0x01: + break; // 30sec + case 0x02: + break; // 01min + case 0x04: + break; // 06min (RFXpower = 05min) + case 0x08: + break; // 12min (RFXpower = 10min) + case 0x10: + break; // 15min1 + case 0x20: + break; // 30min + case 0x40: + break; // 45min + case 0x80: + break; // 60min + default: + rfxm_value = 0x01; // Set to 30 sec if no valid option is found + } + x10buff[2] = rfxm_address; + x10buff[3] = rfxm_value; + break; + default: // Unknown packet type. Set packet type to zero and set counter to + // rfxm_value + rfxm_packet_type = 0; + x10buff[4] = (uint8_t)((rfxm_value >> 16) & 0xff); + x10buff[2] = (uint8_t)((rfxm_value >> 8) & 0xff); + x10buff[3] = (uint8_t)(rfxm_value & 0xff); + } + x10buff[5] = + (rfxm_packet_type << 4); // Packet type goes into byte 5's upper nibble. + // Calculate parity which + uint8_t parity = ~(((x10buff[0] & 0XF0) >> 4) + (x10buff[0] & 0XF) + + ((x10buff[1] & 0XF0) >> 4) + (x10buff[1] & 0XF) + + ((x10buff[2] & 0XF0) >> 4) + (x10buff[2] & 0XF) + + ((x10buff[3] & 0XF0) >> 4) + (x10buff[3] & 0XF) + + ((x10buff[4] & 0XF0) >> 4) + (x10buff[4] & 0XF) + + ((x10buff[5] & 0XF0) >> 4)); + x10buff[5] = (x10buff[5] & 0xf0) + (parity & 0XF); + SendCommand(x10buff, BUFFER_SIZE); } -void x10rf::x10Switch(char house_code, uint8_t unit_code, uint8_t command){ - uint8_t x10buff[3]; // Set message buffer 4 bytes - switch(tolower(house_code)) { - case 'a': x10buff[0] = B0110; break; - case 'b': x10buff[0] = B0111; break; - case 'c': x10buff[0] = B0100; break; - case 'd': x10buff[0] = B0101; break; - case 'e': x10buff[0] = B1000; break; - case 'f': x10buff[0] = B1001; break; - case 'g': x10buff[0] = B1010; break; - case 'h': x10buff[0] = B1011; break; - case 'i': x10buff[0] = B1110; break; - case 'j': x10buff[0] = B1111; break; - case 'k': x10buff[0] = B1100; break; - case 'l': x10buff[0] = B1101; break; - case 'm': x10buff[0] = B0000; break; - case 'n': x10buff[0] = B0001; break; - case 'o': x10buff[0] = B0010; break; - case 'p': x10buff[0] = B0011; break; - default: x10buff[0] = 0; break; - } - x10buff[0] = x10buff[0] << 4; // House code goes into the upper nibble +void x10rf::RFXsensor(uint8_t rfxs_address, uint8_t rfxs_type, + char rfxs_packet_type, uint8_t rfxs_value) { - switch(command) { - case ON: - case OFF: - case BRIGHT: - case DIM: - x10buff[2] = command; break; - } - // Set unit number - unit_code = unit_code - 1; - bitWrite(x10buff[2],6,bitRead(unit_code,2)); - bitWrite(x10buff[2],3,bitRead(unit_code,1)); - bitWrite(x10buff[2],4,bitRead(unit_code,0)); - bitWrite(x10buff[0],2,bitRead(unit_code,3)); - // Set parity - x10buff[1] = ~x10buff[0]; - x10buff[3] = ~x10buff[2]; - SendCommand(x10buff, sizeof(x10buff)); + static const uint8_t BUFFER_SIZE = 4; + + uint8_t x10buff[BUFFER_SIZE; 4 bytes + x10buff[0] = (rfxs_address << 2); + switch (rfxs_type) { + case 't': + break; // Temperature (default) + case 'a': // A/D + x10buff[0] = x10buff[0] + B01; + break; + case 'm': // message + x10buff[0] = x10buff[0] + B11; + break; + case 'v': // voltage + x10buff[0] = x10buff[0] + B10; + break; + } + x10buff[1] = (~x10buff[0] & 0xF0) + + (x10buff[0] & + 0xF); // Calculate byte1 (byte 1 complement MSB nibble of byte0) + x10buff[2] = rfxs_value; + switch (rfxs_packet_type) { + case 't': // temperature sensor (MSB = 0.5 degrees bit off) + x10buff[3] = 0x00; + break; + case 'T': // emperature sensor (MSB = 0.5 degrees bit on) + x10buff[3] = 0x80; + break; + case 'h': // RFU (humidity sensor) + x10buff[3] = 0x20; + break; + case 'p': // RFU (pressure sensor) + x10buff[3] = 0x40; + break; + default: + x10buff[3] = 0x00; + } + uint8_t parity = ~(((x10buff[0] & 0XF0) >> 4) + (x10buff[0] & 0XF) + + ((x10buff[1] & 0XF0) >> 4) + (x10buff[1] & 0XF) + + ((x10buff[2] & 0XF0) >> 4) + (x10buff[2] & 0XF) + + ((x10buff[3] & 0XF0) >> 4)); + x10buff[3] = (x10buff[3] & 0xf0) + (parity & 0XF); + SendCommand(x10buff, BUFFER_SIZE); } -void x10rf::x10Security(uint8_t address, uint8_t command){ - uint8_t x10buff[3]; // Set message buffer 4 bytes - x10buff[0] = address; - x10buff[1] = (~x10buff[0] & 0xF) + (x10buff[0] & 0xF0); // Calculate byte1 (byte 1 complement - x10buff[2] = command; - x10buff[3] = ~x10buff[2]; - // x10buff[4] = code; // Couldn't get 48 bit security working. - // if((x10buff[4] % 2) == 0) { x10buff[5] = 0;} //Calc even parity - // else { x10buff[5] = 0x80;} - SendCommand(x10buff, sizeof(x10buff)); +void x10rf::x10Switch(char house_code, uint8_t unit_code, uint8_t command) { + static const uint8_t BUFFER_SIZE = 4; + uint8_t x10buff[BUFFER_SIZE]; + 4 bytes switch (tolower(house_code)) { + case 'a': + x10buff[0] = B0110; + break; + case 'b': + x10buff[0] = B0111; + break; + case 'c': + x10buff[0] = B0100; + break; + case 'd': + x10buff[0] = B0101; + break; + case 'e': + x10buff[0] = B1000; + break; + case 'f': + x10buff[0] = B1001; + break; + case 'g': + x10buff[0] = B1010; + break; + case 'h': + x10buff[0] = B1011; + break; + case 'i': + x10buff[0] = B1110; + break; + case 'j': + x10buff[0] = B1111; + break; + case 'k': + x10buff[0] = B1100; + break; + case 'l': + x10buff[0] = B1101; + break; + case 'm': + x10buff[0] = B0000; + break; + case 'n': + x10buff[0] = B0001; + break; + case 'o': + x10buff[0] = B0010; + break; + case 'p': + x10buff[0] = B0011; + break; + default: + x10buff[0] = 0; + break; + } + x10buff[0] = x10buff[0] << 4; // House code goes into the upper nibble + switch (command) { + case ON: + case OFF: + case BRIGHT: + case DIM: + x10buff[2] = command; + break; + } + // Set unit number + unit_code = unit_code - 1; + bitWrite(x10buff[2], 6, bitRead(unit_code, 2)); + bitWrite(x10buff[2], 3, bitRead(unit_code, 1)); + bitWrite(x10buff[2], 4, bitRead(unit_code, 0)); + bitWrite(x10buff[0], 2, bitRead(unit_code, 3)); + // Set parity + x10buff[1] = ~x10buff[0]; + x10buff[3] = ~x10buff[2]; + SendCommand(x10buff, BUFFER_SIZE); } -void x10rf::SendCommand(uint8_t *data, uint8_t size){ - if (_led_pin > 0) digitalWrite(_led_pin, HIGH); - for (int i = 0; i < _rf_repeats; i++){ - SEND_HIGH();delayMicroseconds(X10_RF_SB_LONG); - SEND_LOW();delayMicroseconds(X10_RF_SB_SHORT); - for(int i=0; i <= size; i++) { - SendX10RfByte(data[i]); - } - SendX10RfBit(1); - delayMicroseconds(X10_RF_GAP); - } - if (_led_pin > 0) digitalWrite(_led_pin, LOW); +void x10rf::x10Security(uint8_t address, uint8_t command) { + static const uint8_t BUFFER_SIZE = 4; + uint8_t x10buff[BUFFER_SIZE]; + 4 bytes x10buff[0] = address; + x10buff[1] = (~x10buff[0] & 0xF) + + (x10buff[0] & 0xF0); // Calculate byte1 (byte 1 complement + x10buff[2] = command; + x10buff[3] = ~x10buff[2]; + // x10buff[4] = code; // Couldn't get 48 bit security working. + // if((x10buff[4] % 2) == 0) { x10buff[5] = 0;} //Calc even parity + // else { x10buff[5] = 0x80;} + SendCommand(x10buff, BUFFER_SIZE); } -void x10rf::SendX10RfByte(uint8_t data){ - //Serial.println("\n"); - for (int i=7; i >= 0 ; i--){ // send bits from byte - SendX10RfBit((bitRead(data,i)==1)); - //Serial.print(bitRead(data,i)); - } +void x10rf::SendCommand(uint8_t *data, uint8_t size) { + if (_led_pin > 0) + digitalWrite(_led_pin, HIGH); + for (int i = 0; i < _rf_repeats; i++) { + SEND_HIGH(); + delayMicroseconds(X10_RF_SB_LONG); + SEND_LOW(); + delayMicroseconds(X10_RF_SB_SHORT); + for (int j = 0; j < size; j++) { + SendX10RfByte(data[j]); + } + SendX10RfBit(1); + delayMicroseconds(X10_RF_GAP); + } + if (_led_pin > 0) + digitalWrite(_led_pin, LOW); } -void x10rf::SendX10RfBit(unsigned int databit){ - SEND_HIGH();delayMicroseconds(X10_RF_BIT_SHORT); - SEND_LOW();delayMicroseconds(X10_RF_BIT_SHORT); - if (databit) delayMicroseconds(X10_RF_BIT_LONG); +void x10rf::SendX10RfByte(uint8_t data) { + for (int i = 7; i >= 0; i--) { + SendX10RfBit((bitRead(data, i) == 1)); + } } -void x10rf::SEND_HIGH() { - digitalWrite(_tx_pin, HIGH); +void x10rf::SendX10RfBit(unsigned int databit) { + SEND_HIGH(); + delayMicroseconds(X10_RF_BIT_SHORT); + SEND_LOW(); + delayMicroseconds(X10_RF_BIT_SHORT); + if (databit) + delayMicroseconds(X10_RF_BIT_LONG); } -void x10rf::SEND_LOW(){ - digitalWrite(_tx_pin, LOW); -} - - - - - - - - - - +void x10rf::SEND_HIGH() { digitalWrite(_tx_pin, HIGH); } +void x10rf::SEND_LOW() { digitalWrite(_tx_pin, LOW); }