Parent info
Parts you need
Affiliate links — we may earn a small commission
Try this circuit in your browser!
Run the code, press the buttons and watch what happens — before you buy any parts. No account needed.
Open in Simulator →No instructions. You are the engineer.
Every other project in this series gave you a problem to solve, a sensor to use, and code to upload. This one is different.
You’re going to design a device that solves a problem YOU chose. The ESP32 is your platform. The sensors are your palette. The code is yours to write (or adapt). The problem is real.
This is what engineers actually do.
Budget: ~$35 | Time: 4–6 hours | Difficulty: ●●●●●
What you’ll need
| Part | What it does | Price |
|---|---|---|
| ESP32-S3-DevKitC-1 | Your platform for everything | ~$12 |
| Sensors (your choice) | Chosen based on your problem | ~$10 |
| OLED display | For your output interface | ~$4 |
| Breadboard + wires | Prototyping base | ~$5 |
Total: ~$35 approximate | Choose additional components for your specific problem.
Phase 1: Define the problem
Time: ~60 minutes — this is the most important phase
A real engineering project starts with a problem statement, not a solution. The common mistake: “I want to build a device that does X.” Better: “People/things have problem Y, and I think a device could solve it.”
Problem statement template:
“In [context], [person/system] experiences [problem] because [root cause]. A [type of device] could help by [proposed solution].”
Example problem statements:
School context:
- “In our school library, students don’t know which study rooms are occupied without physically checking. A door sensor + web dashboard could show occupancy on a central display.”
- “Athletes at our school can’t easily track their hydration during practice. A wearable moisture + time device could remind them to drink at intervals.”
Home context:
- “My family’s refrigerator is left open accidentally, leading to food waste. A door sensor + alarm could alert within 60 seconds of it being open.”
- “My elderly grandparent sometimes forgets to take medication. A pill dispenser that sounds an alert at set times could help.”
Environmental context:
- “The school parking lot has poor visibility for drivers backing out. Ultrasonic distance sensors + LED alerts could warn when a pedestrian is in the blind spot.”
- “The school greenhouse doesn’t have temperature/humidity monitoring. Sensors + alerts could prevent plant damage.”
Write YOUR problem statement. It must be:
- Specific (names the exact context and person)
- Real (an actual problem, not hypothetical)
- Solvable with ESP32 capabilities
- Measurable (how will you know if it worked?)
Phase 2: Map requirements
Time: ~30 minutes
Good engineers write requirements before building. Fill in this table:
Functional requirements (what it MUST do):
| # | Requirement | How you’ll test it |
|---|---|---|
| F1 | ||
| F2 | ||
| F3 |
Non-functional requirements (how well it must do it):
| # | Requirement | Target |
|---|---|---|
| NF1 | Power (battery life or wired?) | |
| NF2 | Response time | |
| NF3 | Size/portability | |
| NF4 | Cost constraint | <$35 |
Example — School room occupancy monitor:
- F1: Detect whether room door is open or closed (test: open/close 10 times, verify 10/10 correct)
- F2: Display occupancy on a screen visible from the hallway (test: readable from 2m)
- F3: Update within 2 seconds of door state change (test: time with stopwatch)
- NF1: Run on USB power (wired is acceptable for fixed installation)
- NF2: Response ≤2 seconds
- NF3: Fits on door frame, <15cm total
Phase 3: Choose your sensors
Sensor reference table — match to your problem:
| Sensor | Measures | Cost | Use case |
|---|---|---|---|
| HC-SR04 | Distance (2–400cm) | $4 | Proximity, presence |
| PIR | Motion | $3 | Occupancy, trigger |
| DS18B20 | Temperature | $4 | Thermal monitoring |
| DHT22 | Temp + Humidity | $5 | Environment, comfort |
| Soil moisture | Moisture in substrate | $3 | Plant watering |
| Load cell + HX711 | Weight/force | $8 | Dispensing, quantity |
| Reed switch | Magnet/door contact | $1 | Door open/closed |
| LDR | Light level | $1 | Presence, daylight |
| Push button | Manual input | $1 | User control |
| SG90 servo | Physical actuation | $3 | Movement, dispensing |
| Buzzer | Audio alert | $2 | Alarm, notification |
| WS2812B LED | Visual indicator | $5/strip | Status display |
Pick 1–3 sensors that address your problem. More sensors = more complexity.
Phase 4: Design the system
Time: ~30 minutes
Draw a system block diagram (on paper is fine):
[INPUT SENSORS] → [ESP32] → [OUTPUTS]
| | |
DS18B20 Process OLED Display
Reed switch Logic LED strip
WiFi Buzzer
Storage Web page
Then write your data flow:
- What does the ESP32 read, and how often?
- What calculation or logic does it perform?
- What does it output based on that calculation?
Example — Medication reminder:
- Read: DS3231 RTC (real-time clock) every minute
- Logic: Compare current time to preset alarm times
- Output: If time matches → buzz 3 times, light LED red, log to display
Phase 5: Build the hardware prototype
Time: ~60 minutes
Prototype on breadboard first — never build final until it works.
Universal ESP32 wiring template:
Analog sensors → GPIO1-10 (ADC-capable pins) C6: GPIO0-6
Digital sensors → GPIO1-18, GPIO21, GPIO38-47 (most GPIO pins) C6: GPIO0-7, 9-11, 15, 18-23
I2C devices → GPIO8 (SDA), GPIO9 (SCL) C6: GPIO6 (SDA), GPIO7 (SCL)
SPI devices → GPIO12 (CLK), GPIO11 (MOSI), GPIO13 (MISO), GPIO10 (CS)
C6: GPIO23, 22, 21, 18
Servos/PWM → GPIO14, 15, 16 C6: GPIO5, 4, 3
LEDs → Any GPIO with 220Ω resistor
Buttons → Any GPIO with INPUT_PULLUP + connect to GND
Prototype rules:
- Use female-to-male jumper wires for sensor modules
- Label each wire with masking tape
- Test each component individually before combining
Phase 6: Write the code
Time: ~90 minutes
Code architecture template (copy and adapt):
The big picture first. This is a framework — a skeleton you fill in for your specific problem. It shows the correct structure for any ESP32 project:
- Four separate functions keep the code organized:
readSensors(gather data),processLogic(make decisions),updateOutputs(act on those decisions),handleWeb(serve the web page). - The
loop()function calls them in order, every 100ms — like a heartbeat with four distinct phases. - This separation means you can fix one part (e.g., the display) without breaking the sensor reading.
Adapt this skeleton to your specific sensors, outputs, and logic:
// ========== CHOOSE YOUR BOARD ==========
// Uncomment the line for YOUR board:
#define BOARD_S3 // ESP32-S3-DevKitC-1
//#define BOARD_C6 // ESP32-C6-DevKitC-1
// ========================================
#ifdef BOARD_S3
#define PIN_SDA 8
#define PIN_SCL 9
#endif
#ifdef BOARD_C6
#define PIN_SDA 6
#define PIN_SCL 7
#endif
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASS";
void readSensors() {
}
void processLogic() {
}
void updateOutputs() {
}
void handleWeb() {
}
void setup() {
Serial.begin(115200);
}
void loop() {
readSensors();
processLogic();
updateOutputs();
handleWeb();
delay(100);
}
Line-by-line: what every line does and why
Lines 1–2: Borrowing ready-made tools
#include <WiFi.h>
#include <WebServer.h>
#include means “grab this instruction book.” You will add more #include lines for your specific sensors (e.g., #include <DHT.h> for a temperature sensor). Start with these two if your project needs a web interface.
Lines 4–5: WiFi credentials
const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASS";
const char* means “a piece of text that never changes.” ssid is your WiFi network name, password is the WiFi password. Replace these before uploading. The * (pointer) tells the computer where in memory to find the text.
Lines 7–9: readSensors() — gather data, nothing else
void readSensors() {
}
void means this function gives nothing back — it just does work. Fill this with analogRead(), digitalRead(), or sensor library calls. Store results in global variables (variables declared outside any function, at the top of the file) so other functions can use them. The golden rule: this function only reads. It never makes decisions.
Lines 11–13: processLogic() — make decisions
void processLogic() {
}
This function reads the global sensor variables and decides what to do. Example: “if temperature > 30°C, set alarmActive = true.” It updates state variables but does NOT yet turn on the buzzer — that’s the job of updateOutputs. Keeping decisions separate from actions makes debugging much easier.
Lines 15–17: updateOutputs() — act on decisions
void updateOutputs() {
}
This function looks at the state variables set by processLogic and drives the hardware. Turn on an LED, beep a buzzer, update the OLED display, move a servo. Everything physical happens here.
Lines 19–21: handleWeb() — serve the web page
void handleWeb() {
}
If your project has a web interface, put server.handleClient() here. This must be called repeatedly for the web server to respond to requests. If you don’t need WiFi, leave this empty or delete it.
Lines 23–27: setup() — the morning routine
void setup() {
Serial.begin(115200);
}
setup() runs once when the ESP32 powers on. Serial.begin(115200) opens the “phone line” to your computer through USB at a speed of 115200 baud. Add your initialization code here: start the WiFi, initialize sensors, configure pin modes. Anything that needs to happen once at startup goes here.
Lines 29–35: loop() — the heartbeat
void loop() {
readSensors();
processLogic();
updateOutputs();
handleWeb();
delay(100);
}
loop() runs forever, over and over, as long as the ESP32 has power. The four function calls happen in the right order every 100 milliseconds. delay(100) pauses 100ms between each loop — that’s 10 updates per second. Adjust this number based on how fast your sensor needs to be read (a temperature sensor is fine at 1 update/second; a motion detector might need 50 updates/second).
The whole thing in one sentence
This skeleton enforces a clean pipeline: read sensors → make decisions → drive outputs → serve web, repeating 10 times per second, so each part of your project stays independent and easy to debug.
First thing to try: add Serial.println("loop running"); inside loop() and open Serial Monitor. You should see “loop running” printing 10 times per second. Then add one sensor read and print its value — verify it makes sense before adding any logic or outputs.
Debugging tip: Use Serial.println() to print every important value. Watch Serial Monitor as you develop. Fix one thing at a time.
Phase 7: Test against requirements
Time: ~30 minutes
Go back to your requirements table from Phase 2. Test each one systematically.
| Requirement | Expected | Actual | Pass/Fail | Notes |
|---|---|---|---|---|
| F1: | ||||
| F2: | ||||
| NF2: Response ≤2s | 2s | __ s |
If something fails: Don’t give up. Debug the specific requirement. Change ONE thing at a time.
Phase 8: Iterate and refine
Time: varies
Real engineering is iterative. First prototype never works perfectly. Common improvements:
- Accuracy: Average multiple sensor readings, add filtering
- Responsiveness: Reduce delays, optimize loop timing
- Reliability: Add error handling for disconnected sensors
- Usability: Better display layout, clearer labels, intuitive controls
Document what you changed and why.
Phase 9: Present your project
Presentation structure (5–10 minutes):
- Problem statement (1 min): What problem did you identify? Why does it matter?
- Solution overview (1 min): What does your device do?
- Live demo (2–3 min): Show it working. Demonstrate the key functionality.
- How it works (1–2 min): Explain the sensors, the logic, the code structure.
- Requirements review (1 min): What requirements did you meet? Which didn’t you meet, and why?
- What you’d do differently (1 min): Engineering reflection.
The strongest capstone presentations answer: “Why does this matter? Who benefits? What would it take to make this a real product?”
Presentation tip: If your device fails during the demo (it happens to everyone), don’t panic. Say: “The device isn’t responding right now, which gives me a chance to walk you through what should happen and what I think is causing the failure.” Engineers debug in public. That’s a skill too.
What just happened
You went through the engineering design process:
- Define the problem
- Research requirements
- Design a solution
- Prototype
- Test
- Iterate
This is exactly what engineers at Apple, Tesla, and NASA do — at much larger scale with more constraints. The process is identical.
The ESP32 gives you a professional-grade platform: 240MHz dual-core processor, WiFi, Bluetooth, 12-bit ADC, I2C/SPI/UART — hardware that was enterprise-level 10 years ago. You used it to solve a real problem. That’s what engineers do.
Curriculum connections:
- NGSS HS-ETS1-1: Analyze a major global challenge to specify qualitative and quantitative criteria and constraints for solutions
- NGSS HS-ETS1-2: Design a solution to a complex real-world problem by breaking it into smaller, manageable problems
- NGSS HS-ETS1-3: Evaluate a solution to a complex real-world problem based on prioritized criteria and trade-offs
- NGSS HS-ETS1-4: Use a computer simulation to model the impact of proposed solutions
AP Computer Science Principles covers the same concepts computationally — but you built something physical. The code you wrote controls atoms in the real world, not just pixels on a screen.
Capstone checklist
Before you present, confirm you have:
- Written problem statement (specific, real, measurable)
- Requirements table with pass/fail results
- Working hardware prototype (or a documented failure with analysis)
- Code with comments explaining each section
- System block diagram (even hand-drawn)
- Test results table
- “What I’d do differently” section
- Estimate: what would it cost to manufacture 100 of these?
Ideas if you’re stuck
If nothing comes to mind, try these domains:
School: Room temperature logger, cafeteria loudness monitor, bathroom occupancy indicator, water bottle reminder bracelet
Sports: Heart rate logger for training, jump height measurer (accelerometer), pitch count tracker (baseball)
Home: Plant watering reminder, window open/rain detector, mailbox letter notifier, energy usage monitor
Accessibility: Blind spot alert for wheelchair, pill reminder dispenser, loud alarm for hearing-impaired
Environmental: School garden monitor, local air quality tracker, light pollution measurer
Pick the one you’d actually use. The best capstone projects solve problems their builders actually have.
Troubleshooting the design process
| Problem | Fix |
|---|---|
| “I don’t know what to build” | Ask: “What’s something I do every day that wastes time or causes frustration?” That’s usually a solvable problem. |
| “My sensor doesn’t work” | Isolate it. Build the minimal sketch: just read that one sensor, print to Serial. Everything else off. |
| “My code is a mess” | Refactor using the loop() structure above: readSensors(), processLogic(), updateOutputs(). Separation of concerns. |
| “It works on my desk but not in real use” | Power issue (USB vs. battery), sensor placement, environmental interference. Test in the real environment. |
| “I ran out of time” | Present your partial prototype honestly. Explain what works, what doesn’t, and what you’d need to finish it. That’s professional. |