blob: 95cb88014f7a92deb356e8009ba687119f240e40 [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):
Robert Smallshire29187262016-03-22 22:15:39 +010024 super().__init__()
Chris Liechtid07a9e72015-08-22 02:58:47 +020025 self._loop = loop
26 self._protocol = protocol
27 self.serial = serial_instance
28 self._closing = False
29 self._paused = False
30 # XXX how to support url handlers too
31 self.serial.timeout = 0
32 self.serial.nonblocking()
33 loop.call_soon(protocol.connection_made, self)
34 # only start reading when connection_made() has been called
35 loop.call_soon(loop.add_reader, self.serial.fd, self._read_ready)
36
37 def __repr__(self):
38 return '{self.__class__.__name__}({self._loop}, {self._protocol}, {self.serial})'.format(self=self)
39
Chris Liechti88e45ee2016-01-19 22:18:34 +010040 def close(self, exc=None):
Chris Liechtid07a9e72015-08-22 02:58:47 +020041 if self._closing:
42 return
43 self._closing = True
44 self._loop.remove_reader(self.serial.fd)
45 self.serial.close()
Chris Liechti88e45ee2016-01-19 22:18:34 +010046 self._loop.call_soon(self._protocol.connection_lost, exc)
Chris Liechtid07a9e72015-08-22 02:58:47 +020047
48 def _read_ready(self):
Chris Liechti88e45ee2016-01-19 22:18:34 +010049 try:
50 data = self.serial.read(1024)
51 except serial.SerialException as e:
52 self.close(exc=e)
53 else:
54 if data:
55 self._protocol.data_received(data)
Chris Liechtid07a9e72015-08-22 02:58:47 +020056
57 def write(self, data):
Chris Liechti88e45ee2016-01-19 22:18:34 +010058 try:
59 self.serial.write(data)
60 except serial.SerialException as e:
61 self.close(exc=e)
Chris Liechtid07a9e72015-08-22 02:58:47 +020062
63 def can_write_eof(self):
64 return False
65
66 def pause_reading(self):
67 if self._closing:
68 raise RuntimeError('Cannot pause_reading() when closing')
69 if self._paused:
70 raise RuntimeError('Already paused')
71 self._paused = True
72 self._loop.remove_reader(self._sock_fd)
73 if self._loop.get_debug():
Chris Liechtifc011102015-11-18 00:38:19 +010074 logging.debug("%r pauses reading", self)
Chris Liechtid07a9e72015-08-22 02:58:47 +020075
76 def resume_reading(self):
77 if not self._paused:
78 raise RuntimeError('Not paused')
79 self._paused = False
80 if self._closing:
81 return
82 self._loop.add_reader(self._sock_fd, self._read_ready)
83 if self._loop.get_debug():
Chris Liechtifc011102015-11-18 00:38:19 +010084 logging.debug("%r resumes reading", self)
Chris Liechtid07a9e72015-08-22 02:58:47 +020085
86 #~ def set_write_buffer_limits(self, high=None, low=None):
87 #~ def get_write_buffer_size(self):
88 #~ def writelines(self, list_of_data):
89 #~ def write_eof(self):
90 #~ def abort(self):
91
Chris Liechti033f17c2015-08-30 21:28:04 +020092
Chris Liechtid07a9e72015-08-22 02:58:47 +020093@asyncio.coroutine
94def create_serial_connection(loop, protocol_factory, *args, **kwargs):
95 ser = serial.Serial(*args, **kwargs)
96 protocol = protocol_factory()
97 transport = SerialTransport(loop, protocol, ser)
98 return (transport, protocol)
99
100# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
101# test
102if __name__ == '__main__':
103 class Output(asyncio.Protocol):
104 def connection_made(self, transport):
105 self.transport = transport
106 print('port opened', transport)
Chris Liechti0cd7d072015-09-04 23:04:53 +0200107 transport.serial.rts = False
Chris Liechtid07a9e72015-08-22 02:58:47 +0200108 transport.write(b'hello world\n')
109
110 def data_received(self, data):
111 print('data received', repr(data))
112 self.transport.close()
113
114 def connection_lost(self, exc):
115 print('port closed')
116 asyncio.get_event_loop().stop()
117
118 loop = asyncio.get_event_loop()
119 coro = create_serial_connection(loop, Output, '/dev/ttyUSB0', baudrate=115200)
120 loop.run_until_complete(coro)
121 loop.run_forever()
122 loop.close()