├── .gitignore ├── .travis.yml ├── README.md ├── examples ├── ReceiveDemo_Advanced │ ├── ReceiveDemo_Advanced.ino │ └── output.ino ├── ReceiveDemo_Simple │ └── ReceiveDemo_Simple.ino ├── SendDemo │ └── SendDemo.ino ├── TypeA_WithDIPSwitches │ └── TypeA_WithDIPSwitches.ino ├── TypeA_WithDIPSwitches_Lightweight │ └── TypeA_WithDIPSwitches_Lightweight.ino ├── TypeB_WithRotaryOrSlidingSwitches │ └── TypeB_WithRotaryOrSlidingSwitches.ino ├── TypeC_Intertechno │ └── TypeC_Intertechno.ino ├── TypeD_REV │ └── TypeD_REV.ino └── Webserver │ └── Webserver.ino ├── keywords.txt ├── library.json ├── library.properties ├── platformio.ini └── src ├── RCSwitch.cpp └── RCSwitch.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac stuff 2 | .DS_Store 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | 9 | # Compiled Dynamic libraries 10 | *.so 11 | *.dylib 12 | 13 | # Compiled Static libraries 14 | *.lai 15 | *.la 16 | *.a 17 | 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < https://docs.platformio.org/page/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < https://docs.platformio.org/page/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < https://docs.platformio.org/page/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choose one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | matrix: 27 | include: 28 | - language: python 29 | python: 2.7 30 | sudo: false 31 | cache: 32 | directories: 33 | - "~/.platformio" 34 | install: 35 | - pip install -U platformio 36 | - platformio update 37 | script: 38 | - platformio run 39 | 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Fork of RC-SWITCH by @sui77** 2 | 3 | # rc-switch 4 | [![Build Status](https://travis-ci.org/1technophile/rc-switch.svg?branch=master)](https://travis-ci.org/1technophile/rc-switch) 5 | 6 | Use your Arduino or Raspberry Pi to operate remote radio controlled devices 7 | 8 | ## Download 9 | https://github.com/1technophile/rc-switch/releases/latest 10 | 11 | rc-switch is also listed in the arduino library manager. 12 | 13 | ## Wiki 14 | https://github.com/1technophile/rc-switch/wiki 15 | 16 | ## Info 17 | ### Send RC codes 18 | 19 | Use your Arduino or Raspberry Pi to operate remote radio controlled devices. 20 | This will most likely work with all popular low cost power outlet sockets. If 21 | yours doesn't work, you might need to adjust the pulse length. 22 | 23 | All you need is a Arduino or Raspberry Pi, a 315/433MHz AM transmitter and one 24 | or more devices with one of the supported chipsets: 25 | 26 | - SC5262 / SC5272 27 | - HX2262 / HX2272 28 | - PT2262 / PT2272 29 | - EV1527 / RT1527 / FP1527 / HS1527 30 | - Intertechno outlets 31 | - HT6P20X 32 | 33 | ### Receive and decode RC codes 34 | 35 | Find out what codes your remote is sending. Use your remote to control your 36 | Arduino. 37 | 38 | All you need is an Arduino, a 315/433MHz AM receiver (altough there is no 39 | instruction yet, yes it is possible to hack an existing device) and a remote 40 | hand set. 41 | 42 | For the Raspberry Pi, clone the https://github.com/ninjablocks/433Utils project to 43 | compile a sniffer tool and transmission commands. 44 | -------------------------------------------------------------------------------- /examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Example for receiving 3 | 4 | https://github.com/sui77/rc-switch/ 5 | 6 | If you want to visualize a telegram copy the raw data and 7 | paste it into http://test.sui.li/oszi/ 8 | */ 9 | 10 | #include 11 | 12 | RCSwitch mySwitch = RCSwitch(); 13 | 14 | void setup() { 15 | Serial.begin(9600); 16 | mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2 17 | } 18 | 19 | void loop() { 20 | if (mySwitch.available()) { 21 | output(mySwitch.getReceivedValue(), mySwitch.getReceivedBitlength(), mySwitch.getReceivedDelay(), mySwitch.getReceivedRawdata(),mySwitch.getReceivedProtocol()); 22 | mySwitch.resetAvailable(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/ReceiveDemo_Advanced/output.ino: -------------------------------------------------------------------------------- 1 | static const char* bin2tristate(const char* bin); 2 | static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength); 3 | 4 | void output(unsigned long decimal, unsigned int length, unsigned int delay, unsigned int* raw, unsigned int protocol) { 5 | 6 | const char* b = dec2binWzerofill(decimal, length); 7 | Serial.print("Decimal: "); 8 | Serial.print(decimal); 9 | Serial.print(" ("); 10 | Serial.print( length ); 11 | Serial.print("Bit) Binary: "); 12 | Serial.print( b ); 13 | Serial.print(" Tri-State: "); 14 | Serial.print( bin2tristate( b) ); 15 | Serial.print(" PulseLength: "); 16 | Serial.print(delay); 17 | Serial.print(" microseconds"); 18 | Serial.print(" Protocol: "); 19 | Serial.println(protocol); 20 | 21 | Serial.print("Raw data: "); 22 | for (unsigned int i=0; i<= length*2; i++) { 23 | Serial.print(raw[i]); 24 | Serial.print(","); 25 | } 26 | Serial.println(); 27 | Serial.println(); 28 | } 29 | 30 | static const char* bin2tristate(const char* bin) { 31 | static char returnValue[50]; 32 | int pos = 0; 33 | int pos2 = 0; 34 | while (bin[pos]!='\0' && bin[pos+1]!='\0') { 35 | if (bin[pos]=='0' && bin[pos+1]=='0') { 36 | returnValue[pos2] = '0'; 37 | } else if (bin[pos]=='1' && bin[pos+1]=='1') { 38 | returnValue[pos2] = '1'; 39 | } else if (bin[pos]=='0' && bin[pos+1]=='1') { 40 | returnValue[pos2] = 'F'; 41 | } else { 42 | return "not applicable"; 43 | } 44 | pos = pos+2; 45 | pos2++; 46 | } 47 | returnValue[pos2] = '\0'; 48 | return returnValue; 49 | } 50 | 51 | static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength) { 52 | static char bin[64]; 53 | unsigned int i=0; 54 | 55 | while (Dec > 0) { 56 | bin[32+i++] = ((Dec & 1) > 0) ? '1' : '0'; 57 | Dec = Dec >> 1; 58 | } 59 | 60 | for (unsigned int j = 0; j< bitLength; j++) { 61 | if (j >= bitLength - i) { 62 | bin[j] = bin[ 31 + i - (j - (bitLength - i)) ]; 63 | } else { 64 | bin[j] = '0'; 65 | } 66 | } 67 | bin[bitLength] = '\0'; 68 | 69 | return bin; 70 | } 71 | -------------------------------------------------------------------------------- /examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Simple example for receiving 3 | 4 | https://github.com/sui77/rc-switch/ 5 | */ 6 | 7 | #include 8 | 9 | RCSwitch mySwitch = RCSwitch(); 10 | 11 | void setup() { 12 | Serial.begin(9600); 13 | mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2 14 | } 15 | 16 | void loop() { 17 | if (mySwitch.available()) { 18 | 19 | Serial.print("Received "); 20 | Serial.print( mySwitch.getReceivedValue() ); 21 | Serial.print(" / "); 22 | Serial.print( mySwitch.getReceivedBitlength() ); 23 | Serial.print("bit "); 24 | Serial.print("Protocol: "); 25 | Serial.println( mySwitch.getReceivedProtocol() ); 26 | 27 | mySwitch.resetAvailable(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/SendDemo/SendDemo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Example for different sending methods 3 | 4 | https://github.com/sui77/rc-switch/ 5 | 6 | */ 7 | 8 | #include 9 | 10 | RCSwitch mySwitch = RCSwitch(); 11 | 12 | void setup() { 13 | 14 | Serial.begin(9600); 15 | 16 | // Transmitter is connected to Arduino Pin #10 17 | mySwitch.enableTransmit(10); 18 | 19 | // Optional set protocol (default is 1, will work for most outlets) 20 | // mySwitch.setProtocol(2); 21 | 22 | // Optional set pulse length. 23 | // mySwitch.setPulseLength(320); 24 | 25 | // Optional set number of transmission repetitions. 26 | // mySwitch.setRepeatTransmit(15); 27 | 28 | } 29 | 30 | void loop() { 31 | 32 | /* See Example: TypeA_WithDIPSwitches */ 33 | mySwitch.switchOn("11111", "00010"); 34 | delay(1000); 35 | mySwitch.switchOff("11111", "00010"); 36 | delay(1000); 37 | 38 | /* Same switch as above, but using decimal code */ 39 | mySwitch.send(5393, 24); 40 | delay(1000); 41 | mySwitch.send(5396, 24); 42 | delay(1000); 43 | 44 | /* Same switch as above, but using binary code */ 45 | mySwitch.send("000000000001010100010001"); 46 | delay(1000); 47 | mySwitch.send("000000000001010100010100"); 48 | delay(1000); 49 | 50 | /* Same switch as above, but tri-state code */ 51 | mySwitch.sendTriState("00000FFF0F0F"); 52 | delay(1000); 53 | mySwitch.sendTriState("00000FFF0FF0"); 54 | delay(1000); 55 | 56 | delay(20000); 57 | } 58 | -------------------------------------------------------------------------------- /examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Example for outlets which are configured with a 10 pole DIP switch. 3 | 4 | https://github.com/sui77/rc-switch/ 5 | */ 6 | 7 | #include 8 | 9 | RCSwitch mySwitch = RCSwitch(); 10 | 11 | void setup() { 12 | 13 | // Transmitter is connected to Arduino Pin #10 14 | mySwitch.enableTransmit(10); 15 | 16 | // Optional set pulse length. 17 | // mySwitch.setPulseLength(320); 18 | 19 | } 20 | 21 | void loop() { 22 | 23 | // Switch on: 24 | // The first parameter represents the setting of the first 5 DIP switches. 25 | // In this example it's ON-ON-OFF-OFF-ON. 26 | // 27 | // The second parameter represents the setting of the last 5 DIP switches. 28 | // In this example the last 5 DIP switches are OFF-ON-OFF-ON-OFF. 29 | mySwitch.switchOn("11001", "01010"); 30 | 31 | // Wait a second 32 | delay(1000); 33 | 34 | // Switch off 35 | mySwitch.switchOff("11001", "01010"); 36 | 37 | // Wait another second 38 | delay(1000); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This is a minimal sketch without using the library at all but only works for 3 | the 10 pole dip switch sockets. It saves a lot of memory and thus might be 4 | very useful to use with ATTinys :) 5 | 6 | https://github.com/sui77/rc-switch/ 7 | */ 8 | 9 | int RCLpin = 7; 10 | 11 | void setup() { 12 | pinMode(RCLpin, OUTPUT); 13 | } 14 | 15 | void loop() { 16 | RCLswitch(0b010001000001); // DIPs an Steckdose: 0100010000 An:01 17 | delay(2000); 18 | 19 | RCLswitch(0b010001000010); // DIPs an Steckdose: 0100010000 Aus:10 20 | delay(2000); 21 | } 22 | 23 | void RCLswitch(uint16_t code) { 24 | for (int nRepeat=0; nRepeat<6; nRepeat++) { 25 | for (int i=4; i<16; i++) { 26 | RCLtransmit(1,3); 27 | if (((code << (i-4)) & 2048) > 0) { 28 | RCLtransmit(1,3); 29 | } else { 30 | RCLtransmit(3,1); 31 | } 32 | } 33 | RCLtransmit(1,31); 34 | } 35 | } 36 | 37 | void RCLtransmit(int nHighPulses, int nLowPulses) { 38 | digitalWrite(RCLpin, HIGH); 39 | delayMicroseconds( 350 * nHighPulses); 40 | digitalWrite(RCLpin, LOW); 41 | delayMicroseconds( 350 * nLowPulses); 42 | } 43 | 44 | -------------------------------------------------------------------------------- /examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Example for outlets which are configured with two rotary/sliding switches. 3 | 4 | https://github.com/sui77/rc-switch/ 5 | */ 6 | 7 | #include 8 | 9 | RCSwitch mySwitch = RCSwitch(); 10 | 11 | void setup() { 12 | 13 | // Transmitter is connected to Arduino Pin #10 14 | mySwitch.enableTransmit(10); 15 | 16 | // Optional set pulse length. 17 | // mySwitch.setPulseLength(320); 18 | 19 | } 20 | 21 | void loop() { 22 | 23 | // Switch on: 24 | // The first parameter represents the setting of the first rotary switch. 25 | // In this example it's switched to "1" or "A" or "I". 26 | // 27 | // The second parameter represents the setting of the second rotary switch. 28 | // In this example it's switched to "4" or "D" or "IV". 29 | mySwitch.switchOn(1, 4); 30 | 31 | // Wait a second 32 | delay(1000); 33 | 34 | // Switch off 35 | mySwitch.switchOff(1, 4); 36 | 37 | // Wait another second 38 | delay(1000); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /examples/TypeC_Intertechno/TypeC_Intertechno.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Example for Intertechno outlets 3 | 4 | https://github.com/sui77/rc-switch/ 5 | */ 6 | 7 | #include 8 | 9 | RCSwitch mySwitch = RCSwitch(); 10 | 11 | void setup() { 12 | 13 | // Transmitter is connected to Arduino Pin #10 14 | mySwitch.enableTransmit(10); 15 | 16 | // Optional set pulse length. 17 | // mySwitch.setPulseLength(320); 18 | 19 | } 20 | 21 | void loop() { 22 | 23 | // Switch on: 24 | // The first parameter represents the familycode (a, b, c, ... f) 25 | // The second parameter represents the group number 26 | // The third parameter represents the device number 27 | // 28 | // In this example it's family 'b', group #3, device #2 29 | mySwitch.switchOn('b', 3, 2); 30 | 31 | // Wait a second 32 | delay(1000); 33 | 34 | // Switch off 35 | mySwitch.switchOff('b', 3, 2); 36 | 37 | // Wait another second 38 | delay(1000); 39 | 40 | } -------------------------------------------------------------------------------- /examples/TypeD_REV/TypeD_REV.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Example for REV outlets (e.g. 8342L) 3 | 4 | https://github.com/sui77/rc-switch/ 5 | 6 | Need help? http://forum.ardumote.com 7 | */ 8 | 9 | #include 10 | 11 | RCSwitch mySwitch = RCSwitch(); 12 | 13 | void setup() { 14 | 15 | // Transmitter is connected to Arduino Pin #10 16 | mySwitch.enableTransmit(10); 17 | 18 | // set pulse length. 19 | mySwitch.setPulseLength(360); 20 | 21 | } 22 | 23 | void loop() { 24 | 25 | // Switch on: 26 | // The first parameter represents the channel (a, b, c, d) 27 | // The second parameter represents the device number 28 | // 29 | // In this example it's family 'd', device #2 30 | mySwitch.switchOn('d', 2); 31 | 32 | // Wait a second 33 | delay(1000); 34 | 35 | // Switch off 36 | mySwitch.switchOff('d', 2); 37 | 38 | // Wait another second 39 | delay(1000); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /examples/Webserver/Webserver.ino: -------------------------------------------------------------------------------- 1 | /* 2 | A simple RCSwitch/Ethernet/Webserver demo 3 | 4 | https://github.com/sui77/rc-switch/ 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | // Ethernet configuration 12 | uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // MAC Address 13 | uint8_t ip[] = { 192,168,0, 2 }; // IP Address 14 | EthernetServer server(80); // Server Port 80 15 | 16 | // RCSwitch configuration 17 | RCSwitch mySwitch = RCSwitch(); 18 | int RCTransmissionPin = 7; 19 | 20 | // More to do... 21 | // You should also modify the processCommand() and 22 | // httpResponseHome() functions to fit your needs. 23 | 24 | 25 | 26 | /** 27 | * Setup 28 | */ 29 | void setup() { 30 | Ethernet.begin(mac, ip); 31 | server.begin(); 32 | mySwitch.enableTransmit( RCTransmissionPin ); 33 | } 34 | 35 | /** 36 | * Loop 37 | */ 38 | void loop() { 39 | char* command = httpServer(); 40 | } 41 | 42 | /** 43 | * Command dispatcher 44 | */ 45 | void processCommand(char* command) { 46 | if (strcmp(command, "1-on") == 0) { 47 | mySwitch.switchOn(1,1); 48 | } else if (strcmp(command, "1-off") == 0) { 49 | mySwitch.switchOff(1,1); 50 | } else if (strcmp(command, "2-on") == 0) { 51 | mySwitch.switchOn(1,2); 52 | } else if (strcmp(command, "2-off") == 0) { 53 | mySwitch.switchOff(1,2); 54 | } 55 | } 56 | 57 | /** 58 | * HTTP Response with homepage 59 | */ 60 | void httpResponseHome(EthernetClient c) { 61 | c.println("HTTP/1.1 200 OK"); 62 | c.println("Content-Type: text/html"); 63 | c.println(); 64 | c.println(""); 65 | c.println(""); 66 | c.println( "RCSwitch Webserver Demo"); 67 | c.println( ""); 70 | c.println(""); 71 | c.println(""); 72 | c.println( "

