2022 年度 OSS リテラシ 3 : Raspberry Pi でのセンサー利用 (GROVE キット利用版)

はじめに

Grove Base Hat for Raspberry PiGROVE スターターキット を用いて,2 年生のマイコン実験 (Arduino) で行ったような内容をラズパイで行ってみる.

Grove とは,seeed 社が提供するモジュール化されたシステムであり, コネクタが共通化されていることが大きな特徴である. 試しに何か作ってみるときには,配線やケーブルの心配をしなくて良いので, Grove は大変便利である.

また,ラズパイは ADC (A/D コンバータ) 機能を持たないが, Grove Base Hat for Raspberry Pi には ADC 用のアナログポートが存在する. このハットのアナログポートは,ラズパイの機能を使うのではなく, ハットに組み込まれているチップ (STM32) を使っていることを把握しておいてほしい.

なお,教員の手元には他にも多くの I2C の GROVE センサ がある.もし,それらが使いたければ相談して欲しい.

ライブラリのインストール

seeed 社がラズパイ用に提供している python ライブラリ grove.py があるが, 試したところ最新の Raspberry Pi OS (bullseye) (Debian11 相当) では 動かないものがいくつかあった.そのため,本演習では seeed 社の提供する grove.py を基本として,grove.py が動作しないものについては 別のライブラリ (pigpio, RPi.GPIO) を利用することにする. なお,grove.py は裏で RPi.GPIO を利用している.

seeed 社のライブラリ (grove.py) のインストール

ライブラリの詳細はgrove.py の GitHub のリポジトリ を参照してほしい.

$ git clone https://github.com/Seeed-Studio/grove.py
$ cd grove.py
$ sudo pip install .

なお,この grove.py をインストールすると, 各種外部機器を動作させるための コマンド群 も提供される.

pigpio

ライブラリの詳細は pigpio library の Web ページ を確認してほしい.

pip コマンドでインストールする.

$ sudo pip install pigpio

pigpio を利用する際は,管理者権限で pigpiod を起動する必要がある.

$ sudo pigpiod

RPi.GPIO

ライブラリの詳細は raspberry-gpio-python の Web ページ を確認してほしい.

pip コマンドでインストールする.

# sudo pip install RPi.GPIO

外部機器の利用例

以下で用いるプログラム群は, <URL:https://github.com/gfd-dennou-club/grove-raspberrypi/> にて公開している. そこからコピーしてかまわない.

[GPIO] Grove LED

LED を GROVE ハットのデジタルピン (先頭に D が付いているコネクタ) に接続する.以下では LED を D5 ピンに接続した場合の例を示す.

Pythonライブラリ grove.py のコマンドラインインターフェイスを 動かしてみる.1 秒ごとに LED が点滅することが分かる.

$ grove_led 5

Pythonライブラリ grove.py を用いてプログラミングする. 上記コマンドが呼んでいる /usr/local/lib/python3.9/dist-packages/grove/grove_led.py を見ると,以下のようなプログラムを書いて実行すれば良いことが分かる.

$ vi 01-led.py

  from grove.gpio import GPIO
  from time import sleep

  pin = 5
  led = GPIO(pin, GPIO.OUT)

  while True:
      led.write(1)
      sleep(1)
      led.write(0)
      sleep(1)

$ python 01-led.py

[GPIO] Grove ボタン [OK]

ボタンを GROVE ハットのデジタルピン (先頭に D が付いているコネクタ) に接続する.以下ではボタンを D16 ピンに接続した場合の例を示す.

Pythonライブラリ grove.py のコマンドラインインターフェイスを 実行してみる.ボタンを押すと HIGH になることが確認できる.

$ grove_switch 16

  Hat Name = 'Grove Base Hat RPi'
  LOW
  HIGH  <-- スイッチ押す
  LOW
  LOW

Pythonライブラリ grove.py を用いてプログラミングする. 上記コマンドが呼んでいる /usr/local/lib/python3.9/dist-packages/grove/grove_button.py を見ると,以下のようなプログラムを書いて実行すれば良いことが分かる. なお,この grove.gpio は内部で RPi.GPIO を用いている (/usr/local/lib/python3.9/dist-packages/grove/gpio/gpio_rpi.py を読めばわかる).

