8x8 LED Matrix animation using Arduino (and Processing sketch) [電子工作]
8x8 LED Matrixでアニメーションを編集するプロジェクトの紹介です。
1. 前書き
8X8 のLED Matrixは、Arduinoのプロジェクトでよく使用される機器です。
Arduinodoでは標準のMatrixライブラリを使うことで、簡単にLED Matrixを制御することが出来ます。
このライブラリは、MAXIM社のMAX7219シリアルLEDドライバの使用を前提にしていますが、このICを使わないライブラリを公開している方もいらっしゃいます。(参照:Arduinoで遊ぼう - Direct8x8ライブラリ)
今回はMAX7219を使って、8x8 LED Matrixを制御します。
2. プロジェクトの概要
上記の通り、8x8 LEDの利用自体は簡単に実現できますが、いつも同じアニメーションでは面白くありません。
そこでPCで自由にパターンをエディットして、Arduinoに送信する設計としました。
プロジェクトのイメージは下図の通りです。(あくまでイメージなので、回路図の参考にはしないでください)
使用環境
・Windows XP SP2
・Processing 1.0.3
・Arduino 0015
Arduino
・Arduino Duemilanove (ATMega168バージョン)
3. PC側の実装(Processing)
ProcessingにはGUI部品が存在しないので、Exampleを参考にして「Buttonクラス」を作成し、それを継承することで、各ボタン機能をインプリしています。
最初は1フレーム分しか画面に表示されていませんが、次フレームに移動する時に、最後のフレームなら自動的にフレームを追加するようにしています。
画面上ではフレームの追加・削除、パターンのコピー・ペースト、PC上でのアニメーションの開始・停止等が実施できます。
作成したパターンはPCに保存することが出来ます。今のバージョンではファイル名は固定なので、複数のパターンを保存したい場合は、PC上でファイルをリネームするなどの対応が必要になります。
Sendボタンを押すとSerialインターフェース経由でArduinoにパターンを送信します。
データは先頭1バイトが総フレーム数が、2バイト目以降が各フレームのデータになります。
フレームのデータは8x8の一列を1バイトで送信しているので、1フレームしかないアニメーションの場合(動かないのでアニメーションとは呼べないかもしれませんが)1+8で合計9バイトを送信します。
例)
尚、ProcessingではByte型は符号付きなので、128以上の数字はprintすると負数(128 → -128、129→-127)で表示されます。
なお、SerialポートはProcessingからはポート一覧のX番目という指定になるので、環境によって166行目当たりを見直す必要があります。
ボタンアイコンはLGPLで公開されている、Crystal Projectのものを利用しています。
【Processingコード】
4. Arduino側の実装
Matrixライブラリで8x8 LED Matrixをコントロールします(SPI制御)。また Analog PINに可変抵抗を接続して、アニメーションの速度をコントロール出来るようにしています。
setup()ではEEPROMからアニメーションのフレームを呼び出して内部に展開しています。
またSerialのデータを受信した場合は、データの読み込み&EEPROMへの書き出し&内部への展開、を行います。
EEPROMを使うことで、一旦受信したアニメーションパターンは、一旦電源OFFしても保存されます。ただしそのため、保存出来るパターン数には限界があります。
ATMega168の場合はSRAM 1K、EEPROM 512 Byteなので、今の実装では最大48フレームとし、それ以上の長さのデータを受信しても読み飛ばすようにしています。
また回路については下記ページをほぼそのまま使用しています。異なるのはANALOG PIN2 に可変抵抗を接続していることだけです。(もちろん5VとGNDも)
・建築発明工作ゼミ2008:Arduino マトリクスLED2/MAX7219
【Arduinoコード】
5. 感想とTODO
ArduinoのSerialで長い文字を送受信するのは意外とプログラミングが面倒で手間取りました。今は9600bpsで問題なく動作していますが、速度を上げると取りこぼしが発生するかもしれません。
ArduinoとProcessingはIDEは似ていますが、言語的には別物と考えおいたほうが良いと思います。実際同時にプログラミングしていると混乱します。(Arduinoで継承が使えないとか、Processingでdefine文が使えない等々...)
今後は、先日Make Tokyo Meeting 03で購入したXBeeでワイヤレス化にチャレンジしてみたいと考えています。スクロールアニメーション版も作っておきたい。
【デモ動画】
1. 前書き
8X8 のLED Matrixは、Arduinoのプロジェクトでよく使用される機器です。
Arduinodoでは標準のMatrixライブラリを使うことで、簡単にLED Matrixを制御することが出来ます。
このライブラリは、MAXIM社のMAX7219シリアルLEDドライバの使用を前提にしていますが、このICを使わないライブラリを公開している方もいらっしゃいます。(参照:Arduinoで遊ぼう - Direct8x8ライブラリ)
今回はMAX7219を使って、8x8 LED Matrixを制御します。
2. プロジェクトの概要
上記の通り、8x8 LEDの利用自体は簡単に実現できますが、いつも同じアニメーションでは面白くありません。
そこでPCで自由にパターンをエディットして、Arduinoに送信する設計としました。
プロジェクトのイメージは下図の通りです。(あくまでイメージなので、回路図の参考にはしないでください)
- Processingで作成したUIで8x8のアニメーションのパターンを編集します。
- 編集中のパターンはボタン一発で、USB/Serial接続されたArduinoに転送されます。
- Arduinoでは転送されたデータをEEPROMに書き込みます。
- EEPROMからデータを読み込み直し、アニメーションを表示します。
使用環境
・Windows XP SP2
・Processing 1.0.3
・Arduino 0015
Arduino
・Arduino Duemilanove (ATMega168バージョン)
3. PC側の実装(Processing)
ProcessingにはGUI部品が存在しないので、Exampleを参考にして「Buttonクラス」を作成し、それを継承することで、各ボタン機能をインプリしています。
最初は1フレーム分しか画面に表示されていませんが、次フレームに移動する時に、最後のフレームなら自動的にフレームを追加するようにしています。
画面上ではフレームの追加・削除、パターンのコピー・ペースト、PC上でのアニメーションの開始・停止等が実施できます。
作成したパターンはPCに保存することが出来ます。今のバージョンではファイル名は固定なので、複数のパターンを保存したい場合は、PC上でファイルをリネームするなどの対応が必要になります。
Sendボタンを押すとSerialインターフェース経由でArduinoにパターンを送信します。
データは先頭1バイトが総フレーム数が、2バイト目以降が各フレームのデータになります。
フレームのデータは8x8の一列を1バイトで送信しているので、1フレームしかないアニメーションの場合(動かないのでアニメーションとは呼べないかもしれませんが)1+8で合計9バイトを送信します。
例)
【図】パターンと各バイトとの関係 ○○○○○○○○ --> 00000000 -->0x00 ○○○○○○○○ --> 00000000 -->0x00 ○○●○○●○○ --> 00100100 -->0x24 ○○○○○○○○ --> 00000000 -->0x00 ○○○○○○○○ --> 00000000 -->0x00 ○●○○○○●○ --> 01000010 -->0x42 ○○●●●●○○ --> 00111100 -->0x3c ○○○○○○○○ --> 00000000 -->0x00
尚、ProcessingではByte型は符号付きなので、128以上の数字はprintすると負数(128 → -128、129→-127)で表示されます。
なお、SerialポートはProcessingからはポート一覧のX番目という指定になるので、環境によって166行目当たりを見直す必要があります。
ボタンアイコンはLGPLで公開されている、Crystal Projectのものを利用しています。
【Processingコード】
import processing.serial.*; // FOR SIZE int SIZEX=500; int SIZEY=300; // FOR LED MATRIX int TOPX = 30; int TOPY = 30; int LENX=30*8; int LENY=30*8; int WIDTHX=30; int WIDTHY=30; int BACKGROUND=51; int OFFCOLOR=100; int MAXFRAME=64; int MINFRAME=0; // FOR PREV/NEXT BUTTON int FRAME_BUTTON_X=3; int FRAME_BUTTON_Y=96; int FRAME_BUTTON_W=19; int FRAME_BUTTON_H=100; // FOR ACTION BUTTON int BUTTON_START_X= 310; int BUTTON_START_Y= 40; int BUTTON_ROW_HEIGHT= 60; int BUTTON_WIDTH=24; int BUTTON_HEIGHT=36; int BUTTON_SPACE = 10; String DATA_FILE = &"savedat.csv&"; int pX, pY; color c1,c2; PFont label; LedMatrix[] led; LedMatrix c_led; LedMatrix buf_led; PrevButton btn_prev; NextButton btn_next; RunButton btn_run; StopButton btn_stop; ClearButton btn_clear; EraseButton btn_erase; SendButton btn_send; CopyButton btn_copy; PasteButton btn_paste; SaveButton btn_save; LoadButton btn_load; InsertButton btn_insert; boolean c_sw = false; boolean run_sw = false; int p_frame=0; // Current frame Number int max_frame=0; // Max frame Numnber PImage mask; Serial port; // ========================================== // Setup ( ) // ========================================== void setup(){ int i,j; background(BACKGROUND); size(SIZEX,SIZEY); // image files from // http://www.everaldo.com/crystal/ PImage img_run=loadImage(&"player_play.png&"); PImage img_stop=loadImage(&"agt_pause-queue.png&"); PImage img_clear=loadImage(&"editclear.png&"); PImage img_erase=loadImage(&"edit_remove.png&"); PImage img_send=loadImage(&"exec.png&"); PImage img_copy=loadImage(&"editcopy.png&"); PImage img_paste=loadImage(&"editpaste.png&"); PImage img_save=loadImage(&"filesave.png&"); PImage img_load=loadImage(&"fileopen.png&"); PImage img_insert=loadImage(&"edit_add.png&"); // 24x24 png mask = loadImage(&"blank.png&"); // draw LED Matrix stroke(255); fill(0); rect(TOPX,TOPY,LENX,LENY); stroke(195); for (i=1 ; i < 8 ; i++){ line(TOPX,TOPY+WIDTHY*i,TOPX+WIDTHX*8,TOPY+WIDTHY*i); line(TOPX+WIDTHX*i,TOPY,TOPX+WIDTHX*i,TOPY+WIDTHY*8); } // create instance of LED pattern data led= new LedMatrix[MAXFRAME]; led[0] = new LedMatrix(); c_led = led[0]; buf_led = new LedMatrix(); label = createFont(&"Arial&",14); // Set Prev/Next Frame Buttons c1 = color(20,20,20); btn_prev = new PrevButton (FRAME_BUTTON_X, FRAME_BUTTON_Y, FRAME_BUTTON_W, FRAME_BUTTON_H, &"<<&"); btn_next = new NextButton (FRAME_BUTTON_X+TOPX+LENX, FRAME_BUTTON_Y, FRAME_BUTTON_W, FRAME_BUTTON_H, &">>&"); // create & display Action Buttons i = BUTTON_START_X; j = BUTTON_START_Y; btn_run = new RunButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT , img_run, &"Run&"); i += BUTTON_WIDTH + BUTTON_SPACE; btn_stop = new StopButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT, img_stop, &"Pause&"); i += BUTTON_WIDTH + BUTTON_SPACE; i = BUTTON_START_X; j = BUTTON_START_Y + BUTTON_ROW_HEIGHT; btn_copy = new CopyButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT , img_copy, &"Copy&"); i += BUTTON_WIDTH + BUTTON_SPACE; btn_paste = new PasteButton(i , j , BUTTON_WIDTH, BUTTON_HEIGHT, img_paste, &"Paste&"); i += BUTTON_WIDTH + BUTTON_SPACE; btn_clear = new ClearButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT, img_clear, &"Clear&"); i += BUTTON_WIDTH + BUTTON_SPACE; btn_insert = new InsertButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT, img_insert, &"Add&"); i += BUTTON_WIDTH + BUTTON_SPACE; btn_erase = new EraseButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT, img_erase, &"Del&"); i = BUTTON_START_X; j = BUTTON_START_Y + BUTTON_ROW_HEIGHT + BUTTON_ROW_HEIGHT; btn_load = new LoadButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT, img_load, &"Load&"); i += BUTTON_WIDTH + BUTTON_SPACE; btn_save = new SaveButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT, img_save, &"Save&"); i += BUTTON_WIDTH + BUTTON_SPACE; i = BUTTON_START_X; j = BUTTON_START_Y + BUTTON_ROW_HEIGHT + BUTTON_ROW_HEIGHT + BUTTON_ROW_HEIGHT; btn_send = new SendButton (i , j , BUTTON_WIDTH, BUTTON_HEIGHT, img_send, &"Upload&"); i += BUTTON_WIDTH + BUTTON_SPACE; println(Serial.list()); // Assuming that 3rd serial port is connected to Arduino port = new Serial(this, Serial.list()[2], 9600); } // ========================================== // draw ( ) // ========================================== void draw(){ int dsp_p_frame, dsp_max_frame; if (run_sw) { frameRate(5); p_frame ++ ; if ( p_frame > max_frame) { p_frame =0; } // println (&"in run mode, p_frame is &" + p_frame); c_led = led[p_frame]; } else { frameRate(60); } noStroke(); fill(BACKGROUND); rect(0,0,250,23); fill(255); smooth(); dsp_p_frame = p_frame +1 ; dsp_max_frame = max_frame +1 ; textFont(label); text (&"Flame = &" + dsp_p_frame + &"/&" + dsp_max_frame ,160,10); drawAll(); } // ========================================== // mousePressed ( ) // ========================================== void mousePressed(){ if (!run_sw) { if (mouseX > TOPX && mouseX < TOPX+LENX && mouseY > TOPY && mouseY < TOPY + LENY){ pX =floor (map( mouseX-TOPX,0,LENX,0,8)); pY= floor (map( mouseY-TOPY,0,LENY,0,8)); // debug // println(&" Mouse : &" + mouseX + &" &" + mouseY + &" Mapped :&" + pX + &" &" +pY); // invert off/on c_led.setStats(pX, pY, !c_led.getStats(pX, pY)); if ( c_led.getStats(pX, pY) ) // is ture { switch_led(pX, pY, true); } else { switch_led(pX, pY, false); } } else if ( btn_prev.pressed() ) { btn_prev.setButton(100); if ( !c_sw) { btn_prev.doAction(); } } else if ( btn_next.pressed() ) { btn_next.setButton(100); if ( !c_sw) { btn_next.doAction(); } } else if ( btn_run.pressed() ) { btn_run.setButton(100); if ( !c_sw) { btn_run.doAction(); } } else if ( btn_clear.pressed() ) { btn_clear.setButton(100); if ( !c_sw) { btn_clear.doAction(); } } else if ( btn_erase.pressed() ) { btn_erase.setButton(100); if ( !c_sw) { btn_erase.doAction(); } } else if ( btn_send.pressed() ) { btn_send.setButton(100); if ( !c_sw) { btn_send.doAction(); } } else if ( btn_copy.pressed() ) { btn_copy.setButton(100); if ( !c_sw) { btn_copy.doAction(); } } else if ( btn_paste.pressed() ) { btn_paste.setButton(100); if ( !c_sw) { btn_paste.doAction(); } } else if ( btn_save.pressed() ) { btn_save.setButton(100); if ( !c_sw) { btn_save.doAction(); } } else if ( btn_insert.pressed() ) { btn_insert.setButton(100); if ( !c_sw) { btn_insert.doAction(); } } else if ( btn_load.pressed() ) { btn_load.setButton(100); if ( !c_sw) { btn_load.doAction(); } } // to make it sure that doAction() never called in next loop. (When mouse being pressed). c_sw = true ; } else { // if animation is running, only stop button is available. if ( btn_stop.pressed() ) { btn_stop.setButton(100); btn_stop.doAction(); } } } // ========================================== // mouseReleased ( ) // ========================================== void mouseReleased(){ btn_prev.setButton(0); btn_next.setButton(0); btn_run.setButton(0); btn_stop.setButton(0); btn_clear.setButton(0); btn_erase.setButton(0); btn_send.setButton(0); btn_copy.setButton(0); btn_paste.setButton(0); btn_insert.setButton(0); btn_save.setButton(0); btn_load.setButton(0); c_sw = false ; } // ========================================== // functions // ========================================== // drawAll void drawAll() { int i,j; for (i=0; i<8 ;i++){ for (j=0; j<8; j++){ switch_led(i, j, c_led.getStats(i,j)); } } } // switch_led void switch_led(int pX, int pY, boolean on){ noStroke(); if (on) { fill(255,0,0); } else { fill(OFFCOLOR); } ellipse(TOPX + WIDTHX*pX + WIDTHX/2, TOPY + WIDTHY * pY +WIDTHY/2, 20,20); } // ------------------------------------------------------ // L e d M a t r ix // ------------------------------------------------------ class LedMatrix { boolean[][] stats = new boolean[8][8]; // Constructor LedMatrix(){ init(); } // init void init(){ int i,j; for (i=0 ; i < 8 ; i++){ for (j=0 ; j < 8 ; j++){ stats[i][j]=false; } } } // getStats() boolean getStats(int x, int y){ return stats[x][y]; } // setStats() void setStats(int x, int y, boolean val){ stats[x][y] = val; } // on() for future use void on(int x, int y){ stats[x][y] = true; } // off() for future use void off(int x, int y){ stats[x][y] = false; } // getString() for DEBUG String getStrings(){ int x,y; String buf = &"&"; for (y=0 ; y<8 ; y++){ String r=&"&"; for (x=0 ; x < 8 ; x++){ r = r + ( getStats(x,y) ? &"1&" : &"0&" ); } buf = buf + unbinary(r) + &",&"; } return buf; } // getDump() Byte[] getDump(){ int x,y; String r; Byte[] b= new Byte[8]; for (y=0 ; y<8 ; y++){ r=&"&"; for (x=0 ; x < 8 ; x++){ r = r + ( getStats(x,y) ? &"1&" : &"0&" ); } // println(r); // DEBUG b[y]= byte(unbinary(r)); } return b; } } // ------------------------------------------------------ // B u t t o n // ------------------------------------------------------ class Button { int _x,_y,_w,_h; Button (int x, int y, int width, int height) { _x = x; _y = y; _w = width; _h = height; } boolean pressed () { if (mouseX > _x && mouseX < _x + _w && mouseY > _y && mouseY < _y + _h ){ return true; } else { return false; } } } // ------------------------------------------------------ // F r a m e B u t t o n // ------------------------------------------------------ class FrameButton extends Button { String _msg; color _c; FrameButton (int x, int y, int width, int height,String msg ) { super(x, y, width, height); _msg = msg; setButton(0); } void setButton (int c) { stroke(255); fill(c); rect(_x,_y,_w,_h); fill(255); textAlign(CENTER,CENTER); textFont(label); text (_msg, _x + _w/2, _y + _h/2); } void doAction(int x){ int p; p = p_frame + x; if ( p < MINFRAME ) { p = MINFRAME; } if (p > MAXFRAME) { p = MAXFRAME; } if ( led[p] == null ) { led[p] = new LedMatrix(); } p_frame = p ; if (p_frame > max_frame) { max_frame = p_frame; } c_led = led[p]; fill(255); text (&"Flame:&" + p_frame + &" &",125,10); } } // ------------------------------------------------------ // N e x t B u t t o n // ------------------------------------------------------ class NextButton extends FrameButton { NextButton (int x, int y, int width, int height,String msg ) { super (x, y, width, height, msg ); } void doAction() { super.doAction(1); } } // ------------------------------------------------------ // P r e v B u t t o n // ------------------------------------------------------ class PrevButton extends FrameButton { PrevButton (int x, int y, int width, int height,String msg ) { super (x, y, width, height, msg ); } void doAction() { super.doAction(-1); } } // ------------------------------------------------------ // C o n t r o l B u t t o n // ------------------------------------------------------ class ControlButton extends Button { PImage _img; String _msg; PFont label_button; ControlButton (int x, int y, int width, int height, PImage img,String msg) { /* _x = x; _y = y; _w = width; _h = height; */ super(x, y, width, height); _img = img; _msg = msg; label_button = createFont(&"Arial&",10); //noStroke(); stroke(28); fill(BACKGROUND); rect(x -4 , y -4 , 32,50); stroke(128); line(x - 4, y -4 + 50, x -4 + 32, y -4 + 50); line(x - 4 + 32 , y -4 + 50, x -4 +32 , y -4 ); fill(255); textFont(label_button); text(_msg, _x+13, _y+32); setButton(0); } void setButton (int c) { int diff=0; image(mask,_x,_y); if ( c > 0 ) { // button pressed diff = 2; } else { } // fill(BACKGROUND); // rect(_x-2, _y-1, 28,30); // println(_img); image(_img, _x + diff, _y + diff); } } // ------------------------------------------------------ // R u n B u t t o n // ------------------------------------------------------ class RunButton extends ControlButton { RunButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { if (max_frame > 0 ) { run_sw = true; p_frame = 0; c_led = led[0]; } } } // ------------------------------------------------------ // S t o p B u t t o n // ------------------------------------------------------ class StopButton extends ControlButton { StopButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { run_sw = false ; } } // ------------------------------------------------------ // C l e a r B u t t o n // ------------------------------------------------------ class ClearButton extends ControlButton { ClearButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { c_led.init(); } } // ------------------------------------------------------ // E r a s e B u t t o n // ------------------------------------------------------ class EraseButton extends ControlButton { EraseButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { int i; // can not erase last frame if ( max_frame == 0 ) { return; } led[p_frame] = null; if (p_frame == max_frame){ p_frame--; } else { for ( i=p_frame; i<max_frame; i++) { led[i]=led[i+1]; } led[max_frame]=null; } max_frame--; c_led=led[p_frame]; } } // ------------------------------------------------------ // I n s e r t B u t t o n // ------------------------------------------------------ class InsertButton extends ControlButton { InsertButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { int p; // no more frame if ( max_frame == MAXFRAME ) { return; } for ( p = max_frame ; p >= p_frame ; p--) { led[p+1] = led[p]; } led[p_frame] = new LedMatrix(); max_frame++; c_led = led[p_frame]; // 1,2,*3*,4,5,6 6 to 7, 5 to 6 , 4 to 5, 3 to 4 , 3 new) } } // ------------------------------------------------------ // C o p y B u t t o n // ------------------------------------------------------ class CopyButton extends ControlButton { CopyButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { int i,j; for (i=0 ; i < 8 ; i++){ for (j=0 ; j < 8 ; j++){ buf_led.setStats(i,j,c_led.getStats(i,j)); } } } } // ------------------------------------------------------ // P a s t e B u t t o n // ------------------------------------------------------ class PasteButton extends ControlButton { PasteButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { int i,j; for (i=0 ; i < 8 ; i++){ for (j=0 ; j < 8 ; j++){ c_led.setStats(i,j,buf_led.getStats(i,j)); } } } } // ------------------------------------------------------ // S a v e B u t t o n // ------------------------------------------------------ class SaveButton extends ControlButton { SaveButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { int p; String s; String [] buf={ }; for (p = 0 ; p <= max_frame ; p++){ s = p + &",&" + max_frame + &",&" + led[p].getStrings(); buf = append (buf,s); } saveStrings(&"data/&"+ DATA_FILE,buf); } } // ------------------------------------------------------ // L o a d B u t t o n // ------------------------------------------------------ class LoadButton extends ControlButton { LoadButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { String lines[] = loadStrings(DATA_FILE); led = null ; led = new LedMatrix[MAXFRAME]; for (int i = 0 ; i <lines.length ; i++) { String p[] = splitTokens(lines[i], &",&"); led[i] = new LedMatrix(); // println (&"i=&" + i ); // DEBUG // println (lines[i]); // DEBUG for (int j=2 ; j <=9 ; j++){ int pj = int(p[j]); // ex. 0-255 // println (&" j =&" + j ); // DEBUG // println (&" pj =&" + pj ); // DEBUG for (int k=0 ; k < 8 ; k++){ if ( ( pj >>(7-k) & 1 ) == 1) { led[i].on(k,j-2); } } } } max_frame=lines.length-1; p_frame=0; c_led = led[p_frame]; } } // ------------------------------------------------------ // S e n d B u t t o n // ------------------------------------------------------ class SendButton extends ControlButton { SendButton (int x, int y, int width, int height,PImage img, String msg ) { super (x, y, width, height, img, msg ); } void doAction() { int p; Byte[] b = new Byte[8]; port.write(max_frame); for (p = 0 ; p <= max_frame ; p++){ b = led[p].getDump(); for (int i=0 ; i< 8 ; i++){ port.write(b[i]); } } } }
4. Arduino側の実装
Matrixライブラリで8x8 LED Matrixをコントロールします(SPI制御)。また Analog PINに可変抵抗を接続して、アニメーションの速度をコントロール出来るようにしています。
setup()ではEEPROMからアニメーションのフレームを呼び出して内部に展開しています。
またSerialのデータを受信した場合は、データの読み込み&EEPROMへの書き出し&内部への展開、を行います。
EEPROMを使うことで、一旦受信したアニメーションパターンは、一旦電源OFFしても保存されます。ただしそのため、保存出来るパターン数には限界があります。
ATMega168の場合はSRAM 1K、EEPROM 512 Byteなので、今の実装では最大48フレームとし、それ以上の長さのデータを受信しても読み飛ばすようにしています。
また回路については下記ページをほぼそのまま使用しています。異なるのはANALOG PIN2 に可変抵抗を接続していることだけです。(もちろん5VとGNDも)
・建築発明工作ゼミ2008:Arduino マトリクスLED2/MAX7219
【Arduinoコード】
#include <EEPROM.h> #include <Matrix.h> #define MAX_FRAME 48 #define MAX_BUFF 8*MAX_FRAME+1 // MATRIX DRIVER // by elekid Matrix myMatrix = Matrix(4, 2, 3); int InputPin = 2; // 8x8 = 64 ; 8x16=128 ; 8x32 = 256 byte data_buff[MAX_BUFF]; // 8byte * 48frames= 384 byte int max_frame; int p_matrix=0; int waits=0; void setup() { int i; Serial.begin(9600); // INIT FOR TEST DEBUG USE ONLY // EEPROM.write(7,0); max_frame = EEPROM.read(7); if (max_frame == 0 || max_frame==255) { max_frame = 1 ; data_buff[0] = max_frame; for ( i=1 ; i < MAX_BUFF ; i++){ data_buff[i]=0; } data_buff[4] = 24; data_buff[5] = 24; data_buff[11] = 4+8+16+32; data_buff[12] = 4+32; data_buff[13] = 4+32; data_buff[14] = 4+8+16+32; } else { if (max_frame > MAX_FRAME) { max_frame = MAX_FRAME; } for ( i=0 ; i < (max_frame+1)*8 ; i++){ data_buff[i+1]=EEPROM.read(i+8); } } } void loop() { int val; int msglen; int i,j; byte b; // control delay secion val = analogRead(InputPin); val = map (val,0,1024,100,10000); if ( waits > val ){ waits=0; myMatrix.clear(); // clear display for (j = 0 ; j < 8 ; j++){ b = data_buff[p_matrix * 8 + j + 1]; for (i =0 ; i < 8 ; i++){ if ( ( b>>(7-i) ) & 0x01 ) { //if ( ( b >> i ) & 0x01 ) { myMatrix.write(i, j, HIGH); } } } p_matrix++; if ( p_matrix > max_frame ) { p_matrix=0; } } waits++ ; msglen=0; // check new data from PC if ( Serial.available() > 0 ) { b = Serial.read(); data_buff[0]=b; msglen = ((int)b + 1) * 8; for ( i =0 ; i < msglen ; i++){ while ( Serial.available() < 1 ){ // NOP } b=Serial.read(); if ( i <= MAX_BUFF) { data_buff[i+1] = b; } } } if (msglen > 0) { p_matrix = -1; if (max_frame <= MAX_FRAME) { max_frame=data_buff[0]; } else { max_frame=MAX_FRAME; data_buff[0] = MAX_FRAME; } for ( i = 0 ; i < msglen ; i ++){ EEPROM.write(i+7,data_buff[i]); } Serial.flush(); } // if msglen > 0 } // loop()
5. 感想とTODO
ArduinoのSerialで長い文字を送受信するのは意外とプログラミングが面倒で手間取りました。今は9600bpsで問題なく動作していますが、速度を上げると取りこぼしが発生するかもしれません。
ArduinoとProcessingはIDEは似ていますが、言語的には別物と考えおいたほうが良いと思います。実際同時にプログラミングしていると混乱します。(Arduinoで継承が使えないとか、Processingでdefine文が使えない等々...)
今後は、先日Make Tokyo Meeting 03で購入したXBeeでワイヤレス化にチャレンジしてみたいと考えています。スクロールアニメーション版も作っておきたい。
【デモ動画】
はじめまして 千田と申します。
私は 60歳のものづくり大好き人間です youtubeで見た8*8*8のLED CUBEが作りたくてarduinoを購入 先ずは8*8matrix(max7219)で勉強のつもりで(ボケ防止) アニメーションメーカーを作りたくて あなたのページに遭遇 始めてのprocessingはスケッチをcopy-して貼り付けてもエラーばかしで
そこで調べてみたのですがアイコンボタンのページがない!?!
文字列を扱う&”****.***&”行でエラー どうしたらよいよいのか 教えてほしいのですが・・・
arduinoはUNO 10 0022 prrcessingは1.5.1 OSは7です。
なにとぞよろしくお願いいたします。 まったくの初心者です。
by 千田 雅彦 (2012-12-21 09:23)