Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Committer:
arnoz
Date:
Fri Oct 01 08:19:46 2021 +0000
Revision:
116:7a67265d7c19
Parent:
97:fc7727303038
- Correct information regarding your last merge

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 77:0b96f6867312 1 // IR Remote Protocols
mjr 77:0b96f6867312 2 //
mjr 77:0b96f6867312 3 // This file implements a handlers for specific IR protocols. A protocol
mjr 77:0b96f6867312 4 // is the set of rules that a particular IR remote uses to convert binary
mjr 77:0b96f6867312 5 // data to and from a series of timed infrared pulses. A protocol generally
mjr 77:0b96f6867312 6 // encompasses a bit-level encoding scheme (how each data bit is represented
mjr 77:0b96f6867312 7 // as one or more pulses of IR light) and a message or packet format (how a
mjr 77:0b96f6867312 8 // series of bits is assembled to form a higher level datatype, which in the
mjr 77:0b96f6867312 9 // case of an IR remote usually amounts to a representation of a key press
mjr 77:0b96f6867312 10 // on a remote control).
mjr 77:0b96f6867312 11 //
mjr 77:0b96f6867312 12 // Lots of companies make CE products with remotes, and many of them use
mjr 77:0b96f6867312 13 // their own custom protocols. There's some commonality; a few proprietary
mjr 77:0b96f6867312 14 // protocols, such as those from NEC and Philips, are quasi industry
mjr 77:0b96f6867312 15 // standards that multiple companies adopted, more or less intact. On
mjr 77:0b96f6867312 16 // the other hand, other companies not only have their own systems but
mjr 77:0b96f6867312 17 // use multiple proprietary systems across different products. So it'll
mjr 77:0b96f6867312 18 // never be possible for us to recognize every remote code out there.
mjr 77:0b96f6867312 19 // So we'll cover the widely used ones, and then add the rarer ones as
mjr 77:0b96f6867312 20 // needed.
mjr 77:0b96f6867312 21 //
mjr 77:0b96f6867312 22 // For each protocol we recognize, we define a subclass of IRReceiver
mjr 77:0b96f6867312 23 // that implements the code to encode and decode signals for the protocol.
mjr 77:0b96f6867312 24 // To send a command, we call the sender function for the protocol we want
mjr 77:0b96f6867312 25 // to use for the transmission. When we receive a signal, we run it through
mjr 77:0b96f6867312 26 // each protocol class's decode routine, to determine which protocol the
mjr 77:0b96f6867312 27 // signal was encoded with and what the signal means. The decoders can
mjr 77:0b96f6867312 28 // usually tell if a signal uses their protocol, since there's enough
mjr 77:0b96f6867312 29 // structure in most of the protocols that you can distinguish signals that
mjr 77:0b96f6867312 30 // use the protocol from those that don't. This allows the decoders to
mjr 77:0b96f6867312 31 // serve the dual purposes of decoding signals and also classifying them
mjr 77:0b96f6867312 32 // by protocol.
mjr 77:0b96f6867312 33 //
mjr 77:0b96f6867312 34 // To add support for a new protocol, we (a) define a class for it here,
mjr 77:0b96f6867312 35 // and (b) add an entry for the class to IRProtocolList.h. The list entry
mjr 77:0b96f6867312 36 // automatically adds the new protocol to the tables we use to look up the
mjr 77:0b96f6867312 37 // desired protocol class when sending, and to check each supported protocol
mjr 77:0b96f6867312 38 // when receiving.
mjr 77:0b96f6867312 39 //
mjr 77:0b96f6867312 40 // The protocol decoders operate in parallel: as a transmission is received,
mjr 77:0b96f6867312 41 // we run the signal through all of the decoders at the same time. This
mjr 77:0b96f6867312 42 // allows each decoder to keep track of the incoming pulse stream and
mjr 77:0b96f6867312 43 // recognize messages that conform to its protocol. The parallel operation
mjr 77:0b96f6867312 44 // means that each protocol object needs its own complete, independent
mjr 77:0b96f6867312 45 // receiver state.
mjr 77:0b96f6867312 46 //
mjr 77:0b96f6867312 47 // In contrast, there can only be one transmission in progress at a time,
mjr 77:0b96f6867312 48 // since a transmission obviously requires exclusive access to the IR LED.
mjr 77:0b96f6867312 49 // (You can't eve interleave two transmissions in theory, since the coding
mjr 77:0b96f6867312 50 // is all about pulse timing and order.) That means that we *don't* need
mjr 77:0b96f6867312 51 // separate independent transmitter state per object. That lets us save
mjr 77:0b96f6867312 52 // a little memory by using a single, shared transmitter state object,
mjr 77:0b96f6867312 53 // managed by the global transmitter class and passed to the current
mjr 77:0b96f6867312 54 // transmitter on each send. The exception would be any state that has
mjr 77:0b96f6867312 55 // to be maintained across, which would have to be tracked per protocol.
mjr 77:0b96f6867312 56 // The only common example is "toggle bits".
mjr 77:0b96f6867312 57 //
mjr 77:0b96f6867312 58
mjr 77:0b96f6867312 59 #ifndef _IRPROTOCOLS_H_
mjr 77:0b96f6867312 60 #define _IRPROTOCOLS_H_
mjr 77:0b96f6867312 61
mjr 77:0b96f6867312 62 #include <ctype.h>
mjr 77:0b96f6867312 63 #include "NewPwm.h"
mjr 77:0b96f6867312 64 #include "IRRemote.h"
mjr 77:0b96f6867312 65 #include "IRReceiver.h"
mjr 77:0b96f6867312 66 #include "IRProtocolID.h"
mjr 77:0b96f6867312 67 #include "IRCommand.h"
mjr 77:0b96f6867312 68
mjr 77:0b96f6867312 69 using namespace IRRemote;
mjr 77:0b96f6867312 70
mjr 77:0b96f6867312 71 struct DebugItem { char c; int t; DebugItem(char c, int t) : c(c),t(t) {}
mjr 77:0b96f6867312 72 DebugItem(char c) : c(c), t(0) { } DebugItem() : c(0), t(0) { }
mjr 77:0b96f6867312 73 };
mjr 77:0b96f6867312 74 extern CircBuf<DebugItem,256> debug;
mjr 77:0b96f6867312 75
mjr 77:0b96f6867312 76
mjr 77:0b96f6867312 77 // IR transmitter state object.
mjr 77:0b96f6867312 78 //
mjr 77:0b96f6867312 79 // We can only transmit one signal at a time, so we only need to keep
mjr 77:0b96f6867312 80 // track of the state of one transmission at a time. This lets us
mjr 77:0b96f6867312 81 // save a little memory by using a single combined state object that's
mjr 77:0b96f6867312 82 // shared by all transmitters. The transmitter currently operating
mjr 77:0b96f6867312 83 // uses the shared object for as long as the transmission takes.
mjr 77:0b96f6867312 84 struct IRTXState
mjr 77:0b96f6867312 85 {
mjr 77:0b96f6867312 86 // Time since the transmission started
mjr 77:0b96f6867312 87 Timer txTime;
mjr 77:0b96f6867312 88
mjr 77:0b96f6867312 89 // The command code we're transmitting
mjr 77:0b96f6867312 90 uint64_t cmdCode;
mjr 77:0b96f6867312 91
mjr 77:0b96f6867312 92 // Bit stream to transmit. Many of the protocols use a universal
mjr 77:0b96f6867312 93 // command representation (in IRCommand.code) that rearranges the
mjr 77:0b96f6867312 94 // bits from the transmission order. This is a scratch pad where the
mjr 77:0b96f6867312 95 // protocol handler can translate a command code back into transmission
mjr 77:0b96f6867312 96 // order once at the start of the transmission, then just shift bits out
mjr 77:0b96f6867312 97 // of here to transmit them in sequence.
mjr 77:0b96f6867312 98 uint64_t bitstream;
mjr 77:0b96f6867312 99
mjr 77:0b96f6867312 100 // The IR LED control pin
mjr 77:0b96f6867312 101 NewPwmOut *pin;
mjr 77:0b96f6867312 102
mjr 77:0b96f6867312 103 // Protocol ID
mjr 77:0b96f6867312 104 uint8_t protocolId;
mjr 77:0b96f6867312 105
mjr 77:0b96f6867312 106 // Transmission step. The meaning is up to the individual protocols,
mjr 77:0b96f6867312 107 // but generally this is used to keep track of the current structural
mjr 77:0b96f6867312 108 // part of the pulse stream we're generating. E.g., a protocol might
mjr 77:0b96f6867312 109 // use 0 for the header mark, 1 for the header space, etc.
mjr 77:0b96f6867312 110 uint8_t step;
mjr 77:0b96f6867312 111
mjr 77:0b96f6867312 112 // number of bits to transmit
mjr 77:0b96f6867312 113 uint8_t nbits;
mjr 77:0b96f6867312 114
mjr 77:0b96f6867312 115 // Current bit position within the data
mjr 77:0b96f6867312 116 uint8_t bit;
mjr 77:0b96f6867312 117
mjr 77:0b96f6867312 118 // Substep within the current bit. Many of the protocols represent each
mjr 77:0b96f6867312 119 // bit as multiple IR symbols, such as mark+space. This keeps track of
mjr 77:0b96f6867312 120 // the step within the current bit.
mjr 77:0b96f6867312 121 uint8_t bitstep;
mjr 77:0b96f6867312 122
mjr 77:0b96f6867312 123 // Repeat phase. Some protocols have rules about minimum repeat counts,
mjr 77:0b96f6867312 124 // or use different coding for auto-repeats. This lets the sender keep
mjr 77:0b96f6867312 125 // track of the current step. For example, the Sony protocol requires
mjr 77:0b96f6867312 126 // each message to be sent a minimum of 3 times. The NEC protocol uses
mjr 77:0b96f6867312 127 // a "ditto" code for each repeat of a code after the initial command.
mjr 77:0b96f6867312 128 // The OrtekMCE protocol requires a minimum of 2 sends per code, and has
mjr 77:0b96f6867312 129 // a position counter within the code that indicates which copy we're on.
mjr 77:0b96f6867312 130 uint8_t rep;
mjr 77:0b96f6867312 131
mjr 77:0b96f6867312 132 // Is the virtual button that initiated this transmission still pressed?
mjr 77:0b96f6867312 133 // The global transmitter sets this before each call to txStep() to let
mjr 77:0b96f6867312 134 // the protocol know if it should auto-repeat at the end of the code.
mjr 77:0b96f6867312 135 uint8_t pressed : 1;
mjr 77:0b96f6867312 136
mjr 77:0b96f6867312 137 // Use "ditto" codes when sending repeats? Some protocols use special
mjr 77:0b96f6867312 138 // coding for auto-repeats, so that receivers can tell whether a key
mjr 77:0b96f6867312 139 // is being held down or pressed repeatedly. But in some cases, the
mjr 77:0b96f6867312 140 // same protocol may be used with dittos on some devices but without
mjr 77:0b96f6867312 141 // dittos on other devices. It's therefore not always enough to know
mjr 77:0b96f6867312 142 // that the protocol supports dittos; we have to know separately whether
mjr 77:0b96f6867312 143 // the device we're sending to wants them. This flag lets the caller
mjr 77:0b96f6867312 144 // tell us which format to use. This is ignored if the protocol either
mjr 77:0b96f6867312 145 // never uses dittos or always does: in that case we'll do whatever the
mjr 77:0b96f6867312 146 // protocol specifies. To implement a "learning remote", you should
mjr 77:0b96f6867312 147 // make sure that the user holds down each key long enough for several
mjr 77:0b96f6867312 148 // repeats when learning codes, so that the learning remote can determine
mjr 77:0b96f6867312 149 // when dittos are used by observing how the repeats are sent from the
mjr 77:0b96f6867312 150 // reference remote. Then you can set this bit if you saw any ditto
mjr 77:0b96f6867312 151 // codes during training for a given key.
mjr 77:0b96f6867312 152 uint8_t dittos : 1;
mjr 77:0b96f6867312 153
mjr 77:0b96f6867312 154 // TX toggle bit. We provide this for protocols that need it. Note
mjr 77:0b96f6867312 155 // that this is a global toggle bit, so if we switch from transmitting
mjr 77:0b96f6867312 156 // one protocol to another and then return to the first, we'll lose
mjr 77:0b96f6867312 157 // continuity with the toggle sequence in the original protocol. But
mjr 77:0b96f6867312 158 // that shouldn't be a problem: the protocols with toggles only use it
mjr 77:0b96f6867312 159 // to distinguish two rapid presses of the same key in succession from
mjr 77:0b96f6867312 160 // auto-repeat while the key is held down. If we had an intervening
mjr 77:0b96f6867312 161 // transmission to another protocol, the original receiver will see a
mjr 77:0b96f6867312 162 // long gap between the earlier and later messages; the toggle bit isn't
mjr 77:0b96f6867312 163 // necessary in this case to tell that these were two key presses.
mjr 77:0b96f6867312 164 uint8_t toggle : 1;
mjr 77:0b96f6867312 165 };
mjr 77:0b96f6867312 166
mjr 77:0b96f6867312 167
mjr 77:0b96f6867312 168 // Base class for all protocols
mjr 77:0b96f6867312 169 //
mjr 77:0b96f6867312 170 // Note that most of the data parameters are set through virtuals rather
mjr 77:0b96f6867312 171 // than member variables. This helps minimize the RAM footprint.
mjr 77:0b96f6867312 172 //
mjr 77:0b96f6867312 173 class IRProtocol
mjr 77:0b96f6867312 174 {
mjr 77:0b96f6867312 175 public:
mjr 77:0b96f6867312 176 IRProtocol() { }
mjr 77:0b96f6867312 177 virtual ~IRProtocol() { }
mjr 77:0b96f6867312 178
mjr 77:0b96f6867312 179 // look up a protocol by ID
mjr 77:0b96f6867312 180 static IRProtocol *senderForId(int id);
mjr 77:0b96f6867312 181
mjr 77:0b96f6867312 182 // name and ID
mjr 77:0b96f6867312 183 virtual const char *name() const = 0;
mjr 77:0b96f6867312 184 virtual int id() const = 0;
mjr 77:0b96f6867312 185
mjr 77:0b96f6867312 186 // Are we a transmitter for the given protocol? Some protocol
mjr 77:0b96f6867312 187 // handlers send and receive variations on a protocol that have
mjr 77:0b96f6867312 188 // distinct protocol IDs, such as the various Sony codes at
mjr 77:0b96f6867312 189 // different bit sizes. By default, we assume we handle only
mjr 77:0b96f6867312 190 // our nominal protocol as returned by id().
mjr 77:0b96f6867312 191 virtual bool isSenderFor(int protocolId) const { return protocolId == id(); }
mjr 77:0b96f6867312 192
mjr 77:0b96f6867312 193 // parse a pulse on receive
mjr 77:0b96f6867312 194 virtual void rxPulse(IRRecvProIfc *receiver, uint32_t t, bool mark) = 0;
mjr 77:0b96f6867312 195
mjr 77:0b96f6867312 196 // PWM carrier frequency used for the IR signal, expressed as a PWM
mjr 97:fc7727303038 197 // cycle period in seconds. We use this to set the appropriate PWM
mjr 77:0b96f6867312 198 // frequency for transmissions. The most common carrier is 38kHz.
mjr 77:0b96f6867312 199 //
mjr 77:0b96f6867312 200 // We can't use adjust the carrier frequency for receiving signals, since
mjr 77:0b96f6867312 201 // the TSOP sensor we use does the demodulation at a fixed frequency.
mjr 77:0b96f6867312 202 // You can choose a frequency by choosing your sensor, since TSOP sensors
mjr 77:0b96f6867312 203 // are available in a range of carrier frequencies, but once you choose a
mjr 97:fc7727303038 204 // sensor we can't change its frequency in software. Fortunately, the
mjr 77:0b96f6867312 205 // TSOP384xx seems tolerant in practice of a fairly wide range around
mjr 97:fc7727303038 206 // its nominal 38kHz carrier. I've successfully tested it with remotes
mjr 97:fc7727303038 207 // documented to use frequencies from 36 to 40 kHz. Most CE remotes
mjr 97:fc7727303038 208 // fall within this range, so the 38kHz sensor makes a good universal
mjr 97:fc7727303038 209 // receiver.
mjr 77:0b96f6867312 210 virtual float pwmPeriod(IRTXState *state) const { return 26.31578947e-6f; } // 38kHz
mjr 77:0b96f6867312 211
mjr 97:fc7727303038 212 // PWM duty cycle when transmitting. This is the proportion of On to
mjr 97:fc7727303038 213 // Off time for each PWM pulse. A few of the IR protocols that have
mjr 97:fc7727303038 214 // official documentation do specify a duty cycle, so when this is laid
mjr 97:fc7727303038 215 // out in the spec, it's probably a good idea to use the same value in
mjr 97:fc7727303038 216 // our protocol implementation. In practice, though, it doesn't seem
mjr 97:fc7727303038 217 // to be an important parameter as far as the receivers are concerned,
mjr 97:fc7727303038 218 // and I'm not sure it actually matters for any of the protocols. To
mjr 97:fc7727303038 219 // the extent it's specified at all, they might just be documenting the
mjr 97:fc7727303038 220 // original manufacturer's implementation rather than specifying a
mjr 97:fc7727303038 221 // requirement.
mjr 77:0b96f6867312 222 virtual float pwmDutyCycle() const { return .3f; }
mjr 77:0b96f6867312 223
mjr 77:0b96f6867312 224 // Begin transmitting the given command. Before calling, the caller
mjr 77:0b96f6867312 225 // turns off the IR LED and sets its PWM period to the one given by
mjr 77:0b96f6867312 226 // our pwmPeriod() method. The caller also clears 'state', and then
mjr 77:0b96f6867312 227 // sets the 'cmd', 'pin', and 'pressed' fields. The rest of the struct
mjr 77:0b96f6867312 228 // is for the protocol handler's private use during the transmission.
mjr 77:0b96f6867312 229 // handler is free to store its interim state here to pass information
mjr 77:0b96f6867312 230 // from txStart() to txStep() and from one txStep() to the next.
mjr 77:0b96f6867312 231 //
mjr 77:0b96f6867312 232 // Subclass implementations should start by setting up 'state' with any
mjr 77:0b96f6867312 233 // data they need during the transmission. E.g., convert the code word
mjr 77:0b96f6867312 234 // value into a series of bits to transmit, and store this in the
mjr 77:0b96f6867312 235 // 'bitstream' field. Once that's set up, determine how long the initial
mjr 77:0b96f6867312 236 // gap should be (the IR off time between transmissions in the protocol),
mjr 77:0b96f6867312 237 // and return this time in microseconds. The caller will return to
mjr 77:0b96f6867312 238 // other work while the gap time is elapsing, then it will call txStep()
mjr 77:0b96f6867312 239 // to advance to the next step of the transmission.
mjr 77:0b96f6867312 240 //
mjr 77:0b96f6867312 241 // DON'T do a timer pause or spin wait here. This routine is called in
mjr 77:0b96f6867312 242 // interrupt context, so it must return as quickly as possible. All
mjr 77:0b96f6867312 243 // waits should be done simply by returning the desired wait time.
mjr 77:0b96f6867312 244 //
mjr 77:0b96f6867312 245 // By convention, implementations should start each transmission with a
mjr 77:0b96f6867312 246 // sufficient gap time (IR off) to allow senders to recognize the new
mjr 77:0b96f6867312 247 // transmission. It's best to put the gap time at the *start* of each
mjr 77:0b96f6867312 248 // new transmission rather than at the end because two consecutive
mjr 77:0b96f6867312 249 // transmissions might use different protocols with different timing
mjr 77:0b96f6867312 250 // requirements. If the first protocol has a short gap time and the
mjr 77:0b96f6867312 251 // second has a long gap time, putting the gap at the end of the first
mjr 77:0b96f6867312 252 // transmission would only use the shorter time, which might not be
mjr 77:0b96f6867312 253 // sufficient for the second protocol's receiver.
mjr 77:0b96f6867312 254 virtual int txStart(IRTXState *state) = 0;
mjr 77:0b96f6867312 255
mjr 77:0b96f6867312 256 // Continue a transmission with the next pulse step. This carries
mjr 77:0b96f6867312 257 // out the next step of the transmission, then returns a time value
mjr 77:0b96f6867312 258 // with the number of microseconds until the next event. For example,
mjr 77:0b96f6867312 259 // if the current step in the transmission is a 600us mark, turn on the
mjr 77:0b96f6867312 260 // IR transmitter, update 'state' to indicate that we're in the mark,
mjr 77:0b96f6867312 261 // and return 600 to tell the caller to go do other work while the
mjr 77:0b96f6867312 262 // mark is being sent. Don't wait or spin here, since this is called
mjr 77:0b96f6867312 263 // in interrupt context and should thus return as quickly as possible.
mjr 77:0b96f6867312 264 //
mjr 77:0b96f6867312 265 // Before calling this, the caller will update the 'pressed' field in
mjr 77:0b96f6867312 266 // 'state' to let us know if the virtual button is still being pressed.
mjr 77:0b96f6867312 267 // The protocol handler can auto-repeat if the button is still pressed
mjr 77:0b96f6867312 268 // at the end of the current transmission, if that's appropriate for
mjr 77:0b96f6867312 269 // the protocol. We let the handlers choose how to handle auto-repeat
mjr 77:0b96f6867312 270 // rather than trying to manage it globally, since there's a lot of
mjr 77:0b96f6867312 271 // variation in how it needs to be handled. Some protocols, for example,
mjr 77:0b96f6867312 272 // use "ditto" codes (distinct codes that mean "same as last time"
mjr 77:0b96f6867312 273 // rather than actually re-transmitting a full command), while others
mjr 77:0b96f6867312 274 // have internal position markers to indicate a series of repeats
mjr 77:0b96f6867312 275 // for one key press.
mjr 77:0b96f6867312 276 virtual int txStep(IRTXState *state) = 0;
mjr 77:0b96f6867312 277
mjr 77:0b96f6867312 278 // protocol singletons
mjr 77:0b96f6867312 279 static class IRProtocols *protocols;
mjr 77:0b96f6867312 280
mjr 77:0b96f6867312 281 // allocate the protocol singletons, if we haven't already
mjr 77:0b96f6867312 282 static void allocProtocols();
mjr 77:0b96f6867312 283
mjr 77:0b96f6867312 284 protected:
mjr 77:0b96f6867312 285 // report code with a specific protocol and sub-protocol
mjr 77:0b96f6867312 286 void reportCode(IRRecvProIfc *receiver, int pro, uint64_t code, bool3 toggle, bool3 ditto);
mjr 77:0b96f6867312 287 };
mjr 77:0b96f6867312 288
mjr 77:0b96f6867312 289 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 290 //
mjr 77:0b96f6867312 291 // Protocol containing a decoded command value
mjr 77:0b96f6867312 292 //
mjr 77:0b96f6867312 293 template<class CodeType> class IRPWithCode: public IRProtocol
mjr 77:0b96f6867312 294 {
mjr 77:0b96f6867312 295 public:
mjr 77:0b96f6867312 296 IRPWithCode()
mjr 77:0b96f6867312 297 {
mjr 77:0b96f6867312 298 rxState = 0;
mjr 77:0b96f6867312 299 bit = 0;
mjr 77:0b96f6867312 300 code = 0;
mjr 77:0b96f6867312 301 }
mjr 77:0b96f6867312 302
mjr 77:0b96f6867312 303 // Minimum gap on receive (space before header)
mjr 77:0b96f6867312 304 virtual uint32_t minRxGap() const = 0;
mjr 77:0b96f6867312 305
mjr 77:0b96f6867312 306 // Gap time to send on transmit between before the first code, and
mjr 77:0b96f6867312 307 // between repeats. By default, we use the the generic txGap() in
mjr 77:0b96f6867312 308 // both cases.
mjr 77:0b96f6867312 309 virtual uint32_t txGap(IRTXState *state) const = 0;
mjr 77:0b96f6867312 310 virtual uint32_t txPreGap(IRTXState *state) const { return txGap(state); }
mjr 77:0b96f6867312 311 virtual uint32_t txPostGap(IRTXState *state) const { return txGap(state); }
mjr 77:0b96f6867312 312
mjr 77:0b96f6867312 313 // Minimum number of repetitions when transmitting. Some codes
mjr 77:0b96f6867312 314 // (e.g., Sony) require a minimum repeat count, no matter how
mjr 77:0b96f6867312 315 // quickly the button was released. 1 means that only a single
mjr 77:0b96f6867312 316 // transmission is required, which is true of most codes.
mjr 77:0b96f6867312 317 virtual int txMinReps(IRTXState *state) const { return 1; }
mjr 77:0b96f6867312 318
mjr 77:0b96f6867312 319 // Header mark and space length. Most IR protocols have an initial
mjr 77:0b96f6867312 320 // mark and space of fixed length as a lead-in or header. Being of
mjr 77:0b96f6867312 321 // fixed length, they carry no data themselves, but they serve three
mjr 77:0b96f6867312 322 // useful functions: the initial mark can be used to set an AGC level
mjr 77:0b96f6867312 323 // if the receiver needs it (the TSOP sensors don't, but some older
mjr 77:0b96f6867312 324 // devices did); they can help distinguish one protocol from another
mjr 77:0b96f6867312 325 // by observing the distinctive timing of the header; and they serve
mjr 77:0b96f6867312 326 // as synchronization markers, by using timing that can't possibly
mjr 77:0b96f6867312 327 // occur in the middle of a code word to tell us unambiguously that
mjr 77:0b96f6867312 328 // we're at the start of a code word.
mjr 77:0b96f6867312 329 //
mjr 77:0b96f6867312 330 // Not all protocols have the lead-in mark and/or space. Some
mjr 77:0b96f6867312 331 // protocols simply open with the initial data bit, and others use
mjr 77:0b96f6867312 332 // an AGC mark but follow it immediately with a data bit, with no
mjr 77:0b96f6867312 333 // intervening fixed space.
mjr 77:0b96f6867312 334 //
mjr 77:0b96f6867312 335 // * If the protocol doesn't use a header mark at all but opens with
mjr 77:0b96f6867312 336 // a mark that's part of a data bit, set tHeaderMark() to 0.
mjr 77:0b96f6867312 337 //
mjr 77:0b96f6867312 338 // * If the protocol has a fixed AGC mark, but follows it with a
mjr 77:0b96f6867312 339 // varying-length space that's part of the first data bit, set
mjr 77:0b96f6867312 340 // tHeaderMark() to the fixed mark length and set tHeaderSpace() to 0.
mjr 77:0b96f6867312 341 //
mjr 77:0b96f6867312 342 virtual uint32_t tHeaderMark() const = 0;
mjr 77:0b96f6867312 343 virtual uint32_t tHeaderSpace() const = 0;
mjr 77:0b96f6867312 344
mjr 77:0b96f6867312 345 // Can the header space be adjacent to a data space? In most protocols,
mjr 77:0b96f6867312 346 // the header space is of constant length because it's always followed
mjr 77:0b96f6867312 347 // by a mark. This is accomplished in some protocols with explicit
mjr 77:0b96f6867312 348 // structural marks; in some, it happens naturally because all bits start
mjr 77:0b96f6867312 349 // with marks (e.g., NEC, Sony); and in others, the protocol requires a
mjr 77:0b96f6867312 350 // a "start bit" with a fixed 0 or 1 value whose representation starts
mjr 77:0b96f6867312 351 // with a mark (e.g., RC6). But in a few protocols (e.g., OrtekMCE),
mjr 77:0b96f6867312 352 // there's no such care taken, so the header space can flow into a space
mjr 77:0b96f6867312 353 // at the start of the first data bit. Set this to true for such
mjr 77:0b96f6867312 354 // protocols, and we'll divvy up the space after the header mark into
mjr 77:0b96f6867312 355 // a fixed part and a data portion for the first bit.
mjr 77:0b96f6867312 356 virtual bool headerSpaceToData() const { return false; }
mjr 77:0b96f6867312 357
mjr 77:0b96f6867312 358 // Ditto header. For codes with dittos that use a distinct header
mjr 77:0b96f6867312 359 // format, this gives the header timing that identifies a ditto.
mjr 77:0b96f6867312 360 // Return zero for a code that doesn't use dittos at all or encodes
mjr 77:0b96f6867312 361 // them in some other way than a distinctive header (e.g., in the
mjr 77:0b96f6867312 362 // payload data).
mjr 77:0b96f6867312 363 virtual uint32_t tDittoMark() const { return 0; }
mjr 77:0b96f6867312 364 virtual uint32_t tDittoSpace() const { return 0; }
mjr 77:0b96f6867312 365
mjr 77:0b96f6867312 366 // Stop mark length. Many codes have a fixed-length mark following
mjr 77:0b96f6867312 367 // the data bits. Return 0 if there's no final mark.
mjr 77:0b96f6867312 368 virtual uint32_t tStopMark() const { return 0; }
mjr 77:0b96f6867312 369
mjr 77:0b96f6867312 370 // Number of bits in the code. For protocols with multiple bit
mjr 77:0b96f6867312 371 // lengths, use the longest here.
mjr 77:0b96f6867312 372 virtual int nbits() const = 0;
mjr 77:0b96f6867312 373
mjr 77:0b96f6867312 374 // true -> bits arrive LSB-first, false -> MSB first
mjr 77:0b96f6867312 375 virtual bool lsbFirst() const { return true; }
mjr 77:0b96f6867312 376
mjr 77:0b96f6867312 377 // Pulse processing state machine.
mjr 77:0b96f6867312 378 // This state machine handles the basic protocol structure used by
mjr 77:0b96f6867312 379 // most IR remotes:
mjr 77:0b96f6867312 380 //
mjr 77:0b96f6867312 381 // Header mark of fixed duration
mjr 77:0b96f6867312 382 // Header space (possibly followed directly by the first bit's space)
mjr 77:0b96f6867312 383 // Data bits
mjr 77:0b96f6867312 384 // Stop mark
mjr 77:0b96f6867312 385 // Gap between codes
mjr 77:0b96f6867312 386 // Ditto header mark } a pattern that's distinguishable from
mjr 77:0b96f6867312 387 // Ditto header space } the standard header mark
mjr 77:0b96f6867312 388 // Ditto data bits
mjr 77:0b96f6867312 389 // Gap betwee codes
mjr 77:0b96f6867312 390 //
mjr 77:0b96f6867312 391 // The state machine can handle protocols that use all of these sections,
mjr 77:0b96f6867312 392 // or only a subset of them. For example, a few protocols (Denon, RC5)
mjr 77:0b96f6867312 393 // have no header at all and start directly wtih the data bits. Most
mjr 77:0b96f6867312 394 // protocols have no "ditto" coding and just repeat the main code to
mjr 77:0b96f6867312 395 // signal auto-repeat.
mjr 77:0b96f6867312 396 //
mjr 77:0b96f6867312 397 // The monolithic state machine switch looks kind of ugly, but it seems
mjr 77:0b96f6867312 398 // to be the cleanest way to handle this. My initial design was more OO,
mjr 77:0b96f6867312 399 // using virtual subroutines to handle each step. But that turned out to
mjr 77:0b96f6867312 400 // be fragile, because there was too much interaction between the handlers
mjr 77:0b96f6867312 401 // and the overall state machine sequencing. The monolithic switch actually
mjr 77:0b96f6867312 402 // seems much cleaner in practice. The variations are all handled through
mjr 77:0b96f6867312 403 // simple data parameters. The resulting design isn't as flexible in
mjr 77:0b96f6867312 404 // principle as something more virtualized at each step, but nearly all
mjr 77:0b96f6867312 405 // of the IR protocols I've seen so far are so close to the same basic
mjr 77:0b96f6867312 406 // structure that this routine works for practically everything. Any
mjr 77:0b96f6867312 407 // protocols that don't fit the same structure will be best served by
mjr 77:0b96f6867312 408 // replacing the whole state machine for the individual protocols.
mjr 77:0b96f6867312 409 virtual void rxPulse(IRRecvProIfc *receiver, uint32_t t, bool mark)
mjr 77:0b96f6867312 410 {
mjr 77:0b96f6867312 411 uint32_t tRef;
mjr 77:0b96f6867312 412 switch (rxState)
mjr 77:0b96f6867312 413 {
mjr 77:0b96f6867312 414 case 0:
mjr 77:0b96f6867312 415 s0:
mjr 77:0b96f6867312 416 // Initial gap or inter-code gap. When we see a space of
mjr 77:0b96f6867312 417 // sufficient length, switch to Header Mark mode.
mjr 77:0b96f6867312 418 rxState = (!mark && t > minRxGap() ? 1 : 0);
mjr 77:0b96f6867312 419 break;
mjr 77:0b96f6867312 420
mjr 77:0b96f6867312 421 case 1:
mjr 77:0b96f6867312 422 s1:
mjr 77:0b96f6867312 423 // Header mark. If the protocol has no header mark, go
mjr 77:0b96f6867312 424 // straight to the data section. Otherwise, if we have
mjr 77:0b96f6867312 425 // a mark that matches the header mark we're expecting,
mjr 77:0b96f6867312 426 // go to Header Space mode. Otherwise, we're probably in
mjr 77:0b96f6867312 427 // the middle of a code that we missed the beginning of, or
mjr 77:0b96f6867312 428 // we're just receiving a code using another protocol. Go
mjr 77:0b96f6867312 429 // back to Gap mode - that will ignore everything until we
mjr 77:0b96f6867312 430 // get radio silence for long enough to be sure we're
mjr 77:0b96f6867312 431 // starting a brand new code word.
mjr 77:0b96f6867312 432 if ((tRef = tHeaderMark()) == 0)
mjr 77:0b96f6867312 433 goto s3;
mjr 77:0b96f6867312 434 rxState = (mark && inRange(t, tRef) ? 2 : 0);
mjr 77:0b96f6867312 435 break;
mjr 77:0b96f6867312 436
mjr 77:0b96f6867312 437 case 2:
mjr 77:0b96f6867312 438 s2:
mjr 77:0b96f6867312 439 // Header space. If the protocol doesn't have a header
mjr 77:0b96f6867312 440 // space, go straight to the data.
mjr 77:0b96f6867312 441 if ((tRef = tHeaderSpace()) == 0)
mjr 77:0b96f6867312 442 goto s3;
mjr 77:0b96f6867312 443
mjr 77:0b96f6867312 444 // If this protocol has an undelimited header space, make
mjr 77:0b96f6867312 445 // sure this space is long enough to qualify, but allow it
mjr 77:0b96f6867312 446 // to be longer. If it qualifies, deduct the header space
mjr 77:0b96f6867312 447 // span from it and apply the balance as the first data bit.
mjr 77:0b96f6867312 448 if (headerSpaceToData() && aboveRange(t, tRef, tRef))
mjr 77:0b96f6867312 449 {
mjr 77:0b96f6867312 450 t -= tRef;
mjr 77:0b96f6867312 451 goto s3;
mjr 77:0b96f6867312 452 }
mjr 77:0b96f6867312 453
mjr 77:0b96f6867312 454 // If we have a space matching the header space, enter the
mjr 77:0b96f6867312 455 // data section.
mjr 77:0b96f6867312 456 if (!mark && inRange(t, tRef))
mjr 77:0b96f6867312 457 {
mjr 77:0b96f6867312 458 rxState = 3;
mjr 77:0b96f6867312 459 break;
mjr 77:0b96f6867312 460 }
mjr 77:0b96f6867312 461
mjr 77:0b96f6867312 462 // Not a match - go back to gap mode
mjr 77:0b96f6867312 463 goto s0;
mjr 77:0b96f6867312 464
mjr 77:0b96f6867312 465 case 3:
mjr 77:0b96f6867312 466 s3:
mjr 77:0b96f6867312 467 // enter data section
mjr 77:0b96f6867312 468 rxReset();
mjr 77:0b96f6867312 469 if (mark)
mjr 77:0b96f6867312 470 goto s4;
mjr 77:0b96f6867312 471 else
mjr 77:0b96f6867312 472 goto s5;
mjr 77:0b96f6867312 473
mjr 77:0b96f6867312 474 case 4:
mjr 77:0b96f6867312 475 s4:
mjr 77:0b96f6867312 476 // data mark
mjr 77:0b96f6867312 477 if (mark && rxMark(receiver, t))
mjr 77:0b96f6867312 478 {
mjr 77:0b96f6867312 479 rxState = bit < nbits() ? 5 : 7;
mjr 77:0b96f6867312 480 break;
mjr 77:0b96f6867312 481 }
mjr 77:0b96f6867312 482 goto s0;
mjr 77:0b96f6867312 483
mjr 77:0b96f6867312 484 case 5:
mjr 77:0b96f6867312 485 s5:
mjr 77:0b96f6867312 486 // data space
mjr 77:0b96f6867312 487 if (!mark && rxSpace(receiver, t))
mjr 77:0b96f6867312 488 {
mjr 77:0b96f6867312 489 rxState = bit < nbits() ? 4 : 6;
mjr 77:0b96f6867312 490 break;
mjr 77:0b96f6867312 491 }
mjr 77:0b96f6867312 492 else if (!mark && t > minRxGap())
mjr 77:0b96f6867312 493 goto s7;
mjr 77:0b96f6867312 494 goto s0;
mjr 77:0b96f6867312 495
mjr 77:0b96f6867312 496 case 6:
mjr 77:0b96f6867312 497 // stop mark - if we don't have a mark, go to the gap instead
mjr 77:0b96f6867312 498 if (!mark)
mjr 77:0b96f6867312 499 goto s7;
mjr 77:0b96f6867312 500
mjr 77:0b96f6867312 501 // check to see if the protocol even has a stop mark
mjr 77:0b96f6867312 502 if ((tRef = tStopMark()) == 0)
mjr 77:0b96f6867312 503 {
mjr 77:0b96f6867312 504 // The protocol has no stop mark, and we've processed
mjr 77:0b96f6867312 505 // the last data bit. Close the data section and go
mjr 77:0b96f6867312 506 // straight to the next header.
mjr 77:0b96f6867312 507 rxClose(receiver, false);
mjr 77:0b96f6867312 508 goto s8;
mjr 77:0b96f6867312 509 }
mjr 77:0b96f6867312 510
mjr 77:0b96f6867312 511 // there is a mark - make sure it's in range
mjr 77:0b96f6867312 512 if (inRange(t, tRef))
mjr 77:0b96f6867312 513 {
mjr 77:0b96f6867312 514 rxState = 7;
mjr 77:0b96f6867312 515 break;
mjr 77:0b96f6867312 516 }
mjr 77:0b96f6867312 517 goto s0;
mjr 77:0b96f6867312 518
mjr 77:0b96f6867312 519 case 7:
mjr 77:0b96f6867312 520 s7:
mjr 77:0b96f6867312 521 // end of data - require minimum gap
mjr 77:0b96f6867312 522 if (!mark && t > minRxGap())
mjr 77:0b96f6867312 523 {
mjr 77:0b96f6867312 524 // close the data section
mjr 77:0b96f6867312 525 rxClose(receiver, false);
mjr 77:0b96f6867312 526 rxState = 8;
mjr 77:0b96f6867312 527 break;
mjr 77:0b96f6867312 528 }
mjr 77:0b96f6867312 529 goto s0;
mjr 77:0b96f6867312 530
mjr 77:0b96f6867312 531 case 8:
mjr 77:0b96f6867312 532 s8:
mjr 77:0b96f6867312 533 // Ditto header. If the protocol has a ditto header at all,
mjr 77:0b96f6867312 534 // and this mark matches, proceed to the ditto space. Otherwise
mjr 77:0b96f6867312 535 // try interepreting this as a new regular header instead.
mjr 77:0b96f6867312 536 if (mark && (tRef = tDittoMark()) != 0 && inRange(t, tRef))
mjr 77:0b96f6867312 537 {
mjr 77:0b96f6867312 538 rxState = 9;
mjr 77:0b96f6867312 539 break;
mjr 77:0b96f6867312 540 }
mjr 77:0b96f6867312 541 goto s1;
mjr 77:0b96f6867312 542
mjr 77:0b96f6867312 543 case 9:
mjr 77:0b96f6867312 544 // Ditto space. If this doesn't match the ditto space, and
mjr 77:0b96f6867312 545 // the ditto header and regular header are the same, try
mjr 77:0b96f6867312 546 // re-interpreting the space as a new regular header space.
mjr 77:0b96f6867312 547 if (!mark && (tRef = tDittoSpace()) != 0 && inRange(t, tRef))
mjr 77:0b96f6867312 548 {
mjr 77:0b96f6867312 549 rxState = 10;
mjr 77:0b96f6867312 550 break;
mjr 77:0b96f6867312 551 }
mjr 77:0b96f6867312 552 else if (!mark && tDittoMark() == tHeaderMark())
mjr 77:0b96f6867312 553 goto s2;
mjr 77:0b96f6867312 554 goto s0;
mjr 77:0b96f6867312 555
mjr 77:0b96f6867312 556 case 10:
mjr 77:0b96f6867312 557 // Enter ditto data
mjr 77:0b96f6867312 558 rxDittoReset();
mjr 77:0b96f6867312 559 goto s11;
mjr 77:0b96f6867312 560
mjr 77:0b96f6867312 561 case 11:
mjr 77:0b96f6867312 562 s11:
mjr 77:0b96f6867312 563 // Ditto data - mark
mjr 77:0b96f6867312 564 if (mark && rxMark(receiver, t))
mjr 77:0b96f6867312 565 {
mjr 77:0b96f6867312 566 rxState = bit < nbits() ? 12 : 13;
mjr 77:0b96f6867312 567 break;
mjr 77:0b96f6867312 568 }
mjr 77:0b96f6867312 569 goto s0;
mjr 77:0b96f6867312 570
mjr 77:0b96f6867312 571 case 12:
mjr 77:0b96f6867312 572 // data space
mjr 77:0b96f6867312 573 if (!mark && rxSpace(receiver, t))
mjr 77:0b96f6867312 574 {
mjr 77:0b96f6867312 575 rxState = bit < nbits() ? 11 : 13;
mjr 77:0b96f6867312 576 break;
mjr 77:0b96f6867312 577 }
mjr 77:0b96f6867312 578 else if (!mark && t > minRxGap())
mjr 77:0b96f6867312 579 goto s13;
mjr 77:0b96f6867312 580 goto s0;
mjr 77:0b96f6867312 581
mjr 77:0b96f6867312 582 case 13:
mjr 77:0b96f6867312 583 s13:
mjr 77:0b96f6867312 584 // end ditto data
mjr 77:0b96f6867312 585 if (!mark && t > minRxGap())
mjr 77:0b96f6867312 586 {
mjr 77:0b96f6867312 587 // close the ditto data section
mjr 77:0b96f6867312 588 rxClose(receiver, true);
mjr 77:0b96f6867312 589 rxState = 8;
mjr 77:0b96f6867312 590 break;
mjr 77:0b96f6867312 591 }
mjr 77:0b96f6867312 592 goto s0;
mjr 77:0b96f6867312 593 }
mjr 77:0b96f6867312 594
mjr 77:0b96f6867312 595 // if this is a space longer than the timeout, go into idle mode
mjr 77:0b96f6867312 596 if (!mark && t >= IRReceiver::MAX_PULSE)
mjr 77:0b96f6867312 597 rxIdle(receiver);
mjr 77:0b96f6867312 598 }
mjr 77:0b96f6867312 599
mjr 77:0b96f6867312 600 // Start transmission. By convention, we start each transmission with
mjr 77:0b96f6867312 601 // a gap of sufficient length to allow receivers to recognize a new
mjr 77:0b96f6867312 602 // transmission. The only protocol-specific work we usually have to
mjr 77:0b96f6867312 603 // do here is to prepare a bit string to send.
mjr 77:0b96f6867312 604 virtual int txStart(IRTXState *state)
mjr 77:0b96f6867312 605 {
mjr 77:0b96f6867312 606 // convert the code into a bitstream to send
mjr 77:0b96f6867312 607 codeToBitstream(state);
mjr 77:0b96f6867312 608
mjr 77:0b96f6867312 609 // transmit the initial gap to make sure we've been silent long enough
mjr 77:0b96f6867312 610 return txPreGap(state);
mjr 77:0b96f6867312 611 }
mjr 77:0b96f6867312 612
mjr 77:0b96f6867312 613 // Continue transmission. Most protocols have a similar structure,
mjr 77:0b96f6867312 614 // with a header mark, header gap, data section, and trailing gap.
mjr 77:0b96f6867312 615 // We implement the framework for this common structure with a
mjr 77:0b96f6867312 616 // simple state machine:
mjr 77:0b96f6867312 617 //
mjr 77:0b96f6867312 618 // state 0 = done with gap, transmitting header mark
mjr 77:0b96f6867312 619 // state 1 = done with header, transmitting header space
mjr 77:0b96f6867312 620 // state 2 = transmitting data bits, via txDataStep() in subclasses
mjr 77:0b96f6867312 621 // state 3 = done with data, sending post-code gap
mjr 77:0b96f6867312 622 //
mjr 77:0b96f6867312 623 // Subclasses for protocols that don't follow the usual structure can
mjr 77:0b96f6867312 624 // override this entire routine as needed and redefine these internal
mjr 77:0b96f6867312 625 // states. Protocols that match the common structure will only have
mjr 77:0b96f6867312 626 // to define txDataStep().
mjr 77:0b96f6867312 627 //
mjr 77:0b96f6867312 628 // Returns the time to the next step, or a negative value if we're
mjr 77:0b96f6867312 629 // done with the transmission.
mjr 77:0b96f6867312 630 virtual int txStep(IRTXState *state)
mjr 77:0b96f6867312 631 {
mjr 77:0b96f6867312 632 // The individual step handlers can return 0 to indicate
mjr 77:0b96f6867312 633 // that we should go immediately to the next step without
mjr 77:0b96f6867312 634 // a delay, so iterate as long as they return 0.
mjr 77:0b96f6867312 635 for (;;)
mjr 77:0b96f6867312 636 {
mjr 77:0b96f6867312 637 // Use the first-code or "ditto" handling, as appropriate
mjr 77:0b96f6867312 638 int t = state->rep > 0 && state->dittos ?
mjr 77:0b96f6867312 639 txDittoStep(state) :
mjr 77:0b96f6867312 640 txMainStep(state);
mjr 77:0b96f6867312 641
mjr 77:0b96f6867312 642 // If it's a positive time, it's a delay; if it's a negative
mjr 77:0b96f6867312 643 // time, it's the end of the transmission. In either case,
mjr 77:0b96f6867312 644 // return the time to the main transmitter routine. If it's
mjr 77:0b96f6867312 645 // zero, though, it means to proceed without delay, so we'll
mjr 77:0b96f6867312 646 // simply continue iterating.
mjr 77:0b96f6867312 647 if (t != 0)
mjr 77:0b96f6867312 648 return t;
mjr 77:0b96f6867312 649 }
mjr 77:0b96f6867312 650 }
mjr 77:0b96f6867312 651
mjr 77:0b96f6867312 652 // Main transmission handler. This handles the txStep() work for
mjr 77:0b96f6867312 653 // the first code in a repeat group.
mjr 77:0b96f6867312 654 virtual int txMainStep(IRTXState *state)
mjr 77:0b96f6867312 655 {
mjr 77:0b96f6867312 656 // One state might transition directly to the next state
mjr 77:0b96f6867312 657 // without any time delay, so keep going until we come to
mjr 77:0b96f6867312 658 // a wait state.
mjr 77:0b96f6867312 659 int t;
mjr 77:0b96f6867312 660 for (;;)
mjr 77:0b96f6867312 661 {
mjr 77:0b96f6867312 662 switch (state->step)
mjr 77:0b96f6867312 663 {
mjr 77:0b96f6867312 664 case 0:
mjr 77:0b96f6867312 665 // Finished the pre-code gap. This is the actual start
mjr 77:0b96f6867312 666 // of the transmission, so mark the time.
mjr 77:0b96f6867312 667 state->txTime.reset();
mjr 77:0b96f6867312 668
mjr 77:0b96f6867312 669 // if there's a header mark, transmit it
mjr 77:0b96f6867312 670 state->step++;
mjr 77:0b96f6867312 671 if ((t = this->tHeaderMark()) > 0)
mjr 77:0b96f6867312 672 {
mjr 77:0b96f6867312 673 state->pin->write(pwmDutyCycle());
mjr 77:0b96f6867312 674 return t;
mjr 77:0b96f6867312 675 }
mjr 77:0b96f6867312 676 break;
mjr 77:0b96f6867312 677
mjr 77:0b96f6867312 678 case 1:
mjr 77:0b96f6867312 679 // finished header mark, start header space
mjr 77:0b96f6867312 680 state->step++;
mjr 77:0b96f6867312 681 if ((t = this->tHeaderSpace()) > 0)
mjr 77:0b96f6867312 682 {
mjr 77:0b96f6867312 683 state->pin->write(0);
mjr 77:0b96f6867312 684 return this->tHeaderSpace();
mjr 77:0b96f6867312 685 }
mjr 77:0b96f6867312 686 break;
mjr 77:0b96f6867312 687
mjr 77:0b96f6867312 688 case 2:
mjr 77:0b96f6867312 689 // data section - this is up to the subclass
mjr 77:0b96f6867312 690 if ((t = txDataStep(state)) != 0)
mjr 77:0b96f6867312 691 return t;
mjr 77:0b96f6867312 692 break;
mjr 77:0b96f6867312 693
mjr 77:0b96f6867312 694 case 3:
mjr 77:0b96f6867312 695 // done with data; send the stop mark, if applicable
mjr 77:0b96f6867312 696 state->step++;
mjr 77:0b96f6867312 697 if ((t = tStopMark()) > 0)
mjr 77:0b96f6867312 698 {
mjr 77:0b96f6867312 699 state->pin->write(pwmDutyCycle());
mjr 77:0b96f6867312 700 return t;
mjr 77:0b96f6867312 701 }
mjr 77:0b96f6867312 702 break;
mjr 77:0b96f6867312 703
mjr 77:0b96f6867312 704 case 4:
mjr 77:0b96f6867312 705 // post-code gap
mjr 77:0b96f6867312 706 state->step++;
mjr 77:0b96f6867312 707 state->pin->write(0);
mjr 77:0b96f6867312 708 if ((t = this->txPostGap(state)) > 0)
mjr 77:0b96f6867312 709 return t;
mjr 77:0b96f6867312 710 break;
mjr 77:0b96f6867312 711
mjr 77:0b96f6867312 712 default:
mjr 77:0b96f6867312 713 // Done with the iteration. Finalize the transmission;
mjr 77:0b96f6867312 714 // this will figure out if we're going to repeat.
mjr 77:0b96f6867312 715 return this->txEnd(state);
mjr 77:0b96f6867312 716 }
mjr 77:0b96f6867312 717 }
mjr 77:0b96f6867312 718 }
mjr 77:0b96f6867312 719
mjr 77:0b96f6867312 720 // Ditto step handler. This handles txStep() work for a repeated
mjr 77:0b96f6867312 721 // code. Most protocols just re-transmit the same code each time,
mjr 77:0b96f6867312 722 // so by default we use the main step handling. Subclasses for
mjr 77:0b96f6867312 723 // protocols that transmit different codes on repeat (such as NEC)
mjr 77:0b96f6867312 724 // can override this to send the ditto code instead.
mjr 77:0b96f6867312 725 virtual int txDittoStep(IRTXState *state)
mjr 77:0b96f6867312 726 {
mjr 77:0b96f6867312 727 return txMainStep(state);
mjr 77:0b96f6867312 728 }
mjr 77:0b96f6867312 729
mjr 77:0b96f6867312 730 // Handle a txStep() iteration for a data bit. Subclasses must
mjr 77:0b96f6867312 731 // override this to handle the particulars of sending the data bits.
mjr 77:0b96f6867312 732 // At the end of the data transmission, the subclass should increment
mjr 77:0b96f6867312 733 // state->step to tell us that we've reached the post-code gap.
mjr 77:0b96f6867312 734 virtual int txDataStep(IRTXState *state)
mjr 77:0b96f6867312 735 {
mjr 77:0b96f6867312 736 state->step++;
mjr 77:0b96f6867312 737 return 0;
mjr 77:0b96f6867312 738 }
mjr 77:0b96f6867312 739
mjr 77:0b96f6867312 740 // Send the stop bit, if applicable. If there's no stop bit or
mjr 77:0b96f6867312 741 // equivalent, simply increment state->step and return 0;
mjr 77:0b96f6867312 742 int txStopBit(IRTXState *state)
mjr 77:0b96f6867312 743 {
mjr 77:0b96f6867312 744 state->step++;
mjr 77:0b96f6867312 745 return 0;
mjr 77:0b96f6867312 746 }
mjr 77:0b96f6867312 747
mjr 77:0b96f6867312 748 // Handle the end of a transmission. This figures out if we're
mjr 77:0b96f6867312 749 // going to auto-repeat, and if so, resets for the next iteration.
mjr 77:0b96f6867312 750 // If we're going to repeat, we return the time to the next tx step,
mjr 77:0b96f6867312 751 // as usual. If the transmission is done, we return -1 to indicate
mjr 77:0b96f6867312 752 // that no more steps are required.
mjr 77:0b96f6867312 753 int txEnd(IRTXState *state)
mjr 77:0b96f6867312 754 {
mjr 77:0b96f6867312 755 // count the repeat
mjr 77:0b96f6867312 756 state->rep++;
mjr 77:0b96f6867312 757
mjr 77:0b96f6867312 758 // If the button is still down, or we haven't reached our minimum
mjr 77:0b96f6867312 759 // repetition count, repeat the code.
mjr 77:0b96f6867312 760 if (state->pressed || state->rep < txMinReps(state))
mjr 77:0b96f6867312 761 {
mjr 77:0b96f6867312 762 // return to the first transmission step
mjr 77:0b96f6867312 763 state->step = 0;
mjr 77:0b96f6867312 764 state->bit = 0;
mjr 77:0b96f6867312 765 state->bitstep = 0;
mjr 77:0b96f6867312 766
mjr 77:0b96f6867312 767 // re-generate the bitstream, in case we need to encode positional
mjr 77:0b96f6867312 768 // information such as a toggle bit or position counter
mjr 77:0b96f6867312 769 codeToBitstream(state);
mjr 77:0b96f6867312 770
mjr 77:0b96f6867312 771 // restart the transmission timer
mjr 77:0b96f6867312 772 state->txTime.reset();
mjr 77:0b96f6867312 773
mjr 77:0b96f6867312 774 // we can go immediately to the next step, so return a zero delay
mjr 77:0b96f6867312 775 return 0;
mjr 77:0b96f6867312 776 }
mjr 77:0b96f6867312 777
mjr 77:0b96f6867312 778 // we're well and truly done - tell the caller not to call us
mjr 77:0b96f6867312 779 // again by returning a negative time interval
mjr 77:0b96f6867312 780 return -1;
mjr 77:0b96f6867312 781 }
mjr 77:0b96f6867312 782
mjr 77:0b96f6867312 783 protected:
mjr 77:0b96f6867312 784 // Reset the receiver. This is called when the receiver enters
mjr 77:0b96f6867312 785 // the data section of the frame, after parsing the header (or
mjr 77:0b96f6867312 786 // after a gap, if the protocol doesn't use a header).
mjr 77:0b96f6867312 787 virtual void rxReset()
mjr 77:0b96f6867312 788 {
mjr 77:0b96f6867312 789 bit = 0;
mjr 77:0b96f6867312 790 code = 0;
mjr 77:0b96f6867312 791 }
mjr 77:0b96f6867312 792
mjr 77:0b96f6867312 793 // Reset on entering a new ditto frame. This is called after
mjr 77:0b96f6867312 794 // parsing a "ditto" header. This is only needed for protocols
mjr 77:0b96f6867312 795 // that use distinctive ditto framing.
mjr 77:0b96f6867312 796 virtual void rxDittoReset() { }
mjr 77:0b96f6867312 797
mjr 77:0b96f6867312 798 // Receiver is going idle. This is called any time we get a space
mjr 77:0b96f6867312 799 // (IR OFF) that exceeds the general receiver timeout, regardless
mjr 77:0b96f6867312 800 // of protocol state.
mjr 77:0b96f6867312 801 virtual void rxIdle(IRRecvProIfc *receiver) { }
mjr 77:0b96f6867312 802
mjr 77:0b96f6867312 803 // Parse a data mark or space. If the symbol is valid, shifts the
mjr 77:0b96f6867312 804 // bit into 'code' (the code word under construction) and returns
mjr 77:0b96f6867312 805 // true. If the symbol is invalid, returns false. Updates the
mjr 77:0b96f6867312 806 // bit counter in 'bit' if this symbol finishes a bit.
mjr 77:0b96f6867312 807 virtual bool rxMark(IRRecvProIfc *receiver, uint32_t t) { return false; }
mjr 77:0b96f6867312 808 virtual bool rxSpace(IRRecvProIfc *receiver, uint32_t t) { return false; }
mjr 77:0b96f6867312 809
mjr 77:0b96f6867312 810 // Report the decoded value in our internal register, if it's valid.
mjr 77:0b96f6867312 811 // By default, we'll report the code value as stored in 'code', with
mjr 77:0b96f6867312 812 // no toggle bit, if the number of bits we've decoded matches the
mjr 77:0b96f6867312 813 // expected number of bits as given by nbits(). Subclasses can
mjr 77:0b96f6867312 814 // override as needed to do other validation checks; e.g., protocols
mjr 77:0b96f6867312 815 // with varying bit lengths will need to check for all of the valid
mjr 77:0b96f6867312 816 // bit lengths, and protocols that contain error-checking information
mjr 77:0b96f6867312 817 // can validate that.
mjr 77:0b96f6867312 818 //
mjr 77:0b96f6867312 819 // Unless the protocol subclass overrides the basic pulse handler
mjr 77:0b96f6867312 820 // (rxPulse()), this is called when we end the data section of the
mjr 77:0b96f6867312 821 // code.
mjr 77:0b96f6867312 822 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 823 {
mjr 77:0b96f6867312 824 if (bit == nbits())
mjr 77:0b96f6867312 825 reportCode(receiver, id(), code, bool3::null, ditto);
mjr 77:0b96f6867312 826 }
mjr 77:0b96f6867312 827
mjr 77:0b96f6867312 828 // Encode a universal code value into a bit string in preparation
mjr 77:0b96f6867312 829 // for transmission. This should take the code value in state->cmdCode,
mjr 77:0b96f6867312 830 // convert it to the string of bits to serially, and store the result
mjr 77:0b96f6867312 831 // in state->bitstream.
mjr 77:0b96f6867312 832 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 833 {
mjr 77:0b96f6867312 834 // by default, simply store the code as-is
mjr 77:0b96f6867312 835 state->bitstream = state->cmdCode;
mjr 77:0b96f6867312 836 state->nbits = nbits();
mjr 77:0b96f6867312 837 }
mjr 77:0b96f6867312 838
mjr 77:0b96f6867312 839 // Get the next bit for transmission from the bitstream object
mjr 77:0b96f6867312 840 int getBit(IRTXState *state)
mjr 77:0b96f6867312 841 {
mjr 77:0b96f6867312 842 // get the number of bits and the current bit position
mjr 77:0b96f6867312 843 int nbits = state->nbits;
mjr 77:0b96f6867312 844 int bit = state->bit;
mjr 77:0b96f6867312 845
mjr 77:0b96f6867312 846 // figure the bit position according to the bit order
mjr 77:0b96f6867312 847 int bitpos = (lsbFirst() ? bit : nbits - bit - 1);
mjr 77:0b96f6867312 848
mjr 77:0b96f6867312 849 // pull out the bit
mjr 77:0b96f6867312 850 return int((state->bitstream >> bitpos) & 1);
mjr 77:0b96f6867312 851 }
mjr 77:0b96f6867312 852
mjr 77:0b96f6867312 853 // Set the next bit to '1', optionally incrementing the bit counter
mjr 77:0b96f6867312 854 void setBit(bool inc = true)
mjr 77:0b96f6867312 855 {
mjr 77:0b96f6867312 856 // ignore overflow
mjr 77:0b96f6867312 857 int nbits = this->nbits();
mjr 77:0b96f6867312 858 if (bit >= nbits)
mjr 77:0b96f6867312 859 return;
mjr 77:0b96f6867312 860
mjr 77:0b96f6867312 861 // Figure the starting bit position in 'code' for the bit,
mjr 77:0b96f6867312 862 // according to whether we're adding them LSB-first or MSB-first.
mjr 77:0b96f6867312 863 int bitpos = (lsbFirst() ? bit : nbits - bit - 1);
mjr 77:0b96f6867312 864
mjr 77:0b96f6867312 865 // mask in the bit
mjr 77:0b96f6867312 866 code |= (CodeType(1) << bitpos);
mjr 77:0b96f6867312 867
mjr 77:0b96f6867312 868 // advance the bit position
mjr 77:0b96f6867312 869 if (inc)
mjr 77:0b96f6867312 870 ++bit;
mjr 77:0b96f6867312 871 }
mjr 77:0b96f6867312 872
mjr 77:0b96f6867312 873 // Set the next N bits to '1'
mjr 77:0b96f6867312 874 void setBits(int n)
mjr 77:0b96f6867312 875 {
mjr 77:0b96f6867312 876 // ignore overflow
mjr 77:0b96f6867312 877 int nbits = this->nbits();
mjr 77:0b96f6867312 878 if (bit + n - 1 >= nbits)
mjr 77:0b96f6867312 879 return;
mjr 77:0b96f6867312 880
mjr 77:0b96f6867312 881 // Figure the starting bit position in 'code' for the bits we're
mjr 77:0b96f6867312 882 // setting. If bits arrive LSB-first, the 'bit' counter gives us
mjr 77:0b96f6867312 883 // the bit position directly. If bits arrive MSB-first, we need
mjr 77:0b96f6867312 884 // to start from the high end instead, so we're starting at
mjr 77:0b96f6867312 885 // ((nbits()-1) - bit). However, we want to set multiple bits
mjr 77:0b96f6867312 886 // left-to-right in any case, so move our starting position to
mjr 77:0b96f6867312 887 // the "end" of the window by moving right n-1 addition bits.
mjr 77:0b96f6867312 888 int bitpos = (lsbFirst() ? bit : nbits - bit - n);
mjr 77:0b96f6867312 889
mjr 77:0b96f6867312 890 // turn that into a bit mask for the first bit
mjr 77:0b96f6867312 891 uint64_t mask = (CodeType(1) << bitpos);
mjr 77:0b96f6867312 892
mjr 77:0b96f6867312 893 // advance our 'bit' counter past the added bits
mjr 77:0b96f6867312 894 bit += n;
mjr 77:0b96f6867312 895
mjr 77:0b96f6867312 896 // set each added bit to 1
mjr 77:0b96f6867312 897 for ( ; n != 0 ; --n, mask <<= 1)
mjr 77:0b96f6867312 898 code |= mask;
mjr 77:0b96f6867312 899 }
mjr 77:0b96f6867312 900
mjr 77:0b96f6867312 901 // decoding state
mjr 77:0b96f6867312 902 uint8_t rxState;
mjr 77:0b96f6867312 903
mjr 77:0b96f6867312 904 // next bit position
mjr 77:0b96f6867312 905 uint8_t bit;
mjr 77:0b96f6867312 906
mjr 77:0b96f6867312 907 // command code under construction
mjr 77:0b96f6867312 908 CodeType code;
mjr 77:0b96f6867312 909 };
mjr 77:0b96f6867312 910
mjr 77:0b96f6867312 911 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 912 //
mjr 77:0b96f6867312 913 // Basic asynchronous encoding
mjr 77:0b96f6867312 914 //
mjr 77:0b96f6867312 915 // This is essentially the IR equivalent of a wired UART. The transmission
mjr 77:0b96f6867312 916 // for a code word is divided into a fixed number of bit time periods of
mjr 77:0b96f6867312 917 // fixed length, with each period containing one bit, signified by IR ON for
mjr 77:0b96f6867312 918 // 1 or IR OFF for 0.
mjr 77:0b96f6867312 919 //
mjr 77:0b96f6867312 920 // Simple async coding doesn't have any inherent structure other than the
mjr 77:0b96f6867312 921 // bit length. That makes it hard to distinguish from other IR remotes that
mjr 77:0b96f6867312 922 // might share the environment, and even from random noise, which is why
mjr 77:0b96f6867312 923 // most CE manufacturers use more structured protocols. In practice, remotes
mjr 77:0b96f6867312 924 // using this coding are likely have to impose some sort of structure on the
mjr 77:0b96f6867312 925 // bits, such as long bit strings, error checking bits, or distinctive prefixes
mjr 77:0b96f6867312 926 // or suffixes. The only example of a pure async code I've seen in the wild
mjr 77:0b96f6867312 927 // is Lutron, and they do in fact use fairly long codes (36 bits) with set
mjr 77:0b96f6867312 928 // prefixes. Their prefixes aren't only distinctive as bit sequences, but
mjr 77:0b96f6867312 929 // also in the raw space/mark timing, which makes them effectively serve the
mjr 77:0b96f6867312 930 // function that header marks do in most of the structured protocols.
mjr 77:0b96f6867312 931 //
mjr 77:0b96f6867312 932 template<class CodeType> class IRPAsync: public IRPWithCode<CodeType>
mjr 77:0b96f6867312 933 {
mjr 77:0b96f6867312 934 public:
mjr 77:0b96f6867312 935 IRPAsync() { }
mjr 77:0b96f6867312 936
mjr 77:0b96f6867312 937 // duration of each bit in microseconds
mjr 77:0b96f6867312 938 virtual int tBit() const = 0;
mjr 77:0b96f6867312 939
mjr 77:0b96f6867312 940 // maximum legal mark in a data section, if applicable
mjr 77:0b96f6867312 941 virtual uint32_t maxMark() const { return IRReceiver::MAX_PULSE; }
mjr 77:0b96f6867312 942
mjr 77:0b96f6867312 943 // Async codes lack the structure of the more typical codes, so we
mjr 77:0b96f6867312 944 // use a completely custom pulse handler
mjr 77:0b96f6867312 945 virtual void rxPulse(IRRecvProIfc *receiver, uint32_t t, bool mark)
mjr 77:0b96f6867312 946 {
mjr 77:0b96f6867312 947 uint32_t tRef, tRef2;
mjr 77:0b96f6867312 948 switch (this->rxState)
mjr 77:0b96f6867312 949 {
mjr 77:0b96f6867312 950 case 0:
mjr 77:0b96f6867312 951 s0:
mjr 77:0b96f6867312 952 // Gap - if this is a long enough space, switch to header mode.
mjr 77:0b96f6867312 953 this->rxState = (!mark && t > this->minRxGap() ? 1 : 0);
mjr 77:0b96f6867312 954 break;
mjr 77:0b96f6867312 955
mjr 77:0b96f6867312 956 case 1:
mjr 77:0b96f6867312 957 // Header mode. Async protocols don't necessarily have headers,
mjr 77:0b96f6867312 958 // but some (e.g., Lutron) do start all codes with a distinctively
mjr 77:0b96f6867312 959 // long series of bits. If the subclass defines a header space,
mjr 77:0b96f6867312 960 // apply it to sense the start of a code.
mjr 77:0b96f6867312 961 if ((tRef = this->tHeaderMark()) == 0)
mjr 77:0b96f6867312 962 goto s2;
mjr 77:0b96f6867312 963
mjr 77:0b96f6867312 964 // we have a defined header mark - make sure this is long enough
mjr 77:0b96f6867312 965 tRef2 = tBit();
mjr 77:0b96f6867312 966 if (mark && inRangeOrAbove(t, tRef, tRef2))
mjr 77:0b96f6867312 967 {
mjr 77:0b96f6867312 968 // deduct the header time
mjr 77:0b96f6867312 969 t = t > tRef ? t - tRef : 0;
mjr 77:0b96f6867312 970
mjr 77:0b96f6867312 971 // if that leaves us with a single bit time or better,
mjr 77:0b96f6867312 972 // treat it as the first data bit
mjr 77:0b96f6867312 973 if (inRangeOrAbove(t, tRef2, tRef2))
mjr 77:0b96f6867312 974 goto s2;
mjr 77:0b96f6867312 975
mjr 77:0b96f6867312 976 // that consumes the whole mark, so just switch to data
mjr 77:0b96f6867312 977 // mode starting with the next space
mjr 77:0b96f6867312 978 this->rxState = 2;
mjr 77:0b96f6867312 979 break;
mjr 77:0b96f6867312 980 }
mjr 77:0b96f6867312 981
mjr 77:0b96f6867312 982 // doesn't look like the start of a code; back to gap mode
mjr 77:0b96f6867312 983 goto s0;
mjr 77:0b96f6867312 984
mjr 77:0b96f6867312 985 case 2:
mjr 77:0b96f6867312 986 s2:
mjr 77:0b96f6867312 987 // Enter data mode
mjr 77:0b96f6867312 988 this->rxReset();
mjr 77:0b96f6867312 989 goto s3;
mjr 77:0b96f6867312 990
mjr 77:0b96f6867312 991 case 3:
mjr 77:0b96f6867312 992 s3:
mjr 77:0b96f6867312 993 // Data mode. Process the mark or space as a number of bits.
mjr 77:0b96f6867312 994 {
mjr 77:0b96f6867312 995 // figure how many bits this symbol represents
mjr 77:0b96f6867312 996 int tb = tBit();
mjr 77:0b96f6867312 997 int n = (t + tb/2)/tb;
mjr 77:0b96f6867312 998
mjr 77:0b96f6867312 999 // figure how many bits remain in the code
mjr 77:0b96f6867312 1000 int rem = this->nbits() - this->bit;
mjr 77:0b96f6867312 1001
mjr 77:0b96f6867312 1002 // check to see if this symbol overflows the bits remaining
mjr 77:0b96f6867312 1003 if (n > rem)
mjr 77:0b96f6867312 1004 {
mjr 77:0b96f6867312 1005 // marks simply can't exceed the bits remaining
mjr 77:0b96f6867312 1006 if (mark)
mjr 77:0b96f6867312 1007 goto s0;
mjr 77:0b96f6867312 1008
mjr 77:0b96f6867312 1009 // Spaces can exceed the remaining bits, since we can
mjr 77:0b96f6867312 1010 // have a string of 0 bits followed by a gap between
mjr 77:0b96f6867312 1011 // codes. Use up the remaining bits as 0's, and apply
mjr 77:0b96f6867312 1012 // the balance as a gap.
mjr 77:0b96f6867312 1013 this->bit += rem;
mjr 77:0b96f6867312 1014 t -= rem*tb;
mjr 77:0b96f6867312 1015 goto s4;
mjr 77:0b96f6867312 1016 }
mjr 77:0b96f6867312 1017
mjr 77:0b96f6867312 1018 // check if it exceeds the code's maximum mark length
mjr 77:0b96f6867312 1019 if (mark && t > maxMark())
mjr 77:0b96f6867312 1020 goto s0;
mjr 77:0b96f6867312 1021
mjr 77:0b96f6867312 1022 // Make sure that it actually looks like an integral
mjr 77:0b96f6867312 1023 // number of bits. If it's not, take it as a bad code.
mjr 77:0b96f6867312 1024 if (!inRange(t, n*tb, tb))
mjr 77:0b96f6867312 1025 goto s0;
mjr 77:0b96f6867312 1026
mjr 77:0b96f6867312 1027 // Add the bits
mjr 77:0b96f6867312 1028 if (mark)
mjr 77:0b96f6867312 1029 this->setBits(n);
mjr 77:0b96f6867312 1030 else
mjr 77:0b96f6867312 1031 this->bit += n;
mjr 77:0b96f6867312 1032
mjr 77:0b96f6867312 1033 // we've consumed the whole interval as bits
mjr 77:0b96f6867312 1034 t = 0;
mjr 77:0b96f6867312 1035
mjr 77:0b96f6867312 1036 // if that's enough bits, we have a decode
mjr 77:0b96f6867312 1037 if (this->bit == this->nbits())
mjr 77:0b96f6867312 1038 goto s4;
mjr 77:0b96f6867312 1039
mjr 77:0b96f6867312 1040 // stay in data mode
mjr 77:0b96f6867312 1041 this->rxState = 3;
mjr 77:0b96f6867312 1042 }
mjr 77:0b96f6867312 1043 break;
mjr 77:0b96f6867312 1044
mjr 77:0b96f6867312 1045 case 4:
mjr 77:0b96f6867312 1046 s4:
mjr 77:0b96f6867312 1047 // done with the code - close it out and start over
mjr 77:0b96f6867312 1048 this->rxClose(receiver, false);
mjr 77:0b96f6867312 1049 goto s0;
mjr 77:0b96f6867312 1050 }
mjr 77:0b96f6867312 1051 }
mjr 77:0b96f6867312 1052
mjr 77:0b96f6867312 1053 // send data
mjr 77:0b96f6867312 1054 virtual int txDataStep(IRTXState *state)
mjr 77:0b96f6867312 1055 {
mjr 77:0b96f6867312 1056 // get the next bit
mjr 77:0b96f6867312 1057 int b = this->getBit(state);
mjr 77:0b96f6867312 1058 state->bit++;
mjr 77:0b96f6867312 1059
mjr 77:0b96f6867312 1060 // count how many consecutive matching bits follow
mjr 77:0b96f6867312 1061 int n = 1;
mjr 77:0b96f6867312 1062 int nbits = state->nbits;
mjr 77:0b96f6867312 1063 for ( ; state->bit < nbits && this->getBit(state) == b ;
mjr 77:0b96f6867312 1064 ++n, ++state->bit) ;
mjr 77:0b96f6867312 1065
mjr 77:0b96f6867312 1066 // if we're out of bits, advance to the next step
mjr 77:0b96f6867312 1067 if (state->bit >= nbits)
mjr 77:0b96f6867312 1068 ++state->step;
mjr 77:0b96f6867312 1069
mjr 77:0b96f6867312 1070 // 0 bits are IR OFF and 1 bits are IR ON
mjr 77:0b96f6867312 1071 state->pin->write(b ? this->pwmDutyCycle() : 0);
mjr 77:0b96f6867312 1072
mjr 77:0b96f6867312 1073 // stay on for the number of bits times the time per bit
mjr 77:0b96f6867312 1074 return n * this->tBit();
mjr 77:0b96f6867312 1075 }
mjr 77:0b96f6867312 1076 };
mjr 77:0b96f6867312 1077
mjr 77:0b96f6867312 1078
mjr 77:0b96f6867312 1079 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 1080 //
mjr 77:0b96f6867312 1081 // Space-length encoding
mjr 77:0b96f6867312 1082 //
mjr 77:0b96f6867312 1083 // This type of encoding uses the lengths of the spaces to encode the bit
mjr 77:0b96f6867312 1084 // values. Marks are all the same length, and spaces come in two lengths,
mjr 77:0b96f6867312 1085 // short and long, usually T and 2T for a vendor-specific time T (typically
mjr 77:0b96f6867312 1086 // on the order of 500us). The short space encodes 0 and the long space
mjr 77:0b96f6867312 1087 // encodes 1, or vice versa.
mjr 77:0b96f6867312 1088 //
mjr 77:0b96f6867312 1089 // The widely used NEC protocol is a space-length encoding, and in practice
mjr 77:0b96f6867312 1090 // it seems that most of the ad hoc proprietary protocols are also space-
mjr 77:0b96f6867312 1091 // length encodings, mostly with similar parameters to NEC.
mjr 77:0b96f6867312 1092 //
mjr 77:0b96f6867312 1093 template<class CodeType> class IRPSpaceLength: public IRPWithCode<CodeType>
mjr 77:0b96f6867312 1094 {
mjr 77:0b96f6867312 1095 public:
mjr 77:0b96f6867312 1096 IRPSpaceLength() { }
mjr 77:0b96f6867312 1097
mjr 77:0b96f6867312 1098 // mark length, in microseconds
mjr 77:0b96f6867312 1099 virtual int tMark() const = 0;
mjr 77:0b96f6867312 1100
mjr 77:0b96f6867312 1101 // 0 and 1 bit space lengths, in microseconds
mjr 77:0b96f6867312 1102 virtual int tZero() const = 0;
mjr 77:0b96f6867312 1103 virtual int tOne() const = 0;
mjr 77:0b96f6867312 1104
mjr 77:0b96f6867312 1105 // Space-length codings almost always need a mark after the last
mjr 77:0b96f6867312 1106 // bit, since otherwise the last bit's space (the significant part)
mjr 77:0b96f6867312 1107 // would just flow into the gap that follows.
mjr 77:0b96f6867312 1108 virtual uint32_t tStopMark() const { return tMark(); }
mjr 77:0b96f6867312 1109
mjr 77:0b96f6867312 1110 // process a mark
mjr 77:0b96f6867312 1111 virtual bool rxMark(IRRecvProIfc *receiver, uint32_t t)
mjr 77:0b96f6867312 1112 {
mjr 77:0b96f6867312 1113 // marks simply delimit spaces in this protocol and thus
mjr 77:0b96f6867312 1114 // carry no bit information
mjr 77:0b96f6867312 1115 if (inRange(t, tMark()))
mjr 77:0b96f6867312 1116 return true;
mjr 77:0b96f6867312 1117 else
mjr 77:0b96f6867312 1118 return false;
mjr 77:0b96f6867312 1119 }
mjr 77:0b96f6867312 1120
mjr 77:0b96f6867312 1121 // process a space
mjr 77:0b96f6867312 1122 virtual bool rxSpace(IRRecvProIfc *receiver, uint32_t t)
mjr 77:0b96f6867312 1123 {
mjr 77:0b96f6867312 1124 // a short space represents a '0' bit, a long space is a '1'
mjr 77:0b96f6867312 1125 if (inRange(t, tZero()))
mjr 77:0b96f6867312 1126 return this->bit++, true;
mjr 77:0b96f6867312 1127 else if (inRange(t, tOne()))
mjr 77:0b96f6867312 1128 return this->setBit(), true;
mjr 77:0b96f6867312 1129 else
mjr 77:0b96f6867312 1130 return false;
mjr 77:0b96f6867312 1131 }
mjr 77:0b96f6867312 1132
mjr 77:0b96f6867312 1133 // continue a transmission
mjr 77:0b96f6867312 1134 virtual int txDataStep(IRTXState *state)
mjr 77:0b96f6867312 1135 {
mjr 77:0b96f6867312 1136 // Data section.
mjr 77:0b96f6867312 1137 if (state->bitstep == 0)
mjr 77:0b96f6867312 1138 {
mjr 77:0b96f6867312 1139 // mark - these are fixed length
mjr 77:0b96f6867312 1140 state->pin->write(this->pwmDutyCycle());
mjr 77:0b96f6867312 1141 state->bitstep = 1;
mjr 77:0b96f6867312 1142 return tMark();
mjr 77:0b96f6867312 1143 }
mjr 77:0b96f6867312 1144 else
mjr 77:0b96f6867312 1145 {
mjr 77:0b96f6867312 1146 // space - these are variable length according to the data
mjr 77:0b96f6867312 1147 state->pin->write(0);
mjr 77:0b96f6867312 1148 int t = this->getBit(state) ? tOne() : tZero();
mjr 77:0b96f6867312 1149 state->bitstep = 0;
mjr 77:0b96f6867312 1150
mjr 77:0b96f6867312 1151 // advance to the next bit; stop if we're done
mjr 77:0b96f6867312 1152 if (++state->bit >= state->nbits)
mjr 77:0b96f6867312 1153 ++state->step;
mjr 77:0b96f6867312 1154
mjr 77:0b96f6867312 1155 // return the space time
mjr 77:0b96f6867312 1156 return t;
mjr 77:0b96f6867312 1157 }
mjr 77:0b96f6867312 1158 }
mjr 77:0b96f6867312 1159
mjr 77:0b96f6867312 1160 };
mjr 77:0b96f6867312 1161
mjr 77:0b96f6867312 1162
mjr 77:0b96f6867312 1163 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 1164 //
mjr 77:0b96f6867312 1165 // Mark-length encoding
mjr 77:0b96f6867312 1166 //
mjr 77:0b96f6867312 1167 // This is the inverse of space-length coding. In this scheme, the bit
mjr 77:0b96f6867312 1168 // values are encoded in the mark length. Spaces are fixed length, and
mjr 77:0b96f6867312 1169 // marks come in short (0) and long (1) lengths, usually of time T and 2T
mjr 77:0b96f6867312 1170 // for a protocol-specific time T.
mjr 77:0b96f6867312 1171 //
mjr 77:0b96f6867312 1172 // Sony uses this type of encoding.
mjr 77:0b96f6867312 1173 template<class CodeType> class IRPMarkLength: public IRPWithCode<CodeType>
mjr 77:0b96f6867312 1174 {
mjr 77:0b96f6867312 1175 public:
mjr 77:0b96f6867312 1176 IRPMarkLength() { }
mjr 77:0b96f6867312 1177
mjr 77:0b96f6867312 1178 // space length, in microseconds
mjr 77:0b96f6867312 1179 virtual int tSpace() const = 0;
mjr 77:0b96f6867312 1180
mjr 77:0b96f6867312 1181 // 0 and 1 bit mark lengths, in microseconds
mjr 77:0b96f6867312 1182 virtual int tZero() const = 0;
mjr 77:0b96f6867312 1183 virtual int tOne() const = 0;
mjr 77:0b96f6867312 1184
mjr 77:0b96f6867312 1185 // process a mark
mjr 77:0b96f6867312 1186 virtual bool rxMark(IRRecvProIfc *receiver, uint32_t t)
mjr 77:0b96f6867312 1187 {
mjr 77:0b96f6867312 1188 // a short mark represents a '0' bit, a long space is a '1'
mjr 77:0b96f6867312 1189 if (inRange(t, tZero()))
mjr 77:0b96f6867312 1190 this->bit++;
mjr 77:0b96f6867312 1191 else if (inRange(t, tOne()))
mjr 77:0b96f6867312 1192 this->setBit();
mjr 77:0b96f6867312 1193 else
mjr 77:0b96f6867312 1194 return false;
mjr 77:0b96f6867312 1195 return true;
mjr 77:0b96f6867312 1196 }
mjr 77:0b96f6867312 1197
mjr 77:0b96f6867312 1198 // process a space
mjr 77:0b96f6867312 1199 virtual bool rxSpace(IRRecvProIfc *receiver, uint32_t t)
mjr 77:0b96f6867312 1200 {
mjr 77:0b96f6867312 1201 // spaces simply delimit marks in this protocol and carry
mjr 77:0b96f6867312 1202 // no bit information of their own
mjr 77:0b96f6867312 1203 return inRange(t, tSpace());
mjr 77:0b96f6867312 1204 }
mjr 77:0b96f6867312 1205
mjr 77:0b96f6867312 1206 // continue a transmission
mjr 77:0b96f6867312 1207 virtual int txDataStep(IRTXState *state)
mjr 77:0b96f6867312 1208 {
mjr 77:0b96f6867312 1209 // check if we're on a mark (step 0) or space (step 1)
mjr 77:0b96f6867312 1210 if (state->bitstep == 0)
mjr 77:0b96f6867312 1211 {
mjr 77:0b96f6867312 1212 // space - these are variable length according to the data
mjr 77:0b96f6867312 1213 state->pin->write(this->pwmDutyCycle());
mjr 77:0b96f6867312 1214 int t = this->getBit(state) ? tOne() : tZero();
mjr 77:0b96f6867312 1215 state->bitstep = 1;
mjr 77:0b96f6867312 1216
mjr 77:0b96f6867312 1217 // return the mark time
mjr 77:0b96f6867312 1218 return t;
mjr 77:0b96f6867312 1219 }
mjr 77:0b96f6867312 1220 else
mjr 77:0b96f6867312 1221 {
mjr 77:0b96f6867312 1222 // Space - fixed length
mjr 77:0b96f6867312 1223 state->pin->write(0);
mjr 77:0b96f6867312 1224 state->bitstep = 0;
mjr 77:0b96f6867312 1225
mjr 77:0b96f6867312 1226 // advance to the next bit; stop if we're done
mjr 77:0b96f6867312 1227 if (++state->bit >= state->nbits)
mjr 77:0b96f6867312 1228 state->step = 3;
mjr 77:0b96f6867312 1229 return tSpace();
mjr 77:0b96f6867312 1230 }
mjr 77:0b96f6867312 1231 }
mjr 77:0b96f6867312 1232
mjr 77:0b96f6867312 1233 };
mjr 77:0b96f6867312 1234
mjr 77:0b96f6867312 1235
mjr 77:0b96f6867312 1236 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 1237 //
mjr 77:0b96f6867312 1238 // Manchester coding
mjr 77:0b96f6867312 1239 //
mjr 77:0b96f6867312 1240 // This type of coding uses a fixed time per bit, and encodes the bit
mjr 77:0b96f6867312 1241 // value in a mark/space or space/mark transition within each bit's
mjr 77:0b96f6867312 1242 // time window.
mjr 77:0b96f6867312 1243 //
mjr 77:0b96f6867312 1244 // The decoding process is a little tricky to grap when you're looking
mjr 77:0b96f6867312 1245 // at just the raw data, because the raw data renders things in terms of
mjr 77:0b96f6867312 1246 // monolithic marks and spaces of different lengths, whereas the coding
mjr 77:0b96f6867312 1247 // divides the time axis into even chunks and looks at what's going on
mjr 77:0b96f6867312 1248 // in each chunk. In terms of the raw data, we can think of it this way.
mjr 77:0b96f6867312 1249 // Because every bit time window has a transition (mark/space or space/mark)
mjr 77:0b96f6867312 1250 // in the middle of it, there has to be at least one transition per window.
mjr 77:0b96f6867312 1251 // There can also be a transition between each window, or not, as needed
mjr 77:0b96f6867312 1252 // to get the transmitter into the right initial state for the next bit.
mjr 77:0b96f6867312 1253 // This means that each mark and each space is either T or 2T long, where
mjr 77:0b96f6867312 1254 // T is the half the bit window time. So we can simply count these units.
mjr 77:0b96f6867312 1255 // If we see a mark or space of approximate length T, we count one unit;
mjr 77:0b96f6867312 1256 // if the length is around 2T, we count two units. On each ODD count, we
mjr 77:0b96f6867312 1257 // look at the state just before the count. If we were in a space just
mjr 77:0b96f6867312 1258 // before the count, the bit is a 1; if it was a mark, the bit is a 0.
mjr 77:0b96f6867312 1259 //
mjr 77:0b96f6867312 1260 // Manchester coding is used in the Philips RC5 and RC6 protocols, which
mjr 77:0b96f6867312 1261 // are in turn used by most other European CE companies.
mjr 77:0b96f6867312 1262 template<class CodeType> class IRPManchester: public IRPWithCode<CodeType>
mjr 77:0b96f6867312 1263 {
mjr 77:0b96f6867312 1264 public:
mjr 77:0b96f6867312 1265 IRPManchester() { }
mjr 77:0b96f6867312 1266
mjr 77:0b96f6867312 1267 // Half-bit time. This is half of the time interval of one bit,
mjr 77:0b96f6867312 1268 // so it's equal to the time on each side of the mark/space or
mjr 77:0b96f6867312 1269 // space/mark transition in the middle of each bit.
mjr 77:0b96f6867312 1270 virtual int tHalfBit(int bit) const = 0;
mjr 77:0b96f6867312 1271
mjr 77:0b96f6867312 1272 // Bit value (0 or 1) of a space-to-mark transition. A mark-to-space
mjr 77:0b96f6867312 1273 // transition always has the opposite sense.
mjr 77:0b96f6867312 1274 virtual int spaceToMarkBit() const { return 1; }
mjr 77:0b96f6867312 1275 inline int markToSpaceBit() const { return !spaceToMarkBit(); }
mjr 77:0b96f6867312 1276
mjr 77:0b96f6867312 1277 // reset the decoder state
mjr 77:0b96f6867312 1278 virtual void rxReset()
mjr 77:0b96f6867312 1279 {
mjr 77:0b96f6867312 1280 IRPWithCode<CodeType>::rxReset();
mjr 77:0b96f6867312 1281 halfBitPos = 0;
mjr 77:0b96f6867312 1282 }
mjr 77:0b96f6867312 1283
mjr 77:0b96f6867312 1284 // process a mark
mjr 77:0b96f6867312 1285 virtual bool rxMark(IRRecvProIfc *receiver, uint32_t t)
mjr 77:0b96f6867312 1286 {
mjr 77:0b96f6867312 1287 // transitioning from mark to space, so this is a
mjr 77:0b96f6867312 1288 return processTransition(t, spaceToMarkBit());
mjr 77:0b96f6867312 1289 }
mjr 77:0b96f6867312 1290
mjr 77:0b96f6867312 1291 // process a space
mjr 77:0b96f6867312 1292 virtual bool rxSpace(IRRecvProIfc *receiver, uint32_t t)
mjr 77:0b96f6867312 1293 {
mjr 77:0b96f6867312 1294 return (processTransition(t, markToSpaceBit()));
mjr 77:0b96f6867312 1295 }
mjr 77:0b96f6867312 1296
mjr 77:0b96f6867312 1297 // Process a space/mark or mark/space transition. Returns true on
mjr 77:0b96f6867312 1298 // success, false on failure.
mjr 77:0b96f6867312 1299 bool processTransition(uint32_t t, int bitval)
mjr 77:0b96f6867312 1300 {
mjr 77:0b96f6867312 1301 // If this time is close to zero, ignore it.
mjr 77:0b96f6867312 1302 int thb = tHalfBit(this->bit);
mjr 77:0b96f6867312 1303 if (t < ((thb*toleranceShl8) >> 8))
mjr 77:0b96f6867312 1304 return true;
mjr 77:0b96f6867312 1305
mjr 77:0b96f6867312 1306 // If the current time is the middle of a bit, the transition
mjr 77:0b96f6867312 1307 // specifies a bit value, so set the bit. Transitions between
mjr 77:0b96f6867312 1308 // bits are just clocking.
mjr 77:0b96f6867312 1309 if (halfBitPos == 1)
mjr 77:0b96f6867312 1310 {
mjr 77:0b96f6867312 1311 if (bitval)
mjr 77:0b96f6867312 1312 {
mjr 77:0b96f6867312 1313 // set the bit, but keep the bit counter where it is, since
mjr 77:0b96f6867312 1314 // we manage it ourselves
mjr 77:0b96f6867312 1315 this->setBit();
mjr 77:0b96f6867312 1316 this->bit--;
mjr 77:0b96f6867312 1317 }
mjr 77:0b96f6867312 1318 }
mjr 77:0b96f6867312 1319
mjr 77:0b96f6867312 1320 // Advance by the time interval. Check that we have at least one
mjr 77:0b96f6867312 1321 // half-bit interval to work with.
mjr 77:0b96f6867312 1322 if (t < ((thb * (256 - toleranceShl8)) >> 8))
mjr 77:0b96f6867312 1323 return false;
mjr 77:0b96f6867312 1324
mjr 77:0b96f6867312 1325 // If we're at a half-bit position, start by advancing to the next
mjr 77:0b96f6867312 1326 // bit boundary.
mjr 77:0b96f6867312 1327 if (halfBitPos)
mjr 77:0b96f6867312 1328 {
mjr 77:0b96f6867312 1329 // deduct the half-bit time
mjr 77:0b96f6867312 1330 t = (t > thb ? t - thb : 0);
mjr 77:0b96f6867312 1331
mjr 77:0b96f6867312 1332 // advance our position counters to the next bit boundary
mjr 77:0b96f6867312 1333 halfBitPos = 0;
mjr 77:0b96f6867312 1334 this->bit++;
mjr 77:0b96f6867312 1335
mjr 77:0b96f6867312 1336 // Some subprotocols (e.g., RC6) have variable bit timing,
mjr 77:0b96f6867312 1337 // so the timing for this bit might be different than for
mjr 77:0b96f6867312 1338 // the previous bit. Re-fetch the time.
mjr 77:0b96f6867312 1339 thb = tHalfBit(this->bit);
mjr 77:0b96f6867312 1340 }
mjr 77:0b96f6867312 1341
mjr 77:0b96f6867312 1342 // If we have another half-interval left to process, advance
mjr 77:0b96f6867312 1343 // to the middle of the current bit.
mjr 77:0b96f6867312 1344 if (t < ((thb * toleranceShl8) >> 8))
mjr 77:0b96f6867312 1345 {
mjr 77:0b96f6867312 1346 // we already used up the symbol time, so we're done
mjr 77:0b96f6867312 1347 return true;
mjr 77:0b96f6867312 1348 }
mjr 77:0b96f6867312 1349 else if (inRange(t, thb))
mjr 77:0b96f6867312 1350 {
mjr 77:0b96f6867312 1351 // we have another half-bit time to use, so advance to
mjr 77:0b96f6867312 1352 // the middle of the bit
mjr 77:0b96f6867312 1353 halfBitPos = true;
mjr 77:0b96f6867312 1354 return true;
mjr 77:0b96f6867312 1355 }
mjr 77:0b96f6867312 1356 else
mjr 77:0b96f6867312 1357 {
mjr 77:0b96f6867312 1358 // The time remaining is wrong, so terminate decoding.
mjr 77:0b96f6867312 1359 // Note that this could simply be because we've reached
mjr 77:0b96f6867312 1360 // the gap at the end of the code word, in which case we'll
mjr 77:0b96f6867312 1361 // already have all of the bits stored and will generate
mjr 77:0b96f6867312 1362 // the finished code value.
mjr 77:0b96f6867312 1363 return false;
mjr 77:0b96f6867312 1364 }
mjr 77:0b96f6867312 1365 }
mjr 77:0b96f6867312 1366
mjr 77:0b96f6867312 1367 virtual int txDataStep(IRTXState *state)
mjr 77:0b96f6867312 1368 {
mjr 77:0b96f6867312 1369 // Get the current bit
mjr 77:0b96f6867312 1370 int b = this->getBit(state);
mjr 77:0b96f6867312 1371
mjr 77:0b96f6867312 1372 // Determine if this bit uses a space-to-mark or mark-to-space
mjr 77:0b96f6867312 1373 // transition. It uses a space-to-mark transition if it matches
mjr 77:0b96f6867312 1374 // the space-to-mark bit.
mjr 77:0b96f6867312 1375 int stm = (b == spaceToMarkBit());
mjr 77:0b96f6867312 1376
mjr 77:0b96f6867312 1377 // Check to see if we're at the start or middle of the bit
mjr 77:0b96f6867312 1378 if (state->bitstep == 0)
mjr 77:0b96f6867312 1379 {
mjr 77:0b96f6867312 1380 // Start of the current bit. Set the level for the first
mjr 77:0b96f6867312 1381 // half of the bit. If we're doing a space-to-mark bit,
mjr 77:0b96f6867312 1382 // the first half is a space, otherwise it's a mark.
mjr 77:0b96f6867312 1383 state->pin->write(stm ? 0 : this->pwmDutyCycle());
mjr 77:0b96f6867312 1384
mjr 77:0b96f6867312 1385 // leave this on for a half-bit time to get to the
mjr 77:0b96f6867312 1386 // middle of the bit
mjr 77:0b96f6867312 1387 state->bitstep = 1;
mjr 77:0b96f6867312 1388 return tHalfBit(state->bit);
mjr 77:0b96f6867312 1389 }
mjr 77:0b96f6867312 1390 else
mjr 77:0b96f6867312 1391 {
mjr 77:0b96f6867312 1392 // Middle of the current bit. Set the level for the second
mjr 77:0b96f6867312 1393 // half of the bit. If we're in a space-to-mark bit, the
mjr 77:0b96f6867312 1394 // second half is the mark, otherwise it's the space.
mjr 77:0b96f6867312 1395 state->pin->write(stm ? this->pwmDutyCycle() : 0);
mjr 77:0b96f6867312 1396
mjr 77:0b96f6867312 1397 // figure the time to the start of the next bit
mjr 77:0b96f6867312 1398 int t = tHalfBit(state->bit);
mjr 77:0b96f6867312 1399
mjr 77:0b96f6867312 1400 // advance to the start of the next bit
mjr 77:0b96f6867312 1401 state->bit++;
mjr 77:0b96f6867312 1402 state->bitstep = 0;
mjr 77:0b96f6867312 1403
mjr 77:0b96f6867312 1404 // If the next bit is the inverse of the current bit, it will
mjr 77:0b96f6867312 1405 // lead in with the same level we're going out with. That
mjr 77:0b96f6867312 1406 // means we can go straight to the middle of the next bit
mjr 77:0b96f6867312 1407 // without another interrupt.
mjr 77:0b96f6867312 1408 if (state->bit < state->nbits && this->getBit(state) != b)
mjr 77:0b96f6867312 1409 {
mjr 77:0b96f6867312 1410 // proceed to the middle of the next bit
mjr 77:0b96f6867312 1411 state->bitstep = 1;
mjr 77:0b96f6867312 1412
mjr 77:0b96f6867312 1413 // add the half-bit time
mjr 77:0b96f6867312 1414 t += tHalfBit(state->bit);
mjr 77:0b96f6867312 1415 }
mjr 77:0b96f6867312 1416
mjr 77:0b96f6867312 1417 // if this was the last bit, advance to the next state
mjr 77:0b96f6867312 1418 if (state->bit >= state->nbits)
mjr 77:0b96f6867312 1419 state->step++;
mjr 77:0b96f6867312 1420
mjr 77:0b96f6867312 1421 // return the time to the next transition
mjr 77:0b96f6867312 1422 return t;
mjr 77:0b96f6867312 1423 }
mjr 77:0b96f6867312 1424 }
mjr 77:0b96f6867312 1425
mjr 77:0b96f6867312 1426 // Current half-bit position. If the last transition was on the
mjr 77:0b96f6867312 1427 // border between two bits, this is 0. If it was in the middle
mjr 77:0b96f6867312 1428 // of a bit, this is 1.
mjr 77:0b96f6867312 1429 uint8_t halfBitPos : 1;
mjr 77:0b96f6867312 1430 };
mjr 77:0b96f6867312 1431
mjr 77:0b96f6867312 1432 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 1433 //
mjr 77:0b96f6867312 1434 // NEC protocol family. This is one of the more important proprietary
mjr 77:0b96f6867312 1435 // protocols, since many CE companies use the standard NEC code or a
mjr 77:0b96f6867312 1436 // variation of it. This class handles the following variations on the
mjr 77:0b96f6867312 1437 // basic NEC code:
mjr 77:0b96f6867312 1438 //
mjr 97:fc7727303038 1439 // NEC-32: 32-bit payload, 9000us header mark, 4500us header space
mjr 97:fc7727303038 1440 // NEC-32X: 32-bit payload, 4500us header mark, 4500us header space
mjr 97:fc7727303038 1441 // NEC-48: 48-bit payload, 9000us header mark, 4500us header space
mjr 97:fc7727303038 1442 // Pioneer: NEC-32 with address A0..AF + possible "shift" prefixes
mjr 97:fc7727303038 1443 // TCL/Roku: NEC-32 with address EAC7 + doubled code XOR 0x8080
mjr 77:0b96f6867312 1444 //
mjr 77:0b96f6867312 1445 // Each of the three NEC-nn protocol types comes in two sub-types: one
mjr 77:0b96f6867312 1446 // that uses "ditto" codes for repeated keys, and one that simply sends
mjr 77:0b96f6867312 1447 // the same code again on repeats. The ditto code, when used, varies with
mjr 77:0b96f6867312 1448 // the main protocol type:
mjr 77:0b96f6867312 1449 //
mjr 97:fc7727303038 1450 // NEC-32: 9000us mark + 2250us space + 564us mark
mjr 97:fc7727303038 1451 // NEC-32x: 4500us mark + 4500us space + one data bit + 564us mark
mjr 97:fc7727303038 1452 // NEC-48: 9000us mark + 2250us space + 564us mark
mjr 97:fc7727303038 1453 // Pioneer: no dittos
mjr 97:fc7727303038 1454 // TCL/Roku: no dittos
mjr 77:0b96f6867312 1455 //
mjr 77:0b96f6867312 1456 // The NEC-32 and NEC-48 dittos can be detected from the header space
mjr 77:0b96f6867312 1457 // length. The NEC-32x dittos can be detected by the one-bit code length.
mjr 77:0b96f6867312 1458 //
mjr 77:0b96f6867312 1459 // All variations of the NEC code are space-length encodings with 564us
mjr 77:0b96f6867312 1460 // marks between bits, 564us '0' spaces, and 3*564us '1' spaces. All
mjr 77:0b96f6867312 1461 // variations use a long header mark and a 564us stop mark. With those
mjr 77:0b96f6867312 1462 // fixed features, there are three main variations:
mjr 77:0b96f6867312 1463 //
mjr 77:0b96f6867312 1464 // The bits in the NEC 32-bit codes are structured into four 8-bit fields,
mjr 77:0b96f6867312 1465 // with the first bit transmitted in the most significant position:
mjr 77:0b96f6867312 1466 //
mjr 77:0b96f6867312 1467 // A1 A0 C1 C0
mjr 77:0b96f6867312 1468 //
mjr 77:0b96f6867312 1469 // A1 is the high 8 bits of the address, and A0 is the low 8 bits of the
mjr 77:0b96f6867312 1470 // address. The address specifies a particular type of device, such as
mjr 77:0b96f6867312 1471 // "NEC VCR" or "Vizio TV". These are assigned by NEC. C1 and C0 form the
mjr 77:0b96f6867312 1472 // command code, which has a meaning specific to the device type specified
mjr 77:0b96f6867312 1473 // by the address field.
mjr 77:0b96f6867312 1474 //
mjr 77:0b96f6867312 1475 // In the original NEC protocol, the nominal address is in A1, and A0 is
mjr 77:0b96f6867312 1476 // the 1's complement of A1 (A0 = ~A1), for error checking. This was removed
mjr 77:0b96f6867312 1477 // in a later revision to expand the address space. Most modern equipment
mjr 77:0b96f6867312 1478 // uses the newer system, so A0 is typically an independent value in remotes
mjr 77:0b96f6867312 1479 // you'll find in use today.
mjr 77:0b96f6867312 1480 //
mjr 77:0b96f6867312 1481 // In the official version of the protocol, C1 is the nominal command code,
mjr 77:0b96f6867312 1482 // and C0 = ~C1, for error checking. However, some other manufacturers who
mjr 77:0b96f6867312 1483 // use the basic NEC protocol, notably Yamaha and Onkyo, violate this by using
mjr 77:0b96f6867312 1484 // C0 as an independent byte to expand the command space. We therefore don't
mjr 77:0b96f6867312 1485 // test for the complemented byte, so that we don't reject codes from devices
mjr 77:0b96f6867312 1486 // that treat it as independent.
mjr 77:0b96f6867312 1487 //
mjr 77:0b96f6867312 1488 // Pioneer uses the NEC protocol with two changes. First, the carrier is
mjr 77:0b96f6867312 1489 // 40kHz instead of 38kHz. The TSOP384xx seems to receive the 40kHz signal
mjr 77:0b96f6867312 1490 // reliably, so the frequency doesn't matter on receive, but it might matter
mjr 77:0b96f6867312 1491 // on transmit if the target equipment isn't as tolerant. Second, Pioneer
mjr 77:0b96f6867312 1492 // sometimes transmits a second code for the same key. In these cases, the
mjr 77:0b96f6867312 1493 // first code is a sort of "shift" code (shared among many keys), and the
mjr 77:0b96f6867312 1494 // second code has the actual key-specific meaning. To learn or recognize
mjr 77:0b96f6867312 1495 // these extended codes, we have to treat the pair of code words as a single
mjr 77:0b96f6867312 1496 // command. We sense Pioneer codes based on the address field, and use
mjr 77:0b96f6867312 1497 // special handling when we find a Pioneer address code.
mjr 77:0b96f6867312 1498 //
mjr 97:fc7727303038 1499 // TCL's Roku models (that is, their TVs that contain embedded Roku features)
mjr 97:fc7727303038 1500 // use yet another proprietary variant. They use the standard low-level
mjr 97:fc7727303038 1501 // protocol elements (PWM freq and bit timing), but they layer a high-level
mjr 97:fc7727303038 1502 // protocol variation where every command consists of two 32-bit code words
mjr 97:fc7727303038 1503 // in succession. The second code word repeats the first code word, but with
mjr 97:fc7727303038 1504 // the last two bytes (the "command field") each XOR'd with 0x80. TCL's codes
mjr 97:fc7727303038 1505 // are recognizable by EAC7 in the command field. The repeated code scheme is
mjr 97:fc7727303038 1506 // presumably a redundancy check. Dittos aren't used in this scheme.
mjr 97:fc7727303038 1507 //
mjr 77:0b96f6867312 1508 class IRPNEC: public IRPSpaceLength<uint64_t>
mjr 77:0b96f6867312 1509 {
mjr 77:0b96f6867312 1510 public:
mjr 77:0b96f6867312 1511 // code parameters
mjr 77:0b96f6867312 1512 virtual uint32_t minRxGap() const { return 3400; }
mjr 77:0b96f6867312 1513 virtual uint32_t txGap(IRTXState *state) const
mjr 77:0b96f6867312 1514 { return 108000 - state->txTime.read_us(); }
mjr 97:fc7727303038 1515
mjr 97:fc7727303038 1516 // The post-code transmit gap is special for TCL Roku for the gap between
mjr 97:fc7727303038 1517 // the first and second half of the code. These appear to use a fixed
mjr 97:fc7727303038 1518 // 37842us gap. The receiver interprets the normal NEC inter-code gap
mjr 97:fc7727303038 1519 // of (108ms minus code transmit time) as a gap between repeats of the
mjr 97:fc7727303038 1520 // code rather than as half-codes.
mjr 97:fc7727303038 1521 virtual uint32_t txPostGap(IRTXState *state) const
mjr 97:fc7727303038 1522 {
mjr 97:fc7727303038 1523 // Check for TCL Roku models on even reps. An even rep is the
mjr 97:fc7727303038 1524 // first half of a code pair, so we need to use the shorter
mjr 97:fc7727303038 1525 // post-code gap for these.
mjr 97:fc7727303038 1526 if (state->protocolId == IRPRO_TCLROKU
mjr 97:fc7727303038 1527 && (state->rep == 0 || state->rep == 2))
mjr 97:fc7727303038 1528 return 37842;
mjr 97:fc7727303038 1529
mjr 97:fc7727303038 1530 // use the standard NEC timing for others
mjr 97:fc7727303038 1531 return txGap(state);
mjr 97:fc7727303038 1532 }
mjr 97:fc7727303038 1533
mjr 77:0b96f6867312 1534 // space length encoding parameters
mjr 77:0b96f6867312 1535 virtual int tMark() const { return 560; }
mjr 77:0b96f6867312 1536 virtual int tZero() const { return 560; }
mjr 77:0b96f6867312 1537 virtual int tOne() const { return 1680; }
mjr 77:0b96f6867312 1538
mjr 77:0b96f6867312 1539 // PWM period is 40kHz for Pioneer, 38kHz for everyone else
mjr 77:0b96f6867312 1540 virtual float pwmPeriod(IRTXState *state) const
mjr 77:0b96f6867312 1541 {
mjr 77:0b96f6867312 1542 return state->protocolId == IRPRO_PIONEER ? 25.0e-6f : 26.31578947e-6f;
mjr 77:0b96f6867312 1543 }
mjr 77:0b96f6867312 1544
mjr 97:fc7727303038 1545 // For Pioneer, we have to send two codes if we have a shift field.
mjr 97:fc7727303038 1546 // For TCL Roku models, we always send two codes.
mjr 77:0b96f6867312 1547 virtual int txMinReps(IRTXState *state) const
mjr 77:0b96f6867312 1548 {
mjr 77:0b96f6867312 1549 if (state->protocolId == IRPRO_PIONEER
mjr 77:0b96f6867312 1550 && (state->cmdCode & 0xFFFF0000) != 0)
mjr 77:0b96f6867312 1551 return 2;
mjr 97:fc7727303038 1552 else if (state->protocolId == IRPRO_TCLROKU)
mjr 97:fc7727303038 1553 return 2;
mjr 77:0b96f6867312 1554 else
mjr 77:0b96f6867312 1555 return 1;
mjr 77:0b96f6867312 1556 }
mjr 77:0b96f6867312 1557
mjr 77:0b96f6867312 1558 // get the protocol to report for a given data packet bit count
mjr 77:0b96f6867312 1559 virtual int necPro(int bits) const = 0;
mjr 77:0b96f6867312 1560
mjr 77:0b96f6867312 1561 // close out a received bitstream
mjr 77:0b96f6867312 1562 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 1563 {
mjr 77:0b96f6867312 1564 // Check the bit count. If nbits() says we can accept 48 bits,
mjr 77:0b96f6867312 1565 // accept 48 bits, otherwise only accept 32.
mjr 77:0b96f6867312 1566 if (bit == 32 || (nbits() >= 48 && bit == 48))
mjr 77:0b96f6867312 1567 {
mjr 77:0b96f6867312 1568 uint64_t codeOut;
mjr 77:0b96f6867312 1569 if (ditto)
mjr 77:0b96f6867312 1570 {
mjr 77:0b96f6867312 1571 // report 0 for dittos
mjr 77:0b96f6867312 1572 codeOut = 0;
mjr 77:0b96f6867312 1573 }
mjr 77:0b96f6867312 1574 else
mjr 77:0b96f6867312 1575 {
mjr 77:0b96f6867312 1576 // Put the bytes in the right order. The bits are LSB-first, but
mjr 77:0b96f6867312 1577 // we want the bytes to be ordered within the uint32 as (high to
mjr 77:0b96f6867312 1578 // low) A0 A1 C0 C1.
mjr 77:0b96f6867312 1579 uint8_t c1 = uint8_t((code >> 24) & 0xff);
mjr 77:0b96f6867312 1580 uint8_t c0 = uint8_t((code >> 16) & 0xff);
mjr 77:0b96f6867312 1581 uint8_t a1 = uint8_t((code >> 8) & 0xff);
mjr 77:0b96f6867312 1582 uint8_t a0 = uint8_t(code & 0xff);
mjr 77:0b96f6867312 1583 codeOut = (uint64_t(a0) << 24) | (uint64_t(a1) << 16)
mjr 77:0b96f6867312 1584 | (uint64_t(c0) << 8) | uint64_t(c1);
mjr 77:0b96f6867312 1585
mjr 77:0b96f6867312 1586 // If it's a 48-bit code, add the additional 16 bits for E0 E1
mjr 77:0b96f6867312 1587 // (the extended command code) at the low end.
mjr 77:0b96f6867312 1588 if (bit == 48)
mjr 77:0b96f6867312 1589 {
mjr 77:0b96f6867312 1590 // get the E1 and E0 bytes from the top end of the code
mjr 77:0b96f6867312 1591 uint8_t e1 = uint8_t((code >> 40) & 0xff);
mjr 77:0b96f6867312 1592 uint8_t e0 = uint8_t((code >> 32) & 0xff);
mjr 77:0b96f6867312 1593
mjr 77:0b96f6867312 1594 // insert them at the low end in E0 E1 order
mjr 77:0b96f6867312 1595 codeOut <<= 16;
mjr 77:0b96f6867312 1596 codeOut |= (uint64_t(e0) << 8) | uint64_t(e1);
mjr 77:0b96f6867312 1597 }
mjr 77:0b96f6867312 1598 }
mjr 77:0b96f6867312 1599
mjr 77:0b96f6867312 1600 // report it
mjr 77:0b96f6867312 1601 reportCode(receiver, necPro(bit), codeOut, bool3::null, ditto);
mjr 77:0b96f6867312 1602 }
mjr 77:0b96f6867312 1603 }
mjr 77:0b96f6867312 1604
mjr 77:0b96f6867312 1605 // convert a code to a bitstream for sending
mjr 77:0b96f6867312 1606 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 1607 {
mjr 77:0b96f6867312 1608 if (state->protocolId == IRPRO_PIONEER)
mjr 77:0b96f6867312 1609 {
mjr 77:0b96f6867312 1610 // Check if we have an extended code
mjr 77:0b96f6867312 1611 uint32_t c;
mjr 77:0b96f6867312 1612 if ((state->cmdCode & 0xFFFF0000) != 0)
mjr 77:0b96f6867312 1613 {
mjr 77:0b96f6867312 1614 // Extended code. These are transmitted as two codes in
mjr 77:0b96f6867312 1615 // a row, one for the shift code in the high 16 bits, and
mjr 77:0b96f6867312 1616 // one for the subcode in the low 16 bits. Transmit the
mjr 77:0b96f6867312 1617 // shift code on even reps and the subcode on odd reps.
mjr 77:0b96f6867312 1618 if (state->rep == 0 || state->rep == 2)
mjr 77:0b96f6867312 1619 {
mjr 77:0b96f6867312 1620 // even rep - use the shift code
mjr 77:0b96f6867312 1621 c = (state->cmdCode >> 16) & 0xFFFF;
mjr 77:0b96f6867312 1622
mjr 77:0b96f6867312 1623 // wrap back to rep 0 on rep 2
mjr 77:0b96f6867312 1624 state->rep = 0;
mjr 77:0b96f6867312 1625 }
mjr 77:0b96f6867312 1626 else
mjr 77:0b96f6867312 1627 {
mjr 77:0b96f6867312 1628 // odd rep - use the subcode
mjr 77:0b96f6867312 1629 c = state->cmdCode & 0xFFFF;
mjr 77:0b96f6867312 1630 }
mjr 77:0b96f6867312 1631 }
mjr 77:0b96f6867312 1632 else
mjr 77:0b96f6867312 1633 {
mjr 77:0b96f6867312 1634 // it's a single-part code
mjr 77:0b96f6867312 1635 c = state->cmdCode;
mjr 77:0b96f6867312 1636 }
mjr 77:0b96f6867312 1637
mjr 77:0b96f6867312 1638 // encode it in the 32-bit original NEC format with the address
mjr 77:0b96f6867312 1639 // and command byte complemented
mjr 77:0b96f6867312 1640 uint8_t a0 = uint8_t((c >> 8) & 0xff);
mjr 77:0b96f6867312 1641 uint8_t a1 = uint8_t(~a0);
mjr 77:0b96f6867312 1642 uint8_t c0 = uint8_t(c & 0xff);
mjr 77:0b96f6867312 1643 uint8_t c1 = uint8_t(~c0);
mjr 77:0b96f6867312 1644 state->bitstream = (uint64_t(c1) << 24) | (uint64_t(c0) << 16)
mjr 77:0b96f6867312 1645 | (uint64_t(a1) << 8) | uint64_t(a0);
mjr 77:0b96f6867312 1646 state->nbits = 32;
mjr 77:0b96f6867312 1647
mjr 77:0b96f6867312 1648 // Pioneer *can't* use NEC dittos even if the caller thinks we
mjr 77:0b96f6867312 1649 // should, because that breaks the shift-code model Pioneer uses
mjr 77:0b96f6867312 1650 state->dittos = false;
mjr 77:0b96f6867312 1651 }
mjr 97:fc7727303038 1652 else if (state->protocolId == IRPRO_TCLROKU)
mjr 97:fc7727303038 1653 {
mjr 97:fc7727303038 1654 // TCL Roku models use doubled code words. The second code
mjr 97:fc7727303038 1655 // word in the pair is always the same as the first with
mjr 97:fc7727303038 1656 // the two bytes of the command field XOR'd with 0x80.
mjr 97:fc7727303038 1657 uint32_t c;
mjr 97:fc7727303038 1658 if (state->rep == 0 || state->rep == 2)
mjr 97:fc7727303038 1659 {
mjr 97:fc7727303038 1660 // even rep - use the nominal command code
mjr 97:fc7727303038 1661 c = state->cmdCode;
mjr 97:fc7727303038 1662
mjr 97:fc7727303038 1663 // wrap back to rep 0 on rep 2
mjr 97:fc7727303038 1664 state->rep = 0;
mjr 97:fc7727303038 1665 }
mjr 97:fc7727303038 1666 else
mjr 97:fc7727303038 1667 {
mjr 97:fc7727303038 1668 // odd rep - use the code XOR'd with 0x8080
mjr 97:fc7727303038 1669 c = state->cmdCode ^ 0x8080;
mjr 97:fc7727303038 1670 }
mjr 97:fc7727303038 1671
mjr 97:fc7727303038 1672 // use the normal NEC32 encoding, substituting the possibly
mjr 97:fc7727303038 1673 // modified code field 'c' we calculated above
mjr 97:fc7727303038 1674 uint32_t orig = uint32_t(state->cmdCode);
mjr 97:fc7727303038 1675 uint8_t a0 = uint8_t((orig >> 24) & 0xff);
mjr 97:fc7727303038 1676 uint8_t a1 = uint8_t((orig >> 16) & 0xff);
mjr 97:fc7727303038 1677 uint8_t c0 = uint8_t((c >> 8) & 0xff);
mjr 97:fc7727303038 1678 uint8_t c1 = uint8_t(c & 0xff);
mjr 97:fc7727303038 1679 state->bitstream =
mjr 97:fc7727303038 1680 (uint64_t(c1) << 24) | (uint64_t(c0) << 16)
mjr 97:fc7727303038 1681 | (uint64_t(a1) << 8) | uint64_t(a0);
mjr 97:fc7727303038 1682 state->nbits = 32;
mjr 97:fc7727303038 1683
mjr 97:fc7727303038 1684 // this protocol doesn't use dittos
mjr 97:fc7727303038 1685 state->dittos = false;
mjr 97:fc7727303038 1686 }
mjr 77:0b96f6867312 1687 else if (state->protocolId == IRPRO_NEC48)
mjr 77:0b96f6867312 1688 {
mjr 77:0b96f6867312 1689 // NEC 48-bit code. We store the bytes in the universal
mjr 77:0b96f6867312 1690 // representation in order A0 A1 C0 C1 E0 E1. Reverse this
mjr 77:0b96f6867312 1691 // order for transmission.
mjr 77:0b96f6867312 1692 uint64_t code = state->cmdCode;
mjr 77:0b96f6867312 1693 uint8_t a0 = uint8_t((code >> 40) & 0xff);
mjr 77:0b96f6867312 1694 uint8_t a1 = uint8_t((code >> 32) & 0xff);
mjr 77:0b96f6867312 1695 uint8_t c0 = uint8_t((code >> 24) & 0xff);
mjr 77:0b96f6867312 1696 uint8_t c1 = uint8_t((code >> 16)& 0xff);
mjr 77:0b96f6867312 1697 uint8_t e0 = uint8_t((code >> 8) & 0xff);
mjr 77:0b96f6867312 1698 uint8_t e1 = uint8_t((code) & 0xff);
mjr 77:0b96f6867312 1699 state->bitstream =
mjr 77:0b96f6867312 1700 (uint64_t(e1) << 40) | (uint64_t(e0) << 32)
mjr 77:0b96f6867312 1701 | (uint64_t(c1) << 24) | (uint64_t(c0) << 16)
mjr 77:0b96f6867312 1702 | (uint64_t(a1) << 8) | uint64_t(a0);
mjr 77:0b96f6867312 1703 state->nbits = 48;
mjr 77:0b96f6867312 1704 }
mjr 77:0b96f6867312 1705 else
mjr 77:0b96f6867312 1706 {
mjr 77:0b96f6867312 1707 // NEC 32-bit code. The universal representation stores
mjr 77:0b96f6867312 1708 // the bytes in order A0 A1 C0 C1. For transmission, we
mjr 77:0b96f6867312 1709 // need to reverse this to C1 C0 A1 A0.
mjr 77:0b96f6867312 1710 uint32_t code = uint32_t(state->cmdCode);
mjr 77:0b96f6867312 1711 uint8_t a0 = uint8_t((code >> 24) & 0xff);
mjr 77:0b96f6867312 1712 uint8_t a1 = uint8_t((code >> 16) & 0xff);
mjr 77:0b96f6867312 1713 uint8_t c0 = uint8_t((code >> 8) & 0xff);
mjr 77:0b96f6867312 1714 uint8_t c1 = uint8_t(code & 0xff);
mjr 77:0b96f6867312 1715 state->bitstream =
mjr 77:0b96f6867312 1716 (uint64_t(c1) << 24) | (uint64_t(c0) << 16)
mjr 77:0b96f6867312 1717 | (uint64_t(a1) << 8) | uint64_t(a0);
mjr 77:0b96f6867312 1718 state->nbits = 32;
mjr 77:0b96f6867312 1719 }
mjr 77:0b96f6867312 1720 }
mjr 77:0b96f6867312 1721
mjr 77:0b96f6867312 1722 // NEC uses a special "ditto" code for repeats. The ditto consists
mjr 77:0b96f6867312 1723 // of the normal header mark, half a header space, a regular data
mjr 77:0b96f6867312 1724 // mark. After this, a standard inter-message gap follows, and then
mjr 77:0b96f6867312 1725 // we repeat the ditto as long as the button is held down.
mjr 77:0b96f6867312 1726 virtual int txDittoStep(IRTXState *state)
mjr 77:0b96f6867312 1727 {
mjr 77:0b96f6867312 1728 // send the ditto
mjr 77:0b96f6867312 1729 uint32_t t;
mjr 77:0b96f6867312 1730 switch (state->step)
mjr 77:0b96f6867312 1731 {
mjr 77:0b96f6867312 1732 case 0:
mjr 77:0b96f6867312 1733 // Ditto header mark
mjr 77:0b96f6867312 1734 state->step++;
mjr 77:0b96f6867312 1735 state->pin->write(pwmDutyCycle());
mjr 77:0b96f6867312 1736
mjr 77:0b96f6867312 1737 // use the special ditto mark timing if it's different; 0 means
mjr 77:0b96f6867312 1738 // that we use the same timing as the standard data frame header
mjr 77:0b96f6867312 1739 return (t = tDittoMark()) != 0 ? t : tHeaderMark();
mjr 77:0b96f6867312 1740
mjr 77:0b96f6867312 1741 case 1:
mjr 77:0b96f6867312 1742 // Ditto header space
mjr 77:0b96f6867312 1743 state->step++;
mjr 77:0b96f6867312 1744 state->pin->write(0);
mjr 77:0b96f6867312 1745
mjr 77:0b96f6867312 1746 // use the special ditto timing if it's different
mjr 77:0b96f6867312 1747 return (t = tDittoSpace()) != 0 ? t : tHeaderSpace();
mjr 77:0b96f6867312 1748
mjr 77:0b96f6867312 1749 case 2:
mjr 77:0b96f6867312 1750 // Data section. NEC-32X sends one data bit. The others
mjr 77:0b96f6867312 1751 // send no data bits, so go straight to the stop bit.
mjr 77:0b96f6867312 1752 if (state->protocolId == IRPRO_NEC32X && state->bit == 0)
mjr 77:0b96f6867312 1753 return txDataStep(state);
mjr 77:0b96f6867312 1754
mjr 77:0b96f6867312 1755 // for others, fall through to the stop mark
mjr 77:0b96f6867312 1756 state->step++;
mjr 77:0b96f6867312 1757 // FALL THROUGH...
mjr 77:0b96f6867312 1758
mjr 77:0b96f6867312 1759 case 3:
mjr 77:0b96f6867312 1760 // stop mark
mjr 77:0b96f6867312 1761 state->step++;
mjr 77:0b96f6867312 1762 state->pin->write(pwmDutyCycle());
mjr 77:0b96f6867312 1763 return tMark();
mjr 77:0b96f6867312 1764
mjr 77:0b96f6867312 1765 case 4:
mjr 77:0b96f6867312 1766 // send a gap
mjr 77:0b96f6867312 1767 state->step++;
mjr 77:0b96f6867312 1768 state->pin->write(0);
mjr 77:0b96f6867312 1769 return 108000 - state->txTime.read_us();
mjr 77:0b96f6867312 1770
mjr 77:0b96f6867312 1771 default:
mjr 77:0b96f6867312 1772 // done
mjr 77:0b96f6867312 1773 return txEnd(state);
mjr 77:0b96f6867312 1774 }
mjr 77:0b96f6867312 1775 }
mjr 77:0b96f6867312 1776
mjr 77:0b96f6867312 1777 };
mjr 77:0b96f6867312 1778
mjr 97:fc7727303038 1779 // NEC-32, NEC-48, Pioneer, and TCL TVs with embedded Roku
mjr 77:0b96f6867312 1780 class IRPNEC_32_48: public IRPNEC
mjr 77:0b96f6867312 1781 {
mjr 77:0b96f6867312 1782 public:
mjr 77:0b96f6867312 1783 IRPNEC_32_48()
mjr 77:0b96f6867312 1784 {
mjr 77:0b96f6867312 1785 pioneerPrvCode = 0;
mjr 97:fc7727303038 1786 tclRokuPrvCode = 0;
mjr 77:0b96f6867312 1787 }
mjr 77:0b96f6867312 1788
mjr 77:0b96f6867312 1789 // name and ID
mjr 77:0b96f6867312 1790 virtual const char *name() const { return "NEC"; }
mjr 77:0b96f6867312 1791 virtual int id() const { return IRPRO_NEC32; }
mjr 77:0b96f6867312 1792 virtual int necPro(int bits) const
mjr 77:0b96f6867312 1793 {
mjr 77:0b96f6867312 1794 return bits == 48 ? IRPRO_NEC48 : IRPRO_NEC32;
mjr 77:0b96f6867312 1795 }
mjr 77:0b96f6867312 1796
mjr 77:0b96f6867312 1797 // we encode several protocols
mjr 77:0b96f6867312 1798 virtual bool isSenderFor(int pro) const
mjr 77:0b96f6867312 1799 {
mjr 97:fc7727303038 1800 return pro == IRPRO_NEC32
mjr 97:fc7727303038 1801 || pro == IRPRO_NEC48
mjr 97:fc7727303038 1802 || pro == IRPRO_PIONEER
mjr 97:fc7727303038 1803 || pro == IRPRO_TCLROKU;
mjr 77:0b96f6867312 1804 }
mjr 77:0b96f6867312 1805
mjr 77:0b96f6867312 1806 // NEC-32 and NEC-48 use the same framing
mjr 77:0b96f6867312 1807 virtual uint32_t tHeaderMark() const { return 9000; }
mjr 77:0b96f6867312 1808 virtual uint32_t tHeaderSpace() const { return 4500; }
mjr 77:0b96f6867312 1809 virtual uint32_t tDittoMark() const { return 9000; }
mjr 77:0b96f6867312 1810 virtual uint32_t tDittoSpace() const { return 2250; }
mjr 77:0b96f6867312 1811 virtual int nbits() const { return 48; }
mjr 77:0b96f6867312 1812
mjr 77:0b96f6867312 1813 // receiver format descriptor
mjr 77:0b96f6867312 1814 // decode, check, and report a code value
mjr 77:0b96f6867312 1815 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 1816 {
mjr 77:0b96f6867312 1817 // If we're in Pioneer mode, use special handling
mjr 77:0b96f6867312 1818 if (isPioneerCode())
mjr 77:0b96f6867312 1819 {
mjr 77:0b96f6867312 1820 rxClosePioneer(receiver);
mjr 77:0b96f6867312 1821 return;
mjr 77:0b96f6867312 1822 }
mjr 77:0b96f6867312 1823
mjr 77:0b96f6867312 1824 // The new code isn't a pioneer code, so if we had a Pioneer
mjr 77:0b96f6867312 1825 // code stashed, it doesn't have a second half. Report is as
mjr 77:0b96f6867312 1826 // a standalone code.
mjr 77:0b96f6867312 1827 if (pioneerPrvCode != 0)
mjr 77:0b96f6867312 1828 {
mjr 77:0b96f6867312 1829 reportPioneerFormat(receiver, pioneerPrvCode);
mjr 77:0b96f6867312 1830 pioneerPrvCode = 0;
mjr 77:0b96f6867312 1831 }
mjr 77:0b96f6867312 1832
mjr 97:fc7727303038 1833 // If we're in TCL/Roku mode, use special handling
mjr 97:fc7727303038 1834 if (isTCLRokuCode())
mjr 97:fc7727303038 1835 {
mjr 97:fc7727303038 1836 rxCloseTCLRoku(receiver);
mjr 97:fc7727303038 1837 return;
mjr 97:fc7727303038 1838 }
mjr 97:fc7727303038 1839
mjr 97:fc7727303038 1840 // The new code isn't a TCL/Roku code, so if we had a first half
mjr 97:fc7727303038 1841 // code stashed, it doesn't have a second half forthcoming. Report
mjr 97:fc7727303038 1842 // it as a standalone code.
mjr 97:fc7727303038 1843 if (tclRokuPrvCode != 0)
mjr 97:fc7727303038 1844 {
mjr 97:fc7727303038 1845 reportTCLRokuFormat(receiver, tclRokuPrvCode);
mjr 97:fc7727303038 1846 tclRokuPrvCode = 0;
mjr 97:fc7727303038 1847 }
mjr 97:fc7727303038 1848
mjr 77:0b96f6867312 1849 // use the generic NEC handling
mjr 77:0b96f6867312 1850 IRPNEC::rxClose(receiver, ditto);
mjr 77:0b96f6867312 1851 }
mjr 77:0b96f6867312 1852
mjr 77:0b96f6867312 1853 virtual void rxIdle(IRRecvProIfc *receiver)
mjr 77:0b96f6867312 1854 {
mjr 97:fc7727303038 1855 // if we have a stashed prior Pioneer code, close it out, since
mjr 77:0b96f6867312 1856 // no more codes are forthcoming
mjr 77:0b96f6867312 1857 if (pioneerPrvCode != 0)
mjr 77:0b96f6867312 1858 {
mjr 77:0b96f6867312 1859 reportPioneerFormat(receiver, pioneerPrvCode);
mjr 77:0b96f6867312 1860 pioneerPrvCode = 0;
mjr 77:0b96f6867312 1861 }
mjr 97:fc7727303038 1862
mjr 97:fc7727303038 1863 // likewise for TCL/Roku stashed prior codes
mjr 97:fc7727303038 1864 if (tclRokuPrvCode != 0)
mjr 97:fc7727303038 1865 {
mjr 97:fc7727303038 1866 reportTCLRokuFormat(receiver, tclRokuPrvCode);
mjr 97:fc7727303038 1867 tclRokuPrvCode = 0;
mjr 97:fc7727303038 1868 }
mjr 77:0b96f6867312 1869 }
mjr 77:0b96f6867312 1870
mjr 77:0b96f6867312 1871 // close out a Pioneer code
mjr 77:0b96f6867312 1872 virtual void rxClosePioneer(IRRecvProIfc *receiver)
mjr 77:0b96f6867312 1873 {
mjr 77:0b96f6867312 1874 // Check to see if we have a valid previous code and/or
mjr 77:0b96f6867312 1875 // a valid new code.
mjr 77:0b96f6867312 1876 if (pioneerPrvCode != 0)
mjr 77:0b96f6867312 1877 {
mjr 77:0b96f6867312 1878 // We have a stashed Pioneer code plus the new one. If
mjr 77:0b96f6867312 1879 // they're different, we must have an extended code with
mjr 77:0b96f6867312 1880 // a "shift" prefix.
mjr 77:0b96f6867312 1881 if (pioneerPrvCode != code)
mjr 77:0b96f6867312 1882 {
mjr 77:0b96f6867312 1883 // distinct code - it's an extended code with a shift
mjr 77:0b96f6867312 1884 reportPioneerFormat(receiver, pioneerPrvCode, code);
mjr 77:0b96f6867312 1885 }
mjr 77:0b96f6867312 1886 else
mjr 77:0b96f6867312 1887 {
mjr 77:0b96f6867312 1888 // same code - it's just a repeat, so report it twice
mjr 77:0b96f6867312 1889 reportPioneerFormat(receiver, code);
mjr 77:0b96f6867312 1890 reportPioneerFormat(receiver, code);
mjr 77:0b96f6867312 1891 }
mjr 77:0b96f6867312 1892
mjr 77:0b96f6867312 1893 // we've now consumed the previous code
mjr 77:0b96f6867312 1894 pioneerPrvCode = 0;
mjr 77:0b96f6867312 1895 }
mjr 77:0b96f6867312 1896 else
mjr 77:0b96f6867312 1897 {
mjr 77:0b96f6867312 1898 // There's no stashed code. Don't report the new one yet,
mjr 77:0b96f6867312 1899 // since it might be a "shift" prefix. Stash it until we
mjr 77:0b96f6867312 1900 // find out if another code follows.
mjr 77:0b96f6867312 1901 pioneerPrvCode = code;
mjr 77:0b96f6867312 1902 bit = 0;
mjr 77:0b96f6867312 1903 code = 0;
mjr 77:0b96f6867312 1904 }
mjr 77:0b96f6867312 1905 }
mjr 77:0b96f6867312 1906
mjr 77:0b96f6867312 1907 // determine if we have Pioneer address
mjr 77:0b96f6867312 1908 bool isPioneerCode()
mjr 77:0b96f6867312 1909 {
mjr 97:fc7727303038 1910 // pull out the command and address fields
mjr 77:0b96f6867312 1911 uint8_t c1 = uint8_t((code >> 24) & 0xff);
mjr 77:0b96f6867312 1912 uint8_t c0 = uint8_t((code >> 16) & 0xff);
mjr 77:0b96f6867312 1913 uint8_t a1 = uint8_t((code >> 8) & 0xff);
mjr 77:0b96f6867312 1914 uint8_t a0 = uint8_t(code & 0xff);
mjr 77:0b96f6867312 1915
mjr 77:0b96f6867312 1916 // Pioneer uses device codes A0..AF, with A1 complemented, and
mjr 77:0b96f6867312 1917 // uses only the 32-bit code format. Pioneer also always uses
mjr 77:0b96f6867312 1918 // a complemented C0-C1 pair.
mjr 77:0b96f6867312 1919 return bit == 32
mjr 77:0b96f6867312 1920 && (a0 >= 0xA0 && a0 <= 0xAF)
mjr 77:0b96f6867312 1921 && a0 == uint8_t(~a1)
mjr 77:0b96f6867312 1922 && c0 == uint8_t(~c1);
mjr 77:0b96f6867312 1923 }
mjr 97:fc7727303038 1924
mjr 77:0b96f6867312 1925 // Report a code in Pioneer format. This takes the first address
mjr 77:0b96f6867312 1926 // byte and combines it with the first command byte to form a 16-bit
mjr 77:0b96f6867312 1927 // code. Pioneer writes codes in this format because the second
mjr 77:0b96f6867312 1928 // address and command bytes are always the complements of the first,
mjr 77:0b96f6867312 1929 // so they contain no information, so it makes the codes more readable
mjr 77:0b96f6867312 1930 // for human consumption to drop the redundant bits.
mjr 77:0b96f6867312 1931 void reportPioneerFormat(IRRecvProIfc *receiver, uint32_t code)
mjr 77:0b96f6867312 1932 {
mjr 77:0b96f6867312 1933 uint8_t a0 = uint8_t(code & 0xff);
mjr 77:0b96f6867312 1934 uint8_t c0 = uint8_t((code >> 16) & 0xff);
mjr 77:0b96f6867312 1935 reportCode(receiver, IRPRO_PIONEER, (uint64_t(a0) << 8) | c0,
mjr 77:0b96f6867312 1936 bool3::null, bool3::null);
mjr 77:0b96f6867312 1937 }
mjr 77:0b96f6867312 1938
mjr 77:0b96f6867312 1939 // Report an extended two-part code in Pioneer format. code1 is the
mjr 77:0b96f6867312 1940 // first code received (the "shift" prefix code), and code2 is the
mjr 77:0b96f6867312 1941 // second (the key-specific subcode). We'll convert each code to
mjr 77:0b96f6867312 1942 // the standard Pioneer 16-bit format (<address>:<key>), then pack
mjr 77:0b96f6867312 1943 // the two into a 32-bit int with the shift code in the high half.
mjr 77:0b96f6867312 1944 void reportPioneerFormat(IRRecvProIfc *receiver, uint32_t code1, uint32_t code2)
mjr 77:0b96f6867312 1945 {
mjr 77:0b96f6867312 1946 uint8_t a1 = code1 & 0xff;
mjr 77:0b96f6867312 1947 uint8_t c1 = (code1 >> 16) & 0xff;
mjr 77:0b96f6867312 1948 uint8_t a2 = code2 & 0xff;
mjr 77:0b96f6867312 1949 uint8_t c2 = (code2 >> 16) & 0xff;
mjr 77:0b96f6867312 1950 reportCode(
mjr 77:0b96f6867312 1951 receiver, IRPRO_PIONEER,
mjr 77:0b96f6867312 1952 (uint64_t(a1) << 24) | (uint64_t(c1) << 16)
mjr 77:0b96f6867312 1953 | (uint64_t(a2) << 8) | c2,
mjr 77:0b96f6867312 1954 bool3::null, bool3::null);
mjr 77:0b96f6867312 1955 }
mjr 77:0b96f6867312 1956
mjr 97:fc7727303038 1957 // The previous Pioneer code value. This is used in decoding Pioneer
mjr 97:fc7727303038 1958 // codes, since some keys in the Pioneer scheme send a "shift" prefix
mjr 97:fc7727303038 1959 // code plus a subcode.
mjr 77:0b96f6867312 1960 uint32_t pioneerPrvCode;
mjr 97:fc7727303038 1961
mjr 97:fc7727303038 1962 // close out a TCL/Roku code
mjr 97:fc7727303038 1963 virtual void rxCloseTCLRoku(IRRecvProIfc *receiver)
mjr 97:fc7727303038 1964 {
mjr 97:fc7727303038 1965 // Check to see if we have a valid previous code and/or
mjr 97:fc7727303038 1966 // a valid new code.
mjr 97:fc7727303038 1967 if (tclRokuPrvCode != 0)
mjr 97:fc7727303038 1968 {
mjr 97:fc7727303038 1969 // We have a stashed code for the TCL/Roku double-code-word
mjr 97:fc7727303038 1970 // scheme. If this one matches the previous one with the
mjr 97:fc7727303038 1971 // "command field" bytes XOR'd with 0x80, it's the second
mjr 97:fc7727303038 1972 // code in the pair. Otherwise it must be a new code.
mjr 97:fc7727303038 1973 if (tclRokuPrvCode == code ^ 0x80800000)
mjr 97:fc7727303038 1974 {
mjr 97:fc7727303038 1975 // it's the matching code from the pair - report it as one code
mjr 97:fc7727303038 1976 reportTCLRokuFormat(receiver, tclRokuPrvCode);
mjr 97:fc7727303038 1977 }
mjr 97:fc7727303038 1978 else
mjr 97:fc7727303038 1979 {
mjr 97:fc7727303038 1980 // it's not a match, so it must be a distinct code - report
mjr 97:fc7727303038 1981 // the two codes separately
mjr 97:fc7727303038 1982 reportTCLRokuFormat(receiver, tclRokuPrvCode);
mjr 97:fc7727303038 1983 reportTCLRokuFormat(receiver, code);
mjr 97:fc7727303038 1984 }
mjr 97:fc7727303038 1985
mjr 97:fc7727303038 1986 // we've now consumed the previous code
mjr 97:fc7727303038 1987 tclRokuPrvCode = 0;
mjr 97:fc7727303038 1988 }
mjr 97:fc7727303038 1989 else
mjr 97:fc7727303038 1990 {
mjr 97:fc7727303038 1991 // There's no stashed code. Don't report the new one yet, since
mjr 97:fc7727303038 1992 // it might be the first of a pair.
mjr 97:fc7727303038 1993 tclRokuPrvCode = code;
mjr 97:fc7727303038 1994 bit = 0;
mjr 97:fc7727303038 1995 code = 0;
mjr 97:fc7727303038 1996 }
mjr 97:fc7727303038 1997 }
mjr 97:fc7727303038 1998
mjr 97:fc7727303038 1999 // Report a code in TCL/Roku format. This just uses the standard NEC
mjr 97:fc7727303038 2000 // reporting.
mjr 97:fc7727303038 2001 void reportTCLRokuFormat(IRRecvProIfc *receiver, uint32_t code)
mjr 97:fc7727303038 2002 {
mjr 97:fc7727303038 2003 // put the bytes in the reporting order for NEC: A0 A1 C0 C1
mjr 97:fc7727303038 2004 uint8_t c1 = uint8_t((code >> 24) & 0xff);
mjr 97:fc7727303038 2005 uint8_t c0 = uint8_t((code >> 16) & 0xff);
mjr 97:fc7727303038 2006 uint8_t a1 = uint8_t((code >> 8) & 0xff);
mjr 97:fc7727303038 2007 uint8_t a0 = uint8_t(code & 0xff);
mjr 97:fc7727303038 2008 uint64_t codeOut = (uint64_t(a0) << 24) | (uint64_t(a1) << 16)
mjr 97:fc7727303038 2009 | (uint64_t(c0) << 8) | uint64_t(c1);
mjr 97:fc7727303038 2010
mjr 97:fc7727303038 2011 // report it
mjr 97:fc7727303038 2012 reportCode(receiver, IRPRO_TCLROKU, codeOut, bool3::null, bool3::null);
mjr 97:fc7727303038 2013 }
mjr 97:fc7727303038 2014
mjr 97:fc7727303038 2015 // determine if we have a TCL/Roku address
mjr 97:fc7727303038 2016 bool isTCLRokuCode()
mjr 97:fc7727303038 2017 {
mjr 97:fc7727303038 2018 // It's a TCL/Roku model if the address field is EA C7
mjr 97:fc7727303038 2019 return (code & 0xFFFF) == 0xC7EA;
mjr 97:fc7727303038 2020 }
mjr 97:fc7727303038 2021
mjr 97:fc7727303038 2022 // The previous TCL/Roku code value. All codes in this protocol use
mjr 97:fc7727303038 2023 // doubled code words, so we keep track of the first word here.
mjr 97:fc7727303038 2024 uint32_t tclRokuPrvCode;
mjr 77:0b96f6867312 2025 };
mjr 77:0b96f6867312 2026
mjr 77:0b96f6867312 2027 // NEC-32x. This is a minor variation on the standard NEC-32 protocol,
mjr 77:0b96f6867312 2028 // with a slightly different header signature and a different ditto
mjr 77:0b96f6867312 2029 // pattern.
mjr 77:0b96f6867312 2030 class IRPNEC_32x: public IRPNEC
mjr 77:0b96f6867312 2031 {
mjr 77:0b96f6867312 2032 public:
mjr 77:0b96f6867312 2033 virtual int id() const { return IRPRO_NEC32X; }
mjr 77:0b96f6867312 2034 virtual const char *name() const { return "NEC32x"; }
mjr 77:0b96f6867312 2035 virtual int necPro(int bits) const { return IRPRO_NEC32X; }
mjr 77:0b96f6867312 2036
mjr 77:0b96f6867312 2037 virtual int nbits() const { return 32; }
mjr 77:0b96f6867312 2038 virtual uint32_t tHeaderMark() const { return 4500; }
mjr 77:0b96f6867312 2039 virtual uint32_t tHeaderSpace() const { return 4500; }
mjr 77:0b96f6867312 2040
mjr 77:0b96f6867312 2041 // Close out the code. NEC-32x has an unusual variation of the
mjr 77:0b96f6867312 2042 // NEC ditto: it uses the same header as a regular code, but only
mjr 77:0b96f6867312 2043 // has a 1-bit payload.
mjr 77:0b96f6867312 2044 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 2045 {
mjr 77:0b96f6867312 2046 if (bit == 1)
mjr 77:0b96f6867312 2047 reportCode(receiver, IRPRO_NEC32X, code, bool3::null, true);
mjr 77:0b96f6867312 2048 else
mjr 77:0b96f6867312 2049 IRPNEC::rxClose(receiver, ditto);
mjr 77:0b96f6867312 2050 }
mjr 77:0b96f6867312 2051
mjr 77:0b96f6867312 2052 };
mjr 77:0b96f6867312 2053
mjr 77:0b96f6867312 2054 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 2055 //
mjr 77:0b96f6867312 2056 // Kaseikyo protocol handler. Like NEC, this is a quasi industry standard
mjr 77:0b96f6867312 2057 // used by many companies. Unlike NEC, it seems to be used consistently
mjr 77:0b96f6867312 2058 // by just about everyone. There are only two main variations: a 48-bit
mjr 77:0b96f6867312 2059 // coding and a 56-bit coding.
mjr 77:0b96f6867312 2060 //
mjr 77:0b96f6867312 2061 // For all versions, the first 16 bits in serial order provide an OEM ID.
mjr 77:0b96f6867312 2062 // We use this to report manufacturer-specific protocol IDs, even though
mjr 77:0b96f6867312 2063 // the low-level coding is the same for all of them. Differentiating
mjr 77:0b96f6867312 2064 // by manufacturer is mostly for cosmetic reasons, so that human users
mjr 77:0b96f6867312 2065 // looking at learned codes or looking for codes to program will see
mjr 77:0b96f6867312 2066 // names matching their equipment. In some cases it's also useful for
mjr 77:0b96f6867312 2067 // interpreting the internal data fields within the bit string; some
mjr 77:0b96f6867312 2068 // OEMs use checksums or other fields that clients might want to
mjr 77:0b96f6867312 2069 // interpret.
mjr 77:0b96f6867312 2070 //
mjr 77:0b96f6867312 2071 class IRPKaseikyo: public IRPSpaceLength<uint64_t>
mjr 77:0b96f6867312 2072 {
mjr 77:0b96f6867312 2073 public:
mjr 77:0b96f6867312 2074 IRPKaseikyo() { }
mjr 77:0b96f6867312 2075
mjr 77:0b96f6867312 2076 // name and ID
mjr 77:0b96f6867312 2077 virtual const char *name() const { return "Kaseikyo"; }
mjr 77:0b96f6867312 2078 virtual int id() const { return IRPRO_KASEIKYO48; }
mjr 77:0b96f6867312 2079
mjr 77:0b96f6867312 2080 // we handle all of the OEM-specific protocols
mjr 77:0b96f6867312 2081 virtual bool isSenderFor(int id) const
mjr 77:0b96f6867312 2082 {
mjr 77:0b96f6867312 2083 switch (id)
mjr 77:0b96f6867312 2084 {
mjr 77:0b96f6867312 2085 case IRPRO_KASEIKYO48:
mjr 77:0b96f6867312 2086 case IRPRO_KASEIKYO56:
mjr 77:0b96f6867312 2087 case IRPRO_DENONK:
mjr 77:0b96f6867312 2088 case IRPRO_FUJITSU48:
mjr 77:0b96f6867312 2089 case IRPRO_FUJITSU56:
mjr 77:0b96f6867312 2090 case IRPRO_JVC48:
mjr 77:0b96f6867312 2091 case IRPRO_JVC56:
mjr 77:0b96f6867312 2092 case IRPRO_MITSUBISHIK:
mjr 77:0b96f6867312 2093 case IRPRO_PANASONIC48:
mjr 77:0b96f6867312 2094 case IRPRO_PANASONIC56:
mjr 77:0b96f6867312 2095 case IRPRO_SHARPK:
mjr 77:0b96f6867312 2096 case IRPRO_TEACK:
mjr 77:0b96f6867312 2097 return true;
mjr 77:0b96f6867312 2098
mjr 77:0b96f6867312 2099 default:
mjr 77:0b96f6867312 2100 return false;
mjr 77:0b96f6867312 2101 }
mjr 77:0b96f6867312 2102 }
mjr 77:0b96f6867312 2103
mjr 77:0b96f6867312 2104 // code boundary parameters
mjr 77:0b96f6867312 2105 virtual uint32_t tHeaderMark() const { return 3500; }
mjr 77:0b96f6867312 2106 virtual uint32_t tHeaderSpace() const { return 1750; }
mjr 77:0b96f6867312 2107 virtual uint32_t minRxGap() const { return 2500; }
mjr 77:0b96f6867312 2108 virtual uint32_t txGap(IRTXState *state) const { return 173000; }
mjr 77:0b96f6867312 2109
mjr 77:0b96f6867312 2110 // space length coding
mjr 77:0b96f6867312 2111 virtual int nbits() const { return 56; }
mjr 77:0b96f6867312 2112 virtual int tMark() const { return 420; }
mjr 77:0b96f6867312 2113 virtual int tZero() const { return 420; }
mjr 77:0b96f6867312 2114 virtual int tOne() const { return 1300; }
mjr 77:0b96f6867312 2115
mjr 77:0b96f6867312 2116 // protocol/OEM mappings
mjr 77:0b96f6867312 2117 struct OEMMap
mjr 77:0b96f6867312 2118 {
mjr 77:0b96f6867312 2119 uint16_t oem;
mjr 77:0b96f6867312 2120 uint8_t pro;
mjr 77:0b96f6867312 2121 uint8_t bits;
mjr 77:0b96f6867312 2122 };
mjr 77:0b96f6867312 2123 static const OEMMap oemMap[];
mjr 77:0b96f6867312 2124 static const int nOemMap;
mjr 77:0b96f6867312 2125
mjr 77:0b96f6867312 2126 // close code reception
mjr 77:0b96f6867312 2127 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 2128 {
mjr 77:0b96f6867312 2129 // it must be a 48-bit or 56-bit code
mjr 77:0b96f6867312 2130 if (bit != 48 && bit != 56)
mjr 77:0b96f6867312 2131 return;
mjr 77:0b96f6867312 2132
mjr 77:0b96f6867312 2133 // pull out the OEM code in the low 16 bits
mjr 77:0b96f6867312 2134 int oem = int(code & 0xFFFF);
mjr 77:0b96f6867312 2135
mjr 77:0b96f6867312 2136 // Find the protocol based on the OEM
mjr 77:0b96f6867312 2137 uint8_t pro = bit == 48 ? IRPRO_KASEIKYO48 : IRPRO_KASEIKYO56;
mjr 77:0b96f6867312 2138 for (int i = 0 ; i < nOemMap ; ++i)
mjr 77:0b96f6867312 2139 {
mjr 77:0b96f6867312 2140 if (oemMap[i].oem == oem && oemMap[i].bits == bit)
mjr 77:0b96f6867312 2141 {
mjr 77:0b96f6867312 2142 pro = oemMap[i].pro;
mjr 77:0b96f6867312 2143 break;
mjr 77:0b96f6867312 2144 }
mjr 77:0b96f6867312 2145 }
mjr 77:0b96f6867312 2146
mjr 77:0b96f6867312 2147 // report the code
mjr 77:0b96f6867312 2148 reportCode(receiver, pro, code, bool3::null, bool3::null);
mjr 77:0b96f6867312 2149 }
mjr 77:0b96f6867312 2150
mjr 77:0b96f6867312 2151 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 2152 {
mjr 77:0b96f6867312 2153 // presume no OEM and a 48-bit version of the protocol
mjr 77:0b96f6867312 2154 uint16_t oem = 0;
mjr 77:0b96f6867312 2155 state->nbits = 48;
mjr 77:0b96f6867312 2156
mjr 77:0b96f6867312 2157 // find the protocol variation in the table
mjr 77:0b96f6867312 2158 for (int i = 0 ; i < nOemMap ; ++i)
mjr 77:0b96f6867312 2159 {
mjr 77:0b96f6867312 2160 if (state->protocolId == oemMap[i].pro)
mjr 77:0b96f6867312 2161 {
mjr 77:0b96f6867312 2162 state->nbits = oemMap[i].bits;
mjr 77:0b96f6867312 2163 oem = oemMap[i].oem;
mjr 77:0b96f6867312 2164 break;
mjr 77:0b96f6867312 2165 }
mjr 77:0b96f6867312 2166 }
mjr 77:0b96f6867312 2167
mjr 77:0b96f6867312 2168 // if we found a non-zero OEM code, and it doesn't match the
mjr 77:0b96f6867312 2169 // low-order 16 data bits, replace the OEM code in the data
mjr 77:0b96f6867312 2170 uint64_t code = state->cmdCode;
mjr 77:0b96f6867312 2171 if (oem != 0 && int(code & 0xFFFF) != oem)
mjr 77:0b96f6867312 2172 code = (code & ~0xFFFFLL) | oem;
mjr 77:0b96f6867312 2173
mjr 77:0b96f6867312 2174 // store the code (with possibly updated OEM coding)
mjr 77:0b96f6867312 2175 state->bitstream = code;
mjr 77:0b96f6867312 2176 }
mjr 77:0b96f6867312 2177 };
mjr 77:0b96f6867312 2178
mjr 77:0b96f6867312 2179 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 2180 //
mjr 77:0b96f6867312 2181 // Philips RC5 protocol handler. This (along with RC6) is a quasi industry
mjr 77:0b96f6867312 2182 // standard among European CE companies, so this protocol gives us
mjr 77:0b96f6867312 2183 // compatibility with many devices from companies besides Philips.
mjr 77:0b96f6867312 2184 //
mjr 77:0b96f6867312 2185 // RC5 is a 14-bit Manchester-coded protocol. '1' bits are encoded as
mjr 77:0b96f6867312 2186 // low->high transitions.
mjr 77:0b96f6867312 2187 //
mjr 77:0b96f6867312 2188 // The 14 bits of the command are internally structured as follows:
mjr 77:0b96f6867312 2189 //
mjr 77:0b96f6867312 2190 // S F T AAAAA CCCCCC
mjr 77:0b96f6867312 2191 //
mjr 77:0b96f6867312 2192 // S = "start bit". Always 1. We omit this from the reported code
mjr 77:0b96f6867312 2193 // since it's always the same.
mjr 77:0b96f6867312 2194 //
mjr 77:0b96f6867312 2195 // F = "field bit", which selects a default (1) or extended (0) set of
mjr 77:0b96f6867312 2196 // commands. Note the reverse sensing, with '1' being the default.
mjr 77:0b96f6867312 2197 // This is because this position was a second stop bit in the original
mjr 77:0b96f6867312 2198 // code, always set to '1'. When Philips repurposed the bit as the
mjr 77:0b96f6867312 2199 // field code in a later version of the protocol, they used '1' as
mjr 77:0b96f6867312 2200 // the default for compatibility with older devices. We pass the bit
mjr 77:0b96f6867312 2201 // through as-is to the universal representation, so be aware that
mjr 77:0b96f6867312 2202 // you might have to flip it to match some published code tables.
mjr 77:0b96f6867312 2203 //
mjr 77:0b96f6867312 2204 // T = "toggle bit". This changes on each successive key press, to allow
mjr 77:0b96f6867312 2205 // the receiver to distinguish pressing the same key twice from auto-
mjr 77:0b96f6867312 2206 // repeat due to holding down the key.
mjr 77:0b96f6867312 2207 //
mjr 77:0b96f6867312 2208 // A = "address", most significant bit first; specifies which type of
mjr 77:0b96f6867312 2209 // device the command is for (e.g., "TV", "VCR", etc). The meanings
mjr 77:0b96f6867312 2210 // of the possible numeric values are arbitrarily assigned by Philips;
mjr 77:0b96f6867312 2211 // you can Philips; you can find tables online (e.g., at Wikipedia)
mjr 77:0b96f6867312 2212 // with the published assignments.
mjr 77:0b96f6867312 2213 //
mjr 77:0b96f6867312 2214 // C = "command", most significant bit first; the command code. The
mjr 77:0b96f6867312 2215 // meaning of the command code varies according to the type of device
mjr 77:0b96f6867312 2216 // in the address field. Published tables with the standard codes can
mjr 77:0b96f6867312 2217 // be found online.
mjr 77:0b96f6867312 2218 //
mjr 77:0b96f6867312 2219 // Note that this protocol doesn't have a "header" per se; it just starts
mjr 77:0b96f6867312 2220 // in directly with the first bit. As soon as we see a long enough gap,
mjr 77:0b96f6867312 2221 // we're ready for the start bit.
mjr 77:0b96f6867312 2222 //
mjr 77:0b96f6867312 2223 class IRPRC5: public IRPManchester<uint16_t>
mjr 77:0b96f6867312 2224 {
mjr 77:0b96f6867312 2225 public:
mjr 77:0b96f6867312 2226 IRPRC5() { }
mjr 77:0b96f6867312 2227
mjr 77:0b96f6867312 2228 // name and ID
mjr 77:0b96f6867312 2229 virtual const char *name() const { return "Philips RC5"; }
mjr 77:0b96f6867312 2230 virtual int id() const { return IRPRO_RC5; }
mjr 77:0b96f6867312 2231
mjr 77:0b96f6867312 2232 // code parameters
mjr 77:0b96f6867312 2233 virtual float pwmPeriod(IRTXState *state) const { return 27.7777778e-6; } // 36kHz
mjr 77:0b96f6867312 2234 virtual uint32_t minRxGap() const { return 3600; }
mjr 77:0b96f6867312 2235 virtual uint32_t txGap(IRTXState *state) const { return 114000; }
mjr 77:0b96f6867312 2236 virtual bool lsbFirst() const { return false; }
mjr 77:0b96f6867312 2237 virtual int nbits() const { return 14; }
mjr 77:0b96f6867312 2238
mjr 77:0b96f6867312 2239 // RC5 has no header; the start bit immediately follows the gap
mjr 77:0b96f6867312 2240 virtual uint32_t tHeaderMark() const { return 0; }
mjr 77:0b96f6867312 2241 virtual uint32_t tHeaderSpace() const { return 0; }
mjr 77:0b96f6867312 2242
mjr 77:0b96f6867312 2243 // Manchester coding parameters
mjr 77:0b96f6867312 2244 virtual int tHalfBit(int bit) const { return 1778/2; }
mjr 77:0b96f6867312 2245
mjr 77:0b96f6867312 2246 // After the gap, the start of the next mark is in the middle
mjr 77:0b96f6867312 2247 // of the start bit. A '1' start bit always follows the gap,
mjr 77:0b96f6867312 2248 // and a '1' bit is represented by a space-to-mark transition,
mjr 77:0b96f6867312 2249 // so the end of the gap is the middle of the start bit.
mjr 77:0b96f6867312 2250 virtual void rxReset()
mjr 77:0b96f6867312 2251 {
mjr 77:0b96f6867312 2252 IRPManchester<uint16_t>::rxReset();
mjr 77:0b96f6867312 2253 halfBitPos = 1;
mjr 77:0b96f6867312 2254 }
mjr 77:0b96f6867312 2255
mjr 77:0b96f6867312 2256 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 2257 {
mjr 77:0b96f6867312 2258 // add the start bit and toggle bit to the command code
mjr 77:0b96f6867312 2259 state->nbits = nbits();
mjr 77:0b96f6867312 2260 state->bitstream = (state->cmdCode & DataMask) | StartMask;
mjr 77:0b96f6867312 2261 if (state->toggle)
mjr 77:0b96f6867312 2262 state->bitstream |= ToggleMask;
mjr 77:0b96f6867312 2263 }
mjr 77:0b96f6867312 2264
mjr 77:0b96f6867312 2265 // report the code
mjr 77:0b96f6867312 2266 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 2267 {
mjr 77:0b96f6867312 2268 // make sure we have the full code
mjr 77:0b96f6867312 2269 if (bit == 14)
mjr 77:0b96f6867312 2270 {
mjr 77:0b96f6867312 2271 // Pull out the toggle bit to report separately, and zero it
mjr 77:0b96f6867312 2272 // out in the code word, so that a given key always reports
mjr 77:0b96f6867312 2273 // the same code value. Also zero out the start bit, since
mjr 77:0b96f6867312 2274 // it's really just structural.
mjr 77:0b96f6867312 2275 reportCode(
mjr 77:0b96f6867312 2276 receiver, id(),
mjr 77:0b96f6867312 2277 code & ~(StartMask | ToggleMask),
mjr 77:0b96f6867312 2278 (code & ToggleMask) != 0, bool3::null);
mjr 77:0b96f6867312 2279 }
mjr 77:0b96f6867312 2280 }
mjr 77:0b96f6867312 2281
mjr 77:0b96f6867312 2282 // masks for the internal fields
mjr 77:0b96f6867312 2283 static const int CmdMask = 0x3F;
mjr 77:0b96f6867312 2284 static const int AddrMask = 0x1F << 6;
mjr 77:0b96f6867312 2285 static const int ToggleMask = 1 << 11;
mjr 77:0b96f6867312 2286 static const int FieldMask = 1 << 12;
mjr 77:0b96f6867312 2287 static const int StartMask = 1 << 13;
mjr 77:0b96f6867312 2288 static const int DataMask = FieldMask | AddrMask | CmdMask;
mjr 77:0b96f6867312 2289 };
mjr 77:0b96f6867312 2290
mjr 77:0b96f6867312 2291 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 2292 //
mjr 77:0b96f6867312 2293 // RC6 protocol handler. This (along with RC5) is a quasi industry
mjr 77:0b96f6867312 2294 // standard among European CE companies, so this protocol gives us
mjr 77:0b96f6867312 2295 // compatibility with many devices from companies besides Philips.
mjr 77:0b96f6867312 2296 //
mjr 77:0b96f6867312 2297 // RC6 is a 21-bit Manchester-coded protocol. '1' bits are coded as
mjr 77:0b96f6867312 2298 // High->Low transitions. The bits are nominally structured into
mjr 77:0b96f6867312 2299 // fields as follows:
mjr 77:0b96f6867312 2300 //
mjr 77:0b96f6867312 2301 // S FFF T AAAAAAAA CCCCCCCC
mjr 77:0b96f6867312 2302 //
mjr 77:0b96f6867312 2303 // The fields are:
mjr 77:0b96f6867312 2304 //
mjr 77:0b96f6867312 2305 // S = start bit; always 1. We omit this from the reported value since
mjr 77:0b96f6867312 2306 // it's always the same.
mjr 77:0b96f6867312 2307 //
mjr 77:0b96f6867312 2308 // F = "field". These bits are used to select different command sets,
mjr 77:0b96f6867312 2309 // so they're effectively three additional bits (added as the three
mjr 77:0b96f6867312 2310 // most significant bits) for the command code.
mjr 77:0b96f6867312 2311 //
mjr 77:0b96f6867312 2312 // A = "address", specifying the type of device the command is for.
mjr 77:0b96f6867312 2313 // This has the same meanings as the address field in RC5.
mjr 77:0b96f6867312 2314 //
mjr 77:0b96f6867312 2315 // C = "command". The command code, specific to the device type
mjr 77:0b96f6867312 2316 // in the address field.
mjr 77:0b96f6867312 2317 //
mjr 77:0b96f6867312 2318 // As with all protocols, we don't reproduce the internal field structure
mjr 77:0b96f6867312 2319 // in the decoded command value; we simply pack all of the bits into a
mjr 77:0b96f6867312 2320 // 18-bit integer, in the order shown above, field bits at the high end.
mjr 77:0b96f6867312 2321 // (We omit the start bit, since it's a fixed element that's more properly
mjr 77:0b96f6867312 2322 // part of the protocol than part of the code.)
mjr 77:0b96f6867312 2323 //
mjr 77:0b96f6867312 2324 // Note that this protocol contains an odd exception to normal Manchester
mjr 77:0b96f6867312 2325 // coding for the "toggle" bit. This bit has a period 2X that of the other
mjr 77:0b96f6867312 2326 // bits.
mjr 77:0b96f6867312 2327 //
mjr 77:0b96f6867312 2328 class IRPRC6: public IRPManchester<uint32_t>
mjr 77:0b96f6867312 2329 {
mjr 77:0b96f6867312 2330 public:
mjr 77:0b96f6867312 2331 IRPRC6() { }
mjr 77:0b96f6867312 2332
mjr 77:0b96f6867312 2333 // name and ID
mjr 77:0b96f6867312 2334 virtual const char *name() const { return "Philips RC6"; }
mjr 77:0b96f6867312 2335 virtual int id() const { return IRPRO_RC6; }
mjr 77:0b96f6867312 2336
mjr 77:0b96f6867312 2337 // code parameters
mjr 77:0b96f6867312 2338 virtual float pwmPeriod(IRTXState *state) const { return 27.7777778e-6; } // 36kHz
mjr 77:0b96f6867312 2339 virtual uint32_t tHeaderMark() const { return 2695; }
mjr 77:0b96f6867312 2340 virtual uint32_t tHeaderSpace() const { return 895; }
mjr 77:0b96f6867312 2341 virtual uint32_t minRxGap() const { return 2650; }
mjr 77:0b96f6867312 2342 virtual uint32_t txGap(IRTXState *state) const { return 107000; }
mjr 77:0b96f6867312 2343 virtual bool lsbFirst() const { return false; }
mjr 77:0b96f6867312 2344 virtual int nbits() const { return 21; }
mjr 77:0b96f6867312 2345
mjr 77:0b96f6867312 2346 // Manchester coding parameters
mjr 77:0b96f6867312 2347 virtual int spaceToMarkBit() const { return 0; }
mjr 77:0b96f6867312 2348
mjr 77:0b96f6867312 2349 // RC6 has the weird exception to the bit timing in the Toggle bit,
mjr 77:0b96f6867312 2350 // which is twice as long as the other bits. The toggle bit is the
mjr 77:0b96f6867312 2351 // 5th bit (bit==4).
mjr 77:0b96f6867312 2352 virtual int tHalfBit(int bit) const { return bit == 4 ? 895 : 895/2; }
mjr 77:0b96f6867312 2353
mjr 77:0b96f6867312 2354 // create the bit stream for the command code
mjr 77:0b96f6867312 2355 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 2356 {
mjr 77:0b96f6867312 2357 // add the start bit and toggle bit to the command code
mjr 77:0b96f6867312 2358 state->nbits = nbits();
mjr 77:0b96f6867312 2359 state->bitstream = (state->cmdCode & DataMask) | StartMask;
mjr 77:0b96f6867312 2360 if (state->toggle)
mjr 77:0b96f6867312 2361 state->bitstream |= ToggleMask;
mjr 77:0b96f6867312 2362 }
mjr 77:0b96f6867312 2363
mjr 77:0b96f6867312 2364 // report the code
mjr 77:0b96f6867312 2365 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 2366 {
mjr 77:0b96f6867312 2367 // make sure we have the full code
mjr 77:0b96f6867312 2368 if (bit == nbits())
mjr 77:0b96f6867312 2369 {
mjr 77:0b96f6867312 2370 // Pull out the toggle bit to report separately, and zero it
mjr 77:0b96f6867312 2371 // out in the code word, so that a given key always reports
mjr 77:0b96f6867312 2372 // the same code value. Also clear the start bit, since it's
mjr 77:0b96f6867312 2373 // just structural.
mjr 77:0b96f6867312 2374 reportCode(
mjr 77:0b96f6867312 2375 receiver, id(),
mjr 77:0b96f6867312 2376 code & ~(ToggleMask | StartMask),
mjr 77:0b96f6867312 2377 (code & ToggleMask) != 0, bool3::null);
mjr 77:0b96f6867312 2378 }
mjr 77:0b96f6867312 2379 }
mjr 77:0b96f6867312 2380
mjr 77:0b96f6867312 2381 // field masks
mjr 77:0b96f6867312 2382 static const int CmdMask = 0xFF;
mjr 77:0b96f6867312 2383 static const int AddrMask = 0xFF << 8;
mjr 77:0b96f6867312 2384 static const int ToggleMask = 1 << 16;
mjr 77:0b96f6867312 2385 static const int FieldMask = 0x07 << 17;
mjr 77:0b96f6867312 2386 static const int StartMask = 1 << 20;
mjr 77:0b96f6867312 2387 static const int DataMask = FieldMask | AddrMask | CmdMask;
mjr 77:0b96f6867312 2388 };
mjr 77:0b96f6867312 2389
mjr 77:0b96f6867312 2390 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 2391 //
mjr 77:0b96f6867312 2392 // Ortek MCE remote. This device uses Manchester coding, with either 16
mjr 77:0b96f6867312 2393 // or 17 bits, depending on which keys are pressed. The low-order 5 bits
mjr 77:0b96f6867312 2394 // of either code are the device code. The interpretation of the rest of
mjr 77:0b96f6867312 2395 // the bits depends on the device code. Bits are sent least-significant
mjr 77:0b96f6867312 2396 // first (little-endian) for interpretation as integer values.
mjr 77:0b96f6867312 2397 //
mjr 77:0b96f6867312 2398 // Device code = 0x14 = Mouse. This is a 16-bit code, with the fields
mjr 77:0b96f6867312 2399 // as follows (low bit first):
mjr 77:0b96f6867312 2400 //
mjr 77:0b96f6867312 2401 // DDDDD L R MMMMM CCCC
mjr 77:0b96f6867312 2402 //
mjr 77:0b96f6867312 2403 // D = device code (common field for all codes)
mjr 77:0b96f6867312 2404 // L = left-click (1=pressed, 0=not pressed)
mjr 77:0b96f6867312 2405 // R = right-click (1=pressed, 0=not pressed)
mjr 77:0b96f6867312 2406 // M = mouse movement direction. This type of device is a mouse stick
mjr 77:0b96f6867312 2407 // rather than a mouse, so it gives only the direction of travel rather
mjr 77:0b96f6867312 2408 // than X/Y motion in pixels. There are 15 increments of motion numbered
mjr 77:0b96f6867312 2409 // 0 to 15, starting with 0 at North (straight up), going clockwise:
mjr 77:0b96f6867312 2410 // 0 = N
mjr 77:0b96f6867312 2411 // 1 = NNE
mjr 77:0b96f6867312 2412 // 2 = NE
mjr 77:0b96f6867312 2413 // 3 = ENE
mjr 77:0b96f6867312 2414 // 4 = E
mjr 77:0b96f6867312 2415 // 5 = ESE
mjr 77:0b96f6867312 2416 // 6 = SE
mjr 77:0b96f6867312 2417 // 7 = SSE
mjr 77:0b96f6867312 2418 // 8 = S
mjr 77:0b96f6867312 2419 // 9 = SSW
mjr 77:0b96f6867312 2420 // 10 = SW
mjr 77:0b96f6867312 2421 // 11 = WSW
mjr 77:0b96f6867312 2422 // 12 = W
mjr 77:0b96f6867312 2423 // 13 = WNW
mjr 77:0b96f6867312 2424 // 14 = NW
mjr 77:0b96f6867312 2425 // 15 = NNW
mjr 77:0b96f6867312 2426 // The MMMMM field contains 0x10 | the value above when a mouse motion
mjr 77:0b96f6867312 2427 // direction is pressed, so the codes will be 0x10 (N), 0x11 (NNE), ...,
mjr 77:0b96f6867312 2428 // 0x1F (NNW). These are shifted left by two in the reported function
mjr 77:0b96f6867312 2429 // code, so you'll actually see 0x40 ... 0x7C.
mjr 77:0b96f6867312 2430 // C = checksum
mjr 77:0b96f6867312 2431 //
mjr 77:0b96f6867312 2432 // There's no equivalent of the "position" code (see device 0x15 below) for
mjr 77:0b96f6867312 2433 // the mouse commands, so there's no coding for auto-repeat per se. The
mjr 77:0b96f6867312 2434 // remote does let the receiver when the last key is released, though, by
mjr 77:0b96f6867312 2435 // sending one code word with the L, R, and M bits all set to zero; this
mjr 77:0b96f6867312 2436 // apparently signifies "key up". One of these is always sent after the
mjr 77:0b96f6867312 2437 // last key is released, but it's not sent between auto-repeated codes,
mjr 77:0b96f6867312 2438 // so if you hold a key down you'll see a sequence of repeating codes
mjr 77:0b96f6867312 2439 // for that key (or key combination), followed by one "key up" code when
mjr 77:0b96f6867312 2440 // you release the last key.
mjr 77:0b96f6867312 2441 //
mjr 77:0b96f6867312 2442 // Receivers who interpret these codes will probably want to separate the
mjr 77:0b96f6867312 2443 // L, R, and M bits and treat them separately, rather than treating any given
mjr 77:0b96f6867312 2444 // combination as a discrete command. The reason is that the L and R bits
mjr 77:0b96f6867312 2445 // can combine with the mouse movement field when a left-click or right-click
mjr 77:0b96f6867312 2446 // button is held down while the movement keys are pressed. This can be used
mjr 77:0b96f6867312 2447 // to perform click-and-drag operations on a GUI.
mjr 77:0b96f6867312 2448 //
mjr 77:0b96f6867312 2449 // Device code = 0x15 = MCE buttons. This is a 17-bit code, with the
mjr 77:0b96f6867312 2450 // fields as follows (low bit first):
mjr 77:0b96f6867312 2451 //
mjr 77:0b96f6867312 2452 // DDDDD PP FFFFFF CCCC
mjr 77:0b96f6867312 2453 //
mjr 77:0b96f6867312 2454 // D = device code (common field for all codes)
mjr 77:0b96f6867312 2455 // P = "position code", for sensing repeats: 00=first, 01=middle, 10=last
mjr 77:0b96f6867312 2456 // F = function (key code)
mjr 77:0b96f6867312 2457 // C = checksum
mjr 77:0b96f6867312 2458 //
mjr 77:0b96f6867312 2459 // The checksum is the 3 + the total number of '1' bits in the other fields
mjr 77:0b96f6867312 2460 // combined.
mjr 77:0b96f6867312 2461 //
mjr 77:0b96f6867312 2462 // We report these codes in our own 16-bit format, with D in the high byte,
mjr 77:0b96f6867312 2463 // and the function code in the low byte. For the mouse commands (device 0x14),
mjr 77:0b96f6867312 2464 // the low byte is (high bit to low bit) 0MMMMMRL. For the MCE buttons, the
mjr 77:0b96f6867312 2465 // low byte is the function code (F). We drop the position code for uniformity
mjr 77:0b96f6867312 2466 // with other protocols and instead report a synthetic toggle bit, which we
mjr 77:0b96f6867312 2467 // flip whenever we see a new transmission as signified by P=0. We don't do
mjr 77:0b96f6867312 2468 // anything with the toggle bit on mouse commands.
mjr 77:0b96f6867312 2469 //
mjr 77:0b96f6867312 2470 class IRPOrtekMCE: public IRPManchester<uint32_t>
mjr 77:0b96f6867312 2471 {
mjr 77:0b96f6867312 2472 public:
mjr 77:0b96f6867312 2473 IRPOrtekMCE()
mjr 77:0b96f6867312 2474 {
mjr 77:0b96f6867312 2475 toggle = 0;
mjr 77:0b96f6867312 2476 }
mjr 77:0b96f6867312 2477
mjr 77:0b96f6867312 2478 // name and ID
mjr 77:0b96f6867312 2479 virtual const char *name() const { return "OrtekMCE"; }
mjr 77:0b96f6867312 2480 virtual int id() const { return IRPRO_ORTEKMCE; }
mjr 77:0b96f6867312 2481
mjr 77:0b96f6867312 2482 // code parameters
mjr 77:0b96f6867312 2483 virtual float pwmPeriod(IRTXState *state) const { return 25.906736e-6; } // 38.6kHz
mjr 77:0b96f6867312 2484 virtual uint32_t tHeaderMark() const { return 4*480; }
mjr 77:0b96f6867312 2485 virtual uint32_t tHeaderSpace() const { return 1*480; }
mjr 77:0b96f6867312 2486 virtual bool headerSpaceToData() const { return true; }
mjr 77:0b96f6867312 2487 virtual uint32_t minRxGap() const { return 32000; }
mjr 77:0b96f6867312 2488 virtual uint32_t txGap(IRTXState *state) const { return 40000; }
mjr 77:0b96f6867312 2489
mjr 77:0b96f6867312 2490 // We always require a final rep with position code 2, so
mjr 77:0b96f6867312 2491 // ask for a minimum of 3 reps (0, 1, 2).
mjr 77:0b96f6867312 2492 virtual int txMinReps(IRTXState *) const { return 3; }
mjr 77:0b96f6867312 2493
mjr 77:0b96f6867312 2494 // Manchester coding parameters
mjr 77:0b96f6867312 2495 virtual int nbits() const { return 17; }
mjr 77:0b96f6867312 2496 virtual int spaceToMarkBit() const { return 1; }
mjr 77:0b96f6867312 2497 virtual int tHalfBit(int bit) const { return 480; }
mjr 77:0b96f6867312 2498
mjr 77:0b96f6867312 2499 // encode the bitstream for a given code
mjr 77:0b96f6867312 2500 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 2501 {
mjr 77:0b96f6867312 2502 // Set the repeat count. If we're on any repeat > 0 and
mjr 77:0b96f6867312 2503 // the key is still pressed, reset the repeat counter to 1.
mjr 77:0b96f6867312 2504 // All repeats are "1" as long as the key is down. As soon
mjr 77:0b96f6867312 2505 // as the key is up, advance directly to "2", even if there
mjr 77:0b96f6867312 2506 // was never a "1". Our txMinRep() count is 2, so this will
mjr 77:0b96f6867312 2507 // ensure that we always transmit that last position 2 code,
mjr 77:0b96f6867312 2508 // as required by the protocol.
mjr 77:0b96f6867312 2509 if (state->rep > 0)
mjr 77:0b96f6867312 2510 state->rep = state->pressed ? 1 : 2;
mjr 77:0b96f6867312 2511
mjr 77:0b96f6867312 2512 // Function field and checksum bit position - we'll fill
mjr 77:0b96f6867312 2513 // these in momentarily according to the device type.
mjr 77:0b96f6867312 2514 uint32_t f;
mjr 77:0b96f6867312 2515 int c_shift;
mjr 77:0b96f6867312 2516
mjr 77:0b96f6867312 2517 // check the product code to determine the encoding
mjr 77:0b96f6867312 2518 uint32_t cmdcode = uint32_t(state->cmdCode);
mjr 77:0b96f6867312 2519 int dev = (int(cmdcode) >> 8) & 0xff;
mjr 77:0b96f6867312 2520 if (dev == 0x14)
mjr 77:0b96f6867312 2521 {
mjr 77:0b96f6867312 2522 // Mouse function: DDDDD L R MMMMM CCCC
mjr 77:0b96f6867312 2523 // There's no position field in the mouse messages, but
mjr 77:0b96f6867312 2524 // we always have to send a final message with all bits
mjr 77:0b96f6867312 2525 // zeroed. Do this when our rep count is 2, indicating
mjr 77:0b96f6867312 2526 // that this is the final close-out rep.
mjr 77:0b96f6867312 2527 if (state->rep == 2)
mjr 77:0b96f6867312 2528 f = 0;
mjr 77:0b96f6867312 2529 else
mjr 77:0b96f6867312 2530 f = cmdcode & 0xff;
mjr 77:0b96f6867312 2531
mjr 77:0b96f6867312 2532 // the checksum starts at the 12th bit
mjr 77:0b96f6867312 2533 c_shift = 12;
mjr 77:0b96f6867312 2534 }
mjr 77:0b96f6867312 2535 else if (dev == 0x15)
mjr 77:0b96f6867312 2536 {
mjr 77:0b96f6867312 2537 // MCE button: DDDDD PP FFFFFF CCCC
mjr 77:0b96f6867312 2538
mjr 77:0b96f6867312 2539 // Set the position field to the rep counter
mjr 77:0b96f6867312 2540 int p = state->rep;
mjr 77:0b96f6867312 2541
mjr 77:0b96f6867312 2542 // fill in the function fields: PP FFFFFF
mjr 77:0b96f6867312 2543 f = (p) | ((cmdcode & 0x3F) << 2);
mjr 77:0b96f6867312 2544
mjr 77:0b96f6867312 2545 // the checksum starts at the 13th bit
mjr 77:0b96f6867312 2546 c_shift = 13;
mjr 77:0b96f6867312 2547 }
mjr 77:0b96f6867312 2548 else
mjr 77:0b96f6867312 2549 {
mjr 77:0b96f6867312 2550 // unknown device code - just transmit the low byte as given
mjr 77:0b96f6867312 2551 f = cmdcode & 0xff;
mjr 77:0b96f6867312 2552 c_shift = 13;
mjr 77:0b96f6867312 2553 }
mjr 77:0b96f6867312 2554
mjr 77:0b96f6867312 2555 // construct the bit vector with the device code in the low 5
mjr 77:0b96f6867312 2556 // bits and code in the next 7 or 8 bits
mjr 77:0b96f6867312 2557 uint32_t bitvec = dev | (f << 5);
mjr 77:0b96f6867312 2558
mjr 77:0b96f6867312 2559 // figure the checksum: it's the number of '1' bits in
mjr 77:0b96f6867312 2560 // the rest of the fields, plus 3
mjr 77:0b96f6867312 2561 uint32_t checksum = 3;
mjr 77:0b96f6867312 2562 for (uint32_t v = bitvec ; v != 0 ; checksum += (v & 1), v >>= 1) ;
mjr 77:0b96f6867312 2563
mjr 77:0b96f6867312 2564 // construct the bit stream
mjr 77:0b96f6867312 2565 state->bitstream = bitvec | (checksum << c_shift);
mjr 77:0b96f6867312 2566 state->nbits = c_shift + 4;
mjr 77:0b96f6867312 2567 }
mjr 77:0b96f6867312 2568
mjr 77:0b96f6867312 2569 // report the code value
mjr 77:0b96f6867312 2570 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 2571 {
mjr 77:0b96f6867312 2572 // common field masks
mjr 77:0b96f6867312 2573 const uint32_t D_mask = 0x001F; // lowest 5 bits = device code
mjr 77:0b96f6867312 2574
mjr 77:0b96f6867312 2575 // pull out the common fields
mjr 77:0b96f6867312 2576 uint32_t d = code & D_mask;
mjr 77:0b96f6867312 2577
mjr 77:0b96f6867312 2578 // assume no post-toggle
mjr 77:0b96f6867312 2579 bool postToggle = false;
mjr 77:0b96f6867312 2580
mjr 77:0b96f6867312 2581 // check for the different code types, and pull out the fields
mjr 77:0b96f6867312 2582 uint32_t f, c, checkedBits;
mjr 77:0b96f6867312 2583 if (bit == 16 && d == 0x14)
mjr 77:0b96f6867312 2584 {
mjr 77:0b96f6867312 2585 // field masks for the mouse codes
mjr 77:0b96f6867312 2586 const int F_shift = 5;
mjr 77:0b96f6867312 2587 const uint32_t F_mask = 0x7f << F_shift; // next 7 bits
mjr 77:0b96f6867312 2588 const int C_shift = 12;
mjr 77:0b96f6867312 2589 const uint32_t C_mask = 0x3F << C_shift; // top 4 bits
mjr 77:0b96f6867312 2590
mjr 77:0b96f6867312 2591 // separate the fields
mjr 77:0b96f6867312 2592 f = (code & F_mask) >> F_shift;
mjr 77:0b96f6867312 2593 c = (code & C_mask) >> C_shift;
mjr 77:0b96f6867312 2594 checkedBits = code & ~C_mask;
mjr 77:0b96f6867312 2595
mjr 77:0b96f6867312 2596 // if the F bits are all zero, take it as a "key up" sequence,
mjr 77:0b96f6867312 2597 // so flip the toggle bit next time
mjr 77:0b96f6867312 2598 if (f == 0)
mjr 77:0b96f6867312 2599 postToggle = true;
mjr 77:0b96f6867312 2600 }
mjr 77:0b96f6867312 2601 else if (bit == 17 && d == 0x15)
mjr 77:0b96f6867312 2602 {
mjr 77:0b96f6867312 2603 // 17 bits with device code 0x15 = MCE keyboard commands
mjr 77:0b96f6867312 2604
mjr 77:0b96f6867312 2605 // field masks for the keyboard codes
mjr 77:0b96f6867312 2606 const int P_shift = 5;
mjr 77:0b96f6867312 2607 const uint32_t P_mask = 0x0003 << P_shift; // next 2 bits
mjr 77:0b96f6867312 2608 const int F_shift = 7;
mjr 77:0b96f6867312 2609 const uint32_t F_mask = 0x3F << F_shift; // next 6 bits
mjr 77:0b96f6867312 2610 const int C_shift = 13;
mjr 77:0b96f6867312 2611 const uint32_t C_mask = 0x0F << C_shift; // top 4 bits
mjr 77:0b96f6867312 2612
mjr 77:0b96f6867312 2613 // separate the fields
mjr 77:0b96f6867312 2614 uint32_t p = (code & P_mask) >> P_shift;
mjr 77:0b96f6867312 2615 f = (code & F_mask) >> F_shift;
mjr 77:0b96f6867312 2616 c = (code & C_mask) >> C_shift;
mjr 77:0b96f6867312 2617 checkedBits = code & ~C_mask;
mjr 77:0b96f6867312 2618
mjr 77:0b96f6867312 2619 /// validate the position code - 0,1,2 are valid, 3 is invalid
mjr 77:0b96f6867312 2620 if (p == (0x03 << P_shift))
mjr 77:0b96f6867312 2621 return;
mjr 77:0b96f6867312 2622
mjr 77:0b96f6867312 2623 // flip the toggle bit if this is the first frame in a group
mjr 77:0b96f6867312 2624 // as signified by P=0
mjr 77:0b96f6867312 2625 if (p == 0)
mjr 77:0b96f6867312 2626 toggle ^= 1;
mjr 77:0b96f6867312 2627 }
mjr 77:0b96f6867312 2628 else
mjr 77:0b96f6867312 2629 {
mjr 77:0b96f6867312 2630 // invalid bit length or device code - reject the code
mjr 77:0b96f6867312 2631 return;
mjr 77:0b96f6867312 2632 }
mjr 77:0b96f6867312 2633
mjr 77:0b96f6867312 2634 // count the '1' bits in the other fields to get the checksum value
mjr 77:0b96f6867312 2635 int ones = 0;
mjr 77:0b96f6867312 2636 for ( ; checkedBits != 0 ; checkedBits >>= 1)
mjr 77:0b96f6867312 2637 ones += (checkedBits & 1);
mjr 77:0b96f6867312 2638
mjr 77:0b96f6867312 2639 // check the checksum
mjr 77:0b96f6867312 2640 if (c != ones + 3)
mjr 77:0b96f6867312 2641 return;
mjr 77:0b96f6867312 2642
mjr 77:0b96f6867312 2643 // rearrange the code into our canonical format and report it
mjr 77:0b96f6867312 2644 // along with the synthetic toggle
mjr 77:0b96f6867312 2645 reportCode(receiver, id(), (d << 8) | f, toggle, bool3::null);
mjr 77:0b96f6867312 2646
mjr 77:0b96f6867312 2647 // flip the toggle for next time, if desired
mjr 77:0b96f6867312 2648 if (postToggle)
mjr 77:0b96f6867312 2649 toggle ^= 1;
mjr 77:0b96f6867312 2650 }
mjr 77:0b96f6867312 2651
mjr 77:0b96f6867312 2652 // synthetic toggle bit
mjr 77:0b96f6867312 2653 uint8_t toggle : 1;
mjr 77:0b96f6867312 2654 };
mjr 77:0b96f6867312 2655
mjr 77:0b96f6867312 2656 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 2657 //
mjr 77:0b96f6867312 2658 // Sony protocol handler. Sony uses mark-length encoding, with
mjr 77:0b96f6867312 2659 // 8, 12, 15, and 20 bit code words. We use a common receiver
mjr 77:0b96f6867312 2660 // base class that determines how many bits are in the code from
mjr 77:0b96f6867312 2661 // the input itself, plus separate base classes for each bit size.
mjr 77:0b96f6867312 2662 // The common receiver class reports the appropriate sub-protocol
mjr 77:0b96f6867312 2663 // ID for the bit size, so that the appropriate sender class is
mjr 77:0b96f6867312 2664 // used if we want to transmit the captured code.
mjr 77:0b96f6867312 2665 //
mjr 77:0b96f6867312 2666 class IRPSony: public IRPMarkLength<uint32_t>
mjr 77:0b96f6867312 2667 {
mjr 77:0b96f6867312 2668 public:
mjr 77:0b96f6867312 2669 IRPSony() { }
mjr 77:0b96f6867312 2670
mjr 77:0b96f6867312 2671 // Name and ID. We handle all of the Sony bit size variations,
mjr 77:0b96f6867312 2672 // so we use IRPRO_NONE as our nominal ID, but set the actual code
mjr 77:0b96f6867312 2673 // on a successful receive based on the actual bit size. We
mjr 77:0b96f6867312 2674 // transmit any of the Sony codes.
mjr 77:0b96f6867312 2675 virtual const char *name() const { return "Sony"; }
mjr 77:0b96f6867312 2676 virtual int id() const { return IRPRO_NONE; }
mjr 77:0b96f6867312 2677 virtual bool isSenderFor(int protocolId) const
mjr 77:0b96f6867312 2678 {
mjr 77:0b96f6867312 2679 return protocolId == IRPRO_SONY8
mjr 77:0b96f6867312 2680 || protocolId == IRPRO_SONY12
mjr 77:0b96f6867312 2681 || protocolId == IRPRO_SONY15
mjr 77:0b96f6867312 2682 || protocolId == IRPRO_SONY20;
mjr 77:0b96f6867312 2683 }
mjr 77:0b96f6867312 2684
mjr 77:0b96f6867312 2685 // code boundary parameters
mjr 77:0b96f6867312 2686 virtual uint32_t tHeaderMark() const { return 2400; }
mjr 77:0b96f6867312 2687 virtual uint32_t tHeaderSpace() const { return 600; }
mjr 77:0b96f6867312 2688 virtual uint32_t minRxGap() const { return 5000; }
mjr 77:0b96f6867312 2689 virtual uint32_t txGap(IRTXState *state) const { return 45000; }
mjr 77:0b96f6867312 2690
mjr 77:0b96f6867312 2691 // mark-length coding parameters
mjr 77:0b96f6867312 2692 virtual int nbits() const { return 20; } // maximum - can also be 8, 12, or 15
mjr 77:0b96f6867312 2693 virtual int tSpace() const { return 600; }
mjr 77:0b96f6867312 2694 virtual int tZero() const { return 600; }
mjr 77:0b96f6867312 2695 virtual int tOne() const { return 1200; }
mjr 77:0b96f6867312 2696
mjr 77:0b96f6867312 2697 // Sony requires at least 3 sends per key press
mjr 77:0b96f6867312 2698 virtual int txMinReps(IRTXState *state) const { return 3; }
mjr 77:0b96f6867312 2699
mjr 77:0b96f6867312 2700 // set up the bitstream for a code value
mjr 77:0b96f6867312 2701 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 2702 {
mjr 77:0b96f6867312 2703 // store the code, and set the bit counter according to
mjr 77:0b96f6867312 2704 // the Sony protocol subtype
mjr 77:0b96f6867312 2705 state->bitstream = state->cmdCode;
mjr 77:0b96f6867312 2706 switch (state->protocolId)
mjr 77:0b96f6867312 2707 {
mjr 77:0b96f6867312 2708 case IRPRO_SONY8:
mjr 77:0b96f6867312 2709 state->nbits = 8;
mjr 77:0b96f6867312 2710 break;
mjr 77:0b96f6867312 2711
mjr 77:0b96f6867312 2712 case IRPRO_SONY12:
mjr 77:0b96f6867312 2713 state->nbits = 12;
mjr 77:0b96f6867312 2714 break;
mjr 77:0b96f6867312 2715
mjr 77:0b96f6867312 2716 case IRPRO_SONY15:
mjr 77:0b96f6867312 2717 state->nbits = 15;
mjr 77:0b96f6867312 2718 break;
mjr 77:0b96f6867312 2719
mjr 77:0b96f6867312 2720 case IRPRO_SONY20:
mjr 77:0b96f6867312 2721 default:
mjr 77:0b96f6867312 2722 state->nbits = 20;
mjr 77:0b96f6867312 2723 break;
mjr 77:0b96f6867312 2724 }
mjr 77:0b96f6867312 2725 }
mjr 77:0b96f6867312 2726
mjr 77:0b96f6867312 2727 // report a code value
mjr 77:0b96f6867312 2728 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 2729 {
mjr 77:0b96f6867312 2730 // we have a valid code if we have 8, 12, 15, or 20 bits
mjr 77:0b96f6867312 2731 switch (bit)
mjr 77:0b96f6867312 2732 {
mjr 77:0b96f6867312 2733 case 8:
mjr 77:0b96f6867312 2734 reportCode(receiver, IRPRO_SONY8, code, bool3::null, bool3::null);
mjr 77:0b96f6867312 2735 break;
mjr 77:0b96f6867312 2736
mjr 77:0b96f6867312 2737 case 12:
mjr 77:0b96f6867312 2738 reportCode(receiver, IRPRO_SONY12, code, bool3::null, bool3::null);
mjr 77:0b96f6867312 2739 break;
mjr 77:0b96f6867312 2740
mjr 77:0b96f6867312 2741 case 15:
mjr 77:0b96f6867312 2742 reportCode(receiver, IRPRO_SONY15, code, bool3::null, bool3::null);
mjr 77:0b96f6867312 2743 break;
mjr 77:0b96f6867312 2744
mjr 77:0b96f6867312 2745 case 20:
mjr 77:0b96f6867312 2746 reportCode(receiver, IRPRO_SONY20, code, bool3::null, bool3::null);
mjr 77:0b96f6867312 2747 break;
mjr 77:0b96f6867312 2748 }
mjr 77:0b96f6867312 2749 }
mjr 77:0b96f6867312 2750 };
mjr 77:0b96f6867312 2751
mjr 77:0b96f6867312 2752 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 2753 //
mjr 77:0b96f6867312 2754 // Denon protocol handler. Denon uses a 15-bit space-length coding, but
mjr 77:0b96f6867312 2755 // has two unusual features. First, it has no header; it just starts
mjr 77:0b96f6867312 2756 // right off with the data bits. Second, every code is transmitted a
mjr 77:0b96f6867312 2757 // second time, starting 65ms after the start of the first iteration,
mjr 77:0b96f6867312 2758 // with the "F" and "S" fields inverted:
mjr 77:0b96f6867312 2759 //
mjr 77:0b96f6867312 2760 // DDDDD FFFFFFFF SS
mjr 77:0b96f6867312 2761 //
mjr 77:0b96f6867312 2762 // D = device code
mjr 77:0b96f6867312 2763 // F = function code (key)
mjr 77:0b96f6867312 2764 // S = sequence; 0 for the first half of the pair, 1 for the second half
mjr 77:0b96f6867312 2765 //
mjr 77:0b96f6867312 2766 // The first half-code is transmitted with the actual function code and
mjr 77:0b96f6867312 2767 // SS=00; the second half uses ~F (1's complement of the function code)
mjr 77:0b96f6867312 2768 // and SS=11.
mjr 77:0b96f6867312 2769 //
mjr 77:0b96f6867312 2770 // Many learning remotes get this wrong, only learning one or the other
mjr 77:0b96f6867312 2771 // half. That's understandable, since learning remotes often only collect
mjr 77:0b96f6867312 2772 // the raw bit codes and thus wouldn't be able to detect the multi-code
mjr 77:0b96f6867312 2773 // structure. It's a little less forgiveable that some pre-programmed
mjr 77:0b96f6867312 2774 // universal remotes, such as the Logitech Harmony series, also miss the
mjr 77:0b96f6867312 2775 // half-code nuance. To be robust against errant remotes, we'll accept
mjr 77:0b96f6867312 2776 // and report lone half-codes, and we'll invert the F bits if the S bits
mjr 77:0b96f6867312 2777 // indicate that a second half was learned, to ensure that the client sees
mjr 77:0b96f6867312 2778 // the correct version of the codes. We'll also internally track the
mjr 77:0b96f6867312 2779 // pairs so that, when we *do* see a properly formed inverted pair, we'll
mjr 77:0b96f6867312 2780 // only report one code out of the pair to the client.
mjr 77:0b96f6867312 2781 //
mjr 77:0b96f6867312 2782 class IRPDenon: public IRPSpaceLength<uint16_t>
mjr 77:0b96f6867312 2783 {
mjr 77:0b96f6867312 2784 public:
mjr 77:0b96f6867312 2785 IRPDenon()
mjr 77:0b96f6867312 2786 {
mjr 77:0b96f6867312 2787 prvCode = 0;
mjr 77:0b96f6867312 2788 }
mjr 77:0b96f6867312 2789
mjr 77:0b96f6867312 2790 // name and ID
mjr 77:0b96f6867312 2791 virtual const char *name() const { return "Denon"; }
mjr 77:0b96f6867312 2792 virtual int id() const { return IRPRO_DENON; }
mjr 77:0b96f6867312 2793
mjr 77:0b96f6867312 2794 // Code parameters. The Denon protocol has no header; it just
mjr 77:0b96f6867312 2795 // jumps right in with the first bit.
mjr 77:0b96f6867312 2796 virtual uint32_t tHeaderMark() const { return 0; }
mjr 77:0b96f6867312 2797 virtual uint32_t tHeaderSpace() const { return 0; }
mjr 77:0b96f6867312 2798 virtual uint32_t minRxGap() const { return 30000; }
mjr 77:0b96f6867312 2799
mjr 77:0b96f6867312 2800 // use a short gap between paired complemented codes, and a longer
mjr 77:0b96f6867312 2801 // gap between repeats
mjr 77:0b96f6867312 2802 virtual uint32_t txGap(IRTXState *state) const { return 165000; }
mjr 77:0b96f6867312 2803 virtual uint32_t txPostGap(IRTXState *state) const
mjr 77:0b96f6867312 2804 {
mjr 77:0b96f6867312 2805 if (state->rep == 0)
mjr 77:0b96f6867312 2806 {
mjr 77:0b96f6867312 2807 // Even rep - this is the gap between the two halves of a
mjr 77:0b96f6867312 2808 // complemented pair. The next code starts 65ms from the
mjr 77:0b96f6867312 2809 // *start* of the last code, so the gap is 65ms minus the
mjr 77:0b96f6867312 2810 // transmission time for the last code.
mjr 77:0b96f6867312 2811 return 65000 - state->txTime.read_us();
mjr 77:0b96f6867312 2812 }
mjr 77:0b96f6867312 2813 else
mjr 77:0b96f6867312 2814 {
mjr 77:0b96f6867312 2815 // odd rep - use the normal long gap
mjr 77:0b96f6867312 2816 return 165000;
mjr 77:0b96f6867312 2817 }
mjr 77:0b96f6867312 2818 }
mjr 77:0b96f6867312 2819
mjr 77:0b96f6867312 2820 // on idle, clear the previous code
mjr 77:0b96f6867312 2821 virtual void rxIdle(IRRecvProIfc *receiver)
mjr 77:0b96f6867312 2822 {
mjr 77:0b96f6867312 2823 prvCode = 0;
mjr 77:0b96f6867312 2824 }
mjr 77:0b96f6867312 2825
mjr 77:0b96f6867312 2826 // space length coding
mjr 77:0b96f6867312 2827 virtual int nbits() const { return 15; }
mjr 77:0b96f6867312 2828 virtual int tMark() const { return 264; }
mjr 77:0b96f6867312 2829 virtual int tZero() const { return 792; }
mjr 77:0b96f6867312 2830 virtual int tOne() const { return 1848; }
mjr 77:0b96f6867312 2831
mjr 77:0b96f6867312 2832 // handle a space
mjr 77:0b96f6867312 2833 virtual bool rxSpace(IRRecvProIfc *receiver, uint32_t t)
mjr 77:0b96f6867312 2834 {
mjr 77:0b96f6867312 2835 // If this space is longer than the standard space between
mjr 77:0b96f6867312 2836 // adjacent codes, clear out any previous first-half code
mjr 77:0b96f6867312 2837 // we've stored. Complementary pairs have to be transmitted
mjr 77:0b96f6867312 2838 // with the standard inter-code gap between them. Anything
mjr 77:0b96f6867312 2839 // longer must be separate key presses.
mjr 77:0b96f6867312 2840 if (t > 65000)
mjr 77:0b96f6867312 2841 prvCode = 0;
mjr 77:0b96f6867312 2842
mjr 77:0b96f6867312 2843 // do the normal work
mjr 77:0b96f6867312 2844 return IRPSpaceLength<uint16_t>::rxSpace(receiver, t);
mjr 77:0b96f6867312 2845 }
mjr 77:0b96f6867312 2846
mjr 77:0b96f6867312 2847 // always send twice, once for the base version, once for the
mjr 77:0b96f6867312 2848 // inverted follow-up version
mjr 77:0b96f6867312 2849 virtual int txMinReps(IRTXState *state) const { return 2; }
mjr 77:0b96f6867312 2850
mjr 77:0b96f6867312 2851 // encode the bitstream
mjr 77:0b96f6867312 2852 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 2853 {
mjr 77:0b96f6867312 2854 // If we're on an even repetition, just send the code
mjr 77:0b96f6867312 2855 // exactly as given. If we're on an odd repetition,
mjr 77:0b96f6867312 2856 // send the inverted F and S fields.
mjr 77:0b96f6867312 2857 state->nbits = 15;
mjr 77:0b96f6867312 2858 if (state->rep == 0 || state->rep == 2)
mjr 77:0b96f6867312 2859 {
mjr 77:0b96f6867312 2860 // even rep - send the base version
mjr 77:0b96f6867312 2861 state->bitstream = state->cmdCode & (D_mask | F_mask);
mjr 77:0b96f6867312 2862
mjr 77:0b96f6867312 2863 // If we're on rep 2, switch back to rep 0. This will
mjr 77:0b96f6867312 2864 // combine with our minimum rep count of 2 to ensure that
mjr 77:0b96f6867312 2865 // we always transmit an even number of copies. Note that
mjr 77:0b96f6867312 2866 // this loop terminates: when the key is up after we finish
mjr 77:0b96f6867312 2867 // rep 1, the rep counter will advance to 2 and we'll stop.
mjr 77:0b96f6867312 2868 // We'll only reset the rep counter here if we actually
mjr 77:0b96f6867312 2869 // enter rep 2, which means the key is still down at that
mjr 77:0b96f6867312 2870 // point, which means that we'll have to go at least another
mjr 77:0b96f6867312 2871 // two iterations.
mjr 77:0b96f6867312 2872 state->rep = 0;
mjr 77:0b96f6867312 2873 }
mjr 77:0b96f6867312 2874 else
mjr 77:0b96f6867312 2875 {
mjr 77:0b96f6867312 2876 // odd rep - invert the F field, and use all 1's in the S field
mjr 77:0b96f6867312 2877 uint32_t d = state->cmdCode & D_mask;
mjr 77:0b96f6867312 2878 uint32_t f = (~state->cmdCode) & F_mask;
mjr 77:0b96f6867312 2879 state->bitstream = d | f | S_mask;
mjr 77:0b96f6867312 2880 }
mjr 77:0b96f6867312 2881 }
mjr 77:0b96f6867312 2882
mjr 77:0b96f6867312 2883 // report the code
mjr 77:0b96f6867312 2884 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 2885 {
mjr 77:0b96f6867312 2886 // If this code matches the inverted prior code, we have
mjr 77:0b96f6867312 2887 // a matching pair. Otherwise, hang onto this code until
mjr 77:0b96f6867312 2888 // next time.
mjr 77:0b96f6867312 2889 if (bit == 15)
mjr 77:0b96f6867312 2890 {
mjr 77:0b96f6867312 2891 // get the current and previous code's subfields
mjr 77:0b96f6867312 2892 uint16_t curD = code & D_mask;
mjr 77:0b96f6867312 2893 uint16_t curF = code & F_mask;
mjr 77:0b96f6867312 2894 uint16_t curS = code & S_mask;
mjr 77:0b96f6867312 2895 uint16_t prvD = prvCode & D_mask;
mjr 77:0b96f6867312 2896 uint16_t prvF = prvCode & F_mask;
mjr 77:0b96f6867312 2897 uint16_t prvS = prvCode & S_mask;
mjr 77:0b96f6867312 2898
mjr 77:0b96f6867312 2899 // check to see if the current FS fields match the inverted
mjr 77:0b96f6867312 2900 // prior FS fields, to make up a complementary pair as required
mjr 77:0b96f6867312 2901 // by the protocol
mjr 77:0b96f6867312 2902 if (curD == prvD && curF == (~prvF & F_mask) && curS == (~prvS & S_mask))
mjr 77:0b96f6867312 2903 {
mjr 77:0b96f6867312 2904 // The current code is the second half of the first code.
mjr 77:0b96f6867312 2905 // Don't report the current code, since it's just an
mjr 77:0b96f6867312 2906 // error-checking copy of the previous code, which we already
mjr 77:0b96f6867312 2907 // reported. We've now seen both halves, so clear the
mjr 77:0b96f6867312 2908 // previous code.
mjr 77:0b96f6867312 2909 prvCode = 0;
mjr 77:0b96f6867312 2910 }
mjr 77:0b96f6867312 2911 else
mjr 77:0b96f6867312 2912 {
mjr 77:0b96f6867312 2913 // This isn't the second half of an complementary pair, so
mjr 77:0b96f6867312 2914 // report it as a separate code. If the 'S' bits are 1's
mjr 77:0b96f6867312 2915 // in this code, it's the second half of the pair, so invert
mjr 77:0b96f6867312 2916 // the F and S fields to get the original code. It might
mjr 77:0b96f6867312 2917 // have been sent by a learning remote or universal remote
mjr 77:0b96f6867312 2918 // that only captured the second half field.
mjr 77:0b96f6867312 2919 if (curS == S_mask)
mjr 77:0b96f6867312 2920 {
mjr 77:0b96f6867312 2921 // The S bits are 1's, so it's a second-half code. We
mjr 77:0b96f6867312 2922 // don't have a matching first-half code, so either the
mjr 77:0b96f6867312 2923 // first half was lost, or it was never transmitted. In
mjr 77:0b96f6867312 2924 // either case, reconstruct the original first-half code
mjr 77:0b96f6867312 2925 // by inverting the F bits and clearing the S bits.
mjr 77:0b96f6867312 2926 reportCode(receiver, id(), curD | (~curF & F_mask),
mjr 77:0b96f6867312 2927 bool3::null, bool3::null);
mjr 77:0b96f6867312 2928
mjr 77:0b96f6867312 2929 // Forget any previous code. This is a second-half
mjr 77:0b96f6867312 2930 // code, so if we do get a first-half code after this,
mjr 77:0b96f6867312 2931 // it's a brand new key press or repetition.
mjr 77:0b96f6867312 2932 prvCode = 0;
mjr 77:0b96f6867312 2933 }
mjr 77:0b96f6867312 2934 else if (curS == 0)
mjr 77:0b96f6867312 2935 {
mjr 77:0b96f6867312 2936 // The S bits are 0, so it's a first-half code. Report
mjr 77:0b96f6867312 2937 // the code, and save it for next time, so that we can
mjr 77:0b96f6867312 2938 // check the next code to see if it's the second half.
mjr 77:0b96f6867312 2939 reportCode(receiver, id(), code, bool3::null, bool3::null);
mjr 77:0b96f6867312 2940 prvCode = code;
mjr 77:0b96f6867312 2941 }
mjr 77:0b96f6867312 2942 else
mjr 77:0b96f6867312 2943 {
mjr 77:0b96f6867312 2944 // The S bits are invalid, so this isn't a valid code.
mjr 77:0b96f6867312 2945 // Clear out any previous code and reject this one.
mjr 77:0b96f6867312 2946 prvCode = 0;
mjr 77:0b96f6867312 2947 }
mjr 77:0b96f6867312 2948 }
mjr 77:0b96f6867312 2949 }
mjr 77:0b96f6867312 2950 else
mjr 77:0b96f6867312 2951 {
mjr 77:0b96f6867312 2952 // we seem to have an invalid code; clear out any
mjr 77:0b96f6867312 2953 // previous code so we can start from scratch
mjr 77:0b96f6867312 2954 prvCode = 0;
mjr 77:0b96f6867312 2955 }
mjr 77:0b96f6867312 2956 }
mjr 77:0b96f6867312 2957
mjr 77:0b96f6867312 2958 // stored first code - we hang onto this until we see the
mjr 77:0b96f6867312 2959 // inverted second copy, so that we can verify a matching pair
mjr 77:0b96f6867312 2960 uint16_t prvCode;
mjr 77:0b96f6867312 2961
mjr 77:0b96f6867312 2962 // masks for the subfields
mjr 77:0b96f6867312 2963 static const int F_shift = 5;
mjr 77:0b96f6867312 2964 static const int S_shift = 13;
mjr 77:0b96f6867312 2965 static const uint16_t D_mask = 0x1F;
mjr 77:0b96f6867312 2966 static const uint16_t F_mask = 0x00FF << F_shift;
mjr 77:0b96f6867312 2967 static const uint16_t S_mask = 0x0003 << S_shift;
mjr 77:0b96f6867312 2968 };
mjr 77:0b96f6867312 2969
mjr 77:0b96f6867312 2970 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 2971 //
mjr 77:0b96f6867312 2972 // Samsung 20-bit protocol handler. This is a simple space-length
mjr 77:0b96f6867312 2973 // encoding.
mjr 77:0b96f6867312 2974 //
mjr 77:0b96f6867312 2975 class IRPSamsung20: public IRPSpaceLength<uint32_t>
mjr 77:0b96f6867312 2976 {
mjr 77:0b96f6867312 2977 public:
mjr 77:0b96f6867312 2978 IRPSamsung20() { }
mjr 77:0b96f6867312 2979
mjr 77:0b96f6867312 2980 // name and ID
mjr 77:0b96f6867312 2981 virtual const char *name() const { return "Samsung20"; }
mjr 77:0b96f6867312 2982 virtual int id() const { return IRPRO_SAMSUNG20; }
mjr 77:0b96f6867312 2983
mjr 77:0b96f6867312 2984 // code parameters
mjr 77:0b96f6867312 2985 virtual float pwmPeriod(IRTXState *state) const { return 26.0416667e-6; } // 38.4 kHz
mjr 77:0b96f6867312 2986 virtual uint32_t tHeaderMark() const { return 8*564; }
mjr 77:0b96f6867312 2987 virtual uint32_t tHeaderSpace() const { return 8*564; }
mjr 77:0b96f6867312 2988 virtual uint32_t minRxGap() const { return 2*564; }
mjr 77:0b96f6867312 2989 virtual uint32_t txGap(IRTXState *state) const { return 118000; }
mjr 77:0b96f6867312 2990
mjr 77:0b96f6867312 2991 // space length coding
mjr 77:0b96f6867312 2992 virtual int nbits() const { return 20; }
mjr 77:0b96f6867312 2993 virtual int tMark() const { return 1*564; }
mjr 77:0b96f6867312 2994 virtual int tZero() const { return 1*564; }
mjr 77:0b96f6867312 2995 virtual int tOne() const { return 3*564; }
mjr 77:0b96f6867312 2996 };
mjr 77:0b96f6867312 2997
mjr 77:0b96f6867312 2998 // Samsung 36-bit protocol. This is similar to the NEC protocol,
mjr 77:0b96f6867312 2999 // with different header timing.
mjr 77:0b96f6867312 3000 class IRPSamsung36: public IRPSpaceLength<uint32_t>
mjr 77:0b96f6867312 3001 {
mjr 77:0b96f6867312 3002 public:
mjr 77:0b96f6867312 3003 IRPSamsung36() { }
mjr 77:0b96f6867312 3004
mjr 77:0b96f6867312 3005 // name and ID
mjr 77:0b96f6867312 3006 virtual const char *name() const { return "Samsung36"; }
mjr 77:0b96f6867312 3007 virtual int id() const { return IRPRO_SAMSUNG36; }
mjr 77:0b96f6867312 3008
mjr 77:0b96f6867312 3009 // code parameters
mjr 77:0b96f6867312 3010 virtual uint32_t tHeaderMark() const { return 9*500; }
mjr 77:0b96f6867312 3011 virtual uint32_t tHeaderSpace() const { return 9*500; }
mjr 77:0b96f6867312 3012 virtual uint32_t minRxGap() const { return 40000; }
mjr 77:0b96f6867312 3013 virtual uint32_t txGap(IRTXState *state) const { return 118000; }
mjr 77:0b96f6867312 3014
mjr 77:0b96f6867312 3015 // space length coding
mjr 77:0b96f6867312 3016 virtual int nbits() const { return 36; }
mjr 77:0b96f6867312 3017 virtual int tMark() const { return 1*500; }
mjr 77:0b96f6867312 3018 virtual int tZero() const { return 1*500; }
mjr 77:0b96f6867312 3019 virtual int tOne() const { return 3*500; }
mjr 77:0b96f6867312 3020 };
mjr 77:0b96f6867312 3021
mjr 77:0b96f6867312 3022 // -----------------------------------------------------------------------
mjr 77:0b96f6867312 3023 //
mjr 77:0b96f6867312 3024 // Lutron lights, fans, and home automation. Lutron uses a simple async
mjr 77:0b96f6867312 3025 // bit coding: a 2280us space represents '0', a 2280us mark represents '1'.
mjr 77:0b96f6867312 3026 // Each code consists of 36 bits. The start of a code word is indicated
mjr 77:0b96f6867312 3027 // by a space of at least 4*2280us followed by a mark of at least 8*2280us.
mjr 77:0b96f6867312 3028 // The mark amounts to 8 consecutive '1' bits, which Lutron includes as
mjr 77:0b96f6867312 3029 // digits in the published codes, so we'll include them in our representation
mjr 77:0b96f6867312 3030 // as well (as opposed to treating them as a separate header).
mjr 77:0b96f6867312 3031
mjr 77:0b96f6867312 3032 // These raw 36-bit strings use an internal error correction coding. This
mjr 77:0b96f6867312 3033 // isn't mentioned in Lutron's technical documentation (they just list the
mjr 77:0b96f6867312 3034 // raw async bit strings), but there's a description on the Web (see, e.g.,
mjr 77:0b96f6867312 3035 // hifi-remote.com; the original source is unclear). According to that,
mjr 77:0b96f6867312 3036 // you start with a pair of 8-bit values (device code + function). Append
mjr 77:0b96f6867312 3037 // two parity bits for even parity in each byte. This gives us 18 bits.
mjr 77:0b96f6867312 3038 // Divide the 18 bits into 6 groups of 3 bits. For each 3-bit group, take
mjr 77:0b96f6867312 3039 // the binary value (0..7), recode the bits as a reflected Gray code for
mjr 77:0b96f6867312 3040 // the same number (e.g., '111' binary = 7 = '100' Gray coded), then add
mjr 77:0b96f6867312 3041 // an even parity bit. We now have 24 bits. Concatenate these together
mjr 77:0b96f6867312 3042 // and sandwich them between the 8 x '1' start bits and the 4 x '0' stop
mjr 77:0b96f6867312 3043 // bits, and we have the 36-bit async bit stream.
mjr 77:0b96f6867312 3044 //
mjr 77:0b96f6867312 3045 // The error correction code lets us apply at least one validation check:
mjr 77:0b96f6867312 3046 // we can verify that each 4-bit group in the raw data has odd parity. If
mjr 77:0b96f6867312 3047 // it doesn't, we'll reject the code. Ideally, we could also reverse the
mjr 77:0b96f6867312 3048 // whole coding scheme above and check the two even parity bits for the
mjr 77:0b96f6867312 3049 // recovered bytes. Unfortunately, I haven't figured out how to do this;
mjr 77:0b96f6867312 3050 // taking the Web description of the coding at face value doesn't yield
mjr 77:0b96f6867312 3051 // correct parity bits for all of the codes published by Lutron. Either
mjr 77:0b96f6867312 3052 // the description is wrong, or I'm just misinterpreting it (which is
mjr 77:0b96f6867312 3053 // likely given that it's pretty sketchy).
mjr 77:0b96f6867312 3054 //
mjr 77:0b96f6867312 3055 class IRPLutron: public IRPAsync<uint32_t>
mjr 77:0b96f6867312 3056 {
mjr 77:0b96f6867312 3057 public:
mjr 77:0b96f6867312 3058 IRPLutron() { }
mjr 77:0b96f6867312 3059
mjr 77:0b96f6867312 3060 // name and ID
mjr 77:0b96f6867312 3061 virtual const char *name() const { return "Lutron"; }
mjr 77:0b96f6867312 3062 virtual int id() const { return IRPRO_LUTRON; }
mjr 77:0b96f6867312 3063
mjr 77:0b96f6867312 3064 // carrier is 40kHz
mjr 77:0b96f6867312 3065 virtual float pwmPeriod(IRTXState *state) const { return 25.0e-6f; } // 40kHz
mjr 77:0b96f6867312 3066
mjr 77:0b96f6867312 3067 // All codes end with 4 '0' bits, which is as much of a gap as these
mjr 77:0b96f6867312 3068 // remotes provide.
mjr 77:0b96f6867312 3069 virtual uint32_t minRxGap() const { return 2280*4 - 700; }
mjr 77:0b96f6867312 3070 virtual uint32_t txGap(IRTXState *state) const { return 2280*16; }
mjr 77:0b96f6867312 3071
mjr 77:0b96f6867312 3072 // Lutron doesn't have a formal header, but there's a long mark at
mjr 77:0b96f6867312 3073 // the beginning of every code.
mjr 77:0b96f6867312 3074 virtual uint32_t tHeaderMark() const { return 2280*8; }
mjr 77:0b96f6867312 3075 virtual uint32_t tHeaderSpace() const { return 0; }
mjr 77:0b96f6867312 3076
mjr 77:0b96f6867312 3077 // Bit code parameters. The Lutron codes are 36-bits, each bit
mjr 77:0b96f6867312 3078 // is 2280us long, and bits are stored MSB first.
mjr 77:0b96f6867312 3079 virtual bool lsbFirst() const { return false; }
mjr 77:0b96f6867312 3080 virtual int nbits() const { return 24; }
mjr 77:0b96f6867312 3081 virtual int tBit() const { return 2280; }
mjr 77:0b96f6867312 3082
mjr 77:0b96f6867312 3083 // Validate a received code value, using the decoding process
mjr 77:0b96f6867312 3084 // described above. The code value given here is the low-order
mjr 77:0b96f6867312 3085 // 32 bits of the 36-bit code, with the initial FF stripped.
mjr 77:0b96f6867312 3086 bool rxValidate(uint32_t c)
mjr 77:0b96f6867312 3087 {
mjr 77:0b96f6867312 3088 // the low 4 bits must be zeroes
mjr 77:0b96f6867312 3089 if ((c & 0x0000000F) != 0)
mjr 77:0b96f6867312 3090 return false;
mjr 77:0b96f6867312 3091
mjr 77:0b96f6867312 3092 // drop the 4 '0' bits at the end
mjr 77:0b96f6867312 3093 c >>= 4;
mjr 77:0b96f6867312 3094
mjr 77:0b96f6867312 3095 // parity table for 4-bit values 0x00..0x0F - 0=even, 1=odd
mjr 77:0b96f6867312 3096 static const int parity[] = {
mjr 77:0b96f6867312 3097 0, 1, 1, 0, // 0, 1, 2, 3
mjr 77:0b96f6867312 3098 1, 0, 0, 1, // 4, 5, 6, 7
mjr 77:0b96f6867312 3099 1, 0, 0, 1, // 8, 9, A, B
mjr 77:0b96f6867312 3100 0, 1, 1, 0 // C, D, E, F
mjr 77:0b96f6867312 3101 };
mjr 77:0b96f6867312 3102
mjr 77:0b96f6867312 3103 // add up the number of groups with odd parity
mjr 77:0b96f6867312 3104 int odds = 0;
mjr 77:0b96f6867312 3105 for (int i = 0 ; i < 6 ; ++i, c >>= 4)
mjr 77:0b96f6867312 3106 odds += parity[c & 0x0F];
mjr 77:0b96f6867312 3107
mjr 77:0b96f6867312 3108 // we need all 6 groups to have odd parity
mjr 77:0b96f6867312 3109 return odds == 6;
mjr 77:0b96f6867312 3110 }
mjr 77:0b96f6867312 3111
mjr 77:0b96f6867312 3112 // Return the code. Lutron's code tables have all codes starting
mjr 77:0b96f6867312 3113 // with FF (8 consecutive '1' bits), but we treat this as a header,
mjr 77:0b96f6867312 3114 // since it can never occur within a code, so it doesn't show up
mjr 77:0b96f6867312 3115 // in the bit string. We also count the tailing four '0' bits,
mjr 77:0b96f6867312 3116 // which Lutron also encodes in each string, as a gap. So we only
mjr 77:0b96f6867312 3117 // actually capture 24 data bits. To make our codes match the
mjr 77:0b96f6867312 3118 // Lutron tables, add the structural bits back in for reporting.
mjr 77:0b96f6867312 3119 virtual void rxClose(IRRecvProIfc *receiver, bool ditto)
mjr 77:0b96f6867312 3120 {
mjr 77:0b96f6867312 3121 if (bit == 24)
mjr 77:0b96f6867312 3122 {
mjr 77:0b96f6867312 3123 uint64_t report = 0xFF0000000LL | (code << 4);
mjr 77:0b96f6867312 3124 if (rxValidate(report))
mjr 77:0b96f6867312 3125 reportCode(receiver, id(), report, bool3::null, bool3::null);
mjr 77:0b96f6867312 3126 }
mjr 77:0b96f6867312 3127 }
mjr 77:0b96f6867312 3128
mjr 77:0b96f6867312 3129 // For transmission, convert back to the 24 data bits, stripping out
mjr 77:0b96f6867312 3130 // the structural leading 8 '1' bits and trailing 4 '0' bits.
mjr 77:0b96f6867312 3131 virtual void codeToBitstream(IRTXState *state)
mjr 77:0b96f6867312 3132 {
mjr 77:0b96f6867312 3133 state->bitstream = (state->cmdCode >> 4) & 0x00FFFFFF;
mjr 77:0b96f6867312 3134 state->nbits = 24;
mjr 77:0b96f6867312 3135 }
mjr 77:0b96f6867312 3136 };
mjr 77:0b96f6867312 3137
mjr 77:0b96f6867312 3138 // -------------------------------------------------------------------------
mjr 77:0b96f6867312 3139 //
mjr 77:0b96f6867312 3140 // Protocol singletons. We combine these into a container structure
mjr 77:0b96f6867312 3141 // so that we can allocate the whole set of protocol handlers in one
mjr 77:0b96f6867312 3142 // 'new'.
mjr 77:0b96f6867312 3143 //
mjr 77:0b96f6867312 3144 struct IRProtocols
mjr 77:0b96f6867312 3145 {
mjr 77:0b96f6867312 3146 #define IR_PROTOCOL_RXTX(cls) cls s_##cls;
mjr 77:0b96f6867312 3147 #include "IRProtocolList.h"
mjr 77:0b96f6867312 3148 };
mjr 77:0b96f6867312 3149
mjr 77:0b96f6867312 3150 #endif