An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

/media/uploads/mjr/pinscape_no_background_small_L7Miwr6.jpg

This is Version 2 of the Pinscape Controller, an I/O controller for virtual pinball machines. (You can find the old version 1 software here.) Pinscape is software for the KL25Z that turns the board into a full-featured I/O controller for virtual pinball, with support for accelerometer-based nudging, a mechanical plunger, button inputs, and feedback device control.

In case you haven't heard of the idea before, a "virtual pinball machine" is basically a video pinball simulator that's built into a real pinball machine body. A TV monitor goes in place of the pinball playfield, and a second TV goes in the backbox to show the backglass artwork. Some cabs also include a third monitor to simulate the DMD (Dot Matrix Display) used for scoring on 1990s machines, or even an original plasma DMD. A computer (usually a Windows PC) is hidden inside the cabinet, running pinball emulation software that displays a life-sized playfield on the main TV. The cabinet has all of the usual buttons, too, so it not only looks like the real thing, but plays like it too. That's a picture of my own machine to the right. On the outside, it's built exactly like a real arcade pinball machine, with the same overall dimensions and all of the standard pinball cabinet trim hardware.

It's possible to buy a pre-built virtual pinball machine, but it also makes a great DIY project. If you have some basic wood-working skills and know your way around PCs, you can build one from scratch. The computer part is just an ordinary Windows PC, and all of the pinball emulation can be built out of free, open-source software. In that spirit, the Pinscape Controller is an open-source software/hardware project that offers a no-compromises, all-in-one control center for all of the unique input/output needs of a virtual pinball cabinet. If you've been thinking about building one of these, but you're not sure how to connect a plunger, flipper buttons, lights, nudge sensor, and whatever else you can think of, this project might be just what you're looking for.

You can find much more information about DIY Pin Cab building in general in the Virtual Cabinet Forum on vpforums.org. Also visit my Pinscape Resources page for more about this project and other virtual pinball projects I'm working on.

Downloads

  • Pinscape Release Builds: This page has download links for all of the Pinscape software. To get started, install and run the Pinscape Config Tool on your Windows computer. It will lead you through the steps for installing the Pinscape firmware on the KL25Z.
  • Config Tool Source Code. The complete C# source code for the config tool. You don't need this to run the tool, but it's available if you want to customize anything or see how it works inside.

Documentation

The new Version 2 Build Guide is now complete! This new version aims to be a complete guide to building a virtual pinball machine, including not only the Pinscape elements but all of the basics, from sourcing parts to building all of the hardware.

You can also refer to the original Hardware Build Guide (PDF), but that's out of date now, since it refers to the old version 1 software, which was rather different (especially when it comes to configuration).

System Requirements

The new Config Tool requires a fairly up-to-date Microsoft .NET installation. If you use Windows Update to keep your system current, you should be fine. A modern version of Internet Explorer (IE) is required, even if you don't use it as your main browser, because the Config Tool uses some system components that Microsoft packages into the IE install set. I test with IE11, so that's known to work. IE8 doesn't work. IE9 and 10 are unknown at this point.

The Windows requirements are only for the config tool. The firmware doesn't care about anything on the Windows side, so if you can make do without the config tool, you can use almost any Windows setup.

Main Features

Plunger: The Pinscape Controller started out as a "mechanical plunger" controller: a device for attaching a real pinball plunger to the video game software so that you could launch the ball the natural way. This is still, of course, a central feature of the project. The software supports several types of sensors: a high-resolution optical sensor (which works by essentially taking pictures of the plunger as it moves); a slide potentiometer (which determines the position via the changing electrical resistance in the pot); a quadrature sensor (which counts bars printed on a special guide rail that it moves along); and an IR distance sensor (which determines the position by sending pulses of light at the plunger and measuring the round-trip travel time). The Build Guide explains how to set up each type of sensor.

Nudging: The KL25Z (the little microcontroller that the software runs on) has a built-in accelerometer. The Pinscape software uses it to sense when you nudge the cabinet, and feeds the acceleration data to the pinball software on the PC. This turns physical nudges into virtual English on the ball. The accelerometer is quite sensitive and accurate, so we can measure the difference between little bumps and hard shoves, and everything in between. The result is natural and immersive.

Buttons: You can wire real pinball buttons to the KL25Z, and the software will translate the buttons into PC input. You have the option to map each button to a keyboard key or joystick button. You can wire up your flipper buttons, Magna Save buttons, Start button, coin slots, operator buttons, and whatever else you need.

Feedback devices: You can also attach "feedback devices" to the KL25Z. Feedback devices are things that create tactile, sound, and lighting effects in sync with the game action. The most popular PC pinball emulators know how to address a wide variety of these devices, and know how to match them to on-screen action in each virtual table. You just need an I/O controller that translates commands from the PC into electrical signals that turn the devices on and off. The Pinscape Controller can do that for you.

