The jonki

呼ばれて飛び出てじょじょじょじょーんき

【Arduino】【openFrameworks】16個のLEDを簡単制御

Arduinoで16個のLEDをopenFrameworks上から操作できるもの作ってみました。パッドの位置に応じてLEDの光る位置も変わります。



意外と簡単です。
解説に入りますが、まずはこちらを読んでTLC5940について知ってください笑。秋葉原に普通に売ってますので。あと回路も同じです。

おおまかな流れ

ざっくりだとこんな感じ。

  1. Firmataを使ってArduinoとopenFrameworksとつながるようにしておく。
  2. openFrameworksのmouseMovedのイベントをofArduino::sendSysExで飛ばす。
  3. TLC5940ライブラリを使ってArduinoを制御する。

プログラム

AppTypeDef.h

Firmata.hを見ると0x00から0x0Fはユーザー定義で使えることになってる。
でも7番まで埋まっていたので、とりあえず0x08をLEDの制御できるものにとりあえず割り当てた。
// 0x00-0x0F reserved for user-defined commands
AppTypeDef.hはArduinoのプログラムでも利用する。

#define MAX_LED_NUM 16
#define MAX_BRIGHTNESS 4095 // 0-4095 
#define J_SHOW_LED	0x08
testApp.cpp

Arduinoを正しいポートでつなげて、マウスのイベントを送るだけ。ardはofArduinoです。送れるデータは1バイトのため、最初4ビットをLED番号に、最後4ビットを輝度に割り当てました。これで0〜F番目の0〜F段階の輝度を設定するようなコマンドが1バイトで投げられます。とりあえず今回は輝度を最大で送ります。

void testApp::setup()
{
    //
    // 中略
    //
    ard.connect("/dev/tty.usbserial-xxxxxxxx", 57600);
}

void testApp::mouseMoved(int x, int y ){
    std::vector<unsigned char> vec;
    int num = (int)(MAX_LED_NUM * x/ofGetWidth());
    int br = 0x0f;
    int val = (num << 4) | br;
    vec.push_back(val);
    ard.sendSysEx(J_SHOW_LED, vec);
}
JLight.ino (Arduinoプログラム)

sysexCallbackの解説します。Firmata::sendValueAsTwo7bitBytesを見ればわかるけど、わざわざ1バイトの文字を7ビット、1ビットに分け、それぞれ1バイトのお部屋に収納、つまり2バイトで送っています。まぁ他の制御用とぶつからないよう8ビット目を潰すためです。
このあたりこちら様のサイトでも解説あります。そのため面倒ですが、Arduino側で2バイトに別れた7ビットと1ビットのデータをint型の2バイトのお部屋に確保する必要があります。シフト演算苦手!って人も絵を書いてみればすぐわかるかと思います。
showLedでは前半4ビットに割り当てていたLED番号と後半4ビットに割り当てていた輝度を取り出して、LEDの番号と明るさをTLCライブラリで叩いています。
適当につくったので適宜アサーションとか入れてください。あくまで導通レベルで書いたコードです。

/* This sketch accepts strings and raw sysex messages and echos them back.
 *
 * This example code is in the public domain.
 */
#include <Firmata.h>
#include "Tlc5940.h"
#include "AppTypeDef.h"

void showLed(int val) {
  int num = val >> 4;
  int br  =  MAX_BRIGHTNESS * (val & 0x0f) / 0x0f;
  Tlc.set(num, br);
}

void sysexCallback(byte command, byte argc, byte*argv)
{
  int vals[MAX_LED_NUM];
  int num = 0x00;
  int ct = 0;
  for(int i=0; i<(int)argc; i+=2) {
    num = 0x00;
    num = num | argv[i]; 
    num = num | (argv[i+1] << 7);
    vals[ct] = num;
    ++ct;
    Serial.println(num);
  }
  
  Tlc.clear();
  switch((int)command) {
    case  J_SHOW_LED:
      for(int i=0; i<ct; i++) {
        showLed(vals[i]);
      }
      break;
  }
  Tlc.update();
}

void setup()
{
  Firmata.setFirmwareVersion(0, 1);
//  Firmata.attach(STRING_DATA, stringCallback);
  Firmata.attach(START_SYSEX, sysexCallback);
  Firmata.begin(57600);

  Tlc.init();
}

void loop()
{
  while(Firmata.available()) {
    Firmata.processInput();
  }
}


sendSysExはvectorのデータをとれるので今回の例では1個の値しか送ってないけど、同時に複数のLEDに対して制御する、といったことも簡単にできます。ここまでできるといろいろ遊べるので楽しくなってきますね。

続く。(と思う)