400 "The prompt is too long: 325530, model maximum context length: 262144 (ref: 88a4a9ed-0feb-4e1c-8d95-e7df6ce13c81)"


Summary:
## Goal
- Create a personal, sideloaded Garmin Connect IQ freediving/snorkeling app for the user’s existing **Garmin fēnix 3 / fēnix 3 HR**, scoped to breath-hold / surface heart-rate logging because depth measurement is unavailable on that hardware.
- Answer the user’s newer question: which Garmin fēnix watch model supports a freediving app that measures depth via **raw ambient pressure**.

## Constraints & Preferences
- **Current target hardware: fēnix 3 / fēnix 3 HR** (API Level 1.4.0, 10 ATM pressure-equivalent, ANT+ external HR support, 5 physical buttons, Memory-In-Pixel 218×218 screen).
- **Depth-capable fēnix recommendation: fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** — these support Connect IQ API 2.4.0+ (`Activity.Info.ambientPressure` / `rawAmbientPressure`), have a 10 ATM water rating, and forum/developer reports indicate they report accurate depth without artificial underwater pressure caps.
- **fēnix 6 / 7 / 8 and Tactix are unsuitable for depth measurement** despite supporting the pressure API, because forum reports say Garmin artificially caps pressure readings below ~3 m underwater.
- **Venu 2 is out of scope** because it lacks ANT+ support for the user’s HRM‑Tri chest strap and its touchscreen is unusable underwater.
- Max depth **30 m is not feasible on fēnix 3** because API 1.4 only exposes `Sensor.Info.pressure` (sea-level calibrated).
- Underwater HR: user owns **HRM‑Tri** ANT+ chest strap, which pairs with fēnix 3 and fēnix 5.
- Sideload-only distribution; no Connect IQ Store submission (freediving apps are banned).
- App type: **Watch App** with `ActivityRecording` + `FitContributor`.
- fēnix 3 is not dive-rated by Garmin (surface swimming only); fēnix 5 is also not dive-rated despite 10 ATM rating.

## Progress
### Done
- [x] Researched and documented water-resistance ratings and diving limitations of Venu 2, fēnix 3, and fēnix 5.
- [x] Identified Connect IQ policy constraints and sideloading requirements.
- [x] Researched underwater HR limitations and compatible chest straps.
- [x] Drafted a full structured implementation plan (`code/garmin-freedive-app/IMPLEMENTATION_PLAN.md`) covering goal, constraints, approach, steps, verification, risks, and open questions.
- [x] Determined the fēnix 3 / Venu 2 Connect IQ API levels and pressure API support:
  - Venu 2: API 5.0.0, supports `Activity.Info.rawAmbientPressure` (API 2.4.0+).
  - fēnix 3 / fēnix 3 HR: API 1.4.0, does **not** support `rawAmbientPressure` or `ambientPressure`; only `Sensor.Info.pressure` (sea-level calibrated) is exposed.
- [x] Collected user decisions via questionnaire:
  - Water type: both (switchable in app).
  - HR: user has HRM‑Tri, which works with fēnix 3 but not Venu 2.
  - fēnix 3 scope: breath-hold / surface only (no depth).
  - Code path: focus on fēnix 3 with sideloading instructions.
- [x] Implemented the fēnix 3 breath-hold Watch App:
  - `code/garmin-freedive-app/manifest.xml`
  - `code/garmin-freedive-app/monkey.jungle`
  - `code/garmin-freedive-app/source/BreathHoldApp.mc`
  - `code/garmin-freedive-app/source/BreathHoldView.mc`
  - `code/garmin-freedive-app/source/BreathHoldDelegate.mc`
  - `code/garmin-freedive-app/resources/resources.xml`
  - `code/garmin-freedive-app/build.sh`
  - `code/garmin-freedive-app/.gitignore`
  - `code/garmin-freedive-app/README.md`
- [x] Created a structured plan to verify fēnix 3 ambient-pressure accessibility:
  - API/documentation audit.
  - Diagnostic probe app approach.
  - Air calibration and water-submersion test steps.
  - Decision record for whether to add any depth mode.
- [x] Verified and clarified the ambient-pressure distinction:
  - fēnix 3 hardware has a barometric altimeter and a native “Ambient Press.” data field.
  - Connect IQ API 1.4 does **not** expose that raw ambient pressure to third-party apps.
  - Only `Sensor.Info.pressure` (sea-level calibrated) is available in CIQ on fēnix 3.
- [x] Researched which Garmin fēnix models support `rawAmbientPressure`/`ambientPressure` (API Level 2.4.0+).
- [x] Identified fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus as API 2.4+ capable (API 3.1/3.3) and therefore able to read raw ambient pressure for depth calculation.
- [x] Found forum/developer evidence that fēnix 5 / 5 Plus / 5X report accurate freediving depth without artificial caps (reports of accuracy within 0.1 m compared to a dive computer, usable to ~40 m).
- [x] Found forum evidence that fēnix 6 / 7 / 8 and Tactix artificially cap pressure sensor readings below ~3 m underwater by design.
- [x] Verified official fēnix 5 / 5X water rating: **10 ATM** (pressure equivalent to 100 m) per Garmin specifications.
- [x] Verified fēnix 5 ANT+ HR strap compatibility (e.g., HRM‑Tri uses 2.4 GHz ANT+ wireless protocol).

### In Progress
- [ ] Update `code/garmin-freedive-app/IMPLEMENTATION_PLAN.md` and `README.md` to clearly state:
  - fēnix 3 hardware-vs-API ambient-pressure distinction.
  - fēnix 5 / 5X Plus as the recommended depth-capable fēnix model.
  - Warning that fēnix 6 / 7 / 8 / Tactix are not suitable for depth measurement due to artificial ~3 m pressure capping.

### Blocked
- (none)

## Key Decisions
- **Use fēnix 3 / fēnix 3 HR instead of Venu 2**: HRM‑Tri is ANT+ and does not pair with Venu 2; fēnix 3 physical buttons work underwater.
- **No depth measurement on fēnix 3**: fēnix 3 Connect IQ API 1.4 lacks `rawAmbientPressure`/`ambientPressure` (introduced in API 2.4.0); `Sensor.Info.pressure` is sea-level calibrated and auto-corrected for weather/altitude, making depth computation unreliable.
- **App scope is breath-hold / surface HR logging only**: prep → hold → recovery timer, contraction counter, FIT recording of hold time and last HR.
- **Sideload-only distribution**: Because Garmin prohibits freediving apps in the Connect IQ Store.
- **Fresh/salt toggle retained but not used for depth**: Kept in the code for future extensibility if a depth-capable device is later targeted.
- **Hardware CAN measure ambient pressure, CIQ app CANNOT read it raw on fēnix 3**: This distinction must be documented precisely.
- **Recommended depth-capable fēnix: fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**: These support API 2.4.0+ pressure fields, have a 10 ATM water rating, support ANT+ HR straps, and forum reports indicate they do not artificially cap underwater pressure readings.
- **Avoid fēnix 6 / 7 / 8 / Tactix for depth-based freediving app**: Forum reports indicate these models artificially cap pressure sensor readings underwater around 3 m, making them unsuitable for depth measurement even though they support the raw ambient pressure API.

## Next Steps
1. Update `code/garmin-freedive-app/IMPLEMENTATION_PLAN.md` to add:
   - fēnix 5 / 5X Plus recommendation for a depth-capable fēnix model.
   - Warning that fēnix 6 / 7 / 8 / Tactix artificially cap underwater pressure around 3 m.
   - Reiteration that the current fēnix 3 app remains breath-hold / surface only.
2. Update `code/garmin-freedive-app/README.md` with the same clarifications.
3. Deliver a concise answer to the user: the **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** is the fēnix model family needed for raw ambient-pressure depth measurement; newer fēnix models are not recommended due to the ~3 m cap.
4. Optionally build and sideload the current fēnix 3 breath-hold app; fix build errors based on SDK output.
5. If the user later obtains a depth-capable fēnix 5-series watch (or a Descent), port the depth logic from the existing plan using `Activity.Info.rawAmbientPressure`.

## Critical Context
- **fēnix 3 pressure API reality**: Hardware measures ambient pressure and the native watch UI exposes it as “Ambient Press.”, but Connect IQ API 1.4 only gives `Sensor.Info.pressure`, which is documented as “barometric pressure calibrated to sea level.”
- **API level gating**: `Activity.Info.ambientPressure` and `Activity.Info.rawAmbientPressure` require **API Level 2.4.0+**; fēnix 3 / fēnix 3 HR are capped at **API Level 1.4.0**.
- **Forum/developer evidence**: A fēnix 3 developer reported that `Sensor.Info.pressure` initially responds to sudden pressure changes but drifts back to the previous value after seconds, confirming it is auto-calibrated/weather-corrected and unsuitable for depth gauges.
- **Correct statement to document**: “The fēnix 3 hardware measures ambient pressure and the native data field shows it, but a Connect IQ app on the fēnix 3 cannot access that raw value; it only sees sea-level-calibrated pressure.”
- **Depth-capable fēnix evidence**:
  - fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus run Connect IQ API 3.1/3.3, which includes `rawAmbientPressure`.
  - Forum reports: fēnix 5 gives accurate depth readings for freediving without the artificial cap that newer models have.
  - Forum reports: fēnix 6 / 7 / 8 and Tactix deliberately limit pressure readings below ~3 m underwater, likely to protect the dive-computer product line.
- **Water resistance caveat**: fēnix 3 and fēnix 5 are rated 10 ATM (pressure equivalent to 100 m), but Garmin explicitly states they are for surface swimming, not dive-rated. Using them for repeated 30 m freediving is outside the stated use case and may void warranty / risk damage.
- **ANT+ HR**: fēnix 5 officially supports ANT+ chest straps such as HRM‑Tri, matching the user’s existing strap.
- **Project location**: All code and plans are in `~/code/garmin-freedive-app/`.

<modified-files>
code/garmin-freedive-app/.gitignore
code/garmin-freedive-app/IMPLEMENTATION_PLAN.md
code/garmin-freedive-app/README.md
code/garmin-freedive-app/build.sh
code/garmin-freedive-app/manifest.xml
code/garmin-freedive-app/monkey.jungle
code/garmin-freedive-app/resources/resources.xml
code/garmin-freedive-app/source/BreathHoldApp.mc
code/garmin-freedive-app/source/BreathHoldDelegate.mc
code/garmin-freedive-app/source/BreathHoldView.mc
</modified-files>


We need respond to user. User gave a summary of prior conversation and likely expects continuation. Need answer their newer question: which fēnix watch supports freediving app measuring depth via raw ambient pressure. Also continue next steps: update docs. We need read files maybe. We should do updates. First, check current files in code/garmin-freedive-app. Then update IMPLEMENTATION_PLAN.md and README.md. Also answer question. Need maybe verify sources? Summary has enough. But we need be careful about accuracy. The newer question: "which Garmin fēnix watch model supports a freediving app that measures depth via raw ambient pressure." Answer: fēnix 5/5S/5X/5 Plus/5S Plus/5X Plus (API 2.4+). But maybe also fēnix 5? Need ensure. We could also web search current to verify. Since it's a specific claim, maybe verify with web explore for newer models capping and fēnix 5 support. The summary already has evidence. But we can search if needed. However user likely wants answer and doc updates. Let's read files first.

I’ll check the current project files and then update the implementation plan and README with the fēnix depth‑capability findings.

bash

ls -la ~/code/garmin-freedive-app && echo "---" && ls -la ~/code/garmin-freedive-app/source


total 56
drwxr-xr-x  4 liph liph  4096 Jul 26 23:09 .
drwxr-xr-x 10 liph liph  4096 Jul 26 23:01 ..
-rwxr-xr-x  1 liph liph  1472 Jul 26 23:09 build.sh
-rw-r--r--  1 liph liph   138 Jul 26 23:09 .gitignore
-rw-r--r--  1 liph liph 16149 Jul 26 23:07 IMPLEMENTATION_PLAN.md
-rw-r--r--  1 liph liph   678 Jul 26 23:09 manifest.xml
-rw-r--r--  1 liph liph    87 Jul 26 23:08 monkey.jungle
-rw-r--r--  1 liph liph  5739 Jul 26 23:08 README.md
drwxr-xr-x  2 liph liph  4096 Jul 26 23:08 resources
drwxr-xr-x  2 liph liph  4096 Jul 26 23:08 source
---
total 24
drwxr-xr-x 2 liph liph 4096 Jul 26 23:08 .
drwxr-xr-x 4 liph liph 4096 Jul 26 23:09 ..
-rw-r--r-- 1 liph liph  644 Jul 26 23:08 BreathHoldApp.mc
-rw-r--r-- 1 liph liph  904 Jul 26 23:08 BreathHoldDelegate.mc
-rw-r--r-- 1 liph liph 6886 Jul 26 23:09 BreathHoldView.mc


