3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

猿でも作れるモールス信号解読機+LCD with Arduino Uno

Last updated at Posted at 2025-12-16

1 はじめに

この記事は、産技品川 Advent Calendar 2025 17日目の記事です。

タイトルの通り、モールス信号を解読する装置を作成したので、その作り方を紹介します。

ちなみに完成系はこのような形です

1.1 前提条件

・ある程度 C 言語が書ける方向けの記事です。そのため、あまりコードの解説はしていません。

1.2 注意事項

・著者は、プログラミングや回路作成の経験が非常に浅いです。
 そのため、コードが読みにくかったり、バグのある実装や配線が綺麗でない部分が含まれている可能性があります。
 優しい目で見ていただき、コメントなどでご指摘いただけると幸いです。
・参考にした文献は最後に残します。

2 器具一覧

使用器具・ソフトウェア名 型番・バージョン 製造元
Arduino Uno Arduino Uno R3(Rev.3) Arduino(arduino.cc) 1
ブレッドボード - 好きなもの 1
複式電鍵 - 好きなもの 1
LCD LCD 1602A 好きなもの 1
ジャンパ線(オスーオス) 好きなもの 好きなもの 19
スピーカー 好きなもの 好きなもの 1
可変抵抗 好きなもの 好きなもの 1
Arduino IDEの入ったPC 好きなもの 好きなもの 1
パソコン通信用USBケーブル 好きなもの 好きなもの 1
*USB-C to USB-A 好きなもの 好きなもの 1

*Macを使用している場合、USB-A ポートが無いことが多いため、USB-C → USB-A 変換アダプタ(ハブ)を使用しました。Windows PC などで USB-A ポートがある場合は不要です。

*複式電鍵はトミニータという友人に制作してもらいました。

*複式電鍵が見つからない場合は、タクトスイッチを二つ用意してもらっても構いません。ただ、説明は省かせてもらいます。

*複式電鍵は以下のようなものです。ピンでArduino Unoにさせるものならなんでも構いません。

複式電鍵

3 やること

まず、配線をしてから、コードを書いていきます。

トン(・)とツー(ー)を signal という変数に格納し、一定の時間が経ったらそのsignalを読み取ってLCDに表示させるということをします。

もし該当の信号が見つからなかったら#を出力するようにします。

4 配線

まずは、配線をしていきます。
使うものは、複式電鍵・LCD 1602A・可変抵抗・スピーカー・Arduino Uno・ブレッドボードです。

まず、LCD 1602A、可変抵抗、スピーカーをブレッドボードに接続してください。

そして、ジャンパ線を接続してください。

ジャンパ線をどこに刺すかを書いているので、下記の通りに接続してください。

どこ どこ ジャンパ線の長さ
複式電鍵(トン) digital 7番 -
複式電鍵(ツー) digital 6番 -
複式電鍵(真ん中) GNDピン -
LCDのGND GND 短い
LCDのVDD 5V 短い
LCDのV0 可変抵抗の真ん中 長い
LCDのRS digital 12番 短い
LCDのRW GND 短い
LCDのE digital 11番 短い
LCDのD4 digital 5番 長い
LCDのD5 digital 4番 長い
LCDのD6 digital 3番 長い
LCDのD7 digital 2番 長い
LCDのBLA 5V 短い
LCDのBLK GND 短い
可変抵抗の右 5V 短い
可変抵抗の左 GND 短い
スピーカーのプラス digital 8番 長い
スピーカーのマイナス GND 長い

私は、このように配線をしました。

・5V や GND はよく使用するため、ブレッドボード上部にまとめています。

・可変抵抗は、LCDのコントラストを変化させるために使用しています。

・スピーカーにテープを貼っている理由は音量を抑えるためです。

この通りに配線できたら、一回PCに接続してみてください。

PCとArduino Unoの繋ぎ方 パソコン通信用USBケーブルを用意して、Arduino Unoにこのように繋ぎます。

IMG_6773.jpg

これで、LCDのバックライトが光り、可変抵抗を回すとコントラストが変わればひとまずOKです。

5 制御

次に制御のためにコードを書いていきます。
とりあえず動かしたい!という方は、こちらの隠しページを開いてもらって、Arduino IDEにコピペして書き込みをしてもらって動かしても構いません。

