GroveBeginnerKit を、C++(Arduino)とRuby(ラズパイ,Rboard) で使う　PWM編

[しまねソフト研究開発センター](https://www.s-itoc.jp)（略称 ITOC）にいます、東です。

[Grove Beginner Kit for Arduino を使ってみる記事](https://qiita.com/HirohitoHigashi/items/dc43ea72d572b21a6823)の第3回、今回は PWM (Pulse Width Moduration) を題材にします。

ターゲットは、以下の通り。
* Arduino　- 付属の Arduino Uno 互換機
* mruby/c - [RBoard](https://www.sjc-inc.co.jp/service/rboard)

今回、ラズパイのライブラリは、まだPWMを用意していないので一旦保留にします。


# 1. Buzzer

Grove ブザーモジュールは、ピエゾ素子を使ったモジュールです。
Buzzer という名前に反して、自励式ではありません。つまり電源をONしただけでは音が鳴りません。音を鳴らすには、鳴らしたい周波数で信号線を ON/OFF する必要があります。


| 外観 | 回路図 |
|:-:|:-:|
| ![grove_Buzzer.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/c2e60e6b-1089-d5a0-27ab-13eaa8df1ad6.jpeg) | ![grove_Buzzer_Schema.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/6d5138e5-d811-11a4-8eff-0a2d44fcb2cd.png) |

（[メーカーWiki: Grove - Buzzer](https://wiki.seeedstudio.com/Grove-Buzzer/) より引用）


## Arduino

`D5`番に接続します。切り離さなければ、そこに接続されています。
```c++:Buzzer.ino
void setup() {
  pinMode( 5, OUTPUT );
}

void loop() {
  analogWrite( 5, 128 );
  delay( 100 );
  analogWrite( 5, 0 );
  delay( 900 );
}
```

このプログラムでは、周波数 977Hz で音が鳴る→止まるを繰り返します。

マジックナンバー 128 は、デューティー比を表す値で、255を最大として 128はデューティー比 50%を表します。（ここも覚えておかないといけないことが多いですね）
公式Wikiでは、DigitalWriteを使って一秒ごとに ON/OFF を繰り返すサンプルを提示してありましたが、前述の通り自励式ブザーでは無いので 2Hzの音では耳で聞こえません。途中で仕様変更があったのでしょうか？

（参考引用：　公式Wiki Play With Arduino - Softwareより）
```c++
void setup()
{
  pinMode(6, OUTPUT);
}

void loop()
{
  digitalWrite(6, HIGH);
  delay(1000);
  digitalWrite(6, LOW);
  delay(1000);
}
```

音程（周波数）の変更もトライしたかったのですが、Arduinoで周波数を変更する方法は、簡単に調べた範囲ではマイコンのレジスタを直接変更する方法しか見つからず、API として用意はされていないようです。


## RBoard (mruby/c)
Grove 端子 Digital へ接続します。
[ピン配置図](https://yoshihiroogura.github.io/RBoardDocument/pin.html) により、16番ピンを使用することがわかります。

```ruby:Buzzer.rb
buzzer = PWM.new( 16 )

while true
  buzzer.frequency( 1000 )
  sleep( 0.2 )
  buzzer.frequency( 0 )
  sleep( 0.8 )
end
```
周波数 1kHz で音を鳴らす→止めるを繰り返します。ただ、RBoardの電源電圧 3.3Vでは音量が十分に確保できず、とても小さい音でしか鳴りませんでした。


### アナログ入力との組み合わせ

次に、PWM出力をしている状態で、周波数の変更をやってみます。
ADC編で説明した Rotary Potentiometer と組み合わせて、つまみを回す確度に応じた音程の音を出すプログラムを書いてみます。

Grove ADC 端子へ、Rotary Potentiometer を接続します。

```ruby:Buzzer2.rb
buzzer = PWM.new( 16 )
pot = ADC.new( 20 )

while true
  v = pot.read
  buzzer.frequency( v * 1000 )
  sleep( 0.1 )
end
```

ADC機能を使って読み込んだ値を、周波数に反映させています。つまみを回すと音程が滑らかに変化して面白いです。



# 2. LED （デューティー比変更）

PWMのデューティー比変更を試します。
GPIO編で使った　Red LED　と ADC編で使った Potentiometer を使って、つまみを回す角度によって LEDの明るさを変えるプログラムを作ってみます。

| LED | Potentiometer |
|:-:|:-:|
| ![grove_RedLED.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/ab8c259f-bb28-e622-90bc-a03fc597fbb7.jpeg) | ![grove_RotaryPotentiometer.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/b6f072aa-38bf-cf30-0d46-38cea8723c62.jpeg) |


## Arduino

LED モジュールを、D3番に接続します。標準では D4番ですが、D4は PWM が使えないので、D3を使います。切り離すのが嫌なら、このモジュールは使わず別途LEDと抵抗を用意して、ピンヘッダ経由でD3に接続すると良いと思います。
PotentioMeterは、A0 番に接続します。切り離さなければ、そこに接続されています。


```c++:LED_PWM.ino
void setup() {
  pinMode( 3, OUTPUT );
}

void loop() {
  long v = analogRead( A0 );    // 0 to 1023
  analogWrite( 3, v * 255 / 1023 );

  delay( 100 );
}
```
デューティー比変更が `analogWrite` =「アナログ値の出力」と言い切っているのが潔いというかなんというか。


## RBoard (mruby/c)

LEDを　Digital端子へ、RotaryPotentiometerを、ADC端子へ接続します。

```ruby:led_pwm.rb
led = PWM.new( 16, frequency:100, duty:0 )
pot = ADC.new( 20 )

while true
  v = pot.read   # v = 0 to 3.3
  led.duty( v / 3.3 * 100 )

  sleep( 0.1 )
end
```

こちらは、`PWM.new` で、最初に周波数を 100Hz を指定しています。
メインのループ中で `duty` メソッドを使って、デューティー比を 0 から 100 <sub>(%)</sub> まで変更して LED の明るさを変えています。


# おわりに

今回は、PWM を試してみました。
RBoard (mruby/c) の PWMは、ここで紹介したメソッドの他に、１周期のONになる時間をマイクロ秒で指定する `pulse_width_us( micro_sec )` というメソッドもあります。たとえばパルスワイド幅で指示を出す模型用サーボモーターなどを使うときは、`duty`　よりも　`pulse_width_us` の方が使いやすいかもしれません。

次回は、I2C を使って、各種センサーを試してみたいと思います。