diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/IRedoxPotentialSensor.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/IRedoxPotentialSensor.cs
new file mode 100644
index 0000000000..7486ad449b
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/IRedoxPotentialSensor.cs
@@ -0,0 +1,15 @@
+using Meadow.Peripherals.Sensors;
+using Meadow.Units;
+
+namespace Meadow.Foundation.Sensors.Environmental;
+
+///
+/// Represents a sensor for measuring oxidation/reduction potential
+///
+public interface IRedoxPotentialSensor : ISamplingSensor
+{
+ ///
+ /// Last value read from the sensor
+ ///
+ Voltage? Potential { get; }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/IWaterQualityConcentrationsSensor.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/IWaterQualityConcentrationsSensor.cs
new file mode 100644
index 0000000000..528129849a
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/IWaterQualityConcentrationsSensor.cs
@@ -0,0 +1,14 @@
+using Meadow.Peripherals.Sensors;
+
+namespace Meadow.Foundation.Sensors.Environmental;
+
+///
+/// Represents a sensor for measuring water quality concentrations
+///
+public interface IWaterQualityConcentrationsSensor : ISamplingSensor
+{
+ ///
+ /// Last value read from the sensor
+ ///
+ WaterQualityConcentrations? Concentrations { get; }
+}
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/WaterQualityConcentrations.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/WaterQualityConcentrations.cs
new file mode 100644
index 0000000000..11362dd078
--- /dev/null
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/WaterQualityConcentrations.cs
@@ -0,0 +1,22 @@
+using Meadow.Units;
+
+namespace Meadow.Foundation.Sensors.Environmental;
+
+///
+/// Represents concentrations indicating water quality
+///
+public struct WaterQualityConcentrations
+{
+ ///
+ /// Concentration of dissolved Oxygen in water
+ ///
+ public ConcentrationInWater? DissolvedOxygen;
+ ///
+ /// Chlorophyll Concentration (CHL)
+ ///
+ public ConcentrationInWater? Chlorophyl;
+ ///
+ /// Salination (SAL)
+ ///
+ public ConcentrationInWater? BlueGreenAlgae;
+}
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/Y4000.Structs.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/Y4000.Structs.cs
index 85a37febc1..a0b44d9648 100644
--- a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/Y4000.Structs.cs
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/Y4000.Structs.cs
@@ -10,7 +10,7 @@ public partial class Y4000
//check for infinity and switch to zero
//populate all of the properties
- enum Measurement
+ private enum Measurement
{
DissolvedOxygen, //DO
Turbidity,
@@ -27,11 +27,6 @@ enum Measurement
///
public struct Measurements
{
- ///
- /// Concentration of dissolved Oxygen in water
- ///
- public ConcentrationInWater DissolvedOxygen { get; private set; }
-
///
/// Turbidity (NTU)
///
@@ -54,14 +49,9 @@ public struct Measurements
public Voltage OxidationReductionPotential { get; private set; }
///
- /// Chlorophyll Concentration (CHL)
- ///
- public ConcentrationInWater Chlorophyl { get; private set; }
-
- ///
- /// Salination (SAL)
+ /// Water quality concentraions
///
- public ConcentrationInWater BlueGreenAlgae { get; private set; }
+ public WaterQualityConcentrations Concentrations { get; private set; }
///
/// Temperature
@@ -79,8 +69,8 @@ public Measurements(float[] data)
throw new ArgumentException($"Measurements record expects 8 values, received {data.Length}");
}
- float value = Normalize(data[(int)Measurement.DissolvedOxygen]);
- DissolvedOxygen = new ConcentrationInWater(value, ConcentrationInWater.UnitType.MilligramsPerLiter);
+ var value = Normalize(data[(int)Measurement.Temperature]);
+ Temperature = new Units.Temperature(value, Units.Temperature.UnitType.Celsius);
value = Normalize(data[(int)Measurement.Turbidity]);
Turbidity = new Turbidity(value);
@@ -95,16 +85,23 @@ public Measurements(float[] data)
OxidationReductionPotential = new Voltage(value, Voltage.UnitType.Volts);
value = Normalize(data[(int)Measurement.Chlorophyl]);
- Chlorophyl = new ConcentrationInWater(value, ConcentrationInWater.UnitType.MicrogramsPerLiter);
+ var chlorophyl = new ConcentrationInWater(value, ConcentrationInWater.UnitType.MicrogramsPerLiter);
value = Normalize(data[(int)Measurement.BlueGreenAlgae]);
- BlueGreenAlgae = new ConcentrationInWater(value, ConcentrationInWater.UnitType.MilligramsPerLiter);
+ var blueGreenAlgae = new ConcentrationInWater(value, ConcentrationInWater.UnitType.MilligramsPerLiter);
- value = Normalize(data[(int)Measurement.Temperature]);
- Temperature = new Units.Temperature(value, Units.Temperature.UnitType.Celsius);
+ value = Normalize(data[(int)Measurement.DissolvedOxygen]);
+ var dissolvedOxygen = new ConcentrationInWater(value, ConcentrationInWater.UnitType.MilligramsPerLiter);
+
+ Concentrations = new WaterQualityConcentrations
+ {
+ Chlorophyl = chlorophyl,
+ BlueGreenAlgae = blueGreenAlgae,
+ DissolvedOxygen = dissolvedOxygen
+ };
}
- static float Normalize(float value) => float.IsNormal(value) ? value : 0;
+ private static float Normalize(float value) => float.IsNormal(value) ? value : 0;
///
/// Returns a string that represents the current Y4000 measurement data.
@@ -113,13 +110,13 @@ public Measurements(float[] data)
public override string ToString()
{
StringBuilder sb = new StringBuilder();
- sb.AppendLine($"DissolvedOxygen: {DissolvedOxygen} mg/L");
+ sb.AppendLine($"DissolvedOxygen: {Concentrations.DissolvedOxygen} mg/L");
sb.AppendLine($"Turbidity: {Turbidity} NTU");
sb.AppendLine($"ElectricalConductivity: {ElectricalConductivity.MilliSiemensPerCentimeter} mS/cm");
sb.AppendLine($"PH: {PH}");
sb.AppendLine($"OxidationReductionPotential: {OxidationReductionPotential.Millivolts} mV");
- sb.AppendLine($"Chlorophyll: {Chlorophyl.MicrogramsPerLiter} ug/L");
- sb.AppendLine($"BlueGreenAlgae: {BlueGreenAlgae.PartsPerMillion} ppm");
+ sb.AppendLine($"Chlorophyll: {Concentrations.Chlorophyl.Value.MicrogramsPerLiter} ug/L");
+ sb.AppendLine($"BlueGreenAlgae: {Concentrations.BlueGreenAlgae.Value.PartsPerMillion} ppm");
sb.AppendLine($"Temperature: {Temperature.Celsius} C");
return sb.ToString();
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/Y4000.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/Y4000.cs
index fd46c38bd7..dd40634216 100644
--- a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/Y4000.cs
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Driver/Y4000.cs
@@ -1,428 +1,459 @@
using Meadow.Hardware;
using Meadow.Modbus;
+using Meadow.Peripherals.Sensors;
+using Meadow.Peripherals.Sensors.Environmental;
using Meadow.Units;
using System;
+using System.Threading;
using System.Threading.Tasks;
-#nullable enable
-
-namespace Meadow.Foundation.Sensors.Environmental
+namespace Meadow.Foundation.Sensors.Environmental;
+
+///
+/// Represents a Yosemitech Y4000 Multiparameter Sonde water quality sensor
+/// for dissolved oxygen, conductivity, turbidity, pH, chlorophyll,
+/// blue green algae, chlorophyll, and temperature
+///
+public partial class Y4000 :
+ IDisposable,
+ IWaterQualityConcentrationsSensor,
+ IElectricalConductivitySensor,
+ IPotentialHydrogenSensor,
+ ITurbiditySensor,
+ ITemperatureSensor,
+ IRedoxPotentialSensor
{
+ private Timer updateTimer;
+ private TimeSpan? updateInterval = null;
+ private Measurements? lastMeasurements = null;
///
- /// Represents a Yosemitech Y4000 Multiparameter Sonde water quality sensor
- /// for dissolved oxygen, conductivity, turbidity, pH, chlorophyll,
- /// blue green algae, chlorophyll, and temperature
+ /// Did we create the port(s) used by the peripheral
///
- public partial class Y4000 : PollingSensorBase<(ConcentrationInWater? DissolvedOxygen,
- ConcentrationInWater? Chlorophyl,
- ConcentrationInWater? BlueGreenAlgae,
- Conductivity? ElectricalConductivity,
- PotentialHydrogen? PH,
- Turbidity? Turbidity,
- Units.Temperature? Temperature,
- Voltage? OxidationReductionPotential)>, IDisposable
- {
- ///
- /// Raised when the DissolvedOxygen value changes
- ///
- public event EventHandler> DissolvedOxygenUpdated = default!;
-
- ///
- /// Raised when the Chlorophyll value changes
- ///
- public event EventHandler> ChlorophylUpdated = default!;
-
- ///
- /// Raised when the BlueGreenAlgae value changes
- ///
- public event EventHandler> BlueGreenAlgaeUpdated = default!;
-
- ///
- /// Raised when the ElectricalConductivity value changes
- ///
- public event EventHandler> ElectricalConductivityUpdated = default!;
-
- ///
- /// Raised when the PotentialHydrogen (pH) value changes
- ///
- public event EventHandler> PHUpdated = default!;
-
- ///
- /// Raised when the Turbidity value changes
- ///
- public event EventHandler> TurbidityUpdated = default!;
-
- ///
- /// Raised when the Temperature value changes
- ///
- public event EventHandler> TemperatureUpdated = default!;
-
- ///
- /// Raised when the OxidationReductionPotential (redux) value changes
- ///
- public event EventHandler> OxidationReductionPotentialUpdated = default!;
-
- ///
- /// The current Dissolved Oxygen concentration
- ///
- public ConcentrationInWater? DissolvedOxygen => Conditions.DissolvedOxygen;
-
- ///
- /// The current Chlorophyll concentration
- ///
- public ConcentrationInWater? Chlorophyl => Conditions.Chlorophyl;
-
- ///
- /// The current Blue Green Algae concentration
- ///
- public ConcentrationInWater? BlueGreenAlgae => Conditions.BlueGreenAlgae;
-
- ///
- /// The current Electrical Conductivity
- ///
- public Conductivity? ElectricalConductivity => Conditions.ElectricalConductivity;
-
- ///
- /// The current Potential Hydrogen (pH)
- ///
- public PotentialHydrogen? PH => Conditions.PH;
-
- ///
- /// The current Turbidity
- ///
- public Turbidity? Turbidity => Conditions.Turbidity;
-
- ///
- /// The current Oxidation Reduction Potential (redux)
- ///
- public Voltage? OxidationReductionPotential => Conditions.OxidationReductionPotential;
-
- ///
- /// Is the object disposed
- ///
- public bool IsDisposed { get; private set; }
-
- ///
- /// Did we create the port(s) used by the peripheral
- ///
- readonly bool createdPort = false;
-
- readonly IModbusBusClient modbusClient;
-
- ///
- /// 9600 baud 8-N-1
- ///
- readonly ISerialPort? serialPort;
-
- ///
- /// The current modbus address
- ///
- public byte ModbusAddress { get; private set; } = 0x01;
-
- ///
- /// Creates a new Y4000 object
- ///
- public Y4000(
- IModbusBusClient modbusClient,
- byte modbusAddress = 0x01)
- {
- this.modbusClient = modbusClient;
- ModbusAddress = modbusAddress;
- }
+ private readonly bool createdPort = false;
+ private readonly IModbusBusClient modbusClient;
- ///
- /// Creates a new Y4000 object
- ///
- public Y4000(IMeadowDevice device,
- SerialPortName serialPortName,
- byte modbusAddress = 0x01,
- IPin? enablePin = null)
- {
- createdPort = true;
+ ///
+ /// 9600 baud 8-N-1
+ ///
+ private readonly ISerialPort? serialPort;
- serialPort = device.CreateSerialPort(serialPortName, 9600, 8, Parity.None, StopBits.One);
- serialPort.WriteTimeout = serialPort.ReadTimeout = TimeSpan.FromSeconds(5);
+ ///
+ /// The default baud rate for communicating with the device
+ ///
+ public const int DefaultBaudRate = 9600;
+
+ private EventHandler>? tempEvents;
+ private EventHandler>? turbidityEvents;
+ private EventHandler>? phEvents;
+ private EventHandler>? conductivityEvents;
+ private EventHandler>? redoxEvents;
+ private EventHandler>? concentrationEvents;
+
+ Units.Temperature? ITemperatureSensor.Temperature => lastMeasurements?.Temperature;
+ Turbidity? ITurbiditySensor.Turbidity => lastMeasurements?.Turbidity;
+ PotentialHydrogen? IPotentialHydrogenSensor.pH => lastMeasurements?.PH;
+ Conductivity? IElectricalConductivitySensor.Conductivity => lastMeasurements?.ElectricalConductivity;
+ Voltage? IRedoxPotentialSensor.Potential => lastMeasurements?.OxidationReductionPotential;
+ WaterQualityConcentrations? IWaterQualityConcentrationsSensor.Concentrations => lastMeasurements?.Concentrations;
+
+ event EventHandler> ISamplingSensor.Updated
+ {
+ add => tempEvents += value;
+ remove => tempEvents -= value;
+ }
- if (enablePin != null)
- {
- var enablePort = device.CreateDigitalOutputPort(enablePin, false);
- modbusClient = new ModbusRtuClient(serialPort, enablePort);
- }
- else
- {
- modbusClient = new ModbusRtuClient(serialPort);
- }
+ event EventHandler> ISamplingSensor.Updated
+ {
+ add => turbidityEvents += value;
+ remove => turbidityEvents -= value;
+ }
- ModbusAddress = modbusAddress;
- }
+ event EventHandler> ISamplingSensor.Updated
+ {
+ add => phEvents += value;
+ remove => phEvents -= value;
+ }
+
+ event EventHandler> ISamplingSensor.Updated
+ {
+ add => conductivityEvents += value;
+ remove => conductivityEvents -= value;
+ }
- ///
- /// Initialize sensor
- ///
- ///
- public Task Initialize()
+ event EventHandler> ISamplingSensor.Updated
+ {
+ add => redoxEvents += value;
+ remove => redoxEvents -= value;
+ }
+
+ event EventHandler> ISamplingSensor.Updated
+ {
+ add => concentrationEvents += value;
+ remove => concentrationEvents -= value;
+ }
+
+ ///
+ public TimeSpan UpdateInterval => updateInterval ?? TimeSpan.FromSeconds(5);
+
+ ///
+ public bool IsSampling => updateInterval != null;
+
+ ///
+ public void StartUpdating(TimeSpan? updateInterval)
+ {
+ this.updateInterval = updateInterval ?? TimeSpan.FromSeconds(5);
+ updateTimer.Change(TimeSpan.Zero, this.updateInterval.Value);
+ }
+
+ ///
+ public void StopUpdating()
+ {
+ this.updateInterval = null;
+ updateTimer.Change(Timeout.Infinite, Timeout.Infinite);
+ }
+
+ ///
+ /// Creates a new Y4000 object
+ ///
+ public Y4000(IMeadowDevice device,
+ SerialPortName serialPortName,
+ byte modbusAddress = 0x01,
+ IPin? enablePin = null)
+ {
+ createdPort = true;
+
+ serialPort = device.CreateSerialPort(serialPortName, 9600, 8, Parity.None, StopBits.One);
+ serialPort.WriteTimeout = serialPort.ReadTimeout = TimeSpan.FromSeconds(5);
+
+ if (enablePin != null)
{
- return modbusClient.Connect();
+ var enablePort = device.CreateDigitalOutputPort(enablePin, false);
+ modbusClient = new ModbusRtuClient(serialPort, enablePort);
}
-
- ///
- /// Get the device ISDN (address) of the sensor
- /// Note this is a broadcast event so all Y4000 devices on the bus will respond
- ///
- /// The address as a byte
- public async Task GetISDN()
+ else
{
- var data = await modbusClient.ReadHoldingRegisters(0xFF, Registers.ISDN.Offset, Registers.ISDN.Length);
-
- return (byte)(data[0] >> 8);
+ modbusClient = new ModbusRtuClient(serialPort);
}
- ///
- /// Set the ISDN (address) of the sensor
- ///
- /// The address
- ///
- public async Task SetISDN(byte modbusAddress)
- {
- if (ModbusAddress == modbusAddress) { return; }
+ ModbusAddress = modbusAddress;
- await modbusClient.WriteHoldingRegisters(ModbusAddress,
- Registers.ISDN.Offset,
- new ushort[] { (ushort)(modbusAddress << 8) });
+ updateTimer = new Timer(UpdateTimerProc);
+ }
- ModbusAddress = modbusAddress;
- }
+ private async void UpdateTimerProc(object _)
+ {
+ var currentmeasurements = await ReadSensor();
+ RaiseEventsAndNotify(currentmeasurements);
+ }
+
+ ///
+ /// Reads all measurements of the sensor
+ ///
+ public async Task ReadSensor()
+ {
+ var values = await modbusClient.ReadHoldingRegistersFloat(ModbusAddress, Registers.Data.Offset, Registers.Data.Length / 2);
+ return new Measurements(values);
+ }
- ///
- /// Get the current supply voltage
- ///
- ///
- public async Task GetSupplyVoltage()
+ async Task ISensor.Read()
+ {
+ Measurements measurements;
+
+ if (!IsSampling || lastMeasurements == null)
{
- var voltage = await modbusClient.ReadHoldingRegistersFloat(ModbusAddress, Registers.SupplyVoltage.Offset, Registers.SupplyVoltage.Length / 2);
- return new Voltage(voltage[0], Voltage.UnitType.Volts);
+ measurements = await ReadSensor();
}
-
- ///
- /// Get the device serial number
- ///
- /// The serial number as a ushort array
- public async Task GetSerialNumber()
+ else
{
- var data = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.SerialNumber.Offset, Registers.SerialNumber.Length);
-
- return data;
+ measurements = lastMeasurements.Value;
}
+ return measurements.Temperature;
+ }
- ///
- /// Get the device version
- ///
- ///
- public async Task GetVersion()
+ async Task ISensor.Read()
+ {
+ Measurements measurements;
+
+ if (!IsSampling || lastMeasurements == null)
{
- var data = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.Version.Offset, Registers.Version.Length);
- return data;
+ measurements = await ReadSensor();
}
-
- ///
- /// Get the brush or wiper interval
- ///
- ///
- public async Task GetBrushInterval()
+ else
{
- var value = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.BrushInterval.Offset, Registers.BrushInterval.Length);
- return TimeSpan.FromMinutes(value[0]);
+ measurements = lastMeasurements.Value;
}
+ return measurements.Turbidity;
+ }
- ///
- /// Set the brush or wiper interval (normalized to minutes)
- ///
- public Task SetBrushInterval(TimeSpan interval)
+ async Task ISensor.Read()
+ {
+ Measurements measurements;
+
+ if (!IsSampling || lastMeasurements == null)
{
- ushort minutes = (ushort)interval.TotalMinutes;
- return modbusClient.WriteHoldingRegister(ModbusAddress, Registers.BrushInterval.Offset, minutes);
+ measurements = await ReadSensor();
}
-
- ///
- /// Start the brush or wiper
- ///
- ///
- public Task StartBrush()
+ else
{
- return modbusClient.WriteHoldingRegister(ModbusAddress, Registers.StartBrush.Offset, 0);
+ measurements = lastMeasurements.Value;
}
+ return measurements.PH;
+ }
+
+ async Task ISensor.Read()
+ {
+ Measurements measurements;
- ///
- /// Read the error flag from the sensor
- ///
- ///
- public async Task GetErrorFlag()
+ if (!IsSampling || lastMeasurements == null)
{
- var data = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.ErrorCode.Offset, 2);
- return data[0];
+ measurements = await ReadSensor();
}
-
- /*
- * Get and Set time work but Get returns bad values
- * Leaving code here for future investigation
- */
- ///
- /// Set the time on the device
- /// Stores: year, month, day, hour, minute and second
- ///
- ///
- ///
- Task SetTime(DateTime time)
+ else
{
- byte second = 0x17;// (byte)time.Second;
- byte minute = 0x05;//(byte)time.Minute;
- byte hour = 0x13;//(byte)time.Hour;
- byte day = 0x26;//(byte)time.Day;
- byte month = 0x04;//(byte)time.Month;
- //0
- byte year = 0x16; // (byte)time.Year;
- //0
-
- var data = new ushort[4];
- data[0] = (ushort)(minute | (second << 8));
- data[1] = (ushort)(day | (hour << 8));
- data[2] = (ushort)(month << 8 | 0x00);
- data[3] = (ushort)(year << 8 | 0x00);
-
- return modbusClient.WriteHoldingRegisters(ModbusAddress, Registers.Time.Offset, data);
+ measurements = lastMeasurements.Value;
}
+ return measurements.ElectricalConductivity;
+ }
- ///
- /// Get the time stored on the sensor
- ///
- ///
- async Task GetTime()
- {
- var values = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.Time.Offset, 4);
+ async Task ISensor.Read()
+ {
+ Measurements measurements;
- return DateTime.MinValue;
+ if (!IsSampling || lastMeasurements == null)
+ {
+ measurements = await ReadSensor();
}
-
- ///
- /// Reads data from the sensor
- ///
- /// The latest sensor reading
- protected override async Task<(ConcentrationInWater? DissolvedOxygen,
- ConcentrationInWater? Chlorophyl,
- ConcentrationInWater? BlueGreenAlgae,
- Conductivity? ElectricalConductivity,
- PotentialHydrogen? PH,
- Turbidity? Turbidity,
- Units.Temperature? Temperature,
- Voltage? OxidationReductionPotential)>
- ReadSensor()
+ else
{
- (ConcentrationInWater? DissolvedOxygen,
- ConcentrationInWater? Chlorophyl,
- ConcentrationInWater? BlueGreenAlgae,
- Conductivity? ElectricalConductivity,
- PotentialHydrogen? PH,
- Turbidity? Turbidity,
- Units.Temperature? Temperature,
- Voltage? OxidationReductionPotential) conditions;
-
- var values = await modbusClient.ReadHoldingRegistersFloat(ModbusAddress, Registers.Data.Offset, Registers.Data.Length / 2);
- var measurements = new Measurements(values);
-
- conditions.BlueGreenAlgae = measurements.BlueGreenAlgae;
- conditions.Chlorophyl = measurements.Chlorophyl;
- conditions.DissolvedOxygen = measurements.DissolvedOxygen;
- conditions.ElectricalConductivity = measurements.ElectricalConductivity;
- conditions.OxidationReductionPotential = measurements.OxidationReductionPotential;
- conditions.PH = measurements.PH;
- conditions.Temperature = measurements.Temperature;
- conditions.Turbidity = measurements.Turbidity;
-
- return conditions;
+ measurements = lastMeasurements.Value;
}
+ return measurements.OxidationReductionPotential;
+ }
- ///
- /// Raise events for subscribers and notify of value changes
- ///
- /// The updated sensor data
- protected override void RaiseEventsAndNotify(
- IChangeResult<
- (ConcentrationInWater? DissolvedOxygen,
- ConcentrationInWater? Chlorophyl,
- ConcentrationInWater? BlueGreenAlgae,
- Conductivity? ElectricalConductivity,
- PotentialHydrogen? PH,
- Turbidity? Turbidity,
- Units.Temperature? Temperature,
- Voltage? OxidationReductionPotential)
- > changeResult)
- {
- if (changeResult.New.DissolvedOxygen is { } DO)
- {
- DissolvedOxygenUpdated?.Invoke(this, new ChangeResult(DO, changeResult.Old?.DissolvedOxygen));
- }
- if (changeResult.New.Chlorophyl is { } Chl)
- {
- ChlorophylUpdated?.Invoke(this, new ChangeResult(Chl, changeResult.Old?.Chlorophyl));
- }
- if (changeResult.New.BlueGreenAlgae is { } BGR)
- {
- BlueGreenAlgaeUpdated?.Invoke(this, new ChangeResult(BGR, changeResult.Old?.BlueGreenAlgae));
- }
- if (changeResult.New.ElectricalConductivity is { } EC)
- {
- ElectricalConductivityUpdated?.Invoke(this, new ChangeResult(EC, changeResult.Old?.ElectricalConductivity));
- }
- if (changeResult.New.PH is { } PH)
- {
- PHUpdated?.Invoke(this, new ChangeResult(PH, changeResult.Old?.PH));
- }
- if (changeResult.New.Turbidity is { } Tur)
- {
- TurbidityUpdated?.Invoke(this, new ChangeResult(Tur, changeResult.Old?.Turbidity));
- }
- if (changeResult.New.Temperature is { } Temp)
- {
- TemperatureUpdated?.Invoke(this, new ChangeResult(Temp, changeResult.Old?.Temperature));
- }
- if (changeResult.New.OxidationReductionPotential is { } Redux)
- {
- OxidationReductionPotentialUpdated?.Invoke(this, new ChangeResult(Redux, changeResult.Old?.OxidationReductionPotential));
- }
+ async Task ISensor.Read()
+ {
+ Measurements measurements;
- base.RaiseEventsAndNotify(changeResult);
+ if (!IsSampling || lastMeasurements == null)
+ {
+ measurements = await ReadSensor();
}
-
- ///
- public void Dispose()
+ else
{
- Dispose(disposing: true);
- GC.SuppressFinalize(this);
+ measurements = lastMeasurements.Value;
}
+ return measurements.Concentrations;
+ }
+
+ private void RaiseEventsAndNotify(Measurements currentmeasurements)
+ {
+ tempEvents?.Invoke(this, new ChangeResult(currentmeasurements.Temperature, lastMeasurements?.Temperature));
+ turbidityEvents?.Invoke(this, new ChangeResult(currentmeasurements.Turbidity, lastMeasurements?.Turbidity));
+ phEvents?.Invoke(this, new ChangeResult(currentmeasurements.PH, lastMeasurements?.PH));
+ conductivityEvents?.Invoke(this, new ChangeResult(currentmeasurements.ElectricalConductivity, lastMeasurements?.ElectricalConductivity));
+ redoxEvents?.Invoke(this, new ChangeResult(currentmeasurements.OxidationReductionPotential, lastMeasurements?.OxidationReductionPotential));
+ concentrationEvents?.Invoke(this, new ChangeResult(currentmeasurements.Concentrations, lastMeasurements?.Concentrations));
+
+ lastMeasurements = currentmeasurements;
+ }
+
+ ///
+ /// Is the object disposed
+ ///
+ public bool IsDisposed { get; private set; }
+
+ ///
+ /// The current modbus address
+ ///
+ public byte ModbusAddress { get; private set; } = 0x01;
+
+ ///
+ /// Creates a new Y4000 object
+ ///
+ public Y4000(
+ IModbusBusClient modbusClient,
+ byte modbusAddress = 0x01)
+ {
+ this.modbusClient = modbusClient;
+ ModbusAddress = modbusAddress;
+ }
+
+ ///
+ /// Initialize sensor
+ ///
+ ///
+ public Task Initialize()
+ {
+ return modbusClient.Connect();
+ }
+
+ ///
+ /// Get the device ISDN (address) of the sensor
+ /// Note this is a broadcast event so all Y4000 devices on the bus will respond
+ ///
+ /// The address as a byte
+ public async Task GetISDN()
+ {
+ var data = await modbusClient.ReadHoldingRegisters(0xFF, Registers.ISDN.Offset, Registers.ISDN.Length);
+
+ return (byte)(data[0] >> 8);
+ }
+
+ ///
+ /// Set the ISDN (address) of the sensor
+ ///
+ /// The address
+ ///
+ public async Task SetISDN(byte modbusAddress)
+ {
+ if (ModbusAddress == modbusAddress) { return; }
+
+ await modbusClient.WriteHoldingRegisters(ModbusAddress,
+ Registers.ISDN.Offset,
+ new ushort[] { (ushort)(modbusAddress << 8) });
+
+ ModbusAddress = modbusAddress;
+ }
+
+ ///
+ /// Get the current supply voltage
+ ///
+ ///
+ public async Task GetSupplyVoltage()
+ {
+ var voltage = await modbusClient.ReadHoldingRegistersFloat(ModbusAddress, Registers.SupplyVoltage.Offset, Registers.SupplyVoltage.Length / 2);
+ return new Voltage(voltage[0], Voltage.UnitType.Volts);
+ }
+
+ ///
+ /// Get the device serial number
+ ///
+ /// The serial number as a ushort array
+ public async Task GetSerialNumber()
+ {
+ var data = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.SerialNumber.Offset, Registers.SerialNumber.Length);
+
+ return data;
+ }
- ///
- /// Dispose of the object
- ///
- /// Is disposing
- protected virtual void Dispose(bool disposing)
+ ///
+ /// Get the device version
+ ///
+ ///
+ public async Task GetVersion()
+ {
+ var data = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.Version.Offset, Registers.Version.Length);
+ return data;
+ }
+
+ ///
+ /// Get the brush or wiper interval
+ ///
+ ///
+ public async Task GetBrushInterval()
+ {
+ var value = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.BrushInterval.Offset, Registers.BrushInterval.Length);
+ return TimeSpan.FromMinutes(value[0]);
+ }
+
+ ///
+ /// Set the brush or wiper interval (normalized to minutes)
+ ///
+ public Task SetBrushInterval(TimeSpan interval)
+ {
+ ushort minutes = (ushort)interval.TotalMinutes;
+ return modbusClient.WriteHoldingRegister(ModbusAddress, Registers.BrushInterval.Offset, minutes);
+ }
+
+ ///
+ /// Start the brush or wiper
+ ///
+ ///
+ public Task StartBrush()
+ {
+ return modbusClient.WriteHoldingRegister(ModbusAddress, Registers.StartBrush.Offset, 0);
+ }
+
+ ///
+ /// Read the error flag from the sensor
+ ///
+ ///
+ public async Task GetErrorFlag()
+ {
+ var data = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.ErrorCode.Offset, 2);
+ return data[0];
+ }
+
+ /*
+ * Get and Set time work but Get returns bad values
+ * Leaving code here for future investigation
+ */
+ ///
+ /// Set the time on the device
+ /// Stores: year, month, day, hour, minute and second
+ ///
+ ///
+ ///
+ private Task SetTime(DateTime time)
+ {
+ byte second = 0x17;// (byte)time.Second;
+ byte minute = 0x05;//(byte)time.Minute;
+ byte hour = 0x13;//(byte)time.Hour;
+ byte day = 0x26;//(byte)time.Day;
+ byte month = 0x04;//(byte)time.Month;
+ //0
+ byte year = 0x16; // (byte)time.Year;
+ //0
+
+ var data = new ushort[4];
+ data[0] = (ushort)(minute | (second << 8));
+ data[1] = (ushort)(day | (hour << 8));
+ data[2] = (ushort)(month << 8 | 0x00);
+ data[3] = (ushort)(year << 8 | 0x00);
+
+ return modbusClient.WriteHoldingRegisters(ModbusAddress, Registers.Time.Offset, data);
+ }
+
+ ///
+ /// Get the time stored on the sensor
+ ///
+ ///
+ private async Task GetTime()
+ {
+ var values = await modbusClient.ReadHoldingRegisters(ModbusAddress, Registers.Time.Offset, 4);
+
+ return DateTime.MinValue;
+ }
+
+ ///
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Dispose of the object
+ ///
+ /// Is disposing
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!IsDisposed)
{
- if (!IsDisposed)
+ if (disposing && createdPort)
{
- if (disposing && createdPort)
+ if (serialPort is { })
{
- if (serialPort is { })
+ if (serialPort.IsOpen)
{
- if (serialPort.IsOpen)
- {
- serialPort.Close();
- }
-
- serialPort.Dispose();
+ serialPort.Close();
}
- }
- IsDisposed = true;
+ serialPort.Dispose();
+ }
}
+
+ IsDisposed = true;
}
}
}
\ No newline at end of file
diff --git a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Samples/Y4000_Sample/MeadowApp.cs b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Samples/Y4000_Sample/MeadowApp.cs
index 13ff4bd465..8498550c8c 100644
--- a/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Samples/Y4000_Sample/MeadowApp.cs
+++ b/Source/Meadow.Foundation.Peripherals/Sensors.Environmental.Y4000/Samples/Y4000_Sample/MeadowApp.cs
@@ -9,9 +9,9 @@ public class MeadowApp : App
{
//
- Y4000 sensor;
+ private Y4000 sensor;
- public async override Task Initialize()
+ public override async Task Initialize()
{
Resolver.Log.Info("Initialize...");
await Task.Delay(2000);
@@ -32,7 +32,7 @@ public override async Task Run()
var supplyVoltage = await sensor.GetSupplyVoltage();
Resolver.Log.Info($"Supply voltage: {supplyVoltage}");
- var measurements = await sensor.Read();
+ var measurements = await sensor.ReadSensor();
Resolver.Log.Info($"Sensor data: {measurements}");
}