Rasperry Pi 3で9軸センサ(BMX055)を使う


Raspberry Pi 3で、9軸センサ(BMX055)の値を取得する方法があまり見当たらなかったので、まとめてみました。
(2019/10/20) 誤り修正、参考リンク追加

# 環境
- Raspberry Pi Model 3+
- ubuntu mate
- 秋月電子の[BMX055使用9軸センサーモジュール](http://akizukidenshi.com/catalog/g/gK-13010/)

# センサのはんだづけ
ジャンパの設定とピンヘッダを取り付けるため、はんだづけをします。  

* JP1~3 : 私の環境では各センサのアドレスを変更する必要がなかったため、どれもショートしませんでした。
* JP4~5 : ラズパイは、内部でプルアップもしくはプルダウンされているようなので必要なさそうです。  
以下の記事を参考にしました。  
[Raspberry PiのGPIOは起動直後から内部プルダウンされている](https://hnw.hatenablog.com/entry/20150607)  
[ラズパイゼロのプルアップ抵抗を調べる](https://s51517765.hatenadiary.jp/entry/2018/02/03/100530)  
* JP6~8 : GPIOは3.3Vであるため`JP7`をショートします。

# ラズパイでI2Cを使う設定
この内容は他にも多くの方が記事を書いているので、詳しくは書きません。

## I2Cインタフェースの有効化
```
$ sudo raspy-config
```
表示された画面で、`3 Interfacing Options` -> `P4 I2C` と選択したら、
`Would you like the ARM I2C interface to be enabled?`と尋ねられるので、
`yes` を選択し終了します。
その後、再起動します。
再起動後、以下のコマンドを入力し`i2c_bcm2708``i2c_dev`があることを確認します。

```
$ lsmod
...
i2c_bcm2708
...
i2c_dev
...
```

## ライブラリ、ツールのインストール
```
$ sudo apt-get update
$ sudo apt-get install i2c-tools python-smbus libi2c-dev
```

# 回路の作成
私はセンサの電源に5Vを使うことにしたため、以下のように接続しました。
電源を3.3Vにする場合はもう1本、線を追加する必要があると思います(未確認)。
左側がラズパイのGPIOピン、右がセンサです。

```
GPIO 1 (5V) -> VCC
GPIO 3 (SDA) -> SDA
GPIO 5 (SCL) -> SCL
GPIO 9 (GLD) -> GLD
```

# 軸の向き
![bmx055.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293692/5860b403-eaa8-8133-599f-960ebd78fb1e.png)


# 動作確認
`i2cdetect`コマンドで確認します。コマンドの詳細は[こちら](https://www.systutorials.com/docs/linux/man/8-i2cdetect/)です。

```
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- 13 -- -- -- -- -- 19 -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- 69 -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                         
```
加速度、ジャイロ、磁気のアドレスはそれぞれ、0x19、0x69、0x13なので(※)正しく認識できています。
(※ジャンパピンの設定で変わります)

# 値の取得
## ツールによる取得
どのアドレスにアクセスしたら何のデータが取得できるか、はBOSCHが公開している[BMX055のデータシート](https://www.mouser.jp/ProductDetail/Bosch-Sensortec/BMX055?qs=d72FGnIDsgRhsZPBP%2Fm6GQ%3D%3D)を見ればわかるのですが、
慣れていないとなかなか読み解くのが難しいです。簡単にですが説明します。

例えばz方向の加速度であれば、以下のように取得します。

加速度の値は12bitであり、レジスタアドレス0x06と0x07に分割して格納されています。
0x06の4~7bit目に加速度の下位4bitが、0x07に上位8bitが格納されています。
そのため、0x07の値と、0x06の4~7bit目の値を組み合わせる必要があります。

また、0x06には、0bit目には新しいデータかどうかを示すフラグが格納されています。

加速度の値は12bitですが、符号付き整数なので先頭の1bitは正負の符号です。
よって値域は -2048~2047 となります。
先頭bitが1のとき、2の補数を計算して負数にしてもいいのですが、
値が2048以上の場合は4096を引いて負の値に変換するほうが簡単です。

ここまでで求めた値はまだ物理値ではありません。
例えば、ここまでで求めた値が1であったからといって、加速度は1[m/s]ではありません。
レジスタアドレス0x0Fには値のレンジ設定が格納されており、デフォルト値は0x03なので
取得できる値のレンジは+/- 2gでLSBは0.00098[g]です。
つまり、レジスタから読み取った値が1であったとすると、それは0.00098[g]を意味するため、0.00098をかける必要があります。
また、[m/s^2]で使いたいなら、さらに9.8をかける必要があります。

`i2cget`コマンドでセンサの値を取得し、物理値に変換してみます。

```
$ sudo i2cget -y 1 0x19 0x06
0x91
$ sudo i2cget -y 1 0x19 0x07
0x40
```
0x06の値は0x91なので、0bit目は1なので新しいデータであることがわかります。
0x06の4~7bit目と0x07の値を組み合わせると加速度の値は

```
0x409 = 0d1033
1033 x 0.00098 = 1.01234 [g] = 9.920932 [m/s^2]
```
となります。

なお、取得する加速度のレンジや周期など、様々なパラメータが変更できます。
詳しくは[BMX055のデータシート](https://www.mouser.jp/ProductDetail/Bosch-Sensortec/BMX055?qs=d72FGnIDsgRhsZPBP%2Fm6GQ%3D%3D)を見てください。

## python プログラム
python3で加速度を取得するプログラムです。
smbusのapiは[こちら](https://pypi.org/project/smbus2/)から参照できます。

```python:bmx055.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

import smbus
import time

ACC_ADDRESS = 0x19
ACC_REGISTER_ADDRESS = 0x02

def bmx055():
    # initialize
    i2c = smbus.SMBus(1)

    ## 指定したレジスタアドレスから、2byte分取得する
    x_lsb = i2c.read_byte_data(ACC_ADDRESS, ACC_REGISTER_ADDRESS)
    x_msb = i2c.read_byte_data(ACC_ADDRESS, ACC_REGISTER_ADDRESS+1)
    y_lsb = i2c.read_byte_data(ACC_ADDRESS, ACC_REGISTER_ADDRESS+2)
    y_msb = i2c.read_byte_data(ACC_ADDRESS, ACC_REGISTER_ADDRESS+3)
    z_lsb = i2c.read_byte_data(ACC_ADDRESS, ACC_REGISTER_ADDRESS+4)
    z_msb = i2c.read_byte_data(ACC_ADDRESS, ACC_REGISTER_ADDRESS+5)

    x_value = (x_msb * 16) + ((x_lsb & 0xF0) / 16)
    x_value = x_value if x_value < 2048 else x_value - 4096
    y_value = (y_msb * 16) + ((y_lsb & 0xF0) / 16)
    y_value = y_value if y_value < 2048 else y_value - 4096
    z_value = (z_msb * 16) + ((z_lsb & 0xF0) / 16)
    z_value = z_value if z_value < 2048 else z_value - 4096

    x_acc = x_value * 0.00098
    y_acc = y_value * 0.00098
    z_acc = z_value * 0.00098

    print ("[%f, %f, %f]" % (x_acc, y_acc, z_acc))

    i2c.close()


if __name__ == '__main__':
    bmx055()
```

# 参考
python以外のサンプルが[こちら](https://github.com/ControlEverythingCommunity/BMX055)にありました。
こちらのGithubには、他のセンサのサンプルコードもあるようです。