# Pong Game - Introduction to ESP32

A fun pong game for the TTGO T-Display with sound effects. Choose your sound module:

- **DFPlayer Mini** — full MP3 playback from SD card, one track per event
- **ISD1820** — simple record-and-play chip, one recorded sound triggered on paddle hit or score lost

[![](https://github.com/impressto/tdisplay-pong/raw/master/readme-banner.jpg)](https://github.com/impressto/tdisplay-pong/blob/master/readme-banner.jpg)

### The board

[![](https://github.com/impressto/tdisplay-pong/raw/master/esp-pong.jpg)](https://github.com/impressto/tdisplay-pong/blob/master/esp-pong.jpg)

## What You'll Learn

<iframe allowfullscreen="allowfullscreen" height="314" src="https://www.youtube.com/embed/jyTEXo0oIZs" width="560"></iframe>

- How to upload code to an ESP32
- How to wire a sound module (DFPlayer Mini or ISD1820) and trigger it from code
- How software controls hardware through GPIO pins
- Serial communication between devices (DFPlayer)
- How edge-triggered signals work (ISD1820)

## What You Need

### Hardware

- **TTGO T-Display** (ESP32 with built-in screen)
- **USB-C cable** to connect to your computer
- **Small speaker** (3W 8ohm recommended)
- **Jumper wires** to connect things

**Choose one sound module:**

<table id="bkmrk-module-pros-cons-dfp"><thead><tr><th>Module</th><th>Pros</th><th>Cons</th></tr></thead><tbody><tr><td>**DFPlayer Mini**</td><td>Different sound per event, SD card holds many tracks</td><td>Needs SD card and MP3 files</td></tr><tr><td>**ISD1820**</td><td>No SD card needed, record your own voice/sound</td><td>Only one sound, no volume control</td></tr></tbody></table>

### Software

- [VS Code](https://code.visualstudio.com/) with [PlatformIO extension](https://platformio.org/install/ide?install=vscode)
- Start your project with the demo code - [https://github.com/impressto/tdisplay-pong](https://github.com/impressto/tdisplay-pong "https://github.com/impressto/tdisplay-pong")

## How to Play

### Onboard Buttons

1. **Left Button (GPIO 0)** - Move paddle LEFT
2. **Right Button (GPIO 35)** - Move paddle RIGHT

### External Buttons (Optional)

You can add your own buttons for a better gaming experience!

- **GPIO 25** - External button to move paddle LEFT
- **GPIO 13** - External button to move paddle RIGHT

**Wiring external buttons:**

```
Button leg 1 -----> GPIO pin (25 or 13)
Button leg 2 -----> GND

```

<div class="snippet-clipboard-content notranslate position-relative overflow-auto" id="bkmrk--3"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>No resistor needed - the ESP32 has internal pull-up resistors!

### Gameplay

- Hit the ball with your paddle to score points!
- The ball appears to **roll** as it moves - watch it spin!
- If you miss, you lose a point

## Getting Started

### Step 1: Upload the Code

1. Connect your T-Display to your computer with a USB cable
2. Open this folder in VS Code
3. Click the **PlatformIO: Upload** button (→ arrow at the bottom)
4. Wait for it to finish - the game will start automatically!

### Step 2: Play the Game First!

Before wiring anything, play the game to understand when events happen:

- Ball bounces off walls
- Ball hits your paddle
- You score a point
- You miss the ball

## Wiring Up Sound!

Pick **one** sound module and follow its wiring guide below. Then set the matching option in `config.h`.

---

### Option A: DFPlayer Mini

The DFPlayer Mini plays MP3 files from a micro SD card. You can assign a different sound to each game event.

#### Wiring

```
T-Display          DFPlayer Mini
---------          -------------
GPIO 17 (TX) ----> RX  (through 1K resistor recommended)
GPIO 21 (RX) <---- TX
GPIO 22      <---- BUSY  (optional — detects when playing)
GND          ----> GND
5V           ----> VCC
                   SPK1 ----> Speaker (+)
                   SPK2 ----> Speaker (-)

```

<div class="snippet-clipboard-content notranslate position-relative overflow-auto" id="bkmrk--5"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>**Note:** Use 5V for louder output. The 1K resistor on TX protects the DFPlayer's RX pin.

#### Setting Up Sound Files

1. Format a micro SD card as FAT32
2. Create a folder named `mp3` in the root
3. Name your sound files: `0001.mp3`, `0002.mp3`, `0003.mp3`, etc.
4. Insert the SD card into the DFPlayer

**Recommended sound files:**

<table id="bkmrk-file-event-0001.mp3-"><thead><tr><th>File</th><th>Event</th></tr></thead><tbody><tr><td>`0001.mp3`</td><td>Score milestone award (every 5 points by default)</td></tr><tr><td>`0002.mp3`</td><td>Paddle hit (regular)</td></tr><tr><td>`0003.mp3`</td><td>Score lost</td></tr></tbody></table>

#### Enable in config.h

```
#define DFPLAYER_ENABLED   1    // Enable DFPlayer
#define ISD1820_ENABLED    0    // Disable ISD1820
```

<div class="highlight highlight-source-c notranslate position-relative overflow-auto" dir="auto" id="bkmrk--6"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>---

### Option B: ISD1820 Voice Record Playback Module

The ISD1820 is the simplest way to add sound. It stores **one recorded sound** directly on the chip — no SD card, no MP3 files. Just record your voice or a sound effect by holding the REC button, and the ESP32 will trigger it during the game.

#### Recording Your Sound

1. Power the ISD1820 (3.3V or 5V)
2. **Hold the REC button** on the module and speak or make a sound into the microphone
3. **Release REC** when done — the sound is saved permanently, even without power
4. Press the PLAY button on the module to hear it back before wiring to the ESP32

#### Wiring

```
T-Display          ISD1820
---------          -------
GPIO 15      ----> P-E  (Play Edge-triggered)
GND          ----> GND
3.3V or 5V   ----> VCC
                   SP+  ----> Speaker (+)
                   SP-  ----> Speaker (-)

```

<div class="snippet-clipboard-content notranslate position-relative overflow-auto" id="bkmrk--8"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>**Important:** Connect to the **P-E** pin (Play Edge), NOT the P-L pin. P-E plays the sound once when the pin goes HIGH, which is what we want.

#### Enable in config.h

```
#define DFPLAYER_ENABLED            0    // Disable DFPlayer
#define ISD1820_ENABLED             1    // Enable ISD1820
#define ISD1820_TRIGGER_PADDLE_HIT  1    // Play on paddle hit (1=yes, 0=no)
#define ISD1820_TRIGGER_SCORE_LOST  1    // Play on score lost (1=yes, 0=no)
```

<div class="highlight highlight-source-c notranslate position-relative overflow-auto" dir="auto" id="bkmrk--9"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>---

### No Sound Module?

That's fine too — just disable both in `config.h`:

```
#define DFPLAYER_ENABLED   0
#define ISD1820_ENABLED    0
```

<div class="highlight highlight-source-c notranslate position-relative overflow-auto" dir="auto" id="bkmrk--11"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>The game runs perfectly without any sound hardware attached.

---

## Game Events

<table id="bkmrk-event-when-does-it-t"><thead><tr><th>Event</th><th>When Does It Trigger?</th><th>DFPlayer</th><th>ISD1820</th></tr></thead><tbody><tr><td>Wall Bounce</td><td>Ball bounces off any wall</td><td>❌</td><td>❌</td></tr><tr><td>Paddle Hit</td><td>Ball hits your paddle</td><td>✅ regular sound</td><td>✅ (configurable)</td></tr><tr><td>Score Milestone</td><td>Score reaches a multiple of 5</td><td>✅ award sound</td><td>❌</td></tr><tr><td>Score Lost</td><td>You miss the ball</td><td>✅</td><td>✅ (configurable)</td></tr></tbody></table>

## Customizing the Game

Open `src/config.h` to change settings:

```
// ---- DFPlayer ----
#define DFPLAYER_ENABLED   1     // 1 = enable, 0 = disable
#define DFPLAYER_TX_PIN    17
#define DFPLAYER_RX_PIN    21
#define DFPLAYER_VOLUME    10    // 0-30
#define TRACK_AWARD        1     // 0001.mp3 - plays at score milestones
#define TRACK_PADDLE_HIT   2     // 0002.mp3 - plays on regular paddle hit
#define TRACK_SCORE_LOST   3     // 0003.mp3 - plays on score lost
#define SCORE_MILESTONE_INTERVAL  5   // Award sound fires at 5, 10, 15, 20...

// ---- ISD1820 ----
#define ISD1820_ENABLED             0    // 1 = enable, 0 = disable
#define ISD1820_PLAY_PIN            15   // GPIO connected to P-E pin
#define ISD1820_TRIGGER_PADDLE_HIT  1    // 1 = trigger on paddle hit
#define ISD1820_TRIGGER_SCORE_LOST  1    // 1 = trigger on score lost
```

<div class="highlight highlight-source-c notranslate position-relative overflow-auto" dir="auto" id="bkmrk--13"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>### Other Fun Settings to Try

```
#define PADDLE_WIDTH     15    // Make paddle bigger = easier game
#define BALL_SPEED_START  3    // Higher number = slower ball
#define PADDLE_SPEED      2    // How fast paddle moves

// External button pins (set to -1 to disable)
#define EXT_BUTTON_DOWN  25    // External button - paddle left
#define EXT_BUTTON_UP    13    // External button - paddle right
```

<div class="highlight highlight-source-c notranslate position-relative overflow-auto" dir="auto" id="bkmrk--14"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>## Adding Your Own Event Code!

Want to customize what happens when game events occur? You can add your own custom code!

### Where to Put Your Code

Open `src/events.cpp` - this is where all the event magic happens!

**The file has 4 event functions you can modify:**

```
void onWallBounce() {
  // This runs when the ball hits a wall
}

void onPaddleHit() {
  // This runs when you hit the ball
}

void onScoreGained() {
  // This runs when you score a point
}

void onScoreLost() {
  // This runs when you miss the ball
}
```

<div class="highlight highlight-source-c notranslate position-relative overflow-auto" dir="auto" id="bkmrk--15"><div class="zeroclipboard-container"><svg aria-hidden="true" class="octicon octicon-copy js-clipboard-copy-icon" data-view-component="true" height="16" version="1.1" viewbox="0 0 16 16" width="16"></svg>  
</div></div>### Project Files Overview

<table id="bkmrk-file-what%27s-inside-s"><thead><tr><th>File</th><th>What's Inside</th></tr></thead><tbody><tr><td>`src/main.cpp`</td><td>The game code (paddle, ball, score)</td></tr><tr><td>`src/config.h`</td><td>Settings you can change (pins, speeds, sizes)</td></tr><tr><td>`src/events.h`</td><td>List of event functions (don't change this)</td></tr><tr><td>`src/events.cpp`</td><td>**Your code goes here!** Event implementations</td></tr></tbody></table>

## T-Display Pinout

Here are the GPIO pins used in this project:

<table id="bkmrk-pin-usage-notes-0-le"><thead><tr><th>Pin</th><th>Usage</th><th>Notes</th></tr></thead><tbody><tr><td>0</td><td>Left button</td><td>Onboard button</td></tr><tr><td>13</td><td>External button</td><td>Default: External LEFT button</td></tr><tr><td>15</td><td>ISD1820 P-E</td><td>Trigger pin (when ISD1820 enabled)</td></tr><tr><td>17</td><td>DFPlayer TX</td><td>Serial TX to DFPlayer RX (when DFPlayer enabled)</td></tr><tr><td>21</td><td>DFPlayer RX</td><td>Serial RX from DFPlayer TX (when DFPlayer enabled)</td></tr><tr><td>22</td><td>DFPlayer BUSY</td><td>Optional: detect when DFPlayer is playing</td></tr><tr><td>25</td><td>External button</td><td>Default: External RIGHT button</td></tr><tr><td>26</td><td>Event pin</td><td>Ball wall bounce</td></tr><tr><td>27</td><td>Event pin</td><td>Ball paddle hit</td></tr><tr><td>32</td><td>Event pin</td><td>Score gained</td></tr><tr><td>33</td><td>Event pin</td><td>Score lost</td></tr><tr><td>35</td><td>Right button</td><td>Onboard button (input only)</td></tr></tbody></table>

## Troubleshooting

### No sound from DFPlayer

- Check wiring: TX to RX, RX to TX (they cross over!)
- Make sure the SD card is formatted as FAT32
- Verify sound files are in an `mp3` folder and named `0001.mp3`, `0002.mp3`, etc.
- Try a different speaker
- Check the DFPlayer is getting power (5V recommended for reliable operation)
- Open the Serial Monitor — the game prints `[DFPlayer] OK!` or a wiring error on startup

### No sound from ISD1820

- Make sure you have recorded a sound first (hold REC button, speak, release)
- Check you are connected to the **P-E** pin, not P-L
- Test the sound by pressing the PLAY button on the module itself
- Check `ISD1820_ENABLED 1` is set in `config.h`
- Verify the GPIO pin number matches `ISD1820_PLAY_PIN` in `config.h`

### Code won't upload

- Try pressing the RESET button on the T-Display
- Make sure the USB cable supports data (some only charge)
- Try a different USB port

### Game runs but no sound

- Open the Serial Monitor — startup messages show which module is active and whether it initialised successfully

## Want to Learn More?

- Change the ball to a different image! (Look at `kim-jong-un.h`)
- Add your own background image! (Look at `background.h`)
- Create new events for different game situations
- Adjust the ball rotation speed in `main.cpp` (`BALL_ROTATION_SPEED`)

## Features

- 🎨 Custom ball sprite with rolling animation
- 🖼️ Background image support
- 🎮 Onboard + external button control
- 🔊 DFPlayer Mini sound effects (multiple tracks, SD card)
- 🔊 ISD1820 voice module support (record your own sound, no SD card needed)
- 📈 Increasing difficulty as you score

---

**Have fun learning electronics! 🚀**