Projects · MiniMe

MiniMe

Discord bot firmware for a WeAct Studio ESP32-S3-N16R8 - joins Wi-Fi and the Discord Gateway, reads sensors, drives GPIO from chat, shows a live 128×128 SSD1327 OLED dashboard, and serves a LAN web UI. v0.5.1 shipped breadboard firmware.

Full write-up (commands, OLED, LAN web, hardware, setup)

MiniMe ESP32-S3 breadboard with SSD1327 OLED, touch pad, and DS18B20

Breadboard prototype: WeAct Studio ESP32-S3-N16R8, 128×128 SSD1327 (GND / VCC / SCL / SDA), two discrete LEDs, GPIO 4 touch wake pad, and DS18B20 on GPIO 10. Sensor fail on the OLED is T:--Error--.

Status: shipped breadboard firmware · v0.5.1 · green CI compile · PCB / desk case still planned.

I find this working well and have not found any bugs. Unless I find something to add to its function, or a bug, this is now shipped code.

After Wi-Fi connects, MiniMe serves a LAN web dashboard at http://<board-ip>/ (Display, SysInfo, LOG, Serial). Theme matches k9dtv.com (light/dark). IC chips: theme (sun/moon) and Display / Log layout.

MiniMe LAN web UI dark theme - Display, SysInfo, LOG, and Serial MiniMe LAN web UI light theme - Display, SysInfo, LOG, and Serial

LAN web UI (v0.5.1): light/dark + Display/Log layout chips; Sig / Heap / Srv bars share one left edge and stop at the Display panel edge. SysInfo shows live CPU MHz. Screenshot follows the site theme toggle.

This is my first big modern MCU / Discord bot project on ESP32. AI helped with firmware edits, multi-file layout, and GitHub updates. I owned the architecture, wiring, Discord Gateway/OLED design, commands, power/idle trade-offs, and what shipped on the board.

Ongoing project

Firmware on the GitHub repo is shipped. Hardware and optional extras still on the list:

  • Wireless firmware updates - update the ESP32 over the network without USB each time (Wi-Fi OTA is in firmware; keep polishing)
  • Mention / DM indicators on set1 / set2 - DM to the bot flashes set1 at 10 Hz; @mention of OWNER_ID_STR flashes set2 at 10 Hz. Owner !clear turns both off
  • PCB and desk case - move off the breadboard onto a custom board and enclosure

What this bot can do

Same list Discord shows for !help.

Public commands

DMs, TARGET_CHANNEL_ID, or TARGET_CHANNEL_ID1:

  • !apod - NASA Astronomy Picture of the Day
  • !ask <question> - DeepSeek text reply in chat
  • !display <text> - payload on OLED rows 15-16 (not the word !display)
  • !help - command list
  • !iss - International Space Station position
  • !news - space / high-tech headlines
  • !physics - latest arXiv physics papers
  • !sys - uptime (d h m s), heap (SRAM + PSRAM), Wi-Fi RSSI, gateway, USB VBUS
  • !temp - indoor DS18B20 temperature
  • !time - bot local time (US Pacific, DST aware)
  • !weather <zip> - US ZIP weather (OpenWeatherMap)

Owner-only

OWNER_ID_STR:

  • !led on/off / !led <r> <g> <b> - RGB NeoPixel (GPIO 48)
  • !servo <0-90> - servo angle (updates the Srv: bar)
  • !set1 on / !set1 off - digital output pin 1
  • !set2 on / !set2 off - digital output pin 2
  • !clear - stop set1/set2 flash and force both off
  • !ota - Wi-Fi firmware update info (IP / hostname)

Automatic posts

Sent once to TARGET_CHANNEL_ID after Gateway connects (boot only):

  • !sys diagnostics
  • !help command list

No repeating 4-hour sys posts or daily temperature summaries. Commands still work in TARGET_CHANNEL_ID, TARGET_CHANNEL_ID1, and DMs.

Bot Discord presence

  • Starts Online when the Gateway identifies
  • Goes Idle after 5 minutes with no activity
  • Returns to Online on commands and the boot channel announce (touch does not set Online)

How it works

Everything runs on one ESP32-S3. Discord stays in the cloud; MiniMe talks to it over Gateway (websocket) and REST (HTTPS), paints the OLED, serves a LAN web dashboard, and wakes the panel from a touch pad.

  • Gateway - live link for chat commands, presence, Online/Idle, heartbeats
  • REST - bot posts replies, loads member names, pulls weather/science/AI APIs
  • OLED - always the status board; sleep blanks the panel only (Wi-Fi and Gateway stay up)
  • LAN web UI - same board status in a browser at http://<board-ip>/; light/dark theme and Display/Log layout chips; refreshes about once a second; does not replace OLED. The LOG panel is capped at 20 KB; if the next line would go over, LOG is cleared (avoids unbounded growth). Serial stays a fixed 12-line ring
  • Touch - wakes the OLED only; does not change Discord status or fire GPIO commands

