]> vault307.fbx.one Git - micorpython_ir.git/blob - TRANSMITTER.md
3e3290e28d56f2c66ca8e73d9e33891ae17ed21f
[micorpython_ir.git] / TRANSMITTER.md
1 # IR Transmitter
2
3 ##### [Main README](./README.md#1-ir-communication)
4
5 # 1. Hardware Requirements
6
7 The transmitter requires a Pyboard 1.x (not Lite), a Pyboard D or an ESP32.
8 Output is via an IR LED which needs a simple circuit to provide sufficient
9 current. Typically these need 50-100mA of drive to achieve reasonable range and
10 data integrity. A suitable 940nm LED is [this one](https://www.adafruit.com/product/387).
11
12 On the Pyboard the transmitter test script assumes pin X1 for IR output. It can
13 be changed, but it must support Timer 2 channel 1. Pins for pushbutton inputs
14 are arbitrary: X3 and X4 are used. The driver uses timers 2 and 5.
15
16 On ESP32 the demo uses pins 21 and 23 for IR output and pins 18 and 19 for
17 pushbuttons. These pins may be changed.
18
19 ## 1.1 Pyboard Wiring
20
21 I use the following circuit which delivers just under 40mA to the diode. R2 may
22 be reduced for higher current.
23 ![Image](images/circuit.png)
24
25 This alternative delivers a constant current of about 53mA if a higher voltage
26 than 5V is available. R4 determines the current value and may be reduced to
27 increase power.
28 ![Image](images/circuit2.png)
29
30 The transistor type is not critical.
31
32 The driver assumes circuits as shown. Here the carrier "off" state is 0V,
33 which is the driver default. If using a circuit where "off" is required to be
34 3.3V, the class variable `active_high` should be set `False`.
35
36 ## 1.2 ESP32 Wiring
37
38 The ESP32 RMT device does not currently support the carrier option. A simple
39 hardware gate is required to turn the IR LED on when both the carrier pin and
40 the RMT pin are high. A suitable circuit is below; the transistor type is not
41 critical.
42 ![Image](images/gate.png)
43
44 This simpler alternative uses MOSFETS, but the device type needs attention. The
45 chosen type has a low gate-source threshold voltage and low Rdson.
46 ![Image](images/gate_mosfet.png)
47
48 # 2. Dependencies and installation
49
50 ## 2.1 Dependencies
51
52 The device driver has no dependencies.
53
54 On ESP32 a firmware version >= V1.12 is required. The Loboris port is not
55 supported owing to the need for the RMT device.
56
57 The demo program requires `uasyncio` from the official library and `aswitch.py`
58 from [this repo](https://github.com/peterhinch/micropython-async).
59
60 ## 2.2 Installation
61
62 The transmitter is a Python package. This minimises RAM usage: applications
63 only import the device driver for the protocol in use. Clone the repository to
64 the current directory of your PC with:
65 ```bash
66 $ git clone https://github.com/peterhinch/micropython_ir
67 ```
68
69 Copy the following to the target filesystem:
70 1. `ir_tx` Directory and contents.
71
72 The demo is of a 2-button remote controller with auto-repeat. It may be run by
73 issuing:
74 ```python
75 from ir_tx.test import test
76 ```
77 Instructions will be displayed at the REPL.
78
79 # 3. The driver
80
81 This is specific to Pyboard D, Pyboard 1.x (not Lite) and ESP32.
82
83 It implements a class for each supported protocol, namely `NEC`, `SONY_12`,
84 `SONY_15`, `SONY_20`, `RC5` and `RC6_M0`. Each class is subclassed from a
85 common abstract base class in `__init__.py`. The application instantiates the
86 appropriate class and calls the `transmit` method to send data.
87
88 Basic usage on a Pyboard:
89 ```python
90 from machine import Pin
91 from ir_tx.nec import NEC
92 nec = NEC(Pin('X1'))
93 nec.transmit(1, 2) # address == 1, data == 2
94 ```
95 Basic usage on ESP32:
96 ```python
97 from machine import Pin
98 from ir_tx.nec import NEC
99 pins = (Pin(23, Pin.OUT, value = 0), Pin(21, Pin.OUT, value = 0))
100 nec = NEC(pins)
101 nec.transmit(1, 2) # address == 1, data == 2
102 ```
103
104 #### Common to all classes
105
106 Constructor args:
107 1. `pin` A Pin instance instantiated as an output. On a Pyboard this is a
108 `pyb.Pin` instance supporting Timer 2 channel 1: `X1` is employed by the test
109 script. On ESP32 any `machine.Pin` may be used. Must be connected to the IR
110 diode as described below.
111 2. `freq=default` The carrier frequency in Hz. The default for NEC is 38000,
112 Sony is 40000 and Philips is 36000.
113 3. `verbose=False` If `True` emits (a lot of) debug output.
114
115 Method:
116 1. `transmit(addr, data, toggle=0, validate=False)` Args `addr`, `data` and
117 `toggle` are positive integers. The maximum vaues are protocol dependent. If
118 `validate` is `True` passed values are checked and a `ValueError` raised if
119 they are out of range. If `validate` is false invalid bits are silently
120 discarded. For example if an address of 0x11 is passed to `MCE.transmit`, the
121 address sent will be 1 because that protocol supports only a four bit address
122 field. The `toggle` field is unused by some protocols when 0 should be passed.
123
124 Class method:
125 1. `active_low` No args. Pyboard only. A `ValueError` will be thrown on ESP32.
126 The IR LED drive circuit is usually designed to turn the LED on if the driver
127 pin is high. If it has opposite polarity the method must be called before
128 instantiating the class - it will be ineffective if called later.
129
130 Class varaible:
131 1. `timeit=False` If `True` the `.transmit` method times itself and prints the
132 result in μs.
133
134 The `transmit` method is synchronous with rapid return. Actual transmission
135 occurs as a background process, on the Pyboard controlled by timers 2 and 5. On
136 ESP32 the RMT class is used. Execution times were measured on a Pyboard 1.1 and
137 the ESP32 reference board without SPIRAM. Tests were done at stock frequency and
138 with `validate=True`, `verbose=False`. A small saving could be achieved by
139 skipping validation.
140
141 | Protocol | ESP32 | Pyboard |
142 |:--------:|:-----:|:-------:|
143 | NEC | 7.8ms | 3.2ms |
144 | SONY12 | 3.2ms | 1.3ms |
145 | SONY15 | 3.6ms | 1.5ms |
146 | SONY20 | 4.5ms | 1.9ms |
147 | RC5 | 4.9ms | 1.5ms |
148 | RC6_M0 | 6.0ms | 2.0ms |
149 | MCE | 6.7ms | 2.0ms |
150
151 #### NEC class
152
153 Class `NEC`. Example invocation:
154 ```python
155 from ir_tx.nec import NEC
156 ```
157
158 This has an additional method `.repeat` (no args). This causes a repeat code to
159 be transmitted. Should be called every 108ms if a button is held down.
160
161 The NEC protocol accepts 8 or 16 bit addresses. In the former case, a 16 bit
162 value is transmitted comprising the 8 bit address and its one's complement,
163 enabling the receiver to perform a simple error check. The `NEC` class supports
164 these modes by checking the value of `addr` passed to `.transmit` and sending
165 the complement for values < 256.
166
167 A value passed in `toggle` is ignored.
168
169 #### Sony classes
170
171 Classes `SONY_12`, `SONY_15` and `SONY_20`. Example invocation:
172 ```python
173 from ir_tx.sony import SONY_15
174 ```
175
176 The SIRC protocol supports three sizes, supported by the following classes:
177 1. 12 bit (7 data, 5 address) `SONY_12`
178 2. 15 bit (7 data, 8 address) `SONY_15`
179 3. 20 bit (7 data, 5 addresss, 8 extended) `SONY_20`
180
181 The `.transmit` method masks `addr` and `data` values to the widths listed
182 above. `toggle` is ignored except by `SONY_20` which treats it as the extended
183 value.
184
185 #### Philips classes
186
187 Classes `RC5` and `RC6_M0`. Example invocation:
188 ```python
189 from ir_tx.philips import RC5
190 ```
191
192 The RC-5 protocol supports a 5 bit address and 6 or 7 bit (RC5X) data. The
193 driver uses the appropriate mode depending on the `data` value provided.
194
195 The RC-6 protocol accepts 8 bit address and data values.
196
197 Both send a `toggle` bit which remains constant if a button is held down, but
198 changes when the button is released. The application should implement this
199 behaviour, setting the `toggle` arg of `.transmit` to 0 or 1 as required.
200
201 #### Microsoft MCE class
202
203 Class `MCE`. Example invocation:
204 ```python
205 from ir_tx.mce import MCE
206 # MCE.init_cs = 3
207 ```
208 There is a separate demo for the `MCE` class because of the need to send a
209 message on key release. It is run by issuing:
210 ```python
211 from ir_tx.mcetest import test
212 ```
213 Instructions will be displayed at the REPL.
214
215 I have been unable to locate a definitive specification: the protocol was
216 analysed by a mixture of googling and experiment. Behaviour may change if I
217 acquire new information. The protocol is known as OrtekMCE and the remote
218 control is sold on eBay as VRC-1100.
219
220 The remote was designed for Microsoft Media Center and is used to control Kodi
221 on boxes such as the Raspberry Pi. With a suitable PC driver it can emulate a
222 PC keyboard and mouse. The mouse emulation uses a different protocol: the class
223 does not currently support it. Pressing mouse buttons and pad will cause the
224 error function (if provided) to be called.
225
226 This supports a 4 bit address, 6 bit data and 2 bit toggle. The latter should
227 have a value of 0 for the first message, 1 for repeat messages, and 2 for a
228 final message sent on button release.
229
230 The remaining four bits are a checksum which the driver creates. The algorithm
231 requires an initial 'seed' value which my testing proved to be 4. However the
232 only [documentation](http://www.hifi-remote.com/johnsfine/DecodeIR.html#OrtekMCE)
233 I could find stated that the value should be 3. I implemented this as a class
234 variable `MCE.init_cs=4`. This enables it to be changed if some receivers
235 require 3.
236
237 # 4. Principle of operation
238
239 The classes inherit from the abstract base class `IR`. This has an array `.arr`
240 to contain the duration (in μs) of each carrier on or off period. The
241 `transmit` method calls a `tx` method of the subclass which populates this
242 array. This is done by two methods of the base class, `.append` and `.add`. The
243 former takes a list of times (in ) and appends them to the array. A bound
244 variable `.carrier` keeps track of the notional on/off state of the carrier:
245 this is required for bi-phase (manchester) codings.
246
247 The `.add` method takes a single μs time value and adds it to the last value
248 in the array: this pulse lengthening is used in bi-phase encodings.
249
250 On completion of the subclass `.tx`, `.transmit` calls `.trigger` which
251 initiates transmission as a background process. Its behaviour is platform
252 dependent.
253
254 ## 4.1 Pyboard
255
256 Tramsmission is performed by two hardware timers initiated in the constructor.
257 Timer 2, channel 1 is used to configure the output pin as a PWM channel. Its
258 frequency is set in the constructor. The OOK is performed by dynamically
259 changing the duty ratio using the timer channel's `pulse_width_percent` method:
260 this varies the pulse width from 0 to the duty ratio passed to the constructor.
261
262 The duty ratio is changed by the Timer 5 callback `._cb`. This retrieves the
263 next duration from the array. If it is not `STOP` it toggles the duty cycle
264 and re-initialises T5 for the new duration. If it is `STOP` it ensures that the
265 duty ratio is set to the `_SPACE`
266
267 Here `.trigger` appends a special `STOP` value and initiates physical
268 transmission by calling the Timer5 callback.
269
270 ## 4.2 ESP32
271
272 The carrier is output continuously at the specified duty ratio. A pulse train
273 generated by the RMT instance drives a hardware gate such that the IR LED is
274 lit only when both carrier and RMT are high.
275
276 The carrier is generated by PWM instance `.pwm` running continuously. The ABC
277 constructor converts the 0-100 duty ratio specified by the subclass to the
278 0-1023 range used by ESP32.
279
280 The `.trigger` method calls `RMT.write_pulses` and returns with `RMT` operating
281 in the background.
282
283 ## 4.3 Duty ratio
284
285 In every case where I could find a specified figure it was 30%. I measured
286 that from a variety of remotes, and in every case it was close to that figure.
287
288 # 5. Unsupported protocols
289
290 You can use the receiver module to capture an IR burst and replay it with the
291 transmitter. This enables limited support for unknown protocols. This is
292 strictly for experimenters and I haven't documented it in detail.
293
294 There are two limitations. The first is timing accuracy: both receiving and
295 transmitting processes introduce some timing uncertainty. This is only likely
296 to be a practical problem with fast protocols. In brief testing with a known
297 protocol the scripts below worked.
298
299 The more tricky problem is handling repeat keys: different protocols use widely
300 varying approaches. If repeat keys are to be supported some experimentation and
301 coding is likely to be required.
302
303 The following captures a single burst and saves it to a file:
304 ```python
305 from ir_rx.acquire import test
306 import ujson
307
308 lst = test() # May report unsupported or unknown protocol
309 with open('burst.py', 'w') as f:
310 ujson.dump(lst, f)
311 ```
312 This replays it:
313 ```python
314 from ir_tx import Player
315 from sys import platform
316 import ujson
317
318 if platform == 'esp32':
319 from machine import Pin
320 pin = (Pin(23, Pin.OUT, value = 0), Pin(21, Pin.OUT, value = 0))
321 else:
322 from pyb import Pin, LED
323 pin = Pin('X1')
324 with open('burst.py', 'r') as f:
325 lst = ujson.load(f)
326 ir = Player(pin)
327 ir.play(lst)
328 ```
329 The `ir_tx.Player` class is a minimal subclass supporting only the `.play`
330 method. This takes as an arg an iterable comprising time values of successive
331 mark and space periods (in μs).
332
333 The `ir_rx.acquire.test` function makes assumptions about the likely maximum
334 length and maximum duration of a burst. In some cases this may require some
335 modification e.g. to instantiate `IR_GET` with different args.