Skip to content

Commit 7607279

Browse files
committed
Merge pull request #1508 from UncleGrump/uart_add_timeout
Update UART driver with specs, documentation, and read/2 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
2 parents 3dd59d5 + 7ffc76e commit 7607279

File tree

3 files changed

+260
-62
lines changed

3 files changed

+260
-62
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
with nodejs and emscripten)
1313
- Added preliminary support for ESP32P4 (no networking support yet).
1414
- Added memory info in `out_of_memory` crash logs to help developers fix memory issues.
15+
- Added documentation and function specs for uart driver
16+
- Added `uart:read/2` with a timeout parameter.
1517

1618
### Fixed
1719

@@ -53,6 +55,11 @@ and a race condition in otp_socket code
5355
- Fixed an out of memory issue by forcing GC to copy data from message fragments
5456
- Fixed a bug where calling repeatedly `process_info` on a stopped process could cause an out of
5557
memory error
58+
- Fixed possible concurrency problems in ESP32 UART driver
59+
60+
### Changed
61+
62+
- ESP32 UART driver no longer aborts because of badargs in configuration, instead raising an error
5663

5764
## [0.6.5] - 2024-10-15
5865

libs/eavmlib/src/uart.erl

Lines changed: 124 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,143 @@
1919
%
2020

2121
-module(uart).
22-
-export([open/1, open/2, close/1, read/1, write/2]).
22+
-export([open/1, open/2, close/1, read/1, read/2, write/2]).
2323

24+
-type peripheral() :: string() | binary().
25+
% The peripheral `Name' may be one of: `"UART0"' | `"UART1"' | `"UART2"' | `<<"UART0">>' | `<<"UART1">>' | `<<"UART2">>'.
26+
27+
-type uart_opts() :: [
28+
{tx, Tx_pin :: integer()}
29+
| {rx, Rx_pin :: integer()}
30+
| {rts, Rts_pin :: integer()}
31+
| {cts, Cts_pin :: integer()}
32+
| {speed, Speed :: pos_integer()}
33+
| {data_bits, 5..8}
34+
| {stop_bits, 1 | 2}
35+
| {event_queue_len, Qlen :: pos_integer()}
36+
| {flow_control, none | hardware | software}
37+
| {parity, none | even | odd}
38+
| {peripheral, peripheral()}
39+
| []
40+
].
41+
42+
%%-----------------------------------------------------------------------------
43+
%% @param Name the uart peripheral to be opened
44+
%% @param Opts uart configuration options
45+
%% @returns Pid of the driver.
46+
%% @doc Open a connection to the UART driver
47+
%%
48+
%% This function will open a connection to the UART driver.
49+
%% @end
50+
%%-----------------------------------------------------------------------------
51+
-spec open(Name :: peripheral(), Opts :: uart_opts()) -> Pid :: pid() | {error, _Reason :: term()}.
2452
open(Name, Opts) ->
2553
open([{peripheral, Name} | Opts]).
2654

55+
%%-----------------------------------------------------------------------------
56+
%% @param Opts uart configuration options
57+
%% @returns Pid of the driver.
58+
%% @doc Open a connection to the UART driver default port
59+
%%
60+
%% This function will open a connection to the UART driver.
61+
%% @end
62+
%%-----------------------------------------------------------------------------
63+
-spec open(Opts :: uart_opts()) -> Pid :: pid() | {error, _Reason :: term()}.
2764
open(Opts) ->
2865
open_port({spawn, "uart"}, migrate_config(Opts)).
2966

30-
close(Pid) ->
67+
%%-----------------------------------------------------------------------------
68+
%% @param Pid of the uart port to be closed
69+
%% @returns ok.
70+
%% @doc Close a port connection to the UART driver
71+
%%
72+
%% This function will close the given port connection to the UART driver.
73+
%% @end
74+
%%-----------------------------------------------------------------------------
75+
-spec close(Pid :: pid()) -> ok | {error, _Reason :: term()}.
76+
close(Pid) when is_pid(Pid) ->
3177
port:call(Pid, close).
3278

33-
read(Pid) ->
79+
%%-----------------------------------------------------------------------------
80+
%% @param Pid of the uart port to be read
81+
%% @returns {ok, Data} or {error, Reason}
82+
%% @doc Read data from a UART port
83+
%%
84+
%% This function will return any data that is available, or return
85+
%% a `{error, timeout}' tuple. The driver will sent the next available
86+
%% data from the UART driver to the process that made the last read.
87+
%% Example:
88+
%% ```
89+
%% Data = case uart:read(Uart) of
90+
%% {ok, Binary} -> Binary;
91+
%% {error, timeout} ->
92+
%% receive
93+
%% {ok, RecvBinary} -> RecvBinary;
94+
%% Error -> error(Error)
95+
%% end;
96+
%% Error -> error(Error)
97+
%% end,
98+
%% '''
99+
%% Any attempt by another (or the same process) to read from uart before the
100+
%% next uart payload is sent by the driver will result in `{error, ealready}'.
101+
%% @end
102+
%%-----------------------------------------------------------------------------
103+
-spec read(Pid :: pid()) -> {ok, Data :: iodata()} | {error, _Reason :: term()}.
104+
read(Pid) when is_pid(Pid) ->
34105
port:call(Pid, read).
35106

