Welcome to pySerial-asyncio’s documentation

Async I/O extension for the Python Serial Port package for OSX, Linux, BSD. Support for Windows is included, though with a different implementation based on polling which may be slower than on other platforms.

It depends on pySerial and is compatible with Python 3.5 and later.

Other pages (online)

Contents:

Overview

Serial transports, protocols and streams

This module layers asyncio support onto pySerial. It provides support for working with serial ports through asyncio Transports, Protocols, and Streams.

Transports are a low-level abstraction, provided by this package in the form of an asyncio.Transport implementation called SerialTransport, which manages the asynchronous transmission of data through an underlying pySerial Serial instance. Transports are concerned with how bytes are transmitted through the serial port.

Protocols are a callback-based abstraction which determine which bytes are transmitted through an underlying transport. You can implement a subclass of asyncio.Protocol which reads from, and/or writes to, a SerialTransport. When a serial connection is established your protocol will be handed a transport, to which your protocol implementation can write data as necessary. Incoming data and other serial connection lifecycle events cause callbacks on your protocol to be invoked, so it can take action as necessary.

Usually, you will not create a SerialTransport directly. Rather, you will define a Protocol class and pass that protocol to a function such as create_serial_connection() which will instantiate your Protocol and connect it to a SerialTransport.

Streams are a coroutine-based alternative to callback-based protocols. This package provides a function open_serial_connection() which returns asyncio.StreamReader and asyncio.StreamWriter objects for interacting with underlying protocol and transport objects, which this library will create for you.

Protocol Example

This example defines a very simple Protocol which sends a greeting message through the serial port and displays to the console any data received through the serial port, until a newline byte is received.

A call is made to create_serial_connection(), to which the protocol class (not an instance) is passed, together with arguments destined for the Serial constructor. This call returns a coroutine object. When passed to run_until_complete() the coroutine is scheduled to run as an asyncio.Task by the asyncio library, and the result of the coroutine, which is a tuple containing the transport and protocol instances, return to the caller.

While the event loop is running (run_forever()), or until the protocol closes the transport itself, the protocol will process data received through the serial port asynchronously:

import asyncio
import serial_asyncio

class OutputProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
        print('port opened', transport)
        transport.serial.rts = False  # You can manipulate Serial object via transport
        transport.write(b'Hello, World!\n')  # Write serial data via transport

    def data_received(self, data):
        print('data received', repr(data))
        if b'\n' in data:
            self.transport.close()

    def connection_lost(self, exc):
        print('port closed')
        self.transport.loop.stop()

    def pause_writing(self):
        print('pause writing')
        print(self.transport.get_write_buffer_size())

    def resume_writing(self):
        print(self.transport.get_write_buffer_size())
        print('resume writing')

loop = asyncio.get_event_loop()
coro = serial_asyncio.create_serial_connection(loop, OutputProtocol, '/dev/ttyUSB0', baudrate=115200)
transport, protocol = loop.run_until_complete(coro)
loop.run_forever()
loop.close()

Reading data in chunks

This example will read chunks from the serial port every 300ms:

import asyncio
import serial_asyncio


class InputChunkProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport

    def data_received(self, data):
        print('data received', repr(data))

        # stop callbacks again immediately
        self.pause_reading()

    def pause_reading(self):
        # This will stop the callbacks to data_received
        self.transport.pause_reading()

    def resume_reading(self):
        # This will start the callbacks to data_received again with all data that has been received in the meantime.
        self.transport.resume_reading()


async def reader():
    transport, protocol = await serial_asyncio.create_serial_connection(loop, InputChunkProtocol, '/dev/ttyUSB0', baudrate=115200)

    while True:
        await asyncio.sleep(0.3)
        protocol.resume_reading()


loop = asyncio.get_event_loop()
loop.run_until_complete(reader())
loop.close()

pySerial-asyncio API

The following high-level functions are provided for initiating a serial connection:

Appendix

License

Copyright (c) 2015-2021 pySerial-team (see CREDITS.rst) All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Indices and tables