Skip to content

About

A tiny ESP32-C3 that watches you with glowing eyes while it automates your outlets. Runs relay chains, timers, and occupancy simulations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Smart Socket — ESP32-C3 Smart Relay with Animated OLED

A feature-rich smart relay controller built on the ESP32-C3 Super Mini, featuring an animated OLED display, five-button D-pad navigation, programmable ON/OFF action chains, and a passive buzzer — all running entirely on-device with no WiFi dependency.

Think of it as a friendly little automation box for your home: run timers, set cycles (on 10 min / off 5 min), or simulate occupancy when you're away. The OLED face watches and reacts while the relay does its job.


What You'll Build

┌──────────────────────────────────────────────────┐
│                   ESP32-C3                       │
│             Super Mini (USB-C)                   │
│                                                  │
│   ┌─────────┐           ┌─────────────────────┐  │
│   │  Button  │──── GPIO0 ┤  BACK / LEFT       │  │
│   │  D-pad   │──── GPIO1 ┤  UP                │  │
│   │          │──── GPIO2 ┤  ENTER (active-LOW)│  │
│   │          │──── GPIO3 ┤  DOWN              │  │
│   │          │──── GPIO4 ┤  MODE / RIGHT      │  │
│   └─────────┘           └─────────────────────├  │
│                                                  │   
│   OLED SSD1306 (I2C)     Relay Module            │   
│   SDA → GPIO8            SIG → GPIO20            │◄── Output
│   SCL → GPIO9            VCC / GND               │   
│                                                  │   
│   Buzzer             USB-C Port                  │   
│   PWM  → GPIO21      CDC Serial                  │   
└──────────────────────────────────────────────────┘

Product Preview

App Demo App Demo App Demo

⚡ Menu Speedrun Showcase


Features

Feature Details
Processor ESP32-C3 (RISC-V), USB-C native Serial CDC, 8 MHz crystal
Display 128x64 SSD1306 OLED via I2C, animated at ~60 FPS
Relay 5V single-channel module — up to 10A / 250VAC (configurable)
Buttons 5× momentary D-pad (UP / DOWN / ENTER / LEFT / MODE)
Buzzer Passive piezo with multi-tone cues via LEDC PWM
Storage NVS flash — up to 8 programmable ON/OFF chains
Modes Run Chain · Countdown Timer · Manual Mode · Occupancy Simulation

UI Highlights

The OLED is not a static display — it features a full animation engine:

  • "RoboEyes" idle face — Two animated blocks that scan the room, blink, and express mood
  • Spring-physics selection highlights that slide and settle naturally
  • Slide transitions between screens with eased motion
  • Progress rings, pulsing power glyphs, and celebration animations on chain completion
  • Emergency stop screen with angry eyes and flashing border
  • All motion routes through easing curves — zero linear movement

Operating Modes

  1. Run Chain — Select a saved ON/OFF sequence and run it (once, twice, 3×, 5×, 10×, or forever)
  2. Countdown Timer — Set the relay ON or OFF for N minutes, then auto-stop
  3. Manual Mode — Hold UP/DOWN to toggle the relay; release to stop
  4. Occupancy Simulation — Random ON/OFF cycles between min/max intervals to make it look like someone's home
  5. Chain Builder — Create/edit your own multi-step automation sequences
  6. Settings — Buzzer on/off, volume, brightness, screen sleep timeout

Bill of Materials

# Component Notes
1 ESP32-C3 Super Mini USB-C module; Dev Module variant (8 MHz crystal)
2 SSD1306 OLED Module 128×64 I2C, GND/VCC/SCL/SDA (no reset pin needed)
3 5V Relay Module Single-channel; SIG data pin to GPIO20
4 Passive Buzzer Piezo element; PWM-driven via GPIO21
5 Push Buttons × 5 Momentary, 6×6 mm standard (or equivalent)
6 Male Header Pins × 3 For relay SIG, buzzer V/G, and load output (COM/NO)

Wiring Diagram / Schematic

Wiring schematic for Smart Socket

