YAS's VB.NET Tips
 
ラズベリーパイ活用
ラズベリーパイ活用
12
2019/08/16

温度・湿度・気圧ロガー&ロググラフ化合体

| by:YAS
 

 前回作ったBME280のロガーと,ログをグラフ化するコードを合体させてみました。1秒間隔で温度・湿度・気圧を測定し,表示を更新します。ログへの記録は5分間隔で行います。ログは日にちごとに分けて保存します。画面のどこかをクリックすると「グラフ作成中...」と表示され,しばらく待つとグラフが表示されます。ログファイルがない場合は「ログファイルがありません」と表示されます。グラフの画面をクリックすると,リアルタイムデータの画面に戻ります。
 Raspberry Pi -Zeroシリーズで実行すると,グラフを作成するところでフリーズしたかと思うほど待つので,別スレッドでグラフを作り,グラフ作成中もリアルタイムの計測値を表示し続けるようにしてみました。(また,メッセージ表示の枠の角を丸くするなど,どうでもよいところに力を入れています。なぜか,どうでもよいところほどやる気がでるのです。)
 画面表示にはpygameライブラリを,グラフ作成にはmatplotlibライブラリを使っています。

 気圧が下がると一部の発達障害の子どもたちは気持ちが落ち着かなくなるとよく言われますが,あまり事例のデータは無いようです。本当にそうなのか,今回作った装置&コードを使って確かめてみたいと思っています。

☆関連する記事

☆Pythonのコード
 下のコードは,ハードはRaspberry Pi - Zero WHにBME280をI2C接続したもの,OSはRaspbian Buster,Pythonのバージョンは 3.7.3 で作成・動作確認をしています。

17:00
2019/08/16

スレッドプール利用のサンプル

| by:YAS
 Raspberry Pi Zeroシリーズが非力であるのは分かっているが,matplotlibでグラフを作成するのに8~10秒かかるので,サブスレッドで処理することにした。
 下のコードはサブスレッドに引き数を渡し,サブスレッドの終了時に返り値を受け取るサンプル。

16:00
2019/08/15

BME280のログデータのグラフ化

| by:YAS


 先日作った,BME280ボードを使った,温度・湿度・気圧ロガーのデータをmatplotlibでグラフ化してみました。
 DATA_DIRにあるログデータを読み込んで,PIC_DIRに画像で出力します。
 Y軸のラベルの向きと位置を変えて,Y軸の単位の表示にしていますが,出力画像のサイズを変えると位置がずれてしまいます。Y軸の目盛を3種類にしたり,X軸のラベルを斜めにしたり,少し凝って作ってみました。。
 これを前回作ったロガーと合体させて。1つにし,簡易Webサーバーで外部から参照できるように機能追加しようと思っています。

☆コード
 pygameではデフォルトでは日本語を表示できないので,IPAゴシックをダウンロードし,同じフォルダに配置して使っています。
import csv
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import matplotlib.font_manager as mfontm
import matplotlib.dates as mdates
import datetime

