//AVCS SENS - AVCS-DHT1 External Sensor Temperature Data Monitoring Function v4 // - Parses new External DHT11 data from Arduino every 30 seconds // by SemlerPDX Mar/May2022 // VETERANS-GAMING.COM //#define DEBUG //DEV DEBUGGING - comment/uncomment to disable/enable debug output using System; using System.IO.Ports; using System.Text; using System.Text.RegularExpressions; using System.Threading; public class VAInline { double MonitorTimerDelay = 2000; //in milliseconds (default 2000 == 2 seconds) public void ClearSensorVariables() { VA.SetDecimal("AVCS_SENS_TempDHTc", null); VA.SetDecimal("AVCS_SENS_TempDHTh", null); VA.SetDecimal("AVCS_SENS_TempDHTf", null); VA.SetDecimal("AVCS_SENS_TempDHTi", null); } public void ClearMonitorVariables() { ClearSensorVariables(); VA.SetInt("AVCS_SENS_IntervalDHT1", null); VA.SetText("AVCS_SENS_ComportDHT1", null); VA.SetBoolean("AVCS_SENS_MonitoringDHT1", null); VA.SetBoolean("AVCS_SENS_SensorTestDHT1", null); } public void SensorMonitoringEnd() { ClearMonitorVariables(); VA.WriteToLog("External AVCS-DHT1 Sensor Monitor has been terminated...", "black"); } public void SensorTestFailure() { ClearMonitorVariables(); VA.WriteToLog("AVCS-DHT1 Sensor Identification Test FAILURE!", "red"); VA.WriteToLog("Please check USB connection of AVCS-DHT1 Sensor. See user guide for more information.", "red"); VA.SetText("AVCS_SENS_Test_ReturnTTS", "Test failed. Please check device or settings."); } public void SensorIdentifyFailure() { ClearMonitorVariables(); VA.WriteToLog("External AVCS-DHT1 Sensor comport not found...", "black"); VA.WriteToLog("Diagnostic & indoor weather commands will be unavailable", "black"); } private void WriteToLog_Long(string longString, string colorString) { StringBuilder sb = new StringBuilder(); string[] longStringSegments = longString.Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries); //Write each new line of log data, break up any longer than approximate min-width of VA window (~91) to new lines for (int a = 0; a < longStringSegments.Length; a++) { if (longStringSegments[a].Length > 91) { sb.Append(longStringSegments[a].ToString()); while (sb.Length > 91) { try { VA.WriteToLog(sb.ToString(0, 91), colorString); sb.Remove(0, 91); } catch { //ignore } finally { if (sb.Length <= 91) VA.WriteToLog(sb.ToString(), colorString); } } } else { //Lines already less than 91 characters VA.WriteToLog(longStringSegments[a], colorString); } } } public decimal TTS_GetRoundedDecimal(string thisStringDec, int decimalPlaces) { //For Text-To-Speech brevity, return decimal that does not end in .00' decimal thisDecimal = Decimal.Round(Convert.ToDecimal(thisStringDec), decimalPlaces); decimal truncatedDecimal = Decimal.Truncate(thisDecimal); if (truncatedDecimal != thisDecimal) return thisDecimal; return truncatedDecimal; } private string GetArduinoSerialData(string PortDHT1) { System.IO.Ports.SerialPort SerialPortDHT1 = new System.IO.Ports.SerialPort(); bool debugging = false; if (VA.GetBoolean("AVCS_SENS_DebugArduino") == true) debugging = true; string incoming = ""; string dataBuild = ""; bool dataComplete = false; int serialReadTimeouts = 0; int timeoutsMax = 4; int dataChars = 1; byte[] inBuffer = new byte[1]; SerialPortDHT1.PortName = PortDHT1; SerialPortDHT1.BaudRate = 9600; SerialPortDHT1.DataBits = 8; SerialPortDHT1.Parity = Parity.None; SerialPortDHT1.StopBits = StopBits.One; SerialPortDHT1.Handshake = Handshake.None; SerialPortDHT1.Encoding = System.Text.Encoding.Default; SerialPortDHT1.ReadTimeout = 250; if (SerialPortDHT1.IsOpen) { SerialPortDHT1.Close(); while (SerialPortDHT1.IsOpen) Thread.Sleep(150); Thread.Sleep(2000); //Restart Delay for Arduino Sketch? } try { SerialPortDHT1.Open(); Thread.Sleep(100); } catch (Exception ex) { dataComplete = true; dataBuild = "nothing"; if (debugging) WriteToLog_Long("AVCS Error: Serial Port Open() ended on exception" + Environment.NewLine + "Error Details:" + Environment.NewLine + ex.ToString(), "red"); } while (dataComplete != true) { try { SerialPortDHT1.Read(inBuffer, 0, 1); incoming = Encoding.ASCII.GetString(inBuffer, 0, inBuffer.Length); if (String.IsNullOrEmpty(incoming)) { incoming = "nothing"; } else { if (String.IsNullOrEmpty(dataBuild)) { if (incoming == "[") dataBuild += incoming; } else { dataChars++; dataBuild += incoming; if (incoming == "]") dataComplete = true; } if ((dataChars >= 39) && (dataComplete != true)) serialReadTimeouts = timeoutsMax; } } catch (TimeoutException e) { serialReadTimeouts += 1; if (debugging) VA.WriteToLog("AVCS Error: Serial Port read timed out.", "red"); } finally { if ((serialReadTimeouts > timeoutsMax) && (dataComplete != true)) { dataComplete = true; dataBuild = "nothing"; if (debugging) VA.WriteToLog("AVCS Error: Serial Port data read ended on timeout", "red"); } } } SerialPortDHT1.Dispose(); return dataBuild; } private string GetArduinoPortName(string portRegExCheck) { string portNameDHT = ""; string receivedData = ""; string[] ports = SerialPort.GetPortNames(); int serialPortAttemptsMax = 8; if (ports.Length > 0) serialPortAttemptsMax = ports.Length * serialPortAttemptsMax; if ((VA.GetBoolean("AVCS_SENS_SensorTestDHT1") == true) || (ports.Length > 0 && ports.Length <= 3)) serialPortAttemptsMax *= 2; for (int serialPortAttempts = 0; serialPortAttempts <= serialPortAttemptsMax; serialPortAttempts++) { foreach (string port in ports) { try { receivedData = GetArduinoSerialData(port); } catch { receivedData = "nothing"; } finally { if (Regex.IsMatch(receivedData, portRegExCheck)) portNameDHT = port; } if (portNameDHT != "") break; } if (portNameDHT != "") break; } if ((VA.GetBoolean("AVCS_SENS_SensorTestDHT1") == true) && (portNameDHT != "")) { VA.WriteToLog("AVCS-DHT1 Data: " + receivedData, "blue"); VA.WriteToLog("AVCS-DHT1 Sensor Test has succeeded!", "green"); VA.SetText("AVCS_SENS_Test_ReturnTTS", "Test succeeded."); } return portNameDHT; } private void MonitorTimer(double MonitorTimerDelay) { VA.WriteToLog("AVCS-DHT1 Sensor Monitor is now running...", "black"); VA.SetBoolean("AVCS_SENS_TimerWorkingDHT1", null); VA.SetInt("AVCS_SENS_IntervalDHT1", null); System.Timers.Timer t = new System.Timers.Timer(MonitorTimerDelay); t.Elapsed += MonitorTimerElapsed; t.Start(); } private void MonitorTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) { if (VA.GetBoolean("AVCS_SENS_TimerWorkingDHT1") != true) { VA.SetBoolean("AVCS_SENS_TimerWorkingDHT1", true); bool debugging = false; if (VA.GetBoolean("AVCS_SENS_DebugArduino") == true) debugging = true; if (VA.GetBoolean("AVCS_SENS_MonitoringDHT1") != true) { SensorMonitoringEnd(); (sender as System.Timers.Timer).Stop(); } else { // Serial Data in format: [AVCS,-cc.cc,hh.hh,-ff.ff,-xx.xx,DHT1] *negatives possible yet highly unlikely string dataRegEx = @"^\[AVCS\,\-?\d\d\.\d\d\,\d\d\.\d\d\,\-?\d\d\.\d\d\,\-?\d\d\.\d\d\,DHT1\]$"; string comPort = ""; string receivedData = ""; int ArduinoCheckDelay = 15; //in multiples of MonitorTimerDelay (default 15*2000 == 30 seconds) int intervalDHT = Convert.ToInt32(VA.ParseTokens("{INT:AVCS_SENS_IntervalDHT1:" + ArduinoCheckDelay.ToString() + "}")); intervalDHT++; if (intervalDHT > ArduinoCheckDelay) intervalDHT = 0; VA.SetInt("AVCS_SENS_IntervalDHT1", intervalDHT); if (debugging) VA.WriteToLog("INTERVAL: " + intervalDHT.ToString(), "purple"); if ((intervalDHT == 0) || (VA.GetBoolean("AVCS_SENS_SensorTestDHT1") == true)) { if (String.IsNullOrEmpty(VA.GetText("AVCS_SENS_ComportDHT1"))) { ClearSensorVariables(); comPort = GetArduinoPortName(dataRegEx); if (debugging) VA.WriteToLog("COMPORT = " + comPort, "pink"); if (comPort.StartsWith("COM", StringComparison.OrdinalIgnoreCase)) { VA.SetText("AVCS_SENS_ComportDHT1", comPort); } else { comPort = ""; if (VA.GetBoolean("AVCS_SENS_SensorTestDHT1") == true) { SensorTestFailure(); } else { SensorIdentifyFailure(); } } } else { comPort = VA.GetText("AVCS_SENS_ComportDHT1"); } if (VA.GetBoolean("AVCS_SENS_SensorTestDHT1") == true) VA.SetBoolean("AVCS_SENS_SensorTestDHT1", null); //Continuing Operations Check - if not monitoring other systems, or failures above: int checkSystems = 0; if (VA.GetBoolean("AVCS_OWM_Monitor_Startup") != true) { if (VA.GetBoolean("AVCS_OWM_Monitoring") != true) checkSystems++; } if (VA.GetBoolean("AVCS_SENS_Monitor_Startup") != true) { if (VA.GetBoolean("AVCS_SENS_Monitoring") != true) checkSystems++; } if ((VA.GetBoolean("AVCS_SENS_MonitoringDHT1") != true) || (checkSystems == 2)) comPort = ""; if (comPort != "") { receivedData = GetArduinoSerialData(comPort); if (Regex.IsMatch(receivedData, dataRegEx)) { if (debugging) VA.WriteToLog(receivedData.ToString(), "blue"); string[] receivedDataArray = receivedData.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); VA.SetDecimal("AVCS_SENS_TempDHTc", TTS_GetRoundedDecimal(receivedDataArray[1], 2)); VA.SetDecimal("AVCS_SENS_TempDHTh", TTS_GetRoundedDecimal(receivedDataArray[2], 2)); VA.SetDecimal("AVCS_SENS_TempDHTf", TTS_GetRoundedDecimal(receivedDataArray[3], 2)); VA.SetDecimal("AVCS_SENS_TempDHTi", TTS_GetRoundedDecimal(receivedDataArray[4], 0)); } else { //When a single read of previously good ComPort fails, null the ComPort var for one last try on next interval if (debugging) VA.WriteToLog("RegEx was not match - circling back to try again","pink"); VA.SetText("AVCS_SENS_ComportDHT1", null); VA.SetInt("AVCS_SENS_IntervalDHT1", ArduinoCheckDelay); //bypass 30 second wait for last try before quitting } } else { SensorMonitoringEnd(); (sender as System.Timers.Timer).Stop(); } } } if (VA.GetBoolean("AVCS_DHT1_Monitor_Startup") == true) VA.SetBoolean("AVCS_DHT1_Monitor_Startup", null); VA.SetBoolean("AVCS_SENS_TimerWorkingDHT1", false); } } public void main() { //DEV DEBUGGING -- Sets SENS_MONITORING to true to simulate systems running and keep this loop active #if (DEBUG) VA.SetBoolean("AVCS_SENS_Monitoring", true); // (means AIDA64 monitoring is on, allows this test to loop) VA.SetBoolean("AVCS_SENS_DebugArduino", true); #endif if (VA.GetBoolean("AVCS_SENS_MonitoringDHT1") != true) { #if (DEBUG) VA.ClearLog(); #endif VA.SetBoolean("AVCS_SENS_MonitoringDHT1", true); MonitorTimer(MonitorTimerDelay); } #if (DEBUG) else //DEV DEBUGGING ELSE -- for TEST RUN Toggle On/Off in editor { VA.WriteToLog("DEV DISABLE BOOLS - RESET TEST", "grey"); VA.SetBoolean("AVCS_SENS_Monitoring", false); VA.SetBoolean("AVCS_SENS_MonitoringDHT1", false); } #endif } }