Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

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

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mjr 77:0b96f6867312 1 // IR Remote Transmitter
mjr 77:0b96f6867312 2 //
mjr 77:0b96f6867312 3 // This class lets you control an IR emitter LED connected to a GPIO port
mjr 77:0b96f6867312 4 // to transmit remote control codes using numerous standard and proprietary
mjr 77:0b96f6867312 5 // protocols. You can use this to send remote codes to any device with
mjr 77:0b96f6867312 6 // a typical IR remote, such as A/V equipment, home automation devices, etc.
mjr 77:0b96f6867312 7 // You can also use this with the companion IR Receiver class running on
mjr 77:0b96f6867312 8 // a separate KL25Z to send IR commands to the other device.
mjr 77:0b96f6867312 9 //
mjr 77:0b96f6867312 10 // We do all of our transmissions with specific protocols rather than raw
mjr 77:0b96f6867312 11 // IR signals. Every remote control has its own way of representing a
mjr 77:0b96f6867312 12 // string of data bits as a series of timed IR flashes. The exact mapping
mjr 77:0b96f6867312 13 // between data bits and IR flashes is the protocol. There are some quasi
mjr 77:0b96f6867312 14 // industry standard protocols, where several companies use the same format
mjr 77:0b96f6867312 15 // for their codes, but there are many proprietary protocols as well. We
mjr 77:0b96f6867312 16 // have handlers for the most widely used protocols: NEC, Sony, Philips RC5
mjr 77:0b96f6867312 17 // and RC6, Pioneer, Panasonic, and several others. If your device isn't
mjr 77:0b96f6867312 18 // covered yet, it could probably be added, since we've tried to design
mjr 77:0b96f6867312 19 // the system to make it easy to add new protocols.
mjr 77:0b96f6867312 20 //
mjr 77:0b96f6867312 21 // When you transmit a code, you specify it in terms of the protocol to use
mjr 77:0b96f6867312 22 // and the "code" value to send. A "code" is just the data value for a
mjr 77:0b96f6867312 23 // particular key on a particular remote control, usually expressed as a
mjr 77:0b96f6867312 24 // hex number. There are published tables of codes for many remotes, but
mjr 77:0b96f6867312 25 // unfortunately they're not very consistent in how they represent the hex
mjr 77:0b96f6867312 26 // code values, so you'll often see the same key represented with different
mjr 77:0b96f6867312 27 // hex codes in different published tables. We of course have our own way
mjr 77:0b96f6867312 28 // of mapping the hex codes; we've tried to use the format that the original
mjr 77:0b96f6867312 29 // manufacturer uses in their tales, if they publish them at all, but these
mjr 77:0b96f6867312 30 // may or may not be consistent with what you find in any tables you consult.
mjr 77:0b96f6867312 31 // So your best bet for finding the right codes to use here is usually to
mjr 77:0b96f6867312 32 // "learn" the codes using our companion class IRReceiver. That class has a
mjr 77:0b96f6867312 33 // protocol decoder for each protocol transmitter we can use here, so if you
mjr 77:0b96f6867312 34 // set that up and point a remote at it, it will tell you the exact code we
mjr 77:0b96f6867312 35 // use for the key.
mjr 77:0b96f6867312 36 //
mjr 77:0b96f6867312 37 // The transmitter class provides a "virtual remote control" interface.
mjr 77:0b96f6867312 38 // This gives you an imaginary remote control keypad, with a set of
mjr 77:0b96f6867312 39 // virtual buttons programmed for individual remote control commands.
mjr 77:0b96f6867312 40 // You specify the protocol and command code for each virtual button.
mjr 77:0b96f6867312 41 // You can use different protocols for different buttons.
mjr 77:0b96f6867312 42 //
mjr 77:0b96f6867312 43 //
mjr 77:0b96f6867312 44 // How to use the software
mjr 77:0b96f6867312 45 //
mjr 77:0b96f6867312 46 // First, create an instance of IRTransmitter, telling it which pin the
mjr 77:0b96f6867312 47 // IR emitter is connected to (see below for wiring instructions) and how
mjr 77:0b96f6867312 48 // many virtual remote control keys you want. The pin must be PWM capable.
mjr 77:0b96f6867312 49 //
mjr 77:0b96f6867312 50 // IRTransmitter *tx = new IRTransmitter(PTC9, 32);
mjr 77:0b96f6867312 51 //
mjr 77:0b96f6867312 52 // Next, program the virtual remote keys. For each key, set the IR protocol
mjr 77:0b96f6867312 53 // to use (an IRPRO_xxx code from IRProtocolID.h), the "ditto" mode (more on
mjr 77:0b96f6867312 54 // this below), and the hex code for the command.
mjr 77:0b96f6867312 55 //
mjr 77:0b96f6867312 56 // // program virtual button #0 with Sony 20-bit code 0x123, no dittos
mjr 77:0b96f6867312 57 // tx->programButton(0, IRPRO_SONY20, false, 0x123);
mjr 77:0b96f6867312 58 //
mjr 77:0b96f6867312 59 // Now you're set up to transmit. In your main loop, decide when it's time
mjr 77:0b96f6867312 60 // to transmit a button, such as by monitoring a physical pushbutton via a
mjr 77:0b96f6867312 61 // GPIO DigitalIn pin. When you want to transmit a code, just tell the
mjr 77:0b96f6867312 62 // transmitter that your virtual button is pressed, by calling pushButton()
mjr 77:0b96f6867312 63 // with the virtual button ID (corresponding to a virtual button ID you
mjr 77:0b96f6867312 64 // previously programmed wtih programButton()) and a status of 'true',
mjr 77:0b96f6867312 65 // meaning that the button is pressed.
mjr 77:0b96f6867312 66 //
mjr 77:0b96f6867312 67 // tx->pushButton(0, true); // push virtual button #0
mjr 77:0b96f6867312 68 //
mjr 77:0b96f6867312 69 // This starts the transmission and returns immediately. The transmission
mjr 77:0b96f6867312 70 // proceeds in the background (via timer interrupts), so your main loop can
mjr 77:0b96f6867312 71 // go about its other business without waiting for the transmission to
mjr 77:0b96f6867312 72 // finish. Most remote codes take 50ms to 100ms to transmit, and you don't
mjr 77:0b96f6867312 73 // usually want to stall an MCU app for that long.
mjr 77:0b96f6867312 74 //
mjr 77:0b96f6867312 75 // If a prior transmission is still in progress when you call pushButton(),
mjr 77:0b96f6867312 76 // the new transmission doesn't interrupt the previous one. Every code is
mjr 77:0b96f6867312 77 // sent as a complete unit to ensure data integrity, so the old one has to
mjr 77:0b96f6867312 78 // finish before the new one starts. Some protocols have minimum repeat
mjr 77:0b96f6867312 79 // counts, and the transmitter takes this into account as well. For example,
mjr 77:0b96f6867312 80 // the Sony protocols require each command to be sent at least three times,
mjr 77:0b96f6867312 81 // even if the button is only tapped for a brief instant. So if you send
mjr 77:0b96f6867312 82 // a Sony code, a new command won't start transmitting until the last command
mjr 77:0b96f6867312 83 // has been sent completely, not just once, but at least three times.
mjr 77:0b96f6867312 84 //
mjr 77:0b96f6867312 85 // Once the transmitter starts sending the code for a new button, it keeps
mjr 77:0b96f6867312 86 // sending the same code on auto-repeat until you either un-press the
mjr 77:0b96f6867312 87 // virtual button or press a new virtual button. Handling auto-repeat
mjr 77:0b96f6867312 88 // in the transmitter like this has an important benefit, besides just making
mjr 77:0b96f6867312 89 // the API simpler: it allows the transmitter to use the proper coding for
mjr 77:0b96f6867312 90 // the repeats according to the rules of the protocol. Some protocols use
mjr 77:0b96f6867312 91 // a different format for the first code of a key press and auto-repeats
mjr 77:0b96f6867312 92 // of the same key. Some protocols also have other repetition features,
mjr 77:0b96f6867312 93 // such as "toggle bits" or sequence counters. The protocol handlers use
mjr 77:0b96f6867312 94 // the appropriate handling for their protocols, so you only have to think
mjr 77:0b96f6867312 95 // in terms of when the virtual buttons are pressed and un-pressed, without
mjr 77:0b96f6867312 96 // worrying about whether a toggle bit or a "ditto" code or a sequence
mjr 77:0b96f6867312 97 // counter is needed.
mjr 77:0b96f6867312 98 //
mjr 77:0b96f6867312 99 // When the button is no longer pressed, call pushButton() again with a
mjr 77:0b96f6867312 100 // status of 'false':
mjr 77:0b96f6867312 101 //
mjr 77:0b96f6867312 102 // tx->pushButton(0, false);
mjr 77:0b96f6867312 103 //
mjr 77:0b96f6867312 104 // Multiple button presses use simple PC keyboard-like semantics. At any
mjr 77:0b96f6867312 105 // given time, there can be only one pressed button. When you call
mjr 77:0b96f6867312 106 // pushButton(N, true), N becomes the pressed button, which means that the
mjr 77:0b96f6867312 107 // previous pressed button (if any) is forgotten. As mentioned above, this
mjr 77:0b96f6867312 108 // doesn't cancel the previous transmission if it's still in progress. The
mjr 77:0b96f6867312 109 // transmitter continues with the last code until it's finished. When it
mjr 77:0b96f6867312 110 // finishes with a code, the transmitter looks to see if the same button is
mjr 77:0b96f6867312 111 // still pressed. If so, it starts a new transmission for the same button,
mjr 77:0b96f6867312 112 // using the appropriate repeat code. If a new button is pressed, the
mjr 77:0b96f6867312 113 // transmitter starts transmitting the new button's code. If no button is
mjr 77:0b96f6867312 114 // pressed, the transmitter stops sending and becomes idle until you press
mjr 77:0b96f6867312 115 // another button.
mjr 77:0b96f6867312 116 //
mjr 77:0b96f6867312 117 // Note that button presses aren't queued. Suppose you press button #0
mjr 77:0b96f6867312 118 // (while no other code is being sent): this starts transmitting the code
mjr 77:0b96f6867312 119 // for button #0 and returns. Now suppose that a very short time later,
mjr 77:0b96f6867312 120 // while that first send is still in progress, you briefly press and release
mjr 77:0b96f6867312 121 // button #1. Button #1 will never be sent in this case. When you press
mjr 77:0b96f6867312 122 // button #1, the transmitter is still sending the first code, so all it
mjr 77:0b96f6867312 123 // does at this point is mark button #1 as the currently pressed button,
mjr 77:0b96f6867312 124 // replacing button #0. But as explained above, this doesn't cancel the
mjr 77:0b96f6867312 125 // button #0 code transmission in progress. That continues until the
mjr 77:0b96f6867312 126 // complete code has been sent. At that point, the transmitter looks to
mjr 77:0b96f6867312 127 // see which button is pressed, and discovers that NO button is pressed:
mjr 77:0b96f6867312 128 // you already told it button #1 was released. So the transmitter simply
mjr 77:0b96f6867312 129 // stops sending and becomes idle.
mjr 77:0b96f6867312 130 //
mjr 77:0b96f6867312 131 //
mjr 77:0b96f6867312 132 // How to determine command codes and the "ditto" mode
mjr 77:0b96f6867312 133 //
mjr 77:0b96f6867312 134 // Our command codes are expressed as 64-bit integers. The code numbers
mjr 77:0b96f6867312 135 // are in essence the data bits transmitted in the IR signal, but the mapping
mjr 77:0b96f6867312 136 // between the IR data bits and the 64-bit code value is different for each
mjr 77:0b96f6867312 137 // protocol. We've tried to make our codes match the numbers shown in the
mjr 77:0b96f6867312 138 // tables published by the respective manufacturers for any given remote,
mjr 77:0b96f6867312 139 // but you might also find third-party tables that have completely different
mjr 77:0b96f6867312 140 // mappings. The easiest thing to do, really, is to ignore all of that and
mjr 77:0b96f6867312 141 // just treat the codes as arbitrary, opaque identifiers, and identify the
mjr 77:0b96f6867312 142 // codes for the remote you want to use by "learning" them. That is, set up
mjr 77:0b96f6867312 143 // a receiver with our companion class IRReceiver, point your remote at it,
mjr 77:0b96f6867312 144 // and see what IRReceiver reports as the decoded value for each button.
mjr 77:0b96f6867312 145 // Simply use the same code value for each button when sending.
mjr 77:0b96f6867312 146 //
mjr 77:0b96f6867312 147 // The "ditto" flag is ignored for most protocols, but it's important for a
mjr 77:0b96f6867312 148 // few, such as the various NEC protocols. This tells the sender whether to
mjr 77:0b96f6867312 149 // use the protocol's special repeat code for auto-repeats (true), or to send
mjr 77:0b96f6867312 150 // send the same key code repeatedly (false). The concept of dittos only
mjr 77:0b96f6867312 151 // applies to a few protocols; most protocols just do the obvious thing and
mjr 77:0b96f6867312 152 // send the same code repeatedly when you hold down a key. But the NEC
mjr 77:0b96f6867312 153 // protocols and a few others have special coding for repeated keys. It's
mjr 77:0b96f6867312 154 // important to use the special coding for devices that expect it, because
mjr 77:0b96f6867312 155 // it lets them distinguish auto-repeat from multiple key presses, which
mjr 77:0b96f6867312 156 // can affect how they respond to certain commands. The tricky part is that
mjr 77:0b96f6867312 157 // manufacturers aren't always consistent about using dittos even when it's
mjr 77:0b96f6867312 158 // a standard part of the protocol they're using, so you have to determine
mjr 77:0b96f6867312 159 // whether or not to use it on a per-device basis. The easiest way to do
mjr 77:0b96f6867312 160 // this is just like learning codes: set up a receiever with IRReceiver and
mjr 77:0b96f6867312 161 // see what it reports. But this time, you're interested in what happens
mjr 77:0b96f6867312 162 // when you hold down a key. You'll always get one ordinary report first,
mjr 77:0b96f6867312 163 // but check what happens for the repeats. If IRReceiver reports the same
mjr 77:0b96f6867312 164 // code repeatedly, set dittos = false when sending those codes. If the
mjr 77:0b96f6867312 165 // repeats have the "ditto bit" set, though, set dittos = true when sending.
mjr 77:0b96f6867312 166 //
mjr 77:0b96f6867312 167 //
mjr 77:0b96f6867312 168 // How to wire an IR emitter
mjr 77:0b96f6867312 169 //
mjr 77:0b96f6867312 170 // Any IR LED should work as the emitter. I used a Vishay TSAL6400 for my
mjr 77:0b96f6867312 171 // reference/testing implementation. The TSAL6400 is quite bright, so it
mjr 77:0b96f6867312 172 // should send signals well across fairly large distances.
mjr 77:0b96f6867312 173 //
mjr 77:0b96f6867312 174 // WARNING! DON'T connect the LED directly to the GPIO pin. KL25Z GPIO
mjr 77:0b96f6867312 175 // pins have very low current limits - a typical IR emitter LED draws
mjr 77:0b96f6867312 176 // enough current to damage or destroy the KL25Z. You'll need to build a
mjr 77:0b96f6867312 177 // simple transistor circuit to interface with the LED. You'll need a
mjr 77:0b96f6867312 178 // common small signal NPN transistor (such as a 2222 or 2N4401), a 2.2K
mjr 77:0b96f6867312 179 // resistor, the IR LED, of course, and a current-limiting resistor for
mjr 77:0b96f6867312 180 // the LED. Choose the current-limiting resistor by plugging your LED's
mjr 77:0b96f6867312 181 // specs into an LED resistor calculator, using a 5V supply voltage. Now
mjr 77:0b96f6867312 182 // connect the GPIO pin to the current-limiting resistor, connect the
mjr 77:0b96f6867312 183 // resistor to the LED anode (+), connect the LED cathode (-) to the NPN
mjr 77:0b96f6867312 184 // collector, connect the NPN emitter to ground, connect the NPN base to
mjr 77:0b96f6867312 185 // the 2.2K resistor, and connect the 2.2K resistor to the GPIO pin.
mjr 77:0b96f6867312 186 // It's simple enough for a schematic rendered in ASCII art:
mjr 77:0b96f6867312 187 //
mjr 77:0b96f6867312 188 // +5V (from the KL25Z +5V pin, or directly from
mjr 77:0b96f6867312 189 // | the KL25Z's power supply)
mjr 77:0b96f6867312 190 // <
mjr 77:0b96f6867312 191 // > R1 - use an LED resistor calculator to choose
mjr 77:0b96f6867312 192 // < the resistor size based on your selected
mjr 77:0b96f6867312 193 // | LED's forward current & voltage and 5V source
mjr 77:0b96f6867312 194 // --- +
mjr 77:0b96f6867312 195 // \ / LED - Infrared emitter (e.g., Vishay TSAL6400)
mjr 77:0b96f6867312 196 // --- -
mjr 77:0b96f6867312 197 // |
mjr 77:0b96f6867312 198 // |
mjr 77:0b96f6867312 199 // \| 2.2K
mjr 77:0b96f6867312 200 // |-----/\/\/\---> to this GPIO pin
mjr 77:0b96f6867312 201 // /|
mjr 77:0b96f6867312 202 // v
mjr 77:0b96f6867312 203 // |
mjr 77:0b96f6867312 204 // -----
mjr 77:0b96f6867312 205 // --- Ground (KL25Z GND pin, or ground on the
mjr 77:0b96f6867312 206 // - KL25Z's power supply)
mjr 77:0b96f6867312 207 //
mjr 77:0b96f6867312 208 // If you want to be able to see the transmitter in action, you can connect
mjr 77:0b96f6867312 209 // another LED (a blue one, say) and its own current-limiting resistor in
mjr 77:0b96f6867312 210 // parallel with the R1 + IR LED circuit. Let's call the blue LED's
mjr 77:0b96f6867312 211 // resistor R2. Connect R2 to +5V, connect the other end of R2 to the
mjr 77:0b96f6867312 212 // blue LED (+), and connect the blue LED (-) to the NPN collector. This
mjr 77:0b96f6867312 213 // will make the blue LED flash in sync with the IR LED. IR remote control
mjr 77:0b96f6867312 214 // codes are slow enough that you'll be able to see the blue LED come on
mjr 77:0b96f6867312 215 // and flicker during each transmission, although the "bits" are too fast
mjr 77:0b96f6867312 216 // to see individually with the naked eye. The detector shouldn't be
mjr 77:0b96f6867312 217 // bothered by the extra light since these sensors have optical filters
mjr 77:0b96f6867312 218 // that block most of the incoming light outside of the IR band the sensor
mjr 77:0b96f6867312 219 // is looking for.
mjr 77:0b96f6867312 220
mjr 77:0b96f6867312 221 #ifndef _IRTRANSMITTER_H_
mjr 77:0b96f6867312 222 #define _IRTRANSMITTER_H_
mjr 77:0b96f6867312 223
mjr 77:0b96f6867312 224 #include <mbed.h>
mjr 77:0b96f6867312 225
mjr 77:0b96f6867312 226 #include "NewPwm.h"
mjr 77:0b96f6867312 227 #include "IRRemote.h"
mjr 77:0b96f6867312 228 #include "IRCommand.h"
mjr 77:0b96f6867312 229 #include "IRProtocols.h"
mjr 77:0b96f6867312 230
mjr 77:0b96f6867312 231
mjr 77:0b96f6867312 232 // IR Remote Transmitter
mjr 77:0b96f6867312 233 class IRTransmitter
mjr 77:0b96f6867312 234 {
mjr 77:0b96f6867312 235 public:
mjr 77:0b96f6867312 236 // Construct.
mjr 77:0b96f6867312 237 //
mjr 77:0b96f6867312 238 // 'pin' is the GPIO pin controlling the IR LED. The pin must be
mjr 77:0b96f6867312 239 // PWM-capable. (Note also that each PWM channel on the KL25Z is
mjr 77:0b96f6867312 240 // shared among multiple pins, so be sure you're using a pin connected
mjr 77:0b96f6867312 241 // to a channel that isn't already used elsewhere in your application.)
mjr 77:0b96f6867312 242 // Don't connect the LED directly to this pin; see the circuit diagram
mjr 77:0b96f6867312 243 // at the top of the file for details of how to connect it through a
mjr 77:0b96f6867312 244 // transistor to safely boost the current to LED levels.
mjr 77:0b96f6867312 245 //
mjr 77:0b96f6867312 246 // 'nButtons' is the number of virtual button slots to allocate. Each
mjr 77:0b96f6867312 247 // slot represents a virtual remote control button that can be programmed
mjr 77:0b96f6867312 248 // with a remote code to transmit. Allocate as many slots as you need
mjr 77:0b96f6867312 249 // for unique commands or buttons. Note that the caller is responsible
mjr 77:0b96f6867312 250 // for deciding when a button is pressed; if you want to tie these to
mjr 77:0b96f6867312 251 // physical buttons, you'll need to create your own DigitalIn objects
mjr 77:0b96f6867312 252 // for the pins, monitor them, and call pushButton() to press and
mjr 77:0b96f6867312 253 // release virtual buttons when the physical button states change.
mjr 77:0b96f6867312 254 IRTransmitter(PinName pin, int nButtons) : ledPin(pin)
mjr 77:0b96f6867312 255 {
mjr 77:0b96f6867312 256 // make sure the protocol singletons are allocated
mjr 77:0b96f6867312 257 IRProtocol::allocProtocols();
mjr 77:0b96f6867312 258
mjr 77:0b96f6867312 259 // no command is active
mjr 77:0b96f6867312 260 curBtnId = -1;
mjr 77:0b96f6867312 261
mjr 77:0b96f6867312 262 // allocate the command list
mjr 77:0b96f6867312 263 buttons = new ButtonCmd[nButtons];
mjr 77:0b96f6867312 264
mjr 77:0b96f6867312 265 // the transmitter "thread" isn't yet running
mjr 77:0b96f6867312 266 txRunning = false;
mjr 77:0b96f6867312 267 txBtnId = -1;
mjr 77:0b96f6867312 268 txProtocol = 0;
mjr 77:0b96f6867312 269 }
mjr 77:0b96f6867312 270
mjr 77:0b96f6867312 271 ~IRTransmitter()
mjr 77:0b96f6867312 272 {
mjr 77:0b96f6867312 273 delete[] buttons;
mjr 77:0b96f6867312 274 }
mjr 77:0b96f6867312 275
mjr 77:0b96f6867312 276 // Program the command code for a virtual button
mjr 77:0b96f6867312 277 void programButton(int buttonId, int protocolId, bool dittos, uint64_t cmdCode)
mjr 77:0b96f6867312 278 {
mjr 77:0b96f6867312 279 ButtonCmd &btn = buttons[buttonId];
mjr 77:0b96f6867312 280 btn.pro = protocolId;
mjr 77:0b96f6867312 281 btn.dittos = dittos;
mjr 77:0b96f6867312 282 btn.cmd = cmdCode;
mjr 77:0b96f6867312 283 }
mjr 77:0b96f6867312 284
mjr 77:0b96f6867312 285 // Push a virtual button.
mjr 77:0b96f6867312 286 //
mjr 77:0b96f6867312 287 // When this is called, we'll start transmitting the command code
mjr 77:0b96f6867312 288 // associated with the button immediately if no other transmission
mjr 77:0b96f6867312 289 // is already in progress. On the other hand, if a transmission of
mjr 77:0b96f6867312 290 // a prior command code is already in progress, the previous command
mjr 77:0b96f6867312 291 // isn't interrupted; we always send whole commands, and never
mjr 77:0b96f6867312 292 // interrupt a command in progress. Instead, the new button is
mjr 77:0b96f6867312 293 // set as pending. As soon as the prior transmission finishes,
mjr 77:0b96f6867312 294 // the pending button becomes the current button and we start
mjr 77:0b96f6867312 295 // transmitting its code - but only if the button is still pressed
mjr 77:0b96f6867312 296 // when the previous code finishes. This means that if you both
mjr 77:0b96f6867312 297 // press and release a button during the time that another
mjr 77:0b96f6867312 298 // transmission is in progress, the new button will never be
mjr 77:0b96f6867312 299 // transmitted. We operate this way to keep things simple and
mjr 77:0b96f6867312 300 // consistent when it comes to more than just one pending button.
mjr 77:0b96f6867312 301 // This way we don't have to consider queues of pending buttons
mjr 77:0b96f6867312 302 // or create mechanisms for canceling pending commands.
mjr 77:0b96f6867312 303 //
mjr 77:0b96f6867312 304 // If the button is still down when its first transmission ends,
mjr 77:0b96f6867312 305 // and no other button has been pressed in the meantime, the button
mjr 77:0b96f6867312 306 // will auto-repeat. This continues as long as the button is still
mjr 77:0b96f6867312 307 // pressed and no other button has been pressed.
mjr 77:0b96f6867312 308 //
mjr 77:0b96f6867312 309 // Only one code can be transmitted at a time, obviously. The
mjr 77:0b96f6867312 310 // semantics for multiple simultaneous button presses are like those
mjr 77:0b96f6867312 311 // of a PC keyboard. Suppose you press button A, then a while later,
mjr 77:0b96f6867312 312 // while A is still down, you press B. Then a while later still,
mjr 77:0b96f6867312 313 // you press C, continuing to hold both A and B down. We transmit
mjr 77:0b96f6867312 314 // A repeatedly until you press B, at which point we finish sending
mjr 77:0b96f6867312 315 // the current repeat of A (we never interrupt a code in the middle:
mjr 77:0b96f6867312 316 // once started, a code is always finished whole) and start sending
mjr 77:0b96f6867312 317 // B. B continues to repeat until you press C, at which point we
mjr 77:0b96f6867312 318 // finish the last repetition of B and start sending C. Once A or
mjr 77:0b96f6867312 319 // B have been superseded, it makes no difference whether you continue
mjr 77:0b96f6867312 320 // to hold them down or release them. They'll never start repeating
mjr 77:0b96f6867312 321 // again, even if you then release C while A and B are still down.
mjr 77:0b96f6867312 322 void pushButton(int id, bool on)
mjr 77:0b96f6867312 323 {
mjr 77:0b96f6867312 324 if (on)
mjr 77:0b96f6867312 325 {
mjr 77:0b96f6867312 326 // make this the current command
mjr 77:0b96f6867312 327 curBtnId = id;
mjr 77:0b96f6867312 328
mjr 77:0b96f6867312 329 // start the transmitter
mjr 77:0b96f6867312 330 txStart();
mjr 77:0b96f6867312 331 }
mjr 77:0b96f6867312 332 else
mjr 77:0b96f6867312 333 {
mjr 77:0b96f6867312 334 // if this is the current command, cancel it
mjr 77:0b96f6867312 335 if (id == curBtnId)
mjr 77:0b96f6867312 336 curBtnId = -1;
mjr 77:0b96f6867312 337 }
mjr 77:0b96f6867312 338 }
mjr 77:0b96f6867312 339
mjr 77:0b96f6867312 340 // Is a transmission in progress?
mjr 77:0b96f6867312 341 bool isSending() const { return txRunning; }
mjr 77:0b96f6867312 342
mjr 77:0b96f6867312 343
mjr 77:0b96f6867312 344 protected:
mjr 77:0b96f6867312 345 // Start the transmitter "thread", if it's not already running. The
mjr 77:0b96f6867312 346 // thread is actually just a series of timer interrupts; each interrupt
mjr 77:0b96f6867312 347 // sets the next interrupt at an appropriate interval, so the effect is
mjr 77:0b96f6867312 348 // like a thread.
mjr 77:0b96f6867312 349 void txStart()
mjr 77:0b96f6867312 350 {
mjr 77:0b96f6867312 351 if (!txRunning)
mjr 77:0b96f6867312 352 {
mjr 77:0b96f6867312 353 // The thread isn't running. Note that this means that there's
mjr 77:0b96f6867312 354 // no possibility that txRunning will change out from under us
mjr 77:0b96f6867312 355 // asynchronously, since there's no pending interrupt handler
mjr 77:0b96f6867312 356 // to change it. Mark the thread as running.
mjr 77:0b96f6867312 357 txRunning = true;
mjr 77:0b96f6867312 358
mjr 77:0b96f6867312 359 // Directly invoke the thread handler for the first call. It
mjr 77:0b96f6867312 360 // will normally run in interrupt context, but since there's
mjr 77:0b96f6867312 361 // no pending interrupt yet that would re-enter it, we can
mjr 77:0b96f6867312 362 // launch it first in application context. If there's work
mjr 77:0b96f6867312 363 // pending, it'll kick off the transmission and schedule the
mjr 77:0b96f6867312 364 // next timer interrupt to continue the thread.
mjr 77:0b96f6867312 365 txThread();
mjr 77:0b96f6867312 366 }
mjr 77:0b96f6867312 367 }
mjr 77:0b96f6867312 368
mjr 77:0b96f6867312 369 // Transmitter "thread" main. This handles the timer interrupt for each
mjr 77:0b96f6867312 370 // event in a transmission.
mjr 77:0b96f6867312 371 void txThread()
mjr 77:0b96f6867312 372 {
mjr 77:0b96f6867312 373 // if we're working on a command, process the next step
mjr 77:0b96f6867312 374 if (txProtocol != 0)
mjr 77:0b96f6867312 375 {
mjr 77:0b96f6867312 376 // Determine if the virtual button for the current transmission
mjr 77:0b96f6867312 377 // is still pressed. It's still pressed if we have a valid
mjr 77:0b96f6867312 378 // transmitting button ID, and the current pressed button is the
mjr 77:0b96f6867312 379 // same as the transmitting button.
mjr 77:0b96f6867312 380 txState.pressed = (txBtnId != -1 && txBtnId == curBtnId);
mjr 77:0b96f6867312 381
mjr 77:0b96f6867312 382 // Perform the next step via the protocol handler. The handler
mjr 77:0b96f6867312 383 // returns a positive time value for the next timeout if it still
mjr 77:0b96f6867312 384 // has more work to do.
mjr 77:0b96f6867312 385 int t = txProtocol->txStep(&txState);
mjr 77:0b96f6867312 386
mjr 77:0b96f6867312 387 // check if the transmission is done
mjr 77:0b96f6867312 388 if (t > 0)
mjr 77:0b96f6867312 389 {
mjr 77:0b96f6867312 390 // The handler returned a positive time value, so it has
mjr 77:0b96f6867312 391 // more work to do. That means we're done here - just set
mjr 77:0b96f6867312 392 // the next timeout and exit the interrupt handler.
mjr 77:0b96f6867312 393 txTimeout.attach_us(this, &IRTransmitter::txThread, t);
mjr 77:0b96f6867312 394 return;
mjr 77:0b96f6867312 395 }
mjr 77:0b96f6867312 396 else
mjr 77:0b96f6867312 397 {
mjr 77:0b96f6867312 398 // The transmission is done. Clear the send data.
mjr 77:0b96f6867312 399 txBtnId = -1;
mjr 77:0b96f6867312 400 txProtocol = 0;
mjr 77:0b96f6867312 401 }
mjr 77:0b96f6867312 402 }
mjr 77:0b96f6867312 403
mjr 77:0b96f6867312 404 // If we made it here, the transmitter is now idle. Check to
mjr 77:0b96f6867312 405 // see if we have a new virtual button press.
mjr 77:0b96f6867312 406 if (curBtnId != -1)
mjr 77:0b96f6867312 407 {
mjr 77:0b96f6867312 408 // load the command
mjr 77:0b96f6867312 409 txBtnId = curBtnId;
mjr 77:0b96f6867312 410 txCmd = buttons[curBtnId];
mjr 77:0b96f6867312 411 txProtocol = IRProtocol::senderForId(txCmd.pro);
mjr 77:0b96f6867312 412
mjr 77:0b96f6867312 413 // If we found a protocol handler, start the transmission
mjr 77:0b96f6867312 414 if (txProtocol != 0)
mjr 77:0b96f6867312 415 {
mjr 77:0b96f6867312 416 // fill in the transmission state object with the new command
mjr 77:0b96f6867312 417 // details
mjr 77:0b96f6867312 418 txState.cmdCode = txCmd.cmd;
mjr 77:0b96f6867312 419 txState.protocolId = txCmd.pro;
mjr 77:0b96f6867312 420 txState.dittos = txCmd.dittos;
mjr 77:0b96f6867312 421 txState.pin = &ledPin;
mjr 77:0b96f6867312 422 txState.pressed = true;
mjr 77:0b96f6867312 423
mjr 77:0b96f6867312 424 // reset the transmission step counters
mjr 77:0b96f6867312 425 txState.step = 0;
mjr 77:0b96f6867312 426 txState.bit = 0;
mjr 77:0b96f6867312 427 txState.bitstep = 0;
mjr 77:0b96f6867312 428 txState.rep = 0;
mjr 77:0b96f6867312 429
mjr 77:0b96f6867312 430 // this is a new transmission, so toggle the toggle bit
mjr 77:0b96f6867312 431 txState.toggle ^= 1;
mjr 77:0b96f6867312 432
mjr 77:0b96f6867312 433 // Turn off the IR and set the PWM frequency of the IR LED to
mjr 77:0b96f6867312 434 // the carrier frequency for the chosen protocol
mjr 77:0b96f6867312 435 ledPin.write(0);
mjr 77:0b96f6867312 436 ledPin.getUnit()->period(txProtocol->pwmPeriod(&txState));
mjr 77:0b96f6867312 437
mjr 77:0b96f6867312 438 // start the transmission timer
mjr 77:0b96f6867312 439 txState.txTime.reset();
mjr 77:0b96f6867312 440 txState.txTime.start();
mjr 77:0b96f6867312 441
mjr 77:0b96f6867312 442 // initiate the transmission
mjr 77:0b96f6867312 443 int t = txProtocol->txStart(&txState);
mjr 77:0b96f6867312 444
mjr 77:0b96f6867312 445 // set the timer for the next step of the transmission, then
mjr 77:0b96f6867312 446 // we're done
mjr 77:0b96f6867312 447 txTimeout.attach_us(this, &IRTransmitter::txThread, t);
mjr 77:0b96f6867312 448 return;
mjr 77:0b96f6867312 449 }
mjr 77:0b96f6867312 450 }
mjr 77:0b96f6867312 451
mjr 77:0b96f6867312 452 // If we made it here, there's no transmission in progress,
mjr 77:0b96f6867312 453 // so the thread is no longer running.
mjr 77:0b96f6867312 454 txRunning = false;
mjr 77:0b96f6867312 455 }
mjr 77:0b96f6867312 456
mjr 77:0b96f6867312 457 // LED output pin controlling the IR LED. The pin must be PWM-capable.
mjr 77:0b96f6867312 458 // WARNING! Don't connect the IR LED directly to the pin. See wiring
mjr 77:0b96f6867312 459 // diagram at the top of the file.
mjr 77:0b96f6867312 460 NewPwmOut ledPin;
mjr 77:0b96f6867312 461
mjr 77:0b96f6867312 462 // Virtual button slots. Each slot represents a virtual remote control
mjr 77:0b96f6867312 463 // button, containing a preprogrammed IR command code to send when the
mjr 77:0b96f6867312 464 // button is pressed. Program a button by calling programButton().
mjr 77:0b96f6867312 465 // Press a button by calling pushButton().
mjr 77:0b96f6867312 466 struct ButtonCmd
mjr 77:0b96f6867312 467 {
mjr 77:0b96f6867312 468 uint64_t cmd; // command code
mjr 77:0b96f6867312 469 uint8_t pro; // protocol ID (IRPRO_xxx)
mjr 77:0b96f6867312 470 uint8_t dittos : 1; // use "ditto" codes for auto-repeat
mjr 77:0b96f6867312 471 } __attribute__ ((packed));
mjr 77:0b96f6867312 472 ButtonCmd *buttons;
mjr 77:0b96f6867312 473
mjr 77:0b96f6867312 474 // Current active virtual button ID. This is managed in application
mjr 77:0b96f6867312 475 // context and read in interrupt context. This represents the currently
mjr 77:0b96f6867312 476 // pushed button.
mjr 77:0b96f6867312 477 int curBtnId;
mjr 77:0b96f6867312 478
mjr 77:0b96f6867312 479 // Is the transmitter "thread" running? This is true when a timer is
mjr 77:0b96f6867312 480 // pending, false if not. The timer interrupt handler clears this
mjr 77:0b96f6867312 481 // before exiting on its last run of a transmission.
mjr 77:0b96f6867312 482 //
mjr 77:0b96f6867312 483 // Synchronization: if txRunning is false, no timer interrupt is either
mjr 77:0b96f6867312 484 // running or pending, so there's no possibility that anyone else will
mjr 77:0b96f6867312 485 // change it, so it's safe for the application to test and set it. If
mjr 77:0b96f6867312 486 // txRunning is true, only interrupt context can change it, so application
mjr 77:0b96f6867312 487 // context can only read it.
mjr 77:0b96f6867312 488 volatile bool txRunning;
mjr 77:0b96f6867312 489
mjr 77:0b96f6867312 490 // Transmitter thread timeout
mjr 77:0b96f6867312 491 Timeout txTimeout;
mjr 77:0b96f6867312 492
mjr 77:0b96f6867312 493 // Command ID being transmitted in the background "thread". The thread
mjr 77:0b96f6867312 494 // loads this from curBtnID whenever it's out of other work to do.
mjr 77:0b96f6867312 495 int txBtnId;
mjr 77:0b96f6867312 496
mjr 77:0b96f6867312 497 // Protocol for the current transmission
mjr 77:0b96f6867312 498 IRProtocol *txProtocol;
mjr 77:0b96f6867312 499
mjr 77:0b96f6867312 500 // Command value we're currently transmitting
mjr 77:0b96f6867312 501 ButtonCmd txCmd;
mjr 77:0b96f6867312 502
mjr 77:0b96f6867312 503 // Protocol state. This is for use by the individual protocol
mjr 77:0b96f6867312 504 // classes to keep track of their state while the transmission
mjr 77:0b96f6867312 505 // proceeds.
mjr 77:0b96f6867312 506 IRTXState txState;
mjr 77:0b96f6867312 507 };
mjr 77:0b96f6867312 508
mjr 77:0b96f6867312 509 #endif