DATA_DIR = "./BME280log/" # データのあるディレクトリ
LOG_NAME = " BME280.log" # 入力ログファイル名
PIC_DIR = "./BME280log/" # 画像を出力するディレクトリ
PIC_NAME = " BME280.png" # 出力画像ファイル名
PIC_SIZE = (4.83.2# 出力画像サイズ(480 x 320)
LINE_WIDTH = 1 # 折れ線グラフの線の太さ
DAYS_AGO = 0  # DAYS_AGO日前のデータを使う

# データを読み込む
timestamps = []
tempertures = []
humidities = []
pressures = []
date = "{0:%Y.%m.%d}".format(datetime.date.today() - datetime.timedelta(days=DAYS_AGO))
log_filename = DATA_DIR + date + LOG_NAME
with open(log_filename, "r"as f:
    reader = csv.reader(f)
    for row in reader:
        timestamps.append(datetime.datetime.strptime(row[0] ,"%Y/%m/%d %H:%M:%S"))
        tempertures.append(float(row[1]))
        humidities.append(float(row[2]))
        pressures.append(float(row[3]))
# グラフ作成
# フォントファイルの指定
fp = mfontm.FontProperties(fname="./ipag.ttf")
# 3つ重ね合わせたグラフ作成
fig = plt.figure(figsize=PIC_SIZE)
plt.subplots_adjust(left=0.07, bottom=0.15, top=0.9, right=1)
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax3 = ax1.twinx()
ax1.plot(timestamps, tempertures, color='orange', alpha=1.0, lw=LINE_WIDTH, antialiased=True)
ax2.plot(timestamps, humidities, color='deepskyblue', alpha=0.6, lw=LINE_WIDTH, antialiased=True)
ax3.plot(timestamps, pressures, color='green', alpha=0.8, lw=LINE_WIDTH, antialiased=True)
# Y軸の目盛り設定
ax1.set_ylim(040)
ax2.set_ylim(0100)
ax3.set_ylim(9601020)
ax1.yaxis.set_major_locator(ticker.MultipleLocator(5))
ax2.yaxis.set_major_locator(ticker.MultipleLocator(10))
ax3.yaxis.set_major_locator(ticker.MultipleLocator(10))
# 気圧の目盛りを外側に表示する
fig.subplots_adjust(right=0.80)
ax3.spines["right"].set_position(("axes"1.13))
# X軸のラベル(時分)
hoursfmt = mdates.DateFormatter("%H:%M")
ax1.xaxis.set_major_formatter(hoursfmt)
xlabels = ax1.get_xticklabels()
plt.setp(xlabels, rotation=45)
# Y軸のラベル(単位)
ax1.yaxis.set_label_coords(-0.0451.03)
ax2.yaxis.set_label_coords(1.071.09)
ax3.yaxis.set_label_coords(1.211.09)
ax1.set_ylabel("(℃)", fontproperties=fp, rotation=0)
ax2.set_ylabel("(%)", fontproperties=fp, rotation=0)
ax3.set_ylabel("(hPa)", fontproperties=fp, rotation=0)
# 凡例
fig.legend(["温度""湿度""気圧"], bbox_to_anchor=(00), bbox_transform=ax1.transAxes, loc="lower left", prop=fp)
#plt.xkcd()
pic_filename = PIC_DIR + date + PIC_NAME
plt.savefig(pic_filename)

11:49
2019/08/14

温度・湿度・気圧ロガー

| by:YAS


 先日作った,BME280ボードを使って,温度・湿度・気圧ロガーを作ってみました。画面の表示はpygameを使っています。1秒ごとに測定と画面更新を行い,10秒ごとにログに出力します。ログは日が変わる毎に新しく作られます。480 x 320のミニ液晶が前提で,フルスクリーン表示で起動します。ESCキーで終了します。
 今後は,画面タッチで1日の変化をmatplotlibを使ってグラフ表示するよう機能追加をしていきます。

☆コード
 pygameではデフォルトでは日本語を表示できないので,IPAゴシックをダウンロードし,同じフォルダに配置して使っています。
import smbus2 
import bme280
import datetime
import time
import pygame
from pygame.locals import *
import sys
import csv

BME280_PORT = 1
BME280_ADDRESS = 0x76
PYGAME_SCREEN_SIZE = (480320)
LOG_INTERVAL = 10

# BME280初期設定
bus = smbus2.SMBus(BME280_PORT)
calibration_params = bme280.load_calibration_params(bus, BME280_ADDRESS)

# pygame初期設定
pygame.init()
screen = pygame.display.set_mode((PYGAME_SCREEN_SIZE), FULLSCREEN)
pygame.display.set_caption("温度・湿度・気圧計"
myfont1 = pygame.font.Font("ipag.ttf"35)
myfont2 = pygame.font.Font("ipag.ttf"62)

old_time = time.perf_counter()
while True:
    new_time = time.perf_counter()
    if new_time - old_time >= 1:
        old_time = new_time
        # BME280からデータを読み出す
        data = bme280.sample(bus, BME280_ADDRESS, calibration_params)
        timestamp_str = "{0:%Y年%m月%d日 %H時%M分%S秒}".format(data.timestamp)
        temperture_str = "温度:{0:>6.1f} ℃".format(data.temperature)
        humidity_str = "湿度:{0:>6.1f} %".format(data.humidity)
        pressure_str = "気圧:{0:>6.1f} hPa".format(data.pressure)
        # pygameでデータを表示する
        timestamp = myfont1.render(timestamp_str, True, (255255255))
        temperture = myfont2.render(temperture_str, True, (255255255))
        humidity = myfont2.render(humidity_str, True, (255255255))
        pressure = myfont2.render(pressure_str, True, (255255255))                       
        screen.fill((000))
        screen.blit(timestamp, (010))
        screen.blit(temperture, (070))
        screen.blit(humidity, (0155))
        screen.blit(pressure, (0240))
        pygame.display.update()
        # 10秒毎にCSVに保存する
        if data.timestamp.second % 10 == 0:
            filename = "./BME280log/{0:%Y.%m.%d} BME280.log".format(datetime.date.today())
            f = open(filename, "a")
            writer = csv.writer(f)
            csvrow = []
            csvrow.append("{0:%Y/%m/%d %H:%M:%S}".format(data.timestamp))
            csvrow.append("{0:.1f}".format(data.temperature))
            csvrow.append("{0:.1f}".format(data.humidity))
            csvrow.append("{0:.1f}".format(data.pressure))
            writer.writerow(csvrow)
            f.close()
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == KEYDOWN and event.key == K_ESCAPE:
            pygame.quit()
            sys.exit()                

23:45
2019/08/14

Quimat 3.5inch モニター

| by:YAS
 Amazonで安かったのでQuimat 3.5inch モニターを買ってみました。
 HDMI接続で,タッチパネルでSPIを1つ使いますが,GPIOのピンが出ているので便利です。残念ながら最初に購入した物は初期不良でだったので返品し,再度購入しました。(HDMIケーブルを接続しないときの「No Signal」の表示も崩れていたので,不良なのは間違いないと思います。)
 Amazonのカスタマーレビューなどを参考にしながら設定したのですが,タッチパネルの設定がうまくいかず,180度タッチパネルだけ回転していたり,X軸・Y軸が入れ替わったりとなかなか手強かったので,備忘録として残しておきます。

☆ディスプレイの設定
  Amazonのカスタマーレビューを参考にしました。一度でうまく表示されました。
 ①/boot/config.txtを書き換えるのですが,管理権限がないと書き込めないので,まずファイルマネージャを管理権限で起動します。(やっぱりGUIの方が楽なので。)
$ sudo pcmanfm
 ②/boot/config.txtの末尾に次の設定を書き込み,保存して再起動する。
# for Quimat 3.5inch Monitor
hdmi_force_hotplug=1
hdmi_drive=2
hdmi_group=2
hdmi_mode=87
hdmi_cvt=480 320 60 6 0 0 0

☆タッチパネルの設定
 Amazonのカスタマーレビューにある,config.txtに書き込む方法では,タッチパネルの反応が180度回転してしまいました。(display_rotate=1にすれば全部ひっくりかえって使えるようになるのですが,HDMIの端子が下に出ることになるので,やっぱり使いにくいです。)
 /usr/share/X11/xorg.conf.d/99-calibration.confにキャリブレーションの設定を書き込む方法は,設定が無視されてしまい,だめでした。
 OSがRaspbian busterと新しかったり,やらない方がよいというrpi-updateをしたりしたからかもしれません。
 結局,うまくいったのは,下のように,gitからドライバをダウンロードしてインストールする方法でした。
$ git clone https://github.com/goodtft/LCD-show.git

$ chmod -R 755 LCD-show

$ cd LCD-show

sudo ./MPI3508-show


☆参考にしたwebページ
・Amazon カスタマーレビュー
 https://www.amazon.co.jp/gp/customer-reviews/RVTJH47O2U2YT/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&ASIN=B075K56C12
・GitHub - goodtft/LCD-show
 : 2.4" 2.8"3.2" 3.5" 5.0" 7.0" TFT LCD driver for the Raspberry PI 3B+/A/A+/B/B+/PI2/ PI3/ZERO/ZERO W
 https://github.com/goodtft/LCD-show
17:00
2019/08/13

BME280のボード作成

| by:YAS
 前回ブレッドボードで試作したBME280ボードをラズパイZero用ユニバーサル基板で作り直しました。
 I2C通信での接続なので配線が少ないのはよいのですが,どうしても配線がクロスするので少し悩みました。(結局,配線を短くするため,BME280キットを逆さにしました。)
 I2C通信に設定するにはJ3をはんだジャンパするとのことだったが,秋月のマニュアルの回路図を見ると,J3はCSBとVDDをショートするだけなので,CSBにVDDを入力すれば,はんだジャンパをする必要がないことがわかりました。(はんだジャンパ,うまくできずにイモはんだでかっこ悪いので,吸い取り線で除去して,CSBにVDDを繋ぐことにしました。)
 
☆材料
 ラズパイZero用ユニバーサル基板 秋月 P-14031100円 
 ラズパイ用スタッキングコネクタ 秋月 C-10702150円 
 BME280使用
 温湿度・気圧センサモジュールキット
 秋月 K-09421 1,080円 
 カーボン抵抗 1/4W 0Ω 秋月 R-25000 (100本)100円 
 合計  1,430円 

☆配線図


☆できあがり


☆テストコード
 ブレッドボードでテストしたときと同じコードでテスト。
 どうやら大丈夫そう。
import smbus2 
import bme280
import time

port = 1
address = 0x76
bus = smbus2.SMBus(port)
calibration_params = bme280.load_calibration_params(bus, address)

while True:
    data = bme280.sample(bus, address, calibration_params)
    print("{0:%Y年%m月%d日 %H時%M分%S秒}".format(data.timestamp))
    print(" 温度:{0:.2f} ℃".format(data.temperature))
    print(" 湿度:{0:.2f} %".format(data.humidity))
    print(" 気圧:{0:.2f} hPa\n".format(data.pressure))
    time.sleep(1)
>>> %Run BME280_Rpi.bme280.py

  2019年xx月xx日 xx時xx分xx秒
   温度:30.02 ℃
   湿度:50.79 %
   気圧:1007.31 hPa

  2019年xx月xx日 xx時xx分xx秒
   温度:30.03 ℃
   湿度:50.71 %
   気圧:1007.34 hPa

17:00
2019/08/12

BME280で温度・湿度・気圧を測る

| by:YAS
 秋月で買った温度・湿度・気圧センサーBME280をラズパイに繋いでみました。
 校正をしていないLM335Zよりも正確そうな数値が得られました。

☆材料
 ブレッドボード BB-801 秋月 P-05294200円 
 ラズベリーパイ用ブレッドボード接続キット 秋月 K-08892450円 
 BME280使用
 温湿度・気圧センサモジュールキット
 秋月 K-09421 1,080円 
 ブレッドボードジャンパーワイヤセット 秋月 C-05159 (60本)220円 
 合計  1,950円 

☆回路図


☆できあがり


☆チェック
 
①「i2cdetect -y 1」でアドレス76が表示されれば認識されています。
$ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- 76 --

☆ライブラリのインストール
 
①smbus2をインストールする。(Python3を使っているときは「pip3」でインストールする。)
$ sudo pip3 install smbus2
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting smbus2
Using cached https://www.piwheels.org/simple/smbus2/smbus2-0.2.3-py2.py3-none-any.whl
Installing collected packages: smbus2
Successfully installed smbus2-0.2.3
 ②RPi.bme280をインストールする。(Python3を使っているときは「pip3」でインストールする。)
$ sudo pip3 install RPi.bme280
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting RPi.bme280
  Using cached https://files.pythonhosted.org/packages/8a/55/2c738564ceb478952f15d6331759c0ca4f4d518e8e44689766deac9f42de/RPi.bme280-0.2.3-py2.py3-none-any.whl
Requirement already satisfied: smbus2 in /usr/local/lib/python3.7/dist-packages (from RPi.bme280) (0.2.3)
Installing collected packages: RPi.bme280
Successfully installed RPi.bme280-0.2.3

☆テストコード
 RPi.bme280ライブラリを使えばとても簡単なコードで温度・湿度・気圧が取得できます。(SWITCHSCIENCEさんのサンプルコードとほぼ同じ数値が得られました。「ほぼ」なのは,同時刻に取得した数値ではないからで,まったく同じといってよいと思います。)
 
参考にしたWebページによると,この温度は湿度・気圧を求めるときの補正用に使うセンサー自身の温度であって,周辺の環境の温度とは異なるとのことです。校正していないLM335Zと比べると,確かに3℃近く高い温度を示しますが,市販の温度計や体温計などを近くに置いてみると,BME280に近い温度を示します。気温・湿度・気圧のロガーを作る際に,気温だけLM335Zで測るようにしようかと思いましたが,校正するのも大変そうなので,BME280の温度をそのまま使うことにします。
import smbus2 
import bme280
import time

port = 1
address = 0x76
bus = smbus2.SMBus(port)
calibration_params = bme280.load_calibration_params(bus, address)

while True:
    data = bme280.sample(bus, address, calibration_params)
    print("{0:%Y年%m月%d日 %H時%M分%S秒}".format(data.timestamp))
    print(" 温度:{0:.2f} ℃".format(data.temperature))
    print(" 湿度:{0:.2f} %".format(data.humidity))
    print(" 気圧:{0:.2f} hPa\n".format(data.pressure))
    time.sleep(1)
>>> %Run BME280_Rpi.bme280.py

  2019年xx月xx日 xx時xx分xx秒
   温度:32.58 ℃
   湿度:58.63 %
   気圧:1005.91 hPa

  2019年xx月xx日 xx時xx分xx秒
   温度:32.57 ℃
   湿度:58.55 %
   気圧:1005.87 hPa
 上の直後にSWITCHSCIENCEさんのサンプルコードを実行した結果が下です。ほぼ同じ値です。
>>> %Run BME280_SRpi.bme280.py

  temp : 32.56  ℃
  pressure : 1005.89 hPa
  hum :  58.47 %


☆参考にしたwebページ
・RPi.bme280 · PyPI
  https://pypi.org/project/RPi.bme280/

・BME280/bme280_sample.py at master · SWITCHSCIENCE/BME280 · GitHub
 https://github.com/SWITCHSCIENCE/BME280/blob/master/Python27/bme280_sample.py

・5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT(25) ディジタル湿度センサ3 I2C BME280 | 電子工作の環境向上
 https://www.denshi.club/pc/raspi/5raspberry-pi-zeroiot25-3-i2c-bme280.html

・ツール・ラボ » 第28回 温湿度・気圧センサ(BME280) 〜仕様概要〜
 https://tool-lab.com/make/pic-practice-28/

17:00
2019/08/12

MCP3002とLM335Zで温度を測る

| by:YAS
 ADコンバータのMCP3002と,温度センサーのLM335Zで気温を測ってみます。
 spidevとgpiozeroでテストコードを作ってみましたが,どうも2つの値が一致しません。交互に測ってもgpiozeroで計測した方が,生の値で1大きくなる傾向がありました。基準電圧にラズパイの3.3Vを使っていますし,コンデンサーも省略しているので,処理によって微妙に基準電圧がゆらいでいるのかもしれません。
 基準電圧が3.3Vなので,3.3 * 100 - 273.15 = 56.85℃ まで測れます。気温計に使うには十分です。
 また,3.3 * 100 / 1023 ≒ 0.32℃ 刻みの測定になります。LM335Zは非校正時には2℃の誤差があることを考えれば精度は十分ですが,0.1℃刻みで測定したい気もします。0.1℃刻みにするには,3.3 * 100 / 0.1 = 3300 なので,12bit(=4095)のMCP3204あたりを使う必要がありそうです。(パッケージの小さい12bit2chのMCP3202は秋月にもないみたい。)

☆材料
 ブレッドボード BB-801 × 2 秋月 P-05294400円 
 ラズベリーパイ用ブレッドボード接続キット 秋月 K-08892450円 
 10bit 2ch ADコンバータ MCP3002-I/P 秋月 I-02584 160円 
 精密級高精度温度センサーLM335Z 秋月 I-1115875円 
 カーボン抵抗 1/4W 1kΩ 秋月 R-25102 (100本)100円 
 ブレッドボードジャンパーワイヤセット 秋月 C-05159 (60本)220円 
 合計  1,405円 

☆回路図


☆できあがり


☆テストコード
 ①spidevを使ってMCP3002に繋いだLM335Zの値を読む。
import spidev
import time

# spidevの設定
SPI_BUS = 0
SPI_DEVICE = 0
SPI_MAX_SPEED = 200000 # 200kHz

# MCP3002の設定
MCP3002_VREF = 3.3 # 基準電圧
MCP3002_START = 0b01000000 # スタートビット
MCP3002_SGL = 0b00100000 # シングルエンド入力
MCP3002_DIFF = 0b00000000 # 差動入力
MCP3002_ODD_CH0 = 0b00000000 # チャンネル0
MCP3002_ODD_CH1 = 0b00010000 # チャンネル1
MCP3002_MSBF =0b00001000 # MSBファースト
MCP3002_LSBF =0b00000000 # LSBファースト

while True:
    spi_values = []
    spi_values.append(MCP3002_START | MCP3002_SGL | MCP3002_ODD_CH0 | MCP3002_MSBF)
    spi_values.append(0x00)
    spi = spidev.SpiDev()
    spi.open(SPI_BUS, SPI_DEVICE)
    spi.max_speed_hz = SPI_MAX_SPEED
    result = spi.xfer2(spi_values)
    data = int.from_bytes(result, byteorder='big') & 0x3FF
    print ("{0} {1}℃".format(data, data * MCP3002_VREF / 1023 * 100 - 273.15))
    spi.close()
    time.sleep(1)
>>> %Run MCP3002_LM335Z_spidev.py

  934 28.140322580645147℃
  934 28.140322580645147℃
  934 28.140322580645147℃
  934 28.140322580645147℃
  934 28.140322580645147℃
  934 28.140322580645147℃
  934 28.140322580645147℃
②gpiozeroを使ってMCP3002に繋いだLM335Zの値を読む。
  何と簡単!
  でも,raw_valueがspidevを使ったときの値より1大きい気がする。
from gpiozero import MCP3002
import time

ch0 = MCP3002()

while True:
    result = ch0.raw_value
    print("{0} {1}℃".format(result, result * ch0.max_voltage /1023 * 100 - 273.15))
    time.sleep(1)
>>> %Run MCP3002_LM335Z_gpiozero.py

  935 28.462903225806485℃
  935 28.462903225806485℃
  934 28.140322580645147℃
  935 28.462903225806485℃
  935 28.462903225806485℃
  935 28.462903225806485℃
  935 28.462903225806485℃

17:00
2019/08/08

起動スイッチ,リブート・シャットダウンスイッチ

| by:YAS
 ラズパイ工作の定番と言われる,起動・リブート・シャットダウンスイッチです。
 赤のタクトスイッチが起動ボタンです。出力として使用しているときに押されてもショートしないように330kΩの抵抗を挟んでいます。(1kΩでは起動しませんでした。330Ωであれば,電流は10mA(3.3 / 330 = 0.01)に抑えられるので,簡単に壊れたりはしないと思います。) 
 白のタクトスイッチがリブート・シャットダウンボタンです。プルアップし,Pythonで立ち下がりを検出するようにします。LEDが3回点滅するまで押し続けることでリブート,5回点滅するまで押し続けることでシャットダウンするようにPythonのコードを書きます。また,LEDは0.5秒ごとに点滅するようにします。
 ケースに組み込んだときなどに,外付けのボタンで操作できるように,タクトスイッチと平行にXHコネクタを付けておくことにします。

☆材料
 ラズパイZero用ユニバーサル基板 秋月 P-14031100円 
 ラズパイ用スタッキングコネクタ 秋月 C-10702150円 
 タクトスイッチ×2(赤・白) 秋月 P-03648 20円 
 XHコネクタ(2ピン)×2 秋月 C-1224720円 
 超高輝度5mmオレンジ色LED 秋月 I-06404 (10個)200円 
 カーボン抵抗 1/4W 0Ω 秋月 R-25000 (100本)100円 
 カーボン抵抗 1/4W 330Ω 秋月 R-25331 (100本)100円 
 カーボン抵抗 1/4W 1kΩ 秋月 R-25102 (100本)100円 
 カーボン抵抗 1/4W 10kΩ 秋月 R-25103 (100本)100円 
 合計  890円 

☆配線図 (抵抗のカラーコードの間違いを修正しました。R01.08.26)


☆できあがり


☆テストコード
 ①まずは,LEDチカチカ
  LEDを10回,0.5秒間隔で点けたり消したりする。
import RPi.GPIO as GPIO
import time

LED_PIN = 12

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT)

for i in range(10):
    GPIO.output(LED_PIN, GPIO.HIGH)
    time.sleep(0.5)
    GPIO.output(LED_PIN, GPIO.LOW)
    time.sleep(0.5)
GPIO.cleanup()
 ②タクトスイッチの押下検出
  タクトスイッチを押すまで待ち,スイッチを押したら,"Button Pushed.”のメッセージを表示する。
import RPi.GPIO as GPIO
import time

BUTTON_PIN = 26

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON_PIN, GPIO.IN)
GPIO.wait_for_edge(BUTTON_PIN, GPIO.FALLING)
print("Button Pushed.")
GPIO.cleanup()

☆リブート&シャットダウン
 ①タクトスイッチの押下でリブートまたはシャットダウンするコード
 ・wait_for_edge()を使うと,killしても待ち続け,すぐに終了しない。(killしたあと,タクトスイッチを押した時点で終了する。)そのため,add_event_detect()を使ってコールバック関数を呼ぶようにし,ループで待機する。
 ・コールバック関数で長い時間処理をすると,チャタリングが起きるのか,2回連続でコールバック関数が呼ばれることがあった。そのため,コールバック関数ではフラグをセットしてすぐに終了し,待機ループでフラグの変化を検知して必要な処理を行うようにした。
import RPi.GPIO as GPIO
import time
import sys
import os
import signal

LED_PIN = 12
BUTTON_PIN = 26
button_flag = False

def main():
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LED_PIN, GPIO.OUT)
    GPIO.setup(BUTTON_PIN, GPIO.IN)
    GPIO.output(LED_PIN, GPIO.LOW)
    # 起動したサインとしてLEDを3回点滅させる
    blink_led(30.1)
    # 入力待ちのサインとしてLEDを点灯させる
    GPIO.output(LED_PIN, GPIO.HIGH)
    GPIO.add_event_detect(BUTTON_PIN, GPIO.FALLING, callback=button_pushed, bouncetime=300) 
    # killされた場合も終了処理(finally節)を行う
    signal.signal(signal.SIGTERM, sigterm_handler)
    # wait_for_edgeではkillしてもイベントが発生するまでフリーズするので、
    # ループで待機するようにする
    global button_flag
    try:
        while True:
            time.sleep(0.1)
            if button_flag:
                choose_action()
                button_flag = False
    except KeyboardInterrupt:
        pass
    finally:
        # 終了のサインとしてLEDを消灯させる
        GPIO.output(LED_PIN, GPIO.LOW)
        GPIO.remove_event_detect(BUTTON_PIN)
        GPIO.cleanup()   

