目次
前
次
カラーセンサー情報取得
秋月電子で販売されているカラーセンサーを利用して
ArduinoとProcessingの協調システムをつくります。
カラーセンサーは、次のように半田付けしました。
ArduinoとProcessingの役割分担を決めます。
Arduino担当
2つの機能を持たせます。
自動計測では、3秒周期でデータを取得して
色に関する情報を内部変数に格納します。
ワンショット計測は、Processingコードからトリガー
を受け取って、その場で色に関する情報を取得します。
結果に関しても、Processingコードからの要求で
出力します。
Processing担当
Arduinoにコマンドを送り、現在の色情報を取得します。
同時に色情報の%を矩形で表示します。
コマンドを決めます。
- M ワンショット計測
- C 色情報取得
- S 現在の状態表示
ボタンの矩形領域を定義して、操作しやすくします。
Arduinoスケッチ
Arduinoにカラーセンサーを接続するため、デジタルピンの
どこを利用するのかを決めます。
3秒ごとに自動でカラーセンサーから、情報取得するように
タイマー2で1.5秒ごとにカウンタをインクリメントします。
カウンタ値が奇数のときに、計測トリガーを出力します。
void update_trigger(void)
{
send_led( xcnt & ON );
xcnt++ ;
if ( xcnt & ON ) { tflag = ON ; }
}
1.5秒ごとにLEDの点灯、消灯を切換えると同時に、計測の
トリガーに利用します。LEDの点滅で、Arduinoが動作中と
判断できます。
コマンドを定義します。
- ? help
- M one shot measure
- C show color values
- H high range
- L low range
- S show status
コマンドを定義したなら、対応する処理を定義します。
help
Processingでは必要ありませんが、TeraTermのような端末ソフト
で動作テストするときに使います。関数でまとめます。
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("M one shot measure") ; crlf();
rs_puts("C show color values") ; crlf();
rs_puts("H high range") ; crlf();
rs_puts("L low range") ; crlf();
rs_puts("S show status") ; crlf();
}
関数を起動するのは、コマンドインタプリタを使います。
if ( cmd == '?' ) { show_help() ; }
one shot measure
計測すればよいので、関数を利用します。
void get_color(byte which,byte xd)
{
byte i ;
long rgb[3] ;
/* set RANGE */
put_range( which ) ;
/* disable GATE and CK */
put_gate(OFF);
put_ck(OFF);
/* open GATE */
put_gate( ON ) ;
delay(xd+2);
put_gate(OFF);
/* loop */
for ( i = 0 ; i < 3 ; i++ ) {
/* interval */
delayMicroseconds(4);
/* get RGB information */
*(rgb+i) = get_color_value() ;
}
}
word get_color_value()
{
word result ;
byte i ;
/* clear */
result = 0 ;
/* loop */
for ( i = 0 ; i < 12 ; i++ ) {
/* shift */
result >>= 1 ;
/* CK : H */
put_ck( ON ) ;
/* delay */
delayMicroseconds(2);
/* get data */
if ( PINC & 0x01 ) { result |= 0x800 ; }
/* CK : L */
put_ck( OFF ) ;
/* delay */
delayMicroseconds(2);
}
/* adjust */
result &= 0xfff ;
return( result );
}
12ビットデータ入力を3回として、12ビットデータ入力は
独立した関数でまとめます。
コマンドインタプリタの扱いは、以下。
if ( cmd == 'M' ) { get_color(bflag,timcnt); }
show color values
各色は0から4095の値をとるので、10進数4桁を表示する
独立した関数を用意し、その関数をサブで利用します。
void show_digit4(word x)
{
byte j ;
char msg[4] ;
/* separate */
for ( j = 0 ; j < 4 ; j++ ) {
*(msg+3-j) = x % 10 + '0' ;
x /= 10 ;
}
/* show */
for ( j = 0 ; j < 4 ; j++ ) { rs_putchar( *(msg+j) ); }
}
void show_values()
{
/* copy */
rmdat = rxdat ;
gmdat = gxdat ;
bmdat = bxdat ;
/* show */
rs_putchar('C');
show_digit4( rmdat );
show_digit4( gmdat );
show_digit4( bmdat );
/* new line */
crlf();
}
コマンドインタプリタの扱いは、以下。
if ( cmd == 'C' ) { show_values(); }
high range
フラグを用意してあるので、セットして対応。
if ( cmd == 'H' ) { bflag = ON ; }
low range
フラグを用意してあるので、リセットして対応。
if ( cmd == 'L' ) { bflag = OFF ; }
show status
計測しているかどうかは、変数値の奇数、偶数で
判断しています。
if ( cmd == 'S' ) {
rs_putchar('S');
if ( xcnt & ON ) { rs_putchar('m'); }
else { rs_putchar('i'); }
crlf();
}
スケッチとしてまとめると、以下。
#include <MsTimer2.h>
#define OFF 0
#define ON OFF+1
#define LD 0
#define HD 1
/* color sensor pin assignment */
#define RANGE_BIT 3
#define CK_BIT 2
#define GATE_BIT 1
#define DOUT_BIT 0
/* monitor LED */
#define LED_BIT 5
/* global variables */
byte tflag ;
byte bflag ;
byte uflag ;
byte sindex ;
byte sbuf[8] ;
byte xcnt ;
word rxdat ;
word gxdat ;
word bxdat ;
word rmdat ;
word gmdat ;
word bmdat ;
byte timcnt ;
void send_led(byte x)
{
if ( x ) { PORTB |= (1 << LED_BIT) ; }
else { PORTB &= ~(1 << LED_BIT) ; }
}
void update_trigger(void)
{
send_led( xcnt & ON );
xcnt++ ;
if ( xcnt & ON ) { tflag = ON ; }
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *x)
{
while ( *x ) {
rs_putchar( *x ) ;
x++ ;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
byte get_hex(char x)
{
byte result ;
result = 0 ;
if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }
return result ;
}
void show_digit4(word x)
{
byte j ;
char msg[4] ;
/* separate */
for ( j = 0 ; j < 4 ; j++ ) {
*(msg+3-j) = x % 10 + '0' ;
x /= 10 ;
}
/* show */
for ( j = 0 ; j < 4 ; j++ ) { rs_putchar( *(msg+j) ); }
}
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("M one shot measure") ; crlf();
rs_puts("C show color values") ; crlf();
rs_puts("H high range") ; crlf();
rs_puts("L low range") ; crlf();
rs_puts("S show status") ; crlf();
}
void get_color(byte which,byte xd)
{
byte i ;
long rgb[3] ;
/* set RANGE */
put_range( which ) ;
/* disable GATE and CK */
put_gate(OFF);
put_ck(OFF);
/* open GATE */
put_gate( ON ) ;
delay(xd+2);
put_gate(OFF);
/* loop */
for ( i = 0 ; i < 3 ; i++ ) {
/* interval */
delayMicroseconds(4);
/* get RGB information */
*(rgb+i) = get_color_value() ;
}
}
void put_range(byte x)
{
if ( x == ON ) {
PORTC |= (1 << RANGE_BIT) ;
} else {
PORTC &= ~(1 << RANGE_BIT) ;
}
}
void put_gate(byte x)
{
if ( x == HD ) {
PORTC |= (1 << GATE_BIT) ;
} else {
PORTC &= ~(1 << GATE_BIT) ;
}
}
void put_ck(byte x)
{
if ( x == ON ) {
PORTC |= (1 << CK_BIT) ;
} else {
PORTC &= ~(1 << CK_BIT) ;
}
}
word get_color_value()
{
word result ;
byte i ;
/* clear */
result = 0 ;
/* loop */
for ( i = 0 ; i < 12 ; i++ ) {
/* shift */
result >>= 1 ;
/* CK : H */
put_ck( ON ) ;
/* delay */
delayMicroseconds(2);
/* get data */
if ( PINC & 0x01 ) { result |= 0x800 ; }
/* CK : L */
put_ck( OFF ) ;
/* delay */
delayMicroseconds(2);
}
/* adjust */
result &= 0xfff ;
return( result );
}
void show_values()
{
/* copy */
rmdat = rxdat ;
gmdat = gxdat ;
bmdat = bxdat ;
/* show */
show_digit4( rmdat );
show_digit4( gmdat );
show_digit4( bmdat );
/* new line */
crlf();
}
void setup()
{
/* initialize serial port */
Serial.begin(9600);
sindex = 0 ;
/* define digital pin */
PORTB = 0x00 ;
PORTC = 0x03 ;
PORTD = 0x01 ;
/* set initial state */
DDRB = 0xff ;
DDRC = 0xfc ;
DDRD = 0xfe ;
/* clear flags */
tflag = OFF ;
bflag = OFF ;
uflag = OFF ;
/* initialize variables */
timcnt = 0 ;
xcnt = 0 ;
/* 1500ms period */
MsTimer2::set(1500,update_trigger);
/* enable */
MsTimer2::start();
}
void loop()
{
byte cmd ;
/* timer handling */
if ( tflag == ON ) {
/* clear flag */
tflag = OFF ;
/* measure */
get_color(bflag,timcnt);
}
/* serial handling */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* new line */
crlf();
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* one shot measure */
if ( cmd == 'M' ) { get_color(bflag,timcnt); }
/* show color values */
if ( cmd == 'C' ) { show_values(); }
/* set Hight range */
if ( cmd == 'H' ) { bflag = ON ; }
/* set Low range */
if ( cmd == 'L' ) { bflag = OFF ; }
/* show status */
if ( cmd == 'S' ) {
rs_putchar('S');
if ( xcnt & ON ) { rs_putchar('m'); }
else { rs_putchar('i'); }
crlf();
}
}
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
Processingスケッチ
スケッチを動かしたときのGUIを考えます。
ボタンを画像で用意していきます。
ボタンをクリックしたときの処理を定義していきます。
Exit
Exitの領域をクリックしたときは、終了させます。
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,200+YBEGIN,200+YBEGIN+HEIGHT) ) {
exit();
}
Measure
ボタン領域をクリックしたときは、ArduinoにコマンドMを送信。
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,YBEGIN,YBEGIN+HEIGHT) ) {
/* println("Measure"); */
cport.write('M');
cport.write('\r');
}
Color
ボタン領域をクリックしたときは、ArduinoにコマンドCを送信。
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,30+YBEGIN,30+YBEGIN+HEIGHT) ) {
/* println("Color"); */
cport.write('C');
cport.write('\r');
}
Status
ボタン領域をクリックしたときは、ArduinoにコマンドSを送信。
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,60+YBEGIN,60+YBEGIN+HEIGHT) ) {
/* println("Status"); */
cport.write('S');
cport.write('\r');
}
H range
ボタン領域をクリックしたときは、ArduinoにコマンドHを送信。
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,100+YBEGIN,100+YBEGIN+HEIGHT) ) {
/* println("range H"); */
cport.write('H');
cport.write('\r');
}
L range
ボタン領域をクリックしたときは、ArduinoにコマンドLを送信。
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,130+YBEGIN,130+YBEGIN+HEIGHT) ) {
/* println("range L"); */
cport.write('L');
cport.write('\r');
}
関数drawの内容を定義します。
バー描画
イベントフラグで通知されたなら、関数を呼び出します。
if ( dflag == true ) {
/* clear flag */
dflag = false ;
/* draw */
bar_draw(xr,xg,xb);
}
実際のバー描画は、専用関数で実行。
void bar_draw(int rx,int gx,int bx)
{
/* red */
fill(255, 0, 0); rect(BSX, BSY,rx,BSHEIGHT);
/* green */
fill( 0,255, 0); rect(BSX,BSHEIGHT+BSY,gx,BSHEIGHT);
/* blue */
fill( 0, 0,255); rect(BSX,2*BSHEIGHT+BSY,bx,BSHEIGHT);
}
データ入力
受信割込みから、データが入ったと通知がくるので
各色の成分値を計算します。
if ( uflag == true ) {
/* clear flag */
uflag = false ;
/* get command */
cmd = sbuf[0] ;
/* get color values */
if ( cmd == 'C' ) {
xr = 0 ;
xg = 0 ;
xb = 0 ;
for ( int i = 1 ; i < 5 ; i++ ) {
/* multiply */
xr *= 10 ;
xg *= 10 ;
xb *= 10 ;
/* digit -> value */
xr += get_hex( sbuf[i] ) ;
xg += get_hex( sbuf[i+4] ) ;
xb += get_hex( sbuf[i+8] ) ;
}
/* send draw */
dflag = true ;
}
受信バッファには、情報が数字で格納されているので
数値に変換します。
byte get_hex(byte x)
{
byte result ;
/* default */
result = 0 ;
/* judge */
if ( '0' <= x && x <= '9' ) { result = (byte)(x - 48) ; }
if ( 'A' <= x && x <= 'F' ) { result = (byte)(x - 55) ; }
if ( 'a' <= x && x <= 'f' ) { result = (byte)(x - 87) ; }
return result ;
}
スケッチとしてまとめると、以下。
PImage imgMeasure ;
PImage imgColor ;
PImage imgStatus ;
PImage imgRH ;
PImage imgRL ;
PImage imgExit ;
import processing.serial.*;
Serial cport;
/* button caption */
PImage imgMeasure ;
PImage imgColor ;
PImage imgStatus ;
PImage imgRH ;
PImage imgRL ;
PImage imgExit ;
/* frame rate */
int FRATE = 45 ;
int WX = 320 ;
int WY = 240 ;
int XBEGIN = 0 ;
int YBEGIN = 0 ;
int WIDTH = 80 ;
int HEIGHT = 24 ;
int BSX = 100 ;
int BSY = 30 ;
int BSHEIGHT = 20 ;
int xr ;
int xg ;
int xb ;
boolean dflag ;
byte[] sbuf ;
byte sindex;
boolean uflag ;
byte cmd ;
byte get_hex(byte x)
{
byte result ;
/* default */
result = 0 ;
/* judge */
if ( '0' <= x && x <= '9' ) { result = (byte)(x - 48) ; }
if ( 'A' <= x && x <= 'F' ) { result = (byte)(x - 55) ; }
if ( 'a' <= x && x <= 'f' ) { result = (byte)(x - 87) ; }
return result ;
}
boolean isRangeOk(int x,int bx,int ex)
{
boolean result ;
/* default */
result = false ;
/* judge */
if ( bx <= x && x <= ex ) { result = true ; }
return result ;
}
void bar_draw(int rx,int gx,int bx)
{
/* red */
fill(255, 0, 0); rect(BSX, BSY,rx,BSHEIGHT);
/* green */
fill( 0,255, 0); rect(BSX,BSHEIGHT+BSY,gx,BSHEIGHT);
/* blue */
fill( 0, 0,255); rect(BSX,2*BSHEIGHT+BSY,bx,BSHEIGHT);
}
void setup()
{
/* title caption */
frame.setTitle("Color sensor");
/* select framerate */
frameRate(FRATE);
/* select displya dimension */
size(WX,WY);
/* select back ground color with WHITE */
background(255);
/* initialize serial port */
cport = new Serial(this,"COM1",9600);
sindex = 0 ;
dflag = false ;
uflag = false ;
/* get image */
imgMeasure = loadImage("measure.png");
imgColor = loadImage("color.png");
imgStatus = loadImage("status.png");
imgRH = loadImage("rh.png");
imgRL = loadImage("rl.png");
imgExit = loadImage("exit.png");
/* event flag */
lflag = false ;
rflag = false ;
/* test string */
textSize( 12 );
textAlign( LEFT );
}
void draw()
{
/* */
fill(0,0,0);
text("element",100,20);
/* draw button */
image(imgMeasure,XBEGIN, 0+YBEGIN);
image(imgColor ,XBEGIN, 30+YBEGIN);
image(imgStatus ,XBEGIN, 60+YBEGIN);
image(imgRH ,XBEGIN,100+YBEGIN);
image(imgRL ,XBEGIN,130+YBEGIN);
image(imgExit ,XBEGIN,200+YBEGIN);
/* data handling */
if ( uflag == true ) {
/* clear flag */
uflag = false ;
/* get color values */
xr = 0 ;
xg = 0 ;
xb = 0 ;
for ( int i = 1 ; i < 5 ; i++ ) {
/* multiply */
xr *= 10 ;
xg *= 10 ;
xb *= 10 ;
/* digit -> value */
xr += get_hex( sbuf[i] ) ;
xg += get_hex( sbuf[i+4] ) ;
xb += get_hex( sbuf[i+8] ) ;
}
/* send draw */
dflag = true ;
}
/* show bar */
if ( dflag == true ) {
/* clear flag */
dflag = false ;
/* draw */
bar_draw(xr,xg,xb);
}
/* command interpreter */
if ( uflag == true ) {
/* clear flag */
uflag = false ;
/* get command */
cmd = sbuf[0] ;
/* status */
if ( cmd == 'S' ) {
fill(0,0,0);
if ( sbuf[1] == 'i' ) { text("idle",100,200); }
if ( sbuf[1] == 'm' ) { text("measure",100,200); }
}
/* get color values */
if ( cmd == 'C' ) {
xr = 0 ;
xg = 0 ;
xb = 0 ;
for ( int i = 1 ; i < 5 ; i++ ) {
/* multiply */
xr *= 10 ;
xg *= 10 ;
xb *= 10 ;
/* digit -> value */
xr += get_hex( sbuf[i] ) ;
xg += get_hex( sbuf[i+4] ) ;
xb += get_hex( sbuf[i+8] ) ;
}
/* send draw */
dflag = true ;
}
}
}
void mouseClicked()
{
/* enable */
if ( mouseButton == LEFT ) {
/* Measure */
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,YBEGIN,YBEGIN+HEIGHT) ) {
/* println("Measure"); */
cport.write('M');
cport.write('\r');
}
/* Color */
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,30+YBEGIN,30+YBEGIN+HEIGHT) ) {
/* println("Color"); */
cport.write('C');
cport.write('\r');
}
/* Status */
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,60+YBEGIN,60+YBEGIN+HEIGHT) ) {
/* println("Status"); */
cport.write('S');
cport.write('\r');
}
/* range H */
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,100+YBEGIN,100+YBEGIN+HEIGHT) ) {
/* println("range H"); */
cport.write('H');
cport.write('\r');
}
/* range L */
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,130+YBEGIN,130+YBEGIN+HEIGHT) ) {
/* println("range L"); */
cport.write('L');
cport.write('\r');
}
/* Exit */
if ( isRangeOk(mouseX,XBEGIN,XBEGIN+WIDTH) &&
isRangeOk(mouseY,200+YBEGIN,200+YBEGIN+HEIGHT) ) {
exit();
}
}
}
void serialEvent(Serial p)
{
byte ch ;
if ( cport.available() > 0 ) {
ch = (byte)cport.read();
/* judge */
if ( ch == '\r' ) {
uflag = true ;
sindex = 0 ;
}
/* store */
sbuf[sindex] = ch ;
/* update */
sindex++ ;
}
}
目次
前
次