using System; using System.Diagnostics; using System.Threading.Tasks; using Windows.Devices.Enumeration; using Windows.Devices.I2c; namespace TempPressure { // The source code of this class was borrowed (and slightly modified) // from: https://github.com/ms-iot/adafruitsample/blob/master/Lesson_203V2/FullSolution/BME280.cs public class BMP280_CalibrationData { //BMP280 Registers public UInt16 dig_T1 { get; set; } public Int16 dig_T2 { get; set; } public Int16 dig_T3 { get; set; } public UInt16 dig_P1 { get; set; } public Int16 dig_P2 { get; set; } public Int16 dig_P3 { get; set; } public Int16 dig_P4 { get; set; } public Int16 dig_P5 { get; set; } public Int16 dig_P6 { get; set; } public Int16 dig_P7 { get; set; } public Int16 dig_P8 { get; set; } public Int16 dig_P9 { get; set; } } public class BMP280 { //The BMP280 register addresses according the the datasheet: http://www.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf const byte BMP280_Address = 0x77; const byte BMP280_Signature = 0x58; enum eRegisters : byte { BMP280_REGISTER_DIG_T1 = 0x88, BMP280_REGISTER_DIG_T2 = 0x8A, BMP280_REGISTER_DIG_T3 = 0x8C, BMP280_REGISTER_DIG_P1 = 0x8E, BMP280_REGISTER_DIG_P2 = 0x90, BMP280_REGISTER_DIG_P3 = 0x92, BMP280_REGISTER_DIG_P4 = 0x94, BMP280_REGISTER_DIG_P5 = 0x96, BMP280_REGISTER_DIG_P6 = 0x98, BMP280_REGISTER_DIG_P7 = 0x9A, BMP280_REGISTER_DIG_P8 = 0x9C, BMP280_REGISTER_DIG_P9 = 0x9E, BMP280_REGISTER_CHIPID = 0xD0, BMP280_REGISTER_VERSION = 0xD1, BMP280_REGISTER_SOFTRESET = 0xE0, BMP280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0 BMP280_REGISTER_CONTROLHUMID = 0xF2, BMP280_REGISTER_CONTROL = 0xF4, BMP280_REGISTER_CONFIG = 0xF5, BMP280_REGISTER_PRESSUREDATA_MSB = 0xF7, BMP280_REGISTER_PRESSUREDATA_LSB = 0xF8, BMP280_REGISTER_PRESSUREDATA_XLSB = 0xF9, // bits <7:4> BMP280_REGISTER_TEMPDATA_MSB = 0xFA, BMP280_REGISTER_TEMPDATA_LSB = 0xFB, BMP280_REGISTER_TEMPDATA_XLSB = 0xFC, // bits <7:4> BMP280_REGISTER_HUMIDDATA_MSB = 0xFD, BMP280_REGISTER_HUMIDDATA_LSB = 0xFE, }; //String for the friendly name of the I2C bus const string I2CControllerName = "I2C1"; //Create an I2C device private I2cDevice bmp280 = null; //Create new calibration data for the sensor BMP280_CalibrationData CalibrationData; //Variable to check if device is initialized bool init = false; //Method to initialize the BMP280 sensor public async Task Initialize() { Debug.WriteLine("BMP280::Initialize"); try { //Instantiate the I2CConnectionSettings using the device address of the BMP280 I2cConnectionSettings settings = new I2cConnectionSettings(BMP280_Address); //Set the I2C bus speed of connection to fast mode settings.BusSpeed = I2cBusSpeed.FastMode; //Use the I2CBus device selector to create an advanced query syntax string string aqs = I2cDevice.GetDeviceSelector(I2CControllerName); //Use the Windows.Devices.Enumeration.DeviceInformation class to create a collection using the advanced query syntax string DeviceInformationCollection dis = await DeviceInformation.FindAllAsync(aqs); //Instantiate the the BMP280 I2C device using the device id of the I2CBus and the I2CConnectionSettings bmp280 = await I2cDevice.FromIdAsync(dis[0].Id, settings); //Check if device was found if (bmp280 == null) { Debug.WriteLine("Device not found"); } } catch (Exception e) { Debug.WriteLine("Exception: " + e.Message + "\n" + e.StackTrace); throw; } } private async Task Begin() { Debug.WriteLine("BMP280::Begin"); byte[] WriteBuffer = new byte[] { (byte)eRegisters.BMP280_REGISTER_CHIPID }; byte[] ReadBuffer = new byte[] { 0xFF }; //Read the device signature bmp280.WriteRead(WriteBuffer, ReadBuffer); Debug.WriteLine("BMP280 Signature: " + ReadBuffer[0].ToString()); //Verify the device signature if (ReadBuffer[0] != BMP280_Signature) { Debug.WriteLine("BMP280::Begin Signature Mismatch."); return; } //Set the initalize variable to true init = true; //Read the coefficients table CalibrationData = await ReadCoefficients(); //Write control register await WriteControlRegister(); //Write humidity control register await WriteControlRegisterHumidity(); } //Method to write 0x03 to the humidity control register private async Task WriteControlRegisterHumidity() { byte[] WriteBuffer = new byte[] { (byte)eRegisters.BMP280_REGISTER_CONTROLHUMID, 0x03 }; bmp280.Write(WriteBuffer); await Task.Delay(1); return; } //Method to write 0x3F to the control register private async Task WriteControlRegister() { byte[] WriteBuffer = new byte[] { (byte)eRegisters.BMP280_REGISTER_CONTROL, 0x3F }; bmp280.Write(WriteBuffer); await Task.Delay(1); return; } //Method to read a 16-bit value from a register and return it in little endian format private UInt16 ReadUInt16_LittleEndian(byte register) { UInt16 value = 0; byte[] writeBuffer = new byte[] { 0x00 }; byte[] readBuffer = new byte[] { 0x00, 0x00 }; writeBuffer[0] = register; bmp280.WriteRead(writeBuffer, readBuffer); int h = readBuffer[1] << 8; int l = readBuffer[0]; value = (UInt16)(h + l); return value; } //Method to read an 8-bit value from a register private byte ReadByte(byte register) { byte value = 0; byte[] writeBuffer = new byte[] { 0x00 }; byte[] readBuffer = new byte[] { 0x00 }; writeBuffer[0] = register; bmp280.WriteRead(writeBuffer, readBuffer); value = readBuffer[0]; return value; } //Method to read the caliberation data from the registers private async Task ReadCoefficients() { // 16 bit calibration data is stored as Little Endian, the helper method will do the byte swap. CalibrationData = new BMP280_CalibrationData(); // Read temperature calibration data CalibrationData.dig_T1 = ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_T1); CalibrationData.dig_T2 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_T2); CalibrationData.dig_T3 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_T3); // Read presure calibration data CalibrationData.dig_P1 = ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P1); CalibrationData.dig_P2 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P2); CalibrationData.dig_P3 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P3); CalibrationData.dig_P4 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P4); CalibrationData.dig_P5 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P5); CalibrationData.dig_P6 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P6); CalibrationData.dig_P7 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P7); CalibrationData.dig_P8 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P8); CalibrationData.dig_P9 = (Int16)ReadUInt16_LittleEndian((byte)eRegisters.BMP280_REGISTER_DIG_P9); await Task.Delay(1); return CalibrationData; } //t_fine carries fine temperature as global value Int32 t_fine = Int32.MinValue; //Method to return the temperature in DegC. Resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC. private double BMP280_compensate_T_double(Int32 adc_T) { double var1, var2, T; //The temperature is calculated using the compensation formula in the BMP280 datasheet var1 = ((adc_T / 16384.0) - (CalibrationData.dig_T1 / 1024.0)) * CalibrationData.dig_T2; var2 = ((adc_T / 131072.0) - (CalibrationData.dig_T1 / 8192.0)) * CalibrationData.dig_T3; t_fine = (Int32)(var1 + var2); T = (var1 + var2) / 5120.0; return T; } //Method to 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 private Int64 BMP280_compensate_P_Int64(Int32 adc_P) { Int64 var1, var2, p; //The pressure is calculated using the compensation formula in the BMP280 datasheet var1 = t_fine - 128000; var2 = var1 * var1 * (Int64)CalibrationData.dig_P6; var2 = var2 + ((var1 * (Int64)CalibrationData.dig_P5) << 17); var2 = var2 + ((Int64)CalibrationData.dig_P4 << 35); var1 = ((var1 * var1 * (Int64)CalibrationData.dig_P3) >> 8) + ((var1 * (Int64)CalibrationData.dig_P2) << 12); var1 = (((((Int64)1 << 47) + var1)) * (Int64)CalibrationData.dig_P1) >> 33; if (var1 == 0) { Debug.WriteLine("BMP280_compensate_P_Int64 Jump out to avoid / 0"); return 0; //Avoid exception caused by division by zero } //Perform calibration operations as per datasheet: http://www.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf p = 1048576 - adc_P; p = (((p << 31) - var2) * 3125) / var1; var1 = ((Int64)CalibrationData.dig_P9 * (p >> 13) * (p >> 13)) >> 25; var2 = ((Int64)CalibrationData.dig_P8 * p) >> 19; p = ((p + var1 + var2) >> 8) + ((Int64)CalibrationData.dig_P7 << 4); return p; } public async Task ReadTemperature() { //Make sure the I2C device is initialized if (!init) await Begin(); //Read the MSB, LSB and bits 7:4 (XLSB) of the temperature from the BMP280 registers byte tmsb = ReadByte((byte)eRegisters.BMP280_REGISTER_TEMPDATA_MSB); byte tlsb = ReadByte((byte)eRegisters.BMP280_REGISTER_TEMPDATA_LSB); byte txlsb = ReadByte((byte)eRegisters.BMP280_REGISTER_TEMPDATA_XLSB); // bits 7:4 //Combine the values into a 32-bit integer Int32 t = (tmsb << 12) + (tlsb << 4) + (txlsb >> 4); //Convert the raw value to the temperature in degC double temp = BMP280_compensate_T_double(t); return temp; } public async Task ReadPressure() { //Make sure the I2C device is initialized if (!init) await Begin(); //Read the temperature first to load the t_fine value for compensation if (t_fine == Int32.MinValue) { await ReadTemperature(); } //Read the MSB, LSB and bits 7:4 (XLSB) of the pressure from the BMP280 registers byte tmsb = ReadByte((byte)eRegisters.BMP280_REGISTER_PRESSUREDATA_MSB); byte tlsb = ReadByte((byte)eRegisters.BMP280_REGISTER_PRESSUREDATA_LSB); byte txlsb = ReadByte((byte)eRegisters.BMP280_REGISTER_PRESSUREDATA_XLSB); // bits 7:4 //Combine the values into a 32-bit integer Int32 t = (tmsb << 12) + (tlsb << 4) + (txlsb >> 4); //Convert the raw value to the pressure in Pa Int64 pres = BMP280_compensate_P_Int64(t); //Return the temperature as a double value return ((double)pres) / 256; } //Method to take the sea level pressure in Hectopascals(hPa) as a parameter and calculate the altitude using current pressure. public async Task ReadAltitude(double seaLevel) { //Make sure the I2C device is initialized if (!init) await Begin(); //Read the pressure first double pressure = await ReadPressure(); //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 / seaLevel), 0.1903)); } } }