Raspberry Pi 3に温湿度センサ「SHT21/SHT11」を接続

Raspberry Pi 3に温湿度センサ「SHT21」と「SHT11」を接続し、温湿度データを入力して表示します。プログラム言語はPython3でライブラリ「pigpio」を使用します。Raspberry Pi 3の設定については「pigpioによるI2CとSPIインタフェースの実装」を参考にしてください。

温湿度センサ「SHT21」の接続

温湿度センサ「SHT21」は、I2CでRaspberry Pi 3と接続します。仕様を次に示します。

  • 動作電圧:2.1V~3.6V
  • 測定範囲:-40~+125℃,0~100%RH
  • 精度:温度±0.3℃ 湿度±2%RH
  • 変換時間:温度 85ms 湿度 30ms

温湿度センサ「SHT21」は、ebayから購入しました。

温湿度センサ「SHT21」

次のebayから購入したSOT23のDIP変換基板にSHT21をはんだ付けし、ブレッドボードで使用できるようにしました(裏面のGNDの部分にはんだ付けされないように、DIP変換基板のパターンをカットしています)。

sht2101

Raspberry Pi 3と温湿度センサ「SHT21」間で、I2CのSDAとSCLのそれぞれに接続します。温湿度センサ「SHT21」のVCCにはRaspberry Piの3.3Vを入力します。接続した画像を次に示します。

Raspberry Pi 3とSHT21との接続

温湿度センサ「SHT21」のピン配置を次に示します。

温湿度センサ「SHT21」のピン配置

次のコマンドでRaspberry Pi 3との接続を確認します。

$ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
  • 温湿度センサ「SHT21」のアドレス - 0x40

温湿度センサ「SHT21」のPythonスクリプト

Python3でライブラリ「pigpio」を使用して、温湿度センサ「SHT21」のPythonスクリプト「mSHT21.py」を作成します。SHT21のデータシートを示します。

  1. 温湿度センサ「SHT21」から温度データを取得するには「Trigger T measurement(no hold master)」コマンド「0xF3」、湿度データを取得するには「Trigger RH measurement(no hold master)」コマンド「0xF5」を使用します。温湿度センサ「SHT21」のコマンド一覧を次に示します。
  2. 温湿度センサ「SHT21」のコマンド

  3. 温湿度センサ「SHT21」からのセンサーデータにより、次の計算式に従って、温度/湿度を計算します。
  4. sht2104

    SRH:センサーからの湿度データ、ST:センサーからの温度データ、2RES:2**16

  5. 温湿度センサ「SHT21」は、通信データの信頼性を確保するため8ビットのCRCが付与されています。
    SHT21のCRC-8演算多項式: x8 + x5 + x4 +1
# -*- coding: utf-8 -*-

import pigpio

import time
import devtemperature

def _calculate_checksum(data, number_of_bytes):
    """5.7 CRC Checksum using the polynomial given in the datasheet"""
    # CRC
    POLYNOMIAL = 0x131  # //P(x)=x^8+x^5+x^4+1 = 100110001
    crc = 0
    # calculates 8-Bit checksum with given polynomial
    for byteCtr in range(number_of_bytes):
        crc ^= ((data[byteCtr]))
        for bit in range(8, 0, -1):
            if crc & 0x80:
                crc = (crc << 1) ^ POLYNOMIAL
            else:
                crc = (crc << 1)
    return crc

def _get_temperature_from_buffer(data):
    """This function reads the first two bytes of data and
    returns the temperature in C by using the following function:
    T = -46.85 + (175.72 * (ST/2^16))
    where ST is the value from the sensor
    """
    unadjusted = ((data[0]) << 8) + (data[1])
    unadjusted &= Sht21._STATUS_BITS_MASK  # zero the status bits
    unadjusted *= 175.72
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 46.85
    return unadjusted
    
    
