LoginSignup
16
10

More than 3 years have passed since last update.

Node.js で車とシリアル通信する

Last updated at Posted at 2021-01-17

先日車が壊れたのを機に車について調べていたら OBD (On-board diagnostics) という診断機能を知りました。大抵の車には OBD のコネクタが搭載されていて、これに ELM327というマイコンを使った製品をつなぐと Bluetooth のシリアル通信などで簡単に情報をやりとりできるそうです。おもしろそうなので試してみました。

重要 このポストと同じ方法で車の診断ログをリセットしたりすることもできます。診断ログが正しくない状態になると、修理の適切なタイミングなどがわからなくなるはずです。コマンドを車に送る前によく考えた上で自己責任で試してください。


ELM327 のドングルを手に入れる

これらの製品は Amazon 等で廉価に入手できるようです。全製品で固定っぽい Bluetooth の接続パスワードが書かれていたので念のため黒塗りにしました。

connector1.jpg

車のOBDポートを探す

運転席のどこかにある場合が多いようです。私の車はここにありました。

connector2.jpg

ここに購入した上記のドングル型デバイスを接続します。

通信方法を確認

送信

AT コマンドというものを送るとマイコンを操作することができるようです。今回は以下の2つのコマンドを使いました。

  • AT Z: 状態をリセットする
  • AT SP 0: OBDの通信プロトコルを自動で検出

通信プロトコルは様々なものがあるようですが、私の車で試した限りでは AT SP 0 の自動検出でうまく通信できました。

AT コマンドの他に OBD コマンドというものがあり、これで様々な OBD の情報を取得できました。Wikipedia にコマンドのリストがあったため、これを参照しました。

OBD コマンドは Mode と PID から構成されています。現在のデータを読み取る 01 というモードを今回は使いました。取得できるデータごとに PID が振られており、例えば PID0C のエンジン回転数の情報を読み取る OBD コマンドは 010C になります。

受信

データは、41 0C AA BB ... のようにスペース区切りの16進数の文字列で帰ってきます。1つめが Mode の値に40を足したもの、2つめが PID で、それ以降が実際のデータになります。例えばエンジン回転数の場合は値が4倍されているなど、それぞれ決まりがあるようです。

Node.js でシリアル通信をする

serialport というライブラリが著名そうだったので利用しました。

Node.js のランタイムでシリアルポートをオープンする TypeScript のコードはこんな感じ。ELM327 のデバイスに Bluetooth で接続した後に connect の関数を実行すると、非同期で SerialPort のインスタンスが得られます。

import SerialPort from 'serialport'

const DEVICE_PATH = '/dev/tty.OBDII-SPPslave'
export const connect = () => new Promise<SerialPort>((resolve, reject) => {
    const port = new SerialPort(DEVICE_PATH, { baudRate: 9600, autoOpen: false })
    console.log(`[SP] opening on ${DEVICE_PATH}..`)
    port.open((error) => {
        if(error) {
            console.log('[SP] port opening error.')
            reject(error)
        } else {
            console.log('[SP] port opened.')
            resolve(port)
        }
    })
})

ポートに書き込むには write メソッドを使います。コマンドのデリミタはキャリッジリターンなので注意してください。

// `AT Z` の AT コマンドを送出
port.write('AT Z\r')

データはイベントリスナーを使って取得できます。
文字列を処理して、目的の値を抽出します。

port.on('data', (data: string) => {
    const [mode, pid, ...values] = data.split(' ')
    
    let modeNum
    try {
        modeNum = parseInt(mode, 16) - 40  
    } catch {
        return
    }

    if(modeNum === 1) {
        const hexValue = parseInt(values.filter(value => value !== '').join(''), 16)
        if(pid === '0C') {
            console.log('EngineRPM: ' + hexValue / 4)
        }
    }
})

WebSocket でデータをブラウザに送信できるようにする

上記でデータが取得できる準備が整ったので、あとは WebSocket を使ってブラウザにデータを送ってみます。ws というライブラリを使います。

WebSocket サーバーを立ち上げるコードはこんな感じ。 createSocket の関数をコールすると非同期で WebSocket のインスタンスが得られます。

import WebSocket from 'ws'

export const createSocket = () => new Promise<WebSocket>((resolve, reject) => {
    console.log('[WS] connecting...')
    const wss = new WebSocket.Server({ port: 8081 })
    wss.on('connection', (socket) => {
        console.log('[WS] connected.')
        resolve(socket)
    })
})

WebSocket のインスタンスを使って、シリアル通信で得られたデータを送ります。上記のシリアル通信のイベントリスナーに socket.send(data) を差し込めば OK です。

クライアント側は、こんな感じ。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #display {
            width: 500px;
            height: 100px;
            font-size: 2em;
        }
    </style>
</head>
<body>
    <textarea id="display"></textarea>
    <script>
        const ws = new WebSocket('ws://localhost:8081')
        const display = document.getElementById('display')

        let currentData = {}

        ws.onmessage = (event) => {
           const data = JSON.parse(event.data)
            display.value = "エンジン回転数: " + data.engineRPM + "rpm"
        }
    </script>
</body>
</html>

動作させる

以上で全ての材料が揃ったので、これを組み込んでいきます。今回は Mac 上で以下の全てのプロセスを立ち上げて試してみました。

  • 車とシリアル通信をする WebSocket サーバー
  • クライアントを表示する ウェブサーバー

また、エンジン回転数以外にも現在の運転速度と冷却水温度も取得してみました。

image.png
https://youtu.be/jtPIO3J6cEs

動画を撮りながら走ってみると、いい感じにデータが取れているようです✌️


上記のコードは以下のリポジトリにあります。
https://github.com/kamataryo/parse-obd-serial

16
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
10