private static void LcdUpdate_Thread() //TODO: backlight timeout should be in driver!
        {
            for ( ; ;)
            {
                try
                {
                    //palthreeDisplay.BacklightOn = true; //TODO: this causes display corruption!
                    //TODO: create a menu handler to scroll through the display!
                    palthreeDisplay.Update($"{DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm")}", //Time shortened to fit on display (excludes seconds)
                                           $"IP: {System.Net.NetworkInformation.IPGlobalProperties.GetIPAddress()}");


                    //palthreeDisplay.Display($"PCB Temp: { palthree.GetTemperatureOnBoard().ToString("n2")}C",
                    //    $"Voltage: { palthree.GetBatteryUnregulatedVoltage().ToString("n2")}VDC");
                    Thread.Sleep(1000); //TODO: arbitary value... what should the update rate be?!
                                        //palthreeDisplay.BacklightOn = false;
                }
                catch (Exception e)
                {
                    _logger.LogWarning(e.Message.ToString());
                }
            }
        }
        public static void Main()
        {
            _logger = new DebugLogger("debugLogger");

            if (!Debugger.IsAttached)
            {
                // Unknown why this is required, but it seems to block here when disconnected from debug
                // ( even worse with fresh power which seems to need to be disconnected for over 12 seconds (Router DHCP?)!
                // Current thinking is due to System.Net blocking if already connected to a TLS target (MQTT)....
                Thread.Sleep(5000);
                LogDispatcher.LoggerFactory = new DebugLoggerFactory();
            }
            //else
            //{
            // TODO: Cannot actually use this yet as storage is not setup!
            //var _stream = new FileStream("D:\\logging.txt", FileMode.Open, FileAccess.ReadWrite);
            //LogDispatcher.LoggerFactory = new StreamLoggerFactory(_stream);
            //}

            //_logger.MinLogLevel = LogLevel.Trace;
            _logger.LogInformation($"{SystemInfo.TargetName} AWS MQTT Demo.");
            _logger.LogInformation("");

#if ORGPAL_THREE
            palthreeButtons     = new Buttons();
            palthreeInternalAdc = new OnboardAdcDevice();
            palAdcExpBoard      = new AdcExpansionBoard();

            palthreeDisplay = new CharacterDisplay
            {
                BacklightOn = true
            };

            palthreeDisplay.Update("Initializing:", "Please Wait...");
#endif

            try
            {
                foreach (byte b in Utilities.UniqueDeviceId) //STM32 devices only!
                {
                    _serialNumber += b.ToString("X2");       //Generates a unique ID for the device.
                }
            }
            catch (Exception)
            {
                _serialNumber = Guid.NewGuid().ToString();
            }

#if ORGPAL_THREE
            palthreeDisplay.Update("Device S/N,", $"{_serialNumber}");
            Thread.Sleep(1000);
#endif

            _logger.LogInformation($"Time before network available: {DateTime.UtcNow.ToString("o")}");

            var netConnected         = false;
            int netConnectionAttempt = 0;
            while (!netConnected)
            {
#if ORGPAL_THREE
                palthreeDisplay.Update("Initializing:", $"Network... {netConnectionAttempt}");
#endif
                _logger.LogInformation($"Network Connection Attempt: {netConnectionAttempt}");
                netConnected = SetupNetwork();
                if (!netConnected)
                {
                    netConnectionAttempt += 1;
                    Thread.Sleep(1000);
                }
            }

            startTime = DateTime.UtcNow; //set now because the clock might have been wrong before ntp is checked.

            _logger.LogInformation($"Time after network available: {startTime.ToString("o")}");
            _logger.LogInformation("");


#if ORGPAL_THREE
            palthreeDisplay.Update("Initializing:", "Storage");
#endif
            // add event handlers for Removable Device insertion and removal
            StorageEventManager.RemovableDeviceInserted += StorageEventManager_RemovableDeviceInserted;
            StorageEventManager.RemovableDeviceRemoved  += StorageEventManager_RemovableDeviceRemoved;

            while (AwsIotCore.MqttConnector.RootCA == null || AwsIotCore.MqttConnector.ClientRsaSha256Crt == string.Empty || AwsIotCore.MqttConnector.ClientRsaKey == string.Empty)
            {
                try
                {
                    ReadStorage(); // We cannot start without valid Certs!
                }
                catch (Exception e)
                {
                    _logger.LogWarning(e.Message.ToString());
                }
            }


            var mqttConnected         = false;
            int mqttConnectionAttempt = 0;
            while (!mqttConnected)
            {
#if ORGPAL_THREE
                palthreeDisplay.Update("Initializing:", $"MQTT... {mqttConnectionAttempt}");
#endif
                _logger.LogInformation($"MQTT Connection Attempt: {mqttConnectionAttempt}");
                mqttConnected = SetupMqtt();
                if (!mqttConnected)
                {
                    mqttConnectionAttempt += 1;
                    Thread.Sleep(1000);
                }
            }

#if ORGPAL_THREE
            palthreeDisplay.Update("Initializing:", "MQTT Timers...");
#endif
            SetupMqttMessageTimers();

#if ORGPAL_THREE
            palthreeDisplay.Update("Initializing:", "Finished!");

            Thread lcdUpdateThread = new Thread(new ThreadStart(LcdUpdate_Thread));
            lcdUpdateThread.Start();
#endif

            Thread.Sleep(Timeout.Infinite);
        }