車のコントローラ間に流れるCAN信号を読み取る装置の作り方 (Processing編)

これまで車両に流れるCAN信号を読み取るために必要なハードの作り方と、車両からCAN信号を読み出すARDUINOのプログラムを説明してきました。

今回はARDUINOで読み出したCAN信号をパソコンに表示させるプログラムについて説明します。
プログラムはProcessingという言語で作成します。

Processingを使う理由は、最終的な目的として、CAN信号をリアルタイムでPC画面上にグラフ表示させたいためです。
私はプログラミングに関して初心者ですので、ネットで検索してProcessing+Arduinoの組み合わせでデータのグラフ化をされている例がいくつか見つかりましたので、そちらを参考に作成してみました。

まずは、ARDUINOから送られてきたCAN信号を、IDごとに並べて表示するというシンプルなプログラムです。

〜
import processing.serial.*;
Serial port;
int[] id = new int[2]; //シリアル受信生値 id 2byte分 

//=============ウィンドサイズ設定==================
int WinSize_X = 1000;
int WinSize_Y = 700;

//=============CAN生値表示位置設定==================
float CanValPosi_X = WinSize_X*1/100;
float CanValPosi_Y = WinSize_Y*3/100;
int ValPosi = 0;

//=============ログ表示設定==================
int SetFps = 60;       //FPS設定

//============= シリアル受信生値 CAN値 8byte分 ==================
int[] output_024_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_116_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_119_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_11C_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_120_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_122_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_124_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_130_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_180_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_310_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_314_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_318_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_380_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_3A0_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_3D0_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_3F4_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int[] output_3F5_Val = new int[]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};


//=============通信開始/終了用==================
int Comflag;

void settings() {
  size(WinSize_X, WinSize_Y);
}

void setup() {
  printArray(Serial.list());//シリアル通信に使用するポートを出力
  port = new Serial(this, Serial.list()[1], 115200);//Serial(this, 上で確認したポートを引数に設定, ボーレート)
  background(0, 0, 0);
  PFont font = createFont("MS Gothic",50);
  textFont (font);
  //noSmooth(); //noSmoothを有効にするとアンチエイリアスOFF

  frameRate(SetFps);// FPSは画面の更新頻度であるとともに、Processingの場合、draw()が実行される間隔となる。
}

void draw() {
// 描画エリア設定
  fill(0);
  noStroke();
  rect(0, 0, width, height);
  fill(255);
  textSize(height/40);
  textAlign(LEFT);
  int j = 1;

//CAN値の表示----------------------------------------------------------------------------
  text("024", CanValPosi_X, CanValPosi_Y*j);
   for(int i = 0; i < 8; i++){
     text(hex(output_024_Val[i],2), width*(0.05+(0.03*i)), CanValPosi_Y*j);
   }
     j++;
  text("116", CanValPosi_X, CanValPosi_Y*j);
   for(int i = 0; i < 8; i++){
     text(hex(output_116_Val[i],2), width*(0.05+(0.03*i)), CanValPosi_Y*j);
   }
     j++;
  text("119", CanValPosi_X, CanValPosi_Y*j);
   for(int i = 0; i < 8; i++){
     text(hex(output_119_Val[i],2), width*(0.05+(0.03*i)), CanValPosi_Y*j);
   } 
     j++;
  text("11C", CanValPosi_X, CanValPosi_Y*j);
   for(int i = 0; i < 8; i++){
     text(hex(output_11C_Val[i],2), width*(0.05+(0.03*i)), CanValPosi_Y*j);
   }
     j++;
  text("120", CanValPosi_X, CanValPosi_Y*j);
   for(int i = 0; i < 8; i++){
     text(hex(output_120_Val[i],2), width*(0.05+(0.03*i)), CanValPosi_Y*j);
   }
     j++;
  text("122", CanValPosi_X, CanValPosi_Y*j);
  for(int i = 0; i < 8; i++){
    text(hex(output_122_Val[i],2), width*(0.05+(0.03*i)), CanValPosi_Y*j);
  }
     j++;
  text("124", CanValPosi_X, CanValPosi_Y*j);
  for(int i = 0; i < 8; i++){
    text(hex(output_124_Val[i],2), width*(0.05+(0.03*i)), CanValPosi_Y*j);   
  }
     j++;
  text("130", CanValPosi_X, CanValPosi_Y*j);
  for(int i = 0; i < 8; i++){
    text(hex(output_130_Val[i],2), width*(0.05+(0.03*i)), CanValPosi_Y*j);   
  }
}


