ffrouin
Published © GPL3+

Arduino UNO as a Retro Game Station

A joystick, a display, sound, let's play Tetris !

IntermediateWork in progress3 hours194
Arduino UNO as a Retro Game Station

Things used in this project

Hardware components

ws2812b 8x32 matrix
https://fr.aliexpress.com/item/1005002489849443.html
×1
Buzzer
Buzzer
×1
Arduino UNO
Arduino UNO
×1
Single Turn Potentiometer- 10k ohms
Single Turn Potentiometer- 10k ohms
×1
Analog joystick (Generic)
https://www.amazon.fr/dp/B08SQ7ZVST
×1
Telephone Modular Cable, 4P4C Handset Modular Plug to 4P4C Handset Modular Plug
Telephone Modular Cable, 4P4C Handset Modular Plug to 4P4C Handset Modular Plug
I used an old telephony cable that have 8 copper wires
×1

Story

Read more

Schematics

Circuit Diagram

Joystick prototype (recto)

very bad circuit example (lookup to circuit diagram), but working...

Joystick prototype (verso)

Joystick link to Arduino UNO

telephony cable (8 copper wires inside)

Arduino UNO board

a variable resistor for display brightness and a buzzer

Tetris display sample

Tetris Demo with Sound

Code

Tetris Game

C/C++
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <MemoryFree.h>
#include "joystick.h"
#include "tetris.h"
#include "tetrisMusic.h"

#define SCREEN_PIN 6
#define BRIGHTNESS_PIN 5
#define MAX_BRIGHTNESS 5

joystick j1(0,1,2,7,8);

int playSpeed = 400;
int makeItPlayable = 50;

RGB stackColor = { 255, 255, 255 };

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(8, 32, SCREEN_PIN,
  NEO_MATRIX_TOP     + NEO_MATRIX_RIGHT +
  NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
  NEO_GRB            + NEO_KHZ800);

void userBrightness() {
  int ub = analogRead(BRIGHTNESS_PIN);
  float b = ub;
  b /= 1024;
  b *= MAX_BRIGHTNESS;
  matrix.setBrightness(floor(b));
}

tBlockI myBlockI(1,0);
tBlockO myBlockO(1,0);
tBlockZ myBlockZ(1,0);
tBlockS myBlockS(1,0);
tBlockT myBlockT(1,0);
tBlockJ myBlockJ(1,0);
tBlockL myBlockL(1,0);

tBlock *currentBlock = &myBlockI;
tStack p1Stack(stackColor);

int blockIndex = 0;

unsigned long int start = millis();
unsigned long int contact = 0;

void nextBlock(void) {
  blockIndex = random(7);
  switch (blockIndex) {
      case 0:
        currentBlock=&myBlockO;
        break;
      case 1:
        currentBlock=&myBlockZ;
        break;
      case 2:
        currentBlock=&myBlockS;
        break;
      case 3:
        currentBlock=&myBlockT;
        break;
      case 4:
        currentBlock=&myBlockJ;
        break;
      case 5:
        currentBlock=&myBlockL;
        break;
      case 6:
        currentBlock=&myBlockI;
        break;
    }
}

void melodyGameOver() {
  // kindly copied and modifyed from https://www.hackster.io/dennis-v/buzz-wire-deluxe-650cbc
  tone(11, NOTE_A4);
  delay(300);
  tone(11, NOTE_GS4);
  delay(300);
  tone(11, NOTE_G4);
  delay(300);
  tone(11, NOTE_FS4);
  delay(300);
  tone(11, NOTE_F4);
  delay(1000);
  noTone(11);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println();
  Serial.println("Starting");
  Serial.println(String(freeMemory()) + " bytes RAM free");
  matrix.begin();
  userBrightness();
  matrix.fill(0);
  matrix.show();
}

  bool blockAlreadySentDown = false;

