Issue
I am porting some C++ code to Python and I am having a heck of a time figuring out how to have an onReceive handler for serial bytes. I am using
import serial_asyncio
class Arduino: #comms is with an Arduino in serial mode)
async def connect(self, serial_port):
(self.serial_reader, self.serial_writer) = await
serial_asyncio.open_serial_connection(url=serial_port, baudrate=115200)
print("serial opened:", serial_port)
self.buffer = b""
async def recv(self, data):
self.buffer += data
self.process_buffer(self.buffer)
if __name__ == '__main__':
ardunio = Arduino("/dev/ttyS0")
loop = asyncio.get_event_loop()
loop.run_until_complete(ardunio.connect())
loop.run_forever()
However I cannot figure out how to patch the recv handler in to the read. I Qt, I could:
connect(&QAbstractSocket::readyRead, &Arduino::handleBytes);
In Node:
arduino.on('data', line => console.log(line))
In Python, there doesn't seem to be any obvious answer? How do I get the serial port bytes that arrive to be passed into Arduino.receive(self, data)?
Solution
However I cannot figure out how to patch the recv handler in to the read.
open_serial_connection
is not a callback-based interface, it returns a pair of streams which expose the contents through coroutines. This allows you to communicate with the serial port as if you were writing blocking code, i.e. without the use of callbacks and buffers that build up data. For example (untested):
async def main():
reader, writer = await serial_asyncio.connect(url="/dev/ttyS0", baudrate=115200)
# instead of: arduino.on('data', line => console.log(line))
# ...we can just read some data from the serial port
data = await reader.read(1024)
# ...and print it right here
print(repr(data))
asyncio.run(main())
Coroutines like StreamReader.read
will appear to block waiting for data, but they will really just suspend the current coroutine and let the event loop do other things. This allows you to easily express timeouts or do other processing while waiting for the data to arrive from the serial port.
If you still need callbacks, for example because you need to communicate with a C API, you have two options:
use the lower-level
create_serial_connection
function. It accepts a type that inheritsasyncio.Protocol
where you can define hooks likedata_received
(as a callback, not a coroutine), which is close to how you modeled yourArduino
class.keep using the coroutine API, but use
add_done_callback
to register a callback to run when the coroutine is ready.
An example of the latter would be:
async def main():
reader, writer = await serial_asyncio.connect(url="/dev/ttyS0", baudrate=115200)
# Python equivalent of arduino.on('data', x):
# 1. call reader.read() but don't await - instead, create a "future"
# which works like a JavaScript Promise
future = asyncio.ensure_future(reader.read(1024))
# register a done callback when the result is available
future.add_done_callback(future, lambda _: print(repr(future.result())))
# go do something else - here we wait for an event just so main()
# doesn't exit immediately and terminate our program
await asyncio.Event().wait()
asyncio.run(main())
But unless you are communicating with C, I see no advantage in using this style over the normal async/await.
Answered By - user4815162342
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.