Chris Liechti | d07a9e7 | 2015-08-22 02:58:47 +0200 | [diff] [blame^] | 1 | #!/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 | """\ |
| 11 | Support asyncio with serial ports. EXPERIMENTAL |
| 12 | |
| 13 | Posix platforms only, Python 3.4+ only. |
| 14 | |
| 15 | Windows event loops can not wait for serial ports with the current |
| 16 | implementation. It should be possible to get that working though. |
| 17 | """ |
| 18 | import asyncio |
| 19 | import serial |
| 20 | |
| 21 | class 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 |
| 84 | def 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 |
| 92 | if __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() |