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