Need read IMPLEMENTATION_PLAN.md and README.md to update.


# Garmin Connect IQ Freediving App — Implementation Plan

## 0. Revised Scope (after user decisions)

Based on the follow-up questions, the project scope is now:

- **Primary target device:** **Garmin fēnix 3 / fēnix 3 HR** (API Level 1.4.0).
- **Depth measurement:** **Not implemented.** The fēnix 3 cannot expose raw ambient pressure, so depth is unreliable. The app is therefore **not a dive computer**.
- **Core features for fēnix 3:**
  1. **Static breath-hold timer** — prep breathe, breath-hold, and recovery phases.
  2. **Heart-rate logging** — supports the user’s HRM‑Tri ANT+ chest strap (and fēnix 3 HR wrist HR as a fallback on surface).
  3. **FIT activity recording** — stores breath-hold duration and HR to a FIT file via `FitContributor`.
  4. **Sideloading instructions** — copy `.PRG` to `GARMIN/APPS/` over USB.
- **Venu 2:** Out of scope. Its touchscreen is unusable underwater and the user’s HRM‑Tri is ANT+ only, which the Venu 2 does not support.
- **Water type:** Fresh/salt toggle retained for future extensibility; not used for depth on fēnix 3.

The original feasibility analysis below (§1–§3) is preserved for reference.

---

## 1. Executive Summary / Feasibility Verdict

**Goal:** Build a personal, sideloaded Garmin Connect IQ app that records freedive sessions (dive time, depth, heart rate, static breath-hold) up to ~30 m on watches the user already owns.

**Feasibility:**

| Watch | Can measure depth accurately? | Verdict |
|-------|------------------------------|---------|
| **Garmin Venu 2** | **Yes** (raw ambient pressure API available) | **Recommended primary target** |
| **Garmin fēnix 3 / fēnix 3 HR** | **No, not reliably** (only sea-level-calibrated pressure available) | Not suitable as a depth dive computer; usable only for surface logging / breath-hold timer |

**Key finding:** Depth calculation requires *raw ambient* barometric pressure. Garmin exposes this via `Activity.Info.rawAmbientPressure` starting at Connect IQ API Level 2.4.0. The fēnix 3 family is capped at API Level 1.4 and does **not** support this field. The only pressure value available on fēnix 3 (`Sensor.Info.pressure`) is sea-level calibrated and auto-corrected for weather/altitude, making depth computation unreliable.

**Recommendation:** Build for the **Venu 2** first. If desired, add a degraded fēnix 3 mode that uses altitude-derived depth (with heavy caveats) or limits fēnix 3 to static breath-hold / surface HR logging.

---

## 2. Device/API Findings

### 2.1 Venu 2

| Item | Detail |
|------|--------|
| Connect IQ API Level | **5.0.0** (can be targeted with modern SDK; device files available) |
| Raw ambient pressure | **Supported** — `Activity.Info.rawAmbientPressure` (API 2.4.0+) |
| Smoothed ambient pressure | **Supported** — `Activity.Info.ambientPressure` (API 2.4.0+) |
| Activity recording / FIT export | **Supported** — `Toybox.ActivityRecording` + `Toybox.FitContributor` |
| Heart rate | Built-in optical wrist HR (unreliable underwater) |
| External ANT+ HR chest strap | Supported (HRM-Swim / HRM-Tri recommended for underwater HR) |
| Water resistance | 5 ATM (~50 m pressure equivalent), "Swim" rating; **not dive-rated by Garmin** |
| Controls | Touchscreen + two buttons; touchscreen may be unresponsive when wet / underwater |
| Screen | 416 × 416 AMOLED; bright, but higher battery draw than MIP displays |

**Depth accuracy on Venu 2:** Good enough for recreational freediving to 30 m *if* you calibrate at the surface and account for fresh vs. salt water density. The raw pressure sensor is temperature-compensated and read directly from the internal sensor, so it responds to hydrostatic pressure changes.

### 2.2 fēnix 3 / fēnix 3 HR

| Item | Detail |
|------|--------|
| Connect IQ API Level | **1.4.0** (max) |
| Raw ambient pressure | **Not supported** — `rawAmbientPressure` requires API 2.4.0+ |
| Available pressure value | `Sensor.Info.pressure` only; returns **sea-level-calibrated barometric pressure** |
| Activity recording / FIT export | Supported at API 1.4 (`ActivityRecording`, `FitContributor` since 1.3.0) |
| Heart rate | fēnix 3: external ANT+ chest strap required; fēnix 3 HR: built-in optical HR (still unreliable underwater) |
| Water resistance | 10 ATM (~100 m pressure equivalent), but Garmin explicitly limits it to surface swimming |
| Controls | Five physical buttons — excellent underwater control |
| Screen | 218 × 218 Memory-In-Pixel (16 colors), sunlight-readable, low power |

**Why fēnix 3 cannot give accurate depth:**
- `Sensor.Info.pressure` is calibrated to mean sea level using the current altimeter setting and GPS altitude.
- Garmin's altimeter algorithm deliberately filters out rapid pressure changes to distinguish weather from altitude changes.
- Underwater pressure changes are interpreted as altitude changes with significant lag and may be rejected/flattened.
- Historical depth data fields for fēnix 3 used `Sensor.Info.altitude` and reverse-calculated pressure; this is approximate and weather-sensitive.

**Conclusion:** The fēnix 3 is a poor choice for a 30 m freediving depth app, despite its better water-resistance rating and buttons. It can still host a static breath-hold timer or log surface heart rate.

### 2.3 Connect IQ API Context

- `Activity.Info.rawAmbientPressure` — raw, temperature-compensated sensor reading. Best for depth.
- `Activity.Info.ambientPressure` — two-stage filtered/smoothed reading. Slightly slower response, less noise.
- `Sensor.Info.pressure` — mean sea level pressure. **Not usable for depth on fēnix 3.**
- `SensorHistory.getPressureHistory()` — returns pressure history, but forum reports indicate it stores MSL pressure, not ambient.

---

## 3. Recommended Approach

### 3.1 Primary Target Device

**Garmin Venu 2** (device ID `venu2` in the SDK).

**Why:** It is the only one of the two watches that exposes raw ambient pressure, which is a hard requirement for accurate depth to 30 m.

### 3.2 App Type

**Watch App** with `ActivityRecording` module.

- A **Data Field** cannot call `Sensor` methods (app crash) and cannot control recording.
- A **Watch App** can:
  - Start/stop a FIT activity session.
  - Read `Activity.getActivityInfo().rawAmbientPressure` in the foreground.
  - Create custom FIT fields via `FitContributor` to store depth, dive time, etc.
  - Render a full-screen UI with large dive numbers and button/touch controls.

### 3.3 Recording Strategy

1. On app launch, enter a pre-dive screen.
2. User calibrates surface pressure (auto-capture current `rawAmbientPressure` as `P_surface`).
3. Start `ActivityRecording` session with sport `Activity.SPORT_GENERIC` or `Activity.SPORT_SWIMMING` and an appropriate sub-sport.
4. Every second (or on sensor update), compute:
   - `depth = (P - P_surface) / (ρ · g)`
   - Update a `FitContributor` field for depth in the FIT `RECORD` message.
   - Update max depth, dive time, surface interval.
5. Stop/save session when user finishes.

### 3.4 Heart Rate Strategy

- **Surface / breath-hold mode:** Use wrist optical HR (`Sensor.Info.heartRate` or `ActivityRecording`'s built-in HR).
- **Underwater dive mode:** Wrist optical HR does not work reliably in water. Recommend an **ANT+ chest strap** such as Garmin HRM-Swim or HRM-Tri, which store HR during the dive and download it at the surface.
- The app should enable `SENSOR_HEARTRATE` and pair with the chest strap if available.

### 3.5 Distribution

- **Sideload only.** Garmin prohibits scuba/free-diving apps in the Connect IQ Store.
- Build `.PRG` and copy to `GARMIN/APPS/` over USB.
- Optionally host source on a private GitHub repo; the user builds/sideloads themselves.

---

## 4. Depth Calculation

### 4.1 Formula

```
ΔP = P_current - P_surface            // Pascals
ρ  = water density (kg/m³)
g  = 9.80665 m/s²
depth = ΔP / (ρ · g)                  // meters
```

Density values:
- Fresh water: ~997–1000 kg/m³
- Salt water: ~1020–1025 kg/m³ (varies with salinity/temperature)

### 4.2 Calibration

- At the surface, sample `rawAmbientPressure` for a few seconds and average it.
- Allow the user to set fresh/salt water and to manually override surface pressure if needed.
- Optionally use `Activity.Info.ambientPressure` instead of `rawAmbientPressure` for a less noisy (but slower) reading.

### 4.3 Accuracy Expectations

| Factor | Impact |
|--------|--------|
| Sensor noise | ± few cm at shallow depths |
| Temperature compensation | Handled by rawAmbientPressure |
| Water density error | ~2–3% depth error between fresh and salt |
| Surface pressure drift during session | Small over a dive session; can be re-calibrated at surface |
| Rapid movement / splashing | Use smoothing / median filter to reduce spikes |

At 30 m in salt water, expected pressure increase: ~30 m × 1025 kg/m³ × 9.81 ≈ **302 kPa** above surface pressure. This is well within a 5 ATM sensor's range (~400 kPa above surface).

---

## 5. Implementation Steps

### Phase 0 — Environment & Verification (1–2 days)

1. Install latest Connect IQ SDK (SDK Manager / VS Code Monkey C extension).
2. Download device files for `venu2` (and `fenix3` if degraded support is desired).
3. Create a skeleton Watch App project.
4. Write a quick sensor probe app that prints `Activity.Info.rawAmbientPressure`, `ambientPressure`, `altitude`, and `Sensor.Info.pressure` to confirm values on the real Venu 2.
5. Test in a bucket / shallow water to verify depth response.

### Phase 1 — App Skeleton (1–2 days)

1. Define `manifest.xml` as a Watch App with permissions:
   - `Toybox.ActivityRecording`
   - `Toybox.Sensor`
   - `Toybox.FitContributor`
   - `Toybox.AntPlus` (for external HR sensors)
2. Create main app class (`AppBase` subclass).
3. Create initial views:
   - Pre-dive / calibration view
   - Dive view (current depth, max depth, dive time, HR)
   - Breath-hold view (timer, contractions counter, pulse)
   - Summary / save view
4. Implement input handling (Venu 2: button + touch; fēnix 3: buttons only).

### Phase 2 — Depth & Recording (2–3 days)

1. Implement surface calibration logic.
2. Implement depth computation with fresh/salt-water setting.
3. Implement `ActivityRecording` session start/stop/save.
4. Add `FitContributor` custom fields:
   - `depth_m` (float, units `m`) on `MESG_TYPE_RECORD`
   - `max_depth_m` (float, units `m`) on `MESG_TYPE_SESSION`
   - `dive_time_s` (uint16/uint32, units `s`) on `MESG_TYPE_SESSION`
5. Add per-second update loop (Timer or sensor events).
6. Add max-depth tracking and surface-interval timer.

### Phase 3 — Heart Rate & Sensors (1–2 days)

1. Enable `SENSOR_HEARTRATE`.
2. Detect paired ANT+ chest strap (HRM-Swim/Tri).
3. Display current HR.
4. For fēnix 3, focus on external HR only (no depth channel).

### Phase 4 — Safety Alerts (1 day)

1. Configurable max-depth alarm (vibration + screen flash).
2. Configurable dive-time alarm.
3. Optional surface-interval / recovery timer.

### Phase 5 — Static Breath-Hold Mode (1 day)

1. Timer with prep breathe, hold, and recovery phases.
2. Optional contractions tap counter.
3. Store breath-hold duration to FIT session fields.

### Phase 6 — Testing & Sideloading (2–3 days)

1. Simulator testing for UI and recording logic.
2. Real-device shallow-water tests in pool.
3. Controlled depth tests to 5 m, 10 m, 20 m, 30 m (with safety diver).
4. Compare app-reported depth against a known reference dive computer.
5. Sideload packaging and document install steps.

---

## 6. Risks & Mitigations

| Risk | Likelihood | Impact | Mitigation |
|------|------------|--------|------------|
| Venu 2 is not dive-rated; water damage at 30 m | Medium | High (watch destroyed) | Limit to shallow testing first; understand warranty is void; consider fēnix 3 for 10 m snorkeling only; accept that 30 m is beyond Garmin's stated use case |
| Touchscreen unusable underwater | High | Medium | Use physical buttons on Venu 2 (Start/Back) for all critical actions; keep UI simple |
| Wrist HR unreliable underwater | High | Medium | Require ANT+ chest strap for meaningful underwater HR |
| Pressure sensor drift / weather changes | Low-Medium | Medium | Calibrate at surface before each dive; allow manual override; re-calibrate between dives |
| App crash due to memory limits | Medium | Medium | Keep data structures small; avoid storing per-second arrays in memory; rely on FIT recording |
| Garmin policy / sideload friction | Low | Low | Never submit to store; use private source repo; build `.PRG` manually |
| fēnix 3 depth is inaccurate | Certain | High | Do not rely on fēnix 3 for depth; limit to breath-hold / surface mode |
| Battery drain on AMOLED Venu 2 | Medium | Low | Keep screen on only during dive; use low-brightness mode; end session promptly |

---

## 7. Open Questions / Decisions Needed

Before coding begins, the user should decide:

1. **Primary water type:** Fresh water (pool/lake) or salt water? This sets the default density constant.
2. **External HR strap:** Do you own or plan to buy an HRM-Swim or HRM-Tri? This determines whether underwater HR is a core feature or a best-effort optional feature.
3. **Safety alerts:** Which alarms do you want? (e.g., depth at 15 m, 20 m, 25 m; dive time at 1:00, 1:30, 2:00; surface interval)
4. **fēnix 3 support scope:**
   - A) Drop fēnix 3 depth entirely; use it only as a breath-hold / surface-HR logger.
   - B) Keep a degraded fēnix 3 depth mode using altitude-derived pressure, clearly labeled as unreliable.
