Skip to main content

Arduino Virtual Pet using Arduino Uno – OLED Interactive Game in Wokwi

Arduino Virtual Pet Game – OLED Interactive Project | MakeMindz
▶ Open Wokwi
🎮 Summer Class Project

Arduino Virtual Pet Game
on OLED Display

Build a Tamagotchi-style digital pet using Arduino Uno + SSD1306 OLED in the free Wokwi simulator — no hardware needed!

SSD1306 OLED
😊
H
E
T
🍎 Feed
🎮 Play
💤 Sleep
Arduino Uno Age: 12

What You'll Build

A fully interactive digital pet that lives on a tiny OLED screen — feed it, play with it, let it sleep!

⏱️
~30
Minutes to build
Easy
Difficulty level
🖥️
Free
Wokwi simulator
🎓
STEM
Class project
💡
No hardware needed! This project runs completely inside the free Wokwi browser simulator. Perfect for online summer classes.

What You Need

All parts are available virtually inside Wokwi — nothing to buy!

Arduino Uno R3
SSD1306 OLED 128×64 (I2C)
Push Button × 3 (Feed, Play, Sleep)
Passive Buzzer
Wokwi Browser Simulator
USB Cable (for real hardware)

Pin Connections

Connect the OLED via I2C, buttons to digital pins, and the buzzer to Pin 8.

A4
OLED SDA
I2C Data line
A5
OLED SCL
I2C Clock line
2
Feed Button
INPUT_PULLUP, green
3
Play Button
INPUT_PULLUP, blue
4
Sleep Button
INPUT_PULLUP, yellow
8
Buzzer (+)
Sound output
🔌

Full Connection Table

FromTo (Arduino)Wire Color
OLED GNDGND⬛ Black
OLED VCC5V🔴 Red
OLED SDAA4🔵 Blue
OLED SCLA5🟢 Green
Button 1 (Feed) Pin 1Pin 2🟢 Green
Button 1 (Feed) Pin 2GND⬛ Black
Button 2 (Play) Pin 1Pin 3🔵 Blue
Button 2 (Play) Pin 2GND⬛ Black
Button 3 (Sleep) Pin 1Pin 4🟡 Yellow
Button 3 (Sleep) Pin 2GND⬛ Black
Buzzer (+)Pin 8🔴 Red
Buzzer (–)GND⬛ Black

Step-by-Step Guide

Follow these steps to get your virtual pet running in minutes.

  1. Open Wokwi in your browser

    Go to wokwi.com and click "New Project" → Arduino Uno. No account needed to try it!

  2. Add components to the diagram

    Click the blue + button to add: SSD1306 OLED, three Push Buttons, and one Buzzer. Alternatively, paste the diagram.json below directly — it auto-places everything.

  3. Paste the diagram.json

    Click the diagram.json tab in Wokwi and replace its content with the JSON code from the Diagram JSON section below. This sets up all wiring automatically.

  4. Add the Arduino libraries

    In Wokwi, click Libraries (book icon) and search for Adafruit GFX and Adafruit SSD1306. Add both.

    ℹ️
    In Wokwi these libraries are pre-installed — just make sure the #include lines are at the top of your sketch.
  5. Paste the Arduino code

    Open the sketch.ino tab and paste the full code from the Code section below. Replace any existing code completely.

  6. Click the green Play ▶ button

    Hit the green ▶ Start Simulation button. You'll see the OLED light up with "Virtual Pet!" and hear the startup melody. Your pet is alive!

    Expected output: OLED shows the welcome screen for 2 seconds, then the pet face with bouncing animation and live status bars.
  7. Interact with the buttons

    Click the green button to feed, blue to play, yellow to sleep. Watch the pet's face and status bars react in real time. If you ignore it too long — it will die! 💀

  8. Open the Serial Monitor (optional)

    Click the Serial Monitor tab in Wokwi to see live stat values printed every 5 seconds: Hunger, Happiness, and Energy numbers.


