From 4a66f23dc6b69c3d05f3a874eb2a1c38df60234f Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Mon, 29 Jan 2024 20:55:00 +0100 Subject: [PATCH] Improving WiFi AP sample (#349) --- samples/WiFiAP/Program.cs | 87 ++++++++------------------------- samples/WiFiAP/WebServer.cs | 6 +++ samples/WiFiAP/WiFiAP.nfproj | 10 ++-- samples/WiFiAP/Wireless80211.cs | 81 +++++++++++++++++++++++++++--- samples/WiFiAP/WirelessAP.cs | 54 ++++++++++++++++++-- samples/WiFiAP/packages.config | 4 +- 6 files changed, 159 insertions(+), 83 deletions(-) diff --git a/samples/WiFiAP/Program.cs b/samples/WiFiAP/Program.cs index e5d03cd0..d1c909dd 100644 --- a/samples/WiFiAP/Program.cs +++ b/samples/WiFiAP/Program.cs @@ -18,84 +18,39 @@ namespace WifiAP public class Program { // Start Simple WebServer - static WebServer server = new WebServer(); + private static WebServer _server = new WebServer(); + private static bool _wifiApMode = false; // Connected Station count - static int connectedCount = 0; + private static int _connectedCount = 0; // GPIO pin used to put device into AP set-up mode - const int SETUP_PIN = 5; + private const int SetupPin = 5; public static void Main() { Debug.WriteLine("Welcome to WiFI Soft AP world!"); var gpioController = new GpioController(); - GpioPin setupButton = gpioController.OpenPin(SETUP_PIN, PinMode.InputPullUp); + GpioPin setupButton = gpioController.OpenPin(SetupPin, PinMode.InputPullUp); // If Wireless station is not enabled then start Soft AP to allow Wireless configuration // or Button pressed - if (!Wireless80211.IsEnabled() || (setupButton.Read() == PinValue.High)) + if (setupButton.Read() == PinValue.High) { - - Wireless80211.Disable(); - if (WirelessAP.Setup() == false) - { - // Reboot device to Activate Access Point on restart - Debug.WriteLine($"Setup Soft AP, Rebooting device"); - Power.RebootDevice(); - } - - var dhcpserver = new DhcpServer - { - CaptivePortalUrl = $"http://{WirelessAP.SoftApIP}" - }; - var dhcpInitResult = dhcpserver.Start(IPAddress.Parse(WirelessAP.SoftApIP), new IPAddress(new byte[] { 255, 255, 255, 0 })); - if (!dhcpInitResult) - { - Debug.WriteLine($"Error initializing DHCP server."); - } - - Debug.WriteLine($"Running Soft AP, waiting for client to connect"); - Debug.WriteLine($"Soft AP IP address :{WirelessAP.GetIP()}"); - - // Link up Network event to show Stations connecting/disconnecting to Access point. - //NetworkChange.NetworkAPStationChanged += NetworkChange_NetworkAPStationChanged; - // Now that the normal Wifi is deactivated, that we have setup a static IP - // We can start the Web server - server.Start(); + WirelessAP.SetWifiAp(); + _wifiApMode = true; } else { - Debug.WriteLine($"Running in normal mode, connecting to Access point"); - var conf = Wireless80211.GetConfiguration(); - - bool success; - - // For devices like STM32, the password can't be read - if (string.IsNullOrEmpty(conf.Password)) - { - // In this case, we will let the automatic connection happen - success = WifiNetworkHelper.Reconnect(requiresDateTime: true, token: new CancellationTokenSource(60000).Token); - } - else - { - // If we have access to the password, we will force the reconnection - // This is mainly for ESP32 which will connect normaly like that. - success = WifiNetworkHelper.ConnectDhcp(conf.Ssid, conf.Password, requiresDateTime: true, token: new CancellationTokenSource(60000).Token); - } - - if (success) - { - Debug.WriteLine($"Connection is {success}"); - Debug.WriteLine($"We have a valid date: {DateTime.UtcNow}"); - } - else - { - Debug.WriteLine($"Something wrong happened, can't connect at all"); - } + _wifiApMode = Wireless80211.ConnectOrSetAp(); } + Console.WriteLine($"Connected with wifi credentials. IP Address: {(_wifiApMode ? WirelessAP.GetIP() : Wireless80211.GetCurrentIPAddress())}"); + if( _wifiApMode ) + { + _server.Start(); + } // Just wait for now // Here you would have the reset of your program using the client WiFI link @@ -120,26 +75,26 @@ private static void NetworkChange_NetworkAPStationChanged(int NetworkIndex, Netw string macString = BitConverter.ToString(station.MacAddress); Debug.WriteLine($"Station mac {macString} Rssi:{station.Rssi} PhyMode:{station.PhyModes} "); - connectedCount++; + _connectedCount++; // Start web server when it connects otherwise the bind to network will fail as // no connected network. Start web server when first station connects - if (connectedCount == 1) + if (_connectedCount == 1) { // Wait for Station to be fully connected before starting web server // other you will get a Network error Thread.Sleep(2000); - server.Start(); + _server.Start(); } } else { // Station disconnected. When no more station connected then stop web server - if (connectedCount > 0) + if (_connectedCount > 0) { - connectedCount--; - if (connectedCount == 0) - server.Stop(); + _connectedCount--; + if (_connectedCount == 0) + _server.Stop(); } } diff --git a/samples/WiFiAP/WebServer.cs b/samples/WiFiAP/WebServer.cs index 5dce920b..284242c0 100644 --- a/samples/WiFiAP/WebServer.cs +++ b/samples/WiFiAP/WebServer.cs @@ -85,6 +85,12 @@ private void ProcessRequest(HttpListenerContext context) string message = "

New settings saved.

Rebooting device to put into normal mode

"; + bool res = Wireless80211.Configure(ssid, password); + if (res) + { + message += $"

And your new IP address should be {Wireless80211.GetCurrentIPAddress()}.

"; + } + responseString = CreateMainPage(message); OutPutResponse(response, responseString); diff --git a/samples/WiFiAP/WiFiAP.nfproj b/samples/WiFiAP/WiFiAP.nfproj index 321850dd..d8c83360 100644 --- a/samples/WiFiAP/WiFiAP.nfproj +++ b/samples/WiFiAP/WiFiAP.nfproj @@ -74,17 +74,15 @@ packages\nanoFramework.System.Device.Gpio.1.1.38\lib\System.Device.Gpio.dll True - - packages\nanoFramework.System.Device.Wifi.1.5.71\lib\System.Device.Wifi.dll - True + + packages\nanoFramework.System.Device.Wifi.1.5.74\lib\System.Device.Wifi.dll packages\nanoFramework.System.IO.Streams.1.1.52\lib\System.IO.Streams.dll True - - packages\nanoFramework.System.Net.1.10.68\lib\System.Net.dll - True + + packages\nanoFramework.System.Net.1.10.70\lib\System.Net.dll packages\nanoFramework.System.Net.Http.Server.1.5.118\lib\System.Net.Http.dll diff --git a/samples/WiFiAP/Wireless80211.cs b/samples/WiFiAP/Wireless80211.cs index 34303e2d..3e095f84 100644 --- a/samples/WiFiAP/Wireless80211.cs +++ b/samples/WiFiAP/Wireless80211.cs @@ -3,6 +3,8 @@ // See LICENSE file in the project root for full license information. // +using System; +using System.Device.Wifi; using System.Diagnostics; using System.Net.NetworkInformation; using System.Threading; @@ -12,19 +14,62 @@ namespace WifiAP { class Wireless80211 { + /// + /// Checks if the wireless 802.11 interface is enabled. + /// + /// + /// Returns true if the wireless 802.11 interface is enabled (i.e., the SSID is not null or empty), + /// otherwise returns false. + /// public static bool IsEnabled() { Wireless80211Configuration wconf = GetConfiguration(); return !string.IsNullOrEmpty(wconf.Ssid); } + /// + /// Get current IP address. Only valid if successfully provisioned and connected + /// + /// IP address string + public static string GetCurrentIPAddress() + { + NetworkInterface ni = NetworkInterface.GetAllNetworkInterfaces()[0]; + + // get first NI ( Wifi on ESP32 ) + return ni.IPv4Address.ToString(); + } + + /// + /// Coonnects to the Wifi or sets the Access Point mode. + /// + /// True if access point is setup. + public static bool ConnectOrSetAp() + { + if (IsEnabled()) + { + Debug.WriteLine("Wireless client activated"); + if (!WifiNetworkHelper.Reconnect(true, token: new CancellationTokenSource(30_000).Token)) + { + WirelessAP.SetWifiAp(); + return true; + } + } + else + { + WirelessAP.SetWifiAp(); + return true; + } + + return false; + } + /// /// Disable the Wireless station interface. /// public static void Disable() { Wireless80211Configuration wconf = GetConfiguration(); - wconf.Options = Wireless80211Configuration.ConfigurationOptions.None; + wconf.Options = Wireless80211Configuration.ConfigurationOptions.None | Wireless80211Configuration.ConfigurationOptions.SmartConfig; wconf.SaveConfiguration(); } @@ -35,14 +80,38 @@ public static void Disable() /// /// public static bool Configure(string ssid, string password) - { - // And we have to force connect once here even for a short time - var success = WifiNetworkHelper.ConnectDhcp(ssid, password, token: new CancellationTokenSource(10000).Token); - Debug.WriteLine($"Connection is {success}"); + { + // Make sure we are disconnected before we start connecting otherwise + // ConnectDhcp will just return success instead of reconnecting. + WifiAdapter wa = WifiAdapter.FindAllAdapters()[0]; + wa.Disconnect(); + + CancellationTokenSource cs = new(30_000); + Console.WriteLine("ConnectDHCP"); + WifiNetworkHelper.Disconnect(); + + // Reconfigure properly the normal wifi Wireless80211Configuration wconf = GetConfiguration(); wconf.Options = Wireless80211Configuration.ConfigurationOptions.AutoConnect | Wireless80211Configuration.ConfigurationOptions.Enable; + wconf.Ssid = ssid; + wconf.Password = password; wconf.SaveConfiguration(); - return true; + + WifiNetworkHelper.Disconnect(); + bool success; + + success = WifiNetworkHelper.ConnectDhcp(ssid, password, WifiReconnectionKind.Automatic, true, token: cs.Token); + + if (!success) + { + wa.Disconnect(); + // Bug in network helper, we've most likely try to connect before, let's make it manual + var res = wa.Connect(ssid, WifiReconnectionKind.Automatic, password); + success = res.ConnectionStatus == WifiConnectionStatus.Success; + Console.WriteLine($"Connected: {res.ConnectionStatus}"); + } + + return success; } /// diff --git a/samples/WiFiAP/WirelessAP.cs b/samples/WiFiAP/WirelessAP.cs index f161a13c..00a8f6ca 100644 --- a/samples/WiFiAP/WirelessAP.cs +++ b/samples/WiFiAP/WirelessAP.cs @@ -3,13 +3,57 @@ // See LICENSE file in the project root for full license information. // +using System; +using System.Net; using System.Net.NetworkInformation; +using Iot.Device.DhcpServer; +using nanoFramework.Runtime.Native; namespace WifiAP { + /// + /// Provides methods and properties to manage a wireless access point. + /// public static class WirelessAP { - public const string SoftApIP = "192.168.4.1"; + /// + /// Gets or sets the IP address of the Soft AP. + /// + public static string SoftApIP { get; set; } = "192.168.4.1"; + + /// + /// Gets or sets the SSID of the Soft AP. + /// + public static string SoftApSsid { get; set; } = "MySuperSSID"; + + /// + /// Sets the configuration for the wireless access point. + /// + public static void SetWifiAp() + { + Wireless80211.Disable(); + if (Setup() == false) + { + // Reboot device to Activate Access Point on restart + Console.WriteLine($"Setup Soft AP, Rebooting device"); + Power.RebootDevice(); + } + + var dhcpserver = new DhcpServer + { + CaptivePortalUrl = $"http://{SoftApIP}" + }; + var dhcpInitResult = dhcpserver.Start(IPAddress.Parse(SoftApIP), new IPAddress(new byte[] { 255, 255, 255, 0 })); + if (!dhcpInitResult) + { + Console.WriteLine($"Error initializing DHCP server."); + // This happens after a very freshly flashed device + Power.RebootDevice(); + } + + Console.WriteLine($"Running Soft AP, waiting for client to connect"); + Console.WriteLine($"Soft AP IP address :{GetIP()}"); + } /// /// Disable the Soft AP for next restart. @@ -51,13 +95,13 @@ public static bool Setup() WirelessAPConfiguration.ConfigurationOptions.Enable; // Set the SSID for Access Point. If not set will use default "nano_xxxxxx" - //wapconf.Ssid = "MySsid"; + wapconf.Ssid = SoftApSsid; // Maximum number of simultaneous connections, reserves memory for connections wapconf.MaxConnections = 1; // To set-up Access point with no Authentication - wapconf.Authentication = AuthenticationType.Open; + wapconf.Authentication = System.Net.NetworkInformation.AuthenticationType.Open; wapconf.Password = ""; // To set up Access point with no Authentication. Password minimum 8 chars. @@ -80,6 +124,10 @@ public static WirelessAPConfiguration GetConfiguration() return WirelessAPConfiguration.GetAllWirelessAPConfigurations()[ni.SpecificConfigId]; } + /// + /// Gets the network interface for the wireless access point. + /// + /// The network interface for the wireless access point. public static NetworkInterface GetInterface() { NetworkInterface[] Interfaces = NetworkInterface.GetAllNetworkInterfaces(); diff --git a/samples/WiFiAP/packages.config b/samples/WiFiAP/packages.config index 558de962..7b219c76 100644 --- a/samples/WiFiAP/packages.config +++ b/samples/WiFiAP/packages.config @@ -7,9 +7,9 @@ - + - +