5. **Implementation ownership:** Do you want to write the Monkey C code yourself with guidance, or do you want the code scaffolded/implemented for you?
6. **Screen behavior:** Should the dive view stay always-on during the dive, or sleep between updates to save battery?

---

## 8. Appendix A — Sample Depth Calculation (Monkey C)

```monkey-c
using Toybox.Activity;
using Toybox.System;

const G = 9.80665;
const RHO_FRESH = 997.0;   // kg/m³
const RHO_SALT  = 1023.0;  // kg/m³

var surfacePressure = null;
var isSaltWater = false;

function calibrateSurface() {
    var info = Activity.getActivityInfo();
    if (info has :rawAmbientPressure && info.rawAmbientPressure != null) {
        surfacePressure = info.rawAmbientPressure;
        System.println("Surface pressure: " + surfacePressure);
    }
}

function computeDepth() {
    if (surfacePressure == null) { return null; }
    var info = Activity.getActivityInfo();
    if (info == null || !(info has :rawAmbientPressure) || info.rawAmbientPressure == null) {
        return null;
    }
    var rho = isSaltWater ? RHO_SALT : RHO_FRESH;
    var deltaP = info.rawAmbientPressure - surfacePressure;
    return deltaP / (rho * G);
}
```

## 9. Appendix B — Sample FIT Field Registration

```monkey-c
using Toybox.ActivityRecording;
using Toybox.FitContributor;

var depthField = null;
var session = null;

function startDiveSession() {
    session = ActivityRecording.createSession({
        :name => "Freedive",
        :sport => Activity.SPORT_GENERIC,
        :subSport => Activity.SUB_SPORT_GENERIC
    });

    depthField = session.createField(
        "depth",
        0,                                  // developer field id
        FitContributor.DATA_TYPE_FLOAT,
        { :mesgType => Fit.MESG_TYPE_RECORD, :units => "m" }
    );

    session.start();
}

function onTimerTick() {
    var d = computeDepth();
    if (d != null && depthField != null) {
        depthField.setData(d);
    }
}
```

---

## 10. Appendix C — Key Sources

- Garmin Connect IQ API: `Toybox.Activity.Info` — `rawAmbientPressure` since API 2.4.0.
- Garmin Connect IQ API: `Toybox.Sensor.Info` — `pressure` is sea-level calibrated (API 1.0.0).
- Garmin Connect IQ Compatible Devices matrix: Venu 2 = API 5.0; fēnix 3/3 HR = API 1.4.
- Garmin Forums: fēnix 3 depth data fields were removed from the store; they relied on altitude-derived pressure.
- Garmin App Review Guidelines: diving apps are not listed in the Connect IQ Store.

---

**Document status:** Draft — awaiting user decisions in §7 before moving to code.


# Garmin fēnix 3 Freedive Breath App

A personal, sideloaded Connect IQ app for the **Garmin fēnix 3** and **fēnix 3 HR** that records static breath-hold sessions and heart rate to a FIT file.

> **Important:** This app does **not** measure depth. The fēnix 3 does not expose raw ambient pressure through the Connect IQ API, so accurate depth measurement is impossible. For depth logging you need a newer device such as a fēnix 5+, a Descent, or a Venu 2 with an ANT+ capable chest strap.

---

## What it does

- **Static breath-hold timer** with three phases:
  1. **Prep breathe** (default 2 minutes, configurable in code)
  2. **Breath-hold** (counts up until you press START)
  3. **Recovery** (default 2 minutes, configurable in code)
- **Contraction counter** during the hold phase (press UP or DOWN).
- **Heart-rate display** from the HRM‑Tri / HRM‑Swim ANT+ chest strap or fēnix 3 HR wrist sensor.
- **FIT recording** of the session with custom fields:
  - `hold_time_s` — best breath-hold time of the session
  - `last_hr_bpm` — last sampled heart rate
- **Physical-button control** — works with wet fingers and underwater.

---

## Build prerequisites

1. A modern computer with the **Connect IQ SDK** installed.
   - Download the SDK Manager from: https://developer.garmin.com/connect-iq/sdk/
   - Use the SDK Manager to download the latest SDK plus the **fēnix 3** and **fēnix 3 HR** device files.
2. **Visual Studio Code** with the official **Monkey C** extension (recommended), or the Eclipse plugin.
3. A USB cable to connect the watch to your computer.

---

## Build steps

1. Open this folder in VS Code.
2. Run **Monkey C: Build Current Project** (or **Monkey C: Export Project**) from the command palette.
3. Select the `fenix3` or `fenix3_hr` target.
4. The compiler produces a `.PRG` file, typically under:
   - `bin/<build-name>.prg` when building for a single device, or
   - A `.iq` zip when exporting for multiple devices.

If you prefer the command line, use the `monkeyc` compiler and `monkeydo` simulator scripts that ship with the SDK.

---

## Sideload onto the watch

Garmin does not allow diving/breath-hold apps in the Connect IQ Store, so this app is installed manually.

### 1. Put the watch in USB storage mode

1. Connect the watch to your computer with the USB cable.
2. On the watch, confirm **Mass Storage / USB mode** if prompted.
3. The watch appears as a removable drive (e.g., `GARMIN` on Windows, `/media/GARMIN` on Linux, or `GARMIN` on macOS).

### 2. Copy the app

1. Open the watch drive.
2. Copy the compiled `.PRG` file into the `GARMIN/APPS/` folder.
   - Do **not** create a sub-folder.
   - If a previous version exists, overwrite it.
3. Safely eject the watch.

### 3. Launch the app

1. Disconnect the cable.
2. On the watch, go to **Activities** → scroll down to **Apps** (or **More** → **Apps**, depending on firmware).
3. Select **Freedive Breath** and press **START**.

---

## Usage

| Button | Function |
|--------|----------|
| **START** | Begin session / skip prep / end hold / finish recovery / start new session |
| **UP / DOWN** | Count a contraction during the hold phase |
| **BACK (ESC)** | Exit from idle / discard and exit during an active session |

### Typical flow

1. Start the app. You see `FREEDIVE` — press **START**.
2. The 2-minute **PREP BREATHE** countdown starts. Press **START** again to skip to the hold.
3. **HOLD** counts up. Press **UP** or **DOWN** each time you feel a contraction.
4. When you finish the hold, press **START** to move to **RECOVERY**.
5. Recovery counts down from 2 minutes. Press **START** to finish early, or let it time out.
6. The session is saved to a FIT file. You see `SAVED` with your max hold time.
7. Press **START** to begin a new session, or **BACK** to exit.

---

## HRM‑Tri / HRM‑Swim pairing

1. Pair the chest strap with the watch in the regular Garmin **Sensors & Accessories** menu before using the app.
2. Put the strap on before starting the app.
3. The app reads `Sensor.getInfo().heartRate`, which automatically uses the connected ANT+ HR source when available.
4. The strap stores HR data during the breath-hold and syncs it to the watch. The app only samples the live value once per second, so the FIT `last_hr_bpm` field is the last HR seen in the recovery phase.

---

## Customization

Edit these values in `source/BreathHoldView.mc` before building:

```monkey-c
var prepTime = 120;     // seconds
var recoveryTime = 120; // seconds
```

For a pool-only fresh/salt toggle, a depthless water-type setting, or multiple rounds, ask and it can be added.

---

## Troubleshooting

| Problem | Fix |
|---------|-----|
| App does not appear on the watch | Confirm the `.PRG` is directly in `GARMIN/APPS/`, not in a sub-folder. |
| Build fails with API errors | Make sure the SDK includes fēnix 3 device files and the project `minApiLevel` is `1.4.0`. |
| Heart rate shows `--` | Pair the HRM‑Tri with the watch first; wet the strap electrodes; stand close to the watch. |
| App crashes on start | Check that `manifest.xml` includes the `Sensor`, `ActivityRecording`, and `FitContributor` permissions. |
| Timer stops when screen sleeps | This is a known Connect IQ behavior for watch apps. The app resumes when you wake the screen. For long breath-holds, keep the watch awake by pressing LIGHT if needed, or the session will still save with the elapsed time when you wake it. |

---

## Disclaimer

This is a hobby project for personal use only. It is **not** a dive computer, medical device, or safety instrument. Freediving is inherently dangerous. Always dive with a buddy, use proper safety protocols, and rely on certified dive equipment for depth and safety monitoring.


Need update IMPLEMENTATION_PLAN.md and README.md. Also maybe update code? Summary says in progress update docs. We can do edits. We need clearly state fēnix 3 hardware-vs-API distinction; fēnix 5 recommendation; warning fēnix 6/7/8/Tactix not suitable. Also answer user question.

Need perhaps verify if any nuance. The user's question: "which Garmin fēnix watch model supports a freediving app that measures depth via raw ambient pressure." We should answer with fēnix 5 family. But maybe also fēnix Chronos? That's based on fēnix 3, no. fēnix 5 is API 3.x. Need ensure no other fēnix like fēnix 5X. We can say fēnix 5 / 5S / 5X and Plus variants. fēnix 6 has raw ambient pressure API support but caps underwater. However question asks model supports measuring depth via raw ambient pressure. Strictly fēnix 5 family. Could mention fēnix 6/7/8 support rawAmbientPressure API but not for depth due cap; fēnix 3 only calibrated. Also Descent series supports, but user asked fēnix. Include note that Descent is purpose-built, but if wanting fēnix, 5 family.

Need produce final answer after updates. Let's edit files.

For IMPLEMENTATION_PLAN.md, we need update executive summary and device findings maybe. The plan was for Venu 2 primary originally, but after decisions target fēnix 3. Need integrate fēnix 5 recommendation for depth. Add a section 2.4 or update section 1 table. We should keep it coherent.

Let's craft updates:

- In Section 1 table, maybe add row for fēnix 5/5X Plus as recommended depth-capable fēnix. Remove "Recommended primary target" for Venu 2 (out of scope). Actually project scope is fēnix 3. But the question is about depth-capable fēnix. Update table:
  - fēnix 3: No depth.
  - fēnix 5 / 5S / 5X / 5 Plus variants: Yes (raw ambient pressure, no cap).
  - fēnix 6/7/8/Tactix: No due artificial cap.
  - Venu 2: API supports raw but no ANT+ for HRM-Tri; out of scope.