$ vi 02-button.py

  from grove.grove_switch import GroveSwitch
  import time

  PIN = 16
  swicth = GroveSwitch(PIN)

  while True:
     if swicth.state:
        print("high")
     else:
        print("low")
     time.sleep(1)

$ python 02-button.py

  LOW
  HIGH  <-- スイッチ押す
  LOW
  LOW

また,LED と同じ GPIO クラスを用いると以下のように書ける. 以下のような書き方の方が良く見る一般的な書き方であろう.

$ vi 02-button-2.py

  from grove.gpio import GPIO
  import time

  pin = 16
  switch = GPIO(pin, GPIO.IN)

  while True:
      print (switch.read())
      time.sleep(1)

$ python 02-button-2.py

[GPIO] Grove ミニファン

ファンを GROVE ハットのデジタルピン (先頭に D が付いているコネクタ) に接続する.以下では LED を D18 ピンに接続した場合の例を示す.

ピンの電圧を上げるとファンが回転し,ピンの電圧を下げるとファンが止まる. LED のオンオフと基本的に同じプログラムで動作させることができる. 以下のプログラムを実行すれば,1 秒おきにファンを回転・停止させることができる.

$ vi 03-fan.py

  from grove.gpio import GPIO
  from time import sleep

  pin = 18
  led = GPIO(pin, GPIO.OUT)

  while True:
      led.write(1)
      sleep(1)
      led.write(0)
      sleep(1)

$ python 03-fan.py

[GPIO] Grove リレー

リレーを GROVE ハットのデジタルピン (先頭に D が付いているコネクタ) に接続する.以下では LED を D22 ピンに接続した場合の例を示す.

Pythonライブラリ grove.py のコマンドラインインターフェイスを 動かしてみる.1 秒ごとにカチカチ音がし,基板上の LED が点滅することが分かる.

$ grove_relay 22

リレーも GPIO で電圧の上げ下げで制御することができる.

$ vi 04-relay.py

  from grove.gpio import GPIO
  from time import sleep

  pin = 22
  led = GPIO(pin, GPIO.OUT)

  while True:
      led.write(1)
      sleep(1)
      led.write(0)
      sleep(1)

$ python 04-relay.py

なお,/usr/local/lib/python3.9/dist-packages/grove/grove_relay.py を見ると, GroveRelay クラスが定義されており,以下のようなプログラムでも 実行できることがわかる.

$ vi 04-relay-2.py

     import time
     from grove.grove_relay import GroveRelay

     PIN   = 22
     relay = GroveRelay(PIN)

     while True:
         relay.on()
         time.sleep(1)
         relay.off()
         time.sleep(1)

$ python 04-relay-2.py

[PWM] Grove パッシブブザー

ラズパイで PWM を扱えるのは,ハットの PWM コネクタと D18 コネクタの 2 つである.そのため,ブザーは PWM か D18 コネクタのどちらかに 接続しないといけない. 以下ではブザーを PWM ピン (= 12 ピン) に接続した場合の例を示す.

Pythonライブラリ grove.py で提供されるコマンド ラインインターフェイスは動かない.必要となる grove.grove_pwm_buzzer.py ファイルが存在しないためである.

$ grove_pwm_buzzer

  Traceback (most recent call last):
    File "/usr/local/bin/grove_pwm_buzzer", line 5, in <module>
      from grove.grove_pwm_buzzer import main
  ModuleNotFoundError: No module named 'grove.grove_pwm_buzzer'

そこで,Python ライブラリ pigpio で PWM 信号を扱うことにする. 以下のようなプログラムを作成して実行すれば良い.

$ sudo pigpiod

$ vi 11-buzzer.py

  import time
  import pigpio

  pi = pigpio.pi()

  #PWMパラメータ
  pwm_pin = 12 #PWM出力ピンを指定 (PWM ピン)
  duty = 50 #デューティー比を%で指定
  freq = 400 #PWM周波数をHzで指定

  #パラメータ変換
  cnv_dutycycle = int((duty * 1000000 / 100))

  #PWMを出力
  pi.hardware_PWM(pwm_pin, freq, cnv_dutycycle)

  # 3 秒間鳴らす
  time.sleep(3)

  #PWMを止める. duty = 0 に.
  pi.hardware_PWM(pwm_pin, freq, 0)

