mirror of
https://github.com/pyrou/X10RF-Arduino.git
synced 2026-07-28 04:06:11 +00:00
329 lines
9.2 KiB
C++
329 lines
9.2 KiB
C++
/*
|
|
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 <stdlib.h>
|
|
|
|
#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 <counter value> 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); }
|