Introduction: Brick a Bottle or Three

About: Retired due to health. Oldish. My background is in Structural Engineering. Also smith of many trades. The majority of my project will be what can be made sat in a chair within arm's reach, on a plotter, 3D pri…

A little game to show how a PCF8574 can be used with a Nokia 5110 screen.

Step 1: Assembled

Can be made easily on a breadboard, using standard components bought off the web.

Step 2: Arduino

Any Arduino can be used. (eBay)

Step 3: Breadboard (and Leads)

any suitable Breadboard and leads (eBay)

Step 4: Nokia 5110 Screen

There are several flavours of the Nokia 5110 Screen (eBay)

If you have ever got one and fried it, it is possible to get the screen on it's own without the PCB. (makes it cheaper to replace)

Step 5: PCF8574

The PCF8574 also comes in many flavours. (eBay)

All will have the I2C Header, but there are some tailor made for specific tasks.

I will be using: I2C Serial Interface Board Module for 1602 LCD Display (PCF8574) (eBay)

Step 6: Piezo

Piezo (for sound) (eBay)

By gluing it to a piece of corrugated card board, amplifies the sound made by the piezo.

Step 7: Buttons

Four Push Buttons are required with a common ground. (eBay)

Many flavours of these are available.

The best ones are the ones that have a capacitor and resister to stop bounce.

Step 8: Power

Can be powered from a battery or from the USB.

Step 9: Why Use a Module for the 1602 LCD (PCF8574)

As I have mentioned iIn this project I am using the module designed for 1602 LCD diplay.

There are several reasons for using this module.

The first time I bought a Nokia 5110 screed the advert say it was 5v compatible (I fried it), no it isn't, it's 3.3 volt.

This can be sorted using resistors as voltage dividers, or MOSFET circuits.

The Nokia 5110 screen also communicates via SPI requiring 4 communication lines and power.

If we use the Module:

The PCF8574 requires only 2 communication lines and power.

Also the module has a transistor connected to PFC8574 GPIO pin 3, which connects Header Pin 16 to GND. (Used for switching the LED).

If the module is powered by 3.3 volt, it will communicate with 3.3 volt to the Nokia 5110 screen and still be able handle the I2C communication from the Arduino.

Step 10: The Circuit of the Module:

Step 11: Connections:

Step 12: The Code

Tims_Brick_a_Bottle_or_Three.

Credit for the library used in the code goes to: Maxint R&D

The library is used like most display libraries.

It is defined like: PCF8574_PCD8544 display = PCF8574_PCD8544(0x27, 7, 6, 5, 4, 2);

It also uses library (credit to: Limor Fried/Ladyada) so the commands to draw graphics, is as the Adafruit GFX library.

In setup:

I have made pins A0, A1 and A2 raise an interrupt when button 1, 2 or 3 is pressed.

PCICR = 0b00000010; // Pin Change Interrupt Enable.
PCMSK1 = 0b00000111; // Enable Pin Change Interrupt for A0, A1, A2.
sei(); //enable interrupts.

I have enabled pullup resistors on the button pins. The buttons close to GND. (the button module has capacitors to help with bounce)

pinMode(BUTTON_1, INPUT_PULLUP);
pinMode(BUTTON_2, INPUT_PULLUP);
pinMode(BUTTON_3, INPUT_PULLUP);
pinMode(LED_ON, INPUT_PULLUP);

The function to receive the interrupts is:

