最終更新: 2023/07/28(Fri)22:35

自作と修理を愛するブログ。トイラジ、電子工作、ボルティー、NucleusCMS 、いろいろゴソゴソやってます
3Dプリンター / 3Dスキャナーも -kyu-

Home

アナログタコメータをつくる(3) OBD2ライブラリ

前回ちょっと触れましたが、本当は縦2行程度の LCD を装備して、エンジン回転数以外の情報も表示させる目論見でした。

ちょっとググると、OBD2 スキャナと Arduino を使って同様のことをやっている事例は結構見つかります。
ライブラリに頼らずに、 AT コマンドを発行するところからなにから作り込んでる例もありましたが、大多数は特定のライブラリを利用している模様。あるものは使わなけりゃなぁと。

Arduino の OBD2シールドの販売もしているオーストラリアの Freematics 、この道では超有名です。
ライブラリも含めて自社製品用のスケッチが Gitbub で公開されています。ありがたや。
https://github.com/stanleyhuangyc/ArduinoOBD

一式ダウンロードすると、製品別のスケッチと共用ライブラリが。
libraries\OBD2UART\example フォルダにある rpm_led_uart.ino が一番簡単なサンプルですね。

使い方は簡単。OBD2UART.h をインクルードしておいて、Loop() 内で obd.readPID(PID_RPM, value) とやると、指定した pid に応じて欲しい数値が value に入るわけです。

★LCD表示テスト環境
LCD をつないでテスト。OBD2 スキャナから引き出した TX、RX、GND、5V を UNO に接続。LCD はここで得た知見を元に I2C で繋ぎます。

スケッチの Loop() 部分はこんな感じ。

void loop()
{
  static byte pids[]= {PID_RPM, PID_SPEED};
  static byte index = 0;
  byte pid = pids[index];
  int value;
  if (obd.readPID(pid, value)) {
    showData(pid, value);
  }
  index = (index + 1) % sizeof(pids);
}

ちょっと画質が悪いんですが、クルマと接続してエンジンをかけた時の挙動がこちらです。
★LCD表示テスト(動画)
通信が確立するまでにちょっと間がありますが、表示自体はうまくいってますね。走行していないのでスピードはゼロのままです。

あとはエンジン回転数を適当な計算でステップモータの位置に変換してあげれば、メカ式タコメータ、簡単に完成しそうです。で、やってみました。

‥動きません。

正確には動かないのではなく、モータの動きがひじょーに遅いんです。

紙で作った簡単な針をモータの出力軸に取り付けてよくよく観察してみると、カタカタと針が動いているのはわかりました。ただ、その動きが異常に遅いのです。とてもリアルタイムで追従なんてしてません。ここからが相当ハマりました。

スケッチの書き方が悪いのだろうとあちらこちらといじり倒しても大した変化なし。必要最小限の構成にしてもダメ。UNO が遅くて処理しきれないんじゃないかと、OBD2 データを吸い上げるところとモータを回すところをそれぞれ別の UNO にして、シリアル通信させてみたりもしました。このときは逆にシリアル通信の文字化けやら INT 型の数値のやり取りとか、あらぬところで沼にハマってしまってボツに。

で、モータのライブラリの説明文を読み返してみて、おぼろげに理解しました。

ステッピングモータを動かすためのライブラリは、位置をセットするステートメントと実際にモータを動かすステートメントが分かれているのが普通です。AccelStepper 然り、SwitecX25 然り。で、モータを動かすステートメントには2種類あります。BLOCKING と NON-BLOCKING です。前者は Synchronous、後者は Asynchronous と表現されることもあります。同期、非同期ですね。

モータを回転させるというのは、マイコンにとっては非常に時間のかかる作業です。ある位置からある位置まで回転させる時、その処理だけをずっとやっていると他のことができません。通信していればデータの取りこぼしがあるだろうし、刻々と変化するセンサの値を読み取れないかもしれません。なので、時間を専有しないような工夫がされています。

NON-BLOCKING は文字通り「ブロックしない」ということ。SwitecX25 の場合、モータを回転させる update() は、一回実行すると1ステップだけモータを回して処理を次に渡します。Loop() の中でぐるぐる実行すれば連続して回転するというわけですね。速度は Loop() を回す速度に依存します。これが非同期。
対する BLOCKING は、一回実行すると目標値に達するまでずっとモータを回転させていきます。その間他の処理はできません。これが同期。普通は非同期の方を使います。

さて、Loop() 内で、OBD2 の値を取得する、モータの目標値をセットする、モータを回転させるという処理を順にぐるぐる行っていて、モータの動きが非常に遅いということはつまり、OBD2 の値を取得するのにかなり時間がかかっているのではないかと推測できます。

ではなぜ LCD の表示はうまくいくのか。LCD はモータに比べて十分高速だからでしょう。残像もあるので表示がパラパラ変わってもよく分かりませんしね。

ググっていて興味深いやり取りを二つ見つけました。

Splitting up the call and receive? - Freematics Forum
https://freematics.com/forum/viewtopic.php?t=1931

How to speed up OBDII uart? - Arduino Forum
http://forum.arduino.cc/index.php?topic=240047.0

PID の値を取得するには、OBD2 に対して特定のコマンドを投げます。返事はいつ来るかわかりません。
Freematics のライブラリにある readPID は、コマンドを投げてから返事が返って来るまでずっと待っているのですね。モータの例でいう同期処理、BLOCKING なわけです。だからその間の処理が止まってしまう。非同期処理にしないとダメなんです。ほとほと困りました。

でもね、世の中には同じことを考える人というのは必ずいるのですよ。Github を検索していて見つけました。

Arduino-OBD2-Async
https://github.com/vova-ivanyshyn/Arduino-OBD2-Async

Freematics のライブラリを改造して Asynchronous にしたものです。素晴らしい。
惜しむらくは、複数の PID に対応していないこと。一度に読むことができる PID は一つだけです。んー、残念。

ってことで、LCD にいろいろな情報を表示するのは諦めて、メカ式のタコメータだけをつくることにしました。
いつか C++ のスキルが上がったらチャレンジしてみますかね。

あー、今回の記事は文字ばっかりだなぁ。写真に撮れるような目に見えるものがないってことです。
次回は回路とスケッチについて書く予定。

コメント

この記事へのコメントはありません

コメントを投稿

( *は必須項目です )

%3c%69%6e%70%75%74%20%74%79%70%65%3d%22%68%69%64%64%65%6e%22%20%6e%61%6d%65%3d%22%6e%70%5f%70%72%6f%74%65%63%74%62%79%6d%64%35%22%20%76%61%6c%75%65%3d%22%31%63%38%32%37%35%33%64%34%62%63%62%30%63%35%38%63%63%36%63%39%33%63%63%37%66%37%65%32%64%30%38%22%3e %3c%69%6e%70%75%74%20%74%79%70%65%3d%22%68%69%64%64%65%6e%22%20%6e%61%6d%65%3d%22%6e%70%5f%70%72%6f%74%65%63%74%62%79%6d%64%35%5f%68%61%73%68%22%20%76%61%6c%75%65%3d%22%37%37%37%65%61%36%37%36%62%37%33%61%31%37%66%33%37%66%34%39%39%61%37%37%66%39%33%32%65%65%62%30%22%3e

サイト内検索

↑ページ先頭へ

2551906 [Mode]
Copyright © kyu-weblog All Rights Reserved. Powered by Nucleus CMS v3.71 管理