Library Utilities

Utility classes and function which are not part of the main library, but which assist in the building of servers using the library.

All code in here is strictly optional, and nothing inside the library should directly depend on it.

Wireless Network Support

Utility function and exceptions which handle the network initialisation for the Pico W are provided by the urest.utils.network_connect module. The majority of the urest.utils.network_connect module is boiler-plate, and mimics the C library set-up required by the Pico W. For compatibility with that library (and the documentation) the errors enumerated by the LinkStatus mirror the names and the code of the C library.

Pico W Only

This module will only work on the Pico W, and with the Pico W network library. Loading this module on a Pico H, or without the Pico W network library, will result in an error.

References

Classes

LinkStatus

Bases: IntEnum

Source code in urest/utils/network_connect.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
class LinkStatus(IntEnum):
    CYW43_LINK_DOWN = const(0)
    """Failure code indicating the wireless link is unavailable.

    Check the SSID is correct.
    """
    CYW43_LINK_JOIN = const(1)
    """Failure code indicating the SSID or password is incorrect and the
    specified SSID cannot be joined.

    Check the SSID and password.
    """
    CYW43_LINK_NOIP = const(2)
    """Failure code indicating the join request was successful: but no IP
    address was returned.

    Check the DHCP settings for the specified SSID
    """
    CYW43_LINK_UP = const(3)
    """Success code indicating a successful join, and that an IP address was
    obtained from the network."""
    CYW43_LINK_FAIL = const(-1)
    """General failure code.

    The link specified by the SSID is unavailable for unknown reasons.
    """
    CYW43_LINK_NONET = const(-2)
    """General failure code _after_ authentication.

    The authentication likely succeeded: but the subsequent attempts to
    complete the connection failed.
    """
    CYW43_LINK_BADAUTH = const(-3)
    """General failure code _before_ authentication.

    Check the supplied password.
    """
Attributes
CYW43_LINK_BADAUTH = const(-3)

General failure code before authentication.

Check the supplied password.

CYW43_LINK_DOWN = const(0)

Failure code indicating the wireless link is unavailable.

Check the SSID is correct.

CYW43_LINK_FAIL = const(-1)

General failure code.

The link specified by the SSID is unavailable for unknown reasons.

CYW43_LINK_JOIN = const(1)

Failure code indicating the SSID or password is incorrect and the specified SSID cannot be joined.

Check the SSID and password.

CYW43_LINK_NOIP = const(2)

Failure code indicating the join request was successful: but no IP address was returned.

Check the DHCP settings for the specified SSID

CYW43_LINK_NONET = const(-2)

General failure code after authentication.

The authentication likely succeeded: but the subsequent attempts to complete the connection failed.

CYW43_LINK_UP = const(3)

Success code indicating a successful join, and that an IP address was obtained from the network.

WirelessNetworkError

Bases: Exception

General Wireless Network Exception. Thrown in response to one of the codes listed in the module Attributes, and originating from the underlying C library.

The message of the the Exception is set to an appropriate response, and it should be assumed that the text of the Exception will be passed back to the user. Therefore it is important that the message aids further debugging without having to consult the underlying library documentation.

Source code in urest/utils/network_connect.py
119
120
121
122
123
124
125
126
127
128
129
130
131
class WirelessNetworkError(Exception):
    """General Wireless Network Exception. Thrown in response to one of the
    codes listed in the module `Attributes`, and originating from the
    underlying C library.

    The message of the the `Exception` is set to an appropriate
    response, and it should be assumed that the text of the `Exception`
    will be passed back to the user. Therefore it is important that the
    message aids further debugging without having to consult the
    underlying library documentation.
    """

    pass

Functions

netcode_to_str

netcode_to_str(error_code: LinkStatus) -> str

Convert the given wireless network error code to a short string, indicating the possible error.

Note that no validation for sanity of the error code is attempted by this function. However the returned values are expected to be displayed directly to the user, and so should indicate (at least minimally) where further investigation might be helpful.

