⚡ ESP32-S3 Development Session

From Zero to Beeping Button
in One Conversation

A complete embedded development session with Claude — from hardware detection to flashed firmware, fully automated.

Freenove ESP32-S3 FNK0086
🎵 880 Hz I2S Audio
📺 ST7789 2.8" Touch LCD
🤖 Claude Sonnet 4.6
Full Session Transcript
The Complete ESP32 Journey
Step 1 · Hardware Detection
👤
Richard

check if my Freenove Development Kit for ESP32-S3 ESP32 S3 CAM (Included), Dual-core 32-bit 240 MHz Microcontroller, Onboard Camera Wireless, Touch Screen Stereo Speakers Heart Rate Sensor, Tutorial Code is attached

Claude

Your Freenove ESP32-S3 board does not appear to be attached right now.

The only serial ports present are Bluetooth and the debug console — no USB serial devices were detected. Make sure you're using a data-capable USB cable, not a charge-only cable.

👤
Richard

i installed the driver and rebooted

👤
Richard

check the usb now!

Claude

✓ Your ESP32-S3 is detected!

Serial port: /dev/cu.wchusbserial5AB90133721

The wch prefix confirms the CH343/WCH USB serial driver is working. This is your port for flashing and serial communication.

Step 2 · Python Tooling
👤
Richard

install esptool py so that you can work with the board

Claude

redo that but use a virtual environment. the venv should be stored in the repo

(User interrupted — Claude switched to venv approach)

python3 -m venv .venv
.venv/bin/pip install esptool
# esptool.py v4.11.0 ✓
✓ esptool v4.11.0 .venv/ isolated in repo .gitignore updated
Step 3 · Flash Backup
👤
Richard

back up the current esp32 sketch to this repo — i mean back it up from the esp32 which is connected

Claude

Detected 8MB flash. Reading full image from the board…

.venv/bin/esptool.py --port /dev/cu.wchusbserial5AB90133721 \
  read_flash 0x0 0x800000 backup/flash_backup_default.bin

# Chip: ESP32-S3 (QFN56) rev v0.2
# PSRAM: 8MB (AP_3v3)
# Flash: 8MB → backup/flash_backup_default.bin ✓

Also wrote restore_default.sh — run it any time to restore the factory firmware.

✓ 8MB backup saved restore_default.sh created
Step 4 · Hardware Research
👤
Richard

write a sketch which runs on the esp32. i want it to display a button on the lcd and when i hit it, the speakers beep

Claude

Researching pin assignments from the official Freenove GitHub repo — downloading their TFT_eSPI setup zip and parsing the actual source files…

PeripheralChipPins
LCD 2.8" 240×320ST7789MOSI=20, SCLK=21, DC=0, CS=GND
Touch controllerFT6336U (I2C)SDA=2, SCL=1
I2S SpeakersStereo ampBCLK=42, DOUT=41, LRC=14
Heart rate sensorMAX30102I2C shared bus
SD cardSDMMCCMD=38, CLK=39, D0=40

Writing sketches/beep_button/beep_button.ino with TFT_eSPI + FT6336U + I2S…

✓ Sketch written User_Setup.h configured CLAUDE.md updated with full pin reference
Step 5 · Automated Flash Script
👤
Richard

write a script which flashes that sketch to the esp32

👤
Richard

shouldn't you be using esptool py?

Claude

You're right — esptool.py handles the flashing. The pipeline is:

arduino-cli compiles .ino → binary → esptool.py flashes it.

Wrote flash-sketch-001-button-beep.sh which automatically:

  • Installs arduino-cli via Homebrew if missing
  • Adds Espressif board index and installs ESP32 core
  • Installs TFT_eSPI + FT6336U libraries
  • Patches TFT_eSPI User_Setup.h with FNK0086 config
  • Compiles → build/
  • Flashes bootloader + partitions + app via .venv/bin/esptool.py
./flash-sketch-001-button-beep.sh           # compile + flash
./flash-sketch-001-button-beep.sh --compile  # compile only
./flash-sketch-001-button-beep.sh --flash    # flash only
Step 6 · Iterate to Working Firmware
👤
Richard

run ./flash-sketch-001-button-beep.sh, iterate until it works

Claude

⚠ Error 1: Arduino-FT6336U not in Arduino registry — Freenove custom library.

→ Fixed: download zip directly from Freenove GitHub repo.

⚠ Error 2: Variable named touch clashes with TouchStatusEnum::touch inside the FT6336U library.

→ Fixed: renamed to ts.

⚠ Error 3: driver/i2s.h API deprecated in ESP32 Arduino core 3.x.

→ Fixed: migrated to driver/i2s_std.h with new channel API.

⚠ Error 4: boot_app0.bin lookup used ~/.arduino15 — macOS uses ~/Library/Arduino15.

→ Fixed: search both paths.

# Final flash output:
Wrote 386624 bytes at 0x00010000 in 4.9 seconds
Hash of data verified.
Hard resetting via RTS pin...
[flash] Flash complete. Board is resetting...
✓ Compiled — 29% flash used ✓ Flashed successfully LCD shows BEEP button
Step 7 · Toggle Behaviour
👤
Richard

great but it keeps beeping. when i hit the button, start the beeping. when i hit it again, stop the beep

Claude

Redesigned as a toggle. Audio now runs as a FreeRTOS task on Core 0, continuously generating a sine wave into a phase accumulator. The UI loop on Core 1 simply flips a volatile bool beeping.

10ms fade-in/out envelope avoids clicks on start and stop.

  • Tap once → button turns red, shows STOP, 880 Hz tone starts
  • Tap again → button turns blue, shows BEEP, tone stops
// FreeRTOS audio task — Core 0
xTaskCreatePinnedToCore(beep_task, "beep", 4096, NULL, 1, NULL, 0);

// Loop on Core 1 — leading-edge toggle
if (hit && !prev_hit) {
    beeping = !beeping;
    draw_button(beeping);
}
✓ Compiled & flashed ✓ Toggle working FreeRTOS dual-core
Session Summary
Claude — What We Built Together
FileWhat it does
CLAUDE.mdFull hardware reference: pins, libs, venv rules
backup/flash_backup_default.bin8MB factory flash image from the board
restore_default.shOne-command factory restore via esptool.py
sketches/beep_button/beep_button.inoToggle-beep sketch: LCD button + I2S audio
flash-sketch-001-button-beep.shFull compile + flash automation
.claude/settings.json24 whitelisted commands for this project
.venv/Python venv with esptool v4.11.0