🚀 Try the Live Simulation

Run this project right now in your browser — completely free, no downloads, no hardware.

Open in Wokwi Simulator

Arduino Sketch

Copy and paste this entire code into your Wokwi sketch.ino file.

sketch.ino — Arduino C++
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH  128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Button pins
const int BTN_FEED  = 2;
const int BTN_PLAY  = 3;
const int BTN_SLEEP = 4;

// Buzzer pin
const int BUZZER = 8;

// Pet stats (0–100)
int hunger    = 50;   // higher = more hungry
int happiness = 70;
int energy    = 80;
int age       = 0;

// Animation
int           frame      = 0;
unsigned long lastUpdate = 0;
unsigned long lastAnim   = 0;

// ─────────────── SETUP ───────────────
void setup() {
  Serial.begin(9600);

  pinMode(BTN_FEED,  INPUT_PULLUP);
  pinMode(BTN_PLAY,  INPUT_PULLUP);
  pinMode(BTN_SLEEP, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Display failed!");
    for (;;);
  }

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);

  // Welcome screen
  playStartupSound();
  display.setTextSize(2);
  display.setCursor(10, 20);
  display.println("Virtual");
  display.setCursor(25, 40);
  display.println("Pet!");
  display.display();
  delay(2000);
}

// ─────────────── LOOP ────────────────
void loop() {
  unsigned long now = millis();

  // Decay stats every 5 seconds
  if (now - lastUpdate > 5000) {
    hunger    = min(100, hunger    + 3);
    happiness = max(0,   happiness - 2);
    energy    = max(0,   energy    - 2);
    age++;
    lastUpdate = now;

    if (hunger > 80 || happiness < 20 || energy < 20)
      playWarningSound();

    Serial.print("Hunger:"); Serial.print(hunger);
    Serial.print(" Happy:"); Serial.print(happiness);
    Serial.print(" Energy:"); Serial.println(energy);
  }

  // Bounce animation frame
  if (now - lastAnim > 500) {
    frame = (frame + 1) % 2;
    lastAnim = now;
  }

  // ── Button checks ──
  if (digitalRead(BTN_FEED) == LOW) {
    hunger    = max(0,   hunger    - 30);
    happiness = min(100, happiness + 5);
    playEatSound();
    showMessage("Fed!");
    delay(500);
  }

  if (digitalRead(BTN_PLAY) == LOW) {
    if (energy > 20) {
      happiness = min(100, happiness + 20);
      energy    = max(0,   energy    - 15);
      playHappySound();
      showMessage("Fun!");
    } else {
      playSadSound();
      showMessage("Too tired!");
    }
    delay(500);
  }

  if (digitalRead(BTN_SLEEP) == LOW) {
    energy = min(100, energy + 40);
    playSleepSound();
    showMessage("Zzz...");
    delay(1000);
  }

  // ── Draw ──
  display.clearDisplay();
  if (hunger >= 100 || happiness <= 0) {
    playDeathSound();
    drawDead();
  } else {
    drawPet();
    drawStats();
    drawButtons();
  }
  display.display();
  delay(100);
}

// ════════════ SOUND FUNCTIONS ════════════

