Blink(1)日記

長嶋 洋一


2012年12月14日(金)

遂に届いた。 これ である。

つまりこれは、 これ である。 とりあえず電源(USB)を供給してみた。

YouTube

2012年12月16日(日)

まずは ここ にあるサンプルを走らせてみよう。

test0 というサンプルプログラムを解凍して実行すると、以下のようになった。

YouTube

ColorPicker というサンプルプログラムを解凍して実行すると、以下のようになった。

YouTube

ColorOrgan というサンプルプログラムを解凍して実行すると、以下のようになった。

YouTube

次に、 blink1-java-processing-lib というProcessingライブラリをダウンロード・解凍してインストールした。 そして、以下のように「Blink1Test0」というProcessingサンプルプログラムで動いた。

import thingm.blink1.*;

Blink1 blink1;
Random rand;

void setup(){
  frameRate(1);
  blink1 = new Blink1();
  int rc = blink1.open();

  if( rc != 0 ) { 
      println("uh oh, no Blink1 device found");
  }
  rand = new Random();
}

void draw(){
  background(0, 0);
  int r = rand.nextInt() & 0xFF;
  int g = rand.nextInt() & 0xFF;
  int b = rand.nextInt() & 0xFF;
  color c = color(r, g, b);
  fill(c);
  rect(0, 0, width, height);
  blink1.setRGB( r, g, b );  
}

YouTube

また、以下のように「Blink1ColorPicker」というProcessingサンプルプログラムで動いた。

import thingm.blink1.*;

Blink1 blink1;
int sketchWidth  = 400;
int sketchHeight = 240;
int colorPickerX = 10;
int colorPickerY = 20;
int colorPickerWidth  = 255; // this is harccoded in createColorPicker
int colorPickerHeight = 207; // this is hardcoded in createColorPicker
int previewWidth  = 40;
int previewHeight = 40;
int pickX, pickY;
int previewX, previewY;
color backColor = color(226);
color previewColor = color(255,0,255); // start off with a default
PGraphics colorPickerImage;
PFont font;
String statusText="";

void setup(){
  size(sketchWidth, sketchHeight);
  frameRate(20);
  smooth();
  font = loadFont("LucidaSans-12.vlw");
  blink1 = new Blink1();
  int rc = blink1.open();
  if( rc!=0 ) { 
    println("oops no blink1");
    statusText = "no Blink1 found";
  }
  else {
    statusText = "Blink1";
  }
  pickX = colorPickerX + (colorPickerWidth/8)*7; // hack
  pickY = colorPickerY + colorPickerHeight/2;
  previewX = colorPickerX + colorPickerWidth + 30;
  previewY = colorPickerY - 5 ;
  colorPickerImage = createColorPickerImage();
  updateBlink1();
}

void updateBlink1() {
    int r = int(red(previewColor));
    int g = int(green(previewColor));
    int b = int(blue(previewColor));
    int rn = log2lin(r);
    int gn = log2lin(g);
    int bn = log2lin(b);
    println("r,g,b: (lin)"+r+","+g+","+b + " ==> (log)"+rn+","+gn+","+bn);
    //blink1.open();
    blink1.setRGB( r, g, b );
    //blink1.close();
}

void draw(){
  background( backColor );
  drawColorPicker();
  drawPreview( previewX, previewY, previewWidth, previewHeight);
  drawStatus();
}

void mousePressed() {
  // if in colorpicker, we're updating previewColor
  if( mouseX > colorPickerX && mouseX < (colorPickerX+colorPickerWidth) &&
      mouseY > colorPickerY && mouseY < (colorPickerY+colorPickerHeight) ) {
    //previewColor = get(mouseX,mouseY);
    previewColor = colorPickerImage.get( mouseX-colorPickerX,mouseY-colorPickerY);
    pickX = mouseX; pickY = mouseY;
    updateBlink1();
  }
}
// handle drag the same way as a press, kinda lame, yeah
void mouseDragged() {
  mousePressed();
}

void drawStatus() {
  textFont(font, 12);
  text( statusText, colorPickerX+colorPickerWidth+5, height-13);
}

void drawPreview(int x, int y, int w, int h) {
  color c =  previewColor;
  pushMatrix();
  translate(x,y);
  textFont(font, 12);
  text("color", 0,0);
  text("selected", 0,12);
  strokeWeight(1.0);
  stroke(180);
  fill(c);
  rect( 0,15, w,h);
  fill(10);
  textFont(font,8);
  //text( int(red(c))+","+int(green(c))+","+int(blue(c)), 0,h+30);
  text( int(red(c)),   w+2,25);
  text( int(green(c)), w+2,35);
  text( int(blue(c)),  w+2,45);
  popMatrix();
}

