このサイトはMacを使って Arduino Uno の動作や活用法を学びます。

32 Arduino モールス通信練習機 4 On/Off時間測定 [ステート制御]

第4回のこの記事では、モールス練習器から電鍵のOn/Off時間をコンピュータに送信するスケッチを作成します。

使用する部品は、今までと同様に圧電スピーカーと縦振れ電鍵で、以下のピンに接続しています。

 

スケッチの概要

電鍵のOn/Off時間をコンピュータに送信するのにシリアル通信を使用します。今までの例の多くは、通信速度を一般的な 9600bsud にしていますが、今回は高速で大量のデータを送信するために、115200baud の必要があります。

loop関数 フロー図

loop関数のプログラムフローを下図左に示します。

  1. 電顕ピンを確認し、ON(down)であればLED,ToneをONにします。
  2. 次にmillis関数で時間を調べ、1msに一度の間隔で電鍵のOn/Off処理を行い
  3. 他の時間は、シリアル受信の処理をします。
 

keyingTime関数 フロー図

keyingTime関数は、1msごとに実行されるステート制御方式のプログラムで、

その流れを上図右に示します。

スケッチの内容

スケッチの keyingTime関数は、1msごとに実行されますが、実行のたびに使用している変数が初期化されると機能しないため、多くの変数が static 修飾子をつけて定義しています。

 

// KeyOsc_4
//  MkSp  7-10

#include <eeprom.h>

#define bounceCk    5     // チャタリング防止する時間
#define gapLenMs    2000  // Gapと判断する時間

int frequency;

int keyPin = 2;     // 電顕は pin 2
int spkPin = 8;     // 圧電スピーカーは pin 8
int ledPin = 13;    // 内蔵LEDは pin 13

String sFreq1 = "1 -> Frequency 1000 Hz";
String sFreq2 = "2 -> Frequency  800 Hz";
String sFreq3 = "3 -> Frequency  600 Hz";
String sSel = " Selected";
String sTtl = "No  Mark  Space (mS)";

void setup() {
  Serial.begin(115200);           // シリアル通信の設定
  pinMode(keyPin, INPUT_PULLUP);  // 出力に設定
  pinMode(ledPin, OUTPUT);        // 出力に設定
  // コマンド説明
  Serial.println("++++++++++++++++++++++");
  Serial.println("Select Tone Frequency.");
  Serial.println(sFreq1);
  Serial.println(sFreq2);
  Serial.println(sFreq3);
  Serial.println();
  // EEPROM 読み込み
  ckEEPROMdata();    // EEPROMデータ取得
}

void loop() {
  static uint32_t msTime = 0;     // 経過時間
  if(!digitalRead(keyPin)){       // keyが押されたら
    digitalWrite(ledPin, HIGH);   //   LEDをオン
    tone(spkPin,frequency);       //   サイドトーン ON
  }else{                          // keyが離れていれば
    digitalWrite(ledPin, LOW);    //   LEDをオフ
    noTone(spkPin);               //   サイドトーン OFF
  }
  if(msTime == millis()){
    // シリアル通信でデータ受信した時の処理 ++++++++++++++++
    if(Serial.available()){       // 受信してるなら
      int val = Serial.read();    // 一文字入力し
      setPara(val);               // setPara関数へ
      if((val>='1')&(val<='3')){  // 有効データなら
        EEPROM.write(1, val);     // EEPROMに上書き
        delay(5);                 // 書き込み時間の確保
      }
    }
  }else{                // 1 msに一度実行
    msTime = millis();  // 経過時間を最新に更新
    keyingTime();       // On/Off時間測定
  }
}