$ python 11-buzzer.py

[PWM] Grove サーボ

ラズパイで PWM を扱えるのは,ハットの PWM コネクタと D18 コネクタの 2 つである.そのため,サーボモーターは PWM か D18 コネクタのどちらかに 接続しないといけない. 以下ではブザーを PWM ピン (= 12 ピン) に接続した場合の例を示す.

Pythonライブラリ grove.py のコマンドラインインターフェイスを 動かしてみる.角度を順次変更していることが分かる.

$ grove_servo 12

  Hat Name = 'Grove Base Hat RPi'
  0 degree
  1 degree
  2 degree

Pythonライブラリ grove.py を用いてプログラミングする. 上記コマンドが呼んでいる /usr/local/lib/python3.9/dist-packages/grove/grove_servo.py を見ると,GroveServo クラスが定義されており, 以下のようなプログラムを書いて実行すれば良いことが分かる.

$ vi 12-servo.py

  from grove.grove_servo import GroveServo
  import time

  pin = 12
  servo = GroveServo(pin)

  while True:
      for x in range(0, 180):
          print(x, "degree")
          servo.setAngle(x)
          time.sleep(0.05)
      for x in range(180, 0, -1):
          print(x, "degree")
          servo.setAngle(x)
          time.sleep(0.05)

$ python 12-servo.py

[ADC] Grove 回転角度センサ

回転角度センサを GROVE ハットのアナログピンに接続する. 以下ではセンサを A0 に接続した場合の例を示す.

Pythonライブラリ grove.py のコマンドラインインターフェイスを 実行させてみる. つまみを回すことで値が変化するのが分かるだろう.

$ grove_rotary_angle_sensor 0

  Rotary Value: 0
  Rotary Value: 88
  Rotary Value: 312
  Rotary Value: 524
  Rotary Value: 615
  Rotary Value: 999

Pythonライブラリ grove.py を用いてプログラミングする. 上記コマンドが呼んでいる /usr/local/lib/python3.9/dist-packages/grove/grove_rotary_angle_sensor.py から,以下のようなプログラムを書いて実行すれば良いことが分かる.

$ vi 21-rotary_angle.py 

     import time
     from grove.grove_rotary_angle_sensor import GroveRotaryAngleSensor

     PIN = 0
     sensor = GroveRotaryAngleSensor(PIN)

     while True:
         print('Rotary Value: {}'.format(sensor.value))
         time.sleep(.2)

$ python 21-rotary_angle.py 

なお, /usr/local/lib/python3.9/dist-packages/grove/grove_rotary_angle_sensor.py を読むと, /usr/local/lib/python3.9/dist-packages/grove/adc.py で定義されている ADC クラスのラッパーとして GroveRotaryAngleSensor クラスが定義されていることがわかる. /usr/local/lib/python3.9/dist-packages/grove/adc.py を元に, ADC クラスを陽に用いたプログラムを作成すると以下のようになる.

$ vi 21-rotary_angle-2.py 

  import time
  from grove.adc import ADC

  adc = ADC()
  while True:
      # Read channel 0(Slot A0) voltage. Max 3300 mV
      print( int( adc.read_voltage(0) / 3300 * 1000 ) )  # val 0-1000
      time.sleep(1)

$ python 21-rotary_angle-2.py 

[ADC] Grove サウンドセンサ

サウンドセンサを GROVE ハットのアナログピンに接続する. 以下ではセンサを A2 に接続した場合の例を示す.

Pythonライブラリ grove.py のコマンドラインインターフェイスで 実行させてみる. 音を鳴らすと数字が変化するのが分かるだろう.

$ grove_sound_sensor 2

  Hat Name = 'Grove Base Hat RPi'
  Detecting sound...
  Sound value: 644
  Sound value: 668
  Sound value: 467
  Sound value: 999