- Section 2.1 maybe rename to fēnix 5/depth-capable watch. Add note.
- Section 2.2 fēnix 3 add hardware-vs-API distinction.
- Section 3.1 primary target device is fēnix 3, but for depth capable fēnix recommend fēnix 5.
- Add warning in risks.
- Appendix C add sources maybe.

But need be careful: The IMPLEMENTATION_PLAN is long. We can make targeted edits. Use edit with multiple replacements.

For README, update important note to be precise: fēnix 3 hardware measures ambient pressure and native data field shows it but CIQ app cannot access raw; only sea-level calibrated. For depth need fēnix 5/5S/5X/5 Plus; avoid fēnix 6/7/8/Tactix due ~3m cap. Maybe add a short "Which fēnix for depth?" section.

Let's make edits.

First IMPLEMENTATION_PLAN.md. We'll modify top section to include fēnix 5 recommendation. The section 0 revised scope currently mentions Venu 2 out of scope, fēnix 3 target. We can add bullet about depth-capable fēnix 5 recommendation.

Let's compose edits:

Edit 1: Section 0 bullet list add:
- **Depth-capable fēnix recommendation:** For a fēnix watch that can measure depth in a Connect IQ app, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Newer fēnix 6 / 7 / 8 / Tactix models support the API but reportedly cap pressure readings underwater around 3 m, making them unsuitable for freediving depth.

Edit 2: Update Section 1 feasibility table and recommendation. Replace table. Also recommendation text.

Old table:
| Watch | Can measure depth accurately? | Verdict |
|-------|------------------------------|---------|
| **Garmin Venu 2** | **Yes** (raw ambient pressure API available) | **Recommended primary target** |
| **Garmin fēnix 3 / fēnix 3 HR** | **No, not reliably** (only sea-level-calibrated pressure available) | Not suitable as a depth dive computer; usable only for surface logging / breath-hold timer |

Replace with:
| Watch | Can measure depth accurately? | Verdict |
|-------|------------------------------|---------|
| **Garmin fēnix 3 / fēnix 3 HR** | **No** (CIQ API 1.4 only; `Sensor.Info.pressure` is sea-level calibrated) | Breath-hold / surface HR only |
| **Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** (raw ambient pressure API, no reported underwater cap) | Recommended depth-capable fēnix |
| **Garmin fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth (reportedly cap pressure below ~3 m underwater) | Not recommended for depth measurement |
| **Garmin Venu 2** | API supports raw pressure, but **not suitable here** | Out of scope (no ANT+ for HRM‑Tri, poor wet touchscreen) |

And replace recommendation text:
"Key finding... Recommendation: Build for Venu 2 first..." -> Update: "Recommendation: Use the fēnix 3 app for surface breath-hold / HR logging. For a depth-measuring fēnix app, target the fēnix 5 family; avoid fēnix 6/7/8/Tactix due to the reported underwater pressure cap."

Edit 3: In Section 2.2 fēnix 3, add hardware-vs-API distinction paragraph after "Why cannot give accurate depth" maybe before conclusion. We'll add a note. Use edit.

Edit 4: Add new section 2.4 "Depth-capable fēnix models (for future reference)" after 2.3 maybe. Or integrate into 2.1 as Venu 2 replaced? Since Venu 2 out of scope, but still useful? Could add section 2.4. Let's add after 2.3. But the document has Section 3 Recommended Approach etc. We can insert 2.4 before 2.3 or after. Better after 2.3. Need old text to match unique.

At end of 2.3: "SensorHistory... not ambient." Then add 2.4.

Also update Section 3.1 Primary target device to say fēnix 3 and depth capable fēnix 5. But maybe not necessary because scope already says fēnix 3.

Edit 5: Section 6 risks add a row about fēnix 5 not dive-rated and 10 ATM. And fēnix 6/7/8 cap.

But not all needed. The minimum required per summary: update docs to clearly state hardware-vs-API distinction, fēnix 5 recommendation, warning fēnix 6 etc cap, reiterate fēnix 3 app remains surface. We'll do concise targeted updates.

Now README.md. Update Important note. Maybe replace paragraph:

Old:
> Important: This app does not measure depth. The fēnix 3 does not expose raw ambient pressure through the Connect IQ API, so accurate depth measurement is impossible. For depth logging you need a newer device such as a fēnix 5+, a Descent, or a Venu 2 with an ANT+ capable chest strap.

New:
> Important: This app does **not** measure depth. The fēnix 3 hardware measures ambient pressure and the native "Ambient Press." data field displays it, but the Connect IQ API on fēnix 3 (API Level 1.4.0) only exposes `Sensor.Info.pressure`, which is sea-level calibrated and auto-corrected for weather/altitude. A third-party app therefore cannot read the raw ambient pressure needed for depth.
> For depth logging on a fēnix watch, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Avoid the fēnix 6 / 7 / 8 / Tactix for depth-based freediving: reports say Garmin caps pressure readings underwater around 3 m to protect the Descent product line, even though the raw-pressure API is present.

Add a "Which fēnix for depth?" section maybe under What it does. But enough.

Maybe also update build prerequisites to mention SDK for fēnix 5 if porting? Not necessary.

Let's execute edits.

edit

- **Venu 2:** Out of scope. Its touchscreen is unusable underwater and the user’s HRM‑Tri is ANT+ only, which the Venu 2 does not support.
- **Depth-capable fēnix recommendation:** For a fēnix watch that can measure depth in a Connect IQ app, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Newer fēnix 6 / 7 / 8 / Tactix models support the API but reportedly cap pressure readings underwater around 3 m, making them unsuitable for freediving depth.
- **Water type:** Fresh/salt toggle retained for future extensibility; not used for depth on fēnix 3.

- **Venu 2:** Out of scope. Its touchscreen is unusable underwater and the user’s HRM‑Tri is ANT+ only, which the Venu 2 does not support.
- **Water type:** Fresh/salt toggle retained for future extensibility; not used for depth on fēnix 3.

| Watch | Can measure depth accurately? | Verdict |
|-------|------------------------------|---------|
| **Garmin fēnix 3 / fēnix 3 HR** | **No** (CIQ API 1.4 only; `Sensor.Info.pressure` is sea-level calibrated) | Breath-hold / surface HR only |
| **Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** (raw ambient pressure API, no reported underwater cap) | Recommended depth-capable fēnix |
| **Garmin fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth (reportedly cap pressure below ~3 m underwater) | Not recommended for depth measurement |
| **Garmin Venu 2** | API supports raw pressure, but **not suitable here** | Out of scope (no ANT+ for HRM‑Tri, poor wet touchscreen) |

**Key finding:** Depth calculation requires *raw ambient* barometric pressure. Garmin exposes this via `Activity.Info.rawAmbientPressure` starting at Connect IQ API Level 2.4.0. The fēnix 3 family is capped at API Level 1.4 and does **not** support this field. The only pressure value available on fēnix 3 (`Sensor.Info.pressure`) is sea-level calibrated and auto-corrected for weather/altitude, making depth computation unreliable.

**Hardware vs. API distinction:** The fēnix 3 physical sensor and the native watch data field *Ambient Press.* do measure ambient pressure, but the Connect IQ SDK does not expose that raw value to third-party apps on this device.

**Recommendation:** Use the fēnix 3 app for **surface breath-hold / HR logging only**. For a depth-measuring fēnix app, target the **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family. Avoid the fēnix 6 / 7 / 8 / Tactix for depth-based freediving because of the reported underwater ~3 m pressure cap.

| Watch | Can measure depth accurately? | Verdict |
|-------|------------------------------|---------|
| **Garmin Venu 2** | **Yes** (raw ambient pressure API available) | **Recommended primary target** |
| **Garmin fēnix 3 / fēnix 3 HR** | **No, not reliably** (only sea-level-calibrated pressure available) | Not suitable as a depth dive computer; usable only for surface logging / breath-hold timer |

**Key finding:** Depth calculation requires *raw ambient* barometric pressure. Garmin exposes this via `Activity.Info.rawAmbientPressure` starting at Connect IQ API Level 2.4.0. The fēnix 3 family is capped at API Level 1.4 and does **not** support this field. The only pressure value available on fēnix 3 (`Sensor.Info.pressure`) is sea-level calibrated and auto-corrected for weather/altitude, making depth computation unreliable.

**Recommendation:** Build for the **Venu 2** first. If desired, add a degraded fēnix 3 mode that uses altitude-derived depth (with heavy caveats) or limits fēnix 3 to static breath-hold / surface HR logging.

**Conclusion:** The fēnix 3 is a poor choice for a 30 m freediving depth app, despite its better water-resistance rating and buttons. It can still host a static breath-hold timer or log surface heart rate.

**Important distinction:** The fēnix 3 *hardware* does measure ambient pressure and the native watch UI can display it as *Ambient Press.*, but the Connect IQ API on fēnix 3 only returns `Sensor.Info.pressure`. That value is documented as mean sea-level pressure and is auto-corrected for weather and altitude, so it drifts back to the previous reading seconds after a rapid pressure change. A CIQ app on fēnix 3 therefore cannot use it for reliable depth.

- `SensorHistory.getPressureHistory()` — returns pressure history, but forum reports indicate it stores MSL pressure, not ambient.

### 2.4 Depth-capable fēnix models (future reference)

If you later want a fēnix watch that can run a depth-measuring freediving app, target one of these models:

| Model family | Connect IQ API | rawAmbientPressure | Notes |
|--------------|----------------|--------------------|-------|
| fēnix 5 / 5S / 5X | 3.1.x | Yes | Forum reports: accurate depth to ~40 m, no artificial underwater cap. |
| fēnix 5 Plus / 5S Plus / 5X Plus | 3.2.x / 3.3.x | Yes | Same pressure sensor behavior as the base fēnix 5 family. |
| fēnix 6 / 6S / 6X / 7 / 7S / 7X / 8 / Tactix | 3.2.x+ | Yes (API present) | **Not recommended for depth** — forum/developer reports say Garmin caps pressure readings underwater around 3 m, likely to protect the Descent dive-computer line. |
| fēnix 3 / fēnix 3 HR | 1.4.0 | **No** | Current project target; breath-hold / surface HR only. |

All of these watches are **10 ATM** (pressure equivalent to 100 m) or better, but Garmin explicitly rates them for surface swimming, not diving. Using any of them for repeated 30 m freediving is outside the stated use case and may void the warranty or cause damage.

---

## 3. Recommended Approach

- `SensorHistory.getPressureHistory()` — returns pressure history, but forum reports indicate it stores MSL pressure, not ambient.

---

## 3. Recommended Approach

| fēnix 3 depth is inaccurate | Certain | High | Do not rely on fēnix 3 for depth; limit to breath-hold / surface mode |
| fēnix 5 is not dive-rated despite 10 ATM | Certain | High | Use at own risk; understand warranty implications for repeated 30 m use |
| fēnix 6/7/8/Tactix pressure cap makes depth unusable | High (per forum reports) | High | Avoid these models for a depth-based freediving app |


Successfully replaced 5 block(s) in ~/code/garmin-freedive-app/IMPLEMENTATION_PLAN.md.

     ...
  11   2. **Heart-rate logging** — supports the user’s HRM‑Tri ANT+ chest strap (and fēnix 3 HR wrist HR as a fallback on surface).
  12   3. **FIT activity recording** — stores breath-hold duration and HR to a FIT file via `FitContributor`.
  13   4. **Sideloading instructions** — copy `.PRG` to `GARMIN/APPS/` over USB.
  14 - **Venu 2:** Out of scope. Its touchscreen is unusable underwater and the user’s HRM‑Tri is ANT+ only, which the Venu 2 does not support.
+ 15 - **Depth-capable fēnix recommendation:** For a fēnix watch that can measure depth in a Connect IQ app, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Newer fēnix 6 / 7 / 8 / Tactix models support the API but reportedly cap pressure readings underwater around 3 m, making them unsuitable for freediving depth.
  15 - **Water type:** Fresh/salt toggle retained for future extensibility; not used for depth on fēnix 3.
  16
  17 The original feasibility analysis below (§1–§3) is preserved for reference.
  18
     ...
  25 **Feasibility:**
  26
  27 | Watch | Can measure depth accurately? | Verdict |
  28 |-------|------------------------------|---------|
