]> vault307.fbx.one Git - micorpython_ir.git/blob - ir_tx/__init__.py
ef52e84a3cccc8126a56d8968dc7dd9aa68216ba
[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' or platform == 'esp32_LoBo'
9 if ESP32:
10 from machine import Pin, Timer, PWM, freq
11 else:
12 from pyb import Pin, Timer # Pyboard does not support machine.PWM
13
14 from micropython import const
15 from array import array
16 import micropython
17
18 # micropython.alloc_emergency_exception_buf(100)
19
20 # ABC only
21 _SPACE = const(0)
22 # If the wiring is such that 3.3V turns the LED off, set _SPACE as follows
23 # On Pyboard 100, on ESP32 1023
24 # Shared by NEC
25 STOP = const(0) # End of data
26
27 # IR abstract base class. Array holds periods in μs between toggling 36/38KHz
28 # carrier on or off. Physical transmission occurs in an ISR context controlled
29 # by timer 2 and timer 5. See README.md for details of operation.
30 class IR:
31
32 def __init__(self, pin, cfreq, asize, duty, verbose):
33 if ESP32:
34 freq(240000000)
35 self._pwm = PWM(pin) # Produces 36/38/40KHz carrier
36 self._pwm.deinit()
37 self._pwm.init(freq=cfreq, duty=_SPACE)
38 # ESP32: 0 <= duty <= 1023
39 self._duty = round((duty if not _SPACE else (100 - duty)) * 10.23)
40 self._tim = Timer(-1) # Controls carrier on/off times
41 self._off = self.esp_off # Turn IR LED off
42 self._onoff = self.esp_onoff # Set IR LED state and refresh timer
43 else: # Pyboard
44 tim = Timer(2, freq=cfreq) # Timer 2/pin produces 36/38/40KHz carrier
45 self._ch = tim.channel(1, Timer.PWM, pin=pin)
46 self._ch.pulse_width_percent(_SPACE) # Turn off IR LED
47 # Pyboard: 0 <= pulse_width_percent <= 100
48 self._duty = duty if not _SPACE else (100 - duty)
49 self._tim = Timer(5) # Timer 5 controls carrier on/off times
50 self._off = self.pb_off
51 self._onoff = self.pb_onoff
52 self._tcb = self.cb # Pre-allocate
53 self.verbose = verbose
54 self.arr = array('H', 0 for _ in range(asize)) # on/off times (μs)
55 self.carrier = False # Notional carrier state while encoding biphase
56 self.aptr = 0 # Index into array
57
58 # Before populating array, zero pointer, set notional carrier state (off).
59 def transmit(self, addr, data, toggle=0): # NEC: toggle is unused
60 self.aptr = 0 # Inital conditions for tx: index into array
61 self.carrier = False
62 self.tx(addr, data, toggle)
63 self.append(STOP)
64 self.aptr = 0 # Reset pointer
65 self.cb(self._tim) # Initiate physical transmission.
66
67 # Turn IR LED off (pyboard and ESP32 variants)
68 def pb_off(self):
69 self._ch.pulse_width_percent(_SPACE)
70
71 def esp_off(self):
72 self._pwm.duty(_SPACE)
73
74 # Turn IR LED on or off and re-initialise timer (pyboard and ESP32 variants)
75 @micropython.native
76 def pb_onoff(self, p, v):
77 self._ch.pulse_width_percent(_SPACE if p & 1 else self._duty)
78 self._tim.init(prescaler=84, period=v, callback=self._tcb)
79
80 @micropython.native
81 def esp_onoff(self, p, v):
82 self._pwm.duty(_SPACE if p & 1 else self._duty)
83 self._tim.init(mode=Timer.ONE_SHOT, freq=v, callback=self.cb)
84
85 def cb(self, t): # T5 callback, generate a carrier mark or space
86 t.deinit()
87 p = self.aptr
88 v = self.arr[p]
89 if v == STOP:
90 self._off() # Turn off IR LED.
91 return
92 self._onoff(p, v)
93 self.aptr += 1
94
95 def append(self, *times): # Append one or more time peiods to .arr
96 for t in times:
97 if ESP32 and t:
98 t -= 350 # ESP32 sluggishness
99 t = round(1_000_000 / t) # Store in Hz
100 self.arr[self.aptr] = t
101 self.aptr += 1
102 self.carrier = not self.carrier # Keep track of carrier state
103 self.verbose and print('append', t, 'carrier', self.carrier)
104
105 def add(self, t): # Increase last time value
106 assert t > 0
107 self.verbose and print('add', t)
108 # .carrier unaffected
109 if ESP32:
110 t -= 350
111 self.arr[self.aptr - 1] = round((self.arr[self.aptr - 1] / 1_000_000 + t) / 1_000_000)
112 else:
113 self.arr[self.aptr - 1] += t