# LEDを指定の回数、指定の間隔で点滅させる
def blink_led(times, wait):
    for i in range(times):
        GPIO.output(LED_PIN, GPIO.HIGH)
        time.sleep(wait)
        GPIO.output(LED_PIN, GPIO.LOW)
        time.sleep(wait)

# コールバック関数で時間のかかる処理をすると2回実行されることがあるので、
# グローバル変数のフラグの変更のみ行う。
def button_pushed(gpio_pin):
    global button_flag
    button_flag = True

# トグルボタンを押した長さでreboot、shutdownの処理を選ぶ
def choose_action():
    GPIO.output(LED_PIN, GPIO.LOW)
    time.sleep(0.2)
    count = 0
    while True:
        blink_led(10.5)
        count += 1
        if GPIO.input(BUTTON_PIN) == GPIO.HIGH:
            # 3回点滅した場合は再起動
            if count == 3:
                blink_led(30.1)
                os.system("sudo reboot")
                sys.exit()
            # 5回点滅した場合はシャットダウン
            if count == 5:
                blink_led(50.1)
                os.system("sudo shutdown -h now")
                sys.exit()
            # その他の場合は5回すばやくLEDを点滅させる
            blink_led(50.1)
            GPIO.output(LED_PIN, GPIO.HIGH)
            break

