Arduinoで16個のLEDをopenFrameworks上から操作できるもの作ってみました。パッドの位置に応じてLEDの光る位置も変わります。
意外と簡単です。
解説に入りますが、まずはこちらを読んでTLC5940について知ってください笑。秋葉原に普通に売ってますので。あと回路も同じです。
おおまかな流れ
ざっくりだとこんな感じ。
- Firmataを使ってArduinoとopenFrameworksとつながるようにしておく。
- openFrameworksのmouseMovedのイベントをofArduino::sendSysExで飛ばす。
- 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に対して制御する、といったことも簡単にできます。ここまでできるといろいろ遊べるので楽しくなってきますね。
続く。(と思う)