micro:bit x Processing!
[ Updated at 2020/05/24 ]
micro:bit

はじめに

micro:bitで操作をすると、シリアル通信でProcessingとやりとりをするシステムを作る記事です。具体的には、マイクロビットでボタンが押されたら、Processingの画面でオブジェクトが表示されるようにしていきます。

全体のシステム

以下のように、MakeCodeというMicrosoftのソフトで、micro:bitのコーディングを行い.hexファイル形式で、micro:bit本体にインストールします。そして、micro:bitからポートを用いたシリアル通信でProcessingに信号を送ります。

この記事では、信号を受信したProcessingでデジタルアートを描きたいと思います。

micro_proc_figure

手順

1. micro:bitの準備

まずは、micro:bitとマイクロUSBケーブルを用意し、パソコンと接続します。

setup

2. micro:bitのコーディング

2-a. 新しいプロジェクトの作成

まずMakeCodeのmicro:bitのウェブサイト(こちら)にアクセスをして、新しいプロジェクトを作成しましょう。

new_project

2-a. 接続テスト

まずボタンを押したら、micro:bitの画面に文字が表示されるか実験してみましょう。

以下のようにプログラミングをしてましょう。

それぞれ基本と入力のブロックを使っています。

first_pgm

プログラムができたら、ダウンロードをして、.hexファイルを出力しましょう。

出力ができたら、以下のように.hexファイルをMICROBITへドラッグ&ドロップして、コピーします。

download_hex

この状態で実行して、以下のように文字が表示されれば、接続テスト完了です。

2-b. シリアル通信のプログラム

続いて、シリアル通信のプログラムを追加していきます。以下のようんブロックを追加してみましょう。シリアル通信ブロックは、高度なブロック > シリアル通信の中にあります。

final_pgm

これで再びダウンロードをして、MICROBITへコピーしたら、microbitのプログラミングは完了です。

3. Processingのコーディング

3-a. Processingのダウンロード

Processingのダウンロードサイト(こちら)よりダウンロードしましょう。

processing_download

ダウンロードができたら、アプリケーションを起動しましょう。

new_proc

3-b. シリアル通信受信のコーディング

起動ができたら、以下のようにコーディングを行なっていきます。下半分は円を綺麗に生成するためのスクリプトなため、完全に理解はできなくても大丈夫です。

3つの重要な箇所だけソースコードの後に解説します。

import processing.serial.*;
Serial microbit;

int _num = 10;
Circle[] _circleArr = {};   

void setup(){
  size(640, 480);
  background(255);
  smooth();
  strokeWeight(1);
  fill(150, 50);
  String portName = Serial.list()[1];
  println(portName);
  microbit = new Serial(this, portName, 115200);
  microbit.clear();
  microbit.bufferUntil(10);
}

void draw(){
  background(255);
  for (int i=0; i<_circleArr.length; i++) {
    Circle thisCirc = _circleArr[i];
    thisCirc.updateMe();
  }
}

void serialEvent(Serial microbit){
  String str = microbit.readStringUntil('\n');
  str = trim(str);
  int[] str_split = int(split(str, '\n'));
  println(str_split[0]);

  
  if (str_split[0] == 0){
    println("red");
    drawCircles("red");
  }else if(str_split[0] == 1){
    println("blue");
    drawCircles("blue");
  }else if(str_split[0] == 2){
    println("end processing.");
    exit();
  }else{
    println("else");
    drawCircles("else");
  }
}

void drawCircles(String col_type) {
  for (int i=0; i<_num; i++) { 
    Circle thisCirc = new Circle(col_type);
    thisCirc.drawMe();
    _circleArr = (Circle[])append(_circleArr, thisCirc);
  }
}

class Circle {

  // properties
  float x, y;
  float radius; 
  color linecol, fillcol; 
  float alph;
  float xmove, ymove;
  
  Circle (String col_type) {
    x = random(width);
    y = random(height);
    radius = random(100) + 10; 
    if (col_type == "red"){
      linecol = color(random(150,255), random(100,150), random(100,150));
      fillcol = color(random(150,255), random(100,150), random(100,150));
    }else if(col_type == "blue"){
      linecol = color(random(100,150), random(100,150), random(150,255));
      fillcol = color(random(100,150), random(100,150), random(150,255));
    }else{
      linecol = color(random(100,255), random(100,255), random(100,255));
      fillcol = color(random(100,255), random(100,255), random(100,255));
    }
    alph = random(255);
    xmove = random(10) - 5;   
    ymove = random(10) - 5;  
  }

