おうちでできるOBD2(Arduino+MCP2515+ELM327)

# はじめに
前回記事、[おうちでできるCAN通信（Arduino+MCP2515+sigrok）](https://qiita.com/covao/items/d30fa5e36470bbee3be7)に続き、今回は、MCP2515 [MCP_CAN_lib](https://github.com/coryjfowler/MCP_CAN_lib) に含まれる[OBD_Sim](https://github.com/coryjfowler/MCP_CAN_lib/blob/master/examples/OBD_Sim/OBD_Sim.ino)を用い、実車を使わず、ELM327アダプターとスマホアプリにより、[OBD2スキャンツール](https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%B3%E3%83%BB%E3%83%9C%E3%83%BC%E3%83%89%E3%83%BB%E3%83%80%E3%82%A4%E3%82%A2%E3%82%B0%E3%83%8E%E3%83%BC%E3%82%B7%E3%82%B9)をテストできる環境を構築します。:alien:
# 準備するもの
## ハードウエア
- Arduino Unoマイコンボード  (または、互換ボード）
- MCP2515 CAN通信モジュール 　([Amazon](https://www.amazon.co.jp/s?k=MCP2515&__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&ref=nb_sb_noss)で１個あたり500円程度で入手できます）
- ELM327 OBD2インターフェース (Bluetooth接続タイプが[Amazon](https://www.amazon.co.jp/s?k=elm327&__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&ref=nb_sb_noss_2)で1500円程度で入手できます）
- 12Vが出力できる電源（ACアダプター等）
- ジャンパー線（ELM327とMCP2515、Arduinoと結線します）

## ソフトウエア
- [Arduino IDE](https://www.arduino.cc/en/main/software)
- [MCP_CAN_lib](https://github.com/coryjfowler/MCP_CAN_lib) Arduino MCP2515ライブラリ
- ELM327 OBD2スキャンアプリ [Torque Lite(OBD2&Car) for Andoroid](https://play.google.com/store/apps/details?id=org.prowl.torquefree&hl=ja)など

# ハードウエアの結線
- ArduinoとMCP2515モジュール、ELM327 OBD2インターフェースをジャンパー線などで接続します。
- あまりお勧めできませんが、ELM327のコネクタピンが、Arduinoなどのピンよりも太いため、今回はジャンパー線のメスコネクタ穴を拡げて挿入しています。:smiling_imp:
- ELM327は、自動車のOBDコネクタから12V電源を供給して動作するため、12VのACアダプタ等から供給します。

 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/d186e18f-185d-8cfd-ec98-ed0e9e64583d.png)

** OBD2コネクタピン**


|ピン番号 |機能     |接続先     |
|:-----------------:|:------------------:|:-----------:|
| 4|シャーシアース   |12V電源 GND |
| 5|信号アース   |Arduino GND |
| 6|CAN High   |MCP2515 CAN H |
|14|CAN Low   |MCP2515 CAN L |
|16|バッテリー電圧   |12V電源 + |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/ccc39b4a-5a9d-40ed-737f-558b08b643a4.png)

# 動作確認
## サンプルコードのビルド
- MCP_CAN_libのExampleにある[OBD_Sim.ino](https://github.com/coryjfowler/MCP_CAN_lib/blob/master/examples/OBD_Sim/OBD_Sim.ino)を使います。

[OBD_Sim.ino 50行](https://github.com/coryjfowler/MCP_CAN_lib/blob/master/examples/OBD_Sim/OBD_Sim.ino#L50 )のMCP2515のクロック周波数を8MHzに修正します。

```ruby:qiita.rb
 if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_8MHZ) == CAN_OK)
    Serial.println("MCP2515 Initialized Successfully!");
  else
    Serial.println("Error Initializing MCP2515...");
```

Arduino IDEでビルド後、MCP2515との接続が正常に動作すると、シリアルモニタに表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/bd3213c2-c906-62f4-ba16-4a4faa5f5ccd.png)
## OBD2スキャンアプリの接続
- スマホのOBD2スキャンアプリを起動し、BluetoothやWifi等でELM327と接続します。
- OBD2の通信プロトコルがいくつかあり、Arduinoで動作している仮想ECUが自動的に認識されます。(サンプルプログラムでは、ISO15765 500Kbps)

## エンジン回転数の確認
エンジン回転数の値を変化させるため、[OBD_Sim.ino 169行](https://github.com/coryjfowler/MCP_CAN_lib/blob/master/examples/OBD_Sim/OBD_Sim.ino#L169) Engine RPM レスポンス値(2bytes)を修正します。(0→7000rpmを繰り返します）

```ruby:OBD_Sim.ino
    else if(pid == 0x0C){    // Engine RPM
      txData[0] = 0x04;
      unsigned int rpm = (unsigned int)(millis() % 7000) * 4; // RPM:0->7000
      txData[3] = (unsigned char)(rpm >> 8);//RPM A //0x9C; 
      txData[4] = (unsigned char)(rpm - ((rpm >> 8) << 8));//RPM B //0x00;
      tx = true;
    }
    else if(pid == 0x0D){    // Vehicle speed
```
- スマホアプリで回転数を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/3e178e74-6cd5-4e79-a6dd-d063cbfe3e74.png)

## 故障コードの表示と消去
[OBD_Sim.ino 33行](https://github.com/coryjfowler/MCP_CAN_lib/blob/master/examples/OBD_Sim/OBD_Sim.ino#L33) DTCコード用変数を追加します。

```ruby:OBD_Sim.ino
//Current Diagnostic Trouble Code
byte DTC[] = { 0x05, 0xA0, 0x09, 0x00, 0x13, 0x80, 0x14, 0x90, 0x45, 0xA0, 0x36};

// CAN RX Variables
unsigned long rxId;
byte dlc;
byte rxBuf[8];
```

[OBD_Sim.ino 525行](https://github.com/coryjfowler/MCP_CAN_lib/blob/master/examples/OBD_Sim/OBD_Sim.ino#L525) DTCコードの表示とクリア処理を行うよう修正します。

```ruby:OBD_Sim.ino
  //=============================================================================
  // MODE $03 - Show stored DTCs
  //=============================================================================
  else if(mode == 0x03){
     //byte DTCs[] = {(0x40 | mode), 0x05, 0xC0, 0xBA, 0x00, 0x11, 0x80, 0x13, 0x90, 0x45, 0xA0, 0x31};
     byte DTCs[] = {(0x40 | mode), DTC[0], DTC[1], DTC[2], DTC[3], DTC[4], DTC[5], DTC[6], DTC[7], DTC[8], DTC[9],DTC[10]};
     iso_tp(mode, pid, 12, DTCs);
  }
  
  //=============================================================================
  // MODE $04 - Clear DTCs and stored values
  //=============================================================================
  else if(mode == 0x04){
      // Need to cleat DTCs.  We just acknowledge the command for now.
    for(int i = 0; i < =10; i++){
      DTC[i] = 0;
    }
      txData[0] = 0x01;
      tx = true;
  }
```
- スマホから、故障コードを表示し、次に消去を行います:smiling_imp:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329764/9bff95f6-a441-f72d-bd0d-9040abbeedf3.png)

# 参考 OBD2データフレームに関する情報
- WikiPedia OBD-II PIDs　https://en.wikipedia.org/wiki/OBD-II_PIDs
- CAN Multiple Frame Transmission https://happilyembedded.wordpress.com/tag/can/