]>
vault307.fbx.one Git - micorpython_ir.git/blob - ir_rx.py
210d61767a79ef24ed0eb762de770e1b44849acd
1 # ir_rx.py Decoder for IR remote control using synchronous code
2 # Supports RC-5 RC-6 mode 0 and NEC protocols.
3 # For a remote using NEC see https://www.adafruit.com/products/389
6 # Copyright Peter Hinch 2020 Released under the MIT license
8 from sys
import platform
9 from micropython
import const
10 from machine
import Timer
11 from array
import array
12 from utime
import ticks_us
, ticks_diff
14 if platform
== 'pyboard':
15 from pyb
import Pin
, ExtInt
17 from machine
import Pin
19 ESP32
= platform
== 'esp32' or platform
== 'esp32_LoBo'
22 # from micropython import alloc_emergency_exception_buf
23 # alloc_emergency_exception_buf(100)
25 # Result codes (accessible to application)
37 # On 1st edge start a block timer. While the timer is running, record the time
38 # of each edge. When the timer times out decode the data. Duration must exceed
39 # the worst case block transmission time, but be less than the interval between
40 # a block start and a repeat code start (~108ms depending on protocol)
43 _erstr
= "'{}' object has no attribute '{}'"
45 def __init__(self
, pin
, nedges
, tblock
, callback
, *args
): # Optional args for callback
48 self
.callback
= callback
50 self
._errf
= lambda _
: None
53 self
._times
= array('i', (0 for _
in range(nedges
+ 1))) # +1 for overrun
54 if platform
== 'pyboard':
55 ei
= ExtInt(pin
, ExtInt
.IRQ_RISING_FALLING
, Pin
.PULL_NONE
, self
._cb
_pin
)
57 ei
= pin
.irq(handler
= self
._cb
_pin
, trigger
= (Pin
.IRQ_FALLING | Pin
.IRQ_RISING
))
59 ei
= pin
.irq(handler
= self
._cb
_pin
, trigger
= (Pin
.IRQ_FALLING | Pin
.IRQ_RISING
), hard
= True)
60 self
._eint
= ei
# Keep reference??
62 self
.tim
= Timer(-1) # Sofware timer
66 # Pin interrupt. Save time of each edge for later decode.
67 def _cb_pin(self
, line
):
69 # On overrun ignore pulses until software timer times out
70 if self
.edge
<= self
._nedges
: # Allow 1 extra pulse to record overrun
71 if not self
.edge
: # First edge received
72 self
.tim
.init(period
=self
._tblock
, mode
=Timer
.ONE_SHOT
, callback
=self
.cb
)
73 self
._times
[self
.edge
] = t
78 raise AttributeError(self
._erstr
.format(self
.__qualname
__, 'extended'))
82 raise AttributeError(self
._erstr
.format(self
.__qualname
__, 'bits'))
84 def do_callback(self
, cmd
, addr
, ext
, thresh
=0):
87 self
.callback(cmd
, addr
, ext
, *self
.args
)
91 def error_function(self
, func
):
95 def __init__(self
, pin
, callback
, *args
):
96 # Block lasts <= 80ms (extended mode) and has 68 edges
97 super().__init
__(pin
, 68, 80, callback
, *args
)
104 raise RuntimeError(OVERRUN
)
105 width
= ticks_diff(self
._times
[1], self
._times
[0])
106 if width
< 4000: # 9ms leading mark for all valid data
107 raise RuntimeError(BADSTART
)
108 width
= ticks_diff(self
._times
[2], self
._times
[1])
109 if width
> 3000: # 4.5ms space for normal data
110 if self
.edge
< 68: # Haven't received the correct number of edges
111 raise RuntimeError(BADBLOCK
)
112 # Time spaces only (marks are always 562.5µs)
113 # Space is 1.6875ms (1) or 562.5µs (0)
114 # Skip last bit which is always 1
116 for edge
in range(3, 68 - 2, 2):
118 if ticks_diff(self
._times
[edge
+ 1], self
._times
[edge
]) > 1120:
120 elif width
> 1700: # 2.5ms space for a repeat code. Should have exactly 4 edges.
121 raise RuntimeError(REPEAT
if self
.edge
== 4 else BADREP
) # Treat REPEAT as error.
123 raise RuntimeError(BADSTART
)
124 addr
= val
& 0xff # 8 bit addr
125 cmd
= (val
>> 16) & 0xff
126 if cmd
!= (val
>> 24) ^
0xff:
127 raise RuntimeError(BADDATA
)
128 if addr
!= ((val
>> 8) ^
0xff) & 0xff: # 8 bit addr doesn't match check
129 if not self
._extended
:
130 raise RuntimeError(BADADDR
)
131 addr |
= val
& 0xff00 # pass assumed 16 bit address to callback
133 except RuntimeError as e
:
135 addr
= self
._addr
if cmd
== REPEAT
else 0 # REPEAT uses last address
136 # Set up for new data burst and run user callback
137 self
.do_callback(cmd
, addr
, 0, REPEAT
)
141 return self
._extended
144 def extended(self
, value
):
145 self
._extended
= bool(value
)
147 class SONY_IR(IR_RX
):
148 def __init__(self
, pin
, callback
, *args
):
149 # 20 bit block has 42 edges and lasts <= 39ms nominal. Add 4ms to time
150 # for tolerances except in 20 bit case where timing is tight with a
151 # repeat period of 45ms.
152 t
= int(3 + bits
* 1.8) + (1 if bits
== 20 else 4)
153 super().__init
__(pin
, 2 + bits
* 2, t
, callback
, *args
)
159 nedges
= self
.edge
# No. of edges detected
160 self
.verbose
and print('nedges', nedges
)
162 raise RuntimeError(OVERRUN
)
163 bits
= (nedges
- 2) // 2
164 if nedges
not in (26, 32, 42) or bits
> self
._bits
:
165 raise RuntimeError(BADBLOCK
)
166 self
.verbose
and print('SIRC {}bit'.format(bits
))
167 width
= ticks_diff(self
._times
[1], self
._times
[0])
168 if not 1800 < width
< 3000: # 2.4ms leading mark for all valid data
169 raise RuntimeError(BADSTART
)
170 width
= ticks_diff(self
._times
[2], self
._times
[1])
171 if not 350 < width
< 1000: # 600μs space
172 raise RuntimeError(BADSTART
)
174 val
= 0 # Data received, LSB 1st
177 while x
< nedges
- 2:
178 if ticks_diff(self
._times
[x
+ 1], self
._times
[x
]) > 900:
183 cmd
= val
& 0x7f # 7 bit command
186 addr
= val
& 0xff # 5 or 8 bit addr
189 addr
= val
& 0x1f # 5 bit addr
190 val
>>= 5 # 8 bit extended
191 except RuntimeError as e
:
195 self
.do_callback(cmd
, addr
, val
)
202 def bits(self
, value
):
203 if value
not in (12, 15, 20):
204 raise ValueError('bits must be 12, 15 or 20')
208 def __init__(self
, pin
, callback
, *args
):
209 # Block lasts <= 30ms and has <= 28 edges
210 super().__init
__(pin
, 28, 30, callback
, *args
)
214 nedges
= self
.edge
# No. of edges detected
215 if not 14 <= nedges
<= 28:
216 raise RuntimeError(OVERRUN
if nedges
> 28 else BADSTART
)
217 # Regenerate bitstream
220 for x
in range(1, nedges
):
221 width
= ticks_diff(self
._times
[x
], self
._times
[x
- 1])
222 if not 500 < width
< 2000:
223 raise RuntimeError(BADBLOCK
)
224 for _
in range(1 if width
< 1334 else 2):
228 self
.verbose
and print(bin(bits
)) # Matches inverted scope waveform
229 # Decode Manchester code
233 m0
= 1 << x
# Mask MS two bits (always 01)
235 v
= 0 # 14 bit bitstream
241 raise RuntimeError(BADBLOCK
)
245 # Split into fields (val, addr, ctrl)
246 val
= (v
& 0x3f) |
(0x40 if ((v
>> 12) & 1) else 0)
247 addr
= (v
>> 6) & 0x1f
250 except RuntimeError as e
:
251 val
, addr
, ctrl
= e
.args
[0], 0, 0
252 # Set up for new data burst and run user callback
253 self
.do_callback(val
, addr
, ctrl
)
256 # Even on Pyboard D the 444μs nominal pulses can be recorded as up to 705μs
257 # Scope shows 360-520 μs (-84μs +76μs relative to nominal)
258 # Header nominal 2666, 889, 444, 889, 444, 444, 444, 444 carrier ON at end
259 hdr
= ((1800, 4000), (593, 1333), (222, 750), (593, 1333), (222, 750), (222, 750), (222, 750), (222, 750))
260 def __init__(self
, pin
, callback
, *args
):
261 # Block lasts 23ms nominal and has <=44 edges
262 super().__init
__(pin
, 44, 30, callback
, *args
)
266 nedges
= self
.edge
# No. of edges detected
267 if not 22 <= nedges
<= 44:
268 raise RuntimeError(OVERRUN
if nedges
> 28 else BADSTART
)
269 for x
, lims
in enumerate(self
.hdr
):
270 width
= ticks_diff(self
._times
[x
+ 1], self
._times
[x
])
271 if not (lims
[0] < width
< lims
[1]):
272 self
.verbose
and print('Bad start', x
, width
, lims
)
273 raise RuntimeError(BADSTART
)
275 width
= ticks_diff(self
._times
[x
+ 1], self
._times
[x
])
276 # 2nd bit of last 0 is 444μs (0) or 1333μs (1)
277 if not 222 < width
< 1555:
278 self
.verbose
and print('Bad block 1 Width', width
, 'x', x
)
279 raise RuntimeError(BADBLOCK
)
283 bits
= 1 # Bits decoded
285 width
= ticks_diff(self
._times
[x
+ 1], self
._times
[x
])
286 if not 222 < width
< 1555:
287 self
.verbose
and print('Bad block 2 Width', width
, 'x', x
)
288 raise RuntimeError(BADBLOCK
)
292 x
+= 1 + int(short
) # If it's short, we know width of next
294 v |
= bit
# MSB of result
298 # -1 convert count to index, -1 because we look ahead
300 raise RuntimeError(BADBLOCK
)
301 # width is 444/889 nominal
302 width
= ticks_diff(self
._times
[x
+ 1], self
._times
[x
])
303 if not 222 < width
< 1111:
304 self
.verbose
and print('Bad block 3 Width', width
, 'x', x
)
305 raise RuntimeError(BADBLOCK
)
315 ss
= '20-bit format {:020b} x={} nedges={} bits={}'
316 print(ss
.format(v
, x
, nedges
, bits
))
319 addr
= (v
>> 8) & 0xff
321 except RuntimeError as e
:
322 val
, addr
, ctrl
= e
.args
[0], 0, 0
323 # Set up for new data burst and run user callback
324 self
.do_callback(val
, addr
, ctrl
)