- 29 | **Garmin Venu 2** | **Yes** (raw ambient pressure API available) | **Recommended primary target** |
- 30 | **Garmin fēnix 3 / fēnix 3 HR** | **No, not reliably** (only sea-level-calibrated pressure available) | Not suitable as a depth dive computer; usable only for surface logging / breath-hold timer |
+ 30 | **Garmin fēnix 3 / fēnix 3 HR** | **No** (CIQ API 1.4 only; `Sensor.Info.pressure` is sea-level calibrated) | Breath-hold / surface HR only |
+ 31 | **Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** (raw ambient pressure API, no reported underwater cap) | Recommended depth-capable fēnix |
+ 32 | **Garmin fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth (reportedly cap pressure below ~3 m underwater) | Not recommended for depth measurement |
+ 33 | **Garmin Venu 2** | API supports raw pressure, but **not suitable here** | Out of scope (no ANT+ for HRM‑Tri, poor wet touchscreen) |
  31
  32 **Key finding:** Depth calculation requires *raw ambient* barometric pressure. Garmin exposes this via `Activity.Info.rawAmbientPressure` starting at Connect IQ API Level 2.4.0. The fēnix 3 family is capped at API Level 1.4 and does **not** support this field. The only pressure value available on fēnix 3 (`Sensor.Info.pressure`) is sea-level calibrated and auto-corrected for weather/altitude, making depth computation unreliable.
  33
- 34 **Recommendation:** Build for the **Venu 2** first. If desired, add a degraded fēnix 3 mode that uses altitude-derived depth (with heavy caveats) or limits fēnix 3 to static breath-hold / surface HR logging.
+ 37 **Hardware vs. API distinction:** The fēnix 3 physical sensor and the native watch data field *Ambient Press.* do measure ambient pressure, but the Connect IQ SDK does not expose that raw value to third-party apps on this device.
  35
+ 39 **Recommendation:** Use the fēnix 3 app for **surface breath-hold / HR logging only**. For a depth-measuring fēnix app, target the **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family. Avoid the fēnix 6 / 7 / 8 / Tactix for depth-based freediving because of the reported underwater ~3 m pressure cap.
+ 40
  36 ---
  37
  38 ## 2. Device/API Findings
  39
     ...
  73 - Historical depth data fields for fēnix 3 used `Sensor.Info.altitude` and reverse-calculated pressure; this is approximate and weather-sensitive.
  74
  75 **Conclusion:** The fēnix 3 is a poor choice for a 30 m freediving depth app, despite its better water-resistance rating and buttons. It can still host a static breath-hold timer or log surface heart rate.
  76
+ 82 **Important distinction:** The fēnix 3 *hardware* does measure ambient pressure and the native watch UI can display it as *Ambient Press.*, but the Connect IQ API on fēnix 3 only returns `Sensor.Info.pressure`. That value is documented as mean sea-level pressure and is auto-corrected for weather and altitude, so it drifts back to the previous reading seconds after a rapid pressure change. A CIQ app on fēnix 3 therefore cannot use it for reliable depth.
+ 83
  77 ### 2.3 Connect IQ API Context
  78
  79 - `Activity.Info.rawAmbientPressure` — raw, temperature-compensated sensor reading. Best for depth.
  80 - `Activity.Info.ambientPressure` — two-stage filtered/smoothed reading. Slightly slower response, less noise.
  81 - `Sensor.Info.pressure` — mean sea level pressure. **Not usable for depth on fēnix 3.**
  82 - `SensorHistory.getPressureHistory()` — returns pressure history, but forum reports indicate it stores MSL pressure, not ambient.
  83
+ 91 ### 2.4 Depth-capable fēnix models (future reference)
+ 92
+ 93 If you later want a fēnix watch that can run a depth-measuring freediving app, target one of these models:
+ 94
+ 95 | Model family | Connect IQ API | rawAmbientPressure | Notes |
+ 96 |--------------|----------------|--------------------|-------|
+ 97 | fēnix 5 / 5S / 5X | 3.1.x | Yes | Forum reports: accurate depth to ~40 m, no artificial underwater cap. |
+ 98 | fēnix 5 Plus / 5S Plus / 5X Plus | 3.2.x / 3.3.x | Yes | Same pressure sensor behavior as the base fēnix 5 family. |
+ 99 | fēnix 6 / 6S / 6X / 7 / 7S / 7X / 8 / Tactix | 3.2.x+ | Yes (API present) | **Not recommended for depth** — forum/developer reports say Garmin caps pressure readings underwater around 3 m, likely to protect the Descent dive-computer line. |
+100 | fēnix 3 / fēnix 3 HR | 1.4.0 | **No** | Current project target; breath-hold / surface HR only. |
+101
+102 All of these watches are **10 ATM** (pressure equivalent to 100 m) or better, but Garmin explicitly rates them for surface swimming, not diving. Using any of them for repeated 30 m freediving is outside the stated use case and may void the warranty or cause damage.
+103
  84 ---
  85
  86 ## 3. Recommended Approach
  87
     ...
 238 | Pressure sensor drift / weather changes | Low-Medium | Medium | Calibrate at surface before each dive; allow manual override; re-calibrate between dives |
 239 | App crash due to memory limits | Medium | Medium | Keep data structures small; avoid storing per-second arrays in memory; rely on FIT recording |
 240 | Garmin policy / sideload friction | Low | Low | Never submit to store; use private source repo; build `.PRG` manually |
 241 | fēnix 3 depth is inaccurate | Certain | High | Do not rely on fēnix 3 for depth; limit to breath-hold / surface mode |
+262 | fēnix 5 is not dive-rated despite 10 ATM | Certain | High | Use at own risk; understand warranty implications for repeated 30 m use |
+263 | fēnix 6/7/8/Tactix pressure cap makes depth unusable | High (per forum reports) | High | Avoid these models for a depth-based freediving app |
 242 | Battery drain on AMOLED Venu 2 | Medium | Low | Keep screen on only during dive; use low-brightness mode; end session promptly |
 243
 244 ---
 245
     ...

--- ~/code/garmin-freedive-app/IMPLEMENTATION_PLAN.md
+++ ~/code/garmin-freedive-app/IMPLEMENTATION_PLAN.md
@@ -11,8 +11,9 @@
   2. **Heart-rate logging** — supports the user’s HRM‑Tri ANT+ chest strap (and fēnix 3 HR wrist HR as a fallback on surface).
   3. **FIT activity recording** — stores breath-hold duration and HR to a FIT file via `FitContributor`.
   4. **Sideloading instructions** — copy `.PRG` to `GARMIN/APPS/` over USB.
 - **Venu 2:** Out of scope. Its touchscreen is unusable underwater and the user’s HRM‑Tri is ANT+ only, which the Venu 2 does not support.
+- **Depth-capable fēnix recommendation:** For a fēnix watch that can measure depth in a Connect IQ app, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Newer fēnix 6 / 7 / 8 / Tactix models support the API but reportedly cap pressure readings underwater around 3 m, making them unsuitable for freediving depth.
 - **Water type:** Fresh/salt toggle retained for future extensibility; not used for depth on fēnix 3.

 The original feasibility analysis below (§1–§3) is preserved for reference.

@@ -25,15 +26,19 @@
 **Feasibility:**

 | Watch | Can measure depth accurately? | Verdict |
 |-------|------------------------------|---------|
-| **Garmin Venu 2** | **Yes** (raw ambient pressure API available) | **Recommended primary target** |
-| **Garmin fēnix 3 / fēnix 3 HR** | **No, not reliably** (only sea-level-calibrated pressure available) | Not suitable as a depth dive computer; usable only for surface logging / breath-hold timer |
+| **Garmin fēnix 3 / fēnix 3 HR** | **No** (CIQ API 1.4 only; `Sensor.Info.pressure` is sea-level calibrated) | Breath-hold / surface HR only |
+| **Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** (raw ambient pressure API, no reported underwater cap) | Recommended depth-capable fēnix |
+| **Garmin fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth (reportedly cap pressure below ~3 m underwater) | Not recommended for depth measurement |
+| **Garmin Venu 2** | API supports raw pressure, but **not suitable here** | Out of scope (no ANT+ for HRM‑Tri, poor wet touchscreen) |

 **Key finding:** Depth calculation requires *raw ambient* barometric pressure. Garmin exposes this via `Activity.Info.rawAmbientPressure` starting at Connect IQ API Level 2.4.0. The fēnix 3 family is capped at API Level 1.4 and does **not** support this field. The only pressure value available on fēnix 3 (`Sensor.Info.pressure`) is sea-level calibrated and auto-corrected for weather/altitude, making depth computation unreliable.

-**Recommendation:** Build for the **Venu 2** first. If desired, add a degraded fēnix 3 mode that uses altitude-derived depth (with heavy caveats) or limits fēnix 3 to static breath-hold / surface HR logging.
+**Hardware vs. API distinction:** The fēnix 3 physical sensor and the native watch data field *Ambient Press.* do measure ambient pressure, but the Connect IQ SDK does not expose that raw value to third-party apps on this device.

+**Recommendation:** Use the fēnix 3 app for **surface breath-hold / HR logging only**. For a depth-measuring fēnix app, target the **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family. Avoid the fēnix 6 / 7 / 8 / Tactix for depth-based freediving because of the reported underwater ~3 m pressure cap.
+
 ---

 ## 2. Device/API Findings

@@ -73,15 +78,30 @@
 - Historical depth data fields for fēnix 3 used `Sensor.Info.altitude` and reverse-calculated pressure; this is approximate and weather-sensitive.

 **Conclusion:** The fēnix 3 is a poor choice for a 30 m freediving depth app, despite its better water-resistance rating and buttons. It can still host a static breath-hold timer or log surface heart rate.

+**Important distinction:** The fēnix 3 *hardware* does measure ambient pressure and the native watch UI can display it as *Ambient Press.*, but the Connect IQ API on fēnix 3 only returns `Sensor.Info.pressure`. That value is documented as mean sea-level pressure and is auto-corrected for weather and altitude, so it drifts back to the previous reading seconds after a rapid pressure change. A CIQ app on fēnix 3 therefore cannot use it for reliable depth.
+
 ### 2.3 Connect IQ API Context

 - `Activity.Info.rawAmbientPressure` — raw, temperature-compensated sensor reading. Best for depth.
 - `Activity.Info.ambientPressure` — two-stage filtered/smoothed reading. Slightly slower response, less noise.
 - `Sensor.Info.pressure` — mean sea level pressure. **Not usable for depth on fēnix 3.**
 - `SensorHistory.getPressureHistory()` — returns pressure history, but forum reports indicate it stores MSL pressure, not ambient.

+### 2.4 Depth-capable fēnix models (future reference)
+
+If you later want a fēnix watch that can run a depth-measuring freediving app, target one of these models:
+
+| Model family | Connect IQ API | rawAmbientPressure | Notes |
+|--------------|----------------|--------------------|-------|
+| fēnix 5 / 5S / 5X | 3.1.x | Yes | Forum reports: accurate depth to ~40 m, no artificial underwater cap. |
+| fēnix 5 Plus / 5S Plus / 5X Plus | 3.2.x / 3.3.x | Yes | Same pressure sensor behavior as the base fēnix 5 family. |
+| fēnix 6 / 6S / 6X / 7 / 7S / 7X / 8 / Tactix | 3.2.x+ | Yes (API present) | **Not recommended for depth** — forum/developer reports say Garmin caps pressure readings underwater around 3 m, likely to protect the Descent dive-computer line. |
+| fēnix 3 / fēnix 3 HR | 1.4.0 | **No** | Current project target; breath-hold / surface HR only. |
+
+All of these watches are **10 ATM** (pressure equivalent to 100 m) or better, but Garmin explicitly rates them for surface swimming, not diving. Using any of them for repeated 30 m freediving is outside the stated use case and may void the warranty or cause damage.
+
 ---

 ## 3. Recommended Approach

@@ -238,8 +258,10 @@
 | Pressure sensor drift / weather changes | Low-Medium | Medium | Calibrate at surface before each dive; allow manual override; re-calibrate between dives |
 | App crash due to memory limits | Medium | Medium | Keep data structures small; avoid storing per-second arrays in memory; rely on FIT recording |
 | Garmin policy / sideload friction | Low | Low | Never submit to store; use private source repo; build `.PRG` manually |
 | fēnix 3 depth is inaccurate | Certain | High | Do not rely on fēnix 3 for depth; limit to breath-hold / surface mode |