# killされた場合も終了処理(finally節)を行う
def sigterm_handler(signal_number, stack_frame):
    sys.exit()

if __name__ == "__main__":
    main()
 ②サービスとして自動起動するために,下のUnit定義ファイルをファイル名「reboot_shutdown.service」で保存する。
[Unit]
Description=for Reboot and Shutdown board

[Service]
ExecStart=/usr/bin/python3 /home/pi/reboot_shutdown.py

[Install]
WantedBy = multi-user.target
 ③「/etc/systemd/system/」にUnit定義ファイルをコピーする。
$ sudo mv reboot_shutdown.service /etc/systemd/system/
 ④サービスとして起動
  LEDが3回点滅したあと,点灯して待機することを確認。
$ sudo systemctl start reboot_shutdown
 ⑤サービス再起動
  LEDが一度消灯し,その後再起動することを確認。
$ sudo systemctl restart reboot_shutdown
 ⑥サービス終了
  LEDが消灯し,終了することを確認。
$ sudo systemctl stop reboot_shutdown
  ⑦サービス自動起動を有効にする
$ sudo systemctl enable reboot_shutdown
Created symlink /etc/systemd/system/multi-user.target.wants/reboot_shutdown.service ? /etc/systemd/system/reboot_shutdown.service.
 ⑧再起動
  再起動後,LEDが点灯し,自動起動したことを確認。