Pythonライブラリ grove.py を用いてプログラミングする. 上記コマンドが呼んでいる /usr/local/lib/python3.9/dist-packages/grove/grove_sound_sensor.py から,以下のようなプログラムを書けば良いことが分かる.

$ vi 22-sound_sensor.py

     import time
     from grove.grove_sound_sensor import GroveSoundSensor

     PIN = 2
     sensor = GroveSoundSensor(PIN)

     print('Detecting sound...')
     while True:
         print('Sound value: {0}'.format(sensor.sound))
         time.sleep(.3)

$ python 22-sound_sensor.py

[ADC] Grove ライトセンサ

ライトセンサを GROVE ハットのアナログピンに接続する. 以下ではセンサを A4 に接続した場合の例を示す.

Pythonライブラリ grove.py のコマンドラインインターフェイスを 実行させてみる. センサを手で覆うなどすると,値が変化するのが分かるだろう.

$ grove_light_sensor_v1_2  4

  Hat Name = 'Grove Base Hat RPi'
  Detecting light...
  Light value: 592
  Light value: 646
  Light value: 642

Pythonライブラリ grove.py を用いてプログラミングする. 上記コマンドが呼んでいる /usr/local/lib/python3.9/dist-packages/grove/grove_light_sensor_v1_2.py から,以下のようなプログラムを書いて実行すれば良いことが分かる.

$ vi 23-light.py

  import time
  from grove.grove_light_sensor_v1_2 import GroveLightSensor

  PIN = 4
  sensor = GroveLightSensor(PIN)

  print('Detecting light...')
  while True:
      print('Light value: {0}'.format(sensor.light))
      time.sleep(1)

$ python 23-light.py

Grove 温度湿度センサ DHT11

温湿度センサ DHT11 を GROVE ハットのデジタルピン (先頭に D が付いている コネクタ) に接続する.以下では LED を D26 ピンに接続した場合の例を示す.

Pythonライブラリ grove.py で提供されるコマンドは実行できない. upm が RaspberryPi OS (bullseye) で提供されていないようだ.

$ grove_temperature_sensor
   ... (略) ...
   ModuleNotFoundError: No module named 'upm'

DHT11 用に seeed 社が Python ライブラリを公開しているので, それをインストールして使うことにする.

$ sudo pip install seeed-python-dht

$ vi 41-dht11.py

  import time
  import seeed_dht

  # DHT11 type
  dht11 = seeed_dht.DHT("11", 26)

  while True:
      humi, temp = dht11.read()
      print('DHT{0}, humidity {1:.1f}%, temperature {2:.1f}*'.format(dht11.dht_type, humi, temp))
      time.sleep(1)

$ python 41-dht11.py

  DHT11, humidity 55.0%, temperature 24.0*
  DHT11, humidity 53.0%, temperature 25.0*

[I2C] Grove 16x2 LCD

LCD を GROVE ハットの I2C ピンに接続する.

<URL:https://wiki.seeedstudio.com/Grove-LCD_RGB_Backlight/#play-with-raspberry-pi> を参考にプログラムを作成する. GrovePigrove_rgb_lcd を元にプログラムを作成する.

$ vi 51-lcd.py

  import time,sys
  import smbus

  bus = smbus.SMBus(1)

  # this device has two I2C addresses
  DISPLAY_TEXT_ADDR = 0x3e

  # send command to display (no need for external use)
  def textCommand(cmd):
      bus.write_byte_data(DISPLAY_TEXT_ADDR,0x80,cmd)

  # set display text \n for second line(or auto wrap)
  def setText(text):
      textCommand(0x01) # clear display
      time.sleep(.05)
      textCommand(0x08 | 0x04) # display on, no cursor
      textCommand(0x28) # 2 lines
      time.sleep(.05)
      count = 0
      row = 0
      for c in text:
          if c == '\n' or count == 16:
              count = 0
              row += 1
              if row == 2:
                  break
              textCommand(0xc0)
              if c == '\n':
                  continue
          count += 1
          bus.write_byte_data(DISPLAY_TEXT_ADDR,0x40,ord(c))

  # main
  setText("Hello world?? \nThis is an LCD test")

