A robust Wi-Fi connection manager for CircuitPython. Give it several networks
in settings.toml and it connects to the best available one, verifies the
connection actually works, and (optionally) syncs the clock over NTP.
Built for boards that roam — a sensor node that might see your home network, a workshop AP, or a phone hotspot depending on where it is — but just as handy for a single-network board that wants connection verification and time sync.
- Priority-based multi-network selection from
settings.toml— list up to 11 networks; it tries them in order, or by strongest signal if you prefer. - Connectivity verification at three levels: associated only, gateway reachable, or full internet reachability (ping with an HTTP fallback).
- Optional NTP time sync after connect, with a configurable resync interval.
- Drop-in
wifistand-in — unknown attributes proxy to the built-inwifimodule, so you can use the instance wherever you'd usewifi(e.g..radio). - Verbose logging, optional event callbacks, configurable timeouts/retries, and optional radio TX-power control.
- CircuitPython 9.x or later (developed on 10.x) on a Wi-Fi-capable board.
- Optional:
adafruit_ntp— only if you usesync_time=True. Seerequirements.txt.
Until this lands in the Community Bundle, grab the single file directly:
circup install adafruit_ntp # optional, recommended for NTP time sync
curl -O https://raw.githubusercontent.com/grgrant/GRGrant_CircuitPython_wifi_connection/main/wifi_connection.pyThen copy wifi_connection.py into CIRCUITPY/lib/.
Copy example_settings.toml to settings.toml and fill in your networks.
Never commit your real settings.toml — it holds Wi-Fi passwords. You can
use either style below, or both.
# Single network (CircuitPython's built-in vars, tried first / priority 0)
CIRCUITPY_WIFI_SSID = "MyHomeNetwork"
CIRCUITPY_WIFI_PASSWORD = "changeme"
# Or a numbered list, tried in order (priority 1..10)
WIFI_SSID0 = "MyHomeNetwork"
WIFI_PASSWORD0 = "changeme"
WIFI_SSID1 = "MyPhoneHotspot"
WIFI_PASSWORD1 = "changeme"
# ... up to WIFI_SSID9 / WIFI_PASSWORD9from wifi_connection import WiFiConnection
wifi = WiFiConnection(verbose=True, tz_offset=-5)
if wifi.connect():
print("Connected to", wifi.connected_ssid, "IP:", wifi.ip_address)Because unknown attributes proxy to the built-in wifi module, the instance
doubles as a wifi replacement:
wifi = WiFiConnection()
wifi.connect()
print(wifi.radio.ap_info.rssi) # proxied straight to wifi.radioAll are constructor keyword arguments; see the docstrings for the full list.
| Argument | Default | What it does |
|---|---|---|
verification_level |
2 |
0 = associated, 1 = gateway ping, 2 = internet. |
prefer_rssi |
False |
Order candidate networks by signal strength, not by number. |
sync_time |
True |
Sync the RTC over NTP after connecting. |
tz_offset |
0 |
Timezone offset in hours (e.g. -5). None is treated as 0. |
ntp_update_frequency |
7200 |
Seconds between NTP resyncs. |
wifi_timeout |
5 |
Connection timeout per SSID (seconds). |
overall_timeout |
30 |
Total time budget for the whole connect process. |
tx_power |
None |
Leave radio at board default, or set an explicit power. |
verbose |
False |
Print timestamped progress/diagnostic logs. |
- Level 0 — accept association without testing anything.
- Level 1 — confirm the gateway is reachable (ping). Good for LAN-only work.
- Level 2 — confirm the internet is reachable (ping a public target, with an HTTP GET fallback if ICMP is blocked). This is the default.
With sync_time=True (default), the RTC is set via NTP on first connect and re-synced
every ntp_update_frequency seconds. Pass tz_offset in hours; None is
normalized to 0 (UTC), so a caller that hasn't set a timezone still works.
Credentials are read only from settings.toml via os.getenv() — none are
stored in code. Keep settings.toml out of version control (only commit
example_settings.toml); the included .gitignore handles this.
MIT.