$ sudo reboot

17:00
2019/08/08

スイッチでH・Lを入力する

| by:YAS
 Raspberry Piは,GPIOの各信号線を入力に設定した場合,チップ内部でプルアップ・プルダウンを設定できる。
 しかし,ソフトウェアでプルアップ・プルダウンの設定を誤ったり,入力・出力の設定を誤ったりすることもあるだろう。
 そこで,チップの外部でプルアップを行った場合の電流の流れを確認した。
 ネットの情報によると,チップ内部のプルアップ・プルダウンの抵抗値は,50kΩ程度らしいので,外部のプルアップを10kΩで行えば,チップ内部のプルアップ・プルダウンを上書きできる。また出力設定になったときに備えて1kΩで保護するようにした。(下の⑧の状態は,1kΩの保護の抵抗がなれけばショートになる。)

【 ①チップ内部プルアップ, スイッチOFFの場合】
→入力はHになる。
【②チップ内部プルアップ, スイッチONの場合】
→入力はLになる。
 【③ チップ内部プルダウン, スイッチOFFの場合】
→入力はHになる。
 【 ④チップ内部プルアップ, スイッチONの場合】
→入力はLになる。
【⑤(間違えて) 出力L, スイッチOFFの場合】
→ショートにはならない。
 
 【⑥(間違えて) 出力L, スイッチONの場合】
→ショートにはならない。
 
 【⑦(間違えて) 出力H, スイッチOFFの場合】
→ショートにはならない。
 
 【⑧(間違えて) 出力L, スイッチOFFの場合】
→ショートにはならない。
 



17:00
12