← index #11678Issue #9979
Related · high · value 2.457
QUERY · ISSUE

RP2 UART timeout is incorrect

openby peterhinchopened 2023-06-01updated 2023-06-01
bug

To repro link pins 0 and 1 and paste the following:

from machine import UART, Pin
uart = UART(0, 115200, rx=Pin(1, Pin.IN), tx=Pin(0, Pin.OUT), timeout=1000_000, timeout_char=1000_000)
uart.readline()

It times out after 15s rather than 16.6minutes.

Setting a long timeout is a hack to resolve https://github.com/micropython/micropython/issues/10867. In my opinion it should be possible to disable the timeout (set it to infinity). In uasyncio systems, uasyncio should provide any timeouts required by the application.

CANDIDATE · ISSUE

UART timeout parameter not functioning as expected on ESP32 port

openby jamesquiltyopened 2022-11-15updated 2022-11-15
bug

I've been trying to use the timeout parameter of the UART class and found that its behaviour does not appear to match the description in the documentation https://docs.micropython.org/en/latest/library/machine.UART.html and the actual behaviour is rather odd... so much so that I believe that the timeout parameter is not functioning as intended (i.e. there's a bug).

We've connected our ESP32 to a u-blox SARA-R410M-02B on a SparkFun SARA-R4 Shield via UART. On both 1.14 and 1.19.1 I'm able to use the script below to reproducibly observe in the REPL the following odd behaviour

Steps to Reproduce

  1. Import the test script
  2. Execute test_UART_timeout()
  3. Execute test_UART_timeout(timeout=1000) (or any timeout value in ms)

Actual Result

MicroPython v1.19.1 on 2022-09-13; ESP32 module (spiram) with ESP32
Type "help()" for more information.

>>> from testScript import test_UART_timeout
>>> test_UART_timeout()
modem.read() elapsed time: 0 ms
Modem response: b'AT\r\r\nOK\r\n'

>>> test_UART_timeout(timeout=1000)
modem.read() elapsed time: 1999 ms
Modem response: b'AT\r\r\nOK\r\n'

Note that when the timeout parameter is used the elapsed time is nearly twice the timeout duration, even though there are characters immediately available to read. This occurs for other values, e.g. 100, 10, etc.

The only difference in function calls is that in the first invocation, test_UART_timeout(), instantiates a UART() object without using the timeout keyword-only parameter while the second invocation instantiates the object with the specified timeout period. Identical behaviour is seen when UART.init(timeout=<value> is used to change the timeout parameter value of an object.

I see this behaviour on both 1.14 and 1.19.1 (nightly), so I believe it's a core problem.

Expected Result

MicroPython v1.19.1 on 2022-09-13; ESP32 module (spiram) with ESP32
Type "help()" for more information.

>>> from testScript import test_UART_timeout
>>> test_UART_timeout()
modem.read() elapsed time: 0 ms
Modem response: b'AT\r\r\nOK\r\n'

>>> test_UART_timeout(timeout=1000)
modem.read() elapsed time: 0 ms
Modem response: b'AT\r\r\nOK\r\n'

I would expect:

  1. As there are characters immediately available to read, UART.read() with a wait-for-first-character timeout should not wait for the entire timeout period before attempting to read the first character — that's not a timeout it's a wait period.

  2. Even if the intended implementation is a wait period, the actual elapsed time when characters are immediately available to read should be, in this example where the read takes less than 1 ms, the wait period and not twice the wait period.

The immediate workaround is to not use the UART timeout parameter and instead to implement a "wait for first character with timeout" loop in the code, as shown in the test script below. That seems rather redundant, when there's supposed to be a timeout parameter which "specifies the time to wait for the first character (in ms)."

Test Script

Save this as testScript.py for this example and copy it to the target system:

import sys
import time
from machine import UART

# GPIO and SARA-R4 configuration
MODEM_TX_PIN = 27
MODEM_RX_PIN = 26
MODEM_DEFAULT_BAUD = 115200
MODEM_STANDARD_TIMEOUT = 1000  # Standard AT command time out

def test_UART_timeout(timeout=None):

    if timeout == None:
        modem = UART(1, MODEM_DEFAULT_BAUD, bits=8, parity=None, stop=1, tx=MODEM_TX_PIN, rx=MODEM_RX_PIN)
    else:
        modem = UART(1, MODEM_DEFAULT_BAUD, bits=8, parity=None, stop=1, tx=MODEM_TX_PIN, rx=MODEM_RX_PIN, timeout=timeout)

    modem.write("AT\r")  # Simplest modem command, response should be `AT\r\r\nOK\r\n`

    # Wait for first character on UART, with a timeout
    loop_count = 0
    start_time = time.ticks_ms()
    while modem.any() == 0 and time.ticks_ms() < (start_time + MODEM_STANDARD_TIMEOUT):
        loop_count += 1

    if time.ticks_ms() > (start_time + MODEM_STANDARD_TIMEOUT):
        print("AT{0}  No response within {1:d} ms.".format(MODEM_STANDARD_TIMEOUT))
        sys.exit()

    # There's something to read - let's read!
    start_time = time.ticks_ms()
    read_str = modem.read()
    end_time = time.ticks_ms()
    
    print("modem.read() elapsed time: {0} ms".format((end_time-start_time)))
    print("Modem response: {0}".format(repr(read_str)))

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied