Skip to content
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

Update UART driver with specs, documentaion, and read/2 #1508

Open
wants to merge 3 commits into
base: release-0.6
Choose a base branch
from

Conversation

UncleGrumpy
Copy link
Collaborator

@UncleGrumpy UncleGrumpy commented Feb 2, 2025

Updates the UART driver to include function specs and documentation.
Fixes possible concurrency problems, and insufficient sized memory checks.
Now cleans up and returns errors when initializing with badarg parameters.
Adds new uart:read/2 with a timeout parameter.

Closes #1446

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

Copy link
Contributor

@petermm petermm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

works, great! even when doing hundreds of uart:read per second!

libs/eavmlib/src/uart.erl Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
@UncleGrumpy
Copy link
Collaborator Author

UncleGrumpy commented Feb 3, 2025

I pushed changes that eliminate the use of any timer or task in the C code, and instead added a cancel_read command, handling the timeout and sending the cancel command from erlang, following the suggestion @pguyot made.

I believe this is much more efficient, and less resource intensive than creating a timer.

@UncleGrumpy UncleGrumpy requested a review from pguyot February 3, 2025 01:02
Copy link
Collaborator

@pguyot pguyot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this new version! Besides the little bug on timeout result, could you please fix potential concurrency issues?

libs/eavmlib/src/uart.erl Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
@petermm
Copy link
Contributor

petermm commented Feb 3, 2025

Works great still.

nitpick:
Should it only take a positive integer as timeout value? pos_integer()

Tried :uart.read(port, 0) but it would never get a read, of course also a nonsensical value..

@UncleGrumpy
Copy link
Collaborator Author

Thank you for this new version! Besides the little bug on timeout result, could you please fix potential concurrency issues?

Yes. I see what you mean now, I thought your comment about concurrency was related to the previous implementation, but I see now that you were talking about pre-existing concurrency problems.

@pguyot
Copy link
Collaborator

pguyot commented Feb 3, 2025

Yes. I see what you mean now, I thought your comment about concurrency was related to the previous implementation, but I see now that you were talking about pre-existing concurrency problems.

Exactly. And issues could be amplified by the new code.

@UncleGrumpy UncleGrumpy force-pushed the uart_add_timeout branch 8 times, most recently from abfdc3d to 6f19486 Compare February 5, 2025 23:52
@UncleGrumpy
Copy link
Collaborator Author

This should be ready for another look. I made more bug fixes that should be checked.

CHANGELOG.md Outdated Show resolved Hide resolved
libs/eavmlib/src/uart.erl Outdated Show resolved Hide resolved
libs/eavmlib/src/uart.erl Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
src/platforms/esp32/components/avm_builtins/uart_driver.c Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
@UncleGrumpy UncleGrumpy force-pushed the uart_add_timeout branch 4 times, most recently from 0cdd473 to 38b751a Compare February 7, 2025 23:09
Copy link
Collaborator

@pguyot pguyot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to take advantage of this PR to change the if (xQueueRemove(...)) into a while? It would make queue overflow less likely.

int rx_pin = get_uart_pin_opt(opts, RX_ATOM);
int rts_pin = get_uart_pin_opt(opts, RTS_ATOM);
int cts_pin = get_uart_pin_opt(opts, CTS_ATOM);
if (UNLIKELY(tx_pin < UART_PIN_NO_CHANGE || rx_pin < UART_PIN_NO_CHANGE
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better, but code is still inferring that -2 is less than UART_PIN_NO_CHANGE, which is a little bit hacky or ugly.

The function should take an additional parameter as suggested (int *ok) or we should define a new constant for -2 (UART_BADARG ?) and a static assert that it's less than UART_PIN_NO_CHANGE.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with the static assert option, since we only need to check the value of the returned integers. Using the *ok pointer would have required 4 individual checks, rather than one after all pins have been evaluated.

@UncleGrumpy
Copy link
Collaborator Author

Do you want to take advantage of this PR to change the if (xQueueRemove(...)) into a while? It would make queue overflow less likely.

I @almost overlooked this. Great idea, I added that change.

Add type definitions for input parameters.
Add spec for all exported functions.
Add documentation for functions.

Signed-off-by: Winford <[email protected]>
@UncleGrumpy UncleGrumpy force-pushed the uart_add_timeout branch 3 times, most recently from dd22180 to 6baafa5 Compare February 9, 2025 23:41
Fixes possible concurrency problems by using a mutex for access to `reader_process_pid` and
`reader_ref_ticks` in `uart_data`.

Avoid possible overflow of the queue by handling all mesages in the interrupt callback.

Now cleans up and returns errors, with improved log messages when initalization fails with a badarg
in the configuration parameters, rather than aborting.

Fixes incorrect pin integers being cast to terms.

Pin numbers are validated for the chip the VM is compiled for before use to mitigate VM crashes from
bad input parameters.

Signed-off-by: Winford <[email protected]>
Adds a new uart:read/2 that takes a timeout parameter for reads. If no data is received during the
timeout period `timeout` is returned, and the listener is removed allowing for another read without
getting the `{error, ealready}` error tuple.

Closes atomvm#1446

Signed-off-by: Winford <[email protected]>
@robinchew
Copy link

httpd_example cannot load index.html if you open UART a certain way using this PR.

This issue does NOT occur in commit 4d38f90 of this PR.

Find my modified http_example code here robinchew/atomvm_lib@033a226 which is essentially the following code which spawns a process that opens UART a certain way before httpd:start:

    spawn(fun() ->
        Port = uart:open("UART2", [{rx, 16}, {tx, 17}]), % need to set rx and tx, and maybe UART2 as well
        io:format("port ~p~n", [Port]),
    end),
    
    httpd:start(....)

Trying to load index.html would result with:

~$ curl -v http://192.168.1.43:8080/index.html
*   Trying 192.168.1.43:8080...
* connect to 192.168.1.43 port 8080 from 192.168.1.4 port 37574 failed: Connection refused
* Failed to connect to 192.168.1.43 port 8080 after 306 ms: Couldn't connect to server
* Closing connection
curl: (7) Failed to connect to 192.168.1.43 port 8080 after 306 ms: Couldn't connect to server

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants