En este proyecto se busca hacer una mini version de tetris, con el fin de mostrar la potencialidad de un arduino uno y la pantalla oled i2c de 0.96″
Elementos necesarios
- Arduino Uno
- Pantalla oled SSD1306
- Joystick
- Cables de conexión
Esquemático
En el siguiente diagrama se muestra la conexión electrica necesaria, en donde se debe tener en cuenta el SCL y SDA del arduino (i2c) para la conexion de pantalla y las entradas análogas A0 y A1
Código
Puedes descargar el código en el siguiente link de github, sin embargo lo colocamos tambien en este mismo block
IMPORTANTE: debes verificar la direccion i2c de tu pantalla y reemplazar la dirección hexadecimal en #define OLED_ADDRESS
#include <Wire.h>
#define OLED_ADDRESS 0x3C //you may need to change this, this is the OLED I2C address.
#define OLED_COMMAND 0x80
#define OLED_DATA 0x40
#define OLED_DISPLAY_OFF 0xAE
#define OLED_DISPLAY_ON 0xAF
#define OLED_NORMAL_DISPLAY 0xA6
#define OLED_INVERSE_DISPLAY 0xA7
#define OLED_SET_BRIGHTNESS 0x81
#define OLED_SET_ADDRESSING 0x20
#define OLED_HORIZONTAL_ADDRESSING 0x00
#define OLED_VERTICAL_ADDRESSING 0x01
#define OLED_PAGE_ADDRESSING 0x02
#define OLED_SET_COLUMN 0x21
#define OLED_SET_PAGE 0x22
#define KEYPAD_PIN A0
#define KEYPAD_PIN2 A1
//The pieces , To do: this should go in program memory-
const bool BlockI[4][4] = { { 0, 1, 0, 0 },{ 0, 1, 0, 0 },{ 0, 1, 0, 0 },{ 0, 1, 0, 0 }, };
const bool BlockJ[4][4] = { { 0, 1, 0, 0 },{ 0, 1, 0, 0 },{ 1, 1, 0, 0 },{ 0, 0, 0, 0 }, };
const bool BlockL[4][4] = { { 0, 1, 0, 0 },{ 0, 1, 0, 0 },{ 0, 1, 1, 0 },{ 0, 0, 0, 0 }, };
const bool BlockO[4][4] = { { 0, 0, 0, 0 },{ 0, 1, 1, 0 },{ 0, 1, 1, 0 },{ 0, 0, 0, 0 }, };
const bool BlockS[4][4] = { { 0, 0, 0, 0 },{ 0, 1, 1, 0 },{ 1, 1, 0, 0 },{ 0, 0, 0, 0 }, };
const bool BlockT[4][4] = { { 0, 0, 0, 0 },{ 1, 1, 1, 0 },{ 0, 1, 0, 0 },{ 0, 0, 0, 0 }, };
const bool BlockZ[4][4] = { { 0, 0, 0, 0 },{ 1, 1, 0, 0 },{ 0, 1, 1, 0 },{ 0, 0, 0, 0 }, };
// the numbers for score, To do: create letter fonts
const byte NumberFont[10][8] PROGMEM = {
{ 0x00, 0x1c, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x1c },
{ 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x0c, 0x08 },
{ 0x00, 0x3e, 0x02, 0x04, 0x18, 0x20, 0x22, 0x1c },
{ 0x00, 0x1c, 0x22, 0x20, 0x18, 0x20, 0x22, 0x1c },
{ 0x00, 0x10, 0x10, 0x3e, 0x12, 0x14, 0x18, 0x10 },
{ 0x00, 0x1c, 0x22, 0x20, 0x20, 0x1e, 0x02, 0x3e },
{ 0x00, 0x1c, 0x22, 0x22, 0x1e, 0x02, 0x04, 0x18 },
{ 0x00, 0x04, 0x04, 0x04, 0x08, 0x10, 0x20, 0x3e },
{ 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c },
{ 0x00, 0x0c, 0x10, 0x20, 0x3c, 0x22, 0x22, 0x1c }
};
#define KEY_LEFT 1
#define KEY_RIGHT 2
#define KEY_DOWN 3
#define KEY_ROTATE 4
byte uiKeyLeft = 2;
byte uiKeyRight = 3;
byte uiKeyDown = 4;
byte uiKeyRotate = 5;
//struct for Key press control
struct keyPress
{
long left;
long right;
long down;
long rotate;
};
//struct for pieces
struct PieceSpace
{
byte umBlock[4][4];
char Row;
char Coloum;
};
//Globals, is a mess. To do: tidy up and reduce glogal use if possible
byte pageArray[8] = { 0 };
byte scoreDisplayBuffer[8][6] = { { 0 },{ 0 } };
byte nextBlockBuffer[8][2] = { { 0 },{ 0 } };
bool optomizePageArray[8] = { 0 };
byte blockColoum[10] = { 0 };
byte tetrisScreen[14][25] = { { 1 } ,{ 1 } };
PieceSpace currentPiece = { 0 };
PieceSpace oldPiece = { 0 };
byte nextPiece = 0;
keyPress key = { 0 };
bool gameOver = false;
unsigned long moveTime = 0;
int pageStart = 0;
int pageEnd = 0;
int score = 0;
int acceleration = 0;
int level = 0;
int levellineCount = 0;
int dropDelay = 1000;
// I2C
void OLEDCommand(byte command) {
Wire.beginTransmission(OLED_ADDRESS);
Wire.write(OLED_COMMAND);
Wire.write(command);
Wire.endTransmission();
}
void OLEDData(byte data) {
Wire.beginTransmission(OLED_ADDRESS);
Wire.write(OLED_DATA);
Wire.write(data);
Wire.endTransmission();
}
void setup()
{
Serial.begin(9600);
while (!Serial) { ; }
Wire.begin();
Wire.setClock(400000);
OLEDCommand(OLED_DISPLAY_OFF);
delay(20);
OLEDCommand(OLED_DISPLAY_ON);
delay(20);
OLEDCommand(OLED_NORMAL_DISPLAY);
delay(20);
OLEDCommand(0x8D);
delay(20);
OLEDCommand(0x14);
delay(20);
OLEDCommand(OLED_NORMAL_DISPLAY);
fillTetrisScreen(0);
randomSeed(analogRead(7)); /// To do: create a decent random number generator.
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
delay(100);
digitalWrite(13, LOW);
delay(200);
digitalWrite(13, HIGH);
delay(50);
digitalWrite(13, LOW);
}
void fillTetrisArray(byte value)
{
for (char r = 0; r < 24; r++)
{
for (char c = 0; c < 14; c++)
{
tetrisScreen[c][r] = value;
}
}
for (char r = 21; r < 24; r++) for (char c = 0; c < 14; c++) tetrisScreen[c][r] = 0;
}
void fillTetrisScreen(byte value)
{
for (int r = 1; r < 21; r++)
{
for (int c = 2; c < 12; c++)
{
tetrisScreen[c][r] = value;
}
}
}
void drawTetrisScreen()
{
for (byte r = 1; r < 21; r++)
{
//loop through rows to see if there is data to be sent
for (byte c = 2; c < 12; c++)
{
if ((tetrisScreen[c][r] == 2) | (tetrisScreen[c][r] == 3))
{
//send line to screen
for (byte i = 0; i < 10; i++)/// i=2 i<12!!
{
blockColoum[i] = tetrisScreen[i + 2][r];
//clear delete block
if (tetrisScreen[i + 2][r] == 3) tetrisScreen[i + 2][r] = 0;
}
drawTetrisLine((r - 1) * 6);
break; break;
}
}
}
}
void drawTetrisLine(byte x)
{
//fill array with blocks based on blockRow
//clear page and Optimize array
memset(optomizePageArray, 0, 8); ///review this... declare them here? interesting question...
memset(pageArray, 0, 8);
x++; // up one
//*********Column 0***********
//draw block
if (blockColoum[0] == 2 | blockColoum[0] == 1)
{
pageArray[0] = pageArray[0] | B11111001;
optomizePageArray[0] = 1;
}
//delete block
if (blockColoum[0] == 3)
{
pageArray[0] = pageArray[0] | B00000001; //create side wall
pageArray[0] = pageArray[0] & B00000111;
optomizePageArray[0] = 1;
}
//*********Column 1***********
if (blockColoum[1] == 2 | blockColoum[1] == 1)
{
pageArray[1] = pageArray[1] | B00111110;
optomizePageArray[1] = 1;
}
//delete block
if (blockColoum[1] == 3)
{
pageArray[1] = pageArray[1] & B11000001;
optomizePageArray[1] = 1;
}
//*********Column 2***********
if (blockColoum[2] == 2 | blockColoum[2] == 1)
{
pageArray[1] = pageArray[1] | B10000000;
optomizePageArray[1] = 1;
pageArray[2] = pageArray[2] | B00001111;
optomizePageArray[2] = 1;
}
//delete block
if (blockColoum[2] == 3)
{
pageArray[1] = pageArray[1] & B01111111;
optomizePageArray[1] = 1;
pageArray[2] = pageArray[2] & B11110000;
optomizePageArray[2] = 1;
}
//*********Column 3***********
if (blockColoum[3] == 2 | blockColoum[3] == 1)
{
pageArray[2] = pageArray[2] | B11100000;
optomizePageArray[2] = 1;
pageArray[3] = pageArray[3] | B00000011;
optomizePageArray[3] = 1;
}
//delete block
if (blockColoum[3] == 3)
{
pageArray[2] = pageArray[2] & B00011111;
optomizePageArray[2] = 1;
pageArray[3] = pageArray[3] & B11111100;
optomizePageArray[3] = 1;
}
//*********Column 4***********
if (blockColoum[4] == 2 | blockColoum[4] == 1)
{
pageArray[3] = pageArray[3] | B11111000;
optomizePageArray[3] = 1;
}
//delete block
if (blockColoum[4] == 3)
{
pageArray[3] = pageArray[3] & B00000111;
optomizePageArray[3] = 1;
}
//*********Column 5***********
if (blockColoum[5] == 2 | blockColoum[5] == 1)
{
pageArray[4] = pageArray[4] | B00111110;
optomizePageArray[4] = 1;
}
//delete block
if (blockColoum[5] == 3)
{
pageArray[4] = pageArray[4] & B11000001;
optomizePageArray[4] = 1;
}
//*********Column 6***********
if (blockColoum[6] == 2 | blockColoum[6] == 1)
{
pageArray[4] = pageArray[4] | B10000000;
optomizePageArray[4] = 1;
pageArray[5] = pageArray[5] | B00001111;
optomizePageArray[5] = 1;
}
//delete block
if (blockColoum[6] == 3)
{
pageArray[4] = pageArray[4] & B01111111;
optomizePageArray[4] = 1;
pageArray[5] = pageArray[5] & B11110000;
optomizePageArray[5] = 1;
}
//*********Column 7***********
if (blockColoum[7] == 2 | blockColoum[7] == 1)
{
pageArray[5] = pageArray[5] | B11100000;
optomizePageArray[5] = 1;
pageArray[6] = pageArray[6] | B00000011;
optomizePageArray[6] = 1;
}
if (blockColoum[7] == 3)
{
pageArray[5] = pageArray[5] & B00011111;
optomizePageArray[5] = 1;
pageArray[6] = pageArray[6] & B11111100;
optomizePageArray[6] = 1;
}
//*********Column 8***********
if (blockColoum[8] == 2 | blockColoum[8] == 1)
{
pageArray[6] = pageArray[6] | B11111000;
optomizePageArray[6] = 1;
}
//delete block
if (blockColoum[8] == 3)
{
pageArray[6] = pageArray[6] & B00000111;
optomizePageArray[6] = 1;
}
//*********Column 9***********
if (blockColoum[9] == 2 | blockColoum[9] == 1)
{
pageArray[7] = pageArray[7] | B10111110;
optomizePageArray[7] = 1;
}
if (blockColoum[9] == 3)
{
pageArray[7] = pageArray[7] | B10000000;//create side wall
pageArray[7] = pageArray[7] & B11000001;
optomizePageArray[7] = 1;
}
//Optimize - figure out what page array has data
for (int page = 0; page < 8; page++)
{
if (optomizePageArray[page])
{
//block found set page start
pageStart = page;
break;
}
}
for (int page = 7; page >= 0; page--)
{
if (optomizePageArray[page])
{
//block found set page end
pageEnd = page;
break;
}
}
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(x);
OLEDCommand(x + 4);
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(pageStart);
OLEDCommand(pageEnd);
//send the array 5 times
for (int c = 0; c <5; c++)
{
for (int p = pageStart; p <= pageEnd; p++)
{
OLEDData(pageArray[p]);
}
}
}
void loadPiece(byte peiceNumber, byte row, byte coloum, bool loadScreen)
{
//load the piece from piece array to screen
byte pieceRow = 0;
byte pieceColoum = 0;
byte c = 0;
switch (peiceNumber)
{
case 1: memcpy(currentPiece.umBlock, BlockI, 16); break;
case 2: memcpy(currentPiece.umBlock, BlockJ, 16); break;
case 3: memcpy(currentPiece.umBlock, BlockL, 16); break;
case 4: memcpy(currentPiece.umBlock, BlockO, 16); break;
case 5: memcpy(currentPiece.umBlock, BlockS, 16); break;
case 6: memcpy(currentPiece.umBlock, BlockT, 16); break;
case 7: memcpy(currentPiece.umBlock, BlockZ, 16); break;
}
currentPiece.Row = row;
currentPiece.Coloum = coloum;
if (loadScreen) {
oldPiece = currentPiece;
for (c = coloum; c < coloum + 4; c++)
{
for (int r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 2;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
}
}
void drawPiece()
{
char coloum;
char row;
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
// delete blocks first
coloum = oldPiece.Coloum;
row = oldPiece.Row;
for (c = coloum; c < coloum + 4; c++)
{
for (char r = row; r < row + 4; r++) {
if (oldPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 3;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
//draw new blocks
pieceRow = 0;
pieceColoum = 0;
c = 0;
coloum = currentPiece.Coloum;
row = currentPiece.Row;
for (c = coloum; c < coloum + 4; c++)
{
for (char r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 2;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
}
void drawLandedPiece()
{
char coloum;
char row;
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
// Landed pieces are 1
coloum = currentPiece.Coloum;
row = currentPiece.Row;
for (c = coloum; c < coloum + 4; c++)
{
for (int r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 1;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
processCompletedLines();
}
bool led = true;
void RotatePiece()
{
byte i, j;
byte umFig[4][4] = { 0 };
memcpy(oldPiece.umBlock, currentPiece.umBlock, 16);
oldPiece.Row = currentPiece.Row;
oldPiece.Coloum = currentPiece.Coloum;
for (i = 0; i < 4; ++i)
for (j = 0; j < 4; ++j)
umFig[j][i] = currentPiece.umBlock[4 - i - 1][j];
oldPiece = currentPiece;
memcpy(currentPiece.umBlock, umFig, 16);
if (checkColloision()) currentPiece = oldPiece;
// no need for this...
if (led) { digitalWrite(13, HIGH); led = false; }
delay(1);
digitalWrite(13, LOW);
if (led == false) { digitalWrite(13, LOW); led = true; }
}
bool movePieceDown()
{
bool pieceLanded = false;
char rndPiece = 0;
oldPiece = currentPiece;
currentPiece.Row = currentPiece.Row - 1;
//check collision
if (checkColloision()) {
// its at the bottom make it a landed piece and start new piece
currentPiece = oldPiece; // back to where it was
drawLandedPiece();
pieceLanded = true;
}
if (pieceLanded)
{
loadPiece(nextPiece, 19, 4, false);
acceleration = 0;
if (checkColloision()) gameOver = true;
else
{
loadPiece(nextPiece, 19, 4, true);
acceleration = 0;//reset acceleration as there is a new piece
}
nextPiece = random(1, 7);
setNextBlock(nextPiece);
}
}
void movePieceLeft() {
oldPiece = currentPiece;
currentPiece.Coloum = currentPiece.Coloum - 1;
//check collision
if (checkColloision()) currentPiece = oldPiece; // back to where it was
}
void movePieceRight() {
oldPiece = currentPiece;
currentPiece.Coloum = currentPiece.Coloum + 1;
//check collision
if (checkColloision()) currentPiece = oldPiece; // back to where it was
}
bool checkColloision()
{
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
char coloum = currentPiece.Coloum;
char row = currentPiece.Row;
//scan across piece and translate to Tetris array and check Collisions.
for (c = coloum; c < coloum + 4; c++)
{
for (char r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow])
{
if (tetrisScreen[c][r] == 1) return true; //is it on landed blocks?
}
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
return false;
}
void processCompletedLines() {
char rowCheck = 0;
char coloumCheck = 0;
bool fullLine = false;
bool noLine = true;
char linesProcessed = 0;
char clearedLines = 0;
char topRow = 0;
char bottomRow = 0;
char currentRow = 0;
int amountScored = 0;
if (currentPiece.Row < 1)bottomRow = 1;
else bottomRow = currentPiece.Row;
for (int rowCheck = bottomRow; rowCheck < currentPiece.Row + 4; rowCheck++)
{
bool fullLine = true;
for (coloumCheck = 2; coloumCheck < 12; coloumCheck++) {
if (tetrisScreen[coloumCheck][rowCheck] == 0) { fullLine = false; break; }
}
if (fullLine)
{
//make line values 3's and render
for (char c = 2; c < 12; c++) tetrisScreen[c][rowCheck] = 3;
bottomRow = rowCheck + 1;
//line is now all 0's
linesProcessed++;
delay(77); // animation :)
}
drawTetrisScreen();
}
//******all lines are 0's and have been removed from the screen
if (linesProcessed)
{
clearedLines = linesProcessed;
while (clearedLines) {
for (currentRow = 1; currentRow < 20; currentRow++) {
noLine = true;
for (char c = 2; c < 12; c++)
{
if (tetrisScreen[c][currentRow]) noLine = false;
}
if (noLine) {
//move all lines down
for (int r = currentRow + 1; r < 20; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r]) tetrisScreen[c][r - 1] = 2;
else tetrisScreen[c][r - 1] = 3;
}
}
}
}
//make the 2's 1's
for (char r = 1; r < 24; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r] == 2)tetrisScreen[c][r] = 1;
}
}
clearedLines--;
drawTetrisScreen();
}
}
// ************** process score *******************
switch (linesProcessed)
{
case 1: amountScored = 40 * (level + 1); break;
case 2: amountScored = 100 * (level + 1); break;
case 3: amountScored = 300 * (level + 1); break;
case 4: amountScored = 1200 * (level + 1);
//do 4 line affect
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(100);
OLEDCommand(OLED_NORMAL_DISPLAY);
break;
}
//score animation
for (long s = score; s < score + amountScored; s = s + (1 * (level+1))) setScore(s, false);
score = score + amountScored;
setScore(score, false);
//****update level line count
levellineCount = levellineCount + linesProcessed;
if (levellineCount > 10) {
level++;
levellineCount = 0;
//do level up affect
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(100);
OLEDCommand(OLED_NORMAL_DISPLAY);
delay(100);
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(100);
OLEDCommand(OLED_NORMAL_DISPLAY);
}
//make the 2's 1's
for (char r = bottomRow; r <= topRow; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r]) tetrisScreen[c][r] = 1;
}
}
}
void tetrisScreenToSerial()
{
//for debug
for (int r = 0; r < 24; r++)
{
for (int c = 0; c < 14; c++)
{
Serial.print(tetrisScreen[c][r], DEC);
}
Serial.println();
}
Serial.println();
}
long keytimer = 0;
bool processKey = true;
int Debounce = 0;
bool processKeys()
{
// not happy with this, To do: sort this out and get the movment right!
char uiKeyCode = 0;
bool keypressed = true;
int leftRight = 300 - acceleration;
int rotate = 700;
int down = 110 - acceleration;
int analogKey = analogRead(KEYPAD_PIN);
int analogKey2 = analogRead(KEYPAD_PIN2);
//Serial.println(analogKey);
//delay(500);
Serial.println(analogKey2);
// delay(500);
if ((analogKey > -1) && (analogKey < 300)) {
Debounce++;
if (Debounce > 10) {
if (processKey) {
uiKeyCode = KEY_LEFT; //key will be processed immediately
key.left = millis();
}
if (millis() < key.left + leftRight) processKey = false;
else {
processKey = true;
acceleration = acceleration + 70;
if (acceleration > leftRight) acceleration = leftRight;
}
}
}
else if ((analogKey > 700) && (analogKey < 1030)) {
Debounce++;
if (Debounce > 10) {
if (processKey) {
uiKeyCode = KEY_RIGHT; //key will be processed immediately
key.right = millis();
}
if (millis() < key.right + leftRight) processKey = false;
else {
processKey = true;
acceleration = acceleration + 70;
if (acceleration > leftRight) acceleration = leftRight;
}
}
}
else if ((analogKey2 > -1) && (analogKey2 < 300)) {
Serial.println("SI");
Debounce++;
if (Debounce > 10) {
if (processKey) {
uiKeyCode = KEY_DOWN; //key will be processed immediately
key.down = millis();
}
if (millis() < key.down + down) processKey = false;
else {
processKey = true;
acceleration = acceleration + 40;
if (acceleration > down) acceleration = down;
}
}
}
else if ((analogKey2 > 700) && (analogKey2 < 1030)) {
Debounce++;
if (Debounce > 10) {
if (processKey) {
uiKeyCode = KEY_ROTATE; //key will be processed immediately
key.rotate = millis();
}
if (millis() < key.rotate + rotate) processKey = false;
else processKey = true;
}
}
else {
acceleration = 0; processKey = true; Debounce = 0;
}
switch (uiKeyCode)
{
case KEY_LEFT: movePieceLeft(); break;
case KEY_RIGHT: movePieceRight(); break;
case KEY_DOWN: movePieceDown(); break;
case KEY_ROTATE: RotatePiece(); break;
default: keypressed = false; break;
}
if (keypressed)
{
drawPiece();
drawTetrisScreen();
}
}
void setScore(long score, bool blank)
{
// this is a kludge. To do: create a proper system for rendering numbers and letters.
long ones = (score % 10);
long tens = ((score / 10) % 10);
long hundreds = ((score / 100) % 10);
long thousands = ((score / 1000) % 10);
long tenthousands = ((score / 10000) % 10);
long hunderedthousands = ((score / 100000) % 10);
//Serial.println(ones);
//Serial.println(tens);
//Serial.println(hundreds);
//Serial.println(thousands);
//Serial.println(tenthousands);
//Serial.println(hunderedthousands);
//create the score in upper left part of the screen
byte font = 0;
char bytes_out[8];
memset(scoreDisplayBuffer, 0, sizeof scoreDisplayBuffer);
//****************score digit 6****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[hunderedthousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][0] = scoreDisplayBuffer[i][0] | bytes_out[i] >> 1;
}
//****************score digit 5****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[tenthousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][0] = scoreDisplayBuffer[i][0] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][1] = scoreDisplayBuffer[i][1] | bytes_out[i] >> 1;
}
//****************score digit 4****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[thousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][1] = scoreDisplayBuffer[i][1] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][2] = scoreDisplayBuffer[i][2] | bytes_out[i] >> 1;
}
//****************score digit 3****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[hundreds][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][2] = scoreDisplayBuffer[i][2] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][3] = scoreDisplayBuffer[i][3] | bytes_out[i] >> 1;
}
//****************score digit 2****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[tens][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][3] = scoreDisplayBuffer[i][3] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][4] = scoreDisplayBuffer[i][4] | bytes_out[i] >> 1;
}
//****************score digit 1****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[ones][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][4] = scoreDisplayBuffer[i][4] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][5] = scoreDisplayBuffer[i][5] | bytes_out[i] >> 1;
}
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(120); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(5); //Set page end
for (int p = 0; p < 8; p++)
{
for (int c = 0; c <6; c++)
{
if (blank) OLEDData(0);
else OLEDData(scoreDisplayBuffer[p][c]);
}
}
}
void setNextBlock(byte peiceNumber)
{
memset(nextBlockBuffer, 0, sizeof nextBlockBuffer); //clear buffer
switch (peiceNumber)
{
case 1:
//************l piece - 1 *************
for (int k = 2; k < 6; k++)
{
nextBlockBuffer[k][0] = B01110111;
nextBlockBuffer[k][1] = B01110111;
}
break;
case 2:
//************J piece - 2 *************
for (int k = 0; k < 3; k++)
{
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
for (int k = 4; k < 7; k++)
{
nextBlockBuffer[k][0] = B01110000;
}
break;
case 3:
//************L piece - 3 *************
for (int k = 0; k < 3; k++)
{
nextBlockBuffer[k][0] = B01110000;
}
for (int k = 4; k < 7; k++)
{
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
break;
case 4:
//************O piece - 4 *************
for (int k = 0; k < 3; k++)
{
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++)
{
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
break;
case 5:
//************S piece - 5 *************
for (int k = 0; k < 3; k++)
{
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++)
{
nextBlockBuffer[k][0] = B00000000;
nextBlockBuffer[k][1] = B11101110;
}
break;
case 6:
//************T piece - 6 *************
for (int k = 0; k < 3; k++)
{
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
for (int k = 4; k < 7; k++)
{
nextBlockBuffer[k][0] = B00000000;
nextBlockBuffer[k][1] = B00001110;
}
break;
case 7:
//************Z piece - 7 *************
for (int k = 0; k < 3; k++)
{
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++)
{
nextBlockBuffer[k][0] = B11101110;
nextBlockBuffer[k][1] = B00000000;
}
break;
}
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(120); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(6); //Set page start
OLEDCommand(7); //Set page end
for (int p = 0; p < 8; p++)
{
for (int c = 0; c <2; c++)
{
OLEDData(nextBlockBuffer[p][c]);
}
}
}
void drawBottom()
{
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(0); //Set column start
OLEDCommand(0); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(7); //Set page end
for (int c = 0; c <8; c++)
{
OLEDData(255);
}
}
void drawSides()
{
//set Vertical addressing mode and column - page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(0); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(7); //Set page end
for (int r = 0; r < 128; r++) {
for (int c = 0; c < 8; c++)
{
if (c == 0) OLEDData(1);
else if (c == 7) OLEDData(128);
else OLEDData(0);
}
}
}
void loop()
{
//main loop code
//To do: create high score system that savees to EEprom
gameOver = false;
score = 0;
fillTetrisArray(1); //fill with 1's to make border
fillTetrisScreen(2);
drawTetrisScreen();
delay(200);
fillTetrisScreen(3);
drawTetrisScreen();
delay(200);
drawSides();
drawBottom();
tetrisScreenToSerial();
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(200);
OLEDCommand(OLED_NORMAL_DISPLAY);
loadPiece(random(1, 7), 20, 5, true);
drawTetrisScreen();
nextPiece = random(1, 7);
setNextBlock(nextPiece);
setScore(0, false);
delay(300);
setScore(0, true);
delay(300);
setScore(0, false);
byte rnd = 0;
//for (int j = 0; j < 1000000; j++) {
// setScore(j, false);
//}
while (!gameOver)
{
movePieceDown();
drawPiece();
drawTetrisScreen();
moveTime = millis();
while (millis() - moveTime<(dropDelay - (level * 50))) processKeys();
}
}
Simulación
Puedes encontrar la simulacion del codigo en el siguiente link de wokwi
porfa un tutorial mas detallado porfa