Project 2: The Deep Sleep Champion (Power Profiler)
Build a repeatable power profiling workflow and produce a current waveform that explains exactly where energy goes on XIAO ESP32-C3 and XIAO nRF52840.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Intermediate |
| Time Estimate | 1-2 weeks |
| Main Programming Language | C (ESP-IDF / nRF Connect SDK) |
| Alternative Programming Languages | Arduino C++, Zephyr |
| Coolness Level | High |
| Business Potential | Medium (battery devices) |
| Prerequisites | GPIO basics, timers, serial logs, basic electronics |
| Key Topics | Sleep states, wake sources, leakage, current profiling |
1. Learning Objectives
By completing this project, you will:
- Explain how deep sleep differs from idle and why wake looks like a reset on ESP32.
- Identify board-level current drains and isolate MCU sleep current.
- Capture and annotate current waveforms that show active, idle, and sleep phases.
- Compute average current and battery life from duty cycle measurements.
- Build a reusable measurement protocol for future projects.
2. All Theory Needed (Per-Concept Breakdown)
2.1 Concept 1: MCU Sleep States and Wakeup Flows
Fundamentals
Microcontrollers offer multiple power states that trade functionality for current draw. At one end is active mode, where the CPU and peripherals run at full speed. At the other is deep sleep, where most clocks are off and RAM may be powered down. ESP32 deep sleep wakes via a reset-like path, while nRF52840 uses System ON or System OFF with different retention behavior. Wake sources are typically timers, GPIO edges, or RTC events. A correct low-power design chooses the minimal state that still preserves needed context and defines the exact wake path so that the firmware can restore state deterministically. If you misunderstand the wake flow, you end up with devices that never sleep, never wake, or consume far more power than expected.
Deep Dive into the Concept
Sleep states exist because clocks and switching activity are the dominant sources of dynamic power. When you disable clocks, you reduce switching; when you power down blocks, you reduce leakage. ESP32 deep sleep disables the main cores and most peripherals, leaving the RTC domain active to handle wake. The wake process is similar to a cold boot; you lose most RAM, and the bootloader runs again. That means you must store state in RTC memory or non-volatile storage if you need it across sleeps. In contrast, nRF52840 System ON sleep stops the CPU while keeping RAM and some peripherals alive, which makes wake faster but consumes more current. System OFF powers down more, but wake is closer to a reset. The distinction matters because a design that relies on RAM state will break if you choose a sleep mode that does not retain RAM.
Wake sources also differ. ESP32 can wake from timers, ext0/ext1 GPIO, ULP co-processor events, and touch. Each wake source has its own configuration constraints. For example, ext0 typically uses a single RTC-capable pin, while ext1 can use multiple pins but only certain logic levels. The nRF52840 uses GPIO Sense for low-power wake, but only specific pins can trigger from System OFF. The board layout and pin mux matter: if your wake pin is not in the RTC domain, deep sleep wake will fail. Additionally, the wakeup cause should always be read at boot so you can branch logic accordingly. Without that, you can spend power re-initializing sensors unnecessarily or miss a critical event.
Another critical aspect is peripheral and pin state before sleep. A pin left floating can leak current through ESD diodes or pull-up networks. On ESP32, you should configure unused pins as inputs with pull-downs or disable them explicitly. On nRF52840, you can set the GPIO to disconnect input buffer and set a defined pull. If you forget, the board-level LED or a sensor can create a constant drain even in deep sleep. This is why sleep state design is not just firmware: it is a system-level configuration that spans the MCU, board, and attached sensors.
Finally, deep sleep is not a silver bullet. If you wake too frequently, the wake overhead dominates energy. The cost of booting, initializing Wi-Fi, and sampling sensors can be significant. That cost must be amortized by long sleep intervals. The best designs use a duty cycle model: measure how long each phase takes, measure current in each phase, and compute average current. Only then can you predict battery life. This project forces you to build that model with real measurements rather than assumptions.
How this fits on projects
You will configure deep sleep and wake sources, measure the wake path, and store minimal state across sleep. The same knowledge is needed for the Zigbee sensor project and the Matter light project.
Definitions & Key Terms
- Deep sleep: Lowest power state, most clocks off, limited wake sources.
- System ON/OFF: Nordic terminology for sleep with and without RAM retention.
- RTC domain: Always-on low-power domain that handles wake.
- Wake source: The event that wakes the MCU (timer, GPIO, etc.).
- Retention: Keeping RAM or registers powered during sleep.
Mental Model Diagram (ASCII)
Active -> Idle -> Deep Sleep
| | |
| | +--> Wake source fires
| +--------------> Resume
+-----------------------> Full boot
How It Works (Step-by-Step)
- Configure wake source (timer or GPIO) before sleeping.
- Configure all unused pins to avoid leakage.
- Enter deep sleep instruction or API call.
- RTC domain waits for wake event.
- Wake event triggers reset or resume path.
- Boot code reads wake reason and restores minimal state.
Minimal Concrete Example
// ESP-IDF deep sleep example
esp_sleep_enable_timer_wakeup(5 * 1000 * 1000ULL);
printf("Entering deep sleep\n");
esp_deep_sleep_start();
Common Misconceptions
- “Deep sleep always resumes where it left off.” Often it resets the CPU.
- “Sleep current equals datasheet numbers.” Board-level LEDs and sensors add drain.
- “Any GPIO can wake from sleep.” Only RTC-capable pins can on ESP32.
Check-Your-Understanding Questions
- Why does ESP32 deep sleep look like a reset?
- What is the difference between nRF System ON and System OFF?
- Why is wake pin selection tied to the RTC domain?
Check-Your-Understanding Answers
- Because the main CPU power domain is turned off and the boot ROM runs again.
- System ON keeps RAM and some peripherals powered; System OFF powers down more.
- Only the RTC domain is powered during deep sleep, so only its pins can wake.
Real-World Applications
- Battery sensor nodes that wake once per hour.
- Wearables that wake on button press.
Where You’ll Apply It
- See Section 3.2 Functional Requirements and Section 5.10 Phase 2 in this project.
- Also used in: P07 Zigbee Environment Sensor and P04 Matter Smart Light.
References
- ESP32-C3 Technical Reference Manual (sleep chapter)
- Nordic nRF52840 Product Specification (power management)
- “Making Embedded Systems” (low-power design)
Key Insights
Deep sleep is a system design choice, not just a function call.
Summary
Understanding sleep states and wake flows is the foundation of low-power design. You must choose the right sleep mode, configure the right wake pins, and plan for a reset-like wake path.
Homework/Exercises to Practice the Concept
- List all wake sources supported by your MCU and mark which are RTC-capable.
- Draw a flow chart of your firmware boot path for a timer wake vs a cold boot.
Solutions to the Homework/Exercises
- Use the TRM; mark RTC pins and timer-based wake options.
- Timer wake path should branch early to skip full re-init when possible.
2.2 Concept 2: Power Profiling, Leakage, and Energy Budgeting
Fundamentals
Power profiling is the process of measuring how much current your system draws over time and mapping that to firmware behavior. A single average number hides critical details; you need a waveform to see spikes, sleep plateaus, and bursts. Leakage current comes from board components like LEDs, regulators, and sensors. A low-power MCU can still consume tens of microamps if a power LED is always on. Energy budgeting turns current and time into battery life estimates. You compute average current by weighting each current level by its duration (duty cycle). This converts measurements into practical decisions such as how often to sample a sensor or transmit a packet.
Deep Dive into the Concept
A power profiler (such as Nordic PPK2) measures current across a shunt resistor and reports current vs time. The resulting waveform is a story: a boot spike, a radio spike, a sensor read plateau, and a deep sleep floor. If you only measure with a multimeter, you get a blurred average and lose the ability to attribute energy to specific firmware phases. Profiling requires synchronization between firmware and measurement: you need to log timestamps, toggle a debug GPIO, or emit a serial marker so you can align the waveform with code events.
Leakage is the enemy of microamp sleep. On a typical dev board, the power LED might consume 500-2000 uA, dominating the MCU’s 5-20 uA deep sleep. Regulators also have quiescent current, sometimes 10-50 uA. Sensors may remain powered and draw standby current. The correct procedure is to measure sleep current with the MCU alone, then with each peripheral attached. You can physically cut an LED trace or disable it via GPIO if the board supports it. You can also reconfigure pins to avoid back-powering sensors. For example, if you leave an I2C pin high while the sensor is unpowered, current can flow through the sensor’s protection diodes.
Energy budgeting uses a simple equation: average current = sum(current_i * time_i) / total_time. Suppose your device draws 60 mA for 200 ms to wake, read, and send, then sleeps at 40 uA for 9.8 s. The average is (60mA * 0.2s + 0.04mA * 9.8s) / 10s = 1.24 mA. A 1000 mAh battery would last ~806 hours or about 33 days. If you reduce the active time or increase the sleep interval, the average drops quickly. This is why optimization often focuses on reducing wake frequency rather than shaving a few mA during active time.
Measurement accuracy depends on bandwidth and range. A profiler often has multiple ranges: high current for spikes and low current for sleep. You must ensure the tool does not saturate or lose resolution. If you only log every 100 ms, you will miss short radio bursts and underestimate peak currents. Good profiling practice includes: calibrating the shunt, using a fixed sampling rate, taking multiple runs, and documenting environmental conditions. You also must watch for ground loops and ensure the measurement setup does not change the system behavior (for example, a long USB cable can affect power).
Finally, power profiling is most powerful when paired with firmware instrumentation. Use a GPIO pin to create “markers” on the waveform: set it high during active work, low during sleep. This gives you a clean alignment between code phases and current. For reproducibility, fix your firmware to a deterministic schedule: fixed wake interval, fixed sensor read, fixed transmission. This gives you a stable waveform that can be compared across revisions.
How this fits on projects
You will build a firmware loop that alternates between active work and deep sleep, then measure the current waveform and compute average current and battery life. The same workflow is reused in the Zigbee sensor project and any battery device.
Definitions & Key Terms
- Duty cycle: Fraction of time spent in active vs sleep.
- Quiescent current: Current consumed by regulators or circuits when idle.
- Shunt resistor: Small resistor used to measure current by voltage drop.
- Current waveform: Current vs time plot.
- Energy budget: Estimate of battery life based on average current.
Mental Model Diagram (ASCII)
Current (mA)
^
| spike plateau sleep floor
| /\ ____ ____
| / \ / \ / \
+-------------------------------> time
How It Works (Step-by-Step)
- Build firmware that toggles a GPIO marker around active work.
- Connect the power profiler between USB and the board.
- Capture waveform while the device runs for multiple cycles.
- Annotate waveform segments with log markers.
- Compute average current and battery life.
Minimal Concrete Example
// Toggle a marker pin to align with the current waveform
gpio_set_level(MARKER_GPIO, 1);
read_sensor();
transmit_data();
gpio_set_level(MARKER_GPIO, 0);
enter_deep_sleep(10);
Common Misconceptions
- “Average current from a multimeter is enough.” It hides spikes and duty cycle.
- “Datasheet sleep current equals board sleep current.” Board components add leakage.
- “Shorter active time always matters more than sleep.” Sleep time often dominates.
Check-Your-Understanding Questions
- Why do you need a waveform instead of a single average current?
- How can a power LED dominate deep sleep current?
- How do you compute average current with duty cycle?
Check-Your-Understanding Answers
- Because spikes and sleep phases are invisible in a single average.
- A LED can draw hundreds of microamps, larger than MCU sleep current.
- Average current is the weighted sum of current and time across phases.
Real-World Applications
- Long-life environmental sensors.
- Asset trackers that transmit once per day.
Where You’ll Apply It
- See Section 3.7 Real World Outcome and Section 6 Testing Strategy.
- Also used in: P07 Zigbee Environment Sensor and P10 Web Oscilloscope for power-aware streaming.
References
- Nordic PPK2 documentation
- “Making Embedded Systems” (power profiling)
- “Practical Electronics for Inventors” (measurement basics)
Key Insights
If you cannot draw the current waveform, you do not truly know your power budget.
Summary
Power profiling ties firmware events to actual energy use. Measure, annotate, and compute averages so you can make informed design tradeoffs.
Homework/Exercises to Practice the Concept
- Calculate average current for a device that sleeps 99 percent of the time at 5 uA and is active 1 percent at 20 mA.
- Identify three board components that could add leakage current.
Solutions to the Homework/Exercises
- Average = 0.990.005mA + 0.0120mA = 0.20095 mA.
- Power LED, regulator quiescent current, and always-on sensors.
3. Project Specification
3.1 What You Will Build
A dual-board power profiling experiment that runs a deterministic active/sleep loop on XIAO ESP32-C3 and XIAO nRF52840, captures the current waveform, and produces a report comparing sleep current, active current, and estimated battery life.
3.2 Functional Requirements
- Sleep Loop: Firmware cycles through active work and deep sleep with a fixed period.
- Wake Logging: On wake, log the wake cause and cycle count.
- Marker GPIO: Toggle a GPIO to align waveform with firmware phases.
- Measurement: Capture current waveform for at least 5 cycles.
- Comparison Report: Compute average current and battery life for both boards.
3.3 Non-Functional Requirements
- Repeatability: Same firmware and measurement procedure produce similar results.
- Accuracy: Measure sleep current to within 10 percent using a profiler.
- Reliability: Run for 30 minutes without crash.
3.4 Example Usage / Output
[Boot] Wakeup cause: TIMER
[Cycle] 7: active
[Sleep] 10s
3.5 Data Formats / Schemas / Protocols
- Measurement CSV:
timestamp_us,current_uasampled at a fixed rate. - Report Summary: Markdown table with active, sleep, average, and battery life.
3.6 Edge Cases
- Board never enters deep sleep due to a running peripheral.
- Power LED dominates measured sleep current.
- Wake pin misconfigured, device never wakes.
3.7 Real World Outcome
A report with waveforms and calculated battery life for both boards.
3.7.1 How to Run (Copy/Paste)
# ESP32-C3
idf.py set-target esp32c3
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor
# nRF52840 (example)
west build -b xiao_nrf52840 .
west flash
3.7.2 Golden Path Demo (Deterministic)
- Fixed active time: 200 ms
- Fixed sleep time: 10 s
- Fixed marker GPIO pulse in each active phase
Expected serial log:
[Cycle] 0 ACTIVE
[Cycle] 0 SLEEP 10s
[Cycle] 1 ACTIVE
3.7.3 Failure Demo (Bad Wake Source)
If the wake source is misconfigured, the device never wakes:
[Boot] Entering deep sleep with GPIO wake on pin 3
# No further output, device stays asleep
3.7.4 If CLI
No standalone CLI tool. Exit codes not applicable.
3.7.5 If Web App
Not applicable.
3.7.6 If API
No API is exposed. Error JSON shape not applicable.
3.7.7 If GUI / Desktop / Mobile
Not applicable.
3.7.8 If TUI
Not applicable.
4. Solution Architecture
4.1 High-Level Design
[Wake] -> [Active work + marker] -> [Log] -> [Sleep] -> [Wake]
4.2 Key Components
| Component | Responsibility | Key Decisions | |———-|—————-|—————| | Sleep controller | Configure wake sources and enter sleep | Use timer wake for deterministic loops | | Marker GPIO | Align waveform to code phases | Dedicated pin to avoid conflicts | | Logger | Print cycle count and wake reason | Minimal logs to reduce power | | Measurement pipeline | Capture waveform and export CSV | Fixed sampling rate |
4.3 Data Structures (No Full Code)
struct cycle_stats {
uint32_t cycle_id;
uint32_t active_ms;
uint32_t sleep_ms;
};
4.4 Algorithm Overview
Key Algorithm: Power Loop
- Wake and log cycle ID.
- Toggle marker GPIO high.
- Perform dummy workload for fixed duration.
- Toggle marker GPIO low.
- Enter deep sleep for fixed duration.
Complexity Analysis:
- Time: O(1) per cycle
- Space: O(1)
5. Implementation Guide
5.1 Development Environment Setup
# ESP-IDF (ESP32-C3)
idf.py set-target esp32c3
# nRF Connect SDK
west init -l .
west update
5.2 Project Structure
p02_power_profiler/
+-- esp32c3/
| +-- main.c
+-- nrf52840/
| +-- main.c
+-- report/
+-- waveform.png
+-- summary.md
5.3 The Core Question You’re Answering
“Where does the energy go when my device is doing nothing?”
5.4 Concepts You Must Understand First
- Sleep modes and wake sources
- Pin leakage and pull configurations
- Duty cycle math and average current
5.5 Questions to Guide Your Design
- Which pins remain powered in deep sleep on your board?
- How will you label waveform segments in a reproducible way?
- What time interval provides a realistic duty cycle?
5.6 Thinking Exercise
Compute the average current for 60 mA active for 200 ms and 40 uA sleep for 9.8 s.
5.7 The Interview Questions They’ll Ask
- Why does ESP32 deep sleep look like a reset?
- How do pull-ups affect sleep current?
- How do you compute battery life from duty cycle?
5.8 Hints in Layers
Hint 1: Disable the power LED or account for its current draw.
Hint 2: Use a marker GPIO to align waveform with firmware phases.
Hint 3: Start with timer wake before GPIO wake.
Hint 4: Measure both boards with the same sample rate.
5.9 Books That Will Help
| Topic | Book | Chapter | |——|——|———| | Low-power design | Making Embedded Systems | Power chapters | | Measurement | Practical Electronics for Inventors | Measurement chapters |
5.10 Implementation Phases
Phase 1: Deterministic Sleep Loop (1 day)
Goals:
- Build a consistent active/sleep cycle.
- Log wake reason and cycle count.
Tasks:
- Implement timer-based wake.
- Print cycle count on boot.
Checkpoint: Device wakes every cycle and logs correctly.
Phase 2: Measurement Setup (1 day)
Goals:
- Connect profiler and capture waveform.
- Align waveform with marker GPIO.
Tasks:
- Configure marker GPIO.
- Capture waveform for 5 cycles.
Checkpoint: Clear active spikes and sleep plateau visible.
Phase 3: Comparison and Report (1-2 days)
Goals:
- Compute average current and battery life.
- Document board-level drains.
Tasks:
- Export CSV and compute averages.
- Write report with conclusions.
Checkpoint: Report includes numbers for both boards.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale | |———-|———|—————-|———–| | Wake source | Timer vs GPIO | Timer | Most deterministic for profiling | | Marker method | GPIO vs serial timestamp | GPIO | Clear alignment on waveform | | Measurement tool | PPK2 vs multimeter | PPK2 | Higher resolution and waveform |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples | |———-|———|———-| | Unit Tests | Validate duty cycle math | Host-side calculations | | Integration Tests | Sleep/wake cycle | On-device logs | | Edge Case Tests | Bad wake pin, LED leakage | Sleep current checks |
6.2 Critical Test Cases
- Timer Wake: Device wakes every 10 seconds.
- Marker GPIO: Pulse shows in waveform each cycle.
- Sleep Current: Measured sleep is within expected order of magnitude.
6.3 Test Data
active_ms=200
sleep_ms=10000
active_current_ma=60
sleep_current_ua=40
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution | |——–|———|———-| | Power LED always on | Sleep current too high | Disable LED or subtract its current | | Wrong wake pin | Device never wakes | Use timer wake first | | Floating pins | Random wake or high current | Set pull-downs |
7.2 Debugging Strategies
- Use GPIO marker and serial logs to align waveform to firmware phases.
- Measure sleep current with nothing connected to eliminate sensor leakage.
7.3 Performance Traps
- Waking too often can dominate battery life even if sleep current is low.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add a second sleep interval and compare averages.
- Log wake reason and count only to RTC memory.
8.2 Intermediate Extensions
- Add GPIO wake and compare to timer wake current.
- Measure current with a different regulator or power source.
8.3 Advanced Extensions
- Implement an adaptive duty cycle based on battery voltage.
- Compare ESP32-C6 low-power core vs main core consumption.
9. Real-World Connections
9.1 Industry Applications
- Remote sensors in agriculture and smart buildings.
- Wearables that spend most of their time asleep.
9.2 Related Open Source Projects
- ESP-IDF low power examples - reference for sleep modes.
- Zephyr power management samples - cross-platform low power patterns.
9.3 Interview Relevance
- Low-power design and duty cycle math are common in embedded interviews.
10. Resources
10.1 Essential Reading
- ESP32-C3 TRM (sleep and RTC chapters)
- Nordic nRF52840 Product Specification (power chapters)
10.2 Video Resources
- “Low Power Design for Embedded Systems” (conference talk)
- “Battery Life Estimation” (lecture)
10.3 Tools & Documentation
- Nordic PPK2 User Guide
- ESP-IDF power management docs
10.4 Related Projects in This Series
- P07 Zigbee Environment Sensor - sleepy end device design
- P04 Matter Smart Light - power-aware smart device
11. Self-Assessment Checklist
11.1 Understanding
- I can explain the wake path for ESP32 deep sleep.
- I can identify sources of leakage on a dev board.
- I can compute average current from a waveform.
11.2 Implementation
- Both boards sleep and wake deterministically.
- Current waveform is captured and annotated.
- Battery life estimate is documented.
11.3 Growth
- I documented at least one measurement surprise.
- I can explain how to reduce average current further.
12. Submission / Completion Criteria
Minimum Viable Completion:
- Sleep/wake loop runs on at least one board.
- Current waveform captured for 5 cycles.
- Average current computed.
Full Completion:
- Comparison between ESP32-C3 and nRF52840 with measured data.
- Board-level drains identified and documented.
Excellence (Going Above & Beyond):
- Repeat measurements after removing LED or changing regulator.
- Publish a short report with annotated waveform screenshots.