uart.StreamReader and asyncio no data

All ESP8266 boards running MicroPython.
Official boards are the Adafruit Huzzah and Feather boards.
Target audience: MicroPython users with an ESP8266 board.
kurt
Posts: 20
Joined: Sun Feb 11, 2018 7:45 pm

uart.StreamReader and asyncio no data

Post by kurt » Wed Jul 04, 2018 10:01 am

Hi,
run MicroPython v1.9.4-241-g14ab81e on my ESP8266 nodemcu board.
load

Code: Select all

import uasyncio as asyncio
from machine import UART
uart = UART(0, 115200)

async def sender():
    swriter = asyncio.StreamWriter(uart, {})
    while True:
        await swriter.awrite('Hello uart\n')
        await asyncio.sleep(2)

async def receiver():
    sreader = asyncio.StreamReader(uart)
    while True:
        res = await sreader.readline()
        print('Recieved', res)

loop = asyncio.get_event_loop()
loop.create_task(sender())
loop.create_task(receiver())
loop.run_forever()
which I copied from https://github.com/peterhinch/micropyth ... -mechanism
and got the following output
Hello uart
Hello uart
Hello uart
Traceback (most recent call last):
File "main.py", line 20, in <module>
File "uasyncio/core.py", line 77, in run_forever
File "uasyncio/__init__.py", line 69, in wait
KeyboardInterrupt:

MicroPython v1.9.4-241-g14ab81e on 2018-07-04; ESP module with ESP8266
Type "help()" for more information.
>>> asdf
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'asdf' is not defined
>>>

I expected to get the typed input printed out: Recieved asdf

As I understand the typed input is passed to stdin and not to sreader.readline().

Any help is welcome.
Regards
Kurt

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uart.StreamReader and asyncio no data

Post by pythoncoder » Thu Jul 05, 2018 7:50 am

That code is intended to be run on a Pyboard or similar, using a UART which is not used for the REPL. A wire link between the UART's Txd and Rxd provides a "loopback" for the test.

