From 5bdfdd8333ec035dd71a5b4a031096087e07c682 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Wed, 26 Feb 2020 13:57:15 +0000 Subject: [PATCH] TX and RX support NEC, RC5 and RC6 mode 0. --- README.md | 286 +++++++++++++++++++++++++++++++------------- images/circuit.png | Bin 0 -> 27357 bytes images/circuit2.png | Bin 0 -> 25218 bytes images/circuits.fzz | Bin 0 -> 2933 bytes ir_rx.py | 159 ++++++++++++------------ ir_rx_test.py | 3 +- ir_tx.py | 23 ++-- ir_tx_test.py | 8 +- 8 files changed, 301 insertions(+), 178 deletions(-) create mode 100644 images/circuit.png create mode 100644 images/circuit2.png create mode 100644 images/circuits.fzz diff --git a/README.md b/README.md index 03e013e..3a92dce 100644 --- a/README.md +++ b/README.md @@ -2,79 +2,123 @@ This repo provides a driver to receive from IR (infra red) remote controls and a driver for IR "blaster" apps. The device drivers are nonblocking. They do not -require `uasyncio` but are entirely compatible with it. +require `uasyncio` but are compatible with it. + +# 1. IR communication IR communication uses a carrier frequency to pulse the IR source. Modulation -takes the form of OOK (on-off keying). There are a several mutually -incompatible protocols and at least two options for carrier frequency, namely -36KHz and 38KHz. +takes the form of OOK (on-off keying). There are multiple protocols and at +least two options for carrier frequency, namely 36KHz and 38KHz. The drivers support the NEC protocol and two Philips protocols, namely RC-5 and RC-6 mode 0. In the case of the transmitter the carrier frequency is a runtime -parameter so any value may be used. The receiver uses a hardware demodulator -which must be specified for the correct frequency. The device driver is carrier -frequency agnostic. +parameter: any value may be specified. The receiver uses a hardware demodulator +which must be specified for the correct frequency. The receiver device driver +sees the demodulated signal and is hence carrier frequency agnostic. + +Examining waveforms from various remote controls it is evident that numerous +protocols exist. Some are doubtless proprietary and undocumented. The supported +protocols are those for which I managed to locate documentation. My preference +is for the NEC version. It has conservative timing and ample scope for error +detection. RC-5 has limited error detection, and RC-6 mode 0 has rather fast +timing: I doubt that detection can be accomplished on targets slower than a +Pyboard. + +A remote using the NEC protocol is [this one](https://www.adafruit.com/products/389). -# Hardware Requirements +Remotes normally transmit an address and a data byte. The address denotes the +physical device being controlled. The data is associated with the button on the +remote. Provision exists for differentiating between a button repeatedly +pressed and one which is held down; the mechanism is protocol dependent. + +# 2. Hardware Requirements The receiver is cross-platform. It requires an IR receiver chip to demodulate -the carrier. There are two options for carrier frequency: 36KHz and 38KHz. The -chip must be selected for the frequency in use by the remote. +the carrier. The chip must be selected for the frequency in use by the remote. +For 38KHz devices a receiver chip such as the Vishay TSOP4838 or the +[adafruit one](https://www.adafruit.com/products/157) is required. This +demodulates the 38KHz IR pulses and passes the demodulated pulse train to the +microcontroller. The tested chip returns a 0 level on carrier detect, but the +driver design should ensure operation regardless of sense. + +The pin used to connect the decoder chip to the target is arbitrary but the +test programs assume pin X3 on the Pyboard, pin 13 on the ESP8266 and pin 23 on +ESP32. The transmitter requires a Pyboard 1.x (not Lite) or a Pyboard D. Output is via an IR LED which will normally need a transistor to provide sufficient current. +Typically these need 50-100mA of drive to achieve reasonable range and data +integrity. A suitable LED is [this one](https://www.adafruit.com/product/387). -# Decoder for IR Remote Controls using the NEC protocol +The transmitter test script assumes pin X1 for IR output. It can be changed, +but it must support Timer 2 channel 1. Pins for pushbutton inputs are +arbitrary: X3 and X4 are used. -This protocol is widely used. An example remote is [this one](https://www.adafruit.com/products/389). -To interface the device a receiver chip such as the Vishay TSOP4838 or the -[adafruit one](https://www.adafruit.com/products/157) is required. This -demodulates the 38KHz IR pulses and passes the demodulated pulse train to the -microcontroller. +# 3. Installation -The driver and test programs run on the Pyboard and ESP8266. +On import, demos print an explanation of how to run them. -# Files +## 3.1 Receiver - 1. `aremote.py` The device driver. - 2. `art.py` A test program to characterise a remote. - 3. `art1.py` Control an onboard LED using a remote. The data and addresss - values need changing to match your characterised remote. +Copy the following files to the target filesystem: + 1. `ir_rx.py` The receiver device driver. + 2. `ir_rx_test.py` Demo of a receiver. -# Dependencies +There are no dependencies. -The driver requires the `uasyncio` library and the file `asyn.py` from this -repository. +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. When the +demo runs, the REPL prompt reappears: this is because it sets up an ISR context +and returns. Press `ctrl-d` to cancel it. A real application would run code +after initialising reception so this behaviour would not occur. -# Usage +## 3.2 Transmitter -The pin used to connect the decoder chip to the target is arbitrary but the -test programs assume pin X3 on the Pyboard and pin 13 on the ESP8266. +Copy the following files to the Pyboard filesystem: + 1. `ir_tx.py` The transmitter device driver. + 2. `ir_tx_test.py` Demo of a 2-button remote controller. + +The device driver has no dependencies. The test program requires `uasyncio` +from the official library and `aswitch.py` from +[this repo](https://github.com/peterhinch/micropython-async). -The driver is event driven. Pressing a button on the remote causes a user -defined callback to be run. The NEC protocol returns a data value and an -address. These are passed to the callback as the first two arguments (further -user defined arguments may be supplied). The address is normally constant for a -given remote, with the data corresponding to the button. Applications should -check the address to ensure that they only respond to the correct remote. +# 4. Receiver -Data values are 8 bit. Addresses may be 8 or 16 bit depending on whether the -remote uses extended addressing. +This implements a class for each supported protocol, namely `NEC_IR`, `RC5_IR` +and `RC6_M0`. Applications should instantiate the appropriate class with a +callback. The callback will run whenever an IR pulsetrain is received. -If a button is held down a repeat code is sent. In this event the driver -returns a data value of `REPEAT` and the address associated with the last -valid data block. +Constructor: +`NEC_IR` args: `pin`, `callback`, `extended=True`, `*args` +`RC5_IR` and `RC6_M0`: args `pin`, `callback`, `*args` +Args: + 1. `pin` is a `machine.Pin` instance configured as an input, connected to the + IR decoder chip. + 2. `callback` is the user supplied callback (see below). + 3. `extended` is an NEC specific boolean. Remotes using the NEC protocol can + send 8 or 16 bit addresses. If `True` 16 bit addresses are assumed - an 8 bit + address will be correctly received. Set `False` to enable extra error checking + for remotes that return an 8 bit address. + 4. `*args` Any further args will be passed to the callback. -To characterise a remote run `art.py` and note the data value for each button -which is to be used. If the address is less than 256, extended addressing is -not in use. +The callback takes the following args: + 1. `data` Integer value fom the remote. A negative value indicates an error + except for the value of -1 which signifies an NEC repeat code (see below). + 2. `addr` Address from the remote + 3. `ctrl` 0 in the case of NEC. Philips protocols toggle this bit on repeat + button presses. If the button is held down the bit is not toggled. The + transmitter demo implements this behaviour. + 4. Any args passed to the constructor. -# Reliability +Class variable: + 1. `verbose=False` If `True` emits debug output. + +# 4.1 Errors IR reception is inevitably subject to errors, notably if the remote is operated near the limit of its range, if it is not pointed at the receiver or if its batteries are low. So applications must check for, and usually ignore, errors. -These are flagged by data values < `REPEAT`. +These are flagged by data values < `REPEAT` (-1). On the ESP8266 there is a further source of errors. This results from the large and variable interrupt latency of the device which can exceed the pulse @@ -82,34 +126,134 @@ duration. This causes pulses to be missed. This tendency is slightly reduced by running the chip at 160MHz. In general applications should provide user feedback of correct reception. -Users tend to press the key again if no acknowledgement is received. +Users tend to press the key again if the expected action is absent. + +Data values passed to the callback are normally positive. Negative values +indicate a repeat code or an error. + +`REPEAT` A repeat code was received. + +Any data value < `REPEAT` denotes an error. In general applications do not +need to decode these, but they may be of use in debugging. For completeness +they are listed below. + +`BADSTART` A short (<= 4ms) start pulse was received. May occur due to IR +interference, e.g. from fluorescent lights. The TSOP4838 is prone to producing +200µs pulses on occasion, especially when using the ESP8266. +`BADBLOCK` A normal data block: too few edges received. Occurs on the ESP8266 +owing to high interrupt latency. +`BADREP` A repeat block: an incorrect number of edges were received. +`OVERRUN` A normal data block: too many edges received. +`BADDATA` Data did not match check byte. +`BADADDR` Where `extended` is `False` the 8-bit address is checked +against the check byte. This code is returned on failure. + +# 4.2 Receiver platforms + +The NEC protocol has been tested against Pyboard, ESP8266 and ESP32 targets. +The Philips protocols - especially RC-6 - have tighter timing constraints. I +have not yet tested these, but I anticipate problems. + +# 4.3 Principle of operation -# The NEC_IR class +Protocol classes inherit from the abstract base class `IR_RX`. This uses a pin +interrupt to store in an array the start and end times of pulses (in μs). +Arrival of the first pulse triggers a software timer which runs for the +expected duration of an IR block (`tblock`). When it times out its callback +(`.decode`) decodes the data and calls the user callback. The use of a software +timer ensures that `.decode` and the user callback can allocate. -The constructor takes the following positional arguments. +The size of the array and the duration of the timer are protocol dependent and +are set by the subclasses. The `.decode` method is provided in the subclass. - 1. `pin` A `Pin` instance for the decoder chip. - 2. `cb` The user callback function. - 3. `extended` Set `False` to enable extra error checking if the remote - returns an 8 bit address. - 4. Further arguments, if provided, are passed to the callback. +CPU times used by `.decode` (not including the user callback) were measured on +a Pyboard D SF2W at stock frequency. They were NEC 1ms for normal data, 100μs +for a repeat code. Philips codes: RC-5 900μs, RC-6 mode 0 5.5ms. -The callback receives the following positional arguments: +# 5 Transmitter - 1. The data value returned from the remote. - 2. The address value returned from the remote. - 3. Any further arguments provided to the `NEC_IR` constructor. +This is specific to Pyboard D and Pyboard 1.x (not Lite). -Negative data values are used to signal repeat codes and transmission errors. +It implements a class for each supported protocol, namely `NEC`, `RC5` and +`RC6_M0`. The application instantiates the appropriate class and calls the +`transmit` method to send data. -The test program `art1.py` provides an example of a minimal application. +Constructor +All constructors take the following args: + 1. `pin` An initialised `pyb.Pin` instance supporting Timer 2 channel 1: `X1` + is employed by the test script. Must be connected to the IR diode as described + below. + 2. `freq=default` The carrier frequency in Hz. The default for NEC is 38000, + and for Philips is 36000. + 3. `verbose=False` If `True` emits debug output. -# How it works +Method: + 1. `transmit(addr, data, toggle=0)` Integer args. `addr` and `data` are + normally 8-bit values and `toggle` is 0 or 1. + In the case of NEC, if an address < 256 is passed, normal mode is assumed and + the complementary value is appended. 16-bit values are transmitted as extended + addresses. + In the case of NEC the `toggle` value is ignored. For Philips protocols it + should be toggled each time a button is pressed, and retained if the button is + held down. The test program illustrates a way to do this. -The NEC protocol is described in these references. +The `transmit` method is synchronous with rapid return. Actual transmission +occurs as a background process, 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. + +# 5.1 Wiring + +I use the following circuit which delivers just under 40mA to the diode. R2 may +be reduced for higher current. +![Image](images/circuit.png) + +This alternative delivers a constant current of about 53mA if a higher voltage +than 5V is available. R4 determines the current value and may be reduced to +increase power. +![Image](images/circuit2.png) + +The transistor type is not critical. + +These circuits assume 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.py` should be changed to 100. + +# 5.2 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 +`transmit` method calls a `tx` method in the subclass which populates this +array. On completion `transmit` appends a special `STOP` value and initiates +physical transmission which occurs in an interrupt context. + +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 Philips ones to 30%. + +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. + +The `IR.append` enables times to be added to the array, keeping track of the +notional carrier on/off state for biphase generation. The `IR.add` method +facilitates lengthening a pulse as required in the biphase sequences used in +Philips protocols. + +# 6. References + +The NEC protocol is described in these references: [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) +The Philips protocols may be found in these refs: +[RC5](https://en.wikipedia.org/wiki/RC-5) +[RC6](https://www.sbprojects.net/knowledge/ir/rc6.php) + +# Appendix 1 NEC Protocol description + A normal burst comprises exactly 68 edges, the exception being a repeat code which has 4. An incorrect number of edges is treated as an error. All bursts begin with a 9ms pulse. In a normal code this is followed by a 4.5ms space; a @@ -135,25 +279,3 @@ any asyncio latency when setting its delay period. The algorithm promotes interrupt handler speed over RAM use: the 276 bytes used for the data array could be reduced to 69 bytes by computing and saving deltas in the interrupt service routine. - -# Error returns - -Data values passed to the callback are normally positive. Negative values -indicate a repeat code or an error. - -`REPEAT` A repeat code was received. - -Any data value < `REPEAT` denotes an error. In general applications do not -need to decode these, but they may be of use in debugging. For completeness -they are listed below. - -`BADSTART` A short (<= 4ms) start pulse was received. May occur due to IR -interference, e.g. from fluorescent lights. The TSOP4838 is prone to producing -200µs pulses on occasion, especially when using the ESP8266. -`BADBLOCK` A normal data block: too few edges received. Occurs on the ESP8266 -owing to high interrupt latency. -`BADREP` A repeat block: an incorrect number of edges were received. -`OVERRUN` A normal data block: too many edges received. -`BADDATA` Data did not match check byte. -`BADADDR` Where `extended` is `False` the 8-bit address is checked -against the check byte. This code is returned on failure. diff --git a/images/circuit.png b/images/circuit.png new file mode 100644 index 0000000000000000000000000000000000000000..132f939162fea45f177ac14a556b9427225afd4e GIT binary patch literal 27357 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 literal 0 HcmV?d00001 diff --git a/images/circuit2.png b/images/circuit2.png new file mode 100644 index 0000000000000000000000000000000000000000..de65dc6b4b3ad73a81273f1bedce0995ce01d70d GIT binary patch literal 25218 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

