← SHEET 02 · ASSEMBLIES EL-001
ESP32 Desk Buddy
| PART NO | EL-001 |
|---|---|
| MATL / SYSTEM | ESP32 · SSD1306 OLED · DHT22 · PIR |
| TOOLS | Arduino IDE · Wokwi · C++ |
A small desk-mounted companion built around an ESP32: it watches for you sitting
down, greets you with an animated face on a 128x64 OLED, tracks temperature and
humidity, and chirps a reminder if you’ve been sitting too long. WiFi/NTP gives it
a live clock when a network is available and falls back to an uptime counter when
it isn’t. The whole thing is one non-blocking millis() scheduler — five
peripherals, five GPIOs, no delay() in the main loop.
OVERVIEW & MOTIVATION
Most desk-gadget builds either block on delay() calls (freezing sensor reads
while an animation plays) or hard-fail the moment WiFi drops. This one is built
the other way round from the start: every subsystem — display, DHT22, PIR,
buzzer, WiFi/NTP — is serviced from its own millis()-based timer inside a single
cooperative loop, and the clock view degrades gracefully to an uptime counter
instead of hanging or blanking when the network is unreachable. The behavior
target: react to presence within half a second, read environment data on a slow
5 s cadence appropriate for a DHT22, and nag (gently) after 45 minutes of
continuous sitting.
COMPONENTS & BOM
| REF | COMPONENT | SPEC | ROLE |
|---|---|---|---|
| U1 | ESP32 DevKit-C V4 | dual-core, WiFi/BT, 3.3 V logic | main controller, runs the scheduler |
| U2 | SSD1306 OLED | 128×64, I2C, addr 0x3C | face / clock / env display |
| U3 | DHT22 (AM2302) | ±0.5 °C, ±2 %RH, 1-wire digital | temperature + humidity |
| U4 | PIR motion sensor | HC-SR501-style, digital active-HIGH | presence detection |
| BZ1 | Passive buzzer | piezo, PWM/tone-driven | chirps and alert tones |
| R1 | 10 kΩ resistor | pull-up | DHT22 data-line pull-up to 3V3 |
WIRING
| ESP32 PIN | NET | PERIPHERAL PIN |
|---|---|---|
| 3V3 | 3V3 RAIL | OLED VCC, DHT22 VCC |
| GND | GND RAIL | OLED GND, DHT22 GND, PIR GND, BUZZER − |
| GPIO21 | I2C_SDA | OLED SDA |
| GPIO22 | I2C_SCL | OLED SCL |
| GPIO4 | DHT_DATA | DHT22 DATA (10 kΩ pull-up to 3V3) |
| 5V | 5V RAIL | PIR VCC |
| GPIO27 | PIR_OUT | PIR OUT |
| GPIO26 | BUZZER_DRV | BUZZER + |
FIRMWARE
sketch.ino runs one cooperative loop: serviceWifi(), serviceDht(),
servicePir(), serviceStateMachine(), serviceBuzzer(), and renderUi() each
check their own millis() timer and return immediately if it isn’t their turn —
nothing in loop() blocks. The UI is a small state machine (STATE_IDLE,
STATE_GREET, STATE_REMINDER) driven by PIR presence and a sit-timer, and the
buzzer runs its own step sequencer so multi-tone alert patterns don’t need
delay() either:
void servicePir(unsigned long now) {
bool raw = digitalRead(PIN_PIR) == HIGH;
if (raw != rawPirLast) { rawPirLast = raw; pirLastChangeAt = now; }
if (now - pirLastChangeAt >= PIR_DEBOUNCE_MS && raw != presence) {
presence = raw;
if (presence) {
sitStartMs = now;
breakArmed = true;
if (uiState == STATE_IDLE) enterState(STATE_GREET, now);
} else {
sitStartMs = 0;
breakArmed = false;
}
}
}
void serviceBuzzer(unsigned long now) {
if (!buzzerBusy) return;
if ((long)(now - buzzerNextAt) < 0) return;
if (buzzerStep >= buzzerSeqLen) { buzzerBusy = false; return; }
const ToneStep& s = buzzerSeq[buzzerStep];
tone(PIN_BUZZER, s.freq, s.durationMs);
buzzerNextAt = now + s.durationMs + s.gapAfterMs;
buzzerStep++;
}
The idle view alternates between the face and a clock/temperature/humidity
screen every 6 s (IDLE_VIEW_CYCLE_MS). Presence rising-edge triggers
STATE_GREET for 4 s with a single chirp; 45 continuous minutes of presence
(BREAK_REMINDER_MS) triggers STATE_REMINDER for 6 s with a three-tone chirp
pattern, then the sit-timer restarts so it repeats if you’re still there. DHT22
is polled every 5 s (DHT_INTERVAL_MS) and the last good reading is held on a
failed read rather than blanking the display.
SIMULATION
The project runs end-to-end in Wokwi against
diagram.json — no real hardware involved yet. What the sim shows: the OLED
boots to the idle face with blinking eyes, clicking the PIR sensor’s sense
control in the part inspector fires the greet chirp and swaps to the “HI!”
face for 4 s, and the idle view cycles to a clock/temp/humidity screen every
6 s (falling back to an uptime counter if WIFI_SSID is left blank, since
Wokwi’s simulated ESP32 does have real internet access to NTP if credentials
are filled in). To run it: open wokwi.com, start a new ESP32 project, paste
sketch.ino and diagram.json in over the defaults, and press Play. All five
Wokwi part types used (board-esp32-devkit-c-v4, wokwi-ssd1306,
wokwi-dht22, wokwi-pir-motion-sensor, wokwi-buzzer) are real parts —
no substitutions were needed for this build.
STATUS
Design and firmware are complete and self-consistent (pinout matches across
sketch.ino, diagram.json, and this page); the circuit has been exercised
in the Wokwi simulator only. No physical hardware has been assembled — no
bench measurements, no real DHT22/PIR timing numbers, no photos. Everything
above is a design value (pin assignment, polling interval, debounce window,
reminder interval) or an expected simulator behavior, not a measured result.
Next step: order the BOM parts and bring up the same firmware on real
hardware unchanged.
USE CASES & APPLICATIONS
Desk/workstation presence-aware assistants — posture and break reminders for
long work sessions, ambient room-condition readout (temp/humidity) without a
phone app, and a friendly idle-face status indicator for a home office. The
same non-blocking scheduler pattern (millis-timed subsystem services, no
delay() in loop()) generalizes to any small ESP32 project mixing a
display, a slow analog/digital sensor, an interrupt-ish digital input, and
audio feedback — smart mirrors, plant monitors, doorbell-style greeters.
FILES
sources/electronics/desk-buddy/sketch.ino— full ESP32 Arduino firmware: non-blocking scheduler, OLED UI state machine, DHT22/PIR/buzzer/WiFi services.sources/electronics/desk-buddy/diagram.json— Wokwi wiring diagram, pinout matches the firmware exactly.sources/electronics/desk-buddy/README.md— how to run the Wokwi sim, library list, and real-hardware build notes.