36-
write(Pid, B) ->
37-
case is_iolist(B) of
107+
%%-----------------------------------------------------------------------------
108+
%% @param Pid of the uart port to be read
109+
%% @param Timeout millisecond to wait for data to become available
110+
%% @returns `{ok, Data}', or `{error, Reason}'
111+
%% @doc Read data from a UART port
112+
%%
113+
%% This function will return any data that is available within the
114+
%% timeout period to the process. After the timeout has expired a new
115+
%% read command may be used regardless of whether the last read was
116+
%% sent a payload.
117+
%% Example:
118+
%% ```
119+
%% Data = case uart:read(Uart, 3000) of
120+
%% {ok, Bin} -> Bin;
121+
%% {error, timeout} -> <<"">>;
122+
%% Error -> error_handler_fun(Uart, Error)
123+
%% end,
124+
%% '''
125+
%% Any data sent to the esp32 over uart between reads with a timeout will
126+
%% be lost, so be sure this is what you want. Most applications will want
127+
%% a single process to read from UART and continue to listen until a payload
128+
%% is received, and likely pass the payload off for processing and
129+
%% immediately begin another read.
130+
%% @end
131+
%%-----------------------------------------------------------------------------
132+
-spec read(Pid :: pid(), Timeout :: pos_integer()) ->
133+
{ok, Data :: iodata()} | {error, _Reason :: term()}.
134+
read(Pid, Timeout) when is_pid(Pid) ->
135+
case port:call(Pid, read, Timeout) of
136+
{error, timeout} ->
137+
port:call(Pid, cancel_read),
138+
{error, timeout};
139+
Result ->
140+
Result
141+
end.
142+
143+
%%-----------------------------------------------------------------------------
144+
%% @param Pid of the uart port to be written to
145+
%% @param Data to be written to the given uart port
146+
%% @returns ok or {error, Reason}
147+
%% @doc Write data to a UART port
148+
%%
149+
%% This function will write the given data to the UART port.
150+
%% @end
151+
%%-----------------------------------------------------------------------------
152+
-spec write(Pid :: pid(), Data :: iodata()) -> ok | {error, _Reason :: term()}.
153+
write(Pid, Data) ->
154+
case is_iolist(Data) andalso is_pid(Pid) of
38155
true ->
39-
port:call(Pid, {write, B});
156+
port:call(Pid, {write, Data});
40157
false ->
41-
throw(badarg)
158+
error(badarg)
42159
end.
43160

44161
%% @private

0 commit comments

Comments
 (0)