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

Enhanced connection management for esp32 network driver #1181

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added support for `registered_name` in `erlang:process_info/2` and `Process.info/2`
- Added `net:gethostname/0` on platforms with gethostname(3).
- Added `socket:getopt/2`
- Added `network:connect/0,1` and `network:disconnect/0` to ESP32 network driver.
- Added `network:sta_status/0` to get the current connection state of the sta interface.

### Fixed
- ESP32: improved sntp sync speed from a cold boot.

### Changed

- Using a custom callback for STA disconnected events in esp32 network driver will stop automatic re-connect,
allowing applications to use scan results or other means to decide when and where to connect.

## [0.6.6] - Unreleased

### Added
Expand Down
25 changes: 23 additions & 2 deletions doc/src/network-programming-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ The `<sta-properties>` property list should contain the following entries:
* `{ssid, string() | binary()}` The SSID to which the device should connect.
* `{psk, string() | binary()}` The password required to authenticate to the network, if required.

Optionally on the ESP32 platform, using the `managed` atom in the configuration, the `ssid` and `psk` may be omitted, but if they are also supplied a connection will not be initiated immediately. The initially configured connection can be started using [`network:connect/0`](./apidocs/erlang/eavmlib/network.md#connect0). This will allow for the use of `network:wifi_scan` to find available access points, and connecting with [`network:connect/1`](./apidocs/erlang/eavmlib/network.md#connect1) to update the `ssid` and `psk` for a new connection. When starting the driver in this mode all callback functions must be configured when the driver is started, and providing a callback for `disconnected` events is recommended, so the application can also control when, and to which access point, it will make a new connection.

The [`network:start/1`](./apidocs/erlang/eavmlib/network.md#start1) will immediately return `{ok, Pid}`, where `Pid` is the process ID of the network server instance, if the network was properly initialized, or `{error, Reason}`, if there was an error in configuration. However, the application may want to wait for the device to connect to the target network and obtain an IP address, for example, before starting clients or services that require network access.

Applications can specify callback functions, which get triggered as events emerge from the network layer, including connection to and disconnection from the target network, as well as IP address acquisition.

Callback functions can be specified by the following configuration parameters:

* `{connected, fun(() -> term())}` A callback function which will be called when the device connects to the target network.
* `{disconnected, fun(() -> term())}` A callback function which will be called when the device disconnects from the target network.
* `{disconnected, fun(() -> term())}` A callback function which will be called when the device disconnects from the target network. If no callback function is provided the default behavior is to attempt to reconnect immediately. By providing a callback function the application can decide whether to reconnect, or connect to a new access point.
* `{got_ip, fun((ip_info()) -> term())}` A callback function which will be called when the device obtains an IP address. In this case, the IPv4 IP address, net mask, and gateway are provided as a parameter to the callback function.

```{warning}
Expand Down Expand Up @@ -75,7 +77,8 @@ gotIp(IpInfo) ->
io:format("Got IP: ~p~n", [IpInfo]).

disconnected() ->
io:format("Disconnected from AP.~n").
io:format("Disconnected from AP, attempting to reconnect~n"),
network:connect().
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need a delay here eg 500-1000ms or something? (ideally exponential backoff - but static delay should be fine..)

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Jun 7, 2024

Choose a reason for hiding this comment

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

No. This just signals the driver to start attempting a connection, if that attempt succeeds the connected callback will be triggered, otherwise a disconnected callback will be triggered - in which case the attempt will be made again… this would function exactly as the driver always has… by defining a custom callback the user could add any extra delay, or scan for networks instead.

Copy link
Collaborator Author

@UncleGrumpy UncleGrumpy Jun 7, 2024

Choose a reason for hiding this comment

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

The only difference is this was connection was called internally, so stop would never be possible. Now by defining a callback the user can control when a reconnection (or now a new connection) will happen.

```

In a typical application, the network should be configured and an IP address should be acquired first, before starting clients or services that have a dependency on the network.
Expand All @@ -102,6 +105,24 @@ end

To obtain the signal strength (in decibels) of the connection to the associated access point use [`network:sta_rssi/0`](./apidocs/erlang/eavmlib/network.md#sta_rssi0).

### STA (or AP+STA) mode functions

#### `sta_status`

The function [`network:sta_status/0`](./apidocs/erlang/eavmlib/network.md#sta_status0) may be used any time after the driver has been started to get the current connection state of the sta interface. When a connection is initiated, either at start up or when `network:connect/1` is used in application `managed` mode (which will start with a `disconnected` state) the interface will be marked as `connecting` followed by `associated` after a connection is established with an access point. After receiving an IP address the connection will be fully `connected`. If a beacon timeout event is received (this indicates poor signal strength or a heavily congested network) the status will change to `degraded` for the remainder of the connection session. This does not always mean that the connection is still poor, but it can be a helpful diagnostic when experiencing network problems, and often does result is a dropped connection. When a stopping the interface with `network:disconnect/0` the state will change to `disconnecting` until the interface is completely stopped and set to `disconnected`.

#### `disconnect`

The function [`network:disconnect/0`](./apidocs/erlang/eavmlib/network.md#disconnect0) will disconnect a station from the associated access point. Note that using this function without providing a custom `disconnected` event callback function will result in the driver immediately attempting to reconnect to the last associated access point.

This function is currently only supported on the ESP32 platform.

#### `connect`

Using the function [`network:connect/0`](./apidocs/erlang/eavmlib/network.md#connect0) will start a connection to the last configured access point. To connect to a new access point use either a proplist consisting of `[{ssid, "Network Name"} | {psk, "Password"} | {dhcp_hostname, "hostname"}]`, or a complete `network_config()` consisting of `[sta_config() | sntp_config()]`. If any callback functions or default scan configuration options are defined in the `network_config()` they will be ignored; default scan options and callback functions must be configured when the driver is started.

This function is currently only supported on the ESP32 platform.

## AP mode

In AP mode, the ESP32 starts a WiFi network to which other devices (laptops, mobile devices, other ESP32 devices, etc) can connect. The ESP32 will create an IPv4 network, and will assign itself the address `192.168.4.1`. Devices that attach to the ESP32 in AP mode will be assigned sequential addresses in the `192.168.4.0/24` range, e.g., `192.168.4.2`, `192.168.4.3`, etc.
Expand Down
Loading
Loading