Notes:

  • [10k] = 10 kΩ pull-down resistor on each active-HIGH button input
  • [BTN] = momentary push button, one side to GPIO, other to GND
  • BUTTON_ENTER (GPIO2) is the ONLY active-LOW button (strapping pin constraint)
  • Relay module: SIG=active-LOW trigger; wire SIG→GPIO20, VCC→5V, GND→GND
  • Buzzer: + lead → GPIO21 via small series resistor (~100Ω), - lead → GND
  • Relay NO/COM wired in the hot line between mains live and the socket

I2C Bus Detail

     SSD1306 OLED (0x3C)
       SDA ──→ GPIO8
       SCL ──→ GPIO9
       VCC ──→ 3.3V
       GND ──→ GND

Physical Layout Suggestion

  ┌──────────────────────────────────────┐
  │           ESP32-C3 Module            │
  │     [USB-C]    [GPIO pins down]      │
  │                                      │
  │   OLED on top, facing user           │
  │   Buttons accessible from front      │
  │   Relay module mounted behind        │
  │   Buzzer peeking through case        │
  └──────────────────────────────────────┘

Installation / Setup

Prerequisites

  1. Arduino CLI installed (or the Arduino IDE) with ESP32 board support
  2. ESP32 board package: esp32 version 3.3.10 (or newer) targeting esp32:esp32c3
  3. U8g2 library (by Oliver Ertl) — version from the Arduino Library Manager

Option A: Arduino IDE

1. Install ESP32 boards via Boards Manager:
   → Go to File > Preferences
   → Board Manager URLs:
     https://raw.githubusercontent.com/espressif/arduino-esp32/master/package_esp32_index.json
   → Search "ESP32 by espressif" and install (v3.x+)

2. Install U8g2 library:
   → Go to Sketch > Include Library > Manage Libraries
   → Search "U8g2" and install

3. Select board:
   → Tools > Board > ESP32 Boards > ESP32-C3 Super Mini
   → Flash Method: Download Mode (or Native USB)
   → CDC on Boot: No CDC (since we use the native CDC ourselves)

4. Open the project:
   → File > Open > Navigate to SmartSocket/SmartSocket.ino

5. Compile and upload via USB-C.

Option B: Arduino CLI

# Install ESP32 core
arduino-cli core install esp32:esp32 --additional-urls \
  https://raw.githubusercontent.com/espressif/arduino-esp32/master/package_esp32_index.json

# Compile
arduino-cli compile \
  --fqbn esp32:esp32:esp32c3 \
  --build-path build \
  "SmartSocket/SmartSocket.ino"

# Upload
arduino-cli upload \
  --fqbn esp32:esp32:esp32c3 \
  -p /dev/cu.usbserial-XXXX \
  "SmartSocket/SmartSocket.ino"

# Monitor serial output (115200 baud)
arduino-cli monitor \
  -p /dev/cu.usbserial-XXXX \
  -c baudrate=115200

Verify It Works

After flashing, open the Serial Monitor at 115200 baud. You should see:

[I] ==== Smart Socket boot ====
[I] log level 3
[I]    UP     pin 1 active-HIGH
[I]    DOWN   pin 3 active-HIGH
[I]    BACK   pin 0 active-HIGH
[I]    ENTER  pin 2 active-LOW
[I]    MODE   pin 4 active-HIGH
[I] buttons init: ignore window 600ms

The OLED should come alive showing the idle face — two curious "eyes" scanning around looking for people. Press any button to enter the menu.


Project Structure

SmartSocket/
├── SmartSocket.ino            # Sketch entry point (just includes src/)
├── README.md                  # You are here
└── src/
    ├── config.h               # All hardware constants, pin map, enums, logging macros
    ├── SmartSocket.ino        # setup() and loop() — wire everything together
    ├── buttons.h / .cpp       # 5-button D-pad with debouncing, hold detection, auto-repeat
    ├── menu.h / .cpp          # Screen state machine: 20+ screens, full navigation logic
    ├── oled.h / .cpp          # SSD1306 rendering + animation engine (RoboEyes, springs)
    ├── timer.h / .cpp         # Chain execution engine with NVS power-loss resume
    ├── chain.h / .cpp         # NVS storage for up to 8 ActionChain blobs
    ├── settings.h / .cpp      # Device preferences persisted via Preferences/NVS
    ├── relay.h / .cpp         # Relay controller with safety timing floors
    └── buzzer.h / .cpp        # Passive buzzer: multi-tone pattern queue via LEDC PWM

