キーボード一体型Raspberry Pi 400レビューはこちら

プログラミング初心者がPythonを学ぶのにRaspberry Piはちょうどいい

enviro-sqlite-title 特集記事
特集記事

プログラム言語のPythonを学ぶのに、Raspberry Piはお手軽です。必要なライブラリも最初から入っているからです。

文系のおっさんでもPythonは比較的に分かりやすい言語だと感じます。過去にN88-BASICやVisualBasic、Excelで扱うVBAなどを触ったことがある人なら、他の言語よりも理解し易いハズです。

逆に何もプログラム言語を知らないなら、何から始めても構いませんが、環境を整えるのにRaspberry Pi を1つ用意するだけで事足りる点で優れています。

Raspberry Piでは何かとお世話になる機会が多いPythonを、少しでも扱えると楽しみが広がります。

プログラミング初心者である私が、具体例に沿って学んでいる様子をご紹介します。

初心者ながら解説チックになっている点はご容赦ください。キチンとした書籍や学校などで学ぶことが良いのは云うまでもありません。参考程度にお付き合いください。

スポンサーリンク

扱うデータ

今回、環境センサーから取得したデータを格納する手順を試みました。そしてそれをデータベースに格納して取り出して使おうというのが目的です。

利用したのはEnviro pHATというRaspberry Pi専用に販売されていた古いpHATです。初期のEnviroです。在庫があれば2,000円程度で購入できるくらい古いです。

2018年頃に英Pimoroniから購入しました。3,000円程度の簡易的な環境センサー基板です。拡張基板にヘッダー端子が半田付けされていない頃の物で、自分で半田付けしないとなりません。

LEDは色を読み取る対象物に光を当てるため

現在は、バージョンアップして小さな液晶画面も付いたモデルのEnviroが売っています。更に「Enviro for Raspberry Pi – Enviro + Air Quality」という上位版もあります。センサーは増えていますが、実はコードは基本同じです。

現在販売されているEnviro+

このEnviroというのは、Environmentの略で付けられた商品名です。環境という意味ですね。いくつかセンサーが付いています。

  • 方位、動き、方位を検出するための LSM303D 加速度計/磁力計
  • 温度/圧力センサー(BMP280)
  • 光の量と色を検出するカラー センサー(TCS3472)
  • 外部センサー用のアナログ センサー(ADS1015)
  • カラー センサー上のオブジェクトを照らすためにGPIO4番に接続された2つのLED

大きく4つのセンサーです。とても簡易的な物で、温度などは取り付けたRaspberry Pi 自身から発する熱にも影響します。

誤差はあれど、それらしいデータは取得できます。このデータ結果を何かに渡す使い方を学ぶために利用しました。

リアルタイムに取得できる物が面白いと思いますので、電子工作キットの1つをブレッドボード経由でRaspberry PiのGPIOに繋げても良いでしょう。

dht11-temp6
温度センサーのDHT11とラズパイを繋ぐ
スポンサーリンク

公式のサンプルプログラム

Pimoroniにはチュートリアルがあります。最初はそれに習って学習を進めました。

参考Getting started with Enviro pHAT

curl https://get.pimoroni.com/envirophat | bash

Pimoroniの製品は1行でセットアップができるので助かります。

今回はpHATの関係でRaspberry Pi Zero Wで行いました。OSはRaspberry Pi OS Liteです。Zero系はデスクトップ環境が無いだけでなく、ライブラリもあまり収録されていません。データを取得してデータベースへ格納する分にはそれほど懸念はありませんでした。

このHATでは、ライト、モーション、天気、アナログ、LED用の個別のモジュールに分割されています。(light、motion、weather、analog、leds)

全部割り当てるならこう書きます。

import time
from envirophat import light, motion, weather, leds

import timeは連続読み込みを遅延させるために必要で一緒に読み込みます。

lightの中にカラーセンサーがあり、wetherの中に気温と気圧があり、それぞれ取得できます。(高度は大気圧から概算)

