blob: 476959ae010a14ee57e3d86aa69f8601f060519b [file] [log] [blame]
Chris Liechtid07a9e72015-08-22 02:58:47 +02001#!/usr/bin/env python3
2#
3# Python Serial Port Extension for Win32, Linux, BSD, Jython
4# module for serial IO for POSIX compatible systems, like Linux
5# see __init__.py
6#
7# (C) 2015 Chris Liechti <cliechti@gmx.net>
8#
9# SPDX-License-Identifier: BSD-3-Clause
10"""\
11Support asyncio with serial ports. EXPERIMENTAL
12
13Posix platforms only, Python 3.4+ only.
14
15Windows event loops can not wait for serial ports with the current
16implementation. It should be possible to get that working though.
17"""
18import asyncio
19import serial
20
21class SerialTransport(asyncio.Transport):
22 def __init__(self, loop, protocol, serial_instance):
23 self._loop = loop
24 self._protocol = protocol
25 self.serial = serial_instance
26 self._closing = False
27 self._paused = False
28 # XXX how to support url handlers too
29 self.serial.timeout = 0
30 self.serial.nonblocking()
31 loop.call_soon(protocol.connection_made, self)
32 # only start reading when connection_made() has been called
33 loop.call_soon(loop.add_reader, self.serial.fd, self._read_ready)
34
35 def __repr__(self):
36 return '{self.__class__.__name__}({self._loop}, {self._protocol}, {self.serial})'.format(self=self)
37
38 def close(self):
39 if self._closing:
40 return
41 self._closing = True
42 self._loop.remove_reader(self.serial.fd)
43 self.serial.close()
44 self._loop.call_soon(self._protocol.connection_lost, None)
45
46 def _read_ready(self):
47 data = self.serial.read(1024)
48 if data:
49 self._protocol.data_received(data)
50
51 def write(self, data):
52 self.serial.write(data)
53
54 def can_write_eof(self):
55 return False
56
57 def pause_reading(self):
58 if self._closing:
59 raise RuntimeError('Cannot pause_reading() when closing')
60 if self._paused:
61 raise RuntimeError('Already paused')
62 self._paused = True
63 self._loop.remove_reader(self._sock_fd)
64 if self._loop.get_debug():
65 logger.debug("%r pauses reading", self)
66
67 def resume_reading(self):
68 if not self._paused:
69 raise RuntimeError('Not paused')
70 self._paused = False
71 if self._closing:
72 return
73 self._loop.add_reader(self._sock_fd, self._read_ready)
74 if self._loop.get_debug():
75 logger.debug("%r resumes reading", self)
76
77 #~ def set_write_buffer_limits(self, high=None, low=None):
78 #~ def get_write_buffer_size(self):
79 #~ def writelines(self, list_of_data):
80 #~ def write_eof(self):
81 #~ def abort(self):
82
83@asyncio.coroutine
84def create_serial_connection(loop, protocol_factory, *args, **kwargs):
85 ser = serial.Serial(*args, **kwargs)
86 protocol = protocol_factory()
87 transport = SerialTransport(loop, protocol, ser)
88 return (transport, protocol)
89
90# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
91# test
92if __name__ == '__main__':
93 class Output(asyncio.Protocol):
94 def connection_made(self, transport):
95 self.transport = transport
96 print('port opened', transport)
97 transport.serial.setRTS(0)
98 transport.write(b'hello world\n')
99
100 def data_received(self, data):
101 print('data received', repr(data))
102 self.transport.close()
103
104 def connection_lost(self, exc):
105 print('port closed')
106 asyncio.get_event_loop().stop()
107
108 loop = asyncio.get_event_loop()
109 coro = create_serial_connection(loop, Output, '/dev/ttyUSB0', baudrate=115200)
110 loop.run_until_complete(coro)
111 loop.run_forever()
112 loop.close()