In this project we will use the Adafruit Starter Pack for Windows 10 IoT Core on Raspberry Pi 2 kit components to create a project that uses a sensor to read the temperature, pressure, humidity and altitude.
NOTE: there are two versions of the kit, one includes the BMP280 sensor the other the BME280. If you have the BMP280 you will want to go to the Weather Station v1 project https://www.hackster.io/windows-iot/weather-station
HardwareConnect the Raspberry Pi2 to the breadboard and the other components as per the Fritzing diagram in the "Schematics" section below.
SoftwareYou can download the code starting project from https://github.com/ms-iot/adafruitsample and we will lead you through the addition of the code needed to talk to the web service and get your pin on the map. What map?
Open up "Lesson_203v2\StartSolution\lesson_203v2.sln" and open the mainpage.xaml.cs file.
We have filled in a few methods as a starting point for you in this solution. If you want to jump ahead you can find a solution with all the code completed at: "Lesson_203v2\FullSolution\lesson_203v2.sln"
MainPage.xaml.csOpen the MainPage.xaml.cs file.
Add a reference to the sensor (BME280) class.
public sealed partial class MainPage : Page
{
// Create a new object for our sensor class
BME280 BME280;
Now we add code in the OnNavigatedTo method which will create a new BME280 object for the sensor and initialize the object.
// Create a new object for our sensor class
BME280 = new BME280Sensor();
//Initialize the sensor
await BME280.Initialize();
Next we add code to do the following:
- Create variables to store the temperature, pressure, humidity and altitude. Set them to 0.
- Create a variable for the pressure at sea level. The default value is 1013.25 hPa.
- Read the temperature, pressure, humidity and altitude 10 times and output the values to the debug console.
//Initialize them to 0.
float temp = 0;
float pressure = 0;
float altitude = 0;
float humidity = 0;
//Create a constant for pressure at sea level.
//This is based on your local sea level pressure (Unit: Hectopascal)
const float seaLevelPressure = 1022.00f;
//Read 10 samples of the data
for (int i = 0; i < 10; i++){
temp = await BME280.ReadTemperature();
pressure = await BME280.ReadPreasure();
altitude = await BME280.ReadAltitude(seaLevelPressure);
humidity = await BME280.ReadHumidity();
//Write the values to your debug console
Debug.WriteLine("Temperature: " + temp.ToString() + " deg C");
Debug.WriteLine("Humidity: " + humidity.ToString() + " %");
Debug.WriteLine("Pressure: " + pressure.ToString() + " Pa");
Debug.WriteLine("Altitude: " + altitude.ToString() + " m");
Debug.WriteLine("");
}
BME280.csOpen the BME280.cs file.
The first part of the code is making a list of the addresses of the different registers in the BME280. These values can be found in the BMP280 datasheet.
In the BME280 class, add the following after the enum for register addresses.
//String for the friendly name of the I2C bus
const string I2CControllerName = "I2C1";
//Create an I2C device
private I2cDevice bme280 = null;
//Create new calibration data for the sensor
BME280_CalibrationData CalibrationData;
//Variable to check if device is initialized
bool init = false;
Next add the following code in the Initialize function to:
//Method to initialize the BME280 sensor
public async Task Initialize()
{
Debug.WriteLine("BME280::Initialize");
try
{
//Instantiate the I2CConnectionSettings using the device address of the BME280
I2cConnectionSettings settings = new I2cConnectionSettings(BME280_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 BME280 I2C device using the device id of the I2CBus and the I2CConnectionSettings
bme280 = await I2cDevice.FromIdAsync(dis[0].Id, settings);
//Check if device was found
if (bme280 == null)
{
Debug.WriteLine("Device not found");
}
}
catch (Exception e)
{
Debug.WriteLine("Exception: " + e.Message + "\n" + e.StackTrace);
throw;
}
}
Add the following code in the Begin function to:
private async Task Begin()
{
Debug.WriteLine("BME280::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("BME280 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 ReadCoefficeints();
//Write control register
await WriteControlRegister();
//Write humidity control register
await WriteControlRegisterHumidity();
}
Add the following code to the next 2 functions to write to the control registers.
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;
}
Add the following code to the ReadUInt16_LittleEndian function to:
//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;
}
Add the following code to the ReadByte function to read 8-bit data from a register.
//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;
}
The next 3 functions are done for you. The information required to write these functions can be found in the datasheet.
ReadCoefficeints: This is the function where all the calibration data is read from the register addresses.
BMP280_compensate_T_double: In this function, the temperature in ºC is calculated using the compensation formula in the BMP280 datasheet.
BMP280_compensate_P_Int64: In this function, the pressure in Pa is calculated using the compensation formula in the BMP280 datasheet.
Add the following code to complete the ReadTemperature function.
public async Task<float> 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 the temperature as a float value
return (float)temp;
}
Repeat the same steps to complete the ReadPressure function.
public async Task<float> ReadPreasure()
{
//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 float value
return ((float)pres) / 256;
}
Finally complete the ReadAltitude function:
//Method to take the sea level pressure in Hectopascals(hPa) as a parameter and calculate the altitude using current pressure.
public async Task<float> ReadAltitude(float seaLevel)
{
//Make sure the I2C device is initialized
if (!init) await Begin();
//Read the pressure first
float pressure = await ReadPreasure();
//Convert the pressure to Hectopascals(hPa)
pressure /= 100;
//Calculate and return the altitude using the international barometric formula
return 44330.0f * (1.0f - (float)Math.Pow((pressure / seaLevel), 0.1903f));
}
Your project is now ready to deploy!
Expected OutputTemperature: 24.46189 deg C
Humidity: 54.372 %
Pressure: 99738.73 Pa
Altitude: 205.1726 m
Check out the next lesson here.
Comments