diff --git a/Firmware/Source/v1.2/grove_pi_v1_2_7/RCSwitch.cpp b/Firmware/Source/v1.2/grove_pi_v1_2_7/RCSwitch.cpp new file mode 100644 index 00000000..2c972001 --- /dev/null +++ b/Firmware/Source/v1.2/grove_pi_v1_2_7/RCSwitch.cpp @@ -0,0 +1,804 @@ +/* + 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 / .(at)gmail(dot)com + - Andreas Steinel / A.(at)gmail(dot)com + - Max Horn / max(at)quendi(dot)de + - Robert ter Vehn / .(at)gmail(dot)com + - Johann Richard / .(at)gmail(dot)com + - Vlad Gheorghe / .(at)gmail(dot)com https://github.com/vgheo + + 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" + +#ifdef RaspberryPi + // PROGMEM and _P functions are for AVR based microprocessors, + // so we must normalize these for the ARM processor: + #define PROGMEM + #define memcpy_P(dest, src, num) memcpy((dest), (src), (num)) +#endif + +#ifdef ESP8266 + // interrupt handler and related code must be in RAM on ESP8266, + // according to issue #46. + #define RECEIVE_ATTR ICACHE_RAM_ATTR +#else + #define RECEIVE_ATTR +#endif + + +/* Format for protocol definitions: + * {pulselength, Sync bit, "0" bit, "1" bit} + * + * pulselength: pulse length in microseconds, e.g. 350 + * Sync bit: {1, 31} means 1 high pulse and 31 low pulses + * (perceived as a 31*pulselength long pulse, total length of sync bit is + * 32*pulselength microseconds), i.e: + * _ + * | |_______________________________ (don't count the vertical bars) + * "0" bit: waveform for a data bit of value "0", {1, 3} means 1 high pulse + * and 3 low pulses, total length (1+3)*pulselength, i.e: + * _ + * | |___ + * "1" bit: waveform for a data bit of value "1", e.g. {3,1}: + * ___ + * | |_ + * + * These are combined to form Tri-State bits when sending or receiving codes. + */ +#ifdef ESP8266 +static const RCSwitch::Protocol proto[] = { +#else +static const RCSwitch::Protocol PROGMEM proto[] = { +#endif + { 350, { 1, 31 }, { 1, 3 }, { 3, 1 }, false }, // protocol 1 + { 650, { 1, 10 }, { 1, 2 }, { 2, 1 }, false }, // protocol 2 + { 100, { 30, 71 }, { 4, 11 }, { 9, 6 }, false }, // protocol 3 + { 380, { 1, 6 }, { 1, 3 }, { 3, 1 }, false }, // protocol 4 + { 500, { 6, 14 }, { 1, 2 }, { 2, 1 }, false }, // protocol 5 + { 450, { 23, 1 }, { 1, 2 }, { 2, 1 }, true } // protocol 6 (HT6P20B) +}; + +enum { + 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" ??? + } +#ifdef ESP8266 + this->protocol = proto[nProtocol-1]; +#else + memcpy_P(&this->protocol, &proto[nProtocol-1], sizeof(Protocol)); +#endif +} + +/** + * 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) ); +} + +/** + * Get code value to turn 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) + */ +unsigned long RCSwitch::switchOnCode(char sGroup, int nDevice) { + return this->getBitPattern( this->getCodeWordD(sGroup, nDevice, true) ); +} + +/** + * Get code value to turn 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) + */ +unsigned long RCSwitch::switchOffCode(char sGroup, int nDevice) { + return this->getBitPattern( 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) ); +} + +/** + * Get code value to turn 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) + */ +unsigned long RCSwitch::switchOnCode(char sFamily, int nGroup, int nDevice) { + return this->getBitPattern( this->getCodeWordC(sFamily, nGroup, nDevice, true) ); +} + +/** + * Get code value to turn 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) + */ +unsigned long RCSwitch::switchOffCode(char sFamily, int nGroup, int nDevice) { + return this->getBitPattern( 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) ); +} + +/** + * Get code value to turn 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) + */ +unsigned long RCSwitch::switchOnCode(int nAddressCode, int nChannelCode) { + this->getBitPattern( this->getCodeWordB(nAddressCode, nChannelCode, true) ); +} + +/** + * Get code value to turn 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) + */ +unsigned long RCSwitch::switchOffCode(int nAddressCode, int nChannelCode) { + this->getBitPattern( 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) ); +} + +/** + * Get code to turn 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") + */ +unsigned long RCSwitch::switchOnCode(const char* sGroup, const char* sDevice) { + this->getBitPattern( this->getCodeWordA(sGroup, sDevice, true) ); +} + +/** + * Get code to turn 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") + */ +unsigned long RCSwitch::switchOffCode(const char* sGroup, const char* sDevice) { + this->getBitPattern( this->getCodeWordA(sGroup, sDevice, false) ); +} + +/** + * Returns a char[13], representing the code word to be send. + * + */ +char* RCSwitch::getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + for (int i = 0; i < 5; i++) { + sReturn[nReturnPos++] = (sGroup[i] == '0') ? 'F' : '0'; + } + + for (int i = 0; i < 5; i++) { + sReturn[nReturnPos++] = (sDevice[i] == '0') ? 'F' : '0'; + } + + sReturn[nReturnPos++] = bStatus ? '0' : 'F'; + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Encoding for type B switches with two rotary/sliding switches. + * + * The code word is a tristate word and with following bit pattern: + * + * +-----------------------------+-----------------------------+----------+------------+ + * | 4 bits address | 4 bits address | 3 bits | 1 bit | + * | switch group | switch number | not used | on / off | + * | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | FFF | on=F off=0 | + * +-----------------------------+-----------------------------+----------+------------+ + * + * @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], representing a tristate code word of length 12 + */ +char* RCSwitch::getCodeWordB(int nAddressCode, int nChannelCode, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) { + return 0; + } + + for (int i = 1; i <= 4; i++) { + sReturn[nReturnPos++] = (nAddressCode == i) ? '0' : 'F'; + } + + for (int i = 1; i <= 4; i++) { + sReturn[nReturnPos++] = (nChannelCode == i) ? '0' : 'F'; + } + + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Like getCodeWord (Type C = Intertechno) + */ +char* RCSwitch::getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + int nFamily = (int)sFamily - 'a'; + if ( nFamily < 0 || nFamily > 15 || nGroup < 1 || nGroup > 4 || nDevice < 1 || nDevice > 4) { + return 0; + } + + // encode the family into four bits + sReturn[nReturnPos++] = (nFamily & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 2) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 4) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 8) ? 'F' : '0'; + + // encode the device and group + sReturn[nReturnPos++] = ((nDevice-1) & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nDevice-1) & 2) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nGroup-1) & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nGroup-1) & 2) ? 'F' : '0'; + + // encode the status code + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Encoding for the REV Switch Type + * + * The code word is a tristate word and with following bit pattern: + * + * +-----------------------------+-------------------+----------+--------------+ + * | 4 bits address | 3 bits address | 3 bits | 2 bits | + * | switch group | device number | not used | on / off | + * | A=1FFF B=F1FF C=FF1F D=FFF1 | 1=0FF 2=F0F 3=FF0 | 000 | on=10 off=01 | + * +-----------------------------+-------------------+----------+--------------+ + * + * 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], representing a tristate code word of length 12 + */ +char* RCSwitch::getCodeWordD(char sGroup, int nDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + // sGroup must be one of the letters in "abcdABCD" + int nGroup = (sGroup >= 'a') ? (int)sGroup - 'a' : (int)sGroup - 'A'; + if ( nGroup < 0 || nGroup > 3 || nDevice < 1 || nDevice > 3) { + return 0; + } + + for (int i = 0; i < 4; i++) { + sReturn[nReturnPos++] = (nGroup == i) ? '1' : 'F'; + } + + for (int i = 1; i <= 3; i++) { + sReturn[nReturnPos++] = (nDevice == i) ? '1' : 'F'; + } + + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = '0'; + + sReturn[nReturnPos++] = bStatus ? '1' : '0'; + sReturn[nReturnPos++] = bStatus ? '0' : '1'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * @param sCodeWord a tristate code word consisting of the letter 0, 1, F + */ +void RCSwitch::sendTriState(const char* sCodeWord) { + // turn the tristate code word into the corresponding bit pattern, then send it + unsigned long code = 0; + unsigned int length = 0; + for (const char* p = sCodeWord; *p; p++) { + code <<= 2L; + switch (*p) { + case '0': + // bit pattern 00 + break; + case 'F': + // bit pattern 01 + code |= 1L; + break; + case '1': + // bit pattern 11 + code |= 3L; + break; + } + length += 2; + } + this->send(code, length); +} + +/** + * @param sCodeWord a tristate code word consisting of the letter 0, 1, F + */ +unsigned long RCSwitch::getBitPattern(const char* sCodeWord) { + // turn the tristate code word into the corresponding bit pattern, then return it + unsigned long code = 0; + unsigned int length = 0; + for (const char* p = sCodeWord; *p; p++) { + code <<= 2L; + switch (*p) { + case '0': + // bit pattern 00 + break; + case 'F': + // bit pattern 01 + code |= 1L; + break; + case '1': + // bit pattern 11 + code |= 3L; + break; + } + length += 2; + } + return code; +} + +/** + * @param sCodeWord a binary code word consisting of the letter 0, 1 + */ +void RCSwitch::send(const char* sCodeWord) { + // turn the tristate code word into the corresponding bit pattern, then send it + unsigned long code = 0; + unsigned int length = 0; + for (const char* p = sCodeWord; *p; p++) { + code <<= 1L; + if (*p != '0') + code |= 1L; + length++; + } + this->send(code, length); +} + +/** + * Transmit the first 'length' bits of the integer 'code'. The + * bits are sent from MSB to LSB, i.e., first the bit at position length-1, + * then the bit at position length-2, and so on, till finally the bit at position 0. + */ +void RCSwitch::send(unsigned long code, unsigned int length) { + if (this->nTransmitterPin == -1) + return; + +#if not defined( RCSwitchDisableReceiving ) + // make sure the receiver is disabled while we transmit + int nReceiverInterrupt_backup = nReceiverInterrupt; + if (nReceiverInterrupt_backup != -1) { + this->disableReceive(); + } +#endif + + for (int nRepeat = 0; nRepeat < nRepeatTransmit; nRepeat++) { + for (int i = length-1; i >= 0; i--) { + if (code & (1L << i)) + this->transmit(protocol.one); + else + this->transmit(protocol.zero); + } + this->transmit(protocol.syncFactor); + } + +#if not defined( RCSwitchDisableReceiving ) + // enable receiver again if we just disabled it + if (nReceiverInterrupt_backup != -1) { + this->enableReceive(nReceiverInterrupt_backup); + } +#endif +} + +/** + * Transmit a single high-low pulse. + */ +void RCSwitch::transmit(HighLow pulses) { + uint8_t firstLogicLevel = (this->protocol.invertedSignal) ? LOW : HIGH; + uint8_t secondLogicLevel = (this->protocol.invertedSignal) ? HIGH : LOW; + + digitalWrite(this->nTransmitterPin, firstLogicLevel); + delayMicroseconds( this->protocol.pulseLength * pulses.high); + digitalWrite(this->nTransmitterPin, secondLogicLevel); + delayMicroseconds( this->protocol.pulseLength * pulses.low); +} + + +#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) // Arduino + 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 receiveProtocol method */ +static inline unsigned int diff(int A, int B) { + return abs(A - B); +} + +/** + * + */ +bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCount) { +#ifdef ESP8266 + const Protocol &pro = proto[p-1]; +#else + Protocol pro; + memcpy_P(&pro, &proto[p-1], sizeof(Protocol)); +#endif + + unsigned long code = 0; + //Assuming the longer pulse length is the pulse captured in timings[0] + const unsigned int syncLengthInPulses = ((pro.syncFactor.low) > (pro.syncFactor.high)) ? (pro.syncFactor.low) : (pro.syncFactor.high); + const unsigned int delay = RCSwitch::timings[0] / syncLengthInPulses; + const unsigned int delayTolerance = delay * RCSwitch::nReceiveTolerance / 100; + + /* For protocols that start low, the sync period looks like + * _________ + * _____________| |XXXXXXXXXXXX| + * + * |--1st dur--|-2nd dur-|-Start data-| + * + * The 3rd saved duration starts the data. + * + * For protocols that start high, the sync period looks like + * + * ______________ + * | |____________|XXXXXXXXXXXXX| + * + * |-filtered out-|--1st dur--|--Start data--| + * + * The 2nd saved duration starts the data + */ + const unsigned int firstDataTiming = (pro.invertedSignal) ? (2) : (1); + + for (unsigned int i = firstDataTiming; i < changeCount - 1; 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 > 7) { // ignore very short transmissions: no device sends them, so this must be noise + RCSwitch::nReceivedValue = code; + RCSwitch::nReceivedBitlength = (changeCount - 1) / 2; + RCSwitch::nReceivedDelay = delay; + RCSwitch::nReceivedProtocol = p; + } + + return true; +} + +void RECEIVE_ATTR RCSwitch::handleInterrupt() { + + static unsigned int changeCount = 0; + static unsigned long lastTime = 0; + static unsigned int repeatCount = 0; + + const long time = micros(); + const unsigned int duration = time - lastTime; + + if (duration > RCSwitch::nSeparationLimit) { + // A long stretch without signal level change occurred. This could + // be the gap between two transmission. + if (diff(duration, RCSwitch::timings[0]) < 200) { + // This long signal is close in length to the long signal which + // started the previously recorded timings; this suggests that + // it may indeed by a a gap between two transmissions (we assume + // here that a sender will send the signal multiple times, + // with roughly the same gap between them). + repeatCount++; + if (repeatCount == 2) { + for(unsigned int i = 1; i <= numProto; i++) { + if (receiveProtocol(i, changeCount)) { + // receive succeeded for protocol i + break; + } + } + repeatCount = 0; + } + } + changeCount = 0; + } + + // detect overflow + if (changeCount >= RCSWITCH_MAX_CHANGES) { + changeCount = 0; + repeatCount = 0; + } + + RCSwitch::timings[changeCount++] = duration; + lastTime = time; +} +#endif diff --git a/Firmware/Source/v1.2/grove_pi_v1_2_7/RCSwitch.h b/Firmware/Source/v1.2/grove_pi_v1_2_7/RCSwitch.h new file mode 100644 index 00000000..36ee8b13 --- /dev/null +++ b/Firmware/Source/v1.2/grove_pi_v1_2_7/RCSwitch.h @@ -0,0 +1,163 @@ +/* + 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 / .(at)gmail(dot)com + - Max Horn / max(at)quendi(dot)de + - Robert ter Vehn / .(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 +*/ +#ifndef _RCSwitch_h +#define _RCSwitch_h + + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific + #include "Energia.h" +#elif defined(RPI) // Raspberry Pi + #define RaspberryPi + + // Include libraries for RPi: + #include /* memcpy */ + #include /* abs */ + #include +#else + #include "WProgram.h" +#endif + +#include + + +// At least for the ATTiny X4/X5, receiving has to be disabled due to +// missing libm depencies (udivmodhi4) +#if defined( __AVR_ATtinyX5__ ) or defined ( __AVR_ATtinyX4__ ) +#define RCSwitchDisableReceiving +#endif + +// Number of maximum High/Low changes per packet. +// We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync +#define RCSWITCH_MAX_CHANGES 67 + +class RCSwitch { + + public: + RCSwitch(); + + void switchOn(int nGroupNumber, int nSwitchNumber); + void switchOff(int nGroupNumber, int nSwitchNumber); + void switchOn(const char* sGroup, int nSwitchNumber); + void switchOff(const char* sGroup, int nSwitchNumber); + void switchOn(char sFamily, int nGroup, int nDevice); + void switchOff(char sFamily, int nGroup, int nDevice); + void switchOn(const char* sGroup, const char* sDevice); + void switchOff(const char* sGroup, const char* sDevice); + void switchOn(char sGroup, int nDevice); + void switchOff(char sGroup, int nDevice); + unsigned long switchOnCode(int nGroupNumber, int nSwitchNumber); + unsigned long switchOffCode(int nGroupNumber, int nSwitchNumber); + unsigned long switchOnCode(char sFamily, int nGroup, int nDevice); + unsigned long switchOffCode(char sFamily, int nGroup, int nDevice); + unsigned long switchOnCode(const char* sGroup, const char* sDevice); + unsigned long switchOffCode(const char* sGroup, const char* sDevice); + unsigned long switchOnCode(char sGroup, int nDevice); + unsigned long switchOffCode(char sGroup, int nDevice); + + void sendTriState(const char* sCodeWord); + void send(unsigned long code, unsigned int length); + void send(const char* sCodeWord); + + #if not defined( RCSwitchDisableReceiving ) + void enableReceive(int interrupt); + void enableReceive(); + void disableReceive(); + bool available(); + void resetAvailable(); + + unsigned long getReceivedValue(); + unsigned int getReceivedBitlength(); + unsigned int getReceivedDelay(); + unsigned int getReceivedProtocol(); + unsigned int* getReceivedRawdata(); + #endif + + void enableTransmit(int nTransmitterPin); + void disableTransmit(); + void setPulseLength(int nPulseLength); + void setRepeatTransmit(int nRepeatTransmit); + #if not defined( RCSwitchDisableReceiving ) + void setReceiveTolerance(int nPercent); + #endif + + struct HighLow { + uint8_t high; + uint8_t low; + }; + + struct Protocol { + int pulseLength; + HighLow syncFactor; + HighLow zero; + HighLow one; + /** @brief if true inverts the high and low logic levels in the HighLow structs */ + bool invertedSignal; + }; + + void setProtocol(Protocol protocol); + void setProtocol(int nProtocol); + void setProtocol(int nProtocol, int nPulseLength); + + private: + char* getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus); + char* getCodeWordB(int nGroupNumber, int nSwitchNumber, bool bStatus); + char* getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus); + char* getCodeWordD(char group, int nDevice, bool bStatus); + unsigned long getBitPattern(const char *sCodeWord); + void transmit(HighLow pulses); + + #if not defined( RCSwitchDisableReceiving ) + static void handleInterrupt(); + static bool receiveProtocol(const int p, unsigned int changeCount); + int nReceiverInterrupt; + #endif + int nTransmitterPin; + int nRepeatTransmit; + + Protocol protocol; + + #if not defined( RCSwitchDisableReceiving ) + static int nReceiveTolerance; + static unsigned long nReceivedValue; + static unsigned int nReceivedBitlength; + static unsigned int nReceivedDelay; + static unsigned int nReceivedProtocol; + const static unsigned int nSeparationLimit; + /* + * timings[0] contains sync timing, followed by a number of bits + */ + static unsigned int timings[RCSWITCH_MAX_CHANGES]; + #endif + +}; + +#endif diff --git a/Firmware/Source/v1.2/grove_pi_v1_2_7/README.md b/Firmware/Source/v1.2/grove_pi_v1_2_7/README.md index 9b125c5e..4362e10e 100644 --- a/Firmware/Source/v1.2/grove_pi_v1_2_7/README.md +++ b/Firmware/Source/v1.2/grove_pi_v1_2_7/README.md @@ -82,7 +82,6 @@ I2C address: 0x4 | Chainable RGB set LEDs with modulo | 94 | pin | offset | divisor | _none_ | Set color on all LEDs >= offset when mod remainder is 0 | | Chainable RGB set level | 95 | pin | level | reverse | _none_ | Set color on all LEDs <= level, outwards unless reverse | - ### Library Dependencies * [DHT](https://github.com/karan259/DHT-sensor-library) @@ -91,3 +90,4 @@ I2C address: 0x4 * [Grove_LED_bar](https://github.com/Seeed-Studio/Grove_LED_Bar) * [TM1637](https://github.com/mcauser/TM1637-led-driver-7-segment) * [Chainable_RGB_LED](https://github.com/mcauser/Grove-Chainable-RGB-LED) +* [Modified version of RCSwitch](https://github.com/sui77/rc-switch) diff --git a/Firmware/Source/v1.2/grove_pi_v1_2_7/grove_pi_v1_2_7.ino b/Firmware/Source/v1.2/grove_pi_v1_2_7/grove_pi_v1_2_7.ino index c39ccea6..eb11b17d 100644 --- a/Firmware/Source/v1.2/grove_pi_v1_2_7/grove_pi_v1_2_7.ino +++ b/Firmware/Source/v1.2/grove_pi_v1_2_7/grove_pi_v1_2_7.ino @@ -6,6 +6,10 @@ #include "Encoder.h" #include "TimerOne.h" +// Note that RCSwitch.h is a patched version of the one from the https://github.com/sui77/rc-switch repository. +// Additional functions have been added to get a numerical version of radio sequences sent/received. +#include "RCSwitch.h" + DHT dht; Grove_LED_Bar ledbar[6]; // 7 instances for D2-D8, however, max 4 bars, you can't use adjacent sockets, 4 pin display TM1637 fourdigit[6]; // 7 instances for D2-D8, however, max 4 displays, you can't use adjacent sockets, 4 pin display @@ -25,6 +29,15 @@ ChainableLED rgbled[6]; // 7 instances for D2-D8 #define flow_en_cmd 18 #define flow_dis_cmd 13 +#define rc_switch_send_cmd 110 +#define rc_switch_subscribe_cmd 111 +#define rc_switch_read_cmd 112 +#define rc_switch_typeA 0 +#define rc_switch_typeB 1 +#define rc_switch_typeC 2 +#define rc_switch_typeD 3 +#define rc_switch_max_sub 8 // Max number of switch subscriptions + int cmd[5]; int index=0; int flag=0; @@ -60,6 +73,23 @@ int flow_run_bk=0; long flow_read_start; byte flow_val[3]; //Given it's own I2C buffer so that it does not corrupt the data from other sensors when running in background +// 433MHz RCSwitch variables +RCSwitch rc_switch; // Library instance +// TODO define constants for on/off +uint8_t rc_switch_state; // Desired state of the remote-controlled switch. 0=OFF, 1=ON +uint8_t rc_switch_type; // Remote-controlled switch type. 0 == type A, 1 == type B, 2 == type C, 3 == type D. +char rc_switch_typeA_group [6]; // For type A switches, group DIP setting as a binary string ("00110" == OFF OFF ON ON OFF) +char rc_switch_typeA_device [6]; // For type A switches, device DIP setting as a binary string ("00110" == OFF OFF ON ON OFF) +struct rc_switch_subscription_struct { // Struct describing a subscription to a set of on/off codes + unsigned long onCode = 0; + unsigned long offCode = 0; + byte lastStatus = 255; // 255: unknown +}; +struct rc_switch_subscription_struct rc_switch_subscription[rc_switch_max_sub - 1]; // Subscription storage +uint8_t rc_switch_subscription_number; +boolean rc_switch_run_bk = false; +unsigned long rc_switch_code; + void setup() { // Serial.begin(38400); // start serial for output @@ -67,7 +97,6 @@ void setup() Wire.onReceive(receiveData); Wire.onRequest(sendData); - attachInterrupt(0,readPulseDust,CHANGE); } int pin; int j; @@ -536,6 +565,511 @@ void loop() detachInterrupt(0); cmd[0]=0; } + /* Command 110 - 433MHz transmitter: send command to radio-controlled switch or socket. + * + * Based on the RCSwitch library (https://github.com/sui77/rc-switch) + * This library supports 4 types of switches, with different parameters. + * For each of these, the firmware expects different parameters, as follows: + * + * +-------------------------------------------------------------------------------+ + * | Type A switch | + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: send command to RC switch. | + * | | | Decimal value: 110 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Pin on which the 433 MHz transmitter module is connected. | + * | | | 0 -> D2 / 1 -> D3 | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 4 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 5 | Requested switch state. | + * | | | 0 = off / 1 = on | + * | +---------+-----------------------------------------------------------+ + * | | 6 - 7 | RC switch type. | + * | | | Type A: 00 | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 4 | Group DIP switch. | + * | | | Ex: OFF-ON-ON-ON-OFF --> 01110 | + * | +---------+-----------------------------------------------------------+ + * | | 5 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 4 | Device DIP switch. | + * | | | Ex: OFF-ON-ON-ON-OFF --> 01110 | + * | +---------+-----------------------------------------------------------+ + * | | 5 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + * + * + * +-------------------------------------------------------------------------------+ + * | Type B switch | + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: send command to RC switch. | + * | | | Decimal value: 110 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Pin on which the 433 MHz transmitter module is connected. | + * | | | 0 -> D2 / 1 -> D3 | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 4 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 5 | Requested switch state. | + * | | | 0 = off / 1 = on | + * | +---------+-----------------------------------------------------------+ + * | | 6 - 7 | RC switch type. | + * | | | Type B: 01 | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 7 | Group id (1 - 4). | + * | | | Ex: 3 --> 0000 0011 | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 7 | Device id (1 - 4). | + * | | | Ex: 3 --> 0000 0011 | + * +---------+---------+-----------------------------------------------------------+ + * + * + * +-------------------------------------------------------------------------------+ + * | Type C switch | + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: send command to RC switch. | + * | | | Decimal value: 110 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Pin on which the 433 MHz transmitter module is connected. | + * | | | 0 -> D2 / 1 -> D3 | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 4 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 5 | Requested switch state. | + * | | | 0 = off / 1 = on | + * | +---------+-----------------------------------------------------------+ + * | | 6 - 7 | RC switch type. | + * | | | Type C: 10 | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 7 | Device family ('a' - 'f'), as the ASCII code of the | + * | | | desired letter. | + * | | | Ex: 'b' --> 0110 0001 | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 1 | Device id (1 - 4), minus 1. | + * | | | Ex: device #3 --> 10 | + * | +---------+-----------------------------------------------------------+ + * | | 2 - 3 | Device group (1 - 4), minus 1. | + * | | | Ex: group #1 --> 00 | + * | +---------+-----------------------------------------------------------+ + * | | 4 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + * + * + * +-------------------------------------------------------------------------------+ + * | Type D switch | + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: send command to RC switch. | + * | | | Decimal value: 110 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Pin on which the 433 MHz transmitter module is connected. | + * | | | 0 -> D2 / 1 -> D3 | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 4 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 5 | Requested switch state. | + * | | | 0 = off / 1 = on | + * | +---------+-----------------------------------------------------------+ + * | | 6 - 7 | RC switch type. | + * | | | Type D: 11 | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 7 | Device family ('A' - 'D'), as the ASCII code of the | + * | | | desired letter. | + * | | | Ex: 'C' --> 0100 0011 | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 7 | Device id (1 - 3). | + * | | | Ex: 3 --> 0000 0011 | + * +---------+---------+-----------------------------------------------------------+ + */ + else if (cmd[0]==rc_switch_send_cmd) { + if (run_once == 1) {// Ensure we do not repeatedly execute this command while waiting for another one + + // Apply bit-mask to recover data from the 2nd command byte + // pin: bit 0 of 2nd byte. 0 --> pin 2. 1 --> pin 3. + pin = 2 + (cmd[1] & 1); + // state (on/off) : bit 5 + rc_switch_state = uint8_t(cmd[1] >> 5 & 1); + // remote switch type: 4 possible types encoded in bits 6-7 + rc_switch_type = uint8_t(cmd[1] >> 6); + + // Enable radio + rc_switch.enableTransmit(pin); + + // Handle remaining bytes depending on the switch type + switch (rc_switch_type) { + case rc_switch_typeA: { + // For this type of remote controlled switches, the RCSwitch library requires 2 parameters, group and + // device, as 5-chars binary strings, padded with zeroes. These parameters should match DIP switches present + // on the switch device. + // Ex: a DIP switch set as OFF-ON-ON-ON-OFF is represented by the parameter string "01110" + + // Conversion of the group parameter in the appropriate format + for (i = 4; i >= 0; --i) { + rc_switch_typeA_group[4 - i] = (cmd[2] & (1 << i) ? '1' : '0'); + } + rc_switch_typeA_group[5] = '\0'; + + // Conversion of the device parameter in the appropriate format + for (i = 4; i >= 0; --i) { + rc_switch_typeA_device[4 - i] = (cmd[3] & (1 << i) ? '1' : '0'); + } + rc_switch_typeA_device[5] = '\0'; + + + if (rc_switch_state == 0) { + rc_switch.switchOff(rc_switch_typeA_group, rc_switch_typeA_device); + rc_switch_code = rc_switch.switchOffCode(rc_switch_typeA_group, rc_switch_typeA_device); + } else { + rc_switch.switchOn(rc_switch_typeA_group, rc_switch_typeA_device); + rc_switch_code = rc_switch.switchOnCode(rc_switch_typeA_group, rc_switch_typeA_device); + } + break; + } + case rc_switch_typeB: { + // For this type of remote controlled switches, the RCSwitch library requires two parameters: + // group as an int [1..4] --> directly passed in 3rd byte cmd[2] + // device as an int [1..4] --> directly passed in 4th byte cmd[3] + if (rc_switch_state == 0) { + rc_switch.switchOff(cmd[2], cmd[3]); + rc_switch_code = rc_switch.switchOffCode(cmd[2], cmd[3]); + } else { + rc_switch.switchOn(cmd[2], cmd[3]); + rc_switch_code = rc_switch.switchOnCode(cmd[2], cmd[3]); + } + break; + } + case rc_switch_typeC: { + // For this type of remote controlled switches, the RCSwitch library requires three parameters: + // family code as a char ['a'..'f'] --> directly passed in 3rd byte cmd[2] + // group as int [1..4] --> this value minus one is passed using bits 2 and 3 of the 4th byte cmd[3] + // device as int [1..4] --> this value minus one is passed using bits 0 and 1 of the 4th byte cmd[3] + if (rc_switch_state == 0) { + rc_switch.switchOff(char(cmd[2]), 1 + (cmd[3] >> 2 & 3), 1 + (cmd[3] & 3)); + rc_switch_code = rc_switch.switchOffCode(char(cmd[2]), 1 + (cmd[3] >> 2 & 3), 1 + (cmd[3] & 3)); + } else { + rc_switch.switchOn(char(cmd[2]), 1 + (cmd[3] >> 2 & 3), 1 + (cmd[3] & 3)); + rc_switch_code = rc_switch.switchOnCode(char(cmd[2]), 1 + (cmd[3] >> 2 & 3), 1 + (cmd[3] & 3)); + } + break; + } + case rc_switch_typeD: { + // For this type of remote controlled switches, the RCSwitch library requires two parameters: + // group as a char ['A'..'D'] --> directly passed in 3rd byte cmd[2] + // device as an int [1..3] --> directly passed in 4th byte cmd[3] + if (rc_switch_state == 0) { + rc_switch.switchOff(char(cmd[2]), cmd[3]); + rc_switch_code = rc_switch.switchOffCode(char(cmd[2]), cmd[3]); + } else { + rc_switch.switchOn(char(cmd[2]), cmd[3]); + rc_switch_code = rc_switch.switchOffCode(char(cmd[2]), cmd[3]); + } + break; + } + default: { + // Unknown switch type --> do nothing + } + } + // Disable radio + rc_switch.disableTransmit(); + } + cmd[0] = 0; + run_once = 0; + } + /* Command 111 - 433MHz receiver: subscribe to radio commands + * + * Based on the RCSwitch library (https://github.com/sui77/rc-switch) + * This library supports 4 types of switches, with different parameters. + * For each of these, the firmware expects different parameters, as follows: + * + * +-------------------------------------------------------------------------------+ + * | Type A switch | + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + * | | | Decimal value: 111 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Pin on which the 433 MHz receiver module is connected. | + * | | | 0 -> D2 / 1 -> D3 | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 3 | Subscription number | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 4 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 5 | Initial state. | + * | | | 0 = off / 1 = on | + * | +---------+-----------------------------------------------------------+ + * | | 6 - 7 | RC switch type. | + * | | | Type A: 00 | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 4 | Group DIP switch. | + * | | | Ex: OFF-ON-ON-ON-OFF --> 01110 | + * | +---------+-----------------------------------------------------------+ + * | | 5 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 4 | Device DIP switch. | + * | | | Ex: OFF-ON-ON-ON-OFF --> 01110 | + * | +---------+-----------------------------------------------------------+ + * | | 5 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + * + * + * +-------------------------------------------------------------------------------+ + * | Type B switch | + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + * | | | Decimal value: 111 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Pin on which the 433 MHz receiver module is connected. | + * | | | 0 -> D2 / 1 -> D3 | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 3 | Subscription number | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 4 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 5 | Initial state. | + * | | | 0 = off / 1 = on | + * | +---------+-----------------------------------------------------------+ + * | | 6 - 7 | RC switch type. | + * | | | Type B: 01 | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 7 | Group id (1 - 4). | + * | | | Ex: 3 --> 0000 0011 | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 7 | Device id (1 - 4). | + * | | | Ex: 3 --> 0000 0011 | + * +---------+---------+-----------------------------------------------------------+ + * + * + * +-------------------------------------------------------------------------------+ + * | Type C switch | + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + * | | | Decimal value: 111 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Pin on which the 433 MHz receiver module is connected. | + * | | | 0 -> D2 / 1 -> D3 | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 3 | Subscription number | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 4 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 5 | Initial state. | + * | | | 0 = off / 1 = on | + * | +---------+-----------------------------------------------------------+ + * | | 6 - 7 | RC switch type. | + * | | | Type C: 10 | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 7 | Device family ('a' - 'f'), as the ASCII code of the | + * | | | desired letter. | + * | | | Ex: 'b' --> 0110 0001 | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 1 | Device id (1 - 4), minus 1. | + * | | | Ex: device #3 --> 10 | + * | +---------+-----------------------------------------------------------+ + * | | 2 - 3 | Device group (1 - 4), minus 1. | + * | | | Ex: group #1 --> 00 | + * | +---------+-----------------------------------------------------------+ + * | | 4 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + * + * + * +-------------------------------------------------------------------------------+ + * | Type D switch | + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + * | | | Decimal value: 111 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Pin on which the 433 MHz receiver module is connected. | + * | | | 0 -> D2 / 1 -> D3 | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 3 | Subscription number | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 4 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 5 | Initial state. | + * | | | 0 = off / 1 = on | + * | +---------+-----------------------------------------------------------+ + * | | 6 - 7 | RC switch type. | + * | | | Type D: 11 | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 7 | Device family ('A' - 'D'), as the ASCII code of the | + * | | | desired letter. | + * | | | Ex: 'C' --> 0100 0011 | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 7 | Device id (1 - 3). | + * | | | Ex: 3 --> 0000 0011 | + * +---------+---------+-----------------------------------------------------------+ + */ + else if (cmd[0]==rc_switch_subscribe_cmd) { + if (run_once == 1) {// Ensure we do not repeatedly execute this command while waiting for another one + + // Apply bit-mask to recover data from the 2nd command byte + // pin: bit 0. 0 --> pin 2 / 1 --> pin 3 + pin = 2 + (cmd[1] & 1); + + // subscription number: bits 1-3 + rc_switch_subscription_number = uint8_t(cmd[1] >> 1 & 7); + + // initial state (on/off): bit 5 + rc_switch_state = uint8_t(cmd[1] >> 5 & 1); + + // remote switch type: 4 possible types encoded in bits 6-7 + rc_switch_type = uint8_t(cmd[1] >> 6); + + // Enable radio if it is not already done + if (!rc_switch_run_bk) { + rc_switch.enableReceive(digitalPinToInterrupt(pin)); + rc_switch_run_bk = true; + } + + // Handle remaining bytes depending on the switch type + switch (rc_switch_type) { + case rc_switch_typeA: { + // For this type of remote controlled switches, the RCSwitch library requires 2 parameters, group and + // device, as 5-chars binary strings, padded with zeroes. These parameters should match DIP switches present + // on the switch device. + // Ex: a DIP switch set as OFF-ON-ON-ON-OFF is represented by the parameter string "01110" + + // Conversion of the group parameter in the appropriate format + for (i = 4; i >= 0; --i) { + rc_switch_typeA_group[4 - i] = (cmd[2] & (1 << i) ? '1' : '0'); + } + rc_switch_typeA_group[5] = '\0'; + + // Conversion of the device parameter in the appropriate format + for (i = 4; i >= 0; --i) { + rc_switch_typeA_device[4 - i] = (cmd[3] & (1 << i) ? '1' : '0'); + } + rc_switch_typeA_device[5] = '\0'; + + // Store subscription + rc_switch_subscription[rc_switch_subscription_number].offCode = + rc_switch.switchOffCode(rc_switch_typeA_group, rc_switch_typeA_device); + rc_switch_subscription[rc_switch_subscription_number].onCode = + rc_switch.switchOnCode(rc_switch_typeA_group, rc_switch_typeA_device); + rc_switch_subscription[rc_switch_subscription_number].lastStatus = rc_switch_state; + + break; + } + case rc_switch_typeB: { + // For this type of remote controlled switches, the RCSwitch library requires two parameters: + // group as an int [1..4] --> directly passed in 3rd byte cmd[2] + // device as an int [1..4] --> directly passed in 4th byte cmd[3] + + // Store subscription + rc_switch_subscription[rc_switch_subscription_number].offCode = + rc_switch.switchOffCode(cmd[2], cmd[3]); + rc_switch_subscription[rc_switch_subscription_number].onCode = + rc_switch.switchOnCode(cmd[2], cmd[3]); + + rc_switch_subscription[rc_switch_subscription_number].lastStatus = rc_switch_state; + break; + } + case rc_switch_typeC: { + // For this type of remote controlled switches, the RCSwitch library requires three parameters: + // family code as a char ['a'..'f'] --> directly passed in 3rd byte cmd[2] + // group as int [1..4] --> this value minus one is passed using bits 2 and 3 of the 4th byte cmd[3] + // device as int [1..4] --> this value minus one is passed using bits 0 and 1 of the 4th byte cmd[3] + + // Store subscription + rc_switch_subscription[rc_switch_subscription_number].offCode = + rc_switch.switchOffCode(char(cmd[2]), 1 + (cmd[3] >> 2 & 3), 1 + (cmd[3] & 3)); + rc_switch_subscription[rc_switch_subscription_number].onCode = + rc_switch.switchOnCode(char(cmd[2]), 1 + (cmd[3] >> 2 & 3), 1 + (cmd[3] & 3)); + rc_switch_subscription[rc_switch_subscription_number].lastStatus = rc_switch_state; + break; + } + case rc_switch_typeD: { + // For this type of remote controlled switches, the RCSwitch library requires two parameters: + // group as a char ['A'..'D'] --> directly passed in 3rd byte cmd[2] + // device as an int [1..3] --> directly passed in 4th byte cmd[3] + + // Store subscription + rc_switch_subscription[rc_switch_subscription_number].offCode = + rc_switch.switchOffCode(char(cmd[2]), cmd[3]); + rc_switch_subscription[rc_switch_subscription_number].onCode = + rc_switch.switchOnCode(char(cmd[2]), cmd[3]); + rc_switch_subscription[rc_switch_subscription_number].lastStatus = rc_switch_state; + break; + } + default: { + // Unknown switch type --> do nothing + } + + } + } + cmd[0] = 0; + run_once = 0; + } + /* Command 112 - 433MHz receiver: read last status + * + * Retrieve last received command for a subscription slot + * + * +---------+---------+-----------------------------------------------------------+ + * | Byte | Bits | Description | + * +---------+---------+-----------------------------------------------------------+ + * | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + * | | | Decimal value: 112 | + * +---------+---------+-----------------------------------------------------------+ + * | 2 | 0 | Unused | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 1 - 3 | Subscription number | + * | | | | + * | +---------+-----------------------------------------------------------+ + * | | 4 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + * | 3 | 0 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + * | 4 | 0 - 7 | Unused | + * | | | | + * +---------+---------+-----------------------------------------------------------+ + */ + else if (cmd[0]==rc_switch_read_cmd) { + if (run_once == 1) { // Ensure we do not repeatedly execute this command while waiting for another one + + // Apply bit-mask to recover data from the 2nd command byte + // subscription number: bits 1-3 + rc_switch_subscription_number = uint8_t(cmd[1] >> 1 & 7); + val = rc_switch_subscription[rc_switch_subscription_number].lastStatus; + } + run_once = 0; + } } //Dust sensor can run in background so has a dedicated if condition if(dust_run_bk) @@ -585,6 +1119,23 @@ void loop() flow_read_start=millis(); } } + + if(rc_switch_run_bk) + { + // Check if we have received a radio command + if (rc_switch.available()) + { + // Check if we have to update one of our subscriptions + for (j = 0; j < rc_switch_max_sub; j++) { + if (rc_switch_subscription[j].offCode == rc_switch.getReceivedValue()) { + rc_switch_subscription[j].lastStatus = 0; + } else if (rc_switch_subscription[j].onCode == rc_switch.getReceivedValue()) { + rc_switch_subscription[j].lastStatus = 1; + } + } + rc_switch.resetAvailable(); + } + } } void receiveData(int byteCount) @@ -638,6 +1189,11 @@ void sendData() flow_val[0]=0; cmd[0]=0; } + if(cmd[0]==rc_switch_read_cmd) + { + Wire.write(val); + cmd[0]=0; + } } diff --git a/Software/Python/grove_rflink433mhz_rcswitch/433rf_link_kit.png b/Software/Python/grove_rflink433mhz_rcswitch/433rf_link_kit.png new file mode 100644 index 00000000..1fbc35c4 Binary files /dev/null and b/Software/Python/grove_rflink433mhz_rcswitch/433rf_link_kit.png differ diff --git a/Software/Python/grove_rflink433mhz_rcswitch/README.md b/Software/Python/grove_rflink433mhz_rcswitch/README.md new file mode 100644 index 00000000..f474fb1b --- /dev/null +++ b/Software/Python/grove_rflink433mhz_rcswitch/README.md @@ -0,0 +1,25 @@ +Grove 433MHz Simple RF link kit +=============================== + +![Grove 433MHz Simple RF link kit](433rf_link_kit.png) + +This kit contains two Grove components: a transmitter (TX) and a receiver (RX). +Both are tuned to the 433MHz frequency, which is frequently used in short range communication between devices. +As a result, these components can be used in various use cases. This library helps with some of these. + +GrovePi supported use-cases +=========================== + +Remote switch operation +----------------------- + +Some devices can be remote-controlled using specific codes sent over the 433MHz frequency. This is the case +of some switches or power outlets easily found on the market. +The `grove_433mhz_tx_rcswitch.py` provides support to command such power outlets by embedding the +[RCSwitch library](https://github.com/sui77/rc-switch/wiki). + +Remote control commands reception +--------------------------------- + +These power outlets come with a RF remote control. The `grove_433mhz_rx_rcswitch.py` library allows to listen to +commands sent by such remote controls. \ No newline at end of file diff --git a/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_rx_rcswitch.py b/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_rx_rcswitch.py new file mode 100644 index 00000000..cc98bb54 --- /dev/null +++ b/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_rx_rcswitch.py @@ -0,0 +1,301 @@ +import grovepi + + +class Grove433mhzRxRCSwitch: + """ Receives command codes from remotes sold with radio controlled switches or devices. + + This library receives on / off commands sent from the radio remotes of 4 different types of RC devices. + Each of these types can be identified by the way the device address is configured: + - Type A devices addresses are set by 5 DIP switches + - Type B devices addresses are set by 2 rotary or sliding switches, each with 4 positions + - Type C 'Intertechno' devices addresses are set with 3 elements: family (a to f), group (1 to 4) and device + (1 to 4) + - Type D 'REV' devices addresses are set with 2 elements: group (A to D) and device (1 to 3) + + Using this library requires 2 steps: + 1. Subscribe to the on/off commands normally sent to a device. The GrovePi will from that point listen + to these commands, and store the last one. + + 2. Periodically read the last received command, and act accordingly. + + This library makes use of the RCSwitch library: https://github.com/sui77/rc-switch/ + Please have a look at it for more details on the supported devices. + + """ + + # Remote-controlled switch types + TYPE_A = 0 + TYPE_B = 1 + TYPE_C = 2 + TYPE_D = 3 + + # Last received command + STATE_ON = 1 + STATE_OFF = 0 + STATE_UNKNOWN = 255 + + def __init__(self, rx_pin): + """Sets the Digital pin on which the RX component is wired. + + :param rx_pin: For D2, set to 2. Only pins D2 and D3 are compatible with this grove component. + """ + self.rx_pin = rx_pin + + def subscribe_type_a(self, subscription_number, group, device, initial_state): + """Subscribe to commands normally sent to a type A device. + + Type A devices addresses are typically set by 5 DIP switches. + + :param subscription_number: subscription slot to use. Can be any value from 0 to 7. + + :param group: group DIP switch setting, as a string of five 0s/1s. Set to the '00101' string for a DIP switch + set to OFF OFF ON OFF ON. + :param device: device DIP switch setting, as a string of five 0s/1s. Set to the '00101' string for a DIP switch + set to OFF OFF ON OFF ON. + + :param initial_state: set the value to be returned by the first call to read_subscription. Can be STATE_ON or + STATE_OFF. + """ + + # +-------------------------------------------------------------------------------+ + # | Type A switch | + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + # | | | Decimal value: 111 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Pin on which the 433 MHz receiver module is connected. | + # | | | 0 -> D2 / 1 -> D3 | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 3 | Subscription number | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 4 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 5 | Initial state. | + # | | | 0 = off / 1 = on | + # | +---------+-----------------------------------------------------------+ + # | | 6 - 7 | RC switch type. | + # | | | Type A: 00 | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 4 | Group DIP switch. | + # | | | Ex: OFF-ON-ON-ON-OFF --> 01110 | + # | +---------+-----------------------------------------------------------+ + # | | 5 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 4 | Device DIP switch. | + # | | | Ex: OFF-ON-ON-ON-OFF --> 01110 | + # | +---------+-----------------------------------------------------------+ + # | | 5 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_sub_cmd + + [self.TYPE_A * 64 + initial_state * 32 + subscription_number * 2 + self.rx_pin - 2, + int(group, 2), + int(device, 2)]) + + def subscribe_type_b(self, subscription_number, group, device, initial_state): + """Subscribe to commands normally sent to a type B device. + + Type B devices addresses are typically set by two rotary or sliding switches, numbered from 1 to 4. + + :param subscription_number: subscription slot to use. Can be any value from 0 to 7. + + :param group: group switch setting (1 to 4). + + :param device: device switch setting (1 to 4). + + :param initial_state: set the value to be returned by the first call to read_subscription. Can be STATE_ON or + STATE_OFF. + """ + + # Send command to the firmware, using the following scheme: + # + # +-------------------------------------------------------------------------------+ + # | Type B switch | + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + # | | | Decimal value: 111 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Pin on which the 433 MHz receiver module is connected. | + # | | | 0 -> D2 / 1 -> D3 | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 3 | Subscription number | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 4 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 5 | Initial state. | + # | | | 0 = off / 1 = on | + # | +---------+-----------------------------------------------------------+ + # | | 6 - 7 | RC switch type. | + # | | | Type B: 01 | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 7 | Group id (1 - 4). | + # | | | Ex: 3 --> 0000 0011 | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 7 | Device id (1 - 4). | + # | | | Ex: 3 --> 0000 0011 | + # +---------+---------+-----------------------------------------------------------+ + + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_sub_cmd + + [self.TYPE_B * 64 + initial_state * 32 + subscription_number * 2 + self.rx_pin - 2, + group, + device]) + + def subscribe_type_c(self, subscription_number, family, group, device, initial_state): + """Subscribe to commands normally sent to a type C device. + + :param subscription_number: subscription slot to use. Can be any value from 0 to 7. + + :param family: device family setting ('a' to 'f') + + :param group: device group (1 to 4). + + :param device: device id (1 to 4). + + :param initial_state: set the value to be returned by the first call to read_subscription. Can be STATE_ON or + STATE_OFF. + """ + + # Send command to the firmware, using the following scheme: + # + # +-------------------------------------------------------------------------------+ + # | Type C switch | + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + # | | | Decimal value: 111 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Pin on which the 433 MHz receiver module is connected. | + # | | | 0 -> D2 / 1 -> D3 | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 3 | Subscription number | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 4 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 5 | Initial state. | + # | | | 0 = off / 1 = on | + # | +---------+-----------------------------------------------------------+ + # | | 6 - 7 | RC switch type. | + # | | | Type C: 10 | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 7 | Device family ('a' - 'f'), as the ASCII code of the | + # | | | desired letter. | + # | | | Ex: 'b' --> 0110 0001 | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 1 | Device id (1 - 4), minus 1. | + # | | | Ex: device #3 --> 10 | + # | +---------+-----------------------------------------------------------+ + # | | 2 - 3 | Device group (1 - 4), minus 1. | + # | | | Ex: group #1 --> 00 | + # | +---------+-----------------------------------------------------------+ + # | | 4 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_sub_cmd + + [self.TYPE_C * 64 + initial_state * 32 + subscription_number * 2 + self.rx_pin - 2, + ord(family), + (group - 1) * 4 + (device - 1)]) + + def subscribe_type_d(self, subscription_number, group, device, initial_state): + """Subscribe to commands normally sent to a type D device. + + :param subscription_number: subscription slot to use. Can be any value from 0 to 7. + + :param family: device family setting ('A' to 'D') + + :param device: device id (1 to 4). + + :param initial_state: set the value to be returned by the first call to read_subscription. Can be STATE_ON or + STATE_OFF. + """ + + # Send command to the firmware, using the following scheme: + # + # +-------------------------------------------------------------------------------+ + # | Type D switch | + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + # | | | Decimal value: 111 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Pin on which the 433 MHz receiver module is connected. | + # | | | 0 -> D2 / 1 -> D3 | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 3 | Subscription number | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 4 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 5 | Initial state. | + # | | | 0 = off / 1 = on | + # | +---------+-----------------------------------------------------------+ + # | | 6 - 7 | RC switch type. | + # | | | Type D: 11 | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 7 | Device family ('A' - 'D'), as the ASCII code of the | + # | | | desired letter. | + # | | | Ex: 'C' --> 0100 0011 | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 7 | Device id (1 - 3). | + # | | | Ex: 3 --> 0000 0011 | + # +---------+---------+-----------------------------------------------------------+ + + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_sub_cmd + + [self.TYPE_D * 64 + initial_state * 32 + subscription_number * 2 + self.rx_pin - 2, + ord(group), + device]) + + def read_subscription(self, subscription_number): + """Read last command received for a given subscription number + + :param subscription_number: + :return: STATE_ON, STATE_OFF or STATE_UNKNOWN + """ + + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: subscribe to RC switch commands. | + # | | | Decimal value: 112 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 3 | Subscription number | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 4 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_read_cmd + # Byte 1 + [subscription_number * 2, # Byte 2 + 0, # Byte 3 + 0]) # Byte 4 + + return grovepi.read_i2c_byte(grovepi.address) \ No newline at end of file diff --git a/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_rx_rcswitch_example.py b/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_rx_rcswitch_example.py new file mode 100644 index 00000000..91c4c524 --- /dev/null +++ b/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_rx_rcswitch_example.py @@ -0,0 +1,19 @@ +import grove_433mhz_rx_rcswitch as rcswitch +import time + +# Transmitter module should be connected to grovepi socket D2 +rx_pin = 2 + +rc = rcswitch.Grove433mhzRxRCSwitch(rx_pin) + + +# Subscribe to commands sent to a type B remote, with a group 2, device 3 settings. +# Subscription uses slot #1. +rc.subscribe_type_b(1, 2, 3, rc.STATE_OFF) + +time.sleep(1); + +# Continuously show the last received command +while 1: + print rc.read_subscription(1) + time.sleep(3) \ No newline at end of file diff --git a/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_tx_rcswitch.py b/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_tx_rcswitch.py new file mode 100644 index 00000000..6794e8af --- /dev/null +++ b/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_tx_rcswitch.py @@ -0,0 +1,220 @@ +import grovepi + + +class Grove433mhzTxRCSwitch: + """ Sends command codes to radio remote controlled switches or devices. + + This library sends on / off commands to 4 different types of RC devices. Each of these types can be identified by + the way the device address is configured: + - Type A devices addresses are set by 5 DIP switches + - Type B devices addresses are set by 2 rotary or sliding switches, each with 4 positions + - Type C 'Intertechno' devices addresses are set with 3 elements: family (a to f), group (1 to 4) and device + (1 to 4) + - Type D 'REV' devices addresses are set with 2 elements: group (A to D) and device (1 to 3) + + This library makes use of the RCSwitch library: https://github.com/sui77/rc-switch/ + + This method does not update existing subscriptions defined using grove_433mhz_rx_rcswitch + + """ + + # Remote-controlled switch types + TYPE_A = 0 + TYPE_B = 1 + TYPE_C = 2 + TYPE_D = 3 + + # Remote-controlled switch state + COMMAND_ON = 1 + COMMAND_OFF = 0 + + def __init__(self, tx_pin): + """Sets the Digital pin on which the TX component is wired. + + :param tx_pin: For D2, set to 2. Only pins D2 and D3 are compatible with this grove component. + """ + self.tx_pin = tx_pin + + def send_type_a(self, command, group, device): + """Sends an ON or OFF command to a type A device. + + Type A devices addresses are typically set by 5 DIP switches. + + :param command: set to COMMAND_ON or COMMAND_OFF. + :param group: group DIP switch setting, as a string of five 0s/1s. Set to the '00101' string for a DIP switch + set to OFF OFF ON OFF ON. + :param device: device DIP switch setting, as a string of five 0s/1s. Set to the '00101' string for a DIP switch + set to OFF OFF ON OFF ON. + """ + + # Send command to the firmware, using the following scheme: + # +-------------------------------------------------------------------------------+ + # | Type A switch | + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: send command to RC switch. | + # | | | Decimal value: 110 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Pin on which the 433 MHz transmitter module is connected. | + # | | | 0 -> D2 / 1 -> D3 | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 4 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 5 | Requested switch state. | + # | | | 0 = off / 1 = on | + # | +---------+-----------------------------------------------------------+ + # | | 6 - 7 | RC switch type. | + # | | | Type A: 00 | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 4 | Group DIP switch. | + # | | | Ex: OFF-ON-ON-ON-OFF --> 01110 | + # | +---------+-----------------------------------------------------------+ + # | | 5 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 4 | Device DIP switch. | + # | | | Ex: OFF-ON-ON-ON-OFF --> 01110 | + # | +---------+-----------------------------------------------------------+ + # | | 5 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_send_cmd + # Byte 1 + [self.TYPE_A * 64 + command * 32 + self.tx_pin - 2, # Byte 2 + int(group, 2), # Byte 3 + int(device, 2)]) # Byte 4 + + + def send_type_b(self, command, group, device): + """Sends an ON or OFF command to a type B device. + + Type B devices addresses are typically set by two rotary or sliding switches, numbered from 1 to 4. + + :param command: set to COMMAND_ON or COMMAND_OFF. + :param group: group switch setting (1 to 4). + :param device: device switch setting (1 to 4). + """ + + # Send command to the firmware, using the following scheme: + # +-------------------------------------------------------------------------------+ + # | Type B switch | + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: send command to RC switch. | + # | | | Decimal value: 110 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Pin on which the 433 MHz transmitter module is connected. | + # | | | 0 -> D2 / 1 -> D3 | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 4 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 5 | Requested switch state. | + # | | | 0 = off / 1 = on | + # | +---------+-----------------------------------------------------------+ + # | | 6 - 7 | RC switch type. | + # | | | Type B: 01 | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 7 | Group id (1 - 4). | + # | | | Ex: 3 --> 0000 0011 | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 7 | Device id (1 - 4). | + # | | | Ex: 3 --> 0000 0011 | + # +---------+---------+-----------------------------------------------------------+ + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_send_cmd + + [self.TYPE_B * 64 + command * 32 + self.tx_pin - 2, + group, + device]) + + def send_type_c(self, command, family, group, device): + """Sends an ON or OFF command to a type C (Intertechno) device. + + :param command: set to COMMAND_ON or COMMAND_OFF. + :param family: device family setting ('a' to 'f') + :param group: device group (1 to 4). + :param device: device id (1 to 4). + """ + + # +-------------------------------------------------------------------------------+ + # | Type C switch | + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: send command to RC switch. | + # | | | Decimal value: 110 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Pin on which the 433 MHz transmitter module is connected. | + # | | | 0 -> D2 / 1 -> D3 | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 4 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 5 | Requested switch state. | + # | | | 0 = off / 1 = on | + # | +---------+-----------------------------------------------------------+ + # | | 6 - 7 | RC switch type. | + # | | | Type C: 10 | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 7 | Device family ('a' - 'f'), as the ASCII code of the | + # | | | desired letter. | + # | | | Ex: 'b' --> 0110 0001 | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 1 | Device id (1 - 4), minus 1. | + # | | | Ex: device #3 --> 10 | + # | +---------+-----------------------------------------------------------+ + # | | 2 - 3 | Device group (1 - 4), minus 1. | + # | | | Ex: group #1 --> 00 | + # + +---------+-----------------------------------------------------------+ + # | | 4 - 7 | Unused | + # | | | | + # +---------+---------+-----------------------------------------------------------+ + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_send_cmd + + [self.TYPE_C * 64 + command * 32 + self.tx_pin - 2, + ord(family), + (group - 1) * 4 + (device - 1)]) + + def send_type_d(self, command, family, device): + """Sends an ON or OFF command to a type D device. + + :param command: set to COMMAND_ON or COMMAND_OFF. + :param family: device family setting ('A' to 'D') + :param device: device id (1 to 4). + """ + + # +-------------------------------------------------------------------------------+ + # | Type D switch | + # +---------+---------+-----------------------------------------------------------+ + # | Byte | Bits | Description | + # +---------+---------+-----------------------------------------------------------+ + # | 1 | 0 - 7 | Command byte: send command to RC switch. | + # | | | Decimal value: 110 | + # +---------+---------+-----------------------------------------------------------+ + # | 2 | 0 | Pin on which the 433 MHz transmitter module is connected. | + # | | | 0 -> D2 / 1 -> D3 | + # | +---------+-----------------------------------------------------------+ + # | | 1 - 4 | Unused | + # | | | | + # | +---------+-----------------------------------------------------------+ + # | | 5 | Requested switch state. | + # | | | 0 = off / 1 = on | + # | +---------+-----------------------------------------------------------+ + # | | 6 - 7 | RC switch type. | + # | | | Type D: 11 | + # +---------+---------+-----------------------------------------------------------+ + # | 3 | 0 - 7 | Device family ('A' - 'D'), as the ASCII code of the | + # | | | desired letter. | + # | | | Ex: 'C' --> 0100 0011 | + # +---------+---------+-----------------------------------------------------------+ + # | 4 | 0 - 7 | Device id (1 - 3). | + # | | | Ex: 3 --> 0000 0011 | + # +---------+---------+-----------------------------------------------------------+ + grovepi.write_i2c_block(grovepi.address, + grovepi.rcswitch_send_cmd + + [self.TYPE_D * 64 + command * 32 + self.tx_pin - 2, + ord(family), + device]) diff --git a/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_tx_rcswitch_example.py b/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_tx_rcswitch_example.py new file mode 100644 index 00000000..36355149 --- /dev/null +++ b/Software/Python/grove_rflink433mhz_rcswitch/grove_433mhz_tx_rcswitch_example.py @@ -0,0 +1,59 @@ +import grove_433mhz_tx_rcswitch as rcswitch +import time + +# Transmitter module should be connected to grovepi socket D3 +tx_pin = 3 + +rc = rcswitch.Grove433mhzTxRCSwitch(tx_pin) + +# Example 1: remote control of a type A switch, with a family DIP selector set to "ON ON ON OFF OFF" and a device +# DIP selector set to "OFF ON OFF ON OFF". + +# Turn on the switch +rc.send_type_a(rc.COMMAND_ON, "11100", "01010") + +time.sleep(3) + +# Turn off the switch +rc.send_type_a(rc.COMMAND_OFF, "11100", "01010") + +time.sleep(3) + + +# Example 2: remote control of a type B switch, with two rotary/sliding switchs. Group switch set to II and device +# switch set to 3. + +# Turn on the switch +rc.send_type_b(rc.COMMAND_ON, 2, 3) + +time.sleep(3) + +# Turn off the switch +rc.send_type_b(rc.COMMAND_OFF, 2, 3) + +time.sleep(3) + + +# Example 3: remote control of a type C switch (Intertechno), with a family code of c, a group set to 3 and a device set +# to 1. + +# Turn on the switch +rc.send_type_c(rc.COMMAND_ON, "c", 3, 1) + +time.sleep(3) + +# Turn off the switch +rc.send_type_c(rc.COMMAND_OFF, "c", 3, 1) + +time.sleep(3) + + +# Example 4: remote control of a type D switch, with a group set to D and a device set to 3 + +# Turn on the switch +rc.send_type_d(rc.COMMAND_ON, "D", 3) + +time.sleep(3) + +# Turn off the switch +rc.send_type_d(rc.COMMAND_OFF, "D", 3) diff --git a/Software/Python/grovepi.py b/Software/Python/grovepi.py index 020b0e5d..c36abafd 100755 --- a/Software/Python/grovepi.py +++ b/Software/Python/grovepi.py @@ -158,6 +158,11 @@ flow_read_cmd=[12] flow_disable_cmd=[13] flow_en_cmd=[18] + +rcswitch_send_cmd=[110] +rcswitch_sub_cmd=[111] +rcswitch_read_cmd=[112] + # This allows us to be more specific about which commands contain unused bytes unused = 0 retries = 10