The MCP9808 is a temperature sensor that uses the I2C communication standard to transmit a continuously polled temperature. It supports an alarm function and a standby mode to save on power when called for. In this explaination we perform a few python comands to get the temperature from the sensor.
I2C on MCP9808
Microchip uses 0x18 for its primary address on this board, with 3 pins available on the chip to alter the address for I2C address collisions. When calling any register in the sensor you should expect to write or get a two byte response (MSB first, LSB last). I used the breakout board from Adafruit 1528-1032-ND, which already has some pullup resistors.
Registers to note:
0x01 : 0000000X 00011111: X here is the standby bit in the configuration, it defaults to 0, which is continuous polling mode. The temperature register is always updated, with a drawback to power consumption. Setting this bit to 1 will stop polling and conserve power.
0x05 : AAASMMMM LLLLLLLL: This register contains the 12bit floating point temperature where “A” is the alert information, “S” is the sign bit, and “M” and “L” stand for MSB and LSB respectively. The final output will appear as MMMMLLLL.LLLL.
Note that for this explanation I leave the Sign bit and Alert bits unimplemented, but it is needed if you are going to expect to measure subzero temperatures.
Implementation on a Raspberry PI:
What is nice about using Raspberry Pi to interface with I2C is the interactivity of a Python terminal. I used a Raspberry Pi 4 with the I2C enabled on a Raspbian distribution. Make sure your GPIOs going to 3V3, GND, SDA, and SCL going to the correct pins on the MCP9808. Open a terminal in Raspbian and perform the following three commands:
python
from smbus import SMBus
bus = SMBus(1)
This will create a bus object we will interface to, which will in turn collect the I2C data we request. Next, ask the chip for its temperature data:
temp_binary = format(bus.read_word_data(0x18, 0x05),‘016b’)
The bus.read_byte_data(Address, Register)
will retrieve the data from the sensor, we use the enclosing format()
function to read the information as a binary number, we do this to make pulling out the two bytes trivial. Python likes to use int or float normally and this gets in the way with more than one byte return values. We will index these in [0:8][8:16]
order, with MSB existing at the latter half of the array.
Making Sense of the Output:
We can implement the following function to give us the Output correctly
def word_To_LSB_MSB(word):
return word[0:8], word[12 :16] // note that word indices [8,9,10,11] are not used in this example.
See this diagram for our desired output.
All that is left is putting the two back together and a little floating point math
LSB, MSB = word_To_LSB_MSB(temp_binary)
float(int(MSB + LSB,2)) / 16
Division by 16 will shift the result to MMMMLLLL.LLLL and give us decimal precision. Note that the result should come back in Celsius. In the case pictured above, the temperature we calculated was 23.3125 degrees Celsius from the binary floating number 00010111.0101
Saving power when you are done:
If you want to turn the device off when you are done simply set the standby bit for power a power saving mode:
bus.write_byte_data(0x18,0x01,0b00000001)
We write a byte since we only need to change the MSB in the MCP9809’s configuration register, by cutting the word short we don’t change what is in the LSB of the register.
You will have to power cycle the sensor or flush the register with a zero to return the MCP9808 to full operational functionality.
The datasheet from Microchip continues to be useful in application with this sensor, reference it regularly if you decide to implemnt the MCP9808. Note that some important features of the sensor have not been touched on, such as interrupts, the sign bit for negative temperatures, and standby operation.
Items used:
Raspberry Pi: 1690-RASPBERRYPI4B/4GB-ND
Breadboard wire in the female to male: 1568-1511-ND
Adafruit breakout board for the MCP9808: 1528-1032-ND