diff --git a/src/devices/Bmxx80/Bme280.cs b/src/devices/Bmxx80/Bme280.cs index dfe195e0ee..047112c2e6 100644 --- a/src/devices/Bmxx80/Bme280.cs +++ b/src/devices/Bmxx80/Bme280.cs @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Device.I2c; -using System.Threading.Tasks; using Iot.Device.Bmxx80.CalibrationData; -using Iot.Device.Bmxx80.PowerMode; using Iot.Device.Bmxx80.Register; namespace Iot.Device.Bmxx80 @@ -13,7 +12,7 @@ namespace Iot.Device.Bmxx80 /// /// Represents a BME280 temperature, barometric pressure and humidity sensor. /// - public class Bme280 : Bmx280Base + public sealed class Bme280 : Bmx280Base { /// /// The expected chip ID of the BME280. @@ -23,7 +22,9 @@ public class Bme280 : Bmx280Base /// /// Calibration data for the . /// - private readonly Bme280CalibrationData _bme280Calibration; + private Bme280CalibrationData _bme280Calibration; + + private Sampling _humiditySampling; /// /// Initializes a new instance of the class. @@ -32,62 +33,79 @@ public class Bme280 : Bmx280Base public Bme280(I2cDevice i2cDevice) : base(DeviceId, i2cDevice) { - var bme280CalibrationData = new Bme280CalibrationData(); - bme280CalibrationData.ReadFromDevice(this); - _bme280Calibration = bme280CalibrationData; - _calibrationData = bme280CalibrationData; - + _bme280Calibration = (Bme280CalibrationData)_calibrationData; _communicationProtocol = CommunicationProtocol.I2c; } /// - /// Get the current sample rate for humidity measurements. + /// Gets or sets the humidity sampling. /// - /// The humidity . - public Sampling ReadHumiditySampling() + /// Thrown when the is set to an undefined mode. + public Sampling HumiditySampling { - byte status = Read8BitsFromRegister((byte)Bme280Register.CTRL_HUM); - status = (byte)(status & 0b_0000_0111); - return ByteToSampling(status); + get => _humiditySampling; + set + { + if (!Enum.IsDefined(typeof(Sampling), value)) + throw new ArgumentOutOfRangeException(); + + byte status = Read8BitsFromRegister((byte)Bme280Register.CTRL_HUM); + status = (byte)(status & 0b_1111_1000); + status = (byte)(status | (byte)value); + + Span command = stackalloc[] {(byte)Bme280Register.CTRL_HUM, status}; + _i2cDevice.Write(command); + + // Changes to the above register only become effective after a write operation to "CTRL_MEAS". + byte measureState = Read8BitsFromRegister((byte)Bmx280Register.CTRL_MEAS); + + command = stackalloc[] {(byte)Bmx280Register.CTRL_MEAS, measureState}; + _i2cDevice.Write(command); + _humiditySampling = value; + } } /// - /// Sets the humidity sampling to the given value. + /// Reads the humidity. A return value indicates whether the reading succeeded. /// - /// The to set. - public void SetHumiditySampling(Sampling sampling) + /// + /// Contains the measured humidity as %rH if the was not set to . + /// Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public bool TryReadHumidity(out double humidity) { - byte status = Read8BitsFromRegister((byte)Bme280Register.CTRL_HUM); - status = (byte)(status & 0b_1111_1000); - status = (byte)(status | (byte)sampling); - _i2cDevice.Write(new[] { (byte)Bme280Register.CTRL_HUM, status }); - - // Changes to the above register only become effective after a write operation to "CTRL_MEAS". - byte measureState = Read8BitsFromRegister((byte)Bmx280Register.CTRL_MEAS); - _i2cDevice.Write(new[] { (byte)Bmx280Register.CTRL_MEAS, measureState }); + if (HumiditySampling == Sampling.Skipped) + { + humidity = double.NaN; + return false; + } + + // Read the temperature first to load the t_fine value for compensation. + TryReadTemperature(out _); + + var hum = Read16BitsFromRegister((byte)Bme280Register.HUMIDDATA, Endianness.BigEndian); + + humidity = CompensateHumidity(hum); + return true; } /// - /// Reads the Humidity from the sensor as %rH. + /// Gets the required time in ms to perform a measurement with the current sampling modes. /// - /// Returns a percentage from 0 to 100. - public async Task ReadHumidityAsync() + /// The time it takes for the chip to read data in milliseconds rounded up. + public override int GetMeasurementDuration() { - if (ReadPowerMode() == Bmx280PowerMode.Forced) - { - await Task.Delay(GetMeasurementTimeForForcedMode(ReadHumiditySampling())); - } - - // Read the temperature first to load the t_fine value for compensation. - await ReadTemperatureAsync(); - - byte msb = Read8BitsFromRegister((byte)Bme280Register.HUMIDDATA_MSB); - byte lsb = Read8BitsFromRegister((byte)Bme280Register.HUMIDDATA_LSB); - - // Combine the values into a 32-bit integer. - int t = (msb << 8) | lsb; + return s_osToMeasCycles[(int)PressureSampling] + s_osToMeasCycles[(int)TemperatureSampling] + s_osToMeasCycles[(int)HumiditySampling]; + } - return CompensateHumidity(t); + /// + /// Sets the default configuration for the sensor. + /// + protected override void SetDefaultConfiguration() + { + base.SetDefaultConfiguration(); + HumiditySampling = Sampling.UltraLowPower; } /// diff --git a/src/devices/Bmxx80/Bme680.cs b/src/devices/Bmxx80/Bme680.cs index ce096f5dc5..6b22fda56a 100644 --- a/src/devices/Bmxx80/Bme680.cs +++ b/src/devices/Bmxx80/Bme680.cs @@ -3,9 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Device.I2c; -using System.Threading.Tasks; +using System.Linq; using Iot.Device.Bmxx80.CalibrationData; +using Iot.Device.Bmxx80.FilteringMode; using Iot.Device.Bmxx80.PowerMode; using Iot.Device.Bmxx80.Register; using Iot.Units; @@ -15,7 +17,7 @@ namespace Iot.Device.Bmxx80 /// /// Represents a BME680 temperature, pressure, relative humidity and VOC gas sensor. /// - public class Bme680 : Bmxx80Base + public sealed class Bme680 : Bmxx80Base { /// /// Default I2C bus address. @@ -35,7 +37,23 @@ public class Bme680 : Bmxx80Base /// /// Calibration data for the . /// - private readonly Bme680CalibrationData _bme680Calibration; + private Bme680CalibrationData _bme680Calibration; + + /// + protected override int _tempCalibrationFactor => 16; + + private readonly List _heaterConfigs = new List(); + private bool _gasConversionIsEnabled; + private bool _heaterIsEnabled; + + private Bme680HeaterProfile _heaterProfile; + private Bme680FilteringMode _filterMode; + private Sampling _humiditySampling; + + private static readonly byte[] s_osToMeasCycles = { 0, 1, 2, 4, 8, 16 }; + private static readonly byte[] s_osToSwitchCount = { 0, 1, 1, 1, 1, 1 }; + private static readonly double[] s_k1Lookup = { 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8, 0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0 }; + private static readonly double[] s_k2Lookup = { 0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8, -0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; /// /// Initialize a new instance of the class. @@ -44,132 +62,372 @@ public class Bme680 : Bmxx80Base public Bme680(I2cDevice i2cDevice) : base(DeviceId, i2cDevice) { - var bme680CalibrationData = new Bme680CalibrationData(); - bme680CalibrationData.ReadFromDevice(this); - _bme680Calibration = bme680CalibrationData; - _calibrationData = bme680CalibrationData; - _communicationProtocol = CommunicationProtocol.I2c; - _controlRegister = (byte)Bme680Register.CTRL_MEAS; } /// - /// Set the humidity sampling. + /// Gets or sets the humidity sampling. /// - /// The to set. - public void SetHumiditySampling(Sampling sampling) + /// Thrown when the is set to an undefined mode. + public Sampling HumiditySampling { - var register = (byte)Bme680Register.CTRL_HUM; - byte read = Read8BitsFromRegister(register); + get => _humiditySampling; + set + { + if (!Enum.IsDefined(typeof(Sampling), value)) + throw new ArgumentOutOfRangeException(); - // Clear first 3 bits. - var cleared = (byte)(read & 0b_1111_1000); + var status = Read8BitsFromRegister((byte)Bme680Register.CTRL_HUM); + status = (byte)((status & (byte)~Bme680Mask.HUMIDITY_SAMPLING) | (byte)value); - _i2cDevice.Write(new[] { register, (byte)(cleared | (byte)sampling) }); + Span command = stackalloc[] { (byte)Bme680Register.CTRL_HUM, status }; + _i2cDevice.Write(command); + _humiditySampling = value; + } } /// - /// Sets the power mode to the given mode + /// Gets or sets the heater profile to be used for measurements. + /// Current heater profile is only set if the chosen profile is configured. /// - /// The to set. - public void SetPowerMode(Bme680PowerMode powerMode) + /// Thrown when the is set to an undefined profile. + public Bme680HeaterProfile HeaterProfile + { + get => _heaterProfile; + set + { + if (_heaterConfigs.Exists(config => config.HeaterProfile == value)) + { + if (!Enum.IsDefined(typeof(Bme680HeaterProfile), value)) + throw new ArgumentOutOfRangeException(); + + var heaterProfile = Read8BitsFromRegister((byte)Bme680Register.CTRL_GAS_1); + heaterProfile = (byte)((heaterProfile & (byte)~Bme680Mask.NB_CONV) | (byte)value); + + Span command = stackalloc[] { (byte)Bme680Register.CTRL_GAS_1, heaterProfile }; + _i2cDevice.Write(command); + _heaterProfile = value; + } + } + } + + /// + /// Gets or sets the filtering mode to be used for measurements. + /// + /// Thrown when the is set to an undefined mode. + public Bme680FilteringMode FilterMode { - byte read = Read8BitsFromRegister(_controlRegister); + get => _filterMode; + set + { + if (!Enum.IsDefined(typeof(Bme680FilteringMode), value)) + throw new ArgumentOutOfRangeException(); - // Clear first 2 bits. - var cleared = (byte)(read & 0b_1111_1100); + var filter = Read8BitsFromRegister((byte)Bme680Register.CONFIG); + filter = (byte)((filter & (byte)~Bme680Mask.FILTER_COEFFICIENT) | (byte)value << 2); - _i2cDevice.Write(new[] { _controlRegister, (byte)(cleared | (byte)powerMode) }); + Span command = stackalloc[] { (byte)Bme680Register.CONFIG, filter }; + _i2cDevice.Write(command); + _filterMode = value; + } } /// - /// Read a value indicating whether or not new sensor data is available. + /// Gets or sets whether the heater is enabled. /// - /// True if new data is available. - public bool ReadHasNewData() + public bool HeaterIsEnabled { - byte read = Read8BitsFromRegister((byte)Bme680Register.STATUS); + get => _heaterIsEnabled; + set + { + var heaterStatus = Read8BitsFromRegister((byte)Bme680Register.CTRL_GAS_0); + heaterStatus = (byte)((heaterStatus & (byte)~Bme680Mask.HEAT_OFF) | Convert.ToByte(!value) << 3); + + Span command = stackalloc[] { (byte)Bme680Register.CTRL_GAS_0, heaterStatus }; + _i2cDevice.Write(command); + _heaterIsEnabled = value; + } + } - // Get only the power mode bit. - var hasNewData = (byte)(read & 0b_1000_0000); + /// + /// Gets or sets whether gas conversions are enabled. + /// + public bool GasConversionIsEnabled + { + get => _gasConversionIsEnabled; + set + { + var gasConversion = Read8BitsFromRegister((byte)Bme680Register.CTRL_GAS_1); + gasConversion = (byte)((gasConversion & (byte)~Bme680Mask.RUN_GAS) | Convert.ToByte(value) << 4); - return (hasNewData >> 7) == 1; + Span command = stackalloc[] { (byte)Bme680Register.CTRL_GAS_1, gasConversion }; + _i2cDevice.Write(command); + _gasConversionIsEnabled = value; + } } /// - /// Read the humidity. + /// Reads whether new data is available. /// - /// Calculated humidity. - public async Task ReadHumidityAsync() + public bool ReadNewDataIsAvailable() { - // Read humidity data. - byte msb = Read8BitsFromRegister((byte)Bme680Register.HUMIDITYDATA_MSB); - byte lsb = Read8BitsFromRegister((byte)Bme680Register.HUMIDITYDATA_LSB); + var newData = Read8BitsFromRegister((byte)Bme680Register.STATUS); + newData = (byte)(newData >> 7); - // Convert to a 32bit integer. - var adcHumidity = (msb << 8) + lsb; + return Convert.ToBoolean(newData); + } - return CompensateHumidity((await ReadTemperatureAsync()).Celsius, adcHumidity); + /// + /// Reads whether a gas measurement is in process. + /// + public bool ReadGasMeasurementInProcess() + { + var gasMeasInProcess = Read8BitsFromRegister((byte)Bme680Register.STATUS); + gasMeasInProcess = (byte)((gasMeasInProcess & (byte)Bme680Mask.GAS_MEASURING) >> 6); + + return Convert.ToBoolean(gasMeasInProcess); + } + + /// + /// Reads whether a measurement of any kind is in process. + /// + public bool ReadMeasurementInProcess() + { + var measInProcess = Read8BitsFromRegister((byte)Bme680Register.STATUS); + measInProcess = (byte)((measInProcess & (byte)Bme680Mask.MEASURING) >> 5); + + return Convert.ToBoolean(measInProcess); + } + + /// + /// Reads whether the target heater temperature is reached. + /// + public bool ReadHeaterIsStable() + { + var heaterStable = Read8BitsFromRegister((byte)Bme680Register.GAS_RANGE); + heaterStable = (byte)((heaterStable & (byte)Bme680Mask.HEAT_STAB) >> 4); + + return Convert.ToBoolean(heaterStable); + } + + /// + /// Sets the power mode to the given mode + /// + /// The to set. + /// Thrown when the power mode does not match a defined mode in . + public void SetPowerMode(Bme680PowerMode powerMode) + { + if (!Enum.IsDefined(typeof(Bme680PowerMode), powerMode)) + throw new ArgumentOutOfRangeException(); + + var status = Read8BitsFromRegister((byte)Bme680Register.CTRL_MEAS); + status = (byte)((status & (byte)~Bme680Mask.PWR_MODE) | (byte)powerMode); + + Span command = stackalloc[] { (byte)Bme680Register.CTRL_MEAS, status }; + _i2cDevice.Write(command); + } + + /// + /// Configures a heater profile, making it ready for use. + /// + /// The to configure. + /// The target temperature in °C. Ranging from 0-400. + /// The duration in ms. Ranging from 0-4032. + /// The ambient temperature in °C. + /// Thrown when the heating profile does not match a defined profile in . + /// + public void ConfigureHeatingProfile(Bme680HeaterProfile profile, ushort targetTemperature, ushort duration, double ambientTemperature) + { + if (!Enum.IsDefined(typeof(Bme680HeaterProfile), profile)) + throw new ArgumentOutOfRangeException(); + + // read ambient temperature for resistance calculation + var heaterResistance = CalculateHeaterResistance(targetTemperature, (short)ambientTemperature); + var heaterDuration = CalculateHeaterDuration(duration); + + Span resistanceCommand = stackalloc[] { (byte)((byte)Bme680Register.RES_HEAT_0 + profile), heaterResistance }; + Span durationCommand = stackalloc[] { (byte)((byte)Bme680Register.GAS_WAIT_0 + profile), heaterDuration }; + _i2cDevice.Write(resistanceCommand); + _i2cDevice.Write(durationCommand); + + // cache heater configuration + if (_heaterConfigs.Exists(config => config.HeaterProfile == profile)) + _heaterConfigs.Remove(_heaterConfigs.Single(config => config.HeaterProfile == profile)); + _heaterConfigs.Add(new Bme680HeaterProfileConfig(profile, heaterResistance, duration)); } /// /// Read the state. /// /// The current . - /// Thrown when the power mode does not match a defined mode in . public Bme680PowerMode ReadPowerMode() { - byte read = Read8BitsFromRegister(_controlRegister); + var status = Read8BitsFromRegister((byte)Bme680Register.CTRL_MEAS); - return (Bme680PowerMode)(read & 0b_0000_0011); + return (Bme680PowerMode)(status & (byte)Bme680Mask.PWR_MODE); } /// - /// Read the pressure. + /// Gets the required time in ms to perform a measurement. The duration of the gas + /// measurement is not considered if is set to false + /// or the chosen is not configured. + /// The precision of this duration is within 1ms of the actual measurement time. /// - /// Calculated pressure in Pa. - public async Task ReadPressureAsync() + /// The used . + /// + public int GetMeasurementDuration(Bme680HeaterProfile profile) { - // Read pressure data. - byte lsb = Read8BitsFromRegister((byte)Bme680Register.PRESSUREDATA_LSB); - byte msb = Read8BitsFromRegister((byte)Bme680Register.PRESSUREDATA_MSB); - byte xlsb = Read8BitsFromRegister((byte)Bme680Register.PRESSUREDATA_XLSB); + var measCycles = s_osToMeasCycles[(int)TemperatureSampling]; + measCycles += s_osToMeasCycles[(int)PressureSampling]; + measCycles += s_osToMeasCycles[(int)HumiditySampling]; + + var switchCount = s_osToSwitchCount[(int)TemperatureSampling]; + switchCount += s_osToSwitchCount[(int)PressureSampling]; + switchCount += s_osToSwitchCount[(int)HumiditySampling]; + + double measDuration = measCycles * 1963; + measDuration += 477 * switchCount; // TPH switching duration + + if (GasConversionIsEnabled) + measDuration += 477 * 5; // Gas measurement duration + measDuration += 500; // get it to the closest whole number + measDuration /= 1000.0; // convert to ms + measDuration += 1; // wake up duration of 1ms + + if (GasConversionIsEnabled && _heaterConfigs.Exists(config => config.HeaterProfile == profile)) + measDuration += _heaterConfigs.Single(config => config.HeaterProfile == profile).HeaterDuration; + + return (int)Math.Ceiling(measDuration); + } + + /// + /// Reads the humidity. A return value indicates whether the reading succeeded. + /// + /// + /// Contains the measured humidity as %rH if the was not set to . + /// Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public bool TryReadHumidity(out double humidity) + { + if (HumiditySampling == Sampling.Skipped) + { + humidity = double.NaN; + return false; + } + + // Read humidity data. + var hum = Read16BitsFromRegister((byte)Bme680Register.HUMIDITYDATA, Endianness.BigEndian); + + TryReadTemperature(out _); + humidity = CompensateHumidity(hum); + return true; + } + + /// + /// Reads the pressure. A return value indicates whether the reading succeeded. + /// + /// + /// Contains the measured pressure in Pa if the was not set to . + /// Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public override bool TryReadPressure(out double pressure) + { + if (PressureSampling == Sampling.Skipped) + { + pressure = double.NaN; + return false; + } + - // Convert to a 32bit integer. - var adcPressure = (msb << 12) + (lsb << 4) + (xlsb >> 4); + // Read pressure data. + var press = (int)Read24BitsFromRegister((byte)Bme680Register.PRESSUREDATA, Endianness.BigEndian); // Read the temperature first to load the t_fine value for compensation. - await ReadTemperatureAsync(); + TryReadTemperature(out _); - return CompensatePressure(adcPressure); + pressure = CompensatePressure(press >> 4); + return true; } /// - /// Read the temperature. + /// Reads the temperature. A return value indicates whether the reading succeeded. /// - /// Calculated temperature. - public Task ReadTemperatureAsync() + /// + /// Contains the measured temperature if the was not set to . + /// Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public override bool TryReadTemperature(out Temperature temperature) { - // Read temperature data. - byte lsb = Read8BitsFromRegister((byte)Bme680Register.TEMPDATA_LSB); - byte msb = Read8BitsFromRegister((byte)Bme680Register.TEMPDATA_MSB); - byte xlsb = Read8BitsFromRegister((byte)Bme680Register.TEMPDATA_XLSB); + if (TemperatureSampling == Sampling.Skipped) + { + temperature = Temperature.FromCelsius(double.NaN); + return false; + } + - // Convert to a 32bit integer. - var adcTemperature = (msb << 12) + (lsb << 4) + (xlsb >> 4); + var temp = (int)Read24BitsFromRegister((byte)Bme680Register.TEMPDATA, Endianness.BigEndian); - return Task.FromResult(CompensateTemperature(adcTemperature)); + temperature = CompensateTemperature(temp >> 4); + return true; + } + + /// + /// Reads the gas resistance. A return value indicates whether the reading succeeded. + /// + /// + /// Contains the measured gas resistance in Ohm if the heater module reached the target temperature and + /// the measurement was valid. Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public bool TryReadGasResistance(out double gasResistance) + { + if (!ReadGasMeasurementIsValid() || !ReadHeaterIsStable()) + { + gasResistance = double.NaN; + return false; + } + + // Read 10 bit gas resistance value from registers + var gasResRaw = Read8BitsFromRegister((byte)Bme680Register.GAS_RES); + var gasRange = Read8BitsFromRegister((byte)Bme680Register.GAS_RANGE); + + var gasRes = (ushort)((ushort)(gasResRaw << 2) + (byte)(gasRange >> 6)); + gasRange &= (byte)Bme680Mask.GAS_RANGE; + + gasResistance = CalculateGasResistance(gasRes, gasRange); + return true; + } + + /// + /// Sets the default configuration for the sensor. + /// + protected override void SetDefaultConfiguration() + { + base.SetDefaultConfiguration(); + HumiditySampling = Sampling.UltraLowPower; + FilterMode = Bme680FilteringMode.C0; + + _bme680Calibration = (Bme680CalibrationData)_calibrationData; + TryReadTemperature(out var temp); + ConfigureHeatingProfile(Bme680HeaterProfile.Profile1, 320, 150, temp.Celsius); + HeaterProfile = Bme680HeaterProfile.Profile1; + + HeaterIsEnabled = true; + GasConversionIsEnabled = true; } /// /// Compensates the humidity. /// - /// The temperature to use. /// The humidity value read from the device. /// The percentage relative humidity. - private double CompensateHumidity(double temperature, int adcHumidity) + private double CompensateHumidity(int adcHumidity) { // Calculate the humidity. + var temperature = TemperatureFine / 5120.0; var var1 = adcHumidity - ((_bme680Calibration.DigH1 * 16.0) + ((_bme680Calibration.DigH3 / 2.0) * temperature)); var var2 = var1 * ((_bme680Calibration.DigH2 / 262144.0) * (1.0 + ((_bme680Calibration.DigH4 / 16384.0) * temperature) + ((_bme680Calibration.DigH5 / 1048576.0) * temperature * temperature))); @@ -198,22 +456,22 @@ private double CompensatePressure(int adcPressure) { // Calculate the pressure. var var1 = (TemperatureFine / 2.0) - 64000.0; - var var2 = var1 * var1 * (_calibrationData.DigP6 / 131072.0); - var2 += (var1 * _calibrationData.DigP5 * 2.0); - var2 = (var2 / 4.0) + (_calibrationData.DigP4 * 65536.0); - var1 = ((_calibrationData.DigP3 * var1 * var1 / 16384.0) + (_calibrationData.DigP2 * var1)) / 524288.0; - var1 = (1.0 + (var1 / 32768.0)) * _calibrationData.DigP1; + var var2 = var1 * var1 * (_bme680Calibration.DigP6 / 131072.0); + var2 += (var1 * _bme680Calibration.DigP5 * 2.0); + var2 = (var2 / 4.0) + (_bme680Calibration.DigP4 * 65536.0); + var1 = ((_bme680Calibration.DigP3 * var1 * var1 / 16384.0) + (_bme680Calibration.DigP2 * var1)) / 524288.0; + var1 = (1.0 + (var1 / 32768.0)) * _bme680Calibration.DigP1; var calculatedPressure = 1048576.0 - adcPressure; // Avoid exception caused by division by zero. if (var1 != 0) { calculatedPressure = (calculatedPressure - (var2 / 4096.0)) * 6250.0 / var1; - var1 = _calibrationData.DigP9 * calculatedPressure * calculatedPressure / 2147483648.0; - var2 = calculatedPressure * (_calibrationData.DigP8 / 32768.0); + var1 = _bme680Calibration.DigP9 * calculatedPressure * calculatedPressure / 2147483648.0; + var2 = calculatedPressure * (_bme680Calibration.DigP8 / 32768.0); var var3 = (calculatedPressure / 256.0) * (calculatedPressure / 256.0) * (calculatedPressure / 256.0) - * (_calibrationData.DigP10 / 131072.0); - calculatedPressure += (var1 + var2 + var3 + (_calibrationData.DigP7 * 128.0)) / 16.0; + * (_bme680Calibration.DigP10 / 131072.0); + calculatedPressure += (var1 + var2 + var3 + (_bme680Calibration.DigP7 * 128.0)) / 16.0; } else { @@ -223,7 +481,64 @@ private double CompensatePressure(int adcPressure) return calculatedPressure; } - /// - protected override int _tempCalibrationFactor => 16; + private bool ReadGasMeasurementIsValid() + { + var gasMeasValid = Read8BitsFromRegister((byte)Bme680Register.GAS_RANGE); + gasMeasValid = (byte)((gasMeasValid & (byte)Bme680Mask.GAS_VALID) >> 5); + + return Convert.ToBoolean(gasMeasValid); + } + + private double CalculateGasResistance(ushort adcGasRes, byte gasRange) + { + var var1 = 1340.0 + 5.0 * _bme680Calibration.RangeSwErr; + var var2 = var1 * (1.0 + s_k1Lookup[gasRange] / 100.0); + var var3 = 1.0 + s_k2Lookup[gasRange] / 100.0; + var gasResistance = 1.0 / (var3 * 0.000000125 * (1 << gasRange) * ((adcGasRes - 512.0) / var2 + 1.0)); + + return gasResistance; + } + + private byte CalculateHeaterResistance(ushort setTemp, short ambientTemp) + { + // limit maximum temperature to 400°C + if (setTemp > 400) + setTemp = 400; + + var var1 = _bme680Calibration.DigGh1 / 16.0 + 49.0; + var var2 = _bme680Calibration.DigGh2 / 32768.0 * 0.0005 + 0.00235; + var var3 = _bme680Calibration.DigGh3 / 1024.0; + var var4 = var1 * (1.0 + var2 * setTemp); + var var5 = var4 + var3 * ambientTemp; + var heaterResistance = (byte)(3.4 * (var5 * (4.0 / (4.0 + _bme680Calibration.ResHeatRange)) * (1.0 / (1.0 + _bme680Calibration.ResHeatVal * 0.002)) - 25)); + + return heaterResistance; + } + + // The duration is interpreted as follows: + // Byte [7:6]: multiplication factor of 1, 4, 16 or 64 + // Byte [5:0]: 64 timer values, 1ms step size + // Values are rounded down + private byte CalculateHeaterDuration(ushort duration) + { + byte factor = 0; + byte durationValue; + + // check if value exceeds maximum duration + if (duration > 0xFC0) + durationValue = 0xFF; + else + { + while (duration > 0x3F) + { + duration = (ushort)(duration >> 2); + factor += 1; + } + + durationValue = (byte)(duration + factor * 64); + } + + return durationValue; + } } } diff --git a/src/devices/Bmxx80/Bme680HeaterProfile.cs b/src/devices/Bmxx80/Bme680HeaterProfile.cs new file mode 100644 index 0000000000..14bfd1d277 --- /dev/null +++ b/src/devices/Bmxx80/Bme680HeaterProfile.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Iot.Device.Bmxx80 +{ + /// + /// 10 addressable heater profiles stored on the Bme680. + /// + public enum Bme680HeaterProfile : byte + { + /// + /// Heater Profile 1. + /// + Profile1 = 0b0000, + /// + /// Heater Profile 2. + /// + Profile2 = 0b0001, + /// + /// Heater Profile 3. + /// + Profile3 = 0b0010, + /// + /// Heater Profile 4. + /// + Profile4 = 0b0011, + /// + /// Heater Profile 5. + /// + Profile5 = 0b0100, + /// + /// Heater Profile 6. + /// + Profile6 = 0b0101, + /// + /// Heater Profile 7. + /// + Profile7 = 0b0110, + /// + /// Heater Profile 8. + /// + Profile8 = 0b0111, + /// + /// Heater Profile 9. + /// + Profile9 = 0b1000, + /// + /// Heater Profile 10. + /// + Profile10 = 0b1001 + } +} diff --git a/src/devices/Bmxx80/Bme680HeaterProfileConfig.cs b/src/devices/Bmxx80/Bme680HeaterProfileConfig.cs new file mode 100644 index 0000000000..eeaf003b2c --- /dev/null +++ b/src/devices/Bmxx80/Bme680HeaterProfileConfig.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Iot.Device.Bmxx80 +{ + /// + /// The heater profile configuration saved on the device. + /// + public class Bme680HeaterProfileConfig + { + /// + /// The chosen heater profile slot, ranging from 0-9. + /// + public Bme680HeaterProfile HeaterProfile { get; set; } + /// + /// The heater resistance. + /// + public ushort HeaterResistance { get; set; } + /// + /// The heater duration in the internally used format. + /// + public ushort HeaterDuration { get; set; } + + /// + /// + /// + /// The used heater profile. + /// The heater resistance in Ohm. + /// The heating duration in ms. + /// + public Bme680HeaterProfileConfig(Bme680HeaterProfile profile, ushort heaterResistance, ushort heaterDuration) + { + if (!Enum.IsDefined(typeof(Bme680HeaterProfile), profile)) + throw new ArgumentOutOfRangeException(); + + HeaterProfile = profile; + HeaterResistance = heaterResistance; + HeaterDuration = heaterDuration; + } + } +} diff --git a/src/devices/Bmxx80/Bme680Mask.cs b/src/devices/Bmxx80/Bme680Mask.cs new file mode 100644 index 0000000000..e59c3e9677 --- /dev/null +++ b/src/devices/Bmxx80/Bme680Mask.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Iot.Device.Bmxx80 +{ + internal enum Bme680Mask : byte + { + BIT_H1_DATA_MSK = 0x0F, + + PWR_MODE = 0x03, + HEAT_OFF = 0x08, + RUN_GAS = 0x10, + + HUMIDITY_SAMPLING = 0x07, + FILTER_COEFFICIENT = 0x1C, + NB_CONV = 0x0F, + + GAS_RANGE = 0x0F, + RH_RANGE = 0x30, + RS_ERROR = 0xF0, + + GAS_MEASURING = 0x40, + MEASURING = 0x20, + GAS_VALID = 0x20, + HEAT_STAB = 0x10 + } +} diff --git a/src/devices/Bmxx80/Bmp280.cs b/src/devices/Bmxx80/Bmp280.cs index 4cbda16ddf..080cc7b3be 100644 --- a/src/devices/Bmxx80/Bmp280.cs +++ b/src/devices/Bmxx80/Bmp280.cs @@ -3,14 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Device.I2c; -using Iot.Device.Bmxx80.CalibrationData; namespace Iot.Device.Bmxx80 { /// /// Represents a BME280 temperature and barometric pressure sensor. /// - public class Bmp280 : Bmx280Base + public sealed class Bmp280 : Bmx280Base { /// /// The expected chip ID of the BMP280. @@ -25,8 +24,6 @@ public Bmp280(I2cDevice i2cDevice) : base(DeviceId, i2cDevice) { _communicationProtocol = CommunicationProtocol.I2c; - _calibrationData = new Bmp280CalibrationData(); - _calibrationData.ReadFromDevice(this); } } } diff --git a/src/devices/Bmxx80/Bmx280Base.cs b/src/devices/Bmxx80/Bmx280Base.cs index a126cdcb0a..e84bdd1473 100644 --- a/src/devices/Bmxx80/Bmx280Base.cs +++ b/src/devices/Bmxx80/Bmx280Base.cs @@ -8,9 +8,9 @@ using System; using System.Device.I2c; using System.IO; -using System.Threading.Tasks; using Iot.Device.Bmxx80.PowerMode; using Iot.Device.Bmxx80.Register; +using Iot.Device.Bmxx80.FilteringMode; using Iot.Units; namespace Iot.Device.Bmxx80 @@ -30,56 +30,78 @@ public abstract class Bmx280Base : Bmxx80Base /// public const byte SecondaryI2cAddress = 0x76; + /// + /// Converts oversampling to needed measurement cycles for that oversampling. + /// + protected static readonly int[] s_osToMeasCycles = { 0, 7, 9, 14, 23, 44 }; + + private Bmx280FilteringMode _filteringMode; + private StandbyTime _standbyTime; + /// /// Initializes a new instance of the class. /// /// The ID of the device. /// The to create with. protected Bmx280Base(byte deviceId, I2cDevice i2cDevice) - : base(deviceId, i2cDevice) + : base(deviceId, i2cDevice) { } + + /// + /// Gets or sets the IIR filter mode. + /// + /// Thrown when the is set to an undefined mode. + public Bmx280FilteringMode FilterMode { - _controlRegister = (byte)Bmx280Register.CTRL_MEAS; + get => _filteringMode; + set + { + byte current = Read8BitsFromRegister((byte)Bmx280Register.CONFIG); + current = (byte)((current & 0b_1110_0011) | (byte)value << 2); + + Span command = stackalloc[] { (byte)Bmx280Register.CONFIG, current }; + _i2cDevice.Write(command); + _filteringMode = value; + } } /// - /// Reads the current IIR filter mode the device is running in. + /// Gets or sets the standby time between two consecutive measurements. /// - /// The current . - public FilteringMode ReadFilterMode() + /// Thrown when the is set to an undefined mode. + public StandbyTime StandbyTime { - byte current = Read8BitsFromRegister((byte)Bmx280Register.CONFIG); - var mode = (byte)((current & 0b_0001_1100) >> 2); - - return mode switch + get => _standbyTime; + set { - 0b000 => FilteringMode.Off, - 0b001 => FilteringMode.X2, - 0b010 => FilteringMode.X4, - 0b011 => FilteringMode.X8, - _ => FilteringMode.X16 - }; + byte current = Read8BitsFromRegister((byte)Bmx280Register.CONFIG); + current = (byte)((current & 0b_0001_1111) | (byte)value << 5); + + Span command = stackalloc[] { (byte)Bmx280Register.CONFIG, current }; + _i2cDevice.Write(command); + _standbyTime = value; + } } /// - /// Read the temperature. + /// Reads the temperature. A return value indicates whether the reading succeeded. /// - /// Calculated temperature. - public async Task ReadTemperatureAsync() + /// + /// Contains the measured temperature if the was not set to . + /// Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public override bool TryReadTemperature(out Temperature temperature) { - if (ReadPowerMode() == Bmx280PowerMode.Forced) + if (TemperatureSampling == Sampling.Skipped) { - await Task.Delay(GetMeasurementTimeForForcedMode(ReadTemperatureSampling())); + temperature = Temperature.FromCelsius(double.NaN); + return false; } - //Read the MSB, LSB and bits 7:4 (XLSB) of the temperature from the BMP280 registers - byte msb = Read8BitsFromRegister((byte)Bmx280Register.TEMPDATA_MSB); - byte lsb = Read8BitsFromRegister((byte)Bmx280Register.TEMPDATA_LSB); - byte xlsb = Read8BitsFromRegister((byte)Bmx280Register.TEMPDATA_XLSB); // bits 7:4 + var temp = (int)Read24BitsFromRegister((byte)Bmx280Register.TEMPDATA_MSB, Endianness.BigEndian); - //Combine the values into a 32-bit integer - int t = (msb << 12) + (lsb << 4) + (xlsb >> 4); - - return CompensateTemperature(t); + temperature = CompensateTemperature(temp >> 4); + return true; } /// @@ -109,49 +131,60 @@ public Bmx280PowerMode ReadPowerMode() } /// - /// Reads the pressure from the sensor. + /// Reads the pressure. A return value indicates whether the reading succeeded. /// - /// Atmospheric pressure in Pa. - public async Task ReadPressureAsync() + /// + /// Contains the measured pressure in Pa if the was not set to . + /// Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public override bool TryReadPressure(out double pressure) { - if (ReadPowerMode() == Bmx280PowerMode.Forced) + if (PressureSampling == Sampling.Skipped) { - await Task.Delay(GetMeasurementTimeForForcedMode(ReadPressureSampling())); - } - - // Read the temperature first to load the t_fine value for compensation. - await ReadTemperatureAsync(); + pressure = double.NaN; + return false; + } - // Read pressure data. - byte msb = Read8BitsFromRegister((byte)Bmx280Register.PRESSUREDATA_MSB); - byte lsb = Read8BitsFromRegister((byte)Bmx280Register.PRESSUREDATA_LSB); - byte xlsb = Read8BitsFromRegister((byte)Bmx280Register.PRESSUREDATA_XLSB); // bits 7:4 + // Read the temperature first to load the t_fine value for compensation. + TryReadTemperature(out _); - //Combine the values into a 32-bit integer. - int t = (msb << 12) + (lsb << 4) + (xlsb >> 4); + // Read pressure data. + var press = (int)Read24BitsFromRegister((byte)Bmx280Register.PRESSUREDATA, Endianness.BigEndian); //Convert the raw value to the pressure in Pa. - long pres = CompensatePressure(t); + long pressPa = CompensatePressure(press >> 4); //Return the temperature as a float value. - return (double)pres / 256; + pressure = (double)pressPa / 256; + return true; } /// /// Calculates the altitude in meters from the specified sea-level pressure(in hPa). /// /// Sea-level pressure in hPa. - /// Height in meters from sea-level. - public async Task ReadAltitudeAsync(double seaLevelPressure) + /// + /// Contains the calculated metres above sea-level if the was not set to . + /// Contains otherwise. + /// + /// true if pressure measurement was not skipped, otherwise false. + public bool TryReadAltitude(double seaLevelPressure, out double altitude) { // Read the pressure first. - double pressure = await ReadPressureAsync(); + var success = TryReadPressure(out var pressure); + if (!success) + { + altitude = double.NaN; + return false; + } // Convert the pressure to Hectopascals (hPa). pressure /= 100; // Calculate and return the altitude using the international barometric formula. - return 44330.0 * (1.0 - Math.Pow((pressure / seaLevelPressure), 0.1903)); + altitude = 44330.0 * (1.0 - Math.Pow(pressure / seaLevelPressure, 0.1903)); + return true; } /// @@ -175,17 +208,6 @@ public DeviceStatus ReadStatus() }; } - /// - /// Sets the IIR filter mode. - /// - /// The to set. - public void SetFilterMode(FilteringMode filteringMode) - { - byte current = Read8BitsFromRegister((byte)Bmx280Register.CONFIG); - current = (byte)((current & 0b_1110_0011) | (byte)filteringMode << 2); - _i2cDevice.Write(new[] { (byte)Bmx280Register.CONFIG, current }); - } - /// /// Sets the power mode to the given mode /// @@ -197,47 +219,27 @@ public void SetPowerMode(Bmx280PowerMode powerMode) // Clear last 2 bits. var cleared = (byte)(read & 0b_1111_1100); - _i2cDevice.Write(new[] { _controlRegister, (byte)(cleared | (byte)powerMode) }); + Span command = stackalloc[] { _controlRegister, (byte)(cleared | (byte)powerMode) }; + _i2cDevice.Write(command); } /// - /// Sets the standby time mode the device will used when operating in normal mode. + /// Gets the required time in ms to perform a measurement with the current sampling modes. /// - /// The to set. - public void SetStandbyTime(StandbyTime standbyTime) - { - byte current = Read8BitsFromRegister((byte)Bmx280Register.CONFIG); - current = (byte)((current & 0b_0001_1111) | (byte)standbyTime << 5); - _i2cDevice.Write(new[] { (byte)Bmx280Register.CONFIG, current }); - } - - /// - /// Reads the currently configured standby time mode the device will used when operating in normal mode. - /// - /// The current . - public StandbyTime ReadStandbyTime() + /// The time it takes for the chip to read data in milliseconds rounded up. + public virtual int GetMeasurementDuration() { - byte current = Read8BitsFromRegister((byte)Bmx280Register.CONFIG); - - return (StandbyTime)((current & 0b_1110_0000) >> 5); + return s_osToMeasCycles[(int)PressureSampling] + s_osToMeasCycles[(int)TemperatureSampling]; } /// - /// Recommended wait timings from the datasheet. + /// Sets the default configuration for the sensor. /// - /// The to get for. - /// The time it takes for the chip to read data in milliseconds rounded up. - internal int GetMeasurementTimeForForcedMode(Sampling sampleMode) + protected override void SetDefaultConfiguration() { - return sampleMode switch - { - Sampling.UltraLowPower => 7, - Sampling.LowPower => 9, - Sampling.Standard => 14, - Sampling.HighResolution => 23, - Sampling.UltraHighResolution => 44, - _ => 0 - }; + base.SetDefaultConfiguration(); + FilterMode = Bmx280FilteringMode.Off; + StandbyTime = StandbyTime.Ms125; } /// diff --git a/src/devices/Bmxx80/Bmxx80.csproj b/src/devices/Bmxx80/Bmxx80.csproj index 3b1096e7e2..3a9422993b 100644 --- a/src/devices/Bmxx80/Bmxx80.csproj +++ b/src/devices/Bmxx80/Bmxx80.csproj @@ -9,6 +9,9 @@ + + + @@ -17,7 +20,8 @@ - + + diff --git a/src/devices/Bmxx80/Bmxx80Base.cs b/src/devices/Bmxx80/Bmxx80Base.cs index 4f98b04152..e3d2e47768 100644 --- a/src/devices/Bmxx80/Bmxx80Base.cs +++ b/src/devices/Bmxx80/Bmxx80Base.cs @@ -17,29 +17,34 @@ namespace Iot.Device.Bmxx80 /// public abstract class Bmxx80Base : IDisposable { + /// + /// Calibration data for the sensor. + /// internal Bmxx80CalibrationData _calibrationData; /// - /// I2C device used to communicate with the device + /// I2C device used to communicate with the device. /// protected I2cDevice _i2cDevice; /// - /// Communication protocol + /// Chosen communication protocol. /// protected CommunicationProtocol _communicationProtocol; /// - /// Control register + /// The control register of the sensor. /// protected byte _controlRegister; /// - /// Bmxx80 communication protocol + /// Bmxx80 communication protocol. /// public enum CommunicationProtocol { - /// Communication protocol + /// + /// I²C communication protocol. + /// I2c } @@ -49,6 +54,14 @@ public enum CommunicationProtocol /// protected int TemperatureFine; + /// + /// The temperature calibration factor. + /// + protected virtual int _tempCalibrationFactor => 1; + + private Sampling _temperatureSampling; + private Sampling _pressureSampling; + /// /// Initializes a new instance of the class. /// @@ -64,67 +77,82 @@ protected Bmxx80Base(byte deviceId, I2cDevice i2cDevice) byte readSignature = _i2cDevice.ReadByte(); if (readSignature != deviceId) - { throw new IOException($"Unable to find a chip with id {deviceId}"); - } + + ReadCalibrationData(); + SetDefaultConfiguration(); } /// - /// Sets the pressure sampling. + /// Gets or sets the pressure sampling. /// - /// The to set. - public void SetPressureSampling(Sampling sampling) + /// Thrown when the is set to an undefined mode. + public Sampling PressureSampling { - byte status = Read8BitsFromRegister(_controlRegister); - status = (byte)(status & 0b1110_0011); - status = (byte)(status | (byte)sampling << 2); - _i2cDevice.Write(new[] { _controlRegister, status }); + get => _pressureSampling; + set + { + byte status = Read8BitsFromRegister(_controlRegister); + status = (byte)(status & 0b1110_0011); + status = (byte)(status | (byte)value << 2); + + Span command = stackalloc[] { _controlRegister, status }; + _i2cDevice.Write(command); + _pressureSampling = value; + } } /// - /// Set the temperature oversampling. + /// Gets or sets the temperature sampling. /// - /// The to set. - public void SetTemperatureSampling(Sampling sampling) + /// Thrown when the is set to an undefined mode. + public Sampling TemperatureSampling { - byte status = Read8BitsFromRegister(_controlRegister); - status = (byte)(status & 0b0001_1111); - status = (byte)(status | (byte)sampling << 5); - _i2cDevice.Write(new[] { _controlRegister, status }); + get => _temperatureSampling; + set + { + byte status = Read8BitsFromRegister(_controlRegister); + status = (byte)(status & 0b0001_1111); + status = (byte)(status | (byte)value << 5); + + Span command = stackalloc[] { _controlRegister, status }; + _i2cDevice.Write(command); + _temperatureSampling = value; + } } /// - /// Get the current sample rate for pressure measurements + /// When called, the device is reset using the complete power-on-reset procedure. + /// The device will reset to the default configuration. /// - /// The current pressure rate. - public Sampling ReadPressureSampling() + public void Reset() { - byte status = Read8BitsFromRegister(_controlRegister); - status = (byte)((status & 0b0001_1100) >> 2); + const byte resetCommand = 0xB6; + Span command = stackalloc[] { (byte)Bmxx80Register.RESET, resetCommand }; + _i2cDevice.Write(command); - return ByteToSampling(status); + SetDefaultConfiguration(); } /// - /// Get the sample rate for temperature measurements. + /// Reads the temperature. A return value indicates whether the reading succeeded. /// - /// The current temperature rate. - public Sampling ReadTemperatureSampling() - { - byte status = Read8BitsFromRegister(_controlRegister); - status = (byte)((status & 0b1110_0000) >> 5); - - return ByteToSampling(status); - } + /// + /// Contains the measured temperature if the was not set to . + /// Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public abstract bool TryReadTemperature(out Temperature temperature); /// - /// When called, the device is reset using the complete power-on-reset procedure. + /// Reads the pressure. A return value indicates whether the reading succeeded. /// - public void Reset() - { - const byte resetCommand = 0xB6; - _i2cDevice.Write(new[] { (byte)Bmxx80Register.RESET, resetCommand }); - } + /// + /// Contains the measured pressure in Pa if the was not set to . + /// Contains otherwise. + /// + /// true if measurement was not skipped, otherwise false. + public abstract bool TryReadPressure(out double pressure); /// /// Compensates the temperature. @@ -145,11 +173,6 @@ protected Temperature CompensateTemperature(int adcTemperature) return Temperature.FromCelsius(temp); } - /// - /// Temperature calibration factor - /// - protected virtual int _tempCalibrationFactor => 1; - /// /// Reads an 8 bit value from a register. /// @@ -164,71 +187,123 @@ protected internal byte Read8BitsFromRegister(byte register) return value; } else - { throw new NotImplementedException(); - } } /// /// Reads a 16 bit value over I2C. /// /// Register to read from. + /// Interpretation of the bytes (big or little endian). /// Value from register. - protected internal ushort Read16BitsFromRegister(byte register) + protected internal ushort Read16BitsFromRegister(byte register, Endianness endianness = Endianness.LittleEndian) { - if (_communicationProtocol == CommunicationProtocol.I2c) + Span bytes = stackalloc byte[2]; + switch (_communicationProtocol) { - Span bytes = stackalloc byte[2]; - - _i2cDevice.WriteByte(register); - _i2cDevice.Read(bytes); - - return BinaryPrimitives.ReadUInt16LittleEndian(bytes); + case CommunicationProtocol.I2c: + _i2cDevice.WriteByte(register); + _i2cDevice.Read(bytes); + break; + default: + throw new NotImplementedException(); } - else + + return endianness switch { - throw new NotImplementedException(); - } + Endianness.LittleEndian => BinaryPrimitives.ReadUInt16LittleEndian(bytes), + Endianness.BigEndian => BinaryPrimitives.ReadUInt16BigEndian(bytes), + _ => throw new ArgumentOutOfRangeException(nameof(endianness), endianness, null) + }; } /// /// Reads a 24 bit value over I2C. /// /// Register to read from. + /// Interpretation of the bytes (big or little endian). /// Value from register. - protected internal uint Read24BitsFromRegister(byte register) + protected internal uint Read24BitsFromRegister(byte register, Endianness endianness = Endianness.LittleEndian) { - if (_communicationProtocol == CommunicationProtocol.I2c) + Span bytes = stackalloc byte[4]; + switch (_communicationProtocol) { - Span bytes = stackalloc byte[4]; - - _i2cDevice.WriteByte(register); - _i2cDevice.Read(bytes.Slice(1)); - - return BinaryPrimitives.ReadUInt32LittleEndian(bytes); + case CommunicationProtocol.I2c: + _i2cDevice.WriteByte(register); + _i2cDevice.Read(bytes.Slice(1)); + break; + default: + throw new NotImplementedException(); } - else + + return endianness switch { - throw new NotImplementedException(); - } + Endianness.LittleEndian => BinaryPrimitives.ReadUInt32LittleEndian(bytes), + Endianness.BigEndian => BinaryPrimitives.ReadUInt32BigEndian(bytes), + _ => throw new ArgumentOutOfRangeException(nameof(endianness), endianness, null) + }; } /// - /// Convert byte to sampling + /// Converts byte to . /// - /// Value to convert - /// Sampling + /// Value to convert. + /// protected Sampling ByteToSampling(byte value) { // Values >=5 equals UltraHighResolution. if (value >= 5) - { return Sampling.UltraHighResolution; - } return (Sampling)value; } + /// + /// Sets the default configuration for the sensor. + /// + protected virtual void SetDefaultConfiguration() + { + PressureSampling = Sampling.UltraLowPower; + TemperatureSampling = Sampling.UltraLowPower; + } + + /// + /// Specifies the Endianness of a device. + /// + protected internal enum Endianness + { + /// + /// Indicates little endian. + /// + LittleEndian, + + /// + /// Indicates big endian. + /// + BigEndian + } + + private void ReadCalibrationData() + { + switch (this) + { + case Bme280 _: + _calibrationData = new Bme280CalibrationData(); + _controlRegister = (byte)Bmx280Register.CTRL_MEAS; + break; + case Bmp280 _: + _calibrationData = new Bmp280CalibrationData(); + _controlRegister = (byte)Bmx280Register.CTRL_MEAS; + break; + case Bme680 _: + _calibrationData = new Bme680CalibrationData(); + _controlRegister = (byte)Bme680Register.CTRL_MEAS; + break; + } + + _calibrationData.ReadFromDevice(this); + } + /// /// Cleanup. /// diff --git a/src/devices/Bmxx80/CalibrationData/Bme280CalibrationData.cs b/src/devices/Bmxx80/CalibrationData/Bme280CalibrationData.cs index d3fe69e27d..934e079523 100644 --- a/src/devices/Bmxx80/CalibrationData/Bme280CalibrationData.cs +++ b/src/devices/Bmxx80/CalibrationData/Bme280CalibrationData.cs @@ -1,6 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using Iot.Device.Bmxx80.Register; namespace Iot.Device.Bmxx80.CalibrationData diff --git a/src/devices/Bmxx80/CalibrationData/Bme680CalibrationData.cs b/src/devices/Bmxx80/CalibrationData/Bme680CalibrationData.cs index 53964f0df3..ea7ce6d5f7 100644 --- a/src/devices/Bmxx80/CalibrationData/Bme680CalibrationData.cs +++ b/src/devices/Bmxx80/CalibrationData/Bme680CalibrationData.cs @@ -11,6 +11,8 @@ namespace Iot.Device.Bmxx80.CalibrationData /// internal class Bme680CalibrationData : Bmxx80CalibrationData { + public byte DigP10 { get; set; } + public ushort DigH1 { get; set; } public ushort DigH2 { get; set; } public sbyte DigH3 { get; set; } @@ -19,6 +21,14 @@ internal class Bme680CalibrationData : Bmxx80CalibrationData public byte DigH6 { get; set; } public sbyte DigH7 { get; set; } + public sbyte DigGh1 { get; set; } + public short DigGh2 { get; set; } + public sbyte DigGh3 { get; set; } + + public byte ResHeatRange { get; set; } + public sbyte ResHeatVal { get; set; } + public sbyte RangeSwErr { get; set; } + /// /// Read coefficient data from device. /// @@ -31,7 +41,7 @@ protected internal override void ReadFromDevice(Bmxx80Base bmxx80Base) DigT3 = bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_T3); // Read humidity calibration data. - DigH1 = (ushort)((bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H1_MSB) << 4) | (bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H1_LSB) & 0b0000_1111)); + DigH1 = (ushort)((bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H1_MSB) << 4) | (bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H1_LSB) & (byte)Bme680Mask.BIT_H1_DATA_MSK)); DigH2 = (ushort)((bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H2_MSB) << 4) | (bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H2_LSB) >> 4)); DigH3 = (sbyte)bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H3); DigH4 = (sbyte)bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H4); @@ -50,6 +60,16 @@ protected internal override void ReadFromDevice(Bmxx80Base bmxx80Base) DigP8 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_P8_LSB); DigP9 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_P9_LSB); DigP10 = bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_P10); + + // read gas calibration data. + DigGh1 = (sbyte)bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_GH1); + DigGh2 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_GH2); + DigGh3 = (sbyte)bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_GH3); + + // read heater calibration data + ResHeatRange = (byte)((bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.RES_HEAT_RANGE) & (byte)Bme680Mask.RH_RANGE) >> 4); + RangeSwErr = (sbyte)((bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.RANGE_SW_ERR) & (byte)Bme680Mask.RS_ERROR) >> 4); + ResHeatVal = (sbyte)bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.RES_HEAT_VAL); } } } diff --git a/src/devices/Bmxx80/CalibrationData/Bmxx80CalibrationData.cs b/src/devices/Bmxx80/CalibrationData/Bmxx80CalibrationData.cs index 0aa2965389..3907b32bb5 100644 --- a/src/devices/Bmxx80/CalibrationData/Bmxx80CalibrationData.cs +++ b/src/devices/Bmxx80/CalibrationData/Bmxx80CalibrationData.cs @@ -22,12 +22,11 @@ internal abstract class Bmxx80CalibrationData public short DigP7 { get; set; } public short DigP8 { get; set; } public short DigP9 { get; set; } - public byte DigP10 { get; set; } - + /// /// Read coefficient data from device. /// /// The to read coefficient data from. - protected internal virtual void ReadFromDevice(Bmxx80Base bmxx80Base) { } + protected internal abstract void ReadFromDevice(Bmxx80Base bmxx80Base); } } diff --git a/src/devices/Bmxx80/DeviceStatus.cs b/src/devices/Bmxx80/DeviceStatus.cs index 13d51c2cea..a284146606 100644 --- a/src/devices/Bmxx80/DeviceStatus.cs +++ b/src/devices/Bmxx80/DeviceStatus.cs @@ -1,4 +1,8 @@ -namespace Iot.Device.Bmxx80 +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Iot.Device.Bmxx80 { /// /// Indicates the status of the device. diff --git a/src/devices/Bmxx80/FilteringMode/Bme680FilteringMode.cs b/src/devices/Bmxx80/FilteringMode/Bme680FilteringMode.cs new file mode 100644 index 0000000000..8bfd8ea976 --- /dev/null +++ b/src/devices/Bmxx80/FilteringMode/Bme680FilteringMode.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Iot.Device.Bmxx80.FilteringMode +{ + /// + /// IIR filter coefficient. The higher the coefficient, the slower the sensors + /// responds to external inputs. + /// + public enum Bme680FilteringMode + { + /// + /// Filter coefficient of 0. + /// + C0 = 0b000, + /// + /// Filter coefficient of 1. + /// + C1 = 0b001, + /// + /// Filter coefficient of 3. + /// + C3 = 0b010, + /// + /// Filter coefficient of 7. + /// + C7 = 0b011, + /// + /// Filter coefficient of 15. + /// + C15 = 0b100, + /// + /// Filter coefficient of 31. + /// + C31 = 0b101, + /// + /// Filter coefficient of 63. + /// + C63 = 0b110, + /// + /// Filter coefficient of 127. + /// + C127 = 0b111 + } +} diff --git a/src/devices/Bmxx80/FilteringMode.cs b/src/devices/Bmxx80/FilteringMode/Bmx280FilteringMode.cs similarity index 91% rename from src/devices/Bmxx80/FilteringMode.cs rename to src/devices/Bmxx80/FilteringMode/Bmx280FilteringMode.cs index ee0169964b..5d6edb6c5c 100644 --- a/src/devices/Bmxx80/FilteringMode.cs +++ b/src/devices/Bmxx80/FilteringMode/Bmx280FilteringMode.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Iot.Device.Bmxx80 +namespace Iot.Device.Bmxx80.FilteringMode { /// /// Bmx280 devices feature an internal IIR filter. @@ -11,11 +11,11 @@ namespace Iot.Device.Bmxx80 /// This filter effectively reduces the bandwidth of the temperature and pressure output signals /// and increases the resolution of the pressure and temperature output data to 20 bits. /// - /// The higher the coefficient, the slower the sensors respond to external inputs. + /// The higher the coefficient, the slower the sensors responds to external inputs. /// /// See the data sheet with recommended settings for different scenarios. /// - public enum FilteringMode : byte + public enum Bmx280FilteringMode : byte { /// /// Filter off. diff --git a/src/devices/Bmxx80/README.md b/src/devices/Bmxx80/README.md index a6c501b30f..9fcf21e4ca 100644 --- a/src/devices/Bmxx80/README.md +++ b/src/devices/Bmxx80/README.md @@ -11,13 +11,13 @@ The implementation supports the following devices: - BMP280 temperature and barometric pressure sensor ([Datasheet](https://cdn-shop.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf)) - BME280 temperature, barometric pressure and humidity sensor ([Datasheet](https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280-DS002.pdf)) -- BME680 Temperature, barometric pressure, Humidity and VOC gas sensor ([Datasheet](https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001.pdf)) +- BME680 temperature, barometric pressure, humidity and VOC gas sensor ([Datasheet](https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001.pdf)) ## Usage -2 examples on how to use this device binding are available in the [samples](samples) folder. +3 examples on how to use this device binding are available in the [samples](samples) folder. -The following fritzing diagram illustrates one way to wire up the BMP280 with a Raspberry Pi using I2C. +The following fritzing diagram illustrates one way to wire up the BMP280 with a Raspberry Pi using I2C: ![Raspberry Pi Breadboard diagram](samples/rpi-bmp280_i2c.png) diff --git a/src/devices/Bmxx80/Register/Bme280Register.cs b/src/devices/Bmxx80/Register/Bme280Register.cs index f76b5f35a1..943e52b5e5 100644 --- a/src/devices/Bmxx80/Register/Bme280Register.cs +++ b/src/devices/Bmxx80/Register/Bme280Register.cs @@ -1,4 +1,8 @@ -namespace Iot.Device.Bmxx80.Register +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Iot.Device.Bmxx80.Register { /// /// General control registers for the BME280. @@ -14,7 +18,6 @@ internal enum Bme280Register : byte DIG_H5 = 0xE5, DIG_H6 = 0xE7, - HUMIDDATA_LSB = 0xFE, - HUMIDDATA_MSB = 0xFD, + HUMIDDATA = 0xFD } } diff --git a/src/devices/Bmxx80/Register/Bme680Register.cs b/src/devices/Bmxx80/Register/Bme680Register.cs index 799bf8e010..4a0d9f64a9 100644 --- a/src/devices/Bmxx80/Register/Bme680Register.cs +++ b/src/devices/Bmxx80/Register/Bme680Register.cs @@ -12,9 +12,6 @@ namespace Iot.Device.Bmxx80.Register /// internal enum Bme680Register : byte { - CTRL_MEAS = 0x74, - CTRL_HUM = 0x72, - DIG_H1_LSB = 0xE2, DIG_H1_MSB = 0xE3, DIG_H2_LSB = 0xE2, @@ -40,17 +37,28 @@ internal enum Bme680Register : byte DIG_P9_LSB = 0x9E, DIG_P10 = 0xA0, - HUMIDITYDATA_LSB = 0x26, - HUMIDITYDATA_MSB = 0x25, - - PRESSUREDATA_MSB = 0x1F, - PRESSUREDATA_LSB = 0x20, - PRESSUREDATA_XLSB = 0x21, + DIG_GH1 = 0xED, + DIG_GH2 = 0xEB, + DIG_GH3 = 0xEE, + RES_HEAT_VAL = 0x00, + RES_HEAT_RANGE = 0x02, + RANGE_SW_ERR = 0x04, STATUS = 0x1D, - TEMPDATA_MSB = 0x22, - TEMPDATA_LSB = 0x23, - TEMPDATA_XLSB = 0x24, + PRESSUREDATA = 0x1F, + TEMPDATA = 0x22, + HUMIDITYDATA = 0x25, + + GAS_RES = 0x2A, + GAS_RANGE = 0x2B, + RES_HEAT_0 = 0x5A, + GAS_WAIT_0 = 0x64, + + CTRL_GAS_0 = 0x70, + CTRL_GAS_1 = 0x71, + CTRL_HUM = 0x72, + CTRL_MEAS = 0x74, + CONFIG = 0x75 } } diff --git a/src/devices/Bmxx80/Register/Bmx280Register.cs b/src/devices/Bmxx80/Register/Bmx280Register.cs index ad55261ce0..9d7231c9b4 100644 --- a/src/devices/Bmxx80/Register/Bmx280Register.cs +++ b/src/devices/Bmxx80/Register/Bmx280Register.cs @@ -25,20 +25,10 @@ internal enum Bmx280Register : byte DIG_P8 = 0x9C, DIG_P9 = 0x9E, - VERSION = 0xD1, - SOFTRESET = 0xE0, - - CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0 - STATUS = 0xF3, CONFIG = 0xF5, - PRESSUREDATA_MSB = 0xF7, - PRESSUREDATA_LSB = 0xF8, - PRESSUREDATA_XLSB = 0xF9, // bits <7:4> - - TEMPDATA_MSB = 0xFA, - TEMPDATA_LSB = 0xFB, - TEMPDATA_XLSB = 0xFC, // bits <7:4> + PRESSUREDATA = 0xF7, + TEMPDATA_MSB = 0xFA } } diff --git a/src/devices/Bmxx80/Register/Bmxx80Register.cs b/src/devices/Bmxx80/Register/Bmxx80Register.cs index f93ba57a4b..be62dbfe62 100644 --- a/src/devices/Bmxx80/Register/Bmxx80Register.cs +++ b/src/devices/Bmxx80/Register/Bmxx80Register.cs @@ -10,6 +10,6 @@ namespace Iot.Device.Bmxx80.Register internal enum Bmxx80Register : byte { CHIPID = 0xD0, - RESET = 0xE0, + RESET = 0xE0 } } diff --git a/src/devices/Bmxx80/samples/Bme280.sample.cs b/src/devices/Bmxx80/samples/Bme280.sample.cs index 7b3c3abd7e..615247bd22 100644 --- a/src/devices/Bmxx80/samples/Bme280.sample.cs +++ b/src/devices/Bmxx80/samples/Bme280.sample.cs @@ -5,16 +5,15 @@ using System; using System.Device.I2c; using System.Threading; -using System.Threading.Tasks; using Iot.Device.Bmxx80; +using Iot.Device.Bmxx80.FilteringMode; using Iot.Device.Bmxx80.PowerMode; -using Iot.Units; namespace Iot.Device.Samples { class Program { - static async Task Main(string[] args) + static void Main(string[] args) { Console.WriteLine("Hello Bme280!"); @@ -31,60 +30,52 @@ static async Task Main(string[] args) { while (true) { + //set higher sampling + i2CBmpe80.TemperatureSampling = Sampling.LowPower; + i2CBmpe80.PressureSampling = Sampling.UltraHighResolution; + i2CBmpe80.HumiditySampling = Sampling.Standard; + //set mode forced so device sleeps after read i2CBmpe80.SetPowerMode(Bmx280PowerMode.Forced); - //set samplings - i2CBmpe80.SetTemperatureSampling(Sampling.UltraLowPower); - i2CBmpe80.SetPressureSampling(Sampling.UltraLowPower); - i2CBmpe80.SetHumiditySampling(Sampling.UltraLowPower); + // wait for measurement to be performed + var measurementTime = i2CBmpe80.GetMeasurementDuration(); + Thread.Sleep(measurementTime); //read values - Temperature tempValue = await i2CBmpe80.ReadTemperatureAsync(); + i2CBmpe80.TryReadTemperature(out var tempValue); Console.WriteLine($"Temperature: {tempValue.Celsius} C"); - double preValue = await i2CBmpe80.ReadPressureAsync(); + i2CBmpe80.TryReadPressure(out var preValue); Console.WriteLine($"Pressure: {preValue} Pa"); - double altValue = await i2CBmpe80.ReadAltitudeAsync(defaultSeaLevelPressure); + i2CBmpe80.TryReadAltitude(defaultSeaLevelPressure, out var altValue); Console.WriteLine($"Altitude: {altValue} meters"); - double humValue = await i2CBmpe80.ReadHumidityAsync(); + i2CBmpe80.TryReadHumidity(out var humValue); Console.WriteLine($"Humidity: {humValue} %"); Thread.Sleep(1000); - //set higher sampling - i2CBmpe80.SetTemperatureSampling(Sampling.LowPower); - Console.WriteLine(i2CBmpe80.ReadTemperatureSampling()); - i2CBmpe80.SetPressureSampling(Sampling.UltraHighResolution); - Console.WriteLine(i2CBmpe80.ReadPressureSampling()); - i2CBmpe80.SetHumiditySampling(Sampling.Standard); - Console.WriteLine(i2CBmpe80.ReadHumiditySampling()); - - i2CBmpe80.SetFilterMode(FilteringMode.Off); - Console.WriteLine(i2CBmpe80.ReadFilterMode()); + //change sampling and filter + i2CBmpe80.TemperatureSampling = Sampling.UltraHighResolution; + i2CBmpe80.PressureSampling = Sampling.UltraLowPower; + i2CBmpe80.HumiditySampling = Sampling.UltraLowPower; + i2CBmpe80.FilterMode = Bmx280FilteringMode.X2; //set mode forced and read again i2CBmpe80.SetPowerMode(Bmx280PowerMode.Forced); + // wait for measurement to be performed + measurementTime = i2CBmpe80.GetMeasurementDuration(); + Thread.Sleep(measurementTime); + //read values - tempValue = await i2CBmpe80.ReadTemperatureAsync(); + i2CBmpe80.TryReadTemperature(out tempValue); Console.WriteLine($"Temperature: {tempValue.Celsius} C"); - preValue = await i2CBmpe80.ReadPressureAsync(); + i2CBmpe80.TryReadPressure(out preValue); Console.WriteLine($"Pressure: {preValue} Pa"); - altValue = await i2CBmpe80.ReadAltitudeAsync(defaultSeaLevelPressure); + i2CBmpe80.TryReadAltitude(defaultSeaLevelPressure, out altValue); Console.WriteLine($"Altitude: {altValue} meters"); - humValue = await i2CBmpe80.ReadHumidityAsync(); + i2CBmpe80.TryReadHumidity(out humValue); Console.WriteLine($"Humidity: {humValue} %"); Thread.Sleep(5000); - - //set sampling to higher - i2CBmpe80.SetTemperatureSampling(Sampling.UltraHighResolution); - Console.WriteLine(i2CBmpe80.ReadTemperatureSampling()); - i2CBmpe80.SetPressureSampling(Sampling.UltraLowPower); - Console.WriteLine(i2CBmpe80.ReadPressureSampling()); - i2CBmpe80.SetHumiditySampling(Sampling.UltraLowPower); - Console.WriteLine(i2CBmpe80.ReadHumiditySampling()); - - i2CBmpe80.SetFilterMode(FilteringMode.X2); - Console.WriteLine(i2CBmpe80.ReadFilterMode()); } } } diff --git a/src/devices/Bmxx80/samples/Bme680.sample.cs b/src/devices/Bmxx80/samples/Bme680.sample.cs index efc666baf5..328efa4202 100644 --- a/src/devices/Bmxx80/samples/Bme680.sample.cs +++ b/src/devices/Bmxx80/samples/Bme680.sample.cs @@ -1,7 +1,10 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Device.I2c; -using System.Threading; -using System.Threading.Tasks; +using System.Threading; using Iot.Device.Bmxx80; using Iot.Device.Bmxx80.PowerMode; @@ -15,7 +18,7 @@ public static class Program /// /// Main entry point for the program. /// - static async Task Main() + static void Main() { Console.WriteLine("Hello BME680!"); @@ -27,33 +30,63 @@ static async Task Main() using (var bme680 = new Bme680(unixI2cDevice)) { - // Prevents reading old data from the sensor's registers. - bme680.Reset(); - - bme680.SetHumiditySampling(Sampling.UltraLowPower); - bme680.SetTemperatureSampling(Sampling.LowPower); - bme680.SetPressureSampling(Sampling.UltraHighResolution); - while (true) { - // Once a reading has been taken, the sensor goes back to sleep mode. - if (bme680.ReadPowerMode() == Bme680PowerMode.Sleep) + // get the time a measurement will take with the current settings + var measurementDuration = bme680.GetMeasurementDuration(bme680.HeaterProfile); + + // 10 consecutive measurement with default settings + for (var i = 0; i < 10; i++) { // This instructs the sensor to take a measurement. bme680.SetPowerMode(Bme680PowerMode.Forced); + + // wait while measurement is being taken + Thread.Sleep(measurementDuration); + + // Print out the measured data + bme680.TryReadTemperature(out var temperature); + bme680.TryReadPressure(out var pressure); + bme680.TryReadHumidity(out var humidity); + bme680.TryReadGasResistance(out var gasResistance); + + Console.WriteLine($"{temperature.Celsius:N2} °c | {pressure / 100:N2} hPa | {humidity:N2} %rH | {gasResistance:N2} Ohm"); + + // when measuring the gas resistance on each cycle it is important to wait a certain interval + // because a heating plate is activated which will heat up the sensor without sleep, this can + // falsify all readings coming from the sensor + Thread.Sleep(1000); } - // This prevent us from reading old data from the sensor. - if (bme680.ReadHasNewData()) + // change the settings + bme680.TemperatureSampling = Sampling.HighResolution; + bme680.HumiditySampling = Sampling.UltraHighResolution; + bme680.PressureSampling = Sampling.Skipped; + + bme680.ConfigureHeatingProfile(Bme680HeaterProfile.Profile2, 280, 80, 24); + bme680.HeaterProfile = Bme680HeaterProfile.Profile2; + + measurementDuration = bme680.GetMeasurementDuration(bme680.HeaterProfile); + + // 10 consecutive measurements with custom settings + for (int i = 0; i < 10; i++) { - var temperature = Math.Round((await bme680.ReadTemperatureAsync()).Celsius, 2).ToString("N2"); - var pressure = Math.Round(await bme680.ReadPressureAsync() / 100, 2).ToString("N2"); - var humidity = Math.Round(await bme680.ReadHumidityAsync(), 2).ToString("N2"); + // perform the measurement + bme680.SetPowerMode(Bme680PowerMode.Forced); + Thread.Sleep(measurementDuration); - Console.WriteLine($"{temperature} °c | {pressure} hPa | {humidity} %rH"); + // Print out the measured data + bme680.TryReadTemperature(out var temperature); + bme680.TryReadPressure(out var pressure); + bme680.TryReadHumidity(out var humidity); + bme680.TryReadGasResistance(out var gasResistance); + Console.WriteLine($"{temperature.Celsius:N2} °c | {pressure / 100:N2} hPa | {humidity:N2} %rH | {gasResistance:N2} Ohm"); Thread.Sleep(1000); } + + // reset will change settings back to default + bme680.Reset(); } } } diff --git a/src/devices/Bmxx80/samples/Bmp280.sample.cs b/src/devices/Bmxx80/samples/Bmp280.sample.cs index 7b68f25807..99ec367183 100644 --- a/src/devices/Bmxx80/samples/Bmp280.sample.cs +++ b/src/devices/Bmxx80/samples/Bmp280.sample.cs @@ -5,16 +5,15 @@ using System; using System.Device.I2c; using System.Threading; -using System.Threading.Tasks; using Iot.Device.Bmxx80; +using Iot.Device.Bmxx80.FilteringMode; using Iot.Device.Bmxx80.PowerMode; -using Iot.Units; namespace Iot.Device.Samples { class Program { - static async Task Main(string[] args) + static void Main(string[] args) { Console.WriteLine("Hello Bmp280!"); @@ -31,45 +30,46 @@ static async Task Main(string[] args) { while (true) { + //set higher sampling + i2CBmp280.TemperatureSampling = Sampling.LowPower; + i2CBmp280.PressureSampling = Sampling.UltraHighResolution; + //set mode forced so device sleeps after read i2CBmp280.SetPowerMode(Bmx280PowerMode.Forced); - //set samplings - i2CBmp280.SetTemperatureSampling(Sampling.UltraLowPower); - i2CBmp280.SetPressureSampling(Sampling.UltraLowPower); + // wait for measurement to be performed + var measurementTime = i2CBmp280.GetMeasurementDuration(); + Thread.Sleep(measurementTime); //read values - Temperature tempValue = await i2CBmp280.ReadTemperatureAsync(); + i2CBmp280.TryReadTemperature(out var tempValue); Console.WriteLine($"Temperature {tempValue.Celsius}"); - double preValue = await i2CBmp280.ReadPressureAsync(); + i2CBmp280.TryReadPressure(out var preValue); Console.WriteLine($"Pressure {preValue}"); - double altValue = await i2CBmp280.ReadAltitudeAsync(defaultSeaLevelPressure); + i2CBmp280.TryReadAltitude(defaultSeaLevelPressure, out var altValue); Console.WriteLine($"Altitude: {altValue}"); Thread.Sleep(1000); - //set higher sampling - i2CBmp280.SetTemperatureSampling(Sampling.LowPower); - Console.WriteLine(i2CBmp280.ReadTemperatureSampling()); - i2CBmp280.SetPressureSampling(Sampling.UltraHighResolution); - Console.WriteLine(i2CBmp280.ReadPressureSampling()); + //change sampling rate + i2CBmp280.TemperatureSampling = Sampling.UltraHighResolution; + i2CBmp280.PressureSampling = Sampling.UltraLowPower; + i2CBmp280.FilterMode = Bmx280FilteringMode.X4; //set mode forced and read again i2CBmp280.SetPowerMode(Bmx280PowerMode.Forced); + // wait for measurement to be performed + measurementTime = i2CBmp280.GetMeasurementDuration(); + Thread.Sleep(measurementTime); + //read values - tempValue = await i2CBmp280.ReadTemperatureAsync(); + i2CBmp280.TryReadTemperature(out tempValue); Console.WriteLine($"Temperature {tempValue.Celsius}"); - preValue = await i2CBmp280.ReadPressureAsync(); + i2CBmp280.TryReadPressure(out preValue); Console.WriteLine($"Pressure {preValue}"); - altValue = await i2CBmp280.ReadAltitudeAsync(defaultSeaLevelPressure); + i2CBmp280.TryReadAltitude(defaultSeaLevelPressure, out altValue); Console.WriteLine($"Altitude: {altValue}"); Thread.Sleep(5000); - - //set sampling to higher - i2CBmp280.SetTemperatureSampling(Sampling.UltraHighResolution); - Console.WriteLine(i2CBmp280.ReadTemperatureSampling()); - i2CBmp280.SetPressureSampling(Sampling.UltraLowPower); - Console.WriteLine(i2CBmp280.ReadPressureSampling()); } } }