2 RCSwitch - Arduino libary for remote control outlet switches
3 Copyright (c) 2011 Suat Özgür. All right reserved.
6 - Andre Koehler / info(at)tomate-online(dot)de
7 - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com
8 - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46
9 - Dominik Fischer / dom_fischer(at)web(dot)de
10 - Frank Oltmanns / <first name>.<last name>(at)gmail(dot)com
11 - Andreas Steinel / A.<lastname>(at)gmail(dot)com
12 - Max Horn / max(at)quendi(dot)de
13 - Robert ter Vehn / <first name>.<last name>(at)gmail(dot)com
14 - Johann Richard / <first name>.<last name>(at)gmail(dot)com
15 - Vlad Gheorghe / <first name>.<last name>(at)gmail(dot)com https://github.com/vgheo
16 - Dmitry Shalnov / <last name>(at)interplaymedium(dot)org
18 Project home: https://github.com/sui77/rc-switch/
20 This library is free software; you can redistribute it and/or
21 modify it under the terms of the GNU Lesser General Public
22 License as published by the Free Software Foundation; either
23 version 2.1 of the License, or (at your option) any later version.
25 This library is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 Lesser General Public License for more details.
30 You should have received a copy of the GNU Lesser General Public
31 License along with this library; if not, write to the Free Software
32 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
40 // PROGMEM and _P functions are for AVR based microprocessors,
41 // so we must normalize these for the ARM processor:
43 #define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
47 // interrupt handler and related code must be in RAM on ESP8266,
48 // according to issue #46.
49 #define RECEIVE_ATTR ICACHE_RAM_ATTR
55 /* Format for protocol definitions:
56 * {pulselength, Sync bit, "0" bit, "1" bit}
58 * pulselength: pulse length in microseconds, e.g. 350
59 * Sync bit: {1, 31} means 1 high pulse and 31 low pulses
60 * (perceived as a 31*pulselength long pulse, total length of sync bit is
61 * 32*pulselength microseconds), i.e:
63 * | |_______________________________ (don't count the vertical bars)
64 * "0" bit: waveform for a data bit of value "0", {1, 3} means 1 high pulse
65 * and 3 low pulses, total length (1+3)*pulselength, i.e:
68 * "1" bit: waveform for a data bit of value "1", e.g. {3,1}:
72 * These are combined to form Tri-State bits when sending or receiving codes.
75 static const RCSwitch::Protocol proto[] = {
77 static const RCSwitch::Protocol PROGMEM proto[] = {
79 { 350, { 1, 31 }, { 1, 3 }, { 3, 1 } }, // protocol 1
80 { 650, { 1, 10 }, { 1, 2 }, { 2, 1 } }, // protocol 2
81 { 100, { 30, 71 }, { 4, 11 }, { 9, 6 } }, // protocol 3
82 { 380, { 1, 6 }, { 1, 3 }, { 3, 1 } }, // protocol 4
83 { 500, { 6, 14 }, { 1, 2 }, { 2, 1 } }, // protocol 5
87 numProto = sizeof(proto) / sizeof(proto[0])
90 #if not defined( RCSwitchDisableReceiving )
91 unsigned long RCSwitch::nReceivedValue = 0;
92 unsigned int RCSwitch::nReceivedBitlength = 0;
93 unsigned int RCSwitch::nReceivedDelay = 0;
94 unsigned int RCSwitch::nReceivedProtocol = 0;
95 int RCSwitch::nReceiveTolerance = 60;
96 const unsigned int RCSwitch::nSeparationLimit = 4600;
97 // separationLimit: minimum microseconds between received codes, closer codes are ignored.
98 // according to discussion on issue #14 it might be more suitable to set the separation
99 // limit to the same time as the 'low' part of the sync signal for the current protocol.
100 unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES];
103 RCSwitch::RCSwitch() {
104 this->nTransmitterPin = -1;
105 this->setRepeatTransmit(10);
106 this->setProtocol(1);
107 #if not defined( RCSwitchDisableReceiving )
108 this->nReceiverInterrupt = -1;
109 this->setReceiveTolerance(60);
110 RCSwitch::nReceivedValue = 0;
115 * Sets the protocol to send.
117 void RCSwitch::setProtocol(Protocol protocol) {
118 this->protocol = protocol;
122 * Sets the protocol to send, from a list of predefined protocols
124 void RCSwitch::setProtocol(int nProtocol) {
125 if (nProtocol < 1 || nProtocol > numProto) {
126 nProtocol = 1; // TODO: trigger an error, e.g. "bad protocol" ???
129 this->protocol = proto[nProtocol-1];
131 memcpy_P(&this->protocol, &proto[nProtocol-1], sizeof(Protocol));
136 * Sets the protocol to send with pulse length in microseconds.
138 void RCSwitch::setProtocol(int nProtocol, int nPulseLength) {
139 setProtocol(nProtocol);
140 this->setPulseLength(nPulseLength);
145 * Sets pulse length in microseconds
147 void RCSwitch::setPulseLength(int nPulseLength) {
148 this->protocol.pulseLength = nPulseLength;
152 * Sets Repeat Transmits
154 void RCSwitch::setRepeatTransmit(int nRepeatTransmit) {
155 this->nRepeatTransmit = nRepeatTransmit;
159 * Set Receiving Tolerance
161 #if not defined( RCSwitchDisableReceiving )
162 void RCSwitch::setReceiveTolerance(int nPercent) {
163 RCSwitch::nReceiveTolerance = nPercent;
169 * Enable transmissions
171 * @param nTransmitterPin Arduino Pin to which the sender is connected to
173 void RCSwitch::enableTransmit(int nTransmitterPin) {
174 this->nTransmitterPin = nTransmitterPin;
175 pinMode(this->nTransmitterPin, OUTPUT);
179 * Disable transmissions
181 void RCSwitch::disableTransmit() {
182 this->nTransmitterPin = -1;
186 * Switch a remote switch on (Type D REV)
188 * @param sGroup Code of the switch group (A,B,C,D)
189 * @param nDevice Number of the switch itself (1..3)
191 void RCSwitch::switchOn(char sGroup, int nDevice) {
192 this->sendTriState( this->getCodeWordD(sGroup, nDevice, true) );
196 * Switch a remote switch off (Type D REV)
198 * @param sGroup Code of the switch group (A,B,C,D)
199 * @param nDevice Number of the switch itself (1..3)
201 void RCSwitch::switchOff(char sGroup, int nDevice) {
202 this->sendTriState( this->getCodeWordD(sGroup, nDevice, false) );
206 * Switch a remote switch on (Type C Intertechno)
208 * @param sFamily Familycode (a..f)
209 * @param nGroup Number of group (1..4)
210 * @param nDevice Number of device (1..4)
212 void RCSwitch::switchOn(char sFamily, int nGroup, int nDevice) {
213 this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, true) );
217 * Switch a remote switch off (Type C Intertechno)
219 * @param sFamily Familycode (a..f)
220 * @param nGroup Number of group (1..4)
221 * @param nDevice Number of device (1..4)
223 void RCSwitch::switchOff(char sFamily, int nGroup, int nDevice) {
224 this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, false) );
228 * Switch a remote switch on (Type B with two rotary/sliding switches)
230 * @param nAddressCode Number of the switch group (1..4)
231 * @param nChannelCode Number of the switch itself (1..4)
233 void RCSwitch::switchOn(int nAddressCode, int nChannelCode) {
234 this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, true) );
238 * Switch a remote switch off (Type B with two rotary/sliding switches)
240 * @param nAddressCode Number of the switch group (1..4)
241 * @param nChannelCode Number of the switch itself (1..4)
243 void RCSwitch::switchOff(int nAddressCode, int nChannelCode) {
244 this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, false) );
248 * Deprecated, use switchOn(const char* sGroup, const char* sDevice) instead!
249 * Switch a remote switch on (Type A with 10 pole DIP switches)
251 * @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")
252 * @param nChannelCode Number of the switch itself (1..5)
254 void RCSwitch::switchOn(const char* sGroup, int nChannel) {
255 const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" };
256 this->switchOn(sGroup, code[nChannel]);
260 * Deprecated, use switchOff(const char* sGroup, const char* sDevice) instead!
261 * Switch a remote switch off (Type A with 10 pole DIP switches)
263 * @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")
264 * @param nChannelCode Number of the switch itself (1..5)
266 void RCSwitch::switchOff(const char* sGroup, int nChannel) {
267 const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" };
268 this->switchOff(sGroup, code[nChannel]);
272 * Switch a remote switch on (Type A with 10 pole DIP switches)
274 * @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")
275 * @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")
277 void RCSwitch::switchOn(const char* sGroup, const char* sDevice) {
278 this->sendTriState( this->getCodeWordA(sGroup, sDevice, true) );
282 * Switch a remote switch off (Type A with 10 pole DIP switches)
284 * @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")
285 * @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")
287 void RCSwitch::switchOff(const char* sGroup, const char* sDevice) {
288 this->sendTriState( this->getCodeWordA(sGroup, sDevice, false) );
293 * Returns a char[13], representing the code word to be send.
296 char* RCSwitch::getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus) {
297 static char sReturn[13];
300 for (int i = 0; i < 5; i++) {
301 sReturn[nReturnPos++] = (sGroup[i] == '0') ? 'F' : '0';
304 for (int i = 0; i < 5; i++) {
305 sReturn[nReturnPos++] = (sDevice[i] == '0') ? 'F' : '0';
308 sReturn[nReturnPos++] = bStatus ? '0' : 'F';
309 sReturn[nReturnPos++] = bStatus ? 'F' : '0';
311 sReturn[nReturnPos] = '\0';
316 * Encoding for type B switches with two rotary/sliding switches.
318 * The code word is a tristate word and with following bit pattern:
320 * +-----------------------------+-----------------------------+----------+------------+
321 * | 4 bits address | 4 bits address | 3 bits | 1 bit |
322 * | switch group | switch number | not used | on / off |
323 * | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | FFF | on=F off=0 |
324 * +-----------------------------+-----------------------------+----------+------------+
326 * @param nAddressCode Number of the switch group (1..4)
327 * @param nChannelCode Number of the switch itself (1..4)
328 * @param bStatus Whether to switch on (true) or off (false)
330 * @return char[13], representing a tristate code word of length 12
332 char* RCSwitch::getCodeWordB(int nAddressCode, int nChannelCode, bool bStatus) {
333 static char sReturn[13];
336 if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) {
340 for (int i = 1; i <= 4; i++) {
341 sReturn[nReturnPos++] = (nAddressCode == i) ? '0' : 'F';
344 for (int i = 1; i <= 4; i++) {
345 sReturn[nReturnPos++] = (nChannelCode == i) ? '0' : 'F';
348 sReturn[nReturnPos++] = 'F';
349 sReturn[nReturnPos++] = 'F';
350 sReturn[nReturnPos++] = 'F';
352 sReturn[nReturnPos++] = bStatus ? 'F' : '0';
354 sReturn[nReturnPos] = '\0';
359 * Like getCodeWord (Type C = Intertechno)
361 char* RCSwitch::getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus) {
362 static char sReturn[13];
365 int nFamily = (int)sFamily - 'a';
366 if ( nFamily < 0 || nFamily > 15 || nGroup < 1 || nGroup > 4 || nDevice < 1 || nDevice > 4) {
370 // encode the family into four bits
371 sReturn[nReturnPos++] = (nFamily & 1) ? 'F' : '0';
372 sReturn[nReturnPos++] = (nFamily & 2) ? 'F' : '0';
373 sReturn[nReturnPos++] = (nFamily & 4) ? 'F' : '0';
374 sReturn[nReturnPos++] = (nFamily & 8) ? 'F' : '0';
376 // encode the device and group
377 sReturn[nReturnPos++] = ((nDevice-1) & 1) ? 'F' : '0';
378 sReturn[nReturnPos++] = ((nDevice-1) & 2) ? 'F' : '0';
379 sReturn[nReturnPos++] = ((nGroup-1) & 1) ? 'F' : '0';
380 sReturn[nReturnPos++] = ((nGroup-1) & 2) ? 'F' : '0';
382 // encode the status code
383 sReturn[nReturnPos++] = '0';
384 sReturn[nReturnPos++] = 'F';
385 sReturn[nReturnPos++] = 'F';
386 sReturn[nReturnPos++] = bStatus ? 'F' : '0';
388 sReturn[nReturnPos] = '\0';
393 * Encoding for the REV Switch Type
395 * The code word is a tristate word and with following bit pattern:
397 * +-----------------------------+-------------------+----------+--------------+
398 * | 4 bits address | 3 bits address | 3 bits | 2 bits |
399 * | switch group | device number | not used | on / off |
400 * | A=1FFF B=F1FF C=FF1F D=FFF1 | 1=0FF 2=F0F 3=FF0 | 000 | on=10 off=01 |
401 * +-----------------------------+-------------------+----------+--------------+
403 * Source: http://www.the-intruder.net/funksteckdosen-von-rev-uber-arduino-ansteuern/
405 * @param sGroup Name of the switch group (A..D, resp. a..d)
406 * @param nDevice Number of the switch itself (1..3)
407 * @param bStatus Whether to switch on (true) or off (false)
409 * @return char[13], representing a tristate code word of length 12
411 char* RCSwitch::getCodeWordD(char sGroup, int nDevice, bool bStatus) {
412 static char sReturn[13];
415 // sGroup must be one of the letters in "abcdABCD"
416 int nGroup = (sGroup >= 'a') ? (int)sGroup - 'a' : (int)sGroup - 'A';
417 if ( nGroup < 0 || nGroup > 3 || nDevice < 1 || nDevice > 3) {
421 for (int i = 0; i < 4; i++) {
422 sReturn[nReturnPos++] = (nGroup == i) ? '1' : 'F';
425 for (int i = 1; i <= 3; i++) {
426 sReturn[nReturnPos++] = (nDevice == i) ? '1' : 'F';
429 sReturn[nReturnPos++] = '0';
430 sReturn[nReturnPos++] = '0';
431 sReturn[nReturnPos++] = '0';
433 sReturn[nReturnPos++] = bStatus ? '1' : '0';
434 sReturn[nReturnPos++] = bStatus ? '0' : '1';
436 sReturn[nReturnPos] = '\0';
441 * @param sCodeWord a tristate code word consisting of the letter 0, 1, F
443 void RCSwitch::sendTriState(const char* sCodeWord) {
444 // turn the tristate code word into the corresponding bit pattern, then send it
445 unsigned long code = 0;
446 unsigned int length = 0;
447 for (const char* p = sCodeWord; *p; p++) {
464 this->send(code, length);
468 * @param sCodeWord a binary code word consisting of the letter 0, 1
470 void RCSwitch::send(const char* sCodeWord) {
471 // turn the tristate code word into the corresponding bit pattern, then send it
472 unsigned long code = 0;
473 unsigned int length = 0;
474 for (const char* p = sCodeWord; *p; p++) {
480 this->send(code, length);
484 * Transmit the first 'length' bits of the integer 'code'. The
485 * bits are sent from MSB to LSB, i.e., first the bit at position length-1,
486 * then the bit at position length-2, and so on, till finally the bit at position 0.
488 void RCSwitch::send(unsigned long code, unsigned int length) {
489 if (this->nTransmitterPin == -1)
492 #if not defined( RCSwitchDisableReceiving )
493 // make sure the receiver is disabled while we transmit
494 int nReceiverInterrupt_backup = nReceiverInterrupt;
495 if (nReceiverInterrupt_backup != -1) {
496 this->disableReceive();
500 for (int nRepeat = 0; nRepeat < nRepeatTransmit; nRepeat++) {
501 for (int i = length-1; i >= 0; i--) {
502 if (code & (1L << i))
503 this->transmit(protocol.one);
505 this->transmit(protocol.zero);
507 this->transmit(protocol.syncFactor);
510 #if not defined( RCSwitchDisableReceiving )
511 // enable receiver again if we just disabled it
512 if (nReceiverInterrupt_backup != -1) {
513 this->enableReceive(nReceiverInterrupt_backup);
519 * Transmit a single high-low pulse.
521 void RCSwitch::transmit(HighLow pulses) {
522 digitalWrite(this->nTransmitterPin, HIGH);
523 delayMicroseconds( this->protocol.pulseLength * pulses.high);
524 digitalWrite(this->nTransmitterPin, LOW);
525 delayMicroseconds( this->protocol.pulseLength * pulses.low);
529 #if not defined( RCSwitchDisableReceiving )
531 * Enable receiving data
533 void RCSwitch::enableReceive(int interrupt) {
534 this->nReceiverInterrupt = interrupt;
535 this->enableReceive();
538 void RCSwitch::enableReceive() {
539 if (this->nReceiverInterrupt != -1) {
540 RCSwitch::nReceivedValue = 0;
541 RCSwitch::nReceivedBitlength = 0;
543 #if defined(RaspberryPi) // Raspberry Pi
544 wiringPiISR(this->nReceiverInterrupt, INT_EDGE_BOTH, &handleInterrupt);
546 attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE);
553 * Disable receiving data
555 void RCSwitch::disableReceive() {
556 #if not defined(RaspberryPi) // Arduino
557 detachInterrupt(this->nReceiverInterrupt);
558 #endif // For Raspberry Pi (wiringPi) you can't unregister the ISR
559 this->nReceiverInterrupt = -1;
562 bool RCSwitch::available() {
563 return RCSwitch::nReceivedValue != 0;
566 void RCSwitch::resetAvailable() {
567 RCSwitch::nReceivedValue = 0;
570 unsigned long RCSwitch::getReceivedValue() {
571 return RCSwitch::nReceivedValue;
574 unsigned int RCSwitch::getReceivedBitlength() {
575 return RCSwitch::nReceivedBitlength;
578 unsigned int RCSwitch::getReceivedDelay() {
579 return RCSwitch::nReceivedDelay;
582 unsigned int RCSwitch::getReceivedProtocol() {
583 return RCSwitch::nReceivedProtocol;
586 unsigned int* RCSwitch::getReceivedRawdata() {
587 return RCSwitch::timings;
590 /* helper function for the receiveProtocol method */
591 static inline unsigned int diff(int A, int B) {
598 bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCount) {
600 const Protocol &pro = proto[p-1];
603 memcpy_P(&pro, &proto[p-1], sizeof(Protocol));
606 unsigned long code = 0;
607 const unsigned int delay = RCSwitch::timings[0] / pro.syncFactor.low;
608 const unsigned int delayTolerance = delay * RCSwitch::nReceiveTolerance / 100;
610 for (unsigned int i = 1; i < changeCount - 1; i += 2) {
612 if (diff(RCSwitch::timings[i], delay * pro.zero.high) < delayTolerance &&
613 diff(RCSwitch::timings[i + 1], delay * pro.zero.low) < delayTolerance) {
615 } else if (diff(RCSwitch::timings[i], delay * pro.one.high) < delayTolerance &&
616 diff(RCSwitch::timings[i + 1], delay * pro.one.low) < delayTolerance) {
625 if (changeCount > 7) { // ignore very short transmissions: no device sends them, so this must be noise
626 RCSwitch::nReceivedValue = code;
627 RCSwitch::nReceivedBitlength = (changeCount - 1) / 2;
628 RCSwitch::nReceivedDelay = delay;
629 RCSwitch::nReceivedProtocol = p;
635 void RECEIVE_ATTR RCSwitch::handleInterrupt() {
637 static unsigned int changeCount = 0;
638 static unsigned long lastTime = 0;
639 static unsigned int repeatCount = 0;
641 const long time = micros();
642 const unsigned int duration = time - lastTime;
644 if (duration > RCSwitch::nSeparationLimit) {
645 // A long stretch without signal level change occurred. This could
646 // be the gap between two transmission.
647 if (diff(duration, RCSwitch::timings[0]) < 200) {
648 // This long signal is close in length to the long signal which
649 // started the previously recorded timings; this suggests that
650 // it may indeed by a a gap between two transmissions (we assume
651 // here that a sender will send the signal multiple times,
652 // with roughly the same gap between them).
654 if (repeatCount == 2) {
655 for(unsigned int i = 1; i <= numProto; i++) {
656 if (receiveProtocol(i, changeCount)) {
657 // receive succeeded for protocol i
668 if (changeCount >= RCSWITCH_MAX_CHANGES) {
673 RCSwitch::timings[changeCount++] = duration;
677 // where no wiringPiISR supported (nanopi)
679 void RCSwitch::handleNoInterrupt( unsigned char PIN ){
681 static unsigned char dr, drPrev;
683 dr = digitalRead( PIN );