[Raspberry Pi メニューへ戻る]

ボクにもわかる I2C インタフェース方式
for Raspberry Pi

Raspberry Piを使ってI2Cの実験をしてみよう(IchigoJamはこちら)。
ちょっと難し目バージョン


CO2センサ(SENSIRON SGP30搭載ユニット)を Raspberry Pi の拡張IO端子に I2Cインタフェース接続した

はじめに

 I2Cインタフェース(以下I2Cインタフェース)は、1982年にPhilips(現NXP)が開発した IC間の通信規格です。クロック信号SCL、データ信号SDA、GNDの3本の配線で複数のI2C機器との通信が行えます。
 本ページでは、I2C温度センサモジュール、または二酸化炭素CO2センサユニットRaspberry Pi へ接続し、I2Cインタフェースの基礎的な使い方を説明します。
 意図的に未経験者の方に難し目の内容にしてありますが、読み進めると徐々に簡単になります。結論だけを得るのではなく、これから様々なI2Cデバイスが扱えるような情報を詰め込みました。
 分からない部分は読み飛ばしてください。動かしてみて、流れをつかみ、なんとなく達成感が得られれば十分です。さぁ、チャレンジしてみましょう。

本ページの主な学習内容(Raspberry Pi版)
項目 内容

配線方法

SCL、SDA、GNDとプルアップ抵抗を接続する

I2Cアドレス

I2Cデバイスを選択する

アクセス方法

i2cget コマンドでセンサ値を取得する

検索方法

i2cdetectでスレーブノードを検索する

温度センサ用
プログラム


温度センサ値を取得する Python コード
センサデバイス=ABLIC(SII) S-5851A
二酸化炭素
センサ用
プログラム

二酸化炭素CO2センサ値を取得する
センサデバイス=SENSIRION SGP30

 様々なI2CセンサのRaspberry Pi用サンプル・プログラムを、Git Hubで公開しています。

親機のマスター・ノードと子機のスレーブ・ノード、配線方法

 I2C機器には通信の親機となるマスターノードと、子機となるスレーブノードの2種類があります。Raspberry Piのように頭脳を持った機器がマスターノードです。また、その Raspberry Pi の周辺機器(センサ等の周辺デバイス側)がスレーブノードです。
 マスター、スレーブのそれぞれのI2C機器に備わっているクロック信号SCLと、データ信号SDA、GND同士を下記の回路図のように接続して使用します。


親機マスタノードのSCL、SDAのそれぞれの信号線に、子機スレーブノードのSCL、SDAを接続する。子機スレーブ側では、それぞれの信号線をプルアップ抵抗(数kΩ~10kΩ程度)で電源へ接続する(マスター、スレーブともに同じ電源電圧を想定)
回路の説明:図中の上部の4本の線(VCC)は途切れているように見えるが、4本が相互に接続されていることを示している。また、明示されていないが、この4本のVCCは、3.3Vの電源に接続されている。図中の下部のGND端子については、その近くにあるアース記号によって、GNDへ接続されていることを示している。アース記号があれば、GND端子を線で結ぶ必要は無いが、この回路図では分かりやすいように左右のICのGNDを相互に接続した。SCL信号は左の Raspberry Pi から右の周辺機器へ送信されるが、その信号の戻り電流がGNDを通して Raspberry Pi へ帰ってくる。このように、GNDには様々な信号の戻り電流が複雑に入り混じって流れる。SDA信号の途中でSCLのプルアップ抵抗が交差しているように見えるが、ドット「●」の無い交差は接続しない。

 本ページでは、より詳しい情報を【Column】として紹介しています。【Column】のタイトルを見て、興味がない場合や、疑問に思わない場合は、読み飛ばして、次に進みましょう。

【Column】なぜマスターノードとスレーブノードが必要なのか?
 Raspberry Pi の周辺機器のように、親子関係が明確な通信インタフェースでは、マスターとスレーブに分かれていることによるメリットが大きいので、例えばUSBについても、ホストとデバイスに分かれています。
 I2Cインタフェースでは、親機となるマスターノードがクロックを出力し、子機となるスレーブノードはマスターのクロックに合わせてデータを送受信します。このようにマスターが主管となって通信を行うことで、一つの親機マスタノードのSCL、SDA信号線に、複数台のスレーブノードを接続することが容易に出来るようになります。
 また、クロック信号の速度が既定の100kbps以外であっても動作させることが出来るので、Raspberry Pi 側の処理能力に合わせてクロック信号の速度を加減することも出来ます。スレーブ側には通信インタフェース用クロック発振器が不要で簡単なロジック回路で実装できる点も低価格化や普及に貢献しました。

