This is my fifth post on how to make some external electronic modules work with C# for the Raspberry Pi 3. Previously I’ve looked at servos, servo hats, webcams, and distance sensors, and this time I decided to address another common sensor – a chip that senses the temperature.

There are some very common temperature sensors such as the DHT11 or DHT22 which work well with the Arduino, as the Arduino is able to send signals with microsecond accuracy. The Raspberry Pi with Windows 10 IoT Core has the DHT11 as a device which is confirmed to work – however, this uses C++ instead of C#, and apparently is at the very edge of what the framework is capable of. I have tried to use the DHT11 without much consistent success, and decided I wanted to try the temperature sensing device, the MCP9808.

How does the MCP9808 work?

This sensor uses the I2C protocol, which is supported by Windows 10 IoT Core on the Pi 3. As there’s already extensive integration of this protocol in the framework, this makes coding significantly easier. The MCP9808 can be initialised using the slave address of 0x18, and the ambient temperature can be read back from two bytes of data after writing 0x05 to the device.

Connecting the MCP9808 to the Raspberry Pi

There are 4 pins on the MCP9808 temperature sensor that we need to use to get a basic temperature reading. I connected mine to the Pi using the pins specified below.

  • Supply Voltage – Pin 2
  • Ground – Pin 6
  • Serial Clock – Pin 5
  • Serial Data – Pin 3

Talking to the MCP9808 using C#

First of all, I initialised the MCP9808 device using the I2C commands in C# – remember the initialisation address to use is 0x18.

var advancedQueryString = I2cDevice.GetDeviceSelector();
 
var deviceInformations = await DeviceInformation.FindAllAsync(advancedQueryString);
 
if (!deviceInformations.Any())
{
    throw new Exception("No I2C controllers are connected.");
}
 
var settings = new I2cConnectionSettings(0x18);
 
settings.BusSpeed = I2cBusSpeed.FastMode;
 
var i2cDevice = await I2cDevice.FromIdAsync(deviceInformations[0].Id, settings);

Next, I request information about the ambient temperature back from the device by sending a message of 0x05. This gives me raw information back in a byte array.

In order to determine the temperature in Celcius, I need to process this raw information.The datasheet gives a formula for how to do this – it’s slightly complicated.

Sample code for this is shown below.

public float GetTemperature()
{
    byte[] readBuffer = new byte[2];
 
    this.i2cDevice.WriteRead(new byte[] { 0x05 }, readBuffer);
 
    byte upperByte = readBuffer[0];
    byte lowerByte = readBuffer[1];
 
    // we need to mask out the upper three boundary bits
    upperByte = Convert.ToByte(upperByte & 0x1F);
 
    var processedUpperByte = (float)upperByte;
    var processedLowerByte = (float)lowerByte;
 
    if (Convert.ToByte(upperByte & 0x10== 0x10)
    {
        // temperature < 0
        processedUpperByte = Convert.ToByte(upperByte & 0x0F);
        return 256 - (processedUpperByte * 16f + processedLowerByte / 16f);
    }
    else
    {
        // temperature > 0
        return processedUpperByte * 16f + processedLowerByte / 16f;
    }
}

And that’s pretty much all there is to it. I’ve extended this a little by creating my own custom project for abstract I2C devices, and I’ve created a custom project for the MCP9808 sensor – both of which are available on GitHub.  I can use this code much more simply now by including those projects and just calling the code below, which gives me the temperature every second.

var temperatureSensor = new MCP9808();
 
while (true)
{
    Debug.WriteLine(temperatureSensor.GetTemperature());
    Task.Delay(1000).Wait();
}

Next time, I’ll write more about creating an abstract library of I2C slave devices in C#, which I can re-use in future projects.