車のHUD-スピードとコンパス用のフロントガラスディスプレイ
コンポーネントと消耗品
> ![]() |
| × | 1 |
このプロジェクトについて
説明:
欲しかった! 私は友人のTaggiの大きな車がフロントガラスに速度を表示していることに気づいていました。私はそれを持っていなければなりませんでした、もちろん私はそれを自分で作らなければなりませんでした!
<図>
ソフトウェアの観点:
回路の投影で最も困難で混乱を招く部分は、7セグメントディスプレイをBCDデコーダーに接続することです。これは、数値が「反転」しているためです (ミラーリング)。
3つのボタンがあります :+および-明るさおよびS / Hを増減して、速度(km / h)と方位(度)を切り替えます。;ヘディングの場合も赤いLEDが点灯し、移動時のコンパスの「度」(1〜360°)を意味します。これは電子コンパスではなく、GPSであり、移動する方向の正しい情報を取得するには移動する必要があります。 明るさはEEPROMに保存されます 1分後のメモリ。明るさは、PWMピンを介してディスプレイとLEDの両方で変更されます。
コードの重要な部分は、GPSからデータ、主に速度と方向を収集し、すべてのNMEAセンテンスからデータを取得することです。 Stringクラスを使用しても、主にシリアルNMEAセンテンスの操作に使用されますが、エラボレーションフロー全体は安定していて安定しています。 「serialEvent()」を使用して、 GPSから1秒に1回データを受信します 、「nmeaExtractData()」を呼び出し、最後に「nmea0183_checksum()でデータパケットをチェックして、データの整合性を確認します。別のメーカーとモデルのGPSを使用する場合は、文が同じ構造であるか、ここで変更を加える必要があります。 。たとえば、EM406Aは「$ GPRMC」パケットIDを使用し、 BT220は「$ GNRMC」を使用します 代わりに...小さな名前の変更...便利なリンクがチェックサムテストに役立ちます:https://nmeachecksum.eqth.net-完全なNMEAセンテンスの例として、id、time、validity、緯度、経度、速度、実際のコース、日付、変動、チェックサム。
$ GNRMC、095836.000、A、4551.9676、N、01328.7118、E、2.09、341.84、280519 、、 * 08
スケッチは、すべての単一ディスプレイBCDデコーダーのラッチを一度に1つずつ有効にし、バイナリ4ビットバスのコード番号を設定し、値が変更されたときにラッチを無効にするなどを提供します。左側の重要でないゼロは空白になります(表示されません)。
新しいスケッチを読み込む前 から MCU ジャンパーを取り出すことを忘れないでください :ArduinoのRxピンに接続されており、ロード中はGPSTxと確実に競合します。ソフトウェアをロードした後、ジャンパーを再度配置して、通常の機能を復元します。
<図>
コンポーネントリスト:
- 1 x MCU Arduino Nano
- 3 x 5161、7セグメントディスプレイ、共通カソード、赤
- 1 x BeitianBN-220シリアルTTLGPS(1 Hz GNRMCセンテンス)
- 1xジャンパー
- 3 xボタン(通常は開いている)+ 3xキャップ
- 22 x 1 / 4W220オーム抵抗器
- 1 x 3mm LED、赤
- 2 x100nコンデンサ
- 3 x 14511BCDデコーダー+ラッチ
- 1 xUSB「B」メス
- 1 xストリップライン2xオスピン(ジャンパー用)
- 1 xストリップライン4xオスピンで90°に曲げる(GPSの場合)
- GPSをオンボードの4ピンに接続する1xピグテールケーブル
- 2つのPCBをスタックに結合するための22個のストリップラインオスピン
- GPSをPCBに貼り付けるための両面接着剤1個
- 6 x回転ストリップライン5xメスピン(ディスプレイ用)
- 50x銅0.6mmリベット
- 8 xM3ネジ
- 高さ20mmのM3メスタワー4個
- 1 xプラスチックの箱+カバー(以下の印刷可能な3Dファイルをご覧ください)
PCB(プリント回路基板):
2つの両面PCBを使用しました そのため、回路全体のルートを解決するために、約50個のパススルーリベットまたはピンが使用されます。 5つの位置合わせ穴もあります 最初に作る。私はすべてのPCBにこれらの5つの位置合わせスポットを設計しました。ダウンロードセクションには、すべてのPCBファイル、コンポーネント、はんだ面がミラーリングされており、「黄色」または「青色」のシートにレーザープリンタを使用してダウンロードおよび印刷できます;今回は青いものを使用しましたが、黄色のものも良くて値段も安いです。青いシートの方が優れていると言わざるを得ません...印刷するときは、トナー節約設定を無効にすることを忘れないでください。代わりに1200 dpiの解像度を使用して、深いリアルブラックの結果を得ることができます。マジックシートからPCBへのトナー転写プロセスは、ホットアイアンを使用して行われます...ネットの周りには、優れたPCBの製造方法を示すチュートリアルがいくつかあります。 しかし、これらの重要なポイントを覚えておいてください:キッチンの鮮やかなスポンジで銅を完全にきれいにし、わずかに磨きます、5分間のアイロンがけ、水熱衝撃、5つの穴による2つの面の位置合わせ(大きな白いLED表面に5つのピンを使用して確認しました)穴)、腐食プロセス中にもう一方の面を保護します。コンポーネントの面も印刷すると、プロジェクトは「プロフェッショナル」になります:-)
<図>
注: 写真は最後のバージョンより前に撮影されたものです。つまり、最終バージョンのPCBの周りの奇妙な緑色のワイヤーが消えるか、ジャンパーがPCB#1からPCB#2に移動したなど、いくつかの詳細が異なる可能性があります。 GPSは9600bpsの速度、1Hz GNRMCNMEAセンテンスのみでセットアップする必要があります。これは、独自のセットアップソフトウェアを使用して作成できます。最後に、USBメスコネクタをはんだ付けする前に、その下にあるコンポーネント側の銅線との不要な接触を避けるために、その下に小さな絶縁テープを貼り付けます。
<図>
<図>