少し分かりにくいI2Cアドレス

 マスターがスレーブと通信を行うには、I2Cアドレスが必要です。例えば、教室での授業中に先生Aが生徒Cへ命令するときには、先生Aは生徒Cの名前を呼んでから本題である命令を話します。このように生徒Cの名前を付与するのは、複数の生徒から生徒Cを特定するためです。そして、その命令に対して行動を起こしたり、結果を先生に報告するのは生徒Cだけです(他の生徒は口出しせずに黙っていることが前提)。
 同じように、I2Cのマスター(親機・先生)は、I2Cスレーブ(子機・生徒)へコマンドを送る前に、スレーブを特定するための情報(生徒の名前)を付与します。このときに付与する情報がI2Cアドレスです。

 I2Cアドレスは予めI2C機器に設定されている8番から119番までの番号です。I2C機器を入手したら、まずは機器の説明書や部品のデータシートなどに記載されているI2Cアドレスを確認して下さい。I2Cアドレスの表記方法には、10進数表記、16進数表記、2進数表記、7ビット表記、8ビット表記などがあります。詳しくは理解できなくても大丈夫です。複数の表記方法があることを知っておきましょう。
 例えば、10進数表記のIC2アドレスが60だった場合、データシートには、60、0x3C、0x78、0b0111100、0b01111000のいずれかが記載されています。もし、ここで7ビット表記の0x3Cを8ビット表記の0x3Cだと勘違いしてしまったら、10進数表記のアドレスは30となってしまいます。先生が「佐藤さん」を「国野さん」と、呼んでも伝わらないのと同じで、I2Cアドレスが一致せずに通信が行えなくなります。

少し分かりにくいI2Cアドレスの表記方法
表記方法 範囲 表記例
位取り ビット 最小 最大 60 62 72
10進数 7 bit 8 119 60 62 72
16進数 7 bit 0x08 0x77 0x3C 0x3E 0x48
16進数 8 bit 0x10 0xEE 0x78 0x7C 0x90
2進数 7 bit 0b0001000 0b1110111 0b0111100 0b0111110 0b1001000
2進数 8 bit 0b00010000 0b11101110 0b01111000 0b01111100 0b10010000

 親機マスタノードのSCL、SDA信号線には、最大112台までの子機スレーブノードのSCL、SDAを接続することが出来ます。しかし、もし教室に二人の佐藤さんが居たとして、先生が「佐藤さん」と呼ぶと2人が返事をしてしまい、特定することが出来ません。I2Cアドレスは、マスターが個々のスレーブを特定するために用います。このため、同じ信号線(I2Cバス上)に同じI2Cアドレスの機器を接続することは出来ません。
 多くのI2C機器には、I2Cアドレスを変更する機能がついています。片方の「佐藤さん」を「国野さん」に変更することが出来るのです。複数台の同じI2C機器を使用するときや、たまたま同じI2Cアドレスの機器があったときは、各I2C機器のI2Cアドレスが重複しないように設定してください。

【Column】7ビット表記のI2Cアドレスを8ビット表記へ変換する
 もっとも分かりにくいのは、8ビット表記と7ビット表記の部分でしょう。7ビット表記のアドレスを左へ1桁シフトすると8ビット表記になります。つまり7ビット表記値を2倍すれば8ビット表記値が得られます。10進数、16進数、2進数の記数法に慣れていない人は、デジタル回路や、論理回路の入門書、教科書などを参考にすると理解が深まりやすいでしょう。
【Column】8ビット表記のI2Cアドレスの最下位ビット
 インタフェース規格上、I2Cアドレスは7ビットで表記されていますが、最下位ビットに0を挿入し、8ビットで表記する場合もあります。8ビット表記の最下位ビットはマスターからスレーブへの書き込み/読み取りの識別値として利用されます。実際の通信においても、書き込み/読み取りの識別値が付与され、バイト単位で通信を行います。
 一例として、8ビット表記のI2Cアドレスが0x90の場合、最下位ビットは0です。この0は、「マスターからスレーブへの書き込み」を示します。読み取り時は、8ビット表記I2Cアドレス0x90の最下位ビットが1となり、0x91になります。

本ページの実験に必要な部品のリスト

 本稿の製作に必要な部品のリストを以下に示します。