+| fēnix 5 is not dive-rated despite 10 ATM | Certain | High | Use at own risk; understand warranty implications for repeated 30 m use |
+| fēnix 6/7/8/Tactix pressure cap makes depth unusable | High (per forum reports) | High | Avoid these models for a depth-based freediving app |
 | Battery drain on AMOLED Venu 2 | Medium | Low | Keep screen on only during dive; use low-brightness mode; end session promptly |

 ---


> **Important:** This app does **not** measure depth. The fēnix 3 *hardware* measures ambient pressure and the native *Ambient Press.* data field displays it, but the Connect IQ API on fēnix 3 (API Level 1.4.0) only exposes `Sensor.Info.pressure`. That value is **sea-level calibrated** and auto-corrected for weather/altitude, so a third-party app cannot read the raw ambient pressure needed for depth.
>
> **For depth logging on a fēnix watch**, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Avoid the **fēnix 6 / 7 / 8 / Tactix** for depth-based freediving: reports say Garmin caps pressure readings underwater around 3 m, even though the raw-pressure API is present.
>
> A Garmin **Descent** is the purpose-built alternative if you want a Garmin freediving computer.

> **Important:** This app does **not** measure depth. The fēnix 3 does not expose raw ambient pressure through the Connect IQ API, so accurate depth measurement is impossible. For depth logging you need a newer device such as a fēnix 5+, a Descent, or a Venu 2 with an ANT+ capable chest strap.

## What it does

- **Static breath-hold timer** with three phases:

## Which fēnix for depth?

If you later switch to a depth-capable fēnix, the current app can be ported to use `Activity.Info.rawAmbientPressure`.

| Watch family | Good for depth? | Why |
|--------------|-----------------|-----|
| **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** | `rawAmbientPressure` is exposed and reportedly not capped underwater. |
| **fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth | API is present, but pressure readings are reportedly capped below ~3 m underwater. |
| **fēnix 3 / fēnix 3 HR** | **No** | Only sea-level-calibrated `Sensor.Info.pressure` is available (this app). |

All are 10 ATM rated, but Garmin does not dive-rate them. Use for depth at your own risk.

---

## Build prerequisites


Successfully replaced 3 block(s) in ~/code/garmin-freedive-app/README.md.

   1 # Garmin fēnix 3 Freedive Breath App
   2
   3 A personal, sideloaded Connect IQ app for the **Garmin fēnix 3** and **fēnix 3 HR** that records static breath-hold sessions and heart rate to a FIT file.
   4
-  5 > **Important:** This app does **not** measure depth. The fēnix 3 does not expose raw ambient pressure through the Connect IQ API, so accurate depth measurement is impossible. For depth logging you need a newer device such as a fēnix 5+, a Descent, or a Venu 2 with an ANT+ capable chest strap.
+  5 > **Important:** This app does **not** measure depth. The fēnix 3 *hardware* measures ambient pressure and the native *Ambient Press.* data field displays it, but the Connect IQ API on fēnix 3 (API Level 1.4.0) only exposes `Sensor.Info.pressure`. That value is **sea-level calibrated** and auto-corrected for weather/altitude, so a third-party app cannot read the raw ambient pressure needed for depth.
+  6 >
+  7 > **For depth logging on a fēnix watch**, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Avoid the **fēnix 6 / 7 / 8 / Tactix** for depth-based freediving: reports say Garmin caps pressure readings underwater around 3 m, even though the raw-pressure API is present.
+  8 >
+  9 > A Garmin **Descent** is the purpose-built alternative if you want a Garmin freediving computer.
   6
   7 ---
   8
   9 ## What it does
     ...
  20 - **Physical-button control** — works with wet fingers and underwater.
  21
  22 ---
  23
+ 28 ## Which fēnix for depth?
+ 29
+ 30 If you later switch to a depth-capable fēnix, the current app can be ported to use `Activity.Info.rawAmbientPressure`.
+ 31
+ 32 | Watch family | Good for depth? | Why |
+ 33 |--------------|-----------------|-----|
+ 34 | **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** | `rawAmbientPressure` is exposed and reportedly not capped underwater. |
+ 35 | **fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth | API is present, but pressure readings are reportedly capped below ~3 m underwater. |
+ 36 | **fēnix 3 / fēnix 3 HR** | **No** | Only sea-level-calibrated `Sensor.Info.pressure` is available (this app). |
+ 37
+ 38 All are 10 ATM rated, but Garmin does not dive-rate them. Use for depth at your own risk.
+ 39
+ 40 ---
+ 41
  24 ## Build prerequisites
  25
  26 1. A modern computer with the **Connect IQ SDK** installed.
  27    - Download the SDK Manager from: https://developer.garmin.com/connect-iq/sdk/
     ...

--- ~/code/garmin-freedive-app/README.md
+++ ~/code/garmin-freedive-app/README.md
@@ -1,9 +1,13 @@
 # Garmin fēnix 3 Freedive Breath App

 A personal, sideloaded Connect IQ app for the **Garmin fēnix 3** and **fēnix 3 HR** that records static breath-hold sessions and heart rate to a FIT file.

-> **Important:** This app does **not** measure depth. The fēnix 3 does not expose raw ambient pressure through the Connect IQ API, so accurate depth measurement is impossible. For depth logging you need a newer device such as a fēnix 5+, a Descent, or a Venu 2 with an ANT+ capable chest strap.
+> **Important:** This app does **not** measure depth. The fēnix 3 *hardware* measures ambient pressure and the native *Ambient Press.* data field displays it, but the Connect IQ API on fēnix 3 (API Level 1.4.0) only exposes `Sensor.Info.pressure`. That value is **sea-level calibrated** and auto-corrected for weather/altitude, so a third-party app cannot read the raw ambient pressure needed for depth.
+>
+> **For depth logging on a fēnix watch**, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Avoid the **fēnix 6 / 7 / 8 / Tactix** for depth-based freediving: reports say Garmin caps pressure readings underwater around 3 m, even though the raw-pressure API is present.
+>
+> A Garmin **Descent** is the purpose-built alternative if you want a Garmin freediving computer.

 ---

 ## What it does
@@ -20,8 +24,22 @@
 - **Physical-button control** — works with wet fingers and underwater.

 ---

+## Which fēnix for depth?
+
+If you later switch to a depth-capable fēnix, the current app can be ported to use `Activity.Info.rawAmbientPressure`.
+
+| Watch family | Good for depth? | Why |
+|--------------|-----------------|-----|
+| **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** | `rawAmbientPressure` is exposed and reportedly not capped underwater. |
+| **fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth | API is present, but pressure readings are reportedly capped below ~3 m underwater. |
+| **fēnix 3 / fēnix 3 HR** | **No** | Only sea-level-calibrated `Sensor.Info.pressure` is available (this app). |
+
+All are 10 ATM rated, but Garmin does not dive-rate them. Use for depth at your own risk.
+
+---
+
 ## Build prerequisites

 1. A modern computer with the **Connect IQ SDK** installed.
    - Download the SDK Manager from: https://developer.garmin.com/connect-iq/sdk/


Need check edits didn't mess. Read relevant sections.


# Garmin fēnix 3 Freedive Breath App

A personal, sideloaded Connect IQ app for the **Garmin fēnix 3** and **fēnix 3 HR** that records static breath-hold sessions and heart rate to a FIT file.

> **Important:** This app does **not** measure depth. The fēnix 3 *hardware* measures ambient pressure and the native *Ambient Press.* data field displays it, but the Connect IQ API on fēnix 3 (API Level 1.4.0) only exposes `Sensor.Info.pressure`. That value is **sea-level calibrated** and auto-corrected for weather/altitude, so a third-party app cannot read the raw ambient pressure needed for depth.
>
> **For depth logging on a fēnix watch**, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Avoid the **fēnix 6 / 7 / 8 / Tactix** for depth-based freediving: reports say Garmin caps pressure readings underwater around 3 m, even though the raw-pressure API is present.
>
> A Garmin **Descent** is the purpose-built alternative if you want a Garmin freediving computer.

---

## What it does

- **Static breath-hold timer** with three phases:
  1. **Prep breathe** (default 2 minutes, configurable in code)
  2. **Breath-hold** (counts up until you press START)
  3. **Recovery** (default 2 minutes, configurable in code)
- **Contraction counter** during the hold phase (press UP or DOWN).
- **Heart-rate display** from the HRM‑Tri / HRM‑Swim ANT+ chest strap or fēnix 3 HR wrist sensor.
- **FIT recording** of the session with custom fields:
  - `hold_time_s` — best breath-hold time of the session
  - `last_hr_bpm` — last sampled heart rate
- **Physical-button control** — works with wet fingers and underwater.

---

## Which fēnix for depth?

If you later switch to a depth-capable fēnix, the current app can be ported to use `Activity.Info.rawAmbientPressure`.

| Watch family | Good for depth? | Why |
|--------------|-----------------|-----|
| **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** | `rawAmbientPressure` is exposed and reportedly not capped underwater. |
| **fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth | API is present, but pressure readings are reportedly capped below ~3 m underwater. |
| **fēnix 3 / fēnix 3 HR** | **No** | Only sea-level-calibrated `Sensor.Info.pressure` is available (this app). |

All are 10 ATM rated, but Garmin does not dive-rate them. Use for depth at your own risk.

---

## Build prerequisites

1. A modern computer with the **Connect IQ SDK** installed.
   - Download the SDK Manager from: https://developer.garmin.com/connect-iq/sdk/
   - Use the SDK Manager to download the latest SDK plus the **fēnix 3** and **fēnix 3 HR** device files.
2. **Visual Studio Code** with the official **Monkey C** extension (recommended), or the Eclipse plugin.
3. A USB cable to connect the watch to your computer.

---

## Build steps

1. Open this folder in VS Code.
2. Run **Monkey C: Build Current Project** (or **Monkey C: Export Project**) from the command palette.
3. Select the `fenix3` or `fenix3_hr` target.
4. The compiler produces a `.PRG` file, typically under:
   - `bin/<build-name>.prg` when building for a single device, or
   - A `.iq` zip when exporting for multiple devices.

[88 more lines in file. Use offset=61 to continue.]


# Garmin Connect IQ Freediving App — Implementation Plan

## 0. Revised Scope (after user decisions)

Based on the follow-up questions, the project scope is now:

- **Primary target device:** **Garmin fēnix 3 / fēnix 3 HR** (API Level 1.4.0).
- **Depth measurement:** **Not implemented.** The fēnix 3 cannot expose raw ambient pressure, so depth is unreliable. The app is therefore **not a dive computer**.
- **Core features for fēnix 3:**
  1. **Static breath-hold timer** — prep breathe, breath-hold, and recovery phases.
  2. **Heart-rate logging** — supports the user’s HRM‑Tri ANT+ chest strap (and fēnix 3 HR wrist HR as a fallback on surface).
  3. **FIT activity recording** — stores breath-hold duration and HR to a FIT file via `FitContributor`.
  4. **Sideloading instructions** — copy `.PRG` to `GARMIN/APPS/` over USB.
- **Venu 2:** Out of scope. Its touchscreen is unusable underwater and the user’s HRM‑Tri is ANT+ only, which the Venu 2 does not support.
- **Depth-capable fēnix recommendation:** For a fēnix watch that can measure depth in a Connect IQ app, use a **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus**. These support `Activity.Info.rawAmbientPressure` (API Level 2.4.0+) and forum reports indicate they do not artificially cap underwater pressure. Newer fēnix 6 / 7 / 8 / Tactix models support the API but reportedly cap pressure readings underwater around 3 m, making them unsuitable for freediving depth.
- **Water type:** Fresh/salt toggle retained for future extensibility; not used for depth on fēnix 3.

The original feasibility analysis below (§1–§3) is preserved for reference.

---

## 1. Executive Summary / Feasibility Verdict

**Goal:** Build a personal, sideloaded Garmin Connect IQ app that records freedive sessions (dive time, depth, heart rate, static breath-hold) up to ~30 m on watches the user already owns.

**Feasibility:**

