OBDIIで車両情報をモニタすることにした話

ちょっといいレーダー探知機とかだとにはOBDII対応を謳ったモノがあり、別売のコネクタとセットで使えば車速、HVバッテリ残量等々が読めますよね。 でもその探知機も結構お値段する。別にレーダー探知機機能は無しでもよいので、マイコン(Arduino)を使って自分で読むことにした話。

ハードウェア

マイコン

MEGA328を利用した。Arduinoを使うが、標準のFWでは外部クリスタルが必要で面倒なので、内蔵発振器で動作するFWを使用する。 詳細は Arduino - ArduinoToBreadboard 後半にある「Minimal Circuit (Eliminating the External Clock)」を参考に。

CAN-BUS

情報読み出しにはCANバスプロトコルをしゃべる必要がある。 このCANバスのプロトコルArduino(MEGA328)は直接対応していないので、シールドを利用する。 意外とこれが結構広く出回っており、Amazon等でMCP2515等で検索すると出てくるのがそれで、およそ800円弱。コンパクトなものを購入した。

また、車両のOBDIIポートに接続することになるが、このコネクタは格安で購入したELM327モジュールからばらして利用することにした。

ライブラリは MCP_CAN_libを使用。

ディスプレイ

0.91インチ、128x32のI2C接続有機ELモジュールを使用。このモジュールは比較的フチも狭く、ケースに入れなくても見栄えがするので利用しやすい。またAdafruitのライブラリも利用できるので、一般的なキャラクタ液晶のように利用することができる。

ライブラリはAdafruit_SSD1306Adafruit-GFX-Libraryを使用。

車両

車両はホンダ・ヴェゼルハイブリッド。ヴェゼルは運転席左足の上のあたりにOBDIIコネクタが出ており、ここに接続する。 また、メーカーによってCANバスで指定するID番号が異なったりするのがくせ者で、今回のホンダの場合は0x18DB33F1に対して送ればよい。 こちらのサイトさんとWikipediaが非常に参考になる。

回路図はこんな感じ。

f:id:soiyadakara:20191124205014p:plain
回路図、だいたいこんなかんじ。

実装してこんな感じ。青い基板がCANBUSシールド、上の黒いコネクタがOBDIIコネクタ(裏側)、右側に電源コネクタと、プログラム書き換え用のピンヘッダ。写真では見えないが、MCP2515基板に隠れるようにATMEGA328が実装されている。

f:id:soiyadakara:20191124143107j:plain
コントローラ部
ケーブルの先にはディスプレイ。スモークのアクリルで適当にケースを作って、メーターの近くに配置。車のメータと色味が似ているのでいい感じ。
f:id:soiyadakara:20191207111305j:plain
ディスプレイ部分。

ソフトウェア

setup関数内でCANバス、ディスプレイの初期化と割込みの設定。loop関数内でディスプレイの更新と車両へのリクエスト信号の送信。ピン変化割込みでデータ受信を検知し取得するようにしている。

今後の発展

今回は速度、回転数、冷却水温度、バッテリ残量のみをモニタしているが、欲を言うとバッテリ/ジェネレータ電流あたりをモニタしたいと思っていた。しかしホンダ車情報はネットを探してみても情報がなく、残念ながら断念した。トヨタのハイブリッドはこの辺りがよく解析されており、ネットを探すとそれらしい情報がありうらやましい限りである。

車のCANバス上を流れている情報を見ればそれらしい情報を見つけられるはずだが、ヴェゼルの場合はCANゲートウェイと呼ばれるものが挟まれているせいか、OBDIIコネクタのような末端まで信号は流れていないようだ。メーターを外し裏のどこかのケーブルを読めば…何かが見えたりするのだろうか‥‥?

RaspberryPiを使ってcronで水やりをした話

ベランダでナスとピーマンを育てています(いました)。そういうことをしていると長期連休中の水やりが問題になります。 長期連休まで時間がなかったため、ちょうどよく余らせていたRaspberryPiで贅沢に自動水やり機を作ってみた話。

