Is there a library for the MCP9808 temperature sensor?

Is there a library to connect the MCP9808 temperature sensor to an Electric Imp? It’s an I2C device and I haven’t used an I2C device before. If there is no library currently (and I didn’t see one), is there an example from elsewhere on how to use I2C on this device (or any other source of specific instructions)? Adafruit has instructions for connecting an Arduino to the MCP9808 here:


but it’s not clear to me if this is helpful in connecting an Electric Imp.

Thanks for any comments or suggestions.

Looking at the Adafruit library, it wouldn’t be hard to port it to Squirrel – it’s only about five functions. It’ll get you almost all of the way there - an imp-centric I2C primer will do the rest.

Check out our ‘I2C Explained’ article for guidance on using I2C, and our ‘Porting Arduino Code to the imp’ guide for assistance in converting the Adafruit code to Squirrel.

Here’s a very quick and dirty port, @Rfarmer. No guarantees that it will work: it was done using Adafruit’s code and the MCP9808 datasheet but no MCP9808, or much of an error check! That said, it’ll give you a starting point.

`// Class definition

class MCP9808 {
    // MCP9808 'constants'
    static MCP9808_I2CADDR_DEFAULT = 0x18;
    static MCP9808_REG_CONFIG = 0x01;

    static MCP9808_REG_CONFIG_SHUTDOWN = 0x0100;
    static MCP9808_REG_CONFIG_CRITLOCKED = 0x0080;
    static MCP9808_REG_CONFIG_WINLOCKED = 0x0040;
    static MCP9808_REG_CONFIG_INTCLR = 0x0020;
    static MCP9808_REG_CONFIG_ALERTSTAT = 0x0010;
    static MCP9808_REG_CONFIG_ALERTCTRL = 0x0008;
    static MCP9808_REG_CONFIG_ALERTSEL = 0x0004;
    static MCP9808_REG_CONFIG_ALERTPOL = 0x0002;
    static MCP9808_REG_CONFIG_ALERTMODE = 0x0001;

    static MCP9808_REG_UPPER_TEMP = 0x02;
    static MCP9808_REG_LOWER_TEMP = 0x03;
    static MCP9808_REG_CRIT_TEMP = 0x04;
    static MCP9808_REG_AMBIENT_TEMP = 0x05;
    static MCP9808_REG_MANUF_ID = 0x06;
    static MCP9808_REG_DEVICE_ID = 0x07;
    
    // Instance variables
    _i2cAddr = null;
    _i2cBus = null;

    // Constructor
    Constructor (bus = null, addr = 0x18) {
        // Default I2C address = 0x18

        if (addr != null) {
            _i2caddr = addr << 1;
        } else {
            throw("Supplied MCP9808 I2C address can't be null");
        }
        
        if (bus != null) {
            _i2cBus = bus;
        } else {
            throw("Supplied imp I2C bus can't be null");
        }
    }
 
    // Read temperature in Celsius
    function readTempC() {
        local t = read16(MCP9808_REG_AMBIENT_TEMP);
        
        if (t == null) {
            server.error("Bus mis-read");
            return 0;
        }
        
        local temp = (t & 0x0FFF) / 16.0;
        if (t & 0x1000) temp -= 256;
        return temp;
    }

    function shutdown_wake(sw_ID) {
        local conf_shutdown ;
        local conf_register = read16(MCP9808_REG_CONFIG);
    
        if (sw_ID == 1) {
            conf_shutdown = conf_register | MCP9808_REG_CONFIG_SHUTDOWN ;
            write16(MCP9808_REG_CONFIG, conf_shutdown);
        }
    
        if (sw_ID == 0) {
            conf_shutdown = conf_register ^ MCP9808_REG_CONFIG_SHUTDOWN ;
            write16(MCP9808_REG_CONFIG, conf_shutdown);
        }
    }
    
    // Write 16-bit value to bus
    function write16(reg, value) {
        _i2cBus.write(_i2cAddr, reg.tochar() + (value >> 8).tochar() + (value & 0xFF).tochar());
    }

    // Read 16-bit value from bus
    function read16(reg) {
        local val = _i2cBus.read(_i2cAddr, reg.tochar(), 2);
        if (val && val.len() == 2) {
            local result = val[0] << 8;
            result = result + val[1]; 
            return result;
        } else {
            return null;
        }
    }
}


// Program stub

hardware.i2c89.configure(CLOCK_SPEED_400_KHZ);
local mcp = MCP9808(hardware.i2c89);
server.log("Temperature is " + mcp.readTempC() + " Celsius");`

Thanks !!! I’ll try it.

Thanks, smittytone, for your suggested port from Arduino. However, it didn’t work. There were two compile errors (remove double parenthesis from class name, change Constructor() to constructor) and then a run time error of: ERROR: Bus mis-read; Temperature is 0 Celsius

So I just tried this:

hardware.i2c89.configure(CLOCK_SPEED_400_KHZ);
local temp=hardware.i2c89.read(0x18,0x05,2)
server.log("Temp= "+temp)

but these three lines of code give this error:
ERROR: bad parameters to i2c.read(target, subaddress, size)

Can you tell me what’s wrong with one or the other? Thanks in advance !!

Fixed the bad parameter error. This code:

hardware.i2c89.configure(CLOCK_SPEED_400_KHZ);
local addr=0x18; local laddr=addr<<1; local cmd=0x05
local temp=hardware.i2c89.read(laddr,cmd.tochar(),2)
local error=hardware.i2c89.readerror()

gives an error of -2 -"timeout while selecting transmit mode"
If anyone can give me some ideas of what might be wrong, I would appreciate it.

Sounds like the MCP9808 is not acknowledging the imp’s initial transmission.

Do you have the right address? I know the MCP9808 can support multiple addresses by connecting the A0, A1 and A2 pins to VCC - I’d double-check these, and that SDA and SCL are correctly wired.

You might also want to try a slower I2C clock speed. The datasheet lists 400kHz as max, so maybe try 100kHz while you get it to work and then bump it back up later.

Also check out ‘I²C Errors Explained

PS. Just ordered one of these so I can try this out myself.

Success !! I switched from imp pins 8/9 to imp pins 1/2 for I2C and it worked. It was the first time I had tried imp pins 8/9 so maybe something is wrong with them or I didn’t get them soldered correctly or something. In any event, the MCP9808 seems to be working although I will continue to check it out to make sure I understand what it’s doing. Thanks for your help !!

@Rfarmer, would be interested in seeing the final code as I want to use this sensor myself. Mind sharing?

Sure - here’s my function code. It’s quick and dirty, and doesn’t conform to any documentation conventions, but it’s short and has been working on my outside sensor for several weeks, and it seems to be reasonably accurate. No guarantees, of course.

function MCP9808(addr) { //i2c12 or i2c89
i2c<-hardware.i2c12; i2c.configure(CLOCK_SPEED_400_KHZ);
//local addr=0x18; default
local laddr=addr<<1; local cmd=0x05
local val=i2c.read(laddr,cmd.tochar(),2); local error=i2c.readerror();
if (error!=0) {server.log("MCP9808 Error= "+error)}
local result = val[0] << 8; result = result + val[1];
local temp = (result & 0x0FFF) / 16.0; if (result & 0x1000) temp -= 256;
return temp }

1 Like

Thanks ;), works like a charm!