Raspberry PiでOBD-II (CAN)の情報を取得するための基板を自作する

車の自己診断情報から、エンジン回転数や燃費情報などをRaspberry Piを使って取得する方法を紹介します

ここでは、その前段階として車のOBD-II端子とRaspberry PiのSPIインタフェースを繋ぐためのCAN-SPI変換基板と、OBD-IIケーブルを作るための手順を書いています

手軽に済ますにはELM327を使うのが良いと思いますが、以下のような懸念が考えられます

* 偽物が出回っている
* 電波を使うのでセキュリティーホールになり得る

せっかく買ったのに使えなかったという話も聞きますが、中にはエンジンや各種装置の警告灯が付くようになってディーラーに行く羽目になったという痛々しい体験談もあるようです。また、(運よく?)使えたとしても、見ず知らずの誰かが勝手にペアリングして悪さをすることも考えられます

その点、自作すれば

* チップが偽物である可能性は低い
* ケーブル接続なのでセキュリティー面では安心

なにより、博打に掛けるより自分でやった方が楽しいでしょう！[^1]

# 前提

ここで作成する基板は車のOBD-II端子のCANとRaspberry PiのSPI端子を繋ぐための物です。そのため、車両側にCANのインタフェースがあることが必要となります。ほとんどの最近の車(2001年以降)にはOBD-II端子は付いていますが、中には古い規格のK-LINEしか付いていなかったり、TOYOTA CANのように独自の形式を使ったりする場合もあります。この辺りの事情は [別の記事](https://qiita.com/suzutsuki0220/items/633b26e16da93fb7516f) にまとめましたので合わせてご覧ください

# 材料

基板を作るための材料です

* CAN Controller MCP2515　×1
* CAN Transceiver MCP2551　×1
* OBD-II コネクタ　×1
* 水晶発振子　16MHｚ　×1
* セラミックコンデンサ　15pF　×2 (※)
* セラミックコンデンサ　0.1μF　×2
* 抵抗 10kΩ　×3
* ユニバーサル基板　×1

※ 参考にしたサイトでは16pFと書かれていましたが、売っていなかったので近い容量の15pFにしました

MCP2515とMCP2551は、アールエスコンポーネンツから購入しました。色々種類はありますが、ユニバーサル基板に実装するのでPDIPのものを選びます。注文の数量単位に注意してください。

その他の材料は、一般的な電子部品の店で用意できるかと思います

# 回路図

![image](https://qiita-image-store.s3.amazonaws.com/0/61404/90d7d33d-8969-f81e-5e7d-6a4117a3c20f.png)

汚い手書きですみません(笑

見苦しいようでしたら、ネットに探せば沢山転がってますので、そちらを参考にしてください

## 実装例

回路図の通りに配線して、作ったのが以下になります

![image](https://qiita-image-store.s3.amazonaws.com/0/61404/e8dc756a-93d8-a1bb-d0c1-9e8afada9c1d.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/61404/ac3cc9ae-3841-1b83-3c61-2ed5139f00b0.png)

ケーブルも作り、Raspberry Piと接続します

![image](https://qiita-image-store.s3.amazonaws.com/0/61404/add500bd-9fcc-846a-12db-d21fbf3e3c23.png)

ケーブルは何か補強手段を考えなければなりません。

OBD-II端子の6番ピンにCAN-H、14番ピンにCAN-Lを繋ぎます

# Raspberry Pi の設定

/boot/config.txt に以下の設定を追加します

```/boot/config.txt
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=25
dtoverlay=spi-bcm2835
```

config.txtを変更したら、Raspberry Piを再起動します

## 補足

いつの頃か(kernel 4.4あたり)にoverlayパラメータの変更があり、それまで後ろに `-overlay` が付いてたのが不要になりました。以前の記事では以下の設定を紹介していましたが、現在では上記の通りに設定しないとドライバは読み込まれませんのでご注意ください

```/boot/config.txt(昔のバージョン)
dtoverlay=mcp2515-can0-overlay,oscillator=16000000,interrupt=25
dtoverlay=spi-bcm2835-overlay
```

## 参考: パラメータの意味

oscillatorに水晶発振子の周波数、interruptに割込み用のGPIOピンを指定します。なお、MCP2515のCSをRaspberry PiのCE1(26番)に繋いだ場合はcan0をcan1に変更します

以下のREADMEより
<https://github.com/raspberrypi/firmware/tree/master/boot/overlays>

```
Name:   mcp2515-can0
Info:   Configures the MCP2515 CAN controller
Load:   dtoverlay=mcp2515-can0,<param>=<val>
Params: oscillator               Clock frequency for the CAN controller (Hz)

        spimaxfrequency          Maximum SPI frequence (Hz)

        interrupt                GPIO for interrupt signal
```

---

## ドライバーが読み込まれたことの確認

再起動後にCANコントローラーのドライバーが読み込まれていることを確認します

lsmodを実行するとCAN関連とmcp251xのドライバが読み込まれていることがわかります

```shell-session
$ lsmod | grep mcp251x
mcp251x                20480  0
can_dev                28672  1 mcp251x
```

mcp251x の表示が無い場合 config.txt の設定が間違っていないかご確認ください

また、dmesgコマンドでMCP2515のドライバが successfully initialized と出力されていればCANコントローラーとの通信はうまくいっています

```shell-session
$ dmesg | egrep -i "can|spi"
[    6.305202] CAN device driver interface
[    6.334702] mcp251x spi0.0 can0: MCP2515 successfully initialized.
```

もしも、CANコントローラーが繋がっていない場合は、以下のようなエラーが出ます

```shell-session
$ dmesg | egrep -i "can|spi"
[    6.319000] CAN device driver interface
[    7.350380] mcp251x spi0.0: MCP251x didn't enter in conf mode after reset
[    7.350441] mcp251x spi0.0: Probe failed, err=16
[    7.350523] mcp251x: probe of spi0.0 failed with error -16
```

接続が上手くいかない場合はオシロスコープを使うと切り分けが出来るようです
<https://misoji-engineer.com/archives/oscilloscope-4ch.html>

## 補足: カーネルパッチについて

別のサイトでは、カーネルにパッチを当てて再構築する手順を紹介しているものがありますが、新しいLinuxではその手順は不要になっています。パッチも古いもので最近のLinux 4.xでは当てられないと思います

カーネルのバージョンを確認するには、unameコマンドを使います

実行例

```shell-session
$ uname -r
4.19.97-v7+
```

# can-utils

CAN通信を行うために can-utils をインストールします

## インストール

Raspberry Pi OS (Raspbian)であればaptコマンドでインストールします

```shell
$ sudo apt install can-utils
```

### 参考: 自前でmakeする

<details><summary>パッケージが用意されていないLinuxの場合は開発環境を整えてmakeします (クリックして開く...)</summary><div>

can-utils
https://github.com/linux-can/can-utils

[Code] → [Download ZIP]をクリックして、ソースコード一式をダウンロードします

ダウンロードしたZIPファイルを解凍します

```shell-session
$ unzip can-utils-master.zip
```

ソースコードをコンパイルした後、root権限でインストールします

```shell-session
$ ./autogen.sh 
$ ./configure
$ make
# make install
```

----
</div></details>

# CANインタフェースの動作確認

CANインタフェースをループバックモードでリンクアップにします

```shell-session
$ sudo ip link set can0 type can bitrate 500000 loopback on
$ sudo ip link set can0 up
```

ターミナルを2つ開きます

■ターミナル1 (待ち受け)

1つのターミナルでcandumpを実行し、CANインタフェースの全パケットをdumpするように待ち受けます

```shell-session:待ち受け側ターミナル
$ candump any,0:0,#FFFFFFFF
```

■ターミナル2 (送信)

もう1つのターミナルでcansendを実行して、適当なメッセージ `1234ABCD` を送信します

```shell-session:送信側ターミナル
$ cansend can0 123#1234ABCD
```

candumpを実行したターミナルで、以下のようにメッセージが表示されます

```shell-session:待ち受け側の出力結果
$ candump any,0:0,#FFFFFFFF 
  can0  123   [4]  12 34 AB CD
  can0  123   [4]  12 34 AB CD
```

確認が終わったらloopbackをoffにします。再起動すればoffになるので面倒な方はこの手順を省略しても構いません

```shell-session:確認後loopbackをoffにする
$ sudo ip link set can0 down
$ sudo ip link set can0 type can bitrate 500000 loopback off
$ sudo ip link set can0 up
```

loopbackをoffにしないとCANのデータが車に流れないのでご注意ください

# 起動時にcanインタフェースを有効にする

canインタフェースを有効にするには以下のコマンドを入れます。

なお、bitrate の後に設定する `500000` は通信速度で `500kbps` を指します。自動車のCAN通信においては 250kbps か 500kbps のどちらかです。車種によって変わりますので適宜変更してください

```shell-session:canインタフェースを有効にする
$ sudo ip link set can0 type can bitrate 500000
$ sudo ip link set can0 up
```

しかし、これを起動する度に入力するのは手間がかかるので、自動的に実行するようにしましょう

`/etc/network/interfaces.d/can0` を以下の内容で追加します

```:/etc/network/interfaces.d/can0
auto can0
iface can0 inet manual
  pre-up /sbin/ip link set $IFACE type can bitrate 500000
  up /sbin/ip link set $IFACE up
  down /sbin/ip link set $IFACE down
```

設定した後に reboot すれば can インタフェースが有効になっているはずです

この設定が有効になっていれば、ifup / ifdown コマンドでcanインタフェースを有効/無効の切り替えが出来るようになります

```shell-session
# 有効にする時
$ sudo ifup can0

# 無効にする時
$ sudo ifdown can0
```

OBD-II端子を抜き差しして通信できなくなった時も、一度 ifdown した後に ifup を行うと復帰できます

# 車に繋いでみる

車のOBD-II端子に自作したケーブルを繋いで、candumpを実行してみます

```shell-session
$ candump any,0:0,#FFFFFFFF
```

上手くいけば、以下のように車から受信したデータが流れます

![image](https://qiita-image-store.s3.amazonaws.com/0/61404/74b25075-8702-064b-5bbc-58fc25faf841.png)

[^1]: 初版公開時から状況が大分変ってきており、自作せずとも既にMCP2515のチップが搭載された基板が販売されています。本記事は自作にこだわりたい方のために残しています