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
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)
+ #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
+ # 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 = 0
- bit = 0
- for x in range(start, nedges):
- width = ticks_diff(self._times[x], self._times[x - 1])
+ bits = 1 # MSB is a dummy 1 to mark start of bitstream
+ bit = int(ctrl)
+ for x in range(start, nedges - 1):
+ width = ticks_diff(self._times[x + 1], self._times[x])
if not 222 < width < 1333:
- print('Width', width)
+ #print('Width', width, 'x', x)
raise RuntimeError(BADBLOCK)
for _ in range(1 if width < 666 else 2):
bits <<= 1
bits |= bit
bit ^= 1
- print(bin(bits), len(bin(bits)) - 2)
+ print('36-bit format {:036b} x={} nedges={}'.format(bits, x, nedges))
- # Decode Manchester code
- x = 32
+ # Decode Manchester code. Bitstream varies in length: find MS 1.
+ x = 36
while not bits >> x:
x -= 1
- m0 = 1 << (x - 1)
- m1 = 1 << x # MSB of pair
+ # 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):
v <<= 1
b0 = (bits & m0) > 0
b1 = (bits & m1) > 0
- #print(int(b1), int(b0))
+ print(int(b1), int(b0))
if b0 == b1:
raise RuntimeError(BADBLOCK)
- v |= b0
+ v |= b1
m0 >>= 2
m1 >>= 2
# Split into fields (val, addr)
_T2_RC6 = const(889)
# IR abstract base class. Array holds periods in μs between toggling 36/38KHz
-# carrier on or off.
-# Subclass is responsible for populating .arr and initiating transmission.
-# Operation is in two phases: .transmit populates .arr with times in μs, then
-# calls .start to initiate physical transmission.
+# 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.
class IR:
- def __init__(self, pin, freq, asize, duty):
- tim = Timer(2, freq=freq)
+ 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)
- self.duty = duty
- self.arr = array('H', 0 for _ in range(asize))
- self._tim = Timer(5)
+ self._ch.pulse_width_percent(_SPACE) # Turn off IR LED
+ self._duty = duty
+ self._tim = Timer(5) # Timer 5 controls carrier on/off times
self._tcb = self._cb
- self.pretrans()
+ 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
+ self.aptr = 0 # Index into array
# Before populating array, zero pointer, set notional carrier state (off).
- def pretrans(self):
- self.aptr = 0 # Index into array
+ def transmit(self, addr, data, toggle=0): # NEC: toggle is unused
+ self.aptr = 0 # Inital conditions for tx: index into array
self.carrier = False
+ self.tx(addr, data, toggle)
+ self.append(_STOP)
+ self.aptr = 0 # Reset pointer
+ self._cb(self._tim) # Initiate physical transmission.
- def start(self):
- self.aptr = 0 # Reset pointer and initiate TX.
- self._cb(self._tim)
-
- def _cb(self, t):
+ def _cb(self, t): # T5 callback, generate a carrier mark or space
t.deinit()
p = self.aptr
v = self.arr[p]
if v == _STOP:
self._ch.pulse_width_percent(_SPACE) # Turn off IR LED.
return
- self._ch.pulse_width_percent(_SPACE if p & 1 else self.duty)
+ self._ch.pulse_width_percent(_SPACE if p & 1 else self._duty)
self._tim.init(prescaler=84, period=v, callback=self._tcb)
self.aptr += 1
self.arr[self.aptr] = t
self.aptr += 1
self.carrier = not self.carrier # Keep track of carrier state
- print('append', t, 'carrier', self.carrier)
+ self.verbose and print('append', t, 'carrier', self.carrier)
def add(self, t): # Increase last time value
- print('add', t)
+ self.verbose and print('add', t)
self.arr[self.aptr - 1] += t # Carrier unaffected
# NEC protocol
class NEC(IR):
- def __init__(self, pin, freq=38000): # NEC specifies 38KHz
- super().__init__(pin, freq, 68, 50)
+ def __init__(self, pin, freq=38000, verbose=False): # NEC specifies 38KHz
+ super().__init__(pin, freq, 68, 50, verbose)
def _bit(self, b):
self.append(_TBURST, _T_ONE if b else _TBURST)
- def transmit(self, addr, data, _=0): # Ignore toggle if passed
- self.pretrans() # Set initial conditions
+ def tx(self, addr, data, _): # Ignore toggle
self.append(9000, 4500)
if addr < 256: # Short address: append complement
addr |= ((addr ^ 0xff) << 8)
for x in range(16):
self._bit(data & 1)
data >>= 1
- self.append(_TBURST, _STOP)
- self.start()
+ self.append(_TBURST,)
def repeat(self):
self.aptr = 0
- self.append(9000, 2250, _TBURST, _STOP)
+ self.append(9000, 2250, _TBURST)
# Philips RC5 protocol
class RC5(IR):
- def __init__(self, pin, freq=36000):
- super().__init__(pin, freq, 28, 30)
+ def __init__(self, pin, freq=36000, verbose=False):
+ super().__init__(pin, freq, 28, 30, verbose)
- def transmit(self, addr, data, toggle):
- self.pretrans() # Set initial conditions
+ def tx(self, addr, data, toggle):
d = (data & 0x3f) | ((addr & 0x1f) << 6) | ((data & 0x40) << 6) | ((toggle & 1) << 11)
- print(bin(d))
+ self.verbose and print(bin(d))
mask = 0x2000
while mask:
if mask == 0x2000:
else:
self.append(_T_RC5, _T_RC5)
mask >>= 1
- self.append(_STOP)
- self.start()
# Philips RC6 mode 0 protocol
class RC6_M0(IR):
- def __init__(self, pin, freq=36000):
- super().__init__(pin, freq, 44, 30)
+ def __init__(self, pin, freq=36000, verbose=False):
+ super().__init__(pin, freq, 44, 30, verbose)
- def transmit(self, addr, data, toggle):
- self.pretrans() # Set initial conditions
+ def tx(self, addr, data, toggle):
# leader, 1, 0, 0, 0
self.append(2666, _T2_RC6, _T_RC6, _T2_RC6, _T_RC6, _T_RC6, _T_RC6, _T_RC6, _T_RC6)
# Append a single bit of twice duration
self.append(_T2_RC6, _T2_RC6)
d = (data & 0xff) | ((addr & 0xff) << 8)
mask = 0x8000
- print('toggle', toggle, self.carrier, bool(d & mask))
+ self.verbose and print('toggle', toggle, self.carrier, bool(d & mask))
while mask:
bit = bool(d & mask)
if bit ^ self.carrier:
self.add(_T_RC6)
self.append(_T_RC6)
mask >>= 1
- self.append(_STOP)
- self.start()