]> vault307.fbx.one Git - micorpython_ir.git/commitdiff
Prior to package.
authorPeter Hinch <peter@hinch.me.uk>
Fri, 6 Mar 2020 07:41:27 +0000 (07:41 +0000)
committerPeter Hinch <peter@hinch.me.uk>
Fri, 6 Mar 2020 07:41:27 +0000 (07:41 +0000)
README.md
ir_rx.py
ir_rx_test.py

index b35bd208d8aebb2875a3c65e454543e26ed84db1..c107a784dd7d79f2eee252c0f0998889c9def5d5 100644 (file)
--- a/README.md
+++ b/README.md
@@ -96,37 +96,20 @@ This implements a class for each supported protocol, namely `NEC_IR`,
 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.  
@@ -134,15 +117,38 @@ The callback takes the following args:
  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
@@ -152,14 +158,9 @@ On ESP8266 some improvment may be achieved by running the chip at 160MHz.
 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
@@ -169,7 +170,7 @@ 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
+`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
index 69d2da983fedb4bfcf617f55af0b6232e75e2de4..210d61767a79ef24ed0eb762de770e1b44849acd 100644 (file)
--- a/ir_rx.py
+++ b/ir_rx.py
@@ -40,12 +40,15 @@ BADADDR = -7
 # 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':
@@ -70,12 +73,29 @@ class IR_RX():
             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, _):
@@ -113,19 +133,26 @@ class NEC_IR(IR_RX):
         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:
@@ -165,8 +192,17 @@ class SONY_IR(IR_RX):
             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):
@@ -213,8 +249,8 @@ class RC5_IR(IR_RX):
 
         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
@@ -284,5 +320,5 @@ class RC6_M0(IR_RX):
             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)
index b4f1cf95511a3636e749ce7949c0f0baee304d3e..a436082b411779f7f87c12693ac6bec533065ba1 100644 (file)
@@ -22,18 +22,19 @@ elif platform == 'esp8266':
 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
@@ -52,13 +53,14 @@ def test(proto=0):
     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')