From: Peter Hinch Date: Sun, 15 Mar 2020 18:43:35 +0000 (+0000) Subject: Doc improvements. Add acquire script. X-Git-Url: https://vault307.fbx.one/gitweb/micorpython_ir.git/commitdiff_plain/63e4147cf13da6508817fbdefb3096c7bd9f772d Doc improvements. Add acquire script. --- diff --git a/README.md b/README.md index 371615f..44318d5 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,14 @@ require `uasyncio` but are compatible with it, and are designed for standard 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 diff --git a/RECEIVER.md b/RECEIVER.md index edd27dc..12d9577 100644 --- a/RECEIVER.md +++ b/RECEIVER.md @@ -21,26 +21,43 @@ On the WeMos D1 Mini the equivalent pin is D7. 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 @@ -92,13 +109,14 @@ The user callback takes the following args: 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: diff --git a/TRANSMITTER.md b/TRANSMITTER.md index 24cd244..c29a0dd 100644 --- a/TRANSMITTER.md +++ b/TRANSMITTER.md @@ -37,10 +37,13 @@ which is the driver default. If using a circuit where "off" is required to be 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. diff --git a/images/circuits.fzz b/images/circuits.fzz index ad48376..7ecee24 100644 Binary files a/images/circuits.fzz and b/images/circuits.fzz differ diff --git a/images/gate_mosfet.png b/images/gate_mosfet.png new file mode 100644 index 0000000..d2e137c Binary files /dev/null and b/images/gate_mosfet.png differ diff --git a/ir_rx/__init__.py b/ir_rx/__init__.py index 4b96524..0e7ad7f 100644 --- a/ir_rx/__init__.py +++ b/ir_rx/__init__.py @@ -32,6 +32,7 @@ class IR_RX(): 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 @@ -41,10 +42,9 @@ class IR_RX(): 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 @@ -68,3 +68,7 @@ class IR_RX(): def error_function(self, func): self._errf = func + + def close(self): + self._pin.irq(handler = None) + self.tim.deinit() diff --git a/ir_rx/acquire.py b/ir_rx/acquire.py new file mode 100644 index 0000000..7b492e0 --- /dev/null +++ b/ir_rx/acquire.py @@ -0,0 +1,99 @@ +# 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