void serialEvent(Serial port){           // シリアルポートからデータを受け取ったら
  if (port.available() ==10) {           // 10byte分のデータがそろったら処理開始
          id[0] = port.read();
          id[1] = port.read();
            switch(id[0]*256+id[1]){
              case 0x024:          
                for(int i =0; i<8; i++){ 
                  output_024_Val[i] = port.read();
                }
              break;
              
              case 0x116:          
                for(int i =0; i<8; i++){ 
                  output_116_Val[i] = port.read();
                }
              break;

              case 0x119:          
                for(int i =0; i<8; i++){ 
                  output_119_Val[i] = port.read();
                }
              break;
              
              case 0x11C:          
                for(int i =0; i<8; i++){ 
                  output_11C_Val[i] = port.read();
                }
              break;
              
              case 0x120:          
                for(int i =0; i<8; i++){ 
                  output_120_Val[i] = port.read();
                }
              break;
              
              case 0x122:          
                for(int i =0; i<8; i++){ 
                  output_122_Val[i] = port.read();
                }
              break;
                           
              case 0x124:          
                for(int i =0; i<8; i++){ 
                  output_124_Val[i] = port.read();
                }
              break;
              
              case 0x130:          
                for(int i =0; i<8; i++){ 
                  output_130_Val[i] = port.read();
                }
              break;
              
              case 0x180:          
                for(int i =0; i<8; i++){ 
                  output_180_Val[i] = port.read();
                }
              break;
              
              case 0x310:          
                for(int i =0; i<8; i++){ 
                  output_310_Val[i] = port.read();
                }
              break;
              
              case 0x314:          
                for(int i =0; i<8; i++){ 
                  output_314_Val[i] = port.read();
                }
              break;

              case 0x318:          
                for(int i =0; i<8; i++){ 
                  output_318_Val[i] = port.read();
                }
              break;
              
              case 0x380:          
                for(int i =0; i<8; i++){ 
                  output_380_Val[i] = port.read();
                }
              break;
              
              case 0x3A0:          
                for(int i =0; i<8; i++){ 
                  output_3A0_Val[i] = port.read();
                }
              break; 
              
              case 0x3D0:          
                for(int i =0; i<8; i++){ 
                  output_3D0_Val[i] = port.read();
                }
              break; 
              
              case 0x3F4:          
                for(int i =0; i<8; i++){ 
                  output_3F4_Val[i] = port.read();
                }
              break;

              case 0x3F5:          
                for(int i =0; i<8; i++){ 
                  output_3F5_Val[i] = port.read();
                }
              break;
                            
              default:
                 while (port.available()>0)port.read();  // 受信対象外のidの場合はバッファを空にする。データのビットズレに対してもリセットがかかることになる。
              break;  
            }
         
    } else if (port.available() > 10){
        while (port.available()>0)port.read();  // バッファが10byteを超えた場合はバッファを空にし、エラーメッセージを表示
        println("エラー:バッファが10byteを超えました");
        
      }
}


//マウスクリックで通信開始⇔終了
void mouseClicked(){
  port.clear();                  //バッファ領域を空にする 

  if (Comflag == 0xFF){
    Comflag = 0X01;
  }else{
    Comflag = 0xFF;
  }
  
  port.write(Comflag);
}
〜

プログラムの中で、以下の部分は、プログラムを実行するとProcessingのメイン画面の下に、COMポートの番号リストが表示されますので、ARDUINOを接続しているCOMポートの番号を確認して、Serial.list()内に記入してください。
私の環境では”1”だったので、Serial.list()[1]としています。
printArray(Serial.list()); //シリアル通信に使用するポートを出力
port = new Serial(this, Serial.list()[1], 115200); //Serial(this, 上で確認したポートを引数に設定, ボーレート)


CANの読み出しをスタートさせるには、Processingの画面上をクリックする必要があります。(もう一度クリックすると一時停止)
読み出した値は下記のように表示されます。

f:id:SX99:20220324225854p:plain
CAN取得データ


このプログラムはあくまで、私の車(スズキ SX4)向けのプログラムなので、CAN idの部分は車に合わせて変更する必要があります。

では、車両に流れているCAN idをどうやって知るか、、、?
それを次回説明します。