LCDディスプレイへの各種情報の表示

かなり前にも書きましたが、I2C接続の2行表示のディスプレイに、手持ちのセンサーなどから取得した情報を表示してみます

表示する情報は以下の内容

  • 現在の日付、時間
  • CPU使用率と温度、メモリの使用量と全量
  • ホスト名
  • IPアドレスと、それを保持するインターフェース名
  • 温度と大気圧
  • 温度と湿度


使用した機器です(本体、Raspberry Pi は除く)

 

LCDディスプレイ

いろいろと似たようなものがありますが、手持ちのものと同じものです

 

温度・湿度センサー

手持ちのものと同じものが見つかりませんでしたが、I2C接続できる「SHT3X」という型番で探しました。たぶん、大丈夫

 

温度・大気圧センサー

上の温度センサーの値と、この温度センサーの値が、ちょっと異なるんですよね。1℃くらい。

 

ソースコード

情報を1秒ごとに表示します

以前の内容から、少し変えています

 

from time import sleep
import smbus

class i2c:
    ''' i2cクラス '''
    # i2c オブジェクト
    i2c = None
    # i2c アドレス
    ADR = None
    
    def __init__(self, adr, bus = 1) -> None:
        ''' コンストラクタ '''
        self.ADR = adr
        self.i2c = smbus.SMBus(bus)
    
    def readS8(self, register):
        ''' 符号あり8ビット読込 '''
        result = self.i2c.read_byte_data(self.ADR, register)
        if result > 127:
            # 符号算出
            result -= 256
        return result

    def readS16(self, register):
        ''' 符号あり16ビット読込(アドレス値は指定8ビット と 指定8ビット + 1) '''
        hi = self.readS8(register)
        lo = self.readU8(register+1)
        return (hi << 8) + lo

    def readU8(self, register):
        ''' 符号なし8ビット読込 '''
        result = self.i2c.read_byte_data(self.ADR, register)
        return result

    def readU16(self, register):
        ''' 符号なし16ビット読込(アドレス値は指定8ビット と 指定8ビット + 1) '''
        hi = self.readU8(register)
        lo = self.readU8(register+1)
        return (hi << 8) + lo

    def readBlockData(self, register, blocks):
        ''' 指定ブロック読込 '''
        block = self.i2c.read_i2c_block_data(self.ADR, register, blocks)
        return block
    
    def write8(self, register, value):
        ''' 8ビット書込 '''
        self.i2c.write_byte_data(self.ADR, register, value)
    
    def writeByte(self, value):
        ''' 8ビット書込 '''
        self.i2c.write_byte(self.ADR, value)
    
class SHT3X:
    ''' 温度・湿度センサー '''
    SHT3X_FREQ_MSB           = 0x23
    SHT3X_FREQ_LSB           = 0x34
    SHT3X_READ_MSB           = 0xe0
    SHT3X_READ_LSB           = 0x00
    SHT3X_READ_ADR           = 0x00
    I2C_ADR                  = 0x44
    i2c                      = None   
    
    def __init__(self) -> None:
        ''' コンストラクタ '''
        # i2cオブジェクト
        self.i2c = i2c(self.I2C_ADR)
    
    def calcTemperatureHumidity(self):
        ''' 温度と湿度を算出 '''
        # 測定頻度の設定
        self.i2c.write8(self.SHT3X_FREQ_MSB, self.SHT3X_FREQ_LSB)
        sleep(0.5)
        # 測定値の読出し
        self.i2c.write8(self.SHT3X_READ_MSB, self.SHT3X_READ_LSB)
        sleep(0.5)

        # 6バイト取得
        d = self.i2c.readBlockData(self.SHT3X_READ_ADR, 6)

        # 0~1バイトが温度
        # 測定データ(生データ)を取得
        t = ((d[0]) << 8) | (d[1])
        # 換算する
        t = -45 + (175 * t / 65535)

        # 3~4バイトが湿度
        # 測定データ(生データ)を取得
        h = ((d[3]) << 8) | (d[4])
        # 換算する
        h = 100 * h / 65535

        # 結果を返却
        return t, h

