LoginSignup
9
18

More than 3 years have passed since last update.

Raspberry PiでI2Cする方法と注意点

Posted at

概要

ラズパイでI2Cセンサモジュールとの通信をPythonでプログラミングする際に使うライブラリ2種類と,ハマってわかった注意点をログしておきます

環境

  • Raspberry Pi 3B
  • OS: Raspbian (Jessie)
  • Python: 2.7.9

使ったI2Cモジュールは以下の通り

  • ADT7410 (Analog Devices) 温度センサ
  • AM2322 (Aosong) 温湿度センサ

ライブラリ比較

SMBus

webで調べるとこのライブラリを使っている例が多いですね.SMBusはSystem Management Busで,I2Cの一種とのことです.

関数の参考サイト: Using the I2C Interface – Raspberry Pi Projects
さらに関数詳細: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/Documentation/i2c/smbus-protocol

インストール

sudo apt install python-smbus

使い方

ADT7410を例に温度を読み取ってみます1

# ADT7410の温度読み取り
import smbus
i2c = smbus.SMBus(1) # 注:ラズパイのI2Cポート
address = 0x48
block = i2c.read_i2c_block_data(address,0x00,2) # 0x48のI2Cスレーブのレジスタ0x00から2バイト読み取る
temp = (block[0] << 8 | block[1]) >> 3
if(temp >= 4096):
        temp -= 8192
print("温度: "+temp/16.0)

pigpio

I2Cに限らず,Raspberry PiのGPIO制御するのに便利な環境.バックでプログラム(pigpio)を走らせて,そこにアクセスすることでGPIOやらI2Cを使えるようにしています

Python関数参考: pigpio library

インストールと準備

sudo apt install pigpio python-pigpio
sudo systemctl start pigpiod

再起動後も自動的にpigpiodを起動させるには,

sudo systemctl enable pigpiod

使い方

AM2322を例に温度と湿度を読み取ってみます

import pigpio
import time
pi = pigpio.pi()
addr = 0x5c
h = pi.i2c_open(1,addr) # ハンドル取得
try:
    pi.i2c_write_quick(h, 0) # AM2322のスリープ解除
except:
    pass
pi.i2c_write_i2c_block_data(h,0x03,[0x00,0x04])
time.sleep(0.003)
(count, buf) = pi.i2c_read_device(h,0x08)
hum = (buf[2]*256 + buf[3])/10.0
temp = ((buf[4]&0x7f)*256 + buf[5])/10.0 * ( -1 if buf[4]&0x80 > 0 else 1 ) # tempの最上位ビット(7)が負数ビット
print("温度: {:.1f}, 湿度: {:.1f}".format(temp,hum))
pi.i2c_close(h)

注意点(ハマりどころ)

AM2322の温湿度をログするためにcronで定期的に実行させていたのですが,当初i2c_closeを書いておらず,しばらくすると実行されなくなることがありました.
その時,単発で実行すると以下のエラーが出ます

pigpio.error: 'no handle available'

pigpioライブラリで取得しているi2c用のハンドル(上記のh)は32個までしか割り当てられないとのことで,ちゃんとcloseしないと枯渇することが原因だったようです.
しばらく動いてしまうので分かりにくいハマりポイントでした.

参考: https://www.raspberrypi.org/forums/viewtopic.php?t=88545

I2Cの注意点

Raspberry Piに限りませんが,I2Cモジュールはものによって操作が結構異なるみたいなので,使うモジュールのデータシートをよく読まないとハマります(ハマりました)

ADT7410の場合

データシートP19: http://akizukidenshi.com/download/ds/analog/ADT7410a.pdf

Reading back from any register first requires a single-byte write operation to the address pointer register to set up the address of the register that is going to be read from. In the case of reading back from the 2-byte registers, the address pointer automatically increments from the MSB register address to the LSB register address.

読みたいレジスタのアドレスを1バイト書き込んだ後,2バイトの読み取り操作をします.SMBusでは複数バイトが読み取れるread_i2c_block_dataを使いました.

AM2322の場合

データシートP13 : http://akizukidenshi.com/download/ds/aosong/AM2321_e.pdf

ホストから "0x03" "開始アドレス" "読み取りレジスタ数" を書き込み,その後スレーブから "0x03" "レジスタ数" "実際のデータ" "CRC" が送られます.

アドレス0x00から順に湿度MSB・LSB,温度MSB・LSBの4バイトなので,"0x03, 0x00, 0x04" を書き込んだあと,8バイトの読み取りをする必要があります.
しかし,SMBusでは書き込みと読み取り別々に行う関数が用意されていなかったため,pigpioのi2c_read_deviceを使うことになりました.


  1. I2Cポートはls /dev/i2c*で出てきた方の数字を使えばいいはず 

9
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
18