void loop() {
  bool blockAlreadyMovedDown = false;
  userBrightness();

  matrix.fill(0);
  currentBlock->draw(&matrix);
  p1Stack.draw(&matrix);

  matrix.show();
 // Serial.println(String(freeMemory()) + " bytes RAM free");
  // put your main code here, to run repeatedly:
  j1.loop();
  if (j1.bt0->isPressed()) {
    if (++blockIndex == 7)
      blockIndex = 0;
    switch (blockIndex) {
      case 0:
        currentBlock=&myBlockO;
        break;
      case 1:
        currentBlock=&myBlockZ;
        break;
      case 2:
        currentBlock=&myBlockS;
        break;
      case 3:
        currentBlock=&myBlockT;
        break;
      case 4:
        currentBlock=&myBlockJ;
        break;
      case 5:
        currentBlock=&myBlockL;
        break;
      case 6:
        currentBlock=&myBlockI;
        break;
    }
  }
  if (j1.bt1->isPressed()) {
    currentBlock->rotate();
  }
  if (j1.bt2->isPressed()) {
    if (!blockAlreadySentDown) {
      blockAlreadySentDown = true;
      p1Stack.moveDown(currentBlock);
    }
  }

  if (p1Stack.contact(currentBlock) || currentBlock->y0 > TETRIS_STACK_HEIGHT) {
    if (contact == 0)
      contact = millis();
    if (millis()-contact>600) {
      currentBlock->freeze();
      if (p1Stack.merge(currentBlock)) {
        currentBlock->reset();
        blockAlreadySentDown = false;
        contact = 0;
        int linesDone = p1Stack.removeFullLines(&matrix);
          playSpeed -= 10*linesDone;
          tempo += 2*linesDone;
          int lineTone = 440 + linesDone*200;
         for(int i=lineTone;i>440;i--){ //Starting at 800 and decreasing to 500
           tone(11,i); //play the tone with pitch equal to variable 'i'
           delay(2); //delay 1 millisecond before changing the pitch and playing again
      }
        randomSeed(micros());
        nextBlock();
      } else {
        melodyGameOver();
        for (int i=0; i<3; i++) {
          matrix.rainbow();
          matrix.show();
          delay(500);
          matrix.fill(0);
          matrix.show();
          delay(500);
        }
        contact = 0;
        p1Stack.clean();
        currentBlock->reset();
        blockAlreadySentDown = false;
        nextBlock();
        // game over
      }
    }
  }

  if (j1.X() > 5)
    currentBlock->moveRight();

  if (j1.X() < -5)
    currentBlock->moveLeft();

  if (j1.Y() < -5) {
    currentBlock->moveDown();
    blockAlreadyMovedDown = true;
  }

  if (millis()-start>playSpeed) {
    if (!blockAlreadyMovedDown)
      currentBlock->moveDown();
    start = millis();
  }
  tetrisMusicPlay();
  delay(makeItPlayable);
}

joystick.h

C/C++
joystick drivers that can refresh at 8kHz
/*
 * by Freddy Frouin <freddy@linuxtribe.fr>
 */
 
#ifndef JOYSTICK_h
#define JOYSTICK_h

#include "Arduino.h"
#include "appButton.h"

#define JOYSTICK_NOISE_FACTOR 20

class joystick {
  private:
	int	ud_pin;
	int lr_pin;	
	
	float ud_init;
	float lr_init;
	
	int ud_pos;
	int lr_pos;
	
  public:
    appButton *bt0;
	appButton *bt1;
	appButton *bt2;
	
    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);
};

#endif

joystick.cpp

C/C++
joystick driver that can refresh at 8kHz
#include "joystick.h"

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();
}

tetris.h

C/C++
tetris objects
/*
 * by Freddy Frouin <freddy@linuxtribe.fr>
 */
 
#ifndef TETRIS_h
#define TETRIS_h

#include "Arduino.h"
#include <Adafruit_NeoMatrix.h>

