今回は、Arduinoで超音波センサとI2C通信を行います。
そのなかで、とくに便利なのがWire関数です。
まず最初に、I2C通信の概要を説明します。
![](https://hiiragikoubou.com/wp-content/uploads/2021/07/5-2.png)
I2C通信とは
I2C(Inter-Integrated Circuit)は、フィリップス社が提唱した周辺デバイスとのシリアル通信の方式で、主にEEPROMメモリICなどとの高速通信を実現する方式です。
基本構成は、マスタ側とスレーブ側を明確に分かれており、マスタ側が全ての制御の主導権を持っています。
接続は、マスターは一機だけですが、スレーブは何機も接続することが出来ます。
![](https://hiiragikoubou.com/wp-content/uploads/2021/07/2-1.png)
I2Cは、全ての同じ線(SDA,SCL)を並列で接続します。並列なのに個々と通信ができるのは、インターネットのネットワークと同様に、それぞれにアドレスが割り振られているからです。
アドレスは、2進数でも16進数でも10進数でも構いません。例えば16進数で書く場合は、0x70や、0x56などの形で書きます。また、マスターにはアドレスはありません。
通信をする際は、必ず最初にSlaveの宛先(アドレス)を宣言してから、データを送ります。送り先が変わるときも逐次、宛先を言います。人間の生活でも、手紙、宅配便には必ず宛先を書きますよね。それと同じです。当たり前といえば当たり前ですね。
![](https://hiiragikoubou.com/wp-content/uploads/2021/07/3-2.png)
Wire.hの主なコマンド
ここでは、細かい説明は、なるべく省き、使い方に特化させます。
なので、まず使えるコマンドを確認しましょう。
#include<Wire.h>
I2Cで通信する際は、最初に宣言する必要になります。これは、「Wireのライブラリを使います」という宣言文で、プログラムの先頭に書きます。
Wire.begin();
wireライブラリを初期化します。自身がSlaveの場合は、括弧の中にアドレスを入力します。Masterはアドレスの入力は不要です。
Wire.beginTransmission(slave address);
カッコ内に指定したアドレスのSlaveに対して、通信を開始する準備をします。通信を始めるときは必ず宣言します。
Wire.write( data );
Wire.beginTransmissionで指定したアドレスに対して、Wire.writeのカッコ内のデータを送信します。基本的にデータは1byte以内です。
Wire.endTransmission();
Slaveに対してのデータの送信処理を完了します。なお、WireライブラリではbeginTransmissionからendTransmissionまでのデータバッファは32byteなので送信データが多い場合は適宜endTransmissionで区切る必要があります。
Wire.requestFrom(address, byte);
MasterがSlaveにデータを要求するとき、最初に宣言します。要求する先のアドレスと、ほしいデータの容量を書きます。
Wire.available()
read()で読み取ることができるバイト数を返します。
Wire.read()
マスタデバイスでは、requestFrom()を実行したあと、スレーブから送られてきたデータを読み取るときに使用します。スレーブがマスタからのデータを受信するときにも使用します。
超音波センサにおけるI2Cのプログラム
以前のブログでお話した、超音波センサのプログラムを示します。
前回のブログでは、あまり触れていなかった通信の部分に焦点をあてます。
#include <Wire.h>
void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial communication at 9600bps
pinMode(11,OUTPUT);
}
int reading = 0;
int SPEAKER = 11;
int TIME=100;
int m[9]={262,294,330,349,392,440,494,524,0 };
int melody=0;
void loop()
{
// step 1: instruct sensor to read echoes
Wire.beginTransmission(112); // transmit to device #112 (0x70)
Wire.write(byte(0x00)); // sets register pointer to the command register (0x00)
Wire.write(byte(0x51)); // command sensor to measure in "centimeters" (0x51)
Wire.endTransmission(); // stop transmitting
// step 2: wait for readings to happen
delay(20); // datasheet suggests at least 65 milliseconds
// step 3: instruct sensor to return a particular echo reading
Wire.beginTransmission(112); // transmit to device #112
Wire.write(byte(0x02)); // sets register pointer to echo #1 register (0x02)
Wire.endTransmission(); // stop transmitting
// step 4: request reading from sensor
Wire.requestFrom(112, 2); // request 2 bytes from slave device #112
// step 5: receive reading from sensor
if (2 <= Wire.available()) // if two bytes were received
{
reading = Wire.read(); // receive high byte (overwrites previous reading)
reading = reading << 8; // shift high byte to be high 8 bits
reading |= Wire.read(); // receive low byte as lower 8 bits
Serial.print(reading); // print the reading
Serial.println("cm");
if(reading>105){
}else if(reading>95){
melody=m[7];
tone(11,melody) ;
delay(TIME);
}else if(reading>85){
melody=m[6];
tone(11,melody) ;
delay(TIME);
}else if(reading>75){
melody=m[5];
tone(11,melody) ;
delay(TIME);
}else if(reading>65){
melody=m[4];
tone(11,melody) ;
delay(TIME);
}else if(reading>55){
melody=m[3];
tone(11,melody) ;
delay(TIME);
}else if(reading>45){
melody=m[2];
tone(11,melody) ;
delay(TIME);
}else if(reading>35){
melody=m[1];
tone(11,melody) ;
delay(TIME);
}else if(reading>25){
melody=m[0];
tone(11,melody) ;
delay(TIME);
}else if(reading>15){
melody=m[8];
tone(11,melody) ;
delay(TIME);
}
tone(11,melody) ;
}
delay(5); // wait a bit since people have to read the output :)
}
まず、voidsetup(){ の中に、 #include <Wire.h> と Wire.begin(); が入っています。これはそれぞれ、Wireライブラリの読み出しと、Wireライブラリの初期化の意味があります。
#include <Wire.h>
void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial communication at 9600bps
pinMode(11,OUTPUT);
}
メインのプログラムは、5段構成です。
まず、Wire.beginTransmission(112);で、Slaveデバイスのアドレス(112)を指定します。
次に、Wire.writeで、0x00を送信。超音波センサに通信開始する合図です。
その後、0x51を書き込みます。これは、あとで読み出す、超音波センサの距離を、センチメートルで返す設定をします。
Wire.endTransmissionで送信します。
20ミリ秒待ったのち、0x02を送信。超音波の測定値を送るようにセンサに要求しています。
そして、Wire.requestFormで、受信の準備をします。容量は2byteです。
さいご、Wire.read()で測定値を読み込むという流れになっています。
// step 1: instruct sensor to read echoes
Wire.beginTransmission(112); // transmit to device #112 (0x70)
// the address specified in the datasheet is 224 (0xE0)
// but i2c adressing uses the high 7 bits so it's 112
Wire.write(byte(0x00)); // sets register pointer to the command register (0x00)
Wire.write(byte(0x51)); // command sensor to measure in "centimeters" (0x51)
// use 0x51 for centimeters
// use 0x52 for ping microseconds
Wire.endTransmission(); // stop transmitting
// step 2: wait for readings to happen
delay(20); // datasheet suggests at least 65 milliseconds
// step 3: instruct sensor to return a particular echo reading
Wire.beginTransmission(112); // transmit to device #112
Wire.write(byte(0x02)); // sets register pointer to echo #1 register (0x02)
Wire.endTransmission(); // stop transmitting
// step 4: request reading from sensor
Wire.requestFrom(112, 2); // request 2 bytes from slave device #112
// step 5: receive reading from sensor
if (2 <= Wire.available()) // if two bytes were received
{
reading = Wire.read(); // receive high byte (overwrites previous reading)
reading = reading << 8; // shift high byte to be high 8 bits
reading |= Wire.read(); // receive low byte as lower 8 bits
Serial.print(reading); // print the reading
Serial.println("cm");
きょうはここまで。