質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.50%

ArduinoでI2C通信とBluetoothシリアル通信を両立させる方法について

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 2,810

zum1309

score 13

数か月前から電子工作を始めました。
ArduinoとPCのBluetooth通信について行き詰ってしまったため教えていただけると幸いです。

最終目標

ArduinoにI2C通信のセンサを取り付け、センサの値をBluetoothチップのシリアル通信でPCで受信する

構成

・Arduino側
Arduino UNO
Bluetoothチップ RN-42使用 Bluetooth評価キット
6軸センサ MPU6050使用

・Arduino配線
MPU6050とArduinoの配線
VCCと5V
GNDとGND
SCLとSCL
SDAとSDA
で配線

Bluetoothチップの配線はこちらを参考にさせていただきました。

・PC側
ubuntu16.04でbluemanを使用して接続
BluetoothドングルはLBT-UAN05C2/Nを使用

問題点

Bluetooth通信の参考にさせていただいたこちらのサイト通りに試したところ正常にハードウェアシリアルで通信が可能でした。
ArduinoIDEのシリアルモニタで仮想シリアルポートrfcommを指定してArduinoからのシリアル送信を受信することが出来ます。

ですが、MPU6050からI2C通信でデータを取得しようとするとシリアルモニタで何も受信できなくなってしまいます。
MPU6050とのI2C通信はArduinoUNO付属のUSBケーブルを用いてのシリアル通信では成功しています。

以下、問題のスケッチです。

// MPU-6050 Accelerometer + Gyro

// I2CにアクセスするためにWireライブラリを使用
#include <Wire.h>

// レジスタアドレス
#define MPU6050_ACCEL_XOUT_H 0x3B  // R
#define MPU6050_WHO_AM_I     0x75  // R
#define MPU6050_PWR_MGMT_1   0x6B  // R/W
#define MPU6050_I2C_ADDRESS  0x68
#define MPU6050_GYRO 131.0
#define MPU6050_G 16384.0

double acc_x = 0;
double acc_y = 0;
double acc_z = 0;

double gyro_x = 0;
double gyro_y = 0;
double gyro_z = 0;


// 構造体定義
typedef union accel_t_gyro_union {
  struct {
    uint8_t x_accel_h;
    uint8_t x_accel_l;
    uint8_t y_accel_h;
    uint8_t y_accel_l;
    uint8_t z_accel_h;
    uint8_t z_accel_l;
    uint8_t t_h;
    uint8_t t_l;
    uint8_t x_gyro_h;
    uint8_t x_gyro_l;
    uint8_t y_gyro_h;
    uint8_t y_gyro_l;
    uint8_t z_gyro_h;
    uint8_t z_gyro_l;
  }
  reg;
  struct {
    int16_t x_accel;
    int16_t y_accel;
    int16_t z_accel;
    int16_t temperature;
    int16_t x_gyro;
    int16_t y_gyro;
    int16_t z_gyro;
  }
  value;
};

// デバイス初期化時に実行される
void setup() {
  int error;
  uint8_t c;

  Wire.begin();

  // ボーレートを115200bpsにセット
  Serial.begin(115200);
  //Serial.print("MPU-6050 ");

  // 初回の読み出し
  error = MPU6050_read(MPU6050_WHO_AM_I, &c, 1);
  Serial.print("WHO_AM_I : ");
  Serial.print(c, HEX);
  Serial.print(", error = ");
  Serial.println(error, DEC);

  // 動作モードの読み出し
  error = MPU6050_read(MPU6050_PWR_MGMT_1, &c, 1);
  Serial.print("PWR_MGMT_1 : ");
  Serial.print(c, HEX);
  Serial.print(", error = ");
  Serial.println(error, DEC);

  // MPU6050動作開始
  MPU6050_write_reg(MPU6050_PWR_MGMT_1, 0);
}