本稿の実験に必要な主要な部品のリスト(Raspberry Pi版)
 温度センサ:
  • ABLIC S-5851A 使用 2ワイヤデジタル温度センサモジュール (秋月電子通商・110円)
  • ミニブレッドボードBB-601(汎用品・130円)
  • 抵抗 10kΩ(汎用品×2個)
  • 積層セラミックコンデンサ 0.1μF(汎用品×2個)
  • ブレッドボード用ジャンパワイヤ(オス-メス 10cm程度×4本)
  • ブレッドボード用ジャンパワイヤ(橙1本、紫1本、灰1本、ほかに+2本)
 二酸化炭素CO2センサ:
  • SENSIRION SGP30 使用 TVOC/eCO2センサユニット
  • 接続ケーブル(自作 Grove - 2.54mmピッチ・メス)
 共通:
  • Raspberry Pi および 周辺機器

I2C温度センサ ABLIC製S-5851A を Raspberry Pi へ接続する

 それでは、実験用のハードウェアの準備に移りましょう。I2C機器として温度センサを Raspberry Pi へ接続してみます。下図はABLIC製のI2C温度センサを搭載した2ワイヤデジタル温度センサモジュールS-5851AAA-M6T1U(秋月電子通商製)です。12ビットの高分解能なセンサにもかかわらず税込み110円と低価格で販売されています。また、国内メーカ品であることから、日本語データシートが理解しやすいと考え、この部品を選定しました。


I2C温度センサ ABLIC製 S-5851A使用2ワイヤデジタル温度センサモジュール。基板へ実装済みのモジュールが110円。ICはハンダ付けされているが、写真の左右のピンヘッダを基板へハンダ付けする必要がある。ABLIC社は、世界に誇るSEIKOブランドの時計を研究・開発・製造しているセイコーインスツル社の半導体事業を継承した会社。温度センサとしては、CMOS技術を活かしたS-8120Cが世界中で使用されている。S-5851AはI2Cインタフェースを搭載した製品だが、データシートやカタログでは「2ワイヤシリアルインタフェース(I2Cバス)」という表現で記載されている(NXP社の商標を意識したものと思われる)

 下図は温度センサモジュールS-5851AAA-M6T1Uをミニ・ブレッドボード上に実装したときの様子です。ICのマーカ部が写真の上側に来るように実装してください。プルアップ抵抗には10kΩを使用しました。
 本品に限らず、CMOS ICのVDD(VCC)とVSS(GND)にはデカップリング用のコンデンサを追加し、電源へのノイズ混入を防止します。ここでは0.1μFを使用しました。コンデンサをつけ忘れたとしても、実験程度であれば何も変わらないと思います。ここでは、原則として必要な部品につき省略しませんでした。ただし、実験には差し支えない程度の罠を仕掛けてあります。読み進めると、発見できるかもしれません。


I2C信号のSCLとSDAにはプルアップ抵抗が必要。プルアップ抵抗の値は各デバイスの仕様や伝送速度、伝送距離などを考慮して概ね数kΩから10kΩ程度の範囲内にする。抵抗値が小さいほどIC側のドライブ能力に負担をかけるが、より高い速度で遠くまで伝えることが出来る。今回のように10cm程度の短いブレッドボード用ジャンパワイヤで接続する場合は、10kΩくらいにしておくことで、ドライブ能力不足による不具合を避けることが出来る。

 製作したブレッドボード上の VCC、SDA、SCL、GNDを、Raspberry Pi の拡張IO端子の3.3V、I2C_SDA、I2C_SCL、GNDへ接続します。


温度センサモジュール S-5851AAA-M6T1U の VCC、SDA、SCL、GNDを、Raspberry Pi の拡張IO端子の3.3V、I2C_SDA、I2C_SCL、GNDにへ接続する。配線を行うときは電源を切っておくこと

 下図はRaspberry Pi の拡張IO端子のピン配置図です。電源電圧はセンサによって異なり、S-5851Aの場合は3.3Vなので、左下の1番ピンを使用します。I2Cは3番ピンと5番ピン、GNDは9番ピンです。


Raspberry Pi の拡張IO端子のピン配置図。電源電圧はセンサによって異なり、S-5851Aの場合は3.3Vなので、左下の1番ピンを使用する。I2Cは3番ピンと5番ピン、GNDは9番ピンです。ジャンパワイヤの色に一定のルールを設けておくと誤配線の対策となる。一般的に電源のプラス側には赤を、GNDには黒を使うことが多い。ただし電池のマイナス極には青を、アースの時は緑を使用する場合もある。その他の信号については、主要信号、出力側、速度が高いものなどに目立つ色(橙、黄など)、その対になるものを目立たない色(青、緑など)を使用するなど、自分なりのルールを決めておくと良い