Source code in urest/utils/network_connect.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def netcode_to_str(error_code: LinkStatus) -> str:
    """Convert the given wireless network error code to a short string,
    indicating the possible error.

    Note that no validation for sanity of the error code is attempted by
    this function. However the returned values are expected to be
    displayed directly to the user, and so should indicate (at least
    minimally) where further investigation might be helpful.
    """
    if error_code == LinkStatus.CYW43_LINK_DOWN:
        return "The wireless link is unavailable - check the SSID is correct"
    elif error_code == LinkStatus.CYW43_LINK_JOIN:
        return "The SSID or password is incorrect  - check the SSID and password."
    elif error_code == LinkStatus.CYW43_LINK_NOIP:
        return (
            "The join request was successful, but no IP address was returned - check"
            " the DHCP settings for the specified SSID"
        )
    elif error_code == LinkStatus.CYW43_LINK_UP:
        return "Successfully joined, and an IP address was obtained from the network"
    elif error_code == LinkStatus.CYW43_LINK_FAIL:
        return "The link specified by the SSID is unavailable for unknown reasons"
    elif error_code == LinkStatus.CYW43_LINK_NONET:
        return (
            "The authentication likely succeeded: but the subsequent attempts to"
            " complete the connection failed"
        )
    elif error_code == LinkStatus.CYW43_LINK_BADAUTH:
        return (
            "General failure code before authentication - check the supplied password."
        )
    else:
        return "Unknown error"

wireless_enable

wireless_enable(
    ssid: str,
    password: str,
    link_light: Union[int, str] = "WL_GPIO0",
) -> None

Enable the default wireless interface, connecting to the networking using the specified ssid and password. Optionally also supply the name, or the pin number, of the GPIO pin to use as the link_light, which will be ‘on’ if the network connection succeeds (and ‘off’ otherwise).

Clear Text Password

As with the default wireless library, the password must be supplied to this function in clear text. This presents a potential exposure risk of the password, and that risk should be mitigated by appropriate storage and handling of the password before calling this function.

Parameters:
  • ssid (str) –

    The SSID of the network to connect to.

  • password (str) –

    The plain text of the password needed by the wireless network.

  • link_light (Union[int, str], default: 'WL_GPIO0' ) –

    The name (if a str) or number (if an int) of the GPIO pin to use as the link light. If a connection succeeds, this GPIO Pin will be set ‘high’: otherwise ‘low’ on failure. Defaults to the on-board (user) LED of the Pico W.

Raises:
  • NameError

    The name (int or str) for the link_light is invalid, and a Pin with this name cannot be created.

  • WirelessNetworkError

    The network named ssid cannot be joined with the specified password. See the exact msg of the Exception to determine whether this is a temporary (and may be retried) or a permanent error.

Source code in urest/utils/network_connect.py
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
def wireless_enable(
    ssid: str,
    password: str,
    link_light: Union[int, str] = "WL_GPIO0",
) -> None:
    """Enable the default wireless interface, connecting to the networking
    using the specified `ssid` and `password`. Optionally also supply the name,
    or the pin number, of the GPIO pin to use as the `link_light`, which will
    be 'on' if the network connection succeeds (and 'off' otherwise).

    !!! danger "Clear Text Password"

        As with the default wireless library, the `password` **must** be supplied to
        this function in clear text. This presents a potential exposure risk
        of the `password`, and that risk should be mitigated by appropriate storage and
        handling of the password _before_ calling this function.

    Parameters
    ----------

    ssid: str
        The SSID of the network to connect to.
    password: str
        The plain text of the password needed by the wireless network.
    link_light: int or str, optional
        The name (if a `str`) or number (if an `int`) of the GPIO pin to use as
        the link light. If a connection succeeds, this GPIO Pin will be set 'high':
        otherwise 'low' on failure. Defaults to the on-board (user) LED of the Pico W.

    Raises
    ------

    NameError
        The name (`int` or `str`) for the `link_light` is invalid, and a `Pin` with this name cannot be created.
    WirelessNetworkError
        The network named `ssid` cannot be joined with the specified `password`. See the exact `msg` of the `Exception` to determine whether this is a temporary (and may be retried) or a permanent error.

    """

    # Set-up the link status LED
    try:
        if link_light is not None:
            link_status = Pin(link_light, Pin.OUT)
    except NameError:
        print("Cannot initialise the requested link pin")

    # Set-up the Wireless Driver
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)

    # Number of connection attempts
    # before a hard fail
    max_wait = 10

    while max_wait > 0:
        if wlan.status() == LinkStatus.CYW43_LINK_UP:
            break

        max_wait -= 1
        print("waiting for connection...")
        time.sleep(1)

    # Handle connection error
    if wlan.status() != LinkStatus.CYW43_LINK_UP:
        msg = f"Network connection attempt failed: { netcode_to_str(wlan.status()) } (Code: {wlan.status()})"
        raise WirelessNetworkError(msg)
    else:
        print("Connected")
        print("IP: " + wlan.ifconfig()[0])

    # Display the link light if connected
    if wlan.status() == LinkStatus.CYW43_LINK_UP:
        link_status.on()
    else:
        link_status.off()