/* 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) 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" #elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific #include "Energia.h" #else #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 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::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::RFXsensor(uint8_t rfxs_address, uint8_t rfxs_type, char rfxs_packet_type, uint8_t rfxs_value) { 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::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::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::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::SendX10RfByte(uint8_t data) { for (int i = 7; i >= 0; i--) { SendX10RfBit((bitRead(data, i) == 1)); } } 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_HIGH() { digitalWrite(_tx_pin, HIGH); } void x10rf::SEND_LOW() { digitalWrite(_tx_pin, LOW); }