ArduinoとProcessingで複数データのシリアル通信

Pocket

 ArduinoとProcessingで複数のデータを双方向でシリアル通信する方法(スケッチ(ソース))の一例を紹介したいと思います。

 

 ArduinoIDEにあるシリアルモニターでもいいのですが、物足りないときにProcessing使ってます。いろいろとビジュアル的にモニターできて便利なのでよく使ってます。

 

 ▼モニター例▼

 動画では、Arduinoで6軸センサー(MPU6050)の値を取得・加工して、加速度値やジャイロ値などの複数データをシリアル送信してます。その情報をProcessing(パソコン)側で受信して、リアルタイムで可視化してます。

 

 今回はこの内容をそのまま説明すると結構なボリュームなので、6軸センサー値をProcessingでテキスト表示するという内容で説明したいと思います。

 

Arduino側スケッチ(ソース)

 スケッチを抜粋して説明します。スケッチ全体は記事後半に記載してます。

 

 まず、通信の概要としては、

➀Processing側から「定期的」に送信要求(ダミーデータを送信)する。
➁Arduino側で送信要求を受け取ったらセンサー値を送信。
➂Processinng側でデータ受信し処理

 このような感じで行います。

 

 通信部分のスケッチ(ソース)を抜粋して説明していきます。ここでは6軸センサーの値取得する部分は割愛します。

 ArduinoからMPU6050の使い方はこちらで紹介してます。

 

 ▼setup内▼

  Serial.begin(57600);

 通信速度を指定してシリアル通信を開始します。ここで設定する通信ボーレート(bps)はProcessing側と合わせておきます。シリアル通信に関するセットアップ内容はこの設定のみです。

 

 ▼受信データの確認▼

  if (Serial.available() == 1) {
    byte inBuf[1];
    Serial.readBytes(inBuf, 1);
    if (inBuf[0] == 's') {
      //ここに送信処理を記述
    }
    else {
      while (Serial.available() > 0)Serial.read();
    }
  }
  if (Serial.available() > 1) {
    while (Serial.available() > 0)Serial.read();
  }

 ここからはloop内に記載していきます。ループ処理の中でProcessinngからの送信要求を確認しています。

 

 後述しますが、Processinng側では定期的に「’s’」をArduinoへ送信しています。(文字はなんでもいいです。ただの確認用です。)

 

 Arduino側で「’s’」の受け取りを確認してます。処理内容としては

  • 1byteのデータを受信していて、その値が「’s’」なら送信処理する。
    ⇒「’s’」でないなら受信バッファをクリアにする。
  • 1byte以上のデータを受信していたら受信バッファを空にする。
  • 何も受信していないときはスルー。

 このような処理をしてます。今回、Processing側からは送信要求用の「’s’」のみを送付してますがここで、ほかの情報を加えれば双方で通信も可能かと思います。

 

▼送信処理部分▼

      byte outBuf[14];
      outBuf[0]  = 's';                        // 1
      outBuf[1]  = (int16_t)(gx * 100) >> 8;   // 2
      outBuf[2]  = (int16_t)(gx * 100) & 0xFF; // 3
      outBuf[3]  = (int16_t)(gy * 100) >> 8;   // 4
      outBuf[4]  = (int16_t)(gy * 100) & 0xFF; // 5
      outBuf[5]  = (int16_t)(gz * 100) >> 8;   // 6
      outBuf[6]  = (int16_t)(gz * 100) & 0xFF; // 7
      outBuf[7]  = (int16_t)(ax * 100) >> 8;   // 8
      outBuf[8]  = (int16_t)(ax * 100) & 0xFF; // 9
      outBuf[9]  = (int16_t)(ay * 100) >> 8;   // 10
      outBuf[10] = (int16_t)(ay * 100) & 0xFF; // 11
      outBuf[11] = (int16_t)(az * 100) >> 8;   // 12
      outBuf[12] = (int16_t)(az * 100) & 0xFF; // 13
      outBuf[13]  = 'e';                       // 14
      Serial.write(outBuf, 14);

 送信処理を行っている部分です。今回は6軸センサーの値(加速度:3軸分、ジャイロ:3軸分)の6値のデータ送信する例です。

 

 Arduinoのシリアル通信ではSerial.printで文字列として送信する場合が多い??かと思いますが、今回は、「数値」でデータ授受したい、Serial.writeを使ってbyte単位で情報を送信します。

 