// On/Off時間測定 [ステート制御]
void keyingTime(){
  #define start       0     // ステート名
  #define waitKeyDown 1
  #define waitSpace   2
  #define waitMark    3
  static uint32_t mkTime;         // Mark時間計測
  static uint32_t spTime;         // Space時間計測
  static uint32_t gpTime;         // Gap時間計測
  static int state = start;       // 開始ステート
  static int antiCnt = bounceCk;  // チャタリング防止回数
  static int num;                 // 符号番号
  char chStrg[32];                // sprintf作業用

  switch(state){
    case start:         // 制御開始 ---------
      num = 0;                      // 符号番号リセット
      Serial.println();             // 改行
      state = waitKeyDown;          // keyが押されるのを待つ
      break;
    case waitKeyDown:   // 電鍵が押されるのを待つ -------
      if(!digitalRead(keyPin)){     // keyが押され続けたら
        if(!antiCnt--){             // 規定回数確認に達したら
          mkTime = millis();        // 時間を取得
          antiCnt = bounceCk;       // 防止回数新たにセット
          Serial.println(sTtl);     // タイトル表示
          state = waitSpace;        // keyが離れるのを待つへ
        }
      }else{                        // keyが離れたら
        antiCnt = bounceCk;         // 防止回数を元に戻す
      }
      break;
    case waitSpace:     // 電鍵が離れるのを待つ -------
      if(digitalRead(keyPin)){      // keyが離れ続けたら
        if(!antiCnt--){             // 既に規定回数確認済みなら
          spTime = millis();        // 時間を取得
          mkTime = spTime - mkTime; // Mk時間を計算
          sprintf(chStrg, "%2d %5d ", num++, mkTime);
          Serial.print(chStrg);
          antiCnt = bounceCk;       // 防止回数新たにセット
          state = waitMark;         // keyが離れるのを待つへ
        }
      }else{                      // keyが離れたら
        antiCnt = bounceCk;       // 防止回数を元に戻す
      }
      break;
    case waitMark:      // 電鍵が押されるのを待つ
      if(!digitalRead(keyPin)){       // keyが押され続けたら
        if(!antiCnt--){               // 規定回数確認に達したら
          mkTime = millis();          // 時間を取得
          spTime = mkTime - spTime;   // Sp時間を計算
          sprintf(chStrg, "%5d\r\n", spTime);
          Serial.print(chStrg);
          antiCnt = bounceCk;         // 防止回数新たにセット
          state = waitSpace;          // keyが離れるのを待つへ
        }
      }else{                            // keyが離れたら
        antiCnt = bounceCk;             // 防止回数を元に戻す
        gpTime = spTime + gapLenMs;   // Gap時間を計算
        if(millis() > gpTime){        // Gap時間に達したら
          Serial.println("  ---");    // Gapを送信
          state = start;              // keyが離れるのを待つへ
        }
      }
      break;
  } // -end- switch(state)
} // -end- stateControl

// 周波数データの有効性確認と周波数への変換
// 1番地;周波数コード(1, 2, 3)
// 無効データは '3'に読み替える
void ckEEPROMdata(){
  int val = EEPROM.read(1);
  if((val>'3')|(val<'1'))val='3';
  setPara(val);
}

// データを解釈し処理する
void setPara(int val){
  switch(val){
    case '1':   // 1000Hz 周波数変更 -----------
      frequency = 1000;
      Serial.println(sFreq1 + sSel);
      break;
    case '2':   // 800Hz 周波数変更 -----------
      frequency = 800;
      Serial.println(sFreq2 + sSel);
      break;
    case '3':   // 600Hz 周波数変更 -----------
      frequency = 600;
      Serial.println(sFreq3 + sSel);
      break;
  }
}

 

動作の確認

スケッチを Arduino に書き込みプログラムがスタートしたら、Arduino IDE のシリアルモニターを起動してください。シリアルモニター画面が開いたら以下の手順で操作します。

  1. 通信速度を 115200 に設定する。
  2. ArduinoボードのリセットSWを押す。
  3. 使用できるコマンドがシリアルモニターに表示される。
  4. 電鍵でモールス 符号を打つ。
  5. シリアルモニター上に符号のMark時間、Space時間が表示される。

 

 


 

次回は、コンピュタの表示ソフトで処理しやすいデータフォーマットでシリアル通信できるようスケッチを変更します。