Expansion Boards

There are two main ways to run the Pinscape Controller: standalone, or using the "expansion boards".

In the basic standalone setup, you just need the KL25Z, plus whatever buttons, sensors, and feedback devices you want to attach to it. This mode lets you take advantage of everything the software can do, but for some features, you'll have to build some ad hoc external circuitry to interface external devices with the KL25Z. The Build Guide has detailed plans for exactly what you need to build.

The other option is the Pinscape Expansion Boards. The expansion boards are a companion project, which is also totally free and open-source, that provides Printed Circuit Board (PCB) layouts that are designed specifically to work with the Pinscape software. The PCB designs are in the widely used EAGLE format, which many PCB manufacturers can turn directly into physical boards for you. The expansion boards organize all of the external connections more neatly than on the standalone KL25Z, and they add all of the interface circuitry needed for all of the advanced software functions. The big thing they bring to the table is lots of high-power outputs. The boards provide a modular system that lets you add boards to add more outputs. If you opt for the basic core setup, you'll have enough outputs for all of the toys in a really well-equipped cabinet. If your ambitions go beyond merely well-equipped and run to the ridiculously extravagant, just add an extra board or two. The modular design also means that you can add to the system over time.

Expansion Board project page

Update notes

If you have a Pinscape V1 setup already installed, you should be able to switch to the new version pretty seamlessly. There are just a couple of things to be aware of.

First, the "configuration" procedure is completely different in the new version. Way better and way easier, but it's not what you're used to from V1. In V1, you had to edit the project source code and compile your own custom version of the program. No more! With V2, you simply install the standard, pre-compiled .bin file, and select options using the Pinscape Config Tool on Windows.

Second, if you're using the TSL1410R optical sensor for your plunger, there's a chance you'll need to boost your light source's brightness a little bit. The "shutter speed" is faster in this version, which means that it doesn't spend as much time collecting light per frame as before. The software actually does "auto exposure" adaptation on every frame, so the increased shutter speed really shouldn't bother it, but it does require a certain minimum level of contrast, which requires a certain minimal level of lighting. Check the plunger viewer in the setup tool if you have any problems; if the image looks totally dark, try increasing the light level to see if that helps.

New Features

V2 has numerous new features. Here are some of the highlights...

Dynamic configuration: as explained above, configuration is now handled through the Config Tool on Windows. It's no longer necessary to edit the source code or compile your own modified binary.

Improved plunger sensing: the software now reads the TSL1410R optical sensor about 15x faster than it did before. This allows reading the sensor at full resolution (400dpi), about 400 times per second. The faster frame rate makes a big difference in how accurately we can read the plunger position during the fast motion of a release, which allows for more precise position sensing and faster response. The differences aren't dramatic, since the sensing was already pretty good even with the slower V1 scan rate, but you might notice a little better precision in tricky skill shots.

Keyboard keys: button inputs can now be mapped to keyboard keys. The joystick button option is still available as well, of course. Keyboard keys have the advantage of being closer to universal for PC pinball software: some pinball software can be set up to take joystick input, but nearly all PC pinball emulators can take keyboard input, and nearly all of them use the same key mappings.

Local shift button: one physical button can be designed as the local shift button. This works like a Shift button on a keyboard, but with cabinet buttons. It allows each physical button on the cabinet to have two PC keys assigned, one normal and one shifted. Hold down the local shift button, then press another key, and the other key's shifted key mapping is sent to the PC. The shift button can have a regular key mapping of its own as well, so it can do double duty. The shift feature lets you access more functions without cluttering your cabinet with extra buttons. It's especially nice for less frequently used functions like adjusting the volume or activating night mode.

Night mode: the output controller has a new "night mode" option, which lets you turn off all of your noisy devices with a single button, switch, or PC command. You can designate individual ports as noisy or not. Night mode only disables the noisemakers, so you still get the benefit of your flashers, button lights, and other quiet devices. This lets you play late into the night without disturbing your housemates or neighbors.

Gamma correction: you can designate individual output ports for gamma correction. This adjusts the intensity level of an output to make it match the way the human eye perceives brightness, so that fades and color mixes look more natural in lighting devices. You can apply this to individual ports, so that it only affects ports that actually have lights of some kind attached.

IR Remote Control: the controller software can transmit and/or receive IR remote control commands if you attach appropriate parts (an IR LED to send, an IR sensor chip to receive). This can be used to turn on your TV(s) when the system powers on, if they don't turn on automatically, and for any other functions you can think of requiring IR send/receive capabilities. You can assign IR commands to cabinet buttons, so that pressing a button on your cabinet sends a remote control command from the attached IR LED, and you can have the controller generate virtual key presses on your PC in response to received IR commands. If you have the IR sensor attached, the system can use it to learn commands from your existing remotes.