<スポンサーリンク>

 処理としては、センサー値を100倍してから16bitの整数情報にして、上位(8bit)、下位(8bit)をbyte型の変数(配列)に順番に格納してます▼

      outBuf[1]  = (int16_t)(gx * 100) >> 8; 
      outBuf[2]  = (int16_t)(gx * 100) & 0xFF;

 2byte分でひとつの値を格納してます。これを加速度3軸分、ジャイロ3軸分行ってます。また送信データの最初と最後にチェック用として「’s’」と「’e’」も一緒に送信してます。100倍しているのは小数点以下の情報も送りたいためです。

 

 ですので送信情報は6軸センサー値で12byte分、チェック用で2byte分、合計で14byteとなります。

 

 最後に14byte分の情報をSerial.write()で纏めて送信処理してます。

 

 Arduinoでの送信処理部分はこれだけです。

 

Processing側ソース

 Processing側の送受信処理の抜粋です。ソース全体は記事後半に記載してます。

 

▼セットアップ▼

import processing.serial.*;
Serial myPort;

void setup() {
  myPort = new Serial(this, "COM8", 57600);
}

最初にシリアル通信の定義をします。Aruduinoが接続されているポートと通信ボーレートを定義します。通信ボーレートはArduino側と合わせます。

 

▼送受信処理▼

  if (millis()-serialTimer > 50) {
    serialTimer = millis();
  //ここに受信処理と送信処理を記述
  }

 Processing側ではおおよそ50msごとに送受信処理をしています。それほど正確ではないですが、millis()でカウントしてだいたい50msごとの処理になるようにしてます。

 

▼送信(要求)処理▼

    byte[] outBuf = new byte[1];
    outBuf[0]     = 's';
    myPort.write(outBuf);

 Arduinoで「’s’」(送信要求)を定期的に送付してます。ここでは1byteの情報だけですが、他の情報も送信したければここで加えておきます。

 

▼受信データの確認▼

    if (myPort.available() == 14) {
      myPort.readBytes(inBuf);
      if (inBuf[0]=='s'&&inBuf[13]=='e') {
        //ここに受信処理を記述
      } else {
        while (myPort.available()>0)myPort.read();
        println("missMatch");
      }
    } else if (myPort.available() > 14) {
      while (myPort.available()>0)myPort.read();
      println("overflowe");
    }

 処理としては

  • 14byte分のデータが届いているか確認。受信データの最初と最後が「’s’」「’e’」であれば受信処理。
    ⇒「’s’」「’e’」でなければ受信バッファをクリア。
  • 14byte以上のデータが届いていたら受信バッファをクリア。
  • 14byte分のデータが届いてなければスルー

といった流れです。

 

 ▼受信処理▼

        gx = (inBuf[1]<<8)+(inBuf[2]&0xff);
        gy = (inBuf[3]<<8)+(inBuf[4]&0xff);
        gz = (inBuf[5]<<8)+(inBuf[6]&0xff);
        ax = (inBuf[7]<<8)+(inBuf[8]&0xff);
        ay = (inBuf[9]<<8)+(inBuf[10]&0xff);
        az = (inBuf[11]<<8)+(inBuf[12]&0xff);
        gx/=100.0;
        gy/=100.0;
        gz/=100.0;
        ax/=100.0;
        ay/=100.0;
        az/=100.0;

 Arduino側の送信処理とは逆で、受信した情報を、上位bit、下位bitで順に結合していきます。それで結合した値を100で除します。Processing側の送受信部はこれだけです。

 

 結構まわりくどいやり方で紹介しましたが、有線(USBシリアル)であればここまで記述しなくても十分通信できるかと・・。以前、無線(シリアル通信の無線モジュール)で同様のことをやろとしたときに、結構データの取りこぼしがあったので、送受信のbyteカウントして、今回のようにきっちり処理してみました。ここまですれば無線でもしっかり通信できました。

 

ソース全体

▼Arduino側▼

