Skip to content

Commit 626d0a6

Browse files
committed
v3 Add NEC_IR. Tutorial corrections and improvements.
1 parent db6e4e3 commit 626d0a6

File tree

14 files changed

+239
-110
lines changed

14 files changed

+239
-110
lines changed

v3/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ Documented in the tutorial.
2121
Documented in the tutorial.
2222

2323
#### Asynchronous device drivers
24-
The device drivers are in the process of being ported. Currently there is a
25-
[GPS driver](./docs/GPS.md).
24+
25+
The device drivers are in the process of being ported. These currently
26+
comprise:
27+
28+
* [GPS driver](./docs/GPS.md) Includes various GPS utilities.
29+
* [HTU21D](./docs/HTU21D.md) Temperature and humidity sensor.
2630

2731
# 2 V3 Overview
2832

v3/as_demos/__init__.py

Whitespace-only changes.

v3/as_demos/aledflash.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,21 @@ async def toggle(objLED, time_ms):
1717

1818
# TEST FUNCTION
1919

20-
def test(duration):
21-
duration = int(duration)
22-
if duration > 0:
23-
print("Flash LED's for {:3d} seconds".format(duration))
20+
async def main(duration):
21+
print("Flash LED's for {} seconds".format(duration))
2422
leds = [pyb.LED(x) for x in range(1,4)] # Initialise three on board LED's
25-
for x, led in enumerate(leds): # Create a coroutine for each LED
23+
for x, led in enumerate(leds): # Create a task for each LED
2624
t = int((0.2 + x/2) * 1000)
2725
asyncio.create_task(toggle(leds[x], t))
2826
asyncio.run(killer(duration))
2927

30-
test(10)
28+
def test(duration=10):
29+
try:
30+
asyncio.run(main(duration))
31+
except KeyboardInterrupt:
32+
print('Interrupted')
33+
finally:
34+
asyncio.new_event_loop()
35+
print('as_demos.aledflash.test() to run again.')
36+
37+
test()

v3/as_demos/auart.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,13 @@ async def main():
2525
while True:
2626
await asyncio.sleep(1)
2727

28-
asyncio.run(main())
28+
def test():
29+
try:
30+
asyncio.run(main())
31+
except KeyboardInterrupt:
32+
print('Interrupted')
33+
finally:
34+
asyncio.new_event_loop()
35+
print('as_demos.auart.test() to run again.')
36+
37+
test()

v3/as_demos/auart_hd.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ async def send_command(self, command):
6868
await asyncio.sleep(1) # Wait for 4s after last msg received
6969
return self.response
7070

71-
async def test():
71+
async def main():
7272
print('This test takes 10s to complete.')
7373
master = Master()
7474
device = Device()
@@ -101,6 +101,14 @@ def printexp():
101101
print(st)
102102
print('\x1b[39m')
103103

104-
printexp()
105-
asyncio.run(test())
104+
def test():
105+
printexp()
106+
try:
107+
asyncio.run(main())
108+
except KeyboardInterrupt:
109+
print('Interrupted')
110+
finally:
111+
asyncio.new_event_loop()
112+
print('as_demos.auart_hd.test() to run again.')
106113

114+
test()

v3/as_demos/gather.py

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ async def foo(n):
1818
while True:
1919
await asyncio.sleep(1)
2020
n += 1
21-
except Exception as e: #asyncio.TimeoutError:
22-
print('foo timeout.', e)
21+
except asyncio.CancelledError:
22+
print('Trapped foo timeout.')
23+
raise
2324
return n
2425

2526
async def bar(n):
@@ -28,22 +29,72 @@ async def bar(n):
2829
while True:
2930
await asyncio.sleep(1)
3031
n += 1
31-
except Exception as e:
32-
print('bar stopped.', e)
32+
except asyncio.CancelledError: # Demo of trapping
33+
print('Trapped bar cancellation.')
34+
raise
3335
return n
3436

3537
async def do_cancel(task):
3638
await asyncio.sleep(5)
3739
print('About to cancel bar')
3840
task.cancel()
3941