// really just render the pre-computed image, created w/ createColorPickerImage
void drawColorPicker() {
  fill(10);
  textFont(font, 12);
  text("pick a color", colorPickerX, colorPickerY-5);
  image(colorPickerImage, colorPickerX, colorPickerY);
  noFill();
  stroke( 200 );
  rect( pickX-4,pickY-4, 8,8 );
  stroke( 40 );
  rect( pickX-3,pickY-3, 8,8 );
}

// just do this plotting once because it's static and 
// sucks up cycles if computed every draw()
PGraphics createColorPickerImage() {
  PGraphics pg;
  pg = createGraphics(255,200+4+4,JAVA2D);
  pg.beginDraw();
  pg.colorMode(HSB,255,100,100);
  pg.stroke(255,0,100);
  pg.fill(255,0,100);
  pg.rect(0,0, 255,4);

  for (int i = 0; i < 255; i++) {
    for (int j = 0; j < 100; j++) {
      pg.stroke( i, j, 100);
      pg.point( i, 4+j);
      pg.stroke( i, 100, 100-j);
      pg.point( i, 4+j+100 );
    }
  }
  pg.stroke(255,0,0);
  pg.fill(255,0,0);
  pg.rect(0,204, 255,4);
  pg.endDraw();
  return pg;
}

int log2lin( int n )  {
  //return  (int)(1.0* (n * 0.707 ));  // 1/sqrt(2)
  return (((1<<(n/32))-1) + ((1<<(n/32))*((n%32)+1)+15)/32);
}
YouTube

blink1-tool-mac というコマンドラインツールを解凍して、ターミナルから実行すると、以下のようになった。

YouTube

2012年12月20日(木)

上記の日曜日はその後、アカペラの皆んなとカラオケハーフマラソン、そしてアカペラの三井さんが出演する合唱団の演奏会などに行ったので、 あまり進展が無かった。 たくさんのBlink(1)があるので、それらを個別に制御する方法について、 質問BBS に書いたところでストップした。

そして週が明けた12/17(月)には。なんと BrightEyes が届いて、翌日の火曜日はその記録に没頭した。 水曜日は講義満載で終わり、木曜日の教授会が終わったところで、 質問BBS にリプライがあった、とのメイルが届き、以下のようなサンプルが加わった。

// Two Blink(1) Dance Party
// Sends a random color to alternating Blink1s
// Processing 2.0 Compatible
// Created December 12 2012
// by Carlyn Maw for ThingM

import thingm.blink1.*;
Blink1 myBlink1;
long t = 0;
int framerateVar = 1;
int speedVar = framerateVar;
int bgVar = 0;
int swatchH = 100;
int swatchW = 100;
int swatchOffSet = 10;
color c = color(0, 0, 0);
color pc = color(0, 0, 0);
int myID = 0;
int blink1Count = 0;
boolean blink1FoundFlag = false;
String pathTest = "";
String serialTest = "";

void setup(){
  size(300, 150 );
  frameRate(framerateVar);
  background(bgVar);
  myBlink1 = new Blink1();
  int b1check = myBlink1.open();
  if (b1check == 0) {
    printBlink1List();
    blink1Count = myBlink1.getCount();
    if (blink1Count < 2) {
      println("too few! too few!");
    }
    else if (blink1Count > 2) {
      println("goodness, what a party!!");
    }
    else {
      println("just the right number");
    }
    blink1FoundFlag = true;
    prepareExitHandler();
  }
}

void draw(){
  t = t + 1;
  if ((t % speedVar) == 0) {
    if (myID == 0) {
      myID = 1;
    }
    else {
      myID = 0;
    }
    background(bgVar);
    pc = c;
    c = generateRandomColor();
    if (blink1FoundFlag) {
      sendColor2Blink1ByID(c, myID);
      //OTHER OPTIONS
      //sendColor2Blink1BySerial(c, serialTest);
      //sendColor2Blink1ByPath(c, pathTest);
      //sendColor2Blink1(c);
    } else {
      println("plug in Blink1s and restart");
    }
  }
  rectMode(CENTER);
  noStroke();
  if (myID == 0) {
    fill(c);
    rect(width/4, (height/2)-swatchOffSet, swatchW, swatchH);
    fill(pc);
    rect(3*width/4, (height/2)-swatchOffSet, swatchW, swatchH);
  }
  else {
    fill(c);
    rect(3*width/4, (height/2)-swatchOffSet, swatchW, swatchH);
    fill(pc);
    rect(width/4, (height/2)-swatchOffSet, swatchW, swatchH);
  }
}

private void prepareExitHandler () {
  Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    public void run () {
      myBlink1.openById(0);
      myBlink1.setRGB(0, 0, 0);
      myBlink1.openById(1);
      myBlink1.setRGB(0, 0, 0);
      myBlink1.close();
    }
  }
  ));
}