#define TETRIS_STACK_HEIGHT	32
#define TETRIS_STACK_WIDTH 8

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 {
  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 tBlockI : public tBlock {
  public:
    tBlockI(int _x0, int _y0);
};

class tBlockO : public tBlock {
  public:
    tBlockO(int _x0, int _y0);
};

class tBlockT : public tBlock {
  public:
    tBlockT(int _x0, int _y0);
};

class tBlockS : public tBlock {
  public:
    tBlockS(int _x0, int _y0);
};

class tBlockZ : public tBlock {
  public:
    tBlockZ(int _x0, int _y0);
};

class tBlockJ : public tBlock {
  public:
    tBlockJ(int _x0, int _y0);
};

class tBlockL : public tBlock {
  public:
    tBlockL(int _x0, int _y0);
};

class tStack {
  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);
  
};

#endif

tetris.cpp

C/C++
tetris objects
#include "tetris.h"

RGB cyan = { 0, 255, 255 };
RGB yellow = { 255, 255, 0 };
RGB blue = { 0, 0, 255 };
RGB orange = { 255, 128, 0 };
RGB red = { 255, 0, 0 };
RGB magenta = { 128, 0, 255 };
RGB pink = { 255, 0, 255 };
RGB green = { 0, 255, 0 };
RGB white = { 255, 255, 255 };

tBlock::tBlock(uint8_t _b[2], RGB _c, int _x0, int _y0) {
	b = _b;
  color = _c;
	x0 = _x0;
	y0 = _y0;
  x_min = TETRIS_STACK_WIDTH;
  y_xmin = 0;
  x_max = 0;
  y_xmax = 0;
  rotation = 0;
  blockedRotation = false;
  blockedDown = false;
  blockedRight = false;
  blockedLeft = false;
}

void tBlock::rotate(void) {
  if (!blockedRotation)
    if (++rotation == 4)
      rotation = 0;
}

void tBlock::minmax(int _x, int _y) {
  if (_x<x_min) {
    x_min = _x;
    y_xmin = _y;
  }
  if (_x>x_max) {
     x_max = _x;
     y_xmax = _y;
  }
}

void tBlock::draw(Adafruit_NeoMatrix *_m) {
  int x = 0;
  int y = 0;
  x_max = 0;
  x_min = TETRIS_STACK_WIDTH;
  
  for (int l=0; l < 2; l++) {
	  	for (int c=0; c < 4; c++) {
        if (bitRead(b[l],c)) {
          switch (rotation) {
            case 0:
              x = x0+c-1;
              y = y0+l;
              width = c+1;
              height = l+1;
              minmax(x,y);
              break;
            case 1:
              x = x0-l;
              y = y0+c-1;
              width = l+1;
              height = c+1;
              minmax(x,y);          
              break;
            case 2:
              x = x0-c+1;
              y = y0-l;
              width = c+1;
              height = l+1;
              minmax(x,y);
              break;
            case 3:
              x = x0+l;
              y = y0-c+1;
              width = l+1;
              height = c+1;   
              minmax(x,y);         
              break;
            default:
              break;
          }
          _m->drawPixel(x, y, _m->Color(color.r, color.g, color.b));
       }
	  }
  }
 // _m->drawPixel(x0, y0, _m->Color(white.r, white.g, white.b)); // if you need see x0,y0
}

void tBlock::moveRight(void) {
  if (!blockedRight)
    if (x_max<7)
      x0++;
}

void tBlock::moveLeft(void) {
  if (!blockedLeft)
    if (x_min>0)
      x0--;
}

void tBlock::moveDown(void) {
  if (!blockedDown)
    y0++;
}

void tBlock::freeze(void) {
  blockedRotation = true;
  blockedDown = true;
  blockedRight = true;
  blockedLeft = true;
  blockedRotation = true;
}

void tBlock::unfreeze(void) {
  blockedRotation = false;
  blockedDown = false;
  blockedRight = false;
  blockedLeft = false;
  blockedRotation = false;
}