OLED dashboard

128×128 SSD1327 status board (U8g2). Font is 5×7 with 1px padding (8px per row).

RowBaseline yWhat it shows
07MiniMe, GW:Good / GW:Bad, time
215Bot Online/Idle; date (right)
323Uptime + temp; fail: T:--Error--
431Sig: Wi-Fi RSSI bar
539Heap: memory bar (SRAM + PSRAM)
647Srv: servo bar 0-90°
7-1455...111Eight user rows: name, presence, command count (24h)
15-16119 / 127Command text or !display payload (blank when idle)

After 1 minute idle the panel dims, then turns off (setPowerSave). Touch on GPIO 4, commands, and the boot channel announce wake it. When the OLED is off and Discord is Idle, CPU is 80 MHz; otherwise 240 MHz.

Secrets and IDs

Copy MiniMe_Discord_Bot/secrets.example.h to secrets.h and fill in:

#define WIFI_SSID            "ssid"
#define WIFI_PASSWORD        "password"
#define BOT_TOKEN            "bot token"
#define WEATHER_API_KEY      "WEATHER_API_KEY"
#define NASA_API_KEY         "NASA_API_KEY"
#define DEEPSEEK_API_KEY     "DEEPSEEK_API_KEY"
#define BOT_GUILD_ID         "GUILD_ID"
#define OWNER_ID_STR         "OWNER_ID_STR"
#define TARGET_CHANNEL_ID    "TARGET_CHANNEL_ID"
#define TARGET_CHANNEL_ID1   "TARGET_CHANNEL_ID1"

Use #define (not const char*) so every .cpp can include secrets.h without linker errors. IDs are digits only as C strings. OTA password (if used) also lives in secrets.h.

How to get a Discord bot token
  1. Open the Discord Developer Portal.
  2. New Application → name it → Create.
  3. BotAdd Bot if needed → copy token into BOT_TOKEN.
  4. Enable Message Content, Server Members, and Presence intents.
  5. OAuth2 → URL Generator: scope bot, permissions View Channels, Send Messages, Read Message History → invite to your server.
Owner, channel, and guild IDs

Enable Discord Developer Mode, then:

  • Owner ID: right-click your avatar → Copy User ID → OWNER_ID_STR
  • Channel ID: right-click text channel → Copy Channel ID
  • Guild ID: right-click server icon → Copy Server ID → BOT_GUILD_ID

Hardware (default pins)

WeAct Studio ESP32-S3-N16R8 (16MB flash, 8MB PSRAM). Display: SSD1327 128×128 I2C.

DeviceGPIO
RGB NeoPixel (!led)48
Servo47
Digital out 1 (!set1)6
Digital out 2 (!set2)7
DS18B20 data10
OLED SDA / SCL8 / 9
Touch wake pad4
USB VBUS ADC1

OLED labels: GND, VCC, SCL, SDA. Change pins in minime_config.h if your wiring differs.

Touch wake pad (GPIO 4)

Capacitive pad on GPIO 4 wakes the OLED after dim/off. Connect copper tape or a short wire to GPIO 4; no external pull-up needed. USB VBUS on GPIO 1 (10 kΩ divider) compensates touch thresholds when port voltage sags.

Touch only wakes the OLED - it does not send Discord messages, change Online/Idle, or move GPIO outputs.

Arduino IDE setup

  1. Install Arduino IDE and the Espressif esp32 board package.
  2. Open only MiniMe_Discord_Bot/MiniMe_Discord_Bot.ino (plus the sketch .cpp / .h files and partitions.csv).
  3. Board: ESP32S3 Dev Module; enable USB CDC On Boot, OPI PSRAM, 16MB flash; use the sketch partitions.csv (dual OTA apps, no filesystem).
  4. Libraries: WebSockets, ArduinoJson, U8g2, OneWire, DallasTemperature, Adafruit NeoPixel, NTPClient.
  5. Copy secrets.example.h to secrets.h and fill in.
  6. Upload. Confirm with Discord !help, the OLED, and http://<board-ip>/.

GitHub Actions compiles the sketch on every push to master (compile check only; does not upload to the board).

Safety

Never commit live bot tokens, API keys, passwords, or Discord snowflake IDs. Reset leaked tokens in the Developer Portal immediately.

Original MiniMe source and docs in the repo are under the MIT License. See LICENSE on GitHub.