Core Files Quick Reference

File Responsibility
config.h Single source of truth — pin map, timing constants, all enums (MenuScreen, ButtonId, ChainStep), per-button polarity table
menu.cpp 20+ screen state machine — the central brain. Processes button events, dispatches to per-screen handlers, calls render methods each frame
oled.cpp Animation engine — RoboEyes face (searching/happy/angry/sleepy), spring-physics highlights, slide transitions, progress rings, power glyphs
timer.cpp Chain executor — runs saved sequences against the relay, handles step timing, cycle counting, NVS snapshot for power-loss resume
chain.cpp NVS persistence — saves/loads ActionChain blobs to/from flash across reboots
buttons.cpp Debounce + hold detection — per-button active-high/low polarity, startup-ignore window, auto-repeat ramp for UP/DOWN

How to Use It

Navigation Grammar (Same Everywhere)

  UP / DOWN   → Navigate lists or adjust values
  ENTER       → Select / confirm / enter edit mode
  BACK (LEFT) → Go back / cancel
  MODE (RIGHT)→ Secondary action / next field / change value

Home Screen

Press ENTER or MODE from the idle face to open the home menu:

┌──────────────────────────┐
│        Smart Socket      │
│ ──────────────────────── │
│ ▸ Run Chain              │  ← Press and run a saved sequence
│   Timer                  │  ← One-shot countdown (ON or OFF for N min)
│   Manual Mode            │  ← Hold UP to turn on, DOWN to turn off
│   Presence               │  ← Random ON/OFF to simulate occupancy
│   New Chain              │  ← Create a custom automation sequence
│   Settings               │  ← Buzzer, brightness, sleep timeout
└──────────────────────────┘

Running a Chain

  1. Select Run Chain → ENTER
  2. Pick a chain from the list (UP/DOWN) → ENTER
  3. Watch it run! The OLED shows a progress ring around a pulsing lightning bolt
  4. Press BACK to stop, or MODE to pause/resume

Creating a Chain

  1. Select New Chain → ENTER
  2. Name it: use UP/DOWN to cycle characters, RIGHT to move to the next slot
  3. The chain builder shows each step; ENTER on a step to edit its ON/OFF state and duration
  4. Use + Add Step to insert new steps, or DELETE (BACK on a step) to remove
  5. Adjust Repeat count: Once / 2× / 3× / 5× / 10× / Forever
  6. Press Save Chain → ENTER to store it in flash

Countdown Timer

  1. Select Timer → ENTER
  2. Set target: ON or OFF (toggle the chip)
  3. Set duration: 1–240 minutes (UP/DOWN, RIGHT to advance field)
  4. Press Start → The relay activates for N minutes, then flips off and completes

Occupancy Simulation

  1. Select Presence → ENTER
  2. Set min/max interval in minutes (the randomness window)
  3. Press Start — The relay alternates ON/OFF randomly between those bounds, forever
  4. Great for making your home look lived-in while you're away

Manual Mode

Long-press ENTER from idle to enter manual mode:

  • Hold UP → Relay turns ON (hold to keep it on)
  • Hold DOWN → Relay turns OFF (hold to keep it off)
  • Press BACK to exit

Emergency Stop: Hold ENTER for 3 seconds during any run or manual mode — the relay is forcibly turned off immediately and an angry red warning screen flashes.

Settings

Setting What It Does
Buzzer: ON/OFF Toggle haptic audio feedback on/off
Volume: 0-100% Adjust buzzer PWM duty cycle (mapped to perceived loudness)
Bright: 0-100% OLED contrast — maps to SSD1306 internal contrast register
Sleep: 15s–5min Screen inactivity timeout before the OLED dims; 0 = never
Delete Chain Pick and erase a saved chain from flash

Advanced Configuration

Editing config.h

All hardware constants live in one file. Key things you might change:

