HTU21とSHT21

温度湿度センサーのTEConnectivity社のHTU21とSENSIRION社のSHT21は同じようにI2Cでシリアルナンバーが拾えます。アドレスも同じ0x40です。

コマンドと8バイト6バイトの構成はまったく同じです。

TEConnectivityのgithubのCソースからFreeBSD/mipsで動くmrubyのコードを作ってみました。

```ruby
#!/usr/local/bin/mruby
# HTU21D Serial number dump script
# based on https://github.com/TEConnectivity/HTU21D_Generic_C_Driver

def htu21_crc_check value, crc
  
  polynom = 0x988000   # x^8 + x^5 + x^4 + 1
  msb     = 0x800000
  mask    = 0xFF8000
  result  = value << 8   # Pad with zeros as specified in spec 

  while msb != 0x80
    if (result & msb) != 0
      result = ((result ^ polynom) & mask) | ( result & ~mask)
    end
    # Shift by one
    msb >>= 1
    mask >>= 1
    polynom >>=1
  end
  result == crc
end

begin

HTU21_ADDR = 0x40

HTU21_READ_SERIAL_FIRST_8BYTES_COMMAND = [0xFA, 0x0F]
HTU21_READ_SERIAL_LAST_6BYTES_COMMAND = [0xFC, 0xC9]

t = BsdIic.new(0)

rcv_data = t.read(HTU21_ADDR, 8, HTU21_READ_SERIAL_FIRST_8BYTES_COMMAND)
if rcv_data == nil
p "Read Error"
else
  rcv_data += t.read(HTU21_ADDR, 6, HTU21_READ_SERIAL_LAST_6BYTES_COMMAND)

  values = [0, 2, 4, 6]
  values.each do |n|
    if !htu21_crc_check rcv_data[n], rcv_data[n+1]
      raise
    end
  end

  values = [8, 11]
  values.each do |n|
    if !htu21_crc_check rcv_data[n] << 8 | rcv_data[n+1], rcv_data[n+2]
      raise
    end
  end

  serial_number = ""
  values = [0, 2, 4, 6, 8, 9, 11, 12]
  values.each do |n|
    serial_number += rcv_data[n].to_s(16).rjust(2, '0')
  end

  p serial_number

end

rescue
p "CRC Error"
end
```

HTU21ではちゃんとシリアルが拾えるのですが、SHT21はCRC Errorになります。

SENSIRIONのサイトにあるSHT21のソースを見るとCRCを無視していました。^ ^;

```c
//==============================================================================
u8t SHT2x_GetSerialNumber(u8t u8SerialNumber[])
//==============================================================================
{
  u8t  error=0;                          //error variable

  //Read from memory location 1
  I2c_StartCondition();
  error |= I2c_WriteByte (I2C_ADR_W);    //I2C address
  error |= I2c_WriteByte (0xFA);         //Command for readout on-chip memory
  error |= I2c_WriteByte (0x0F);         //on-chip memory address
  I2c_StartCondition();
  error |= I2c_WriteByte (I2C_ADR_R);    //I2C address
  u8SerialNumber[5] = I2c_ReadByte(ACK); //Read SNB_3
  I2c_ReadByte(ACK);                     //Read CRC SNB_3 (CRC is not analyzed)
  u8SerialNumber[4] = I2c_ReadByte(ACK); //Read SNB_2
  I2c_ReadByte(ACK);                     //Read CRC SNB_2 (CRC is not analyzed)
  u8SerialNumber[3] = I2c_ReadByte(ACK); //Read SNB_1
  I2c_ReadByte(ACK);                     //Read CRC SNB_1 (CRC is not analyzed)
  u8SerialNumber[2] = I2c_ReadByte(ACK); //Read SNB_0
  I2c_ReadByte(NO_ACK);                  //Read CRC SNB_0 (CRC is not analyzed)
  I2c_StopCondition();

  //Read from memory location 2
  I2c_StartCondition();
  error |= I2c_WriteByte (I2C_ADR_W);    //I2C address
  error |= I2c_WriteByte (0xFC);         //Command for readout on-chip memory
  error |= I2c_WriteByte (0xC9);         //on-chip memory address
  I2c_StartCondition();
  error |= I2c_WriteByte (I2C_ADR_R);    //I2C address
  u8SerialNumber[1] = I2c_ReadByte(ACK); //Read SNC_1
  u8SerialNumber[0] = I2c_ReadByte(ACK); //Read SNC_0
  I2c_ReadByte(ACK);                     //Read CRC SNC0/1 (CRC is not analyzed)
  u8SerialNumber[7] = I2c_ReadByte(ACK); //Read SNA_1
  u8SerialNumber[6] = I2c_ReadByte(ACK); //Read SNA_0
  I2c_ReadByte(NO_ACK);                  //Read CRC SNA0/1 (CRC is not analyzed)
  I2c_StopCondition();

  return error;
```

CRCの仕様は前半は1バイトごとで後半は2バイトごとと、とっても不思議で、微妙に並びも違うし、なんでこうなったんだろう。。。