Raspberry Pi のI2Cインタフェースを有効に設定する

 Raspberry Pi上でターミナル LXTerminal を起動し下記のコマンドを入力すると、Raspberry Piの設定ツールが起動します。

pi@raspberrypi:~ $ sudo raspi-config ⏎

 下図のような画面が表示されたら、カーソルキーで 「Interfaceing Options」を選択し、[Enter]キーを押してください。


 続いて I2Cを選択し、[Enter]キーを押してください。


 I2Cを有効にする場合は左側の「はい」をカーソルの左右キーで選択し、[Enter]キーを2回、押してください。


 最初の画面に戻ったら、[ESC]キーで終了します。

 以上でハードウェアの製作は完了です。動作しない場合は、一度、Raspberry Piを再起動してください。

【Column】VCC、VEE、VDD、VSS、3.3V、3V3、GNDなどの表示について
 これらは電源に用いられる信号名です。VCC、VDD、3.3V、3V3は、プラス側の電源を示し、VEE、VSS、GNDはマイナス側を示します。それだけを理解しておけば十分ですが、本コラムでは、これらの使い分けについて説明します。
 GNDには単にマイナス側という意味だけでなく、各種の電源や各種の信号の戻り電流が流れる共通の基準電位を示す意味もあります。GND以外には電源電圧を意味する「V」のアルファベットが用いられます。
 VCCとVEEはTTLロジック回路(トランジスタを使った回路)に用いられた電源の表記方法で、トランジスタのコレクタ側のプラス電源をVCC、エミッタ側のマイナス電源をVEEと示します。CMOSロジック回路(FETを使った回路)の時代に入ると、FETのドレイン側のプラス電源をVDD、ソース側のマイナス電源をVSSと示すようになりました。
 回路図の信号名で3.3Vの電圧値とともに電源であることを示す場合には、3V3のように記します。近年は回路CADなどのエンジニアリングシステムの進化により、信号名に小数点が使えるようになってきたので3.3Vと記すことも出来るようになってきました。
 以上のように、ロジック回路の構造の移り変わりや、システムの進化によって、様々な表現がされるようになりました。ICや部品のピン名には、どのような名称が来ても、極性を理解する必要があります。回路配線の信号名(ネット名)をつける際に、統一するようにしましょう。

I2Cアドレス検索を行う

 この節のタイトルを見て、前述のI2Cアドレスの説明は何だったのかと思う人も居るでしょう。てっとり早く解を得て、物事の本質に時間をかけることは、技術進歩には欠かせない重要な手法です。教室の先生や生徒の話なんて聞きたくなかったかもしれません。
 しかし、ボクの中で定着している製作手法には、I2Cアドレスの検索という手順はありません。もう少し読み進めると、適切な理由があることに気づくかもしれません。

 Raspberry Pi(マスターノード)にI2C機器(スレーブ)を接続し、i2cdetectというソフトでI2Cアドレスを検索すれば、簡単にアドレスを知ることが出来ます。
 以下にのi2cdetectの実行結果の一例を示します。見つかったI2Cアドレスが16進数・7ビット表記で表示されます。

【Column】Raspberry Pi の i2cdetectでI2Cアドレスを探す
 i2cdetectでI2Cアドレスを探すことが出来ます。下記の黄色文字のコマンドを実行すると、2つのデバイスが見つかりました。I2Cアドレス「48」が温度センサ S-5851A 、「58」が CO2センサ SGP30 のアドレスです。

実行結果 Raspberry Piで i2cdetectを実行する
pi@raspberrypi:~ $ i2cdetect 1 ⏎
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n] Y ⏎
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- 58 -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@raspberrypi:~ $ ■

 実行のたびに Y ⏎ を入力するのが煩わしい場合は、 i2cdetect -y 1と入力することで Continue? の問い合わせを省略することも出来ます。

 次節では、このI2C機器の理解を深めるために、少しだけデータシートを確認します。この段階で、すぐにでも温度を測定したい人は「I2Cコマンドで温度データを取得する」へ進んでください。


