Arduino Tetris 1
Today, we’re going to try and build a game – Tetris. Check out our final product!
What you’ll need:
- Arduino UNO
- USB cable
- Joystick
- Breadboard
- 8×8 LED grid model 1088BS
- 21 wires, male on one end and female on the other
- laptop/PC
- Latest Arduino software
Optional
Wiring
Make sure you’ve got the latest Arduino IDE installed and updated. Let’s start by getting the initial hardware out of the way, shall we? We’ve got two configurations for this – see below.
Notice that the anodes and cathodes on our LED grid are shared and mixed randomly. You will need the right pair to get just one light. If your software doesn’t match your wiring then the results will be strange – but not fatal. Make sure that your LED has a small bump (it’s notated in the diagram as well) pointed the right way, because it becomes important where your wires go – the bump is meant to guide you.
We’re going to try and combine two things: firstly, drawing a dot on a screen exactly where we want it, and then reading the joystick position.
One Dot
Let’s begin by drawing the dot.
// a list of anode pins, sorted by top to bottom of the grid
// anode[0] is the same as saying "the top row"
// anode[7] is the same as saying "the bottom row"
const int anode[8] = { 8,13,7,11,0,6,1,4 };
// a list of cathode pins, sorted by left to right of the grid
// cathode[0] is the same as saying "the left most column"
// cathode[7] is the same as saying "the right most column"
const int cathode[8] = { 12,2,3,9,5,10,14,15 };
// translate the pins on the LED panel to pins on the Arduino
const int arduino_to_grid[16] = { 13,12,11,10, 16,17,18,19, 2,3,4,5, 6,7,8,9 };
void setup() {
int i;
// set all the pins to output.
for(i=0;i<16;++i) {
pinMode(arduino_to_grid[i],OUTPUT);
}
// turn on all resistors, should produce no light
for(i=0;i<8;++i) {
digitalWrite(out(i),LOW);
digitalWrite(in(i),HIGH);
}
}
Great. In our first loop(), we are going to try and turn on just one light at a time in a left to right, top to bottom method – the same as reading text.
// I want to turn on the column N from the left.
// this figures out which pin on the LED that is,
// then figures out which pin on the arduino matches that LED pin.
// two translations!
int out(int x) {
return arduino_to_grid[cathode[x]];
}
// I want to turn on the row N from the top.
// this figures out which pin on the LED that is,
// then figures out which pin on the arduino matches that LED pin.
// two translations!
int in(int y) {
return arduino_to_grid[anode[y]];
}
// light one dot at a time
void one_at_a_time() {
int i,j;
for(j=0;j<8;++j) {
digitalWrite(out(j),HIGH);
for(i=0;i<8;++i) {
digitalWrite(in(i),LOW);
delay(100);
digitalWrite(in(i),HIGH);
}
digitalWrite(out(j),LOW);
}
}
void loop() {
one_at_a_time();
}
At this point, you should have dots moving in the predicted pattern. “one_at_a_time() value j” is moving horizontally. and the “i” value is moving vertically. Now we want to be able to light just the light we want. Check it out:
// size of the LED grid
#define GRID_W (8)
#define GRID_H (8)
// I want to turn on point P(x,y), which is X from the left and Y from the
// top.
// I might also want to hold it on for ms milliseconds.
void p(int x,int y,int ms) {
// don't try to turn on a light that doesn't exist
// if(x<0 || x>GRID_W) return;
// if(y<0 || y>GRID_H) return;
// Now light it.
digitalWrite(out(x),HIGH);
digitalWrite(in(y),LOW);
delay(ms);
// Turn it off again
digitalWrite(in(y),HIGH);
digitalWrite(out(x),LOW);
}
// light the whole grid
void test_p() {
int x,y;
for(x=0;x<GRID_W;++x) {
for(y=0;y<GRID_H;++y) {
p(x,y,100);
}
}
}
Change the loop() method like this:
void loop() {
//one_at_a_time();
test_p();
}
Joystick Position
Now we’ll be able to use p(x, y, ms) to light a specific LED whenever we want. Now that the first part is done, we just need to be able to read the joystick position. Let’s go ahead and replace the setup() and loop() as follows:
void setup() {
int i;
// set all the pins to output.
for(i=0;i<16;++i) {
pinMode(arduino_to_grid[i],OUTPUT);
}
// turn on all resistors, should produce no light
for(i=0;i<8;++i) {
digitalWrite(out(i),LOW);
digitalWrite(in(i),HIGH);
}
// set the start to the top left corner
px=0;
py=0;
clock=millis();
}
void loop() {
//one_at_a_time();
//test_p();
move_dot();
}
Add this now:
void move_dot() {
// draw the dot
p(px,py,0); // light for a fraction of a millisecond
// 10 times a second,
if(millis()-clock>100) {
// get ready to wait another 1/10th of a second
clock=millis();
// read the joystick
int dx=map(analogRead(0),0,1023,500,-500);
int dy=map(analogRead(1),0,1023,-500,500);
// Moves the dot. I made a small deadzone in the center of
// the joystick so when you let go the dot doesn't drift.
if(dx> JOYSTICK_DEAD_ZONE) px++;
if(dx<-JOYSTICK_DEAD_ZONE) px--;
if(dy> JOYSTICK_DEAD_ZONE) py++;
if(dy<-JOYSTICK_DEAD_ZONE) py--;
// don't let the dot go off the side of the grid
if(px>GRID_W-1) px=GRID_W-1;
if(px<0 ) px=0;
if(py>GRID_H-1) py=GRID_H-1;
if(py<0 ) py=0;
}
}
Almost there! Add the last “define” and jam this right below:
#define JOYSTICK_DEAD_ZONE (10)
long clock;
// time of last update
int px, py;
// dot position
Now, the Arduino will remember a spot on the grid and lights it up all the time. The Arduino will check ten times per second to see if you are pushing the joystick – if you are, then Arduino will change its memory of the dot’s location, and it’ll be drawn at the new position stored in (px, py) the next time move_dot() is called. If you’re not, it’ll retain its stored memory. Ultimately, the net effect is that you push the joystick and the dot goes where you pushed it.
If you’ve got a hang of it, let’s try some extra stuff!
Questions
- Change these lines. Try to explain what happens and why.
int dx=map(analogRead(0),0,1023,500,-500);
#define JOYSTICK_DEAD_ZONE (250)
if(px>GRID_W/2) px=GRID_W/2;
p(px,GRID_H-1-py,0);
- Think about how we could make the pieces move or rotate.