サンプルコードから学ぶ

先ずはチュートリアル通りのPythonコードを書いて実行させます。
実際に実行させてみて、エラーが出ることで学べます。コピペでも良いのですけど、実際にキーボードから入力した方が覚えているものです。

ちょっとだけ解説チックで失礼します。

ちなみに、初心者の味方であるエディタはnanoで新規にファイルを作って記述していきます。拡張子は.pyです。実行はpython3と実行するファイルを指定します。

sudo nano sample.py

pyton3 sample.py
サンプルコード(Pimoroni)
import time
from envirophat import light, motion, weather, leds

out = open('enviro.log', 'w')
out.write('light\trgb\tmotion\theading\ttemp\tpress\n')

try:
    while True:
        lux = light.light()
        leds.on()
        rgb = str(light.rgb())[1:-1].replace(' ', '')
        leds.off()
        acc = str(motion.accelerometer())[1:-1].replace(' ', '')
        heading = motion.heading()
        temp = weather.temperature()
        press = weather.pressure()
        out.write('%f\t%s\t%s\t%f\t%f\t%f\n' % (lux, rgb, acc, heading, temp, press))
        time.sleep(1)

except KeyboardInterrupt:
    leds.off()
    out.close()

最初の2行が先程のモジュールの読み込みです。使うモジュールを読み込んでデータを取得するために定義しています。

次の2行は取得したデータをログファイルとして保存させるために項目名を保存していますね。これ、他にも応用できそうです。

out = open('enviro.log', 'w')
out.write('light\trgb\tmotion\theading\ttemp\tpress\n')
公式ページ

Pythonの構文チェックは、公式の文書ページがとても詳しく載っていますので、ブックマークすることをオススメしています。
参考Python公式文書 -組み込み関数 open

ログファイル名をwモードで開くとなっています。wの他にaなんかも覚えましょう。Writeのw、aはAppend(追記)です。

このファイル名とモードをoutという変数に代入しています。そして、次の行で実際に書き込むデータを指定していますね。

out.write('light\trgb\tmotion\theading\ttemp\tpress\n')