void tBlock::reset(void) {
  x0 = 1;
  y0 = 0;
  rotation = 0;
  unfreeze();
}

tBlockI::tBlockI(int _x0, int _y0) : tBlock(blockI,cyan,_x0,_y0) {
  
}

tBlockO::tBlockO(int _x0, int _y0) : tBlock(blockO,yellow,_x0,_y0) {
  
}

tBlockT::tBlockT(int _x0, int _y0) : tBlock(blockT,pink,_x0,_y0) {
  
}

tBlockS::tBlockS(int _x0, int _y0) : tBlock(blockS,red,_x0,_y0) {
  
}

tBlockZ::tBlockZ(int _x0, int _y0) : tBlock(blockZ,green,_x0,_y0) {
  
}

tBlockJ::tBlockJ(int _x0, int _y0) : tBlock(blockJ,blue,_x0,_y0) {
  
}

tBlockL::tBlockL(int _x0, int _y0) : tBlock(blockL,orange,_x0,_y0) {
  
}

tStack::tStack(RGB _c) {
  color = _c;
  clean();
}

int tStack::height(int c) {
  if (empty)
    return(0);
    
  for (int i=0; i<TETRIS_STACK_HEIGHT; i++) {
    if (bitRead(stack[i],c))
      return(TETRIS_STACK_HEIGHT-i);
  }
  
  return(0);
}

void tStack::removeLine(int i) {
  for (int j=i; j>0; j--)
    stack[j]=stack[j-1];
}

bool tStack::contact(tBlock *_b) {
  int x = 0;
  int y = 0;
  for (int l=0; l < 2; l++) {
    for (int c=0; c < 4; c++) {
      if (bitRead(_b->b[l],c)) {
        int h=0;
        switch (_b->rotation) {
          case 0:
            x = _b->x0+c-1;
            y = _b->y0+l;
            break;
          case 1:
            x = _b->x0-l;
            y = _b->y0+c-1;
            break;
          case 2:
            x = _b->x0-c+1;
            y = _b->y0-l;
            break;
          case 3:
            x = _b->x0+l;
            y = _b->y0-c+1;  
            break;
          default:
            break;
       }
       h=height(x);
       if (y == TETRIS_STACK_HEIGHT-h-1) {
          _b->blockedDown=true;
           return(true);  
       } else {
         _b->blockedDown=false;       
       }
     }
   }
 }

 if (_b->x_min>0)
   if (bitRead(stack[_b->y_xmin], _b->x_min-1))
     _b->blockedLeft = true;
   else
     _b->blockedLeft = false;

 if (_b->x_max<TETRIS_STACK_WIDTH)
   if (bitRead(stack[_b->y_xmax], _b->x_max+1))
     _b->blockedRight = true;
   else
     _b->blockedRight = false;
   
 return(false);
}

bool tStack::merge(tBlock *_b) {
  int x = 0;
  int y = 0;
  for (int l=0; l < 2; l++) {
    for (int c=0; c < 4; c++) {
      if (bitRead(_b->b[l],c)) {
        empty = false;
        switch (_b->rotation) {
          case 0:
            x = _b->x0+c-1;
            y = _b->y0+l;
            break;
          case 1:
            x = _b->x0-l;
            y = _b->y0+c-1;
            break;
          case 2:
            x = _b->x0-c+1;
            y = _b->y0-l;
            break;
          case 3:
            x = _b->x0+l;
            y = _b->y0-c+1;  
            break;
          default:
            break;  
       }
       if (y<=2)
         return(false);
       bitWrite(stack[y],x,1);
     }
   }
 }
 return(true);
}

