Page 1 of 1

Receiving uart data and polling sensor with uasyncio

Posted: Mon Mar 15, 2021 7:45 pm
by picouser78
Hi,

I've been trying to use uasyncio for some "parallel" tasks (uart communication and sensor polling).
My problem is that "data = await sreader.readline()" behaves very buggy in my program:
When I send a bytestring like b'test12345\n' sometimes I receive nothing after the first try.
Then after a second attempt I receive something like b'test12345test12345\n'.

I think the problem lies in the delays (time.sleep_us(10)) of the sensor library (HC-SR094) which locks the readline() command.
Does anybody know a good practice to deal with modules that uses such delays?


Here is a minimal example:

Code: Select all

from machine import Pin, UART
import uasyncio as asyncio
uart = UART(1, baudrate=115200, tx=Pin(4), rx=Pin(5))

async def receiver():
    sreader = asyncio.StreamReader(uart)
    while True:
        data = await sreader.readline()
        print(data)

async def read_sensor():
    # read data from sensor
    await asyncio.sleep_ms(100)


async def main():
    asyncio.create_task(receiver())    
    asyncio.create_task(read_sensor())
    while True:    
        await asyncio.sleep_ms(1000)

if __name__ == '__main__':    
    try:
        asyncio.run(main())
    finally:
        asyncio.new_event_loop()
Without read_sensor(), uart works flawless...

Regards Marc

Re: Receiving uart data and polling sensor with uasyncio

Posted: Thu Mar 18, 2021 7:54 pm
by mbrizuarg
Hi Marc

I'm also starting with asyncio. From what I understand you will have to modify the library so that it is also asyncio.

Greetings. Mariano.

Re: Receiving uart data and polling sensor with uasyncio

Posted: Fri Mar 19, 2021 6:17 am
by kevinkk525
If you're using the uart, make sure to modify the uart driver like this PR: https://github.com/micropython/micropython/pull/7022
Otherwise it's extremely buggy and unusable. Hope it'll be in the daily build soon.

Re: Receiving uart data and polling sensor with uasyncio

Posted: Fri Mar 19, 2021 9:09 am
by pythoncoder
As @Kevinkk525 says, the UART firmware is unstable.

A delay of 10μs (time.sleep_us(10)) is not going to affect any real uasyncio program. The latency involved in switching between concurrent tasks is typically three orders of magnitude greater than this. Consequently uasyncio can't provide μs level delays with any semblance of accuracy. If you need such delays, there is no choice other than to use time.sleep_us().

As for device drivers, it is perfectly OK to use conventional synchronous drivers with uasyncio so long as these don't block for long periods. Drivers that read sensors don't usually block for long. Examples of blocking drivers are communications devices where reception blocks while waiting for a transmission. This is where the uasyncio stream mechanism comes in. It would be bad practice to issue uart.read(10) because it would lock up all tasks for the duration: stream I/O solves this, enabling the reading task to pause while others continue.

Re: Receiving uart data and polling sensor with uasyncio

Posted: Sat Mar 20, 2021 1:33 pm
by picouser78
Thank you all for your answers! Some days ago I've modified the HC-SR04 lib to work asynchronous.
I've used the very good example (https://github.com/peterhinch/micropyth ... /HTU21D.md) of the asynchronous HTU21D driver as blueprint.

When three coros (sensor, send, receive) are running, uart will still behave very buggy and unpredictable.
So I'm partly relieved that the uart driver itself is buggy. ;)

Is there a quick way to modify the files on the pico until they are part of the official build?

Re: Receiving uart data and polling sensor with uasyncio

Posted: Sat Mar 20, 2021 1:41 pm
by pythoncoder
Not without rebuilding, as the UART driver code is in C. If you are able to rebuild, get this PR, rebuild and flash the firmware.

Re: Receiving uart data and polling sensor with uasyncio

Posted: Sun Mar 21, 2021 8:27 am
by picouser78
Ok, I've rebuilt/flashed the firmware but now there seems to be a new (?) issue with uasyncio.
Without it, everything works as intended. Here's a working minimal example for an uart test:

CPython

Code: Select all

import serial
import time
ser = serial.Serial(port='/dev/serial0', baudrate = 9600)

for x in range(100):
    message = str(x) + '\n'
    message = message.encode()
    ser.write(message)
    print(message)
    time.sleep(5)
RP2

Code: Select all

from machine import Pin, UART
uart = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))

while True:
    data = uart.readline()
    if data:
        print(data)
In my second example (with uasyncio), "data = await sreader.readline()" returns something only every other write.
So when the sender sends b'1\n' readline() returns nothing. After b'2\n' readline() returns both lines.
Is this a timout problem?

Code: Select all

from machine import Pin, UART
import uasyncio as asyncio
uart = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))

async def receiver():
    sreader = asyncio.StreamReader(uart)
    while True:
        data = await sreader.readline()
        print(data)

async def main():
    asyncio.create_task(receiver())    
    while True:    
        await asyncio.sleep_ms(1000)

if __name__ == '__main__':    
    try:
        asyncio.run(main())
    finally:
        asyncio.new_event_loop()

With the current daily firmware (rp2-pico-20210321-unstable-v1.14-121-g4fc2866f4.uf2) the uasynio example is working. So it seems the changes in the PR affect StreamReader(uart).readline().