public override void DoWork(I2C thisSensor) { I2cSensordata thisReading = new I2cSensordata(); Sup.LogTraceInfoMessage($"I2cSensorDevice {SensorUsed}: DoWork routine entry"); try { lock ( Response ) { thisConnect.Write(SHT31SingleShotCommand); Response = thisConnect.Read(6); } // For debugging, normally not on Sup.LogTraceInfoMessage($"I2cSensorDevice {SensorUsed} data read: {Response[ 0 ]}; {Response[ 1 ]}; {Response[ 2 ]}; {Response[ 3 ]}; {Response[ 4 ]}; {Response[ 5 ]}; "); // {Response[6]}; {Response[7]}; thisReading.Humidity = 100 * (double)(Response[3] * 0x100 + Response[4]) / 65535; // BitConverter.ToUInt16(Response, 3) / 65535; thisReading.TemperatureF = -49 + 315 * (double)(Response[0] * 0x100 + Response[1]) / 65535; // Must be in Fahrenheit for the Davis simulation thisReading.TemperatureC = -45 + 175 * (double)(Response[0] * 0x100 + Response[1]) / 65535; // This is the same in Celsius thisSensor.ObservationList.Add(thisReading); } catch (Exception e) { Sup.LogTraceWarningMessage($"I2cSensorDevice Exception {SensorUsed}:...{e.Message}"); } return; }
public void SetMinuteValuesFromObservations() { if (Sensor.Valid) { lock ( MinuteValues ) { Sup.LogTraceInfoMessage($"SetMinuteValuesFromObservations: Creating minutevalues as average of the 10 second observations... {SensorUsed}"); if (Program.CUSensorsSwitch.TraceInfo) { int i = 0; foreach (PMSensordata entry in ObservationList) { Sup.LogTraceInfoMessage($"Observationlist data {i++}: {entry.Pm1_atm:F1}; {entry.Pm25_atm:F1}; {entry.Pm10_atm:F1};"); } } MinuteValues.Pm1_atm = ObservationList.Select(x => x.Pm1_atm).Average(); MinuteValues.Pm25_atm = ObservationList.Select(x => x.Pm25_atm).Average(); MinuteValues.Pm10_atm = ObservationList.Select(x => x.Pm10_atm).Average(); Sup.LogTraceInfoMessage($"Minutevalues data: {MinuteValues.Pm1_atm:F1}; {MinuteValues.Pm25_atm:F1}; {MinuteValues.Pm10_atm:F1};"); } // Renew the observationlist ObservationList.Clear(); // ObservationList = new List<PMSensordata>(); // The old list disappears through the garbage collector. } return; }
public void SetMinuteValuesFromObservations() { if (Sensor.Valid) { Sup.LogTraceInfoMessage($"SetMinuteValuesFromObservations: Creating minutevalues as average of the 10 second observations... {SensorUsed}"); if (Program.CUSensorsSwitch.TraceInfo) { int i = 0; foreach (I2cSensordata entry in ObservationList) { Sup.LogTraceInfoMessage($"Observationlist data {i++}: {entry.Humidity:F0}; {entry.TemperatureC:F1}; {entry.TemperatureF:F1}"); } } MinuteValues.Humidity = ObservationList.Select(x => x.Humidity).Average(); MinuteValues.TemperatureC = ObservationList.Select(x => x.TemperatureC).Average(); MinuteValues.TemperatureF = ObservationList.Select(x => x.TemperatureF).Average(); Sup.LogTraceInfoMessage($"Minutevalues data: {MinuteValues.Humidity:F0}; {MinuteValues.TemperatureC:F1}; {MinuteValues.TemperatureF:F1};"); // Renew the observationlist ObservationList.Clear(); //ObservationList = new List<I2cSensordata>(); // The old list disappears through the garbage collector. } return; }
internal async Task Send(EmulateAirLink thisEmulator) { string HTDataToSend; string PMDataToSend; HTDataToSend = "{ " + $"\"timestamp:\" : \"{DateTime.Now}\", " + $"\"sensordatavalues\" : [" + $"{{ \"value_type\":\"temperature\",\"value\":\"{thisEmulator.TemperatureC:F1}\" }}," + $"{{ \"value_type\":\"humidity\",\"value\":\"{thisEmulator.Humidity:F1}\"}}" + $"]" + "}"; PMDataToSend = "{ " + $"\"timestamp:\" : \"{DateTime.Now}\", " + $"\"sensordatavalues\" : [" + $"{{ \"value_type\":\"P1\",\"value\":\"{thisEmulator.PM10_last:F2}\"}}," + $"{{ \"value_type\":\"P2\",\"value\":\"{thisEmulator.PM25_last:F2}\"}}" + $"]" + "}"; Sup.LogTraceInfoMessage($" PMDataToSend {PMDataToSend} "); Sup.LogTraceInfoMessage($" HTDateToSend {HTDataToSend} "); await PostToSensorCommunity(HTDataToSend, PMDataToSend); }
public SHT31Device(Support s, string Name) { Sup = s; SensorUsed = I2cSensorsSupported.SHT31; Valid = true; Sup.LogTraceInfoMessage($"I2cSensorDevice SHT31 Constructor...{Name}, Valid = {Valid}"); Sup.LogTraceInfoMessage($"Print AddressDetected for SensorUsed: {I2C.i2cAddressDetected[ (int) SensorUsed ]} for {SensorUsed}"); }
public override void DoWork(I2C thisSensor) { I2cSensordata thisReading = new I2cSensordata(); Sup.LogTraceInfoMessage($"I2cSimulatorDevice {SensorUsed}: DoWork routine entry"); // new DateTime(1970, 1, 1) thisReading.Humidity = 70; thisReading.TemperatureF = 77.0; thisReading.TemperatureC = 25.0; thisSensor.ObservationList.Add(thisReading); }
public PMS1003Device(Support s, string Name) { Sup = s; Sup.LogTraceInfoMessage($"DoPMS1003Device: Constructor...{Name}"); try { SensorUsed = (SerialSensorsSupported)Enum.Parse(typeof(SerialSensorsSupported), Name, true); Port = new DefinitionSerialPort(Sup, Name); Valid = true; } catch (Exception e) when(e is ArgumentException || e is ArgumentNullException) { // We arrive here if the sensor does not exist in the Enum definition Sup.LogTraceErrorMessage($"Serial: Exception on Serial Port definitions in Inifile : {e.Message}"); Sup.LogTraceErrorMessage("No use continuing when the particle sensor is not there - trying anyway"); Valid = false; } }// PMS1003 Constructor
public void DoAirLink() { // For Airquality (AirNow) and nowscast documentation: see Program.cs // We get here from the main loop once per minute Sup.LogTraceInfoMessage($"DoAirLink: Adding minutevalues to the averageslists..."); TemperatureC = I2cDevice.MinuteValues.TemperatureC; TemperatureF = I2cDevice.MinuteValues.TemperatureF; Humidity = I2cDevice.MinuteValues.Humidity; PM1_last = SerialDevice.MinuteValues.Pm1_atm; // 14.8 + (0.3834 * PM25) + (-0.1498 * rHum) + (-0.1905 * temperature) if (DoCalibrated) { PM25_last = 14.8 + 0.3834 * SerialDevice.MinuteValues.Pm25_atm + -0.1498 * Humidity + -0.1905 * TemperatureC; } else { PM25_last = SerialDevice.MinuteValues.Pm25_atm; } // 14.7 + (0.3151 * PM10) + (-0.0948 * rHum) + (0.2445 * temperature) if (DoCalibrated) { PM10_last = 14.7 + 0.3151 * SerialDevice.MinuteValues.Pm10_atm + -0.0948 * Humidity + 0.2445 * TemperatureC; } else { PM10_last = SerialDevice.MinuteValues.Pm10_atm; } if (PM25_last_1_hourList.Count == 60) { PM25_last_1_hourList.RemoveAt(0); } PM25_last_1_hourList.Add(PM25_last); Sup.LogTraceInfoMessage($"DoAirLink: PM25_last_1_hourList - count: {PM25_last_1_hourList.Count} / Average: {PM25_last_1_hourList.Average():F1}"); if (PM25_last_3_hourList.Count == 3 * 60) { PM25_last_3_hourList.RemoveAt(0); } PM25_last_3_hourList.Add(PM25_last); Sup.LogTraceInfoMessage($"DoAirLink: PM25_last_3_hourList - count: {PM25_last_3_hourList.Count} / Average {PM25_last_3_hourList.Average():F1}"); if (PM25_last_24_hourList.Count == 24 * 60) { PM25_last_24_hourList.RemoveAt(0); } PM25_last_24_hourList.Add(PM25_last); Sup.LogTraceInfoMessage($"DoAirLink: PM25_last_24_hourList - count: {PM25_last_24_hourList.Count} / Average {PM25_last_24_hourList.Average():F1}"); if (PM10_last_1_hourList.Count == 60) { PM10_last_1_hourList.RemoveAt(0); } PM10_last_1_hourList.Add(PM10_last); Sup.LogTraceInfoMessage($"DoAirLink: PM10_last_1_hourList - count: {PM10_last_1_hourList.Count} / Average {PM10_last_1_hourList.Average():F1}"); if (PM10_last_3_hourList.Count == 3 * 60) { PM10_last_3_hourList.RemoveAt(0); } PM10_last_3_hourList.Add(PM10_last); Sup.LogTraceInfoMessage($"DoAirLink: PM10_last_3_hourList - count: {PM10_last_3_hourList.Count} / Average {PM10_last_3_hourList.Average():F1}"); if (PM10_last_24_hourList.Count == 24 * 60) { PM10_last_24_hourList.RemoveAt(0); } PM10_last_24_hourList.Add(PM10_last); Sup.LogTraceInfoMessage($"DoAirLink: PM10_last_24_hourList - count: {PM10_last_24_hourList.Count} / Average {PM10_last_24_hourList.Average():F1}"); #if PARANOIA // Do a paranoia check on the queue lengths: this should not be excuted in a release version // Print message to logfile if such thing occurs. Note that above the checks are on equal of the count value. // As soon as the value goes above, it is an uncorrectable issue. if (PM25_last_1_hourList.Count > 60) { Sup.LogTraceErrorMessage($"DoAirLink: PM25_last_1_hourList - count: {PM25_last_1_hourList.Count} / Average: {PM25_last_1_hourList.Average():F1}"); } if (PM25_last_3_hourList.Count > 3 * 60) { Sup.LogTraceErrorMessage($"DoAirLink: PM25_last_3_hourList - count: {PM25_last_3_hourList.Count} / Average: {PM25_last_3_hourList.Average():F1}"); } if (PM25_last_24_hourList.Count > 24 * 60) { Sup.LogTraceErrorMessage($"DoAirLink: PM25_last_24_hourList - count: {PM25_last_24_hourList.Count} / Average: {PM25_last_24_hourList.Average():F1}"); } if (PM10_last_1_hourList.Count > 60) { Sup.LogTraceErrorMessage($"DoAirLink: PM10_last_1_hourList - count: {PM10_last_1_hourList.Count} / Average: {PM10_last_1_hourList.Average():F1}"); } if (PM10_last_3_hourList.Count > 3 * 60) { Sup.LogTraceErrorMessage($"DoAirLink: PM10_last_3_hourList - count: {PM10_last_3_hourList.Count} / Average: {PM10_last_3_hourList.Average():F1}"); } if (PM10_last_24_hourList.Count > 24 * 60) { Sup.LogTraceErrorMessage($"DoAirLink: PM10_last_24_hourList - count: {PM10_last_24_hourList.Count} / Average: {PM10_last_24_hourList.Average():F1}"); } #endif NowCast25 = CalculateNowCast(PM25_last_24_hourList); NowCast10 = CalculateNowCast(PM10_last_24_hourList); return; }
void Init() { bool NoSerialDevice = false; bool NoI2cDevice = false; // Setup logging and Ini Sup = new Support(); Sup.LogDebugMessage(message: "Init() : Start"); string AirLinkPMDevice = ""; string AirLinkTHDevice = ""; Console.CancelKeyPress += new ConsoleCancelEventHandler(CtrlCHandler); CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; // Setup tracing Trace.Listeners.Add(new TextWriterTraceListener($"log/{DateTime.Now.ToString( "yyMMddHHmm", CultureInfo.InvariantCulture )}sensors.log")); Trace.AutoFlush = true; CUSensorsSwitch = new TraceSwitch("CUSensorsSwitch", "Tracing switch for CUSensors") { Level = TraceLevel.Verbose }; Sup.LogDebugMessage($"Initial {CUSensorsSwitch} => Error: {CUSensorsSwitch.TraceError}, Warning: {CUSensorsSwitch.TraceWarning}, Info: {CUSensorsSwitch.TraceInfo}, Verbose: {CUSensorsSwitch.TraceVerbose}, "); string thisTrace = Sup.GetSensorsIniValue("General", "TraceInfo", "Warning"); // Verbose, Information, Warning, Error, Off // Now set the Trace level to the wanted value switch (thisTrace.ToLower()) { case "error": CUSensorsSwitch.Level = TraceLevel.Error; break; case "warning": CUSensorsSwitch.Level = TraceLevel.Warning; break; case "info": CUSensorsSwitch.Level = TraceLevel.Info; break; case "verbose": CUSensorsSwitch.Level = TraceLevel.Verbose; break; default: CUSensorsSwitch.Level = TraceLevel.Off; break; } Sup.LogDebugMessage($"According to Inifile {CUSensorsSwitch} => Error: {CUSensorsSwitch.TraceError}, Warning: {CUSensorsSwitch.TraceWarning}, Info: {CUSensorsSwitch.TraceInfo}, Verbose: {CUSensorsSwitch.TraceVerbose}, "); // Determine which sensor has to be for the AirLink: AirLinkEmulation = Sup.GetSensorsIniValue("General", "AirLinkEmulation", "false").Equals("true", StringComparison.OrdinalIgnoreCase); AirLinkPMDevice = Sup.GetSensorsIniValue("AirLinkDevices", $"PMdevice", ""); AirLinkTHDevice = Sup.GetSensorsIniValue("AirLinkDevices", $"THdevice", ""); // Check if we want AirLinkEmulation and do accordingly // if (AirLinkEmulation) { Sup.LogDebugMessage(message: "Init : Creating the AirLink Emulator and the Webserver"); thisEmulator = new EmulateAirLink(Sup); thisWebserver = new WebServer(Sup, thisEmulator); } // Do the Serial devices Sup.LogDebugMessage(message: "Init : Creating the Serial devices"); for (int i = 0; i < MaxNrSerialSensors; i++) { string DeviceName = Sup.GetSensorsIniValue("SerialDevices", $"Serial{i}", ""); thisSerial[i] = new Serial(Sup, DeviceName); if (AirLinkEmulation && $"Serial{i}" == AirLinkPMDevice) { Sup.LogDebugMessage(message: $"Init : Serial{i} is AirLink device"); thisSerial[i].IsAirLinkSensor = true; thisEmulator.SetSerialDevice(thisSerial[i]); } if (i == 0 && string.IsNullOrEmpty(DeviceName)) { NoSerialDevice = true; break; } }// Serial devices // Do the i2c devices // Define the driver on this level so we only have one driver in the system on which we open all connections Sup.LogTraceInfoMessage(message: "Init : Creating the I2C driver"); ThisDriver = new I2cDriver(ProcessorPin.Gpio02, ProcessorPin.Gpio03, false); Sup.LogTraceInfoMessage(message: $"Init : created the I2cDriver {ThisDriver}"); I2C.DetectSensors(Sup); for (int i = 0; i < MaxNrI2cSensors; i++) { string DeviceName = Sup.GetSensorsIniValue("I2CDevices", $"I2C{i}", ""); thisI2C[i] = new I2C(Sup, DeviceName); if (AirLinkEmulation && $"I2C{i}" == AirLinkTHDevice) { Sup.LogDebugMessage(message: $"Init : I2C{i} is AirLink device"); thisI2C[i].IsAirLinkSensor = true; thisEmulator.SetI2cDevice(thisI2C[i]); } if (i == 0 && string.IsNullOrEmpty(DeviceName)) { NoI2cDevice = true; break; } }// i2c devices if (AirLinkEmulation && (NoI2cDevice || NoSerialDevice)) { Sup.LogDebugMessage("Init: At leat one serial and one I2c device is required for PM and T/H in AirLink emulation"); Environment.Exit(0); } if (AirLinkEmulation) { Sup.LogDebugMessage(message: $"Init : Starting the webserver"); thisWebserver.Start(); // Do the SensorCommunity Init DoSensorCommunity = Sup.GetSensorsIniValue("General", "SensorCommunity", "false").Equals("true", StringComparison.OrdinalIgnoreCase); Sup.LogDebugMessage(message: $"Init : Starting SensorCommunity: SensorCommunity is {DoSensorCommunity}"); if (DoSensorCommunity) { thisSensorCommunity = new SensorCommunity(Sup); } } } // Init
async Task RealMain() { Init(); Sup.LogDebugMessage(message: "ZeroWsensors : ----------------------------"); Sup.LogDebugMessage(message: "ZeroWsensors : Entering Main"); Console.WriteLine($"{Sup.Version()} {Sup.Copyright}"); Sup.LogDebugMessage($"{Sup.Version()} {Sup.Copyright}"); Console.WriteLine("This program comes with ABSOLUTELY NO WARRANTY;\n" + "This is free software, and you are welcome to redistribute it under certain conditions."); Sup.LogDebugMessage("This program comes with ABSOLUTELY NO WARRANTY;\n" + "This is free software, and you are welcome to redistribute it under certain conditions."); #region MainLoop // Start the loop using (StreamWriter of = new StreamWriter("CUSensorArray.txt", true)) { int Clock = 0; do { string thisLine = ""; // Do this condional because the ctrl-c interrupt can be given aanywhere. Sup.LogTraceInfoMessage(message: "ZeroWsensors : Getting sensor values from the Main 10 second loop"); if (Continue) { Clock++; for (int i = 0; i < MaxNrSerialSensors; i++) { thisSerial[i].DoWork(); // Takes care of the reading of the serial devices } for (int i = 0; i < MaxNrI2cSensors; i++) { thisI2C[i].DoWork(); // Takes care of the reading of the I2C devices } // So we came here 6 times every 10 seconds. Create the minute values and remove the existing list, create a new one // The average values are always real averages even if some fetches failed in which case the list is shorter // This is the basic work of the sensor handler: fetch data and write to local logfile if (Clock == 6) { Clock = 0; for (int i = 0; i < MaxNrSerialSensors; i++) { thisSerial[i].SetMinuteValuesFromObservations(); } for (int i = 0; i < MaxNrI2cSensors; i++) { thisI2C[i].SetMinuteValuesFromObservations(); } // Now we do the AirLink handling which is assumed to be called once per minute with the observation list to create // all other necessary lists and calculated values from there if (AirLinkEmulation) { thisEmulator.DoAirLink(); // When all done Write out the values to SensorCommunity if (DoSensorCommunity) { await thisSensorCommunity.Send(thisEmulator); } } // Write out to the logfile for (int i = 0; i < MaxNrSerialSensors; i++) { thisLine += $";{thisSerial[ i ].MinuteValues.Pm1_atm:F1};{thisSerial[ i ].MinuteValues.Pm25_atm:F1};{thisSerial[ i ].MinuteValues.Pm10_atm:F1}"; } for (int i = 0; i < MaxNrI2cSensors; i++) { thisLine += $";{thisI2C[ i ].MinuteValues.TemperatureC:F1};{thisI2C[ i ].MinuteValues.Humidity:F0}"; } Sup.LogTraceInfoMessage(message: "ZeroWsensors : Writing out the data to the logfile"); Sup.LogTraceInfoMessage(message: $"{DateTime.Now:dd-MM-yyyy HH:mm}{thisLine}"); of.WriteLine($"{DateTime.Now:dd-MM-yyyy HH:mm}{thisLine}"); of.Flush(); } } // This is really hardcoded and should NOT change. The whole thing is based on 6 measurements per minute, so loop every 10 seconds Thread.Sleep(10000); } while (Continue); // Do-While } // Using the datafile #endregion Sup.LogDebugMessage("SensorArray Gracefull exit... End"); Trace.Flush(); Trace.Close(); return; } // Real main()