40-
async def main():
42+
async def main(rex):
4143
bar_task = asyncio.create_task(bar(70)) # Note args here
4244
tasks = []
4345
tasks.append(barking(21))
4446
tasks.append(asyncio.wait_for(foo(10), 7))
4547
asyncio.create_task(do_cancel(bar_task))
46-
res = await asyncio.gather(*tasks)
48+
try:
49+
res = await asyncio.gather(*tasks, return_exceptions=rex)
50+
except asyncio.TimeoutError:
51+
print('foo timed out.')
52+
res = 'No result'
4753
print('Result: ', res)
4854

49-
asyncio.run(main())
55+
56+
exp_false = '''Test runs for 10s. Expected output:
57+
58+
Start cancellable bar()
59+
Start normal coro barking()
60+
Start timeout coro foo()
61+
About to cancel bar
62+
Trapped bar cancellation.
63+
Done barking.
64+
Trapped foo timeout.
65+
foo timed out.
66+
Result: No result
67+
68+
'''
69+
exp_true = '''Test runs for 10s. Expected output:
70+
71+
Start cancellable bar()
72+
Start normal coro barking()
73+
Start timeout coro foo()
74+
About to cancel bar
75+
Trapped bar cancellation.
76+
Done barking.
77+
Trapped foo timeout.
78+
Result: [42, TimeoutError()]
79+
80+
'''
81+
82+
def printexp(st):
83+
print('\x1b[32m')
84+
print(st)
85+
print('\x1b[39m')
86+
87+
def test(rex):
88+
st = exp_true if rex else exp_false
89+
printexp(st)
90+
try:
91+
asyncio.run(main(rex))
92+
except KeyboardInterrupt:
93+
print('Interrupted')
94+
finally:
95+
asyncio.new_event_loop()
96+
print()
97+
print('as_demos.gather.test() to run again.')
98+
print('as_demos.gather.test(True) to see effect of return_exceptions.')
99+
100+
test(rex=False)

v3/as_drivers/nec_ir/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .as_GPS import *
1+
from .aremote import *

v3/as_drivers/nec_ir/aremote.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
from sys import platform
88
import uasyncio as asyncio
9-
from asyn import Event
9+
from primitives.message import Message
1010
from micropython import const
1111
from array import array
12-
from utime import ticks_us, ticks_diff
12+
from utime import ticks_ms, ticks_us, ticks_diff
1313
if platform == 'pyboard':
1414
from pyb import Pin, ExtInt
1515
else:
@@ -41,7 +41,7 @@
4141
# Value of 73 allows for up to 35ms latency.
4242
class NEC_IR():
4343
def __init__(self, pin, callback, extended, *args): # Optional args for callback
44-
self._ev_start = Event()
44+
self._ev_start = Message()
4545
self._callback = callback
4646
self._extended = extended
4747
self._addr = 0
@@ -56,15 +56,13 @@ def __init__(self, pin, callback, extended, *args): # Optional args for callbac
5656
pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING), hard = True)
5757
self._edge = 0
5858
self._ev_start.clear()
59-
loop = asyncio.get_event_loop()
60-
loop.create_task(self._run())
59+
asyncio.create_task(self._run())
6160

6261
async def _run(self):
63-
loop = asyncio.get_event_loop()
6462
while True:
6563
await self._ev_start # Wait until data collection has started
6664
# Compensate for asyncio latency
67-
latency = ticks_diff(loop.time(), self._ev_start.value())
65+
latency = ticks_diff(ticks_ms(), self._ev_start.value())
6866
await asyncio.sleep_ms(self.block_time - latency) # Data block should have ended
6967
self._decode() # decode, clear event, prepare for new rx, call cb
7068

@@ -74,8 +72,7 @@ def _cb_pin(self, line):
7472
# On overrun ignore pulses until software timer times out
7573
if self._edge <= _EDGECOUNT: # Allow 1 extra pulse to record overrun
7674
if not self._ev_start.is_set(): # First edge received
77-
loop = asyncio.get_event_loop()
78-
self._ev_start.set(loop.time()) # asyncio latency compensation
75+
self._ev_start.set(ticks_ms()) # asyncio latency compensation
7976
self._times[self._edge] = t
8077
self._edge += 1
8178