def _get_humidity_from_buffer(data):
    """This function reads the first two bytes of data and returns
    the relative humidity in percent by using the following function:
    RH = -6 + (125 * (SRH / 2 ^16))
    where SRH is the value read from the sensor
    """
    unadjusted = ((data[0]) << 8) + (data[1])
    unadjusted &= Sht21._STATUS_BITS_MASK  # zero the status bits
    unadjusted *= 125.0
    unadjusted /= 1 << 16  # divide by 2^16
    unadjusted -= 6
    return unadjusted
    

class Sht21(object):
    # control constants
    _SOFTRESET = 0xFE
    DEFAULT_ADDRESS = 0x40
    _TRIGGER_TEMPERATURE_NO_HOLD = 0xF3
    _TRIGGER_HUMIDITY_NO_HOLD = 0xF5
    _STATUS_BITS_MASK = 0xFFFC
    
    # datasheet (v4), page 9, table 7, thanks to Martin Milata
    # for suggesting the use of these better values
    # code copied from https://github.com/mmilata/growd
    _TEMPERATURE_WAIT_TIME = 0.086  # (datasheet: typ=66, max=85)
    _HUMIDITY_WAIT_TIME = 0.030     # (datasheet: typ=22, max=29)

    
    """コンストラクタ"""

    def __init__(self, pi):
        self.pi = pi
        self._device = pi.i2c_open(1, self.DEFAULT_ADDRESS)
        reg_data = [self._SOFTRESET]
        self.pi.i2c_write_device(self._device,reg_data)
        time.sleep(0.050)

        
    def read_temp(self):
        reg_data = [self._TRIGGER_TEMPERATURE_NO_HOLD]
        self.pi.i2c_write_device(self._device,reg_data)
        time.sleep(self._TEMPERATURE_WAIT_TIME)
        wc, data = self.pi.i2c_read_device(self._device,  3)
        if _calculate_checksum(data, 2) == (data[2]):
            return _get_temperature_from_buffer(data)

    def read_humidity(self):    
        """Reads the humidity from the sensor.  Not that this call blocks 
        for ~30ms to allow the sensor to return the data"""
        
        reg_data = [self._TRIGGER_HUMIDITY_NO_HOLD]
        self.pi.i2c_write_device(self._device,reg_data)
        time.sleep(self._HUMIDITY_WAIT_TIME)
        wc, data = self.pi.i2c_read_device(self._device,  3)
        if _calculate_checksum(data, 2) == (data[2]):
            return _get_humidity_from_buffer(data)

    def cancel(self):
        """
        Cancels the sensor and releases resources.
        """
        if self._device is not None:
            self.pi.i2c_close(self._device)
            self._device = None

if __name__ == "__main__":
    pi = pigpio.pi()
    if not pi.connected:
        exit(0)

    logger = 0
    temp = devtemperature.Sht21(pi)

    while True:
        temperature = temp.read_temp()
        humidity =  temp.read_humidity()
        print("temperature:{:.2f}℃ humidity:{:.2f}% ".format(temperature,humidity))
        time.sleep(2.0)

温湿度センサ「SHT21」のPythonスクリプトの実行

作成したPythonスクリプトを次のコマンドで実行します。2秒ごとに温湿度センサ「SHT21」からの温湿度データを表示します。

$ python3 mSHT21.py
temperature:20.91℃ humidity:52.24%
temperature:20.91℃ humidity:52.24%
temperature:20.91℃ humidity:52.24%
temperature:20.91℃ humidity:52.27%
temperature:20.91℃ humidity:52.24%
temperature:20.91℃ humidity:52.24%
temperature:20.92℃ humidity:52.27%
temperature:20.92℃ humidity:52.27%
temperature:20.92℃ humidity:52.27%
temperature:20.93℃ humidity:52.30%
temperature:20.93℃ humidity:52.30%
temperature:20.93℃ humidity:52.30%
temperature:20.93℃ humidity:52.30%
temperature:20.93℃ humidity:52.30%
temperature:20.94℃ humidity:52.33%
temperature:20.94℃ humidity:52.36%
temperature:20.95℃ humidity:52.36%