念のためにデータシートと合っているかどうかを確認する

 念のための確認というのも大切です。前述の通り、i2cdetectを使って検索すると、I2C温度センサS-5851AのI2Cアドレスは16進数で0x48でした。I2C Detectorを実行すると下図のように16進数値「48」が表示されました。
 Raspberry Piでは16進数を10進数へ変換するには echo $((0x48)) のように入力します。このコマンドは後に使うので、事前に試しておくよ良いでしょう。下図では変換結果「72」が表示されました。反対に10進数の72を16進数に変換したいときは printf "%X\n" 68 のように入力します。

16進数を10進数に変換する/10進数を16進数に変換する
pi@raspberrypi:~ $ echo $((0x48)) ⏎
72
pi@raspberrypi:~ $ echo $((0x58)) ⏎
88
pi@raspberrypi:~ $ printf "%X\n" 72 ⏎
48
pi@raspberrypi:~ $ printf "%X\n" 88 ⏎
58
pi@raspberrypi:~ $ ■

 もし時間に余裕があるようでしたら、念のためにI2C温度センサS-5851Aのデータシートで最後の確認をしておきましょう。下表にはICのAD1端子(3番ピン)、AD0端子(5番ピン)の状態とI2Cアドレス(スレーブアドレス)との関係が記載されています。これらの端子の状態に応じて8種類のI2Cアドレスが選択できます。
 表の最上段のAD1端子=0、AD0端子=0を見てみると、スレーブアドレスは2進数7ビットで「1001000」、すなわち10進数の72、16進数の0x48であることが分かります。ちゃんと検証できたので、問題なさそうです。まだまだ気になる人は、じっくりと下表と回路を見比べてください。謎が解けたときの嬉しさは、応用への強いバネとなって身につきます。解けなくても支障が無いので、正解は書きません。見つけた人に優越感を味わっていただきたいです。


上記は「S-5851Aシリーズ データシート 1.0-2016.01」P.16 から本デバイス使用する目的で引用。AD1とAD0端子が0の状態のときは、7ビットのスレーブアドレスが「1001000」すなわち16進数で0x48、10進数で72になることが分かった。

I2Cコマンド i2cget で温度データを取得する

 我慢強い人も、そろそろ「一体、温度は何℃なんだ!」と、思ってきているでしょう。Raspberry Pi の LXTerminal から以下のコマンドを実行すれば、いますぐに温度を取得できます。ボクも冷静では無いかもしれません。こんな暑い部屋でこの記事を書いていたのです。

pi@raspberrypi:~ $ echo $((`i2cget -y 1 0x48`)) "℃" ⏎
33 ℃
pi@raspberrypi:~ $ ■

 これまでのややこしいI2Cアドレスの説明を忘れるくらい簡単に温度が得られました。この1行コマンドについて、解説しておきます。

 括弧内の i2cget 命令は、Raspberry Pi が I2Cインタフェースを使ってセンサ値などを取得する命令です。オプション -y は、実行時に [Y] ⏎ の入力を避けるために必要です。次の引数「1」はI2Cバス番号で、ここではI2Cバス番号1を使用します。0x48はI2Cアドレスです。
 echo は基本的な表示命令です。$((~))は基本的な算術演算命令です。2つの丸括弧内に $((1+2)) のように演算子を含む数式を入れることで、整数演算が出来ます。小数を扱うことは出来ません。ここでは、16進数を10進数に変換するのに使用します。

Raspberry Pi用 i2cget命令 (I2Cデータの1バイト受信)
 i2cget 1 0x48 ⏎
  • 第1引数:I2Cバス番号=1 (Pin3:GPIO2とPin5:GPIO3を使用)
  • 第2引数:I2Cアドレス=0x48

取得した温度データを温度値に変換する

 先ほど取得した温度は1℃ごとの荒い値でしたが、S-5851Aには12ビットのADコンバータが内蔵されており、より細かな値を得ることも出来ます。下図はデータシート内に書かれた温度レジスタ(S-5851A内のメモリーの一部)の構成図です。


上記は「S-5851Aシリーズ データシート 1.0-2016.01」の P.12から本デバイス使用する目的で引用。温度データは2バイトに分かれており、ADコンバータの解像度に相当する12ビットのうち、上位8ビットが1バイト目に、下位4ビットが2バイト目に含まれていることが分かる。

 上図のように計12ビット分の温度データが2バイトのデータ構造で保持されていることが分かりました。そこで、こんどは下記のコマンドを入力し、2バイト分のデータを受信し、表示してみましょう。最初の応答値の0は通信の成功を意味します。次の応答値は16進数2バイト分の温度値データです。温度データ 0xa021 が得られました。

