Raspi 5VピンをON/OFFする

1.きっかけ

以前、Raspberry PI の5Vは出力されていると書いた。しかしLCD(1602aと表示されているが、1602と同じものと思われる。)を使い電子時計を作成したときに、誰も見ていなのに時刻表示しても意味ないし、無駄だなと思い、ON/OFFを制御できる3.3Vピンを使用したところ、バックパネルが暗くて文字の判読が困難だった。

理想的には、人感センサーを使って人がある距離に入ったら5Vをオン、去っていったら5VをOFFして表示を止める仕様だ。

そこで、どうしても5VピンのON/OFFを制御する必要性に迫られた。

もう1つの解決方法は3.3Vで可動するLCDを購入することだが、そんな無駄なことはしていられない。購入したキットの中に両者が入っているからだ。

LCDである1602の設定方法、人感センサー(型番が明確ではないがHC-SR501と思われる)のそれぞれのやり方については、ネット上にあふれているので、そちらを参考にしてほしい。

ここではこれらにトランジスターをプラスして5Vを制御してみることにする。

出来上がりはこんな調子。

LCDが時刻を表示しているところ
LCDの表示が消えた写真
こちらは表示が消えました

2.準備したもの

  • ラズパイ ZERO WH
  • 人感センサー(HC-SR501)
  • LCD(1602)
  • トランジスター(S8550のデータシート)
  • ジャンパーピン類

3.トランジスターの特性

あの板に乗ったかまぼこ型の板側(平らな面)からC(コレクタ)、B(ベース)、E(エミッタ)の順で(日本製はECBの順になるらしい。)

ポイントは2つ

1つ目は接続方法

「B-E間に弱い電流が流れると、C-E間にも流れる。C-E間には通常は流れていない。」と言う文章を読んで、そうか、C-E間に5Vの電流を流そうということかと思ったが、発熱して1個だめにした。

正解は、5VではなくGNDをつないで、5Vの通り道を作ってやる。B-E間の弱電流はスイッチの役目を果たすということ。

2つ目はトランジスターの型

NPN型とPNP型の2種類があって、上の説明文の「B-E間に電流が流れると」はNPN型に当てはまるが、PNP型は逆になるということ。

すなわちPNP型は「B-E間に電流が流れなくなると」C-E間に電流が流れるということ。

4.実際の配線

HC-SR501ラズパイS85501602A
SDA(GPIO2)SDA
SCL(GPIO3)SCL
3.3V(常時)
GPIO21B(ベース)10KΩ経由
GNDE(エミッタ)
GNDGNDC(コレクタ)GND
Power5V(常時)VCC
センサーGPIO12

ちょっと分かりづらいかもしれませんが、センサとしてのGPIO12がHighになると、トランジスターのB-E間に電流が流れなくなるようにします。するとPNP型トランジスタは、C-E間の通電回路GND(への通路)を開くので、LCDのVCC(5V)がC-E間を経由して、GNDに流れ落ちる(通電する)。こんな感じでしょう。

再度注意しますが、「5Vを制御する」と言ってもトランジスターに直接5Vを流すという意味ではありません。

5.Pythonコード

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
from time import sleep
from time import strftime
import datetime
import smbus
import RPi.GPIO as GPIO
import time

Sensor_PIN = 12 #人感センサーのセンサーピン
Switch_PIN = 21 #トランジスターのコレクタピン
GPIO.setmode(GPIO.BCM)
GPIO.setup(Sensor_PIN,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(Switch_PIN,GPIO.OUT)

# これ以下の関数関係のコードはネット上の諸先輩方のコードを借用させていただきました。
# Define some device parameters
I2C_ADDR  = 0x27 # I2C device address 
LCD_WIDTH = 16   # Maximum characters per line

# Define some device constants
LCD_CHR = 1 # Mode - Sending data
LCD_CMD = 0 # Mode - Sending command

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line

LCD_BACKLIGHT  = 0x08  # On
#LCD_BACKLIGHT = 0x00  # Off

ENABLE = 0b00000100 # Enable bit

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

#Open I2C interface
#bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
bus = smbus.SMBus(1) # Rev 2 Pi uses 1

def lcd_init():
  # Initialise display
  lcd_byte(0x33,LCD_CMD) # 110011 Initialise
  lcd_byte(0x32,LCD_CMD) # 110010 Initialise
  lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
  lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off 
  lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
  lcd_byte(0x01,LCD_CMD) # 000001 Clear display
  sleep(E_DELAY)

def lcd_byte(bits, mode):
  # Send byte to data pins
  # bits = the data
  # mode = 1 for data
  #        0 for command

  bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
  bits_low = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT

  # High bits
  bus.write_byte(I2C_ADDR, bits_high)
  lcd_toggle_enable(bits_high)

  # Low bits
  bus.write_byte(I2C_ADDR, bits_low)
  lcd_toggle_enable(bits_low)

def lcd_toggle_enable(bits):
  # Toggle enable
  sleep(E_DELAY)
  bus.write_byte(I2C_ADDR, (bits | ENABLE))
  sleep(E_PULSE)
  bus.write_byte(I2C_ADDR,(bits & ~ENABLE))
  sleep(E_DELAY)
  

def lcd_string(message,line):
  # Send string to display
  message = message.ljust(LCD_WIDTH," ")
  lcd_byte(line, LCD_CMD)
  for i in range(LCD_WIDTH):
    lcd_byte(ord(message[i]),LCD_CHR)

def main(): #「日時」を表示
    # Main program block
    # Initialise display
    lcd_init()
    n = 0 # 表示のカウンター
    while n < 10: 
# ある程度の回数を表示したら、たとえ人が前にいても、この繰り返しを抜け出て、人感センサーに制御を移すため。While True:文だと、人がいなくなってもこの繰り返しから抜け出せなくなり、結果的にバックライトが点きっぱなしになってしまう。
      local_time = datetime.datetime.now()
      lcd_string(local_time.strftime("%Y.%m.%d (%a)") , LCD_LINE_1) 
      # 1行目は年月日と曜日の表示
      lcd_string(local_time.strftime("%H:%M"), LCD_LINE_2)
      # 2行目は時間の表示
      sleep(0.5)

      local_time = datetime.datetime.now()
      lcd_string(local_time.strftime("%Y.%m.%d (%a)") , LCD_LINE_1)
      lcd_string(local_time.strftime("%H %M"), LCD_LINE_2)
      sleep(0.5)
      n += 1 #カウンタの増加

try: #----こちらは人感センサーの赤外線でのサーチ
      while True:
     if  (GPIO.input(Sensor_PIN) == GPIO.HIGH): #人感センサーに反応があったら
         GPIO.output(Switch_PIN,GPIO.LOW) #トランジスターのB-E間を閉じる。
         main() # 時刻表示に移ります。
         time.sleep(1) 
     else:
         GPIO.output(Switch_PIN,GPIO.HIGH) #通常はトランジスターのB-E間は通電。
         time.sleep(1)

except KeyboardInterrupt:
  print('Cancel')
finally:
  LCD_BACKLIGHT = 0x00  #バックライトオフ
  lcd_byte(0x01, LCD_CMD) #表示内容クリア
  GPIO.cleanup()
  print("-----end-----")

あとは crotab -eに登録して起動時に動き出させるようにします。

$ sudo nano crontab -e

@reboot python3 install-directory/disp1602a_on.py

コメント

タイトルとURLをコピーしました