#include <Wire.h>
int16_t axRaw, ayRaw, azRaw, gxRaw, gyRaw, gzRaw, temperature;
float  ax, ay, az, gx, gy, gz;
void setup() {
  Serial.begin(57600);
  Wire.begin();
  TWBR = 12;
  Wire.beginTransmission(0x68);
  Wire.write(0x6B);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.beginTransmission(0x68);
  Wire.write(0x1C);
  Wire.write(0x10);
  Wire.endTransmission();
  Wire.beginTransmission(0x68);
  Wire.write(0x1B);
  Wire.write(0x08);
  Wire.endTransmission();
  Wire.beginTransmission(0x68);
  Wire.write(0x1A);
  Wire.write(0x05);
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(0x68);
  Wire.write(0x3B);
  Wire.endTransmission();
  Wire.requestFrom(0x68, 14);
  while (Wire.available() < 14);
  axRaw = Wire.read() << 8 | Wire.read();
  ayRaw = Wire.read() << 8 | Wire.read();
  azRaw = Wire.read() << 8 | Wire.read();
  temperature = Wire.read() << 8 | Wire.read();
  gxRaw = Wire.read() << 8 | Wire.read();
  gyRaw = Wire.read() << 8 | Wire.read();
  gzRaw = Wire.read() << 8 | Wire.read();

  ax = axRaw / 4096.0;
  ay = ayRaw / 4096.0;
  az = azRaw / 4096.0;
  gx = gxRaw / 65.5;
  gy = gyRaw / 65.5;
  gz = gzRaw / 65.5;

  if (Serial.available() == 1) {
    byte inBuf[1];
    Serial.readBytes(inBuf, 1);
    if (inBuf[0] == 's') {
      byte outBuf[14];
      outBuf[0]  = 's';                        // 1
      outBuf[1]  = (int16_t)(gx * 100) >> 8;   // 2
      outBuf[2]  = (int16_t)(gx * 100) & 0xFF; // 3
      outBuf[3]  = (int16_t)(gy * 100) >> 8;   // 4
      outBuf[4]  = (int16_t)(gy * 100) & 0xFF; // 5
      outBuf[5]  = (int16_t)(gz * 100) >> 8;   // 6
      outBuf[6]  = (int16_t)(gz * 100) & 0xFF; // 7
      outBuf[7]  = (int16_t)(ax * 100) >> 8;   // 8
      outBuf[8]  = (int16_t)(ax * 100) & 0xFF; // 9
      outBuf[9]  = (int16_t)(ay * 100) >> 8;   // 10
      outBuf[10] = (int16_t)(ay * 100) & 0xFF; // 11
      outBuf[11] = (int16_t)(az * 100) >> 8;   // 12
      outBuf[12] = (int16_t)(az * 100) & 0xFF; // 13
      outBuf[13]  = 'e';                       // 14
      Serial.write(outBuf, 14);
    }
    else {
      while (Serial.available() > 0)Serial.read();
    }
  }
  if (Serial.available() > 1) {
    while (Serial.available() > 0)Serial.read();
  }
}

 

▼Processing側▼

import processing.serial.*;
float gx, gy, gz, ax, ay, az;
int serialTimer;
Serial myPort;

void setup() {
  size(500, 500);
  myPort = new Serial(this, "COM8", 57600);
}

void draw() {
  if (millis()-serialTimer > 50) {
    serialTimer = millis();
    byte[] inBuf = new byte[14];
    println(myPort.available());
    if (myPort.available() == 14) {
      myPort.readBytes(inBuf);
      if (inBuf[0]=='s'&&inBuf[13]=='e') {
        gx = (inBuf[1]<<8)+(inBuf[2]&0xff);
        gy = (inBuf[3]<<8)+(inBuf[4]&0xff);
        gz = (inBuf[5]<<8)+(inBuf[6]&0xff);
        ax = (inBuf[7]<<8)+(inBuf[8]&0xff);
        ay = (inBuf[9]<<8)+(inBuf[10]&0xff);
        az = (inBuf[11]<<8)+(inBuf[12]&0xff);
        gx/=100.0;
        gy/=100.0;
        gz/=100.0;
        ax/=100.0;
        ay/=100.0;
        az/=100.0;
      } else {
        while (myPort.available()>0)myPort.read();
        println("missMatch");
      }
    } else if (myPort.available() > 14) {
      while (myPort.available()>0)myPort.read();
      println("overflowe");
    }

    byte[] outBuf = new byte[1];
    outBuf[0]     = 's';
    myPort.write(outBuf);
  }

  background(0);
  fill(240);
  textSize(24);
  text(nf(gx, 0, 2), 50, 50);
  text(nf(gy, 0, 2), 50, 80);
  text(nf(gz, 0, 2), 50, 110);
  text(nf(ax, 0, 2), 50, 140);
  text(nf(ay, 0, 2), 50, 170);
  text(nf(az, 0, 2), 50, 200);
}
Pocket

<スポンサーリンク>


この投稿へのコメント

コメントはありません。

コメントを残す

メールアドレスが公開されることはありません。

この投稿へのトラックバック

トラックバックはありません。

トラックバック URL