【初心者向け】圧電スピーカーで音を鳴らそう｜ラズパイで電子工作

こんにちは。今回は圧電スピーカーでメリーさんの羊を流したいと思います。


##用意するもの
- ラズパイ
- 圧電スピーカー
今回は秋月電子の[PKM13EPYH4000-A0]を使用しました。30円で購入できました！（2020年3月現在）
[購入はこちら](http://akizukidenshi.com/catalog/g/gP-04118/)
- ブレッドボード
- ジャンパーワイヤ　オス―メス 2本

##rpi.gpioのインストール
rpi.gpioは標準でインストールされているので、アップデートしましょう。
コマンドプロンプトを起動し、以下のコマンドを入力してください。
```sudo apt-get update
sudo apt-get upgrade
```
もしインストールする場合は、以下のコマンドを実行しましょう。

```sudo apt-get install python-rpi.gpio
```

##回路を組む
今回の回路はとてもシンプルです。
スピーカーの片方をGND（ground）、もう片方を好きなGPIOのピンにつなぎましょう。
[=ピン配置はこちら](https://www.raspberrypi.org/documentation/usage/gpio/README.md:title)
![438716.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355521/4ff2a3fe-eb42-dbb8-b6fd-da56755e8d87.jpeg)


![438720.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355521/8628e0cc-4945-e48b-8e92-6bf47b5ccbf0.jpeg)

写真はGPIO21に挿してあります。

##コード

まずは適当な音を鳴らしてみましょう。

```python

#coding:utf-8

import RPi.GPIO as GPIO
import time

pin = 21

GPIO.setmode(GPIO.BCM)
GPIO.setup(pin,GPIO.OUT,initial=GPIO.LOW)

p = GPIO.PWM(pin,1)
p.start(50)

p.ChangeFrequency(220)
time.sleep(3)

p.stop()
GPIO.cleanup()
```

##解説
```python
#coding:utf-8
```
日本語のコメント記述ができるようにしています。

```python
import RPi.GPIO as GPIO
import time
```
使うライブラリをインポート。

```python
pin = 21
```
自分のさしたGPIOピンの番号を指定します。

```python

GPIO.setmode(GPIO.BCM)
```

GPIO.setmode()は、ピン番号の指定の仕方を決定します。
今回のGPIO.setmode(GPIO.BCM)は、役割ごとの番号で指定するという意味。GPIOの〇番という風に指定します。
ちなみに、GPIO.setmode(GPIO.BOARD)だと、左上から右下まで順に数えた番号（１～４０）で指定します。

```python

GPIO.setup(pin,GPIO.OUT,initial=GPIO.LOW)
```

GPIOを出力として指定します。引数は、（ピン番号,出力or入力,初期設定がLOWかHIGHか）

```python

p = GPIO.PWM(pin,1)
p.start(50)
```

PWMのインスタンスをつくり、開始します。
ざっくりいうとスタートの方法みたいな感じです。

```python

p.ChangeFrequency(220)
time.sleep(3)
```
周波数を変えます。ここではラに相当する220Hzに設定。
time.sleep(3)で３秒間続けます。

```python

p.stop()
GPIO.cleanup()
```

stopで停止。
GPIO.cleanup()でもうそのピンを使わないよ～という意味です。これをしないと、次にこのピンを使用する際にエラーが出ます。


##メリーさんの羊のコード
```python

#coding:utf-8

import RPi.GPIO as GPIO
import time
import math

pin = 21   #任意のピン番号
a0=27.500   #低いラの音

#n番目の音階の周波数を返す
def onkai(n):
    return a0*math.pow(math.pow(2,1/12),n)

#それぞれの音の周波数を定義
DO=onkai(27)
RE=onkai(29)
MI=onkai(31)
SO=onkai(34)

#メロディとリズムを配列に
mery_merody=[MI,RE,DO,RE,MI,MI,MI,RE,RE,RE,MI,SO,SO,MI,RE,DO,RE,MI,MI,MI,RE,RE,MI,RE,DO]
mery_rhythm=[0.9,0.3,0.6,0.6,0.6,0.6,1.2,0.6,0.6,1.2,0.6,0.6,1.2,0.9,0.3,0.6,0.6,0.6,0.6,1.2,0.6,0.6,0.9,0.3,1.8]

GPIO.setmode(GPIO.BCM)
GPIO.setup(pin,GPIO.OUT,initial=GPIO.LOW)

p = GPIO.PWM(pin,1)
p.start(50)

#なぜか最初の方は変な音がするので適当に鳴らす
p.ChangeFrequency(2)
time.sleep(2)

#配列の通りに鳴らす
for i, oto in enumerate(mery_merody):
    p.start(50)
    p.ChangeFrequency(oto)
    time.sleep(mery_rhythm[i])
    p.stop()
    time.sleep(0.03)

p.stop()
GPIO.cleanup()
```

##解説
```python

import math
def onkai(n):
    return a0*math.pow(math.pow(2,1/12),n)
```
いちいち周波数を指定するのもあれなんで、関数作ってみました。
[https://tomari.org/main/java/oto.html](https://tomari.org/main/java/oto.html)
このサイトによると、

'''
音階は、「音を高低の順番に並べたもの」あり、音の高低は周波数で表します。。
音は、周波数が半分になると１オクターブ低くなり、周波数が倍になると１オクターブ高くなります。
１オクターブには１２の音があり（①ド、②ド＃（レ♭）、③レ、④レ＃（ミ♭）、⑤ミ、⑥ファ、⑦ファ＃（ソ♭）、⑧ソ、⑨ソ＃（ラ♭）、⑩ラ、⑪ラ＃（シ♭）、⑫シ）、
その１２の音は、隣り合う半音間での周波数の比率が同じです。（音階に対して等比数列的に増える）
音の周波数をa0,a1,a2・・・・a12とし、この等比数列の公比をｒとすると、a12=a0r12=2a0であるから、r12=2よりr=12√2（r=1.059463094）、よってan=a0rn=a0*(2(1/12))nとなります。
'''

よって
an=a0*(2(1/12))n ※a0=27.50Hz(結構低いラ)
らしいので、nを引数にして、ｎ番目の音の周波数を返す関数をかいてみました。

```python

#それぞれの音の周波数を定義
DO=onkai(27)
RE=onkai(29)
MI=onkai(31)
SO=onkai(34)
```

結局ここでこう書いたら関数の意味なかったな…とおもう。

```python

#メロディとリズムを配列に
mery_merody=[MI,RE,DO,RE,MI,MI,MI,RE,RE,RE,MI,SO,SO,MI,RE,DO,RE,MI,MI,MI,RE,RE,MI,RE,DO]
mery_rhythm=[0.9,0.3,0.6,0.6,0.6,0.6,1.2,0.6,0.6,1.2,0.6,0.6,1.2,0.9,0.3,0.6,0.6,0.6,0.6,1.2,0.6,0.6,0.9,0.3,1.8]
```
めりさんの羊の音階、それぞれの音をならす時間の配列です。ここをかえればお好きな曲をながせます。
四分音符を0.6にしてあります。皆さんのお好みの長さに変えてください。

```python

#なぜか最初の方は変な音がするので適当に鳴らす
p.ChangeFrequency(2)
time.sleep(2)
```
なぜだかわからないのですが、実行して最初のほうだけ音がずれてたので、最初2秒くらい聞こえないくらい低い音鳴らしています。なくていいです。

```python

#配列の通りに鳴らす
for i, oto in enumerate(mery_merody):
    p.start(50)
    p.ChangeFrequency(oto)
    time.sleep(mery_rhythm[i])
    p.stop()
    time.sleep(0.03)
```

enumerateを使うことで、iに繰り返し回数が入ります。
もっといい書き方あったら教えてください。

otoの周波数に変えて、それをmery_rhythm[i]秒続けます。
p.stop()、time.sleep(0.03)で一回止めます。音が連続してるとつながってしまったので、この処理を書きました。
リズムずれますけどまあご愛敬…。

##まとめ
ということで、圧電スピーカーを鳴らしてみました。
次回は踏切の音をならしたいな～