]> vault307.fbx.one Git - micorpython_ir.git/blobdiff - TRANSMITTER.md
Release 0.1 Various bugfixes and improvements.
[micorpython_ir.git] / TRANSMITTER.md
index 24cd24423b22c16bcd125372837dba657cbee883..2cf169aabd4c879b601b7f2ce56f88a158298f98 100644 (file)
@@ -31,16 +31,19 @@ The transistor type is not critical.
 
 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.  
 ![Image](images/gate.png)
 
-The transistor type is not critical.
+This simpler alternative uses MOSFETS, but the device type needs attention. The
+chosen type has a low gate-source threshold voltage and low Rdson.  
+![Image](images/gate_mosfet.png)
 
 # 2. Dependencies and installation
 
@@ -57,7 +60,11 @@ from [this repo](https://github.com/peterhinch/micropython-async).
 ## 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.
@@ -90,17 +97,48 @@ Constructor args:
  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.
 
@@ -114,6 +152,11 @@ A value passed in `toggle` is ignored.
 
 #### 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`
@@ -125,6 +168,11 @@ value.
 
 #### 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.
 
@@ -134,9 +182,43 @@ Both send a `toggle` bit which remains constant if a button is held down, but
 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
@@ -149,19 +231,25 @@ this is required for bi-phase (manchester) codings.
 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.
+
+## 4.1 Pyboard
 
-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%.
+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
 
@@ -173,18 +261,59 @@ The carrier is generated by PWM instance `.pwm` running continuously. The ABC
 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.
 
-[General information about IR](https://www.sbprojects.net/knowledge/ir/)
+## 4.3 Duty ratio
 
-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)
+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.
 
-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)
+# 5. Unsupported protocols
+
+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.