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)