blob: fb379fd366ffd78cf33a967c449bd95b71d48e7c [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
Chris Liechtifc011102015-11-18 00:38:19 +010020import logging
Chris Liechti033f17c2015-08-30 21:28:04 +020021
Chris Liechtid07a9e72015-08-22 02:58:47 +020022
23class SerialTransport(asyncio.Transport):
24 def __init__(self, loop, protocol, serial_instance):
25 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
40 def close(self):
41 if self._closing:
42 return
43 self._closing = True
44 self._loop.remove_reader(self.serial.fd)
45 self.serial.close()
46 self._loop.call_soon(self._protocol.connection_lost, None)
47
48 def _read_ready(self):
49 data = self.serial.read(1024)
50 if data:
51 self._protocol.data_received(data)
52
53 def write(self, data):
54 self.serial.write(data)
55
56 def can_write_eof(self):
57 return False
58
59 def pause_reading(self):
60 if self._closing:
61 raise RuntimeError('Cannot pause_reading() when closing')
62 if self._paused:
63 raise RuntimeError('Already paused')
64 self._paused = True
65 self._loop.remove_reader(self._sock_fd)
66 if self._loop.get_debug():
Chris Liechtifc011102015-11-18 00:38:19 +010067 logging.debug("%r pauses reading", self)
Chris Liechtid07a9e72015-08-22 02:58:47 +020068
69 def resume_reading(self):
70 if not self._paused:
71 raise RuntimeError('Not paused')
72 self._paused = False
73 if self._closing:
74 return
75 self._loop.add_reader(self._sock_fd, self._read_ready)
76 if self._loop.get_debug():
Chris Liechtifc011102015-11-18 00:38:19 +010077 logging.debug("%r resumes reading", self)
Chris Liechtid07a9e72015-08-22 02:58:47 +020078
79 #~ def set_write_buffer_limits(self, high=None, low=None):
80 #~ def get_write_buffer_size(self):
81 #~ def writelines(self, list_of_data):
82 #~ def write_eof(self):
83 #~ def abort(self):
84
Chris Liechti033f17c2015-08-30 21:28:04 +020085
Chris Liechtid07a9e72015-08-22 02:58:47 +020086@asyncio.coroutine
87def create_serial_connection(loop, protocol_factory, *args, **kwargs):
88 ser = serial.Serial(*args, **kwargs)
89 protocol = protocol_factory()
90 transport = SerialTransport(loop, protocol, ser)
91 return (transport, protocol)
92
93# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94# test
95if __name__ == '__main__':
96 class Output(asyncio.Protocol):
97 def connection_made(self, transport):
98 self.transport = transport
99 print('port opened', transport)
Chris Liechti0cd7d072015-09-04 23:04:53 +0200100 transport.serial.rts = False
Chris Liechtid07a9e72015-08-22 02:58:47 +0200101 transport.write(b'hello world\n')
102
103 def data_received(self, data):
104 print('data received', repr(data))
105 self.transport.close()
106
107 def connection_lost(self, exc):
108 print('port closed')
109 asyncio.get_event_loop().stop()
110
111 loop = asyncio.get_event_loop()
112 coro = create_serial_connection(loop, Output, '/dev/ttyUSB0', baudrate=115200)
113 loop.run_until_complete(coro)
114 loop.run_forever()
115 loop.close()