void printBlink1List() {
  int howMany = myBlink1.getCount();
  println( "There are " + howMany + " Blink(1)s detected");
  String paths[] = myBlink1.getDevicePaths();
  String serials[] = myBlink1.getDeviceSerials();
  pathTest = paths[0];
  serialTest = serials[0];
  println( "ID" + ": "+ "serials" + " : " + "paths");
  for ( int i = 0;  i < paths.length; i++ ) {
    println( i + ": "+ serials[i] + " : " + paths[i]);
  }
  int myFirmware = myBlink1.getFirmwareVersion();
  println("A Firmware Number: " + myFirmware);
}

void sendColor2Blink1(color y) {
  myBlink1.open();
  myBlink1.setRGB(int(red(y)), int(green(y)), int(blue(y)));
}

void sendColor2Blink1ByID(color y, int i) {
  myBlink1.openById(i);
  myBlink1.setRGB(int(red(y)), int(green(y)), int(blue(y)));
  myBlink1.close();
}

void sendColor2Blink1BySerial(color y, String mySerial) {
  println(mySerial);
  myBlink1.openBySerial(mySerial);
  myBlink1.setRGB(int(red(y)), int(green(y)), int(blue(y)));
}

void sendColor2Blink1ByPath(color y, String myPath) {
  myBlink1.openByPath(myPath);
  myBlink1.setRGB(int(red(y)), int(green(y)), int(blue(y)));
}

color generateRandomColor() {
  int r = int(random(255));
  int g = int(random(255));
  int b = int(random(255));
  color rColor = color(r, g, b);
  return rColor;
}
Processing2.0に対応というのも嬉しいし、これを走らせると、 確かに2個のBlink(1)を交互にランダム発光させた。 そこで、これをあちこち改良して一気に10個まで拡張して、以下のように作ってみた。
import thingm.blink1.*;
Blink1 myBlink1;
color c = color(0, 0, 0);
int myID = 0;
int blink1Count = 0;
boolean blink1FoundFlag = false;
String pathTest = "";
String serialTest = "";

void setup(){
  size(100, 800);
  frameRate(30);
  background(0);
  rectMode(CORNERS);
  noStroke();
  myBlink1 = new Blink1();
  int b1check = myBlink1.open();
  if (b1check == 0) {
    blink1Count = myBlink1.getCount();
    println( "There are " + blink1Count + " Blink(1)s detected");
    String paths[] = myBlink1.getDevicePaths();
    String serials[] = myBlink1.getDeviceSerials();
    pathTest = paths[0];
    serialTest = serials[0];
    println( "ID" + ": "+ "serials" + " : " + "paths");
    for (int i = 0; i < blink1Count; i++) {
      println( i + ": "+ serials[i] + " : " + paths[i]);
    }
    int myFirmware = myBlink1.getFirmwareVersion();
    println("A Firmware Number: " + myFirmware);
    blink1FoundFlag = true;
    prepareExitHandler();
  }
}

void draw(){
  myID = (myID+1) % blink1Count;
  c = generateRandomColor();
  fill(c);
  rect(20, 20+myID*40, 80, 50+myID*40);
  if (blink1FoundFlag) {
    sendColor2Blink1ByID(c, myID);
    //OTHER OPTIONS
    //sendColor2Blink1BySerial(c, serialTest);
    //sendColor2Blink1ByPath(c, pathTest);
    //sendColor2Blink1(c);
  } else {
    println("plug in Blink1s and restart");
  }
}

private void prepareExitHandler () {
  Runtime.getRuntime().addShutdownHook(
    new Thread(
      new Runnable() {
        public void run () {          
           for (int i = 0; i < blink1Count; i++) {
              myBlink1.openById(i);
              myBlink1.setRGB(0, 0, 0);
              myBlink1.close();
            }
        }
      }
    )
  );
}

void sendColor2Blink1(color y) {
  myBlink1.open();
  myBlink1.setRGB(int(red(y)), int(green(y)), int(blue(y)));
}

void sendColor2Blink1ByID(color y, int i) {
  myBlink1.openById(i);
  myBlink1.setRGB(int(red(y)), int(green(y)), int(blue(y)));
  myBlink1.close();
}

void sendColor2Blink1BySerial(color y, String mySerial) {
  println(mySerial);
  myBlink1.openBySerial(mySerial);
  myBlink1.setRGB(int(red(y)), int(green(y)), int(blue(y)));
}

void sendColor2Blink1ByPath(color y, String myPath) {
  myBlink1.openByPath(myPath);
  myBlink1.setRGB(int(red(y)), int(green(y)), int(blue(y)));
}

color generateRandomColor() {
  int r = int(random(255));
  int g = int(random(255));
  int b = int(random(255));
  color rColor = color(r, g, b);
  return rColor;
}
10個のBlink(1)が刺さったUSBハブをMacに繋ぐと、いくつか認識されなかったり、 抜き差しするとゾンビのように総数が10個でなく16個とかに増えるものの(^_^;)、 基本的には以下のようにクリスマス点灯が実現できた。
YouTube
これをYouTubeに上げて「質問BBS」に"Thanks !"と書いて、デザイン学部忘年会のために、ホテルオークラに出発した。