湿度/温度センサー HTS221をArduinoから制御する(I2C)

# HTS221

<img width="237" alt="HTS221_pre.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/7653704f-11fe-7cd2-6989-faab05b670f9.png">

HTS221は、STMicroelectronics製のI2C通信に対応した湿度/温度センサーである。ここでは、Arduino向けのI2C通信のサンプルを作成する方法を掲載する。

# Document
- [Datasheet](http://www.st.com/web/en/resource/technical/document/datasheet/DM00116291.pdf)
- [テクニカルノート](http://www.st.com/st-web-ui/static/active/en/resource/technical/document/technical_note/DM00208001.pdf)

# Slave address

 0x5f

# Register address map

<img width="572" alt="hts221_addressnap.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/05d1a29e-055f-43f2-8e96-1d8423e1d295.png">


<img width="675" alt="hts221_register.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/e8c3f6a3-84b8-6a13-6f2d-44dd80972dee.png">

# Who_am_I

Who_am_I用に割り振られたレジスタアドレスは、0x0F。0xBCが返ってくると、センサーが認識されている事になる。

<img width="554" alt="who_am_i_resgiter.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/13e0cb51-75b3-df3d-e1fd-cf3033b15b92.png">

```cpp:arduino
#define HTS221_WHO_AM_I 0x0F
#define HTS221_DEVICE 0xBC

bool SearchDevice()
{
  byte device = 0x00;
  readI2c(HTS221_WHO_AM_I, 1, &device);

  if(device == HTS221_DEVICE){
      return true;
  } else{
      return false;
  }
}
```

# Configuration 

Configurationは、0x10のRegister Addressを使用している。Defaultは、1B。
Configurationでは、湿度/温度計測時のそれぞれの間隔を設定できる。間隔を細かくすれば、それだけノイズを減らせるが、電流量が増える。

<img width="550" alt="config_reg.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/1d8f4f1f-7903-fd88-fc84-10c847f386e6.png">

<img width="570" alt="config_bit.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/b38d1b98-d471-af6e-2618-878ec9a71af6.png">

Configurationでは、0-2bit目(AVGH0-AVGH1)がHumidityの分解度、3-5bit目(AVGT0-AVGT1)がTemperatureの分解度の設定用に使われる。

<img width="550" alt="config_table.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/6822eade-5aa6-5bb0-8180-03c5451afde1.png">

Defialtの1Bの値は、bitに表すと下記表のようになる。

<img width="608" alt="config_default.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/6238a028-1c5a-1e65-c951-b317811d77b9.png">

AVGTが011、AVGHが011となる。Temperatureのインターンバルの間隔が16, Humidityが32に設定されている。

一番いい精度に設定する場合は、下記のように111, 111をそろぞれ設定する。

```cpp:arduino
#define HTS221_AVGT_256 0b111
#define HTS221_AVGH_512 0b111

void Configuration()
{
   int AV_CONF = HTS221_AVGT_256>>3 | HTS221_AVGH_512;
   writeI2c(HTS221_AV_CONF, AV_CONF);
}
```
# Power onと使用可能かどうかの確認

センサーの稼働は、0x20のRegisterアドレスで設定できる。7bit目を1を設定すると、センサーが動き出し、0にするとセンサーが停止する。また、湿度/温度の周期設定もおこなう。

<img width="554" alt="ctrl_reg1_reg.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/1c360d4d-27e9-df5e-8a8f-aea2f6172cb4.png">

<img width="558" alt="ctrl_reg1_table.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/2afbc1d1-f802-ae7d-fba3-15267a972dc0.png">

<img width="583" alt="ctrl_reg1.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/3f2de35b-097a-d825-4c1f-63777ca67bb0.png">

Status Registerアドレスは0x27になっている。このアドレスの値をチェックする事で、それぞれのセンサーが利用可能になったかの確認が可能になる。

<img width="569" alt="status_reg.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/67e900a0-94da-515c-808a-394f345caa4a.png">

<img width="567" alt="status_bit.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/673cf2bf-ecc1-c2e2-0ce0-c9dc02b61f8c.png">

<img width="580" alt="status_table.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/30b2bc3d-fe84-41e8-5b90-6ec35cea2907.png">



```cpp:arduino

#define HTS221_CTRL_REG1 0x20
#define HTS221_STATUS_REG 0x27

#define HTS221_PD_ON 0b10000000
#define HTS221_BDU_ON 0b100
#define HTS221_ODR_ONESHOT 0b00
#define HTS221_ODR_1KHZ 0b01
#define HTS221_ODR_7KHZ 0b10
#define HTS221_ODR_12_5KHZ 0b11

void PowerOn()
{
  int CTRL_REG1 = HTS221_PD_ON;
  CTRL_REG1 |= HTS221_ODR_1KHZ;
  writeI2c(HTS221_CTRL_REG1, CTRL_REG1);

  bool available = false;
  while(!available){
    byte tmp;
    readI2c(HTS221_STATUS_REG, 1, &tmp);
     if((tmp & 0b1) != 0b1){
       // not available humidity
     } else if((tmp & 0b10) != 0b10){
       // not available temperature
     } else {
        available = true;
     }
  }
}
```

# HeaterとOnShotの設定

執筆中

<img width="568" alt="ctrl_reg2_bit.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/7107f8ac-542d-1a9d-16e3-ca42ba9e54ae.png">

<img width="583" alt="ctrl_reg2_table.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/1dfe0fa4-77f5-fab7-12ac-a0b24b35ecb3.png">

# 温度の計算式

<img width="436" alt="temp_calc.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/dcfd41b3-ce14-287c-1a85-cefbaa6551f7.png">

<img width="665" alt="temp_resgister.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/f1db3284-3efe-d67d-51be-f939b6109756.png">

下記がSTMicroelectronics社のテクニカルシートに記載のソースコードになる。

```cpp:STMicroelectronics社のサンプルコード
/**
* @brief Read HTS221 temperature output registers, and calculate temperature.
* @param Pointer to the returned temperature value that must be divided by 10 to get the value in ['C].
* @retval Error code [HTS221_ OK, HTS221_ ERROR].
*/
HTS221_Error_et HTS221_Get_Temperature(int16_ t *value)
{
  int16_t T0_out, T1_out, T_out, T0_degC_x8_u16, T1_degC_x8_u16;
  int16_t T0_degC, T1_degC;
  uint8_t buffer[4], tmp;
  uint32_t tmp32;

   /*1. Read from 0x32 & 0x33 registers the value of coefficients T0_degC_x8 and T1_degC_x8*/
  if(HTS221_ReadReg(HTS221_T0_DEGC_X8, 2, buffer))
    return HTS221_ERROR;
  
  /*2. Read from 0x35 register the value of the MSB bits of T1_degC and    T0_degC */
  if(HTS221_ReadReg(HTS221_T0_T1_DEGC_H2, 1, &tmp))
   return HTS221_ERROR;

  /*Calculate the T0_deg C and T1_deg C values*/
  T0_degC_x8_u16 = (((uint16_t)(tmp & 0x03)) << 8) | ((uint16_t)buffer[0]);
  T1_degC_x8_u16 = (((uint16_t)(tmp & 0x0C)) << 6) | ((uint16_ t)buffer[1]);
  T0_degC = T0_degC_x8_u16>>3;
  T1_degC = T1_degC_x8_u16>>3;

  /*3. Read from 0x3C & 0x3D registers the value of T0_OUT*/
  /*4. Read from 0x3E & 0x3F registers the value of T1_OUT*/
  if(HTS221_ReadReg(HTS221_T0_OUT_L, 4, buffer))
    return HTS221_ERROR;
  T0_out = (((uint16_t)buffer[1])<<8) | (uint16_t)buffer[0];
  T1_out = (((uint16_t)buffer[3])<<8) | (uint16_t)buffer[2];

  /* 5.Read from 0x2A & 0x2B registers the value T_OUT (ADC _OUT).*/
  if(HTS221_ReadReg(HTS221_TEMP_OUT_L_REG, 2, buffer))
   return HTS221_ERROR;
  T_out = (((uint16_ t)buffer[1])<<8) | (uint16_t)buffer[0];

  /* 6. Compute the Temperature value by linea r interpolation*/
  tmp32 = ((uint32_t)(T_out - T0_out)) * ((uint32_t)(T1_degC - T0_degC)*10);
  *value = tmp32 /(T1_out - T0_out) + T0_degC*10;
  
  return HTS221_OK;
}
```

これを参考にArduinoのコードを作成する。

```cpp:arduino
// Temperature calibration registers
#define HTS221_T0_DEGC_X8 0x32
#define HTS221_T1_DEGC_X8 0x33
#define HTS221_T0_T1_DEGC_H2 0x35
#define HTS221_T0_OUT_L 0x3C
#define HTS221_T0_OUT_H 0x3D
#define HTS221_T1_OUT_L 0x3E
#define HTS221_T1_OUT_H 0x3F
#define HTS221_TEMP_OUT_L 0x2A
#define HTS221_TEMP_OUT_H 0x2B

int GetTemperature()
{
  int16_t T0_out, T1_out, T_out, T0_degC_x8_u16, T1_degC_x8_u16;
  int16_t T0_degC, T1_degC;
  uint8_t buffer[4];
  uint32_t tmp32;

  // T0_degC_x8,T1_degC_x8の値の取得
  readI2c(HTS221_T0_DEGC_X8, 1, &buffer[0]);
  readI2c(HTS221_T1_DEGC_X8, 1, &buffer[1]);
  readI2c(HTS221_T0_T1_DEGC_H2, 1, &buffer[2]);
  
  // T0_degC_x8,T1_degC_x8のH,Lを結合
  T0_degC_x8_u16 = (((uint16_t)(buffer[2] & 0x03)) << 8) | ((uint16_t)buffer[0]);
  T1_degC_x8_u16 = (((uint16_t)(buffer[2] & 0x0C)) << 6) | ((uint16_t)buffer[1]);

  // T0_T1_DEGC_H2の値の取得
  T0_degC = T0_degC_x8_u16>>3;
  T1_degC = T1_degC_x8_u16>>3;

  // T0_OUT_L, T0_OUT_H, T1_OUT_L, T1_OUT_Hの取得
  readI2c(HTS221_T0_OUT_L, 1, &buffer[0]);
  readI2c(HTS221_T0_OUT_H, 1, &buffer[1]);
  readI2c(HTS221_T1_OUT_L, 1, &buffer[2]);
  readI2c(HTS221_T1_OUT_H, 1, &buffer[3]);

  // L,Hの結合
  T0_out = (((uint16_t)buffer[1])<<8) | (uint16_t)buffer[0];
  T1_out = (((uint16_t)buffer[3])<<8) | (uint16_t)buffer[2];

  // TEMP_OUT_L, TEMP_OUT_Hの取得
  readI2c(HTS221_TEMP_OUT_L, 1, &buffer[0]);
  readI2c(HTS221_TEMP_OUT_H, 1, &buffer[1]);

  // L,Hの結合
  T_out = (((uint16_t)buffer[1])<<8) | (uint16_t)buffer[0];

  tmp32 = ((uint32_t)(T_out - T0_out)) * ((uint32_t)(T1_degC - T0_degC)*10);
  return tmp32 /(T1_out - T0_out) + T0_degC*10;
}
```

# 湿度の計算式
湿度の計算は、下記式により導き出される。

<img width="442" alt="humidity_calc.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/0bc25234-e97a-1b9a-94ce-64d36fc2efac.png">

それぞれの値は、下記のCalibration用のレジスタから取得できる。

<img width="672" alt="humidity_map.png" src="https://qiita-image-store.s3.amazonaws.com/0/63450/42cccd60-abaa-44bd-d863-b8104ebaa42b.png">

下記がSTMicroelectronics社のテクニカルシート記載のサンプルコードになる。

```cpp:STMicroelectronics社のサンプルコード
/**
* @brief Read HTS221 Humidity output registers, and calculate humidity.
* @param Pointer to the returned humidity value that must be divided by 10 to get the value in [%].
* @retval Error code [HTS221_ OK, HTS221_ ERROR].
*/
HTS221_ Error_et HTS221_ Get_Humidity(uint16_t* value)
{
  int16_t H0_T0_ou t, H1_T0_ou t, H_T_ou t;
  int16_t H0_rh, H1_rh;
  uint8_t buffer[2];
  uint32_t tmp;

  /* 1. Read H0_rH and H1_rH coefficients*/
  if(HTS221_ReadReg(HTS221_H0_RH_X2, 2, buffer))
  H0_rh = buffer[0]>>1;
  H1_rh = buffer[1]>>1;

  /*2. Read H0_T0_OUT */
  if(HTS221_ ReadReg(HTS221_H0_T0_OUT_L, 2, buffer))
    return HTS221_ ERROR;
  H0_T0_out = (((uint16_t)buffer[1])<<8) | (uint16_t)buffer[0];

  /*3. Read H1_T0_OUT */
  if(HTS221_ReadReg(HTS221_H1_T0_OUT_L, 2, buffer))
    return HTS221_ERROR;
  H1_T0_out = (((uint16_t)buffer[1])<<8) | (uint16_ t)buffer[0];

  /*4. Read H_T_OUT */
  if(HTS221_ReadReg(HTS221_HR_OUT_L_REG, 2, buffer))
    return HTS221_ERROR;
  H_T_out = (((uint16_t)buffer[1])<<8) | (uint16_t)buffer[0];

  /*5. Compute the RH [%] value by linear interpolation */
    tmp = ((uint32_t)(H_T_out - H0_T0_out)) * ((uint32_t)(H1_rh - H0_rh)*10);
   *value = tmp/(H1_T0_out - H0_T0_out) + H0_rh*10;

  /* Saturation condition*/
  if(*value>1000 ) *value = 1000
    return HTS221_ OK;
}
```

これを参考にArduinoのコードを作成する。

```cpp:Arduino
// Humidity calibration registers
#define HTS221_HR_OUT_L_REG 0x28
#define HTS221_HR_OUT_H_REG 0x29
#define HTS221_H0_RH_X2 0x30
#define HTS221_H1_RH_X2 0x31
#define HTS221_H0_T0_OUT_L 0x36
#define HTS221_H0_T0_OUT_H 0x37
#define HTS221_H1_T0_OUT_L 0x3A
#define HTS221_H1_T0_OUT_H 0x3B

int GetHumidity()
{
  uint16_t H0_T0_out, H1_T0_out, H_T_out;
  uint16_t H0_rh, H1_rh;
  uint8_t buffer[2];
  uint32_t humidity;

  // H0_rH, H1_rHの読み込み
  readI2c(HTS221_H0_RH_X2, 1, &buffer[0]);
  readI2c(HTS221_H1_RH_X2, 1, &buffer[1]);
  // 取れてくる値がX2なので、1/2にする
  H0_rh = (buffer[0]&0xff)>>1;
  H1_rh = (buffer[1]&0xff)>>1;
  
  // H0_T0_OUTの読み込み
  readI2c(HTS221_H0_T0_OUT_L, 1, &buffer[0]);
  readI2c(HTS221_H0_T0_OUT_H, 1, &buffer[1]);
  // H,Lを結合する
  H0_T0_out = (((uint16_t)buffer[1])<<8) | (uint16_t)buffer[0];
  
  // H1_T0_OUT 読み込み
  readI2c(HTS221_H1_T0_OUT_L, 1, &buffer[0]);
  readI2c(HTS221_H1_T0_OUT_H, 1, &buffer[1]);  
  // H, Lを結合する
  H1_T0_out = (((uint16_t)buffer[1])<<8) | (uint16_t)buffer[0];
  
  // Read H_T_OUTの読み込み
  readI2c(HTS221_HR_OUT_L_REG, 1, &buffer[0]);
  readI2c(HTS221_HR_OUT_H_REG, 1, &buffer[1]);  
  // H,Lを結合する
  H_T_out = buffer[1]<<8 | buffer[0];

  // RHの計算
  double rh_dx = ((int16_t)H1_rh - (int16_t)H0_rh)*10;
  humidity = ((int16_t)H_T_out - (int16_t)H0_T0_out)*rh_dx / ((int16_t)H1_T0_out - (int16_t)H0_T0_out);
  humidity = (int16_t)(humidity + H0_rh*10);
  
  if(humidity > 1000){
    humidity = 1000;
  }
  return humidity;
}
```

# I2Cのアクセス用コード

```cpp:Arduino
#define HTS221_SLAVE_ADDRESS 0x5F

// I2Cへの書き込み
void writeI2c(byte register_addr, byte value) {
  Wire.beginTransmission(HTS221_SLAVE_ADDRESS);  
  Wire.write(register_addr);         
  Wire.write(value);                 
  Wire.endTransmission();        
}

// I2Cへの読み込み
void readI2c(byte register_addr, int num, byte *buf) {
  Wire.beginTransmission(HTS221_SLAVE_ADDRESS); 
  Wire.write(register_addr);           
  Wire.endTransmission(false);         

  //Wire.beginTransmission(DEVICE_ADDR); 
  Wire.requestFrom(HTS221_SLAVE_ADDRESS, num);  

  int i = 0;
  while (Wire.available())
  {
    buf[i] = Wire.read(); 
    i++;   
  }
  //Wire.endTransmission();         
}
```