]> vault307.fbx.one Git - micorpython_ir.git/blob - ir_tx/__init__.py
6fe771048d3d535c921d6aec0185580b70609f0f
[micorpython_ir.git] / ir_tx / __init__.py
1 # __init__.py Nonblocking IR blaster
2 # Runs on Pyboard D or Pyboard 1.x (not Pyboard Lite) and ESP32
3
4 # Released under the MIT License (MIT). See LICENSE.
5
6 # Copyright (c) 2020 Peter Hinch
7 from sys import platform
8 ESP32 = platform == 'esp32' # Loboris not supported owing to RMT
9 if ESP32:
10 from machine import Pin, PWM
11 from esp32 import RMT
12 else:
13 from pyb import Pin, Timer # Pyboard does not support machine.PWM
14
15 from micropython import const
16 from array import array
17 import micropython
18
19 # micropython.alloc_emergency_exception_buf(100)
20
21 # ABC and Pyboard only: ESP32 ignores this value.
22 # Duty ratio in carrier off state: if driver is such that 3.3V turns the LED
23 # off, set _SPACE = 100
24 _SPACE = const(0)
25 # On ESP32 gate hardware design is led_on = rmt and carrier
26
27 # Shared by NEC
28 STOP = const(0) # End of data
29
30 # IR abstract base class. Array holds periods in μs between toggling 36/38KHz
31 # carrier on or off. Physical transmission occurs in an ISR context controlled
32 # by timer 2 and timer 5. See README.md for details of operation.
33 class IR:
34
35 def __init__(self, pin, cfreq, asize, duty, verbose):
36 if ESP32:
37 self._pwm = PWM(pin[0]) # Continuous 36/38/40KHz carrier
38 self._pwm.deinit()
39 # ESP32: 0 <= duty <= 1023
40 self._pwm.init(freq=cfreq, duty=round(duty * 10.23))
41 self._rmt = RMT(0, pin=pin[1], clock_div=80) # 1μs resolution
42 else: # Pyboard
43 tim = Timer(2, freq=cfreq) # Timer 2/pin produces 36/38/40KHz carrier
44 self._ch = tim.channel(1, Timer.PWM, pin=pin)
45 self._ch.pulse_width_percent(_SPACE) # Turn off IR LED
46 # Pyboard: 0 <= pulse_width_percent <= 100
47 self._duty = duty if not _SPACE else (100 - duty)
48 self._tim = Timer(5) # Timer 5 controls carrier on/off times
49 self._tcb = self._cb # Pre-allocate
50 self._arr = array('H', 0 for _ in range(asize)) # on/off times (μs)
51 self._mva = memoryview(self._arr)
52 # Subclass interface
53 self.verbose = verbose
54 self.carrier = False # Notional carrier state while encoding biphase
55 self.aptr = 0 # Index into array
56
57 def _cb(self, t): # T5 callback, generate a carrier mark or space
58 t.deinit()
59 p = self.aptr
60 v = self._arr[p]
61 if v == STOP:
62 self._ch.pulse_width_percent(_SPACE) # Turn off IR LED.
63 return
64 self._ch.pulse_width_percent(_SPACE if p & 1 else self._duty)
65 self._tim.init(prescaler=84, period=v, callback=self._tcb)
66 self.aptr += 1
67
68 # Public interface
69 # Before populating array, zero pointer, set notional carrier state (off).
70 def transmit(self, addr, data, toggle=0): # NEC: toggle is unused
71 self.aptr = 0 # Inital conditions for tx: index into array
72 self.carrier = False
73 self.tx(addr, data, toggle) # Subclass populates ._arr
74 self.trigger() # Initiate transmission
75
76 # Subclass interface
77 def trigger(self): # Used by NEC to initiate a repeat frame
78 if ESP32:
79 self._rmt.write_pulses(tuple(self._mva[0 : self.aptr]), start = 1)
80 else:
81 self.append(STOP)
82 self.aptr = 0 # Reset pointer
83 self._cb(self._tim) # Initiate physical transmission.
84
85 def append(self, *times): # Append one or more time peiods to ._arr
86 for t in times:
87 self._arr[self.aptr] = t
88 self.aptr += 1
89 self.carrier = not self.carrier # Keep track of carrier state
90 self.verbose and print('append', t, 'carrier', self.carrier)
91
92 def add(self, t): # Increase last time value (for biphase)
93 assert t > 0
94 self.verbose and print('add', t)
95 # .carrier unaffected
96 self._arr[self.aptr - 1] += t