温湿度センサ「SHT11」の接続

温湿度センサ「SHT11」は、二本の信号線「DATA」「SCK」でRaspberry Pi 3と接続します。仕様を次に示します。

  • 完全1チップの温度・湿度センサー
  • A/Dコンバータ内蔵で完全デジタルで温度・湿度を出力
  • 2線式でマイコンと接続
  • 3~5V単一電源
  • 調整不要で湿度±3.5%RH,温度±0.5℃の精度
  • 分解能 湿度:0.03%RH,温度:0.01℃(

温湿度センサ「SHT11」のPythonスクリプト

Python3でライブラリ「pigpio」を使用して、温湿度センサ「SHT11」のPythonスクリプト「mSHT11.py」を作成します。SHT11のデータシートを示します。

  1. 最初にアドレス「000」を送信し、次に5ビットのコマンド「温度測定」「相対温度測定」を送信します。温湿度センサ「SHT11」のコマンド一覧を次に示します。
  2. SHT71のコマンド一覧

  3. 温度の計算は、温度の入力値(SOT)を温度値に変換するには次の式を用います(係数の値を次の表に示します)
  4. T = d1 + d2 x SOT

    温度の計算係数

  5. 相対湿度は、湿度指示値(SORH)に以下の公式と次に示される係数を適用して値を変換します。
  6. RHlinear = c1 + c2 x SORH + c3 x SORH2 (%RH)

    相対湿度の係数

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

import pigpio
import logging.config
import time

DATAPINT1 = 0x4
DATAPINTCW = 0x5

class SHT11(object):
    #   Conversion coefficients from SHT15 datasheet
    D1 = -40.0  # for 14 Bit @ 5V
    D2 = 0.01  # for 14 Bit DEGC

    C1 = -2.0468  # for 12 Bit
    C2 = 0.0367  # for 12 Bit
    C3 = -0.0000015955  # for 12 Bit
    T1 = 0.01  # for 14 Bit @ 5V
    T2 = 0.00008  # for 14 Bit @ 5V

    """コンストラクタ"""

    def __init__(self, pidata, loggerdata, datapin):
        self.pi = pidata
        self.logger = loggerdata
        self.dataPin = datapin
        self.sckPin = SCKPIN
        #        pigpio.setmode(GPIO_BOARD)
        self.__lastinvocationtime = 0

    def read_temperature_and_humidity(self):
        temperature = humidity = 0
        try:
            temperature = self.read_temperature_c()
            self.__wait()
            humidity = self._read_humidity(temperature)
        except SystemError:
            self.logger.error("温湿度取得エラー")

        finally:
            return temperature, humidity

    def __wait(self):
        last_invocation_delta = time.time() - self.__lastinvocationtime
        #        if we queried the sensor less then a second ago, wait until a second is passed
        if last_invocation_delta < 1:
            time.sleep(1 - last_invocation_delta)
        self.__lastinvocationtime = time.time()

    def read_temperature_c(self):
        temperature_command = 0b00000011

        self.__sendcommand(temperature_command)
        self.__waitforresult()
        raw_temperature = self.__getdata16bit()
        #        self.logger.debug("raw_temperature:{:x}".format(raw_temperature))

        self.__skipcrc()
        #        pigpio.cleanup()

        return raw_temperature * SHT71.D2 + SHT71.D1

    def _read_humidity(self, temperature):
        humidity_command = 0b00000101
        self.__sendcommand(humidity_command)
        self.__waitforresult()
        raw_humidity = self.__getdata16bit()
        self.__skipcrc()
        #        GPIO_BOARD.cleanup()
        #        Apply linear conversion to raw value
        linear_humidity = SHT71.C1 + SHT71.C2 * raw_humidity + SHT71.C3 * raw_humidity * raw_humidity
        #        Correct humidity value for current temperature
        return (temperature - 25.0) * (SHT71.T1 + SHT71.T2 * raw_humidity) + linear_humidity

    def __sendcommand(self, command):
        # Transmission start
        self.pi.set_mode(self.dataPin, pigpio.OUTPUT)
        self.pi.set_mode(self.sckPin, pigpio.OUTPUT)

        self.pi.write(self.dataPin, pigpio.HIGH)
        self.__clocktick(pigpio.HIGH)
        self.pi.write(self.dataPin, pigpio.LOW)
        self.__clocktick(pigpio.LOW)
        self.__clocktick(pigpio.HIGH)
        self.pi.write(self.dataPin, pigpio.HIGH)
        self.__clocktick(pigpio.LOW)

        for i in range(8):
            if (command & (1 << 7 - i)) != 0:
                commanddata = 1
            else:
                commanddata = 0
            self.pi.write(self.dataPin, commanddata)
            self.__clocktick(pigpio.HIGH)
            self.__clocktick(pigpio.LOW)

        self.__clocktick(pigpio.HIGH)

        self.pi.set_mode(self.dataPin, pigpio.INPUT)
        ack = self.pi.read(self.dataPin)
        #        self.logger.debug("ack1: %s", ack)
        if ack != pigpio.LOW:
            self.logger.error("nack1")

        self.__clocktick(pigpio.LOW)
        ack = self.pi.read(self.dataPin)
        #        self.logger.debug("ack2: %s", ack)
        if ack != pigpio.HIGH:
            self.logger.error("nack2")

    def __clocktick(self, value):
        self.pi.write(self.sckPin, value)
        #       100 nanoseconds
        time.sleep(.0000001)

    def __getdata16bit(self):
        self.pi.set_mode(self.dataPin, pigpio.INPUT)
        self.pi.set_mode(self.sckPin, pigpio.OUTPUT)
        # Get the most significant bits
        value = self.__shiftin(8)
        value *= 256
        # Send the required ack
        self.pi.set_mode(self.dataPin, pigpio.OUTPUT)
        self.pi.write(self.dataPin, pigpio.HIGH)
        self.pi.write(self.dataPin, pigpio.LOW)
        self.__clocktick(pigpio.HIGH)
        self.__clocktick(pigpio.LOW)
        # Get the least significant bits
        self.pi.set_mode(self.dataPin, pigpio.INPUT)
        value |= self.__shiftin(8)

        return value

    def __shiftin(self, bitnum):
        value = 0
        for i in range(bitnum):
            self.__clocktick(pigpio.HIGH)
            value = value * 2 + self.pi.read(self.dataPin)
            self.__clocktick(pigpio.LOW)
        return value

    def __skipcrc(self):
        #        Skip acknowledge to end trans (no CRC)
        self.pi.set_mode(self.dataPin, pigpio.OUTPUT)
        self.pi.set_mode(self.sckPin, pigpio.OUTPUT)
        self.pi.write(self.dataPin, pigpio.HIGH)
        self.__clocktick(pigpio.HIGH)
        self.__clocktick(pigpio.LOW)

    def __waitforresult(self):
        self.pi.set_mode(self.dataPin, pigpio.INPUT)
        ack = pigpio.LOW
        for i in range(100):
            # 10 milliseconds
            time.sleep(.01)
            ack = self.pi.read(self.dataPin)
            if ack == pigpio.LOW:
                break
        if ack == pigpio.HIGH:
            raise SystemError

    def cancel(self):
        self.pi = 0

if __name__ == "__main__":
    logging.config.fileConfig('logging.conf')
    logger = logging.getLogger()

    pi = pigpio.pi()
    if not pi.connected:
        exit(0)

    temp = SHT11(pi, logger, DATAPINT4)

    while True:
        wtemperature, whumidity = temp.read_temperature_and_humidity()
        print("temperature:{:.2f}℃ humidity:{:.2f}% ".format(wtemperature, whumidity))
        time.sleep(2.0)