584 lines
16 KiB
C++
584 lines
16 KiB
C++
/*
|
|
RCSwitch - Arduino libary for remote control outlet switches
|
|
Copyright (c) 2011 Suat Özgür. All right reserved.
|
|
|
|
Contributors:
|
|
- Andre Koehler / info(at)tomate-online(dot)de
|
|
- Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com
|
|
- Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=48
|
|
|
|
Project home: http://code.google.com/p/rc-switch/
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "RCSwitch.h"
|
|
|
|
unsigned long RCSwitch::nReceivedValue = NULL;
|
|
unsigned int RCSwitch::nReceivedBitlength = 0;
|
|
unsigned int RCSwitch::nReceivedDelay = 0;
|
|
unsigned int RCSwitch::nReceivedProtocol = 0;
|
|
unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES];
|
|
int RCSwitch::nReceiveTolerance = 60;
|
|
|
|
RCSwitch::RCSwitch() {
|
|
this->nReceiverInterrupt = -1;
|
|
this->nTransmitterPin = -1;
|
|
RCSwitch::nReceivedValue = NULL;
|
|
this->setPulseLength(350);
|
|
this->setRepeatTransmit(10);
|
|
this->setReceiveTolerance(60);
|
|
this->setProtocol(1);
|
|
}
|
|
|
|
/**
|
|
* Sets the protocol to send.
|
|
*/
|
|
void RCSwitch::setProtocol(int nProtocol) {
|
|
this->nProtocol = nProtocol;
|
|
if (nProtocol == 1){
|
|
this->setPulseLength(350);
|
|
}
|
|
else if (nProtocol == 2) {
|
|
this->setPulseLength(650);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the protocol to send with pulse length in microseconds.
|
|
*/
|
|
void RCSwitch::setProtocol(int nProtocol, int nPulseLength) {
|
|
this->nProtocol = nProtocol;
|
|
if (nProtocol == 1){
|
|
this->setPulseLength(nPulseLength);
|
|
}
|
|
else if (nProtocol == 2) {
|
|
this->setPulseLength(nPulseLength);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets pulse length in microseconds
|
|
*/
|
|
void RCSwitch::setPulseLength(int nPulseLength) {
|
|
this->nPulseLength = nPulseLength;
|
|
}
|
|
|
|
/**
|
|
* Sets Repeat Transmits
|
|
*/
|
|
void RCSwitch::setRepeatTransmit(int nRepeatTransmit) {
|
|
this->nRepeatTransmit = nRepeatTransmit;
|
|
}
|
|
|
|
/**
|
|
* Set Receiving Tolerance
|
|
*/
|
|
void RCSwitch::setReceiveTolerance(int nPercent) {
|
|
RCSwitch::nReceiveTolerance = nPercent;
|
|
}
|
|
|
|
|
|
/**
|
|
* Enable transmissions
|
|
*
|
|
* @param nTransmitterPin Arduino Pin to which the sender is connected to
|
|
*/
|
|
void RCSwitch::enableTransmit(int nTransmitterPin) {
|
|
this->nTransmitterPin = nTransmitterPin;
|
|
pinMode(this->nTransmitterPin, OUTPUT);
|
|
}
|
|
|
|
/**
|
|
* Disable transmissions
|
|
*/
|
|
void RCSwitch::disableTransmit() {
|
|
this->nTransmitterPin = -1;
|
|
}
|
|
|
|
/**
|
|
* Switch a remote switch on (Type C Intertechno)
|
|
*
|
|
* @param sFamily Familycode (a..f)
|
|
* @param nGroup Number of group (1..4)
|
|
* @param nDevice Number of device (1..4)
|
|
*/
|
|
void RCSwitch::switchOn(char sFamily, int nGroup, int nDevice) {
|
|
this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, true) );
|
|
}
|
|
|
|
/**
|
|
* Switch a remote switch off (Type C Intertechno)
|
|
*
|
|
* @param sFamily Familycode (a..f)
|
|
* @param nGroup Number of group (1..4)
|
|
* @param nDevice Number of device (1..4)
|
|
*/
|
|
void RCSwitch::switchOff(char sFamily, int nGroup, int nDevice) {
|
|
this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, false) );
|
|
}
|
|
|
|
/**
|
|
* Switch a remote switch on (Type B with two rotary/sliding switches)
|
|
*
|
|
* @param nAddressCode Number of the switch group (1..4)
|
|
* @param nChannelCode Number of the switch itself (1..4)
|
|
*/
|
|
void RCSwitch::switchOn(int nAddressCode, int nChannelCode) {
|
|
this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, true) );
|
|
}
|
|
|
|
/**
|
|
* Switch a remote switch off (Type B with two rotary/sliding switches)
|
|
*
|
|
* @param nAddressCode Number of the switch group (1..4)
|
|
* @param nChannelCode Number of the switch itself (1..4)
|
|
*/
|
|
void RCSwitch::switchOff(int nAddressCode, int nChannelCode) {
|
|
this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, false) );
|
|
}
|
|
|
|
/**
|
|
* Switch a remote switch on (Type A with 10 pole DIP switches)
|
|
*
|
|
* @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111")
|
|
* @param nChannelCode Number of the switch itself (1..4)
|
|
*/
|
|
void RCSwitch::switchOn(char* sGroup, int nChannel) {
|
|
this->sendTriState( this->getCodeWordA(sGroup, nChannel, true) );
|
|
}
|
|
|
|
/**
|
|
* Switch a remote switch off (Type A with 10 pole DIP switches)
|
|
*
|
|
* @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111")
|
|
* @param nChannelCode Number of the switch itself (1..4)
|
|
*/
|
|
void RCSwitch::switchOff(char* sGroup, int nChannel) {
|
|
this->sendTriState( this->getCodeWordA(sGroup, nChannel, false) );
|
|
}
|
|
|
|
/**
|
|
* Returns a char[13], representing the Code Word to be send.
|
|
* A Code Word consists of 9 address bits, 3 data bits and one sync bit but in our case only the first 8 address bits and the last 2 data bits were used.
|
|
* A Code Bit can have 4 different states: "F" (floating), "0" (low), "1" (high), "S" (synchronous bit)
|
|
*
|
|
* +-------------------------------+--------------------------------+-----------------------------------------+-----------------------------------------+----------------------+------------+
|
|
* | 4 bits address (switch group) | 4 bits address (switch number) | 1 bit address (not used, so never mind) | 1 bit address (not used, so never mind) | 2 data bits (on|off) | 1 sync bit |
|
|
* | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | F | F | on=FF off=F0 | S |
|
|
* +-------------------------------+--------------------------------+-----------------------------------------+-----------------------------------------+----------------------+------------+
|
|
*
|
|
* @param nAddressCode Number of the switch group (1..4)
|
|
* @param nChannelCode Number of the switch itself (1..4)
|
|
* @param bStatus Wether to switch on (true) or off (false)
|
|
*
|
|
* @return char[13]
|
|
*/
|
|
char* RCSwitch::getCodeWordB(int nAddressCode, int nChannelCode, boolean bStatus) {
|
|
int nReturnPos = 0;
|
|
static char sReturn[13];
|
|
|
|
char* code[5] = { "FFFF", "0FFF", "F0FF", "FF0F", "FFF0" };
|
|
if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) {
|
|
return '\0';
|
|
}
|
|
for (int i = 0; i<4; i++) {
|
|
sReturn[nReturnPos++] = code[nAddressCode][i];
|
|
}
|
|
|
|
for (int i = 0; i<4; i++) {
|
|
sReturn[nReturnPos++] = code[nChannelCode][i];
|
|
}
|
|
|
|
sReturn[nReturnPos++] = 'F';
|
|
sReturn[nReturnPos++] = 'F';
|
|
sReturn[nReturnPos++] = 'F';
|
|
|
|
if (bStatus) {
|
|
sReturn[nReturnPos++] = 'F';
|
|
} else {
|
|
sReturn[nReturnPos++] = '0';
|
|
}
|
|
|
|
sReturn[nReturnPos] = '\0';
|
|
|
|
return sReturn;
|
|
}
|
|
|
|
|
|
/**
|
|
* Like getCodeWord (Type A)
|
|
*/
|
|
char* RCSwitch::getCodeWordA(char* sGroup, int nChannelCode, boolean bStatus) {
|
|
int nReturnPos = 0;
|
|
static char sReturn[13];
|
|
|
|
char* code[6] = { "FFFFF", "0FFFF", "F0FFF", "FF0FF", "FFF0F", "FFFF0" };
|
|
|
|
if (nChannelCode < 1 || nChannelCode > 5) {
|
|
return '\0';
|
|
}
|
|
|
|
for (int i = 0; i<5; i++) {
|
|
if (sGroup[i] == '0') {
|
|
sReturn[nReturnPos++] = 'F';
|
|
} else if (sGroup[i] == '1') {
|
|
sReturn[nReturnPos++] = '0';
|
|
} else {
|
|
return '\0';
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i<5; i++) {
|
|
sReturn[nReturnPos++] = code[ nChannelCode ][i];
|
|
}
|
|
|
|
if (bStatus) {
|
|
sReturn[nReturnPos++] = '0';
|
|
sReturn[nReturnPos++] = 'F';
|
|
} else {
|
|
sReturn[nReturnPos++] = 'F';
|
|
sReturn[nReturnPos++] = '0';
|
|
}
|
|
sReturn[nReturnPos] = '\0';
|
|
|
|
return sReturn;
|
|
}
|
|
|
|
/**
|
|
* Like getCodeWord (Type C = Intertechno)
|
|
*/
|
|
char* RCSwitch::getCodeWordC(char sFamily, int nGroup, int nDevice, boolean bStatus) {
|
|
static char sReturn[13];
|
|
int nReturnPos = 0;
|
|
|
|
if ( (byte)sFamily < 97 || (byte)sFamily > 112 || nGroup < 1 || nGroup > 4 || nDevice < 1 || nDevice > 4) {
|
|
return '\0';
|
|
}
|
|
|
|
char* sDeviceGroupCode = dec2binWzerofill( (nDevice-1) + (nGroup-1)*4, 4 );
|
|
char familycode[16][5] = { "0000", "F000", "0F00", "FF00", "00F0", "F0F0", "0FF0", "FFF0", "000F", "F00F", "0F0F", "FF0F", "00FF", "F0FF", "0FFF", "FFFF" };
|
|
for (int i = 0; i<4; i++) {
|
|
sReturn[nReturnPos++] = familycode[ (int)sFamily - 97 ][i];
|
|
}
|
|
for (int i = 0; i<4; i++) {
|
|
sReturn[nReturnPos++] = (sDeviceGroupCode[3-i] == '1' ? 'F' : '0');
|
|
}
|
|
sReturn[nReturnPos++] = '0';
|
|
sReturn[nReturnPos++] = 'F';
|
|
sReturn[nReturnPos++] = 'F';
|
|
if (bStatus) {
|
|
sReturn[nReturnPos++] = 'F';
|
|
} else {
|
|
sReturn[nReturnPos++] = '0';
|
|
}
|
|
sReturn[nReturnPos] = '\0';
|
|
return sReturn;
|
|
}
|
|
|
|
/**
|
|
* Sends a Code Word
|
|
* @param sCodeWord /^[10FS]*$/ -> see getCodeWord
|
|
*/
|
|
void RCSwitch::sendTriState(char* sCodeWord) {
|
|
for (int nRepeat=0; nRepeat<nRepeatTransmit; nRepeat++) {
|
|
int i = 0;
|
|
while (sCodeWord[i] != '\0') {
|
|
switch(sCodeWord[i]) {
|
|
case '0':
|
|
this->sendT0();
|
|
break;
|
|
case 'F':
|
|
this->sendTF();
|
|
break;
|
|
case '1':
|
|
this->sendT1();
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
this->sendSync();
|
|
}
|
|
}
|
|
|
|
void RCSwitch::send(unsigned long Code, unsigned int length) {
|
|
this->send( this->dec2binWzerofill(Code, length) );
|
|
}
|
|
|
|
void RCSwitch::send(char* sCodeWord) {
|
|
for (int nRepeat=0; nRepeat<nRepeatTransmit; nRepeat++) {
|
|
int i = 0;
|
|
while (sCodeWord[i] != '\0') {
|
|
switch(sCodeWord[i]) {
|
|
case '0':
|
|
this->send0();
|
|
break;
|
|
case '1':
|
|
this->send1();
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
this->sendSync();
|
|
}
|
|
}
|
|
|
|
void RCSwitch::transmit(int nHighPulses, int nLowPulses) {
|
|
boolean disabled_Receive = false;
|
|
int nReceiverInterrupt_backup = nReceiverInterrupt;
|
|
if (this->nTransmitterPin != -1) {
|
|
if (this->nReceiverInterrupt != -1) {
|
|
this->disableReceive();
|
|
disabled_Receive = true;
|
|
}
|
|
digitalWrite(this->nTransmitterPin, HIGH);
|
|
delayMicroseconds( this->nPulseLength * nHighPulses);
|
|
digitalWrite(this->nTransmitterPin, LOW);
|
|
delayMicroseconds( this->nPulseLength * nLowPulses);
|
|
if(disabled_Receive){
|
|
this->enableReceive(nReceiverInterrupt_backup);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Sends a "0" Bit
|
|
* _
|
|
* Waveform Protocol 1: | |___
|
|
* _
|
|
* Waveform Protocol 2: | |__
|
|
*/
|
|
void RCSwitch::send0() {
|
|
if (this->nProtocol == 1){
|
|
this->transmit(1,3);
|
|
}
|
|
else if (this->nProtocol == 2) {
|
|
this->transmit(1,2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends a "1" Bit
|
|
* ___
|
|
* Waveform Protocol 1: | |_
|
|
* __
|
|
* Waveform Protocol 2: | |_
|
|
*/
|
|
void RCSwitch::send1() {
|
|
if (this->nProtocol == 1){
|
|
this->transmit(3,1);
|
|
}
|
|
else if (this->nProtocol == 2) {
|
|
this->transmit(2,1);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Sends a Tri-State "0" Bit
|
|
* _ _
|
|
* Waveform: | |___| |___
|
|
*/
|
|
void RCSwitch::sendT0() {
|
|
this->transmit(1,3);
|
|
this->transmit(1,3);
|
|
}
|
|
|
|
/**
|
|
* Sends a Tri-State "1" Bit
|
|
* ___ ___
|
|
* Waveform: | |_| |_
|
|
*/
|
|
void RCSwitch::sendT1() {
|
|
this->transmit(3,1);
|
|
this->transmit(3,1);
|
|
}
|
|
|
|
/**
|
|
* Sends a Tri-State "F" Bit
|
|
* _ ___
|
|
* Waveform: | |___| |_
|
|
*/
|
|
void RCSwitch::sendTF() {
|
|
this->transmit(1,3);
|
|
this->transmit(3,1);
|
|
}
|
|
|
|
/**
|
|
* Sends a "Sync" Bit
|
|
* _
|
|
* Waveform Protocol 1: | |_______________________________
|
|
* _
|
|
* Waveform Protocol 2: | |__________
|
|
*/
|
|
void RCSwitch::sendSync() {
|
|
|
|
if (this->nProtocol == 1){
|
|
this->transmit(1,31);
|
|
}
|
|
else if (this->nProtocol == 2) {
|
|
this->transmit(1,10);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable receiving data
|
|
*/
|
|
void RCSwitch::enableReceive(int interrupt) {
|
|
this->nReceiverInterrupt = interrupt;
|
|
this->enableReceive();
|
|
}
|
|
|
|
void RCSwitch::enableReceive() {
|
|
if (this->nReceiverInterrupt != -1) {
|
|
RCSwitch::nReceivedValue = NULL;
|
|
RCSwitch::nReceivedBitlength = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disable receiving data
|
|
*/
|
|
void RCSwitch::disableReceive() {
|
|
this->nReceiverInterrupt = -1;
|
|
}
|
|
|
|
bool RCSwitch::available() {
|
|
return RCSwitch::nReceivedValue != NULL;
|
|
}
|
|
|
|
void RCSwitch::resetAvailable() {
|
|
RCSwitch::nReceivedValue = NULL;
|
|
}
|
|
|
|
unsigned long RCSwitch::getReceivedValue() {
|
|
return RCSwitch::nReceivedValue;
|
|
}
|
|
|
|
unsigned int RCSwitch::getReceivedBitlength() {
|
|
return RCSwitch::nReceivedBitlength;
|
|
}
|
|
|
|
unsigned int RCSwitch::getReceivedDelay() {
|
|
return RCSwitch::nReceivedDelay;
|
|
}
|
|
|
|
unsigned int RCSwitch::getReceivedProtocol() {
|
|
return RCSwitch::nReceivedProtocol;
|
|
}
|
|
|
|
unsigned int* RCSwitch::getReceivedRawdata() {
|
|
return RCSwitch::timings;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
bool RCSwitch::receiveProtocol1(unsigned int changeCount){
|
|
|
|
unsigned long code = 0;
|
|
unsigned long delay = RCSwitch::timings[0] / 31;
|
|
unsigned long delayTolerance = delay * RCSwitch::nReceiveTolerance * 0.01;
|
|
|
|
for (int i = 1; i<changeCount ; i=i+2) {
|
|
|
|
if (RCSwitch::timings[i] > delay-delayTolerance && RCSwitch::timings[i] < delay+delayTolerance && RCSwitch::timings[i+1] > delay*3-delayTolerance && RCSwitch::timings[i+1] < delay*3+delayTolerance) {
|
|
code = code << 1;
|
|
} else if (RCSwitch::timings[i] > delay*3-delayTolerance && RCSwitch::timings[i] < delay*3+delayTolerance && RCSwitch::timings[i+1] > delay-delayTolerance && RCSwitch::timings[i+1] < delay+delayTolerance) {
|
|
code+=1;
|
|
code = code << 1;
|
|
} else {
|
|
// Failed
|
|
i = changeCount;
|
|
code = 0;
|
|
}
|
|
}
|
|
code = code >> 1;
|
|
if (changeCount > 6) { // ignore < 4bit values as there are no devices sending 4bit values => noise
|
|
RCSwitch::nReceivedValue = code;
|
|
RCSwitch::nReceivedBitlength = changeCount / 2;
|
|
RCSwitch::nReceivedDelay = delay;
|
|
RCSwitch::nReceivedProtocol = 1;
|
|
}
|
|
|
|
if (code == 0){
|
|
return false;
|
|
}else if (code != 0){
|
|
return true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
bool RCSwitch::receiveProtocol2(unsigned int changeCount){
|
|
|
|
unsigned long code = 0;
|
|
unsigned long delay = RCSwitch::timings[0] / 10;
|
|
unsigned long delayTolerance = delay * RCSwitch::nReceiveTolerance * 0.01;
|
|
|
|
for (int i = 1; i<changeCount ; i=i+2) {
|
|
|
|
if (RCSwitch::timings[i] > delay-delayTolerance && RCSwitch::timings[i] < delay+delayTolerance && RCSwitch::timings[i+1] > delay*2-delayTolerance && RCSwitch::timings[i+1] < delay*2+delayTolerance) {
|
|
code = code << 1;
|
|
} else if (RCSwitch::timings[i] > delay*2-delayTolerance && RCSwitch::timings[i] < delay*2+delayTolerance && RCSwitch::timings[i+1] > delay-delayTolerance && RCSwitch::timings[i+1] < delay+delayTolerance) {
|
|
code+=1;
|
|
code = code << 1;
|
|
} else {
|
|
// Failed
|
|
i = changeCount;
|
|
code = 0;
|
|
}
|
|
}
|
|
code = code >> 1;
|
|
if (changeCount > 6) { // ignore < 4bit values as there are no devices sending 4bit values => noise
|
|
RCSwitch::nReceivedValue = code;
|
|
RCSwitch::nReceivedBitlength = changeCount / 2;
|
|
RCSwitch::nReceivedDelay = delay;
|
|
RCSwitch::nReceivedProtocol = 2;
|
|
}
|
|
|
|
if (code == 0){
|
|
return false;
|
|
}else if (code != 0){
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Turns a decimal value to its binary representation
|
|
*/
|
|
char* RCSwitch::dec2binWzerofill(unsigned long Dec, unsigned int bitLength){
|
|
static char bin[64];
|
|
unsigned int i=0;
|
|
|
|
while (Dec > 0) {
|
|
bin[32+i++] = ((Dec & 1) > 0) ? '1' : '0';
|
|
Dec = Dec >> 1;
|
|
}
|
|
|
|
for (unsigned int j = 0; j< bitLength; j++) {
|
|
if (j >= bitLength - i) {
|
|
bin[j] = bin[ 31 + i - (j - (bitLength - i)) ];
|
|
}else {
|
|
bin[j] = '0';
|
|
}
|
|
}
|
|
bin[bitLength] = '\0';
|
|
|
|
return bin;
|
|
}
|
|
|