FTDX-10 で AH-4 を使う その3

前回の続きです。

組み立て完了しました。

ケース内部は余裕がありますが、歳をとったのでケースは大きめのほうが加工が楽です。

 

AH-4との接続ケーブルもDINコネクタにでもすればよかったと後から思いましたが、まあ良しとします。

せっかく CAT 制御可能なので、外部スイッチで以下の制御も実装しました。

1. ブレークインの ON/OFF

2. フィルタ切り替え

3. CWメモリ#1~#3送信

1 と 2 は以前 ICOM 用の CI-V 制御の VFO を作ったときも実装したもので、やはりあると便利。また、FTDX10 はメモリーキーヤーを使う場合、キーヤーボタンを表示しておかなければならず、そうするとウォーターフォールが見えなくなってしまい非常に不便でしたので、3 も必須と思っていました。

ところで、ヤエスの CAT オペレーションリファレンスマニュアルに間違いがあり、少々ハマりました。

ルーフィングフィルタの切り替えコマンドは RG と書いてありますが、RF が正しい。RG は RF Gain のコマンドで全然違う値が返ってくるので当初 ? 状態でした。さらに、
- Answer 項目の 4 は P2 ではなく P3
- Read 項目では RF P1 P3 が戻る

が実際の動作となります。皆様お気をつけくださいー。

外部スイッチはタッパーウェアを使いました。

リグの横に設置。上の TS-130V は飾り、ではなく現役で 7MHz FT-8 で使用しています。7.042MHz 用に水晶を入れて使ってますが、最近この周波数変更の議論が出ていて動向が気になります。

これで AH-4 の操作性は ICOM 機とほとんど同じになりました。ヨカッタ。なお、今回、FTDX-10 を購入したので移動局免許状に加えて固定局の免許を申請し、免許が来ました。しかし、総務省のサイトは移動局の免許しか表示されず 50W のままです。そういうものなのかな?

回路図

アナログポートで複数スイッチを使う方法はしなぷすさんの I/Oピン一つで読めるキーパッドの設計サービス を使用させていただきました。以前作成したエレキーでは外部ボタンに個別に配線していたので 5P のマイクコネクタを使い、ケーブルも太くなってしまいましたが、しなぷすさんの方法は目からウロコでした。ありがとうございます。

Arduino ソース

/* Arduino NANO
 * VID:1A86
 * PID:7523
 * 
 * 2022/7/30 ver3
 * - 電源LED用コード追加
 * - External Push SW 用に ResKeypard2.ino を統合
 * 
 * 2022/6/20 ver2
 * - Arduino NANO に移植
 * (特に変更点なし)
 *
 * 2022/6/19 ver1
 * - pow_mode2.ino を元に作成
 * * 
 * 注意:書き込み時はシリアルポート接続を切ること
 * ボード Uno
 * VID 2A03
 * PID 0043
 */

#include <ResKeypad.h> // キーを読み取るためにResKeypadライブラリを使う
#include <SoftwareSerial.h> // for comms to Rig
#define BAUD_CAT 9600     // CAT serial speed
#define BAUD_PC 9600     // PC debug serial speed
#define Pin_Meter 9 // SWR Meter output
#define Pin_RX 8
#define Pin_TX 7
#define Pin_AH4 6 //to relay
#define Pin_Start 5
#define Pin_LED 4 //show execution
#define Pin_Power_LED 10
#define Power_Tune "PC010;" //RF output power when AH-4 tune
#define Mode_Tune "MD06;" //MODE when AH-4 tune
#define Meter_SWR "RM6;" //RM6=SWR, RM1=S-meter

SoftwareSerial mySerial = SoftwareSerial(Pin_RX, Pin_TX); // (RX, TX)
uint8_t u8state;
unsigned long waitSWR, waitCMD;
const long intSWR=100;
const long intTX=2000;
const long intCMD=500; // 500;
String exPower, exMode;
boolean chkSWR=false;

const int AIN = A0;                     // キーの読み取りに使用するアナログ入力ピン
const int KeyNum = 5;                  // 読み取るキーの個数
signed char exKey;  //チャタリング検知のため直前のキーを保持
PROGMEM const int threshold[KeyNum] = { // 入力電圧の閾値
  // 次の数列は、しなぷすのハード製作記の回路設計サービスで計算して得られたもの
    89,  296,  516,  733,  935
};
ResKeypad keypad(AIN, KeyNum, threshold); //以後keypadオブジェクトを使って、スイッチの読み取りができる

void setup()
{
  Serial.setTimeout(10);
  Serial.begin(BAUD_CAT, SERIAL_8N2);
  pinMode(13, OUTPUT); digitalWrite(13, LOW); // force LED (pin 13) to turn off.
  pinMode(Pin_RX, INPUT);  // serial communication from PC
  pinMode(Pin_TX, OUTPUT); // serial communication to PC
  pinMode(Pin_AH4, OUTPUT); digitalWrite(Pin_AH4, LOW); //AH4 control
  pinMode(Pin_Start, INPUT_PULLUP);  // Start button
  pinMode(Pin_Meter, OUTPUT); //SWR Meter
  pinMode(Pin_LED, OUTPUT); digitalWrite(Pin_LED, LOW); //show execution
  pinMode(Pin_Power_LED, OUTPUT); digitalWrite(Pin_Power_LED, HIGH);
  mySerial.begin(BAUD_PC);
  mySerial.listen();  // only one port can be made to listen with software serial
  // see reference https://www.arduino.cc/en/Reference/SoftwareSerialListen
  while (mySerial.available()) mySerial.read(); // clean buffer
  u8state=0;
  waitSWR = millis() + intSWR;
  waitCMD = millis() + intCMD;
  exKey=127;
  mySerial.println("setup() done.");
}
//---------------------------------------------------------------------------------------------

