1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-04-01

しまねソフト研究開発センター(略称 ITOC)にいます、東です。

Grove Beginner Kit for Arduino を使ってみる記事の第2回、今回は、アナログ入力を題材にします。
このキットに付属しているアナログ入力センサーは、以下の3種類です。

ターゲットは、以下の通り。

1. Rotary Potentiometer

ロータリーポテンショメーターは、一般にボリュームとか可変抵抗器とか呼ばれているものです。メーカーの方でも、Rotary Potentiometer と言ったり、Rotary Angle Sensor と言ったりと言い方が揺らいでいますが同じものです。
このモジュールの場合、抵抗器の両端に電圧をかけておいて、出力端子で回転軸の位置に比例した分圧電圧を出力させています。

外観 回路図
grove_RotaryPotentiometer.jpeg grove_RotaryPotentiometer_Schema.png

メーカーWiki: Grove - Rotary Angle Sensor より引用)

この記事では、簡単のため読み込んだ値をコンソールに表示させるだけにします。

Arduino

A0 番に接続します。切り離さなければ、そこに接続されています。

void setup() {
  Serial.begin(9600);
}

void loop() {
  int v = analogRead( A0 );   // 0 to 1023
  Serial.println( v );
}

実行結果

rotary_result_arduino.gif

ゼロから最大値1023まで、値は整数で取得できます。

Raspberry Pi (CRuby)

ラズパイには、アナログ入力端子はありません。そのため、この HAT は別途マイコンを搭載しており、その内蔵 A/D によってアナログ入力を実現しています。マイコンとラズパイ間は、I2Cによって通信する仕組みです。
I2C の仕様は、公式ウェブページなどには公開されていないようですが、同ページからダウンロードできるサンプルプログラムのコメントで解説されていますので、容易に使うことが可能です。

ADC クラスの用意

前述したようにI2Cを使うので、I2Cクラスを使って直接操作しても良いのですが、mruby 共通 API の ADC仕様 に合わせておくと都合が良いため、Wrapperを作っておきます。

adc.rb
#
# ADC class for
#  Grove Base Hat for RaspberryPi
#
require "mruby/i2c"

class ADC
  # Grove Base Hat for RPI I2C Registers
  #   0x00 ~ 0x01:
  #   0x10 ~ 0x17: ADC raw data
  #   0x20 ~ 0x27: input voltage
  #   0x29: output voltage (Grove power supply voltage)
  #   0x30 ~ 0x37: input voltage / output voltage

  I2C_ADRS = 0x04
  BUS = I2C.new()

  def initialize( pin )
    raise ArgumentError  if pin < 0 || pin > 7
    @pin = pin
  end

  def read_voltage()
    data = BUS.read( I2C_ADRS, 2, 0x20 + @pin ).bytes
    ((data[1] << 8) + data[0]) / 1000.0
  end
  alias read read_voltage

  def read_raw()
    data = BUS.read( I2C_ADRS, 2, 0x10 + @pin ).bytes
    (data[1] << 8) + data[0]
  end
end

Grove A0 端子に接続します。

RotaryPotentiometer.rb
require_relative "adc"

adc = ADC.new( 0 )
while true
  puts adc.read_voltage
  sleep 1
end

実行結果

rotary_result_raspi.gif

read_voltage メソッドを使っていますので、値は 0.0 から、約 3.3 まで、電圧(浮動小数点値)で取得できています。

RBoard (mruby/c)

Grove ADC 端子へ接続します。

RotaryPotentiometer.rb
adc = ADC.new( 20 )

while true
  puts adc.read_voltage
  sleep 1
end

ラズパイと同じプログラムが動きます。結果もほぼ同じなので、省略します。

2. Light Sensor

フォトトランジスタを使った光センサーです。

外観 回路図
grove_LightSensor.jpeg スクリーンショット 2024-03-18 14.03.06.png

メーカーWiki: Grove - Light Sensor より引用)

使用しているフォトトランジスタ(LS06-MΦ5)の特性は、引用してあるデータシートを見てもあまり詳しい情報が得られないですし、R1 (68kΩ)の値なども影響するので、軽くキャリブレーションして使ってみます。

キャリブレーション

標準として使用する照度計は、東京前川科学社製 No.04-0354 A-BS 型です。これも相当古いものですのでずれはあるでしょうが、参考にはなるでしょう。
IMG_3071.jpeg

光源は、最初クリプトン電球とスライダックで試みたのですが、センサーが赤外領域で感度が強いらしく標準器との相関をとるのが難しかったため、太陽光を適度に遮光して行いました。

Arduino (Vcc=5V) での結果。

出力電圧 (V) 照度 (lx)
0.60 10
1.09 20
1.95 40
2.52 60

grove_LightSensor_CAL_Arduino.png

RBoard (Vcc=3.3V) での結果

出力電圧 (V) 照度 (lx)
0.49 10
0.91 20
1.23 30
1.80 50

grove_LightSensor_CAL_RBoard.png

双方とも、同じような傾きでデータが取得できました。
電圧の高い部分(=明るい)は、ほぼこの電圧でサチります。つまり、50〜60lx 以上の照度は、このセンサー単体では測れません。
電圧の低い部分は、このカーブを伸ばしてもゼロ点でクロスはしないと思います。これは、測定誤差ももちろん大きいですが、暗電流と呼ばれるゼロルクスでもフォトトランジスタにある程度の電流が流れる現象のためです。

