提交 d32ad7a6 编写于 作者: G George Mathieson 提交者: Jose Perez Rodriguez

Add Bme680 binding (#477)

* Create bme680 device binding project from the template

* Add BME680 sensor with temperature, pressure and humidity functionality

* Refactor the Bme680 binding into the existing binding

* Rename registers to match those in the Bmx280 binding

* Rename Register to Bmx280Register and create a Bme680Register

The BMP280 and BME280 use the same registers, so Bmx280Register has been used as the name. As there is only one binding using the registers for the Bme680, this has been named Bme680Register.

* Add a Bmxx80Register for registers shared amongst the Bmxx80 family

* Move registers into their own folder

Similar to how the Mcp25xxx binding is

* Rename Bme680 calibration data to match the Bmx280 naming

* Make ReadFromDevice() virtual and rename bmp280 to bmxBase

* Add calibration data for the Bme680

* Create separate calibration data for Bmx280 and Bme680 bindings

* Order csproj includes

* Create a Bmxx80 base class containing common functionality in this device family

* Change BmxBase to Bmx280Base

Which contains common functionality for the Bmx280 family of devices.

* Update the existing BME280 and BMP280 devices to work with Bmx280Base

* Calibration data only needs functionality from the Bmxx80Base class

* Create an initial Bme680 device binding

* Rename the comment and tidy the spacing

* Move calibration data into its own folder

* Add relative humidity, pressure and temperature functionality for the BME680

* Remove the original Bme680 binding

* Rename Bmx280 to Bmxx80

* Update project reference for existing examples

* Add example for the BME680

* Update readme to include the BME680 sensor

* Move sample project into the root of samples

* Move all the properties for calibration data into the base class

* Update power mode and device ID

Let the device set it's ID, and power modes are different between the BME680 and BMX280 devices.

* Update examples to use specific power modes

* Add using for power mode

* Device ID is only used in the constructor

* Add licence

* Make the base classes abstract

* Add a brief description to the exception XML docs

* Change internal to protected so that a class that is derived from that class can use it

* Change to protected internal Calibration Data uses these methods but does not inherit from the device

It now makes more sense to have the base calibration as a public abstract class.

* These base registers are more useful if they're public

* Use the new switch C# 8 feature

* Update comment with a link to the datasheet

* Validate that the given power mode matches the expected for the sensor

* Remove properties in favour of just calling the Read methods

* Merge branch 'master' into bme680

* Merge BME280 - Humidity Support #435

* Add link to datasheet

* Use latest lang version

* Bring the sample project changes for Bme280.sample over

* Fix loss of precision during ReadPressureAsync

* Update the power mode comments for Bmx280 to match the docs

* Update Bme680.sample to use new SpiDevice.Create

* Remove extra comment delimiter

* Update to use new i2c namespace

* Update example to use new i2c namespace

* Remove unused usings

* Add missing param comment

* Update categorized device listing (#554) for BME680

* Implement the Dispose pattern

* Set TemperatureFine to int.MinValue for the Bmx280Base

* Refactor SetPowerMode to combine both bitwise operations

* Cast as StandbyTime

* Cast as Bme680PowerMode

* Make the BME680 methods Async and return a Task

Done for consistency with other sensors in this family

* await ReadTemperatureAsync for humidity

* Change Main() to async Task

* Remove the int.MinValue check from Bmx280Base pressure and Bme280 humidity

* Read temperature before compensating pressure for Bme680
上级 45456924
......@@ -9,6 +9,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackageRefAssemblyAndDocXml</TargetsForTfmSpecificContentInPackage>
<LangVersion>8</LangVersion>
<!--Disabling default items so samples source won't get build by the main library-->
</PropertyGroup>
......
// 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.Device.I2c;
namespace Iot.Device.Bmx280
{
public class Bmp280 : BmxBase
{
private const byte DeviceId = 0x58;
public const byte DefaultI2cAddress = 0x77;
public Bmp280(I2cDevice i2cDevice)
{
_i2cDevice = i2cDevice;
_deviceId = DeviceId;
_communicationProtocol = CommunicationProtocol.I2c;
}
internal override CalibrationData ReadCalibrationData()
{
return new CalibrationData
{
// Read temperature calibration data
DigT1 = Read16BitsFromRegister((byte)Register.DIG_T1),
DigT2 = (short)Read16BitsFromRegister((byte)Register.DIG_T2),
DigT3 = (short)Read16BitsFromRegister((byte)Register.DIG_T3),
// Read pressure calibration data
DigP1 = Read16BitsFromRegister((byte)Register.DIG_P1),
DigP2 = (short)Read16BitsFromRegister((byte)Register.DIG_P2),
DigP3 = (short)Read16BitsFromRegister((byte)Register.DIG_P3),
DigP4 = (short)Read16BitsFromRegister((byte)Register.DIG_P4),
DigP5 = (short)Read16BitsFromRegister((byte)Register.DIG_P5),
DigP6 = (short)Read16BitsFromRegister((byte)Register.DIG_P6),
DigP7 = (short)Read16BitsFromRegister((byte)Register.DIG_P7),
DigP8 = (short)Read16BitsFromRegister((byte)Register.DIG_P8),
DigP9 = (short)Read16BitsFromRegister((byte)Register.DIG_P9)
};
}
}
}
// 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.
//Ported from https://github.com/adafruit/Adafruit_BMP280_Library/blob/master/Adafruit_BMP280.cpp
//Formulas and code examples can also be found in the datasheet http://www.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf
using System;
using System.Buffers.Binary;
using System.Device.I2c;
using System.Threading.Tasks;
using Iot.Units;
namespace Iot.Device.Bmx280
{
public abstract class BmxBase : IDisposable
{
internal I2cDevice _i2cDevice;
internal byte _deviceId;
internal bool _initialized = false;
internal CommunicationProtocol _communicationProtocol;
internal CalibrationData CalibrationData { get; private set; }
/// <summary>
/// The variable _temperatureFine carries a fine resolution temperature value over to the
/// pressure compensation formula and could be implemented as a global variable.
/// </summary>
protected int TemperatureFine;
internal enum CommunicationProtocol
{
I2c
}
internal abstract CalibrationData ReadCalibrationData();
internal void Begin()
{
_i2cDevice.WriteByte((byte)Register.CHIPID);
byte readSignature = _i2cDevice.ReadByte();
if (readSignature != _deviceId)
{
throw new Exception($"Device ID {readSignature} is not the same as expected {_deviceId}. Please check you are using the right device.");
}
_initialized = true;
//Read the coefficients table
CalibrationData = ReadCalibrationData();
}
/// <summary>
/// Sets the power mode to the given mode
/// </summary>
/// <param name="powerMode"></param>
public void SetPowerMode(PowerMode powerMode)
{
byte status = Read8BitsFromRegister((byte)Register.CTRL_MEAS);
//clear last two bits
status = (byte)(status & 0b1111_1100);
status = (byte)(status | (byte)powerMode);
_i2cDevice.Write(new[] { (byte)Register.CTRL_MEAS, status });
}
/// <summary>
/// Reads the current power mode the device is running in
/// </summary>
/// <returns></returns>
public PowerMode ReadPowerMode()
{
byte status = Read8BitsFromRegister((byte)Register.CTRL_MEAS);
status = (byte)(status & 0b000_00011);
if (status == (byte)PowerMode.Normal)
{
return PowerMode.Normal;
}
else if (status == (byte)PowerMode.Sleep)
{
return PowerMode.Sleep;
}
else
{
return PowerMode.Forced;
}
}
/// <summary>
/// Sets the temperature sampling to the given value
/// </summary>
/// <param name="sampling"></param>
public void SetTemperatureSampling(Sampling sampling)
{
byte status = Read8BitsFromRegister((byte)Register.CTRL_MEAS);
status = (byte)(status & 0b0001_1111);
status = (byte)(status | (byte)sampling << 5);
_i2cDevice.Write(new[] { (byte)Register.CTRL_MEAS, status });
}
/// <summary>
/// Get the sample rate for temperature measurements
/// </summary>
/// <returns></returns>
public Sampling ReadTemperatureSampling()
{
byte status = Read8BitsFromRegister((byte)Register.CTRL_MEAS);
status = (byte)((status & 0b1110_0000) >> 5);
return ByteToSampling(status);
}
internal Sampling ByteToSampling(byte value)
{
//Values >=5 equals UltraHighResolution (others)
if (value >= 5)
{
return Sampling.UltraHighResolution;
}
return (Sampling)value;
}
/// <summary>
/// Get the current sample rate for pressure measurements
/// </summary>
/// <returns></returns>
public Sampling ReadPressureSampling()
{
byte status = Read8BitsFromRegister((byte)Register.CTRL_MEAS);
status = (byte)((status & 0b0001_1100) >> 2);
return ByteToSampling(status);
}
/// <summary>
/// Sets the pressure sampling to the given value
/// </summary>
/// <param name="sampling"></param>
public void SetPressureSampling(Sampling sampling)
{
byte status = Read8BitsFromRegister((byte)Register.CTRL_MEAS);
status = (byte)(status & 0b1110_0011);
status = (byte)(status | (byte)sampling << 2);
_i2cDevice.Write(new[] { (byte)Register.CTRL_MEAS, status });
}
/// <summary>
/// Reads the temperature from the sensor
/// </summary>
/// <returns>
/// Temperature
/// </returns>
public async Task<Temperature> ReadTemperatureAsync()
{
//Make sure the I2C device is initialized
if (!_initialized)
{
Begin();
}
if (ReadPowerMode() == PowerMode.Forced)
{
await Task.Delay(GetMeasurementTimeForForcedMode(ReadTemperatureSampling()));
}
//Read the MSB, LSB and bits 7:4 (XLSB) of the temperature from the BMP280 registers
byte msb = Read8BitsFromRegister((byte)Register.TEMPDATA_MSB);
byte lsb = Read8BitsFromRegister((byte)Register.TEMPDATA_LSB);
byte xlsb = Read8BitsFromRegister((byte)Register.TEMPDATA_XLSB); // bits 7:4
//Combine the values into a 32-bit integer
int t = (msb << 12) + (lsb << 4) + (xlsb >> 4);
return CompensateTemperature(t);
}
/// <summary>
/// Recommended wait timings from the datasheet
/// </summary>
/// <param name="sampleMode">
/// </param>
/// <returns>
/// The time it takes for the chip to read data in milliseconds rounded up
/// </returns>
internal int GetMeasurementTimeForForcedMode(Sampling sampleMode)
{
if (sampleMode == Sampling.UltraLowPower)
{
return 7;
}
else if (sampleMode == Sampling.LowPower)
{
return 9;
}
else if (sampleMode == Sampling.Standard)
{
return 14;
}
else if (sampleMode == Sampling.HighResolution)
{
return 23;
}
else if (sampleMode == Sampling.UltraHighResolution)
{
return 44;
}
return 0;
}
/// <summary>
/// Reads the pressure from the sensor
/// </summary>
/// <returns>
/// Atmospheric pressure in Pa
/// </returns>
public async Task<double> ReadPressureAsync()
{
//Make sure the I2C device is initialized
if (!_initialized)
{
Begin();
}
if (ReadPowerMode() == PowerMode.Forced)
{
await Task.Delay(GetMeasurementTimeForForcedMode(ReadPressureSampling()));
}
//Read the temperature first to load the t_fine value for compensation
if (TemperatureFine == int.MinValue)
{
await ReadTemperatureAsync();
}
//Read the MSB, LSB and bits 7:4 (XLSB) of the pressure from the BMP280 registers
byte msb = Read8BitsFromRegister((byte)Register.PRESSUREDATA_MSB);
byte lsb = Read8BitsFromRegister((byte)Register.PRESSUREDATA_LSB);
byte xlsb = Read8BitsFromRegister((byte)Register.PRESSUREDATA_XLSB); // bits 7:4
//Combine the values into a 32-bit integer
int t = (msb << 12) + (lsb << 4) + (xlsb >> 4);
//Convert the raw value to the pressure in Pa
long pres = CompensatePressure(t);
//Return the temperature as a float value
return (double)pres / 256;
}
/// <summary>
/// Calculates the altitude in meters from the specified sea-level pressure(in hPa).
/// </summary>
/// <param name="seaLevelPressure" >
/// Sea-level pressure in hPa
/// </param>
/// <returns>
/// Height in meters from the sensor
/// </returns>
public async Task<double> ReadAltitudeAsync(double seaLevelPressure)
{
//Make sure the I2C device is initialized
if (!_initialized)
{
Begin();
}
//Read the pressure first
double pressure = await ReadPressureAsync();
//Convert the pressure to hecto pascals (hPa)
pressure /= 100;
//Calculate and return the altitude using the international barometric formula
return 44330.0 * (1.0 - Math.Pow((pressure / seaLevelPressure), 0.1903));
}
/// <summary>
/// Returns the temperature. Resolution is 0.01 DegC. Output value of “5123” equals 51.23 degrees celsius.
/// </summary>
/// <param name="adcTemperature">
/// The temperature value read from the device
/// </param>
/// <returns>
/// Temperature
/// </returns>
private Temperature CompensateTemperature(int adcTemperature)
{
//Formula from the datasheet
//The temperature is calculated using the compensation formula in the BMP280 datasheet
double var1 = ((adcTemperature / 16384.0) - (CalibrationData.DigT1 / 1024.0)) * CalibrationData.DigT2;
double var2 = ((adcTemperature / 131072.0) - (CalibrationData.DigT1 / 8192.0));
var2 *= var2 * CalibrationData.DigT3;
TemperatureFine = (int)(var1 + var2);
double temp = (var1 + var2) / 5120.0;
return Temperature.FromCelsius(temp);
}
/// <summary>
/// Returns the pressure in Pa, in Q24.8 format (24 integer bits and 8 fractional bits).
/// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
/// </summary>
/// <param name="adcPressure">
/// The pressure value read from the device
/// </param>
/// <returns>
/// Pressure in hPa
/// </returns>
private long CompensatePressure(int adcPressure)
{
//Formula from the datasheet
//The pressure is calculated using the compensation formula in the BMP280 datasheet
long var1 = TemperatureFine - 128000;
long var2 = var1 * var1 * (long)CalibrationData.DigP6;
var2 = var2 + ((var1 * (long)CalibrationData.DigP5) << 17);
var2 = var2 + ((long)CalibrationData.DigP4 << 35);
var1 = ((var1 * var1 * (long)CalibrationData.DigP3) >> 8) + ((var1 * (long)CalibrationData.DigP2) << 12);
var1 = (((((long)1 << 47) + var1)) * (long)CalibrationData.DigP1) >> 33;
if (var1 == 0)
{
return 0; //Avoid exception caused by division by zero
}
//Perform calibration operations
long p = 1048576 - adcPressure;
p = (((p << 31) - var2) * 3125) / var1;
var1 = ((long)CalibrationData.DigP9 * (p >> 13) * (p >> 13)) >> 25;
var2 = ((long)CalibrationData.DigP8 * p) >> 19;
p = ((p + var1 + var2) >> 8) + ((long)CalibrationData.DigP7 << 4);
return p;
}
/// <summary>
/// When called, the device is reset using the complete power-on-reset procedure.
/// </summary>
public void Reset()
{
const byte resetCommand = 0xb6;
_i2cDevice.Write(new[] { (byte)Register.RESET, resetCommand });
}
/// <summary>
/// Get the current status of the device.
/// </summary>
/// <returns></returns>
public DeviceStatus ReadStatus()
{
var status = Read8BitsFromRegister((byte)Register.STATUS);
// Bit 3
var measuring = ((status >> 3) & 1) == 1;
// Bit 0
var imUpdate = (status & 1) == 1;
return new DeviceStatus
{
ImageUpdating = imUpdate,
Measuring = measuring
};
}
/// <summary>
/// Sets the IIR filter mode.
/// </summary>
/// <param name="filteringMode"></param>
public void SetFilterMode(FilteringMode filteringMode)
{
byte current = Read8BitsFromRegister((byte)Register.CONFIG);
current = (byte)((current & 0b1110_0011) | (byte)filteringMode << 2);
_i2cDevice.Write(new[] { (byte)Register.CONFIG, current });
}
/// <summary>
/// Reads the current IIR filter mode the device is running in.
/// </summary>
/// <returns></returns>
public FilteringMode ReadFilterMode()
{
byte current = Read8BitsFromRegister((byte)Register.CONFIG);
var mode = (byte)((current & 0b0001_1100) >> 2);
switch (mode)
{
case 0b000:
return FilteringMode.Off;
case 0b001:
return FilteringMode.X2;
case 0b010:
return FilteringMode.X4;
case 0b011:
return FilteringMode.X8;
default:
return FilteringMode.X16;
}
}
/// <summary>
/// Sets the standby time mode the device will used when operating in normal mode.
/// </summary>
/// <param name="standbyTime"></param>
public void SetStandbyTime(StandbyTime standbyTime)
{
byte current = Read8BitsFromRegister((byte)Register.CONFIG);
current = (byte)((current & 0b0001_1111) | (byte)standbyTime << 5);
_i2cDevice.Write(new[] { (byte)Register.CONFIG, current });
}
/// <summary>
/// Reads the currently configured standby time mode the device will used when operating in normal mode.
/// </summary>
/// <returns></returns>
public StandbyTime ReadStandbyTime()
{
byte current = Read8BitsFromRegister((byte)Register.CONFIG);
var time = (byte)((current & 0b1110_0000) >> 5);
switch (time)
{
case 0b000:
return StandbyTime.Ms0_5;
case 0b001:
return StandbyTime.Ms62_5;
case 0b010:
return StandbyTime.Ms125;
case 0b011:
return StandbyTime.Ms250;
case 0b100:
return StandbyTime.Ms500;
case 0b101:
return StandbyTime.Ms1000;
case 0b110:
return StandbyTime.Ms10;
case 0b111:
return StandbyTime.Ms20;
default:
throw new NotImplementedException($"Value read from registers {time:x2} was not defined by specifications.");
}
}
/// <summary>
/// Reads an 8 bit value from a register
/// </summary>
/// <param name="register">
/// Register to read from
/// </param>
/// <returns>
/// Value from register
/// </returns>
internal byte Read8BitsFromRegister(byte register)
{
if (_communicationProtocol == CommunicationProtocol.I2c)
{
_i2cDevice.WriteByte(register);
byte value = _i2cDevice.ReadByte();
return value;
}
else
{
throw new NotImplementedException();
}
}
/// <summary>
/// Reads a 16 bit value over I2C
/// </summary>
/// <param name="register">
/// Register to read from
/// </param>
/// <returns>
/// Value from register
/// </returns>
internal ushort Read16BitsFromRegister(byte register)
{
if (_communicationProtocol == CommunicationProtocol.I2c)
{
Span<byte> bytes = stackalloc byte[2];
_i2cDevice.WriteByte(register);
_i2cDevice.Read(bytes);
return BinaryPrimitives.ReadUInt16LittleEndian(bytes);
}
else
{
throw new NotImplementedException();
}
}
/// <summary>
/// Reads a 24 bit value over I2C
/// </summary>
/// <param name="register">
/// Register to read from
/// </param>
/// <returns>
/// Value from register
/// </returns>
internal uint Read24BitsFromRegister(byte register)
{
if (_communicationProtocol == CommunicationProtocol.I2c)
{
Span<byte> bytes = stackalloc byte[4];
_i2cDevice.WriteByte(register);
_i2cDevice.Read(bytes.Slice(1));
return BinaryPrimitives.ReadUInt32LittleEndian(bytes);
}
else
{
throw new NotImplementedException();
}
}
public void Dispose()
{
if (_i2cDevice != null)
{
_i2cDevice?.Dispose();
_i2cDevice = null;
}
}
}
}
......@@ -4,90 +4,105 @@
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.Bmx280
namespace Iot.Device.Bmxx80
{
public class Bme280 : BmxBase
/// <summary>
/// Represents a BME280 temperature, barometric pressure and humidity sensor.
/// </summary>
public class Bme280 : Bmx280Base
{
/// <summary>
/// The expected chip ID of the BME280.
/// </summary>
private const byte DeviceId = 0x60;
public const byte DefaultI2cAddress = 0x77;
/// <summary>
/// Calibration data for the <see cref="Bme680"/>.
/// </summary>
private readonly Bme280CalibrationData _bme280Calibration;
/// <summary>
/// Initializes a new instance of the <see cref="Bme280"/> class.
/// </summary>
/// <param name="i2cDevice">The <see cref="I2cDevice"/> to create with.</param>
public Bme280(I2cDevice i2cDevice)
: base(DeviceId, i2cDevice)
{
_i2cDevice = i2cDevice;
_deviceId = DeviceId;
var bme280CalibrationData = new Bme280CalibrationData();
bme280CalibrationData.ReadFromDevice(this);
_bme280Calibration = bme280CalibrationData;
_calibrationData = bme280CalibrationData;
_communicationProtocol = CommunicationProtocol.I2c;
}
/// <summary>
/// Get the current sample rate for humidity measurements.
/// </summary>
/// <returns></returns>
/// <returns>The humidity <see cref="Sampling"/>.</returns>
public Sampling ReadHumiditySampling()
{
byte status = Read8BitsFromRegister((byte)Register.CTRL_HUM);
status = (byte)(status & 0b0000_0111);
byte status = Read8BitsFromRegister((byte)Bme280Register.CTRL_HUM);
status = (byte)(status & 0b_0000_0111);
return ByteToSampling(status);
}
/// <summary>
/// Sets the humidity sampling to the given value.
/// </summary>
/// <param name="sampling"></param>
/// <param name="sampling">The <see cref="Sampling"/> to set.</param>
public void SetHumiditySampling(Sampling sampling)
{
byte status = Read8BitsFromRegister((byte)Register.CTRL_HUM);
status = (byte)(status & 0b1111_1000);
byte status = Read8BitsFromRegister((byte)Bme280Register.CTRL_HUM);
status = (byte)(status & 0b_1111_1000);
status = (byte)(status | (byte)sampling);
_i2cDevice.Write(new[] { (byte)Register.CTRL_HUM, status });
_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)Register.CTRL_MEAS);
_i2cDevice.Write(new[] { (byte)Register.CTRL_MEAS, measureState });
byte measureState = Read8BitsFromRegister((byte)Bmx280Register.CTRL_MEAS);
_i2cDevice.Write(new[] { (byte)Bmx280Register.CTRL_MEAS, measureState });
}
/// <summary>
/// Reads the Humidity from the sensor as %rH.
/// </summary>
/// <returns>
/// Returns a percentage from 0 to 100.
/// </returns>
/// <returns>Returns a percentage from 0 to 100.</returns>
public async Task<double> ReadHumidityAsync()
{
// Make sure the I2C device is initialized
if (!_initialized)
{
Begin();
}
if (ReadPowerMode() == PowerMode.Forced)
if (ReadPowerMode() == Bmx280PowerMode.Forced)
{
await Task.Delay(GetMeasurementTimeForForcedMode(ReadHumiditySampling()));
}
}
// Read the temperature first to load the t_fine value for compensation.
await ReadTemperatureAsync();
// Read the temperature first to load the t_fine value for compensation
if (TemperatureFine == int.MinValue)
{
await ReadTemperatureAsync();
}
byte msb = Read8BitsFromRegister((byte)Register.HUMIDDATA_MSB);
byte lsb = Read8BitsFromRegister((byte)Register.HUMIDDATA_LSB);
byte msb = Read8BitsFromRegister((byte)Bme280Register.HUMIDDATA_MSB);
byte lsb = Read8BitsFromRegister((byte)Bme280Register.HUMIDDATA_LSB);
// Combine the values into a 32-bit integer
// Combine the values into a 32-bit integer.
int t = (msb << 8) | lsb;
return CompensateHumidity(t);
}
/// <summary>
/// Compensates the humidity.
/// </summary>
/// <param name="adcHumidity">The humidity value read from the device.</param>
/// <returns>The percentage relative humidity.</returns>
private double CompensateHumidity(int adcHumidity)
{
// The humidity is calculated using the compensation formula in the BME280 datasheet
// The humidity is calculated using the compensation formula in the BME280 datasheet.
double varH = TemperatureFine - 76800.0;
varH = (adcHumidity - (CalibrationData.DigH4 * 64.0 + CalibrationData.DigH5 / 16384.0 * varH)) *
(CalibrationData.DigH2 / 65536.0 * (1.0 + CalibrationData.DigH6 / 67108864.0 * varH *
(1.0 + CalibrationData.DigH3 / 67108864.0 * varH)));
varH *= 1.0 - CalibrationData.DigH1 * varH / 524288.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)
{
......@@ -100,35 +115,5 @@ namespace Iot.Device.Bmx280
return varH;
}
internal override CalibrationData ReadCalibrationData()
{
return new CalibrationData
{
// Read temperature calibration data
DigT1 = Read16BitsFromRegister((byte)Register.DIG_T1),
DigT2 = (short)Read16BitsFromRegister((byte)Register.DIG_T2),
DigT3 = (short)Read16BitsFromRegister((byte)Register.DIG_T3),
// Read pressure calibration data
DigP1 = Read16BitsFromRegister((byte)Register.DIG_P1),
DigP2 = (short)Read16BitsFromRegister((byte)Register.DIG_P2),
DigP3 = (short)Read16BitsFromRegister((byte)Register.DIG_P3),
DigP4 = (short)Read16BitsFromRegister((byte)Register.DIG_P4),
DigP5 = (short)Read16BitsFromRegister((byte)Register.DIG_P5),
DigP6 = (short)Read16BitsFromRegister((byte)Register.DIG_P6),
DigP7 = (short)Read16BitsFromRegister((byte)Register.DIG_P7),
DigP8 = (short)Read16BitsFromRegister((byte)Register.DIG_P8),
DigP9 = (short)Read16BitsFromRegister((byte)Register.DIG_P9),
// Read humidity calibration data
DigH1 = Read8BitsFromRegister((byte)Register.DIG_H1),
DigH2 = (short)Read16BitsFromRegister((byte)Register.DIG_H2),
DigH3 = Read8BitsFromRegister((byte)Register.DIG_H3),
DigH4 = (short)((Read8BitsFromRegister((byte)Register.DIG_H4) << 4) | (Read8BitsFromRegister((byte)Register.DIG_H4 + 1) & 0xF)),
DigH5 = (short)((Read8BitsFromRegister((byte)Register.DIG_H5 + 1) << 4) | (Read8BitsFromRegister((byte)Register.DIG_H5) >> 4)),
DigH6 = (sbyte)Read8BitsFromRegister((byte)Register.DIG_H6)
};
}
}
}
// 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.Tasks;
using Iot.Device.Bmxx80.CalibrationData;
using Iot.Device.Bmxx80.PowerMode;
using Iot.Device.Bmxx80.Register;
using Iot.Units;
namespace Iot.Device.Bmxx80
{
/// <summary>
/// Represents a BME680 temperature, pressure, relative humidity and VOC gas sensor.
/// </summary>
public class Bme680 : Bmxx80Base
{
/// <summary>
/// Default I2C bus address.
/// </summary>
public const byte DefaultI2cAddress = 0x76;
/// <summary>
/// Secondary I2C bus address.
/// </summary>
public const byte SecondaryI2cAddress = 0x77;
/// <summary>
/// The expected chip ID of the BME680.
/// </summary>
private const byte DeviceId = 0x61;
/// <summary>
/// Calibration data for the <see cref="Bme680"/>.
/// </summary>
private readonly Bme680CalibrationData _bme680Calibration;
/// <summary>
/// Initialize a new instance of the <see cref="Bme680"/> class.
/// </summary>
/// <param name="i2cDevice">The <see cref="I2cDevice"/> to create with.</param>
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;
}
/// <summary>
/// Set the humidity sampling.
/// </summary>
/// <param name="sampling">The <see cref="Sampling"/> to set.</param>
public void SetHumiditySampling(Sampling sampling)
{
var register = (byte)Bme680Register.CTRL_HUM;
byte read = Read8BitsFromRegister(register);
// Clear first 3 bits.
var cleared = (byte)(read & 0b_1111_1000);
_i2cDevice.Write(new[] { register, (byte)(cleared | (byte)sampling) });
}
/// <summary>
/// Sets the power mode to the given mode
/// </summary>
/// <param name="powerMode">The <see cref="Bme680PowerMode"/> to set.</param>
public void SetPowerMode(Bme680PowerMode powerMode)
{
byte read = Read8BitsFromRegister(_controlRegister);
// Clear first 2 bits.
var cleared = (byte)(read & 0b_1111_1100);
_i2cDevice.Write(new[] { _controlRegister, (byte)(cleared | (byte)powerMode) });
}
/// <summary>
/// Read a value indicating whether or not new sensor data is available.
/// </summary>
/// <returns>True if new data is available.</returns>
public bool ReadHasNewData()
{
byte read = Read8BitsFromRegister((byte)Bme680Register.STATUS);
// Get only the power mode bit.
var hasNewData = (byte)(read & 0b_1000_0000);
return (hasNewData >> 7) == 1;
}
/// <summary>
/// Read the humidity.
/// </summary>
/// <returns>Calculated humidity.</returns>
public async Task<double> ReadHumidityAsync()
{
// Read humidity data.
byte msb = Read8BitsFromRegister((byte)Bme680Register.HUMIDITYDATA_MSB);
byte lsb = Read8BitsFromRegister((byte)Bme680Register.HUMIDITYDATA_LSB);
// Convert to a 32bit integer.
var adcHumidity = (msb << 8) + lsb;
return CompensateHumidity((await ReadTemperatureAsync()).Celsius, adcHumidity);
}
/// <summary>
/// Read the <see cref="Bme680PowerMode"/> state.
/// </summary>
/// <returns>The current <see cref="Bme680PowerMode"/>.</returns>
/// <exception cref="NotImplementedException">Thrown when the power mode does not match a defined mode in <see cref="Bme680PowerMode"/>.</exception>
public Bme680PowerMode ReadPowerMode()
{
byte read = Read8BitsFromRegister(_controlRegister);
return (Bme680PowerMode)(read & 0b_0000_0011);
}
/// <summary>
/// Read the pressure.
/// </summary>
/// <returns>Calculated pressure in Pa.</returns>
public async Task<double> ReadPressureAsync()
{
// Read pressure data.
byte lsb = Read8BitsFromRegister((byte)Bme680Register.PRESSUREDATA_LSB);
byte msb = Read8BitsFromRegister((byte)Bme680Register.PRESSUREDATA_MSB);
byte xlsb = Read8BitsFromRegister((byte)Bme680Register.PRESSUREDATA_XLSB);
// Convert to a 32bit integer.
var adcPressure = (msb << 12) + (lsb << 4) + (xlsb >> 4);
// Read the temperature first to load the t_fine value for compensation.
await ReadTemperatureAsync();
return CompensatePressure(adcPressure);
}
/// <summary>
/// Read the temperature.
/// </summary>
/// <returns>Calculated temperature.</returns>
public Task<Temperature> ReadTemperatureAsync()
{
// Read temperature data.
byte lsb = Read8BitsFromRegister((byte)Bme680Register.TEMPDATA_LSB);
byte msb = Read8BitsFromRegister((byte)Bme680Register.TEMPDATA_MSB);
byte xlsb = Read8BitsFromRegister((byte)Bme680Register.TEMPDATA_XLSB);
// Convert to a 32bit integer.
var adcTemperature = (msb << 12) + (lsb << 4) + (xlsb >> 4);
return Task.FromResult(CompensateTemperature(adcTemperature));
}
/// <summary>
/// Compensates the humidity.
/// </summary>
/// <param name="temperature">The temperature to use.</param>
/// <param name="adcHumidity">The humidity value read from the device.</param>
/// <returns>The percentage relative humidity.</returns>
private double CompensateHumidity(double temperature, int adcHumidity)
{
// Calculate the humidity.
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)));
var var3 = _bme680Calibration.DigH6 / 16384.0;
var var4 = _bme680Calibration.DigH7 / 2097152.0;
var calculatedHumidity = var2 + ((var3 + (var4 * temperature)) * var2 * var2);
if (calculatedHumidity > 100.0)
{
calculatedHumidity = 100.0;
}
else if (calculatedHumidity < 0.0)
{
calculatedHumidity = 0.0;
}
return calculatedHumidity;
}
/// <summary>
/// Compensates the pressure.
/// </summary>
/// <param name="adcPressure">The pressure value read from the device.</param>
/// <returns>The pressure in Pa.</returns>
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 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);
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;
}
else
{
calculatedPressure = 0;
}
return calculatedPressure;
}
}
}
// 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.Device.I2c;
using Iot.Device.Bmxx80.CalibrationData;
namespace Iot.Device.Bmxx80
{
/// <summary>
/// Represents a BME280 temperature and barometric pressure sensor.
/// </summary>
public class Bmp280 : Bmx280Base
{
/// <summary>
/// The expected chip ID of the BMP280.
/// </summary>
private const byte DeviceId = 0x58;
/// <summary>
/// Initializes a new instance of the <see cref="Bmp280"/> class.
/// </summary>
/// <param name="i2cDevice">The <see cref="I2cDevice"/> to create with.</param>
public Bmp280(I2cDevice i2cDevice)
: base(DeviceId, i2cDevice)
{
_communicationProtocol = CommunicationProtocol.I2c;
_calibrationData = new Bmp280CalibrationData();
_calibrationData.ReadFromDevice(this);
}
}
}
// 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.
//Ported from https://github.com/adafruit/Adafruit_BMP280_Library/blob/master/Adafruit_BMP280.cpp
//Formulas and code examples can also be found in the datasheet http://www.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf
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.Units;
namespace Iot.Device.Bmxx80
{
/// <summary>
/// Represents the core functionality of the Bmx280 family.
/// </summary>
public abstract class Bmx280Base : Bmxx80Base
{
/// <summary>
/// Default I2C bus address.
/// </summary>
public const byte DefaultI2cAddress = 0x77;
/// <summary>
/// Secondary I2C bus address.
/// </summary>
public const byte SecondaryI2cAddress = 0x76;
/// <summary>
/// Initializes a new instance of the <see cref="Bmx280Base"/> class.
/// </summary>
/// <param name="deviceId">The ID of the device.</param>
/// <param name="i2cDevice">The <see cref="I2cDevice"/> to create with.</param>
protected Bmx280Base(byte deviceId, I2cDevice i2cDevice)
: base(deviceId, i2cDevice)
{
_controlRegister = (byte)Bmx280Register.CTRL_MEAS;
}
/// <summary>
/// Reads the current IIR filter mode the device is running in.
/// </summary>
/// <returns>The current <see cref="FilteringMode"/>.</returns>
public FilteringMode ReadFilterMode()
{
byte current = Read8BitsFromRegister((byte)Bmx280Register.CONFIG);
var mode = (byte)((current & 0b_0001_1100) >> 2);
return mode switch
{
0b000 => FilteringMode.Off,
0b001 => FilteringMode.X2,
0b010 => FilteringMode.X4,
0b011 => FilteringMode.X8,
_ => FilteringMode.X16
};
}
/// <summary>
/// Read the temperature.
/// </summary>
/// <returns>Calculated temperature.</returns>
public async Task<Temperature> ReadTemperatureAsync()
{
if (ReadPowerMode() == Bmx280PowerMode.Forced)
{
await Task.Delay(GetMeasurementTimeForForcedMode(ReadTemperatureSampling()));
}
//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
//Combine the values into a 32-bit integer
int t = (msb << 12) + (lsb << 4) + (xlsb >> 4);
return CompensateTemperature(t);
}
/// <summary>
/// Read the <see cref="Bmx280PowerMode"/> state.
/// </summary>
/// <returns>The current <see cref="Bmx280PowerMode"/>.</returns>
/// <exception cref="NotImplementedException">Thrown when the power mode does not match a defined mode in <see cref="Bmx280PowerMode"/>.</exception>
public Bmx280PowerMode ReadPowerMode()
{
byte read = Read8BitsFromRegister(_controlRegister);
// Get only the power mode bits.
var powerMode = (byte)(read & 0b_0000_0011);
if (Enum.IsDefined(typeof(Bmx280PowerMode), powerMode) == false)
{
throw new IOException("Read unexpected power mode");
}
return powerMode switch
{
0b00 => Bmx280PowerMode.Sleep,
0b10 => Bmx280PowerMode.Forced,
0b11 => Bmx280PowerMode.Normal,
_ => throw new NotImplementedException($"Read power mode not defined by specification.")
};
}
/// <summary>
/// Reads the pressure from the sensor.
/// </summary>
/// <returns>Atmospheric pressure in Pa.</returns>
public async Task<double> ReadPressureAsync()
{
if (ReadPowerMode() == Bmx280PowerMode.Forced)
{
await Task.Delay(GetMeasurementTimeForForcedMode(ReadPressureSampling()));
}
// Read the temperature first to load the t_fine value for compensation.
await ReadTemperatureAsync();
// 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
//Combine the values into a 32-bit integer.
int t = (msb << 12) + (lsb << 4) + (xlsb >> 4);
//Convert the raw value to the pressure in Pa.
long pres = CompensatePressure(t);
//Return the temperature as a float value.
return (double)pres / 256;
}
/// <summary>
/// Calculates the altitude in meters from the specified sea-level pressure(in hPa).
/// </summary>
/// <param name="seaLevelPressure">Sea-level pressure in hPa.</param>
/// <returns>Height in meters from sea-level.</returns>
public async Task<double> ReadAltitudeAsync(double seaLevelPressure)
{
// Read the pressure first.
double pressure = await ReadPressureAsync();
// 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));
}
/// <summary>
/// Get the current status of the device.
/// </summary>
/// <returns>The <see cref="DeviceStatus"/>.</returns>
public DeviceStatus ReadStatus()
{
var status = Read8BitsFromRegister((byte)Bmx280Register.STATUS);
// Bit 3.
var measuring = ((status >> 3) & 1) == 1;
// Bit 0.
var imageUpdating = (status & 1) == 1;
return new DeviceStatus
{
ImageUpdating = imageUpdating,
Measuring = measuring
};
}
/// <summary>
/// Sets the IIR filter mode.
/// </summary>
/// <param name="filteringMode">The <see cref="FilteringMode"/> to set.</param>
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 });
}
/// <summary>
/// Sets the power mode to the given mode
/// </summary>
/// <param name="powerMode">The <see cref="Bmx280PowerMode"/> to set.</param>
public void SetPowerMode(Bmx280PowerMode powerMode)
{
byte read = Read8BitsFromRegister(_controlRegister);
// Clear last 2 bits.
var cleared = (byte)(read & 0b_1111_1100);
_i2cDevice.Write(new[] { _controlRegister, (byte)(cleared | (byte)powerMode) });
}
/// <summary>
/// Sets the standby time mode the device will used when operating in normal mode.
/// </summary>
/// <param name="standbyTime">The <see cref="StandbyTime"/> to set.</param>
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 });
}
/// <summary>
/// Reads the currently configured standby time mode the device will used when operating in normal mode.
/// </summary>
/// <returns>The current <see cref="StandbyTime"/>.</returns>
public StandbyTime ReadStandbyTime()
{
byte current = Read8BitsFromRegister((byte)Bmx280Register.CONFIG);
return (StandbyTime)((current & 0b_1110_0000) >> 5);
}
/// <summary>
/// Recommended wait timings from the datasheet.
/// </summary>
/// <param name="sampleMode">The <see cref="Sampling"/> to get for.</param>
/// <returns>The time it takes for the chip to read data in milliseconds rounded up.</returns>
internal int GetMeasurementTimeForForcedMode(Sampling sampleMode)
{
return sampleMode switch
{
Sampling.UltraLowPower => 7,
Sampling.LowPower => 9,
Sampling.Standard => 14,
Sampling.HighResolution => 23,
Sampling.UltraHighResolution => 44,
_ => 0
};
}
/// <summary>
/// Compensates the pressure in Pa, in Q24.8 format (24 integer bits and 8 fractional bits).
/// </summary>
/// <param name="adcPressure">The pressure value read from the device.</param>
/// <returns>Pressure in Hectopascals (hPa).</returns>
/// <remarks>
/// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa.
/// </remarks>
private long CompensatePressure(int adcPressure)
{
// Formula from the datasheet http://www.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf
// The pressure is calculated using the compensation formula in the BMP280 datasheet
long var1 = TemperatureFine - 128000;
long var2 = var1 * var1 * (long)_calibrationData.DigP6;
var2 = var2 + ((var1 * (long)_calibrationData.DigP5) << 17);
var2 = var2 + ((long)_calibrationData.DigP4 << 35);
var1 = ((var1 * var1 * (long)_calibrationData.DigP3) >> 8) + ((var1 * (long)_calibrationData.DigP2) << 12);
var1 = (((((long)1 << 47) + var1)) * (long)_calibrationData.DigP1) >> 33;
if (var1 == 0)
{
return 0; //Avoid exception caused by division by zero
}
//Perform calibration operations
long p = 1048576 - adcPressure;
p = (((p << 31) - var2) * 3125) / var1;
var1 = ((long)_calibrationData.DigP9 * (p >> 13) * (p >> 13)) >> 25;
var2 = ((long)_calibrationData.DigP8 * p) >> 19;
p = ((p + var1 + var2) >> 8) + ((long)_calibrationData.DigP7 << 4);
return p;
}
}
}
......@@ -3,19 +3,28 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<EnableDefaultItems>false</EnableDefaultItems>
<RootNamespace>Iot.Device.Bmx280</RootNamespace>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="Bme280.cs" />
<Compile Include="Bme680.cs" />
<Compile Include="Bmp280.cs" />
<Compile Include="BmxBase.cs" />
<Compile Include="CalibrationData.cs" />
<Compile Include="Bmx280Base.cs" />
<Compile Include="Bmxx80Base.cs" />
<Compile Include="CalibrationData\Bme680CalibrationData.cs" />
<Compile Include="CalibrationData\Bme280CalibrationData.cs" />
<Compile Include="CalibrationData\Bmp280CalibrationData.cs" />
<Compile Include="CalibrationData\Bmxx80CalibrationData.cs" />
<Compile Include="DeviceStatus.cs" />
<Compile Include="FilteringMode.cs" />
<Compile Include="PowerMode.cs" />
<Compile Include="Register.cs" />
<Compile Include="PowerMode\Bme680PowerMode.cs" />
<Compile Include="PowerMode\Bmx280PowerMode.cs" />
<Compile Include="Register\Bme680Register.cs" />
<Compile Include="Register\Bmx280Register.cs" />
<Compile Include="Register\Bmxx80Register.cs" />
<Compile Include="Register\Bme280Register.cs" />
<Compile Include="Sampling.cs" />
<Compile Include="DeviceStatus.cs" />
<Compile Include="StandbyTime.cs" />
<PackageReference Include="System.Device.Gpio" Version="$(SystemDeviceGpioPackageVersion)" />
<ProjectReference Include="..\Units\Units.csproj" />
......
// 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.Buffers.Binary;
using System.Device.I2c;
using System.IO;
using Iot.Device.Bmxx80.CalibrationData;
using Iot.Device.Bmxx80.Register;
using Iot.Units;
namespace Iot.Device.Bmxx80
{
/// <summary>
/// Represents the core functionality of the Bmxx80 family.
/// </summary>
public abstract class Bmxx80Base : IDisposable
{
protected Bmxx80CalibrationData _calibrationData;
protected I2cDevice _i2cDevice;
protected CommunicationProtocol _communicationProtocol;
protected byte _controlRegister;
public enum CommunicationProtocol
{
I2c
}
/// <summary>
/// The variable _temperatureFine carries a fine resolution temperature value over to the
/// pressure compensation formula and could be implemented as a global variable.
/// </summary>
protected int TemperatureFine;
/// <summary>
/// Initializes a new instance of the <see cref="Bmxx80Base"/> class.
/// </summary>
/// <param name="deviceId">The ID of the device.</param>
/// <param name="i2cDevice">The <see cref="I2cDevice"/> to create with.</param>
/// <exception cref="ArgumentNullException">Thrown when the given <see cref="I2cDevice"/> is null.</exception>
/// <exception cref="IOException">Thrown when the device cannot be found on the bus.</exception>
protected Bmxx80Base(byte deviceId, I2cDevice i2cDevice)
{
_i2cDevice = i2cDevice ?? throw new ArgumentNullException(nameof(i2cDevice));
_i2cDevice.WriteByte((byte)Bmxx80Register.CHIPID);
byte readSignature = _i2cDevice.ReadByte();
if (readSignature != deviceId)
{
throw new IOException($"Unable to find a chip with id {deviceId}");
}
}
/// <summary>
/// Sets the pressure sampling.
/// </summary>
/// <param name="sampling">The <see cref="Sampling"/> to set.</param>
public void SetPressureSampling(Sampling sampling)
{
byte status = Read8BitsFromRegister(_controlRegister);
status = (byte)(status & 0b1110_0011);
status = (byte)(status | (byte)sampling << 2);
_i2cDevice.Write(new[] { _controlRegister, status });
}
/// <summary>
/// Set the temperature oversampling.
/// </summary>
/// <param name="sampling">The <see cref="Sampling"/> to set.</param>
public void SetTemperatureSampling(Sampling sampling)
{
byte status = Read8BitsFromRegister(_controlRegister);
status = (byte)(status & 0b0001_1111);
status = (byte)(status | (byte)sampling << 5);
_i2cDevice.Write(new[] { _controlRegister, status });
}
/// <summary>
/// Get the current sample rate for pressure measurements
/// </summary>
/// <returns>The current pressure <see cref="Sampling"/> rate.</returns>
public Sampling ReadPressureSampling()
{
byte status = Read8BitsFromRegister(_controlRegister);
status = (byte)((status & 0b0001_1100) >> 2);
return ByteToSampling(status);
}
/// <summary>
/// Get the sample rate for temperature measurements.
/// </summary>
/// <returns>The current temperature <see cref="Sampling"/> rate.</returns>
public Sampling ReadTemperatureSampling()
{
byte status = Read8BitsFromRegister(_controlRegister);
status = (byte)((status & 0b1110_0000) >> 5);
return ByteToSampling(status);
}
/// <summary>
/// When called, the device is reset using the complete power-on-reset procedure.
/// </summary>
public void Reset()
{
const byte resetCommand = 0xB6;
_i2cDevice.Write(new[] { (byte)Bmxx80Register.RESET, resetCommand });
}
/// <summary>
/// Compensates the temperature.
/// </summary>
/// <param name="adcTemperature">The temperature value read from the device.</param>
/// <returns>The <see cref="Temperature"/>.</returns>
protected Temperature CompensateTemperature(int adcTemperature)
{
// The temperature is calculated using the compensation formula in the BMP280 datasheet.
// See: https://cdn-shop.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf
double var1 = ((adcTemperature / 16384.0) - (_calibrationData.DigT1 / 1024.0)) * _calibrationData.DigT2;
double var2 = ((adcTemperature / 131072.0) - (_calibrationData.DigT1 / 8192.0)) * _calibrationData.DigT3;
var2 *= var2 * _calibrationData.DigT3;
TemperatureFine = (int)(var1 + var2);
double temp = (var1 + var2) / 5120.0;
return Temperature.FromCelsius(temp);
}
/// <summary>
/// Reads an 8 bit value from a register.
/// </summary>
/// <param name="register">Register to read from.</param>
/// <returns>Value from register.</returns>
protected internal byte Read8BitsFromRegister(byte register)
{
if (_communicationProtocol == CommunicationProtocol.I2c)
{
_i2cDevice.WriteByte(register);
byte value = _i2cDevice.ReadByte();
return value;
}
else
{
throw new NotImplementedException();
}
}
/// <summary>
/// Reads a 16 bit value over I2C.
/// </summary>
/// <param name="register">Register to read from.</param>
/// <returns>Value from register.</returns>
protected internal ushort Read16BitsFromRegister(byte register)
{
if (_communicationProtocol == CommunicationProtocol.I2c)
{
Span<byte> bytes = stackalloc byte[2];
_i2cDevice.WriteByte(register);
_i2cDevice.Read(bytes);
return BinaryPrimitives.ReadUInt16LittleEndian(bytes);
}
else
{
throw new NotImplementedException();
}
}
/// <summary>
/// Reads a 24 bit value over I2C.
/// </summary>
/// <param name="register">Register to read from.</param>
/// <returns>Value from register.</returns>
protected internal uint Read24BitsFromRegister(byte register)
{
if (_communicationProtocol == CommunicationProtocol.I2c)
{
Span<byte> bytes = stackalloc byte[4];
_i2cDevice.WriteByte(register);
_i2cDevice.Read(bytes.Slice(1));
return BinaryPrimitives.ReadUInt32LittleEndian(bytes);
}
else
{
throw new NotImplementedException();
}
}
protected Sampling ByteToSampling(byte value)
{
// Values >=5 equals UltraHighResolution.
if (value >= 5)
{
return Sampling.UltraHighResolution;
}
return (Sampling)value;
}
/// <summary>
/// Cleanup.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
_i2cDevice?.Dispose();
_i2cDevice = null;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Iot.Device.Bmxx80.Register;
namespace Iot.Device.Bmxx80.CalibrationData
{
/// <summary>
/// Calibration data for the BME280.
/// </summary>
internal class Bme280CalibrationData : Bmp280CalibrationData
{
public byte DigH1 { get; set; }
public short DigH2 { get; set; }
public ushort DigH3 { get; set; }
public short DigH4 { get; set; }
public short DigH5 { get; set; }
public sbyte DigH6 { get; set; }
/// <summary>
/// Read coefficient data from device.
/// </summary>
/// <param name="bmxx80Base">The <see cref="Bmxx80Base"/> to read coefficient data from.</param>
protected internal override void ReadFromDevice(Bmxx80Base bmxx80Base)
{
// Read humidity calibration data.
DigH1 = bmxx80Base.Read8BitsFromRegister((byte)Bme280Register.DIG_H1);
DigH2 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bme280Register.DIG_H2);
DigH3 = bmxx80Base.Read8BitsFromRegister((byte)Bme280Register.DIG_H3);
DigH4 = (short)((bmxx80Base.Read8BitsFromRegister((byte)Bme280Register.DIG_H4) << 4) | (bmxx80Base.Read8BitsFromRegister((byte)Bme280Register.DIG_H4 + 1) & 0xF));
DigH5 = (short)((bmxx80Base.Read8BitsFromRegister((byte)Bme280Register.DIG_H5 + 1) << 4) | (bmxx80Base.Read8BitsFromRegister((byte)Bme280Register.DIG_H5) >> 4));
DigH6 = (sbyte)bmxx80Base.Read8BitsFromRegister((byte)Bme280Register.DIG_H6);
base.ReadFromDevice(bmxx80Base);
}
}
}
// 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
{
/// <summary>
/// Calibration data for the <see cref="Bme680"/>.
/// </summary>
internal class Bme680CalibrationData : Bmxx80CalibrationData
{
public ushort DigH1 { get; set; }
public ushort DigH2 { get; set; }
public sbyte DigH3 { get; set; }
public sbyte DigH4 { get; set; }
public sbyte DigH5 { get; set; }
public byte DigH6 { get; set; }
public sbyte DigH7 { get; set; }
/// <summary>
/// Read coefficient data from device.
/// </summary>
/// <param name="bmxx80Base">The <see cref="Bmxx80Base"/> to read coefficient data from.</param>
protected internal override void ReadFromDevice(Bmxx80Base bmxx80Base)
{
// Read temperature calibration data.
DigT1 = bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_T1);
DigT2 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_T2);
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));
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);
DigH5 = (sbyte)bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H5);
DigH6 = bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H6);
DigH7 = (sbyte)(bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_H7));
// Read pressure calibration data.
DigP1 = bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_P1_LSB);
DigP2 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_P2_LSB);
DigP3 = bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_P3);
DigP4 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_P4_LSB);
DigP5 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bme680Register.DIG_P5_LSB);
DigP6 = bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_P6);
DigP7 = bmxx80Base.Read8BitsFromRegister((byte)Bme680Register.DIG_P7);
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);
}
}
}
// 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
{
/// <summary>
/// Calibration data for the BMP280.
/// </summary>
internal class Bmp280CalibrationData : Bmxx80CalibrationData
{
/// <summary>
/// Read coefficient data from device.
/// </summary>
/// <param name="bmxx80Base">The <see cref="Bmxx80Base"/> to read coefficient data from.</param>
protected internal override void ReadFromDevice(Bmxx80Base bmxx80Base)
{
// Read temperature calibration data
DigT1 = bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_T1);
DigT2 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_T2);
DigT3 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_T3);
// Read pressure calibration data
DigP1 = bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P1);
DigP2 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P2);
DigP3 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P3);
DigP4 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P4);
DigP5 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P5);
DigP6 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P6);
DigP7 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P7);
DigP8 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P8);
DigP9 = (short)bmxx80Base.Read16BitsFromRegister((byte)Bmx280Register.DIG_P9);
}
}
}
......@@ -2,16 +2,17 @@
// 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.Bmx280
namespace Iot.Device.Bmxx80.CalibrationData
{
internal class CalibrationData
/// <summary>
/// Calibration data for the Bmxx80 family.
/// </summary>
public abstract class Bmxx80CalibrationData
{
// Temperature calibration data
public ushort DigT1 { get; set; }
public short DigT2 { get; set; }
public short DigT3 { get; set; }
// Pressure calibration data
public ushort DigP1 { get; set; }
public short DigP2 { get; set; }
public short DigP3 { get; set; }
......@@ -21,13 +22,12 @@ namespace Iot.Device.Bmx280
public short DigP7 { get; set; }
public short DigP8 { get; set; }
public short DigP9 { get; set; }
public byte DigP10 { get; set; }
// Humidity calibration data (BME280 Only)
public byte DigH1 { get; set; }
public short DigH2 { get; set; }
public ushort DigH3 { get; set; }
public short DigH4 { get; set; }
public short DigH5 { get; set; }
public sbyte DigH6 { get; set; }
/// <summary>
/// Read coefficient data from device.
/// </summary>
/// <param name="bmxx80Base">The <see cref="Bmxx80Base"/> to read coefficient data from.</param>
protected internal virtual void ReadFromDevice(Bmxx80Base bmxx80Base) { }
}
}
// 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.Bmx280
namespace Iot.Device.Bmxx80
{
/// <summary>
/// Indicates the status of the device.
......@@ -15,7 +11,7 @@ namespace Iot.Device.Bmx280
public bool Measuring { get; set; }
/// <summary>
/// True when the NVM data is being copied to images registers and False whe the copying is done.
/// True when the NVM data is being copied to images registers and False when the copying is done.
/// The data is copied at power-on-reset and before every conversion.
/// </summary>
public bool ImageUpdating { get; set; }
......
......@@ -2,37 +2,43 @@
// 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.Bmx280
namespace Iot.Device.Bmxx80
{
/// <summary>
/// Bmx280 devices feature an internal IIR filter.
/// </summary>
/// <remarks>
/// 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.
///
/// See the data sheet with recommended settings for different scenarios.
/// </summary>
/// </remarks>
public enum FilteringMode : byte
{
/// <summary>
/// Filter off
/// Filter off.
/// </summary>
Off = 0b000,
/// <summary>
/// Coefficient x2
/// Coefficient x2.
/// </summary>
X2 = 0b001,
/// <summary>
/// Coefficient x4
/// Coefficient x4.
/// </summary>
X4 = 0b010,
/// <summary>
/// Coefficient x8
/// Coefficient x8.
/// </summary>
X8 = 0b011,
/// <summary>
/// Coefficient x16
/// Coefficient x16.
/// </summary>
X16 = 0b100
}
......
// 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.PowerMode
{
/// <summary>
/// Sensor power mode.
/// </summary>
/// <remarks>
/// Section 3.1 in the datasheet.
/// </remarks>
public enum Bme680PowerMode : byte
{
/// <summary>
/// No measurements are performed.
/// </summary>
/// <remarks>
/// Minimal power consumption.
/// </remarks>
Sleep = 0b00,
/// <summary>
/// Single TPHG cycle is performed.
/// </summary>
/// <remarks>
/// Sensor automatically returns to sleep mode afterwards.
/// Gas sensor heater only operates during gas measurement.
/// </remarks>
Forced = 0b01
}
}
......@@ -2,21 +2,23 @@
// 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.Bmx280
namespace Iot.Device.Bmxx80.PowerMode
{
/// <summary>
/// Bmx280s power modes.
/// Sensor power mode.
/// </summary>
public enum PowerMode : byte
public enum Bmx280PowerMode : byte
{
/// <summary>
/// No operations, all registers accessible, lowest power mode, selected after startup.
/// </summary>
Sleep = 0b00,
/// <summary>
/// Perform one measurement, store results, and return to sleep mode.
/// </summary>
Forced = 0b10,
/// <summary>
/// Perpetual cycling of measurements and inactive periods.
/// This interval is determined by the combination of IIR filter and standby time options.
......
# BMx280 - Digital Pressure Sensors BMP280/BME280
# BMxx80 Device Family
## Summary
BMx280 is a device family that read barometric pressure, altitude and temperature. SPI and I2C can be used to communicate with the device (only I2C implemented so far).
BMxx80 is a device family that senses temperature, barometric pressure, altitude, humidity and VOC gas.
The implementation supports both BME280 and BMP280.
SPI and I2C can be used to communicate with the device (only I2C implemented so far).
## Device Family
The implementation supports the following devices:
[Datasheet](https://cdn-shop.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf) for the BMP280.
[Datasheet](https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280-DS002.pdf) for the BME280.
- 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))
## Usage
......
namespace Iot.Device.Bmxx80.Register
{
/// <summary>
/// General control registers for the BME280.
/// </summary>
internal enum Bme280Register : byte
{
CTRL_HUM = 0xF2,
DIG_H1 = 0xA1,
DIG_H2 = 0xE1,
DIG_H3 = 0xE3,
DIG_H4 = 0xE4,
DIG_H5 = 0xE5,
DIG_H6 = 0xE7,
HUMIDDATA_LSB = 0xFE,
HUMIDDATA_MSB = 0xFD,
}
}
// 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
{
/// <summary>
/// General control registers for the BME680.
/// </summary>
/// <remarks>
/// See section 5.2 Memory map.
/// </remarks>
internal enum Bme680Register : byte
{
CTRL_MEAS = 0x74,
CTRL_HUM = 0x72,
DIG_H1_LSB = 0xE2,
DIG_H1_MSB = 0xE3,
DIG_H2_LSB = 0xE2,
DIG_H2_MSB = 0xE1,
DIG_H3 = 0xE4,
DIG_H4 = 0xE5,
DIG_H5 = 0xE6,
DIG_H6 = 0xE7,
DIG_H7 = 0xE8,
DIG_T1 = 0xE9,
DIG_T2 = 0x8A,
DIG_T3 = 0x8C,
DIG_P1_LSB = 0x8E,
DIG_P2_LSB = 0x90,
DIG_P3 = 0x92,
DIG_P4_LSB = 0x94,
DIG_P5_LSB = 0x96,
DIG_P6 = 0x99,
DIG_P7 = 0x98,
DIG_P8_LSB = 0x9C,
DIG_P9_LSB = 0x9E,
DIG_P10 = 0xA0,
HUMIDITYDATA_LSB = 0x26,
HUMIDITYDATA_MSB = 0x25,
PRESSUREDATA_MSB = 0x1F,
PRESSUREDATA_LSB = 0x20,
PRESSUREDATA_XLSB = 0x21,
STATUS = 0x1D,
TEMPDATA_MSB = 0x22,
TEMPDATA_LSB = 0x23,
TEMPDATA_XLSB = 0x24,
}
}
......@@ -2,13 +2,15 @@
// 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.Bmx280
namespace Iot.Device.Bmxx80.Register
{
/// <summary>
/// Bmx280 registers.
/// Register shared by the Bmx280 family.
/// </summary>
internal enum Register : byte
public enum Bmx280Register : byte
{
CTRL_MEAS = 0xF4,
DIG_T1 = 0x88,
DIG_T2 = 0x8A,
DIG_T3 = 0x8C,
......@@ -23,25 +25,12 @@ namespace Iot.Device.Bmx280
DIG_P8 = 0x9C,
DIG_P9 = 0x9E,
// BME280 Only
DIG_H1 = 0xA1,
DIG_H2 = 0xE1,
DIG_H3 = 0xE3,
DIG_H4 = 0xE4,
DIG_H5 = 0xE5,
DIG_H6 = 0xE7,
CHIPID = 0xD0,
VERSION = 0xD1,
RESET = 0xE0,
SOFTRESET = 0xE0,
CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0
// BME280 Only
CTRL_HUM = 0xF2,
CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0
STATUS = 0xF3,
CTRL_MEAS = 0xF4,
CONFIG = 0xF5,
PRESSUREDATA_MSB = 0xF7,
......@@ -50,10 +39,6 @@ namespace Iot.Device.Bmx280
TEMPDATA_MSB = 0xFA,
TEMPDATA_LSB = 0xFB,
TEMPDATA_XLSB = 0xFC, // bits <7:4>=
// BME280 Only
HUMIDDATA_LSB = 0xFE,
HUMIDDATA_MSB = 0xFD
TEMPDATA_XLSB = 0xFC, // bits <7:4>
}
}
// 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
{
/// <summary>
/// Registers shared in the Bmxx80 family.
/// </summary>
public enum Bmxx80Register : byte
{
CHIPID = 0xD0,
RESET = 0xE0,
}
}
......@@ -2,35 +2,43 @@
// 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.Bmx280
namespace Iot.Device.Bmxx80
{
/// <summary>
/// Oversampling settings. Maximum of x2 is recommended for temperature
/// Oversampling settings.
/// </summary>
/// <remarks>
/// Maximum of x2 is recommended for temperature.
/// </remarks>
public enum Sampling : byte
{
/// <summary>
/// Skipped (output set to 0x80000)
/// Skipped (output set to 0x80000).
/// </summary>
Skipped = 0b000,
/// <summary>
/// Oversampling x1
/// Oversampling x1.
/// </summary>
UltraLowPower = 0b001,
/// <summary>
/// Oversampling x2
/// Oversampling x2.
/// </summary>
LowPower = 0b010,
/// <summary>
/// Oversampling x4
/// Oversampling x4.
/// </summary>
Standard = 0b011,
/// <summary>
/// Oversampling x8
/// Oversampling x8.
/// </summary>
HighResolution = 0b100,
/// <summary>
/// Oversampling x16
/// Oversampling x16.
/// </summary>
UltraHighResolution = 0b101,
}
......
......@@ -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.Bmx280
namespace Iot.Device.Bmxx80
{
/// <summary>
/// Controls the inactive duration in normal mode.
......@@ -10,35 +10,42 @@ namespace Iot.Device.Bmx280
public enum StandbyTime : byte
{
/// <summary>
/// 0.5 ms
/// 0.5 ms.
/// </summary>
Ms0_5 = 0b000,
/// <summary>
/// 62.5 ms
/// 62.5 ms.
/// </summary>
Ms62_5 = 0b001,
/// <summary>
/// 125 ms
/// 125 ms.
/// </summary>
Ms125 = 0b010,
/// <summary>
/// 250 ms
/// 250 ms.
/// </summary>
Ms250 = 0b011,
/// <summary>
/// 500 ms
/// 500 ms.
/// </summary>
Ms500 = 0b100,
/// <summary>
/// 1,000 ms
/// 1,000 ms.
/// </summary>
Ms1000 = 0b101,
/// <summary>
/// 10 ms
/// 10 ms.
/// </summary>
Ms10 = 0b110,
/// <summary>
/// 20 ms
/// 20 ms.
/// </summary>
Ms20 = 0b111,
}
......
......@@ -6,7 +6,8 @@ using System;
using System.Device.I2c;
using System.Threading;
using System.Threading.Tasks;
using Iot.Device.Bmx280;
using Iot.Device.Bmxx80;
using Iot.Device.Bmxx80.PowerMode;
using Iot.Units;
namespace Iot.Device.Samples
......@@ -31,7 +32,7 @@ namespace Iot.Device.Samples
while (true)
{
//set mode forced so device sleeps after read
i2CBmpe80.SetPowerMode(PowerMode.Forced);
i2CBmpe80.SetPowerMode(Bmx280PowerMode.Forced);
//set samplings
i2CBmpe80.SetTemperatureSampling(Sampling.UltraLowPower);
......@@ -61,7 +62,7 @@ namespace Iot.Device.Samples
Console.WriteLine(i2CBmpe80.ReadFilterMode());
//set mode forced and read again
i2CBmpe80.SetPowerMode(PowerMode.Forced);
i2CBmpe80.SetPowerMode(Bmx280PowerMode.Forced);
//read values
tempValue = await i2CBmpe80.ReadTemperatureAsync();
......
......@@ -13,7 +13,10 @@
<ItemGroup>
<Compile Include="Bme280.sample.cs" />
<ProjectReference Include="..\Bmx280.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bmxx80.csproj" />
</ItemGroup>
</Project>
using System;
using System.Device.I2c;
using System.Threading;
using System.Threading.Tasks;
using Iot.Device.Bmxx80;
using Iot.Device.Bmxx80.PowerMode;
namespace Iot.Device.Samples
{
/// <summary>
/// Sample program for reading <see cref="Bme680"/> sensor data on a Raspberry Pi.
/// </summary>
public static class Program
{
/// <summary>
/// Main entry point for the program.
/// </summary>
static async Task Main()
{
Console.WriteLine("Hello BME680!");
// The I2C bus ID on the Raspberry Pi 3.
const int busId = 1;
var i2cSettings = new I2cConnectionSettings(busId, Bme680.DefaultI2cAddress);
var unixI2cDevice = I2cDevice.Create(i2cSettings);
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)
{
// This instructs the sensor to take a measurement.
bme680.SetPowerMode(Bme680PowerMode.Forced);
}
// This prevent us from reading old data from the sensor.
if (bme680.ReadHasNewData())
{
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");
Console.WriteLine($"{temperature} °c | {pressure} hPa | {humidity} %rH");
Thread.Sleep(1000);
}
}
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<None Remove="rpi-bmp280_i2c.fzz" />
<None Remove="rpi-bmp280_i2c.png" />
</ItemGroup>
<ItemGroup>
<Compile Include="Bme680.sample.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bmxx80.csproj" />
</ItemGroup>
</Project>
......@@ -6,7 +6,8 @@ using System;
using System.Device.I2c;
using System.Threading;
using System.Threading.Tasks;
using Iot.Device.Bmx280;
using Iot.Device.Bmxx80;
using Iot.Device.Bmxx80.PowerMode;
using Iot.Units;
namespace Iot.Device.Samples
......@@ -30,8 +31,8 @@ namespace Iot.Device.Samples
{
while (true)
{
////set mode forced so device sleeps after read
i2CBmp280.SetPowerMode(PowerMode.Forced);
//set mode forced so device sleeps after read
i2CBmp280.SetPowerMode(Bmx280PowerMode.Forced);
//set samplings
i2CBmp280.SetTemperatureSampling(Sampling.UltraLowPower);
......@@ -53,7 +54,7 @@ namespace Iot.Device.Samples
Console.WriteLine(i2CBmp280.ReadPressureSampling());
//set mode forced and read again
i2CBmp280.SetPowerMode(PowerMode.Forced);
i2CBmp280.SetPowerMode(Bmx280PowerMode.Forced);
//read values
tempValue = await i2CBmp280.ReadTemperatureAsync();
......
......@@ -13,7 +13,10 @@
<ItemGroup>
<Compile Include="Bmp280.sample.cs" />
<ProjectReference Include="..\Bmx280.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bmxx80.csproj" />
</ItemGroup>
</Project>
......@@ -8,7 +8,7 @@
* [AGS01DB - MEMS VOC Gas Sensor](Ags01db/README.md)
* [BH1750FVI - Ambient Light Sensor](Bh1750fvi/README.md)
* [BMP180 - barometer, altitude and temperature sensor](Bmp180/README.md)
* [BMx280 - Digital Pressure Sensors BMP280/BME280](Bmx280/README.md)
* [BMxx80 - Digital Temperature, Pressure and Humidity Sensors BMP280/BME280/BME680](Bmxx80/README.md)
* [BNO055 - inertial measurement unit](Bno055/README.md)
* [BrickPi3](BrickPi3/README.md)
* [Buzzer - Piezo Buzzer Controller](Buzzer/README.md)
......
......@@ -40,7 +40,7 @@ Our vision: the majority of .NET bindings are written completely in .NET languag
### Barometers
* [BMP180 - barometer, altitude and temperature sensor](Bmp180/README.md)
* [BMx280 - Digital Pressure Sensors BMP280/BME280](Bmx280/README.md)
* [BMxx80 - Digital Temperature, Pressure and Humidity Sensors BMP280/BME280/BME680](Bmxx80/README.md)
* [LPS25H - Piezoresistive pressure and thermometer sensor](Lps25h/README.md)
* [Sense HAT](SenseHat/README.md)
......@@ -51,6 +51,7 @@ Our vision: the majority of .NET bindings are written completely in .NET languag
### Thermometers
* [BMP180 - barometer, altitude and temperature sensor](Bmp180/README.md)
* [BMxx80 - Digital Temperature, Pressure and Humidity Sensors BMP280/BME280/BME680](Bmxx80/README.md)
* [Cpu Temperature](CpuTemperature/README.md)
* [DHTxx - Digital-Output Relative Humidity & Temperature Sensor Module](Dhtxx/README.md)
* [HTS221 - Capacitive digital sensor for relative humidity and temperature](Hts221/README.md)
......@@ -100,6 +101,7 @@ Our vision: the majority of .NET bindings are written completely in .NET languag
### Hygrometers
* [BMxx80 - Digital Temperature, Pressure and Humidity Sensors BME280/BME680](Bmxx80/README.md)
* [DHTxx - Digital-Output Relative Humidity & Temperature Sensor Module](Dhtxx/README.md)
* [HTS221 - Capacitive digital sensor for relative humidity and temperature](Hts221/README.md)
* [Sense HAT](SenseHat/README.md)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册