Skip to content

Commit 61c24c3

Browse files
committed
auart_hd.py added. Tutorial amended to suit.
1 parent d7f8b09 commit 61c24c3

File tree

3 files changed

+134
-2
lines changed

3 files changed

+134
-2
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
This GitHub repository consists of the following parts:
44
* [A tutorial](./TUTORIAL.md) An introductory tutorial on asynchronous
5-
programming and the use of the uasyncio library is offered. This is a work in
6-
progress, not least because uasyncio is not yet complete.
5+
programming and the use of the uasyncio library is offered.
76
* [Asynchronous device drivers](./DRIVERS.md). A module providing drivers for
87
devices such as switches and pushbuttons.
98
* [Synchronisation primitives](./PRIMITIVES.md). Provides commonly used

TUTORIAL.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ and rebuilding.
123123

124124
5.1 [The IORead mechnaism](./TUTORIAL.md#51-the-ioread-mechanism)
125125

126+
5.1.1 [A UART driver example](./TUTORIAL.md#511-a-uart-driver-example)
127+
126128
5.2 [Using a coro to poll hardware](./TUTORIAL.md#52-using-a-coro-to-poll-hardware)
127129

128130
5.3 [Using IORead to poll hardware](./TUTORIAL.md#53-using-ioread-to-poll-hardware)
@@ -211,6 +213,9 @@ results by accessing Pyboard hardware.
211213
8. `aqtest.py` Demo of uasyncio `Queue` class.
212214
9. `aremote.py` Example device driver for NEC protocol IR remote control.
213215
10. `auart.py` Demo of streaming I/O via a Pyboard UART.
216+
11. `auart_hd.py` Use of the Pyboard UART to communicate with a device using a
217+
half-duplex protocol. Suits devices such as those using the 'AT' modem command
218+
set.
214219

215220
**Test Programs**
216221

@@ -1033,6 +1038,28 @@ The mechanism works because the device driver (written in C) implements the
10331038
following methods: `ioctl`, `read`, `write`, `readline` and `close`. See
10341039
section 5.3 for further discussion.
10351040

1041+
### 5.1.1 A UART driver example
1042+
1043+
The program `auart_hd.py` illustrates a method of communicating with a half
1044+
duplex device such as one responding to the modem 'AT' command set. Half duplex
1045+
means that the device never sends unsolicited data: its transmissions are
1046+
always in response to a command from the master.
1047+
1048+
The device is emulated, enabling the test to be run on a Pyboard with two wire
1049+
links.
1050+
1051+
The (highly simplified) emulated device responds to any command by sending four
1052+
lines of data with a pause between each, to simulate slow processing.
1053+
1054+
The master sends a command, but does not know in advance how many lines of data
1055+
will be returned. It starts a retriggerable timer, which is retriggered each
1056+
time a line is received. When the timer times out it is assumed that the device
1057+
has completed transmission, and a list of received lines is returned.
1058+
1059+
The case of device failure is also demonstrated. This is done by omitting the
1060+
transmission before awaiting a response. After the timeout an empty list is
1061+
returned. See the code comments for more details.
1062+
10361063
###### [Contents](./TUTORIAL.md#contents)
10371064

10381065
## 5.2 Using a coro to poll hardware

auart_hd.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# auart_hd.py
2+
# Author: Peter Hinch
3+
# Copyright Peter Hinch 2018 Released under the MIT license
4+
5+
# Demo of running a half-duplex protocol to a device. The device never sends
6+
# unsolicited messages. An example is a communications device which responds
7+
# to AT commands.
8+
# The master sends a message to the device, which may respond with one or more
9+
# lines of data. The master assumes that the device has sent all its data when
10+
# a timeout has elapsed.
11+
12+
# In this test a physical device is emulated by the DEVICE class
13+
# To test link X1-X4 and X2-X3
14+
15+
from pyb import UART
16+
import uasyncio as asyncio
17+
import aswitch
18+
19+
# Dummy device waits for any incoming line and responds with 4 lines at 1 second
20+
# intervals.
21+
class DEVICE():
22+
def __init__(self, uart_no = 4):
23+
self.uart = UART(uart_no, 9600)
24+
self.loop = asyncio.get_event_loop()
25+
self.swriter = asyncio.StreamWriter(self.uart, {})
26+
self.sreader = asyncio.StreamReader(self.uart)
27+
loop = asyncio.get_event_loop()
28+
loop.create_task(self._run())
29+
30+
async def _run(self):
31+
responses = ['Line 1', 'Line 2', 'Line 3', 'Goodbye']
32+
while True:
33+
res = await self.sreader.readline()
34+
for response in responses:
35+
await self.swriter.awrite("{}\r\n".format(response))
36+
# Demo the fact that the master tolerates slow response.
37+
await asyncio.sleep_ms(300)
38+
39+
# The master's send_command() method sends a command and waits for a number of
40+
# lines from the device. The end of the process is signified by a timeout, when
41+
# a list of lines is returned. This allows line-by-line processing.
42+
# A special test mode demonstrates the behaviour with a non-responding device. If
43+
# None is passed, no commend is sent. The master waits for a response which never
44+
# arrives and returns an empty list.
45+
class MASTER():
46+
def __init__(self, uart_no = 2, timeout=4000):
47+
self.uart = UART(uart_no, 9600)
48+
self.timeout = timeout
49+
self.loop = asyncio.get_event_loop()
50+
self.swriter = asyncio.StreamWriter(self.uart, {})
51+
self.sreader = asyncio.StreamReader(self.uart)
52+
self.delay = aswitch.Delay_ms()
53+
self.response = []
54+
loop = asyncio.get_event_loop()
55+
loop.create_task(self._recv())
56+
57+
async def _recv(self):
58+
while True:
59+
res = await self.sreader.readline()
60+
self.response.append(res) # Append to list of lines
61+
self.delay.trigger(self.timeout) # Got something, retrigger timer
62+
63+
async def send_command(self, command):
64+
self.response = [] # Discard any pending messages
65+
if command is None:
66+
print('Timeout test.')
67+
else:
68+
await self.swriter.awrite("{}\r\n".format(command))
69+
print('Command sent:', command)
70+
self.delay.trigger(self.timeout) # Re-initialise timer
71+
while self.delay.running():
72+
await asyncio.sleep(1) # Wait for 4s after last msg received
73+
return self.response
74+
75+
async def test():
76+
print('This test takes 10s to complete.')
77+
for cmd in ['Run', None]:
78+
print()
79+
res = await master.send_command(cmd)
80+
# can use b''.join(res) if a single string is required.
81+
if res:
82+
print('Result is:')
83+
for line in res:
84+
print(line.decode('UTF8'), end='')
85+
else:
86+
print('Timed out waiting for result.')
87+
88+
loop = asyncio.get_event_loop()
89+
master = MASTER()
90+
device = DEVICE()
91+
loop.run_until_complete(test())
92+
93+
# Expected output
94+
# >>> import auart_hd
95+
# This test takes 10s to complete.
96+
#
97+
# Command sent: Run
98+
# Result is:
99+
# Line 1
100+
# Line 2
101+
# Line 3
102+
# Goodbye
103+
#
104+
# Timeout test.
105+
# Timed out waiting for result.
106+
# >>>

0 commit comments

Comments
 (0)