-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
machine.UART: Fix the double timeout with uart.read(), uart.write() and uart.readinto(). #17938
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Code size report:
|
37b0b99
to
688d103
Compare
17a6d11
to
12f7758
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #17938 +/- ##
==========================================
- Coverage 98.38% 98.38% -0.01%
==========================================
Files 171 171
Lines 22296 22300 +4
==========================================
+ Hits 21937 21940 +3
- Misses 359 360 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, | ||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, | ||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto1_obj) }, | ||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write1_obj) }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately using write1
here will be a breaking change, because it doesn't accept the additional arguments like write
does.
So, we'll need to use the same approach as you did with stream_readinto_generic
, to make a stream_write_generic(size_t n_args, const mp_obj_t *args, byte flags)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which additional arguments are expected in the context of machine_uart's write()? Obviously uart.write() works in the test I made.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signature is:
stream.write(buf)
stream.write(buf, max_len)
stream.write(buf, off, max_len)
I don't think this is actually documented anywhere, but it may be relied upon in some code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. I did not find any documentation about it. It it safe then to change write1 to accept the additional arguments? The only places where it seems to be referenced were extmod/modtls_mbedtls.c
and unix/coverage.c
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It it safe then to change write1 to accept the additional arguments?
Yes, it's safe, because it's an extension of the existing signature.
Ideally we would not support these extra arguments, which are a MicroPython specific extension (extension to CPython). Maybe we can remove them in MicroPython v2.0.
But for now, they are needed. Eg umqtt.simple
uses the second argument, eg self.sock.write(pkt, 2)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change implemented & tested with a RP2 board. Both timeout and additional write() arguments work as expected.
ports/cc3200/mods/pybuart.c
Outdated
@@ -579,13 +579,13 @@ static const mp_rom_map_elem_t pyb_uart_locals_dict_table[] = { | |||
{ MP_ROM_QSTR(MP_QSTR_txdone), MP_ROM_PTR(&machine_uart_txdone_obj) }, | |||
|
|||
/// \method read([nbytes]) | |||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, | |||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read1_obj) }, | |||
/// \method readline() | |||
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, | |||
/// \method readinto(buf[, nbytes]) | |||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this use readinto1
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. The CC3200 is always a little bit out of sight.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed & tested. The timeout and timeout_char situation have the same timeout. These values are fixed with the CC3200 port and are set to 16 character times, as far as I read the code. What I see in testing is a delay of about 36 character times.
py/stream.c
Outdated
@@ -303,7 +303,12 @@ static mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { | |||
} | |||
|
|||
int error; | |||
mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); | |||
mp_uint_t out_sz; | |||
if (flags & MP_STREAM_RW_ONCE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This if
can be simplified to a single line:
mp_stream_rw(args[0], bufinfo.buf, len, &error, flags);
and then pass in MP_STREAM_RW_READ
as flags from stream_readinto()
, and MP_STREAM_RW_READ | MP_STREAM_RW_ONCE
from stream_readinto1()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, good hint.
Do not try to read/write again after timeout happened once. Signed-off-by: robert-hh <robert@hammelrath.com>
Before, it was ignored. Tested with ESP32, ESP32S3, ESP32C6. Signed-off-by: robert-hh <robert@hammelrath.com>
Avoiding the double timeout when used with the UART class. stream.readinto1() returns after the first timeout. Signed-off-by: robert-hh <robert@hammelrath.com>
As suggested by using a common stream_readinto_generic() function. Reducing the size increase to about 50 bytes. Signed-off-by: robert-hh <robert@hammelrath.com>
As suggested adding a stream_write_generic() function. Tested the different write timeouts with a RP2 using flow control. Tested support of the additional arguments of uart.write(). Signed-off-by: robert-hh <robert@hammelrath.com>
4148f99
to
601b210
Compare
Summary
When uart.read(), uart.write() and uart_readinto() timed out because less data was transferred than expected, but some, the delay was uart.timeout + uart.timeout.char instead of just uart,timeout_char().
This PR changes the behavior into the expected one. It consists of 3 commits. The first changes uart.read() and uart.write(). The impact of that change is limited to the machine.UART class. The second commit changes stream.readinto() and may affect all uses of that function.
The third commit changes the ESP32's machine_uart.c to deal properly with the timeout_char argument for uart.read().
uart.readline() is unchanged.
This PR addresses issue #17611.
Testing
Tested with STM32(Pyboard V1.1 and PYBD_SF6), RP2 Pico, MIMXRT(Teensy 4.1), SAMD51, ESP32, NRF (nrf52840) and a CC3200 board using strings of varying length. Typical results:
Trade-offs and Alternatives
If the impact of the changes to a stream module seems unsafe, the second commit may be omitted, causing the behavior of uart.readinto() unchanged.