f:id:soiyadakara:20191024063000j:plain
元気なベランダ野菜(監視中)

f:id:soiyadakara:20191117124526p:plain

ハード

WiFi接続が可能なRaspberryPi 3 Model Bを使用。RaspberryPiをベランダ外に出し、WiFiでLANに接続させておく。野菜への水やりは水をためたポリタンクにバスポンプを入れ、ポンプのON/OFFで行う。 利用したポンプはKOSHINのバスポンプKP-104。本来はコンセントに接続後、ボタンでON/OFFを切り替えるものだが、分解して調査してみるとONスイッチをショートさせた上で通電すればでONとなることが分かった。よって、ONスイッチをショートさせ、リレーでAC100VをON/OFFすることでポンプを作動させることにした。

f:id:soiyadakara:20191117120231j:plain
バスポンプのコントローラとリレー

このリレーをRaspberryPiのGPIOから制御する。リレーのドライブのために2SC1815でドライブ回路を作り、今回はGPIO4に接続しておく。 f:id:soiyadakara:20191117120100j:plain

タッパーに入れて雨をしのぐ。

f:id:soiyadakara:20191117114820j:plain
シャワーヘッド

ポンプにホース、シャワーヘッドをつなぎ、シャワーヘッドはフレキシブルアームのスマホスタンドで固定しておくことにした。

あとついでに、せっかくRaspberryPiを使っているのだから、成長の様子が監視できるようにWebカメラを用意しUSB接続した。物干し竿の部分に設置し、ラップで防水した。 f:id:soiyadakara:20191117114705j:plain

ソフト

RaspberryPiのgpioコマンド経由で制御する。 こんな感じのシェルスクリプトを書いて、実行、動作を確認しておく。確認ができたら/root/scripts/mizuyari.shあたりに置き、"crontab -e"コマンドでcrontabに

50 5    * * *   root    /root/scripts/mizuyari.sh

と書いておく。一日一度5時50分に水やりをする設定ができた。

カメラの制御はmotionを使う。 motionをインストール、その後/etc/motion/motion.confを編集してキャプチャサイズ、待ち受けポート等を決め、デーモンを起動する。

daemon on
width 1280
height 720
framerate 2
stream_port 8081  # port8080で待ち受け

これでサーバーの8080ポートに接続すれば見られる。ちょうどトップの写真のように、元気な野菜の様子が得られるはず。

ESP8266からGoogleスプレッドシートに書き込む

種々のIoT用データ投稿サービスはありますが、Googleスプレッドシートに書き込んでしまえばグラフ化・共有等々、何かと便利です。 ESP8266から直接スプレッドシートにデータを蓄積してしまいましょう。

Googleスプレッドシートスクリプト

今回は温湿度・気圧センサであるBME280を例として用いることとします。BME280から得た温度、湿度、気圧の値をESP8266よりPOSTで送ります。送られてくるクエリストリングの例として

temperature=26.75&pressure=1015.84&humidity=36.42

のような形であると想定します。

データの受信にはGoogle Apps Script(GAS)を利用します。下記の説明はかなり天下り式なので気になった部分についてはGoogle Apps Scriptで検索するとよいかと思います。

まず、Googleスプレッドシートを開き、新規ドキュメントを作成します。その後「ツール」->「スクリプトエディタ」を選択しスクリプトエディタを開きます。

f:id:soiyadakara:20181031211517p:plain:w400

最初はテンプレートが入っていますが、それを消してdoPost関数を書いてやります。

f:id:soiyadakara:20181031223444p:plain:w400

function doPost(e) {
  // パラメータ取り出し
  var temperature = e.parameter.temperature,
      pressure = e.parameter.pressure,
      humidity = e.parameter.humidity;
  
  // パラメータがそろっていなければ終了
  if(temperature && pressure && humidity){
    try{
      // スプレッドシート情報収集
      var spreadsheet = SpreadsheetApp.openById("<スプレッドシートID>"),
          sheet = spreadsheet.getSheets()[0],
          date = new Date();
      // スプレッドシートへ書き込み
      sheet.appendRow([date, temperature, pressure, humidity, e.postData.getDataAsString()]); 
      
    }catch(e){
      console.log(e);
    }
  }
}

