void wifiTimer_Tick(GT.Timer timer)
        {
            Debug.Print("wifiTimer start - currentAP " + (currentAP != null ? currentAP.SSID : "(null)") + " wifiUp " + wifiConnected + (wifiConnected ? " IP " + wifi.Interface.NetworkInterface.IPAddress : ""));
            if (sp != null)
            {
                var spMessage = BroadcastMessageWithNewline;
                sp.Write(spMessage, 0, spMessage.Length);
            }
            if (SkipWifiTimer())
            {
                Debug.Print("aborting wifitimer tick since SkipWifiTimer delegate is true");
                return;
            }

            if (wifiConnected && currentAP == null)
            {
                Debug.Print("ERROR: wifi is up but current AP is null! Disconnecting.");
                currentAP = null;
                wifiConnected = false;
                SetLed();
                SetScreen();
                wifi.Interface.Disconnect();
                WebServer.StopLocalServer();
                return;
            }

            if (currentAP != null && !wifiConnected)
            {
                wifiUpTime++;
                if (wifiUpTime == wifiUpTimeout)
                {
                    Debug.Print("WARN: starting wifi for " + currentAP.SSID + " timeout - resetting currentAP");
                    currentAP = null;
                    wifiConnected = false;
                    SetLed();
                    SetScreen();
                    wifi.Interface.Disconnect();
                    WebServer.StopLocalServer();
                    return;
                }
            }

            if (wifiConnected && currentAP != null && APDetails.IsSetupAP(currentAP))
            {
                if (GT.Timer.GetMachineTime() - joinTime > maxTimeOnSetupNetwork)
                {
                    Debug.Print("Disconnecting from setup AP due to timeout (setup should not take this long)");
                    currentAP = null;
                    wifiConnected = false;
                    SetLed();
                    SetScreen();
                    wifi.Interface.Disconnect();
                    WebServer.StopLocalServer();
                    return;
                }
                else
                {
                    // only sending unsolicited beacons when we're not on the home network
                    Debug.Print("Sending unsolicited UDP beacon since we're on a setup network");
                    SendUDPBroadcast(BeaconPort);
                }
            }

            if (wifiConnected && currentAP != null)
            {
                if (!WebServer.IsRunning()) WebServer.StartLocalServer(wifi.Interface.NetworkInterface.IPAddress, 80);
            }

            // skip scanning when connected since it fails
            if (currentAP == null)
            {

                try
                {
                    var scan = wifi.Interface.Scan();
                    Thread.Sleep(1);
                    foreach (var ap in scan)
                    {
                        Debug.Print("Scanned AP " + ap.SSID + " rssi " + ap.RSSI);
                    }
                    if (scan != null) foreach (APDetails knownAP in wifiNetworks)
                        {
                            // APs are in priority order, so break once we see the current ap
                            if (knownAP.Matches(currentAP)) break;
                            WiFiNetworkInfo joinAP = null;
                            foreach (var scanAP in scan)
                            {
                                if (!knownAP.Matches(scanAP)) continue;
                                if (joinAP == null) joinAP = scanAP;
                                if (joinAP.RSSI > scanAP.RSSI) joinAP = scanAP; // lower RSSI is better
                            }
                            if (joinAP == null) continue;

                            try
                            {

                                if (currentAP != null)
                                {
                                    Debug.Print("Disconnecting from WiFi network " + currentAP + " to join " + joinAP.SSID);
                                    wifi.Interface.Disconnect();
                                }

                                //// stop pictimeout and any streaming since wifi join operation hogs processor 
                                //picTimeout.Stop();
                                //camera.StopStreamingBitmaps();

                                joinTime = GT.Timer.GetMachineTime();
                                Debug.Print("Joining WiFi network " + joinAP.SSID + " rssi " + joinAP.RSSI);
                                //wifi.Interface.();
                                currentAP = joinAP;
                                SetLed();
                                SetScreen();
                                wifi.Interface.Join(joinAP, knownAP.Key);
                                //Debug.Print("Joined WiFi network " + scanAP.SSID);
                                wifiUpTime = 0;
                                break;
                            }
                            catch
                            {
                                Debug.Print("Error joining wifi network: " + joinAP.SSID);
                                wifi.Interface.Disconnect();
                                timer.Stop();
                                timer.Start();
                                currentAP = null;
                                SetLed();
                                SetScreen();
                                break;
                            }
                        }
                }
                catch (GHI.Premium.Net.NetworkInterfaceExtensionException wex)
                {
                    wifi.Interface.Disconnect();
                    // restart the timer to prevent it immediately firing again
                    timer.Stop();
                    timer.Start();
                    Debug.Print("Error scanning wifi networks: " + wex);
                    currentAP = null;
                    setLedError();
                    setScreenError();
                }
            }
            Debug.Print("wifiTimer end - currentAP " + (currentAP != null ? currentAP.SSID : "(null)") + " wifiUp " + wifiConnected);
        }