// 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 NucuCar.Sensors.Modules.Environment.Bmxx80.CalibrationData; using NucuCar.Sensors.Modules.Environment.Bmxx80.Register; namespace NucuCar.Sensors.Modules.Environment.Bmxx80 { /// /// Represents a BME280 temperature, barometric pressure and humidity sensor. /// public class Bme280 : Bmx280Base { /// /// The expected chip ID of the BME280. /// private const byte DeviceId = 0x60; /// /// Calibration data for the . /// private Bme280CalibrationData _bme280Calibration; private Sampling _humiditySampling; /// /// Initializes a new instance of the class. /// /// The to create with. public Bme280(I2cDevice i2cDevice) : base(DeviceId, i2cDevice) { _bme280Calibration = (Bme280CalibrationData)_calibrationData; _communicationProtocol = CommunicationProtocol.I2c; } /// /// Gets or sets the humidity sampling. /// /// Thrown when the is set to an undefined mode. public Sampling HumiditySampling { 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; } } /// /// 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 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; } /// /// Gets the required time in ms to perform a measurement with the current sampling modes. /// /// The time it takes for the chip to read data in milliseconds rounded up. public override int GetMeasurementDuration() { return s_osToMeasCycles[(int)PressureSampling] + s_osToMeasCycles[(int)TemperatureSampling] + s_osToMeasCycles[(int)HumiditySampling]; } /// /// Sets the default configuration for the sensor. /// protected override void SetDefaultConfiguration() { base.SetDefaultConfiguration(); HumiditySampling = Sampling.UltraLowPower; } /// /// Compensates the humidity. /// /// The humidity value read from the device. /// The percentage relative humidity. private double CompensateHumidity(int adcHumidity) { // The humidity is calculated using the compensation formula in the BME280 datasheet. double varH = TemperatureFine - 76800.0; varH = (adcHumidity - (_bme280Calibration.DigH4 * 64.0 + _bme280Calibration.DigH5 / 16384.0 * varH)) * (_bme280Calibration.DigH2 / 65536.0 * (1.0 + _bme280Calibration.DigH6 / 67108864.0 * varH * (1.0 + _bme280Calibration.DigH3 / 67108864.0 * varH))); varH *= 1.0 - _bme280Calibration.DigH1 * varH / 524288.0; if (varH > 100) { varH = 100; } else if (varH < 0) { varH = 0; } return varH; } } }