appropriate class with a callback. The callback will run whenever an IR pulse
train is received.
-Constructor:
-`NEC_IR` args: `pin`, `callback`, `extended=True`, `*args`
-`SONY_IR` args: `pin`, `callback`, `bits=20`, `*args`
-`RC5_IR` and `RC6_M0`: args `pin`, `callback`, `*args`
+#### Common to all classes
-Args (all protocols):
+Constructor:
+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).
- 4. `*args` Any further args will be passed to the callback.
-
-Protocol specific args:
- 1. `extended` NEC specific `bool`. Remotes using the NEC protocol can send 8
- or 16 bit addresses. If `True` 16 bit addresses are assumed. If an 8 bit
- address is sent it will be received as a 16 bit value comprising the address
- and (in bits 8-15) its ones complement. Set `False` to enable error checking
- for remotes that return an 8 bit address: the complement will be checked and
- the address will be returned as an 8-bit value.
- 2. `bits=20` Sony specific `int`. The SIRC protocol comes in 3 variants: 12,
- 15 and 20 bits. The default will handle bitstreams from all three types of
- remote. A value matching your remote improves the timing reducing the
- likelihood of errors when handling repeats: in 20-bit mode SIRC timing when a
- button is held down is tight. A worst-case 20-bit block takes 39ms nominal,
- yet the repeat time is 45ms nominal.
- The Sony remote tested issues both 12 bit and 15 bit streams.
-
-The callback takes the following args:
- 1. `data` (`int`) Value from the remote. A negative value indicates an error
- except for the value of -1 which signifies an NEC repeat code (see below).
+ 2. `callback` is the user supplied callback.
+ 3. `*args` Any further args will be passed to the callback.
+
+The user callback takes the following args:
+ 1. `data` (`int`) Value from the remote. Normally in range 0-255. A value < 0
+ signifies an NEC repeat code.
2. `addr` (`int`) Address from the remote.
- 3. `ctrl` (`int`) The meaning of this is protocol dependent.
+ 3. `ctrl` (`int`) The meaning of this is protocol dependent:
NEC: 0
Philips: this is toggled 1/0 on repeat button presses. If the button is held
down it is not toggled. The transmitter demo implements this behaviour.
value.
4. Any args passed to the constructor.
-Class variable:
+Bound variable:
1. `verbose=False` If `True` emits debug output.
+Method:
+ 1. `error_function` Arg: a function taking a single arg. If this is specified
+ it will be called if an error occurs. The value corresponds to the error code
+ (see below).
+
+#### Properties specific to a class
+
+`NEC_IR`:
+`extended` `bool`. Remotes using the NEC protocol can send 8 or 16 bit
+addresses. If `True` 16 bit addresses are assumed. If an 8 bit address is sent
+it will be received as a 16 bit value comprising the address and (in bits 8-15)
+its ones complement. Set `False` to enable error checking for remotes that
+return an 8 bit address: the complement will be checked and the address will be
+returned as an 8-bit value. The default is `True`.
+
+`SONY_IR`:
+`bits` `int`. The SIRC protocol comes in 3 variants: 12, 15 and 20 bits. The
+default will handle bitstreams from all three types of remote. A value matching
+your remote improves the timing reducing the likelihood of errors when handling
+repeats: in 20-bit mode SIRC timing when a button is held down is tight. A
+worst-case 20-bit block takes 39ms nominal, yet the repeat time is 45ms nominal.
+The Sony remote tested issues both 12 bit and 15 bit streams. The default is
+20.
+
# 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. The user callback should check for, and usually ignore,
-errors. These are flagged by data values < `REPEAT` (-1).
+batteries are low. The user callback is not called when an error occurs.
On ESP8266 and ESP32 there is a further source of errors. This results from the
large and variable interrupt latency of the device which can exceed the pulse
In general applications should provide user feedback of correct reception.
Users tend to press the key again if the expected action is absent.
-Data values passed to the callback are zero or positive. Negative values
-indicate a repeat code or an error.
-
-`REPEAT` A repeat code was received.
-
-Any data value < `REPEAT` (-1) 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.
+In debugging a callback can be specified for reporting errors. The value passed
+to the error function are represented by constants indicating the cause of the
+error. These are as follows:
`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
`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
+`BADADDR` (`NEC_IR`) If `extended` is `False` the 8-bit address is checked
against the check byte. This code is returned on failure.
# 4.2 Receiver platforms
# a block start and a repeat code start (~108ms depending on protocol)
class IR_RX():
- verbose = False
+ _erstr = "'{}' object has no attribute '{}'"
+
def __init__(self, pin, nedges, tblock, callback, *args): # Optional args for callback
self._nedges = nedges
self._tblock = tblock
self.callback = callback
self.args = args
+ self._errf = lambda _ : None
+ self.verbose = False
self._times = array('i', (0 for _ in range(nedges + 1))) # +1 for overrun
if platform == 'pyboard':
self._times[self.edge] = t
self.edge += 1
+ @property
+ def extended(self):
+ raise AttributeError(self._erstr.format(self.__qualname__, 'extended'))
+
+ @property
+ def bits(self):
+ raise AttributeError(self._erstr.format(self.__qualname__, 'bits'))
+
+ def do_callback(self, cmd, addr, ext, thresh=0):
+ self.edge = 0
+ if cmd >= thresh:
+ self.callback(cmd, addr, ext, *self.args)
+ else:
+ self._errf(cmd)
+
+ def error_function(self, func):
+ self._errf = func
+
class NEC_IR(IR_RX):
- 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
+ def __init__(self, pin, callback, *args):
+ # Block lasts <= 80ms (extended mode) and has 68 edges
+ super().__init__(pin, 68, 80, callback, *args)
+ self._extended = True
self._addr = 0
def decode(self, _):
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(cmd, addr, 0, *self.args)
+ # Set up for new data burst and run user callback
+ self.do_callback(cmd, addr, 0, REPEAT)
+
+ @property
+ def extended(self):
+ return self._extended
+ @extended.setter
+ def extended(self, value):
+ self._extended = bool(value)
class SONY_IR(IR_RX):
- def __init__(self, pin, callback, bits=20, *args):
+ def __init__(self, pin, callback, *args):
# 20 bit block has 42 edges and lasts <= 39ms nominal. Add 4ms to time
# for tolerances except in 20 bit case where timing is tight with a
# repeat period of 45ms.
t = int(3 + bits * 1.8) + (1 if bits == 20 else 4)
super().__init__(pin, 2 + bits * 2, t, callback, *args)
self._addr = 0
- self._bits = bits
+ self._bits = 20
def decode(self, _):
try:
cmd = e.args[0]
addr = 0
val = 0
- self.edge = 0 # Set up for new data burst and run user callback
- self.callback(cmd, addr, val, *self.args)
+ self.do_callback(cmd, addr, val)
+
+ @property
+ def bits(self):
+ return self._bits
+
+ @bits.setter
+ def bits(self, value):
+ if value not in (12, 15, 20):
+ raise ValueError('bits must be 12, 15 or 20')
+ self._bits = value
class RC5_IR(IR_RX):
def __init__(self, pin, callback, *args):
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
- self.callback(val, addr, ctrl, *self.args)
+ # Set up for new data burst and run user callback
+ self.do_callback(val, addr, ctrl)
class RC6_M0(IR_RX):
# Even on Pyboard D the 444μs nominal pulses can be recorded as up to 705μs
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
- self.callback(val, addr, ctrl, *self.args)
+ # Set up for new data burst and run user callback
+ self.do_callback(val, addr, ctrl)
elif ESP32:
p = Pin(23, Pin.IN) # was 27
-errors = {BADSTART : 'Invalid start pulse', BADBLOCK : 'Error: bad block',
- BADREP : 'Error: repeat', OVERRUN : 'Error: overrun',
- BADDATA : 'Error: invalid data', BADADDR : 'Error: invalid address'}
-
+# User callback
def cb(data, addr, ctrl):
- if data == REPEAT: # NEC protocol sends repeat codes.
+ if data < 0: # NEC protocol sends repeat codes.
print('Repeat code.')
- elif data >= 0:
- print('Data {:03x} Addr {:03x} Ctrl {:01x}'.format(data, addr, ctrl))
else:
- print(errors[data]) # Application would ignore errors
+ print('Data {:03x} Addr {:03x} Ctrl {:01x}'.format(data, addr, ctrl))
+# Optionally print debug information
+def errf(data):
+ errors = {BADSTART : 'Invalid start pulse', BADBLOCK : 'Error: bad block',
+ BADREP : 'Error: repeat', OVERRUN : 'Error: overrun',
+ BADDATA : 'Error: invalid data', BADADDR : 'Error: invalid address'}
+ print(errors[data])
s = '''Test for IR receiver. Run:
from ir_rx_test import test
if proto == 0:
ir = NEC_IR(p, cb) # Extended mode
elif proto < 4:
- bits = (12, 15, 20)[proto - 1]
- ir = SONY_IR(p, cb, bits)
+ ir = SONY_IR(p, cb)
+ ir.bits = (12, 15, 20)[proto - 1]
#ir.verbose = True
elif proto == 5:
ir = RC5_IR(p, cb)
elif proto == 6:
ir = RC6_M0(p, cb)
+ ir.error_function(errf) # Show debug information
# A real application would do something here...
while True:
print('running')