パソコンとの繋ぎ方がわからない場合は、PCとArduino Unoの繋ぎ方を見てください。

Morse_code.ino
#include <LiquidCrystal.h>

String signal = "";
long dt_time = 100;
long last_happened = 0;
int char_cnt = 0;

LiquidCrystal lcd(12,11,5,4,3,2);


void setup() {
  Serial.begin( 9600 );
  pinMode(8, OUTPUT);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);


  lcd.begin(16,2);
  lcd.clear();

}




void loop(){
  int current_ton = digitalRead(7);
  int current_tu = digitalRead(6);

  long elapsed = millis();
  if(!current_tu) {
    signal += '-';
    digitalWrite(8, HIGH);
    delay(dt_time*3);
    digitalWrite(8, LOW);
    delay(dt_time);
    last_happened = elapsed;

  }

  else if(!current_ton){
    signal += '.';
    digitalWrite(8, HIGH);
    delay(dt_time);
    digitalWrite(8, LOW);
    delay(dt_time);
    last_happened = elapsed;
  }
  
  else if (elapsed - last_happened >= dt_time * 3){
    if(signal.length() == 1){
      if(signal == ".") lcd.print("E");
      else if(signal == "-") lcd.print("T");
      else lcd.print("#");
    }
    else if(signal.length() == 2){
      if(signal == ".-") lcd.print("A");
      else if(signal == "..") lcd.print("I");
      else if(signal == "--") lcd.print("M");
      else if(signal == "-.") lcd.print("N");

    }
    else if(signal.length() == 3){
      if(signal == "-..") lcd.print("D");
      else if(signal == "--.") lcd.print("G");
      else if(signal == "-.-") lcd.print("K");
      else if(signal == "---") lcd.print("O");
      else if(signal == ".-.") lcd.print("R");
      else if(signal == "...") lcd.print("S");
      else if(signal == "..-") lcd.print("U");
      else if(signal == ".--") lcd.print("W");
      else lcd.print("#");
      
    }
    else if(signal.length() == 4){
      if(signal == "-...") lcd.print("B");
      else if(signal == "-.-.") lcd.print("C");
      else if(signal == "..-.") lcd.print("F");
      else if(signal == "....") lcd.print("H");
      else if(signal == ".---") lcd.print("J");
      else if(signal == ".-..") lcd.print("L");
      else if(signal == ".--.") lcd.print("P");
      else if(signal == "--.-") lcd.print("Q");
      else if(signal == "...-") lcd.print("V");
      else if(signal == "-..-") lcd.print("X");
      else if(signal == "-.--") lcd.print("Y");
      else if(signal == "--..") lcd.print("Z");
      else lcd.print("#");
    }
    else if(signal.length() == 5){
      if(signal == "-----") lcd.print("0");
      else if(signal == ".----") lcd.print("1");
      else if(signal == "..---") lcd.print("2");
      else if(signal == "...--") lcd.print("3");
      else if(signal == "....-") lcd.print("4");
      else if(signal == ".....") lcd.print("5");
      else if(signal == "-....") lcd.print("6");
      else if(signal == "--...") lcd.print("7");
      else if(signal == "---..") lcd.print("8");
      else if(signal == "----.") lcd.print("9");
      else lcd.print("#");
    }
    else if(signal.length() == 0){
      char_cnt--;
    }
    else lcd.print("#");
    signal = "";
    char_cnt++;
  }


  if(char_cnt == 16) lcd.setCursor(0,1);
  else if(char_cnt == 32){
    lcd.setCursor(0,0);
    char_cnt = 0;

  }
}


5.1 トンとツーの読み込みとそれをsignalに書き込む

まずは、signalというString型のオブジェクトを作成してそこに、トン(・)とツー(ー)を書き込んでいきます。ループごとにsignalがリセットされるわけではないので、void setupvoid loopの外に記述します。

そして、今回は抵抗を使ってないので、入力を受け取るのにINPUT_PULLUPを使用します。PULLUPを使用してるからHIGHとLOWが逆になっていることに注意します。

そして、dt_timeというのは、どのくらい間隔を空けるかの値です。
トン : ツー: トンとトンの間 : 文字と文字の間 : 文章と文章の間 = 1:3:1:3:7
となっています。詳しくは、「モールス信号 符号化方式」で調べてみてください。