| Watch | Can measure depth accurately? | Verdict |
|-------|------------------------------|---------|
| **Garmin fēnix 3 / fēnix 3 HR** | **No** (CIQ API 1.4 only; `Sensor.Info.pressure` is sea-level calibrated) | Breath-hold / surface HR only |
| **Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** | **Yes** (raw ambient pressure API, no reported underwater cap) | Recommended depth-capable fēnix |
| **Garmin fēnix 6 / 7 / 8 / Tactix** | **No** for freediving depth (reportedly cap pressure below ~3 m underwater) | Not recommended for depth measurement |
| **Garmin Venu 2** | API supports raw pressure, but **not suitable here** | Out of scope (no ANT+ for HRM‑Tri, poor wet touchscreen) |

**Key finding:** Depth calculation requires *raw ambient* barometric pressure. Garmin exposes this via `Activity.Info.rawAmbientPressure` starting at Connect IQ API Level 2.4.0. The fēnix 3 family is capped at API Level 1.4 and does **not** support this field. The only pressure value available on fēnix 3 (`Sensor.Info.pressure`) is sea-level calibrated and auto-corrected for weather/altitude, making depth computation unreliable.

**Hardware vs. API distinction:** The fēnix 3 physical sensor and the native watch data field *Ambient Press.* do measure ambient pressure, but the Connect IQ SDK does not expose that raw value to third-party apps on this device.

**Recommendation:** Use the fēnix 3 app for **surface breath-hold / HR logging only**. For a depth-measuring fēnix app, target the **fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus** family. Avoid the fēnix 6 / 7 / 8 / Tactix for depth-based freediving because of the reported underwater ~3 m pressure cap.

---

## 2. Device/API Findings

### 2.1 Venu 2

| Item | Detail |
|------|--------|
| Connect IQ API Level | **5.0.0** (can be targeted with modern SDK; device files available) |
| Raw ambient pressure | **Supported** — `Activity.Info.rawAmbientPressure` (API 2.4.0+) |
| Smoothed ambient pressure | **Supported** — `Activity.Info.ambientPressure` (API 2.4.0+) |
| Activity recording / FIT export | **Supported** — `Toybox.ActivityRecording` + `Toybox.FitContributor` |
| Heart rate | Built-in optical wrist HR (unreliable underwater) |
| External ANT+ HR chest strap | Supported (HRM-Swim / HRM-Tri recommended for underwater HR) |
| Water resistance | 5 ATM (~50 m pressure equivalent), "Swim" rating; **not dive-rated by Garmin** |
| Controls | Touchscreen + two buttons; touchscreen may be unresponsive when wet / underwater |
| Screen | 416 × 416 AMOLED; bright, but higher battery draw than MIP displays |

**Depth accuracy on Venu 2:** Good enough for recreational freediving to 30 m *if* you calibrate at the surface and account for fresh vs. salt water density. The raw pressure sensor is temperature-compensated and read directly from the internal sensor, so it responds to hydrostatic pressure changes.

### 2.2 fēnix 3 / fēnix 3 HR

| Item | Detail |
|------|--------|
| Connect IQ API Level | **1.4.0** (max) |
| Raw ambient pressure | **Not supported** — `rawAmbientPressure` requires API 2.4.0+ |
| Available pressure value | `Sensor.Info.pressure` only; returns **sea-level-calibrated barometric pressure** |
| Activity recording / FIT export | Supported at API 1.4 (`ActivityRecording`, `FitContributor` since 1.3.0) |
| Heart rate | fēnix 3: external ANT+ chest strap required; fēnix 3 HR: built-in optical HR (still unreliable underwater) |
| Water resistance | 10 ATM (~100 m pressure equivalent), but Garmin explicitly limits it to surface swimming |
| Controls | Five physical buttons — excellent underwater control |
| Screen | 218 × 218 Memory-In-Pixel (16 colors), sunlight-readable, low power |

**Why fēnix 3 cannot give accurate depth:**
- `Sensor.Info.pressure` is calibrated to mean sea level using the current altimeter setting and GPS altitude.
- Garmin's altimeter algorithm deliberately filters out rapid pressure changes to distinguish weather from altitude changes.
- Underwater pressure changes are interpreted as altitude changes with significant lag and may be rejected/flattened.
- Historical depth data fields for fēnix 3 used `Sensor.Info.altitude` and reverse-calculated pressure; this is approximate and weather-sensitive.

**Conclusion:** The fēnix 3 is a poor choice for a 30 m freediving depth app, despite its better water-resistance rating and buttons. It can still host a static breath-hold timer or log surface heart rate.

**Important distinction:** The fēnix 3 *hardware* does measure ambient pressure and the native watch UI can display it as *Ambient Press.*, but the Connect IQ API on fēnix 3 only returns `Sensor.Info.pressure`. That value is documented as mean sea-level pressure and is auto-corrected for weather and altitude, so it drifts back to the previous reading seconds after a rapid pressure change. A CIQ app on fēnix 3 therefore cannot use it for reliable depth.

### 2.3 Connect IQ API Context

- `Activity.Info.rawAmbientPressure` — raw, temperature-compensated sensor reading. Best for depth.
- `Activity.Info.ambientPressure` — two-stage filtered/smoothed reading. Slightly slower response, less noise.
- `Sensor.Info.pressure` — mean sea level pressure. **Not usable for depth on fēnix 3.**
- `SensorHistory.getPressureHistory()` — returns pressure history, but forum reports indicate it stores MSL pressure, not ambient.

### 2.4 Depth-capable fēnix models (future reference)

If you later want a fēnix watch that can run a depth-measuring freediving app, target one of these models:

| Model family | Connect IQ API | rawAmbientPressure | Notes |
|--------------|----------------|--------------------|-------|
| fēnix 5 / 5S / 5X | 3.1.x | Yes | Forum reports: accurate depth to ~40 m, no artificial underwater cap. |
| fēnix 5 Plus / 5S Plus / 5X Plus | 3.2.x / 3.3.x | Yes | Same pressure sensor behavior as the base fēnix 5 family. |
| fēnix 6 / 6S / 6X / 7 / 7S / 7X / 8 / Tactix | 3.2.x+ | Yes (API present) | **Not recommended for depth** — forum/developer reports say Garmin caps pressure readings underwater around 3 m, likely to protect the Descent dive-computer line. |
| fēnix 3 / fēnix 3 HR | 1.4.0 | **No** | Current project target; breath-hold / surface HR only. |

All of these watches are **10 ATM** (pressure equivalent to 100 m) or better, but Garmin explicitly rates them for surface swimming, not diving. Using any of them for repeated 30 m freediving is outside the stated use case and may void the warranty or cause damage.

---

## 3. Recommended Approach

### 3.1 Primary Target Device

**Garmin Venu 2** (device ID `venu2` in the SDK).

[253 more lines in file. Use offset=111 to continue.]


We see Section 3.1 still says Venu 2 primary target. Should update to fēnix 3, since scope changed. Also Section 2.1 Venu 2 still primary. We can leave original analysis but maybe update 3.1 to reflect current target. The summary said update docs to reiterate current fēnix 3 app remains breath-hold. So let's edit Section 3.1 to be Primary Target Device = Garmin fēnix 3 for current app; depth capable fēnix 5 for depth. Also perhaps Section 3.2 still relevant. Let's edit.

Also maybe Section 5 Phase 0 mention fēnix 5. But okay.

Let's read more of section 3.1 to edit.


**Why:** It is the only one of the two watches that exposes raw ambient pressure, which is a hard requirement for accurate depth to 30 m.

### 3.2 App Type

**Watch App** with `ActivityRecording` module.

- A **Data Field** cannot call `Sensor` methods (app crash) and cannot control recording.
- A **Watch App** can:
  - Start/stop a FIT activity session.
  - Read `Activity.getActivityInfo().rawAmbientPressure` in the foreground.
  - Create custom FIT fields via `FitContributor` to store depth, dive time, etc.
  - Render a full-screen UI with large dive numbers and button/touch controls.

### 3.3 Recording Strategy

1. On app launch, enter a pre-dive screen.
2. User calibrates surface pressure (auto-capture current `rawAmbientPressure` as `P_surface`).
3. Start `ActivityRecording` session with sport `Activity.SPORT_GENERIC` or `Activity.SPORT_SWIMMING` and an appropriate sub-sport.
4. Every second (or on sensor update), compute:
   - `depth = (P - P_surface) / (ρ · g)`
   - Update a `FitContributor` field for depth in the FIT `RECORD` message.
   - Update max depth, dive time, surface interval.
5. Stop/save session when user finishes.

### 3.4 Heart Rate Strategy

