-
Notifications
You must be signed in to change notification settings - Fork 139
Add wifi_settings_connect library #94
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
base: develop
Are you sure you want to change the base?
Changes from all commits
9acc549
290c96d
3984658
cdde8e5
c6a5a21
06daea4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# | ||
# Copyright (c) 2025 Jack Whitham | ||
# | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
# | ||
# wifi_settings_connect | ||
# | ||
# Library to manage WiFi connections. It provides Flash storage | ||
# for WiFi passwords and hotspot names, and a background async_context | ||
# service to automatically connect to them. | ||
# | ||
|
||
if (NOT PICO_CYW43_SUPPORTED) | ||
message("wifi_settings_connect: WiFi hardware is required: run cmake -DPICO_BOARD=pico_w or -DPICO_BOARD=pico2_w") | ||
elseif (NOT TARGET pico_cyw43_arch) | ||
message("wifi_settings_connect: WiFi driver pico_cyw43_arch is not present") | ||
elseif (NOT TARGET pico_lwip_core) | ||
message("wifi_settings_connect: IP layer pico_lwip_core is not present") | ||
else() | ||
message("wifi_settings_connect: library is available.") | ||
add_library(wifi_settings_connect INTERFACE) | ||
set(WIFI_SETTINGS_VERSION_STRING "0.2.0c") | ||
set(WIFI_SETTINGS_PROJECT_URL "https://github.com/jwhitham/pico-wifi-settings") | ||
|
||
target_compile_definitions(wifi_settings_connect INTERFACE | ||
WIFI_SETTINGS_VERSION_STRING="${WIFI_SETTINGS_VERSION_STRING}" | ||
WIFI_SETTINGS_PROJECT_URL="${WIFI_SETTINGS_PROJECT_URL}" | ||
) | ||
|
||
target_include_directories(wifi_settings_connect INTERFACE | ||
${CMAKE_CURRENT_LIST_DIR}/include | ||
) | ||
|
||
target_sources(wifi_settings_connect INTERFACE | ||
${CMAKE_CURRENT_LIST_DIR}/wifi_settings_connect.c | ||
${CMAKE_CURRENT_LIST_DIR}/wifi_settings_flash_storage.c | ||
${CMAKE_CURRENT_LIST_DIR}/wifi_settings_flash_range.c | ||
${CMAKE_CURRENT_LIST_DIR}/wifi_settings_hostname.c | ||
) | ||
|
||
target_link_libraries(wifi_settings_connect INTERFACE | ||
pico_async_context_base | ||
pico_stdlib | ||
pico_cyw43_arch | ||
pico_lwip_core | ||
) | ||
endif() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# wifi\_settings\_connect | ||
|
||
This is a library to manage WiFi connections. It provides Flash storage | ||
for WiFi passwords and hotspot names, and a background async\_context | ||
service to automatically connect to them. You can store details for | ||
up to 100 hotspots and update them using | ||
[picotool](https://github.com/raspberrypi/pico-sdk-tools/releases). | ||
or a [setup application](https://github.com/jwhitham/pico-wifi-settings/releases/). | ||
This avoids any need to specify build-time flags such as `WIFI_SSID` and `WIFI_PASSWORD`. | ||
|
||
WiFi hotspot details are stored in a Flash sector that isn't normally used by programs, | ||
normally located near the end of Flash memory. This | ||
[wifi-settings file](doc/SETTINGS_FILE.md) is a simple text file which | ||
can be updated by USB or by installing a setup app from the | ||
[pico-wifi-settings home page](https://github.com/jwhitham/pico-wifi-settings) | ||
on Github. | ||
|
||
## Requirements | ||
|
||
- Raspberry Pi Pico W or Pico 2 W hardware | ||
- a "bare metal" C/C++ application for Pico W (not FreeRTOS) | ||
using the `cyw43` driver and `lwip` network stack | ||
which are provided with the [Pico SDK](https://github.com/raspberrypi/pico-sdk/). | ||
- between 2kb and 13kb of code space depending on options used | ||
- WiFi network(s) with a DHCP server and WPA authentication | ||
|
||
## How to use it | ||
|
||
First, you need to configure the WiFi settings file | ||
in Flash. See the [wifi-settings file documentation](doc/SETTINGS_FILE.md). | ||
|
||
Next, you need to modify your application to use wifi\_settings\_connect. | ||
This involves adding a few lines of C code. | ||
There is an [integration guide which explains what you need to do | ||
to add wifi\_settings\_connect to your application](doc/INTEGRATION.md). | ||
|
||
## Enabling remote updates | ||
|
||
wifi\_settings\_connect is a subset of a larger library (wifi\_settings) which | ||
also has support for remote updates of WiFi settings and over-the-air (OTA) | ||
firmware updates. Visit the | ||
[pico-wifi-settings home page](https://github.com/jwhitham/pico-wifi-settings) | ||
for more information. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# Integrating wifi\_settings\_connect into your own Pico application | ||
|
||
You can integrate wifi\_settings\_connect into your own Pico application | ||
with just a few lines of code: | ||
``` | ||
#include "wifi_settings/wifi_settings_connect.h" // << add this | ||
int main() { | ||
stdio_init_all(); | ||
if (wifi_settings_init() != 0) { // << and add this | ||
panic(...); | ||
} | ||
wifi_settings_connect(); // << and add this | ||
// and that's it... | ||
} | ||
``` | ||
The following steps go through the process in more detail for | ||
a [CMake](https://cmake.org) project stored in Git, | ||
similar to all of the official Pico projects. | ||
|
||
You may find it useful to look at [the example app for wifi\_settings\_connect in | ||
the pico-playground repository](https://github.com/raspberrypi/pico-playground/tree/master/wifi_settings_connect/example). | ||
|
||
## Modify CMakeLists.txt to use the library | ||
|
||
The `target_link_libraries` rule for your project should be extended to | ||
add `wifi_settings_connect`. | ||
``` | ||
target_link_libraries(your_app | ||
wifi_settings_connect | ||
pico_stdlib | ||
) | ||
``` | ||
If your project does not include the `pico-extras` repository, then this | ||
must also be added. | ||
``` | ||
include(pico_extras_import.cmake) | ||
``` | ||
You can copy the `pico_extras_import.cmake` file from [the root of | ||
the pico-playground repository](https://github.com/raspberrypi/pico-playground). | ||
|
||
### Additional configuration for LwIP and mbedtls | ||
|
||
If your project has not previously used WiFi you will also need | ||
to add one of the WiFi driver targets to `target_link_libraries`, e.g. | ||
`pico_cyw43_arch_lwip_background` or `pico_cyw43_arch_lwip_poll`. | ||
|
||
You will also need `lwipopts.h` in the project directory (this configures | ||
LwIP). You can copy an example from | ||
[here](https://github.com/raspberrypi/pico-playground/tree/master/wifi_settings_connect/example). | ||
|
||
## Include the header file | ||
|
||
Your main C/C++ source file (containing `main()`) should be modified to include | ||
`wifi_settings/wifi_settings_connect.h`: | ||
``` | ||
#include "wifi_settings/wifi_settings_connect.h" | ||
``` | ||
|
||
## Modify your main function | ||
|
||
Your `main()` function should be modified to call `wifi_settings_init()` once on startup. | ||
|
||
- This must *replace* any call to `cyw43` initialisation functions, because | ||
these are called from `wifi_settings_init()` (with the correct country code). | ||
- The call should be after `stdio_init_all()`. | ||
- If the call returns a non-zero value, an error has occurred. You do not have | ||
to handle this error; it is still safe to call other `wifi_settings` functions, | ||
but they will not work and will return error codes where appropriate. | ||
|
||
Your application should also call `wifi_settings_connect()` when it wishes to connect | ||
to WiFi. This can be called immediately after `wifi_settings_init()` or at any later | ||
time. `wifi_settings_connect()` does not block, as the connection takes | ||
place in the background. | ||
|
||
All other modifications are optional. You can now rebuild your application | ||
and it will include the wifi\_settings\_connect features. | ||
|
||
## CMake command line | ||
|
||
When running `cmake`, you need to provide the location of the `pico-extras` | ||
repository as well as the `pico-sdk` repository. This is typically done | ||
with `-DPICO_EXTRAS_PATH`, e.g.: | ||
``` | ||
cmake -DPICO_BOARD=pico_w \ | ||
-DPICO_SDK_PATH=/home/user/pico-sdk \ | ||
-DPICO_EXTRAS_PATH=/home/user/pico-extras \ | ||
.. | ||
``` | ||
|
||
# Optional modifications | ||
|
||
Your application can call `wifi_settings_is_connected()` at any time | ||
to determine if the WiFi connection is available or not. | ||
|
||
Your application can call various status functions at any time | ||
to get a text report on the connection status. This can be useful for debugging. | ||
Each function should be passed a `char[]` buffer for the output, along with the | ||
size of the buffer. | ||
|
||
- `wifi_settings_get_connect_status_text()` produces a line of | ||
text showing the connection status, e.g. `WiFi is connected to ssid1=MyHomeWiFi`. | ||
- `wifi_settings_get_hw_status_text()` produces a line of | ||
text describing the status of the `cyw43` hardware driver; this will be empty | ||
if the hardware is not initialised. | ||
- `wifi_settings_get_ip_status_text()` produces a line of | ||
text describing the status of the `lwip` network stack e.g. IP address; this will be empty | ||
if unconnected. | ||
- `wifi_settings_get_ip` produces the IP address by itself; this will be empty | ||
if unconnected. | ||
- `wifi_settings_get_ssid` produces the current SSID by itself; this will be empty | ||
if unconnected. If connected using a BSSID, this will be reported as | ||
a `:`-separated lower-case MAC address, e.g. `01:23:45:67:89:ab`. If the wifi-settings | ||
file has been updated since the connection was made, then the result may be `?`, | ||
as the SSID is found by searching the wifi-settings file. | ||
|
||
There is also a function to report the current connection state. | ||
`wifi_settings_get_ssid_status()` returns | ||
a pointer to a static string, indicating the status of a connection attempt to | ||
an SSID, e.g. `SUCCESS`, `NOT FOUND`. | ||
|
||
Your application can call `wifi_settings_disconnect()` to force disconnect, | ||
or `wifi_settings_deinit()` to deinitialise the driver, but this is never necessary | ||
and these steps can be left out. They exist to allow the application to shut down WiFi, | ||
e.g. to save power, or in order to control the WiFi hardware directly for some other | ||
purpose. For example, the | ||
[setup app](https://github.com/jwhitham/pico-wifi-settings/tree/master/doc/SETUP_APP.md) | ||
uses this feature to perform its own WiFi scan. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
# Creating and updating a WiFi settings file | ||
|
||
wifi\_settings\_connect stores WiFi hotspot names and passwords | ||
in a Flash sector that isn't normally used by programs. This | ||
is called the "WiFi settings file". It is similar to a file on a disk, | ||
except that it is always at the same location, and the size is | ||
limited to 4096 bytes. | ||
|
||
The file can be updated over USB by using [picotool](https://github.com/raspberrypi/pico-sdk-tools/releases). | ||
It is a text file which can be edited with any text editor. | ||
Here is an example of typical contents: | ||
``` | ||
ssid1=MyHomeWiFi | ||
pass1=mypassword1234 | ||
ssid2=MyPhoneHotspot | ||
pass2=secretpassword | ||
country=GB | ||
``` | ||
wifi\_settings\_connect will automatically scan for hotspots and connect to | ||
lurch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
hotspots matching the SSID names and passwords in the file. | ||
|
||
- On the [pico-wifi-settings home page](https://github.com/jwhitham/pico-wifi-settings) | ||
you can also find a setup app which runs on your Pico and automates much of the | ||
setup process. The pico-wifi-settings library is a superset of | ||
wifi\_settings\_connect. It includes a remote update feature that allows | ||
a new wifi-settings file to be installed via WiFi. | ||
|
||
# Creating the file on a computer | ||
|
||
Use any text editor to create a text file similar to the example above. | ||
|
||
Each line in the file should contain a key and a value, separated by `=`, | ||
with no spaces around `=`, or at the beginning or end of each line. | ||
|
||
The file must have at least `ssid1` or `bssid1`, otherwise there will | ||
be no connection attempts, and wifi\_settings\_connect will stay in the | ||
STORAGE\_EMPTY\_ERROR state. | ||
|
||
You can also use the following: | ||
|
||
- `ssid<N>` - SSID name for hotspot N (a number from 1 to 100) | ||
- `pass<N>` - Password for hotspot N | ||
- `country` - Your two-letter country code from [ISO-3166-1](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) | ||
- `bssid<N>` - The BSSID ID for hotspot N | ||
- `name` - The hostname of the Pico (sent to DHCP servers) | ||
|
||
# Copying the WiFi settings file by USB | ||
|
||
You can use | ||
[picotool](https://github.com/raspberrypi/pico-sdk-tools/releases). | ||
to copy the file from your computer to your Pico via USB. | ||
|
||
To use picotool, | ||
boot the Pico in bootloader mode by holding down the BOOTSEL button while plugging it | ||
into USB. In bootloader mode, you can upload files with picotool. | ||
The default address is 16kb before the final address in Flash: | ||
|
||
- On Pico W, use `0x101fc000` as the address. | ||
- On Pico 2 W, use `0x103fc000` as the address. | ||
|
||
You must also rename your WiFi settings file so that it ends with `.bin` as | ||
picotool is not able to upload files unless they are `.bin`, `.elf` or `.uf2`. | ||
|
||
Here is a sample upload command for Pico W (RP2040): | ||
``` | ||
picotool load -o 0x101fc000 mywifisettings.bin | ||
``` | ||
and the equivalent for Pico 2 W (RP2350): | ||
``` | ||
picotool load -o 0x103fc000 mywifisettings.bin | ||
``` | ||
|
||
## Location of the wifi-settings file | ||
|
||
The default location of the file (16kb before the final address in Flash) | ||
has been chosen because the final three 4kb Flash sectors are already assigned | ||
a function by the Pico SDK. The Bluetooth library uses two 4kb sectors for storage of | ||
devices that have been paired by Bluetooth. The final 4kb sector is used for a workaround | ||
for the RP2350-E10 bug - this sector may be erased when copying a UF2 file to a Pico 2 | ||
via drag-and-drop. Therefore, these three sectors are avoided. | ||
|
||
If you wish to store the wifi-settings file at a specific address you can | ||
do so by setting `-DWIFI_SETTINGS_FILE_ADDRESS=0x....` when running `cmake`. | ||
The value `0x...` should be an address relative to the start of Flash, so Flash address | ||
`0x1fc000` corresponds to absolute address `0x101fc000`. | ||
|
||
## Backing up a WiFi settings file | ||
|
||
picotool can be used to download WiFi settings files from a Pico W: | ||
``` | ||
picotool save -r 0x101fc000 0x101fd000 backup.bin | ||
``` | ||
and Pico 2 W (RP2350): | ||
``` | ||
picotool save -r 0x103fc000 0x103fd000 backup.bin | ||
``` | ||
Bytes after the end of the file will also be copied (usually either 0x00 or 0xff). | ||
These can be safely deleted. Some text editors will allow you to delete them, | ||
but if you have any difficulty, you can also remove them with a shell command such as: | ||
``` | ||
LC_ALL=C sed -i 's/[\x00\xFF]//g' backup.bin | ||
``` | ||
The backup is restored using `picotool load` as described in "Copying the WiFi settings file by USB". | ||
|
||
These examples use the default location for the wifi-settings file. If you | ||
are using a custom location, e.g. building with | ||
`-DWIFI_SETTINGS_FILE_ADDRESS=0x...`, then | ||
you would need to substitute the actual address. | ||
|
||
# File format details | ||
|
||
The file format is very simple so that it can be read by a simple algorithm | ||
that doesn't require much code space. The parser ignores any line that it | ||
doesn't understand, and skips any keys that are not known. Here are the rules: | ||
|
||
- The key and the value should be separated only by an `=` character, e.g. `ssid1=HomeWiFi`. | ||
- Lines that don't match the form `key=value` are completely ignored; | ||
you can add text, comments etc. in order to help you manage your configuration. | ||
- On a line that does match `key=value`, whitespace is NOT ignored. | ||
Be careful to avoid adding extra spaces around `=`. | ||
A space before `=` will be part of the key, and a space after `=` will be part of the value. | ||
- Unix and Windows line endings are supported. | ||
- The maximum size of the file is 4096 bytes. | ||
- Values can contain any printable UTF-8 character. | ||
- Keys can also contain any printable UTF-8 character except for '='. | ||
- There is no maximum size for a key or a value (except for the file size). | ||
- Values can be zero length. | ||
- Keys must be at least 1 byte. | ||
- If a key appears more than once in the file, the first value is used. | ||
- The end of the file is the first byte with value 0x00, 0xff or 0x1a, or the 4097th byte, | ||
whichever comes first. | ||
Comment on lines
+110
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's easy to imagine this "settings-file reader" code being useful for multiple projects beyond your wifi-settings-connect library. I wonder if it might be worth splitting it out into a library of its own? (If you have the energy & time, of course 🙂 ) EDIT: Haha, I've just read your later "This can be a useful way to store additional configuration data for your Pico application." line 😁 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have certainly found it useful for more than WiFi settings, but I have been thinking that a filesystem module would be a better solution, being much more general, and also avoiding the problem of having to find unused addresses (as in raspberrypi/pico-sdk#1378 ). Is there a plan to officially adopt a filesystem as part of the SDK? In the hope of enabling this library to use a filesystem in the future, I have turned There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
See https://github.com/raspberrypi/pico-sdk/labels/filesystem |
||
|
||
# WiFi settings | ||
|
||
- `ssid<N+1>` is only checked if `ssid<N>` is present. | ||
- The number reflects the priority. Lower numbers take priority over higher | ||
numbers when more than one SSID is found. | ||
- If `pass<N>` is not specified then wifi\_settings\_connect will assume | ||
an open WiFi hotspot. | ||
- If both `bssid<N>` and `ssid<N>` are specified, then the BSSID is used | ||
and the SSID is ignored. | ||
- If you don't specify a country, the default worldwide settings are used, which might work | ||
slightly less well (e.g. fewer WiFi channels are supported). | ||
- `bssid<N>` should be specified as | ||
a `:`-separated lower-case MAC address, e.g. `01:23:45:67:89:ab`. BSSIDs are | ||
not normally required and should only be used if you have a special requirement | ||
e.g. a "hidden" hotspot without an SSID name. | ||
|
||
# Custom keys and values | ||
|
||
The WiFi settings file can have keys which are not used by the wifi\_settings\_connect library. | ||
Your application can obtain their values using the `wifi_settings_get_value_for_key()` function. | ||
This can be a useful way to store additional configuration data for your Pico application. | ||
For example you might use it to store encryption keys, server addresses, user names | ||
or any other setting that you may wish to update without rebuilding your application. | ||
|
||
`wifi_settings_get_value_for_key` uses a linear search, starting at the beginning | ||
lurch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
of the file. This search does not backtrack and is fast because of the simplistic | ||
nature of the file format. However, in algorithmic terms, this is not the best way | ||
to implement or search a key/value store, and if you need frequent access to keys/values, | ||
you may wish to implement something better (e.g. use a hash table to implement a dictionary) | ||
or just load the values when your application starts up and then store them elsewhere. |
Uh oh!
There was an error while loading. Please reload this page.