void playStartupSound() {
  tone(BUZZER, 523, 150); delay(150);  // C
  tone(BUZZER, 659, 150); delay(150);  // E
  tone(BUZZER, 784, 200); delay(200);  // G
}
void playEatSound() {
  tone(BUZZER, 400, 100); delay(120);
  tone(BUZZER, 500, 100); delay(120);
  tone(BUZZER, 600, 100); delay(120);
}
void playHappySound() {
  tone(BUZZER,  800, 100); delay(120);
  tone(BUZZER, 1000, 100); delay(120);
  tone(BUZZER, 1200, 150); delay(170);
}
void playSleepSound() {
  tone(BUZZER, 700, 200); delay(220);
  tone(BUZZER, 600, 200); delay(220);
  tone(BUZZER, 500, 300); delay(320);
}
void playSadSound() {
  tone(BUZZER, 400, 200); delay(220);
  tone(BUZZER, 300, 300); delay(320);
}
void playWarningSound() {
  tone(BUZZER, 800, 100); delay(150);
  tone(BUZZER, 800, 100); delay(150);
}
void playDeathSound() {
  static bool played = false;
  if (!played) {
    tone(BUZZER, 500, 300); delay(320);
    tone(BUZZER, 400, 300); delay(320);
    tone(BUZZER, 300, 500); delay(520);
    played = true;
  }
}

// ════════════ DRAWING FUNCTIONS ════════════

void drawPet() {
  int x = 64, y = 30;
  display.fillCircle(x, y, 12, SSD1306_WHITE);  // Body

  if (hunger > 70) {                              // Hungry
    display.fillCircle(x-4, y-2, 2, SSD1306_BLACK);
    display.fillCircle(x+4, y-2, 2, SSD1306_BLACK);
    display.fillCircle(x,   y+4, 3, SSD1306_BLACK);
  } else if (energy < 30) {                       // Tired
    display.drawLine(x-5,y-2, x-2,y-2, SSD1306_BLACK);
    display.drawLine(x+2,y-2, x+5,y-2, SSD1306_BLACK);
    if (frame == 0) { display.setCursor(x+12,y-8); display.print("Z"); }
  } else if (happiness < 40) {                   // Sad
    display.fillCircle(x-4,y-2,2,SSD1306_BLACK);
    display.fillCircle(x+4,y-2,2,SSD1306_BLACK);
    display.drawLine(x-3,y+6, x,  y+4, SSD1306_BLACK);
    display.drawLine(x,  y+4, x+3,y+6, SSD1306_BLACK);
  } else {                                         // Happy
    display.fillCircle(x-4,y-2,2,SSD1306_BLACK);
    display.fillCircle(x+4,y-2,2,SSD1306_BLACK);
    display.drawLine(x-5,y+3, x-2,y+5, SSD1306_BLACK);
    display.drawLine(x-2,y+5, x+2,y+5, SSD1306_BLACK);
    display.drawLine(x+2,y+5, x+5,y+3, SSD1306_BLACK);
    if (frame == 0) y -= 2;  // Bounce
  }

  display.fillCircle(x-8, y+13, 3, SSD1306_WHITE);  // Feet
  display.fillCircle(x+8, y+13, 3, SSD1306_WHITE);
}

void drawStats() {
  display.setTextSize(1);
  // Hunger bar (inverted)
  display.setCursor(0,0); display.print("H:");
  int h = map(100-hunger, 0,100, 0,20);
  display.drawRect(12,0,22,6,SSD1306_WHITE);
  display.fillRect(13,1,h,4,SSD1306_WHITE);
  // Happiness bar
  display.setCursor(38,0); display.print("E:");
  int e = map(happiness, 0,100, 0,20);
  display.drawRect(50,0,22,6,SSD1306_WHITE);
  display.fillRect(51,1,e,4,SSD1306_WHITE);
  // Energy bar
  display.setCursor(76,0); display.print("T:");
  int t = map(energy, 0,100, 0,20);
  display.drawRect(88,0,22,6,SSD1306_WHITE);
  display.fillRect(89,1,t,4,SSD1306_WHITE);
  // Age
  display.setCursor(114,0); display.print(age);
}

void drawButtons() {
  display.setTextSize(1);
  display.setCursor(0,  56); display.print("Feed");
  display.setCursor(44, 56); display.print("Play");
  display.setCursor(88, 56); display.print("Sleep");
}

void drawDead() {
  display.setTextSize(2); display.setCursor(20,15);
  display.println("R.I.P.");
  display.setTextSize(1); display.setCursor(10,35);
  display.println("Pet has died");
  display.setCursor(5,50);
  display.println("Reset to restart");
}

