This project originally started as a wearable fall detection system using the BMI160 IMU sensor. My initial goal was to develop a compact embedded device capable of detecting sudden movement events and potentially dangerous falls.
However, during development I realized that the BMI160 does not include a dedicated AI or advanced motion classification engine like some newer IMUs. While it still provides good motion sensing and step tracking capabilities, implementing reliable fall detection entirely in firmware would require a much more advanced sensor fusion system than I initially planned.
Because of that, I decided to shift the project into something more practical and useful for daily development: a fully functional ESP32-C3 smartwatch platform.
Instead of abandoning the hardware, I focused on turning it into a stable wearable device that I could actually use every day while continuing to improve the embedded system architecture for future smartwatch generations.
The current version already supports:
- BLE smartphone connectivity
- Heart rate and SpO2 monitoring using MAX30100
- Step tracking using BMI160
- Deep sleep power management
- Raise-to-wake support
- Smooth UI rendering
- Wallpaper transfer over BLE
- Real-time clock synchronization from smartphone
- Multi-day battery life
The watch is powered by a 600mAh battery and currently lasts around 2-4 days depending on usage, averaging roughly 3 days in my personal daily use without aggressive raise-to-wake behavior enabled.
Even though this version is still considered a prototype, it has already become comfortable enough for everyday use.
One of the biggest challenges during development was balancing responsiveness, display smoothness, and battery life on a small embedded platform.
To improve efficiency, the firmware uses:
- Dynamic CPU frequency scaling
- Sensor power shutdown when inactive
- Interrupt-driven wake behavior
- Partial display rendering
- Optimized SPI display updates
- Burst sensor polling techniques
- Deep sleep state transitions
The display system was heavily optimized to reduce flickering and ghosting effects during screen wake and UI transitions. Compared to many DIY smartwatch projects that constantly redraw the entire screen, this firmware uses selective rendering techniques to keep the interface smooth while minimizing unnecessary updates.
For motion sensing and step counting, I spent a significant amount of time tuning the BMI160 configuration registers manually to achieve more stable daily step tracking behavior.
The BMI160 is configured using low-level register access instead of relying only on high-level libraries. This allowed me to fine-tune pedometer behavior, interrupt generation, and low-power motion tracking more directly.
The watch currently uses:
- BMI160 step counter interrupt system
- Interrupt-driven wake events
- Shared GPIO wake architecture
- Low-power accelerometer operation
In this hardware revision, the BMI160 interrupt line is OR-connected together with the physical button wake line on GPIO5 using a small jumper patch. The original PCB revision did not yet include a dedicated IMU wake trace, so this modification allowed both button presses and motion interrupts to wake the system correctly without redesigning the entire board.
The MAX30100 sensor data can already be logged and monitored from a smartphone through BLE communication. While the measurements are not intended to compete with medical-grade wearables, the results are already accurate enough for my current development goals and daily usage testing.
The raise-to-wake feature is already functional, although the motion sensitivity is still slightly too aggressive. I am still experimenting with better filtering and threshold tuning to achieve a more natural wrist detection experience.
This smartwatch is not intended to be a finished commercial product yet.
Instead, it serves as a foundational embedded platform for my next-generation smartwatch development, which will include:
- Improved sensor fusion
- Better power profiling
- Touchscreen support
- More advanced UI systems
- Enhanced BLE integration
- Better motion intelligence
- Cleaner PCB revisions
For me, this project became much more than just building a smartwatch.
It became an exploration of how far a DIY wearable platform can go when treated like a real embedded engineering system instead of simply connecting modules together.
Components UsedThe complete Bill of Materials (BOM) is shown in the image above.
Below is a numbered breakdown of the components used in this build along with their approximate prices.
1. Main Components- ESP32-C3 Super Mini (U1)
Main microcontroller with built-in WiFi and BLE connectivity. This chip runs the entire smartwatch firmware and handles sensor communication, UI rendering, and power management.
Price: Rp 30, 000 (~$1.90 USD)
- MAX30100 Sensor Module
Optical heart rate and SpO₂ sensor communicating through I²C. It measures blood volume changes using infrared and red light.
Price: Rp 20, 000 (~$1.30 USD)
- BMI160 (GY-BMI160 Module)
Accelerometer and gyroscope used for motion detection and step counting. The internal DSP allows step counting without heavy CPU processing.
Price: Rp 46, 000 (~$3.00 USD)
- 1.69" TFT Display (ST7789 Driver)
Main display used for rendering the watch interface, graphs, and sensor data. The display communicates via SPI.
Price: Rp 45, 000 (~$2.90 USD)
- 400 mAh LiPo Battery
Primary power source for the smartwatch.
Price: Rp 8, 000 (~$0.50 USD)
2. Power Management- LTH7 LiPo Charging IC (U3, C5342381)
Handles safe charging of the LiPo battery from the USB input.
Price: Rp 2, 000 (~$0.13 USD)
- ST1340BB Buck Converter (U4)
Converts the input voltage to a stable 3.3V supply for the ESP32 and sensors.
Price: Rp 2, 000 (~$0.13 USD)
- IRLML6401 P-Channel MOSFET (Q2)
Used for battery power path switching.
Price: Rp 500 (~$0.03 USD)
- IRLML2502 MOSFET (Q1)
Used to control the display backlight.
Price: Rp 500 (~$0.03 USD)
- 1N5819 Schottky Diodes (D1, D2)
Provide power path protection.
Price: Rp 500 (~$0.03 USD)
3. Passive Components- 3.3 µH Inductor (L1)
Required by the buck converter to regulate voltage efficiently.
Price: Rp 500 (~$0.03 USD)
- 0603 Resistors (R1–R16)
Used for I²C pull-ups, battery voltage divider, LED resistors, and supporting circuits.
Price: Rp 1, 000 (~$0.06 USD total)
- 0603 Capacitors (C1–C10)
Used for power stabilization and noise filtering.
Price: Rp 1, 500 (~$0.10 USD total)
4. User Interface- Push Button (SW1)
Used for user interaction and menu navigation.
Price: Rp 500 (~$0.03 USD)
5. Other Components- Custom PCB
Manufactured through PCB fabrication services such as JLCPCB or PCBWay.
Typical price per board (after splitting a batch order):
Rp 30, 000 (~$1.90 USD)
6. Estimated Total CostTotal estimated hardware cost:
≈ Rp 188, 000
≈ $12 USD
Component NotesThe MAX30100 sensor can be replaced with MAX30102, which is often easier to find. The pinout is very similar, although some register configuration adjustments may be required in the firmware.
The BMI160 module (GY-BMI160) already includes pull-up resistors and decoupling capacitors on the module itself, reducing the number of external components required.
PCB Cost NotePCB manufacturers such as JLCPCB or PCBWay typically require a minimum order of five boards.
Even if only one board is needed, you will receive multiple boards from the factory. When dividing the total cost across the batch, the effective cost per PCB is usually under Rp 30, 000 (~$1.90 USD).
You can keep the extra pcb for future revisions or share them with others bulding the same project.
Step 1: PCB Design OverviewStep — PCB Files and Important Notes
To make it easier for others to reproduce this project, the PCB design files are provided. The files include the original EasyEDA project, so the design can be opened and edited directly without needing to recreate the schematic or PCB layout from scratch.
Simply import the EasyEDA project file into EasyEDA and the full schematic and PCB layout will load automatically. From there, the design can be modified, exported as Gerber files, or directly sent to a PCB manufacturer.
However, there are a few important notes regarding the current PCB revision.
Known Issue — Display Backlight pin OrientationIn this revision of the PCB, the anode and cathode orientation of the backlight are accidentally swapped in the layout.
Because of this, the diode will not function correctly if soldered according to the silkscreen orientation.
To fix this issue during assembly, the diode needs to be installed with a small jumper wire that corrects the connection between the pads. This modification is simple and does not affect the rest of the circuit.
A photo of the jumper fix will be shown in the assembly section so builders can easily replicate the correction.
EasyEDA Project FileThe EasyEDA project file is included so that builders can:
- Open the schematic and PCB layout directly
- Modify the design if needed
- Export Gerber files for manufacturing
- Order the PCB directly from EasyEDA's integrated fabrication services
This makes it much easier to customize the board or create improved revisions.
Deep Sleep Feature NoteIf you plan to use the ESP32 deep sleep feature, an additional hardware modification is recommended.
The current PCB revision does not include a dedicated wake button for deep sleep mode. To wake the ESP32 from deep sleep using external input, a push button connected to GPIO 5 should be added.
This button should connect GPIO 5 to ground when pressed, allowing the ESP32 to wake from deep sleep via an external interrupt.
Adding this button enables significantly lower power consumption during standby, which is especially useful for battery-powered wearable devices like this smartwatch.
Before assembling the smartwatch hardware, the circuit first needs to be converted into a PCB design that can be manufactured.
After completing the schematic diagram (as shown in the previous image), the next step is to translate that schematic into a PCB layout. The PCB layout defines the physical placement of components and the copper traces that connect them electrically. This layout will eventually be exported as manufacturing files used by PCB fabrication services.
In this project, the PCB layout was designed using EasyEDA, a web-based electronic design automation tool that allows schematic capture, PCB layout, and direct integration with manufacturing services.
Although EasyEDA was used in this project, many other professional and hobbyist tools can also be used to design PCBs. Some popular options include:
- KiCad (open-source and widely used)
- Altium Designer (professional industry tool)
- Autodesk Eagle
- OrCAD / Cadence
- Proteus
- DipTrace
- CircuitMaker
- DesignSpark PCB
All of these tools follow a similar workflow: create the schematic, assign footprints to components, and then move into PCB layout mode.
Converting the Schematic to a PCB LayoutOnce the schematic is complete, each component in the design must be assigned a footprint. A footprint defines the physical copper pads and dimensions that correspond to a specific component package (for example 0603 resistors, SOT-23 MOSFETs, or module headers).
After footprints are assigned, the design can be transferred into the PCB layout editor. The software will place all components on the board area and generate connection lines (nets) indicating which pins need to be connected.
The next step is component placement. This is one of the most important stages in PCB design. Components should be arranged logically based on their function in the circuit.
For example:
- The ESP32-C3 module is placed near the center of the board as the main controller.
- The BMI160 motion sensor and MAX30100 sensor are positioned where they can be easily exposed to the user or environment.
- The power management section (charger IC, buck converter, inductor, and capacitors) is grouped together to keep power routing short and efficient.
- The display connector is placed near the edge of the board for easier cable routing.
Proper component placement helps reduce trace length, improves signal integrity, and simplifies routing.
Routing the PCB TracesAfter components are positioned, the next step is routing, which means drawing copper traces to connect all the required signals.
During routing, several engineering considerations should be followed:
Power traces should be wider than signal traces to handle higher current safely.
High-speed signals such as SPI communication lines should be kept as short as possible.
Sensitive communication lines such as I²C (SDA and SCL) should be routed carefully to avoid interference.
Decoupling capacitors should always be placed very close to the power pins of ICs. These capacitors help stabilize voltage and filter high-frequency noise.
Good routing practice improves reliability and prevents unexpected behavior in the final hardware.
Importance of the Ground PlaneOne extremely important part of PCB design is the ground plane.
A ground plane is a large copper area connected to the circuit ground (GND). Instead of routing ground using thin traces, the PCB design uses a large copper fill that spreads across the board.
Using a ground plane provides several advantages:
First, it significantly reduces electrical noise. High-speed signals such as SPI and I²C generate switching currents, and a solid ground plane provides a stable reference that prevents signal distortion.
Second, a ground plane helps improve signal integrity by reducing impedance and minimizing electromagnetic interference.
Third, it helps with thermal dissipation, allowing heat to spread more evenly across the board.
For this reason, many PCB designs use one full layer dedicated primarily to ground, especially in two-layer boards where the bottom layer often becomes the ground plane.
In this smartwatch PCB, a ground plane was added to ensure stable communication between the ESP32-C3 and the sensors.
Final PCB Checks Before ManufacturingBefore sending the PCB for fabrication, the design should be verified using built-in tools such as Design Rule Check (DRC).
DRC ensures that:
- Trace widths meet manufacturing limits
- Spacing between copper features is sufficient
- Pads and vias follow the fabrication constraints
This step helps prevent manufacturing errors and ensures the board can be fabricated correctly.
Exporting Manufacturing FilesOnce the PCB design is finalized, the layout software generates Gerber files.
Gerber files are the standard format used by PCB manufacturers. These files contain all the necessary information for fabrication, including:
- Copper layers
- Solder mask layers
- Silkscreen markings
- Drill holes
- Board outline
The Gerber package is typically exported as a ZIP file, which will later be uploaded to a PCB manufacturing service.
Ordering the PCBAfter generating the Gerber files, the PCB can be ordered from online manufacturing services.
Popular PCB manufacturers include:
- JLCPCB
- PCBWay
- Seeed Fusion
- OSH Park
- Aisler
The ordering process is usually straightforward.
First, visit the website of the PCB manufacturer.
Upload the Gerber ZIP file generated by the PCB design software.
The website will automatically analyze the files and display a preview of the PCB.
Next, choose the board specifications. Common settings include:
- Board thickness (typically 1.6 mm)
- Number of layers (usually 2 layers for small projects)
- Solder mask color
- Surface finish (HASL or ENIG)
After confirming the specifications, select the quantity of boards and place the order.
Most PCB services manufacture boards within a few days, and the finished PCBs are shipped directly to your address.
Step 2: PCB AssemblyBefore turning on the soldering iron, set up your workspace so the assembly process is efficient and safe.
Place the PCB in a PCB holder or helping hands tool. Avoid holding the board directly with your fingers, since movement while soldering can easily cause poor joints.
Arrange all SMD components in a tray or on an anti-static mat so they are easy to access and don’t get lost. Small components like 0603 resistors and capacitors disappear surprisingly fast if your workspace is messy.
Make sure the following tools are within reach:
- Solder wire (0.3–0.6 mm)
- Flux pen
- Anti-static tweezers
- Magnifier or loupe for inspecting solder joints
- Multimeter set to continuity mode for quick electrical checks after soldering
Keep drinks away from the workbench. Flux and molten solder can splash if contaminated with liquid, which can damage the board and create a safety hazard.
2.2 — Set the Soldering Iron TemperatureUsing the correct soldering temperature is important. Too low and the solder joint will be weak (a cold joint). Too high and components or PCB pads can be damaged.
Choose the temperature based on the type of solder you are using.
For Sn63/Pb37 solder (leaded solder), set the iron to 300–320 °C.
This solder melts at 183 °C, so this temperature range provides good flow without overheating components.
For lead-free solder (SnAgCu / RoHS), set the iron to 340–360 °C.
Lead-free solder melts at about 217 °C, which requires a slightly higher working temperature.
If you are using a Pinecil or TS101 soldering iron, enabling the boost mode can help the iron reach working temperature quickly when starting a solder joint.
When working with small SMD components, try to keep the tip temperature below about 300 °C whenever possible to reduce the risk of overheating the parts.
2.3 — Recommended Component Assembly OrderWhen assembling a PCB, it is best to solder smaller and lower-profile components first, then move to larger components.
This prevents previously installed parts from blocking access to smaller pads.
Recommended order for this project:
- 0603 resistors and capacitors
- These are the smallest parts on the board and should be installed first.
- Schottky diodes (1N5819)
- Make sure the cathode orientation matches the PCB marking.
- 3.3 µH inductor (L1)
- MOSFETs (Q1 and Q2)
- Carefully check the orientation against the datasheet or PCB silkscreen.
- ICsLTH7 Li-Po charging IC
- ST1340BB buck converter
- ESP32-C3 Super Mini module and header connections
- Sensor modules lastBMI160
- MAX30100
These are usually connected via headers or soldered directly once the rest of the board is assembled.
2.4 — Soldering Technique for 0603 ComponentsFor small SMD parts such as 0603 resistors and capacitors, a reliable method is the “tin one pad first” technique.
First, apply a small amount of flux to the pads. Flux improves solder flow and helps prevent cold joints.
Next, pre-tin one pad by applying a small amount of solder.
Using tweezers, place the component onto the pads. Heat the pre-tinned pad while holding the component in place so the solder melts and anchors the part.
After the component is fixed on one side, solder the second pad normally by touching the iron to the pad and feeding a small amount of solder.
A typical solder joint should take about 1–2 seconds. If heating takes much longer, the PCB pad may start to lift or delaminate.
A good solder joint should appear smooth, slightly rounded, and shiny.
A dull or flat joint usually indicates a cold joint.
If solder accidentally bridges two pads, apply a little more flux and gently drag a clean soldering iron tip across the pads. In many cases the excess solder will wick back to the tip and remove the bridge.
2.5 — Inspection and Electrical VerificationDo not immediately connect power after finishing the soldering process. Performing a few quick checks can prevent damage to the board.
Start with a visual inspection using a loupe or magnifier. Look for solder bridges, especially around IC pins and MOSFET pads.
Next, perform a few measurements with a multimeter:
Check continuity between GND and the 3.3 V rail.
The reading should be open (no short).
Check VIN to GND.
This should also be open before the protection diode.
Verify the I²C pull-up resistors.
Measure from SDA and SCL to 3.3 V. The resistance should be approximately 4.7 kΩ.
If there is leftover flux residue, clean the board using 70 % or higher isopropyl alcohol and a soft brush. Flux residue can become corrosive over time if left on the PCB.
Once everything looks correct, connect the board to USB using a current-limited source, such as a power bank or USB tester.
A typical idle current for the board (with sensors inactive) should be around 50–80 mA.
If the current immediately rises above 200 mA, disconnect power immediately. This usually indicates a short circuit or incorrectly soldered component.
Step 3: Using the BMI160 Built-In Step CounterThe smartwatch uses the BMI160 inertial measurement unit (IMU) for motion sensing and step tracking.
Originally, this project was intended to become a wearable fall detection system. However, during development I realized that while the BMI160 includes a built-in motion processing engine and hardware pedometer support, it does not include a dedicated AI motion classification processor for reliable fall analysis.
Because of this limitation, the project direction shifted toward building a fully functional low-power smartwatch platform instead.
Even after the direction change, the BMI160 remained a core part of the system because of its efficient built-in pedometer engine and interrupt capabilities.
Instead of processing raw accelerometer data continuously on the ESP32-C3, the smartwatch uses the BMI160 internal hardware step counter.
This approach significantly reduces CPU workload and power consumption because the sensor performs motion analysis internally while the ESP32-C3 can remain idle or in low-power mode.
This is especially important for the ESP32-C3 because it is a single-core microcontroller that also handles:
BLE communication
- BLE communication
UI rendering
- UI rendering
Display updates
- Display updates
Deep sleep transitions
- Deep sleep transitions
Sensor polling
- Sensor polling
Battery management
- Battery management
By offloading step detection into the BMI160 hardware, the system remains responsive while still achieving multi-day battery life.
BMI160 Register ConfigurationThe pedometer system is configured directly through low-level BMI160 registers instead of relying entirely on high-level abstraction libraries.
This allowed more precise tuning of:
step sensitivity
- step sensitivity
wake behavior
- wake behavior
power stability
- power stability
interrupt handling
- interrupt handling
motion filtering
- motion filtering
The smartwatch currently uses the following configuration:
+----------------+---------+----------------------------------+-----------------+
| Register | Address | Description | Value Used |
+----------------+---------+----------------------------------+-----------------+
| REG_CHIP_ID | 0x00 | Silicon ID — must read as 0xD1 | 0xD1 (verify) |
| REG_ACC_RANGE | 0x41 | Accelerometer sensitivity | 0x03 (±2G) |
| REG_STEP_CNT_0 | 0x78 | Step counter LSB (low byte) | dynamic read |
| REG_STEP_CNT_1 | 0x79 | Step counter MSB (high byte) | dynamic read |
| REG_STEP_CONF0 | 0x7A | Pedometer engine config | 0x15 (enable) |
| REG_STEP_CONF1 | 0x7B | Sensitivity & mode selection | 0x0B (normal) |
| REG_CMD | 0x7E | Command register (reset/boot) | 0xB2 (rst step) |
+----------------+---------+----------------------------------+-----------------+The accelerometer operates in ±2G mode because this range is well suited for human walking motion while maintaining good sensitivity.
The step counter itself is stored inside two registers:
REG_STEP_CNT_0 (LSB)
- REG_STEP_CNT_0 (LSB)
REG_STEP_CNT_1 (MSB)
- REG_STEP_CNT_1 (MSB)
These two registers form a 16-bit hardware step counter.
Sensor Startup SequenceThe initialization order is critical for stable pedometer operation.
Soft reset sensor
- Soft reset sensor
Enable accelerometer normal mode
- Enable accelerometer normal mode
Enable gyroscope normal mode
- Enable gyroscope normal mode
Reset internal step counter
- Reset internal step counter
One important discovery during development was that the gyroscope stabilization delay is extremely important.
If the required delay is skipped, the pedometer engine becomes unstable and may miss early steps after boot.
Even though the smartwatch mainly uses accelerometer-based step tracking, enabling the gyroscope improves the stability of the BMI160 motion engine internally.
Efficient Step PollingThe smartwatch retrieves the step counter using an I²C burst read starting from register 0x78.
Instead of reading both step bytes separately, the firmware reads them in a single transaction to reduce bus overhead and improve efficiency.
This reduces step polling time to well under 1 millisecond.
step_count = (REG_STEP_CNT_1 << 8) | REG_STEP_CNT_0Motion Filtering and False Step ReductionOne useful feature of the BMI160 hardware pedometer is its internal motion filtering system.
The sensor does not immediately count a single movement as a valid step. Instead, it waits until several consecutive walking motions are detected before increasing the step count.
This helps reduce false step detection caused by random hand movement or vibrations.
A significant amount of tuning and testing was required to achieve step counting behavior that felt stable enough for daily use.
Interrupt-Driven Wake SystemThe BMI160 interrupt system is also used as part of the smartwatch low-power architecture.
Motion interrupts can wake the ESP32-C3 from sleep states without requiring constant sensor polling.
In this hardware revision, the BMI160 interrupt line is OR-connected together with the physical wake button on GPIO5 using a small jumper patch.
The original PCB revision did not yet include a dedicated IMU wake trace, so this modification allowed both motion events and button presses to wake the system correctly during testing and daily use.
This approach allowed the smartwatch to maintain responsive interaction while still preserving battery life.
Current StatusThe current smartwatch firmware already provides:
stable step tracking
- stable step tracking
BLE synchronization
- BLE synchronization
smooth UI rendering
- smooth UI rendering
deep sleep support
- deep sleep support
raise-to-wake support
- raise-to-wake support
multi-day battery operation
- multi-day battery operation
While the smartwatch is still under active development, the BMI160 integration has already become stable enough for comfortable daily use.
Step 4: Firmware Development (Heart Rate Sensor)After the hardware is assembled, the next step is developing the firmware that runs on the ESP32-C3. The firmware is responsible for managing the display, handling user input, reading sensors, and controlling the system’s power behavior.
The smartwatch firmware is written in C++ using the Arduino framework, which provides easy access to ESP32 hardware features such as I²C communication, GPIO control, and power management.
Two sensors are connected through the shared I²C bus:
- MAX30100 for heart rate monitoring
- BMI160 for motion tracking and step counting
In this step we focus on the MAX30100 heart rate sensor, which provides real-time pulse detection using optical measurement.
How the MAX30100 Heart Rate Sensor WorksThe MAX30100 is an integrated pulse oximeter and heart rate sensor.
Inside the sensor there are two LEDs:
- A red LED
- An infrared LED
These LEDs shine light into the user’s skin. When blood flows through the capillaries, it slightly changes how much light is absorbed and reflected. The sensor measures these changes using a photodiode.
Every time the heart beats, the blood volume changes slightly, causing a measurable variation in the reflected light. This signal is processed to determine the heart rate in beats per minute (BPM).
This technique is called photoplethysmography (PPG) and is widely used in smartwatches and fitness trackers.
I²C Communication SetupThe MAX30100 communicates with the ESP32-C3 using the I²C bus.
Both sensors in the system share the same bus lines:
SDA is connected to GPIO 8
SCL is connected to GPIO 9
Because I²C supports multiple devices, both the MAX30100 and BMI160 can operate on the same two wires without interfering with each other.
The ESP32 acts as the I²C master, periodically reading measurement data from the MAX30100.
Polling the Sensor DataThe MAX30100 stores measurement samples inside an internal FIFO buffer.
If the firmware does not read the FIFO frequently enough, the buffer may overflow and measurement data can be lost. To prevent this, the driver periodically polls the sensor and retrieves all available samples.
Instead of reading one sample at a time, the firmware uses burst polling, which reads multiple samples in a single transaction. This reduces I²C overhead and improves efficiency.
The driver performs several polling passes during each measurement cycle to ensure the FIFO buffer remains empty and no data is lost.
Heart Rate Data ProcessingThe raw signal coming from the MAX30100 contains noise and variations caused by movement, sensor position, and ambient light.
To obtain stable heart rate readings, the firmware performs several processing steps.
First, the raw infrared signal is analyzed to detect pulse peaks, which correspond to heartbeats.
Next, the time interval between consecutive peaks is calculated. From this interval, the firmware computes the beats per minute (BPM).
To improve stability, recent heart rate values are stored in a short history buffer. This allows the firmware to smooth the output and also enables the UI to draw a small real-time graph of the heart rate signal.
Sensor Power ManagementBecause the smartwatch runs on a battery, sensors must be carefully managed to avoid unnecessary power consumption.
When the heart rate mode is not active, the MAX30100 driver disables the sensor and stops polling the FIFO buffer. This prevents the sensor from continuously drawing current.
When the user enters the heart rate measurement screen, the sensor is initialized again and measurement begins.
Before the device enters deep sleep, the firmware shuts down the MAX30100 to ensure that no current is leaking through the I²C bus or sensor circuitry.
This shutdown process is part of the system’s power management strategy described earlier.
Displaying Heart Rate DataThe measured heart rate values are displayed on the TFT screen together with a small graph showing the recent signal waveform.
To keep the interface responsive and reduce CPU usage, the display system only updates the parts of the screen that change.
Instead of redrawing the entire screen each frame, the firmware performs partial rendering, updating only the numerical value and the graph region.
This approach significantly reduces rendering workload and helps keep the system smooth even with limited hardware resources.
Stability and Debug ProtectionDuring development, debug messages are sometimes printed to the serial console.
However, sending serial messages continuously can waste CPU time and may even block execution if the USB buffer becomes full.
To prevent this, debug output is only executed when a serial connection is actually active.
This simple guard ensures that the smartwatch behaves normally when running from battery without a connected computer.
Step 5: CPU Frequency Optimization (Underclock Strategy)Because the smartwatch runs from a small LiPo battery, power efficiency is extremely important. One effective technique used in this project is dynamic CPU frequency scaling, which adjusts the ESP32-C3 clock speed depending on what the system is doing.
The ESP32-C3 normally runs at 160 MHz, but running at full speed all the time wastes energy when the system is performing simple tasks.
To solve this, the firmware dynamically changes the CPU frequency during operation.
When the system needs high performance, such as during menu animations or UI transitions, the CPU temporarily runs at 160 MHz. This ensures that animations remain smooth and responsive.
During normal operation, such as displaying the watchface, reading sensors, or running timers, the system switches to a lower clock speed of 80 MHz. This provides a good balance between performance and power consumption.
In the future, even lower frequencies such as 10–40 MHz can be used for features like Always-On Display (AOD) mode, where only minimal processing is required.
Reducing the CPU frequency significantly decreases power consumption because the processor performs fewer switching operations per second. This helps extend battery life without affecting user experience.
Another advantage of this approach is that heavy tasks are completed quickly at high frequency and then the processor returns to a lower power state. This method is often called “race to sleep”, where the processor finishes work quickly and then returns to an energy-efficient state.
Step — Source Code and Firmware RepositoryThe full firmware source code for this smartwatch is available on GitHub.
========================================================================================
My Github Repo:
hazzma/ESP-WATCH-V3-ST7789-C3-Supermini
========================================================================================
The repository contains the complete project including:
- Sensor drivers (MAX30100 and BMI160)
- Power management system
- Display control and UI rendering
- Step counter implementation
- Heart rate monitoring logic
If you want to build the firmware yourself or explore the code, download the project from the repository and open it in your development environment.
When cloning the repository, it is recommended to follow the newest branch, since it contains the latest optimizations and improvements to the firmware architecture.
The newest branch includes improvements such as:
- Optimized memory usage for UI rendering
- Improved sensor polling efficiency
- Enhanced power management logic
- Reduced CPU overhead for background tasks
Make sure to check out the latest branch version to ensure you are working with the most optimized codebase.
Example workflow:
- Clone the repository from GitHub
- Switch to the newest branch
- Build and flash the firmware to the ESP32-C3
This allows you to run the smartwatch firmware exactly as demonstrated in this project.
Step 6: Troubleshooting and Known IssuesStep — Troubleshooting and Known IssuesLike most embedded systems, the smartwatch firmware relies on several low-level hardware features. If the system behaves unexpectedly during startup or programming, the issue is often related to CPU clock configuration and USB communication.
One important behavior to understand is how the ESP32-C3 interacts with USB CDC mode, which is used for flashing firmware and serial communication with a computer.
USB CDC Boot IssueDuring early testing, it was discovered that running the ESP32-C3 at a reduced CPU frequency immediately after boot can sometimes cause the device to become stuck in USB CDC mode.
When the CPU is underclocked too early, the USB subsystem may fail to initialize properly. This can cause several symptoms:
- The device appears frozen after boot
- The USB serial port becomes unstable or stops responding
- The firmware upload process fails intermittently
- The system becomes difficult to reset or reconnect
This behavior can make the device appear as if it is “stressed” or locked in CDC mode.
Required Startup ConfigurationTo prevent this issue, the firmware forces the CPU clock to run at 160 MHz during initial startup.
Running the processor at full speed during the first seconds after boot ensures that:
- USB CDC initialization completes successfully
- The computer correctly detects the device
- Firmware flashing and serial communication remain stable
After the system has fully started and USB communication is stable, the firmware can safely switch to the lower power clock modes used during normal operation.
This approach ensures reliable development and debugging while still benefiting from power-saving techniques during regular use.
Recommended Debug ProcedureIf the smartwatch does not behave correctly after flashing firmware, try the following steps:
First, confirm that the CPU is running at 160 MHz during startup before any dynamic frequency scaling occurs.
Second, check that the USB cable and port are functioning properly and that the device appears correctly in the system’s serial port list.
Finally, reflash the firmware and reset the board if the device becomes stuck during boot.
Following these steps usually resolves startup issues related to USB CDC communication.
Tiny engineering lesson hiding here. Low-power optimization is great, but timing matters. If you start underclocking things before critical subsystems finish initializing, weird behavior shows up.
Your fix is actually the standard trick used in many embedded devices:
boot fast → stabilize system → then save power.
This allows you to run the smartwatch firmware exactly as demonstrated in this project.b









_t9PF3orMPd.png?auto=compress%2Cformat&w=40&h=40&fit=fillmax&bg=fff&dpr=2)






Comments