5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

EV3でI2C通信

Last updated at Posted at 2021-06-16

#初めに

 今回、MINDSTORMS社のEV3で、専用品ではない各種センサーを使う方法を調べました。
 EV3とセンサーの間にArduinoを挟むことで実現しています。

##使用機材

  • EV3
  • Arduino互換マイコン
  • センサー(今回はカラーセンサーS11059-02DT)

##使用ライブラリ

  • Arduino Software I2C
  • [ArduinoWireLibrary](Wire Library) (上で代用する場合は不要。今回は使います)

##使用ツール

  • ArduinoIDE
  • ROBOT-C

#回路の作成
 以下の回路図を参考に組み立ててください。
抵抗間違えていました。10KΩではなく100KΩです。

Arduino - EV3
EV3.png

Arduino - S11059-02DT
Sensor.png
 組み立てたものがこちらになります。

P5221279.JPG

#プログラミング
##Arduino Software I2Cのダウンロード
このライブラリはArduinoのI2C専用ピン(SCL,SDA)以外のピンでもI2C通信ができるようになるライブラリです。
センサーとArduinoの通信を担います。

Arduino Software I2C
ライブラリのダウンロード方法はこちら

標準ライブラリの"Wire.h"の関数と互換性があります。
詳しく知りたい場合はダウンロードフォルダ内のREADME.mdを見てください。
また、I2C通信にさらに詳しく知りたい場合はこちら

##Arduino側のプログラミング
 例として、カラーセンサのS11059-02DTを使います。

Arduino側のメインプログラム

[ALDUINO]main.ino
#include "sensor.h" // センサーの値を取得するコードが入ったヘッダー
#include "EV3.h" // EV3との通信を行うコードが入ったヘッダー

// センサから取得した値を保持しておく配列のポインタ
byte *sensor;

void setup()
{
  // デバック用シリアル通信
  Serial.begin(9600);

  // センサとArduinoの通信(SDA,SCL)
  initI2CPort(13, 12);

  // カラーセンサの初期化
  initColSensor();

  // 変数の初期化(0で埋める)
  sensor = (byte*)malloc(sizeof(byte)*DATASIZE);
  memset(sensor, 0, sizeof(byte)*DATASIZE);

  // 送信する配列のポインタを指定
  initEV3Connect(sensor);
}

void loop() 
{
  // センサーの値入手
  getColSensor(&sensor[0]);

  /*R,G,Bのそれぞれの値を変数に入れる*/
  uint16_t r,g,b;
  r = *(uint16_t *)&sensor[0];
  b = *(uint16_t *)&sensor[2];
  g = *(uint16_t *)&sensor[4];
  
  delay(200);
}

EV3と通信するプログラム

[ALDUINO]EV3.h
#include "Wire.h" 

// ArduinoのI2Cアドレス
#define I2C_ADDRESS 0x31
// 送るデーターサイズ
#define DATASIZE 16

uint8_t getAdres;
byte *data;

void receiveEvent(int DataNum);
void requestEvent();

// 変数の初期化とI2Cの設定
void initEV3Connect(byte *d)
{
  data = d;
  // ArduinoをEV3のスレーブ(子)として設定
  Wire.begin(I2C_ADDRESS);

  Wire.onReceive(receiveEvent); // 自身のアドレスが呼ばれた時に実行する関数
  Wire.onRequest(requestEvent); // データの送信を要求が来た時に実行する関数
}

// I2C通信で自分のアドレスが呼ばれた時の処理
void receiveEvent(int DataNum)
{
  // 受けた命令を保存
  getAdres = Wire.read();
}


// データの送信を要求が来た時に実行する関数

void requestEvent()
{ 
  // 0x10が最後に受けた読み込み命令なら
  if (getAdres == 0x10) 
  {
    // DATASIZE個のデータを送信
    Wire.write(data, DATASIZE);
  }
}

センサーの値を取得するプログラム

[ALDUINO]sensor.h
#include "SoftwareI2C.h" // ボード付属のSDA,SDCとは別のポートを使うためのライブラリ

// センサーのI2Cアドレス
#define COL_ADDRESS 0x2A

// I2Cの情報を入れる変数
SoftwareI2C i2c;

// I2Cポートの設定
void initI2CPort(int sda, int scl)
{
  i2c.begin(sda, scl); 
}

// カラーセンサの初期化(各々のデーターシート参照)
void initColSensor()
{
  i2c.beginTransmission(COL_ADDRESS);
  i2c.write(0x00);
  i2c.write(0x8B);
  i2c.endTransmission();

  i2c.beginTransmission(COL_ADDRESS);
  i2c.write(0x00);
  i2c.write(0x0B);
  i2c.endTransmission();
  delay(3000);
}

// カラーセンサーの値を取得
void getColSensor(byte *arry)
{
  // カラーセンサにアドレス0x03のデータを渡すよう要求
  i2c.beginTransmission(COL_ADDRESS);
  i2c.write(0x03);
  i2c.endTransmission();

  // カラーセンサーに0x03から8byte分のデーターを要求
  i2c.requestFrom(COL_ADDRESS, 8);
  if(i2c.available())
  {
    for(int i = 0; i < 4; i++)
    {
      // Arduinoはリトルエンディアンなので注意
      arry[2*i+1] = i2c.read(); //highビット
      arry[2*i+0] = i2c.read(); //lowビット
    }
  }
  i2c.endTransmission();
}