ちなみに、ツー(ー)を先に書いている理由は、複式電鍵の場合はトンを押しっぱなしにしておき、必要になればトンを押しながらツーの方を押すというものになっているからです。

今回は、トン(・)とツー(ー)がしっかり入力できているか調べるためにSerial.printlnを使用します。そのために、void setupの中でシリアル通信を初期化します。

Read_ton_tu.ino
String signal = "";
long dt_time = 100;


void setup() {
  Serial.begin( 9600 ); // シリアル通信を初期化する。通信速度は9600bps
  pinMode(6, INPUT_PULLUP); //6番入力 PULLUP を使用しているため、論理が反転
  pinMode(7, INPUT_PULLUP); //7番入力 PULLUP を使用しているため、論理が反転


}



void loop(){
  //LOWがHIGHでHIGHがLOW.
  int current_ton = digitalRead(7);
  int current_tu = digitalRead(6);

  if(!current_tu) {
    signal += '-';
    delay(dt_time*3);
  }
  else if(!current_ton){
    signal += '.';
    delay(dt_time);
  }
  Serial.println(signal);
}

このようになれば問題ないです。

スクリーンショット 2025-12-15 22.38.53.png

もし、押しているボタンがツー(-)なのに、トン(・)が押されているなどがあった場合、配線を変えるか、コードでcurrent_toncurrent_tuのdigitalReadで読むピンを変更してください。

5.2音を出す

次に音を出していきます。スピーカーに繋いでいるのは、8番ピンです。そのピンを出力ピンに設定して、トン(・)なら、delayミリ秒だけ鳴らして、ツー(ー)なら、delay*3ミリ秒鳴らしていきます。

sound.ino
String signal = "";
long dt_time = 100;


void setup() {
  Serial.begin( 9600 ); // シリアル通信を初期化する。通信速度は9600bps
  pinMode(8, OUTPUT); //8番を出力
  pinMode(6, INPUT_PULLUP); //6番入力 PULLUP を使用しているため、論理が反転
  pinMode(7, INPUT_PULLUP); //7番入力 PULLUP を使用しているため、論理が反転


}



void loop(){
  //LOWがHIGHでHIGHがLOW.
  int current_ton = digitalRead(7);
  int current_tu = digitalRead(6);

  if(!current_tu) {
    signal += '-';
    digitalWrite(8, HIGH);
    delay(dt_time);
    digitalWrite(8, LOW);
    delay(dt_time*3);
  }
  
  else if(!current_ton){
    signal += '.';
     digitalWrite(8, HIGH);
     delay(dt_time);
     digitalWrite(8, LOW);
     delay(dt_time);
  }
  Serial.println(signal);
}

音がなったら成功です。ここまできたら、Serial.printlnは必要ないので消してしまいましょう。

5.3 signalを判断して、文字をLCDに表示

最後にsignalを判断して、文字をLCDに表示させていきます。

今回は簡単な一文字ずつ判断していくようなものにします。

このLCDは英語といくつかの記号しか対応していなかったので、A~Zを識別して、識別不能だったら#を表示するようにしています。
まず書くにあたって、こちらを思い出してください。

トン : ツー: トンとトンの間 : 文字と文字の間 : 文章と文章の間 = 1:3:1:3:7

今回は、トンの認識時間をdelayとしていることから、文字と文字の間がdelay*3であることがわかります。

そのため、最後にsignalへ書き込みがあった時間からdelay*3以上何もなかったら、LCDに表示させます。

時間をはかるためにmillisというArduino Unoが最後にリセットされてからの時間を返すコマンドを使用します。

まず、ライブラリを読み込み、その後、色々なLCDの表示に必要なコマンドを打っていきます。

ここでは詳細な解説は省略し、動作に必要な部分のみ紹介します。

ここでは、LCDに表示させるlcd.print(文字)や、カーソルの位置を決めるlcd.setCursor(n,m)などを知っておくと良いと思います。

Morse_code.ino
//ライブラリを読み込む
#include <LiquidCrystal.h>
String signal = "";
long dt_time = 100; //これが、delayする時間
long last_happened = 0;
int char_cnt = 0;





