33 Arduino モールス通信練習機 5 [完成]
第5回の記事では、モールス練習器からコンピュータに送信するMark時間とSpace時間とを別の行に表示するフォーマットを追加して完成スケッチとします。
- 第1回 モールス通信練習機 基本スケッチ
- 第2回 モールス通信練習機 周波数変更
- 第3回 モールス通信練習機 設定値記憶
- 第4回 モールス通信練習機 On/Off時間測定
- 第5回 モールス通信練習機 完成
使用する部品は、今までと同様に圧電スピーカーと縦振れ電鍵で、以下のピンに接続しています。
- pin 2 -> 電鍵
- pin 8 -> 圧電スピーカー
スケッチの内容
modeSep 変数の値で、コンピュータに送信するフォーマットを切り替えています。modeSep = 0 で、Mark時間とSpace時間が同じ行に表示され、modeSep = 1 では、Mark時間とSpace時間が別の行に表示されます。
modeSep 変数は、起動時に modeSep = 0 に設定されています。シリアル通信で'4'を受信することで、modeSep = 1 に変更されます。
// KeyOsc_5 // MkSp 7-12 #include <EEPROM.h> #define bounceCk 5 // チャタリング防止する時間 #define gapLenMs 2000 // Gapと判断する時間 int frequency; int modeSep = 0; // 出力モード 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 sFreq4 = "4 -> Separated format"; String sFreq5 = "5 -> Combined format"; 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(sFreq4); Serial.println(sFreq5); 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; // 防止回数新たにセット if(!modeSep){ // 分離モードでないなら 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時間を計算 if(modeSep){ sprintf(chStrg, "M %5d\r\n",mkTime); }else{ 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時間を計算 if(modeSep){ sprintf(chStrg, "S %5d\r\n",spTime); }else{ 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時間に達したら if(modeSep){ Serial.println("G ---"); }else{ 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; case '4': // 分離出力フォーマット modeSep = 1; Serial.println(sFreq4 + sSel); break; case '5': // 統合出力フォーマット modeSep = 0; // Serial.println(sFreq5 + sSel); break; } }
動作の確認
スケッチを Arduino に書き込みプログラムがスタートしたら、Arduino IDE のシリアルモニターを起動してください。シリアルモニター画面が開いたら以下の手順で操作します。
- 通信速度を 115200 に設定する。
- ArduinoボードのリセットSWを押す。
- 使用できるコマンドがシリアルモニターに表示される。
- シリアルモニター上部送信欄に「4」を入力する。
- 送信ボタンを押す。
- 電鍵でモールス 符号を打つ。
- シリアルモニター上に符号のMark時間、Space時間が別行表示される。
モールス信号解析アプリ
Arduinoボードからの出力信号は、Arduino IDE シリアルモニターやCoolTermなどの通信ソフトでも表示できます。より直感的に全体像を把握し、細部の検討も可能なアプリケーションをVisual C# 2019 を使い以前に作成しました。今回作成したスケッチもこのアプリに対応しています。詳細は以下の記事をご覧ください。なお、残念ながら Windows 専用アプリです。
モールス信号解析アプリ Processing
Visual C# 2019 のアプリと同等の機能を持つアプリプログラムを Precessingで作成しました。Precessingを使用すれば、WindousでもMacでも使用できるアプリケーションになります。詳細は以下の記事をご覧ください。
[p19]】モールス信号解析アプリ (Processing)