カッコの中をシングルクォーテーション(')で括っています。これも重要でして、統一しておきましょう。

私のようなプログラミング初心者としては、空白がある場合はダブル(")、そうでない時はシングル(')なんて感じで覚えています。トリプル(''')もありますから、初心者は悩みますね。データの出し入れ指定に同じクォーテーションでなければならない(らしい)

いずれ書籍などで体系的に学ぶことはオススメします。

カッコの中身先頭のliteに\tが付いているのが分かるでしょうか。

light\t

これはタブの意味です。データを格納する際にタブで区切って入れています。
最後の\nは改行の意味です。

条件式

私は非エンジニアなので、誤解されるとは思いますけど、プログラミングは条件式の集合体と感じています。それに加え、変数を定義する記述、エラーの処理と例外処理があれば立派なプログラムだと考えています。

このサンプルでも条件式が使われています。無限ループです。

try:
    while True:

このままだと無限なので、except KeyboardInterrupt: も書いて途中で終了できるようになっています。このKeyboardInterruptは ctrl + c を入力すると作用します。

while文は条件式が真の場合だけ実行します。条件式に変数があって、その範囲内だけ実行させるという意味です。

while 条件式:
    処理を書く

ここではその前にtry文が使われています。これは先程の例外処理であるexceptとセットで使いたいからですね。try: 〜 except ~:

私が個人的に気に入っているwhile文は、ループの中にif文があって、breakを使い処理を抜ける方法です。なんか、分かりやすいんですよ。

while True:
    if 条件式:
        break
    次の処理

代入

while文の中にある大多数は、取得したデータを変数に格納しています。

        lux = light.light()
        leds.on()
        rgb = str(light.rgb())[1:-1].replace(' ', '')
        leds.off()
        acc = str(motion.accelerometer())[1:-1].replace(' ', '')
        heading = motion.heading()
        temp = weather.temperature()
        press = weather.pressure()

すべてこのEnviro pHATの物です。最初にモジュールを読み込んでいるので機能するというわけです。

これはそういう物なので、代入する変数名を自分で考えられるくらいです。他にオプションとして指定できる物があれば一緒に指定します。これはメーカーのページで確認するしかありません。

取得データ書き込み

変数に代入した取得された数値などを、先程のlogファイルに書き込んでいる処理です。

        out.write('%f\t%s\t%s\t%f\t%f\t%f\n' % (lux, rgb, acc, heading, temp, press))
        time.sleep(1)

ここ、少し難しい。

データのフォーマット形式を指定しています。最初の%fは浮動小数点数(Float)で、%sは文字列(String)です。間の\tは先程もあったタブ、\nは改行です。

他にも整数は%dなんかもあります。

真ん中の%は文字配置に使うやり方で、本来は余りを意味しています。

Excelのcountifのテクニックにある * なんかと同じような使い方ですかね。
=countif(($A$1:$B$10=F4) * ($C$1:$C$10=G4))

最初のフォーマットの順番と、取得したデータが格納されちえる変数がセットになっているのが分かるでしょう。型を指定して格納すると理解しました。

同じwriteでも、先程はヘッダ名として書き込んでいただけです。

logの中身はこうなります。

light rgb motion heading temp press
390.000000 42,47,64 -0.00115966796875,-0.00531005859375,1.0928955078125 280.770000 32.436408 1004.576839

表にするとこんなです。

lightrgbmotionheadingtemppress
390.00000042,47,64-0.00115966796875,-0.00531005859375,1.0928955078125280.77000032.4364081004.576839

データフォーマットが合っていないとエラーが出ます。

次のtime.sleep(1)は1秒待ってからループさせるためのものです。

取得したデータをデータベース(SQLite)に記録

テキストのログファイルに保存されても良いのですけど、データベースに登録できたら、後から取り出すのが簡単になります。

SQLite3は簡単なデータベースの仕組みです。1つのファイルに格納するため初心者には理解し易いと思います。

sqlite3をインストールして直ぐに使えます。

sudo apt update
sudo apt full-upgrade

sudo apt install sqlite3

プログラム

今回は2つのファイルに分けました。これは参考にしたサイト(ゆきの野望)がとても分かりやすく、そのコードをEnviro pHATの場合に置き換えて修正したからです。
データ取得と格納、読み取りが分けられていたためです。

# -*- coding: utf-8 -*-

import sqlite3
import time
import datetime
from envirophat import light, motion, weather, leds

timestamp = datetime.datetime.now().isoformat()

out = open('enviro.log', 'w')
out.write('timestamp\tlight\trgb\tmotion\theading\ttemp\tpress\n')

lux = light.light()
leds.on()
rgb = str(light.rgb())[1:-1].replace(' ', '')
leds.off()
acc = str(motion.accelerometer())[1:-1].replace(' ', '')
heading = motion.heading()
temp = weather.temperature()
press = weather.pressure(unit='hPa')
out.write('%s\t%f\t%s\t%s\t%f\t%f\t%f\n' % (timestamp, lux, rgb, acc, heading, temp, press))
leds.off()
out.close()

# データベースファイルのパス
# 慣例的に拡張子に、”.sqlite”をつけるらしい。
dbname = '/home/pi/enviro/enviro_db.sqlite'
dbtable = 'sensorhat'
# データベース接続とカーソル生成
# dppathと同名のファイルがなければ、DBファイルが作成されます。
conn = sqlite3.connect(dbname)
# 自動コミットにする場合は下記を指定(コメントアウトを解除のこと)
# connection.isolation_level = None
c = conn.cursor()

# SQLiteにテーブルがあるかどうか確認するクエリ
checkdb = c.execute("SELECT * FROM sqlite_master WHERE type='table' and name='%s'" % dbtable)
# もしテーブルがなかったら新規でテーブルを作成する
if checkdb.fetchone() == None:
    # ID、タイムスタンプ、温度、湿度など列のテーブルを作成するクエリを作成。IDは自動附番。
    create_table = '''create table ''' + dbtable + '''(id integer primary key autoincrement, timestamp datetime,
                 lux real, rgb real, acc real, heading real, temp real, press real)'''
    # クエリを実行
    c.execute(create_table)
    # 変更を保存する
    conn.commit()

sql = 'insert into sensorhat (timestamp, lux, rgb, acc, heading, temp, press) values (?,?,?,?,?,?,?)'
data = (timestamp, lux, rgb, acc, heading, temp, press)
c.execute(sql, data)
conn.commit()
conn.close()

while文を辞めて、単純に1回だけデータを取得してデータベースに格納しています。主に変更したのは、dbname、dbtableと、タイムスタンプを追加しています。

timestampは定義した後、CREATE TABLEで使ったデータ型はdatetimeでOKです。なお、データ型のrealはfloatみたいなもので、お手本もすべての数字をreal型にしていました。

dbnameは同じようにフルパスで記述しました。

冒頭のデータ取得した変数値などは、Enviro pHATの取得に変更しています。(オリジナルはDHT11のデータ)

本来は何度か取得した方が正確(?)な数字になるでしょう。これでもPythonの勉強としては充分でした。

ログに書き込むところから追記してデータベースに格納しています。

# -*- coding: utf-8 -*-
import sqlite3

#SQliteのテーブルの中身を確認する
def read_table(dbname,dbtable):
    conn = sqlite3.connect(dbname)
    c = conn.cursor()

    select_sql = 'select * from sensorhat'
    for row in c.execute(select_sql):
        print(row)

    conn.close()

#SQliteのテーブルを削除する
def del_table(dbname,dbtable):
    conn = sqlite3.connect(dbname)
    c = conn.cursor()
    sql = "drop table "+dbtable
    c.execute(sql,)
    conn.commit()

#メイン関数
if __name__ =='__main__':
    #データベース名
    dbname = '/home/pi/enviro/enviro_db.sqlite'
    #テーブル名
    dbtable = 'sensorhat'

    #使いたくないほうをコメントアウトして実行
    #del_table(dbname,dbtable)
    read_table(dbname,dbtable)

こちらはほとんど変更していません。dbnameとdbtableを変更したくらいです。

実際に実行させて、エラーが無くデータベースに格納されていることを確認しました。

プログラミング初心者は(私も!)データベースの扱いがとても難しいように感じます。使いこなせると何かと重宝すると思います。

エラーが出る人のためにヒントを。

ヒント

Pythonでは処理をインデント(字下げ)を揃えないとなりません。半角スペース4つずつ。

カンマとドットを間違えたり、スペース入れちゃったり、スペルミスが多発するので何度も見直すことをオススメします。

書籍はあった方がいい

今回のEnviro pHATは、現在同じ物を購入できる人も少ないため、記事内容と同じにならず心苦しく思います。

Pythonの部分だけは応用が利きますので、自分の環境に置き換えてトライしてみてください。

独学でも良いと思いますので、信頼できる書籍はいくつかあった方が良いでしょう。書籍からは体系的に学ぶことができます。

Piレッド
Piレッド

いつもトライアンドエラーで学ぶから時間がかかるのが難点です。

Pythonとsqlite3のプログラミングでした。

データベースアプリケーション部門、売れ筋ナンバー1
\ 初めてのRaspberry Pi はセットで始めよう /
NewLife NewDesingストア

2台目にも最適な最小基本セット!

\ 初めてのRaspberry Pi はセットで始めよう /
NewLife NewDesingストア

2台目にも最適な最小基本セット!

スポンサーリンク
ラズパイダ

コメント ご感想をお願いします。

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