Rewrote library to allow for multiple RX pins #2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
WARNING: This breaks the current API (although a fix is simple enough).
Rewrote example (
ReceiveDemo_Advanced.ino
) to use the new API.The number of RX pins must be defined at compile-time with
#define RCSWITCH_MAX_RX_PINS
.Root cause
Previously, if multiple pins were enabled at once (with
enableReceive(int interrupt)
), the interrupt handler for decoding signals was attached to both(/all) pins. But because interrupts must interface with the main program solely through global/static variables, and have no other information (e.g. callbacks), there is no way for the interrupt handler to know what pin actually triggered the interrupt. Therefore, when multiple pins were naïvely attached, noise from each pin--which would normally be filtered out--would be present, making it impossible to work with the data.There is the additional issue that the version of RCSwitch that Particle uses (repo here) is very out-of-date, which causes a few extra problems. The implementation of many functions is different from those in the official version, there is missing support for many protocols, and the API is slightly different. For the official RCSwitch library, there have been attempts in the past to implement multiple-pin RX (see here and here), but none have been successful. I have opened an issue detailing the changes needed to get multiple-pin RX actually working, but due to how behind the Particle library is, patching it into the official library is non-trivial (will either require porting or updating Particle's library to match the current official repo).
Solution
To address this issue, the simplest solution is to separate the global/static data used in the interrupt handler into different sets per RX pin (create an array), and size it based off a
#define
d variable. (e.g.#define RCSWITCH_MAX_RX_PINS 2
). This use of a macro#define
is acceptable because the number of RX pins needed is something that changes infrequently and is well-known to the developer, and also because this library is intended as performant embedded code (e.g. for MCUs like the ATtiny).I refactored all the data used in the interrupt handler into a struct (
RCSwitch::InterruptData
). The static variables are then replaced with an array of these, one per RX pin (RCSwitch::receiverInterrupts[RCSWITCH_MAX_RX_PINS]
). All of the code was simply extended to handle the array, and in most cases an additional parameter was added to specify which interrupt pin was intended. There is the issue of identifying each element of the array: I included aRCSwitch::InterruptData.interrupt
member in the struct for this purpose. (std::map
would be more straightforward but it is a lot of overhead.) There are also convenience functions which convert from array index to interrupt pin, and these functions should be inverses of the process since each pin can only be used for a single interrupt. (To actually perform the conversion, the array is simply searched for a matching interrupt pin. This is acceptable becauseRCSWITCH_MAX_RX_PINS
should be small, and this search should have a negligible performance impact.) Another thing to note: functions might take arguments dealing with array index or interrupt pin, and these different arguments are differentiated withint interrupt_i
andint interrupt
, respectively. (Most of the functions takingint interrupt_i
should beprivate
.)I also added an additional member,
int nInterruptSourcePin
, which is updated with the last RX pin to go high. This can be used to fetch which pin triggered the interrupt. In the interrupt handler itself, the data (in theRCSwitch::InterruptData
array) is tracked separately for each pin, with a newisPinPrevHigh
member in the data struct. This ensures that the correct member ofRCSwitch::receiverInterrupts
is updated by the handler.