温度センサDS18B20の調査

# はじめに
データシート

https://akizukidenshi.com/catalog/g/g105276/

通信方式は　1-Wire bus方式です。

https://www.marubun.co.jp/technicalsquare/9840/

購入した温度センサDS18B20
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/60891565-072e-2b30-e3cc-e37ab9f79700.png)

https://www.amazon.co.jp/gp/product/B0CC82881G/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1

# 目的
温度制御に利用するうえで、以下の点を調べました。

1. 最小制御周期
1. 測定結果の精度
    1. センサ同士の誤差
    1. 時間的なブレ
    1. 電源電圧の影響
1. 温度応答性
    
# 結論
1. 最小制御周期 → 1秒周期で可能（センサ自体の熱容量からして、もっと粗くてもいいけど）
1. 測定結果の精度
    * センサ同士の誤差 →　測定単位1でブレる。ほぼないと見ていい
    * 時間的なブレ　→　測定単位1でブレる。ほぼないと見ていい
    * 電源電圧の影響　→　測定単位1でブレる。ほぼないと見ていい
1. 温度応答性
    * 7秒遅れがあるとみなして良い


# 通信について：1-Wire bus
通信は以下の流れになります。
1. バスリセット（スレーブの存在確認）
1. データ送信・受診

### バスリセット
マスタ：480us以上LOW　→　スレーブ：60～240usLOWにして返す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/84abe531-d9af-9286-6e43-df664c500cac.png)

スレーブが存在することを確認します。

### データ送受信
スロット時間幅（1bitデータ時間幅）を60～120usとして、HIGH/LOWを使用して送受信する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/62121f6a-db79-4f42-b564-1dfe5d4ddace.png)

### 時間定義

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/8979937e-8737-47d0-1798-b8b209642979.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/acb48545-d2a1-28a8-6916-ea08f9e8f8fb.png)

### 通信プロトコル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/a34dbca6-4ff8-5520-efdb-1a86f2995be6.png)

以下の順にデータを送信します。
1．バスリセット
2. ROMコマンド
3. ファンクションコマンド

### コマンド
* SEARCH ROM →　0xF0
* READ ROM　→　0x33
* MATCH ROM　→　0x55
* SKIP ROM　→　0xCC
* ALARM SEARCH　→　0xEC
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/b767a391-9f5d-4f1d-2034-bcd6aa51a2b8.png)


# 信号測定

```C:Arduino側
#include <OneWire.h>
OneWire ds(8);  // This is where DQ of your DS18B20 will connect.
void setup(void) {
  Serial.begin(9600);
  getDeviceAddress();
}



void getDeviceAddress(void) {
  byte i;
  byte addr[8];
  Serial.println("Getting the address...\n\r");
  /* initiate a search for the OneWire object we created and read its value into

    addr array we declared above*/

  while (ds.search(addr)) {
    Serial.print("The address is:\t");
    //read each byte in the address array
    for ( i = 0; i < 8; i++) {
      Serial.print("0x");
      if (addr[i] < 16) {
        Serial.print('0');
      }
      // print each byte in the address array in hex format
      Serial.print(addr[i], HEX);
      if (i < 7) {
        Serial.print(", ");
      }
    }
    // a check to make sure that what we read is correct.
    if ( OneWire::crc8( addr, 7) != addr[7]) {
      Serial.print("CRC is not valid!\n");
      return;
    }
  }
  ds.reset_search();
  return;
}



void loop(void) {

  // do nothing

}
```
* センサ接続していないとき
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/7366ec04-5d0e-cadc-3e9a-ffe8806c5255.png)
480usLOWに落としている。本来はスレーブがHIGHにして返すため、それがない今はこれ以上の通信は起きない

* センサを接続すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/5ad29a83-9f30-70cb-c5e4-619a5085881a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/c8b6efe7-1f6a-7368-78e0-b89d3c208808.png)
センサがLOWにすることでバスリセットに返答している。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/977df70c-7c0b-e6f2-250f-d13f721b9393.png)
こんな感じで読み取れる。長＝0，短=1であるため、これはB00001111=0x0F=サーチコマンド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/8c4ec3ce-d88e-5fcc-3c00-7db88cd07b85.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/0147ec7a-ac27-9dfb-b5be-cc3720255658.png)

全部読み解くのは大変

# 実測:温度測定

