子どもの夏休みの実験の準備で、Arduino Uno R3とLCD 1602 をつないでみた。かなり簡単に表示できることに気が付いて感動。
これはこれで、一段落ではあったけれど。 Arduinoでモールス符号って感じで、Lチカで遊んだな。と思い出す。これ、現在、打っているモールスの文字が表示できれば実はモールスの練習に使えるんじゃないの?と思い立つ。
Lチカに合わせて、打っているモールスをLCDに表示
とりあえずで、この前作ったLチカのコードにちょっとばかり追加。 追加個所は冒頭部分の、
/*
Morse Blink /w SPEAKER and LCD Unit
*/
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display
int morseCode[26][4] = { // DOT=1 , DASH=2 , BLANK=0
{1,2,0,0}, {2,1,1,1}, {2,1,2,1}, {2,1,1,0}, {1,0,0,0}, // A,B,C,D,E
{1,1,2,1}, {2,2,1,0}, {1,1,1,1}, {1,1,0,0}, {1,2,2,2}, // F,G,H,I,J
{2,1,2,0}, {1,2,1,1}, {2,2,0,0}, {2,1,0,0}, {2,2,2,0}, // K,L,N,M,O
{1,2,2,1}, {2,2,1,2}, {1,2,1,0}, {1,1,1,0}, {2,0,0,0}, // P,Q,R,S,T
{1,1,2,0}, {1,1,1,2}, {1,2,2,0}, {2,1,1,2}, {2,1,2,2}, // U,V,W,X,Y
{2,2,1,1} }; // Z
int SPEAKER = 12;
これと、表示させる部分の
void keying (String Text, int wpm) {
int i, j;
int Tone = 400;
int KeyingSpeed = (60 / (50 * (double)wpm)) * 1000;
for ( i=0; i < Text.length(); i++) {
lcd.setCursor(i,0);
lcd.print(Text.substring(i,i+1));
for ( j=0; j<4; j++) {
の部分、合計2か所、4行を追加するだけです。 たったこれだけの追加で、打っているコードをLCDに表記できるようになります。
Arduino Uno R3とLCD 1602 をつないでみるで実際にやってみたところのまんま。ユニットを初期化して、場所指定で文字を投げ込むだけですから。そりゃ、簡単にできるわけで。
ランダムなコードを送り続けるモールス練習機
ここまでできると、実は「送出文字をランダムにすればモールス練習機が作れるのでは?」という話か?しかも簡単に。 物理的な構成はそのままに、ソフトウェアだけ書き換えればいけそうな予感。
ランダムな文字列を取得する
ランダム文字列をArduino上で取得。 これはほかの実装とあまり大差なく、簡単に対応可能。 Arduinoのリファレンスには、こんな感じのサンプルが用意されています。
long randNumber;
void setup() {
Serial.begin(9600);
randomSeed(analogRead(0)); // 未接続ピンのノイズを利用
}
void loop() {
randNumber = random(300);
Serial.println(randNumber);
delay(50);
}
簡単ですっきり。 randomSeedで種の制御。ほかの言語だと種としてtimeとかを食わせてみるけれど。 Arduinoの場合、RTCはというと…ということで、ほかの実装のようなことはできない。 ということで、N/CのAnalogピンを読んで代用するというわけ。 言われればよくわかるけれど。これはコロンブスの卵的な発想ですね。 実際に使用する乱数(数値)はrandom関数で値を引き出します。 こっちはこっちで
void loop() {
randNumber = random(300); // 0から299の乱数を生成
Serial.println(randNumber);
randNumber = random(10, 20); // 10から19の乱数を生成
Serial.println(randNumber);
delay(50);
}
こんな書き方になります。randomのカッコ内にrandom(最大値)、もしくはrandom(最小値,最大値)と記述します。 最大と最小を指定できるだけでもかなり省力化できます。 今回もランダム生成に対応する文字はA – Zの範囲だとすれば、65 – 91の間の乱数が発生すれば事足ります。
KeyingString.concat((char)random(65,91));
こんな書き方をすれば、何かしらのアルファベットが出てくることになります。
LCDにどう表示するか?
練習機として使用するのであれば、打つタイミングで文字は表示されてほしいけど。 直前に打ったものも履歴で残ってほしいかも。 そう考えると、LCDがせっかく2段なので、1段目にライブな情報、2段目に直前の情報を表示してみましょう。 であれば、
void loop() {
...
keying(KeyingString, 18);
lcd.setCursor(0,1);
lcd.print(KeyingString);
...
}
こんな感じで、打ち終わったら、カーソルを2段目に移して打った文字を表示させればいいでしょう。
打つ文字列はどうしようか?
練習用として、ただただだらだらと打ち続けるというのもありかな? でも、そうなると聞き落とした時に復帰できなくなる。 一定の場所で区切られたほうがよさそう。 なら、一般的な電信の試験と同様に ランダムな5文字をひとくくり。 それを垂れ流すのはどうかな?
イメージとしては、こんな感じ。
| P | A | R | I | S | T | O | K | Y | O | ||||||
| O | S | A | K | A | O | M | I | Y | A |
OSAKA OMIYA PARIS TOKYOと打っているイメージ。 1行目に5文字1組を2組。 終わると、それらが2行目に移動。 これなら、結構簡単にスケッチできそう。
void loop() {
String KeyingString;
int i;
for(i = 0; i < 11; i++) {
if(i == 5){
KeyingString.concat(" ");
}else{
KeyingString.concat((char)random(65,91));
}
}
keying(KeyingString, 18);
lcd.setCursor(0,1);
lcd.print(KeyingString);
}
打つ文字を5文字+空白1文字+5文字で生成。 それをkeyingの関数に渡しちゃう。 打ち終わったら、2行目に渡した文字を表示。 これだけで完了。 これを実機に出してみましょう。 うん、上出来。 通電するといきなり始まるという部分さえ置いておけば、モールス練習機としては悪くない。
まとめ
Arduinoって、アイデアさえあれば、結構、「簡単に」「安価に」何でもできるんだね。 面白いもんです。
完成版のコード
できた。けど、なんかもっときれいに書く方法もありそうな気がするなぁ。
/*
Morse Blink /w SPEAKER and LCD Unit
*/
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display
int morseCode[26][4] = { // DOT=1 , DASH=2 , BLANK=0
{1,2,0,0}, {2,1,1,1}, {2,1,2,1}, {2,1,1,0}, {1,0,0,0}, // A,B,C,D,E
{1,1,2,1}, {2,2,1,0}, {1,1,1,1}, {1,1,0,0}, {1,2,2,2}, // F,G,H,I,J
{2,1,2,0}, {1,2,1,1}, {2,2,0,0}, {2,1,0,0}, {2,2,2,0}, // K,L,N,M,O
{1,2,2,1}, {2,2,1,2}, {1,2,1,0}, {1,1,1,0}, {2,0,0,0}, // P,Q,R,S,T
{1,1,2,0}, {1,1,1,2}, {1,2,2,0}, {2,1,1,2}, {2,1,2,2}, // U,V,W,X,Y
{2,2,1,1} }; // Z
int SPEAKER = 12;
void setup() {
lcd.init(); // LCD Initialize
lcd.backlight();
lcd.setCursor(0,0);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(SPEAKER, OUTPUT);
randomSeed(analogRead(0));
}
void keying (String Text, int wpm) {
int i, j;
int Tone = 400;
int KeyingSpeed = (60 / (50 * (double)wpm)) * 1000;
for ( i=0; i < Text.length(); i++) {
lcd.setCursor(i,0);
lcd.print(Text.substring(i,i+1));
for ( j=0; j<4; j++) {
switch (morseCode[(int)Text.charAt(i)-65][j]){
case 1: // DOT
digitalWrite(LED_BUILTIN, HIGH);
tone(SPEAKER, Tone, KeyingSpeed);
delay(KeyingSpeed);
digitalWrite(LED_BUILTIN, LOW);
delay(KeyingSpeed);
break;
case 2: // DASH
digitalWrite(LED_BUILTIN, HIGH);
tone(SPEAKER, Tone, KeyingSpeed*3);
delay(KeyingSpeed*3);
digitalWrite(LED_BUILTIN, LOW);
delay(KeyingSpeed);
break;
default: // BLANK
break;
}
delay(KeyingSpeed*2);
}
delay(KeyingSpeed*4);
}
lcd.clear();
}
// the loop function runs over and over again forever
void loop() {
String KeyingString;
int i;
for(i = 0; i < 11; i++) {
if(i == 5){
KeyingString.concat(" ");
}else{
KeyingString.concat((char)random(65,91));
}
}
keying(KeyingString, 18);
lcd.setCursor(0,1);
lcd.print(KeyingString);
}
最終版コードはこちらからもダウンロードできます。

コメント