環境:
- マイコン:CH32V003
- 開発環境:Arduino IDE
- 操作ボタン:タクトスイッチ=内部プルアップ入力
- その他:ブザー
12月7日の【電波文化祭】出展に向けて、UIAPduino を使った何かしらの作例を展示したいと思いました。
そこで急遽、ChatGPT に相談して作ったのが、この 『モールス信号クイズ』 です。
青(初級)・オレンジ(中級)・赤(上級)のボタンを押すと、5つの単語をモールス信号に変換してブザーから鳴らすだけの、シンプルな装置です。
ChatGPT に仕様を説明したところ、まずは Arduino UNO 版のソースコードを一発で出力してくれました。
それが問題なく動いたので、UIAPduino 用にピン番号だけ書き換えたのが、下に載せたコードです。
コミュニティのメンバーにモールス信号の内容も確認してもらえたので、正しく動作しているはず。
ところが、せっかく作ったものの……当日の【電波文化祭】は 大盛況!
会場中がにぎやかで、あちこちでモールス信号の音も飛び交っていたため、このブザー音だけでクイズとして遊んでもらうのは難しい状況 でした。これは残念。
ただ、マイコンボードに興味のある方には
「こういうの、ChatGPT でサッと作れるんですよ!」
としっかりアピールできたので、展示としては意味があったと思います。
ChatGPT 曰く、
「このコードは、読むだけでモールス装置の仕組みが理解できる構成になっています」
たしかに、case 'P': return ".--."; のように、モールス信号がそのまま見える形で記述されていて、なかなか面白い仕上がりになっています。
せっかく生成してもらったので、ソースコードを置いておきますね。
ではでは!
完成コード(コピペでOK)
注意: このスケッチ単体だけを1つの .ino に貼り付けてください(重複定義エラー対策)。
/**********************************************************************
* Morse Quiz Device for UIAPduino
* ---------------------------------
* ● LED と ブザー でモールス信号を送信する装置
* ● A/B/C の3つのボタンで難易度別の問題を再生
* - A → 初級(動物)
* - B → 中級(電子工作)
* - C → 上級(実用CW・高速)
*
* 移植しやすいよう、Arduino 標準関数のみ使用。
* このコードを読むだけでモールス装置の仕組みが理解できる構成。
**********************************************************************/
// ------------------------------------------------------------
// ピン定義
// ------------------------------------------------------------
// モールスの光を出すLED(UIAPduinoの2番は内蔵LEDに繋がっている)
const int LED_PIN = 2;
// ブザー(アクティブブザー推奨:音がすぐ出るタイプ)
const int BUZZER_PIN = 8;
// A/B/C の3ボタン
// ボタンは GND に落とし、INPUT_PULLUP で内部プルアップを使う。
// → 押していない:HIGH
// → 押した:LOW
const int BUTTON_A_PIN = 5;
const int BUTTON_B_PIN = 3;
const int BUTTON_C_PIN = 4;
// ------------------------------------------------------------
// クイズ再生中フラグ
// ・ボタンを連打されても途中で割り込まないために使う。
// ------------------------------------------------------------
bool isPlaying = false;
// ------------------------------------------------------------
// 5単語とドット長(速度)をまとめた構造体
// ------------------------------------------------------------
struct Quiz {
const char* words[5]; // 5つの単語
unsigned int dotMs; // ドット(・)の長さ(ミリ秒)
};
// ------------------------------------------------------------
// 初級:子ども向け(ゆっくり)
// dot = 180ms → 聞き取りやすい速度
// ------------------------------------------------------------
Quiz quizA = {
{ "CAT", "DOG", "FROG", "LION", "BEAR" },
180
};
// ------------------------------------------------------------
// 中級:電子工作(普通の速度)
// dot = 120ms → 標準的なモールス速度
// ------------------------------------------------------------
Quiz quizB = {
{ "LED", "BOARD", "PROBE", "RESIN", "CABLE" },
120
};
// ------------------------------------------------------------
// 上級:実用CW(高速)
// dot = 60ms → CW経験者がニヤリとする速さ
// ------------------------------------------------------------
Quiz quizC = {
{ "CQDX", "QRZ", "QTH", "RST", "ANT" },
60
};
// ------------------------------------------------------------
// 文字 → モールスパターン変換
// 使用する文字のみ対応(A〜Z と 0〜9)
// ------------------------------------------------------------
const char* getMorsePattern(char c) {
switch (c) {
case 'A': return ".-";
case 'B': return "-...";
case 'C': return "-.-.";
case 'D': return "-..";
case 'E': return ".";
case 'F': return "..-.";
case 'G': return "--.";
case 'H': return "....";
case 'I': return "..";
case 'J': return ".---";
case 'K': return "-.-";
case 'L': return ".-..";
case 'M': return "--";
case 'N': return "-.";
case 'O': return "---";
case 'P': return ".--.";
case 'Q': return "--.-";
case 'R': return ".-.";
case 'S': return "...";
case 'T': return "-";
case 'U': return "..-";
case 'V': return "...-";
case 'W': return ".--";
case 'X': return "-..-";
case 'Y': return "-.--";
case 'Z': return "--..";
// 数字(コールサイン対応)
case '0': return "-----";
case '1': return ".----";
case '2': return "..---";
case '3': return "...--";
case '4': return "....-";
case '5': return ".....";
case '6': return "-....";
case '7': return "--...";
case '8': return "---..";
case '9': return "----.";
}
return ""; // 未対応文字は空
}
// ------------------------------------------------------------
// 1つの信号(・または-)を送信
// dot = dotMs, dash = dotMs × 3
// LED と ブザーを同時にONにする
// ------------------------------------------------------------
void sendSymbol(char symbol, unsigned int dotMs) {
unsigned int onTime;
if (symbol == '.') {
onTime = dotMs; // dot(短点)
} else if (symbol == '-') {
onTime = dotMs * 3; // dash(長点)
} else {
return; // パターン外
}
// 信号 ON
digitalWrite(LED_PIN, HIGH);
tone(BUZZER_PIN, 1000); // 1000Hzのビープ音
delay(onTime);
// 信号 OFF
digitalWrite(LED_PIN, LOW);
noTone(BUZZER_PIN);
// シンボル間スペース(1単位)
delay(dotMs);
}
// ------------------------------------------------------------
// 1文字分のモールス信号を送信
// 文字間スペース:合計3単位(ここでは追加2単位)
// ------------------------------------------------------------
void sendLetter(char letter, unsigned int dotMs) {
// スペースは単語の区切り → 追加の長めの休止
if (letter == ' ') {
delay(dotMs * 4);
return;
}
// 小文字を大文字に変換
char upper = letter;
if (upper >= 'a' && upper <= 'z') {
upper = upper - 'a' + 'A';
}
// モールスパターン取得
const char* pattern = getMorsePattern(upper);
if (!pattern || pattern[0] == '\0') {
return; // 未定義文字は無視
}
// パターンを順に送信(例:"--." → '-', '-', '.')
for (int i = 0; pattern[i] != '\0'; i++) {
sendSymbol(pattern[i], dotMs);
}
// 文字間スペース(残り2単位)
delay(dotMs * 2);
}
// ------------------------------------------------------------
// 単語全体を送信
// ------------------------------------------------------------
void sendWord(const char* word, unsigned int dotMs) {
for (int i = 0; word[i] != '\0'; i++) {
sendLetter(word[i], dotMs);
}
}
// ------------------------------------------------------------
// クイズ1問分(5単語)を順に送信
// ------------------------------------------------------------
void playQuiz(const Quiz& q) {
isPlaying = true;
// ★ スタート合図:短いビープ1回
digitalWrite(LED_PIN, HIGH);
tone(BUZZER_PIN, 1500);
delay(150);
digitalWrite(LED_PIN, LOW);
noTone(BUZZER_PIN);
delay(300);
// 5単語連続で送信
for (int i = 0; i < 5; i++) {
sendWord(q.words[i], q.dotMs);
// 単語間スペース(約7単位)
if (i < 4) {
delay(q.dotMs * 7);
}
}
// ★ 終了合図:ピッピッ
delay(400);
for (int k = 0; k < 2; k++) {
digitalWrite(LED_PIN, HIGH);
tone(BUZZER_PIN, 1800);
delay(120);
digitalWrite(LED_PIN, LOW);
noTone(BUZZER_PIN);
delay(150);
}
isPlaying = false;
}
// ------------------------------------------------------------
// ボタン押下判定(簡易デバウンス付き)
// ------------------------------------------------------------
bool isButtonPressed(int pin) {
// ボタンは押していないと HIGH(プルアップ)
if (digitalRead(pin) == LOW) {
delay(20); // チャタリング軽減
if (digitalRead(pin) == LOW) {
// 押しっぱなしで誤作動しないよう、
// 離されるまで待つ
while (digitalRead(pin) == LOW) {
delay(5);
}
return true;
}
}
return false;
}
// ------------------------------------------------------------
// 初期化処理
// ------------------------------------------------------------
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON_A_PIN, INPUT_PULLUP);
pinMode(BUTTON_B_PIN, INPUT_PULLUP);
pinMode(BUTTON_C_PIN, INPUT_PULLUP);
digitalWrite(LED_PIN, LOW);
noTone(BUZZER_PIN);
}
// ------------------------------------------------------------
// メインループ
// ・クイズ再生中は他のボタンを無視する
// ------------------------------------------------------------
void loop() {
if (!isPlaying) {
if (isButtonPressed(BUTTON_A_PIN)) {
playQuiz(quizA); // 初級
}
else if (isButtonPressed(BUTTON_B_PIN)) {
playQuiz(quizB); // 中級
}
else if (isButtonPressed(BUTTON_C_PIN)) {
playQuiz(quizC); // 上級(高速)
}
}
}
0 件のコメント:
コメントを投稿