John Bradnam
Published © GPL3+

Brick a Bottle or Three

A simple game on a Nokia 5110 LCD screen

IntermediateFull instructions provided8 hours88
Brick a Bottle or Three

Things used in this project

Hardware components

Microchip ATtiny1614 Microprocessor
×1
Nokia 5110 LCD Screen
×1
LM1117-33 3.3V SMD Regulator
SOT223-T
×1
Tactile Switch, Top Actuated
Tactile Switch, Top Actuated
17mm Shaft
×4
Buzzer
Buzzer
×1
Passive Components
3 x 330R 0805 Resistors, 1 x 220R 0805 Resistior, 2 x 0.1uF 0805 ceramic capacitors, 1 x 220uF/10V through hole capacitor, 1 x 10uF 1206 ceramic capacitor
×1
DC power socket
See images for the type used
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

STL Files

Files for 3D printing

Schematics

Schematic

PCB

Eagle files

Schematic and PCB in Eagle format

Code

Brick_A_Bottle_V1.ino

Arduino
/* 
 *  Brick a bottle or three
 *  Based on "Brick a Bottle or Three" by Palingenesis
 *  (https://www.instructables.com/Brick-a-Bottle-or-Three/)
 *  
 *  230516 John Bradnam (jbrad2089@gial.com)
 *    - Coverted project to use a ATtiny1614 processor
 *    
 *-------------------------------------------------------------------------
 Arduino IDE:
 --------------------------------------------------------------------------
  BOARD: ATtiny1614/1604/814/804/414/404/214/204
  Chip: ATtiny1614
  Clock Speed: 20MHz
  millis()/micros(): "Enabled (default timer)"
  Programmer: jtag2updi (megaTinyCore)

  ATTiny1614 Pins mapped to Ardunio Pins
 
              +--------+
          VCC + 1   14 + GND
  (SS)  0 PA4 + 2   13 + PA3 10 (SCK)
        1 PA5 + 3   12 + PA2 9  (MISO)
  (DAC) 2 PA6 + 4   11 + PA1 8  (MOSI)
        3 PA7 + 5   10 + PA0 11 (UPDI)
  (RXD) 4 PB3 + 6    9 + PB0 7  (SCL)
  (TXD) 5 PB2 + 7    8 + PB1 6  (SDA)
              +--------+
  
 **************************************************************************/

#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>

#define LIGHT 0     //PA4
#define SCLK 1      //PA5
#define DIN 2       //PA6
#define DC 3        //PA7
#define CS_LCD 4    //PB3
#define RST 5       //PB2
#define PIEZO 7     //PB0
#define SWITCHES 10 //PA3

Adafruit_PCD8544 display = Adafruit_PCD8544(SCLK,DIN,DC,CS_LCD,RST);

enum SWITCH { NONE, ONE, TWO, THREE, FOUR };
struct SWITCH_VALUES
{
  SWITCH name;
  int low;
  int high;
};
#define SWITCH_COUNT 4
SWITCH_VALUES switches[SWITCH_COUNT] = {{ONE,600,700},{TWO,450,600},{THREE,300,450},{FOUR,0,100}};
SWITCH lastButtonPressed = NONE;

enum MODES {MENU, HELP, QUERY, TAKE, WIN};
MODES mode = MENU;

bool backlight;
int bottleNumber;
int youTake;
int iTake;

//---------------------------------------------------------------
// Bottle graphivs
//  brick 14x6 (Use Rectangle)
//  bottle 6x16
//---------------------------------------------------------------

const uint8_t bottle_img[] PROGMEM =
{ 
  B00110000,
  B00110000,
  B00110000,
  B00110000,
  B01111000,
  B11111100,
  B11111100,
  B11111100,
  B11111100,
  B11111100,
  B11111100,
  B11111100,
  B11111100,
  B11111100,
  B01111000 
};

//Smashed bottle 6x16
const uint8_t smash_bottle_img[] PROGMEM =
{ 
  B01001000,
  B10000100,
  B00100000,
  B01001000,
  B10000100,
  B00110000,
  B00100100,
  B10000000,
  B10001000,
  B11001100,
  B01000100,
  B11010100,
  B10010100,
  B10110100,
  B00101100 
};

//------------------------------------------------------------
// Hardware setup
//------------------------------------------------------------