LiquidCrystal lcd(12,11,5,4,3,2); // LCDの表示に必要なピンを宣言する
void setup() {
 Serial.begin( 9600 );
 pinMode(8, OUTPUT);
 pinMode(6, INPUT_PULLUP); 
 pinMode(7, INPUT_PULLUP);


 //LCDの桁数と行数を指定する(16桁2行)
 lcd.begin(16,2);

 //画面をクリアしてカーソルの位置を左上(0,0)にする
 lcd.clear();

}




void loop(){
 //LOWがHIGHでHIGHがLOW.
 int current_ton = digitalRead(7);
 int current_tu = digitalRead(6);

 long elapsed = millis();
 if(!current_tu) {
   signal += '-';
   digitalWrite(8, HIGH);
   delay(dt_time*3);
   digitalWrite(8, LOW);
   delay(dt_time);
   last_happened = elapsed;

 }

 else if(!current_ton){
   signal += '.';
   digitalWrite(8, HIGH);
   delay(dt_time);
   digitalWrite(8, LOW);
   delay(dt_time);
   last_happened = elapsed;
 }
 
 else if (elapsed - last_happened >= dt_time * 3){ //ここで、signalを読み取る
   if(signal.length() == 1){
     if(signal == ".") lcd.print("E");
     else if(signal == "-") lcd.print("T");
     else lcd.print("#");
   }
   else if(signal.length() == 2){
     if(signal == ".-") lcd.print("A");
     else if(signal == "..") lcd.print("I");
     else if(signal == "--") lcd.print("M");
     else if(signal == "-.") lcd.print("N");

   }
   else if(signal.length() == 3){
     if(signal == "-..") lcd.print("D");
     else if(signal == "--.") lcd.print("G");
     else if(signal == "-.-") lcd.print("K");
     else if(signal == "---") lcd.print("O");
     else if(signal == ".-.") lcd.print("R");
     else if(signal == "...") lcd.print("S");
     else if(signal == "..-") lcd.print("U");
     else if(signal == ".--") lcd.print("W");
     else lcd.print("#");
     
   }
   else if(signal.length() == 4){
     if(signal == "-...") lcd.print("B");
     else if(signal == "-.-.") lcd.print("C");
     else if(signal == "..-.") lcd.print("F");
     else if(signal == "....") lcd.print("H");
     else if(signal == ".---") lcd.print("J");
     else if(signal == ".-..") lcd.print("L");
     else if(signal == ".--.") lcd.print("P");
     else if(signal == "--.-") lcd.print("Q");
     else if(signal == "...-") lcd.print("V");
     else if(signal == "-..-") lcd.print("X");
     else if(signal == "-.--") lcd.print("Y");
     else if(signal == "--..") lcd.print("Z");
     else lcd.print("#");
   }
   else if(signal.length() == 5){
     if(signal == "-----") lcd.print("0");
     else if(signal == ".----") lcd.print("1");
     else if(signal == "..---") lcd.print("2");
     else if(signal == "...--") lcd.print("3");
     else if(signal == "....-") lcd.print("4");
     else if(signal == ".....") lcd.print("5");
     else if(signal == "-....") lcd.print("6");
     else if(signal == "--...") lcd.print("7");
     else if(signal == "---..") lcd.print("8");
     else if(signal == "----.") lcd.print("9");
     else lcd.print("#");
   }
   else if(signal.length() == 0){
     char_cnt--;
   }
   else lcd.print("#");
   signal = "";
   char_cnt++;
 }


 if(char_cnt == 16) lcd.setCursor(0,1);
 else if(char_cnt == 32){
   lcd.setCursor(0,0);
   char_cnt = 0;

 }
 Serial.println(signal);
}

これを実際に書き込んでみて、知っているモールス信号を打ってみて、このように表示されたら成功です。

IMG_6775.jpg

これで完成です!
動かなかったら、配線が間違っている可能性もあるのでコードを確認してから配線を確認してみてください。
断線の可能性もあるので気をつけてください。

6 今後の展望

このままではあまり綺麗とは言えないので、かっこいいもので周りを囲いたいです。
やる気があれば、打たれた信号をLCDにリアルタイムで表示できるようにしたいです。
他にも、強烈に目が覚めるような目覚まし装置を作ってみたいです

7 最後に

読んでいただきありがとうございました!
初記事なので、至らない点がありましたら、ご指摘いただけると幸いです🙇
皆さんも良きモールス信号LIFEを!

8 参考文献

配線でお世話になりました。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?