X-Plane コクピットの自作に挑戦!
個別のプログラミング例
まず、このB777 Wordlinerプロジェクトでは5つの種類の部品が使われています。
まず、このB777 Wordlinerプロジェクトでは5つの種類の部品が使われています。
- トグルスイッチ
- タクトスイッチ(大)
- タクトスイッチ(小)
- ロータリーエンコーダ
- LED
ドグルスイッチ
このスイッチはON/OFFとスイッチを移動するたびに切り替わります。
FD_ToggleSW.ino
//トグルスイッチでON/OFF #include <Bounce.h> const int FDSwPin = 0; Bounce FDir_toggle_switch = Bounce(FDSwPin, 5); // ピン0のトグルスイッチ、5msデバウンス FlightSimInteger FlightDirector; elapsedMillis inactivityTimeout;// 無活動タイムアウト // setupはTeensyが起動するときに一度だけ実行されます。 // void setup() { // すべてのハードウェアを初期化する pinMode(FDSwPin, INPUT_PULLUP); // 入力がプルアップ・モードの場合これを使う。 FlightDirector = XPlaneRef("anim/34/switch"); //datarefのパスをここに入れる。 } void loop() { // 通常、loop()の最初のステップは、X-Planeを更新する必要があります。 FlightSim.update(); // スイッチを押したボタンの状態を読んで、X-Planeコマンドを送る作業をここで行う。 FDir_toggle_switch.update(); //スイッチのON/OFFのコントロール if (FDir_toggle_switch.fallingEdge()) { //押し下げエッヂを読む FlightDirector = 1; // inactivityTimeout = 0; //無活動タイムアウトをリセット(スイッチのチャタリングを防止するため) } if (FDir_toggle_switch.risingEdge()) { //押し上げエッヂを読む FlightDirector = 0; } }
トグルスイッチは全て同じスクリプトになります。datarefを変更すればそのまま使えます。但し、他のスイッチと同じプラグラム内で使用する場合はTeensyのピン番号、変数名を変更する必要があります。
- A/T ARM トグルスイッチdataref:anim/33/switch
タクトスイッチ(大:12mm)
このタクトスイッチと言われているプッシュスイッチは押すとON、離すとOFFになり、ONを維持することができません。
コマンドを使用する場合に便利なスイッチになります。PCのキーと同じような原理のスイッチです。
コマンドを使用する場合に便利なスイッチになります。PCのキーと同じような原理のスイッチです。
A_P_SW.ino
//タクトスイッチ #include <Bounce.h> elapsedMillis inactivityTimeout;// 無活動タイムアウト const int APSwPin = 2; Bounce APSwitch = Bounce(APSwPin, 5); FlightSimInteger AutoPilotSW; void setup() { pinMode(APSwPin, INPUT_PULLUP); AutoPilotSW = XPlaneRef("anim/13/button"); } void loop() { FlightSim.update(); APSwitch.update(); if (APSwitch.fallingEdge()) { AutoPilotSW = 2; inactivityTimeout = 0; } if (APSwitch.risingEdge()) { AutoPilotSW = 0; } }
以下のA/Tスイッチは上と違って押し上げたときもONの状態を維持する必要があります。datarefの動きがそうなっています。トグル状態です。
チョット複雑になって以下のようなプログラムになります。
チョット複雑になって以下のようなプログラムになります。
A_T_SW.ino
//タクトスイッチ //このスイッチは押上げした時点でONを保持する必要があります。普通はOFFになるのでONを維持するために一工夫が必要になります。 //Serial.println(AT_SwPin_state)でシリアルモニタを使うと「1」が表示されるので理解できると思う。 #include <Bounce.h> elapsedMillis inactivityTimeout;// 無活動タイムアウト const int AT_SwPin = 4; Bounce AT_Switch = Bounce(AT_SwPin, 5); int AT_SwPin_val = 0; int AT_SwPin_old_val = 0; //古い int AT_SwPin_state = 0; FlightSimInteger AT_SW; void setup() { Serial.begin(9600); pinMode(AT_SwPin, INPUT_PULLUP); //BUTTONは入力に設定 AT_SW = XPlaneRef("anim/15/button"); } void loop() { FlightSim.update(); //スイッチの保持設定スクリプト。 AT_SwPin_val = digitalRead(AT_SwPin); //ピンの状態、HIGHかLOWを読む if((AT_SwPin_val == LOW) && (AT_SwPin_old_val == HIGH)) { //最初のピン状態がLOWで古いピン状態がHIGHなら(これはタクトスイッチの押下、押上を実行した状態) AT_SwPin_state = 1 - AT_SwPin_state; // 1-0=1 //delay(10); //チャタリングが起きるとき使用。 Serial.println(AT_SwPin_state) ; } AT_SwPin_old_val = AT_SwPin_val; //AT_SwPin_valは0、AT_SwPin_old_valは1になる。 if(AT_SwPin_state == 1){ //もしAT_SwPin_stateが1なら AT_SW = 1; //datarefの値を1にする } else { AT_SW = 0; //datarefの値を0にする } }
タクトイッチは単純な単一のdatarefだけで動作していて、ほとんどはこのタイプのプログラムが使われています。但し、他のスイッチと同じプラグラム内で使用する場合はTeensyのピン番号、変数名を変更する必要があります。
この中でdatarefをどうしても見つけることができないスイッチがあります。
- LNAV タクトイッチdataref:anim/16/button
- VNAV タクトイッチdataref:anim/17/button
- FLCH タクトイッチdataref:anim/18/button
- IAS/MACH チェンジ タクトイッチdataref:anim/19/button
- TOGA タクトイッチdataref:anim/149/button
- DISC タクトイッチdataref:anim/176/button
- HDG BANK LIMIT 切り替えタクトイッチdataref:anim/176/button
- HDG HOLD タクトイッチdataref:anim/22/button
- HDG/TRK 切り替え タクトイッチdataref:anim/68/switch
- V/S FPA タクトイッチdataref:anim/24/button
- V/S FPA 切り替え タクトイッチdataref:anim/23/button
- ALT HOLD タクトイッチdataref:anim/25/button
- LOC タクトイッチdataref:anim/26/button
- APP タクトイッチdataref:anim/27/button
- AUTO 1000 セレクト タクトイッチdataref:anim/69/switch
この中でdatarefをどうしても見つけることができないスイッチがあります。
- CLB CON タクトイッチdataref:不明(どのdatarefが動作しているのかがわからない。)使い方もよくわかりません。ほとんどの場合、このスイッチは使用しないと書いてあったので無視しています。
- LNAVの右横にある小さなスイッチ、これは名前すら書いてないがスイッチを押すと動作はしています。
ロータリーエンコーダ(プッシュスイッチ付き)
Speed_RotaryEncoder.ino
//プッシュ付きロータリーエンコーダ、B777Wordliner。 #include <Encoder.h> #include <Bounce.h> elapsedMillis inactivityTimeout;// 無活動タイムアウト //=============================================================================================== /////////// AirSeed エンコーダ ////////////////// //=============================================================================================== const int AirSpEncR_pin = 9; //エンコーダ teensy5ピンを使用 const int AirSpEncL_Pin = 10; //エンコーダ teensy6ピンを使用 Encoder AirSpEnc(AirSpEncR_pin, AirSpEncL_Pin); short AirSpEncPrev = 0; //エンコーダの逆回転 FlightSimInteger AirSpeed; FlightSimInteger AirSpeedknob; //AirSpeedのノブを回転させるためだけの専用 //=============================================================================================== /////////// AirSeed エンコーダ プッシュスイッチ ////////////////// //=============================================================================================== const int AirSpeed_EncPush_SwPin = 11; int AirSpeed_EncPush_SwPin_val = 0; int AirSpeed_EncPush_SwPin_old_val = 0; int AirSpeed_EncPush_SwPin_state = 0; FlightSimInteger AirSpeed_EncPush_SW; void setup() { //=============================================================================================== /////////// setup内 エアースピード エンコーダ ////////////////// //=============================================================================================== pinMode(AirSpEncR_pin, INPUT_PULLUP); //エンコーダ pinMode(AirSpEncL_Pin, INPUT_PULLUP); //エンコーダ AirSpeed = XPlaneRef("T7Avionics/ap/spd_act"); //encoder AirSpeedknob = XPlaneRef("anim/1/spacial"); //ノブを回転させるだけのために //=============================================================================================== /////////// setup内 エアースピード エンコーダ プッシュスイッチ ////////////////// //=============================================================================================== pinMode(AirSpeed_EncPush_SwPin, INPUT_PULLUP); //BUTTONは入力に設定 AirSpeed_EncPush_SW = XPlaneRef("anim/186/button"); } void loop() { FlightSim.update(); //============================================================================================== /////////// loop内 AirSpeed エンコーダ ////////////////// //============================================================================================== // 1クリックで4つ変化するので4で割って1つずつカウントするようにする short AirSpEncDiff = (AirSpEnc.read() - AirSpEncPrev) / 4; if (AirSpEncDiff) { // only update prev when we've reached a detent! //戻り止めに達した場合にのみPREVを更新! AirSpEncPrev = 0; AirSpEnc.write(0); // datarefを一時的な値にコピー float airSp = AirSpeed; float airSpkn = AirSpeedknob; // 一時的な値に変更を適用 airSp += AirSpEncDiff; airSpkn += AirSpEncDiff; // 範囲チェックを行う while (airSp < 100) airSp += 1; //エアスピードが 100 より小さい場合は 1 をプラスする(つまり100で止まる) while (airSp >= 400) airSp -= 1; //エアスピードが 400 より大きい場合は 1 をマイナスする(つまり399で止まる) // datarefへ検証された新しいエアースピードの数値を書く AirSpeed = airSp; AirSpeedknob = airSpkn; } //============================================================================================== /////////// loop内 Part-A AirSpeed エンコーダ プッシュスイッチ ////////////////// //============================================================================================== //スイッチの保持設定スクリプト。 AirSpeed_EncPush_SwPin_val = digitalRead(AirSpeed_EncPush_SwPin); if((AirSpeed_EncPush_SwPin_val == LOW) && (AirSpeed_EncPush_SwPin_old_val == HIGH)) { AirSpeed_EncPush_SwPin_state = 1 - AirSpeed_EncPush_SwPin_state; //delay(10); //この位がチャタリングが起きない(500位が安定している) } AirSpeed_EncPush_SwPin_old_val = AirSpeed_EncPush_SwPin_val; //valはもう古くなったので保持しておく if(AirSpeed_EncPush_SwPin_state ==1){ AirSpeed_EncPush_SW = 1; } else { AirSpeed_EncPush_SW = 0; } }
LED
LED点灯のスクリプトで、このA/Pランプは一つのなdatarefだけを参照することで実現します。
他のランプはこんなに単純ではないので、別の方法を考えなけらばなりません。
そして、プログラム内の共通スクリプト部分は74HC595(LED表示)シフトレジスタを動かすためのもので、これを1つ入れておけばLEDのON/OFFは全て使えます。
他のランプはこんなに単純ではないので、別の方法を考えなけらばなりません。
そして、プログラム内の共通スクリプト部分は74HC595(LED表示)シフトレジスタを動かすためのもので、これを1つ入れておけばLEDのON/OFFは全て使えます。
A_P_LED.ino
//75HC595を使ったLED用 #include <LedControl.h> //////////////////////////////////////////////////////////////////////////////////////////////////////// /////////// 74HC595(LED表示)シフトレジスタ 共通スクリプト /////////// //////////////////////////////////////////////////////////////////////////////////////////////////////// int SER_dataPin = 43; //pin 14(DS-data) on the 75HC595 int RCLK_latchPin = 44; //pin 12(ST_CP-latch) on the 75HC595 int SRCLK_clockPin = 45; //pin 11(SH_CP-clock) on the 75HC595 #define number_of_74hc595s 3 //シフトレジスタ74HC595の数 #define numOfRegisterPins number_of_74hc595s * 8 //ここは変更しないこと boolean registers[numOfRegisterPins]; //上のレジスタの数を反映させる共通スクリプト void clearRegisters(){ for(int i = numOfRegisterPins - 1; i >= 0; i--){ registers[i] = LOW; //最初にLEDを全消灯するため(LOWは全点灯)、 //確認の為にスタート時にLEDをON,完全にX-Planeが起動したらOFFになる。 } } void writeRegisters(){ digitalWrite(RCLK_latchPin, LOW); for(int i = numOfRegisterPins - 1; i >= 0; i--){ digitalWrite(SRCLK_clockPin, LOW); int val = registers[i]; digitalWrite(SER_dataPin, val); digitalWrite(SRCLK_clockPin, HIGH); } digitalWrite(RCLK_latchPin, HIGH); } // 個々のピンをHIGかLOWに設定する void setRegisterPin(int index, int value){ //indexでピン位置、valueでLOWかHIGH registers[index] = value; } //////// 共通ここまで /////////////////////////////// //============================================================================================== /////////// Part-A LED表示(74HC595)シフトレジスタ A/P ランプ////////////////// //============================================================================================== const int AP_74HC595_Pin = 0; FlightSimInteger AutoPilotSwLED; void setup() { //////////////////////////////////////////////////////////////////////////////////////////////////////// // setup内 シフトレジスタ 共通スクリプト // //////////////////////////////////////////////////////////////////////////////////////////////////////// pinMode(SER_dataPin, OUTPUT); pinMode(RCLK_latchPin, OUTPUT); pinMode(SRCLK_clockPin, OUTPUT); clearRegisters(); //すべてのレジスタピンをリセット writeRegisters(); //////// 共通スクリプト ここまで /////////////////////// AutoPilotSwLED = XPlaneRef("T7Avionics/ap/ap_mode"); AutoPilotSwLED.onChange(updateAutoPilotSwLED); } void loop() { FlightSim.update(); //////////////////////////////////////////////////////////////////////////////////////////////////////// // loop内 74HC595 シフトレジスタ 共通 // //////////////////////////////////////////////////////////////////////////////////////////////////////// writeRegisters(); //変更内容を表示するために呼び出さなければなりません //必要な値が設定された後に一度だけ呼び出します。 //////// loop内 シフトレジスタ 共通 END //////////////// } //////////////////////////////////////////////////////////////////////////////////////////////////////// /////////// 関数 LEDピン 74HC595(出力)シフトレジスタ経由 /////////// //////////////////////////////////////////////////////////////////////////////////////////////////////// void updateAutoPilotSwLED(long value) { if (value == 1) { //T7Avionics/ap/ap_modeを見ている★ setRegisterPin(AP_74HC595_Pin, HIGH); //点灯 } else if (value == 3) { setRegisterPin(AP_74HC595_Pin, HIGH); //点灯 } else { setRegisterPin(AP_74HC595_Pin, LOW); //消灯 } }