こんな感じ。なおスプレッドシートのIDはスプレッドシートのURLに情報があります。

https://docs.google.com/spreadsheets/d/<スプレッドシートID>/edit#gid=0

doPost関数が準備できれば、それを公開する作業に移ります。適当なプロジェクト名で保存し、「公開」->「ウェブアプリケーションとして導入」を選択。

f:id:soiyadakara:20181031213410p:plain:w400

「アプリケーションにアクセスできるユーザー」を「全員(匿名ユーザーを含む)」に変更、「導入」を選択。

f:id:soiyadakara:20181031215455p:plain:w400

認証が必要と言われるので、「許可を確認」し、アカウントでログイン。さらに「このアプリは確認されていません」と出るので、左下「詳細を表示」を選択、下に出てくる「無題のプロジェクト(安全ではないページ)に移動」を選択。

f:id:soiyadakara:20181031214517p:plain:w400

すると、ようやくURLが与えられます。URLをメモしておきます。

f:id:soiyadakara:20181031214550p:plain:w400

このURLをテストしてみます。curlコマンドでPOSTメッセージを送ってみます。

$ curl --include --data 'temperature=26.75&pressure=1015.84&humidity=36.42' -X POST '<コピーしたURL>'

f:id:soiyadakara:20181031223133p:plain:w400

ちゃんとパラメータの対応等がとれていれば、問題なくスプレッドシートに追加されているはずです。うまく追加されない場合はデバッグをする必要がありますが、、、デバッグは少し大変です。

f:id:soiyadakara:20181031223335p:plain:w400

後はこのPOSTメッセージをESP8266で送るだけです。

ESP8266プログラム

ESP8266はArduinoで開発します。BME280とはI2Cで接続し、データ取得はAdafruitのライブラリを利用します。取得したデータのPOSTはWiFiClientSecureを用いて実装しました。

下記プログラムで、約1分毎に温湿度、気圧を測定しPOSTしてくれます。 実際にWiFi機能を利用しているのはpostValues関数内で、毎回接続するようになっています。

少しスプレッドシートを開いて眺めているとおもしろいかもしれません…?

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const char* ssid     = "<WiFi SSID>";
const char* password = "<WiFi Password>";

const String host = "script.google.com";   // コピーしたURLのホスト部
const String url = "/macros/s/<文字列>/exec";  // URL後半部分

Adafruit_BME280 bme; // I2C

void setup() {
  Serial.begin(9600);
  Serial.println(F("BME280 test"));

  bool status;

  // BME280への接続
  Wire.begin(4, 5); // SDA=IO4, SCL=IO5
  status = bme.begin(0x76, &Wire);  // BME280アドレス指定
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  Serial.println();

  // おまじないに切断
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
}


void loop() {
  postValues();
  delay(10*1000);
}


void postValues() {
  // WiFi接続開始
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  
  WiFiClientSecure sslclient;
  String params;

  // センサデータ取得、パラメータ準備
  params = "temperature=" + String(bme.readTemperature(), 2);
  params += "&pressure=" + String(bme.readPressure()/100.0F, 2);
  params += "&humidity=" + String(bme.readHumidity(), 2);
  Serial.println(params);

  // SSL接続開始、POSTメッセージ送信
  if (sslclient.connect(host, 443) > 0) {
    sslclient.println("POST " + url + " HTTP/1.1");
    sslclient.println("Host: " + host);
    sslclient.println("User-Agent: ESP8266/1.0");
    sslclient.println("Connection: close");
    sslclient.println("Content-Type: application/x-www-form-urlencoded;");
    sslclient.print("Content-Length: ");
    sslclient.println(params.length());
    sslclient.println();
    sslclient.println(params);
    delay(10);
    String response = sslclient.readString();
    int bodypos =  response.indexOf("\r\n");
  } else {
    // HTTP client errors
    Serial.println("[HTTPS] no connection or no HTTP server.");
  }

  // 送信完了、接続終了
  WiFi.disconnect();
}