void setup() 
{
  //Encoder
  pinMode(PIEZO,OUTPUT);
  pinMode(SWITCHES,INPUT);
  pinMode(LIGHT,OUTPUT);
  analogWrite(LIGHT, 0);
  backlight = false;

  display.begin(60, 4);
  //display.setContrast(50);
  display.display(); // show splashscreen
  delay(2000);
  writeTitleText();
  
  display.clearDisplay();
  drawBrickWall();
  for (size_t i = 1; i < 10; i++) 
  {
    drawBottle(i + bottleNumber);
  }
  display.display();
  delay(500);
  
  for (size_t i = 1; i < 10; i++) {
    smashBottle(i + bottleNumber);
    delay(100);
  }
  //delay(2000);
  
  display.setTextSize(1);
  display.setTextColor(BLACK);
  mode = MENU;
}

//------------------------------------------------------------
// Main loop
//------------------------------------------------------------

void loop() 
{
  switch (mode)
  {
    case MENU: writeMenuText(); break;
    case HELP: writeHelpText(); break;
    case QUERY: writeQuestionText(); break;
    case TAKE: writeITakeText(); break;
    case WIN: writeIWinText(); break;
  }
}

//-------------------------------------------------------------
// Graphic routines
//-------------------------------------------------------------

void drawBrickWall(void) {

  byte FirstBrickLeft = 0;
  byte FirstBrickTop = 0;

  for (size_t i = 0; i < 3; i++) 
  {
    if (i == 1) 
    { 
      FirstBrickLeft = 10; 
    }
    else 
    { 
      FirstBrickLeft = 3; 
    }

    for (int16_t ii = 0; ii < 100; ii += 15) 
    {
      display.fillRect(ii - FirstBrickLeft, 31 + FirstBrickTop, 14, 6, 1);
    }
    FirstBrickTop += 7;
  }
  display.display();
}

void drawBottle(byte position) 
{
  display.drawBitmap(position * 7, 16, bottle_img, 6, 15, 1);
  display.display();
}

void smashBottle(byte position) 
{

  display.fillRect(position * 7, 16, 6, 15, 0);
  display.drawBitmap(position * 7, 16, smash_bottle_img, 6, 15, 1);
  display.display();
  playBottleSmashSound();
  delay(500);
  display.fillRect(position * 7, 16, 6, 15, 0);
  display.display();
}

//--------------------------------------------------
// Sound routines
//--------------------------------------------------

void playBottleSmashSound() 
{
  tone(PIEZO, 2000);
  delay(100);
  tone(PIEZO, 500);
  delay(200);
  for (size_t i = 400; i > 0; i-=10)  
  {
    tone(PIEZO, i);
    delay(15);
  }
  noTone(PIEZO);
}

void playStartSound() 
{
  for (size_t i = 0; i < 4; i++) 
  {
    tone(PIEZO, 500);
    delay(100);
    tone(PIEZO, 1000);
    delay(100);
    tone(PIEZO, 1500);
    delay(100);
    tone(PIEZO, 2000);
    delay(100);
    tone(PIEZO, 2500);
    delay(100);

  }
  noTone(PIEZO);
}

//----------------------------------------------------
// Display Text routines
//----------------------------------------------------

void writeTitleText() 
{
  display.clearDisplay();
  display.setCursor(27, 10);
  display.print("BRICK");
  display.setCursor(18, 20);
  display.print("A BOTTLE");
  display.setCursor(18, 30);
  display.print("OR THREE");
  display.display();
  playStartSound();
}

void writeMenuText() 
{
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("");
  display.setCursor(0, 9);
  display.print("1 = NEW GAME");
  display.setCursor(0, 18);
  display.print("2 = Help");
  display.setCursor(0, 27);
  display.print("3 = Exit");
  display.setCursor(0, 36);
  display.print("4 = LED");
  display.display();
  
  bottleNumber = 3;
  SWITCH button = readSwitches(true);
  while (button == NONE)
  {
    button = readSwitches(true);
  }

  switch (button)
  {
    case ONE:
      //Start a new game
      display.clearDisplay();
      drawBrickWall();
      for (size_t i = 1; i < 10; i++) {
        drawBottle(i + 2);
      }
      mode = QUERY;
      break;

    case TWO:
      //Help
      mode = HELP;
      break;

    case THREE:
      //Exit
      display.clearDisplay();
      display.setCursor(0, 0);
      display.print("");
      display.setCursor(0, 9);
      display.print("BYE");
      display.setCursor(0, 18);
      display.print("TRY AGAIN");
      display.setCursor(0, 27);
      display.print("ANOTHER TIME");
      display.setCursor(0, 41);
      display.print("");
      display.display();
      delay(5000);
      mode = MENU;
      break;

    case FOUR:
      backlight = !backlight;
      analogWrite(LIGHT, (backlight) ? 192 : 0);
      break;

    case NONE:
      break;
  }
}

