スピーカーでチャイムを鳴らす
スピーカーは電気信号を音(振動)に変える装置です。次は、このスピーカーをTessel 2のGPIOピンに接続して音を鳴らしてみましょう。ちなみにスピーカーは振動をあたえると電気信号に変換できるため、一種の振動センサーともいえます。
スピーカーとは
機器の組み込み用途には、電圧をかけると変形する圧電素子を使った圧電スピーカーがよく使われますが、今回は一般的な、磁石とコイルを使ったダイナミック型の薄型スピーカーを利用しました。ダイナミック型スピーカーは、磁場内において電流が流れる導体に力が発生する、フレミングの左手の法則を利用したものです。
スピーカーとの接続
音を鳴らすだけなら、スピーカーをTessel 2のGNDとGPIOピンに直接つなぐだけで可能です。ただ、今回はTessel 2とスピーカーの保護のために、抵抗とコンデンサを直列に接続しました。
スピーカーは直流に対しての抵抗が小さく、大きな電流が流れる恐れがあります。そのため、100Ω程度の抵抗を直列に接続します。また、直流電圧はスピーカーを振動させるには不要なため、コンデンサを用いてカットしています。コンデンサとは電荷を蓄えたり放出したりするパーツで、直流を通さずに交流を通すといった性質があります。このような交流電流だけを通過させる目的に使うコンデンサのことをカップリングコンデンサと呼びます。
今回は手持ちのパーツの都合で、10μFのコンデンサを利用しました。1~10μF程度のものが適当です。なお、F(ファラッド)はコンデンサ容量の単位で、数字が大きいほど容量が大きくなります。
全体の回路図は、次のようになります。
音を鳴らすには
スピーカーは、GPIOに接続して電圧をかけるだけでは音は出せません。音を鳴らすにはコイルを振動させないとならないため、コイルに流す電気信号に変化が必要です。そこで前回も用いたPWM出力を利用します。前回はPWM出力がONになる割合のデューティ比を変更して、LEDの明るさを変化させました。今回はPWM信号の時間間隔であるPWM周期を設定して、特定の周波数の音を発生させます。
次のサンプルコードはPWM周期を440Hz、デューティ比50%のPWM信号を出力するものです。この出力にスピーカーを接続すると、440Hzの音が発生します。
const bucketsJs = require('buckets-js'); // buckets-jsモジュール const tessel = require('tessel'); const pwd = tessel.port.A.pwm[0]; // PWD対応ピンオブジェクト // PWM周波数 tessel.pwmFrequency(440); // デューティ比 pwd.pwmDutyCycle(0.5); setInterval(() => {}, 1000);
スマートフォンのチューナーアプリで計測すると、ちゃんと440ヘルツの音になっています。
音階のしくみ
メロディーを奏でるためには音の高さと周波数を変える必要があります。ではどのような周波数に設定すれば、音階となるのでしょうか。
ある周波数の音を基準として周波数の比で音階を定めることを音律といいます。今回は1オクターブを12等分する音律(平均律)を使って、周波数を設定します。平均律では先ほどの440Hzの音を音階のA(ラ)の音と定め、他の音の周波数を決定します。周波数の計算方法は、MIDI tuning standardなどを参考にしてください。
メロディーを奏でる
音階の周波数がわかったところで、音階を鳴らしてみましょう。次のコードは0.5秒単位でドレミと鳴らすサンプルです。指定する周波数は計算では小数となりますが、Tessel 2では整数値でしか周波数を指定できないため、小数点以下を切り捨てた値にしています。
const tessel = require('tessel'); const pwd = tessel.port.A.pwm[0]; // PWD対応ピンオブジェクト tessel.pwmFrequency(261); // ド(C4) pwd.pwmDutyCycle(0.5); sleep(500).then(() => { tessel.pwmFrequency(293); // レ(D4) pwd.pwmDutyCycle(0.5); sleep(500).then(() => { tessel.pwmFrequency(329); // ミ(E4) pwd.pwmDutyCycle(0.5); sleep(500); }); }); // 指定時間後にresolveが実行されるPromiseオブジェクトを返す(1) function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); }
このコードのポイントは2つあります。
1つは、周波数を変更する処理です。必ずpwmFrequency、pwmDutyCycleメソッドの順にセットで実行する必要があります。
2つ目は、Node.jsでの同期処理の書き方です。サンプルでは指定時間後にコールバックされるようなPromiseオブジェクトを返すsleepメソッド(1)を定義しています。Promiseオブジェクトのthenメソッドで次の処理を指定することにより、処理が同期的に実行されるようにしています。
人感センサーでチャイムを鳴らす
では最後に、人を検知するとチャイム(メロディー)を鳴らすようにしてみましょう。サンプルコードは次のようになります。
サンプルではメロディーのデータ保存用に、buckets-jsモジュールのQueueクラスを使用しています。サンプルコードを実行する前に、コマンドプロンプトからnpm install buckets-jsと実行して、モジュールをインストールしておきます。
const bucketsJs = require('buckets-js'); // buckets-jsモジュール const tessel = require('tessel'); const pin = tessel.port.A.pin[2]; // ポートAの2番ピンのオブジェクト const pwd = tessel.port.A.pwm[0]; // PWD対応ピンオブジェクト // 演奏用キュー var play_queue = null; // ピンの入力電圧ONでチャイムを鳴らす(1) setInterval(() => { pin.read(function(error, value) { if ((value==1) && !play_queue) { // 入力電圧ONかつ演奏中でないなら play_queue = set_melody(); // メロディーデータの設定 play(play_queue); // 演奏 } }); }, 1000); // メロディーデータ(周波数と長さのセット)のキューを返す(2) function set_melody() { // 四分音符の長さの定義 const bpm = 60*1000/160; // メロディーデータの設定 const melody = new bucketsJs.Queue(); melody.enqueue([740,1*bpm]); // F#5 melody.enqueue([587,1*bpm]); // D5 melody.enqueue([440,1*bpm]); // A4 melody.enqueue([587,1*bpm]); // D5 melody.enqueue([659,1*bpm]); // E5 melody.enqueue([880,3*bpm]); // A5 melody.enqueue([659,1*bpm]); // E5 melody.enqueue([740,1*bpm]); // F#5 melody.enqueue([659,1*bpm]); // E5 melody.enqueue([440,1*bpm]); // A4 melody.enqueue([587,4*bpm]); // D5 return melody; } // 指定時間後にresolveが実行されるPromiseオブジェクトを返す function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); } // キューからデータを取り出し、1音の周波数、長さをPWMに設定する(3) function play(q) { if (q.isEmpty()) { // キューが空なら pwd.pwmDutyCycle(0); // PWM出力を停止する play_queue = null; return; } var note = q.dequeue(); tessel.pwmFrequency(note[0]); // 周波数を指定する pwd.pwmDutyCycle(0.5); sleep(note[1]).then(() => { play(q); // 再帰的に呼び出す }); }
人感センサーの出力状態をsetIntervalで1秒ごとに判定し検知した場合は、演奏中でない(変数play_queueがnull)なら、メロディーデータを設定して演奏を開始してます(1)。キューのモジュールを使うと、onメソッドがうまく動作しなかったため、このような形にしています。
メロディーデータは、周波数と1音の長さを1組として、キューに保存しています(2)。
playメソッドではキューからデータを取り出し、1音の周波数、長さをPWMピンに設定しています(3)。このメソッドのポイントは、再帰呼び出しです。Promiseオブジェクトのthenメソッドでplayメソッド自身を指定することにより、キューのデータを同期的に処理するようにしています。キューが空になった時点で、再帰呼び出しを終了します。
サンプルを実行すると次の動画のようになります。
最後に
今回利用した赤外線センサーモジュールは、まさにスイッチ代わりなので、チャイムを鳴らすだけでなく、いろいろなものに使えそうです。例えばUSBカメラを接続して、人を感知したときだけ画像を撮影するようにすれば、防犯カメラにもなるでしょう。この連載で紹介したように、Tessel 2に標準モジュールだけでなく、さまざまなセンサーを接続すれば、IoTデバイスとしての活用範囲が大きく広がるのではないでしょうか。