シンプル電子ピアノを作ってみよう
PWMはLEDの点灯以外にも、モーターの制御など、いろいろな用途に利用できます。PWMのパルス信号(矩形波)を、圧電スピーカーに加えると音が鳴ります。圧電スピーカーとは、圧電素子を用いた安価で小型のスピーカーです。PWM信号の矩形波では、いわゆるビープ音と呼ばれる機械音となりますが、PWMの周波数を変えることで、音の高さを変えることができます。この圧電スピーカーとタッチセンサーで、かんたんな電子ピアノを作ってみましょう。
なお、圧電スピーカーと似たパーツに、圧電ブザーがあります。圧電ブザーは、発振回路が内蔵されており、電源に接続するだけで単一の音が鳴ります。圧電ブザーでは音を変化させることができないので注意してください。
音を鳴らす
.NET nanoFrameworkでは、スピーカーを鳴らすためのクラスも提供されていますが、ここまでのPWM制御のクラスだけでも、簡単にプログラムできます。
まずは、音階の基準音となる、440Hzの「ラ」の音を鳴らしてみましょう。LEDと抵抗の代わりに、圧電スピーカーを接続します。
プログラムは次のように書き換えます。
public static void Main()
{
Configuration.SetPinFunction(22, DeviceFunction.PWM1);
// 440Hzの設定(デューティ比は50%)
var pwmPin = PwmChannel.CreateFromPin(22, 440);
// PWM出力の開始
pwmPin.Start();
Thread.Sleep(3000);
// PWM出力の停止
pwmPin.Stop();
}
実行すると、440Hzの音が3秒間鳴ります。 「音程チェッカー」というアプリで確かめてみたところ、440Hzのラとして認識されました。
タッチイベントとの連動
タッチイベントと連動するには、イベントハンドラで、タッチされたときにPWMをStart、はなしたときにStopとします。
接続図は、以下のようになります。接続といっても、端子にスピーカーと鍵盤の代わりとなる導体(アルミテープなど)を接続するだけです。
ソース一式は、次のようになります。
public class Program
{
// 音階の周波数(ド、レ、ミ、ファ、ソ、ラ、シ)(1)
static readonly int[] Tone = { 262, 294, 330, 349, 392, 440, 494 };
// 使用するタッチセンサー番号(2)
static readonly int[] TouchNo = { 3, 0, 4, 5, 6, 8, 9 };
static PwmChannel PwmPin;
public static void Main()
{
Configuration.SetPinFunction(22, DeviceFunction.PWM1);
PwmPin = PwmChannel.CreateFromPin(22);
TouchPad.MeasurementMode = MeasurementMode.Timer;
// 使用する数のタッチパッドオブジェクトの配列を生成する(3)
var touchpad = new TouchPad[TouchNo.Length];
// タッチパッドオブジェクトの生成と初期化
for (int i = 0; i < TouchNo.Length; i++)
{
touchpad[i] = new TouchPad(TouchNo[i]);
touchpad[i].GetCalibrationData();
touchpad[i].Threshold = (uint)(touchpad[i].CalibrationData * 0.8);
touchpad[i].ValueChanged += TouchpadValueChanged;
}
// イベントハンドラ(4)
static void TouchpadValueChanged(object _, TouchPadEventArgs e)
{
if (!e.Touched)
{
// タッチセンサー番号を元に周波数の配列を参照する(5)
PwmPin.Frequency = Tone[Array.IndexOf(TouchNo, e.PadNumber)];
PwmPin.Start();
}
else
{
PwmPin.Stop();
}
}
Thread.Sleep(Timeout.Infinite);
}
}
音階の周波数
(1)行では、音階の周波数の配列を定義しています。音階の周波数の詳細は割愛しますが、いわゆる十二平均律とよばれる周波数です。ESP32のPWMでは、整数の周波数しか設定できないので、ここでは周波数を整数で定義しています。
複数のタッチセンサー
タッチセンサーの数に合わせて、TouchPadオブジェクトも複数生成しています(3)。ただし、(4)行のイベントハンドラは共通の関数として定義しています。このような書き方では、イベントハンドラの関数内でローカル変数は参照できないので、PWMオブジェクトと、周波数やタッチセンサー番号の配列は、クラスのメンバーとしています。
また、冒頭で書いたようにタッチセンサーの1、2、7番は動作しなかったので、それ以外のタッチセンサーを使用します。ここでは、接続のしやすさも考慮し、タッチセンサーの並びを決めています(2)。対応するGPIO番号は、順に15、4、13、12、14、33、32になります。
周波数の設定
イベントハンドラでは、タッチされたタッチセンサーの番号を元に、周波数の配列を参照して設定しています(5)。タッチセンサーの番号は、TouchPadEventArgsオブジェクトのPadNumberプロパティで参照できます。このタッチセンサーの番号を、配列TouchNoから Array.IndexOfメソッドで検索しています。Array.IndexOfメソッドは、最初に見つかった値のインデックスを返しますので、そのインデックスを利用して、音階の周波数の配列から該当する周波数を求めています。
最後に
今回は、タッチセンサーとPWM機能を使ったプログラムを作成しました。次回は、7セグメントLEDやドットマトリックスLEDを使ったプログラムを作成する予定です。
