気圧センサ AP40R-025KG-2 をArduinoで動かす

秋月電子通商で販売されている[気圧センサー AP40R-025KG-2](http://akizukidenshi.com/catalog/g/gP-13717/)をArduinoで動かすために、データシートを使って調べたので、その過程をドキュメント化しました。このセンサに限らず、センサ値をデジタル出力するI2C通信のデバイスであれば、手順として共通する点は多いのではないかと思いますので、参考にしてみてください。

## センサのアドレスを調べる
まず、気圧センサの通信規格について。これは秋月電子の商品ページにも載っていて、I2Cと書かれています。
Arduinoを使ったI2C通信については、[こちらの記事](https://qiita.com/MergeCells/items/20c3c1a0adfb222a19cd)でとても分かりやすく記載されています。
今回は、センサ側の情報を受け取るため、上の記事における、masterからの一方的な読み込みのコードが該当します。

```test_I2C_master.ino
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  Wire.requestFrom(0x1E, 2);

  while (Wire.available()) {
    byte val = Wire.read();
    Serial.print(val);
  }

  delay(500);
}
```

I2C通信では、センサ値にアクセスするためのレジストのアドレス値を調べる必要があるので、まずはそれをデータシートを使って調べてみます。

[データシート](http://akizukidenshi.com/download/ds/fujikura/ap4.pdf)を読むと、2ページ目にこのような画像があります。
![Screenshot 2019-08-07_12-55-53-915.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412023/7e7e433c-7bc4-41cf-7ffa-e4ff4621c560.png)
（画像出典：http://akizukidenshi.com/download/ds/fujikura/ap4.pdf）

使うセンサの型番はAP40R-025KG-2なので、アドレス値は0x28だと分かりました。

ちなみにセンサのアドレス値を調べるコードとして、Arduinoの公式ページに[i2c_scanner](https://playground.arduino.cc/Main/I2cScanner/)というスケッチもあります。
センサをArduioに接続したのちスケッチを書き込むと、Serialモニタで、アドレス値を確認することができます。
![Screenshot 2019-08-07_17-09-08-610.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412023/72a0b016-7302-b399-e33f-703a52b0f987.png)

ちなみにArduinoと気圧センサの配線は、[データシート](http://akizukidenshi.com/download/ds/fujikura/ap4.pdf)の2ページ目と4ページ目に記載されています。
![Screenshot 2019-08-07_17-29-08-089.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412023/84534879-d07e-71ae-f3af-087716bd4b7d.png)
![Screenshot 2019-08-07_17-29-47-149.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412023/c2fe3193-700e-6e0b-d13c-987ae4f10747.png)
（画像出典：http://akizukidenshi.com/download/ds/fujikura/ap4.pdf）


##２バイト分のデータを受け取る
続いて、データを受け取るためのコードを書きます。
データシートを読むと、データは２バイトで送られてくることが分かります。
そこで、２バイト分のデータを毎回受け取るために、関数Wire.requestFromの中身を(0x28,2)に変更します。0x28は、先ほど調べたアドレス値です。

そして、受け取ったデータを、ビットシフトを使って結合させます。
ビットシフトとは、２進数で並んだ文字を、指定した数だけ右または左にずらす操作のことです。
よってコードは以下のようになります。
なお変数valは、byte型だと8ビット分しか受け取れないため、byteからintに変更しています。


```test_I2C_master.ino
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  Wire.requestFrom(0x28, 1);

  while (Wire.available()) {
  　int val = Wire.read() << 8;
 　 val += Wire.read();
    Serial.print(val);
  }

  delay(500);
}
```

これでひとまず、信号を受け取るためのコードを書くことができました。

##センサ値を圧力に変換するために必要な情報を調べる
ただしこのままだとただ数字が得られるだけで、なんの値なのかさっぱり分かりません。
ということで、センサ値を圧力に変換するために必要な情報を調べます。

データシートを使って、センサから送られるデータを見てみます。
3ページ目を見ると初めの8ビットはアドレス値、それ以降はセンサ値が送られていることが分かります。
![Screenshot 2019-08-07_17-27-59-330.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412023/a83008f8-9b62-66f5-24b1-ea406c50417a.png)
（画像出典：http://akizukidenshi.com/download/ds/fujikura/ap4.pdf）

ちなみにACKはAcknowledgement、NACKはNegative Acknowledgementの略で、I2C通信のやり取りに使われる信号なので、センサ値とは関係ないものです。
センサ値データの入った箱は14個、すなわち14ビットのデータを受け取っていることが分かります。残り2ビットはstatus Bitとして使われていますが、こちらは通信に以上がない限り0を出力しています。
14ビットで表現できる数の幅は2の14乗、計算すると16384です。
その一方で、定格圧力は20kPaで、このセンサは正圧のみ計測できるので、0〜20kPaの間を計測することができます。
よって、送られてくるデータ0〜16383に応じて0〜20kPaに変換し、
P=(data/16383)*20(kPa)
で求められるように思えますが、これだと正確な値は計測できません。
データシートをもう少しよく読んでみます。

##オフセット値、スパンを調べる
データシートには、オフセット値、スパンが以下のように記載されています。
これはつまり、個体差はあるものの、オフセット値819の時に圧力は0kPaで、15565の時に20kPaになるということです。そしてスパンの欄に記載されている14746は、この二つの値819と15565の差になります。
![Screenshot 2019-08-07_17-31-45-799.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412023/c70216f1-8a40-7344-e4de-fa08dfe1b9cf.png)
（画像出典：http://akizukidenshi.com/download/ds/fujikura/ap4.pdf）

よって計算式は以下のようになります。
P=((data-819)/14746)*20(kPa)
これで圧力値を計算することができます！

最後に参考として、今回作成したサンプルコードを載せておきます。オフセット値は個体差があるため、安静時にゲージ圧値がちょうどゼロになるよう調整して下さい。

```test_I2C_master.ino
#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
}

void loop() {
  Wire.requestFrom(0x28, 1);

  while (Wire.available()) {
    byte val = Wire.read();
  　int val = Wire.read() << 8;
 　 val += Wire.read();

  //819はオフセット値なので適宜変更する
  float value = ((float(val) - 819) * 25 / 14746);
  Serial.print("kPa");
  Serial.print("\n");

    Serial.print(val);

  
  }

  delay(500);
}
```