IoT4Car
コンポーネントと消耗品
> |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
アプリとオンラインサービス
> |
| |||
| ||||
|
このプロジェクトについて
背景
ダッシュボードをちらっと見ながら車を運転しているときに、メーターの読み取り値を収集して分析を行うことを考えたことはありますか?これらのデータには、隠された財宝が含まれている可能性があります。個人の場合、それはあなたの運転習慣を反映することができ、あなたの速度、あなたの平均mpg、あなたが持っている信号機の数、そして各交差点でのあなたの待ち時間をあなたに伝えることができます。企業にとって、これらのデータは、フリート管理のリアルタイム監視にとって重要です。車両の状態、作業負荷の分散、ガソリン効率、さらには車両の位置さえも、クラウドを介して中央制御システムにフィードバックできます。企業は機械学習を使用してデータをトレーニングモデルにフィードし、コストを予測し、ドライバーの特性を分析することもできます。 IoTが広く普及しているため、上記のアプリケーションはそれほど遠くありません。 IoTアプリケーションを対象としたArduinoMKRボードを使用すると、車と通信してテレメトリデータをクラウドにアップロードするデバイスをすべて自分で構築できます。かっこいいじゃないですか?
車に話しかける
車両システムにアクセスするためのインターフェースが必要です。どこで車にハッキングできますか?答えはOBD-IIインターフェースです。
OBD-IIとは何ですか?
オンボード診断(OBD)は、車両に組み込まれた自己診断システムであり、これを介して車と通信することができます。これは1994年に米国で最初に導入され、1996年以降のすべての米国車両で要件となりました。カナダ、欧州連合の一部、日本、オーストラリア、ブラジルを含む他の国々も同様の法律を採用しました。 OBD-II(第2世代)には5つのシグナリングプロトコルがあり、コントローラーエリアネットワーク(CANバス)はそのうちの1つです。 CANバスは、2008年以降、すべての米国車に実装する必要があります。YoutubeでCSSElectronicsが提供するOBDIIのすばらしい紹介があります。このプロジェクトでは、16ピンOBD-IIインターフェースを介してデータにアクセスします。
私のコントローラー
Arduinoは、愛好家、メーカー、専門家にとって優れたプラットフォームです。さまざまなアプリケーションを対象としたさまざまなボードがあります。ここでは、WiFi機能があるためArduino MKR WiFi1000ボードを使用します。好きな他のボードを使うこともできます。 Arduino MKR GSM 1400をお勧めします。これは、GSMがWiFiよりもはるかに広いエリアをカバーしているからです。しかし、心配しないでください。WiFiを使用しても、道路に沿ってインターネットにアクセスできます。回避策を紹介します。
通訳委員会
Arduino自体にはたくさんのI / Oと多数のライブラリがありますが、OBDプロトコルをArduinoが認識できる言語に変換できるボードが必要です。私が使用しているボードはSparkFunOBD-IIUARTボードです。
このボードを使用すると、車のOBD-IIバスと接続できます。 ELM327コマンドセットを使用してシリアルインターフェイスを提供し、CANなどのすべての主要なOBD-II標準をサポートします。ボードにはSTN1110チップが含まれています。これは、現在使用されている任意のOBD-IIプロトコルとUARTの間でメッセージを変換するために使用できるOBDからUARTへのインタープリターです。
ただし、インタープリターボードのI / O電圧は5Vであるため、直接接続するとArduinoMKRボードのI / Oが損傷する可能性があることに注意してください。 Arduino MKR WiFI 1000はより低い電圧で動作し、そのI / O電圧は3.3Vです。したがって、信号を5Vから3.3Vに、またはその逆に変換するには、レベルシフターが必要です。以下は私が使用しているレベルシフトの画像です。
接続する
回路の接続は非常に簡単です。 ArduinoMRKのピン13Rxとピン14Txを、レベルシフターを介してOBD-IIUARTボードのTxピンとRxピンに接続するだけです。もちろん、2枚のボードのアースを接続する必要があります。
デバッグとデモンストレーションを容易にするために、LCD 1602画面をArduinoに接続して、データをリアルタイムで表示しました。 LCDからArduinoへの配線はこのチュートリアルに記載されているため、ここでは詳しく説明しません。
以下は、ブレッドボード接続の画像です。緑の線はArduinoとOBD-IIUARTボードを接続するワイヤー用で、黄色の線はArduinoとLCDを接続するワイヤー用です。回路図は添付ファイルにもあります。
ブレッドボードの面積が限られているため、実際の接続は少し面倒ですが、上記の回路図に従います。写真にはマイクロUSBとODB-II-DB9ケーブルを含めました。
Serial1はSerialではありません
さて、ArduinoMKRボードをプログラムする時が来ました。私のArduinoMKRボードはUARTを介して解釈ボードと通信するため、サードパーティのライブラリをインストールする必要はありません。インタープリターボードにコマンドを送信することは、シリアルモニターと通信するのと同じです。私が強調したいのは、ピン13とピン14に関連付けられているシリアルポートがシリアル1 であることだけです。 ! ArduinoMKRボードシリアルポート コンピュータとの通信に使用されるUSBポートを指します。 シリアル1 を初期化することを忘れないでください setup()関数のポート。
Serial1.begin(9600);
そして、シリアル1 を使用します コマンドを解釈ボードにプッシュします。
Serial1.println(message);
メッセージ
ご覧のとおり、コマンドを格納するために変数「message」を使用しています。 OBDコマンドは、ASCII文字で記述された16進コードで構成されています。最初の2つの16進数は、使用されるサービスモードを示します。最新のOBD-II標準SAEJ1979に記載されている10の診断サービスがあります。リアルタイムの監視に関心があるため、 01 のみを使用します このプロジェクトの現在のデータを表示するコード。
サービスモードの後の16進数は、特別な機能を実現するためのパラメーターID(PID)を表します。以下は、01サービスモードのPIDのスクリーンショットです。詳細については、ウィキペディアをご覧ください。
このプロジェクトでは、車の速度、エンジンのRPM、燃料レベル、およびエンジンの冷却水温度を取得する方法を示します。これら4つの機能のOBDコマンドは次のとおりです。
- 010D //車の速度
- 010C //エンジンRPM
- 012F //燃料レベル
- 0105 //クーラント温度。
データをデコードする
コマンドが送信されると、ArduinoMKRボードはシリアル1ポートで応答をリッスンします。コマンドを送信した後、200ミリ秒の遅延を設定することをお勧めします。次のコードを使用して応答を受信します。
void getResponse(void){while(Serial1.available()> 0){//メッセージの終わりの文字( '\ r')を受け取ったかどうかを確認することから始めます。 if(Serial1.peek()=='\ r'){//メッセージの最後に到達し、シリアルバッファをクリアしますinChar =Serial1.read(); rxData [rxIndex] ='\ 0'; //バッファインデックスをリセットして、次の文字が文字列の先頭に戻るようにしますrxIndex =0; } //メッセージ文字の終わりを取得できなかった場合は、新しい文字を文字列に追加するだけですelse {//シリアルポートから新しい文字を取得します:inChar =Serial1.read(); //新しい文字を文字列に追加し、インデックス変数を増やします。rxData[rxIndex ++] =inChar; }}}
通訳ボードからの応答はフォーマットに従います
"> 1回の繰り返しPIDデータ"
たとえば、上のスクリーンショットでは、車の速度を取得するために「010D」を送信しています。応答は "> 1 0D00"です。最初の5文字は、車がコマンドを受信し、PID0x0Dを繰り返すことを示しています。最後の2桁は、速度データ0x00を返します。
次に、「010C」を送信してエンジンRPMを取得します。応答「> 1 0C」はコマンドの確認応答を示し、データ0x098Cは16進数のエンジンRPM値の4倍です。 0x098C / 4 =611 decなので、エンジンのRPMは611rpmです。
その後、コマンド「012F」を送信して燃料レベルを取得し、データ0x1Dを取得します。燃料レベルは、0x1D / 255 * 100 =11%decとして計算されます。
最後のコマンドは「0105」で、クーラント温度は0x79になります。実際の温度は0x79-40 =81℃decです。その後、コマンドシーケンスが繰り返されます。
ご覧のとおり、応答行には2つの16進数の間にスペースがあり、最初の5桁は単にコマンドを繰り返しています。したがって、実際のデータは6番目の文字から始まります(最初のデータは0インデックスから始まります)。
プログラミングとデバッグでは、シリアルモニターが役立ちますが、実際のアプリケーションでは、LCD画面の方がポータブルであり、IoTの電力要件を満たしています。シリアルモニターを液晶画面に交換するだけで、車のデータをリアルタイムで監視できます。以下は、自分の車でプロジェクトを使用した写真です。
データをクラウド化する
UNOに対するArduinoMKRの利点は、インターネットへのアクセス性です。 Arduino MKRは、IoTアプリケーションを対象として、業界をよりインテリジェントで接続性の高いものにします。自動車用途では、屋外環境ではWiFi信号がまれであるため、MKR WiFi 1000は最適なボードではない可能性がありますが、私は携帯電話を個人のホットスポットとして使用しているため、問題ありません。
データを保存、表示、後処理するクラウドプラットフォームは他にもたくさんあります。好きなものを選ぶことができます。例としてdweet.ioとfreeboard.ioを使用します。 Dweet.ioは、データを送信できるAPIを提供します。 Freeboard.ioには、dweet.ioデータを取得して視覚化するためのハンドルがあります。 dweet.ioとfreebboard.ioを設定するためのチュートリアルがいくつかあるので、これ以上詳しく説明しません。興味がある場合は、ここにいくつかの例、例1、例2があります。
データプッシュコードは、dweetコマンドの作成方法を示す例として以下に示されています。
void httpRequest(){client.stop(); //フリーボードに送信するデータ文字列を作成しますif(client.connect(server、80)){Serial.println( "Connected");文字列データ="POST / dweet / for / mkr1000?RPM ="; data.concat(vRPM); //エンジンRPMをアップロードしますdata.concat( "&Speed ="); data.concat(vSpeed); //車の速度をアップロードしますdata.concat( "&Fuel ="); data.concat(vFuel); //燃料レベルdata.concat( "&Temp =");をアップロードしますdata.concat(vTemp); //クーラント温度をアップロードclient.println(data); client.println( "ホスト:https://www.dweet.io"); client.println( "接続:閉じる"); //接続の終了client.println(); } else {lcd.clear(); lcd.setCursor(0,0); lcd.println( "接続に失敗しました"); }}
freeboard.ioで、新しいダッシュボードを作成する必要があります。このダッシュボード内に、新しいデータソースを作成します。このデータソースを、コードで定義したdweet.ioのものにリンクします。私の場合はmkr1000です。データを表示するために使用する新しいゲージウィジェットを作成します。名前を付けて、変数の1つにリンクします。以下は私のダッシュボードのスクリーンショットです。速度、RPM、燃料レベル、および冷却水温度が表示されます。
結論
自分の車でボードを試してみましたが、うまくいきました。集積回路のすべての機能を含むPCBの設計に取り組んでいます。うまくいけば、私は将来もっと多くのチュートリアルを書くでしょう。ビデオデモも含めることができます。今回は申し訳ありませんが、車を運転するだけでなく、ビデオを撮ることもできませんでした。また、路上で運転しているときにコードをデバッグするときも注意が必要です。
このアプリケーションには、Arduino MKRWiFiボードで十分です。ボードがもっとあれば、MKR GSM1400ボードを試すことができると思います。このチュートリアルでは、他のIoTボードを自由に使用して、フィードバックをお聞かせください。
プロジェクトに取り組むことは楽しくて教育的です。問題をデバッグする感覚を楽しんでいます。私が知っていることをウェブ上で共有することも私の喜びです。読んでくれてありがとう。ご不明な点やご意見がございましたら、お気軽にお問い合わせください。
コード
- IoT4Car_code
IoT4Car_code C / C ++
このプログラムは、OBDII-UARTボードを使用して車両と通信し、結果をLCDに表示して、フリーボードIoTプラットフォームにアップロードします/ ** OBDII-UART-Serialバージョン9 *このプログラムは、OBDIIを使用して車両と通信します-UARTボード、*および結果をLCDに表示し、フリーボードIoTプラットフォームにアップロードします* *作成者:zhaoshentech *更新:2018-08-27 * *更新:* v3:バッファが受信するようにgetResponse()関数を変更しました正しい応答。* getRPM()を追加して車両からエンジンRPMを取得します。* v4:getSpeed()関数を追加して車両の速度を取得します* v5:LCDモジュールを追加し、速度とRPMをLCD * v6:はwifiバージョンです* v7:は非wifi、非シリアルバージョンです。シリアル初期化*を削除して、ボードがコンピューターなしで動作できるようにします。* v8:は、非Wi-Fi、非シリアルバージョンです。燃料レベルと冷却水温度を追加します。*表示場所を再配置します。* v9:Wi-Fi、非シリアルバージョンです。 Upolad速度、RPM、燃料レベル、クーラント温度* * LCD回路接続:* LCDRSピンからデジタルピン12 * LCDイネーブルピンからデジタルピン11 * LCDD4ピンからデジタルピン5 * LCDD5ピンからデジタルピン4 * LCD D6ピンからデジタルピン3 * LCDD7ピンからデジタルピン2 * LCD R / Wピンからアース* 10 K電位計:*エンドは+ 5V、アース*ワイパーからLCD VOピン(ピン3)* ///// ////////////////////////////////////////////////// ////// WiFi関連////////////////////////////////////////// ///////////////#include#include char ssid [] ="YOUR WIFI SSID"; // Wi-Fi IDchar pass [] ="YOUR WIFI PSWD"; // Wi-Fi passwordchar server [] ="www.dweet.io"; //乾舷とdweetの設定unsignedlong lastConnectionTime =0; //最後の接続時間を追跡しますconstunsigned long postInterval =10L * 1000L; // 10秒ごとにデータを投稿するWiFiClientclient; // Wi-Fiクライアントを初期化しますintstatus =WL_IDLE_STATUS; // WiFi無線ステータス// LDCを含めるlibaray#include const int rs =12、en =11、d4 =5、d5 =4、d6 =3、d7 =2; LiquidCrystal lcd(rs、 en、d4、d5、d6、d7); //これは、シリアルポートからのデータを格納する文字バッファーです:char rxData [20]; char rxIndex =0; char inChar =0; String message; //変数速度とRPMデータを保持するには:int vSpeed =0; int vRPM =0; int vFuel =0; int vTemp =0; void setup(){// LCDの列と行の数を設定します:lcd.begin( 16,2); lcd.clear(); //シールドの存在を確認します:if(WiFi.status()==WL_NO_SHIELD){lcd.println( "WiFi not ready"); while(true); } // WiFiネットワークへの接続を試みます:while(status!=WL_CONNECTED){lcd.clear(); lcd.setCursor(0、0); lcd.println( "WiFiに接続しています...");ステータス=WiFi.begin(ssid、pass); //接続を5秒間待ちます:delay(5000); } lcd.setCursor(0、1); lcd.println( "Connected!"); // Serial1は、車両Serial1.begin(9600);と通信するための実際のポートです。 resetBuffer();} void loop(){while(status!=WL_CONNECTED){lcd.clear(); lcd.setCursor(0,0); // WPA / WPA2 Wi-Fiネットワークに接続しますSerial.println( "Connecting to Wifi"); lcd.println( "Connect WiFi ...");ステータス=WiFi.begin(ssid、pass); //接続delay(5000);を10秒間待ちます} getSpeed(); getRPM(); getFuel(); getCoolTemp(); if(millis()-lastConnectionTime> postInterval){httpRequest(); lastConnectionTime =millis(); }} // getRPMデータは「010C」コマンドをSerial1ポートに送信し、// getResponse()を呼び出してデータを収集します。次に、RPMデータをシリアルモニターに出力します。voidgetRPM(void){message ="010C"; Serial1.println(メッセージ); delay(200); //現在の行をクリアします(int i =8; i <16; ++ i){lcd.setCursor(i、0); // 0行、i列lcd.write( ''); } lcd.setCursor(8,0); // LCD画面の最初の行後半//応答を待つgetResponse(); // RPM応答を4で割ると、正しい値が得られます。 vRPM =((strtol(&rxData [6]、0,16)* 256)+ strtol(&rxData [9]、0,16))/ 4; lcd.print(vRPM); lcd.print( "rpm");} void getSpeed(void){message ="010D"; Serial1.println(メッセージ); delay(200); //現在の行をクリアします:for(int i =0; i <8; ++ i){lcd.setCursor(i、0); // 0行、i列lcd.write( ''); } lcd.setCursor(0,0); // LCD画面の前半の最初の行//車からの応答を待つgetResponse(); vSpeed =strtol(&rxData [6]、0、16); // km / h単位vSpeed =vSpeed * 0.621371; // mphの単位でlcd.print(vSpeed); lcd.print( "mph");} void getFuel(void){message ="012F"; Serial1.println(メッセージ); delay(200); //現在の行をクリアします:for(int i =0; i <8; i ++){lcd.setCursor(i、1); // 1行目、i列lcd.write( ''); } lcd.setCursor(0、1); // LCD画面の前半の2行目//車からの応答を待ちますgetResponse(); vFuel =strtol(&rxData [6]、0、16); // 255のスケールで// vFuel =244; //使用法をデバッグしますvFuel =1.0 * vFuel / 255 * 100; // 100のスケールでlcd.print(vFuel); lcd.print( "%"); //Serial.println(vFuel); //デバッグの使用法} void getCoolTemp(void){message ="0105"; Serial1.println(メッセージ); delay(200); //現在の行をクリアします:for(int i =8; i <16; i ++){lcd.setCursor(i、1); // 1行目、i列lcd.write( ''); } lcd.setCursor(8、1); // LCD画面の後半の2行目//車からの応答を待ちますgetResponse(); vTemp =strtol(&rxData [6]、0、16); // Cの単位ですが、40度オフセットされていますvTemp =vTemp-40; // 0でオフセットlcd.print(vTemp); //摂氏Cを出力しますlcd.write(0xDF); lcd.print( "C");} // getResponse関数は、UARTからrxDataバッファーに着信データを収集します//応答が転送されると終了します。キャリッジリターン文字列が検出されると、rxDataバッファはnullで終了し(文字列として扱うことができるように)//次の文字列をコピーできるようにrxDataインデックスが0にリセットされます。voidgetResponse(void ){while(Serial1.available()> 0){//メッセージの終わり文字( '\ r')を受信したかどうかを確認することから始めます。 if(Serial1.peek()=='\ r'){//メッセージの最後に到達し、シリアルバッファをクリアしますinChar =Serial1.read(); rxData [rxIndex] ='\ 0'; //バッファインデックスをリセットして、次の文字が文字列の先頭に戻るようにしますrxIndex =0; } //メッセージ文字の終わりを取得できなかった場合は、新しい文字を文字列に追加するだけですelse {//シリアルポートから新しい文字を取得します:inChar =Serial1.read(); //新しい文字を文字列に追加し、インデックス変数を増やします。rxData[rxIndex ++] =inChar; }}} void resetBuffer(void){for(int i =0; i <20; i ++){rxData [i] =0; }} void httpRequest(){client.stop(); //フリーボードに送信するデータ文字列を作成しますif(client.connect(server、80)){Serial.println( "Connected");文字列データ="POST / dweet / for / mkr1000?RPM ="; data.concat(vRPM); //エンジンRPMをアップロードしますdata.concat( "&Speed ="); data.concat(vSpeed); //車の速度をアップロードしますdata.concat( "&Fuel ="); data.concat(vFuel); //燃料レベルdata.concat( "&Temp =");をアップロードしますdata.concat(vTemp); //クーラント温度をアップロードclient.println(data); client.println( "ホスト:https://www.dweet.io"); client.println( "接続:閉じる"); //接続の終了client.println(); } else {lcd.clear(); lcd.setCursor(0,0); lcd.println( "接続に失敗しました"); }}
回路図
Arduino MKR WiFi 1000、SparkFun OBD-II UARTボード、SparkFunロジックレベルシフター、およびLCD1602を接続します製造プロセス