void writeHelpText() 
{
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("== THE GAME ==");
  display.setCursor(0, 9);
  display.print("THERE ARE");
  display.setCursor(0, 18);
  display.print("9 BOTTLES");
  display.setCursor(0, 27);
  display.print("ON A WALL.");
  display.setCursor(0, 41);
  display.print("(Press Button)");
  display.display();
  while (readSwitches(true) == NONE);
   
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("YOU ARE");
  display.setCursor(0, 9);
  display.print("EXPERT");
  display.setCursor(0, 18);
  display.print("AT THROWING");
  display.setCursor(0, 27);
  display.print("STONES.");
  display.setCursor(0, 41);
  display.print("(Press Button)");
  display.display();
  while (readSwitches(true) == NONE);

  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("YOU ALWAYS");
  display.setCursor(0, 9);
  display.print("HIT A BOTTLE.");
  display.setCursor(0, 18);
  display.print("I'M");
  display.setCursor(0, 27);
  display.print("JUST AS GOOD.");
  display.setCursor(0, 41);
  display.print("(Press Button)");
  display.display();
  while (readSwitches(true) == NONE);

  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("WE MAY TAKE");
  display.setCursor(0, 9);
  display.print("1, 2 OR 3");
  display.setCursor(0, 18);
  display.print("STONES");
  display.setCursor(0, 27);
  display.print("EACH TURN.");
  display.setCursor(0, 41);
  display.print("(Press Button)");
  display.display();
  while (readSwitches(true) == NONE);
  
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("ONE WITH THE");
  display.setCursor(0, 9);
  display.print("LAST BOTTLE,");
  display.setCursor(0, 18);
  display.print("LOSES");
  display.setCursor(0, 27);
  display.print("THE GAME.");
  display.setCursor(0, 41);
  display.print("(Press Button)");
  display.display();
  while (readSwitches(true) == NONE);

  mode = MENU;
}

void writeQuestionText() 
{
  display.fillRect(0, 0, 83, 14, 0);
  display.setCursor(0, 3);
  display.print("  1, 2 or 3?");
  display.display();
  
  SWITCH button = readSwitches(true);
  while (button == NONE || button == FOUR)
  {
    button = readSwitches(true);
  }
  youTake = (int)button;

  for (int i = 0; i < youTake; i++) 
  {
    smashBottle(i + bottleNumber);
    delay(100);
  }
  bottleNumber += youTake;
  mode = TAKE;
}

void writeITakeText() 
{
  iTake = 4 - youTake;

  display.fillRect(0, 0, 83, 14, 0);
  display.setCursor(0, 3);
  display.print("  I TAKE " + String(iTake));
  display.display();

  delay(2000);

  for (int i = 0; i < iTake; i++) 
  {
    smashBottle(i + bottleNumber);
    delay(100);
  }
  bottleNumber += iTake;

  mode = (bottleNumber < 9) ? QUERY : WIN;
}

void writeIWinText() 
{
  display.fillRect(0, 0, 83, 14, 0);
  display.setCursor(0, 3);
  display.println("Only 1 left.");
  display.print("I WIN");
  display.display();
  delay(500);
  playStartSound();
  mode = MENU;
}

//----------------------------------------------------
// Buttons
// - Returns NONE, ONE, TWO, THREE or FOUR
//----------------------------------------------------

SWITCH readSwitches(bool waitForRelease)
{
  SWITCH sw = NONE;
  int value = analogRead(SWITCHES);
  if (value < 1000)
  {
    delay(10);    //debounce
    if (analogRead(SWITCHES) < 1000)
    {
      for(int i = 0; i < SWITCH_COUNT; i++)
      {
        if (value >= switches[i].low && value < switches[i].high)
        {
          sw = switches[i].name;
          tone(PIEZO, 500);
          delay(10);
          noTone(PIEZO);
          if (waitForRelease)
          {
            while (analogRead(SWITCHES) < 1000)
            {
              yield();
            }
          }
          break;
        }
      }
    }
  }
  return sw;
}

Credits

John Bradnam

John Bradnam

141 projects • 167 followers
Thanks to Palingenesis.

Comments