pi@raspberrypi:~ $ i2cget -y 1 0x48 0x00 w ⏎
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will read from device file /dev/i2c-1, chip address 0x48, data address
0x00, using read word data.
Continue? [Y/n] Y ⏎
0xa021
pi@raspberrypi:~ $ echo $((0x21)) ⏎
33
pi@raspberrypi:~ $ echo $((0xa0 * 100 / 256)) ⏎
62
pi@raspberrypi:~ $ ■

 Raspberry Piが 取得した温度値データ 0xa021 の下2桁の21は整数部です。echo $((0x21)) で 33 が得られます。残る2桁のa0は小数部で $((0xa0 * 100 / 256)) で 62 でした。したがって、33.62℃となります。

 以下にi2cget命令で2バイトのデータを得る時の引数をまとめておきます。

Raspberry Pi用 i2cget命令 (I2Cデータの2バイト受信)
 i2cget 1 0x48 0x00 w ⏎
  • 第1引数:I2Cバス番号=1 (Pin3:GPIO2とPin5:GPIO3を使用)
  • 第2引数:I2Cアドレス=0x48
  • 第3引数:受信を行うためにスレーブへ送信する命令=0x00
  • 第4引数:受信オプション(b:1バイト, w:2バイト)=w

I2C温度センサ S-5851A 用 Python プログラム

 次に、得られた温度データを摂氏温度へ変換するプログラムを実行してみます。Raspberry Pi上のLXTerminalで以下のコマンドを実行してください。

pi@raspberrypi:~ $ git clone https://bokunimo.net/git/RaspberryPi ⏎
pi@raspberrypi:~ $ cd RaspberryPi/gpio ⏎
pi@raspberrypi:~/RaspberryPi/gpio $ ./raspi_s5851a.py ⏎
Temp.= 32.69 ℃
Temp.= 32.75 ℃
Temp.= 32.81 ℃
^C Traceback (most recent call last):
  File "./raspi_s5851a.py", line 29, in <module>
    sleep(1)
KeyboardInterrupt
pi@raspberrypi:~/RaspberryPi/gpio $ ■

 Raspberry Pi は、温度センサからのデータ取得に成功すると、測定した温度値が1秒ごとに LXTerminal 上に表示します。プログラムを停止するときは[Ctrl]キーを押しながら[C]キーを押下します。適切に動作しないときは、一度、Raspberry Piを再起動してみて下さい。それでも動作しないときは、Raspberry Piの電源を切ってから、配線を見直してください。

【Column】Raspberry Pi でI2C温度センサのデータを取得する

 下記は温度センサ S-5851A からセンサ値を取得する Raspberry Pi 用の Python プログラムです。GitHubからダウンロードすることも出来ます。

プログラム Raspberry Pi用 I2C温度センサ プログラム「raspi_s5851a.py」
#!/usr/bin/env python3

s5851 = 0x48

import smbus
from time import sleep

def word2int(d1,d2):
    i = d1
    i <<= 8
    i += d2
    if i >= 32768:
        i -= 65536
    return i

i2c = smbus.SMBus(1)
while i2c:
    data=i2c.read_i2c_block_data(s5851,0x00,2)
    if len(data) >= 2:
        temp = float(word2int(data[0],data[1])) / 256.
        print("Temp.= %.2f ℃" % (temp))
    sleep(1)

 I2Cインタフェースを搭載したセンサの多くは、特別な変換なしに温度値が簡単に計算できるように工夫されています。本S-5851Aも取得後の計算が簡単になるように1バイト目に整数部、2バイト目に小数部で構成されています。
 上記のプログラムでは、word2intで2バイトのデータを結合し、256で除算するだけで温度値を得ます。

【Column】C言語でI2C温度センサのデータを取得する

 組み込み機器には、古くからある C言語が良く使われています。Pythonに比べ、ソースリストが長く、また設計者が考慮しなければならない要素が増える欠点がありますが、ハードウェアの負担が少ないため、低価格なマイコンなどで実現できるので、製品コストにメリットがあります。
 C言語で書かれたRaspberry Pi用のプログラムは「raspi_s5851a.c」です。以下のコマンドでダウンロードし、コンパイルしてから「./raspi_s5851a」を実行してください。下記はプログラムの抜粋です。

 ご注意:C言語のプログラムを実行すると、I2C用のGPIOポートが使用され、OSやPythonのプログラムから使用できなくなります。OSを再起動すれば使えるようになります。

