X-Git-Url: https://vault307.fbx.one/gitweb/micorpython_ir.git/blobdiff_plain/d2a0c49467a3a7832d5d350ea00a50c59359722b..952ea7385e6eb4f389fd1d1f8089977c0d58eecf:/ir_rx.py diff --git a/ir_rx.py b/ir_rx.py index 6f20b2b..d73016a 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,109 @@ 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(cmd, addr, 0, *self.args) + + +class SONY_IR(IR_RX): + def __init__(self, pin, callback, bits=20, *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 + + def decode(self, _): + try: + nedges = self.edge # No. of edges detected + print(nedges) + if nedges > 42: + raise RuntimeError(OVERRUN) + bits = (nedges - 2) // 2 + if nedges not in (26, 32, 42) or bits > self._bits: + raise RuntimeError(BADBLOCK) + self.verbose and print('SIRC {}bit'.format(bits)) + width = ticks_diff(self._times[1], self._times[0]) + if not 1800 < width < 3000: # 2.4ms leading mark for all valid data + raise RuntimeError(BADSTART) + width = ticks_diff(self._times[2], self._times[1]) + if not 350 < width < 1000: # 600μs space + raise RuntimeError(BADSTART) + + val = 0 # Data received, LSB 1st + x = 2 + bit = 1 + while x < nedges - 2: + if ticks_diff(self._times[x + 1], self._times[x]) > 900: + val |= bit + bit <<= 1 + x += 2 + + cmd = val & 0x7f # 7 bit command + val >>= 7 + if nedges < 42: + addr = val & 0xff # 5 or 8 bit addr + val = 0 + else: + addr = val & 0x1f # 5 bit addr + val >>= 5 # 8 bit extended + except RuntimeError as e: + cmd = e.args[0] + addr = 0 + val = 0 self.edge = 0 # Set up for new data burst and run user callback - self.callback(val, addr, *self.args) + self.callback(cmd, addr, val, *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 +188,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: @@ -166,13 +216,15 @@ class RC5_IR(IR_RX): self.callback(val, addr, ctrl, *self.args) class RC6_M0(IR_RX): - # Even on Pyboard D these 444us nominal pulses can be recorded as up to 705us + # Even on Pyboard D the 444μs nominal pulses can be recorded as up to 705μs + # Scope shows 360-520 μs (-84μs +76μs relative to nominal) + # Header nominal 2666, 889, 444, 889, 444, 444, 444, 444 carrier ON at end hdr = ((1800, 4000), (593, 1333), (222, 750), (593, 1333), (222, 750), (222, 750), (222, 750), (222, 750)) def __init__(self, pin, callback, *args): # 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: @@ -180,48 +232,55 @@ class RC6_M0(IR_RX): for x, lims in enumerate(self.hdr): width = ticks_diff(self._times[x + 1], self._times[x]) 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]) - ctrl = width > 889 # Long bit - start = x + 2 # Skip 2nd long bit - - # Regenerate bitstream - bits = 0 - bit = 0 - for x in range(start, nedges): - width = ticks_diff(self._times[x], self._times[x - 1]) - if not 222 < width < 1333: - print('Width', width) - raise RuntimeError(BADBLOCK) - for _ in range(1 if width < 666 else 2): - bits <<= 1 - bits |= bit + # 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 - print(bin(bits), len(bin(bits)) - 2) - - # Decode Manchester code - x = 32 - while not bits >> x: - x -= 1 - m0 = 1 << (x - 1) - m1 = 1 << x # MSB of pair - v = 0 # 16 bit bitstream - for _ in range(16): - v <<= 1 - b0 = (bits & m0) > 0 - b1 = (bits & m1) > 0 - #print(int(b1), int(b0)) - if b0 == b1: + 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) - v |= b0 - m0 >>= 2 - m1 >>= 2 - # Split into fields (val, addr) + # width is 444/889 nominal + width = ticks_diff(self._times[x + 1], self._times[x]) + if not 222 < width < 1111: + self.verbose and print('Bad block 3 Width', width, 'x', x) + raise RuntimeError(BADBLOCK) + short = width < 666 + if not short: + bit ^= 1 + v <<= 1 + 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