void loop() {
  int error;
  float dT;
  accel_t_gyro_union accel_t_gyro;

  // 加速度、角速度の読み出し
  // accel_t_gyroは読み出した値を保存する構造体、その後ろの引数は取り出すバイト数
  error = MPU6050_read(MPU6050_ACCEL_XOUT_H, (uint8_t *)&accel_t_gyro, sizeof(accel_t_gyro));

  // I2C通信正常終了
  if(error == 0){
    // 取得できるデータはビッグエンディアンなので上位バイトと下位バイトの入れ替え(AVRはリトルエンディアン)
    uint8_t swap;
    #define SWAP(x,y) swap = x; x = y; y = swap
    SWAP (accel_t_gyro.reg.x_accel_h, accel_t_gyro.reg.x_accel_l);
    SWAP (accel_t_gyro.reg.y_accel_h, accel_t_gyro.reg.y_accel_l);
    SWAP (accel_t_gyro.reg.z_accel_h, accel_t_gyro.reg.z_accel_l);
    SWAP (accel_t_gyro.reg.t_h, accel_t_gyro.reg.t_l);
    SWAP (accel_t_gyro.reg.x_gyro_h, accel_t_gyro.reg.x_gyro_l);
    SWAP (accel_t_gyro.reg.y_gyro_h, accel_t_gyro.reg.y_gyro_l);
    SWAP (accel_t_gyro.reg.z_gyro_h, accel_t_gyro.reg.z_gyro_l);

    // 取得した加速度値
    acc_x += accel_t_gyro.value.x_accel ;
    acc_y += accel_t_gyro.value.y_accel ;
    acc_z += accel_t_gyro.value.z_accel ;

    // 取得した角速度値
    gyro_x += accel_t_gyro.value.x_gyro ;
    gyro_y += accel_t_gyro.value.y_gyro ;
    gyro_z += accel_t_gyro.value.z_gyro ;

    Serial.print("\t");
    Serial.print(error, DEC);
    Serial.print(",");
    Serial.print(acc_x);
    Serial.print(",");
    Serial.print(acc_y);
    Serial.print(",");
    Serial.print(acc_z);
    Serial.print(",");
    Serial.print(gyro_x);
    Serial.print(",");
    Serial.print(gyro_y);
    Serial.print(",");
    Serial.print(gyro_z);
    Serial.println("");  

    delay(100);

  }
}

// MPU6050_read
int MPU6050_read(int start, uint8_t *buffer, int size) {
  int i, n, error;
  Wire.beginTransmission(MPU6050_I2C_ADDRESS);
  n = Wire.write(start);
  if (n != 1) {
    return (-10);
  }
  n = Wire.endTransmission(false);// hold the I2C-bus
  if (n != 0) {
    return (n);
  }
  // Third parameter is true: relase I2C-bus after data is read.
  Wire.requestFrom(MPU6050_I2C_ADDRESS, size, true);
  i = 0;
  while (Wire.available() && i < size) {
    buffer[i++] = Wire.read();
  }
  if ( i != size) {
    return (-11);
  }
  return (0); // return : no error
}

// MPU6050_write
int MPU6050_write(int start, const uint8_t *pData, int size) {
  int n, error;
  Wire.beginTransmission(MPU6050_I2C_ADDRESS);
  n = Wire.write(start);// write the start address
  if (n != 1) {
    return (-20);
  }
  n = Wire.write(pData, size);// write data bytes
  if (n != size) {
    return (-21);
  }
  error = Wire.endTransmission(true); // release the I2C-bus
  if (error != 0) {
    return (error);
  }

  return (0);// return : no error
}

// MPU6050_write_reg
int MPU6050_write_reg(int reg, uint8_t data) {
  int error;
  error = MPU6050_write(reg, &data, 1);
  //Serial.print("error = ");
  //Serial.println(error);
  return (error);
};

ソースを部分的に変えておかしくなる箇所を特定していったところ
Wire.beginTransmission(MPU6050_I2C_ADDRESS);
の命令があるとBluetoothを経由してシリアル通信ができなくなっているようです。
MPU6050がつながっているSDAとSCLについて調べたところ、それぞれシリアルデータピンとシリアルクロックピンとの事でしたので、もしかしてこのピンで通信しているとTX、RXピンを用いたシリアル通信はできないのでしょうか?
何かわかることがあればよろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

ざっと見ただけで詳細までは目を通していませんが、、
シリアル通信、IIC通信はビジーループで実装してますよね?

#プログラムで受信/送信ステータスをチェックし、データ可の状態に
#なればデータの読み出し/書き込みを行う

