ラズパイからPythonのSMBusモジュールを使ってArduinoにデータを送信しよう
ラズパイ(Raspberry Pi)とArduinoは初心者に人気の高いマイコンボードです。ドキュメントも充実していて、Device Plusでも度々記事になるので皆さんも一度は見たことがあるのではないでしょうか?
このラズパイとArduinoについてよく聞かれるのが「どちらから始めるのがベストか?」です。
今回は2回に分けてラズパイとArduinoの特徴を説明し、両者を電線でつなげて通信をする実験を行います。
本記事はその1回目「基本編」です。両者の特徴とその通信に使用するI2Cバスの説明を中心に進めます。
目次
- ラズパイとArduinoの特徴
- I2Cを使った通信
- 2.1. I2Cとは
- 2.2. I2Cとほかのプロトコルの比較
- 電源電圧の違うマイコン同士の接続
- 3.1. 3.3Vと5Vの電圧レベルの違い
- 3.2. オープンコレクタで3.3Vと5Vのシステムを接続
- 準備
- ArduinoとラズパイでI2Cを使えるようにする
- 5.1. ArduinoでI2Cを使用する準備
- 5.2. ラズパイでI2Cを使用する準備
- 5.3. ラズパイにArduino IDEをインストール
- I2Cバスの接続
- 6.1. Arduino(子機)のスケッチを作成
- 6.2. ラズパイとArduinoをI2Cバスで接続
- Pythonを使ってArduinoにデータを送信
- 7.1. ArduinoにLEDを接続
- 7.2. PythonからI2Cのデータを送信
- まとめ
1. ラズパイとArduinoの特徴
一言で違いを説明するなら
「ラズパイはLinuxが搭載された小型のコンピュータ」
「Arduinoは機械を制御するためのマイコンボード」
です。
ラズパイは基板のサイズが小さいだけで、本格的なパソコンと機能は同等です。
OSにLinuxを採用しているのでキーボードやマウスなどのUSB機器が使用でき、ディスプレイを接続すればグラフィカルユーザインターフェース(GUI)で操作できます。
Linuxの強みとするネットワークの機能も充実しており、簡単にインターネットにアクセスできるだけでなくサーバマシンとしても機能します。
もちろんソフトウェアも通常のパソコン向け(Linux)に作られたものが動くので、ラズパイ上でさまざまな開発環境が使用でき、使い慣れたプログラミング言語で開発が可能です。
今回はラズパイにArduinoの開発環境であるArduino IDEをインストールし、Arduinoのスケッチを作成、デバッグします。
Arduinoとラズパイの処理能力は、ラズパイの方が圧倒的に優れていますが、それだけで単純に良し悪しを決めることはできません。
ラズパイは「名刺サイズのPC」とも言われるように、モニタをつなげるHDMI端子やマウス・キーボードを接続するUSB端子を搭載しています。これらの処理を同時に実行するため、CPU(マイコン)もパソコン相当のパワーが求められています。
Arduinoに使われるマイコンは電子機器の中に組み込み、周囲の電子部品の制御や監視が得意です。
電子機器の制御は複数のタスクをこなすことより、一つのタスクを高速に正確なタイミングで実行することが求められます。Arduinoのマイコンはそのような用途で力を発揮しますよ。
また、Arduino上で動作するプログラム(スケッチ)は、マイコンの能力を十分に発揮するために、パソコンでコンパイルされてからArduinoに転送されます。
コンパイルとは、人間が読み書きできる状態のプログラム(ソースコード)を実行する前に、マイコンが読める状態に翻訳する作業です。マイコンが読める状態は数字の羅列なので人間は内容を把握できません。
ラズパイとArduinoを比較する場合、最も大きな違いがOSの有無です。
ラズパイは、GPIO(汎用入出力)と呼ばれる端子を搭載するLinuxパソコンです。普通の家庭用パソコンのように使える上に、Linuxで使えるソフトやライブラリーも活用できるため、搭載したい機能を簡単に実現できます。
特に、USBの通信や無線モジュールの制御はLinuxが大部分を制御してくれるため、通信制御やプロセス管理などを気にすることなく、必要な機能の開発に集中することが出来ます。
一方のArduinoは、LinuxのようなOSは搭載していないため、いくつものモジュールを同時に管理したり、外部との通信を制御したりするのには向きません。ですがコンパイルによって記述された処理を直接実行しているため、最低限のスケッチで自由度の高い高速な処理を行わせることは可能です。
LEDの点灯を制御する程度の使い方なら大きな違いはありませんが、大きなシステムを構築しようとすると、得手不得手が見えてくるようになります。
そのため、使い方によっては、それぞれ得意な方に処理を任せて、ラズパイでネットワークを介した複数のタスクを同時にこなし、電子回路の制御はArduinoが担当するような使い方にすれば、お互いの利点を最大限に発揮できます。
これを実現するのが、I2Cと呼ばれる通信プロトコルです。I2CはラズパイとArduinoのどちらも対応しているため使いやすく手軽に連携することができます。
2. I2Cを使った通信
ラズパイとArduinoを橋渡しする通信プロトコルI2Cについて説明します。
2.1. I2Cとは
I2Cは「Inter-Integrated Circuit」の略です。読み方は「アイ・スクエアド・シー」ですが「アイ・アイ・シー」と呼ばれることもあります。
I2Cは通信プロトコルの一種で、異なるデバイス間で情報を交換する際のルールが定義されています。I2Cは近距離のデバイス間の通信を想定したもので、マイコン同士の情報交換によく使用されます。
I2Cのバス(通信経路)はSDA(シリアルデータ)とSCL(シリアルクロック)の2本の信号線で構成されます。
実際に2つのデバイスをバスで接続する際は、両者の電位を同じにするためグランド同士も接続してください。
I2Cプロトコルでは親機となる「マスターノード」と子機の「スレーブノード」が定義されています。
子機は複数接続することができ、親機のコマンドに対して応答しますが「どの子機へのコマンドか?」を見分けるために、子機には重複しないアドレス(番号)を事前に割り当てます。
2.2. I2Cとほかのプロトコルの比較
I2Cはデータの送受信に2本の信号線しか使わないため、ほかのシリアル通信より手軽です。また、I2Cは1個の親機に対して複数の子機との間で交互に通信することもできます。
UARTと呼ばれる通信方式は古くから使われているシリアル転送ですが、親機/子機という概念はなくデバイスとデバイスの接続は1対1です。
ほかにはSPIという方式があり1個の親機に対して複数の子機を接続できますが、信号線がもう2本必要です。
I2Cのメリットは手軽に使用できることですが通信速度は速くありません。I2Cはもともとほかのマイコンに処理を依頼するコマンドの送信や、数多くのセンサを接続して値を読み取るなど、速度がそれほど重要にならない用途に向けて開発されたプロトコルだからです。
ファイル転送やストリーミングなど、大きなデータの送受信を考えているのであれば、SPIなどの高速に動作するプロトコルを使用した方がよいでしょう。
3. 電源電圧の違うマイコン同士の接続
ラズパイの電源電圧は3.3V、Arduinoは5Vであり、電圧の異なるマイコンを接続する際に必要なポイントを説明します。
3.1. 3.3Vと5Vの電圧レベルの違い
ラズパイとArduinoを接続することで両者の利点を最大限に発揮できますが、電圧レベルが異なるため、そのままの接続はできません。
ラズパイの電源電圧は3.3V、Arduinoは5Vの両者の入出力端子の動作電圧の違いに対処しなければいけません。
一般的には、3.3Vの信号を5Vで動作するデバイス(マイコン)に送ることは可能です。マイコンの入力には「何V以上をHIGH(入力がある状態)と見なすか?」というしきい値が決められていて、通常、しきい値は3.3Vより低い電圧に設定されます。
一方で、5Vの信号をラズパイのような3.3Vのデバイスに送ることはできません。最悪の場合マイコンが破損します。
3.2. オープンコレクタで3.3Vと5Vのシステムを接続
I2Cの仕様では2本の信号線は双方向(送信/受信で使用する)です。このことからラズパイからArduinoへデータを送ることもあり、またその逆もあります。
ラズパイからArduinoへの送信は3.3Vなので問題はありません。ところがArduinoからラズパイへは5Vなのでラズパイは破損してしまいます。
この問題に対処するために、I2Cバスとなるピンはオープンコレクタという構造になっています。
オープンコレクタは「マイコンから電流を出す/止める」を制御するのではなく「マイコンに電流を流し込む/止める」を制御する構造です。「マイコンに電流を流し込む」ことを「引き込む」「吸い込む」と言う場合があります。
「流し込む」と言っても「入力」とは違います。なぜ「流し込む」ことが「出力」になるのか、ラズパイ(もしくはArduino)にLEDを接続する場合(Lチカ)を例に説明します。
通常のLチカは
「ラズパイのGPIOピン === LED === LEDを保護する抵抗 === GND」
と配線します。
プログラムからGPIOピンをONすることで電流がLEDに流れ込み点灯します。
一方、ピンがオープンコレクタの場合、電流は流れません。
LEDを点灯するためには上記の接続のGPIOピンとLEDの間に新たな抵抗を接続します。この抵抗のもう一端は電源(ラズパイの場合は3.3V)に接続してください。
オープンコレクタは、この電源に接続した抵抗を通して流れ込む電流を「マイコン内に流すことでLEDには流さない」「マイコン内に流さないでLEDの方に流す」と制御することで、LEDをプログラムからON/OFF(出力)します。(ただしONは消灯、OFFは点灯と論理が逆になります)
また、この「電源に接続した抵抗」のことをプルアップ抵抗と呼びます。
I2Cバスとなるマイコンのピンはオープンコレクタであることが仕様で決められています。
そこで「I2Cバスにデータを送信する」と言っても、マイコンから電流が流れるわけではありません。バス上の電流を引き込むことで相手側のピンがLOW(LEDの例なら消灯)します。
また通常はI2Cバスに電流を流すためプルアップが必要ですが、ラズパイのI2Cバスは内部で3.3Vの電源にプルアップされています。
Arduinoはラズパイと違いプルアップ用の抵抗が取り付けられていません。そのため、wire.begin()は関数内でマイコンに内蔵されているプルアップ抵抗を自動的にONする仕様です。今回の例では動作電圧の低いラズパイに合わせるため、wire.begin()関数の呼び出し後にプルアップ抵抗の使用をOFFすることで、ラズパイのプルアップ抵抗のみから電流を供給します。
そのため、ラズパイとArduinoを2本の電線で接続するだけで、問題なくI2Cバスとして機能します。
I2Cバスに限らず、オープンコレクタを使用すれば3.3Vと5Vの電子回路を共存させることが可能です。
ただし全てのケースに対応できるわけではないので注意は必要です。
4. 準備
以下のものを準備してください。
ラズパイ本体
ブレッドボード
GPIO拡張基板
スイッチサイエンス製(互換可)
Arduino Uno
LED
緑・赤(色違いであれば何でも可)
抵抗
680Ω(LEDの破損防止用)
GPIO拡張基板はAdafruit製(海外)やスイッチサイエンス製(国内)が入手しやすそうです。
ほかにも、さまざまな製品が販売されているので、皆さんの使いやすいものを探してみるのも良いでしょう。
上記に加えてブレッドボード用のジャンパワイヤが必要です。
ラズパイで作業するためにディスプレイ、USBキーボードとマウスを使用します。リモート接続ができる場合は不要です。
5. ArduinoとラズパイでI2Cを使えるようにする
ラズパイとArduinoをI2Cで接続するためには、それぞれで準備が必要です。
また今回はラズパイにイントールしたArduino IDEでArduinoのスケッチを開発します。Arduino IDEもここで準備します。
5.1. ArduinoでI2Cを使用する準備
ArduinoではWireと呼ばれるライブラリーが用意されているので、スケッチの先頭でヘッダーファイルをインクルードするだけでI2Cを使用できます。
#include
5.2. ラズパイでI2Cを使用する準備
ラズパイはArduinoに比べてI2Cを使用するにはいくつかのステップが必要です。
最初にラズパイの設定ツールを起動します。ターミナルから次のコマンドを入力します。
sudo raspi-config
ラズパイの設定画面が表示されるので「Interfacing Options」を選択し、続く画面の一覧から「I2C」を選びます。
ラズパイは「I2C0」「I2C1」の2組のI2Cバスがあります。I2C1が汎用的に使用できるので通常はこちらを選択。(以降「バス(1)」と呼びます)
バス(1)に接続されているデバイスを確認するには以下のコマンドを入力します。
i2cdetect -y 1
現時点ではI2Cに対応する子機を接続していないので、デバイスは1つも表示されていないはずです。
5.3. ラズパイにArduino IDEをインストール
ラズパイではパソコンと同様さまざまな開発環境を利用できます。そこで本記事ではArduino IDEをラズパイにインストールしてスケッチを作成、Arduinoに転送します。
最初にラズパイを最新の状態にアップデートしましょう。
sudo apt update && sudo apt upgrade -y
アップデートが完了したらArduino IDEをインストールします。以下のコマンドを入力してください。
sudo apt install arduino -y
以上でArduinoのスケッチの開発をラズパイ上で行う準備が整いました。
6. I2Cバスの接続
I2Cの接続にはラズパイとArduino間をI2Cバスでつなぐ配線と、I2Cの子機にアドレスを設定するソフトウェアが必要です。
アドレスの設定が必要なのは子機となるArduinoです。そのため先にArduinoのスケッチを完成させ転送しておきます。
6.1. Arduino(子機)のスケッチを作成
Arduino UnoをラズパイのUSBに接続して、ラズパイ上のArduino IDEからスケッチを作成します。
以下がスケッチの全体です。スケッチのあとに解説が続きます。
#include const int GreenLedPin = 6; const int RedLedPin = 7; void setup() { //Join the I2C bus at address 0x8 wire.begin(0x8); //Deactivate pullups for I2C digitalWrite(SDA, 0); digitalWrite(SCL, 0); //When data is received, run the switchLed function below wire.onReceive(switchLed); //Setup pin 6 as an output pin for a green LED and turn it off pinMode(GreenLedPin, OUTPUT); digitalWrite(GreenLedPin, LOW); //Setup pin 7 as an output pin for a red LED and turn it off pinMode(RedLedPin, OUTPUT); digitalWrite(RedLedPin, LOW); } //This function switches LEDs on and off when the Raspberry Pi sends a message void switchLed(int bitstream) { while (wire.available()) { char c = wire.read(); switch (c) { case 0x0: digitalWrite(GreenLedPin, HIGH); break; case 0x1: digitalWrite(GreenLedPin, LOW); break; case 0x2: digitalWrite(RedLedPin, HIGH); break; case 0x3: digitalWrite(RedLedPin, LOW); } } } void loop() { delay(100); }
このスケッチはArduinoを子機としてI2Cに接続します。子機なので重複しないアドレスを割り当てる必要がありwire.begin()関数で0x8を指定。
wire.onReceive()関数でI2Cバスからデータを受信した際に呼び出される独自の関数switchLed()を指定します。
以降、I2Cバスのデータを受信するごとにswitchLed()関数が呼び出されます。
switchLed()の中では、while文を使用してI2Cバスから送られてきた「未処理のデータが残っているか?」をチェックします。チェックにはwire.available()関数を使用し、データがなくなれば繰り返し処理(while文)を終了。
while文が繰り返されるごとにwire.read()関数を使って「未処理のデータ」を1バイト読み出し変数cに格納してください。
以降は、変数cに格納された値に応じて処理を振り分けます。緑色LEDのON/OFF、赤色LEDのON/OFFの計4種類の処理があります。
スケッチの最後にloop()関数があります。今回のスケッチで必要なのはデータを受信した際の処理だけです。そこでloop()関数の中は単にdelay()関数を呼び出し終了します。delay()に深い意味はありません。
スケッチが完成したらArduinoに転送してください。
以降、ArduinoにつながるUSBケーブルは使用しないのでスケッチの転送はできません。
6.2. ラズパイとArduinoをI2Cバスで接続
最初にArduinoに接続しているUSBケーブルを外してください。
以下はラズパイとブレッドボードの接続にGPIO拡張基板を使用した例です。GPIO拡張基板がない場合はラズパイの対象のピンをジャンパワイヤでブレッドボードに接続してください。
それではラズパイが接続されたブレッドボードとArduinoを配線します。以下の図のようにラズパイのSDAピンとArduinoのA4ピンを、ラズパイのSCLピンとArduinoのA5ピンを接続します。
Arduino UnoにはI2Cバスとして使用できるSDAピンとSCLピンがありますが、入出力ピンのA4・A5がそれぞれSDA・SCLとしても利用できます。
次にラズパイとArduinoのGNDピン同士を接続します。
最後にラズパイの5VピンをArduinoのVinに接続すれば完了です。
Vinを接続すると先ほどArduinoに転送したスケッチが動作を始めます。
ここからはラズパイでの作業です。
この状態でラズパイのターミナルから再度、次のコマンドを入力してください。
i2cdetect -y 1
今度はArduino Unoに割り当てたアドレス「0x08」が表示されたはずです。
7. Pythonを使ってArduinoにデータを送信
ラズパイから電源を供給されたArduinoは、Arduinoボードに接続した緑色・赤色のLEDを点灯/消灯するコマンド(データ)がラズパイから送られてくるのを待っています。
ラズパイのPythonからArduinoにコマンドを送りLEDを点灯/消灯してみましょう。
7.1. ArduinoにLEDを接続
ラズパイのGNDピンをブレッドボードの電源ライン(-)に接続します。
緑色・赤色LEDをブレッドボードに挿します。LEDには電流の流れる方向があり、アノード(長い方の端子)が右側になるように挿します。
LEDは流れる電流を制限するための抵抗(680Ω)を接続する必要があります。それぞれのLEDのカソード(短い方の端子)に抵抗の一端を接続します。
抵抗のもう一端は電源ライン(-)に接続します。
緑色LEDのアノードをArduinoの6番ピン、赤色LEDのアノードは7番ピンに接続します
これでラズパイからのコマンド(データ)に応じてLEDが点灯/消灯する準備が整いました。
7.2. PythonからI2Cのデータを送信
ラズパイのターミナルからPythonを起動します。以下のコマンドを入力してください。
python3
I2Cを制御するためにPythonのライブラリーSMBusをインポートします。
Pythonは大文字、小文字を区別するので間違えないように入力してください。
from smbus import SMBus
データの転送に使用する値をあらかじめ定義しておきます。以下の2行を入力してください。
arduino = 0x8 i2cbus = SMBus(1)
変数arduinoは子機(Arduinoボード)に割り当てたアドレスです。Arduinoのスケッチ内で指定した値と一致する必要があります。
2行目はI2Cバスの番号バス(1)を指定しSMBusオブジェクトを作成。以降、I2Cバスの操作は変数i2cbusを通して行います。
I2Cで転送するデータを定義します。
greenOn = 0x0 greenOff = 0x1 redOn = 0x2 redOff = 0x3
緑色LEDのON/OFF、赤色LEDのON/OFFと続きます。
データは数字(16進数で表記)です。数字を直接コマンドで使用することもできますが、間違いを防止するため分かりやすい名前をつけるイメージです。
ここで定義する数字はArduinoのスケッチ内で指定した値と一致する必要があります。
SMBusオブジェクトのwrite_byte()関数を使用してArduinoにデータを送信します。
緑色LEDを点灯/消灯するコマンドは以下の通りです。
i2cbus.write_byte(arduino, greenOn) i2cbus.write_byte(arduino, greenOff)
配線が正しければ、緑色LEDがコマンドに合わせて点灯、消灯するはずです。
赤色LEDはご自身で試してみてください。
8. まとめ
今回は「基本編」ということでラズパイとArduinoを橋渡しするI2Cの概要を説明しました。また、Arduinoボードに接続したLEDを点灯/消灯する実験を通してラズパイのコマンドでArduinoを制御するポイントも紹介しました。
記事は少し長くなりましたが、一つ一つの作業に難しい点はなかったはずです。
次回の「実用編」では、このラズパイとArduinoの接続がどういった用途に力を発揮するか説明します。
今回の連載の流れ
基本編:ラズパイからPythonのSMBusモジュールを使ってArduinoにデータを送信しよう(今回)
実用編:ラズパイの苦手なアナログ信号の処理をArduinoに任せよう