Raspberry Pi用の様々なI2Cセンサ・プログラム集
pi@raspberrypi:~ $ git clone https://bokunimo.net/git/RaspberryPi ⏎
pi@raspberrypi:~ $ cd RaspberryPi/gpio ⏎
pi@raspberrypi:~/RaspberryPi/gpio $ make ⏎
pi@raspberrypi:~/RaspberryPi/gpio $ ./raspi_s5851a ⏎
32.69
pi@raspberrypi:~/RaspberryPi/gpio $

C言語で書かれた Raspberry Pi用 I2C温度センサ プログラム「raspi_s5851a.c」
#include "../libs/soft_i2c.h"
typedef unsigned char byte;
byte i2c_address=0x48;

int16_t _getReg(byte data){
    byte rx[2];
    i2c_write(i2c_address,&data,1);
    delay(10);
    i2c_read(i2c_address,rx,2);
    return (int16_t)(((uint16_t)rx[0])<<8)|((uint16_t)rx[1]);
}

float getTemp(){
    int16_t ret;
    ret = _getReg(0x00);
    return (float)ret / 256.;
}

int main(){
    i2c_init();
    delay(20);
    printf("%3.2f\n",getTemp());
    i2c_close();
    return 0;
}

二酸化炭素CO2センサ SGP30 を Raspberry Pi へ接続する

 下図は SENSIRION 製の二酸化炭素CO2センサ SGP30 を搭載した TVOC/eCO2ユニット(M5Stack製)です。ケーブルは Raspberry Pi の 拡張IO端子 に接続できるように自作しました。製品に付属するケーブルを切断し、Raspberry Piに、直接、ハンダ付けしても良いでしょう


二酸化炭素CO2センサ SGP30 を搭載した TVOC/eCO2ユニット(M5Stack製)。スイッチ・サイエンスなどで売られている。写真は Raspberry Pi の 拡張IO端子 に接続するためのケーブル(自作)を Grove端子に接続した。ユニットに付属するケーブルを切断して、Raspberry Piに、直接、ハンダ付けしても良い

 TVOC/eCO2ユニット(M5Stack製)の内部には、SENSIRION 製 SGP30と、1.8Vの電源レギュレータが内蔵されています。Raspberry Pi のI2Cインタフェースの信号電圧は3.3Vなので、VCC(+5Vと表示)には 3.3Vの電源を接続してください。
 ※ 誤って 5V を供給すると Raspberry Pi の GPIO が壊れる場合があります。


二酸化炭素CO2センサ SGP30 を搭載した TVOC/eCO2ユニット(M5Stack製)。スイッチ・サイエンスなどで売られている。写真は Raspberry Pi の 拡張IO端子 に接続するためのケーブル(自作)を Grove端子に接続した。ユニットに付属するケーブルを切断して、Raspberry Piに、直接、ハンダ付けしても良い
回路図の出典および権利者(M5Stack):https://docs.m5stack.com/#/en/unit/tvoc

二酸化炭素CO2センサ SGP30 用 Python プログラム

 下記は、二酸化炭素CO2センサ SGP30 用のサンプル・プログラムを実行したときの様子です。すでに git clone でサンプル・プログラム集をダウンロードしていた場合は、git cloneの再実行は不要です。
 実行すると、推定CO2濃度と、TVOC(総揮発性有機化合物)濃度の測定結果を約1秒間隔で表示出力します。数分で値が得られ、12時間以上の動作でセンサ本来の精度が得られます。

pi@raspberrypi:~ $ git clone https://bokunimo.net/git/RaspberryPi ⏎
pi@raspberrypi:~ $ cd RaspberryPi/gpio ⏎
pi@raspberrypi:~/RaspberryPi/gpio $ ./raspi_sgp30.py ⏎
CO2= 400 ppm, TVOC= 0 ppb
CO2= 400 ppm, TVOC= 0 ppb
..........................
CO2= 431 ppm, TVOC= 10 ppb
CO2= 450 ppm, TVOC= 25 ppb
CO2= 467 ppm, TVOC= 31 ppb
CO2= 471 ppm, TVOC= 36 ppb
^C Traceback (most recent call last):
  File "./raspi_sgp30.py", line 32, in <module>
    sleep(1)