- **Surface / breath-hold mode:** Use wrist optical HR (`Sensor.Info.heartRate` or `ActivityRecording`'s built-in HR).
- **Underwater dive mode:** Wrist optical HR does not work reliably in water. Recommend an **ANT+ chest strap** such as Garmin HRM-Swim or HRM-Tri, which store HR during the dive and download it at the surface.
- The app should enable `SENSOR_HEARTRATE` and pair with the chest strap if available.

### 3.5 Distribution

- **Sideload only.** Garmin prohibits scuba/free-diving apps in the Connect IQ Store.
- Build `.PRG` and copy to `GARMIN/APPS/` over USB.
- Optionally host source on a private GitHub repo; the user builds/sideloads themselves.

---

## 4. Depth Calculation

[213 more lines in file. Use offset=151 to continue.]


Need edit Section 3.1 to reflect current primary target fēnix 3, and note future depth fēnix 5. Let's do exact replacement.

### 3.1 Primary Target Device

**Current app: Garmin fēnix 3 / fēnix 3 HR** (device IDs `fenix3` and `fenix3_hr` in the SDK).

**Why:** This is the watch the user already owns. Its Connect IQ API is capped at 1.4.0, so the app is intentionally limited to **surface breath-hold / heart-rate logging** with no depth measurement.

**Depth-capable fēnix target (future): Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus.** These expose `Activity.Info.rawAmbientPressure` and are the recommended fēnix family for a freediving depth app. Avoid fēnix 6 / 7 / 8 / Tactix for depth because of the reported underwater ~3 m pressure cap.

### 3.1 Primary Target Device

**Garmin Venu 2** (device ID `venu2` in the SDK).

**Why:** It is the only one of the two watches that exposes raw ambient pressure, which is a hard requirement for accurate depth to 30 m.


Successfully replaced 1 block(s) in ~/code/garmin-freedive-app/IMPLEMENTATION_PLAN.md.

     ...
 106 ## 3. Recommended Approach
 107
 108 ### 3.1 Primary Target Device
 109
-110 **Garmin Venu 2** (device ID `venu2` in the SDK).
+110 **Current app: Garmin fēnix 3 / fēnix 3 HR** (device IDs `fenix3` and `fenix3_hr` in the SDK).
 111
-112 **Why:** It is the only one of the two watches that exposes raw ambient pressure, which is a hard requirement for accurate depth to 30 m.
+112 **Why:** This is the watch the user already owns. Its Connect IQ API is capped at 1.4.0, so the app is intentionally limited to **surface breath-hold / heart-rate logging** with no depth measurement.
 113
+114 **Depth-capable fēnix target (future): Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus.** These expose `Activity.Info.rawAmbientPressure` and are the recommended fēnix family for a freediving depth app. Avoid fēnix 6 / 7 / 8 / Tactix for depth because of the reported underwater ~3 m pressure cap.
+115
 114 ### 3.2 App Type
 115
 116 **Watch App** with `ActivityRecording` module.
 117
     ...

--- ~/code/garmin-freedive-app/IMPLEMENTATION_PLAN.md
+++ ~/code/garmin-freedive-app/IMPLEMENTATION_PLAN.md
@@ -106,12 +106,14 @@
 ## 3. Recommended Approach

 ### 3.1 Primary Target Device

-**Garmin Venu 2** (device ID `venu2` in the SDK).
+**Current app: Garmin fēnix 3 / fēnix 3 HR** (device IDs `fenix3` and `fenix3_hr` in the SDK).

-**Why:** It is the only one of the two watches that exposes raw ambient pressure, which is a hard requirement for accurate depth to 30 m.
+**Why:** This is the watch the user already owns. Its Connect IQ API is capped at 1.4.0, so the app is intentionally limited to **surface breath-hold / heart-rate logging** with no depth measurement.

+**Depth-capable fēnix target (future): Garmin fēnix 5 / 5S / 5X / 5 Plus / 5S Plus / 5X Plus.** These expose `Activity.Info.rawAmbientPressure` and are the recommended fēnix family for a freediving depth app. Avoid fēnix 6 / 7 / 8 / Tactix for depth because of the reported underwater ~3 m pressure cap.
+
 ### 3.2 App Type

 **Watch App** with `ActivityRecording` module.


Now maybe answer user. Also maybe we should build/verify code? The summary next steps mention optionally build and sideload current fēnix 3 breath-hold app; fix build errors. Should we attempt build? Might be beneficial. The user asked only question, but we are in coding assistant harness. The summary includes in progress and next steps. Since we're resuming, maybe also build the app. We can run build.sh? Let's inspect build.sh and source files to ensure compile correctness. The summary says implemented. We can attempt to compile if SDK installed. But maybe SDK not installed. Let's check.

bash

which monkeyc || echo "monkeyc not found"; which connectiq || echo "connectiq not found"; ls ~/.Garmin/ConnectIQ* 2>/dev/null || true


#!/bin/bash
# Build script for the fēnix 3 Freedive Breath app.
#
# Prerequisites:
#   1. Install the Connect IQ SDK Manager (https://developer.garmin.com/connect-iq/sdk/).
#   2. Use the SDK Manager to download the latest SDK and the fēnix 3 / fēnix 3 HR device files.
#   3. Make sure ~/.Garmin/ConnectIQ/current-sdk.cfg points to the active SDK.
#   4. Java 11+ must be installed.

set -e

SDK_CFG="$HOME/.Garmin/ConnectIQ/current-sdk.cfg"

if [ ! -f "$SDK_CFG" ]; then
    echo "Connect IQ SDK not found."
    echo "Install the SDK Manager, download an SDK + devices, then try again."
    exit 1
fi

SDK=$(cat "$SDK_CFG")
DEV_KEY="${DEV_KEY:-developer_key.der}"

# Generate a self-signed developer key if the user does not have one.
if [ ! -f "$DEV_KEY" ]; then
    echo "Generating a new developer key ($DEV_KEY)..."
    openssl genrsa -out developer_key.pem 4096
    openssl pkcs8 -topk8 -inform PEM -outform DER -in developer_key.pem -out "$DEV_KEY" -nocrypt
fi

mkdir -p bin

echo "Building for fēnix 3..."
"$SDK/bin/monkeyc" \
    -d fenix3 \
    -f monkey.jungle \
    -o bin/FreediveBreath_fenix3.prg \
    -y "$DEV_KEY"

echo "Built: bin/FreediveBreath_fenix3.prg"

echo "Building for fēnix 3 HR..."
"$SDK/bin/monkeyc" \
    -d fenix3_hr \
    -f monkey.jungle \
    -o bin/FreediveBreath_fenix3_hr.prg \
    -y "$DEV_KEY"

echo "Built: bin/FreediveBreath_fenix3_hr.prg"

echo ""
echo "To sideload, copy the .PRG for your watch to GARMIN/APPS/ over USB."


which: no monkeyc in (/home/liph/.pi/agent/bin:/home/liph/.opencode/bin:/home/liph/.local/bin:/home/liph/.pyenv/shims:/home/liph/.local/share/nvim/mason/bin:/home/liph/.tmuxifier/bin:/home/liph/.local/share/zinit/polaris/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl)
monkeyc not found
which: no connectiq in (/home/liph/.pi/agent/bin:/home/liph/.opencode/bin:/home/liph/.local/bin:/home/liph/.pyenv/shims:/home/liph/.local/share/nvim/mason/bin:/home/liph/.tmuxifier/bin:/home/liph/.local/share/zinit/polaris/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl)
connectiq not found


SDK not installed, can't build. Could maybe check code for issues. Let's read source quickly to see any obvious syntax issues.


using Toybox.Application;
using Toybox.WatchUi;
using Toybox.Sensor;

class BreathHoldApp extends Application.AppBase {

    function initialize() {
        AppBase.initialize();
    }

    function onStart(state) {
        // Enable the ANT+/on-board heart rate sensor.
        if (Sensor has :setEnabledSensors) {
            Sensor.setEnabledSensors([Sensor.SENSOR_HEARTRATE]);
        }
    }

    function onStop(state) {
        // Nothing to clean up explicitly.
    }

    function getInitialView() {
        var view = new BreathHoldView();
        var delegate = new BreathHoldDelegate(view);
        return [view, delegate];
    }
}


using Toybox.WatchUi;

class BreathHoldDelegate extends WatchUi.InputDelegate {

    var view;

    function initialize(v) {
        InputDelegate.initialize();
        view = v;
    }

    function onKey(keyEvent) {
        var key = keyEvent.getKey();

        if (key == WatchUi.KEY_START || key == WatchUi.KEY_ENTER) {
            view.onStartButton();
            return true;
        }

        if (key == WatchUi.KEY_UP || key == WatchUi.KEY_DOWN) {
            view.onContractionButton();
            return true;
        }

        if (key == WatchUi.KEY_ESC || key == WatchUi.KEY_LAP) {
            if (view.state == view.STATE_IDLE) {
                System.exit();
            } else {
                view.discardSession();
                view.state = view.STATE_IDLE;
                WatchUi.requestUpdate();
            }
            return true;
        }

        return false;
    }
}


using Toybox.WatchUi;
using Toybox.Graphics;
using Toybox.System;
using Toybox.Lang;
using Toybox.Timer;
using Toybox.ActivityRecording;
using Toybox.FitContributor;
using Toybox.Sensor;

class BreathHoldView extends WatchUi.View {

    // App states
    const STATE_IDLE     = 0;
    const STATE_PREP     = 1;
    const STATE_HOLD     = 2;
    const STATE_RECOVERY = 3;
    const STATE_DONE     = 4;

    var state = STATE_IDLE;
    var timer = null;

    // FIT session
    var session = null;
    var holdField = null;
    var hrField = null;

    // Breath-hold configuration (seconds)
    var prepTime = 120;
    var recoveryTime = 120;

    // Runtime counters
    var currentTime = 0;
    var maxHoldTime = 0;
    var contractionCount = 0;
    var lastHeartRate = null;

    function initialize() {
        View.initialize();
    }

    function onShow() {
        timer = new Timer.Timer();
    }

    function onHide() {
        if (timer != null) {
            timer.stop();
        }
    }

    function onUpdate(dc) {
        // Clear screen
        dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);
        dc.clear();
        dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);

        var cx = dc.getWidth() / 2;
        var y = 16;

        var statusText = "";
        var timeText = "";
        var infoText = "";

        if (state == STATE_IDLE) {
            statusText = "FREEDIVE";
            timeText = "Press START";
            infoText = "Back = exit";
        } else if (state == STATE_PREP) {
            statusText = "PREP BREATHE";
            timeText = formatTime(prepTime - currentTime);
            infoText = "START = skip to hold";
        } else if (state == STATE_HOLD) {
            statusText = "HOLD";
            timeText = formatTime(currentTime);
            infoText = "Contractions: " + contractionCount;
        } else if (state == STATE_RECOVERY) {
            statusText = "RECOVERY";
            timeText = formatTime(recoveryTime - currentTime);
            infoText = "START = finish";
        } else if (state == STATE_DONE) {
            statusText = "SAVED";
            timeText = formatTime(maxHoldTime);
            infoText = "START = new session";
        }

        // Title
        dc.drawText(cx, y, Graphics.FONT_SMALL, statusText, Graphics.TEXT_JUSTIFY_CENTER);

        // Main timer
        dc.drawText(cx, y + 28, Graphics.FONT_NUMBER_MEDIUM, timeText, Graphics.TEXT_JUSTIFY_CENTER);

        // Heart rate
        var hrStr = "--";
        if (lastHeartRate != null) {
            hrStr = lastHeartRate.toString();
        }
        dc.drawText(cx, y + 88, Graphics.FONT_SMALL, "HR " + hrStr, Graphics.TEXT_JUSTIFY_CENTER);

        // Info / instruction line
        dc.drawText(cx, y + 115, Graphics.FONT_TINY, infoText, Graphics.TEXT_JUSTIFY_CENTER);
    }

    function formatTime(seconds) {
        if (seconds < 0) {
            seconds = 0;
        }
        var m = seconds / 60;
        var s = seconds % 60;
        return m.format("%02d") + ":" + s.format("%02d");
    }

    // Called once per second by the Timer
    function onTimer() {
        if (state == STATE_PREP) {
            currentTime++;
            if (currentTime >= prepTime) {
                state = STATE_HOLD;
                currentTime = 0;
            }
        } else if (state == STATE_HOLD) {
            currentTime++;
        } else if (state == STATE_RECOVERY) {
            currentTime++;
            if (currentTime >= recoveryTime) {
                finishSession();
                return;
            }
        }

        updateHeartRate();
        WatchUi.requestUpdate();
    }

    function updateHeartRate() {
        var info = Sensor.getInfo();
        if (info != null && info.heartRate != null) {
            lastHeartRate = info.heartRate;
        }
    }

    function startSession() {
        if (session == null || !session.isRecording()) {
            session = ActivityRecording.createSession({
                :name => "BreathHold",
                :sport => Activity.SPORT_GENERIC,
                :subSport => Activity.SUB_SPORT_GENERIC
            });

            holdField = session.createField(
                "hold_time_s",
                0,
                FitContributor.DATA_TYPE_UINT16,
                {:mesgType => Fit.MESG_TYPE_SESSION, :units => "s"}
            );

            hrField = session.createField(
                "last_hr_bpm",
                1,
                FitContributor.DATA_TYPE_UINT8,
                {:mesgType => Fit.MESG_TYPE_SESSION, :units => "bpm"}
            );

            session.start();
        }
    }

    function finishSession() {
        if (state == STATE_HOLD) {
            maxHoldTime = currentTime;
        }

        if (session != null && session.isRecording()) {
            if (holdField != null) {
                holdField.setData(maxHoldTime);
            }
            if (hrField != null && lastHeartRate != null) {
                hrField.setData(lastHeartRate);
            }
            session.stop();
            session.save();
        }

        if (timer != null) {
            timer.stop();
        }

        state = STATE_DONE;
        WatchUi.requestUpdate();
    }

    function discardSession() {
        if (session != null && session.isRecording()) {
            session.stop();
            session.discard();
        }
        session = null;
        if (timer != null) {
            timer.stop();
        }
        resetRunState();
    }

    function resetRunState() {
        state = STATE_IDLE;
        currentTime = 0;
        maxHoldTime = 0;
        contractionCount = 0;
        lastHeartRate = null;
    }

    // Button-driven state machine. The delegate calls this on START.
    function onStartButton() {
        if (state == STATE_IDLE) {
            startSession();
            state = STATE_PREP;
            currentTime = 0;
            contractionCount = 0;
            timer.start(method(:onTimer), 1000, true);
        } else if (state == STATE_PREP) {
            // Skip remaining prep and start the breath-hold.
            state = STATE_HOLD;
            currentTime = 0;
        } else if (state == STATE_HOLD) {
            // End the hold, move to recovery.
            maxHoldTime = currentTime;
            state = STATE_RECOVERY;
            currentTime = 0;
        } else if (state == STATE_RECOVERY) {
            // Finish early and save.
            finishSession();
            return;
        } else if (state == STATE_DONE) {
            // Reset and return to idle.
            resetRunState();
        }
        WatchUi.requestUpdate();
    }

    // UP/DOWN buttons: count contractions during the hold.
    function onContractionButton() {
        if (state == STATE_HOLD) {
            contractionCount++;
            WatchUi.requestUpdate();
        }
    }
}


<?xml version="1.0"?>
<iq:manifest xmlns:iq="http://www.garmin.com/connect-iq" version="1">
    <iq:application entry="BreathHoldApp" id="b896271ba7e04e12988fd03fb8714fc3" minApiLevel="1.4.0" type="watch-app" name="@Strings.AppName">
        <iq:products>
            <iq:product id="fenix3"/>
            <iq:product id="fenix3_hr"/>
        </iq:products>
        <iq:permissions>
            <iq:permission id="Sensor"/>
            <iq:permission id="ActivityRecording"/>
            <iq:permission id="FitContributor"/>
        </iq:permissions>
        <iq:languages>
            <iq:language>eng</iq:language>
        </iq:languages>
    </iq:application>
</iq:manifest>


project.manifest = manifest.xml
base.sourcePath = source
base.resourcePath = resources


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <strings>
        <string id="AppName">Freedive Breath</string>
    </strings>
</resources>