ISR(PCINT1_vect){  //code goes here  }

The images to draw the bottles are made from char Array.

Example:

//bottle 6x16
static const unsigned char PROGMEM bottle_img[] = {
B00110000,
B00110000,
B00110000,
B00110000,
B01111000,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B01111000 };

Although the bottle is only 6 bits wide, the library only works with 8 bit values.

But when using the library command to draw the image, we still use the value 6 for the width.

display.drawBitmap(positionX, positionX, bottle_img, 6, 15, 1);

The sound is produced using the tone function.

The full code below:

This part of the code is just notes/comments so I remember the pins

/*Name:  Tims_Brick_a_Bottle_or_Three.ino
 A little game.
 Created: 03/08/2019 20:30:46 (based on original I did for TI99/4a back in 1985 )
 Author:     TIM
 Using I2C module for 0216 LCD
 Has PCF8574 (8 x GPIO pins)
 Nokia 5110 screen.
 4 push to close buttons
 PIEZO for sound.
 Pin 01 = GND
 Pin 02 = VCC
 Pin 03 = Contrast (adjustable VCC->Pot->Pin3)
 Pin 04 = GPIO_0 (PCF8574)
 Pin 05 = GPIO_1 (PCF8574)
 Pin 06 = GPIO_2 (PCF8574)
 Pin 07 =
 Pin 08 =
 Pin 09 =
 Pin 10 =
 Pin 11 = GPIO_4 (PCF8574)
 Pin 12 = GPIO_5 (PCF8574)
 Pin 13 = GPIO_6 (PCF8574)
 Pin 14 = GPIO_7 (PCF8574)
 Pin 15 = VCC (for LED)
 Pin 16 = GPIO_3 (PCF8574) (connected via LED jumper to J3Y (=S8050) transistor, sink)
 I2C module 4 pin header
 Pin 1 = GND (GND from Arduino)
 Pin 2 = VCC (3.3v from Arduino)
 Pin 3 = SDA (Arduino Pin A4)
 Pin 4 = SCL (Arduino Pin A5)
 Nokia 5110 Pins (PCD8544)
 Pin 1 = RST
 Pin 2 = CE
 Pin 3 = DC
 Pin 4 = DIN
 Pin 5 = CLK
 Pin 6 = VCC (3.3v)
 Pin 7 = LED (-) Cathode
 Pin 8 = GND

 Connections between I2C Module and Nokia 5110 Screen
 Module Pin 01 -- Pin 8 Nokia 5110 Screen (GND from Arduino)
 Module Pin 02 -- Pin 6 Nokia 5110 Screen (3.3v from Arduino)
 Module Pin 03 -- NC
 Module Pin 04 -- NC (free GPIO_0)
 Module Pin 05 -- NC (free GPIO_1)
 Module Pin 06 -- Pin 1 Nokia 5110 Screen
 Module Pin 07 -- NC
 Module Pin 08 -- NC
 Module Pin 09 -- NC
 Module Pin 10 -- NC
 Module Pin 11 -- Pin 2 Nokia 5110 Screen
 Module Pin 12 -- Pin 3 Nokia 5110 Screen
 Module Pin 13 -- Pin 4 Nokia 5110 Screen
 Module Pin 14 -- Pin 5 Nokia 5110 Screen
 Module Pin 15 -- NC
 Module Pin 16 -- Pin 7 Nokia 5110 Screen
*/

This part Defines the object

#include <PCF8574_PCD8544.h>
// Credit for library: Maxint R&D <a href="https://github.com/maxint-rd/I2C-PCF8574-PCD8544-Nokia-5110-LCD" rel="nofollow">  <a href="https://github.com/maxint-rd/I2C-PCF8574-PCD8544-..." rel="nofollow">   https://github.com/maxint-rd/I2C-PCF8574-PCD8544-...>>

#define SCREEN_WIDTH 84
#define SCREEN_HEIGHT 48

//BUTTONS
#define BUTTON_1 14 //(A0) Button 1
#define BUTTON_2 16 //(A2) Button 2
#define BUTTON_3 15 //(A1) Button 3
#define LED_ON  7 //(D7) Button 4
#define PIEZO  11 // Pin D11
boolean button = false;
boolean button1 = false;
boolean button2 = false;
boolean button3 = false;
boolean ledOn = false;
byte menu =0;
byte iTake = 0;
byte youTake = 0;
byte bottleNumber = 3;
//brick 14x6 (Use Rectangle)
//bottle 6x16
static const unsigned char PROGMEM bottle_img[] =
{ B00110000,
B00110000,
B00110000,
B00110000,
B01111000,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B11111100,
B01111000 };
//Smashed bottle 6x16
static const unsigned char PROGMEM smash_bottle_img[] =
{ B01001000,
B10000100,
B00100000,
B01001000,
B10000100,
B00110000,
B00100100,
B10000000,
B10001000,
B11001100,
B01000100,
B11010100,
B10010100,
B10110100,
B00101100 };
PCF8574_PCD8544 display = PCF8574_PCD8544(0x27, 7, 6, 5, 4, 2); 

This part configures the pins and starts the game

<p>void setup() {
 //Buttons
 pinMode(BUTTON_1, INPUT_PULLUP);
 pinMode(BUTTON_2, INPUT_PULLUP);
 pinMode(BUTTON_3, INPUT_PULLUP);
 pinMode(LED_ON, INPUT_PULLUP);
 pinMode(PIEZO, OUTPUT);

 //00000|PCIE2|PCIE1|PCIE0
 PCICR = 0b00000010; // Pin Change Interrupt Enable.
 //PCMSK0=PCINT 7,6,5,4,3,2,1,0.(Pins= crystal, crystal, 13, 12, 11, 10, 9, 8)
 //PCMSK1=PCINT 14,13,12,11,10,9,8.(Pins= reset, A5,A4, A3, A2, A1, A0)
 //PCMSK2=PCINT 23,22,21,20,19,18,17,16.(Pins= 7, 6, 5, 4, 3, 2, 1, 0)
 PCMSK1 = 0b00000111; // Enable Pin Change Interrupt for A0, A1, A2.
 sei();//enable interrupts

 display.begin(50, 4);
 display.display(); // show splashscreen
 delay(2000);
 display.clearDisplay();   // clears the screen and buffer
 writeTitleText();
 display.clearDisplay();
 drawBrickWall();
 for (size_t i = 1; i < 10; i++) {
  drawBottle(i + bottleNumber);
 }
 delay(500);
 smashBottle(1 + bottleNumber);
 delay(2000);
 button = true;
 display.setTextSize(1);
 display.setTextColor(BLACK);
 }

This part checks for button changes

void loop() {
 if (button) {
  switch (menu) {
  case 0: writeMenuText(); break;
  case 1: writeHelpText(); break;
  case 2: writeQuestionText(); break;
  case 3: writeITakeText(); break;
  case 4: writeIWinText(); break;
  default: break;
  }
 }
}
ISR(PCINT1_vect) {
 button1 = false;
 button2 = false;
 button3 = false;
 // If interrupt is triggered by the button
 if (!digitalRead(BUTTON_1)) { button1 = true; }
 if (!digitalRead(BUTTON_2)) { button2 = true; }
 if (!digitalRead(BUTTON_3)) { button3 = true; }
 if (button1 || button2 || button3) { button = true; }
}

The rest are functions to show what is displayed

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();
}
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);
}
void switchLED() {
 if (ledOn) { display.digitalWrite(3, LOW); }
 else { display.digitalWrite(3, HIGH); }
 ledOn = !ledOn;
 delay(500);
}
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 writeHelpText() {<br> menu = 0;
 button = false;
 while (!button) {
  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();
  if (!digitalRead(LED_ON)) { switchLED(); }
 }
 button = false;
 while (!button) {
  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();
  if (!digitalRead(LED_ON)) { switchLED(); }
 }
 button = false;
 while (!button) {
  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();
  if (!digitalRead(LED_ON)) { switchLED(); }
 }
 button = false;
 while (!button) {
  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();
  if (!digitalRead(LED_ON)) { switchLED(); }
 }
 button = false;
 while (!button) {
  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();
  if (!digitalRead(LED_ON)) { switchLED(); }
 }
}
void writeMenuText() {
 display.clearDisplay();
 button = false;
 while (!button) {
  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;
  if (!digitalRead(LED_ON)) { switchLED(); }
 }
 display.clearDisplay();
 if (button1) {
  drawBrickWall();
  for (size_t i = 1; i < 10; i++) {
   drawBottle(i + 2);
  }
  menu = 2;
 }
 else if (button2) { menu = 1; }
 else if (button3) {
  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();
  menu = 5;
 }}