String rig_ctl(String cmd) {
  String data;
  Serial.write(cmd.c_str()); 
  delay(25); //delay(10) にするとまだ文字がすべて到達していない。

  int nbChar = Serial.available();
  if (nbChar > 0) {
    data=Serial.readString();
  }
  mySerial.print("sent cmd=");mySerial.print(cmd);
  mySerial.print("\trec data=");mySerial.println(data);
  return data;
}

String rig_ctl_NM(String cmd) { //No Message output
  String data;
  Serial.write(cmd.c_str()); 
  delay(25); //delay(10) にするとまだ文字がすべて到達していない。

  int nbChar = Serial.available();
  if (nbChar > 0) {
    data=Serial.readString();
  }
  return data;
}

void getSWR() {
  String s=rig_ctl_NM(Meter_SWR); //RM1 S-meter
  long value=s.substring(3,6).toInt();
  analogWrite(Pin_Meter, value);
}

void loop() {
  String s;
  int btn_start;
  signed char key;

  if (chkSWR&&(millis() > waitSWR)) {
      getSWR(); // wait state
      waitSWR=millis()+intSWR;
  }
  key=keypad.GetKeyState();
  
  switch( u8state ) {
  case 0:
    if (key<0) {
      exKey=127; //キーが連続して押されなかったので前回キーをリセット
      btn_start=digitalRead(Pin_Start);
      if (btn_start==LOW) {
        u8state=2;
        waitCMD=millis()+intCMD;
      } 
    } else { 
      if (key!=exKey) {
        u8state=key+100;
      }
      exKey=key;
    }
  break;
  case 1:
    /* getSWR() */
    u8state=0;
  break;
  case 2:
    mySerial.println("--- start ---");
    digitalWrite(Pin_LED, HIGH);
    //read power
    exPower=rig_ctl("PC;");
    u8state++; // wait state
  break;
  case 3:
    if (millis() > waitCMD) {
      waitCMD=millis()+intCMD;
      //set power
      s=rig_ctl(Power_Tune);
      u8state++; // wait state
    }
  break;
  case 4:
    if (millis() < waitCMD) break;
    waitCMD=millis()+intCMD;
    //read mode
    exMode=rig_ctl("MD0;");
    //set mode
    s=rig_ctl(Mode_Tune);
    u8state++; // wait state
  break;
  case 5:
    if (millis() < waitCMD) break;
    waitCMD=millis()+intCMD;
    //verify power before TX
    s=rig_ctl("PC;");
    if (!s.equals(Power_Tune)) {
      mySerial.println("CAT [PC] command response is invalid.");
      digitalWrite(Pin_LED, LOW);
      u8state=0;
      break;
    }
    //tx on
    chkSWR=true;
    //mySerial.println("tx on");
    s=rig_ctl("TX1;");
    u8state++; // wait state
  break;
  case 6:
    if (millis() < waitCMD) break;
    waitCMD=millis()+intCMD;
    //AH-4 tune start
    digitalWrite(Pin_AH4, HIGH);
    mySerial.println("AH4 on");
    u8state++; // wait state
  break;
  case 7:
    if (millis() < waitCMD) break;
    waitCMD=millis()+intTX; //next TX
    digitalWrite(Pin_AH4, LOW);
    mySerial.println("AH4 off");
    u8state++;
  break;
  case 8:  
    if (millis() < waitCMD) break;
    waitCMD=millis()+intCMD;
    //tx off
    mySerial.println("tx off");
    s=rig_ctl("TX0;");
    chkSWR=false;
    //restore original
    s=rig_ctl(exPower);
    s=rig_ctl(exMode);
  
    mySerial.println("original value restored");
    digitalWrite(Pin_LED, LOW);
    u8state = 0;
    break;
  case 100: //Memory Keyer 1
    s=rig_ctl("KY6;");
    u8state = 0;
  break;
  case 101: //Memory Keyer 2
    s=rig_ctl("KY7;");
    u8state = 0;
  break;
  case 102: //Memory Keyer 3
    s=rig_ctl("KY8;");
    u8state = 0;
  break;
  case 103: //Filter (マニュアルは間違えている Set/Read で値が変わるので注意)
    s=rig_ctl("RF0;");
    if (s.equals("RF06;")) {
      rig_ctl("RF02;");
    } else if (s.equals("RF07;")) {
      rig_ctl("RF04;");
    } else if (s.equals("RF09;")) {
      rig_ctl("RF01;"); //300Hz filter を買ったら RF05 にする
    } else if (s.equals("RF0A;")) {
      rig_ctl("RF01;");
    }
    u8state = 0;
  break;
  case 104: //Break-in (設定可能状態でないとコマンドエラーになる。例: モードがCW以外など)
    s=rig_ctl("BI;");
    if (s.equals("BI0;")) {
      rig_ctl("BI1;");
    } else {
      rig_ctl("BI0;");
    }
//    delay(500);
    u8state = 0;
  break;
  } //end switch()
}