初めて力を与えると、フロントガラス(鏡)に映っている数字を見るように作られているので、数字が「読めない」ことに気付くでしょう。 運転席前の車に取り付けます 、快適な場所を見つけて修正します。 USB電源ケーブルを接続することをお勧めします イグニッションポジションの後、エンジンのオン/オフ時にオン/オフが切り替わります。それはすべてご列席の皆様です!
ニュースと改善点:
- 25.03.2021:プロジェクトを完了するために、2つの3D.STLモデルを準備しました このリンク:https://grabcad.com/library/car-hud-1では、このプロジェクトの成形ボックスと彼のカバーパネルに関するファイルをダウンロードして、3Dプリンターで印刷できます。
- 20.07.2021:V2(バージョン2)が利用可能です 2本のワイヤーと1本の抵抗器と一緒に数千メートル(つまり0.89 =890m)としてディスプレイに高度情報が表示されるソフトウェアの場合、もう一度S / Hボタンを押すだけです!手順:1)独自のソフトウェアを使用して$ GNGGAnmeaセンテンスも出力するようにGPSを設定します。 2)d5 Arduino pwmピンと回路の残りの部分の間の実際の接続を切断し、220オームの抵抗で中央ディスプレイの10進ドットピンに接続します。 3)d13Arduinoデジタルピンをd5が接続された回路に接続します。以下のいくつかの写真を見てください。 4)ArduinoNanoにV2スケッチをインストールします。
- 01.11.2021:バージョンV2.2の準備ができています あなたのために。何が新しい?高度(V2にはすでに存在)について、hudは999mまでの完全な値を示し、1000mから始まり、千単位の値、つまり1.24(1240m)または2.02(2020m)を示します。 LDRフォトレジスターGL5539、10kOhm抵抗、2本のワイヤーを追加しました。下の写真でV2.2modを確認し、ArduinoにV2.2スケッチをインストールしてください。 LDRセンサーは、光のレベル(昼、曇り、夜)に応じて、明るさを自動的に変更します(3レベル)。ボタン(+)と(-)は、明るさを手動で変更するために引き続き機能します。これらは優先されます。自動に戻すには、(+)ボタンと(-)ボタンを同時に押します。輝度レベルを保存するための内部EEPROMは使用されなくなりました。最初にV2Modを実行しましょう!




<図>




<図>

