Mifare cardでの参加者表示アプリ作成

これを使って、USBシリアル経由でProcessingでカード情報を受け取り、UIDと参加者の対応表からカードをタッチした参加者は出席とSQLiteのテーブルに書き込み、デスクトップ画面に表示をするようなアプリです。

M5Stickc plusとProcessingのコードは以下のリンクから、

https://github.com/chateight/rfid

注意点はserialEvent()は割り込み処理なので、この中で共通リソースを変更すると、他で使用中のコードからは意図しない(不規則な)変更に見えるので、この中では共通リソースの書き込み処理を行ってはいけない。具体的にはdbTableUpdate()の呼び出しをserialEvent()中で行っていたら、draw()からの呼び出し中にString[] membersの配列を破壊してパニックが発生しました。dbTableUpdate()の実行中に配列を書き換えなければ問題ないのでそれで対応。

10人分の登録をしたらこんな画面になります。

 

admin

Processingで使えるシリアルポートをスキャンする

M5StackからPCへの接続方法にはいろいろありますが、無線でなくても良い、つまりUSBで電源供給しているならばUSB経由のシリアルで接続するのが妥当そうです。

このときにシリアルポートを指定しないといけないですが、ポートをスキャンするサンプルがあったので使ってみました。

参考は、

https://garchiving.com/processing-auto-serial-port/

になります。

PC側はProcessingを使っていますが、12行目から21行目までがポート検出の該当スクリプトです。ポート指定方法としてnew Serialの第二引数に”/dev/tty.usbserial-54260147311″のように直接指定する方法もありますが、環境が変われば改めて設定しなければならないでしょうから、自動検出にはメリットがあります。

import processing.serial.*;

Serial myPort;

int x;
String[] comPort = myPort.list();

void setup(){
  // 画面サイズ
  size(256, 256);

  int num = comPort.length;
  for (int i=0; i<num; i++) {
    try {
      myPort = new Serial(this, comPort[i], 115200);
      println(comPort[i]);
    }
    catch(Exception e) {
      continue;
    }
  }
}

void draw(){
  // 背景色を白に設定
  background(255);

  // XY座標を(x,100)に設定し、
  // 幅50、高さ50の円を描画
  ellipse(x,100,50,50);
}

void serialEvent(Serial p){
  //変数xにシリアル通信で読み込んだ値を代入
  x = p.read();
  println(x);
}

M5Stack側は極めてシンプルなスクリプトで1秒ごとに異なる値を送るだけ。Processing側ではcircleの座標として扱っています。

#include

void setup(){
  Serial.begin(115200);
}

void loop(){
  Serial.write(100);
  delay(1000);
  Serial.write(200);
  delay(1000);
}

 

admin

ProcessingでM5StackのPoseをモデルに反映する

引き続いて、M5Stackのpitch/roll/yaw情報をUDPで送って、Processing上のモデルに反映してみます。

M5Stackからはpitch/roll/yaw情報をカンマ区切りのテキストで送ります。

このテキストをProcessingで受信して、直方体のモデルのposeに反映します。正負が逆になったx/z軸は値を反転。座標系の取り方でどうにでもなるような気もしますが。

Processingのソースは見ての通り単純です。最初3Dの描画できなくて調べたらベータ版では動かないようで4.1.1にアップデートしたらきちんと動作しました。

codeは、

https://github.com/chateight/processing/blob/main/sketch_221220b.pde

/*
read m5stack udp data(IMU data ; pitch, roll, yaw) and move the imaginary object

*/
import hypermedia.net.*;

UDP udp;
final String IP = "0.0.0.0";

PFont myFont;

float[] pry = new float[3];      // pitch, roll, yaw store work


void setup() {
  
  udp = new UDP(this, 3002);
  udp.listen( true );

  size(500,500,P3D);
  frameRate(30);
  loop();
}

void draw() {
  background(0);
 
  translate(width/2, height/2);
  rotateX(radians(-pry[1]));
  rotateY(radians(pry[0]));
  rotateZ(radians(-pry[2]));
  box(150, 150, 50);
}

void receive( byte[] data, String ip, int port ) {
  String message = new String( data );
  println( "received : \""+message+"\" from "+ip+" on port "+port );
  
  String[][] matchedTexts = matchAll(message, "[0-9|-]+.[0-9]+");

  if (matchedTexts != null) {
    int index = 0;
    for (String[] matchedText : matchedTexts){
      pry[index] = float(matchedText[0]);
      index++;
    }
  }
  println(pry[0], pry[1], pry[2]);    // pitch, roll, yaw
  
}

動かして見たのは、今のYouTube画面で参照ください。yawは時間経過とともにずれていくのでいくらか補正はしていますが、十分じゃ無いですね。

 

admin

Processingで受信データをグラフ化する

M5Stackからのデータを視覚化するのに、一番手軽に使えそうなツールだと思うProcessingでM5StackからUDPで送られてくる温度データを時系列でグラフ化してみました。Bluetoothで送るのは接続不安定でストレスになるから、簡単に送れるUDPを使います。電池寿命とか関係ないし、

<M5Stackから送られるメッセージ形式>

日時(秒まで)と温度データ

<Processingのコード>

メッセージから、温度部分を抜き出して表示しています。Processingの画像処理は専用のAPIになっています。正規化処理もJavaとは別物ですが、

receive()メソッドは、UDPリッスンで受信データがあれば起動されるでしょうが、drawの読み出し(val)と書き込みが競合すれば値は不定になる可能性はありそうですが、視覚的なものなので排他制御をするまでもないでしょう。

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             

import hypermedia.net.*;
import controlP5.*;

UDP udp;
final String IP = "0.0.0.0";

ControlP5 cp5;
Chart chart_tmp;
final int NUM_GRAPH_DATA = 200;

PFont myFont;

float val;

void setup() {

  size(650, 700);
  myFont = createFont("Arial", 30); 
  
  udp = new UDP(this, 3002);
  udp.listen( true );

  frameRate(10);
  cp5 = new ControlP5(this);
  
  chart_tmp = cp5.addChart("tmp sensor");
  chart_tmp.setView(Chart.LINE)                             
            .setRange(20, 22)                         
            .setSize(600, 200)                               
            .setPosition(10, 250)                           
            .setColorCaptionLabel(color(0,0,255))          
            .setStrokeWeight(1.5)                            
            .getColor().setBackground(color(224, 224, 224))  
            ;
  chart_tmp.addDataSet("temp");
  chart_tmp.setData("temp", new float[NUM_GRAPH_DATA]);
  chart_tmp.setColors("temp", color(0, 0, 192), color(255,255,128));

}

void draw() {
  
  background(255);
  fill(0);

  chart_tmp.unshift("temp", val);
}

void receive( byte[] data, String ip, int port ) {
  String message = new String( data );
  println( "received : \""+message+"\" from "+ip+" on port "+port );
  
  String[] matchedTexts = match(message, "[0-9|-]*.[0-9]*$");

  if (matchedTexts != null) {
    println(matchedTexts[0]);
    val = float(matchedTexts[0]);
  }

}

動画はこちらを参照ください、グラフ下端が20℃、上端が22℃で範囲を拡大して見やすくしています。

processing – SD 480p

 

admin