Monday, 7 May 2018

Using an MCP9808 digital temperature sensor with the BBC micro:bit

Introduction
MakeCode for the BBC micro:bit has a Temperature block that provides a temperature reading in °C. There are two problems with this:
  1. The block infers the temperature from the temperature of the silicon die on the main CPU. This tends to overestimate the temperature due to self-heating of the CPU and the degree of self-heating depends on the load on the processor. Typically, the reading will be 3 degrees Celsius higher than the ambient temperature.
  2. You might, in any case, want to monitor the temperature somewhere other than where the micro:bit is situated (e.g. outside).
The way around this is to connect a separate temperature sensor to the micro:bit. Several tutorials exist for doing this, but the ones I have seen are all for analogue devices such as the TMP36 where the voltage output by the sensor is proportional to the temperature. These are relatively low-precision devices (accurate to ±1°C at +25°C).

I wanted to use a more accurate, digital temperature sensor and found the Microchip MCP9808, which has a typical accuracy ±0.25°C from -40°C to +125°C. This uses the I2C bus and works over a 2.7V ~ 5.5V logic level voltage range. Another possibility would be the Maxim/Dallas DS18B20, which uses a "one-wire protocol" but it takes at least 0.75 secs for each conversion, plus I haven't found out an easy way of using this with the micro:bit. In contrast, the MCP9808 is very easy to use, has a conversion time of only 250ms at maximum resolution and doesn't interfere with any other devices. The easiest way to use it with the micro:bit is to buy a breakout board that exposes its connections via breadboard-friendly pins. Adafruit makes a popular MCP9808 breakout board but I just went for a cheap Chinese board (costing around £2.50) as they do the same job.


The I2C protocol is a serial protocol that allows different data to be sent to different devices along a "bus". Each device must have its own address on the bus. The sensor has a default I2C address of 0x18 but this can be changed to one of seven other addresses by connecting one of more of the additional connections A0, A1, A2 to VCC, via the inbuilt resistors. This would allow you to have up to eight temperature sensors running from the same micro:bit, and controlled separately. See my previous blog posts on using I2C devices with the micro:bit.

Connecting the sensor to the micro:bit
Although there are eight pins, we need just four connections to get going. 

VCC to 3v on the micro:bit, GND to GND, SCL to pin 19 on the micro:bit and SDA to pin 20. To access pins 19 and 20, we need an edge connector breakout board for the micro:bit such as the bread:bit edge connector board from Proto-Pic.

The other four pins on the MCP9808 are for changing its I2C address and a pin that can be used as a trigger if, for example, certain temperature thresholds are reached. 

The micro:bit already has internal pullup resistors on the I2C lines so no other components are needed if you just want to measure temperature and are happy with the default I2C address.

Configuration
By default, the sensor's I2C address is 0x18, its resolution is +0.0625°C and its conversion time is 250ms. For the purposes of this guide, we will leave these defaults as they are.

Micropython code
You'll need the excellent Mu editor for this, and you must also have REPL set up because we will be sending the temperature readings from the micro:bit via the programming cable to the REPL window in Mu. If you have  Windows PC, you'll need to install the driver for this (download it from the Mu website).

The micro:bit implementation of I2C in micropython is slightly different from other boards. The I2C bus does not need to be separately initialised unless the default parameters are unsuitable for the device you wish to connect. As with some other I2C devices, the required configuration registers are written to, then data is read from the bus. In our case, we tell the MCP9808 what function we require by an "i2c.write" to the relevant register address (in this case 0x05 - the ambient temperature register). Then we "i2c.read" a specified number of bytes.

The datasheet for the MCP9808 explains that the temperature is stored as two bytes (one word). The digital word is loaded to a 16-bit read-only ambient temperature register that contains 13-bit temperature data in two’s complement format. The other 3 bits (i.e. bits 7, 6 and 5 of the first byte) contain the alert temperature settings. Bit 4 of the first byte is the sign (i.e. positive or negative Celsius value). If bit 4 is 1, the temperature is negative. This is equivalent to saying that negative temperatures will be represented as 256 plus the numeric value of bits 3 to 0 of the first byte. The second byte of the word contains the 8 least significant bits of the ambient temperature.

Before you flash the code to the micro:bit, remember to click the REPL button so that you can see the data coming back from the micro:bit.


from microbit import *
sleep(1000)
while True:
    i2c.write(0x18,bytearray([0x05])) # select ambient temp. register
    sleep(100) #  small pause needed to allow the device to update
    t_amb=i2c.read(0x18,2) # read two bytes from the ambient temp. register
    t_hi = (t_amb[0] & 0x0f) << 4 # mask upper 4 bits (alarm settings)
    t_lo = t_amb[1] / 16          # lower 8 bits (LSB)
    if t_amb[0] & 0x10 == 0x10: # take twos complement for -ve readings
        temp = (t_hi + t_lo) - 256.0
    else:
        temp = t_hi + t_lo
    print('%.2f' % round(temp,2))  # round and print to 2 places
    sleep(500)

In my next post, I will describe how to connect a 3V LCD screen instead of sending the temperature readings to the REPL. Then you will have a stand-alone digital thermometer which can be used, for example, as part of a weather station project, data logging, or for environmental control.

No comments:

Post a comment