firmware builds.
The receiver is cross platform and has been tested on Pyboard, ESP8266 and
-ESP32. The transmitter driver is compatible with Pyboard (1.x and D series) and
-also (subject to limitations) with ESP32. For the transmitter a Pyboard is
-recommended.
+ESP32.
-The transmitter is documented [here](./TRANSMITTER.md) and the receiver is
-[here](./RECEIVER.md).
+#### [Receiver docs](./RECEIVER.md)
+
+The transmitter driver is compatible with Pyboard (1.x and D series) and ESP32.
+ESP8266 is unsupported; it seems incapable of generating the required signals.
+
+#### [Transmitter docs](./TRANSMITTER.md)
# 1. IR communication
A remote using the NEC protocol is [this one](https://www.adafruit.com/products/389).
-# 2. Installation and demo script
+# 2. Installation and demo scripts
The receiver is a Python package. This minimises RAM usage: applications only
-import the device driver for the protocol in use.
-
+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_rx` Directory and contents.
There are no dependencies.
-The demo can be used to characterise IR remotes. It displays the codes returned
-by each button. This can aid in the design of receiver applications. The demo
-prints "running" every 5 seconds and reports any data received from the remote.
+## 2.1 Test scripts
+The demo can be used to characterise IR remotes where the protocol is known. It
+displays the codes returned by each button. This can aid in the design of
+receiver applications. The demo prints "running" every 5 seconds and reports
+any data received from the remote.
```python
from ir_rx.test import test
```
Instructions will be displayed at the REPL.
+If the protocol in use is unknown, there are two options: trial and error with
+the above script or run the following:
+```python
+from ir_rx.acquire import test
+test()
+```
+This script is under development.
+
+It waits for a single burst from the remote and prints the timing of the pulses
+followed by its best guess at the protocol. It correctly identifies supported
+protocols, but can wrongly identify some unsupported proprietary protocols.
+
# 3. The driver
This implements a class for each supported protocol. Each class is subclassed
Bound variable:
1. `verbose=False` If `True` emits debug output.
-Method:
- 1. `error_function` Arg: a function taking a single `int` arg. If this is
- specified it will be called if an error occurs. The value corresponds to the
- error code (see below). Typical usage might be to provide some user feedback
- of incorrect reception although beware of occasional triggers by external
- events. In my testing the TSOP4838 produces 200µs pulses on occasion for no
- obvious reason. See [section 4](./RECEIVER.md#4-errors).
+Methods:
+ 1. `error_function` Arg: a function taking a single `int` arg. If specified
+ the function will be called if an error occurs. The arg value corresponds to
+ the error code. Typical usage might be to provide some user feedback of
+ incorrect reception although beware of occasional triggers by external events.
+ In my testing the TSOP4838 produces 200µs pulses on occasion for no obvious
+ reason. See [section 4](./RECEIVER.md#4-errors).
+ 2. `close` No args. Shuts down the pin and timer interrupts.
A function is provided to print errors in human readable form. This may be
invoked as follows:
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.
+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.
BADADDR = -7
def __init__(self, pin, nedges, tblock, callback, *args): # Optional args for callback
+ self._pin = pin
self._nedges = nedges
self._tblock = tblock
self.callback = callback
self._times = array('i', (0 for _ in range(nedges + 1))) # +1 for overrun
if platform == 'esp32' or platform == 'esp32_LoBo': # ESP32 Doesn't support hard IRQ
- ei = pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING))
+ pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING))
else:
- ei = pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING), hard = True)
- self._eint = ei # Keep reference??
+ pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING), hard = True)
self.edge = 0
self.tim = Timer(-1) # Sofware timer
self.cb = self.decode
def error_function(self, func):
self._errf = func
+
+ def close(self):
+ self._pin.irq(handler = None)
+ self.tim.deinit()
--- /dev/null
+# acquire.py Acquire a pulse train from an IR remote
+# Supports NEC protocol.
+# For a remote using NEC see https://www.adafruit.com/products/389
+
+# Author: Peter Hinch
+# Copyright Peter Hinch 2020 Released under the MIT license
+
+from machine import Pin, freq
+from sys import platform
+
+from utime import sleep_ms, ticks_us, ticks_diff
+from ir_rx import IR_RX
+
+
+class IR_GET(IR_RX):
+ def __init__(self, pin, nedges=100, twait=100, display=True):
+ self.display = display
+ super().__init__(pin, nedges, twait, lambda *_ : None)
+ self.data = None
+
+ def decode(self, _):
+ def near(v, target):
+ return target * 0.8 < v < target * 1.2
+ nedges = self.edge
+ if nedges < 4:
+ return # Noise
+ burst = []
+ duration = ticks_diff(self._times[0], self._times[nedges]) # 24892 for RC-5 22205 for RC-6
+ for x in range(nedges - 1):
+ dt = ticks_diff(self._times[x + 1], self._times[x])
+ if x > 0 and dt > 10000: # Reached gap between repeats
+ break
+ burst.append(dt)
+
+ if self.display:
+ detected = False
+ for x, e in enumerate(burst):
+ print('{:03d} {:5d}'.format(x, e))
+ print()
+ if burst[0] > 5000:
+ print('NEC')
+ detected = True
+ elif burst[0] > 2000: # Sony or Philips RC-6
+ if burst[1] > 750: # Probably Philips
+ if min(burst) < 600:
+ print('Philips RC-6 mode 0')
+ detected = True
+ else:
+ lb = len(burst)
+ try:
+ nbits = {25:12, 31:15, 41:20}[len(burst)]
+ except IndexError:
+ pass
+ else:
+ detected = True
+ if detected:
+ print('Sony {}bit'.format(nbits))
+
+ elif burst[0] < 1200:
+ print('Philips RC-5')
+ detected = True
+ if not detected:
+ print('Unknown protocol')
+
+ print()
+ self.data = burst
+ # Set up for new data burst. Run null callback
+ self.do_callback(0, 0, 0)
+
+ def acquire(self):
+ while self.data is None:
+ sleep_ms(5)
+ self.close()
+ return self.data
+
+def test():
+ # Define pin according to platform
+ if platform == 'pyboard':
+ pin = Pin('X3', Pin.IN)
+ elif platform == 'esp8266':
+ freq(160000000)
+ pin = Pin(13, Pin.IN)
+ elif platform == 'esp32' or platform == 'esp32_LoBo':
+ pin = Pin(23, Pin.IN)
+ irg = IR_GET(pin)
+ print('Waiting for IR data...')
+ irg.acquire()
+
+# RC Python Calculated
+# NEC 66 66 66
+# Sony 12: 24 24 26 (2 hdr + 2*(7 data + 5 addr) RC issues another: detect 26ms gap
+# Sony 15: 75 30
+# Sony 20 n/a 40
+
+# Yamaha NEC
+# Pi/Vista MCE RC6 mode 0 Din't receive
+# Panasonic TV recorder RC6 mode 0 Didn't receive
+# Virgin RC-5 Receive OK
+# Samsung TV RC6 mode 0 Didn't receive