void writeQuestionText() {
 button = false;
 while (!button) {
  display.fillRect(0, 0, 83, 14, 0);
  display.setCursor(0, 3);
  display.print("  1, 2 or 3?");
  display.display();
  if (!digitalRead(LED_ON)) { switchLED(); }
 }
 if (button1) { youTake = 1; }
 else if (button2) { youTake = 2; }
 else if (button3) { youTake = 3; }
 for (size_t i = 0; i < youTake; i++) {
  smashBottle(i + bottleNumber);
  delay(100);
 }
 bottleNumber += youTake;
 menu = 3;
}
void writeITakeText() {
 iTake = 4 - youTake;
 button = false;
 display.fillRect(0, 0, 83, 14, 0);
 display.setCursor(0, 3);
 display.print("  I TAKE " + String(iTake));
 display.display();
 delay(2000);
 for (size_t i = 0; i < iTake; i++) {
  smashBottle(i + bottleNumber);
  delay(100);
 }
 bottleNumber += iTake;
 if (bottleNumber < 9) {
  menu = 2;
  button = true;
 }
 else {
  menu = 4;
  button = true;
 }
}
void writeIWinText() {
 button = false;
 while (!button) {
  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();
  if (!digitalRead(LED_ON)) { switchLED(); }
 }
 menu = 0;
 button = true;
}

I have done a racing game based on the same setup.

Tim's Racer (Arduino)

Games Contest

Participated in the
Games Contest