Running it on the ESP8266 is problematic because the chip has only one UART which is used for the REPL. Further, when you interrupt the script with ctrl-C it stops running. In Python you are either running a script or interacting with Python at the REPL. (There are special cases in MicroPython where scripts can run in the background, but this simple script isn't one of them).

The fact that the ESP8266 has only one UART is problematic: if you want to work with UARTs the ESP32 (or Pyboard if you don't need WiFi) are better platforms.

It might be best if you explained what you're trying to do, then maybe someone will come up with suggestions.
Peter Hinch
Index to my micropython libraries.

kurt
Posts: 20
Joined: Sun Feb 11, 2018 7:45 pm

Re: uart.StreamReader and asyncio no data

Post by kurt » Thu Jul 05, 2018 9:33 am

Hi Peter,
sorry, you are absolutly right.
My application uses a 3 by 4 keypad matrix and an RFID reader connected via UART to the ESP8266 (at a second solution a RFC522 is connected via SPI, instead. This runs fine). With the asyncio functionalty of micropython, I'm able to run tasks for check keypad matrix, check uart if rfid card is presented by the user, check two buttons connected to ADC, get ntp time, check network, write syslog. Using asyncio makes me my programmer life much easier. At the end, two relayes are driven to open and close two doors.

The problem is getting the seriell data via UART which are not passed to my application. It seems micropython virtual machine catch the data and pass it to stdin. Now, I could use sys.stdin, but I don't know how to timeout sys.stdin to give the other tasks a chance to run. Therefore I thougth "asyncio.StreamReader(uart)" could help me.

As I used micropython 1.9.3 UART data are passed through my application, but not with 1.9.4. Unfortunately, it's not easy for me to go back to 1.9.3 because the application doesn't fit in the ram. I need to build it all by compile the source from github.

The above coding was an attempt to show the problem in a general manner.

Here the part of the code, which should read in UART data:

Code: Select all

...
        from machine import UART
        uart = UART(0, 9600)                         # init with given baudrate
        uart.init(9600, bits=8, parity=None, stop=1) # init with given config
        while True:
            if uart.any():
                c = uart.read()
                num = c[0:10]
...
Hope my long story is understandable.
Kurt

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uart.StreamReader and asyncio no data

Post by pythoncoder » Fri Jul 06, 2018 6:49 am

That way of reading a UART will block. If you're expecting exactly 10 chars from the UART you need to do something like

Code: Select all

async def receiver():
    while uart.any() < 10:
        await asyncio.sleep(0)
    c = uart.read(10)
Or use a StreamReader:

Code: Select all

async def receiver():
    sreader = asyncio.StreamReader(uart)
    while True:
        c = await sreader.read(10)
If data arrives in lines it's best to use the StreamReader.readline() method.
Peter Hinch
Index to my micropython libraries.

kurt
Posts: 20
Joined: Sun Feb 11, 2018 7:45 pm

Re: uart.StreamReader and asyncio no data

Post by kurt » Fri Jul 06, 2018 7:57 am

Hi Peter,
I used

Code: Select all

import uasyncio as asyncio
from machine import UART

uart = UART(0, 115200)

async def receiver():
    sreader = asyncio.StreamReader(uart)
    while True:
        res = await sreader.readline()
        print('Recieved', res)

loop = asyncio.get_event_loop()
loop.create_task(receiver())
loop.run_forever()
load it as main.py, pressed reset, and enter via PuTTY from my PC random characters, like "asdf".

With MicroPython v1.9.3-8-g63826ac5c on 2017-11-01; ESP module with ESP8266, I get after hit ctrl-C:
Traceback (most recent call last):
File "main.py", line 14, in <module>
File "uasyncio/core.py", line 77, in run_forever
File "uasyncio/__init__.py", line 69, in wait
KeyboardInterrupt:

MicroPython v1.9.3-8-g63826ac5c on 2017-11-01; ESP module with ESP8266
Type "help()" for more information.
>>>

With MicroPython v1.9.4-244-g106e594 on 2018-07-06; ESP module with ESP8266, I get after hit ctrl-C
Traceback (most recent call last):
File "main.py", line 14, in <module>
File "uasyncio/core.py", line 90, in run_forever
File "uasyncio/__init__.py", line 69, in wait
KeyboardInterrupt:

MicroPython v1.9.4-244-g106e594 on 2018-07-06; ESP module with ESP8266
Type "help()" for more information.
>>> asdf
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'asdf' is not defined
>>>

In both cases, I did not see a print with the characters, I typed in, like "asdf".

As you can see, MicroPython version 1.9.4 differs from 1.9.3 in the behaviour.

What I want: I like to get the typed character, like "asdf", read into my application. This seems not to be done, otherwise the print statement would printout the characters.

What could be wrong on my side?

Regards
Kurt

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uart.StreamReader and asyncio no data

Post by pythoncoder » Sat Jul 07, 2018 8:09 am

I am not sure. The problem (I think) is that the ESP8266 has only one UART which is expected to do duty supporting the REPL. If a program takes over the UART for its own purposes then REPL output is likely to be suppressed. Which is why you never see "Received asdf". Incidentally you'd need to type asdf<Enter> for sreader.readline() to return.

In my own work with the ESP8266 I regarded the UART as unusable and performed I/O by other means. This because I needed it for printing debugging messages. The thought of trying to develop a program without access to debug print statements is not one I relish. I would use a platform with more than one UART.

I think some people have had success with the ESP8266 UART: perhaps someone will pop up, otherwise try a forum search.
Peter Hinch
Index to my micropython libraries.

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: uart.StreamReader and asyncio no data

Post by kevinkk525 » Sat Jul 07, 2018 2:43 pm

Looks like this guy got the uart to work:
viewtopic.php?f=16&t=4924&p=28368&hilit=uart#p28368

This will disable the repl during the usage of the uart.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

kurt
Posts: 20
Joined: Sun Feb 11, 2018 7:45 pm

Re: uart.StreamReader and asyncio no data

Post by kurt » Sat Jul 07, 2018 9:36 pm

Hi kevinkk525,
thanks for presenting the link. Unfortunately, it does not change anything.
I extend my test app to get WebREPL by start a network connection.

Code: Select all

import network
import uasyncio as asyncio
from machine import UART
import uos

uos.dupterm(None, 1)

uart = UART(0, 115200)
uart.init(115200, bits=8, parity=None, stop=1)

wlan_sta = network.WLAN(network.STA_IF)
wlan_sta.active(True)
wlan_sta.connect('SSID','Password')

while not wlan_sta.isconnected():
    pass

async def receiver():
    sreader = asyncio.StreamReader(uart)
    while True:
        print('before readline')
        res = await sreader.readline()
        print('Recieved', res)

async def other_task():
    while True:
        print('other_task')
        await asyncio.sleep(1)
        
loop = asyncio.get_event_loop()
loop.create_task(receiver())
loop.create_task(other_task())
loop.run_forever()
Do a reset, connect via WebREPL and get the following output:
other_task
before readline
other_task
other_task
other_task
other_task
other_task
other_task
other_task
other_task
other_task
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "main.py", line 33, in <module>
File "uasyncio/core.py", line 77, in run_forever
File "uasyncio/__init__.py", line 69, in wait
KeyboardInterrupt:
>>> asdf
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'asdf' is not defined
>>>

Regardless of uos.dupterm(None) or uos.dupterm(None, 1) or without uos.dupterm always the same behaviour. Always the typed input are passed to MicroPython stdin only. As you can see readline() waits on input for ever.

May be I'm wrong, but dupterm does a duplication of terminal input to REPL or WebREPL. Means, the application should get the data anyway. Or?

Peter, you are right using UART for more than debugging is not a good idea. A change to another platform is not the way, I like to go.
Change to C with FreeRTOS would be a choice, but the effort is enormous.

I'm still looking for a solution in MicroPython.
Any idea is highly welcome.

Regards
Kurt

kurt
Posts: 20
Joined: Sun Feb 11, 2018 7:45 pm

Re: uart.StreamReader and asyncio no data

Post by kurt » Sun Jul 08, 2018 6:22 am

Hi,
problem solved by
uos.dupterm(None) and !!! uos.dupterm(None, 1).
Means both have to be set. REPL and WebREPL are not more usable with this setup, for shure. I have to find an other way to communicate with the app, may be an own web server.
Here my test app:

Code: Select all

import uos
import time
from machine import UART, Pin

uos.dupterm(None, 1)
uos.dupterm(None)

uart = UART(0, 115200)
uart.init(115200, bits=8, parity=None, stop=1)

while True:
    if uart.any():
        c = uart.read()
        if c == b'1':
            led = Pin(2, Pin.OUT)
            led.off()  # led on
            time.sleep(1)
            led.on()   # led off
Many thanks to all.
Regards
Kurt

User avatar
tonyldo
Posts: 5
Joined: Wed Jun 20, 2018 5:04 pm
Location: Brazil

Re: uart.StreamReader and asyncio no data

Post by tonyldo » Wed Jul 25, 2018 8:38 pm

kurt wrote:
Sun Jul 08, 2018 6:22 am
Hi,
problem solved by
uos.dupterm(None) and !!! uos.dupterm(None, 1).
Means both have to be set. REPL and WebREPL are not more usable with this setup, for shure. I have to find an other way to communicate with the app, may be an own web server.
Here my test app:

Code: Select all

import uos
import time
from machine import UART, Pin

uos.dupterm(None, 1)
uos.dupterm(None)

uart = UART(0, 115200)
uart.init(115200, bits=8, parity=None, stop=1)

while True:
    if uart.any():
        c = uart.read()
        if c == b'1':
            led = Pin(2, Pin.OUT)
            led.off()  # led on
            time.sleep(1)
            led.on()   # led off
Many thanks to all.
Regards
Kurt
I wanna disable the RX pin from uart. Do you know if this possible?

Code: Select all

import uos
import time
from machine import UART, Pin

uos.dupterm(None, 1)
uos.dupterm(None)

uart = UART(0, 115200)

//Does rx Keyworld parameter can be None???
uart.init(115200, bits=8, parity=None, stop=1, rx = None)
I wanna use de RX pin (Gpio 3) from esp 01 for general input pin!

Post Reply