```C:Arduino側
#include <OneWire.h>
#include <DallasTemperature.h> 

#define ONE_WIRE_BUS 8 // データ(黄)で使用するポート番号
#define SENSER_BIT    9      // 精度の設定bit 

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

DeviceAddress temp0 = { 0x28, 0x61, 0x64, 0x0B, 0x4A, 0x1E, 0xED, 0x0F }; //1つ目のDS18B20のアドレス
DeviceAddress temp1 = { 0x28,0xFF,0xDC,0x12,0x86,0x16,0x04,0x9C }; //2つ目のDS18B20のアドレス

void setup(void){   
  Serial.begin(9600);
  while (!Serial) {
  ; // wait for serial port to connect. Needed for native USB port only
  }
  sensors.setResolution(SENSER_BIT);
  delay(1000);
}

void loop(void){
  sensors.requestTemperatures();              // 温度取得要求
  delay(800);
  Serial.println(sensors.getTempC(temp0)); //1つ目のDS18B20から温度の取得、シリアルモニタに表示
  delay(1000);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/71b7f3eb-b064-3d2d-708b-179136334f91.png)
1.8秒ごとに繰り返す。

### 温度測定開始コマンドにかかる時間
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/0907cd25-167a-dd30-1b6b-1e328bea4f7e.png)

約12ms

サンプリング等にかかる時間は分解能によって変わるが、以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/898be1f9-54e7-72d1-00de-528e2356f143.png)

### 測定結果読み込みコマンドにかかる時間
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/6b5263c0-ab2f-1daf-ef0d-20843f51a655.png)
約34ms（精度によらない、読み込み2byteは変わらないため）

### 結論
温度測定周期1秒間隔は可能。
分解能を落とせばもう少し可能。

9bit分解能では、以下合計140ms周期が最短である。
* 測定要求コマンド：12ms
* サンプリング：94ms
* 読み込み：34ms

# センサの温度変化
熱容量の大きい、室温放置された水にセンサを入れて測定した。測定精度は12bitとした。
測定期間10分：測定回数312回

結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/237f9a4c-a2f4-2cf6-1070-b1f38c1c6b51.png)

標準偏差→0.0287℃

ただし、測定分解能の影響が強いですね。
測定単位1の前後で変化していると見て良さそうです。

<補足>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/5d4eb92c-c5d1-da41-a9af-62610443a20a.png)
センサは$2^{-4} = 0.0625℃$単位で測定しています。

# センサ同士の誤差
DS18B20の測定結果＋シンワ製温度計2つの温度を比較した。

結果
シンワ温度計→28.0℃（目視）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/35f4400a-f1d5-dce6-02eb-ae04d76a9996.png)
センサ同士にズレはほぼない

センサ電源電圧を変化させても測定単位1でブレる(USB電源3.9Vと外部電源5Vの2パターンで比較)→電源によらない


ちなみに、アドレスはこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/50e4bc6d-a49e-e631-54b2-3eb78c660055.png)


# 温度応答性
温水にセンサを突っ込むことで応答性を確認する。

```C:Arduino
#include <MsTimer2.h>
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 8 // データ(黄)で使用するポート番号
#define SENSER_BIT    12 // 精度の設定bit 

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

DeviceAddress temp0 = {0x28, 0x61, 0x64, 0x0B, 0x4A, 0x1E, 0xED, 0x0F};

volatile bool flag_t = false;

void setup(void) {
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  sensors.setResolution(SENSER_BIT);
  MsTimer2::set(1000, mejure);
  MsTimer2::start();
  delay(1000);
}
void mejure() {
  flag_t = true;
  sensors.requestTemperatures();
}

void loop(void) {
  if (flag_t == true) {
    flag_t = false;
    delay(800);
    Serial.println(sensors.getTempC(temp0), 5);
  }
}
```
82℃のお湯に入れたときの応答
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/7357d07a-c2fd-0afb-3e8e-553b0224ca99.png)


お湯から出したときの応答
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/d3de89d4-f09e-64b9-dc2e-58c4d6d86259.png)

指数関数的に応答していることがわかると思います。

水槽内の温度は$T_{con}$一定であるとして、温度計（熱容量$C[J/K]$,初期温度$T_0$）に対して、伝熱係数$k$,表面積$A$で熱流束$q$で伝熱したと考えられる。

```math
q = kA(T_{con}-T)
```

温度計の温度は以下の微分方程式に従うといえる。

```math
C \frac{dT}{dt} = q
```
上記の方程式を解くと

```math
T= -( T_{con} - T_0) e^{- \frac{kA}{C} t} + T_{con}
```

温度計の応答性を表している時定数$\tau$は以下のように求められると考えられる。

```math
\tau = \frac{C}{kA}
```

$1-e^{-1} = 63.2%$となる時刻は、
水中では7秒
気中では82秒

気中は伝熱係数が低いからしょうがないが、結構長いね。12倍ぐらいか。

https://tec-note.com/652

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/0feb412b-83bf-96fc-c014-1334ae4b00cb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/6b930f7c-863f-8638-b4dc-2b5efaca4633.png)

熱伝導性は20倍ぐらいある。
伝導だけではなく対流もあるから、なんとも言えないけども。

### 応答性

https://www.watanabe-electric.co.jp/sensor/faq/sokuon/03.html

水中での時定数が7秒であることから、計測されるまでに7秒遅れがあると考えて良い。
→ 制御周期によっては発振の原因となるため、注意

# 結論
1. 最小制御周期 → 1秒周期で可能（センサ自体の熱容量からして、もっと粗くてもいいけど）
1. 測定結果の精度
    * センサ同士の誤差 →　測定単位1でブレる。ほぼないと見ていい
    * 時間的なブレ　→　測定単位1でブレる。ほぼないと見ていい
    * 電源電圧の影響　→　測定単位1でブレる。ほぼないと見ていい
1. 温度応答性
    * 7秒遅れがあるとみなして良い