blob: 7f5df2faff9ba224a080fc1da5443ebb0817998d [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
Robert Smallshire48141682016-03-22 21:07:36 +010039 def is_closing(self):
40 """Return True if the transport is closing or closed."""
41 return self._closing
42
Chris Liechti88e45ee2016-01-19 22:18:34 +010043 def close(self, exc=None):
Chris Liechtid07a9e72015-08-22 02:58:47 +020044 if self._closing:
45 return
46 self._closing = True
47 self._loop.remove_reader(self.serial.fd)
48 self.serial.close()
Chris Liechti88e45ee2016-01-19 22:18:34 +010049 self._loop.call_soon(self._protocol.connection_lost, exc)
Chris Liechtid07a9e72015-08-22 02:58:47 +020050
51 def _read_ready(self):
Chris Liechti88e45ee2016-01-19 22:18:34 +010052 try:
53 data = self.serial.read(1024)
54 except serial.SerialException as e:
55 self.close(exc=e)
56 else:
57 if data:
58 self._protocol.data_received(data)
Chris Liechtid07a9e72015-08-22 02:58:47 +020059
60 def write(self, data):
Chris Liechti88e45ee2016-01-19 22:18:34 +010061 try:
62 self.serial.write(data)
63 except serial.SerialException as e:
64 self.close(exc=e)
Chris Liechtid07a9e72015-08-22 02:58:47 +020065
66 def can_write_eof(self):
67 return False
68
69 def pause_reading(self):
70 if self._closing:
71 raise RuntimeError('Cannot pause_reading() when closing')
72 if self._paused:
73 raise RuntimeError('Already paused')
74 self._paused = True
75 self._loop.remove_reader(self._sock_fd)
76 if self._loop.get_debug():
Chris Liechtifc011102015-11-18 00:38:19 +010077 logging.debug("%r pauses reading", self)
Chris Liechtid07a9e72015-08-22 02:58:47 +020078
79 def resume_reading(self):
80 if not self._paused:
81 raise RuntimeError('Not paused')
82 self._paused = False
83 if self._closing:
84 return
85 self._loop.add_reader(self._sock_fd, self._read_ready)
86 if self._loop.get_debug():
Chris Liechtifc011102015-11-18 00:38:19 +010087 logging.debug("%r resumes reading", self)
Chris Liechtid07a9e72015-08-22 02:58:47 +020088
89 #~ def set_write_buffer_limits(self, high=None, low=None):
90 #~ def get_write_buffer_size(self):
91 #~ def writelines(self, list_of_data):
92 #~ def write_eof(self):
93 #~ def abort(self):
94
Chris Liechti033f17c2015-08-30 21:28:04 +020095
Chris Liechtid07a9e72015-08-22 02:58:47 +020096@asyncio.coroutine
97def create_serial_connection(loop, protocol_factory, *args, **kwargs):
98 ser = serial.Serial(*args, **kwargs)
99 protocol = protocol_factory()
100 transport = SerialTransport(loop, protocol, ser)
101 return (transport, protocol)
102
103# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
104# test
105if __name__ == '__main__':
106 class Output(asyncio.Protocol):
107 def connection_made(self, transport):
108 self.transport = transport
109 print('port opened', transport)
Chris Liechti0cd7d072015-09-04 23:04:53 +0200110 transport.serial.rts = False
Chris Liechtid07a9e72015-08-22 02:58:47 +0200111 transport.write(b'hello world\n')
112
113 def data_received(self, data):
114 print('data received', repr(data))
115 self.transport.close()
116
117 def connection_lost(self, exc):
118 print('port closed')
119 asyncio.get_event_loop().stop()
120
121 loop = asyncio.get_event_loop()
122 coro = create_serial_connection(loop, Output, '/dev/ttyUSB0', baudrate=115200)
123 loop.run_until_complete(coro)
124 loop.run_forever()
125 loop.close()