Processingでマイコンから送られてきたデータをリアルタイム表示する


動作確認日2019/06/26

開発環境:
・Processing3.5.3
・M5STACK GRAY + Arduino IDE

目的:
マイコンのデータをシリアル通信で受け取って動作を確認する際に、一々グラフ描画するまでが面倒くさい。
→手っ取り早くデータの動きだけ見れるプログラムを作ろう!


動かしている図

Processingで構造体を受け取る方法が分からなかったので、シリアル通信を2Byte読んでint型を作るrecv_int関数と4Byte読んでlong型を作るrecv_long関数をそれぞれ実装しました。
マイコンで読み取る時間はmsとかが多いのでlong型、マイコンで読み取るデータとしてはAD値を想定してint型をそれぞれ受け取るサンプルブログラムが下になります。ちなみにProcessingではint型が4Byte扱えたはずなのでrecv_long関数なんて名前ですがint型で受け取ってます。(Arduino側がlong型で送ってたからこんな名前をつけたはず)

serial_graph.pde
import processing.serial.*;

Serial port;

IntList time_list;
IntList data_list;

int GRAPH_HEIGHT = 400;
int GRAPH_WIDTH = 1000;
int GRAPH_OFFSETX = 40;
int GRAPH_OFFSETY = 50;

void setup () {
    size (1200, 600);
    port = new Serial (this, Serial.list ()[1], 115200);   

    time_list = new IntList();
    data_list = new IntList();
}

void draw_graph(){
  strokeWeight(4); 
  stroke(255, 89, 23);

  for(int i=1; i< time_list.size(); i++){
    PVector v1 = new PVector(GRAPH_OFFSETX + time_list.get(i-1) - time_list.min(), (height - GRAPH_OFFSETY) - data_list.get(i-1));
    PVector v2 = new PVector(GRAPH_OFFSETX + time_list.get(i) - time_list.min(), (height - GRAPH_OFFSETY) - data_list.get(i));

    line( v1.x, v1.y, v2.x, v2.y);
  }
}

void draw_graph_outline(){
  strokeWeight(5); 
  stroke(240, 240, 240);

  PVector v1 = new PVector(GRAPH_OFFSETX, (height - GRAPH_OFFSETY));
  PVector v2 = new PVector(GRAPH_OFFSETX, (height - GRAPH_OFFSETY) - GRAPH_HEIGHT);
  PVector v3 = new PVector(GRAPH_OFFSETX + GRAPH_WIDTH, (height - GRAPH_OFFSETY));

  line( v1.x, v1.y, v2.x, v2.y);
  line( v1.x, v1.y, v3.x, v3.y);
}

void draw () {
  background(45, 45, 45);
  draw_graph_outline();
  draw_graph();
}

int recv_int(Serial port){
  int data1 = port.read();
  int data2 = port.read();  
  int recv_data = data1 << 8 | data2 << 0 ;

  return recv_data;
}

int recv_long(Serial port){
  int data1 = port.read();
  int data2 = port.read();
  int data3 = port.read();
  int data4 = port.read();
  int recv_data = data1 << 24 | data2 << 16 | data3 << 8 | data4 << 0 ;

  return recv_data;
}

void serialEvent (Serial port) {
  if ( port.available() >= 7 ) {
    if(port.read() == 'H'){
      int recv_time = recv_long(port);
      int recv_data = recv_int(port);

      int time = (int)(recv_time / 10);
      int data = (int)map(recv_data, 0, 4095, 0, GRAPH_HEIGHT);

      time_list.append(time);
      data_list.append(data);

      if(time - time_list.min() > GRAPH_WIDTH){
        time_list.remove(0);
        data_list.remove(0);
      }

      print(recv_time);
      print(",");
      println(recv_data);
    }
  }
}

Processingの動作確認用に今回はM5STACKを使って、テキトーに正弦波をシリアル通信に垂れ流すプログラムを構築しました。
M5STACK側は構造体で送れたような気もしますが、頭を使いたくなかったのでProcessingと同じくwrite_int関数だのwrite_long関数だの実装してポチポチ送信しています。

test_serial.ino
#include<M5Stack.h>

int timer_begin = 0;
float pi = 3.14159265;
int counter = 0;

void setup() {
  M5.begin();

  Serial.begin(115200);

  timer_begin = millis();
}

void write_long(long val, uint8_t d[4]){
  uint32_t _data = val;
  d[0] = (uint8_t)((_data >> 24) & 0xFF);
  d[1] = (uint8_t)((_data >> 16) & 0xFF);
  d[2] = (uint8_t)((_data >>  8) & 0xFF);
  d[3] = (uint8_t)((_data >>  0) & 0xFF);

  Serial.write(d[0]);
  Serial.write(d[1]);
  Serial.write(d[2]);
  Serial.write(d[3]);

}

void write_int(int val, uint8_t d[2]){
  uint32_t _data = val;
  d[0] = (uint8_t)((_data >> 8) & 0xFF);
  d[1] = (uint8_t)((_data >> 0) & 0xFF);

  Serial.write(d[0]);
  Serial.write(d[1]);

}

void loop() {
  int send_data = 500 * sin(pi * counter / 100) + 2000;
  counter++;
  long timer = millis();
  Serial.write('H');

  byte data1[4];  
  byte data2[2];

  write_long(timer-timer_begin, data1);
  write_int(send_data, data2);

  //uint8_t data0[6] = {data2[0], data2[1], data1[0], data1[1], data1[2], data1[3]};
  //Serial.write(data0,6);
  delay(10);
}