而這次要做的是使用加速度感測器製作的控制器。
首先要來看看主角 ADXL335三軸加速度感測器 :
俯視圖 |
這是Analog Devices公司的ADXL335感測器,其上有六個連接插頭分別
標示為GND、Z、Y、X、3V、TEST,將其焊上0.1"排插使用
焊上0.1"排插 |
接下來就是接線囉,感測器是類比裝置,所以將X、Y、Z接到Arduino
上的類比針腳
將ADXL335連接至Arduino |
線路接好了,接著來看看它會丟出甚麼資料吧~
code :
讀取三個軸向的輸入值、並將之輸出到序列埠上 |
result :
這三排數值代表著平放時三軸感測到的加速度,
沿著軸移動,則相對應的數值會跟著變動。
但觀察上圖,在平放時三軸偵測到的值仍然會跳動,
而且若要進一步的運用,還需要確立感測器的最大值和最小值。
解決方法是修改code讓其自動記錄最大最小值,
並設緩衝,連續紀錄幾筆資料後取平均避免跳動。
最後在電路上加上一個按鈕,
控制器就完成了~
加上下拉式電阻按鈕,輸入至數位針腳7 |
控制器 code :
//////////////////////////////////////////////////////////////
#include <Bounce.h>
const unsigned int BUTTON_PIN = 7;
const unsigned int X_AXIS_PIN = 2;
const unsigned int Y_AXIS_PIN = 1;
const unsigned int Z_AXIS_PIN = 0;
const unsigned int BAUD_RATE = 19200;
const unsigned int NUM_AXES = 3;
const unsigned int PINS[NUM_AXES] = {X_AXIS_PIN, Y_AXIS_PIN, Z_AXIS_PIN};
const unsigned int BUFFER_SIZE = 16;
int buffer[NUM_AXES][BUFFER_SIZE];
int buffer_pos[NUM_AXES] = {0};
Bounce button(BUTTON_PIN, 20);
void setup() {
// put your setup code here, to run once:
Serial.begin(BAUD_RATE);
pinMode(BUTTON_PIN, INPUT);
}
int get_axis(const int axis){
delay(1);
buffer[axis][buffer_pos[axis]] = analogRead(PINS[axis]);
buffer_pos[axis] = (buffer_pos[axis] + 1) % BUFFER_SIZE;
long sum = 0;
for(int i = 0; i < BUFFER_SIZE; i++){
sum += buffer[axis][i];
}
return round(sum / BUFFER_SIZE);
}
int get_x(){return get_axis(2);}
int get_y(){return get_axis(1);}
int get_z(){return get_axis(0);}
void loop() {
// put your main code here, to run repeatedly:
Serial.print(get_x());
Serial.print(" ");
Serial.print(get_y());
Serial.print(" ");
Serial.print(get_z());
Serial.print(" ");
if(button.update())
Serial.println(button.read() == HIGH ? "1" : "0");
else
Serial.println("0");
}
///////////////////////////////////////////////////////////////////////////////////
將code上載到Arduino,控制器就完成了,
他會向電腦輸出X、Y、Z及按鈕的0/1值
為了測試,
用Processing寫了個小遊戲來試試手~
Game code :
//////////////////////////////////////////////////////////////////////////////////////
import processing.serial.*;
Serial arduinoPort;
final int COLUMNS = 7;
final int ROWS = 4;
final int BALL_RADIUS = 8;
final int BALL_DIAMETER = BALL_RADIUS * 2;
final int MAX_VELOCITY = 8;
final int PADDLE_WIDTH = 60;
final int PADDLE_HEIGHT = 15;
final int BRICK_WIDTH = 40;
final int BRICK_HEIGHT = 20;
final int MARGIN = 10;
final int WIDTH = COLUMNS * BRICK_WIDTH + 2 * MARGIN;
final int HEIGHT = 300;
final int X_AXIS_MIN = 252;
final int X_AXIS_MAX = 443;
final int LINE_FEED = 10;
final int BAUD_RATE = 19200;
int px, py;
int vx, vy;
int xpos = WIDTH/2;
int[][] bricks = new int[COLUMNS][ROWS];
boolean buttonPressed = false;
boolean paused = true;
boolean done = true;
void setup(){
size(WIDTH, HEIGHT);
noCursor();
textFont(loadFont("Verdana-Bold-36.vlw"));
initGame();
println(Serial.list());
arduinoPort = new Serial(this, Serial.list()[0], BAUD_RATE);
arduinoPort.bufferUntil(LINE_FEED);
}
void initGame(){
initBricks();
initBall();
}
void initBricks(){
for(int x = 0; x < COLUMNS; x++)
for(int y = 0; y < ROWS; y++)
bricks[x][y] = 1;
}
void initBall(){
px = width / 2;
py = height / 2;
vx = int(random(-MAX_VELOCITY, MAX_VELOCITY));
vy = -2;
}
void draw(){
background(0);
stroke(255);
strokeWeight(3);
done = drawBricks();
if(done){
paused = true;
printWinMessage();
}
if(paused)
printPauseMessage();
else
updateGame();
drawBall();
drawPaddle();
}
boolean drawBricks(){
boolean allEmpty = true;
for(int x = 0; x < COLUMNS; x++){
for(int y = 0; y < ROWS; y++){
if(bricks[x][y] > 0){
allEmpty = false;
fill(0, 0, 100 + y*8);
rect(MARGIN + x * BRICK_WIDTH,
MARGIN + y * BRICK_HEIGHT,
BRICK_WIDTH,
BRICK_HEIGHT);
}
}
}
return allEmpty;
}
void drawBall(){
strokeWeight(1);
fill(128, 0, 0);
ellipse(px, py, BALL_DIAMETER, BALL_DIAMETER);
}
void drawPaddle(){
int x = xpos - PADDLE_WIDTH / 2;
int y = height - (PADDLE_HEIGHT + MARGIN);
strokeWeight(1);
fill(128);
rect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT);
}
void printWinMessage(){
fill(255);
textSize(36);
textAlign(CENTER);
text("YOU WIN!", width / 2, height * 2 / 3);
}
void printPauseMessage(){
fill(128);
textSize(16);
textAlign(CENTER);
text("Press Button to Continue", width / 2, height * 5 / 6);
}
void updateGame(){
if(ballDropped()){
initBall();
paused = true;
}
else{
checkBrickCollision();
checkWallCollision();
checkPaddleCollision();
px += vx;
py += vy;
}
}
boolean ballDropped(){
return py + vy > height - BALL_RADIUS;
}
boolean inXRange(final int row, final int v){
return px + v > row * BRICK_WIDTH &&
px + v < (row + 1) * BRICK_WIDTH + BALL_DIAMETER;
}
boolean inYRange(final int col, final int v){
return py + v > col * BRICK_HEIGHT &&
py + v < (col + 1) * BRICK_HEIGHT + BALL_DIAMETER;
}
void checkBrickCollision(){
for(int x = 0; x < COLUMNS; x++){
for(int y = 0; y < ROWS; y++){
if(bricks[x][y] > 0){
if(inXRange(x, vx) && inYRange(y, vy)){
bricks[x][y] = 0;
if(inXRange(x, 0))
vy = -vy;
if(inYRange(y, 0))
vx = -vx;
}
}
}
}
}
void checkWallCollision(){
if(px + vx < BALL_RADIUS || px + vx > width - BALL_RADIUS)
vx = -vx;
if(py + vy < BALL_RADIUS || py + vy > height - BALL_RADIUS)
vy = -vy;
}
void checkPaddleCollision(){
final int cx = xpos;
if(py + vy >= height - (PADDLE_HEIGHT + MARGIN + 6) &&
px >= cx - PADDLE_WIDTH / 2 &&
px <= cx + PADDLE_WIDTH / 2){
vy = -vy;
vx = int(map(px-cx,
-(PADDLE_WIDTH / 2), PADDLE_WIDTH / 2,
-MAX_VELOCITY, MAX_VELOCITY));
}
}
void SerialEvent(Serial port){
final String arduinoData = port.readStringUntil(LINE_FEED);
if(arduinoData != null){
final int[] data = int(split(trim(arduinoData), ' '));
if(data.length == 4){
buttonPressed = (data[3] == 1);
if(buttonPressed){
paused = !paused;
if(done){
done = false;
initGame();
}
}
if(!paused){
xpos = int(map(data[0], X_AXIS_MIN, X_AXIS_MAX, 0, WIDTH));
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
關於code和Processing的資料可以參考下列網址 :
Code : http://pragprog.com/titles/msard/source_code
Processing : http://processing.org
這是一個打磚塊的小遊戲,
可以用控制器控制衡感進行遊戲~
Test Video :
動作感測器可以應用在很多地方,
像是在加一個按鈕就能充當滑鼠了~
總的來說是個很實用的小東西,
可以擴充成很多有趣的電子產品。
這次的小遊戲只是測試用,比較單調,
以後有空會再多增加一些東西,讓他變得更有趣 =目
沒有留言:
張貼留言