Last active
February 15, 2019 17:49
-
-
Save joperezr/58123ec658ba1719f0a4578e8c8cd6a4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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.Gpio; | |
using System.Device.I2c; | |
using System.Device.I2c.Drivers; | |
using System.Device.Spi; | |
using System.Device.Spi.Drivers; | |
using System.Runtime.CompilerServices; | |
namespace Iot.Device.CharacterLcd.Samples | |
{ | |
/// <summary> | |
/// Supports BME280 Pressure, Temperature and Humidity sensor | |
/// </summary> | |
public class Bme280 : IDisposable | |
{ | |
public const byte DefaultI2cAddress = 0x77; | |
public const byte AlternativeI2cAddress = 0x76; | |
// Name of Registers used in the BME280 | |
private const byte BME280_DIG_T1_REG = 0x88; | |
private const byte BME280_DIG_T2_REG = 0x8A; | |
private const byte BME280_DIG_T3_REG = 0x8C; | |
private const byte BME280_DIG_P1_REG = 0x8E; | |
private const byte BME280_DIG_P2_REG = 0x90; | |
private const byte BME280_DIG_P3_REG = 0x92; | |
private const byte BME280_DIG_P4_REG = 0x94; | |
private const byte BME280_DIG_P5_REG = 0x96; | |
private const byte BME280_DIG_P6_REG = 0x98; | |
private const byte BME280_DIG_P7_REG = 0x9A; | |
private const byte BME280_DIG_P8_REG = 0x9C; | |
private const byte BME280_DIG_P9_REG = 0x9E; | |
private const byte BME280_DIG_H1_REG = 0xA1; | |
private const byte BME280_DIG_H2_REG = 0xE1; | |
private const byte BME280_DIG_H3_REG = 0xE3; | |
private const byte BME280_DIG_H4_REG = 0xE4; | |
private const byte BME280_DIG_H5_REG = 0xE5; | |
private const byte BME280_DIG_H6_REG = 0xE7; | |
private const byte BME280_REGISTER_CHIPID = 0xD0; | |
private const byte BME280_REGISTER_VERSION = 0xD1; | |
private const byte BME280_REGISTER_SOFTRESET = 0xE0; | |
private const byte BME280_REGISTER_CAL26 = 0xE1; | |
private const byte BME280_REGISTER_CONTROLHUMID = 0xF2; | |
private const byte BME280_REGISTER_CONTROL = 0xF4; | |
private const byte BME280_REGISTER_CONFIG = 0xF5; | |
private const byte BME280_REGISTER_PRESSUREDATA = 0xF7; | |
private const byte BME280_REGISTER_TEMPDATA = 0xFA; | |
private const byte BME280_REGISTER_HUMIDDATA = 0xFD; | |
// Structure to hold the calibration data that is | |
// programmed into the sensor in the factory | |
// during manufacture | |
private struct Bme280_Calibration_Data | |
{ | |
public ushort dig_T1; | |
public short dig_T2; | |
public short dig_T3; | |
public ushort dig_P1; | |
public short dig_P2; | |
public short dig_P3; | |
public short dig_P4; | |
public short dig_P5; | |
public short dig_P6; | |
public short dig_P7; | |
public short dig_P8; | |
public short dig_P9; | |
public byte dig_H1; | |
public short dig_H2; | |
public byte dig_H3; | |
public short dig_H4; | |
public short dig_H5; | |
public sbyte dig_H6; | |
}; | |
private enum ConnectionProtocol | |
{ | |
Spi, | |
I2c | |
} | |
private Bme280_Calibration_Data _calibrationData; | |
private float _temperature; | |
private float _humidity; | |
private float _pressure; | |
private int _tFine; | |
private readonly SpiConnectionSettings _spiSettings; | |
private SpiDevice _spiDevice; | |
private readonly I2cConnectionSettings _i2cSettings; | |
private I2cDevice _i2cDevice; | |
private GpioController _controller; | |
private readonly ConnectionProtocol _protocol; | |
private readonly int _csPin; | |
public Bme280(int chipSelectLine, SpiConnectionSettings spiSettings) | |
{ | |
_csPin = chipSelectLine; | |
_spiSettings = spiSettings ?? throw new ArgumentNullException(nameof(spiSettings)); | |
_protocol = ConnectionProtocol.Spi; | |
_controller = new GpioController(); | |
} | |
public Bme280(I2cConnectionSettings i2cSettings) | |
{ | |
_i2cSettings = i2cSettings ?? throw new ArgumentNullException(nameof(i2cSettings)); | |
_protocol = ConnectionProtocol.I2c; | |
} | |
public void Dispose() | |
{ | |
if (_spiDevice != null) | |
{ | |
_spiDevice.Dispose(); | |
_spiDevice = null; | |
} | |
if (_i2cDevice != null) | |
{ | |
_i2cDevice.Dispose(); | |
_i2cDevice = null; | |
} | |
} | |
/// <summary> | |
/// Gets or sets the temperature calibration offset in degrees celsius. | |
/// </summary> | |
public float TemperatureCalibrationOffset { get; set; } | |
public void ReadSensor() | |
{ | |
ReadTemperature(); | |
ReadHumidity(); | |
ReadPressure(); | |
} | |
public float SeaLevelPressureInHectopascals { get; set; } | |
public float TemperatureInCelsius => _temperature + TemperatureCalibrationOffset; | |
public float TemperatureInFahrenheit => TemperatureInCelsius * 1.8F + 32; | |
public float Humidity => _humidity; | |
public float PressureInPascals => _pressure; | |
public float PressureInHectopascals => _pressure / 100; | |
public float PressureInMillibars => PressureInHectopascals; | |
public float AltitudInFeet => AltitudeInMeters / 0.3048f; | |
public bool Begin() | |
{ | |
Dispose(); | |
switch (_protocol) | |
{ | |
case ConnectionProtocol.Spi: | |
_controller.OpenPin(_csPin, PinMode.Output); | |
_controller.Write(_csPin, PinValue.High); | |
_spiSettings.Mode = SpiMode.Mode0; | |
_spiSettings.DataBitLength = 8; // 1 byte | |
_spiDevice = new UnixSpiDevice(_spiSettings); | |
break; | |
case ConnectionProtocol.I2c: | |
_i2cDevice = new UnixI2cDevice(_i2cSettings); | |
break; | |
default: | |
throw new NotSupportedException($"Connection protocol '{_protocol}' not supported"); | |
} | |
byte chipId = Read8(BME280_REGISTER_CHIPID); | |
if (chipId != 0x60) | |
{ | |
Console.WriteLine($"chipId = '{chipId:X2}'"); | |
return false; | |
} | |
ReadSensorCoefficients(); | |
// Set Humidity oversampling to 1 | |
// Set before CONTROL (DS 5.4.3): | |
// "Changes to this register only became effective | |
// after a write operation to CONTROL register." | |
Write8(BME280_REGISTER_CONTROLHUMID, 0x01); | |
Write8(BME280_REGISTER_CONTROL, 0x3F); | |
return true; | |
} | |
public float AltitudeInMeters | |
{ | |
get | |
{ | |
// From BMP180 datasheet (page 16): | |
// http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf | |
float result = 44330f * (1f - (float)Math.Pow(PressureInHectopascals / SeaLevelPressureInHectopascals, 0.1903)); | |
return result; | |
} | |
} | |
private void ReadTemperature() | |
{ | |
int var1, var2; | |
uint adc_T = Read24(BME280_REGISTER_TEMPDATA); | |
adc_T >>= 4; | |
var1 = (((int)((adc_T >> 3) - (_calibrationData.dig_T1 << 1))) * | |
_calibrationData.dig_T2) >> 11; | |
var2 = ((((int)((adc_T >> 4) - ((int)_calibrationData.dig_T1)) * | |
(int)((adc_T >> 4) - ((int)_calibrationData.dig_T1))) >> 12) * | |
_calibrationData.dig_T3) >> 14; | |
_tFine = var1 + var2; | |
_temperature = (_tFine * 5 + 128) >> 8; | |
_temperature = _temperature / 100; | |
} | |
private void ReadPressure() | |
{ | |
long var1, var2, p; | |
uint adc_P = Read24(BME280_REGISTER_PRESSUREDATA); | |
adc_P >>= 4; | |
var1 = ((long)_tFine) - 128000; | |
var2 = var1 * var1 * _calibrationData.dig_P6; | |
var2 = var2 + ((var1 * _calibrationData.dig_P5) << 17); | |
var2 = var2 + (((long)_calibrationData.dig_P4) << 35); | |
var1 = ((var1 * var1 * _calibrationData.dig_P3) >> 8) + | |
((var1 * _calibrationData.dig_P2) << 12); | |
var1 = (((((long)1) << 47) + var1) * _calibrationData.dig_P1) >> 33; | |
if (var1 == 0) | |
{ | |
// Avoid divide by zero exception | |
_pressure = 0; | |
} | |
else | |
{ | |
p = 1048576 - adc_P; | |
p = (((p << 31) - var2) * 3125) / var1; | |
var1 = (_calibrationData.dig_P9 * (p >> 13) * (p >> 13)) >> 25; | |
var2 = (_calibrationData.dig_P8 * p) >> 19; | |
p = ((p + var1 + var2) >> 8) + (((long)_calibrationData.dig_P7) << 4); | |
_pressure = (float)p / 256; | |
} | |
} | |
private void ReadHumidity() | |
{ | |
int adc_H = Read16(BME280_REGISTER_HUMIDDATA); | |
int v_x1_u32r; | |
v_x1_u32r = _tFine - 76800; | |
v_x1_u32r = (((adc_H << 14) - (_calibrationData.dig_H4 << 20) - | |
(_calibrationData.dig_H5 * v_x1_u32r) + 16384) >> 15) * | |
(((((((v_x1_u32r * _calibrationData.dig_H6) >> 10) * | |
(((v_x1_u32r * _calibrationData.dig_H3) >> 11) + 32768)) >> 10) + | |
2097152) * _calibrationData.dig_H2 + 8192) >> 14); | |
v_x1_u32r = v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * | |
_calibrationData.dig_H1) >> 4); | |
v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; | |
v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; | |
float h = v_x1_u32r >> 12; | |
_humidity = h / 1024.0f; | |
} | |
/// <summary> | |
/// Read the values that are programmed into the sensor during manufacture | |
/// </summary> | |
private void ReadSensorCoefficients() | |
{ | |
_calibrationData.dig_T1 = Read16LittleEndian(BME280_DIG_T1_REG); | |
_calibrationData.dig_T2 = ReadS16LittleEndian(BME280_DIG_T2_REG); | |
_calibrationData.dig_T3 = ReadS16LittleEndian(BME280_DIG_T3_REG); | |
_calibrationData.dig_P1 = Read16LittleEndian(BME280_DIG_P1_REG); | |
_calibrationData.dig_P2 = ReadS16LittleEndian(BME280_DIG_P2_REG); | |
_calibrationData.dig_P3 = ReadS16LittleEndian(BME280_DIG_P3_REG); | |
_calibrationData.dig_P4 = ReadS16LittleEndian(BME280_DIG_P4_REG); | |
_calibrationData.dig_P5 = ReadS16LittleEndian(BME280_DIG_P5_REG); | |
_calibrationData.dig_P6 = ReadS16LittleEndian(BME280_DIG_P6_REG); | |
_calibrationData.dig_P7 = ReadS16LittleEndian(BME280_DIG_P7_REG); | |
_calibrationData.dig_P8 = ReadS16LittleEndian(BME280_DIG_P8_REG); | |
_calibrationData.dig_P9 = ReadS16LittleEndian(BME280_DIG_P9_REG); | |
_calibrationData.dig_H1 = Read8(BME280_DIG_H1_REG); | |
_calibrationData.dig_H2 = ReadS16LittleEndian(BME280_DIG_H2_REG); | |
_calibrationData.dig_H3 = Read8(BME280_DIG_H3_REG); | |
_calibrationData.dig_H4 = (short)((Read8(BME280_DIG_H4_REG) << 4) | (Read8(BME280_DIG_H4_REG + 1) & 0xF)); | |
_calibrationData.dig_H5 = (short)((Read8(BME280_DIG_H5_REG + 1) << 4) | (Read8(BME280_DIG_H5_REG) >> 4)); | |
_calibrationData.dig_H6 = (sbyte)Read8(BME280_DIG_H6_REG); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private void Write8(byte register, byte value) | |
{ | |
WriteRegister(register, value); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private byte Read8(byte register) | |
{ | |
Span<byte> readBuffer = ReadRegister(register, 1); | |
return readBuffer[0]; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private ushort Read16(byte register) | |
{ | |
Span<byte> readBuffer = ReadRegister(register, 2); | |
return BinaryPrimitives.ReadUInt16BigEndian(readBuffer); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private ushort Read16LittleEndian(byte register) | |
{ | |
Span<byte> readBuffer = ReadRegister(register, 2); | |
return BinaryPrimitives.ReadUInt16LittleEndian(readBuffer); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private short ReadS16(byte register) | |
{ | |
Span<byte> readBuffer = ReadRegister(register, 2); | |
return BinaryPrimitives.ReadInt16BigEndian(readBuffer); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private short ReadS16LittleEndian(byte register) | |
{ | |
Span<byte> readBuffer = ReadRegister(register, 2); | |
return BinaryPrimitives.ReadInt16LittleEndian(readBuffer); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private uint Read24(byte register) | |
{ | |
Span<byte> readBuffer = ReadRegister(register, 3); | |
Span<byte> newValue = stackalloc byte[4]; | |
readBuffer.CopyTo(newValue.Slice(1)); | |
newValue[0] = 0; | |
return BinaryPrimitives.ReadUInt32BigEndian(newValue); | |
} | |
private void WriteRegister(byte register, byte value) | |
{ | |
switch (_protocol) | |
{ | |
case ConnectionProtocol.Spi: | |
_controller.Write(_csPin, PinValue.Low); | |
// write, bit 7 low | |
_spiDevice.Write(new byte[] { (byte)(register & ~0x80), value }); | |
_controller.Write(_csPin, PinValue.High); | |
break; | |
case ConnectionProtocol.I2c: | |
_i2cDevice.Write(new byte[] { register, value }); | |
break; | |
default: | |
throw new NotSupportedException($"Connection protocol '{_protocol}' not supported"); | |
} | |
} | |
private Span<byte> ReadRegister(byte register, int byteCount) | |
{ | |
byte[] result = new byte[byteCount]; | |
switch (_protocol) | |
{ | |
case ConnectionProtocol.Spi: | |
_controller.Write(_csPin, PinValue.Low); | |
// read, bit 7 high | |
_spiDevice.WriteByte((byte)(register | 0x80)); | |
_spiDevice.Read(result); | |
_controller.Write(_csPin, PinValue.High); | |
break; | |
case ConnectionProtocol.I2c: | |
_i2cDevice.WriteByte(register); | |
_i2cDevice.Read(result); | |
break; | |
default: | |
throw new NotSupportedException($"Connection protocol '{_protocol}' not supported"); | |
} | |
return result; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Device.I2c; | |
using System.Device.I2c.Drivers; | |
using System.Diagnostics; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Iot.Device.Mcp23xxx; | |
namespace Sample | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
I2cConnectionSettings settings = new I2cConnectionSettings(busId: 1, Bme280.DefaultI2cAddress); | |
using (Bme280 sensor = new Bme280(settings)) | |
{ | |
sensor.SeaLevelPressureInHectopascals = 1013.25f; | |
bool ok = sensor.Begin(); | |
if (!ok) | |
{ | |
Console.WriteLine("Error initializing sensor"); | |
return; | |
} | |
using (Lcd2004 lcd = new Lcd2004(registerSelect: 22, enable: 17, data: new int[] { 25, 24, 23, 18 })) | |
{ | |
while (true) | |
{ | |
sensor.ReadSensor(); | |
lcd.Clear(); | |
lcd.Home(); | |
lcd.Write($"{DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()}"); | |
lcd.SetCursorPosition(0, 1); | |
lcd.Write($"Humidity: {sensor.Humidity:0.00}%"); | |
lcd.SetCursorPosition(0, 2); | |
lcd.Write($"Temperature: {sensor.TemperatureInFahrenheit:0.00}F"); | |
lcd.SetCursorPosition(0, 3); | |
lcd.Write($"Pressure: {sensor.PressureInHectopascals:0.00}hPa"); | |
Thread.Sleep(1_000); | |
} | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment