blob: a4e8357e8f8d0945d264c170f2707c84d801b53c [file] [log] [blame]
Chris Liechtid07a9e72015-08-22 02:58:47 +02001#!/usr/bin/env python3
2#
Chris Liechti3e02f702015-12-16 23:06:04 +01003# Experimental implementation of asyncio support.
Chris Liechtid07a9e72015-08-22 02:58:47 +02004#
Chris Liechti3e02f702015-12-16 23:06:04 +01005# This file is part of pySerial. https://github.com/pyserial/pyserial
Chris Liechtid07a9e72015-08-22 02:58:47 +02006# (C) 2015 Chris Liechti <cliechti@gmx.net>
7#
8# SPDX-License-Identifier: BSD-3-Clause
9"""\
10Support asyncio with serial ports. EXPERIMENTAL
11
12Posix platforms only, Python 3.4+ only.
13
14Windows event loops can not wait for serial ports with the current
15implementation. It should be possible to get that working though.
16"""
17import asyncio
18import serial
Chris Liechtifc011102015-11-18 00:38:19 +010019import logging
Chris Liechti033f17c2015-08-30 21:28:04 +020020
Chris Liechtid07a9e72015-08-22 02:58:47 +020021
22class SerialTransport(asyncio.Transport):
23 def __init__(self, loop, protocol, serial_instance):
24 self._loop = loop
25 self._protocol = protocol
26 self.serial = serial_instance
27 self._closing = False
28 self._paused = False
29 # XXX how to support url handlers too
30 self.serial.timeout = 0
31 self.serial.nonblocking()
32 loop.call_soon(protocol.connection_made, self)
33 # only start reading when connection_made() has been called
34 loop.call_soon(loop.add_reader, self.serial.fd, self._read_ready)
35
36 def __repr__(self):
37 return '{self.__class__.__name__}({self._loop}, {self._protocol}, {self.serial})'.format(self=self)
38
Chris Liechti88e45ee2016-01-19 22:18:34 +010039 def close(self, exc=None):
Chris Liechtid07a9e72015-08-22 02:58:47 +020040 if self._closing:
41 return
42 self._closing = True
43 self._loop.remove_reader(self.serial.fd)
44 self.serial.close()
Chris Liechti88e45ee2016-01-19 22:18:34 +010045 self._loop.call_soon(self._protocol.connection_lost, exc)
Chris Liechtid07a9e72015-08-22 02:58:47 +020046
47 def _read_ready(self):
Chris Liechti88e45ee2016-01-19 22:18:34 +010048 try:
49 data = self.serial.read(1024)
50 except serial.SerialException as e:
51 self.close(exc=e)
52 else:
53 if data:
54 self._protocol.data_received(data)
Chris Liechtid07a9e72015-08-22 02:58:47 +020055
56 def write(self, data):
Chris Liechti88e45ee2016-01-19 22:18:34 +010057 try:
58 self.serial.write(data)
59 except serial.SerialException as e:
60 self.close(exc=e)
Chris Liechtid07a9e72015-08-22 02:58:47 +020061
62 def can_write_eof(self):
63 return False
64
65 def pause_reading(self):
66 if self._closing:
67 raise RuntimeError('Cannot pause_reading() when closing')
68 if self._paused:
69 raise RuntimeError('Already paused')
70 self._paused = True
71 self._loop.remove_reader(self._sock_fd)
72 if self._loop.get_debug():
Chris Liechtifc011102015-11-18 00:38:19 +010073 logging.debug("%r pauses reading", self)
Chris Liechtid07a9e72015-08-22 02:58:47 +020074
75 def resume_reading(self):
76 if not self._paused:
77 raise RuntimeError('Not paused')
78 self._paused = False
79 if self._closing:
80 return
81 self._loop.add_reader(self._sock_fd, self._read_ready)
82 if self._loop.get_debug():
Chris Liechtifc011102015-11-18 00:38:19 +010083 logging.debug("%r resumes reading", self)
Chris Liechtid07a9e72015-08-22 02:58:47 +020084
85 #~ def set_write_buffer_limits(self, high=None, low=None):
86 #~ def get_write_buffer_size(self):
87 #~ def writelines(self, list_of_data):
88 #~ def write_eof(self):
89 #~ def abort(self):
90
Chris Liechti033f17c2015-08-30 21:28:04 +020091
Chris Liechtid07a9e72015-08-22 02:58:47 +020092@asyncio.coroutine
93def create_serial_connection(loop, protocol_factory, *args, **kwargs):
94 ser = serial.Serial(*args, **kwargs)
95 protocol = protocol_factory()
96 transport = SerialTransport(loop, protocol, ser)
97 return (transport, protocol)
98
99# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
100# test
101if __name__ == '__main__':
102 class Output(asyncio.Protocol):
103 def connection_made(self, transport):
104 self.transport = transport
105 print('port opened', transport)
Chris Liechti0cd7d072015-09-04 23:04:53 +0200106 transport.serial.rts = False
Chris Liechtid07a9e72015-08-22 02:58:47 +0200107 transport.write(b'hello world\n')
108
109 def data_received(self, data):
110 print('data received', repr(data))
111 self.transport.close()
112
113 def connection_lost(self, exc):
114 print('port closed')
115 asyncio.get_event_loop().stop()
116
117 loop = asyncio.get_event_loop()
118 coro = create_serial_connection(loop, Output, '/dev/ttyUSB0', baudrate=115200)
119 loop.run_until_complete(coro)
120 loop.run_forever()
121 loop.close()