運転するときは、運転するだけです。安全運転!
コード
- 車-HUDArduinoスケッチ
車-HUDArduinoスケッチ Arduino
/ *このスケッチは車のウインドスクリーンHUD(ヘッドアップディスプレイ)として機能します。MarcoZonca、2020年10月にCPUとしてArduino Nano、1秒ごとにGPS BT-220 nmea、3 xボタン、3 x7セグメントディスプレイ共通カソード、3 x 14511 BCDラッチデコーダ、MPU EEPROMメモリ(1バイト)および多くの抵抗。警告:=======ソフトウェアを更新する前に、JUMPER * /#include#include String inputString =""を使用してArduino(GPSからTX)のRXピンを切断します。; String nm_time ="00:00:00"; String nm_validity ="V"; String nm_latitude ="dd°mm.mmmm'N"; String nm_longitude ="ddd°mm.mmmm'E"; String nm_knots ="0.0 kn "; float nmf_knots =0.0; float nmf_kmh =0.0; int nmi_kmh =0; String nm_truecourse =" 360 "; float nmf_truecourse =360; String nm_date =" dd / mm / yyyy "; int nmi_truecourse =0; byte kCent =0; byte kDeci =0; byte kUnit =0; byte tCent =0; byte tDeci =0; byte tUnit =0; byte bright =120; byteラッチオフ=HIGH;バイトlatch_on =LOW; int n =0; unsigned long lastmemcheck =0; unsigned long memcheck =60000; // 60秒ごとにEEPROMに「明るさ」の値を保存するようにチェックしますboolstringComplete =false; bool isKMH =true; bool ret =false; const int disp001 =2; //単位はlatchconstint disp010 =8を表示します; //数十の表示latchconstint disp100 =12; //数百の表示latchconstint disp001dim =9; //ユニットは調光器/オフを表示しますpinconstint disp010dim =10; //数十の表示調光器/オフpinconstint disp100dim =11; //数百のディスプレイ調光器/オフpinconstint button_kt =14; // kmh / truecourse buttonconst int button_more =15; //明るさ+ buttonconst int button_less =16; //明るさ-buttonconstintdegreesLED =3; //度LEDconstint bit_3 =7; //ビット3constint bit_2 =6; //ビット2constint bit_1 =5; //ビット1constint bit_0 =4; //ビット0constint dly =10; //ラッチの遅延m / secconstバイトオフ=0; // bright ==0const int addr =0と同じ; //輝度値のEEPROMアドレスconstバイト番号[10] [4] ={{0,0,0,0}、{1,0,0,0}、{0,1,0,0}、{1、 1,0,0}、{0,0,1,0}、{1,0,1,0}、{0,1,1,0}、{1,1,1,0}、{0、 0,0,1}、{1,0,0,1}}; //ビット0,1,2,3voidsetup(){Serial.begin(9600); Wire.begin(); inputString.reserve(200);明るさ=EEPROM.read(addr); if(明るさ> 250 ||明るさ<10){明るさ=120; } //最初のEEPROMからの異常な値を回避する読み取りpinMode(disp001、OUTPUT); pinMode(disp010、OUTPUT); pinMode(disp100、OUTPUT); pinMode(degreesLED、OUTPUT); pinMode(button_kt、INPUT_PULLUP); pinMode(button_less、INPUT_PULLUP); pinMode(button_more、INPUT_PULLUP); pinMode(bit_3、OUTPUT); pinMode(bit_2、OUTPUT); pinMode(bit_1、OUTPUT); pinMode(bit_0、OUTPUT); analogWrite(disp001dim、off); //オフとゼロはanalogWrite(disp010dim、off);を表示しますanalogWrite(disp100dim、off); analogWrite(degreesLED、off); setBusNr(0); digitalWrite(disp001、latch_on); digitalWrite(disp010、latch_on); digitalWrite(disp100、latch_on); delay(dly); digitalWrite(disp001、latch_off); digitalWrite(disp010、latch_off); digitalWrite(disp100、latch_off); analogWrite(disp001dim、明るさ); //ディスプレイにanalogWrite(disp010dim、brightness); analogWrite(disp100dim、brightness);} // setup()void loop(){// GPS NMEA ------------------ if(stringComplete ==true){//シリアルポートRXでnmeaセンテンスを受信ret =nmeaExtractData(); inputString =""; stringComplete =false; if(ret ==true){kCent =nmi_kmh / 100; n =nmi_kmh-(kCent * 100); kDeci =n / 10; n =nmi_kmh-(kCent * 100)-(kDeci * 10); kUnit =n; tCent =nmi_truecourse / 100; n =nmi_truecourse-(tCent * 100); tDeci =n / 10; n =nmi_truecourse-(tCent * 100)-(tDeci * 10); tUnit =n;画面(); }} if(millis()>(lastmemcheck + memcheck)){//メモリの明るさの値を入力(変更されている場合)EEPROM.update(addr、brightness); lastmemcheck =millis(); } checkButtons();} void display(){if(isKMH ==true){// km / h単位の速度(isKMH =true)analogWrite(degreesLED、off); setBusNr(kUnit); digitalWrite(disp001、latch_on); delay(dly); digitalWrite(disp001、latch_off); if(kDeci> 0 || kCent> 0){// tens =0(およびundreds =0)の場合は10をオフに設定しますsetBusNr(kDeci); digitalWrite(disp010、latch_on); delay(dly); digitalWrite(disp010、latch_off); analogWrite(disp010dim、明るさ); } else {analogWrite(disp010dim、off); } if(kCent> 0){//数をオフに設定if =0 setBusNr(kCent); digitalWrite(disp100、latch_on); delay(dly); digitalWrite(disp100、latch_off); analogWrite(disp100dim、明るさ); } else {analogWrite(disp100dim、off); }} else {//度単位の真の方位(isKMH =false)analogWrite(degreesLED、brightness); setBusNr(tUnit); digitalWrite(disp001、latch_on); delay(dly); digitalWrite(disp001、latch_off); if(tDeci> 0 || tCent> 0){// tens =0(およびundreds =0)の場合は10をオフに設定しますsetBusNr(tDeci); digitalWrite(disp010、latch_on); delay(dly); digitalWrite(disp010、latch_off); analogWrite(disp010dim、明るさ); } else {analogWrite(disp010dim、off); } if(tCent> 0){//数をオフに設定if =0 setBusNr(tCent); digitalWrite(disp100、latch_on); delay(dly); digitalWrite(disp100、latch_off); analogWrite(disp100dim、明るさ); } else {analogWrite(disp100dim、off); }}} // display()void checkButtons(){if(digitalRead(button_kt)==LOW){if(isKMH ==true){isKMH =false; } else {isKMH =true; } delay(250); } if(digitalRead(button_more)==LOW){if(brightness <=240){brightness =brightness + 10; } analogWrite(disp001dim、brightness); analogWrite(disp010dim、明るさ); analogWrite(disp100dim、明るさ); delay(100); } if(digitalRead(button_less)==LOW){if(brightness> =20){brightness =brightness-10; } analogWrite(disp001dim、brightness); analogWrite(disp010dim、明るさ); analogWrite(disp100dim、明るさ); delay(100); }} // checkButtons()void setBusNr(int number){// 4ビットバスを(byte b =0; b <=3; b ++){if(numbers [number] [b] ==0){if (b ==0){digitalWrite(bit_0、LOW);} if(b ==1){digitalWrite(bit_1、LOW);} if(b ==2){digitalWrite(bit_2、LOW);} if(b ==3){digitalWrite(bit_3、LOW);}} else {if(b ==0){digitalWrite(bit_0、HIGH);} if(b ==1){digitalWrite(bit_1、HIGH);} if( b ==2){digitalWrite(bit_2、HIGH);} if(b ==3){digitalWrite(bit_3、HIGH);}}}} // setBusNr()// nmea inputStringbool nmeaExtractData(){intからデータを抽出d =0; int s =0; int y =0; int z =0; float t =0; bool ret =false; // nmeaセンテンス=$ GNRMCおよび有効なCHKSUMの場合はtrueif((inputString.substring(0,6)=="$ GNRMC")&&(inputString.substring(inputString.length()-4、inputString.length()- 2)==nmea0183_checksum(inputString))){y =0; for(s =1; s <11; s ++){y =inputString.indexOf( "、"、y); switch(s){case 1:// ----------------------- time z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_time =inputString.substring(y + 1、y + 2 + 1)+ ":" + inputString.substring(y + 1 + 2、y + 4 + 1)+ " : "+ inputString.substring(y + 1 + 4、y + 6 + 1); } y =z;壊す;ケース2:// ----------------------- validity z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_validity =inputString.substring(y + 1、y + 1 + 1); } y =z;壊す;ケース3:// ----------------------- latitude z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_latitude =inputString.substring(y + 1、y + 2 + 1)+ "°" + inputString.substring(y + 1 + 2、y + 10 + 1)+ " '"; } y =z;壊す;ケース4:// ----------------------- north / south z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_latitude =nm_latitude + inputString.substring(y + 1、y + 1 + 1); } y =z;壊す;ケース5:// ----------------------- longitude z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_longitude =inputString.substring(y + 1、y + 3 + 1)+ "°" + inputString.substring(y + 1 + 3、y + 11 + 1)+ " '"; } y =z;壊す;ケース6:// ----------------------- east / west z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_longitude =nm_longitude + inputString.substring(y + 1、y + 1 + 1); } y =z;壊す;ケース7:// -----------------------速度ノットz =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nmf_knots =inputString.substring(y + 1、z).toFloat(); t =roundOneDec(nmf_knots); nm_knots =String(t、1)+ "kn"; nmf_kmh =roundTwoDec(nmf_knots * 1.852); nmi_kmh =roundZeroDec(nmf_knots * 1.852); } y =z;壊す;ケース8:// ----------------------- trueコースz =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nmf_truecourse =inputString.substring(y + 1、z).toFloat(); t =roundZeroDec(nmf_truecourse); nmi_truecourse =t; d =t; nm_truecourse =d; } y =z;壊す;ケース9:// ----------------------- date z =inputString.indexOf( "、"、y + 1); if(z>(y + 1)){nm_date =inputString.substring(y + 1、y + 2 + 1)+ "/" + inputString.substring(y + 1 + 2、y + 4 + 1)+ " / 20 "+ inputString.substring(y + 1 + 4、y + 6 + 1); } y =z;壊す;ケース10://ステートメントn.u.壊す;デフォルト://ステートメントn.u.壊す; }} ret =true; } return ret;} // nmeaExtractData()/ * SerialEventは、新しいデータがハードウェアシリアルRXに到着するたびに発生します。このルーチンは、loop()が実行されるたびに実行されるため、ループ内でdelayを使用すると、応答が遅れる可能性があります。複数バイトのデータが使用可能な場合があります。* / voidserialEvent(){while(Serial.available()){char inChar =(char)Serial.read(); inputString + =inChar; //着信文字が改行の場合、メインループが//それに対して何かを実行できるようにフラグを設定しますif(inChar =='\ n'){stringComplete =true; }}} // serialEvent()// nmeaのチェックサムを計算するsentenceStringnmea0183_checksum(String nmea_data){int crc =0;文字列chSumString =""; int i; //最初の$記号を無視し、文のチェックサムfor(i =1; i <(nmea_data.length()-5); i ++){// "*" + cksum + cr +がない場合は-5を削除しますlfが存在しますcrc ^ =nmea_data [i]; } chSumString =String(crc、HEX); if(chSumString.length()==1){chSumString ="0" + chSumString.substring(0,1); } chSumString.toUpperCase(); return chSumString;} // nmea0183_checksum(String nmea_data)//ラウンドゼロdecimalsfloat roundZeroDec(float f){float y、d; y =f * 1; d =y-(int)y; y =(float)(int)(f * 1)/ 1; if(d> =0.5){y + =1; } else {if(d <-0.5){y- =1; }} return y;} // 1つのdecimalfloatを丸めるroundOneDec(float f){float y、d; y =f * 10; d =y-(int)y; y =(float)(int)(f * 10)/ 10; if(d> =0.5){y + =0.1; } else {if(d <-0.5){y- =0.1; }} return y;} // 2つの小数を丸めるfloatroundTwoDec(float f){float y、d; y =f * 100; d =y-(int)y; y =(float)(int)(f * 100)/ 100; if(d> =0.5){y + =0.01; } else {if(d <-0.5){y- =0.01; }} return y;}
回路図










製造プロセス