class BMP085:
    ''' 温度・大気圧センサー '''
    BMP085_CAL_AC1           = 0xAA
    BMP085_CAL_AC2           = 0xAC
    BMP085_CAL_AC3           = 0xAE
    BMP085_CAL_AC4           = 0xB0
    BMP085_CAL_AC5           = 0xB2
    BMP085_CAL_AC6           = 0xB4
    BMP085_CAL_B1            = 0xB6
    BMP085_CAL_B2            = 0xB8
    BMP085_CAL_MB            = 0xBA
    BMP085_CAL_MC            = 0xBC
    BMP085_CAL_MD            = 0xBE
    BMP085_CONTROL           = 0xF4
    BMP085_TEMPDATA          = 0xF6
    BMP085_PRESSUREDATA      = 0xF6
    BMP085_READTEMPCMD       = 0x2E
    BMP085_READPRESSURECMD   = 0x34 
    MODE                     = 3
    WAIT_TIME                = [0.005, 0.014, 0.008, 0.026]
    I2C_ADR                  = 0x77
    i2c                      = None   
    
    def __init__(self) -> None:
        ''' コンストラクタ '''
        # i2cオブジェクト
        self.i2c = i2c(self.I2C_ADR)
    def calibration(self):
        ''' キャリブレーション '''
        # キャリブレーションデータ読込
        self.cal_AC1 = self.i2c.readS16(self.BMP085_CAL_AC1)
        self.cal_AC2 = self.i2c.readS16(self.BMP085_CAL_AC2)
        self.cal_AC3 = self.i2c.readS16(self.BMP085_CAL_AC3)
        self.cal_AC4 = self.i2c.readU16(self.BMP085_CAL_AC4)
        self.cal_AC5 = self.i2c.readU16(self.BMP085_CAL_AC5)
        self.cal_AC6 = self.i2c.readU16(self.BMP085_CAL_AC6)
        self.cal_B1 =  self.i2c.readS16(self.BMP085_CAL_B1)
        self.cal_B2 =  self.i2c.readS16(self.BMP085_CAL_B2)
        self.cal_MB =  self.i2c.readS16(self.BMP085_CAL_MB)
        self.cal_MC =  self.i2c.readS16(self.BMP085_CAL_MC)
        self.cal_MD =  self.i2c.readS16(self.BMP085_CAL_MD)

    def readUncpmpensatedTemp(self):
        ''' センサーから温度を取得 '''
        self.i2c.write8(self.BMP085_CONTROL, self.BMP085_READTEMPCMD)
        sleep(0.026)
        raw = self.i2c.readU16(self.BMP085_TEMPDATA)
        return raw

    def readUncpmpensatedPressure(self):
        ''' センサーから気圧を取得 '''
        self.i2c.write8(self.BMP085_CONTROL, self.BMP085_READPRESSURECMD + (self.MODE << 6))
        sleep(self.WAIT_TIME[self.MODE])
        msb = self.i2c.readU8(self.BMP085_PRESSUREDATA)
        lsb = self.i2c.readU8(self.BMP085_PRESSUREDATA+1)
        xlsb = self.i2c.readU8(self.BMP085_PRESSUREDATA+2)
        raw = ((msb << 16) + (lsb << 8) + xlsb) >> (8 - self.MODE)
        return raw
    
    def calcTemperaturePressure(self):
        ''' 温度と気圧を算出 '''
        UT = self.readUncpmpensatedTemp()
        UP = self.readUncpmpensatedPressure()

        X1 = ((UT - self.cal_AC6) * self.cal_AC5) / 32768
        X2 = (self.cal_MC * 2048) / (X1 + self.cal_MD)
        B5 = X1 + X2
        B6 = B5 - 4000
        X1 = (self.cal_B2 * (B6 * B6) / 4096) / 2048
        X2 = (self.cal_AC2 * B6) / 2048
        X3 = X1 + X2

        # 温度を算出しておく
        temp = (B5 + 8) / 16 / 10

        B3 = (((self.cal_AC1 * 4 + X3) * ( 2 ** self.MODE)) + 2) / 4
        X1 = (self.cal_AC3 * B6) / 8192
        X2 = (self.cal_B1 * ((B6 * B6) / 4096)) / 65536
        X3 = ((X1 + X2) + 2) / 4
        B4 = (self.cal_AC4 * (X3 + 32768)) / 32768
        B7 = (UP - B3) * (50000 >> self.MODE)

        if (B7 < 0x80000000):
            press = int((B7 * 2) / B4)
        else:
            press = int((B7 / B4) * 2)
       
        X1 = (press / 256 ) * (press / 256)
        X1 = (X1 * 3038) / 65536
        X2 = (-7357 * press)  / 65536

        press = press + ((X1 + X2 + 3791) / 16)

        return temp, press