こういう組み方をすると、たとえばシリアル通信をしている間はIICの通信は出来ません。
また、IIC通信をしている間はシリアル通信はできなくなります
これをどうにかするには、シリアル通信、IIC通信のどちらか、あるいは両方を、割り込みで駆動する方式に変える必要があります

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/01 15:09

    回答ありがとうございます。
    >問題なのは、その関数を呼び出したら、そのサイズ分の通信が終わるまで戻ってこない、ということです。
    >当然その間は他の処理はできません(もちろんシリアル通信の処理も)

    そちらは理解しています。
    なので
    while (Wire.available() && i < size) {
    buffer[i++] = Wire.read();
    }
    の部分でI2C通信が終わるまでループで待ち、I2C通信が終わってから
    Serial.print("");
    でシリアル送信を行っており、I2C通信とシリアル通信を同時に行っていないというのが私の認識です。
    私の認識に間違いがあったら教えていただけると助かります。

    キャンセル

  • 2018/03/01 15:23

    ああ、IIC通信を行ったらそれ以降シリアル通信が出来ないってことですか。
    本来、シリアルとIICは同時に使えるはずのものですんで、プログラムに何かバグがあることになりますね

    提示されたコードだけではどこにバグがあるかというのは判別できないので、
    IIC通信を行う前と後で、CPUのシリアルユニットのレジスタの設定を比較して設定が変わっていないかチェックすること、シリアル通信の関数のどの箇所でおかしくなるか、などを追いかけていってバグの箇所を特定していく必要があります

    キャンセル

  • 2018/03/01 15:54

    回答頂いた内容で調べてみたいと思います。
    全く同じスケッチでタイプB→タイプAのUSBケーブルの有線接続では問題なく通信ができたのでプログラムに異常はないと思い込んでいました。

    回答ありがとうございます。

    キャンセル

checkベストアンサー

0

"PCで"シリアル受信できないのですね。読み間違えていました。

直感的にはエラー時にendTransmissionを呼び出していないので、2回連続でbeginTransmissionを呼び出すケースがあるので、ここで刺さりそうです。(エラー時に呼び出す必要があるかは確認していません)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/01 15:52

    ArduinoUNO付属のタイプB→タイプAケーブルで接続した場合は問題なくセンサの値をシリアル通信で取得できます。
    なのでプログラムのバグではないと思っていましたが、これは有線の場合はRX、TXピンを使用しないで通信を行う為でしょうか?

    プログラム自体にバグがないか、いらないコードを少しずつ削ったりして試してみます。
    回答ありがとうございます。

    キャンセル

  • 2018/03/01 16:16

    > これは有線の場合はRX、TXピンを使用しないで通信を行う為でしょうか?

    理由はわかりませんが、有線の場合(USBシリアル)をつかう場合RX, TXピンを使用しないと言うのは間違いです。USBシリアルの素子にRX, TXが接続されています。なので参考にされているページでも

    > ハードウェアシリアルを使用する場合には、プログラム書き込み時には、Rx、Txの結線を取り除き、実行時に再度結線します。
    > ソフトウェアシリアルを使用する場合には、実行時の結線のままプログラム書き込みができます。

    という注意書きがされています。

    キャンセル

0

結論から書きますと、Wire.writeをした際にエラーが発生し、そのエラーコードが4(その他のエラー)の場合にそれ以降プログラムが動かなくなるようでした。

その他のエラーが発生した理由としましては、センサのチップの配線の長さが1.5mほどあったため、電流が放電されて足りなくなったのではないかと思われます。
Bluetoothの通信を試していた時はモバイルバッテリーからの給電のため、PCからの給電と比べると電力不足なのかもしれません。
ひとまずセンサチップへの配線を10cmほどの短いものに変えたところ、Wire.writeでエラーコード4のエラーが発生しなくなりました。

エラーが発生した際の処理なども考えなくてはなりませんが、ひとまず当初の目的であったI2Cでセンサの値を取り、Bluetoothでシリアル送信することは達成できましたので解決とさせていただきます。

知識の浅い私に丁寧に回答して頂いたお二人に、改めて感謝申し上げます。
ありがとうございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる