What about trying make the Arduino UNO board a simple and reliable retro game station prototype. We need a joystick, a display and some sound.Circuit design
We do use very simple electronic objects :
- a couple of variable resistors to allow end-user to define screen brightness and to collect x/y position of the joystick
- a couple of buttons : the joystick can be clicked, and there is two buttons (A, B)
- a buzzer
- a led strip ws2812b (8x32 matrix) as a display
The joystick device actually requires :
- 2 wires for vcc and ground
- 2 wires to analog ports for the x/y stick
- 3 wires to digital port for the buttons (stick, A, B)
With this hardware configuration, the Arduino UNO board could host 2 joysticks (3 if we find another way than using an analog port to manage display brightness)Joystick driver
The joystick driver consist in a couple of lines just to analyze vcc divider values of variable resistors of the x/y stick and to declare the appButton objects of the joystick. The loop() method of the object will be charged to update object internal values while driver users will call joystick public method to get current joystick status : X(), Y(), btn.isPressed()
When the stick is released on its zero position, the vcc divider value should be perfectly half of vcc value, so 512 in binary for a 1024 bit capable analog to digital converter.
As there is nothing perfect in this world, the zero position of the stick may correspond to a small value, bellow or upper to 512. The JOYSTICK_NOISE_FACTOR helps to solve this issue. While raw data for X() and Y() method should be between 0-512, the noise factor at 20 with reduce this range from 0-25, and value will have a nice 0 value when the stick is released.
Raw performances of the device is (a bit more than) 8kHz with a single device, 4kHz with 2 joysticks and 2.6kHz with 3 joysticks. Quite enough for human games.
#define JOYSTICK_NOISE_FACTOR 20
class joystick {
private:
int ud_pin; // up_down variable resistor
int lr_pin; // left_right variable resistor
float ud_init; // up_down variable resistor
float lr_init; // left_right variable resistor
int ud_pos; // up_down variable resistor
int lr_pos; // left_right variable resistor
public:
appButton *bt0; // stick button
appButton *bt1; // button A
appButton *bt2; // button B
joystick(int _ud, int _lr, int _bt0, int _bt1, int _bt2);
float X(void) { return -floor(lr_pos/JOYSTICK_NOISE_FACTOR); };
float Y(void) { return floor(ud_pos/JOYSTICK_NOISE_FACTOR); };
void loop(void);
};
joystick::joystick(int _ud, int _lr, int _bt0, int _bt1, int _bt2) {
ud_pin = _ud;
lr_pin = _lr;
bt0 = new appButton(_bt0, INPUT_PULLUP);
bt1 = new appButton(_bt1, INPUT_PULLUP);
bt2 = new appButton(_bt2, INPUT_PULLUP);
ud_init = floor(analogRead(ud_pin)/JOYSTICK_NOISE_FACTOR);
lr_init = floor(analogRead(lr_pin)/JOYSTICK_NOISE_FACTOR);
ud_pos = 0;
lr_pos = 0;
}
void joystick::loop(void) {
ud_pos = analogRead(ud_pin);
ud_pos -= (ud_init+512);
lr_pos = analogRead(lr_pin);
lr_pos -= (lr_init+512);
bt0->loop();
bt1->loop();
bt2->loop();
}
Ledstrip Display & GraphicsI used the Adafruit NeoMatrix library to create the display object with the 8x32 ws2812b led strip used as a 2D matrix. Then you just can use and Adafruit GFX primitives to write onto the display (2D screens (x/y): points, lines, circles, etc.).
The graphic work was quite simple with the Tetris game. Graphic objects are simply declared as small bitmap objects (as often with graphic games objects). I also paste here the tetris block and tetris stack declaration as an implementation overview :
const uint8_t blockI[2] = {
B1111,
B0000
};
const uint8_t blockO[2] = {
B0011,
B0011
};
const uint8_t blockT[2] = {
B0010,
B0111
};
const uint8_t blockS[2] = {
B0011,
B0110
};
const uint8_t blockZ[2] = {
B0110,
B0011
};
const uint8_t blockJ[2] = {
B0111,
B0100
};
const uint8_t blockL[2] = {
B0111,
B0001
};
struct RGB {
byte r;
byte g;
byte b;
};
class tBlock { // tetris block
private:
RGB color;
void minmax(int _x, int _y);
public:
int x0;
int y0;
uint8_t *b;
int x_max;
int y_xmax;
int x_min;
int y_xmin;
int rotation;
int width;
int height;
bool blockedRotation;
bool blockedDown;
bool blockedRight;
bool blockedLeft;
tBlock(uint8_t _b[2], RGB _c, int _x0, int _y0);
void draw(Adafruit_NeoMatrix *_m);
void moveDown(void);
void moveRight(void);
void moveLeft(void);
void rotate(void);
void freeze(void);
void unfreeze(void);
void reset(void);
};
class tStack { // tetris stack
private:
uint8_t stack[TETRIS_STACK_HEIGHT];
bool empty;
RGB color;
int height(int c);
void removeLine(int i);
public:
tStack(RGB _c);
bool contact(tBlock *_b);
bool merge(tBlock *_b);
void moveDown(tBlock *b);
int searchFullLines(void);
void animeFullLine(Adafruit_NeoMatrix *_m, int i);
int removeFullLines(Adafruit_NeoMatrix *_m);
void clean(void);
void dump(void);
void draw(Adafruit_NeoMatrix *_m);
};
Sound and MusicA very small research allowed to find github contributions to the well known tetris music for arduino boards.
I kindly hacked what I found in order the music to play and not to block the game.
Music is something that can benefit from great SRAM usage optimization. As all music is known before the program to run, it means all music or FX sounds is static data we can store on Flash Memory rather than on SRAM using the PROGMEM feature of the Arduino UNO board.
What to do next ?Fix a couple of bugs (the ghost brick feature, you need play!)
Remove the cheat button in this tetris game (would you find it ?)
Short term
Work on the game storage on an SD card. Would need to have a specific program on board to be able load a game.
Add a second joystick to this prototype (split the screen in 2 parts, bottom of the tetris stack in the middle of the screen, when a user complete a line it pushes a line into other player screen).
Long term
Investigate Arduino UNO with wifi and think about network gaming prototypes.
Comments