v3/as_drivers/nec_ir/art.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Copyright Peter Hinch 2017 Released under the MIT license
66

77
# Run this to characterise a remote.
8+
# import as_drivers.nec_ir.art
89

910
from sys import platform
1011
import uasyncio as asyncio
@@ -17,7 +18,7 @@
1718
else:
1819
print('Unsupported platform', platform)
1920

20-
from aremote import *
21+
from .aremote import *
2122

2223
errors = {BADSTART : 'Invalid start pulse', BADBLOCK : 'Error: bad block',
2324
BADREP : 'Error: repeat', OVERRUN : 'Error: overrun',
@@ -33,6 +34,7 @@ def cb(data, addr):
3334

3435
def test():
3536
print('Test for IR receiver. Assumes NEC protocol.')
37+
print('ctrl-c to stop.')
3638
if platform == 'pyboard':
3739
p = Pin('X3', Pin.IN)
3840
elif platform == 'esp8266':
@@ -42,6 +44,11 @@ def test():
4244
p = Pin(23, Pin.IN)
4345
ir = NEC_IR(p, cb, True) # Assume r/c uses extended addressing
4446
loop = asyncio.get_event_loop()
45-
loop.run_forever()
47+
try:
48+
loop.run_forever()
49+
except KeyboardInterrupt:
50+
print('Interrupted')
51+
finally:
52+
asyncio.new_event_loop() # Still need ctrl-d because of interrupt vector
4653

4754
test()

v3/as_drivers/nec_ir/art1.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ def test():
5555
led(1)
5656
ir = NEC_IR(p, cb, True, led) # Assume extended address mode r/c
5757
loop = asyncio.get_event_loop()
58-
loop.run_forever()
58+
try:
59+
loop.run_forever()
60+
except KeyboardInterrupt:
61+
print('Interrupted')
62+
finally:
63+
asyncio.new_event_loop() # Still need ctrl-d because of interrupt vector
5964

6065
test()

v3/docs/NEC_IR.md

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,32 @@ microcontroller.
88

99
The driver and test programs run on the Pyboard and ESP8266.
1010

11-
# Files
11+
## An alternative solution
1212

13-
1. `aremote.py` The device driver.
14-
2. `art.py` A test program to characterise a remote.
15-
3. `art1.py` Control an onboard LED using a remote. The data and addresss
16-
values need changing to match your characterised remote.
13+
This solution provides an example of an asynchronous device driver. A more
14+
complete IR solution may be found
15+
[here](https://github.com/peterhinch/micropython_ir). This supports other
16+
protocols and IR "blasting". It does not use `uasyncio` but is nonblocking and
17+
is compatible with `uasyncio` applications.
18+
19+
# Demo scripts
20+
21+
The following prints data and address values received from a remote. These
22+
values enable you to respond to individual butons.
23+
```python
24+
import as_drivers.nec_ir.art
25+
```
26+
27+
Control an onboard LED using a remote. The data and addresss values must be
28+
changed to match your characterised remote.
29+
```python
30+
import as_drivers.nec_ir.art1
31+
```
1732

1833
# Dependencies
1934

20-
The driver requires the `uasyncio` library and the file `asyn.py` from this
21-
repository.
35+
The driver requires the `uasyncio` library and the `primitives` package from
36+
this repository.
2237

2338
# Usage
2439

@@ -131,3 +146,9 @@ owing to high interrupt latency.
131146
`BADDATA` Data did not match check byte.
132147
`BADADDR` Where `extended` is `False` the 8-bit address is checked
133148
against the check byte. This code is returned on failure.
149+
150+
# Files
151+
152+
1. `aremote.py` The device driver.
153+
2. `art.py` A test program to characterise a remote.
154+
3. `art1.py`

0 commit comments

Comments
 (0)