こんな感じ。

f:id:soiyadakara:20181031231901p:plain:w400

所感

GAS、なかなか便利です。

BME280の温度が体感よりも高めな気がします。 使ったBME280モジュールには3.3Vのレギュレータ等が乗っているため、そこからの熱を拾っているのでしょうか。 精度を追求するなら電源を切るといった事が必要かもしれません。

また、今回はESP8266のスリープ機能を利用していませんが、ロガーとしての用途ならばスリープ機能を利用し電池等で動かしたいですね。

Advanced-VXコントローラのWiFi制御化

Advanced-VXにはCelestronからSkyPortal WiFiモジュールが販売されており、公式にタブレット等から制御ができるようになっている。一方、ネット上を調べるとコントローラのシリアル入出力にBluetooth変換モジュールをつけ、AndroidSkySafariから制御している方々をの情報を得ることができる。

しかし、残念なことに私が持っているタブレットiPadである

iPadはデバイス周りで融通が利かず、Bluetooth変換モジュールなどは使えない。Bluetooth赤道儀を制御などというのは夢の話である。なので赤道儀に限らずWiFiを用いるくらいしか手法がなく、その為にはSkyPortal WiFiモジュールを買うしかないか、と思っていた。しかし少し調査した結果、コントローラの入出力をWiFiで飛ばしてやればSkySafariから制御できることが判明した*1。今回はその話。

UART/WiFi変換器の作成

前述のSkyPortalのコントローラの他に、SkySafariの企業がSkyFiと呼ばれるWiFiモジュールを販売し、赤道儀の制御を可能にしている。今回はそのSkyFiと同等の機能を実現する。

といっても、実はこのSkyFiはそこまで複雑なことをしているわけではなかった*2。アクセスポイントを作り、指定のポートでSkySafariからの接続を待ち、接続があればWiFi経由で送られてきたデータをシリアルポートに流し、シリアルポートから受信すればWiFi経由で送信する、というだけ*3

この機能をESP8266を用いて実装した。プログラムを以下に示す。ESP8266はArduinoで開発した。

#include <ESP8266WiFi.h>

// SkySafariのデフォルト設定より、ポート4030
WiFiServer server(4030);

// 作成するアクセスポイントのSSID、パスワード
const char ssid[] = "AVX-Serial";
const char pass[] = "password";

// SkySafariのデフォルト設定より、IPアドレス10.0.0.1
const IPAddress ip(10, 0, 0, 1);
const IPAddress subnet(255, 255, 255, 0);

void setup() {
  // Advanced-VXのボーレート9600
  Serial.begin(9600);

  // アクセスポイント設定
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(ip, ip, subnet);
  WiFi.softAP(ssid, pass);

  // サーバー開始
  server.begin();
}

byte buf[32];
byte len;
void loop() {
  WiFiClient client = server.available();
  // SkySafariからの接続が無ければ何もしない
  if (client) {
    while (client.connected()) {
      // wifi -> uart
      while (client.available()) {
        Serial.write(client.read());
      }

      // uart -> wifi
      if (Serial.available()) {
        // WiFiにはまとめて送るため、データをまとめて受信
        Serial.setTimeout(200);
        len = Serial.readBytes(buf, 32);
        client.write(buf, len); 
      }
    }
    delay(1); 

    // 接続終了
    client.stop();
  }
}

ESP8266はSSIDがAVX-Serialのアクセスポイントを作成し、IPアドレス10.0.0.1、ポート4030で待機する。 このプログラムを書き込み、Advanced-VXのコントローラ内部に仕込む。

