32 Arduino モールス通信練習機 4 On/Off時間測定 [ステート制御]
第4回のこの記事では、モールス練習器から電鍵のOn/Off時間をコンピュータに送信するスケッチを作成します。
- 第1回 モールス通信練習機 基本スケッチ
- 第2回 モールス通信練習機 周波数変更
- 第3回 モールス通信練習機 設定値記憶
- 第4回 モールス通信練習機 On/Off時間測定
- 第5回 モールス通信練習機 完成
使用する部品は、今までと同様に圧電スピーカーと縦振れ電鍵で、以下のピンに接続しています。
- pin 2 -> 電鍵
- pin 8 -> 圧電スピーカー
スケッチの概要
電鍵のOn/Off時間をコンピュータに送信するのにシリアル通信を使用します。今までの例の多くは、通信速度を一般的な 9600bsud にしていますが、今回は高速で大量のデータを送信するために、115200baud の必要があります。
loop関数 フロー図
loop関数のプログラムフローを下図左に示します。
- 電顕ピンを確認し、ON(down)であればLED,ToneをONにします。
- 次にmillis関数で時間を調べ、1msに一度の間隔で電鍵のOn/Off処理を行い
- 他の時間は、シリアル受信の処理をします。
keyingTime関数 フロー図
keyingTime関数は、1msごとに実行されるステート制御方式のプログラムで、
その流れを上図右に示します。
- start
ステート処理の初期化をするステートですが、符号番号リセットがその主な作業です。 - waitKeyDown
電鍵が押されるのを待ちます。私の使用した電鍵はひどいチャタリングがあるため強力な防止プログラムを組み込んんでいます。5回つまり5msに渡り続けて接点がONの時に初めて電鍵が押されたこととします。
電鍵が押されたと認識すると、その時の時間(millis)を変数mkTimeに記憶し、PCには、タイトルを送信します。 - waitSpace
電鍵が離れるのを待ちます。waitKeyDownと同様なチャタリング対策を行います。電鍵が離れたと認識すると、その時の時間(millis)を変数spTimeに記憶するとともに、電鍵が押されていた時間を計算し、PCには、符号番号と押されていたMark時間を送信します。 - waitMark
電鍵が押されるのを待ちます。チャタリング対策を行いながら、電鍵が離れている時間を計測し、Gap時間(2000ms)以上経ったら電鍵操作が終了したとみなし制御をリセットします。Gap時間未満で電鍵が押されれば、その時間(millis)を変数mkTimeに記憶するとともに、電鍵が離れていた時間をSpace時間としてPCに送信します。
スケッチの内容
スケッチの 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 のシリアルモニターを起動してください。シリアルモニター画面が開いたら以下の手順で操作します。
- 通信速度を 115200 に設定する。
- ArduinoボードのリセットSWを押す。
- 使用できるコマンドがシリアルモニターに表示される。
- 電鍵でモールス 符号を打つ。
- シリアルモニター上に符号のMark時間、Space時間が表示される。
次回は、コンピュタの表示ソフトで処理しやすいデータフォーマットでシリアル通信できるようスケッチを変更します。