The driver assumes circuits as shown. Here the carrier "off" state is 0V,
which is the driver default. If using a circuit where "off" is required to be
-3.3V, the constant `_SPACE` in `ir_tx.__init__.py` should be changed to 100.
+3.3V, the class variable `active_high` should be set `False`.
## 1.2 ESP32 Wiring
The ESP32 RMT device does not currently support the carrier option. A simple
hardware gate is required to turn the IR LED on when both the carrier pin and
-the RMT pin are high. A suitable circuit is below.
+the RMT pin are high. A suitable circuit is below; the transistor type is not
+critical.

-The transistor type is not critical. A gate could be built with two similarly
-connected N-channel MOSFETS. The 1KΩ resistors would not be required. The
-MOSFETS would require a low RDSon at Vgs == 3.3V. A ZVN4210A seems suitable
-but I haven't tried it.
+This simpler alternative uses MOSFETS, but the device type needs attention. The
+chosen type has a low gate-source threshold voltage and low Rdson.
+
# 2. Dependencies and installation
## 2.2 Installation
The transmitter is a Python package. This minimises RAM usage: applications
-only import the device driver for the protocol in use.
+only import the device driver for the protocol in use. Clone the repository to
+the current directory of your PC with:
+```bash
+$ git clone https://github.com/peterhinch/micropython_ir
+```
Copy the following to the target filesystem:
1. `ir_tx` Directory and contents.
3. `verbose=False` If `True` emits (a lot of) debug output.
Method:
- 1. `transmit(addr, data, toggle=0)` Integer args. `addr` and `data` are
- normally 8-bit values and `toggle` is normally 0 or 1; details are protocol
- dependent and are described below.
+ 1. `transmit(addr, data, toggle=0, validate=False)` Args `addr`, `data` and
+ `toggle` are positive integers. The maximum vaues are protocol dependent. If
+ `validate` is `True` passed values are checked and a `ValueError` raised if
+ they are out of range. If `validate` is false invalid bits are silently
+ discarded. For example if an address of 0x11 is passed to `MCE.transmit`, the
+ address sent will be 1 because that protocol supports only a four bit address
+ field. The `toggle` field is unused by some protocols when 0 should be passed.
+
+Class method:
+ 1. `active_low` No args. Pyboard only. A `ValueError` will be thrown on ESP32.
+ The IR LED drive circuit is usually designed to turn the LED on if the driver
+ pin is high. If it has opposite polarity the method must be called before
+ instantiating the class - it will be ineffective if called later.
+
+Class varaible:
+ 1. `timeit=False` If `True` the `.transmit` method times itself and prints the
+ result in μs.
The `transmit` method is synchronous with rapid return. Actual transmission
-occurs as a background process, on the Pyboard controlled by timers 2 and 5.
-Execution times on a Pyboard 1.1 were 3.3ms for NEC, 1.5ms for RC5 and 2ms
-for RC6.
+occurs as a background process, on the Pyboard controlled by timers 2 and 5. On
+ESP32 the RMT class is used. Execution times were measured on a Pyboard 1.1 and
+the ESP32 reference board without SPIRAM. Tests were done at stock frequency and
+with `validate=True`, `verbose=False`. A small saving could be achieved by
+skipping validation.
+
+| Protocol | ESP32 | Pyboard |
+|:--------:|:-----:|:-------:|
+| NEC | 7.8ms | 3.2ms |
+| SONY12 | 3.2ms | 1.3ms |
+| SONY15 | 3.6ms | 1.5ms |
+| SONY20 | 4.5ms | 1.9ms |
+| RC5 | 4.9ms | 1.5ms |
+| RC6_M0 | 6.0ms | 2.0ms |
+| MCE | 6.7ms | 2.0ms |
#### NEC class
+Class `NEC`. Example invocation:
+```python
+from ir_tx.nec import NEC
+```
+
This has an additional method `.repeat` (no args). This causes a repeat code to
be transmitted. Should be called every 108ms if a button is held down.
#### Sony classes
+Classes `SONY_12`, `SONY_15` and `SONY_20`. Example invocation:
+```python
+from ir_tx.sony import SONY_15
+```
+
The SIRC protocol supports three sizes, supported by the following classes:
1. 12 bit (7 data, 5 address) `SONY_12`
2. 15 bit (7 data, 8 address) `SONY_15`
#### Philips classes
+Classes `RC5` and `RC6_M0`. Example invocation:
+```python
+from ir_tx.philips import RC5
+```
+
The RC-5 protocol supports a 5 bit address and 6 or 7 bit (RC5X) data. The
driver uses the appropriate mode depending on the `data` value provided.
changes when the button is released. The application should implement this
behaviour, setting the `toggle` arg of `.transmit` to 0 or 1 as required.
-# 4. Principle of operation
+#### Microsoft MCE class
-## 4.1 Pyboard
+Class `MCE`. Example invocation:
+```python
+from ir_tx.mce import MCE
+# MCE.init_cs = 3
+```
+There is a separate demo for the `MCE` class because of the need to send a
+message on key release. It is run by issuing:
+```python
+from ir_tx.mcetest import test
+```
+Instructions will be displayed at the REPL.
+
+I have been unable to locate a definitive specification: the protocol was
+analysed by a mixture of googling and experiment. Behaviour may change if I
+acquire new information. The protocol is known as OrtekMCE and the remote
+control is sold on eBay as VRC-1100.
+
+The remote was designed for Microsoft Media Center and is used to control Kodi
+on boxes such as the Raspberry Pi. With a suitable PC driver it can emulate a
+PC keyboard and mouse. The mouse emulation uses a different protocol: the class
+does not currently support it. Pressing mouse buttons and pad will cause the
+error function (if provided) to be called.
+
+This supports a 4 bit address, 6 bit data and 2 bit toggle. The latter should
+have a value of 0 for the first message, 1 for repeat messages, and 2 for a
+final message sent on button release.
+
+The remaining four bits are a checksum which the driver creates. The algorithm
+requires an initial 'seed' value which my testing proved to be 4. However the
+only [documentation](http://www.hifi-remote.com/johnsfine/DecodeIR.html#OrtekMCE)
+I could find stated that the value should be 3. I implemented this as a class
+variable `MCE.init_cs=4`. This enables it to be changed if some receivers
+require 3.
+
+# 4. Principle of operation
The classes inherit from the abstract base class `IR`. This has an array `.arr`
to contain the duration (in μs) of each carrier on or off period. The
The `.add` method takes a single μs time value and adds it to the last value
in the array: this pulse lengthening is used in bi-phase encodings.
-On completion of the subclass `.tx`, `.transmit` appends a special `STOP` value
-and initiates physical transmission which occurs in an interrupt context.
+On completion of the subclass `.tx`, `.transmit` calls `.trigger` which
+initiates transmission as a background process. Its behaviour is platform
+dependent.
-This is performed by two hardware timers initiated in the constructor. Timer 2,
-channel 1 is used to configure the output pin as a PWM channel. Its frequency
-is set in the constructor. The OOK is performed by dynamically changing the
-duty ratio using the timer channel's `pulse_width_percent` method: this varies
-the pulse width from 0 to a duty ratio passed to the constructor. The NEC
-protocol defaults to 50%, the Sony and Philips ones to 30%.
+## 4.1 Pyboard
+
+Tramsmission is performed by two hardware timers initiated in the constructor.
+Timer 2, channel 1 is used to configure the output pin as a PWM channel. Its
+frequency is set in the constructor. The OOK is performed by dynamically
+changing the duty ratio using the timer channel's `pulse_width_percent` method:
+this varies the pulse width from 0 to the duty ratio passed to the constructor.
The duty ratio is changed by the Timer 5 callback `._cb`. This retrieves the
next duration from the array. If it is not `STOP` it toggles the duty cycle
-and re-initialises T5 for the new duration.
+and re-initialises T5 for the new duration. If it is `STOP` it ensures that the
+duty ratio is set to the `_SPACE`
+
+Here `.trigger` appends a special `STOP` value and initiates physical
+transmission by calling the Timer5 callback.
## 4.2 ESP32
constructor converts the 0-100 duty ratio specified by the subclass to the
0-1023 range used by ESP32.
-# 5. References
+The `.trigger` method calls `RMT.write_pulses` and returns with `RMT` operating
+in the background.
+
+## 4.3 Duty ratio
-[General information about IR](https://www.sbprojects.net/knowledge/ir/)
+In every case where I could find a specified figure it was 30%. I measured
+that from a variety of remotes, and in every case it was close to that figure.
-The NEC protocol:
-[altium](http://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol)
-[circuitvalley](http://www.circuitvalley.com/2013/09/nec-protocol-ir-infrared-remote-control.html)
+# 5. Unsupported protocols
-Philips protocols:
-[RC5](https://en.wikipedia.org/wiki/RC-5)
-[RC5](https://www.sbprojects.net/knowledge/ir/rc5.php)
-[RC6](https://www.sbprojects.net/knowledge/ir/rc6.php)
+You can use the receiver module to capture an IR burst and replay it with the
+transmitter. This enables limited support for unknown protocols. This is
+strictly for experimenters and I haven't documented it in detail.
+
+There are two limitations. The first is timing accuracy: both receiving and
+transmitting processes introduce some timing uncertainty. This is only likely
+to be a practical problem with fast protocols. In brief testing with a known
+protocol the scripts below worked.
+
+The more tricky problem is handling repeat keys: different protocols use widely
+varying approaches. If repeat keys are to be supported some experimentation and
+coding is likely to be required.
+
+The following captures a single burst and saves it to a file:
+```python
+from ir_rx.acquire import test
+import ujson
+
+lst = test() # May report unsupported or unknown protocol
+with open('burst.py', 'w') as f:
+ ujson.dump(lst, f)
+```
+This replays it:
+```python
+from ir_tx import Player
+from sys import platform
+import ujson
+
+if platform == 'esp32':
+ from machine import Pin
+ pin = (Pin(23, Pin.OUT, value = 0), Pin(21, Pin.OUT, value = 0))
+else:
+ from pyb import Pin, LED
+ pin = Pin('X1')
+with open('burst.py', 'r') as f:
+ lst = ujson.load(f)
+ir = Player(pin)
+ir.play(lst)
+```
+The `ir_tx.Player` class is a minimal subclass supporting only the `.play`
+method. This takes as an arg an iterable comprising time values of successive
+mark and space periods (in μs).
-Sony protocol:
-[SIRC](https://www.sbprojects.net/knowledge/ir/sirc.php)
+The `ir_rx.acquire.test` function makes assumptions about the likely maximum
+length and maximum duration of a burst. In some cases this may require some
+modification e.g. to instantiate `IR_GET` with different args.