Wrist Health Tracker
Public Unreviewedby TylerK
A wrist-worn nRF52840 health tracker for sleep-quality scoring: heart rate, HRV, SpO2, steps and skin temperature, logged to flash and synced over BLE to a home server. Four-layer 32 x 30 mm KiCad board with single-sided assembly, a 3D-printed 35.7 x 33.9 x 9.95 mm case and a magnetic pogo-pin dock.
d433b2a
14h ago
Hardware design
This document covers the board in kicad/: the circuit, the pin map firmware needs, the layout and the analysis behind it. The enclosure and dock are described in mechanical/README.md.
Block diagram
dock pogo pads (VBUS, D-, D+, GND) ── USBLC6 ESD ──┬── MCP73831 charger ── VBAT ── 302025 Li-Po (PCM)
│ │
└── nRF52840 VBUS (USB) ├── TPS62840 buck ── +1V8 (all logic)
├── MAX30101 VLED
└── 1 M / 470 k divider ── AIN4
+1V8 ── E73 module (nRF52840) ── I²C ── MAX30101 PPG · BMI270 IMU · TMP117 temperature
QSPI ─ W25Q64JW 8 MB flash
GPIO ─ side button · status LED (AO3400A) · 32.768 kHz crystal
Power
| Block | Part | Configuration |
|---|---|---|
| Battery | 302025 Li-Po, about 110 mAh, with protection circuit | Hand-soldered to the BT1 pads on the battery side |
| Charger | MCP73831 (U7) | R1 = 12 kΩ → 83 mA (about 0.75C). Red charge LED D2 on STAT. Charge time under 2 h. |
| 1.8 V rail | TPS62840 (U6), 2.2 µH + 4.7 µF in / 10 µF out | VSET = GND → 1.8 V. MODE = GND (power-save), STOP = GND, EN = VBAT. 60 nA quiescent. |
| nRF52840 supply | E73-2G4M08S1C (U1) | Normal-voltage mode: VDD = VDDH = 1.8 V, DCCH unused. USB runs from VBUS through the internal regulator. |
| PPG LEDs | MAX30101 (U2) VLED | VBAT direct, 10 µF + 100 nF at the pins. VLED needs at least 3.1 V, so firmware stops the PPG below about 3.4 V. |
| Battery sense | R3 1 MΩ / R4 470 kΩ, C5 100 nF | VBAT × 0.32 into AIN4; 1.34 V at 4.2 V, about 2.9 µA |
Dock hot-plug. The device's VBUS has only ceramic capacitance (about 9.4 µF). Against the inductance of the cable and pogo pins, a hot-plug rings VBUS to about 9.3 V in simulation. That's above the nRF52840's 5.8 V VBUS limit. The dock therefore carries a 100 µF / 10 V aluminium electrolytic straight across its VBUS and GND pins; its ESR over-damps the ring. The USBLC6 VBUS clamp is the second line of defence.
Power budget estimate.
| Mode | Average battery current |
|---|---|
| Night: continuous green PPG, FIFO drained by the nRF, BLE sync | about 530 µA |
| Day: PPG 30 s every 5 min, steps continuous | about 70 µA |
| Per day (8 h night, 16 h day) | about 5.4 mAh |
On 110 mAh that's about 20 days on paper. The design target is 3 days, which leaves room for SpO₂ sampling, BLE retries and firmware overhead.
MCU pin map
| Signal | nRF52840 pin | Module pin | Notes |
|---|---|---|---|
| I2C_SDA | P0.15 | 28 | 4.7 kΩ pull-up (R9) |
| I2C_SCL | P0.08 | 16 | 4.7 kΩ pull-up (R8) |
| FLASH_SCK | P1.00 | 36 | QSPI |
| FLASH_CS | P0.22 | 34 | 10 kΩ pull-up (R11) |
| FLASH_IO0 | P1.02 | 38 | |
| FLASH_IO1 | P0.13 | 33 | |
| FLASH_IO2 | P0.20 | 32 | |
| FLASH_IO3 | P0.24 | 35 | |
| PPG_INT | P0.05 | 15 | Open drain, 10 kΩ pull-up (R7) |
| IMU_INT1 | P0.06 | 14 | |
| IMU_INT2 | P1.09 | 17 | |
| TEMP_ALERT | P0.12 | 20 | 10 kΩ pull-up (R10) |
| BTN | P0.07 | 22 | Active low, use the internal pull-up |
| LED_CTRL | P0.04 | 18 | Gate of Q1 (AO3400A), 100 kΩ pull-down |
| VBAT_SENSE | P0.28 / AIN4 | 4 | Divider output |
| XL1 / XL2 | P0.00 / P0.01 | 11 / 13 | 32.768 kHz crystal, 20 pF load capacitors |
| RESET | P0.18 | 26 | SWD pad 4 |
| SWDIO / SWDCLK | 37 / 39 | SWD pads 2 / 3 | |
| D+ / D− / VBUS | 31 / 29 / 27 | Dock pads 3 / 2 / 1 |
I²C addresses:
- MAX30101: 0x57, part ID 0x15.
- TMP117: 0x48 (ADD0 to GND), device ID 0x0117.
- BMI270: 0x68 (SDO to GND), chip ID 0x24.
Flash: W25Q64JW, 1.8 V, JEDEC ID EF 60 17. The QSPI lines use general-purpose pins, so start SCK at 8 MHz and raise it to 16 MHz once reads verify.
Firmware-relevant hardware facts:
- All I/O is 1.8 V. A debug probe must support 1.8 V targets, such as an nRF52840 DK or STLINK-V3MINIE.
- The low-frequency clock is the external crystal.
- The module datasheet doesn't document a DC/DC inductor, so the build uses the LDO (DCDCEN = 0).
- The status LED is white (D1, 220 Ω from VBAT), switched by Q1. Q1 is an AO3400A, whose V_GS(th) of 1.45 V maximum turns it fully on from a 1.8 V GPIO.
Board
| Outline | 32 × 30 mm, 3 mm corner radius, 0.8 mm thick |
| Layers | 4 (JLC04081H-7628 class), 1 oz outer / 0.5 oz inner, ENIG |
| Components | 50 footprints: 44 assembled parts, all on the skin side (F.Cu), plus 6 pad-only footprints (battery, dock, SWD, three test points) |
| Copper | About 540 mm of track and about 300 through vias (0.4 / 0.2 mm), 155 of them GND stitching |
| Rules | Signals 0.1 mm track / 0.1 mm clearance. Power (VBAT, VBUS, +1V8, BUCK_SW, GND) 0.3 mm. USB 0.2 mm. |
Stack-up.
| Layer | Use |
|---|---|
| F.Cu (skin side, all parts) | Signals plus a GND pour |
| In1.Cu | Solid GND plane, no traces |
| In2.Cu | +1V8 plane, with two short signal jumps (about 10 mm of track in all) |
| B.Cu (battery side) | Signals, VBAT/VBUS feeds, plus a GND pour. BT1, the SWD pads and the test points are here. |
Layout:
- Buck converter. The input capacitor's hot loop (C3) is 0.31 mm². The output runs L1 → C4 → the feedback pin.
- Crystal. It sits beside module pins 11 and 13, with short traces.
- Antenna. The E73's ceramic antenna sits at the +x board edge. Nothing is on any copper layer under or beside it, and the GND stitching is kept out of that keep-out.
- TMP117. There's no pour within ±1.9 mm on F or B, so the sensor reads skin through the enclosure's aluminium post, not the board.
- Fixed by the enclosure:
- the antenna end at the +x edge;
- the optical sensor under the case window;
- SW1's plunger overhanging the −x edge;
- the dock pads at the +y edge, where the cradle's pins land.
Current and thermal analysis (four-layer current-density and heat solve):
| Result | Value |
|---|---|
| Peak current density | 27 A/mm² |
| Hottest part | U7 while charging, a 20.5 °C rise in still air (50 °C budget) |
| Current necks | None |
Other analysis findings, and what they mean:
- Signals on B.Cu reference +1V8 rather than GND. This is acceptable: the fastest edge is USB full speed, and the +1V8 plane is stitched to GND through seven 100 nF capacitors and the buck's output capacitors.
- Inductor ripple is expected: the TPS62840 runs in PFM at these loads.
Verification
| Check | Result |
|---|---|
| ERC | 0 errors, 0 warnings |
| Netlist against the design's pin-to-net table | Every pin matches |
| DRC (clearance, shorts, holes, dangling copper, mask bridges) | 0 errors |
| DRC unconnected items | 0 |
| Schematic parity | 0 issues |
| DRC warnings | 2, both expected: lib_footprint_mismatch on SW1 and D2, whose silkscreen was trimmed on the board |
| Module footprint against the vendor land pattern | 43 / 43 pads |
Ordering (JLCPCB)
Files in hardware/: gerbers_jlcpcb.zip, bom_jlcpcb.csv and cpl_jlcpcb.csv.
PCB settings:
- 4 layers, 0.8 mm thick;
- minimum track and space 0.09 mm;
- minimum via hole 0.2 mm, tented;
- ENIG finish, which keeps the LGA and pogo pads flat.
Assembly settings:
- Standard PCBA, top side only.
- The placement file leaves out J1, J2, BT1 and the test points, which are bare pads.
- Check each part's rotation in JLC's placement preview, especially U1, U2, U3, U5, U6 and the LEDs.
Parts cost (JLCPCB, 2026-10-14): $58.00 for two boards, plus a $30 loading fee for the 10 extended parts. The PCB and assembly fees come on top.
Bring-up order
- Program over SWD (the J2 pads) with the battery not fitted, powered from USB through the dock.
- Check, in order:
- the 1.8 V rail;
- the 32 kHz crystal;
- an I²C scan (0x48, 0x57, 0x68) and each part's ID;
- the flash JEDEC ID;
- the LED;
- the button;
- the VBAT reading against a meter.
- Fit the battery and close the case.
# Hardware design
This document covers the board in `kicad/`: the circuit, the pin map firmware needs, the layout and the analysis behind it. The enclosure and dock are described in [`mechanical/README.md`](../mechanical/README.md).
## Block diagram
```
dock pogo pads (VBUS, D-, D+, GND) ── USBLC6 ESD ──┬── MCP73831 charger ── VBAT ── 302025 Li-Po (PCM)
│ │
└── nRF52840 VBUS (USB) ├── TPS62840 buck ── +1V8 (all logic)
├── MAX30101 VLED
└── 1 M / 470 k divider ── AIN4
+1V8 ── E73 module (nRF52840) ── I²C ── MAX30101 PPG · BMI270 IMU · TMP117 temperature
QSPI ─ W25Q64JW 8 MB flash
GPIO ─ side button · status LED (AO3400A) · 32.768 kHz crystal
```
## Power
| Block | Part | Configuration |
|---|---|---|
| Battery | 302025 Li-Po, about 110 mAh, with protection circuit | Hand-soldered to the BT1 pads on the battery side |
| Charger | MCP73831 (U7) | R1 = 12 kΩ → 83 mA (about 0.75C). Red charge LED D2 on STAT. Charge time under 2 h. |
| 1.8 V rail | TPS62840 (U6), 2.2 µH + 4.7 µF in / 10 µF out | VSET = GND → 1.8 V. MODE = GND (power-save), STOP = GND, EN = VBAT. 60 nA quiescent. |
| nRF52840 supply | E73-2G4M08S1C (U1) | Normal-voltage mode: VDD = VDDH = 1.8 V, DCCH unused. USB runs from VBUS through the internal regulator. |
| PPG LEDs | MAX30101 (U2) VLED | VBAT direct, 10 µF + 100 nF at the pins. VLED needs at least 3.1 V, so firmware stops the PPG below about 3.4 V. |
| Battery sense | R3 1 MΩ / R4 470 kΩ, C5 100 nF | VBAT × 0.32 into AIN4; 1.34 V at 4.2 V, about 2.9 µA |
**Dock hot-plug.** The device's VBUS has only ceramic capacitance (about 9.4 µF). Against the inductance of the cable and pogo pins, a hot-plug rings VBUS to about 9.3 V in simulation. That's above the nRF52840's 5.8 V VBUS limit. The dock therefore carries a 100 µF / 10 V aluminium electrolytic straight across its VBUS and GND pins; its ESR over-damps the ring. The USBLC6 VBUS clamp is the second line of defence.
**Power budget estimate.**
| Mode | Average battery current |
|---|---|
| Night: continuous green PPG, FIFO drained by the nRF, BLE sync | about 530 µA |
| Day: PPG 30 s every 5 min, steps continuous | about 70 µA |
| **Per day (8 h night, 16 h day)** | **about 5.4 mAh** |
On 110 mAh that's about 20 days on paper. The design target is 3 days, which leaves room for SpO₂ sampling, BLE retries and firmware overhead.
## MCU pin map
| Signal | nRF52840 pin | Module pin | Notes |
|---|---|---|---|
| I2C_SDA | P0.15 | 28 | 4.7 kΩ pull-up (R9) |
| I2C_SCL | P0.08 | 16 | 4.7 kΩ pull-up (R8) |
| FLASH_SCK | P1.00 | 36 | QSPI |
| FLASH_CS | P0.22 | 34 | 10 kΩ pull-up (R11) |
| FLASH_IO0 | P1.02 | 38 | |
| FLASH_IO1 | P0.13 | 33 | |
| FLASH_IO2 | P0.20 | 32 | |
| FLASH_IO3 | P0.24 | 35 | |
| PPG_INT | P0.05 | 15 | Open drain, 10 kΩ pull-up (R7) |
| IMU_INT1 | P0.06 | 14 | |
| IMU_INT2 | P1.09 | 17 | |
| TEMP_ALERT | P0.12 | 20 | 10 kΩ pull-up (R10) |
| BTN | P0.07 | 22 | Active low, use the internal pull-up |
| LED_CTRL | P0.04 | 18 | Gate of Q1 (AO3400A), 100 kΩ pull-down |
| VBAT_SENSE | P0.28 / AIN4 | 4 | Divider output |
| XL1 / XL2 | P0.00 / P0.01 | 11 / 13 | 32.768 kHz crystal, 20 pF load capacitors |
| RESET | P0.18 | 26 | SWD pad 4 |
| SWDIO / SWDCLK | | 37 / 39 | SWD pads 2 / 3 |
| D+ / D− / VBUS | | 31 / 29 / 27 | Dock pads 3 / 2 / 1 |
**I²C addresses:**
- MAX30101: 0x57, part ID 0x15.
- TMP117: 0x48 (ADD0 to GND), device ID 0x0117.
- BMI270: 0x68 (SDO to GND), chip ID 0x24.
**Flash:** W25Q64JW, 1.8 V, JEDEC ID EF 60 17. The QSPI lines use general-purpose pins, so start SCK at 8 MHz and raise it to 16 MHz once reads verify.
**Firmware-relevant hardware facts:**
- All I/O is 1.8 V. A debug probe must support 1.8 V targets, such as an nRF52840 DK or STLINK-V3MINIE.
- The low-frequency clock is the external crystal.
- The module datasheet doesn't document a DC/DC inductor, so the build uses the LDO (DCDCEN = 0).
- The status LED is white (D1, 220 Ω from VBAT), switched by Q1. Q1 is an AO3400A, whose V_GS(th) of 1.45 V maximum turns it fully on from a 1.8 V GPIO.
## Board
| | |
|---|---|
| Outline | 32 × 30 mm, 3 mm corner radius, 0.8 mm thick |
| Layers | 4 (JLC04081H-7628 class), 1 oz outer / 0.5 oz inner, ENIG |
| Components | 50 footprints: 44 assembled parts, all on the skin side (F.Cu), plus 6 pad-only footprints (battery, dock, SWD, three test points) |
| Copper | About 540 mm of track and about 300 through vias (0.4 / 0.2 mm), 155 of them GND stitching |
| Rules | Signals 0.1 mm track / 0.1 mm clearance. Power (VBAT, VBUS, +1V8, BUCK_SW, GND) 0.3 mm. USB 0.2 mm. |
**Stack-up.**
| Layer | Use |
|---|---|
| F.Cu (skin side, all parts) | Signals plus a GND pour |
| In1.Cu | Solid GND plane, no traces |
| In2.Cu | +1V8 plane, with two short signal jumps (about 10 mm of track in all) |
| B.Cu (battery side) | Signals, VBAT/VBUS feeds, plus a GND pour. BT1, the SWD pads and the test points are here. |
**Layout:**
- **Buck converter.** The input capacitor's hot loop (C3) is 0.31 mm². The output runs L1 → C4 → the feedback pin.
- **Crystal.** It sits beside module pins 11 and 13, with short traces.
- **Antenna.** The E73's ceramic antenna sits at the +x board edge. Nothing is on any copper layer under or beside it, and the GND stitching is kept out of that keep-out.
- **TMP117.** There's no pour within ±1.9 mm on F or B, so the sensor reads skin through the enclosure's aluminium post, not the board.
- **Fixed by the enclosure:**
- the antenna end at the +x edge;
- the optical sensor under the case window;
- SW1's plunger overhanging the −x edge;
- the dock pads at the +y edge, where the cradle's pins land.
**Current and thermal analysis** (four-layer current-density and heat solve):
| Result | Value |
|---|---|
| Peak current density | 27 A/mm² |
| Hottest part | U7 while charging, a 20.5 °C rise in still air (50 °C budget) |
| Current necks | None |
Other analysis findings, and what they mean:
- **Signals on B.Cu reference +1V8 rather than GND.** This is acceptable: the fastest edge is USB full speed, and the +1V8 plane is stitched to GND through seven 100 nF capacitors and the buck's output capacitors.
- **Inductor ripple** is expected: the TPS62840 runs in PFM at these loads.
## Verification
| Check | Result |
|---|---|
| ERC | 0 errors, 0 warnings |
| Netlist against the design's pin-to-net table | Every pin matches |
| DRC (clearance, shorts, holes, dangling copper, mask bridges) | 0 errors |
| DRC unconnected items | 0 |
| Schematic parity | 0 issues |
| DRC warnings | 2, both expected: `lib_footprint_mismatch` on SW1 and D2, whose silkscreen was trimmed on the board |
| Module footprint against the vendor land pattern | 43 / 43 pads |
## Ordering (JLCPCB)
Files in `hardware/`: `gerbers_jlcpcb.zip`, `bom_jlcpcb.csv` and `cpl_jlcpcb.csv`.
**PCB settings:**
- 4 layers, 0.8 mm thick;
- minimum track and space 0.09 mm;
- minimum via hole 0.2 mm, tented;
- ENIG finish, which keeps the LGA and pogo pads flat.
**Assembly settings:**
- Standard PCBA, top side only.
- The placement file leaves out J1, J2, BT1 and the test points, which are bare pads.
- Check each part's rotation in JLC's placement preview, especially U1, U2, U3, U5, U6 and the LEDs.
**Parts cost (JLCPCB, 2026-10-14):** $58.00 for two boards, plus a $30 loading fee for the 10 extended parts. The PCB and assembly fees come on top.
## Bring-up order
1. Program over SWD (the J2 pads) with the battery not fitted, powered from USB through the dock.
2. Check, in order:
1. the 1.8 V rail;
2. the 32 kHz crystal;
3. an I²C scan (0x48, 0x57, 0x68) and each part's ID;
4. the flash JEDEC ID;
5. the LED;
6. the button;
7. the VBAT reading against a meter.
3. Fit the battery and close the case.