2W(_H0Ic#QB)10A%@S7O#2)s7Sd|Fe0B? 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 literal 0 HcmV?d00001 diff --git a/ir_rx.py b/ir_rx.py index 896d9cd..6c484bc 100644 --- a/ir_rx.py +++ b/ir_rx.py @@ -40,6 +40,7 @@ BADADDR = -7 # a block start and a repeat code start (~108ms depending on protocol) class IR_RX(): + verbose = False def __init__(self, pin, nedges, tblock, callback, *args): # Optional args for callback self._nedges = nedges self._tblock = tblock @@ -55,7 +56,7 @@ class IR_RX(): 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 + self.cb = self.decode # Pin interrupt. Save time of each edge for later decode. @@ -69,60 +70,57 @@ class IR_RX(): self.edge += 1 class NEC_IR(IR_RX): - def __init__(self, pin, callback, extended, *args): + def __init__(self, pin, callback, extended=True, *args): # Block lasts <= 80ms and has 68 edges tblock = 80 if extended else 73 # Allow for some tx tolerance (?) super().__init__(pin, 68, tblock, callback, *args) self._extended = extended self._addr = 0 - def _decode(self, _): - overrun = self.edge > 68 - val = OVERRUN if overrun else BADSTART - if not overrun: + def decode(self, _): + try: + if self.edge > 68: + raise RuntimeError(OVERRUN) width = ticks_diff(self._times[1], self._times[0]) - if width > 4000: # 9ms leading mark for all valid data - width = ticks_diff(self._times[2], self._times[1]) - if width > 3000: # 4.5ms space for normal data - if self.edge < 68: - # Haven't received the correct number of edges - val = BADBLOCK - else: - # Time spaces only (marks are always 562.5µs) - # Space is 1.6875ms (1) or 562.5µs (0) - # Skip last bit which is always 1 - val = 0 - for edge in range(3, 68 - 2, 2): - val >>= 1 - if ticks_diff(self._times[edge + 1], self._times[edge]) > 1120: - val |= 0x80000000 - elif width > 1700: # 2.5ms space for a repeat code. Should have exactly 4 edges. - val = REPEAT if self.edge == 4 else BADREP - addr = 0 - if val >= 0: # validate. Byte layout of val ~cmd cmd ~addr addr - addr = val & 0xff - cmd = (val >> 16) & 0xff - if addr == ((val >> 8) ^ 0xff) & 0xff: # 8 bit address OK - val = cmd if cmd == (val >> 24) ^ 0xff else BADDATA - self._addr = addr + if width < 4000: # 9ms leading mark for all valid data + raise RuntimeError(BADSTART) + width = ticks_diff(self._times[2], self._times[1]) + if width > 3000: # 4.5ms space for normal data + if self.edge < 68: # Haven't received the correct number of edges + raise RuntimeError(BADBLOCK) + # Time spaces only (marks are always 562.5µs) + # Space is 1.6875ms (1) or 562.5µs (0) + # Skip last bit which is always 1 + val = 0 + for edge in range(3, 68 - 2, 2): + val >>= 1 + if ticks_diff(self._times[edge + 1], self._times[edge]) > 1120: + val |= 0x80000000 + elif width > 1700: # 2.5ms space for a repeat code. Should have exactly 4 edges. + raise RuntimeError(REPEAT if self.edge == 4 else BADREP) # Treat REPEAT as error. else: + raise RuntimeError(BADSTART) + addr = val & 0xff # 8 bit addr + cmd = (val >> 16) & 0xff + if cmd != (val >> 24) ^ 0xff: + raise RuntimeError(BADDATA) + if addr != ((val >> 8) ^ 0xff) & 0xff: # 8 bit addr doesn't match check + if not self._extended: + raise RuntimeError(BADADDR) addr |= val & 0xff00 # pass assumed 16 bit address to callback - if self._extended: - val = cmd if cmd == (val >> 24) ^ 0xff else BADDATA - self._addr = addr - else: - val = BADADDR - if val == REPEAT: - addr = self._addr # Last valid addresss + self._addr = addr + except RuntimeError as e: + cmd = e.args[0] + addr = self._addr if cmd == REPEAT else 0 # REPEAT uses last address self.edge = 0 # Set up for new data burst and run user callback - self.callback(val, addr, *self.args) + self.callback(cmd, addr, 0, *self.args) class RC5_IR(IR_RX): def __init__(self, pin, callback, *args): # Block lasts <= 30ms and has <= 28 edges super().__init__(pin, 28, 30, callback, *args) - def _decode(self, _): + def decode(self, _): try: nedges = self.edge # No. of edges detected if not 14 <= nedges <= 28: @@ -138,7 +136,7 @@ class RC5_IR(IR_RX): bits <<= 1 bits |= bit bit ^= 1 - #print(bin(bits)) # Matches inverted scope waveform + self.verbose and print(bin(bits)) # Matches inverted scope waveform # Decode Manchester code x = 30 while not bits >> x: @@ -174,60 +172,63 @@ class RC6_M0(IR_RX): # Block lasts 23ms nominal and has <=44 edges super().__init__(pin, 44, 30, callback, *args) - def _decode(self, _): + def decode(self, _): try: nedges = self.edge # No. of edges detected if not 22 <= nedges <= 44: raise RuntimeError(OVERRUN if nedges > 28 else BADSTART) for x, lims in enumerate(self.hdr): width = ticks_diff(self._times[x + 1], self._times[x]) - #print('x = {}, width = {}, lims = {}'.format(x, width, lims)) if not (lims[0] < width < lims[1]): - #print('Bad start', x, width, lims) + self.verbose and print('Bad start', x, width, lims) raise RuntimeError(BADSTART) x += 1 width = ticks_diff(self._times[x + 1], self._times[x]) - # Long bit is 889μs (0) or 1333μs (1) - ctrl = width > 1111 # If 1333, ctrl == True and carrier is off - start = x + 2 if ctrl else x + 3 # Skip 2nd long bit - - # Regenerate bitstream - bits = 1 # MSB is a dummy 1 to mark start of bitstream - bit = int(ctrl) - for x in range(start, nedges - 1): + # 2nd bit of last 0 is 444μs (0) or 1333μs (1) + if not 222 < width < 1555: + self.verbose and print('Bad block 1 Width', width, 'x', x) + raise RuntimeError(BADBLOCK) + short = width < 889 + v = int(not short) + bit = v + bits = 1 # Bits decoded + x += 1 + int(short) + width = ticks_diff(self._times[x + 1], self._times[x]) + if not 222 < width < 1555: + self.verbose and print('Bad block 2 Width', width, 'x', x) + raise RuntimeError(BADBLOCK) + short = width < 1111 + if not short: + bit ^= 1 + x += 1 + int(short) # If it's short, we know width of next + v <<= 1 + v |= bit # MSB of result + bits += 1 + # Decode bitstream + while bits < 17: + # -1 convert count to index, -1 because we look ahead + if x > nedges - 2: + raise RuntimeError(BADBLOCK) + # width is 444/889 nominal width = ticks_diff(self._times[x + 1], self._times[x]) - if not 222 < width < 1333: - #print('Width', width, 'x', x) + if not 222 < width < 1111: + self.verbose and print('Bad block 3 Width', width, 'x', x) raise RuntimeError(BADBLOCK) - for _ in range(1 if width < 666 else 2): - bits <<= 1 - bits |= bit - bit ^= 1 - print('36-bit format {:036b} x={} nedges={}'.format(bits, x, nedges)) - - # Decode Manchester code. Bitstream varies in length: find MS 1. - x = 36 - while not bits >> x: - x -= 1 - # Now points to dummy 1 - x -= 2 # Point to MS biphase pair - m0 = 1 << x - m1 = m0 << 1 # MSB of pair - v = 0 # 16 bit bitstream - for _ in range(16): + short = width < 666 + if not short: + bit ^= 1 v <<= 1 - b0 = (bits & m0) > 0 - b1 = (bits & m1) > 0 - print(int(b1), int(b0)) - if b0 == b1: - raise RuntimeError(BADBLOCK) - v |= b1 - m0 >>= 2 - m1 >>= 2 - # Split into fields (val, addr) + v |= bit + bits += 1 + x += 1 + int(short) + + if self.verbose: + ss = '20-bit format {:020b} x={} nedges={} bits={}' + print(ss.format(v, x, nedges, bits)) + val = v & 0xff addr = (v >> 8) & 0xff - + ctrl = (v >> 16) & 1 except RuntimeError as e: val, addr, ctrl = e.args[0], 0, 0 self.edge = 0 # Set up for new data burst and run user callback diff --git a/ir_rx_test.py b/ir_rx_test.py index 401c077..82f97dd 100644 --- a/ir_rx_test.py +++ b/ir_rx_test.py @@ -46,11 +46,12 @@ print(s) def test(proto=0): if proto == 0: - ir = NEC_IR(p, cb, True, 0) # Extended mode, dummy ctrl arg for callback + ir = NEC_IR(p, cb) # Extended mode elif proto == 5: ir = RC5_IR(p, cb) elif proto == 6: ir = RC6_M0(p, cb) + ir.verbose = True # A real application would do something here... #while True: #time.sleep(5) diff --git a/ir_tx.py b/ir_tx.py index 0697d9a..a1cc9e9 100644 --- a/ir_tx.py +++ b/ir_tx.py @@ -6,7 +6,6 @@ # Copyright (c) 2020 Peter Hinch from pyb import Pin, Timer -from time import sleep_us, sleep from micropython import const from array import array import micropython @@ -27,18 +26,16 @@ _T2_RC6 = const(889) # IR abstract base class. Array holds periods in μs between toggling 36/38KHz # carrier on or off. Physical transmission occurs in an ISR context controlled -# by timer 2 and timer 5. -# Operation is in two phases: .transmit populates .arr with times in μs (via -# subclass), then initiates physical transmission. +# by timer 2 and timer 5. See README.md for details of operation. class IR: def __init__(self, pin, freq, asize, duty, verbose): tim = Timer(2, freq=freq) # Timer 2/pin produces 36/38KHz carrier self._ch = tim.channel(1, Timer.PWM, pin=pin) self._ch.pulse_width_percent(_SPACE) # Turn off IR LED - self._duty = duty + self._duty = duty if not _SPACE else (100 - duty) self._tim = Timer(5) # Timer 5 controls carrier on/off times - self._tcb = self._cb + self._tcb = self.cb # Pre-allocate self.verbose = verbose self.arr = array('H', 0 for _ in range(asize)) # on/off times (μs) self.carrier = False # Notional carrier state while encoding biphase @@ -51,9 +48,9 @@ class IR: self.tx(addr, data, toggle) self.append(_STOP) self.aptr = 0 # Reset pointer - self._cb(self._tim) # Initiate physical transmission. + self.cb(self._tim) # Initiate physical transmission. - def _cb(self, t): # T5 callback, generate a carrier mark or space + def cb(self, t): # T5 callback, generate a carrier mark or space t.deinit() p = self.aptr v = self.arr[p] @@ -88,18 +85,20 @@ class NEC(IR): self.append(9000, 4500) if addr < 256: # Short address: append complement addr |= ((addr ^ 0xff) << 8) - for x in range(16): + for _ in range(16): self._bit(addr & 1) addr >>= 1 data |= ((data ^ 0xff) << 8) - for x in range(16): + for _ in range(16): self._bit(data & 1) data >>= 1 - self.append(_TBURST,) + self.append(_TBURST) def repeat(self): self.aptr = 0 - self.append(9000, 2250, _TBURST) + self.append(9000, 2250, _TBURST, _STOP) + self.aptr = 0 # Reset pointer + self.cb(self._tim) # Initiate physical transmission. # Philips RC5 protocol diff --git a/ir_tx_test.py b/ir_tx_test.py index 0220c7b..08230a3 100644 --- a/ir_tx_test.py +++ b/ir_tx_test.py @@ -46,8 +46,8 @@ class Rbutton: async def main(proto): # Test uses a 38KHz carrier. Some Philips systems use 36KHz. # If button is held down normal behaviour is to retransmit - # but most NEC models send a RPEAT code - rep_code = False # Don't care for RC-X. NEC protocol only. + # but most NEC models send a REPEAT code + rep_code = False # Rbutton constructor requires False for RC-X. NEC protocol only. pin = Pin('X1') if not proto: irb = NEC(pin) # Default NEC freq == 38KHz @@ -56,11 +56,11 @@ async def main(proto): elif proto == 5: irb = RC5(pin, 38000) # My decoder chip is 38KHz elif proto == 6: - irb = RC6_M0(pin, 38000, True) # Verbose + irb = RC6_M0(pin, 38000) b = [] # Rbutton instances b.append(Rbutton(irb, Pin('X3', Pin.IN, Pin.PULL_UP), 0x1, 0x7, rep_code)) - b.append(Rbutton(irb, Pin('X4', Pin.IN, Pin.PULL_UP), 0xfa, 0xb, rep_code)) + b.append(Rbutton(irb, Pin('X4', Pin.IN, Pin.PULL_UP), 0x10, 0xb, rep_code)) led = LED(1) while True: await asyncio.sleep_ms(500) # Obligatory flashing LED. -- 2.47.3