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 real plunger, button inputs, and feedback device control.

In case you haven't heard of the concept 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 serve as the "backglass" display. A third smaller monitor can serve as the "DMD" (the Dot Matrix Display used for scoring on newer machines), or you can even install a real pinball plasma DMD. A computer 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 hardware.

A few small companies build and sell complete, finished virtual pinball machines, but I think it's more fun as a 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 potentionmeter (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 VirtuaPin kit uses the same KL25Z microcontroller that Pinscape uses, but the rest of its hardware is different and incompatible. In particular, the Pinscape firmware doesn't include support for the IR proximity sensor used in the VirtuaPin plunger kit, so you won't be able to use your plunger device with the Pinscape firmware. In addition, the VirtuaPin setup uses a different set of GPIO pins for the button inputs from the Pinscape defaults, so if you do install the Pinscape firmware, you'll have to go into the Config Tool and reassign all of the buttons to match the VirtuaPin wiring.

Committer:
mjr
Date:
Sat Apr 18 19:08:55 2020 +0000
Revision:
109:310ac82cbbee
Parent:
97:fc7727303038
TCD1103 DMA setup time padding to fix sporadic missed first pixel in transfer; fix TV ON so that the TV ON IR commands don't have to be grouped in the IR command first slots

Who changed what in which revision?

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