RCSwitch Webserver Demo

"); 73 | c.println( ""); 77 | c.println( ""); 81 | c.println( "
"); 82 | c.println( "https://github.com/sui77/rc-switch/"); 83 | c.println(""); 84 | c.println(""); 85 | } 86 | 87 | /** 88 | * HTTP Redirect to homepage 89 | */ 90 | void httpResponseRedirect(EthernetClient c) { 91 | c.println("HTTP/1.1 301 Found"); 92 | c.println("Location: /"); 93 | c.println(); 94 | } 95 | 96 | /** 97 | * HTTP Response 414 error 98 | * Command must not be longer than 30 characters 99 | **/ 100 | void httpResponse414(EthernetClient c) { 101 | c.println("HTTP/1.1 414 Request URI too long"); 102 | c.println("Content-Type: text/plain"); 103 | c.println(); 104 | c.println("414 Request URI too long"); 105 | } 106 | 107 | /** 108 | * Process HTTP requests, parse first request header line and 109 | * call processCommand with GET query string (everything after 110 | * the ? question mark in the URL). 111 | */ 112 | char* httpServer() { 113 | EthernetClient client = server.available(); 114 | if (client) { 115 | char sReturnCommand[32]; 116 | int nCommandPos=-1; 117 | sReturnCommand[0] = '\0'; 118 | while (client.connected()) { 119 | if (client.available()) { 120 | char c = client.read(); 121 | if ((c == '\n') || (c == ' ' && nCommandPos>-1)) { 122 | sReturnCommand[nCommandPos] = '\0'; 123 | if (strcmp(sReturnCommand, "\0") == 0) { 124 | httpResponseHome(client); 125 | } else { 126 | processCommand(sReturnCommand); 127 | httpResponseRedirect(client); 128 | } 129 | break; 130 | } 131 | if (nCommandPos>-1) { 132 | sReturnCommand[nCommandPos++] = c; 133 | } 134 | if (c == '?' && nCommandPos == -1) { 135 | nCommandPos = 0; 136 | } 137 | } 138 | if (nCommandPos > 30) { 139 | httpResponse414(client); 140 | sReturnCommand[0] = '\0'; 141 | break; 142 | } 143 | } 144 | if (nCommandPos!=-1) { 145 | sReturnCommand[nCommandPos] = '\0'; 146 | } 147 | // give the web browser time to receive the data 148 | delay(1); 149 | client.stop(); 150 | 151 | return sReturnCommand; 152 | } 153 | return '\0'; 154 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For RCSwitch 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | RCSwitch KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | ########## 16 | #SENDS Begin 17 | ########## 18 | switchOn KEYWORD2 19 | switchOff KEYWORD2 20 | sendTriState KEYWORD2 21 | send KEYWORD2 22 | ########## 23 | #SENDS End 24 | ########## 25 | 26 | ########## 27 | #RECEIVE Begin 28 | ########## 29 | enableReceive KEYWORD2 30 | disableReceive KEYWORD2 31 | available KEYWORD2 32 | resetAvailable KEYWORD2 33 | setReceiveTolerance KEYWORD2 34 | getReceivedValue KEYWORD2 35 | getReceivedBitlength KEYWORD2 36 | getReceivedDelay KEYWORD2 37 | getReceivedProtocol KEYWORD2 38 | getReceivedRawdata KEYWORD2 39 | ########## 40 | #RECEIVE End 41 | ########## 42 | 43 | ########## 44 | #OTHERS Begin 45 | ########## 46 | enableTransmit KEYWORD2 47 | disableTransmit KEYWORD2 48 | setPulseLength KEYWORD2 49 | setProtocol KEYWORD2 50 | setRepeatTransmit KEYWORD2 51 | ########## 52 | #OTHERS End 53 | ########## 54 | 55 | ####################################### 56 | # Constants (LITERAL1) 57 | ####################################### 58 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rc-switch", 3 | "description": "Use your Arduino or Raspberry Pi to operate remote radio controlled devices", 4 | "keywords": "rf, radio, wireless", 5 | "authors": 6 | { 7 | "name": "Suat Ozgur" 8 | }, 9 | "repository": 10 | { 11 | "type": "git", 12 | "url": "https://github.com/sui77/rc-switch.git" 13 | }, 14 | "version": "2.6.2", 15 | "frameworks": [ 16 | "arduino", 17 | "energia", 18 | "wiringpi" 19 | ], 20 | "platforms": "*" 21 | } 22 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=rc-switch 2 | version=2.6.2 3 | author=sui77 4 | maintainer=sui77,fingolfin 5 | sentence=Operate 433/315Mhz devices. 6 | paragraph=Use your Arduino, ESP8266/ESP32 or Raspberry Pi to operate remote radio controlled devices. This will most likely work with all popular low cost power outlet sockets. 7 | category=Device Control 8 | url=https://github.com/sui77/rc-switch 9 | architectures=avr,esp8266,esp32 10 | includes=RCSwitch.h 11 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | src_dir = examples/ReceiveDemo_Advanced 13 | 14 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 15 | ; ENVIRONMENT CHOICE ; 16 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 17 | ;Uncomment the env line corresponding to your board and modules required, ; 18 | ;you can also adapt the modules by removing the corresponding lines in the env detail ; 19 | ; if you go to the build flag section of your env you will see that some user_config.h ; 20 | ; parameters can be overwritten here, for example the gateway name. ; 21 | ; If you want to avoid the lost of your environments at each update you can put them ; 22 | ; into a separate file called prod_env.ini, it will be automatically read by pio ; 23 | ; an example (prod_env.ini.example) is available into the same folder as this file. ; 24 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 25 | 26 | ;default_envs = sonoff-basic-rfr3 27 | ;default_envs = esp32dev-rf 28 | ;default_envs = nodemcuv2-rf 29 | ;default_envs = rf-wifi-gateway 30 | ;default_envs = uno-rf 31 | 32 | 33 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 34 | ; ENVIRONMENTS PARAMETERS ; 35 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 36 | ;Libraries and parameters shared accross environements ; 37 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 38 | 39 | 40 | 41 | [env] 42 | lib_extra_dirs = . 43 | framework = arduino 44 | build_flags = 45 | -w ; supress all warnings 46 | monitor_speed = 115200 47 | 48 | [com] 49 | esp8266_platform = espressif8266@3.2.0 50 | esp32_platform = espressif32@3.3.1 51 | atmelavr_platform = atmelavr@3.3.0 52 | 53 | [com-esp] 54 | build_flags = 55 | ${env.build_flags} 56 | 57 | [com-arduino] 58 | build_flags = 59 | ${env.build_flags} 60 | 61 | [com-arduino-low-memory] 62 | build_flags = 63 | ${env.build_flags} 64 | 65 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 66 | ; ENVIRONMENTS LIST ; 67 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 68 | ;List of environments that can be build ; 69 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 70 | 71 | [env:esp32dev-rf] 72 | platform = ${com.esp32_platform} 73 | board = esp32dev 74 | build_flags = 75 | ${com-esp.build_flags} 76 | 77 | [env:nodemcuv2-rf] 78 | platform = ${com.esp8266_platform} 79 | board = nodemcuv2 80 | build_flags = 81 | ${com-esp.build_flags} 82 | board_build.flash_mode = dout 83 | 84 | [env:rf-wifi-gateway] 85 | platform = ${com.esp8266_platform} 86 | board = nodemcuv2 87 | build_flags = 88 | ${com-esp.build_flags} 89 | board_build.flash_mode = dout 90 | 91 | 92 | [env:sonoff-basic-rfr3] 93 | platform = ${com.esp8266_platform} 94 | board = esp8285 95 | build_flags = 96 | ${com-esp.build_flags} 97 | board_build.flash_mode = dout 98 | 99 | [env:uno-rf] 100 | platform = ${com.atmelavr_platform} 101 | board = uno 102 | build_flags = 103 | ${com-arduino-low-memory.build_flags} 104 | -------------------------------------------------------------------------------- /src/RCSwitch.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | RCSwitch - Arduino libary for remote control outlet switches 3 | Copyright (c) 2011 Suat Özgür. All right reserved. 4 | 5 | Contributors: 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 / .(at)gmail(dot)com 11 | - Andreas Steinel / A.(at)gmail(dot)com 12 | - Max Horn / max(at)quendi(dot)de 13 | - Robert ter Vehn / .(at)gmail(dot)com 14 | - Johann Richard / .(at)gmail(dot)com 15 | - Vlad Gheorghe / .(at)gmail(dot)com https://github.com/vgheo 16 | 17 | Project home: https://github.com/sui77/rc-switch/ 18 | 19 | This library is free software; you can redistribute it and/or 20 | modify it under the terms of the GNU Lesser General Public 21 | License as published by the Free Software Foundation; either 22 | version 2.1 of the License, or (at your option) any later version. 23 | 24 | This library is distributed in the hope that it will be useful, 25 | but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 27 | Lesser General Public License for more details. 28 | 29 | You should have received a copy of the GNU Lesser General Public 30 | License along with this library; if not, write to the Free Software 31 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 32 | */ 33 | 34 | #define KeeLoq_NLF 0x3A5C742EUL 35 | 36 | #include "RCSwitch.h" 37 | 38 | #ifdef RaspberryPi 39 | // PROGMEM and _P functions are for AVR based microprocessors, 40 | // so we must normalize these for the ARM processor: 41 | #define PROGMEM 42 | #define memcpy_P(dest, src, num) memcpy((dest), (src), (num)) 43 | #endif 44 | 45 | #if defined(ESP8266) 46 | // interrupt handler and related code must be in RAM on ESP8266, 47 | // according to issue #46. 48 | #define RECEIVE_ATTR ICACHE_RAM_ATTR 49 | #define VAR_ISR_ATTR 50 | #elif defined(ESP32) 51 | #define RECEIVE_ATTR IRAM_ATTR 52 | #define VAR_ISR_ATTR DRAM_ATTR 53 | #else 54 | #define RECEIVE_ATTR 55 | #define VAR_ISR_ATTR 56 | #endif 57 | 58 | 59 | /* Protocol description format 60 | * 61 | * { 62 | * Pulse length, 63 | * 64 | * PreambleFactor, 65 | * Preamble {high,low}, 66 | * 67 | * HeaderFactor, 68 | * Header {high,low}, 69 | * 70 | * "0" bit {high,low}, 71 | * "1" bit {high,low}, 72 | * 73 | * Inverted Signal, 74 | * Guard time 75 | * } 76 | * 77 | * Pulse length: pulse duration (Te) in microseconds, 78 | * for example 350 79 | * PreambleFactor: Number of high and low states to send 80 | * (One pulse = 2 states, in orther words, number of pulses is 81 | * ceil(PreambleFactor/2).) 82 | * Preamble: Pulse shape which defines a preamble bit. 83 | * Sent ceil(PreambleFactor/2) times. 84 | * For example, {1, 2} with factor 3 would send 85 | * _ _ 86 | * | |__| |__ (each horizontal bar has a duration of Te, 87 | * vertical bars are ignored) 88 | * HeaderFactor: Number of times to send the header pulse. 89 | * Header: Pulse shape which defines a header (or "sync"/"clock") pulse. 90 | * {1, 31} means one pulse of duration 1 Te high and 31 Te low 91 | * _ 92 | * | |_______________________________ (don't count the vertical bars) 93 | * 94 | * "0" bit: pulse shape defining a data bit, which is a logical "0" 95 | * {1, 3} means 1 pulse duration Te high level and 3 low 96 | * _ 97 | * | |___ 98 | * 99 | * "1" bit: pulse shape that defines the data bit, which is a logical "1" 100 | * {3, 1} means 3 pulses with a duration of Te high level and 1 low 101 | * ___ 102 | * | |_ 103 | * 104 | * (note: to form the state bit Z (Tri-State bit), two codes are combined) 105 | * 106 | * Inverted Signal: Signal inversion - if true the signal is inverted 107 | * replacing high to low in a transmitted / received packet 108 | * Guard time: Separation time between two retries. It will be followed by the 109 | * next preamble of the next packet. In number of Te. 110 | * e.g. 39 pulses of duration Te low level 111 | */ 112 | #if defined(ESP8266) || defined(ESP32) 113 | static const VAR_ISR_ATTR RCSwitch::Protocol proto[] = { 114 | #else 115 | static const RCSwitch::Protocol PROGMEM proto[] = { 116 | #endif 117 | { 350, 0, { 0, 0 }, 1, { 1, 31 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 01 (Princeton, PT-2240) 118 | { 650, 0, { 0, 0 }, 1, { 1, 10 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 02 119 | { 100, 0, { 0, 0 }, 1, { 30, 71 }, { 4, 11 }, { 9, 6 }, false, 0 }, // 03 120 | { 380, 0, { 0, 0 }, 1, { 1, 6 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 04 121 | { 500, 0, { 0, 0 }, 1, { 6, 14 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 05 122 | { 450, 0, { 0, 0 }, 1, { 23, 1 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 06 (HT6P20B) 123 | { 150, 0, { 0, 0 }, 1, { 2, 62 }, { 1, 6 }, { 6, 1 }, false, 0 }, // 07 (HS2303-PT, i. e. used in AUKEY Remote) 124 | { 320, 0, { 0, 0 }, 1, { 36, 1 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 08 (Came 12bit, HT12E) 125 | { 700, 0, { 0, 0 }, 1, { 32, 1 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 09 (Nice_Flo 12bit) 126 | { 420, 0, { 0, 0 }, 1, { 60, 6 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 10 (V2 phoenix) 127 | { 500, 2, { 3, 3 }, 0, { 0, 0 }, { 1, 2 }, { 2, 1 }, false, 37 }, // 11 (Nice_FloR-S 52bit) 128 | { 400, 23, { 1, 1 }, 1, { 0, 9 }, { 2, 1 }, { 1, 2 }, false, 39 }, // 12 (Keeloq 64/66) 129 | { 300, 6, { 2, 2 }, 3, { 8, 3 }, { 2, 2 }, { 3, 3 }, false, 0 }, // 13 test (CFM) 130 | { 250, 12, { 4, 4 }, 0, { 0, 0 }, { 1, 1 }, { 2, 2 }, false, 0 }, // 14 test (StarLine) 131 | { 500, 0, { 0, 0 }, 0, { 100, 1 }, { 1, 2 }, { 2, 1 }, false, 35 }, // 15 132 | 133 | { 361, 0, { 0, 0 }, 1, { 52, 1 }, { 1, 3 }, { 3, 1 }, true, 0 }, // 16 (Einhell) 134 | { 500, 0, { 0, 0 }, 1, { 1, 23 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 17 (InterTechno PAR-1000) 135 | { 180, 0, { 0, 0 }, 1, { 1, 15 }, { 1, 1 }, { 1, 8 }, false, 0 }, // 18 (Intertechno ITT-1500) 136 | { 350, 0, { 0, 0 }, 1, { 1, 2 }, { 0, 2 }, { 3, 2 }, false, 0 }, // 19 (Murcury) 137 | { 150, 0, { 0, 0 }, 1, { 34, 3 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 20 (AC114) 138 | { 360, 0, { 0, 0 }, 1, { 13, 4 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 21 (DC250) 139 | { 650, 0, { 0, 0 }, 1, { 1, 10 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 22 (Mandolyn/Lidl TR-502MSV/RC-402/RC-402DX) 140 | { 641, 0, { 0, 0 }, 1, { 115, 1 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 23 (Lidl TR-502MSV/RC-402 - Flavien) 141 | { 620, 0, { 0, 0 }, 1, { 0, 64 }, { 0, 1 }, { 1, 0 }, false, 0 }, // 24 (Lidl TR-502MSV/RC701) 142 | { 560, 0, { 0, 0 }, 1, { 16, 8 }, { 1, 1 }, { 1, 3 }, false, 0 }, // 25 (NEC) 143 | { 385, 0, { 0, 0 }, 1, { 1, 17 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 26 (Arlec RC210) 144 | { 188, 0, { 0, 0 }, 1, { 1, 31 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 27 (Zap, FHT-7901) 145 | 146 | { 700, 1, { 0, 1 }, 1, { 116, 0 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 28 (Quigg GT-7000) from @Tho85 https://github.com/sui77/rc-switch/pull/115 147 | { 220, 0, { 0, 0 }, 1, { 1, 46 }, { 1, 6 }, { 1, 1 }, false, 2 }, // 29 (NEXA) 148 | { 260, 0, { 0, 0 }, 1, { 1, 8 }, { 1, 4 }, { 4, 1 }, true, 0 }, // 30 (Anima) 149 | 150 | { 400, 0, { 0, 0 }, 1, { 1, 1 }, { 1, 2 }, { 2, 1 }, false, 43 }, // 31 (Mertik Maxitrol G6R-H4T1) 151 | { 365, 0, { 0, 0 }, 1, { 18, 1 }, { 3, 1 }, { 1, 3 }, true, 0 }, // 32 (1ByOne Doorbell) from @Fatbeard https://github.com/sui77/rc-switch/pull/277 152 | { 340, 0, { 0, 0 }, 1, { 14, 4 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 33 (Dooya Control DC2708L) 153 | { 120, 0, { 0, 0 }, 1, { 1, 28 }, { 1, 3 }, { 3, 1 }, false, 0 } // 34 DIGOO SD10 - so as to use this protocol RCSWITCH_SEPARATION_LIMIT must be set to 2600 154 | }; 155 | 156 | enum { 157 | numProto = sizeof(proto) / sizeof(proto[0]) 158 | }; 159 | 160 | #if not defined( RCSwitchDisableReceiving ) 161 | volatile unsigned long long RCSwitch::nReceivedValue = 0; 162 | volatile unsigned int RCSwitch::nReceivedBitlength = 0; 163 | volatile unsigned int RCSwitch::nReceivedDelay = 0; 164 | volatile unsigned int RCSwitch::nReceivedProtocol = 0; 165 | int RCSwitch::nReceiveTolerance = 60; 166 | const unsigned int VAR_ISR_ATTR RCSwitch::nSeparationLimit = RCSWITCH_SEPARATION_LIMIT; 167 | unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES]; 168 | unsigned int RCSwitch::buftimings[4]; 169 | #endif 170 | 171 | RCSwitch::RCSwitch() { 172 | this->nTransmitterPin = -1; 173 | this->setRepeatTransmit(5); 174 | this->setProtocol(1); 175 | #if not defined( RCSwitchDisableReceiving ) 176 | this->nReceiverInterrupt = -1; 177 | this->setReceiveTolerance(60); 178 | RCSwitch::nReceivedValue = 0; 179 | #endif 180 | } 181 | 182 | /** 183 | * Sets the protocol to send. 184 | */ 185 | void RCSwitch::setProtocol(Protocol protocol) { 186 | this->protocol = protocol; 187 | } 188 | 189 | /** 190 | * Sets the protocol to send, from a list of predefined protocols 191 | */ 192 | void RCSwitch::setProtocol(int nProtocol) { 193 | if (nProtocol < 1 || nProtocol > numProto) { 194 | nProtocol = 1; // TODO: trigger an error, e.g. "bad protocol" ??? 195 | } 196 | #if defined(ESP8266) || defined(ESP32) 197 | this->protocol = proto[nProtocol-1]; 198 | #else 199 | memcpy_P(&this->protocol, &proto[nProtocol-1], sizeof(Protocol)); 200 | #endif 201 | } 202 | 203 | /** 204 | * Sets the protocol to send with pulse length in microseconds. 205 | */ 206 | void RCSwitch::setProtocol(int nProtocol, int nPulseLength) { 207 | setProtocol(nProtocol); 208 | this->setPulseLength(nPulseLength); 209 | } 210 | 211 | /** 212 | * Get the number of supported protocols (maximum index) 213 | */ 214 | const int RCSwitch::getNumProtocols(void) { 215 | return numProto; 216 | } 217 | 218 | /** 219 | * Sets pulse length in microseconds 220 | */ 221 | void RCSwitch::setPulseLength(int nPulseLength) { 222 | this->protocol.pulseLength = nPulseLength; 223 | } 224 | 225 | /** 226 | * Sets Repeat Transmits 227 | */ 228 | void RCSwitch::setRepeatTransmit(int nRepeatTransmit) { 229 | this->nRepeatTransmit = nRepeatTransmit; 230 | } 231 | 232 | /** 233 | * Set Receiving Tolerance 234 | */ 235 | #if not defined( RCSwitchDisableReceiving ) 236 | void RCSwitch::setReceiveTolerance(int nPercent) { 237 | RCSwitch::nReceiveTolerance = nPercent; 238 | } 239 | #endif 240 | 241 | 242 | /** 243 | * Enable transmissions 244 | * 245 | * @param nTransmitterPin Arduino Pin to which the sender is connected to 246 | */ 247 | void RCSwitch::enableTransmit(int nTransmitterPin) { 248 | this->nTransmitterPin = nTransmitterPin; 249 | pinMode(this->nTransmitterPin, OUTPUT); 250 | } 251 | 252 | /** 253 | * Disable transmissions 254 | */ 255 | void RCSwitch::disableTransmit() { 256 | this->nTransmitterPin = -1; 257 | } 258 | 259 | /** 260 | * Switch a remote switch on (Type D REV) 261 | * 262 | * @param sGroup Code of the switch group (A,B,C,D) 263 | * @param nDevice Number of the switch itself (1..3) 264 | */ 265 | void RCSwitch::switchOn(char sGroup, int nDevice) { 266 | this->sendTriState( this->getCodeWordD(sGroup, nDevice, true) ); 267 | } 268 | 269 | /** 270 | * Switch a remote switch off (Type D REV) 271 | * 272 | * @param sGroup Code of the switch group (A,B,C,D) 273 | * @param nDevice Number of the switch itself (1..3) 274 | */ 275 | void RCSwitch::switchOff(char sGroup, int nDevice) { 276 | this->sendTriState( this->getCodeWordD(sGroup, nDevice, false) ); 277 | } 278 | 279 | /** 280 | * Switch a remote switch on (Type C Intertechno) 281 | * 282 | * @param sFamily Familycode (a..f) 283 | * @param nGroup Number of group (1..4) 284 | * @param nDevice Number of device (1..4) 285 | */ 286 | void RCSwitch::switchOn(char sFamily, int nGroup, int nDevice) { 287 | this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, true) ); 288 | } 289 | 290 | /** 291 | * Switch a remote switch off (Type C Intertechno) 292 | * 293 | * @param sFamily Familycode (a..f) 294 | * @param nGroup Number of group (1..4) 295 | * @param nDevice Number of device (1..4) 296 | */ 297 | void RCSwitch::switchOff(char sFamily, int nGroup, int nDevice) { 298 | this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, false) ); 299 | } 300 | 301 | /** 302 | * Switch a remote switch on (Type B with two rotary/sliding switches) 303 | * 304 | * @param nAddressCode Number of the switch group (1..4) 305 | * @param nChannelCode Number of the switch itself (1..4) 306 | */ 307 | void RCSwitch::switchOn(int nAddressCode, int nChannelCode) { 308 | this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, true) ); 309 | } 310 | 311 | /** 312 | * Switch a remote switch off (Type B with two rotary/sliding switches) 313 | * 314 | * @param nAddressCode Number of the switch group (1..4) 315 | * @param nChannelCode Number of the switch itself (1..4) 316 | */ 317 | void RCSwitch::switchOff(int nAddressCode, int nChannelCode) { 318 | this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, false) ); 319 | } 320 | 321 | /** 322 | * Deprecated, use switchOn(const char* sGroup, const char* sDevice) instead! 323 | * Switch a remote switch on (Type A with 10 pole DIP switches) 324 | * 325 | * @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") 326 | * @param nChannelCode Number of the switch itself (1..5) 327 | */ 328 | void RCSwitch::switchOn(const char* sGroup, int nChannel) { 329 | const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" }; 330 | this->switchOn(sGroup, code[nChannel]); 331 | } 332 | 333 | /** 334 | * Deprecated, use switchOff(const char* sGroup, const char* sDevice) instead! 335 | * Switch a remote switch off (Type A with 10 pole DIP switches) 336 | * 337 | * @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") 338 | * @param nChannelCode Number of the switch itself (1..5) 339 | */ 340 | void RCSwitch::switchOff(const char* sGroup, int nChannel) { 341 | const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" }; 342 | this->switchOff(sGroup, code[nChannel]); 343 | } 344 | 345 | /** 346 | * Switch a remote switch on (Type A with 10 pole DIP switches) 347 | * 348 | * @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") 349 | * @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") 350 | */ 351 | void RCSwitch::switchOn(const char* sGroup, const char* sDevice) { 352 | this->sendTriState( this->getCodeWordA(sGroup, sDevice, true) ); 353 | } 354 | 355 | /** 356 | * Switch a remote switch off (Type A with 10 pole DIP switches) 357 | * 358 | * @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") 359 | * @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") 360 | */ 361 | void RCSwitch::switchOff(const char* sGroup, const char* sDevice) { 362 | this->sendTriState( this->getCodeWordA(sGroup, sDevice, false) ); 363 | } 364 | 365 | 366 | /** 367 | * Returns a char[13], representing the code word to be send. 368 | * 369 | */ 370 | char* RCSwitch::getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus) { 371 | static char sReturn[13]; 372 | int nReturnPos = 0; 373 | 374 | for (int i = 0; i < 5; i++) { 375 | sReturn[nReturnPos++] = (sGroup[i] == '0') ? 'F' : '0'; 376 | } 377 | 378 | for (int i = 0; i < 5; i++) { 379 | sReturn[nReturnPos++] = (sDevice[i] == '0') ? 'F' : '0'; 380 | } 381 | 382 | sReturn[nReturnPos++] = bStatus ? '0' : 'F'; 383 | sReturn[nReturnPos++] = bStatus ? 'F' : '0'; 384 | 385 | sReturn[nReturnPos] = '\0'; 386 | return sReturn; 387 | } 388 | 389 | /** 390 | * Encoding for type B switches with two rotary/sliding switches. 391 | * 392 | * The code word is a tristate word and with following bit pattern: 393 | * 394 | * +-----------------------------+-----------------------------+----------+------------+ 395 | * | 4 bits address | 4 bits address | 3 bits | 1 bit | 396 | * | switch group | switch number | not used | on / off | 397 | * | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | FFF | on=F off=0 | 398 | * +-----------------------------+-----------------------------+----------+------------+ 399 | * 400 | * @param nAddressCode Number of the switch group (1..4) 401 | * @param nChannelCode Number of the switch itself (1..4) 402 | * @param bStatus Whether to switch on (true) or off (false) 403 | * 404 | * @return char[13], representing a tristate code word of length 12 405 | */ 406 | char* RCSwitch::getCodeWordB(int nAddressCode, int nChannelCode, bool bStatus) { 407 | static char sReturn[13]; 408 | int nReturnPos = 0; 409 | 410 | if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) { 411 | return 0; 412 | } 413 | 414 | for (int i = 1; i <= 4; i++) { 415 | sReturn[nReturnPos++] = (nAddressCode == i) ? '0' : 'F'; 416 | } 417 | 418 | for (int i = 1; i <= 4; i++) { 419 | sReturn[nReturnPos++] = (nChannelCode == i) ? '0' : 'F'; 420 | } 421 | 422 | sReturn[nReturnPos++] = 'F'; 423 | sReturn[nReturnPos++] = 'F'; 424 | sReturn[nReturnPos++] = 'F'; 425 | 426 | sReturn[nReturnPos++] = bStatus ? 'F' : '0'; 427 | 428 | sReturn[nReturnPos] = '\0'; 429 | return sReturn; 430 | } 431 | 432 | /** 433 | * Like getCodeWord (Type C = Intertechno) 434 | */ 435 | char* RCSwitch::getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus) { 436 | static char sReturn[13]; 437 | int nReturnPos = 0; 438 | 439 | int nFamily = (int)sFamily - 'a'; 440 | if ( nFamily < 0 || nFamily > 15 || nGroup < 1 || nGroup > 4 || nDevice < 1 || nDevice > 4) { 441 | return 0; 442 | } 443 | 444 | // encode the family into four bits 445 | sReturn[nReturnPos++] = (nFamily & 1) ? 'F' : '0'; 446 | sReturn[nReturnPos++] = (nFamily & 2) ? 'F' : '0'; 447 | sReturn[nReturnPos++] = (nFamily & 4) ? 'F' : '0'; 448 | sReturn[nReturnPos++] = (nFamily & 8) ? 'F' : '0'; 449 | 450 | // encode the device and group 451 | sReturn[nReturnPos++] = ((nDevice-1) & 1) ? 'F' : '0'; 452 | sReturn[nReturnPos++] = ((nDevice-1) & 2) ? 'F' : '0'; 453 | sReturn[nReturnPos++] = ((nGroup-1) & 1) ? 'F' : '0'; 454 | sReturn[nReturnPos++] = ((nGroup-1) & 2) ? 'F' : '0'; 455 | 456 | // encode the status code 457 | sReturn[nReturnPos++] = '0'; 458 | sReturn[nReturnPos++] = 'F'; 459 | sReturn[nReturnPos++] = 'F'; 460 | sReturn[nReturnPos++] = bStatus ? 'F' : '0'; 461 | 462 | sReturn[nReturnPos] = '\0'; 463 | return sReturn; 464 | } 465 | 466 | /** 467 | * Encoding for the REV Switch Type 468 | * 469 | * The code word is a tristate word and with following bit pattern: 470 | * 471 | * +-----------------------------+-------------------+----------+--------------+ 472 | * | 4 bits address | 3 bits address | 3 bits | 2 bits | 473 | * | switch group | device number | not used | on / off | 474 | * | A=1FFF B=F1FF C=FF1F D=FFF1 | 1=0FF 2=F0F 3=FF0 | 000 | on=10 off=01 | 475 | * +-----------------------------+-------------------+----------+--------------+ 476 | * 477 | * Source: http://www.the-intruder.net/funksteckdosen-von-rev-uber-arduino-ansteuern/ 478 | * 479 | * @param sGroup Name of the switch group (A..D, resp. a..d) 480 | * @param nDevice Number of the switch itself (1..3) 481 | * @param bStatus Whether to switch on (true) or off (false) 482 | * 483 | * @return char[13], representing a tristate code word of length 12 484 | */ 485 | char* RCSwitch::getCodeWordD(char sGroup, int nDevice, bool bStatus) { 486 | static char sReturn[13]; 487 | int nReturnPos = 0; 488 | 489 | // sGroup must be one of the letters in "abcdABCD" 490 | int nGroup = (sGroup >= 'a') ? (int)sGroup - 'a' : (int)sGroup - 'A'; 491 | if ( nGroup < 0 || nGroup > 3 || nDevice < 1 || nDevice > 3) { 492 | return 0; 493 | } 494 | 495 | for (int i = 0; i < 4; i++) { 496 | sReturn[nReturnPos++] = (nGroup == i) ? '1' : 'F'; 497 | } 498 | 499 | for (int i = 1; i <= 3; i++) { 500 | sReturn[nReturnPos++] = (nDevice == i) ? '1' : 'F'; 501 | } 502 | 503 | sReturn[nReturnPos++] = '0'; 504 | sReturn[nReturnPos++] = '0'; 505 | sReturn[nReturnPos++] = '0'; 506 | 507 | sReturn[nReturnPos++] = bStatus ? '1' : '0'; 508 | sReturn[nReturnPos++] = bStatus ? '0' : '1'; 509 | 510 | sReturn[nReturnPos] = '\0'; 511 | return sReturn; 512 | } 513 | 514 | /** 515 | * @param sCodeWord a tristate code word consisting of the letter 0, 1, F 516 | */ 517 | void RCSwitch::sendTriState(const char* sCodeWord) { 518 | // turn the tristate code word into the corresponding bit pattern, then send it 519 | unsigned long long code = 0; 520 | unsigned int length = 0; 521 | for (const char* p = sCodeWord; *p; p++) { 522 | code <<= 2L; 523 | switch (*p) { 524 | case '0': 525 | // bit pattern 00 526 | break; 527 | case 'F': 528 | // bit pattern 01 529 | code |= 1ULL; 530 | break; 531 | case '1': 532 | // bit pattern 11 533 | code |= 3ULL; 534 | break; 535 | } 536 | length += 2; 537 | } 538 | this->send(code, length); 539 | } 540 | 541 | /** 542 | * @param duration no. of microseconds to delay 543 | */ 544 | static inline void safeDelayMicroseconds(unsigned long duration) { 545 | #if defined(ESP8266) || defined(ESP32) 546 | if (duration > 10000) { 547 | // if delay > 10 milliseconds, use yield() to avoid wdt reset 548 | unsigned long start = micros(); 549 | while ((micros() - start) < duration) { 550 | yield(); 551 | } 552 | } 553 | else { 554 | delayMicroseconds(duration); 555 | } 556 | #else 557 | delayMicroseconds(duration); 558 | #endif 559 | } 560 | 561 | /** 562 | * @param sCodeWord a binary code word consisting of the letter 0, 1 563 | */ 564 | void RCSwitch::send(const char* sCodeWord) { 565 | // turn the tristate code word into the corresponding bit pattern, then send it 566 | unsigned long long code = 0; 567 | unsigned int length = 0; 568 | for (const char* p = sCodeWord; *p; p++) { 569 | code <<= 1ULL; 570 | if (*p != '0') 571 | code |= 1ULL; 572 | length++; 573 | } 574 | this->send(code, length); 575 | } 576 | 577 | /** 578 | * Transmit the first 'length' bits of the integer 'code'. The 579 | * bits are sent from MSB to LSB, i.e., first the bit at position length-1, 580 | * then the bit at position length-2, and so on, till finally the bit at position 0. 581 | */ 582 | void RCSwitch::send(unsigned long long code, unsigned int length) { 583 | if (this->nTransmitterPin == -1) 584 | return; 585 | 586 | #if not defined( RCSwitchDisableReceiving ) 587 | // make sure the receiver is disabled while we transmit 588 | int nReceiverInterrupt_backup = nReceiverInterrupt; 589 | if (nReceiverInterrupt_backup != -1) { 590 | this->disableReceive(); 591 | } 592 | #endif 593 | 594 | // repeat sending the packet nRepeatTransmit times 595 | for (int nRepeat = 0; nRepeat < nRepeatTransmit; nRepeat++) { 596 | // send the preamble 597 | for (int i = 0; i < ((protocol.PreambleFactor / 2) + (protocol.PreambleFactor %2 )); i++) { 598 | this->transmit({protocol.Preamble.high, protocol.Preamble.low}); 599 | } 600 | // send the header 601 | if (protocol.HeaderFactor > 0) { 602 | for (int i = 0; i < protocol.HeaderFactor; i++) { 603 | this->transmit(protocol.Header); 604 | } 605 | } 606 | // send the code 607 | for (int i = length - 1; i >= 0; i--) { 608 | if (code & (1ULL << i)) 609 | this->transmit(protocol.one); 610 | else 611 | this->transmit(protocol.zero); 612 | } 613 | // for kilok, there should be a duration of 66, and 64 significant data codes are stored 614 | // send two more bits for even count 615 | if (length == 64) { 616 | if (nRepeat == 0) { 617 | this->transmit(protocol.zero); 618 | this->transmit(protocol.zero); 619 | } else { 620 | this->transmit(protocol.one); 621 | this->transmit(protocol.one); 622 | } 623 | } 624 | // Set the guard Time 625 | if (protocol.Guard > 0) { 626 | digitalWrite(this->nTransmitterPin, LOW); 627 | safeDelayMicroseconds(this->protocol.pulseLength * protocol.Guard); 628 | } 629 | } 630 | 631 | // Disable transmit after sending (i.e., for inverted protocols) 632 | digitalWrite(this->nTransmitterPin, LOW); 633 | 634 | #if not defined( RCSwitchDisableReceiving ) 635 | // enable receiver again if we just disabled it 636 | if (nReceiverInterrupt_backup != -1) { 637 | this->enableReceive(nReceiverInterrupt_backup); 638 | } 639 | #endif 640 | } 641 | 642 | /** 643 | * Transmit a single high-low pulse. 644 | */ 645 | void RCSwitch::transmit(HighLow pulses) { 646 | uint8_t firstLogicLevel = (this->protocol.invertedSignal) ? LOW : HIGH; 647 | uint8_t secondLogicLevel = (this->protocol.invertedSignal) ? HIGH : LOW; 648 | 649 | if (pulses.high > 0) { 650 | digitalWrite(this->nTransmitterPin, firstLogicLevel); 651 | delayMicroseconds( this->protocol.pulseLength * pulses.high); 652 | } 653 | if (pulses.low > 0) { 654 | digitalWrite(this->nTransmitterPin, secondLogicLevel); 655 | delayMicroseconds( this->protocol.pulseLength * pulses.low); 656 | } 657 | } 658 | 659 | 660 | #if not defined( RCSwitchDisableReceiving ) 661 | /** 662 | * Enable receiving data 663 | */ 664 | void RCSwitch::enableReceive(int interrupt) { 665 | this->nReceiverInterrupt = interrupt; 666 | this->enableReceive(); 667 | } 668 | 669 | void RCSwitch::enableReceive() { 670 | if (this->nReceiverInterrupt != -1) { 671 | RCSwitch::nReceivedValue = 0; 672 | RCSwitch::nReceivedBitlength = 0; 673 | #if defined(RaspberryPi) // Raspberry Pi 674 | wiringPiISR(this->nReceiverInterrupt, INT_EDGE_BOTH, &handleInterrupt); 675 | #else // Arduino 676 | attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE); 677 | #endif 678 | } 679 | } 680 | 681 | /** 682 | * Disable receiving data 683 | */ 684 | void RCSwitch::disableReceive() { 685 | #if not defined(RaspberryPi) // Arduino 686 | if (this->nReceiverInterrupt != -1) { 687 | detachInterrupt(this->nReceiverInterrupt); 688 | } 689 | #endif // For Raspberry Pi (wiringPi) you can't unregister the ISR 690 | this->nReceiverInterrupt = -1; 691 | } 692 | 693 | bool RCSwitch::available() { 694 | return RCSwitch::nReceivedValue != 0; 695 | } 696 | 697 | void RCSwitch::resetAvailable() { 698 | RCSwitch::nReceivedValue = 0; 699 | } 700 | 701 | unsigned long long RCSwitch::getReceivedValue() { 702 | return RCSwitch::nReceivedValue; 703 | } 704 | 705 | unsigned int RCSwitch::getReceivedBitlength() { 706 | return RCSwitch::nReceivedBitlength; 707 | } 708 | 709 | unsigned int RCSwitch::getReceivedDelay() { 710 | return RCSwitch::nReceivedDelay; 711 | } 712 | 713 | unsigned int RCSwitch::getReceivedProtocol() { 714 | return RCSwitch::nReceivedProtocol; 715 | } 716 | 717 | unsigned int* RCSwitch::getReceivedRawdata() { 718 | return RCSwitch::timings; 719 | } 720 | 721 | /* helper function for the receiveProtocol method */ 722 | static inline unsigned int diff(int A, int B) { 723 | return abs(A - B); 724 | } 725 | 726 | /** 727 | * 728 | */ 729 | bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCount) { 730 | #if defined(ESP8266) || defined(ESP32) 731 | const Protocol &pro = proto[p-1]; 732 | #else 733 | Protocol pro; 734 | memcpy_P(&pro, &proto[p-1], sizeof(Protocol)); 735 | #endif 736 | 737 | unsigned long long code = 0; 738 | unsigned int FirstTiming = 0; 739 | if (pro.PreambleFactor > 0) { 740 | FirstTiming = pro.PreambleFactor + 1; 741 | } 742 | unsigned int BeginData = 0; 743 | if (pro.HeaderFactor > 0) { 744 | BeginData = (pro.invertedSignal) ? (2) : (1); 745 | // Header pulse count correction for more than one 746 | if (pro.HeaderFactor > 1) { 747 | BeginData += (pro.HeaderFactor - 1) * 2; 748 | } 749 | } 750 | //Assuming the longer pulse length is the pulse captured in timings[FirstTiming] 751 | // берем наибольшее значение из Header 752 | const unsigned int syncLengthInPulses = ((pro.Header.low) > (pro.Header.high)) ? (pro.Header.low) : (pro.Header.high); 753 | // определяем длительность Te как длительность первого импульса header деленную на количество импульсов в нем 754 | // или как длительность импульса preamble деленную на количество Te в нем 755 | unsigned int sdelay = 0; 756 | if (syncLengthInPulses > 0) { 757 | sdelay = RCSwitch::timings[FirstTiming] / syncLengthInPulses; 758 | } else { 759 | sdelay = RCSwitch::timings[FirstTiming-2] / pro.PreambleFactor; 760 | } 761 | const unsigned int delay = sdelay; 762 | // nReceiveTolerance = 60 763 | // допустимое отклонение длительностей импульсов на 60 % 764 | const unsigned int delayTolerance = delay * RCSwitch::nReceiveTolerance / 100; 765 | 766 | // 0 - sync перед preamble или data 767 | // BeginData - сдвиг на 1 или 2 от sync к preamble/data 768 | // FirstTiming - сдвиг на preamble к header 769 | // firstDataTiming первый импульс data 770 | // bitChangeCount - количество импульсов в data 771 | 772 | /* For protocols that start low, the sync period looks like 773 | * _________ 774 | * _____________| |XXXXXXXXXXXX| 775 | * 776 | * |--1st dur--|-2nd dur-|-Start data-| 777 | * 778 | * The 3rd saved duration starts the data. 779 | * 780 | * For protocols that start high, the sync period looks like 781 | * 782 | * ______________ 783 | * | |____________|XXXXXXXXXXXXX| 784 | * 785 | * |-filtered out-|--1st dur--|--Start data--| 786 | * 787 | * The 2nd saved duration starts the data 788 | */ 789 | // если invertedSignal=false, то сигнал начинается с 1 элемента массива (высокий уровень) 790 | // если invertedSignal=true, то сигнал начинается со 2 элемента массива (низкий уровень) 791 | // добавляем поправку на Преамбулу и Хедер 792 | const unsigned int firstDataTiming = BeginData + FirstTiming; 793 | unsigned int bitChangeCount = changeCount - firstDataTiming - 1 + pro.invertedSignal; 794 | if (bitChangeCount > 128) { 795 | bitChangeCount = 128; 796 | } 797 | 798 | for (unsigned int i = firstDataTiming; i < firstDataTiming + bitChangeCount; i += 2) { 799 | code <<= 1; 800 | if (diff(RCSwitch::timings[i], delay * pro.zero.high) < delayTolerance && 801 | diff(RCSwitch::timings[i + 1], delay * pro.zero.low) < delayTolerance) { 802 | // zero 803 | } else if (diff(RCSwitch::timings[i], delay * pro.one.high) < delayTolerance && 804 | diff(RCSwitch::timings[i + 1], delay * pro.one.low) < delayTolerance) { 805 | // one 806 | code |= 1; 807 | } else { 808 | // Failed 809 | return false; 810 | } 811 | } 812 | 813 | if (bitChangeCount > 14) { // ignore very short transmissions: no device sends them, so this must be noise 814 | RCSwitch::nReceivedValue = code; 815 | RCSwitch::nReceivedBitlength = bitChangeCount / 2; 816 | RCSwitch::nReceivedDelay = delay; 817 | RCSwitch::nReceivedProtocol = p; 818 | return true; 819 | } 820 | 821 | return false; 822 | } 823 | 824 | void RECEIVE_ATTR RCSwitch::handleInterrupt() { 825 | 826 | static unsigned int changeCount = 0; 827 | static unsigned long lastTime = 0; 828 | static byte repeatCount = 0; 829 | 830 | const long time = micros(); 831 | const unsigned int duration = time - lastTime; 832 | 833 | RCSwitch::buftimings[3]=RCSwitch::buftimings[2]; 834 | RCSwitch::buftimings[2]=RCSwitch::buftimings[1]; 835 | RCSwitch::buftimings[1]=RCSwitch::buftimings[0]; 836 | RCSwitch::buftimings[0]=duration; 837 | 838 | if (duration > RCSwitch::nSeparationLimit || 839 | changeCount == 156 || 840 | (diff(RCSwitch::buftimings[3], RCSwitch::buftimings[2]) < 50 && 841 | diff(RCSwitch::buftimings[2], RCSwitch::buftimings[1]) < 50 && 842 | changeCount > 25)) { 843 | // принят длинный импульс продолжительностью более nSeparationLimit (4300) 844 | // A long stretch without signal level change occurred. This could 845 | // be the gap between two transmission. 846 | if (diff(duration, RCSwitch::timings[0]) < 400 || 847 | changeCount == 156 || 848 | (diff(RCSwitch::buftimings[3], RCSwitch::timings[1]) < 50 && 849 | diff(RCSwitch::buftimings[2], RCSwitch::timings[2]) < 50 && 850 | diff(RCSwitch::buftimings[1], RCSwitch::timings[3]) < 50 && 851 | changeCount > 25)) { 852 | // если его длительность отличается от первого импульса, 853 | // который приняли раньше, менее чем на +-200 (исходно 200) 854 | // то считаем это повторным пакетом и игнорируем его 855 | // This long signal is close in length to the long signal which 856 | // started the previously recorded timings; this suggests that 857 | // it may indeed by a a gap between two transmissions (we assume 858 | // here that a sender will send the signal multiple times, 859 | // with roughly the same gap between them). 860 | 861 | // количество повторных пакетов 862 | repeatCount++; 863 | // при приеме второго повторного начинаем анализ принятого первым 864 | if (repeatCount == 1) { 865 | for(unsigned int i = 1; i <= numProto; i++) { 866 | if (receiveProtocol(i, changeCount)) { 867 | // receive succeeded for protocol i 868 | break; 869 | } 870 | } 871 | // очищаем количество повторных пакетов 872 | repeatCount = 0; 873 | } 874 | } 875 | // дительность отличается более чем на +-200 от первого 876 | // принятого ранее, очищаем счетчик для приема нового пакета 877 | changeCount = 0; 878 | if (diff(RCSwitch::buftimings[3], RCSwitch::buftimings[2]) < 50 && 879 | diff(RCSwitch::buftimings[2], RCSwitch::buftimings[1]) < 50) { 880 | RCSwitch::timings[1]=RCSwitch::buftimings[3]; 881 | RCSwitch::timings[2]=RCSwitch::buftimings[2]; 882 | RCSwitch::timings[3]=RCSwitch::buftimings[1]; 883 | changeCount = 4; 884 | } 885 | } 886 | 887 | // detect overflow 888 | if (changeCount >= RCSWITCH_MAX_CHANGES) { 889 | changeCount = 0; 890 | repeatCount = 0; 891 | } 892 | 893 | // заносим в массив длительность очередного принятого импульса 894 | if (changeCount > 0 && duration < 100) { // игнорируем шумовые всплески менее 100 мкс 895 | RCSwitch::timings[changeCount-1] += duration; 896 | } else { 897 | RCSwitch::timings[changeCount++] = duration; 898 | } 899 | lastTime = time; 900 | } 901 | #endif 902 | 903 | /** 904 | * Initilize Keeloq 905 | */ 906 | Keeloq::Keeloq() { 907 | _keyHigh = 0; 908 | _keyLow = 0; 909 | } 910 | 911 | /** 912 | * Set Keeloq 64 bit cypher key 913 | */ 914 | void Keeloq::SetKey(unsigned long keyHigh, unsigned long keyLow) { 915 | _keyHigh = keyHigh; 916 | _keyLow = keyLow; 917 | } 918 | 919 | /** 920 | * Get Key data 921 | */ 922 | unsigned long Keeloq::GetKey(bool HighLow) { 923 | if (HighLow) { 924 | return _keyHigh; 925 | } 926 | return _keyLow; 927 | } 928 | 929 | /** 930 | * Encrypt Keeloq 32 bit data 931 | */ 932 | unsigned long Keeloq::Encrypt(unsigned long data) { 933 | unsigned long x = data; 934 | unsigned long r; 935 | int keyBitNo, index; 936 | unsigned long keyBitVal,bitVal; 937 | 938 | for (r = 0; r < 528; r++) { 939 | keyBitNo = r & 63; 940 | if (keyBitNo < 32) { 941 | keyBitVal = bitRead(_keyLow,keyBitNo); 942 | } else { 943 | keyBitVal = bitRead(_keyHigh, keyBitNo - 32); 944 | } 945 | index = 1 * bitRead(x, 1) + 2 * bitRead(x, 9) + 4 * bitRead(x, 20) + 8 * bitRead(x, 26) + 16 * bitRead(x, 31); 946 | bitVal = bitRead(x, 0) ^ bitRead(x, 16) ^ bitRead(KeeLoq_NLF, index) ^ keyBitVal; 947 | x = (x >> 1) ^ bitVal << 31; 948 | } 949 | return x; 950 | } 951 | 952 | /** 953 | * Decrypt Keeloq 32 bit data 954 | */ 955 | unsigned long Keeloq::Decrypt(unsigned long data) { 956 | unsigned long x = data; 957 | unsigned long r; 958 | int keyBitNo, index; 959 | unsigned long keyBitVal,bitVal; 960 | 961 | for (r = 0; r < 528; r++) { 962 | keyBitNo = (15-r) & 63; 963 | if(keyBitNo < 32) { 964 | keyBitVal = bitRead(_keyLow,keyBitNo); 965 | } else { 966 | keyBitVal = bitRead(_keyHigh, keyBitNo - 32); 967 | } 968 | index = 1 * bitRead(x, 0) + 2 * bitRead(x, 8) + 4 * bitRead(x, 19) + 8 * bitRead(x, 25) + 16 * bitRead(x, 30); 969 | bitVal = bitRead(x, 31) ^ bitRead(x, 15) ^ bitRead(KeeLoq_NLF, index) ^ keyBitVal; 970 | x = (x << 1) ^ bitVal; 971 | } 972 | return x; 973 | } 974 | 975 | /** 976 | * Set Normal Learning Keeloq key 977 | */ 978 | void Keeloq::NormLearn(unsigned long FixSN) { 979 | unsigned long tmpFixSN; 980 | // заготовки для формируемого ключа 981 | unsigned long NewkeyHigh; 982 | unsigned long NewkeyLow; 983 | 984 | tmpFixSN = FixSN & 0x0FFFFFFF; 985 | tmpFixSN |= 0x20000000; 986 | NewkeyLow = Decrypt(tmpFixSN); 987 | tmpFixSN = FixSN & 0x0FFFFFFF; 988 | tmpFixSN |= 0x60000000; 989 | NewkeyHigh = Decrypt(tmpFixSN); 990 | _keyHigh = NewkeyHigh; 991 | _keyLow = NewkeyLow; 992 | } 993 | 994 | /** 995 | * Reflect a 32 bit package 996 | */ 997 | unsigned long Keeloq::ReflectPack(unsigned long PackSrc) { 998 | unsigned long long PackOut = 0; 999 | for (byte i = 0; i < 32; i++) { 1000 | PackOut = PackOut << 1; 1001 | if (PackSrc & 1) { 1002 | PackOut = PackOut | 1; 1003 | } 1004 | PackSrc = PackSrc >> 1; 1005 | } 1006 | return PackOut; 1007 | } 1008 | -------------------------------------------------------------------------------- /src/RCSwitch.h: -------------------------------------------------------------------------------- 1 | /* 2 | RCSwitch - Arduino libary for remote control outlet switches 3 | Copyright (c) 2011 Suat Özgür. All right reserved. 4 | 5 | Contributors: 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 / .(at)gmail(dot)com 11 | - Max Horn / max(at)quendi(dot)de 12 | - Robert ter Vehn / .(at)gmail(dot)com 13 | 14 | Project home: https://github.com/sui77/rc-switch/ 15 | 16 | This library is free software; you can redistribute it and/or 17 | modify it under the terms of the GNU Lesser General Public 18 | License as published by the Free Software Foundation; either 19 | version 2.1 of the License, or (at your option) any later version. 20 | 21 | This library is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 | Lesser General Public License for more details. 25 | 26 | You should have received a copy of the GNU Lesser General Public 27 | License along with this library; if not, write to the Free Software 28 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 29 | */ 30 | #ifndef _RCSwitch_h 31 | #define _RCSwitch_h 32 | 33 | #if defined(ARDUINO) && ARDUINO >= 100 34 | #include "Arduino.h" 35 | #elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific 36 | #include "Energia.h" 37 | #elif defined(RPI) // Raspberry Pi 38 | #define RaspberryPi 39 | 40 | // Include libraries for RPi: 41 | #include /* memcpy */ 42 | #include /* abs */ 43 | #include 44 | #elif defined(SPARK) 45 | #include "application.h" 46 | #else 47 | #include "WProgram.h" 48 | #endif 49 | 50 | #include 51 | 52 | // At least for the ATTiny X4/X5, receiving has to be disabled due to 53 | // missing libm depencies (udivmodhi4) 54 | #if defined( __AVR_ATtinyX5__ ) or defined ( __AVR_ATtinyX4__ ) 55 | #define RCSwitchDisableReceiving 56 | #endif 57 | 58 | // Number of maximum high/Low changes per packet. 59 | // We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync 60 | // Для keeloq нужно увеличить RCSWITCH_MAX_CHANGES до 23+1+66*2+1=157 61 | #define RCSWITCH_MAX_CHANGES 67 // default 67 62 | 63 | // separationLimit: minimum microseconds between received codes, closer codes are ignored. 64 | // according to discussion on issue #14 it might be more suitable to set the separation 65 | // limit to the same time as the 'low' part of the sync signal for the current protocol. 66 | // should be set to the minimum value of pulselength * the sync signal 67 | #define RCSWITCH_SEPARATION_LIMIT 4100 68 | 69 | class RCSwitch { 70 | 71 | public: 72 | RCSwitch(); 73 | 74 | void switchOn(int nGroupNumber, int nSwitchNumber); 75 | void switchOff(int nGroupNumber, int nSwitchNumber); 76 | void switchOn(const char* sGroup, int nSwitchNumber); 77 | void switchOff(const char* sGroup, int nSwitchNumber); 78 | void switchOn(char sFamily, int nGroup, int nDevice); 79 | void switchOff(char sFamily, int nGroup, int nDevice); 80 | void switchOn(const char* sGroup, const char* sDevice); 81 | void switchOff(const char* sGroup, const char* sDevice); 82 | void switchOn(char sGroup, int nDevice); 83 | void switchOff(char sGroup, int nDevice); 84 | 85 | void sendTriState(const char* sCodeWord); 86 | void send(unsigned long long code, unsigned int length); 87 | void send(const char* sCodeWord); 88 | 89 | #if not defined( RCSwitchDisableReceiving ) 90 | void enableReceive(int interrupt); 91 | void enableReceive(); 92 | void disableReceive(); 93 | bool available(); 94 | void resetAvailable(); 95 | 96 | unsigned long long getReceivedValue(); 97 | unsigned int getReceivedBitlength(); 98 | unsigned int getReceivedDelay(); 99 | unsigned int getReceivedProtocol(); 100 | unsigned int* getReceivedRawdata(); 101 | #endif 102 | 103 | void enableTransmit(int nTransmitterPin); 104 | void disableTransmit(); 105 | void setPulseLength(int nPulseLength); 106 | void setRepeatTransmit(int nRepeatTransmit); 107 | #if not defined( RCSwitchDisableReceiving ) 108 | void setReceiveTolerance(int nPercent); 109 | #endif 110 | 111 | /** 112 | * Description of a single pule, which consists of a high signal 113 | * whose duration is "high" times the base pulse length, followed 114 | * by a low signal lasting "low" times the base pulse length. 115 | * Thus, the pulse overall lasts (high+low)*pulseLength 116 | */ 117 | struct HighLow { 118 | uint8_t high; 119 | uint8_t low; 120 | }; 121 | 122 | /** 123 | * A "protocol" describes how zero and one bits are encoded into high/low 124 | * pulses. 125 | */ 126 | struct Protocol { 127 | /** base pulse length in microseconds, e.g. 350 */ 128 | uint16_t pulseLength; 129 | uint8_t PreambleFactor; 130 | HighLow Preamble; 131 | uint8_t HeaderFactor; 132 | HighLow Header; 133 | 134 | HighLow zero; 135 | HighLow one; 136 | 137 | /** 138 | * If true, interchange high and low logic levels in all transmissions. 139 | * 140 | * By default, RCSwitch assumes that any signals it sends or receives 141 | * can be broken down into pulses which start with a high signal level, 142 | * followed by a a low signal level. This is e.g. the case for the 143 | * popular PT 2260 encoder chip, and thus many switches out there. 144 | * 145 | * But some devices do it the other way around, and start with a low 146 | * signal level, followed by a high signal level, e.g. the HT6P20B. To 147 | * accommodate this, one can set invertedSignal to true, which causes 148 | * RCSwitch to change how it interprets any HighLow struct FOO: It will 149 | * then assume transmissions start with a low signal lasting 150 | * FOO.high*pulseLength microseconds, followed by a high signal lasting 151 | * FOO.low*pulseLength microseconds. 152 | */ 153 | bool invertedSignal; 154 | uint16_t Guard; 155 | }; 156 | 157 | void setProtocol(Protocol protocol); 158 | void setProtocol(int nProtocol); 159 | void setProtocol(int nProtocol, int nPulseLength); 160 | const int getNumProtocols(void); 161 | 162 | private: 163 | char* getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus); 164 | char* getCodeWordB(int nGroupNumber, int nSwitchNumber, bool bStatus); 165 | char* getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus); 166 | char* getCodeWordD(char group, int nDevice, bool bStatus); 167 | void transmit(HighLow pulses); 168 | 169 | #if not defined( RCSwitchDisableReceiving ) 170 | static void handleInterrupt(); 171 | static bool receiveProtocol(const int p, unsigned int changeCount); 172 | int nReceiverInterrupt; 173 | #endif 174 | int nTransmitterPin; 175 | int nRepeatTransmit; 176 | 177 | Protocol protocol; 178 | 179 | #if not defined( RCSwitchDisableReceiving ) 180 | static int nReceiveTolerance; 181 | volatile static unsigned long long nReceivedValue; 182 | volatile static unsigned int nReceivedBitlength; 183 | volatile static unsigned int nReceivedDelay; 184 | volatile static unsigned int nReceivedProtocol; 185 | const static unsigned int nSeparationLimit; 186 | /* 187 | * timings[0] contains sync timing, followed by a number of bits 188 | */ 189 | static unsigned int timings[RCSWITCH_MAX_CHANGES]; 190 | // буфер длительностей последних четырех пакетов, [0] - последний 191 | static unsigned int buftimings[4]; 192 | #endif 193 | 194 | 195 | }; 196 | 197 | class Keeloq { 198 | public: 199 | Keeloq(); 200 | void SetKey(unsigned long keyHigh, unsigned long keyLow); 201 | unsigned long GetKey(bool HighLow); 202 | unsigned long Encrypt(unsigned long data); 203 | unsigned long Decrypt(unsigned long data); 204 | void NormLearn(unsigned long FixSN); 205 | unsigned long ReflectPack(unsigned long PackSrc); 206 | private: 207 | unsigned long _keyHigh; 208 | unsigned long _keyLow; 209 | }; 210 | 211 | #endif 212 | --------------------------------------------------------------------------------