  void drawMe() { 
    noStroke(); 
    fill(fillcol, alph);
    ellipse(x, y, radius*2, radius*2);
    stroke(linecol, 150);
    noFill();
    ellipse(x, y, 10, 10);
  }
  
  void updateMe() {
    x += xmove;
    y += ymove;
    if (x > (width+radius)) { x = 0 - radius; }
    if (x < (0-radius)) { x = width+radius; }
    if (y > (height+radius)) { y = 0 - radius; }
    if (y < (0-radius)) { y = height+radius; }
    
   for (int i=0; i<_circleArr.length; i++) {
      Circle otherCirc = _circleArr[i];
      if (otherCirc != this) {  
        float dis = dist(x, y, otherCirc.x, otherCirc.y); 
        float overlap = dis - radius - otherCirc.radius; 
        if (overlap < 0) { 
          float midx, midy;
          if (x < otherCirc.x) {
            midx = x + (otherCirc.x - x)/2;
          } else {
            midx = otherCirc.x + (x - otherCirc.x)/2;
          }
          if (y < otherCirc.y) {
            midy = y + (otherCirc.y - y)/2;
          } else {
            midy = otherCirc.y + (y - otherCirc.y)/2;
          }
          stroke(0, 100);
          noFill();
          overlap *= -1; 
          ellipse(midx, midy, overlap, overlap);
        }
      }
    }
    drawMe();
  }
}

3-c. 重要箇所の解説

シリアル通信の設定

冒頭のコードで、processingのシリアル通信パッケージをインポートして、micorbitというシリアル型の変数を作成しています。

import processing.serial.*;
Serial microbit;
  1. シリアル通信用のポートの指定

シリアル通信を行うためのポートをSerial.list()から探し、その中で1番目(javaの配列は0始まりなので、正確には2番目)のポートを変数に保持しています。115200は、baudRate(ボーレート)と言われる値(1秒間に何回、デジタルデータを変復調できるかを示す値)を表します。microbit.bufferUntilの10は、line feed(改行)を表します。

void setup(){
  size(640, 480);
  background(255);
  smooth();
  strokeWeight(1);
  fill(150, 50);
  // Find Port
  String portName = Serial.list()[1];
  println(portName);
  microbit = new Serial(this, portName, 115200);
  microbit.clear();
  microbit.bufferUntil(10);
}
  1. 通信の値に応じた処理の実行

改行コードを受信するまでシリアル通信を読み込み、読み込まれたらそのコードをコンソールに表示します。そして、そのコードが0か1か2で場合分けをしています。

0の時(Aが押された時)は、redとコンソールに表示し、暖色系の円を生成します。

1の時(Bが押された時)は、blueと表示し、寒色系の円を生成します。

2の時(A+Bが押された時)は、end processingと表示し、processingを終了します。

void serialEvent(Serial microbit){
  String str = microbit.readStringUntil('\n');
  str = trim(str);
  int[] str_split = int(split(str, '\n'));
  println(str_split[0]);

  
  if (str_split[0] == 0){
    println("red");
    drawCircles("red");
  }else if(str_split[0] == 1){
    println("blue");
    drawCircles("blue");
  }else if(str_split[0] == 2){
    println("end processing.");
    exit();
  }else{
    println("else");
    drawCircles("else");
  }
}

4. ポートの確認

A. Macの場合

terminalでls -l /dev/tty.*と打ち、ポートを表示しましょう。

この場合、Bluetooth-Incoming-Portではなく、usbmodem14102の方になります。

$ ls -l /dev/tty.*
crw-rw-rw- 0,0 root 30 5 19:40 /dev/tty.Bluetooth-Incoming-Port
crw-rw-rw- 0,8 root 30 5 19:43 /dev/tty.usbmodem14102

B. Windowsの場合

少しだけ環境作りが複雑なため、こちらの記事を参考にしてみてください。

シリアル通信【Windows編】

5. 実機テスト

Processingを実行して、microbitでAボタンやBボタンを押してみましょう。

以下のように円が生成されれば、シリアル通信成功です。

その他

サンプルコードもこちらに掲載しておきました。

umi-mori