実行すると,setText 関数で指定した文字列が表示される.

$ python 51-lcd.py

課題

課題 1 は必須とし,課題 2-5 以降は任意とする (提出された場合はエクストラで採点する).

課題 1

GROVE スターターキット wiki (英語) を参考にして,周辺機器を 4 個以上接続したシステムを構築して動作させよ. 但し,GPIO,PWM,ADC は各 1 つ以上使うこと.

  • 教員に動作確認してもらうこと.

課題 2

PWM のパッシブブザーのサンプルプログラムを改良して, LED の明るさと音の高さを同期変化させるプログラムを作成せよ. 段階的に明るく/高音にしていき,明るさが最大になった後は段階的に暗く/低音にすること.

  • 教員に動作確認してもらうこと.

課題 3

パッシブブザーを PWM ではなく,GPIO (電圧の ON/OFF) で 「ドレミファソラシド」と音を鳴らすこと.

  • 2 年生のマイコン実験の課題と同じ
  • 教員に動作確認してもらうこと.

課題 4

ADC のサウンドセンサもしくはライトセンサについて, /usr/local/lib/python3.9/dist-packages/grove/adc.py 内で定義されている ADC クラスを用いてプログラムを作成せよ. すなわち,サンプルスクリプト 21-rotary_angle-2.py と同様な プログラムをサウンドセンサもしくはライトセンサについて作成すること.

  • 教員に動作確認してもらうこと.

課題 5

LCD のデータシート の p.14-15 を参考に,カタカナ文字を液晶ディスプレイに表示させよ.

  • 教員に動作確認してもらうこと.

ペンディングしている機器

[GPIO] モーションセンサー [反応が悪い.壊した?]

LED を GROVE ハットのデジタルピン (先頭に D が付いているコネクタ) に接続する.以下では LED を D24 ピンに接続した場合の例を示す.

Pythonライブラリ grove.py のコマンドラインインターフェイスを 動かしてみる.正直なところ,センサの反応は良くない.

$ grove_mini_pir_motion_sensor 24

  Hat Name = 'Grove Base Hat RPi'
  Motion detected.

プログラムを自分で書く場合は, /usr/local/lib/python3.9/dist-packages/grove/grove_mini_pir_motion_sensor.py を参考にすること (センサの反応が悪くて使い物にならないので,本テキストでは省略).

Grove LED (WS2813ミニ)[光らない.壊した?]

ソース的には 12, 18 のどちらかを指定するように書かれている.

https://qiita.com/m_take/items/e80735e860ce235c1a74

/usr/local/bin/grove_ws2813_rgb_led_strip

$ sudo grove_ws2813_rgb_led_strip 12


$ vi pwm-ws2813.rb

     import time
     from rpi_ws281x import Color
     from grove.grove_ws2813_rgb_led_strip import GroveWS2813RgbStrip

     # connect to pin 12(slot PWM)
     PIN   = 12
     # For Grove - WS2813 RGB LED Strip Waterproof - 30 LED/m
     # there is 30 RGB LEDs.
     COUNT = 30
     strip = GroveWS2813RgbStrip(PIN, COUNT)

     # Define functions which animate LEDs in various ways.
     def colorWipe(strip, color, wait_ms=50):
         """Wipe color across display a pixel at a time."""
         for i in range(strip.numPixels()):
             strip.setPixelColor(i, color)
             strip.show()
             time.sleep(wait_ms/1000.0)

     print ('Color wipe animations.')
     colorWipe(strip, Color(255, 0, 0))  # Red wipe
     colorWipe(strip, Color(0, 255, 0))  # Blue wipe
     colorWipe(strip, Color(0, 0, 255))  # Green wipe

[参考] Ruby で L チカ

Ruby ライブラリ pi_piper を用いても,Lチカなど実行することができる.

$ sudo apt install ruby-dev libssl-dev
$ gem install pi_piper

$ vi led.rb

  require "pi_piper"
  pin_a = PiPiper::Pin.new(:pin => 5, :direction => :out)
  loop do
    pin_a.on
    sleep 1
    pin_a.off
    sleep 1
  end

$ ruby led.rb