wifi-conexion-redes-nanoframework

Wi-Fi Connection in nanoFramework: Scanning and DHCP

  • 5 min

Wi-Fi connectivity in nanoFramework is the way to connect our microcontroller to an IP network to communicate with other systems.

Until now, our ESP32 could read sensors and move servos, but it couldn’t exchange that data with other systems.

In this section, we’re going to connect it to a local network and, from there, to the Internet.

Connecting a microcontroller to a Wi-Fi network might seem trivial (“just enter the password and you’re done”), but in embedded systems you need to handle timeouts, IP addresses, automatic reconnections, and power saving.

Fortunately, nanoFramework offers high-level tools so we don’t have to deal with the Wi-Fi radio registers manually.

For this tutorial you need to install the following NuGet packages:

  • nanoFramework.System.Device.Wifi (To manage the adapter and scan).
  • nanoFramework.System.Net (For network management).

Scanning Wi-Fi Networks

Before attempting to connect, let’s verify that the antenna works and see what’s in the air. We’ll create a Wi-Fi Scanner.

We’ll use the WifiAdapter class.

using System;
using System.Device.Wifi;
using System.Diagnostics;
using System.Threading;

namespace WifiScanner
{
    public class Program
    {
        public static void Main()
        {
            Debug.WriteLine("Starting Wi-Fi scan...");

            // 1. Get the Wi-Fi adapter from the hardware
            // On the ESP32 it's usually the first one available
            WifiAdapter wifi = WifiAdapter.FindAllAdapters()[0];

            // 2. Start the scan
            wifi.ScanAsync();

            // Wait a bit for the scan to finish...
            Thread.Sleep(5000);

            // 3. Get results
            foreach (var network in wifi.NetworkReport.AvailableNetworks)
            {
                Debug.WriteLine($"SSID: {network.Ssid}  |  RSSI: {network.SignalBars} ({network.Rssi}dBm)");
            }

            Thread.Sleep(Timeout.Infinite);
        }
    }
}
Copied!

If you run this, you’ll see a list of all the Wi-Fi networks in your neighborhood with their signal strength (RSSI) in the Visual Studio console. Our radio works!

Connecting with WifiNetworkHelper

Managing the connection “manually” involves dealing with state change events, DHCP, IP assignment… it’s tedious.

To simplify our lives, nanoFramework includes the static class WifiNetworkHelper. It’s a utility that manages the entire connection process and waits until the network is actually ready (i.e., when we have time and a valid IP address).

DHCP Connection

Most of the time we’ll want the Router to assign us an IP automatically.

using System.Device.Wifi;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading;

// ... inside the Main

Debug.WriteLine("Connecting to Wi-Fi...");

// Credentials (Caution! Don't upload them to GitHub)
string ssid = "MiFibra_5G";
string password = "MiSuperPassword";

// This function blocks execution until it connects or times out
bool success = WifiNetworkHelper.ConnectDhcp(ssid, password, requiresDateTime: true, token: new CancellationTokenSource(60000).Token);

if (success)
{
    Debug.WriteLine("Connected successfully!");
    Debug.WriteLine($"IP: {NetworkInterface.GetAllNetworkInterfaces()[0].IPv4Address}");
}
else
{
    Debug.WriteLine("Error: Could not connect after 60 seconds.");
}
Copied!

Notice the parameter requiresDateTime: true. This makes the Helper not return true until the device has synchronized the time via NTP (Network Time Protocol). This is very important if you’re going to connect to secure services (HTTPS/Azure), because without the correct time, SSL certificates will fail.

Static IP Connection

Sometimes we need our device to always have the same IP (for example, if we’re going to set up a web server on it).

In this case, we use ConnectFixAddress.

IPConfiguration ipConfig = new IPConfiguration(
    "192.168.1.200",   // Desired IP
    "255.255.255.0",   // Subnet mask
    "192.168.1.1",     // Gateway (Router)
    new[] { "8.8.8.8" } // DNS (Google)
);

bool success = WifiNetworkHelper.ConnectFixAddress(
    ssid,
    password,
    ipConfig,
    requiresDateTime: true,
    token: new CancellationTokenSource(60000).Token);
Copied!

Managing Reconnection

In a device that must operate for hours or days, the Wi-Fi connection can drop due to a router restart or interference.

If your code only connects at the beginning of Main and the network goes down after 3 hours, your device will be “deaf” forever.

WifiNetworkHelper also helps us reconnect automatically if we tell it to save the profile.

// Connect and save the profile to be able to reconnect later
WifiNetworkHelper.ConnectDhcp(
    ssid,
    password,
    WifiReconnectionKind.Automatic,
    requiresDateTime: true);
Copied!

However, for total robustness, we often implement a Network Monitoring pattern:

// Event that fires when the network state changes (IP obtained, cable disconnected...)
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;

// ...

private static void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
{
    var ipProperties = NetworkInterface.GetAllNetworkInterfaces()[0].IPv4Address;
    
    if (ipProperties != null && ipProperties.Length > 0 && ipProperties != "0.0.0.0")
    {
        Debug.WriteLine($"Network Status: CONNECTED - IP: {ipProperties}");
    }
    else
    {
        Debug.WriteLine("Network Status: DISCONNECTED. Attempting to reconnect...");
        // Here we could force a restart or retry the connection
    }
}
Copied!

Protecting Credentials

Putting the username and password in the source code (string password = "...") is convenient for learning, but a terrible security practice.

Remember that in the post about nanoff we saw that we could inject credentials when flashing:

nanoff --target ESP32_REV0 --update --ssid "MyNetwork" --password "1234"
Copied!

If you do this, nanoFramework saves the data in its non-volatile configuration. In your code, you can call WifiNetworkHelper.Reconnect(requiresDateTime: true) to reuse the automatic profile. This avoids including the key in the repository, but it doesn’t mean it’s encrypted against an attacker with physical access.