-
Robert ter Vehn authoredRobert ter Vehn authored
RCSwitch.cpp 20.78 KiB
/*
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=46
- Dominik Fischer / dom_fischer(at)web(dot)de
- Frank Oltmanns / <first name>.<last name>(at)gmail(dot)com
- Andreas Steinel / A.<lastname>(at)gmail(dot)com
- Max Horn / max(at)quendi(dot)de
- Robert ter Vehn / <first name>.<last name>(at)gmail(dot)com
Project home: https://github.com/sui77/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"
static const RCSwitch::Protocol PROGMEM proto[] = {
{ 350, { 1, 31 }, { 1, 3 }, { 3, 1 } }, // protocol 1
{ 650, { 1, 10 }, { 1, 2 }, { 2, 1 } }, // protocol 2
{ 100, { 1, 71 }, { 4, 11 }, { 9, 6 } }, // protocol 3
{ 380, { 1, 6 }, { 1, 3 }, { 3, 1 } }, // protocol 4
{ 500, { 6, 14 }, { 1, 2 }, { 2, 1 } }, // protocol 5
};
static const int numProto = sizeof(proto) / sizeof(proto[0]);
#if not defined( RCSwitchDisableReceiving )
unsigned long RCSwitch::nReceivedValue = 0;
unsigned int RCSwitch::nReceivedBitlength = 0;
unsigned int RCSwitch::nReceivedDelay = 0;
unsigned int RCSwitch::nReceivedProtocol = 0;
int RCSwitch::nReceiveTolerance = 60;
const unsigned int RCSwitch::nSeparationLimit = 4600;
// separationLimit: minimum microseconds between received codes, closer codes are ignored.
// according to discussion on issue #14 it might be more suitable to set the separation
// limit to the same time as the 'low' part of the sync signal for the current protocol.
unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES];
#endif
RCSwitch::RCSwitch() {
this->nTransmitterPin = -1;
this->setRepeatTransmit(10);
this->setProtocol(1);
#if not defined( RCSwitchDisableReceiving )
this->nReceiverInterrupt = -1;
this->setReceiveTolerance(60);
RCSwitch::nReceivedValue = 0;
#endif
}
/**
* Sets the protocol to send.
*/
void RCSwitch::setProtocol(Protocol protocol) {
this->protocol = protocol;
}
/**
* Sets the protocol to send, from a list of predefined protocols
*/
void RCSwitch::setProtocol(int nProtocol) {
if (nProtocol < 1 || nProtocol > numProto) {
nProtocol = 1; // TODO: trigger an error, e.g. "bad protocol" ???
}
memcpy_P(&this->protocol, &proto[nProtocol-1], sizeof(Protocol));
}
/**
* Sets the protocol to send with pulse length in microseconds.
*/
void RCSwitch::setProtocol(int nProtocol, int nPulseLength) {
setProtocol(nProtocol);
this->setPulseLength(nPulseLength);
}
/**
* Sets pulse length in microseconds
*/
void RCSwitch::setPulseLength(int nPulseLength) {
this->protocol.pulseLength = nPulseLength;
}
/**
* Sets Repeat Transmits
*/
void RCSwitch::setRepeatTransmit(int nRepeatTransmit) {
this->nRepeatTransmit = nRepeatTransmit;
}
/**
* Set Receiving Tolerance
*/
#if not defined( RCSwitchDisableReceiving )
void RCSwitch::setReceiveTolerance(int nPercent) {
RCSwitch::nReceiveTolerance = nPercent;
}
#endif
/**
* 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 D REV)
*
* @param sGroup Code of the switch group (A,B,C,D)
* @param nDevice Number of the switch itself (1..3)
*/
void RCSwitch::switchOn(char sGroup, int nDevice) {
this->sendTriState( this->getCodeWordD(sGroup, nDevice, true) );
}
/**
* Switch a remote switch off (Type D REV)
*
* @param sGroup Code of the switch group (A,B,C,D)
* @param nDevice Number of the switch itself (1..3)
*/
void RCSwitch::switchOff(char sGroup, int nDevice) {
this->sendTriState( this->getCodeWordD(sGroup, nDevice, false) );
}
/**
* 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) );
}
/**
* Deprecated, use switchOn(const char* sGroup, const char* sDevice) instead!
* 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..5)
*/
void RCSwitch::switchOn(const char* sGroup, int nChannel) {
const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" };
this->switchOn(sGroup, code[nChannel]);
}
/**
* Deprecated, use switchOff(const char* sGroup, const char* sDevice) instead!
* 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..5)
*/
void RCSwitch::switchOff(const char* sGroup, int nChannel) {
const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" };
this->switchOff(sGroup, code[nChannel]);
}
/**
* 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 sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111")
*/
void RCSwitch::switchOn(const char* sGroup, const char* sDevice) {
this->sendTriState( this->getCodeWordA(sGroup, sDevice, 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 sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111")
*/
void RCSwitch::switchOff(const char* sGroup, const char* sDevice) {
this->sendTriState( this->getCodeWordA(sGroup, sDevice, false) );
}
/**
* Returns a char[13], representing the code word to be sent.
* 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" (sync bit)
*
* +-----------------------------+-----------------------------+----------+----------+--------------+----------+
* | 4 bits address | 4 bits address | 1 bit | 1 bit | 2 bits | 1 bit |
* | switch group | switch number | not used | not used | on / off | 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 Whether 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];
const 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;
}
/**
* Returns a char[13], representing the code word to be send.
*
*/
char* RCSwitch::getCodeWordA(const char* sGroup, const char* sDevice, boolean bOn) {
static char sDipSwitches[13];
int i = 0;
int j = 0;
for (i = 0; i < 5; i++) {
sDipSwitches[j++] = (sGroup[i] == '0') ? 'F' : '0';
}
for (i = 0; i < 5; i++) {
sDipSwitches[j++] = (sDevice[i] == '0') ? 'F' : '0';
}
if (bOn) {
sDipSwitches[j++] = '0';
sDipSwitches[j++] = 'F';
} else {
sDipSwitches[j++] = 'F';
sDipSwitches[j++] = '0';
}
sDipSwitches[j] = '\0';
return sDipSwitches;
}
/**
* 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';
}
const char* sDeviceGroupCode = dec2binWcharfill( (nDevice-1) + (nGroup-1)*4, 4, '0' );
const 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;
}
/**
* Decoding for the REV Switch Type
*
* Returns a char[13], representing the Tristate to be send.
* A Code Word consists of 7 address bits and 5 command data bits.
* A Code Bit can have 3 different states: "F" (floating), "0" (low), "1" (high)
*
* +-------------------------------+--------------------------------+-----------------------+
* | 4 bits address (switch group) | 3 bits address (device number) | 5 bits (command data) |
* | A=1FFF B=F1FF C=FF1F D=FFF1 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | on=00010 off=00001 |
* +-------------------------------+--------------------------------+-----------------------+
*
* Source: http://www.the-intruder.net/funksteckdosen-von-rev-uber-arduino-ansteuern/
*
* @param sGroup Name of the switch group (A..D, resp. a..d)
* @param nDevice Number of the switch itself (1..3)
* @param bStatus Whether to switch on (true) or off (false)
*
* @return char[13]
*/
char* RCSwitch::getCodeWordD(char sGroup, int nDevice, boolean bStatus){
static char sReturn[13];
int nReturnPos = 0;
// Building 4 bits address
// (Potential problem if dec2binWcharfill not returning correct string)
char *sGroupCode;
switch(sGroup){
case 'a':
case 'A':
sGroupCode = dec2binWcharfill(8, 4, 'F'); break;
case 'b':
case 'B':
sGroupCode = dec2binWcharfill(4, 4, 'F'); break;
case 'c':
case 'C':
sGroupCode = dec2binWcharfill(2, 4, 'F'); break;
case 'd':
case 'D':
sGroupCode = dec2binWcharfill(1, 4, 'F'); break;
default:
return '\0';
}
for (int i = 0; i<4; i++) {
sReturn[nReturnPos++] = sGroupCode[i];
}
// Building 3 bits address
// (Potential problem if dec2binWcharfill not returning correct string)
char *sDevice;
switch(nDevice) {
case 1:
sDevice = dec2binWcharfill(4, 3, 'F'); break;
case 2:
sDevice = dec2binWcharfill(2, 3, 'F'); break;
case 3:
sDevice = dec2binWcharfill(1, 3, 'F'); break;
default:
return '\0';
}
for (int i = 0; i<3; i++)
sReturn[nReturnPos++] = sDevice[i];
// fill up rest with zeros
for (int i = 0; i<5; i++)
sReturn[nReturnPos++] = '0';
// encode on or off
if (bStatus)
sReturn[10] = '1';
else
sReturn[11] = '1';
// last position terminate string
sReturn[12] = '\0';
return sReturn;
}
/**
* @param sCodeWord /^[10FS]*$/ -> see getCodeWord
*/
void RCSwitch::sendTriState(const 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->dec2binWcharfill(code, length, '0') );
}
void RCSwitch::send(const 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) {
if (this->nTransmitterPin != -1) {
#if not defined( RCSwitchDisableReceiving )
int nReceiverInterrupt_backup = nReceiverInterrupt;
if (nReceiverInterrupt_backup != -1) {
this->disableReceive();
}
#endif
digitalWrite(this->nTransmitterPin, HIGH);
delayMicroseconds( this->protocol.pulseLength * nHighPulses);
digitalWrite(this->nTransmitterPin, LOW);
delayMicroseconds( this->protocol.pulseLength * nLowPulses);
#if not defined( RCSwitchDisableReceiving )
if (nReceiverInterrupt_backup != -1) {
this->enableReceive(nReceiverInterrupt_backup);
}
#endif
}
}
void RCSwitch::transmit(HighLow pulses) {
transmit(pulses.high, pulses.low);
}
/**
* Sends a "0" Bit
* _
* Waveform Protocol 1: | |___
* _
* Waveform Protocol 2: | |__
*/
void RCSwitch::send0() {
this->transmit(protocol.zero);
}
/**
* Sends a "1" Bit
* ___
* Waveform Protocol 1: | |_
* __
* Waveform Protocol 2: | |_
*/
void RCSwitch::send1() {
this->transmit(protocol.one);
}
/**
* Sends a Tri-State "0" Bit
* _ _
* Waveform: | |___| |___
*/
void RCSwitch::sendT0() {
this->send0();
this->send0();
}
/**
* Sends a Tri-State "1" Bit
* ___ ___
* Waveform: | |_| |_
*/
void RCSwitch::sendT1() {
this->send1();
this->send1();
}
/**
* Sends a Tri-State "F" Bit
* _ ___
* Waveform: | |___| |_
*/
void RCSwitch::sendTF() {
this->send0();
this->send1();
}
/**
* Sends a "Sync" Bit
* _
* Waveform Protocol 1: | |_______________________________
* _
* Waveform Protocol 2: | |__________
*/
void RCSwitch::sendSync() {
this->transmit(protocol.syncFactor);
}
#if not defined( RCSwitchDisableReceiving )
/**
* Enable receiving data
*/
void RCSwitch::enableReceive(int interrupt) {
this->nReceiverInterrupt = interrupt;
this->enableReceive();
}
void RCSwitch::enableReceive() {
if (this->nReceiverInterrupt != -1) {
RCSwitch::nReceivedValue = 0;
RCSwitch::nReceivedBitlength = 0;
#if defined(RaspberryPi) // Raspberry Pi
wiringPiISR(this->nReceiverInterrupt, INT_EDGE_BOTH, &handleInterrupt);
#else // Arduino
attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE);
#endif
}
}
/**
* Disable receiving data
*/
void RCSwitch::disableReceive() {
#if not defined(RaspberryPi)
detachInterrupt(this->nReceiverInterrupt);
#endif // For Raspberry Pi (wiringPi) you can't unregister the ISR
this->nReceiverInterrupt = -1;
}
bool RCSwitch::available() {
return RCSwitch::nReceivedValue != 0;
}
void RCSwitch::resetAvailable() {
RCSwitch::nReceivedValue = 0;
}
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;
}
/* helper function for the various receiveProtocol methods */
static inline unsigned int diff(int A, int B) {
return abs(A - B);
}
/**
*
*/
bool RCSwitch::receiveProtocol(const int p, unsigned int changeCount) {
Protocol pro;
memcpy_P(&pro, &proto[p-1], sizeof(Protocol));
unsigned long code = 0;
const unsigned int delay = RCSwitch::timings[0] / pro.syncFactor.low;
const unsigned int delayTolerance = delay * RCSwitch::nReceiveTolerance / 100;
for (unsigned int i = 1; i < changeCount; i += 2) {
code <<= 1;
if (diff(RCSwitch::timings[i], delay * pro.zero.high) < delayTolerance &&
diff(RCSwitch::timings[i + 1], delay * pro.zero.low) < delayTolerance) {
// zero
} else if (diff(RCSwitch::timings[i], delay * pro.one.high) < delayTolerance &&
diff(RCSwitch::timings[i + 1], delay * pro.one.low) < delayTolerance) {
// one
code |= 1;
} else {
// Failed
return false;
}
}
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 = p;
}
return true;
}
void RCSwitch::handleInterrupt() {
static unsigned int duration;
static unsigned int changeCount;
static unsigned long lastTime;
static unsigned int repeatCount;
long time = micros();
duration = time - lastTime;
if (duration > RCSwitch::nSeparationLimit && diff(duration, RCSwitch::timings[0]) < 200) {
repeatCount++;
changeCount--;
if (repeatCount == 2) {
if (receiveProtocol(1, changeCount) == false) {
if (receiveProtocol(2, changeCount) == false) {
if (receiveProtocol(3, changeCount) == false) {
//failed
}
}
}
repeatCount = 0;
}
changeCount = 0;
} else if (duration > RCSwitch::nSeparationLimit) {
changeCount = 0;
}
if (changeCount >= RCSWITCH_MAX_CHANGES) {
changeCount = 0;
repeatCount = 0;
}
RCSwitch::timings[changeCount++] = duration;
lastTime = time;
}
#endif
/**
* Turns a decimal value to its binary representation
*/
char* RCSwitch::dec2binWcharfill(unsigned long dec, unsigned int bitLength, char fill) {
static char bin[32];
bin[bitLength] = '\0';
while (bitLength > 0) {
bitLength--;
bin[bitLength] = (dec & 1) ? '1' : fill;
dec >>= 1;
}
return bin;
}