Arduino

A6 番に接続します。切り離さなければ、そこに接続されています。

LightSensor
const double CAL_TBL[][2] = {
//  (V)  (lx)
  {0.60, 10},
  {1.09, 20},
  {1.95, 40},
  {2.52, 60},
};

#define NUMOF(x) (sizeof(x) / sizeof((x)[0]))

void setup() {
  Serial.begin(9600);
}

void loop() {
  double v, lx, v0, v1, lx0, lx1;

  v = analogRead( A6 ) * 5.0 / 1023;
  Serial.print("Volt: ");
  Serial.print( v );

  v0  = CAL_TBL[0][0];
  lx0 = CAL_TBL[0][1];

  int i = 1;
  while( 1 ) {
    v1  = CAL_TBL[i][0];
    lx1 = CAL_TBL[i][1];
    if( v <= v1 ) break;
    if( ++i >= NUMOF(CAL_TBL) ) break;
    v0 = v1;
    lx0 = lx1;
  }

  lx = (lx1 - lx0) / (v1 - v0) * (v - v0) + lx0;
  if( lx < 0.0 ) lx = 0.0;
  Serial.print("  Lux:");
  Serial.println( lx );

  delay( 1000 );
}

analogRead で読んだ値を電圧に換算し、先ほど求めたキャリブレーション値を直線補間して照度を求めています。

実行結果

実行結果

Raspberry Pi (CRuby)

Grove A6 端子に接続します。

LightSensor.rb
require_relative "adc"

CAL_TBL = [
  # (V)  (lx)
  [ 0.49, 10 ],
  [ 0.91, 20 ],
  [ 1.23, 30 ],
  [ 1.80, 50 ],
]

adc = ADC.new( 6 )

while true
  v = adc.read_voltage

  v0,lx0 = CAL_TBL[0]
  i = 1
  while true
    v1,lx1 = CAL_TBL[i]
    break if v <= v1
    break if (i += 1) >= CAL_TBL.size
    v0,lx0 = v1,lx1
  end

  lx = (lx1 - lx0) / (v1 - v0) * (v - v0) + lx0
  lx = 0.0 if lx < 0
  printf "%4.2f V  %5.1f lx\n", v, lx

  sleep	1
end

アルゴリズムはArduinoと同じですが、Vcc 電圧が Raspberry Pi と RBoard は同じ 3.3Vなので校正表は RBoard で取得したものを使います。Arduinoと比べて、こちらの方は電圧値に変換しなくても良い事とRubyの多重代入構文のおかげで、少しだけ短く書くことができます。

実行結果

実行結果

RBoard (mruby/c)

Grove ADC 端子へ接続します。

LightSensor.rb
CAL_TBL = [
  # (V)  (lx)
  [ 0.49, 10 ],
  [ 0.91, 20 ],
  [ 1.23, 30 ],
  [ 1.80, 50 ],
]

adc = ADC.new( 20 )

while true
  v = adc.read_voltage

  v0,lx0 = CAL_TBL[0]
  i = 1
  while true
    v1,lx1 = CAL_TBL[i]
    break if v <= v1
    break if (i += 1) >= CAL_TBL.size
    v0,lx0 = v1,lx1
  end

  lx = (lx1 - lx0) / (v1 - v0) * (v - v0) + lx0
  lx = 0.0 if lx < 0
  printf "%4.2f V  %5.1f lx\n", v, lx

  sleep 1
end

ラズパイと同じプログラムが動きます。結果もほぼ同じなので、省略します。

3. Sound Sensor

単なるマイクです。ちょっとした外部回路がついています。
このモジュールに関しては、後述する理由によりサンプルプログラムは省略します。

外観 回路図
grove_SoundSensor.jpeg grove_SoundSensor_Schema.png 

メーカーWiki: Grove - Sound Sensor より引用)

回路図を見ると、最終出力段は 220nF で AC カップリングしてあるだけです。つまり直流的に電位が定まっていません。また、マイクの出力を約100倍に増幅していますが、バイアスがかかっていないのでマイナス側がクリップされます。

念のため、オシロスコープで実測してみました。

grove_SoundSensor_scope1.png

図は、RaspberryPi + HAT のアナログ入力に接続し、約820Hzの音を入力した時です。

  • Ch2(上のライン) オペアンプ1段目の入力
  • Ch1(下のライン) Grove 出力ピン

入力 32mVp-p 程度で出力 1.9Vp-p 程度の波形が観測できました。出力のマイナス側がクリップされており、グラウンド電位も中途半端です。これを Arduino に接続すると、Arduino 内部でプルダウンされているのか、たまたまなのか、無音状態で0Vに安定します。
また波形は掲載していませんが、少し大きい音だとプラス側の波形もすぐクリップします。音が鳴っているかどうか、おおまかに音量はどのぐらいかが分かれば良いという設計思想でしょうか。

接続する機器によって無音時の電圧が特定できないこと、ある程度連続してサンプリングをして、Peak to Peak から音の大きさを類推する程度がせいぜいであるだろうことから、このモジュールに関してはここで終了にします。

おわりに

今回は、アナログ入力を試してみました。任意のタイミングで電位を測定する用途であれば、とても簡単に試すことができます。一方、ある程度速く一定速度での連続サンプリングが必要なケースだと、ハードウェアの助けを必要とするので、それを Wrap したライブラリがあると便利な場合がありそうですね。

次回は、PWM を試してみたいと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?