jump to navigation

WiiFitバランスボードとProcessingで作る 写真も撮れる体重計

2009-10-19 02:27 Posted by
nase
in : プログラミング
cat_face.jpg

WiiのコントローラがBlueToothを介してPCと接続できることは結構有名な話ですが、これはBalance Boardも同様です。

PCで体重管理をしたいと考えた場合、USB接続対応の体重計を探してみると現在のところ2万円以上の高機能なモデルしか見あたりません。

Wii Balance Boardならば最近発売されたWiiFit Plusがソフト込みで9000円くらいで買えます。(ボードのみの販売はありません)

しかもうちには先日の万華鏡を作る際に購入したWebCamもあるので、同時に写真を撮りためることもできそうです。

というわけで早速WiiFit Plusを購入し、Processingで体重計を作ってみました。

ProcessingでWii Balance Boardの値を取得

WiiRemoteの接続用ライブラリは各言語別にいくつも存在します

WindowsでProcessingから使う場合は、現在のところ以下の二つの選択肢があるようでした。

前者はProcessingだけで完結しますが、残念ながら国内で一般的な東芝製Bluetoothスタックのアダプタに対応していません。

後者はWiiFlashのローカルサーバを立ち上げておく必要はあるものの、東芝製Bluetoothスタックでも動作します。私が購入したBlueToothアダプタは東芝製スタックのものだったので、今回は後者の方法を採用しました。

なお、WiiFlashはバランスボードに対応していますが、Wiimote.pdeはバランスボードに未対応のため少しだけ修正が必要です。

Wiimote.pdeの修正

Wiimote.pdeは元々書籍「WiiRemoteプログラミング」のサンプル用に作られたものです。標準ではWiiリモコンにしか対応していません。

しかし、この書籍の中でバランスボードの仕様が解説されているので、簡単に対応させることができました。

@@ -31,6 +31,14 @@
     y = readFloat();
     z = readFloat();
     extensionType = readByte();
+    if (extensionType == 3) {
+      /**  バランスボードからデータを取得します */
+      bottomLeftKg  = readFloat();
+      bottomRightKg = readFloat();
+      topLeftKg     = readFloat();
+      topRightKg    = readFloat();
+      totalKg       = readFloat();
+    }
   }

   /** 拡張タイプ */
@@ -51,6 +59,8 @@
   float batteryLevel;
   /** 加速度 */
   float x, y, z;
+  /** バランスボード */
+  float bottomLeftKg, bottomRightKg, topLeftKg, topRightKg, totalKg;

   Client client;
   int[] buffer;

Wii Balance Board Weighing Machine

完成した体重計は以下の通り。(値と写真はイメージです)

mangekyo_webcam_s.jpg
Wiiバランスボード体重計

  • 起動後、バランスボードに載って3秒ほど動かずにいると計測値が固定されます。
  • WiiリモコンのAボタンで撮影に移行します。
  • Bボタンで撮影後、Aボタンで終了します。

本体のコードは次のようになっています。なお、実行するにはWiiFlashの他、WebCamの接続とフォントの用意が必要です。

import processing.video.*;

int framerate_val = 60;
PFont ui_font;
String todays_date = "";
int now_step = 1;

Wiimote wiimote;
boolean bb_initialized = false;
float bb_offset_value = 0;
float measured_weight = 0;
boolean weight_lock = false;
int weight_lock_timer = 0;
float temp_weight_memory = 0;

Capture cap;
int camera_width = 320;
int camera_height = 240;
boolean cap_saved = false;