class LCD1602:
    ''' LCD '''
    LCD_LINES       = [0x80, 0xC0]
    # LCDの1行目のアドレス
    LCD_LINE_1      = 0x80
    # LCDの2行目のアドレス
    LCD_LINE_2      = 0xC0
    # 1行あたりの文字数
    LCD_WIDTH       = 16
    # バックライトのON/OFF(ON:0X08, OFF:0X00を指定)
    LCD_BACKLIGHT   = 0X08
    LCD_CHR         = 1
    LCD_CMD         = 0
    ENABLE          = 0b00000100
    E_PULSE         = 0.0005
    E_DELAY         = 0.0005
    I2C_ADR         = 0x27
    i2c             = None
    
    def __init__(self) -> None:
        ''' コンストラクタ '''
        # i2cオブジェクト
        self.i2c = i2c(self.I2C_ADR)
    
    def calibration(self):
        ''' キャリブレーション '''
        self.writeByte(0x33, self.LCD_CMD)
        self.writeByte(0x32, self.LCD_CMD)
        self.writeByte(0x06, self.LCD_CMD)
        self.writeByte(0x0C, self.LCD_CMD)
        self.writeByte(0x28, self.LCD_CMD)
        self.writeByte(0x01, self.LCD_CMD)
        sleep(self.E_DELAY)

    def writeByte(self, bits, mode):
        ''' LCDへの書き込み '''
        bits_high = mode | (bits & 0xF0) | self.LCD_BACKLIGHT
        bits_low =  mode | ((bits << 4) & 0xF0) |  self.LCD_BACKLIGHT
        self.i2c.writeByte(bits_high)
        self.writeByteSub(bits_high)
        self.i2c.writeByte(bits_low)
        self.writeByteSub(bits_low)

    def writeByteSub(self, bits):
        ''' LCDへの書き込み(サブ) '''
        sleep(self.E_DELAY)
        self.i2c.writeByte(bits | self.ENABLE)
        sleep(self.E_DELAY)
        self.i2c.writeByte(bits & ~self.ENABLE)
        sleep(self.E_DELAY)
    
    def dispString(self, message: str, dispLine):
        ''' LCDへの文字の表示(半角文字列のみ) '''
        # 表示対象行の初期化
        self.writeByte(self.LCD_LINES[dispLine], self.LCD_CMD)
        # 文字列を1行あたりの文字数に調整
        message = message.ljust(self.LCD_WIDTH)
        # 表示処理
        for dispIndex in range(self.LCD_WIDTH):
            self.writeByte(ord(message[dispIndex]), self.LCD_CHR)

import socket
import psutil
import datetime

class RaspiInfo:
    ''' RaspberryPi情報 '''
    @staticmethod
    def getMemoryInfo():
        ''' メモリ情報(使用量[MB]、総量[MB])の取得 '''
        mem = psutil.virtual_memory()
        mem_used = round(mem.used / 1024 / 1024)
        mem_total = round(mem.total / 1024 / 1024)
        return mem_used, mem_total
    
    @staticmethod
    def getCpuTemp():
        ''' CPU温度の取得 '''
        f = open("/sys/class/thermal/thermal_zone0/temp","r")
        cpu_temp = 0
        for t in f:
            cpu_temp = t[:2]
            # 小数点以下2桁まで表示する場合
            # cpu_temp = t[:2]+"."+t[2:3]
        f.close()
        return cpu_temp

    @staticmethod
    def getCpupercent():
        ''' CPU使用率の取得 '''
        cpu_percent = psutil.cpu_percent(interval=0)
        return round(cpu_percent)
    
    @staticmethod
    def getHostName():
        ''' ホスト名の取得 '''
        host_name = socket.gethostname()
        return host_name

    @staticmethod
    def getIP():
        ''' IPアドレスとインターフェース名を取得 '''
        for interface, snics in psutil.net_if_addrs().items():
            for snic in snics:
                if snic.address != '127.0.0.1' and snic.family == socket.AF_INET:
                    return snic.address, interface
        return "none", "none"

# メイン処理

# LCDの初期化
lcd = LCD1602()
lcd.calibration()

# インスタンス生成
bmp = BMP085()
sht3x = SHT3X()

# 初期化
bmp.calibration()

while True:
    # 現在日時
    now = datetime.datetime.now()
    lcd.dispString(f'date: {now.strftime("%Y/%m/%d")}', 0)
    lcd.dispString(f'time: {now.strftime("%H:%M:%S")}', 1)
    sleep(1)

    # 「CPU温度」「CPU使用率」と「メモリ情報」
    mem_info = RaspiInfo.getMemoryInfo()
    lcd.dispString(f'cpu : {RaspiInfo.getCpuTemp()}\337C {RaspiInfo.getCpupercent()}%', 0)    
    lcd.dispString(f'mem : {mem_info[0]}/{mem_info[1]}MB', 1)
    sleep(1)

    # ホスト名
    hostName = RaspiInfo.getHostName()
    lcd.dispString(f'host:', 0)
    lcd.dispString(f' {hostName}', 1)
    sleep(1)

    # IPアドレス
    ip = RaspiInfo.getIP()
    lcd.dispString(f'ip  :', 0)
    lcd.dispString(f'{str(ip[1])}:', 0)
    lcd.dispString(f' {ip[0]}', 1)
    sleep(1)

    # 温度と大気圧(次の取得処理中でスリープしているので、ここでの表示後のスリープはしない)
    tempPress = bmp.calcTemperaturePressure()
    lcd.dispString(f'temp: {str(tempPress[0])[:5]}\337C', 0)
    lcd.dispString(f'prss: {str(tempPress[1] / 100.0 )[:6]} hPa', 1)

    # 温度と湿度
    ht = sht3x.calcTemperatureHumidity()
    lcd.dispString(f'temp: {str(ht[0])[:5]}\337C', 0)
    lcd.dispString(f'humi: {str(ht[1])[:5]}%', 1)
    sleep(1)