今回用いたESP8266モジュールはESP01。通常の使い方通りプルアップを施す。 USBコネクタの近くにあるPL2303の2番ピンがTXD、4番ピンがRXDなのでESP8266とTXD同士、RXD同士を接続する。本当はパターンカットするのがよいかもしれないが、今のところ問題なく動いている。 電源はコントローラのISP端子?から拝借。ESP8266が電流を食うせいか少し動作不安定だったが、電源にコンデンサを入れることで解決した。

SkySafariからの利用

回路が問題なければ、あとはアプリから利用できるはず。 "Settings"の"TELESCOPE"から"Celestron AVX/CGE/CGEM/CGX"を選択する。 プログラムで指定したように、"IP Address"を"10.0.0.1"、"Port Number"を"4030"と設定する。

ツールバーのScopeから、Connectを押すと、、

左右にRA,DECのボタンが現れ、制御できるようになる。 目的のポイントをタップし、"GoTo"を押すことで自動導入もされる。 RA,DECボタンはややラグがあり直感的でないかな、という気がするものの問題なく動かすことができる。

*1:なおCelestronはSkyPortalというアプリを提供しているが、今回の場合はSkyPortalでは利用できない。

*2:コアとなる部分に関して言えば、ですが

*3:簡単そうに言っていますが、ESP8266が無ければ困難です

ピリオディックエラーの測定

PHD2を用いたAdvanced-VXのガイドを試行してみるものの±4秒角以上のずれがあり相変わらずうまくいかない。問題の切り分けのためにもピリオディックモーションの測定を行った。

カメラ露出による方法

まずカメラによってピリオディックエラーを測定する。極軸をずらした状態で南中付近の星の挙動を見る。長時間露出でもよく、数枚に分けて比較明合成してもよい。AVXのウォームギアの周期は8分程度のようで、ピリオディックエラーを見るには倍の16分程度は露出すればいい。

AVX+C9.25+NEX5によって1049秒間の露出を行った結果は以下。等倍トリミングのみ行った。

f:id:soiyadakara:20171108231521p:plain

長周期と短周期のギザギザがあり、長周期のものがウォームのエラー、短周期がウォーム手前の平歯車のエラーとみられる。C9.25は焦点距離が2350mm、NEX-5ピクセルサイズは5μmなので、1pxは約0.45秒に対応する。したがって、ウォームのエラーは49秒、±24.5秒ということになる。

え、、、ちょっと大きすぎないですかね・・・?「Advanced-VX ピリオディックエラー」で調べると±10秒強というのがいろいろ出てくるんですが・・・。

C9.25が重すぎるのか、何かパラメータが異なるのか。少し怪しいので、次の手法を試す。

PHD2による方法

PHD2で機器接続・オートキャリブレーションを行った後にガイドアシスタントを起動すると、ガイド出力が切られた状態になり、星の動きをひたすらプロットしてくれる。このまま同様にウォームの周期以上の時間を放置する。すると横軸時間、縦軸移動量というグラフが得られ、縦軸振幅を測ることでエラー量を得ることができる。得られたログデータは保存されており、PHD2 Log Viewer等で確認できる。

得られたログデータをExcelで処理したものが以下。極軸は一分程度に合っており、架台上に60mmf4ガイドスコープのみが乗っている状態。

f:id:soiyadakara:20171109000405p:plain

いや、やっぱり大きすぎないですかね・・・?しかし、どちらも±25秒程度と言うことはこの値を信じるしかなさそう。

少し架台を分解して調整してみるか、もしくはAVXのPEC機能で補正することができるのだろうか・・・?

仙丈ヶ岳日帰り登山

先日の富士山登山から登山に対するモチベーションが上がったままで、次はどの山に登ろうかとずっと思案していたが、名古屋から日帰りで行ける山へ、ということで仙丈ヶ岳へ登山に行ってきた。

