個別のプログラミング例
まず、この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のキーと同じような原理のスイッチです。
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のピン番号、変数名を変更する必要があります。
- 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は全て使えます。
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); //消灯
}
}