void tStack::moveDown(tBlock *_b) {
  int h = 0;
  int x = 0;
  int y = 0;
  
  for (int l=0; l < 2; l++) {
    for (int c=0; c < 4; c++) {
      if (bitRead(_b->b[l],c)) {
        switch (_b->rotation) {
          case 0:
            x = _b->x0+c-1;
            y = _b->y0+l;
            break;
          case 1:
            x = _b->x0-l;
            y = _b->y0+c-1;
            break;
          case 2:
            x = _b->x0-c+1;
            y = _b->y0-l;
            break;
          case 3:
            x = _b->x0+l;
            y = _b->y0-c+1;  
            break;
          default:
            break;
       }
       if (height(x)>h)
        h = height(x);
     }
   }
 } 
 _b->y0 = TETRIS_STACK_HEIGHT-h-_b->height-1;
}

void tStack::draw(Adafruit_NeoMatrix *_m) {
  if (!empty)
    for (int l=0; l<TETRIS_STACK_HEIGHT; l++)
      for (int c=0; c<=8; c++)
        if (bitRead(stack[l],8-c))
          _m->drawPixel(8-c, l, _m->Color(color.r, color.g, color.b));
}

int tStack::searchFullLines(void) {
  if (!empty) {
    int res = 0;
    for (int l=0; l<TETRIS_STACK_HEIGHT; l++) {
      for (int c=0; c<8; c++) {
        res+=bitRead(stack[l],c);
      }
      if (res==8)
        return(l);
      res=0;
    }
  }
  return(0);
}

void tStack::animeFullLine(Adafruit_NeoMatrix *_m, int i) { 
  _m->drawLine(0,i,8,i,0);
  _m->show();
  delay(50);
  _m->drawLine(0,i,8,i,_m->Color(blue.r,blue.g,blue.b));
  _m->show();
  delay(50);
  _m->drawLine(0,i,8,i,0);
  _m->show();
  delay(50);
  _m->drawLine(0,i,8,i,_m->Color(white.r,white.g,white.b));
  _m->show();
  delay(50);
    _m->drawLine(0,i,8,i,0);
  _m->show();
  delay(50);
  _m->drawLine(0,i,8,i,_m->Color(red.r,red.g,red.b));
  _m->show();
  delay(50);
}

int tStack::removeFullLines(Adafruit_NeoMatrix *_m) {
  int rl = 0;
  for (int i=0; i<4; i++) {
    int res = searchFullLines();
    if (res) {
      rl++;
      animeFullLine(_m, res);
      removeLine(res);
    }
  }
  return(rl);
}

void tStack::clean(void) {
  empty = true;
  for (int i=0; i<TETRIS_STACK_HEIGHT; i++)
    stack[i] = B00000000;
}

void tStack::dump(void) {
  if (!empty)
    for (int l=0; l<TETRIS_STACK_HEIGHT; l++) {
      Serial.print("l" + String(l) + " ");
      Serial.println(stack[l], BIN);
    }
}

tetrisMusic.h

C/C++
tetris music
/*
 * by Freddy Frouin <freddy@linuxtribe.fr>
 * kindly hacked from https://github.com/robsoncouto/arduino-songs/blob/master/tetris/tetris.ino
 */

#ifndef TETRISMUSIC_h
#define TETRISMUSIC_h

#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
#define REST 0

// change this to make the song slower or faster
int tempo=120; 

// change this to whichever pin you want to use
int buzzer = 11;