仙丈ヶ岳の一般的なルートとしては長野県伊那市の仙流荘からバスに乗り、北沢峠の登山口まで行くものがある。しかしこのルートを平日に行く場合バスの始発・終発時間が少し問題となってしまう。始発が8:05仙流荘発9:00北沢峠着、終発が16:00北沢峠発となり登山に費やせる時間は7時間。一方標準ルートでは「山と高原地図」に書かれているものを単純に足すと7時間30分となっている(!!)。

もっとも休日なら他の時間の始発もあるので問題がないが、天気のいい日にフラッと登山するタイプの自分にとっては問題である。これでは山頂での休憩時間もなく歩き詰めなのだろうか?いやまさかバスに乗り遅れ一泊野宿するはめになるのだろうか?

などといろいろ考えるはめになったが、結果を申すと問題なく日帰りできた。9時から登り始め問題なく登頂し、昼ご飯を十分ゆっくり食べ(10分ではない)、15:10頃には北沢峠まで到着していた。

天候にも恵まれ、稜線を歩くのは非常に気持ちがよかった。僕の登山に対するモチベーションもまだまだ上がりそう。

以下写真

f:id:soiyadakara:20170913075106j:plain

仙流荘からのバスに乗る。道中は陽気な運転手さんのガイド話を聞くことができる。バスの左側のほうが眺めはいいかな・・? f:id:soiyadakara:20170913095902j:plain

上り始めはしばらく森の中を行く。6合目の手前頃から視界が開け始める。視界が開けたとき、思わず「最高か・・・」と声が出てしまった・・非常に長めがよかった。 f:id:soiyadakara:20170913102616j:plain

目指す山頂方向には大きなカールが見える。 f:id:soiyadakara:20170913100140j:plain

振り返ると甲斐駒ヶ岳f:id:soiyadakara:20170913102403j:plain

あれは富士山だな!!手前は北岳だろう。 f:id:soiyadakara:20170913105021j:plain

山頂付近ではライチョウの兄弟に遭遇する。写真で見ると同化していて見つけづらいww f:id:soiyadakara:20170913111019j:plain

そして山頂。山頂の眺めは言わずもがな最高でした。 f:id:soiyadakara:20170913134311j:plain

帰り道に見つけた岩。何のことやらとしばらく考えてしまったが「ガンバレ」なのね。

NEX-5用DCカプラ

DCカプラとは、外部電源でカメラを利用したい場合、バッテリーの代わりに利用するもの。よく家電量販店のカメラサンプルでバッテリーの蓋からケーブルが延びているのがそれで、ACアダプター(等)を利用して給電できるようになる。インターバル撮影などで長時間の操作を行っているとバッテリーの残量が気になってしまうので、DCカプラによる給電は価値がある。

NEX,α用のカプラは公式ではAC-PW20というのがあるが、値段が八千円強しており高い。またカプラ部分とACアダプタがケーブルで直結しているので、コンセントからの利用を想定されており、カーバッテリー等から使おうとするとちょっと厳しい。

そんなことで、これを買った。

AC-PW20 Dummy Battery NP-FW50 Replacement DC Coupler External Power Supply Adapter for Sony A7 A7R A7000 NEX5 SLT Cameras f:id:soiyadakara:20170828152139p:plain

説明文を読むと、内部にovervoltage,overcurrent,short circuit protection回路が入っているそうで、心強い(?)。公式のバッテリー(NP-FW50)は7.2V出力だが、このDCカプラには7.6~8.5Vを入力してやればよいらしい。

ストックのあったHPH12002Mを利用して8V出力のDC-DC変換を作成し、シガーソケットからの給電ができるようにした。シャッター動作時は結構電流を食うので、最低でも1A程度の出力は必要と思われる。 f:id:soiyadakara:20170831171616j:plain

問題なく使えているが、唯一気になるところを挙げると外部電源なのにバッテリー残量が表示されている点がある。この残量は2分あたり1%程度の速度で徐々に減っていき、最後は電池マークの点滅になる。点滅のまま電源を付けっぱなしで1時間程度放置してみたが電源が落ちる事もなかったので、何の問題もなかろう。

満足!