MCP9808をRaspberry Piで使う

この記事は、MIDI&Audioをは無関係です。

Adafruit MCP9808は高精度な温度計です。インターフェースはI2Cです。 このモジュールをRaspberry Pi Zeroに接続し、WiringPiライブラリを使って動作させます。

ハードウェア

物理的な接続は基本的に4本です。電源2本をI2Cのデータ(SDA)とクロック(SCL)を接続します。





ソフトウェア

プログラムをGithubに置きました。

// main.cpp
    #include <stdio.h>
    #include <stdint.h>
    
    #include "Wiringpi_MCP9808.h"
    #include <wiringPi.h>
    
    int main()
    {
      // Make sure the sensor is found, you can also pass in a different i2c
      // address with tempsensor.begin(0x19) for example, also can be left in blank for default address use
      // Also there is a table with all addres possible for this sensor, you can connect multiple sensors
      // to the same i2c bus, just configure each sensor with a different address and define multiple objects for that
      //  A2 A1 A0 address
      //  0  0  0   0x18  this is the default address
      //  0  0  1   0x19
      //  0  1  0   0x1A
      //  0  1  1   0x1B
      //  1  0  0   0x1C
      //  1  0  1   0x1D
      //  1  1  0   0x1E
      //  1  1  1   0x1F
      Wiringpi_MCP9808 mcp9808;
      bool result = mcp9808.init(MCP9808_I2CADDR_DEFAULT);
    
      if(result){
        fprintf(stderr,"Success to create the interface for MCP9808\n");
      } else {
        fprintf(stderr,"Fale to create the interface for MCP9808\n");
      }
      
      // sets the resolution mode of reading, the modes are defined in the table bellow:
      // Mode Resolution SampleTime
      //  0    0.5°C       30 ms
      //  1    0.25°C      65 ms
      //  2    0.125°C     130 ms
      //  3    0.0625°C    250 ms
      mcp9808.write8(MCP9808_REG_RESOLUTION, 3);	//Set Resolution
      fprintf(stderr,"Resolution = %d\n",mcp9808.read8(MCP9808_REG_RESOLUTION));	//Read Resolution
          
      while(1){
        mcp9808.wake_shutdown(true);
        fprintf(stderr,"%5.2f *C\n",mcp9808.readTempC());
        mcp9808.wake_shutdown(false);
        delay(1000);
      }
    }
/* 
 * Wiringpi_MCP9808.h
 * MCP9808 for using WiringPi
 * by MikataHara 
 * 2024.10.22
*/
   
   #include <wiringPiI2C.h>
   
   #ifndef _WIRINGPI_MCP9808_H
   #define _WIRINGPI_MCP9808_H
   
   #define MCP9808_I2CADDR_DEFAULT 0x18    ///< I2C address
   #define MCP9808_REG_CONFIG 0x01         ///< MCP9808 config register
   
   #define MCP9808_REG_CONFIG_SHUTDOWN 0x0100   ///< shutdown config
   #define MCP9808_REG_CONFIG_CRITLOCKED 0x0080 ///< critical trip lock
   #define MCP9808_REG_CONFIG_WINLOCKED 0x0040  ///< alarm window lock
   #define MCP9808_REG_CONFIG_INTCLR 0x0020     ///< interrupt clear
   #define MCP9808_REG_CONFIG_ALERTSTAT 0x0010  ///< alert output status
   #define MCP9808_REG_CONFIG_ALERTCTRL 0x0008  ///< alert output control
   #define MCP9808_REG_CONFIG_ALERTSEL 0x0004   ///< alert output select
   #define MCP9808_REG_CONFIG_ALERTPOL 0x0002   ///< alert output polarity
   #define MCP9808_REG_CONFIG_ALERTMODE 0x0001  ///< alert output mode
   
   #define MCP9808_REG_UPPER_TEMP 0x02   ///< upper alert boundary
   #define MCP9808_REG_LOWER_TEMP 0x03   ///< lower alert boundery
   #define MCP9808_REG_CRIT_TEMP 0x04    ///< critical temperature
   #define MCP9808_REG_AMBIENT_TEMP 0x05 ///< ambient temperature
   #define MCP9808_REG_MANUF_ID 0x06     ///< manufacture ID
   #define MCP9808_REG_DEVICE_ID 0x07    ///< device ID
   #define MCP9808_REG_RESOLUTION 0x08   ///< resolutin
   
   /*  Class for MCP9808 Temp Sensor */
    
   #define NAN -1
    
   class Wiringpi_MCP9808 {
   public:
       Wiringpi_MCP9808(){}
       bool init(uint8_t addr);
   
       void write16(uint8_t reg, uint16_t val);
       uint16_t read16(uint8_t reg);
       void write8(uint8_t reg, uint8_t val);
       uint8_t read8(uint8_t reg);
   
       void setResolution(uint8_t value);
       void wake_shutdown(bool sw);
   
   private:
       uint8_t _i2caddr;
   };
   
   #endif