// notes of the moledy followed by the duration.
// a 4 means a quarter note, 8 an eighteenth , 16 sixteenth, so on
// !!negative numbers are used to represent dotted notes,
// so -4 means a dotted quarter note, that is, a quarter plus an eighteenth!!
const int melody[] PROGMEM = {

  //Based on the arrangement at https://www.flutetunes.com/tunes.php?id=192
  
  NOTE_E5, 4,  NOTE_B4,8,  NOTE_C5,8,  NOTE_D5,4,  NOTE_C5,8,  NOTE_B4,8,
  NOTE_A4, 4,  NOTE_A4,8,  NOTE_C5,8,  NOTE_E5,4,  NOTE_D5,8,  NOTE_C5,8,
  NOTE_B4, -4,  NOTE_C5,8,  NOTE_D5,4,  NOTE_E5,4,
  NOTE_C5, 4,  NOTE_A4,4,  NOTE_A4,8,  NOTE_A4,4,  NOTE_B4,8,  NOTE_C5,8,

  NOTE_D5, -4,  NOTE_F5,8,  NOTE_A5,4,  NOTE_G5,8,  NOTE_F5,8,
  NOTE_E5, -4,  NOTE_C5,8,  NOTE_E5,4,  NOTE_D5,8,  NOTE_C5,8,
  NOTE_B4, 4,  NOTE_B4,8,  NOTE_C5,8,  NOTE_D5,4,  NOTE_E5,4,
  NOTE_C5, 4,  NOTE_A4,4,  NOTE_A4,4, REST, 4,

  NOTE_E5, 4,  NOTE_B4,8,  NOTE_C5,8,  NOTE_D5,4,  NOTE_C5,8,  NOTE_B4,8,
  NOTE_A4, 4,  NOTE_A4,8,  NOTE_C5,8,  NOTE_E5,4,  NOTE_D5,8,  NOTE_C5,8,
  NOTE_B4, -4,  NOTE_C5,8,  NOTE_D5,4,  NOTE_E5,4,
  NOTE_C5, 4,  NOTE_A4,4,  NOTE_A4,8,  NOTE_A4,4,  NOTE_B4,8,  NOTE_C5,8,

  NOTE_D5, -4,  NOTE_F5,8,  NOTE_A5,4,  NOTE_G5,8,  NOTE_F5,8,
  NOTE_E5, -4,  NOTE_C5,8,  NOTE_E5,4,  NOTE_D5,8,  NOTE_C5,8,
  NOTE_B4, 4,  NOTE_B4,8,  NOTE_C5,8,  NOTE_D5,4,  NOTE_E5,4,
  NOTE_C5, 4,  NOTE_A4,4,  NOTE_A4,4, REST, 4,
  

  NOTE_E5,2,  NOTE_C5,2,
  NOTE_D5,2,   NOTE_B4,2,
  NOTE_C5,2,   NOTE_A4,2,
  NOTE_GS4,2,  NOTE_B4,4,  REST,8, 
  NOTE_E5,2,   NOTE_C5,2,
  NOTE_D5,2,   NOTE_B4,2,
  NOTE_C5,4,   NOTE_E5,4,  NOTE_A5,2,
  NOTE_GS5,2,

};

// sizeof gives the number of bytes, each int value is composed of two bytes (16 bits)
// there are two values per note (pitch and duration), so for each note there are four bytes
int notes=sizeof(melody)/sizeof(melody[0])/2; 

// this calculates the duration of a whole note in ms (60s/tempo)*4 beats
int divider = 0, noteDuration = 0;
int thisNote = 0;
unsigned long int lastNote = 0;

void tetrisMusicPlay() {
  int wholenote = (60000 * 4) / tempo;
  if (millis()-lastNote>=noteDuration) {
    if (lastNote)
      noTone(buzzer);
    if (thisNote < notes * 2) {
      divider = pgm_read_word(&melody[thisNote + 1]);
      if (divider > 0) {
      // regular note, just proceed
        noteDuration = (wholenote) / divider;
      } else if (divider < 0) {
        // dotted notes are represented with negative durations!!
        noteDuration = (wholenote) / abs(divider);
        noteDuration *= 1.5; // increases the duration in half for dotted notes
      }

      // we only play the note for 90% of the duration, leaving 10% as a pause
      lastNote=millis();
      tone(buzzer, pgm_read_word(&melody[thisNote]), 50);
      thisNote = thisNote + 2;
    } else {
      thisNote = 0;
    }
  }
}

#endif

Credits

ffrouin

ffrouin

3 projects • 3 followers
Open Source and Standard greatly help integration and maintenance costs. IT, 3D Printing and Electronic R&D, Repair : The Domestic Industry.

Comments