void showMessage(const char* msg) {
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(20,25);
  display.println(msg);
  display.display();
}

Wokwi diagram.json

Paste this into the diagram.json tab in Wokwi to auto-wire all components.

diagram.json
{
  "version": 1,
  "author":  "Wokwi",
  "editor":  "wokwi",
  "parts": [
    { "type": "wokwi-arduino-uno",  "id": "uno",    "top": 0,    "left": 0,    "attrs": {} },
    { "type": "wokwi-ssd1306",       "id": "oled",  "top": -100, "left": 100,  "attrs": {} },
    { "type": "wokwi-pushbutton",    "id": "btn1", "top": 150,  "left": -50, "attrs": { "color": "green"  } },
    { "type": "wokwi-pushbutton",    "id": "btn2", "top": 150,  "left": 20,  "attrs": { "color": "blue"   } },
    { "type": "wokwi-pushbutton",    "id": "btn3", "top": 150,  "left": 90,  "attrs": { "color": "yellow" } },
    { "type": "wokwi-buzzer",        "id": "buz",   "top": 100,  "left": -100, "attrs": {} }
  ],
  "connections": [
    [ "oled:GND", "uno:GND.1", "black",  ["v0"] ],
    [ "oled:VCC", "uno:5V",    "red",    ["v0"] ],
    [ "oled:SDA", "uno:A4",    "blue",   ["v0"] ],
    [ "oled:SCL", "uno:A5",    "green",  ["v0"] ],
    [ "btn1:1.l", "uno:2",     "green",  ["v0"] ],
    [ "btn1:2.l", "uno:GND.2", "black",  ["v0"] ],
    [ "btn2:1.l", "uno:3",     "blue",   ["v0"] ],
    [ "btn2:2.l", "uno:GND.2", "black",  ["v0"] ],
    [ "btn3:1.l", "uno:4",     "yellow", ["v0"] ],
    [ "btn3:2.l", "uno:GND.2", "black",  ["v0"] ],
    [ "buz:1",    "uno:8",     "red",    ["v0"] ],
    [ "buz:2",    "uno:GND.3", "black",  ["v0"] ]
  ]
}

How It Works

The pet has 4 live stats that change every 5 seconds — and you must keep them healthy!

🍔
Hunger
Rises over time (bad!)
😊
Happiness
Falls over time
Energy
Falls over time
🎂
Age
Counts every 5 sec
🍎 Feed Button — Pin 2 (Green)

Reduces hunger, slightly boosts happiness. Use this when hunger bar is getting full.

↓ Hunger −30 ↑ Happiness +5
🎮 Play Button — Pin 3 (Blue)

Boosts happiness significantly but drains energy. Won't work if energy ≤ 20.

↑ Happiness +20 ↓ Energy −15
💤 Sleep Button — Pin 4 (Yellow)

Restores energy quickly. Use when the pet is tired — you'll see "Z" floating above it.

↑ Energy +40
⚠️
Game Over condition: If Hunger reaches 100 or Happiness drops to 0, the R.I.P. screen appears. Hit the reset button in Wokwi to restart.

Sound Effects

The buzzer on Pin 8 plays different tone sequences for each event.

🎵 Startup melody (C–E–G)
🍎 Eating sound (ascending)
😄 Happy tone (high pitch)
💤 Sleep melody (descending)
😢 Sad / tired sound
⚠️ Warning double beep
💀 Death sound (plays once)

What You'll Learn

This project teaches core embedded systems concepts in a fun, hands-on way.

OLED display interfacing with Arduino
I2C communication basics (SDA / SCL)
Push button input with INPUT_PULLUP
Variables and conditional statements
State-based programming logic
Real-time updates using millis()
Basic embedded game development
Buzzer tone() sound generation

Comments

try for free