// Pin map (modify these if your wiring differs)
constexpr uint8_t PIN_OLED_SDA = 8;
constexpr uint8_t PIN_OLED_SCL = 9;
constexpr uint8_t PIN_RELAY = 20;
constexpr uint8_t PIN_BUZZER = 21;

// Button polarity table (index = ButtonId)
constexpr bool BUTTON_ACTIVE_HIGH[BUTTON_COUNT] = {
    true,   // UP — GPIO1, wired to 3V3, pull-down idle
    true,   // DOWN — GPIO3, wired to 3V3, pull-down idle
    true,   // BACK — GPIO0, wired to 3V3, pull-down idle
    false,  // ENTER — GPIO2 (strapping pin! active-LOW, wired to GND)
    true,   // MODE — GPIO4, wired to 3V3, pull-down idle
};

// Timing constants
constexpr uint32_t RELAY_MIN_ON_MS  = 30000;  // Min relay ON duration (30s safety floor)
constexpr uint32_t RELAY_MIN_OFF_MS = 5000;   // Min relay OFF duration (5s safety floor)

// Logging
#define LOG_LEVEL 3        // 0=off, 1=errors, 2=info, 3=verbose per-frame

⚠️ Important: GPIO2 is a strapping pin on ESP32-C3. It idles HIGH by default and cannot be reliably used as an active-HIGH input. The ENTER button must be wired to GND and treated as active-LOW. Wiring it incorrectly will cause the device to auto-navigate into manual mode at boot.

Serial Debugging

The device uses the USB-C native Serial CDC port — connect your computer via USB and open a serial monitor at 115200 baud for full boot logs including button states, chain loads, and menu navigation events.


Troubleshooting

Problem Likely Cause Fix
OLED stays black at boot I2C address wrong or loose wiring Check SDA/SCL connections; ensure address is 0x3C (not 0x3D)
Device jumps into Manual Mode on boot GPIO2 floating HIGH during strapping Verify ENTER button is wired to GND, not 3V3; check pull-up is active
Buttons do nothing after reboot Startup ignore window triggered by noise The firmware suppresses input for 600ms at boot — wait and try again
Relay clicks but won't stay on RELAY_MIN_ON_MS safety floor violated The chain tries to switch too fast; increase step durations in the builder
NVS chains fail to load after code change ActionChain struct size changed Adding/removing fields invalidates saved chains — re-seed from scratch
Serial prints nothing Wrong baud rate or no CDC init Try 115200; ensure Serial.begin() called before other prints
OLED flickers / garbled pixels I2C pull-ups missing or too weak Add 4.7 kΩ pull-ups on SDA and SCL lines
Buzzer is silent despite settings PWM duty mapped to 0 Check buzzer volume setting; verify PIN_BUZZER wiring

Getting Verbose Logs

Set LOG_LEVEL = 3 in config.h and reflash. This enables per-frame logging of button states, menu transitions, chain execution details, and relay timing events.


Architecture Overview

  ┌──────────┐     ┌──────────┐     ┌──────────┐
  │  Buttons  │────▶│  Menu    │────▶│   OLED   │
  │  (Input)  │     │  (State) │     │  (Output)│
  └──────────┘     └────┬─────┘     └──────────┘
                        │
                 ┌──────┴──────┐
                 │  Timer /    │
                 │  Chain      │
                 │  Engine     │
                 └──────┬──────┘
                        │
                   ┌────┴─────┐
                   │ Relay /  │
                   │ Buzzer   │
                   │ (Actuators)│
                   └──────────┘

  Data Flow: Button Event → Screen State Machine → OLED Render + Relay Actuation

The firmware follows a clean Input → State → Output pattern with all subsystems wired as global singletons. The animation system drives the OLED at up to ~60 FPS using spring physics and easing curves — every pixel movement routes through an easing curve, never linearly.


License

This project is provided as-is for educational and personal use. No warranty expressed or implied.


Built with love for small embedded projects. Every screen transition, every button press, every relay click — all running from a single ESP32-C3 Super Mini.

About

A tiny ESP32-C3 that watches you with glowing eyes while it automates your outlets. Runs relay chains, timers, and occupancy simulations.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages