Raspberry Pi 4 + Python3入門 <STEP1> (4) 7セグメントLED 74HC595 その4 BME280をネイティブにアクセス①

 前回、BME280のライブラリを利用して、温度、湿度、気圧のデータを4桁の7セグメントLEDに表示しました。7セグメントLEDの表示部分を関数にしたのですが、長いので、プログラムが見やすくありません。
 ここでは、最初に、表示部分を別ファイルにして、本体のプログラムを見やすくします。

分離して利用する

 次のように、前回のプログラムの表示分を抜き出し、LED7seg_595.pyで保存します。数字から始まるファイル名は使えません。

import spidev

spi = spidev.SpiDev()
spi.open(0,0) # CE0
spi.max_speed_hz = 1000000 # 1MHz

# gfedcba LED segment
dot = ~0b10000000
blank = ~0b00000000
minus = ~0b01000000
data = [ 0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110,
0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01100111 ]

def disp7seg(tempData):
minusFlag = 0
if '-' in str(tempData):
tempData = abs(float(tempData))
minusFlag = 1
dispTempData = list(str(tempData))[::-1] # reversed
dotFlag =0
for x in dispTempData:
if x != '.':
if dotFlag == 1:
spi.xfer([~data[int(x)] & dot])
dotFlag =0
continue
spi.xfer([~data[int(x)]])
dotFlag =0
else: # find dot
dotFlag =1
if '.' in str(tempData):
if minusFlag:
spi.xfer([minus]) #
else:
spi.xfer([blank]) #

 これを利用するには、import文で次のように記述します。

import LED7seg_595 as LED7

 温度を7セグメントLEDに表示するプログラムです。すごくすっきりと記述できました。

import spidev, smbus, time, bme280
import LED7seg_595 as LED7

bus = smbus.SMBus(1)
bme280.load_calibration_params(bus, 0x76)

s = bme280.sample(bus,0x76)
print(s)

findString = 'temp='
t = str(s).find(findString)
temp = str(s)[t+len(findString):t+len(findString)+4]
print('temperature ',temp)

LED7.disp7seg(temp)

 実行例です。

BME280をI2Cライブラリで利用する

 I2Cライブラリなのに名称はsmbusです。フィリップス(現在NXP)の2線によるプロトコル名がI2Cで、インテルによる2線によるプロトコル名がsmbusです。両者の間にはわずかな誤差程度の電圧レベルの差がありますが、ほとんど同じものです。

 smbusライブラリは、当初リピーテッド・スタート・コンディションに対応していなかったため、独自のI2Cライブラリが作られたりしていました、数年前から対応がなされたようです。

 I2CライブラリでBME280の温度を取得する手順は、次の記事で扱っています。

  「SpresenseでLチカから始める (24) Wireライブラリ 温度気圧BMP280

 BMP280は今利用しているBME280の機能から湿度の測定機能を抜いたデバイスで、温度と気圧が測れます。また、Arduino IDEのWireライブラリを利用しています。この言語はC言語なので、符号あり/なしの16ビットなどがキャストを使って型指定ができます。BME280/BMP280では、デバイス固有の補正データが10項目以上あって、それぞれ符号あり/なしデータが混在しています。温度には3項目あります。

 Pythonの数値の型はintとfloatしかなく、C言語のように細かくキャストができません。先ほどの記事の最後のスケッチの最後に、温度の補正計算と気圧の補正計算があります。どちらもマニュアルに掲載されているものですが、温度はキャストを使用する前提、気圧はそうではない計算例です。でも、最終的な値はキャストしています。

 Pythonで、16もしくは32ビットの型をどのように扱うかがわからなく、調べると、AdafruitのBME280が見つかりました。

 「Adafruit_Python_BME280/Adafruit_BME280.py ;Copyright (c) 2014 Adafruit Industries、 Author: Tony DiCola

 I2Cのアクセスはこちらのソースが関連ありそうです。8ビット/16ビット、符号あり/なしの記述例があります。

  「Adafruit_Python_GPIO/Adafruit_GPIO/I2C.py;Copyright (c) 2014 Adafruit Industries、Author: Tony DiCola

温度の表示

 BME280は変換をスタートさせないと、スリープしたままです。次の書き込みで、変換をスタートさせます。

i2c.write_i2c_block_data(BME280_address, Ctrl_register, [osrs_t | mode_normal])

 温度の補償値は、

dig_T1 = readU16(0x88)
dig_T2 = readS16(0x8A)
dig_T3 = readS16(0x8C)

の三つです。これを使って、read_temp()関数では、生データを読み出した後に補正をかけます。36行目以降がメイン処理です。

# Copyright (c) 2014 Adafruit Industries、 Author: Tony DiCola
# https://github.com/adafruit/Adafruit_Python_BME280/blob/master/Adafruit_BME280.py  , https://github.com/adafruit/Adafruit_Python_GPIO/blob/master/Adafruit_GPIO/I2C.py

import smbus, time
import LED7seg_595 as LED7

BME280_address = 0x76
ID_register = 0xd0
Ctrl_register = 0xf4
Temp_register = 0xfa
osrs_t = 0b00100000 # Temp 16bit
osrs_p = 0b00000100 # Press 16bit
mode_normal = 0b00000011

i2c = smbus.SMBus(1)

def readU16(dig_registor):
temp = i2c.read_word_data(BME280_address, dig_registor)
return temp & 0xffff

def readS16(dig_registor):
result = readU16(dig_registor)
if result > 32767:
result -= 65536
return result

def read_temp():
temp = i2c.read_i2c_block_data(BME280_address, Temp_register, 3)
temperature = float((temp[0] << 16 | temp[1] << 8 | temp[2] ) >> 4)
var1 = (temperature / 16384.0 - float(dig_T1) / 1024.0) * float(dig_T2)
var2 = ((temperature / 131072.0 - float(dig_T1) / 8192.0) * (temperature / 131072.0 - float(dig_T1) / 8192.0)) * float(dig_T3)
t_fine = int(var1 + var2)
T = (var1 + var2) / 5120.0
return T

i2c.write_i2c_block_data(BME280_address, Ctrl_register, [osrs_t | mode_normal])

dig_T1 = readU16(0x88)
dig_T2 = readS16(0x8A)
dig_T3 = readS16(0x8C)

print('temperature ',round(read_temp(),2))
LED7.disp7seg(round(read_temp(),1))

 実行例です。

(※) 本Webのプログラム中、インデントなどのスペースもしくは改行は、通常のプログラム内ではごみ文字になるので、コピペした後、エディタでスペース文字などを入れなおしてください。