子どもの夏休みの実験の準備で、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); }
最終版コードはこちらからもダウンロードできます。
コメント