One powerful feature of the new Notecard Cell+WiFi is the ability to use a Wi-Fi connection with a built-in fallback to cellular connectivity.
To use this feature you must tell the Notecard which Wi-Fi network to use with the card.wifi
request. If the Notecard detects that the provided network is available and has connectivity, the Notecard will use that network to send your data; if the Notecard detects that the provided network is unavailable or lacks connectivity, the Notecard will instead send your data over a cellular connection.
In this article I want to discuss an extension to this workflow you might want to use: the ability to dynamically switch which Wi-Fi network your Notecard uses.
How to Switch Networks
To show what I mean, let’s start with an example. Here’s the typical way you associate a Notecard with a Wi-Fi network using the card.wifi
request.
{
"req": "card.wifi",
"ssid": "your_network",
"password": "your_password"
}
This request is simple and easy to run in our in-browser terminal.
This works, but suppose you’re using the same Notecard across many different facilities, such a piece of manufacturing equipment. Or suppose you’re building the Notecard into a product where the customer has to enter their personal Wi-Fi information. In situations like these it’s not ideal (or sometimes not even possible) to issue an explicit card.wifi
request every time you want to update the Wi-Fi network the Notecard should use.
Luckily, the Notecard gives you other options for more robust automation.
Using Environment Variables
The Notecard and its backing cloud service, Notehub, offer a settings management feature called environment variables.
Environment variables are key-value pairs that can be set in the Notehub UI or through the Notehub API, and propagate to devices or fleets of devices using the same synchronization mechanism used by the Notecard.
Let me explain using an example. Suppose you’re building a fan that activates when the temperature goes above a certain threshold—for example, the fan should turn on when the room’s temperature hits 30 degrees Celsius. Although you could hardcode the threshold value into your firmware, that would make the value very difficult (if not impossible) to update in the future.
As an alternative, you could instead define an environment variable that has this value. Here’s what that might look like in the Notehub user interface.
With an environment variable defined, your firmware can retrieve the value using the Notecard’s env.get
request. The big thing you gain by doing this is, if you ever need to update the threshold, you can do so either in the Notehub UI or by using the Notehub API’s environment variable requests.
Using the Notehub API is especially important here, as it allows you to build your own interfaces (web applications, mobile applications, etc) that can update environment variables—meaning you can pretty easily build user interfaces with controls that affect your live devices.
But wait, there’s more!
Environment variables also have a built-in hierarchy system that allow you to define variables at the device level, fleet level, or project level. You could use this ability to, for example, set a temperature threshold for an entire project, but allow individual devices to override the threshold.
For instance, if you set the temperature_threshold
above at the project level, you would then have the ability for individual devices to override that default as necessary—all while keeping all hardcoded values out of your firmware.
Storing Wi-Fi Credentials in Environment Variables
This same technique can be applied to Wi-Fi credentials. Instead of manually providing your credentials through a card.wifi
request, instead you can create environment variables for a network ssid and password.
Now, your firmware can use one of the Notecard’s firmware libraries to perform an env.get
request to retrieve the Wi-Fi credentials, and apply them with a card.wifi
request.
If your firmware is C-based, we offer a library to make this process easier: Notecard Environment Variable Manager. With Notecard Environment Variable Manager you start by initializing a NotecardEnvVarManager
object.
NotecardEnvVarManager *manager = NotecardEnvVarManager_alloc();
Next, you tell the NotecardEnvVarManager
which variables you want to monitor using NotecardEnvVarManager_fetch
.
const char *vars[] = {
"wifi_ssid",
"wifi_password"
};
const size_t numVars = sizeof(vars) / sizeof(vars[0]);
int ret = NotecardEnvVarManager_fetch(manager, vars, numVars);
The NotecardEnvVarManager_fetch
function makes an env.get
request to the Notecard for the specified variables and invokes a user-provided callback on each key-value pair in the response. Here’s an example callback that handles the wifi_ssid
and wifi_password
values, and invokes the card.wifi
request if it detects an updated value.
Notecard notecard;
static char *wifi_ssid = nullptr;
static char *wifi_password = nullptr;
void envVarManagerCb(const char *var, const char *val, void *user_ctx) {
bool update_wifi = false;
if (strcmp(var, "wifi_ssid") == 0) {
if (!wifi_ssid || strncmp(wifi_ssid, val, 255) != 0) {
delete(wifi_ssid);
if (256 < strnlen(val, 255)) {
notecard.logDebugf("[ERROR] Wi-Fi SSID is too long! (max: 255)\n");
} else {
const size_t ssid_len = (strnlen(val, 255) + 1);
wifi_ssid = new char[256]; // Allocate as reusable chunk to preserve heap
strlcpy(wifi_ssid, val, ssid_len);
notecard.logDebugf("[APP] Updating Wi-Fi SSID to %s.\n", val);
update_wifi = true;
}
}
} else if (strcmp(var, "wifi_password") == 0) {
if (!wifi_password || strncmp(wifi_password, val, 255) != 0) {
delete(wifi_password);
if (256 < strnlen(val, 255)) {
notecard.logDebugf("[ERROR] Wi-Fi password is too long! (max: 255)\n");
} else {
const size_t password_len = (strnlen(val, 255) + 1);
wifi_password = new char[256]; // Allocate as reusable chunk to preserve heap
strlcpy(wifi_password, val, password_len);
notecard.logDebug("[APP] Updating Wi-Fi password to ");
for (size_t i = 0; i < password_len; ++i) {
notecard.logDebug("*");
}
notecard.logDebug(".\n");
update_wifi = true;
}
}
} else {
notecard.logDebugf("[APP] Ignoring unknown environment variable: %s\n", var);
}
// Update Wi-Fi credentials (if necessary)
if (update_wifi) {
if (J *req = notecard.newRequest("card.wifi")) {
JAddItemReferenceToObject(req, "ssid", JCreateString(wifi_ssid));
JAddItemReferenceToObject(req, "password", JCreateString(wifi_password));
notecard.sendRequest(req);
}
}
}
For full context on how to use this approach, see the Blues Smart CO2 Detector accelerator. The project’s source code is open source, and contains firmware that implements the Wi-Fi credential management workflow discussed in this article.
A couple of additional important notes:
-
Although the example code above is written in C, this technique can be used with any of the Notecard’s firmware libraries. All you need to do is invoke
env.get
at a regular cadence, and when you detect a change, invoke thecard.wifi
request to update the Notecard’s Wi-Fi credentials. -
Invoking the Notecard’s
env.get
request does not initiate a network request. Instead, the request retrieves the latest version of the environment variable locally on the Notecard. How often the Notecard checks for environment variable updates is determined by theinbound
value you provide to thehub.set
request. You can have the Notecard immediately retrieve new environment variables on all changes by setting thehub.set
request’smode
to"continuous"
andsync
argument totrue
. With these values set the Notecard maintains a continuous connection to Notehub, which allows you to receive environment variable updates immediately, but also means the Notecard will use considerably more voltage. As such, setting these values is only recommended if you’re running on mains power. -
If you can’t use the Notecard’s continuous mode with
sync
because you’re running on battery power, note that there will be some delay between updating environment variables and getting the updated values on your Notecard. If you’re using the Notecard Cell+WiFi you don’t have to worry too much about this delay, as the Notecard will continue to work over cellular until it receives the updated credentials. But this could be a concern if you’re moving the Notecard to a location where Wi-Fi coverage is available but cellular coverage is not.
Wrapping Up
And that’s it!
In this article we looked at one technique you can use to switch the Notecard between multiple Wi-Fi networks using environment variables. In short, you store the Wi-Fi network credentials you’d like to use in environment variables, and you write firmware that receives the updated credentials and invokes the Notecard’s card.wifi
request.
If you give this technique a shot let us know how it goes in this article’s comments. Or feel free share any other techniques you’ve used to manage Wi-Fi networks in your projects or solutions. Happy hacking!