color bg_color = (#F3F0BB);
color fg_color = (#58626E);
color fg_dark = (#8FA690);
color fg_hilight = (#37334C);

void setup() {
  size(camera_width, camera_height);
  stroke(fg_color);
  smooth();
  frameRate(framerate_val);
  ui_font = loadFont("Arial-BlackItalic-48.vlw");
  textFont(ui_font, 24);
  wiimote = new Wiimote(this);
  cap = new Capture(this, camera_width, camera_height);
  todays_date = String.valueOf(year()) + "-" + String.valueOf(month()) + "-" + String.valueOf(day());
}

void draw() {
  wiimote.update();
  switch(now_step){
    case 1 :
      background(bg_color);

      pushMatrix();
      pushStyle();

      translate(width / 2, 0);
      textAlign(CENTER);
      fill(fg_color);
      text("WBB Weighing Machine", 0, 24);

      if (bb_initialized == false && wiimote.totalKg != 0) {
        bb_offset_value = wiimote.totalKg;
        bb_initialized = true;
      }

      if (bb_initialized) {
        textFont(ui_font, 48);

        float temp_weight = float(round((wiimote.totalKg - bb_offset_value) * 10)) / 10;
        if (temp_weight < 1) {
          temp_weight = 0;
        }

        if (weight_lock) {
          fill(fg_hilight);
          text(measured_weight + " Kg", 0, 150);
          fill(fg_color);
          text("----------------", 0, 180);
        } else {
          fill(fg_dark);
          text(temp_weight + " Kg", 0, 150);
          text("----------------", 0, 180);
          fill(fg_color);
          if (temp_weight > 10 && ((temp_weight - temp_weight_memory) < 3)) {
            if (weight_lock_timer < framerate_val){
              weight_lock_timer += 1;
            } else if (weight_lock_timer < framerate_val * 2){
              text("----", 0, 180);
              weight_lock_timer += 1;
            } else if (weight_lock_timer < framerate_val * 3){
              text("--------", 0, 180);
              weight_lock_timer += 1;
            } else {
              text("----------------", 0, 180);
              measured_weight = temp_weight;
              weight_lock = true;
            }
          } else {
            weight_lock_timer = 0;
            fill(fg_dark);
            text("----------------", 0, 180);
          }
        }
        temp_weight_memory = temp_weight;
      }

      popStyle();
      popMatrix();

      if (wiimote.a.pushed && weight_lock) {
        save_weight();
        now_step += 1;
      }
      break;
    case 2 :
      if(cap.available()) {
        cap.read();
        image(cap, 0, 0);
      }
      if (wiimote.b.pushed) {
        save_cap();
      }
      if (wiimote.a.pushed && cap_saved) {
        exit();
      }
      break;
  }
}

void save_weight() {
  String load_lines[] = loadStrings("list.txt");
  String save_lines[] = splice(load_lines, todays_date + " : " + measured_weight, 0);
  saveStrings("list.txt", save_lines);
  println("save:list.txt");
}

void save_cap() {
  save("capture_" + todays_date + ".png");
  delay(500);
  cap_saved = true;
  println("save:capture_" + todays_date + ".png");
}

実際試してみて分かったこと

WiiRemoteをPCとBlueTooth接続するたびにSyncさせるのが面倒です。

Syncは最初の一回で良いものと思っていましたが、実際は毎回必要でした。特にバランスボードは裏の電池カバーを外さなければならないのが辛いです。

参考書籍

WiiRemoteプログラミング WiiRemoteプログラミング

WiiRemoteを使ったプログラミングについて詳しく解説されているほか、作例の紹介なども多くて楽しめます。今回の記事が書けたのは完全にこの本のおかげです。


Wiiフィット プラス(バランスWiiボードセット) Wiiフィット プラス(バランスWiiボードセット)

完全にPC接続目的で購入しましたが、本来のゲームもなかなか面白いです。Amazonは品薄のようなので楽天で探す方が安いかも知れません。


Comments»

1. aki - 2011-1-29

WiiRemoteプログラミングの著者です。

WiiFlash通信クラスのBalanceBoard対応をしてくれたのですね、すばらしい!

ありがとうございます!

2. nase - 2011-1-29

著者の方ですか!コメントありがとうございます。WiiRemoteプログラミング、大変参考になりました。


*Comments and trackbacks will appear after it is approved by the administrator.