##EV3側のプログラミング

Robot-Cを用いています。

[EV3]main.c
#define Arduino_ADDRESS 0x31

// ポートに応答があるか
void checkI2C(tSensors port)
{
  while(nI2CStatus[S1]!=0)
  {}
  return;
}

task main()
{
  // Arduinoに送信するデータ
  char Wbuf[3] = {2, Arduino_ADDRESS << 1, 0x10};
  // Arduinoからの値を入れるバッファ
  char Rbuf[16];

  while(true)
  {
  // ArduinoにWbufの中身を送信
  sendI2CMsg(S1, Wbuf, 16);
  // Arduinoからの応答があるまで待つ
  checkI2C(S1);
  // Arduinoからの値をRBufに入れる
  readI2CReply(S1, Rbuf,16);

  /*以下表示用プログラム*/
  unsigned short *SensorVal = (unsigned short *)&Rbuf[0];
  		
  eraseDisplay();
  for(int i=0;i<4;i++)
  {
  	displayBigTextLine(i*2,"  %5d : %5d",i,SensorVal[i]);
  }

  wait1Msec(500);
  }
}

うまくいけばこうなります。

P5221281.JPG

#プログラムの説明
Arduinoでのセンサーの扱いについては、ネットに優れた情報が豊富にあるため省きます。それらを参考にしてください。

基本:センサー読み出しのシーケンス

Arduinoに接続されたセンサーの値をEV3から読み出すには、以下のようなシーケンスとなります。

###1, EV3からArduinoへのセンサー読み出しリクエスト

EV3
char Wbuf[3] = {2, Arduino_ADDRESS << 1, 0x10 };

送信する内容を配列に格納します。

  • 第一要素は送信する要素数(アドレスを含むので、データ数+1)
  • 第二要素はArduinoのI2Cアドレス
  • 第三要素からは送る任意のデータ列。

 ArduinoのI2Cアドレスは、0~127の7bit幅の適当な値です。最下位ビットにはW/R命令フラグが入りますので、1bit左シフトしておきます。
 データとして送っている0x10は、「カラーセンサーを読み取る」というコマンド的に使っている、本サンプル固有の適当な値です。

EV3
sendI2CMsg(S1, Wbuf, 16);

配列のデータを送信します。

  • 第一引数はArduinoを接続しているEV3のポート
  • 第二引数は送信する内容の配列
  • 第三引数については現在調査中です。受け取るバイト数と書いているのですが…わからない
    とりあえず、readI2CReply(のちに記載)と同じにします。

###2, ArduinoからEV3への応答

ARDUINO
// I2C通信で自分のアドレスが呼ばれた時の処理
void receiveEvent(int DataNum)
{
  // 受けた命令を保存
  getAdres = Wire.read();
}

 自分のアドレスに向けて通信があった場合、Wire.onReceive() で登録した receiveEvent 関数が実行されます。Wire.read() で、EV3から送信されたデータ列を1byteずつ受け取ります。

ARDUINO
// 送信要請が来た時の処理
void requestEvent() 
{ 
  // 0x10が最後に受けた命令なら
  if (getAdres == 0x10) 
  {
    // DATASIZE個のデータを送信
    Wire.write(data, DATASIZE);
  }
}

次に、Wire.onRequest() で登録した requestEvent 関数が実行されます。
Wire.write(*d, size)で、EV3に渡したいデータを送信します。

  • 第一引数はデータ配列のポインター
  • 第二引数は送信するデータサイズ(byte)

第二引数と readI2CReply() の第三引数は同じにしてください。

###3, Arduinoからの応答をEV3で受け取る

EV3
readI2CReply(S1, Rbuf,16);

Arduinoから送られてきたデータを受信します。

  • 第一引数はArduinoを接続しているEV3のポート
  • 第二引数は受信データを格納するれる配列(ポインタ)
  • 第三引数は何byte受け取るか

です。

応用:複数のセンサーを使う方法

二つの方法が考えられます。

###方法1 全てのセンサーのデータをまとめて送受信する。
 シンプルな方法です。今回の場合、16要素あるsensor配列中、カラーセンサは0~7番目まで使います。残りの8~15番目は空いているので、そこに他のセンサーのデータを入れて送信します。センサーの数とデータサイズによって、配列のサイズを拡大します。

###方法2 読み出したいセンサーを指定して個別に通信する。
 センサー読み出しリクエストに入れているコマンド的な値を、センサーのセレクタとして活用します。今回は0x10が来たらカラーセンサの値を送信していますが、0x11が来たらジャイロ…、0x12が来たら加速度…と、いった感じに分けることが考えられます。

#注意すること
 **ホストPCとのシリアル通信をしているとI2C通信がうまくいかないことがあるようです。**ArduinoのUSBケーブルを外すか、シリアルモニタを閉じましょう
できました。しかし、うまくいかない場合は上記のことも試してみてください。

#余談
ArduinoはEV3ポートからの電力で動く。…いいね。
複数センサーの接続についてはまた別記事で書きます。

#最後に
 今回Qiitaを含め、初めて技術系の解説を書きました。
 読みにくい部分はご指摘していただくと幸いです。

#参考文献

5
2
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
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?