From f8c0519c8a845f819c8515f19ef163aba9c1bb96 Mon Sep 17 00:00:00 2001 From: Pete Bell Date: Thu, 11 Dec 2025 14:15:23 +0000 Subject: [PATCH 1/2] Add WiFi connectivity support for Raspberry Pi Pico W - Introduced WiFi class for managing WiFi connections. - Added example scripts for connecting to WiFi, retrieving connection info, and handling reconnections. - Updated documentation to include WiFi usage examples. - Updated version to 0.7.0. --- .gitignore | 3 + docs/api.rst | 9 ++ docs/changelog.rst | 8 ++ docs/conf.py | 2 +- docs/examples/wifi_connect.py | 8 ++ docs/examples/wifi_info.py | 28 +++++ docs/examples/wifi_reconnect.py | 29 +++++ docs/recipes.rst | 24 ++++ package.json | 2 +- picozero/__init__.py | 3 +- picozero/picozero.py | 190 ++++++++++++++++++++++++++++++++ setup.py | 2 +- 12 files changed, 304 insertions(+), 4 deletions(-) create mode 100644 docs/examples/wifi_connect.py create mode 100644 docs/examples/wifi_info.py create mode 100644 docs/examples/wifi_reconnect.py diff --git a/.gitignore b/.gitignore index 9fefe27..72ebe49 100644 --- a/.gitignore +++ b/.gitignore @@ -147,3 +147,6 @@ tags # MKDocs build site/ + +# MicroPico +.micropico \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index 25680ac..8aabc8e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -165,6 +165,15 @@ DigitalInputDevice :inherited-members: :members: +WiFi +---- + +.. autoclass:: WiFi + :show-inheritance: + :inherited-members: + :members: + + pinout ------ diff --git a/docs/changelog.rst b/docs/changelog.rst index 2481b1e..4be4509 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,14 @@ Change log .. currentmodule:: picozero +0.7.0 - 2025-12-11 +----------- + ++ Introduced ``WiFi`` class for connecting to wireless networks on Pico W ++ Added ``brightness`` parameter to ``RGBLED`` class to control overall LED brightness ++ Added ``min_value`` and ``max_value`` parameters to ``PWMOutputDevice.pulse()`` and ``blink()`` methods to control brightness range ++ Updated documentation + 0.6.1 - 2025-11-28 ----------- diff --git a/docs/conf.py b/docs/conf.py index 7cc3dc7..456b670 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -64,7 +64,7 @@ def __getattr__(cls, name): author = "Raspberry Pi Foundation" # The full version, including alpha/beta/rc tags -release = "0.6.1" +release = "0.7.0" # -- General configuration --------------------------------------------------- diff --git a/docs/examples/wifi_connect.py b/docs/examples/wifi_connect.py new file mode 100644 index 0000000..6f83917 --- /dev/null +++ b/docs/examples/wifi_connect.py @@ -0,0 +1,8 @@ +""" +Simple WiFi connection example for Raspberry Pi Pico W +""" + +from picozero import WiFi + +wifi = WiFi() +wifi.connect("YourNetworkName", "YourPassword") diff --git a/docs/examples/wifi_info.py b/docs/examples/wifi_info.py new file mode 100644 index 0000000..bc70644 --- /dev/null +++ b/docs/examples/wifi_info.py @@ -0,0 +1,28 @@ +""" +Get detailed WiFi connection information +""" + +from picozero import WiFi + +wifi = WiFi() +wifi.connect("YourNetworkName", "YourPassword") + +# Get all connection info +info = wifi.info() +print("Network:", info["ssid"]) +print("IP:", info["ip"]) +print("Subnet:", info["subnet"]) +print("Gateway:", info["gateway"]) +print("DNS:", info["dns"]) +print("Signal:", info["signal"], "dBm") + +# Check signal strength +signal = wifi.signal_strength +if signal > -50: + print("Excellent signal!") +elif signal > -60: + print("Good signal") +elif signal > -70: + print("Fair signal") +else: + print("Weak signal") diff --git a/docs/examples/wifi_reconnect.py b/docs/examples/wifi_reconnect.py new file mode 100644 index 0000000..41ec5bf --- /dev/null +++ b/docs/examples/wifi_reconnect.py @@ -0,0 +1,29 @@ +""" +How to handle WiFi reconnection in a long-running program + +For programs that run continuously, you may want to check the connection +periodically and reconnect if it's been lost. +""" + +from picozero import WiFi +from time import sleep + +wifi = WiFi() +wifi.connect("YourNetworkName", "YourPassword") + +while True: + # Check if still connected + if not wifi.is_connected: + print("Connection lost! Reconnecting...") + try: + wifi.connect("YourNetworkName", "YourPassword") + print("Reconnected! IP:", wifi.ip) + except RuntimeError as e: + print("Reconnection failed:", e) + print("Will retry in 60 seconds...") + + # Do your work here + print("Signal strength:", wifi.signal_strength, "dBm") + + # Check again in 60 seconds + sleep(60) diff --git a/docs/recipes.rst b/docs/recipes.rst index 6288a24..ce5464f 100644 --- a/docs/recipes.rst +++ b/docs/recipes.rst @@ -335,3 +335,27 @@ Get the distance in metres from an ultrasonic distance sensor (HC-SR04): :alt: A diagram of the Raspberry Pi Pico connected to an HC-SR04 distance sensor. .. literalinclude:: examples/ultrasonic_distance_sensor.py + +WiFi (Pico W only) +------------------ + +Connect to a WiFi network +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Connect your Raspberry Pi Pico W to a wireless network using :class:`WiFi`: + +.. literalinclude:: examples/wifi_connect.py + +Get connection information +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Get detailed information about the WiFi connection including IP address and signal strength: + +.. literalinclude:: examples/wifi_info.py + +Handle reconnection +~~~~~~~~~~~~~~~~~~~ + +For long-running programs, check the connection status and reconnect if needed: + +.. literalinclude:: examples/wifi_reconnect.py diff --git a/package.json b/package.json index 60ac58b..63d7080 100644 --- a/package.json +++ b/package.json @@ -5,5 +5,5 @@ ], "deps": [ ], - "version": "0.6.1" + "version": "0.7.0" } diff --git a/picozero/__init__.py b/picozero/__init__.py index 653b078..37c7a4a 100644 --- a/picozero/__init__.py +++ b/picozero/__init__.py @@ -1,6 +1,6 @@ __name__ = "picozero" __package__ = "picozero" -__version__ = "0.6.1" +__version__ = "0.7.0" __author__ = "Raspberry Pi Foundation" from .picozero import ( @@ -34,4 +34,5 @@ TempSensor, Thermistor, DistanceSensor, + WiFi, ) diff --git a/picozero/picozero.py b/picozero/picozero.py index 2e748bb..50d4856 100644 --- a/picozero/picozero.py +++ b/picozero/picozero.py @@ -2,6 +2,12 @@ from micropython import schedule from time import ticks_ms, ticks_us, sleep +try: + import network + _network_available = True +except ImportError: + _network_available = False + ############################################################################### # EXCEPTIONS ############################################################################### @@ -2637,3 +2643,187 @@ def max_distance(self): Returns the maximum distance that the sensor will measure in metres. """ return self._max_distance + + +############################################################################### +# NETWORK +############################################################################### + + +class WiFi: + """ + Provides WiFi connectivity for Raspberry Pi Pico W. + + Basic usage:: + + from picozero import WiFi + + wifi = WiFi() + wifi.connect("MyNetwork", "password123") + print(wifi.ip) + + Or use the convenience function:: + + from picozero import connect_wifi + + ip = connect_wifi("MyNetwork", "password123") + print(ip) + + :param int timeout: + Default timeout in seconds for connection attempts. Defaults to 10. + """ + + def __init__(self, timeout=10): + if not _network_available: + raise ImportError("network module not available - WiFi requires Pico W") + self._timeout = timeout + self._sta = None + + def connect(self, ssid, password, timeout=None): + """ + Connect to a WiFi network. + + :param str ssid: + The network name (SSID) to connect to. + + :param str password: + The network password. + + :param int timeout: + Connection timeout in seconds. If None, uses the default timeout + set during initialization. Defaults to None. + + :returns: + The IP address assigned to the Pico W. + + :raises RuntimeError: + If connection fails or times out. + """ + if timeout is None: + timeout = self._timeout + + self._sta = network.WLAN(network.STA_IF) + + if not self._sta.active(): + self._sta.active(True) + + if self._sta.isconnected(): + return self._sta.ifconfig()[0] + + self._sta.connect(ssid, password) + + # Wait for connection with timeout + elapsed = 0 + while not self._sta.isconnected() and elapsed < timeout: + sleep(0.2) + elapsed += 0.2 + + if not self._sta.isconnected(): + self._sta.active(False) + raise RuntimeError( + f"Failed to connect to '{ssid}' - check SSID and password" + ) + + return self._sta.ifconfig()[0] + + def disconnect(self): + """ + Disconnect from the WiFi network and deactivate the interface. + """ + if self._sta is not None: + self._sta.disconnect() + self._sta.active(False) + self._sta = None + + @property + def is_connected(self): + """ + Returns True if currently connected to a WiFi network. + """ + return self._sta is not None and self._sta.isconnected() + + @property + def ip(self): + """ + Returns the current IP address, or None if not connected. + """ + if self.is_connected: + return self._sta.ifconfig()[0] + return None + + @property + def subnet(self): + """ + Returns the subnet mask, or None if not connected. + """ + if self.is_connected: + return self._sta.ifconfig()[1] + return None + + @property + def gateway(self): + """ + Returns the gateway address, or None if not connected. + """ + if self.is_connected: + return self._sta.ifconfig()[2] + return None + + @property + def dns(self): + """ + Returns the DNS server address, or None if not connected. + """ + if self.is_connected: + return self._sta.ifconfig()[3] + return None + + @property + def signal_strength(self): + """ + Returns the WiFi signal strength (RSSI) in dBm, or None if not connected. + + Typical values: + - -30 dBm: Excellent signal + - -50 dBm: Very good signal + - -60 dBm: Good signal + - -70 dBm: Fair signal + - -80 dBm: Weak signal + - -90 dBm: Very weak signal + """ + if self.is_connected: + return self._sta.status('rssi') + return None + + @property + def ssid(self): + """ + Returns the SSID of the connected network, or None if not connected. + """ + if self.is_connected: + return self._sta.config('ssid') + return None + + def info(self): + """ + Returns a dictionary with connection information, or None if not connected. + + The dictionary includes: + - ip: IP address + - subnet: Subnet mask + - gateway: Gateway address + - dns: DNS server address + - ssid: Network name + - signal: Signal strength in dBm + """ + if not self.is_connected: + return None + + return { + 'ip': self.ip, + 'subnet': self.subnet, + 'gateway': self.gateway, + 'dns': self.dns, + 'ssid': self.ssid, + 'signal': self.signal_strength + } diff --git a/setup.py b/setup.py index 12aa14d..8dff964 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ __project__ = "picozero" __packages__ = ["picozero"] __desc__ = "A beginner-friendly library for using common electronics components with the Raspberry Pi Pico. " -__version__ = "0.6.1" +__version__ = "0.7.0" __author__ = "Raspberry Pi Foundation" __author_email__ = "learning@raspberrypi.org" __license__ = "MIT" From 84ddb81b7277970e693d53c4fa40ab356378946e Mon Sep 17 00:00:00 2001 From: Pete Bell Date: Thu, 11 Dec 2025 15:02:28 +0000 Subject: [PATCH 2/2] Add WiFi connectivity support for Raspberry Pi Pico W with separate module --- picozero/__init__.py | 8 +- picozero/picozero.py | 189 ------------------------------------------- picozero/wifi.py | 172 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 190 deletions(-) create mode 100644 picozero/wifi.py diff --git a/picozero/__init__.py b/picozero/__init__.py index 37c7a4a..aa68fe8 100644 --- a/picozero/__init__.py +++ b/picozero/__init__.py @@ -34,5 +34,11 @@ TempSensor, Thermistor, DistanceSensor, - WiFi, ) + +# WiFi is only available on Pico W, so import it from a separate module to avoid +# memory issues on regular Pico +try: + from .wifi import WiFi +except ImportError: + pass diff --git a/picozero/picozero.py b/picozero/picozero.py index 50d4856..176047a 100644 --- a/picozero/picozero.py +++ b/picozero/picozero.py @@ -2,12 +2,6 @@ from micropython import schedule from time import ticks_ms, ticks_us, sleep -try: - import network - _network_available = True -except ImportError: - _network_available = False - ############################################################################### # EXCEPTIONS ############################################################################### @@ -2644,186 +2638,3 @@ def max_distance(self): """ return self._max_distance - -############################################################################### -# NETWORK -############################################################################### - - -class WiFi: - """ - Provides WiFi connectivity for Raspberry Pi Pico W. - - Basic usage:: - - from picozero import WiFi - - wifi = WiFi() - wifi.connect("MyNetwork", "password123") - print(wifi.ip) - - Or use the convenience function:: - - from picozero import connect_wifi - - ip = connect_wifi("MyNetwork", "password123") - print(ip) - - :param int timeout: - Default timeout in seconds for connection attempts. Defaults to 10. - """ - - def __init__(self, timeout=10): - if not _network_available: - raise ImportError("network module not available - WiFi requires Pico W") - self._timeout = timeout - self._sta = None - - def connect(self, ssid, password, timeout=None): - """ - Connect to a WiFi network. - - :param str ssid: - The network name (SSID) to connect to. - - :param str password: - The network password. - - :param int timeout: - Connection timeout in seconds. If None, uses the default timeout - set during initialization. Defaults to None. - - :returns: - The IP address assigned to the Pico W. - - :raises RuntimeError: - If connection fails or times out. - """ - if timeout is None: - timeout = self._timeout - - self._sta = network.WLAN(network.STA_IF) - - if not self._sta.active(): - self._sta.active(True) - - if self._sta.isconnected(): - return self._sta.ifconfig()[0] - - self._sta.connect(ssid, password) - - # Wait for connection with timeout - elapsed = 0 - while not self._sta.isconnected() and elapsed < timeout: - sleep(0.2) - elapsed += 0.2 - - if not self._sta.isconnected(): - self._sta.active(False) - raise RuntimeError( - f"Failed to connect to '{ssid}' - check SSID and password" - ) - - return self._sta.ifconfig()[0] - - def disconnect(self): - """ - Disconnect from the WiFi network and deactivate the interface. - """ - if self._sta is not None: - self._sta.disconnect() - self._sta.active(False) - self._sta = None - - @property - def is_connected(self): - """ - Returns True if currently connected to a WiFi network. - """ - return self._sta is not None and self._sta.isconnected() - - @property - def ip(self): - """ - Returns the current IP address, or None if not connected. - """ - if self.is_connected: - return self._sta.ifconfig()[0] - return None - - @property - def subnet(self): - """ - Returns the subnet mask, or None if not connected. - """ - if self.is_connected: - return self._sta.ifconfig()[1] - return None - - @property - def gateway(self): - """ - Returns the gateway address, or None if not connected. - """ - if self.is_connected: - return self._sta.ifconfig()[2] - return None - - @property - def dns(self): - """ - Returns the DNS server address, or None if not connected. - """ - if self.is_connected: - return self._sta.ifconfig()[3] - return None - - @property - def signal_strength(self): - """ - Returns the WiFi signal strength (RSSI) in dBm, or None if not connected. - - Typical values: - - -30 dBm: Excellent signal - - -50 dBm: Very good signal - - -60 dBm: Good signal - - -70 dBm: Fair signal - - -80 dBm: Weak signal - - -90 dBm: Very weak signal - """ - if self.is_connected: - return self._sta.status('rssi') - return None - - @property - def ssid(self): - """ - Returns the SSID of the connected network, or None if not connected. - """ - if self.is_connected: - return self._sta.config('ssid') - return None - - def info(self): - """ - Returns a dictionary with connection information, or None if not connected. - - The dictionary includes: - - ip: IP address - - subnet: Subnet mask - - gateway: Gateway address - - dns: DNS server address - - ssid: Network name - - signal: Signal strength in dBm - """ - if not self.is_connected: - return None - - return { - 'ip': self.ip, - 'subnet': self.subnet, - 'gateway': self.gateway, - 'dns': self.dns, - 'ssid': self.ssid, - 'signal': self.signal_strength - } diff --git a/picozero/wifi.py b/picozero/wifi.py new file mode 100644 index 0000000..0971f48 --- /dev/null +++ b/picozero/wifi.py @@ -0,0 +1,172 @@ +"""WiFi connectivity support for Raspberry Pi Pico W.""" + +from time import sleep + +try: + import network as _network_module + + class WiFi: + """ + Provides WiFi connectivity for Raspberry Pi Pico W. + + :param int timeout: + Default timeout in seconds for connection attempts. Defaults to 10. + """ + + def __init__(self, timeout=10): + self._network = _network_module + self._timeout = timeout + self._sta = None + + def connect(self, ssid, password, timeout=None): + """ + Connect to a WiFi network. + + :param str ssid: + The network name (SSID) to connect to. + + :param str password: + The network password. + + :param int timeout: + Connection timeout in seconds. If None, uses the default timeout + set during initialization. Defaults to None. + + :returns: + The IP address assigned to the Pico W. + + :raises RuntimeError: + If connection fails or times out. + """ + if timeout is None: + timeout = self._timeout + + self._sta = self._network.WLAN(self._network.STA_IF) + + if not self._sta.active(): + self._sta.active(True) + + if self._sta.isconnected(): + return self._sta.ifconfig()[0] + + self._sta.connect(ssid, password) + + # Wait for connection with timeout + elapsed = 0 + while not self._sta.isconnected() and elapsed < timeout: + sleep(0.2) + elapsed += 0.2 + + if not self._sta.isconnected(): + self._sta.active(False) + raise RuntimeError( + f"Failed to connect to '{ssid}' - check SSID and password" + ) + + return self._sta.ifconfig()[0] + + def disconnect(self): + """ + Disconnect from the WiFi network and deactivate the interface. + """ + if self._sta is not None: + self._sta.disconnect() + self._sta.active(False) + self._sta = None + + @property + def is_connected(self): + """ + Returns True if currently connected to a WiFi network. + """ + return self._sta is not None and self._sta.isconnected() + + @property + def ip(self): + """ + Returns the current IP address, or None if not connected. + """ + if self.is_connected: + return self._sta.ifconfig()[0] + return None + + @property + def subnet(self): + """ + Returns the subnet mask, or None if not connected. + """ + if self.is_connected: + return self._sta.ifconfig()[1] + return None + + @property + def gateway(self): + """ + Returns the gateway address, or None if not connected. + """ + if self.is_connected: + return self._sta.ifconfig()[2] + return None + + @property + def dns(self): + """ + Returns the DNS server address, or None if not connected. + """ + if self.is_connected: + return self._sta.ifconfig()[3] + return None + + @property + def signal_strength(self): + """ + Returns the WiFi signal strength (RSSI) in dBm, or None if not connected. + + Typical values: + - -30 dBm: Excellent signal + - -50 dBm: Very good signal + - -60 dBm: Good signal + - -70 dBm: Fair signal + - -80 dBm: Weak signal + - -90 dBm: Very weak signal + """ + if self.is_connected: + return self._sta.status('rssi') + return None + + @property + def ssid(self): + """ + Returns the SSID of the connected network, or None if not connected. + """ + if self.is_connected: + return self._sta.config('ssid') + return None + + def info(self): + """ + Returns a dictionary with connection information, or None if not connected. + + The dictionary includes: + - ip: IP address + - subnet: Subnet mask + - gateway: Gateway address + - dns: DNS server address + - ssid: Network name + - signal: Signal strength in dBm + """ + if not self.is_connected: + return None + + return { + 'ip': self.ip, + 'subnet': self.subnet, + 'gateway': self.gateway, + 'dns': self.dns, + 'ssid': self.ssid, + 'signal': self.signal_strength + } + +except ImportError: + # WiFi not available on regular Pico + pass