Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 58 additions & 22 deletions src/RCSwitch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Robert ter Vehn / <first name>.<last name>(at)gmail(dot)com
- Johann Richard / <first name>.<last name>(at)gmail(dot)com
- Vlad Gheorghe / <first name>.<last name>(at)gmail(dot)com https://github.com/vgheo
- Matias Cuenca-Acuna

Project home: https://github.com/sui77/rc-switch/

Expand Down Expand Up @@ -40,7 +41,7 @@
#define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
#endif

#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
// interrupt handler and related code must be in RAM on ESP8266,
// according to issue #46.
#define RECEIVE_ATTR ICACHE_RAM_ATTR
Expand All @@ -50,7 +51,7 @@


/* Format for protocol definitions:
* {pulselength, Sync bit, "0" bit, "1" bit}
* {pulselength, Sync bit, "0" bit, "1" bit, invertedSignal}
*
* pulselength: pulse length in microseconds, e.g. 350
* Sync bit: {1, 31} means 1 high pulse and 31 low pulses
Expand All @@ -68,29 +69,36 @@
*
* These are combined to form Tri-State bits when sending or receiving codes.
*/
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
static const RCSwitch::Protocol proto[] = {
#else
static const RCSwitch::Protocol PROGMEM proto[] = {
#endif
{ 350, { 1, 31 }, { 1, 3 }, { 3, 1 } }, // protocol 1
{ 650, { 1, 10 }, { 1, 2 }, { 2, 1 } }, // protocol 2
{ 100, { 30, 71 }, { 4, 11 }, { 9, 6 } }, // protocol 3
{ 380, { 1, 6 }, { 1, 3 }, { 3, 1 } }, // protocol 4
{ 500, { 6, 14 }, { 1, 2 }, { 2, 1 } }, // protocol 5
{ 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)
{ 150, { 2, 62 }, { 1, 6 }, { 6, 1 }, false }, // protocol 7 (HS2303-PT, i. e. used in AUKEY Remote)
{ 200, { 3, 130}, { 7, 16 }, { 3, 16}, false}, // protocol 8 Conrad RS-200 RX
{ 200, { 130, 7 }, { 16, 7 }, { 16, 3 }, true}, // protocol 9 Conrad RS-200 TX
{ 365, { 18, 1 }, { 3, 1 }, { 1, 3 }, true }, // protocol 10 (1ByOne Doorbell)
{ 270, { 36, 1 }, { 1, 2 }, { 2, 1 }, true }, // protocol 11 (HT12E)
{ 320, { 36, 1 }, { 1, 2 }, { 2, 1 }, true } // protocol 12 (SM5212)
};

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;
volatile unsigned long RCSwitch::nReceivedValue = 0;
volatile unsigned int RCSwitch::nReceivedBitlength = 0;
volatile unsigned int RCSwitch::nReceivedDelay = 0;
volatile unsigned int RCSwitch::nReceivedProtocol = 0;
int RCSwitch::nReceiveTolerance = 60;
const unsigned int RCSwitch::nSeparationLimit = 4600;
const unsigned int RCSwitch::nSeparationLimit = 4300;
// 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.
Expand Down Expand Up @@ -122,7 +130,7 @@ void RCSwitch::setProtocol(int nProtocol) {
if (nProtocol < 1 || nProtocol > numProto) {
nProtocol = 1; // TODO: trigger an error, e.g. "bad protocol" ???
}
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
this->protocol = proto[nProtocol-1];
#else
memcpy_P(&this->protocol, &proto[nProtocol-1], sizeof(Protocol));
Expand Down Expand Up @@ -504,6 +512,9 @@ void RCSwitch::send(unsigned long code, unsigned int length) {
this->transmit(protocol.syncFactor);
}

// Disable transmit after sending (i.e., for inverted protocols)
digitalWrite(this->nTransmitterPin, LOW);

#if not defined( RCSwitchDisableReceiving )
// enable receiver again if we just disabled it
if (nReceiverInterrupt_backup != -1) {
Expand All @@ -516,9 +527,12 @@ void RCSwitch::send(unsigned long code, unsigned int length) {
* Transmit a single high-low pulse.
*/
void RCSwitch::transmit(HighLow pulses) {
digitalWrite(this->nTransmitterPin, HIGH);
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, LOW);
digitalWrite(this->nTransmitterPin, secondLogicLevel);
delayMicroseconds( this->protocol.pulseLength * pulses.low);
}

Expand Down Expand Up @@ -591,18 +605,39 @@ static inline unsigned int diff(int A, int B) {
*
*/
bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCount) {
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
const Protocol &pro = proto[p-1];
#else
Protocol pro;
memcpy_P(&pro, &proto[p-1], sizeof(Protocol));
#endif

unsigned long code = 0;
const unsigned int delay = RCSwitch::timings[0] / pro.syncFactor.low;
//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 (unsigned int i = 1; i < changeCount - 1; i += 2) {

/* 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) {
Expand All @@ -622,9 +657,10 @@ bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCoun
RCSwitch::nReceivedBitlength = (changeCount - 1) / 2;
RCSwitch::nReceivedDelay = delay;
RCSwitch::nReceivedProtocol = p;
return true;
}

return true;
return false;
}

void RECEIVE_ATTR RCSwitch::handleInterrupt() {
Expand All @@ -639,7 +675,7 @@ void RECEIVE_ATTR RCSwitch::handleInterrupt() {
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) {
if ((repeatCount==0) || (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
Expand Down
44 changes: 38 additions & 6 deletions src/RCSwitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
#include <string.h> /* memcpy */
#include <stdlib.h> /* abs */
#include <wiringPi.h>
#elif defined(SPARK)
#include "application.h"
#else
#include "WProgram.h"
#endif
Expand All @@ -54,7 +56,7 @@
#define RCSwitchDisableReceiving
#endif

// Number of maximum High/Low changes per packet.
// 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

Expand Down Expand Up @@ -100,16 +102,46 @@ class RCSwitch {
void setReceiveTolerance(int nPercent);
#endif

/**
* Description of a single pule, which consists of a high signal
* whose duration is "high" times the base pulse length, followed
* by a low signal lasting "low" times the base pulse length.
* Thus, the pulse overall lasts (high+low)*pulseLength
*/
struct HighLow {
uint8_t high;
uint8_t low;
};

/**
* A "protocol" describes how zero and one bits are encoded into high/low
* pulses.
*/
struct Protocol {
int pulseLength;
/** base pulse length in microseconds, e.g. 350 */
uint16_t pulseLength;

HighLow syncFactor;
HighLow zero;
HighLow one;

/**
* If true, interchange high and low logic levels in all transmissions.
*
* By default, RCSwitch assumes that any signals it sends or receives
* can be broken down into pulses which start with a high signal level,
* followed by a a low signal level. This is e.g. the case for the
* popular PT 2260 encoder chip, and thus many switches out there.
*
* But some devices do it the other way around, and start with a low
* signal level, followed by a high signal level, e.g. the HT6P20B. To
* accommodate this, one can set invertedSignal to true, which causes
* RCSwitch to change how it interprets any HighLow struct FOO: It will
* then assume transmissions start with a low signal lasting
* FOO.high*pulseLength microseconds, followed by a high signal lasting
* FOO.low*pulseLength microseconds.
*/
bool invertedSignal;
};

void setProtocol(Protocol protocol);
Expand All @@ -135,10 +167,10 @@ class RCSwitch {

#if not defined( RCSwitchDisableReceiving )
static int nReceiveTolerance;
static unsigned long nReceivedValue;
static unsigned int nReceivedBitlength;
static unsigned int nReceivedDelay;
static unsigned int nReceivedProtocol;
volatile static unsigned long nReceivedValue;
volatile static unsigned int nReceivedBitlength;
volatile static unsigned int nReceivedDelay;
volatile static unsigned int nReceivedProtocol;
const static unsigned int nSeparationLimit;
/*
* timings[0] contains sync timing, followed by a number of bits
Expand Down