Yet more USB fixes: I've been gradually finding and fixing USB bugs in the mbed library for months now. This version has all of the fixes of the last couple of releases, of course, plus some new ones. It also has a new "last resort" feature, since there always seems to be "just one more" USB bug. The last resort is that you can tell the device to automatically reboot itself if it loses the USB connection and can't restore it within a given time limit.

More Downloads

  • Custom VP builds: I created modified versions of Visual Pinball 9.9 and Physmod5 that you might want to use in combination with this controller. The modified versions have special handling for plunger calibration specific to the Pinscape Controller, as well as some enhancements to the nudge physics. If you're not using the plunger, you might still want it for the nudge improvements. The modified version also works with any other input controller, so you can get the enhanced nudging effects even if you're using a different plunger/nudge kit. The big change in the modified versions is a "filter" for accelerometer input that's designed to make the response to cabinet nudges more realistic. It also makes the response more subdued than in the standard VP, so it's not to everyone's taste. The downloads include both the updated executables and the source code changes, in case you want to merge the changes into your own custom version(s).

    Note! These features are now standard in the official VP releases, so you don't need my custom builds if you're using 9.9.1 or later and/or VP 10. I don't think there's any reason to use my versions instead of the latest official ones, and in fact I'd encourage you to use the official releases since they're more up to date, but I'm leaving my builds available just in case. In the official versions, look for the checkbox "Enable Nudge Filter" in the Keys preferences dialog. My custom versions don't include that checkbox; they just enable the filter unconditionally.
  • Output circuit shopping list: This is a saved shopping cart at mouser.com with the parts needed to build one copy of the high-power output circuit for the LedWiz emulator feature, for use with the standalone KL25Z (that is, without the expansion boards). The quantities in the cart are for one output channel, so if you want N outputs, simply multiply the quantities by the N, with one exception: you only need one ULN2803 transistor array chip for each eight output circuits. If you're using the expansion boards, you won't need any of this, since the boards provide their own high-power outputs.
  • Cary Owens' optical sensor housing: A 3D-printable design for a housing/mounting bracket for the optical plunger sensor, designed by Cary Owens. This makes it easy to mount the sensor.
  • Lemming77's potentiometer mounting bracket and shooter rod connecter: Sketchup designs for 3D-printable parts for mounting a slide potentiometer as the plunger sensor. These were designed for a particular slide potentiometer that used to be available from an Aliexpress.com seller but is no longer listed. You can probably use this design as a starting point for other similar devices; just check the dimensions before committing the design to plastic.

Copyright and License

The Pinscape firmware is copyright 2014, 2021 by Michael J Roberts. It's released under an MIT open-source license. See License.

Warning to VirtuaPin Kit Owners

This software isn't designed as a replacement for the VirtuaPin plunger kit's firmware. If you bought the VirtuaPin kit, I recommend that you don't install this software. The KL25Z can only run one firmware program at a time, so if you install the Pinscape firmware on your KL25Z, it will replace and erase your existing VirtuaPin proprietary firmware. If you do this, the only way to restore your VirtuaPin firmware is to physically ship the KL25Z back to VirtuaPin and ask them to re-flash it. They don't allow you to do this at home, and they don't even allow you to back up your firmware, since they want to protect their proprietary software from copying. For all of these reasons, if you want to run the Pinscape software, I strongly recommend that you buy a "blank" retail KL25Z to use with Pinscape. They only cost about $15 and are available at several online retailers, including Amazon, Mouser, and eBay. The blank retail boards don't come with any proprietary firmware pre-installed, so installing Pinscape won't delete anything that you paid extra for.

With those warnings in mind, if you're absolutely sure that you don't mind permanently erasing your VirtuaPin firmware, it is at least possible to use Pinscape as a replacement for the VirtuaPin firmware. Pinscape uses the same button wiring conventions as the VirtuaPin setup, so you can keep your buttons (although you'll have to update the GPIO pin mappings in the Config Tool to match your physical wiring). As of the June, 2021 firmware, the Vishay VCNL4010 plunger sensor that comes with the VirtuaPin v3 plunger kit is supported, so you can also keep your plunger, if you have that chip. (You should check to be sure that's the sensor chip you have before committing to this route, if keeping the plunger sensor is important to you. The older VirtuaPin plunger kits came with different IR sensors that the Pinscape software doesn't handle.)

Committer:
mjr
Date:
Fri Mar 17 22:02:08 2017 +0000
Revision:
77:0b96f6867312
Child:
97:fc7727303038
New memory pool management; keeping old ones as #ifdefs for now for reference.

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