KeyboardInterrupt
pi@raspberrypi:~/RaspberryPi/gpio $ ■
【Column】Raspberry Pi で二酸化炭素CO2センサのデータを取得する

  下記は、二酸化炭素CO2センサ SGP30 のセンサ値を取得する Raspberry Pi用の Python プログラムです。GitHubからダウンロードすることも出来ます。

プログラム Raspberry Pi用 I2C温度センサ プログラム「raspi_sgp30.py」
#!/usr/bin/env python3

sgp30 = 0x58

import smbus
from time import sleep

def word2uint(d1,d2):
    i = d1
    i <<= 8
    i += d2
    return i

i2c = smbus.SMBus(1)
i2c.write_byte_data(sgp30, 0x20, 0x03)
sleep(1.012)
while i2c:
    i2c.write_byte_data(sgp30, 0x20, 0x08)
    sleep(0.014)
    data=i2c.read_i2c_block_data(sgp30,0x00,6)
    if len(data) >= 5:
        co2 = word2uint(data[0],data[1])
        tvoc= word2uint(data[3],data[4])
        print("CO2= %d ppm, TVOC= %d ppb" % (co2,tvoc))
    sleep(1)

 温度センサ S-5851A と Raspberry Pi を使った実験は以上ですが、当サイト内には、様々なI2Cインターフェース搭載センサやマイコンを使ったサンプル・プログラムを用意しているので、ぜひ活用ください。

Copyright (c) 2017年7月-2021年3月 国野 亘




以下はI2Cインタフェースに関する、より詳しい情報です。




I2Cデータ波形

 ここで、実際のI2Cデータの波形を見てみましょう。下図は自作したI2C信号をキャプチャした結果です。上側のSCLの立ち上がり時にSDAのデータの読み書きが行われます。



I2C接続LCDモジュールAQM0802へ、2バイト分のI2Cデータを送信したときの信号波形の様子。IC2アドレスは先頭の1バイトの上位7ビットに配置される。下位1ビットは0で書き込み、1で読み取りであることを示す。続く2バイトのうち1バイト目は、スレーブノードへのコマンド、もしくはスレーブノードのレジスタアドレスを示すことが多い。また、2バイト目にコマンドの属性値やレジスタへ書き込む内容が構成される。こういったルールはI2C機器によって異なるので、ドライバソフトウェアを作成するときは、データシートをよく読んで実装する。

ソフトウェアによるI2Cドライバ

 I2Cインタフェースをソフトウェアで構成することで、様々なGPIO端子をI2Cポートとして使用することも出来ます。下記はソフトウェア I2C ドライバ soft_i2cの主要部のソースリストです。上図の波形は本ドライバで出力しました。ソースリスト全体はGitHubへ公開しています。

ソフトウェア I2C ドライバ
// ソフトウェア I2C ドライバ soft_i2c
// Copyright (c) 2014-2017 Wataru KUNINO
// https://github.com/bokunimowakaru/RaspberryPi/blob/master/libs/soft_i2c.c

byte i2c_SCL(byte level){
    byte ret=0;
    if( level ){
        ret += !pinMode(PORT_SCL, INPUT);
    }else{
        ret += !pinMode(PORT_SCL, OUTPUT);
        ret += !digitalWrite(PORT_SCL, LOW);
    }
    _delayMicroseconds(I2C_RAMDA);
    return !ret;
}

byte i2c_SDA(byte level){
    byte ret=0;
    if( level ){
        ret += !pinMode(PORT_SDA, INPUT);
    }else{
        ret += !pinMode(PORT_SDA, OUTPUT);
        ret += !digitalWrite(PORT_SDA, LOW);
    }
    _delayMicroseconds(I2C_RAMDA);
    return !ret;
}

byte i2c_tx(const byte in){
    int i;
    for(i=0;i<8;i++){
        if( (in>>(7-i))&0x01 ){
                i2c_SDA(1);
        }else   i2c_SDA(0);
        /*Clock*/
        i2c_SCL(1);
        i2c_SCL(0);
    }
    /* ACK処理 */
    _delayMicroseconds(I2C_RAMDA);
    i2c_SDA(1);
    i2c_SCL(1);
    for(i=3;i>0;i--){
        if( digitalRead(PORT_SDA) == 0 ) break;
        _delayMicroseconds(I2C_RAMDA/2);
    }
    if(i==0 && ERROR_CHECK ){
        i2c_SCL(0);
        i2c_log("no ACK");
        return 0;
    }
    return (byte)i;
}



Raspberry Pi メインメニューへ戻るページの先頭へ戻る