(注)16ビットのRead/Writeの関数では、LSB Firstのuint16_tの値をMSB Firstに変換しています。 Raspberry Pi上はLSB First、I2CはMSB FIrstで扱う為、変換する必要があります。 swapBytesはChatGPTを参考に記載しています。

/* 
 * Wiringpi_MCP9808.cpp
 * Modified for Trinket by MikataHara 
 * 2024.10.19
*/

#include <stdint.h>
#include <stdio.h>
#include "Wiringpi_MCP9808.h"

// LSBとMSBを交換
uint16_t swapBytes(uint16_t num) {
  return ((num&0xFF00) >> 8) | ((num << 8)&0xFF00);
}

/*  Initialize */
bool Wiringpi_MCP9808::init(uint8_t addr) {
    uint16_t manuf_id, device_id;
    _i2caddr = wiringPiI2CSetup (addr);
    fprintf(stderr,"_i2caddr=%d\n",_i2caddr);
    if (_i2caddr > 0){
        manuf_id = read16(MCP9808_REG_MANUF_ID);
        device_id = read16(MCP9808_REG_DEVICE_ID);
        fprintf(stderr,"MCP9808_REG_MANUF_ID=%04x\n",manuf_id);
        fprintf(stderr,"MCP9808_REG_DEVICE_ID=%04x\n",device_id);
        if (manuf_id != 0x0054) return false;
        if (device_id != 0x0400) return false;
        write16(MCP9808_REG_CONFIG, 0x0);
    } else {
        return false;
    }
    return true;
}

void Wiringpi_MCP9808::write8(uint8_t reg, uint8_t val){
    wiringPiI2CWriteReg8 (_i2caddr, reg, val);
}

uint8_t Wiringpi_MCP9808::read8(uint8_t reg){
    uint16_t result =  wiringPiI2CReadReg8 (_i2caddr,reg);
    return result;
}

void Wiringpi_MCP9808::write16(uint8_t reg, uint16_t val){
  wiringPiI2CWriteReg16 (_i2caddr, reg, swapBytes(val));
}

uint16_t Wiringpi_MCP9808::read16(uint8_t reg){
  uint16_t result =  wiringPiI2CReadReg16 (_i2caddr,reg);
  return swapBytes(result);
}

/* Read Temperature */
float Wiringpi_MCP9808::readTempC() {
  float temp = NAN;
  uint16_t t = read16(MCP9808_REG_AMBIENT_TEMP);

  if (t != 0xFFFF) {
    temp = t & 0x0FFF;
    temp /= 16.0;
    if (t & 0x1000)
      temp -= 256;
  }
  return temp;
}

/* Wakeup and Shutdown */
/* true : wake */
/* false : shutdown */

void Wiringpi_MCP9808::wake_shutdown(bool sw) {
  uint16_t conf_shutdown;
  uint16_t conf_register = read16(MCP9808_REG_CONFIG);
  if (sw == true) {
    conf_shutdown = conf_register | MCP9808_REG_CONFIG_SHUTDOWN;
    write16(MCP9808_REG_CONFIG, conf_shutdown);
  }
  else if (sw == false) {
    conf_shutdown = conf_register & ~MCP9808_REG_CONFIG_SHUTDOWN;
    write16(MCP9808_REG_CONFIG, conf_shutdown);
  }
}