Now, let me also think about whether I should try to install the Connect IQ SDK to compile this. The user is on Linux. The SDK might be installable. But it's a Java-based SDK and requires device files. It could take a while and might not be necessary if I provide good instructions. However, compiling would catch syntax errors.

Let me see if the SDK is available. The Connect IQ SDK download URLs are known. But I don't have a browser. I can try to download via curl/wget. The SDK Manager is the official way, but there's also direct SDK downloads.

Actually, the SDK download URLs are like:
- Linux: `https://developer.garmin.com/downloads/connect-iq/sdks/connectiq-sdk-lin-X.Y.Z.zip`

But I don't know the latest version. I could try to find it. However, this might take a while. Since the user can build it themselves with the SDK Manager, and I've written the code carefully, I think it's acceptable to not compile locally. But I should at least do a sanity check of the code.

Let me read through the code one more time and think about logic issues.

In `BreathHoldView.mc`:
- `onShow()` creates a new Timer. If the view is shown, hidden, then shown again, we create a new Timer but the old one is stopped in `onHide()`. Good.
- `onHide()` stops the timer. Good.
- In `discardSession()`, I set `session = null` but don't null out `holdField` and `hrField`. That's okay because they are only used when session is recording.
- In `onStartButton()` when state is IDLE, after `startSession()` we set state and start timer. Good.
- In `onStartButton()` when state is RECOVERY, we call `finishSession()` which stops timer and sets state = DONE. We have `return` so we don't call `WatchUi.requestUpdate()`. But `finishSession()` calls `WatchUi.requestUpdate()` itself. Good.
- In `onStartButton()` when state is DONE, we call `resetRunState()` which sets state = IDLE. Then `WatchUi.requestUpdate()`. Good.
- `finishSession()` when called from `onTimer()` during recovery: it checks `if (state == STATE_HOLD)` before setting maxHoldTime. But at this point state is STATE_RECOVERY, not STATE_HOLD, so maxHoldTime is not updated. Wait, that's a bug!

When we transition from HOLD to RECOVERY in `onStartButton()`, we set `maxHoldTime = currentTime`. So when recovery times out and calls `finishSession()`, state is RECOVERY and maxHoldTime is already set. Good.

But what if the user never pressed START to end the hold? The prep automatically transitions to hold when currentTime >= prepTime. The hold counts up forever until the user presses START. There's no automatic end to hold. That's fine - breath-hold ends when the user decides.

So `finishSession()` only checks `if (state == STATE_HOLD)` to handle the case where it's called while still in hold. But in our code, `finishSession()` is only called from RECOVERY timeout or from RECOVERY early finish. In both cases, maxHoldTime is already set. So the check is redundant but safe.

Actually wait, in `onStartButton()` for RECOVERY, we call `finishSession()` directly. State is RECOVERY. maxHoldTime is set when we transitioned to RECOVERY. Good.

In `onTimer()` for RECOVERY, when currentTime >= recoveryTime, we call `finishSession()`. State is RECOVERY. maxHoldTime set. Good.

So the check in `finishSession()` is defensive and won't trigger in normal flow. It's fine.

Another issue: `startSession()` checks `if (session == null || !session.isRecording())`. If a previous session exists but is not recording, we create a new one. Good. But we don't null out old holdField/hrField. They get overwritten. Good.

Another issue: if the user is in DONE state and presses START to start a new session, `resetRunState()` sets session = null? No! `resetRunState()` does not set session = null. It sets state = IDLE, currentTime = 0, etc. But `session` is not null. Then `onStartButton()` in IDLE calls `startSession()`, which checks `session == null || !session.isRecording()`. Since session is not null and not recording (it was saved and stopped), it will create a new session. Good.

Wait, but `session.isRecording()` after save - is it false? Yes, after `session.save()` the session is no longer recording. Actually `session.save()` is called after `session.stop()`. So `isRecording()` should be false. So `startSession()` creates a new session. Good.

But actually, `session.save()` closes the session. So `session.isRecording()` is false. Good.

One more issue: In `onShow()`, if the view is shown again after the app was backgrounded, the timer is recreated. But if the timer was running when backgrounded, it may have been stopped by the system. We recreate it but don't restart it. The user would need to press START again. That's not ideal but acceptable.

Actually, a bigger issue: on fēnix 3, the app may go to sleep / inactive mode, which pauses timers and sensor events. When it resumes, the timer might not resume automatically. The user would need to wake the screen. This is a known limitation. I mention it in the README.

Now, another potential issue: `Sensor.setEnabledSensors([Sensor.SENSOR_HEARTRATE])` might crash if called without the Sensor permission. We have the permission. Good.

Also, I should check if `Sensor.SENSOR_HEARTRATE` exists. Yes, it's in the Sensor module constants since API 1.0.0.

Now let me think about the fēnix 3 HR. It has built-in optical HR. The `Sensor.SENSOR_HEARTRATE` will use it if no ANT+ strap is connected. Good.

For HRM-Tri, it's ANT+. The fēnix 3 supports ANT+ sensors. When paired, `Sensor.getInfo().heartRate` returns the strap's HR. Good.

Now, one more thing: the manifest `id` I used is 32 hex chars but starts with letters. That's fine - it just needs to be 32 hex chars. But it's not a valid UUID format. Actually, Connect IQ manifest IDs are typically UUIDs. Let me generate a proper UUID-like string.

Actually, the manifest ID in the examples is often a 32-char hex string. Let me use a more realistic one like `12345678-1234-1234-1234-123412341234` without dashes = 32 chars. My current one is `a1b2c3d4e5f678901234567890123456` which is 32 chars. Fine.

Actually, looking at the docs, the format is `12345678-1234-1234-1234-123456789012` with dashes? Let me check. In the manifest examples, the `id` attribute is usually a UUID with dashes removed, like `1234567890abcdef1234567890abcdef`. My string is fine.

Now, I should consider whether to add a launcher icon or not. Not needed for basic app.

Let me also create a `.vscode` settings file or `launch.json`? Not necessary, but could be helpful. Let me skip for now.

Let me also add a `.gitignore` for build artifacts. That would be nice.

And maybe a `README` note about building. I already have that.

Now, should I also try to compile with the SDK? Let me see if I can quickly install it. The SDK is large (hundreds of MB). It might not be worth it. But I can try to see if `monkeyc` is already installed somewhere.

bash

which monkeyc 2>/dev/null; which java 2>/dev/null; ls ~/.garmin 2>/dev/null; ls ~/.config/Code 2>/dev/null | head


No monkeyc installed. Java is available. I could try to install the SDK, but it's a large download and setup. The SDK Manager requires a GUI or can be run headless? There's a command-line SDK manager. Let me see if I can download it quickly.

Actually, the Connect IQ SDK Manager is a Java application. The download URL is something like:
`https://developer.garmin.com/downloads/connect-iq/sdk-manager/connectiq-sdk-manager-linux.zip` or similar.

But this might take a while. Given that the user can install it themselves, and my main deliverable is the code and plan, I think it's fine to not compile locally. However, I could at least attempt to verify syntax by downloading a minimal SDK or using a known linter.

Actually, let me try to install the SDK command-line tool. It would be valuable to catch errors. But if it takes too long or fails, I'll document that.

Let me search for the latest SDK download URL. I'll use web search.

Question: Garmin Connect IQ SDK linux download latest version URL monkeyc


1. Get the SDK | Connect IQ | Garmin Developers
   URL: https://developer.garmin.com/connect-iq/sdk/
   Get the SDK | Connect IQ | Garmin Developers

MenuGet the SDK

## Connect IQ SDK

By downloading any of the following SDKs, you accept our license agreement.

##### I. Development of Applications using the SDK

- a. License. Subject to the terms and conditions of this Agreement, Garmin hereby grants you a limited, non-exclusive, personal, revocable, non-assignable, non-sublicensable and non-transferable license to use the Program Materials for the sole purpose of developing and testing Applications. All licenses not expressly granted in this Agreement are reserved.
- b. Restrictions: You may not use the Program Materials for any purpose not expressly permitted by this Agreement. You agree not to rent, lease, lend, upload to or host on any website or server, sell, redistribute, or sublicense the Program Materials, in whole or in part, or to enable others to do so. You may not and you agree not to, or to enable others to, modify, create derivative works of, copy (except as expressly permitted under this Agreement), decompile, reverse engineer, disassemble, attempt to derive the source code of, or decrypt the Program Materials, or any part thereof. You may not: (a) alter or remove any copyright, trademark, or other proprietary notices appearing on or in the Program Materials; (b) engage in any activity with respect to the Program Materials that interferes with, disrupts, damages, or accesses in an unauthorized manner any Garmin devices, platforms, or systems, or those of any of its affiliates or any third party; or (c) make any statements that you or your Application are affiliated with, or sponsored, certified, or otherwise endorsed by Garmin or any of its affiliates, unless expressly permitted by Garmin in writing.
- c. Updates; No Support or Maintenance: Garmin may, but is not obligated to, extend, enhance, or otherwise modify the SDK at any time without notice. If updates are made available by Garmin, the terms of this Agreement will govern such updates, unless the update is accompanied by a separate license in which case the terms of that license will govern. Garmin may, but is not obligated to, provide any maintenance, technical or other support for the SDK. You agree that the form and nature of the Program Materials may change without prior notice to you and that future versions of the Program Materials may be incompatible with applications developed on previous versions of the Program Materials.
- d. Application Requirements: You are solely responsible for all aspects of development and use of your Application, including related documentation, user assistance, support and warranty. You will ensure that your Application complies with (i) the Developer Guidelines, available at https://developer.garmin.com/connect-iq/app-review-guidelines/, as updated by Garmin from time to time; and (ii) the requirements set forth in Exhibit A (Application Requirements). None of Garmin's review, testing, or approval of your Application, if applicable, limits or relieves you of any responsibilities related to your Application.

##### II. Distribution of Applications on the CIQ Store

a. Registration and Application Submission Process. To submit an Application, you must comply with the following requirements:

4. Application Submission. By submitting your Application you represent and warrant that:

- A. Your Application complies with the Application Requirements.
- B. You will not attempt to conceal, misrepresent or obscure any features, content, services or functionality in your Application from Garmin's review or hinder Garmin from being able to fully review your Application.

c. Monetizing Your Apps.

1. General Requirements. For purposes of this Agreement, “Monetizing” an Application means that you are collecting money or receiving items with monetary value in connection with your Application – whether such remuneration is required to download your Application, to use certain features in your Application, as a solicited tip or donation, or requested or suggested by you for any other reason. If you Monetize your App, you represent and warrant that:

- A. You will accurately provide any information required by Garmin during the onboarding process or as requested from time to time to maintain your account in good standing, including but not limited to information necessary to authenticate your identity.
- B. You will accurately disclose to end users the scope of any payment requirements and fulfill all such purchases.
- C. If you use a payment processor not provided by Garmin, you will ensure the security of any such payment method.

2. Use of Garmin Merchant Service. You are not required to use the Merchant Service to Monetize your Application, but if you choose to do so, then the requirements in this Section apply.

F. Taxes.

- (i) You agree to timely provide, as soon as reasonably practicable, any tax documentation or certification requested by Garmin in connection with your use of the Merchant Service.
- (ii) Where Garmin or its payment processor reasonably determines that it is required by applicable (local) legislation or by the applicable governmental tax authority to declare, charge, deduct, or withhold any taxes (in each case, "Withholding Taxes"), Garmin may also deduct the amount of such Withholding Taxes from the amount Garmin remits to you.
- (iii) In Garmin’s discretion, Garmin may add applicable sales taxes, VAT taxes, or similar transaction taxes to the final purchase of an Application to an end user.

##### III. Term and Termination

b. Termination.

- 1. Termination by You. You may terminate this Agreement by delivering written notice to Garmin of termination at least 30 days prior to the termination date.
- 2. Termination for Cause. If you fail to perform or comply in any material respect with any of its obligations under this Agreement and such failure is not remedied within 30 days after receipt of written notice of such failure, then Garmin may terminate this Agreement effective upon expiration of such 30-day cure period.

c. Effect of Termination. Upon termination of the Agreement for any reason:

- 1. You shall immediately stop using and delete the Program Materials.
- 2. Upon termination, Garmin will remove your Application from the CIQ Store. End users who have previously downloaded a copy of the Application may continue to use that copy indefinitely. Garmin will pay any amounts due in your account in accordance with the ordinary schedule specified in the Documentation.
- 3. Provisions that naturally survive termination of this Agreement shall so survive.

##### IV. Proprietary Rights

- a. Garmin's Ownership. Garmin retains all rights, title, and interest in and to the Program Materials, including any updates. You agree to cooperate with Garmin to maintain Garmin's ownership of the Program Materials, and you agree to promptly provide notice to Garmin of any claims relating to the Program Materials. There are no implied licenses under this Agreement, and any rights not expressly granted to you hereunder are reserved by Garmin.
- b. License to Your Marks. You grant Garmin a revocable, non-exclusive, non-transferable royalty-free license to use your Trademarks in connection with Garmin’s activities under this Agreement and to publicize, in Garmin’s sole discretion, your participation in the Garmin Developer Program and your Application.
- c. Feedback. If you provide any Feedback to Garmin: (a) you grant to Garmin a worldwide, non-exclusive, royalty-free, transferable, sublicensable, perpetual and irrevocable license to use and otherwise exploit such Feedback in connection with any Garmin products, applications and services; (b) Garmin shall be free to use, disclose, reproduce, distribute and otherwise commercialize all Feedback that you provide without obligation or restriction of any kind on account of Intellectual Property Rights or otherwise; (c) you waive all rights to be compensated or seek compensation for your Feedback; and (d) Feedback, even if marked confidential, shall not create any confidentiality obligations on Garmin, unless Garmin has otherwise expressly agreed in a signed agreement.
- d. Confidential Information. You shall hold Garmin's Confidential Information in confidence and shall not disclose such Confidential Information to third parties or use such Confidential Information for any purpose other than as necessary to perform under this Agreement. You agree to limit access to the Confidential Information to Authorized Users. All such Authorized Users must have a written confidentiality agreement with you that is no less restrictive than the terms contained herein. You will protect the Confidential Information from unauthorized use, access, or disclosure in the same manner as you protect your own confidential or proprietary information of a similar nature and with no less than reasonable care. The foregoing restrictions on disclosure shall not apply to Confidential Information that (a) becomes, through no act or fault of yours, publicly known; (b) is received by you from a third party without a restriction on disclosure or use; or (c) is independently developed by you without reference to Garmin's Confidential Information. In addition, you will not disclose to any third party the existence of any new feature of the Garmin Developer Program before Garmin makes a public announcement regarding their existence.

##### V. Representations and Warranties

- a. Mutual Warranties. Each party represents and warrants to the other that: (a) it has all necessary right, power and ability to execute, and to perform the obligations contemplated by, this Agreement; (b) no authorization or approval from any third party is required in connection with such party's execution, delivery or performance of this Agreement; and (c) such party's obligations under this Agreement do not violate any Law or breach any other agreement to which such party is bound.
- a. Your Warranties. You represent and warrant to Garmin that: (a) you are duly licensed to conduct your business under the Laws of all jurisdictions in which you conduct business; (b) any materials you provide, create or develop that are in any way related to this Agreement, or the use thereof, do not and will not infringe any Intellectual Property Rights of any third party; and (c) all information you provide to Garmin is true, correct and complete in all respects and you will update Garmin with any changes to information you have previously supplied. You represent and warrant that you have all Intellectual Property Rights, including all necessary patent, trademark, trade secret, copyright or other proprietary rights, in and to the Application (including without limitation any and all content therein or portions thereof). If you use third-party materials, you represent and warrant that you have the right to distribute the third-party material in the Application.
- c. DISCLAIMER OF WARRANTIES. THE PROGRAM MATERIALS AND ALL INFORMATION AND OTHER MATERIALS CONTAINED OR MADE AVAILABLE THEREIN IS PROVIDED ON AN "AS IS", "AS AVAILABLE" BASIS WITHOUT WARRANTY OF ANY KIND, AND YOUR USE OF ANY OF THE FOREGOING IS AT YOUR OWN RISK. NONE OF THE GARMIN PARTIES (AS DEFINED BELOW) REPRESENT OR WARRANT THAT THE PROGRAM MATERIALS OR ANY INFORMATION OR OTHER MATERIALS CONTAINED OR MADE AVAILABLE THEREIN WILL BE AVAILABLE, ACCESSIBLE, UNINTERRUPTED, TIMELY, SECURE, ACCURATE, COMPLETE, OR ERROR-FREE. YOU EXPRESSLY ACKNOWLEDGE THAT COMPUTER NETWORK-BASED SERVICES MAY BE SUBJECT TO OUTAGES, INTERRUPTIONS, ATTACKS BY THIRD PARTIES AND DELAY OCCURRENCES. GARMIN SPECIFICALLY DISCLAIMS ALL REPRESENTATIONS, WARRANTIES, AND CONDITIONS WHETHER EXPRESS OR IMPLIED, ARISING BY STATUTE, OPERATION OF LAW, USAGE OF TRADE, COURSE OF DEALING, OR OTHERWISE. You may have rights that vary from jurisdiction to jurisdiction; however, the above disclaimers apply to the maximum extent permitted by applicable Law.

##### VI. LIMITATIONS OF LIABILITY

- TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAWS, UNDER NO CIRCUMSTANCES WILL: (A) GARMIN OR ANY OF ITS CURRENT OR FORMER AFFILIATES (OR ANY OFFICERS, DIRECTORS, EMPLOYEES, AGENTS, SUCCESSORS OR ASSIGNS OF ANY OF THEM) (COLLECTIVELY, THE "GARMIN PARTIES") BE LIABLE FOR ANY INDIRECT, INCIDENTAL, CONSEQUENTIAL, SPECIAL OR EXEMPLARY DAMAGES (HOWEVER ARISING), UNDER ANY THEORY OF LIABILITY OR CAUSE OF ACTION WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT OR OTHERWISE, REGARDLESS OF WHETHER IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; AND (B) EXCEPT FOR ANY ACCOUNT BALANCE OWED TO YOU FOR APPLICATION SALES THROUGH THE MERCHANT SERVICE, THE GARMIN PARTIES' TOTAL LIABILITY TO YOU, WHETHER ARISING IN TORT (INCLUDING NEGLIGENCE), CONTRACT OR OTHERWISE, UNDER THIS AGREEMENT OR WITH REGARD TO THE PROGRAM MATERIALS OR ANY OTHER INFORMATION AND OTHER MATERIALS CONTAINED OR MADE AVAILABLE THEREIN, WILL IN NO CASE EXCEED $100.

##### VII. Indemnification

- You agree to indemnify, release, hold harmless and, upon request, defend the Garmin Parties from and against any and all claims, actions, proceedings, and suits and all related liabilities, damages, settlements, penalties, fines, costs or expenses (including reasonable attorneys' fees and other litigation expenses, whether prior to, at trial or any other proceeding and in any appeal or other post judgment proceeding) incurred by Garmin, arising out of or relating to: (a) any breach or alleged breach by you of any representation, warranty, or obligation contained in this Agreement; (b) any damage or loss caused by negligence, fraud, dishonesty or willful misconduct by you, any Authorized User, or any of your affiliates, employees, agents, contractors, suppliers or customers; (c) any contract or agreement between you and a third party; (d) use by any party of your Application, including any claims by any end user; (e) any claims related to the nature or characteristics of your Application, or any of your other products or services; (f) any infringement or alleged infringement of a patent, copyright, trademark or other intellectual property or proprietary right relating to this Agreement; (g) any alleged or actual violation by you of any applicable Laws; or (h) any claims or governmental regulatory actions related to your collection, use or sharing of user data collected through your Application in violation of this Agreement or the privacy or other rights of any user or other third party.

##### VIII. General Provisions

- a. Publicity.You may not issue any press release regarding your association with Garmin without the express prior written consent of Garmin. You shall not include any marketing of Garmin's name, logo or other trademarks in your Application or in any other materials without Garmin's prior written consent. Notwithstanding the foregoing, if your submitted Application is approved by Garmin, you may promote your Application’s compatibility with certain Garmin products so long as your statements concerning compatibility are accurate and you adhere to Garmin’s guidelines at https://developer.garmin.com/brand-guidelines/overview/.
- b. Independent Development. Nothing in this Agreement will impair the right of Garmin or its affiliates to develop, acquire, license, market, promote or distribute any products, software or technologies, including those that perform the same or similar functions as, or otherwise compete with, the Application or any other products, software or technologies that you may develop, produce, market, sell or distribute.
- c. Notices. Garmin may provide notices to you via the email address that you have provided with your account. You may send notices to Garmin via postal mail to Garmin International, Inc., 1200 E. 151st St., Olathe, KS 66062, ATTN: Legal Department. You agree to receive electronic communications from Garmin. You agree that any notice, agreement, disclosure or other communication that Garmin sends you electronically will satisfy any legal communication requirements, including that such communications be in writing.
- d. Governing Law; Venue. All legal issues arising from or related to your use of the Program Materials and this Agreement shall be construed in accordance with and governed by the laws of the State of Kansas applicable to contracts entered into and performed within the State of Kansas without respect to its conflict of laws principles. You agree to submit to the personal and exclusive jurisdiction of the United States District Court for the District of Kansas, or any Kansas State court sitting in Johnson County, to resolve any dispute, claim or cause of action arising out of this Agreement. You hereby irrevocably waive, to the fullest extent permitted by law, any objection which you may now or hereafter have to the laying of the venue of any such proceeding brought in such a court and any claim that any such proceeding brought in such a court has been brought in an inconvenient forum. Specifically excluded from application to this Agreement is the United Nations Convention on Contracts for the International Sale of Goods.
- e. Costs and Expenses. You are solely responsible for all costs and expenses related to participating in the Garmin Developer Program, including the development of any Application.
- f. Severability. If any provision in this Agreement is held invalid, the remainder of this Agreement shall continue to be enforceable. If any provision in this Agreement is deemed unlawful, void or unenforceable, then that provision is deemed severable from this Agreement and the remaining provisions are still valid and enforceable.
- g. Assignment. You may not assign, whether by transfer, merger operation of law or otherwise, any of your rights or delegate the performance of any of your obligations under this Agreement without the prior written consent of Garmin. Any purported assignment in violation of hereof shall be null and void. Garmin has the right to assign this Agreement to an affiliate without prior approval from you.
- h. Force Majeure. Garmin will not be liable to you for any losses arising out of the delay or interruption of Garmin's performance of obligations under the Agreement due to any acts of God, or any other occurrences which are beyond Garmin's reasonable control.
- i. Entire Agreement. This Agreement together with all of Garmin's policies referenced herein sets forth the entire understanding and agreement of the parties, and supersedes any and all prior or contemporaneous oral or written agreements or understandings between the parties, as to the subject matter of this Agreement. Garmin's failure to insist upon or enforce strict performance of this Agreement will not be considered a waiver of any provision or right. Neither the course of conduct between the parties nor trade practice will modify this Agreement.
- j. Export Restrictions. You may not use, export, re-export, import, sell or transfer any of the Program Materials except as authorized by United States Law and other applicable Laws in the jurisdiction in which you obtain such Program Materials. You will comply with all Laws that restrict your and/or Garmin's ability to operate in or transact business with certain countries or share certain technologies and data with certain individuals ("Export Restrictions"). Your compliance with the Export Restrictions shall be such that Garmin shall not violate any of the Export Restrictions as a result of your acts or omissions.

##### IX. Definitions

- a. "Application(s)" means any software programs that you develop by using components of the Program Materials.
- b. "Application Requirements" means this Agreement, the Connect IQ Store Review Guidelines, and any additional guidelines that Garmin may otherwise provide or make available to you.
- c. "Authorized Users" means your employees, agents and contractors who have a need to access and use your Garmin Developer Account as permitted under this Agreement.
- d. "CIQ Store" means the Garmin-operated website, mobile application, or on-device application where end users download Applications.
- e. "Companion SDK" means the SDK provided by Garmin to enable the development of Applications for third-party platforms. Unless otherwise specified, references in this Agreement to the SDK include the Companion SDK.
- f. "Confidential Information" means the SDK, the Program Materials, the underlying computer code to which you may obtain or receive access, the functional or technical design, logic or other internal routines or workings of the Garmin SDK, and any other materials of Garmin that Garmin designates as confidential or which you should reasonably believe to be confidential.
- g. "Documentation" means the Connect IQ Monetization System manual available to you in the developer dashboard, available at https://developer.garmin.com/connect-iq/monetization.
- h. "Feedback" means any suggestions, comments, feedback or other information or materials you provide to Garmin with respect to the Garmin Developer Program, the SDK, or any of Garmin's products or services.
- i. "Garmin Developer" means any developer having gone through the Registration process and been approved by Garmin to use the Program Materials.
- j. "Garmin Developer Account" means the account by which you and/or Authorized Users gain access to the Program Materials subject to the terms and conditions of this Agreement.
- k. "Garmin Developer Program" means the program Garmin has developed to enable Garmin Developers to use the Program Materials, as such program may be revised from time to time at Garmin's sole discretion.
- l. "Intellectual Property Rights" means ideas, inventions, discoveries, improvements, modifications, updates, enhancements, designs, business models, software, derivatives, know-how, processes, methodologies, technical information, data, test results, information, know-how, concepts, and works of authorship, whether patentable or not and whether reduced to practice or not, and all related intellectual property, including, but not limited to, patents and patent applications (including but not limited to all reissues, continuations, continuations-in-part, revisions, extensions, divisionals, designs and reexaminations thereof), copyrights, mask works, trademarks, trade secrets, and other forms of intellectual property protection related thereto, including all applications, certifications and registrations therefor.
- m. "Laws" means all local, state, regional, national, foreign, international or other laws, policies, guidelines, standards, regulations, ordinances, rules and judgments applicable to you, your business or your access to and use of the Garmin Developer Program, including without limitation Federal Trade Commission regulations, rules and judgments, and those of any other regulatory body or agency having jurisdiction over the subject matter hereof.
- n. "Merchant Service" means Garmin’s service to enable the sale of Applications to end users directly on the CIQ Store through Garmin’s authorized payment processor.
- o. "SDK" means Garmin's CONNECT IQ software development kit, in object code form, that is licensed to you under this Agreement, including documentation, firmware, software, sample code, tools, libraries, APIs, data, and files made available to you by Garmin.
- p. "Program Materials" means any materials made available to you by Garmin or a Garmin affiliate in connection with the Garmin Developer Program, at a Garmin developer website or otherwise, including the SDK and all tools, the Documentation, other SDK- and CIQ Store-related documentation and materials, in written or digital form, such as sample code, simulators, tools, and libraries, and including any upgrades, modified versions, updates, enhancements, bug fixes, supplements to, revisions, new releases, and/or additions thereto, if any, that may be provided or made available by Garmin.
- q. "Trademark(s)" means all means, collectively, all trademarks, service marks, logos, trade dress, trade names and service names, any registrations and applications to register, and all goodwill in, any of the foregoing, and all similar or related rights arising under any of the Laws of the United States or any other country or jurisdiction, whether now existing or hereafter adopted or acquired.

#### Exhibit A - Application Requirements

##### I. Privacy.a. Privacy Policy. If your Application will collect any data from users of the Application, then you must have a privacy policy for the Application that complies with all applicable Laws. You must update your privacy policy if you change the way you collect, use, store, or disclose user data. Additionally, you may not change the URL or location for your privacy policy without redirecting users to the new location of your privacy policy. Your privacy policy or other notices or terms must make it clear that any data submitted to the Application is submitted to you and not to Garmin and that Garmin has no responsibility or liability for any such data.b. Data Retention. You are not permitted to retain user data for longer than needed for the reasonable operation of your Application unless the user gives you express consent to retain the user's data for a longer period.c. Location Data. You represent and warrant that your Application does not default to collect location data about users and that users are notified and required to opt in to allow your Application to collect location data.d. Other Data Restrictions. Your Application may only access and/or collect information from Garmin about a user or the user's activities if the user gives you express prior consent, and your Application may only use that information when, and for the limited purposes for which, the user has given you express consent. In addition, you or your Application may not: (i) retain any data you receive from Garmin concerning a user or the user's activities without the user's express consent, (ii) retain any user data collected through the Application or from Garmin after a user has revoked consent or asked you to delete such data; or (iii) sell, rent, or transfer (directly or indirectly) any data concerning a user or the user's activities you receive from Garmin.

##### II. Security; Data.You are solely responsible for the security of user data residing on server(s) or systems owned or operated by you, or by a third party designated by you (e.g., a web hosting company, processor, or other service provider). You must maintain, and must require any third party designated by you to maintain processes and controls to protect and secure user data. You acknowledge that Garmin will not be liable for any improperly processed or unauthorized transactions or illegal or fraudulent access to your Garmin Developer Account, your Application or to Program Materials. You must notify us immediately in the event of any unauthorized access (confirmed or suspected) to Program Materials, your Garmin Developer Account credentials, your authentication key, and/or your Application.

##### III. Other Regulations.a. You are required to comply with all applicable laws, regulations, and policies, including but not limited to all U.S. Food and Drug Administration (“FDA”) laws, regulations, and policies, related to the manufacturing, marketing, sale, and distribution of your Application in the United States, as well as in other jurisdictions where your Application is available, manufactured, marketed, sold, distributed or used. You agree, upon Garmin’s request, to promptly provide any such clearance documentation related to this section E(i) to support the marketing of your Application.b. You are solely responsible for complying with any laws, orders, rules, and regulations of the Federal Communications Commission, FDA, other U.S. regulatory bodies such as the U.S. Federal Trade Commission and the U.S. Department of Health and Human Services, any Federal, State or local governmental authority, and the laws, regulations, and policies of any jurisdiction applicable to its use of the Licensed Material and associated Devices (e.g. MHRA, CFDA). You will not seek any regulatory marketing permissions or make any determinations that may result in the Program Materials, Connect IQ SDK, or any other Garmin products or services being determined to be regulated products or that may impose any obligations or limitations on Garmin.

##### IV. Push Notifications.Garmin may make available to you a push notification API to enable your Application to push notifications to the Garmin devices of the end users of your Application. If you are authorized by Garmin to use the push notification API, then you agree that you will not push messages to the Garmin devices of the end users of your Application without their consent. In addition, push notifications must not be required for your Application to function, must not require payment or other charge, and must not be used for advertising, promotions, or direct marketing purposes or to send sensitive personal or confidential information.

### Get the Connect IQ 9.2.0 SDK

#### Install the SDK Manager

Accept & Download for Windows

Accept & Download for Mac

Accept & Download for Linux

1. Download the SDK manager.
2. Launch the SDK manager from your chosen download location.
3. Complete the first-time setup process.
4. Use the SDK manager to download the latest Connect IQ SDK and devices.
5. Once the download completes, click Yes when prompted to use the new SDK version as your active SDK.
6. Close the SDK Manager.

#### Installing the Visual Studio Code Monkey C Extension:

1. In Visual Studio Code, go to the View > Extension menu
2. In the Extensions Marketplace, search box type "Monkey C"
3. Select the Monkey C extension from Garmin.
4. Use the Install button to install the extension in Visual Studio Code. This will require a restart of Visual Studio Code.
5. After Visual Studio Code restarts, summon the command palette with Ctrl + Shift + P (Command + Shift + P on Mac).
6. Type "Verify Installation" and select Monkey C: Verify Installation

### Get the Connect IQ Mobile SDK

- The Android SDK is available via Maven Central. A sample app is available on GitHub that demonstrates integration and use of the SDK in an Android app.
- The iOS SDK is available via GitHub. A sample app is available on GitHub that demonstrates integration and use of the SDK in an iOS app.
- By using the SDK, you agree to the license agreement above. A copy of the license agreement is included with the SDK as well.

2. Casks/c/connectiq.rb
   URL: https://github.com/Homebrew/homebrew-cask/blob/f218bac272d36d54956c0c0bca9e03f4e98255ff/Casks/c/connectiq.rb
   # Casks/c/connectiq.rb

- Branch: f218bac272d36d54956c0c0bca9e03f4e98255ff
- Repository: Homebrew/homebrew-cask

---

cask "connectiq" do
 version "9.2.0,2026-06-09,92a1605b2"
 sha256 "13bcc210483284074819c80dc90946db233af37be2518225e3f533d5804ebc1d"

 url "https://developer.garmin.com/downloads/connect-iq/sdks/connectiq-sdk-mac-#{version.tr(",", "-")}.dmg"
 name "Garmin Connect IQ SDK"
 desc "Build wearable experiences for Garmin devices and sensors with ConnectIQ SDK"
 homepage "https://developer.garmin.com/connect-iq/"

 livecheck do
 url "https://developer.garmin.com/downloads/connect-iq/sdks/sdks.json"
 regex(/connectiq-sdk-mac[._-]v?(\d+(?:\.\d+)+)[._-](\d+(?:-\d+)+)[._-](\h+)\.dmg/i)
 strategy :json do |json, regex|
 json.map do |item|
 match = item["mac"]&.match(regex)
 next if match.blank?

 "#{match[1]},#{match[2]},#{match[3]}"
 end
 end
 end

 depends_on macos: :big_sur

 app "connectiq-sdk-mac-#{version.tr(",", "-")}/bin/ConnectIQ.app"
 app "connectiq-sdk-mac-#{version.tr(",", "-")}/bin/MonkeyMotion.app"
 binary "connectiq-sdk-mac-#{version.tr(",", "-")}/bin/monkeybrains.jar"
 binary "connectiq-sdk-mac-#{version.tr(",", "-")}/bin/monkeyc"
 binary "connectiq-sdk-mac-#{version.tr(",", "-")}/bin/monkeydo"
 binary "connectiq-sdk-mac-#{version.tr(",", "-")}/bin/monkeydoc"
 binary "connectiq-sdk-mac-#{version.tr(",", "-")}/bin/shell"

 zap trash: [
 "~/Library/Application Support/Garmin",
 "~/Library/Preferences/com.garmin.connectiq.simulator.plist",
 "~/Library/Saved Application State/com.garmin.connectiq.simulator.savedState",
 ]
end

3. eisbaw/garmin_nix
   URL: https://github.com/eisbaw/garmin_nix/
   # eisbaw/garmin_nix

Garmin App development environment - SDK Manager, compiler, simulator GUI - all in Nix

- Stars: 1
- Forks: 0
- Watchers: 1
- Open issues: 0
- Default branch: main
- Created: 2025-08-11T22:08:23Z

## Languages

- Just
- Monkey C
- Nix
- Shell

## Top Contributors

- eisbaw (5 contributions)

---

## README

# Nixified Garmin Connect IQ SDK

This repository provides a Nix development environment for the Garmin Connect IQ SDK.

## Prerequisites

- Nix package manager
- X11 display (for SDK Manager GUI)

## Setup Instructions

### Step 1: Download SDK Manager

Download the Linux SDK Manager from: https://developer.garmin.com/connect-iq/sdk/

Extract the zip file in this directory:
```bash
unzip connectiq-sdk-manager-linux.zip
```

This will create `bin/sdkmanager` and related files.

### Step 2: Install SDK via GUI

Enter the Nix shell and launch the SDK Manager:
```bash
nix-shell --run "garmin-sdk-env -c 'bin/sdkmanager'"
```

In the SDK Manager GUI:
1. Login with your Garmin developer account
2. Select and download the Connect IQ SDK version you need
3. Select and download device simulators (e.g., Forerunner 970)
4. Close the SDK Manager when downloads complete

### Step 3: Exit the nix-shell

```bash
exit
```

### Step 4: Re-enter nix-shell to use the SDK

```bash
nix-shell
```

The shell will now detect and configure the installed SDK under `~/.Garmin/ConnectIQ/`

## Available Commands

Once the SDK is installed, the following commands are available:

- `monkeyc` - Monkey C compiler
- `monkeybrains` - Garmin device simulator
- `barrelbuild` - Build tool for creating barrels (libraries)
- `sdkmanager` - SDK Manager for updating SDKs and devices

## Testing the Installation

After installing the SDK and re-entering the nix-shell, test the compiler:

```bash
monkeyc --help
monkeyc --version
```

## FHS Environment

All Garmin tools run inside an FHS (Filesystem Hierarchy Standard) environment to ensure compatibility with the pre-built Linux binaries. The `shell.nix` file automatically wraps the tools to work transparently.

## SDK Location

The SDK is installed to: `~/.Garmin/ConnectIQ/`

## Troubleshooting

If tools are not found after installing the SDK:
1. Make sure you've exited and re-entered the nix-shell
2. Check that the SDK was properly installed in `~/.Garmin/ConnectIQ/Sdks/`
3. Try running tools directly through the FHS environment:
   ```bash
   garmin-sdk-env -c "~/.Garmin/ConnectIQ/Sdks/*/bin/monkeyc --help"
   ```

4. Monkey C - Visual Studio Marketplace
   URL: https://marketplace.visualstudio.com/items?itemName=garmin.monkey-c
   Monkey C - Visual Studio Marketplace Skip to content

# Monkey C

## Garmin

| More from Garmin publisher | 169,953 installs | (9) | Free Monkey C Language Support Install Trouble Installing? |
| --- |

OverviewVersion HistoryQ & ARating & Review

# Connect IQ

# Quick Start

1. Install extension
2. Download the Connect IQ SDK Manager
3. Download an SDK and set it as the current SDK using the SDK Manager
4. Download devices using the SDK Manager
5. Run the`Monkey C: Verify Installation` command to ensure the development environment is correctly setup

# Features

- Autocomplete
- Folding Ranges
- Real time errors
- Renaming
- Syntax highlighting

- Find symbols in workspace or document
- Find all references for a value
- Highlight uses of the value that is currently selected
- Hover over a member to see its documentation
- Jump to definitions
- Show type hierarchy

- `Build Current Project`: compile an app
- `Build for Device`: launch the Build for Device wizard to select the device, location, and build mode of the build
- `Clean Project`: remove the output folder of the project
- `Configure Monkey Barrel`: add or remove a barrel to the current project
- `Edit Annotations`: add or remove annotations to a barrel project
- `Edit Application`: set the app type, minimum SDK, and other application settings in manifest.xml
- `Edit Languages`: select the translations provided by the app
- `Edit Permissions`: select the permissions needed by the app
- `Edit Products`: select individual products to support
- `Export Project`: generate a .iq file for upload or a .barrel file for distribution
- `Generate a Developer Key`: create a new developer key and set as the current key
- `Launch Native Pairing`: launch the app in native pairing mode
- `New Project`: launch the New Project wizard
- `Open Monkey Graph`: opens the Monkey Graph FIT tool
- `Open Monkey Motion`: opens the Monkey Motion animation tool
- `Open ERA Viewer`: opens the ERA Viewer
- `Open Samples Folder`: opens the folder containing the samples
- `Open SDK Manager`: opens the SDK Manager
- `Regenerate UUID`: regenerate the UUID for the app
- `Run Tests`: run an app's tests
- `Set Products by Product Category`: select products to support based on device grouping and support Connect IQ versions
- `Verify Installation`: verify that the Connect IQ SDK is correctly installed
- `View Documentation`: view the documentation that is found in the SDK

# Extension Settings

- `monkeyC.compilerOptions`: additional parameters to pass to the compiler
- `monkeyC.compilerWarnings`: show warnings from the compiler, default is`true`
- `monkeyC.debugLogLevel`: the debug log level to apply when compiling
- `monkeyC.developerKeyPath`: the path to your Connect IQ developer key
- `monkeyC.javaPath`: the path to the root Java SDK folder to use for running Java based tools
- `monkeyC.jungleFiles`: the jungle file(s) to use for compilation, default is`monkey.jungle`
- `monkeyC.optimizationLevel`: the level of optimization to apply when compiling with SDK 4.1.6 or greater
- `monkeyC.typeCheckLevel`: the level of type checking to apply when using CIQ 4.0 or greater
- `monkeyC.developerId`: developer ID to use with the ERA Viewer

# Feedback

# Attributions

- The GSON library which is licensed under the Apache 2.0 License.
- The LSP4J library which is licensed under the Eclipse Public License v2.0.
- The Apache Commons project which is licensed under the Apache 2.0 License.

| The Monkey C extension adds support for using the Connect IQ SDK. Building and running a Connect IQ app requires the Connect IQ SDK Manager. This extension requires Connect IQ SDK 4.0.6 or greater. For more information see Getting Started guide. The Monkey C extension allows for creating, building, running, and debugging a Connect IQ app. The extension supports the following features for .mc, .jungle, and .mss files: The extension additionally supports the following features for .mc files: The following commands are supported: The commands`Build Current Project`,`Run Tests`, and`New Project` can also be launched through the context menu by right-clicking on a folder or file in the Explorer Side Bar. This extension contributes the following settings: Provide any feedback or submit bug reports using the Connect IQ Development Forums This extension uses the following libraries: |
| --- |

5. skills/garmin-dev/references/connect-iq-docs/reference/reference-guides/command-line-setup.md
   URL: https://github.com/athitupakan/garmin-dev-skills/blob/main/skills/garmin-dev/references/connect-iq-docs/reference/reference-guides/command-line-setup.md
   # skills/garmin-dev/references/connect-iq-docs/reference/reference-guides/command-line-setup.md

- Branch: main
- Repository: athitupakan/garmin-dev-skills

---

---
source: https://developer.garmin.com/connect-iq/reference-guides/monkey-c-command-line-setup/
sdk: 9.1.0
fetched: 2026-05-30
generated: auto-converted from SDK doc/docs/Reference_Guides/Monkey_C_Command_Line_Setup.html via _refresh/htmlmd.js — faithful mirror, not hand-curated
---

# Using Monkey C from the Command Line

Before getting started with the installation on Mac or Windows, you'll need version 11 or higher of the Oracle Java™ Runtime Environment installed. Once that's done, continue on to your platform's installation instructions.

## OS X Installation

1. Install the Connect IQ SDK Manager, download the SDK, and set the active SDK.
2. Point your `PATH` to the active Connect IQ bin directory in the Terminal. To temporarily add it to a single local shell instance:

```
$ export PATH=$PATH:`cat $HOME/Library/Application\ Support/Garmin/ConnectIQ/current-sdk.cfg`/bin
```

For a more persistent addition, open `.bash_profile` in a text editor:

```
$ touch ~/.bash_profile
$ open ~/.bash_profile
```

Then add the line below to the file and save the changes:

```
export PATH=$PATH:`cat $HOME/Library/Application\ Support/Garmin/ConnectIQ/current-sdk.cfg`/bin
```

## Windows Installation

1. Install the Connect IQ SDK Manager, download the SDK, and set the active SDK.
2. Point your `PATH` to the active Connect IQ bin directory in the command prompt:

```
> for /f usebackq %i in (%APPDATA%\Garmin\ConnectIQ\current-sdk.cfg) do set CIQ_HOME=%~pi
> set PATH=%PATH%;%CIQ_HOME%\bin
```

## Linux Installation

1. Install the Connect IQ SDK Manager, download the SDK, and set the active SDK.
2. Point your `PATH` to the active Connect IQ bin directory in the command prompt:

```
$ export PATH=$PATH:`cat $HOME/.Garmin/ConnectIQ/current-sdk.cfg`/bin
```

For a more persistent addition, open `.bash_profile` in a text editor:

```
$ touch ~/.bash_profile
$ nano ~/.bash_profile
```

Then add the line below to the file and save the changes: CTRL-X

```
export PATH=$PATH:`cat $HOME/.Garmin/ConnectIQ/current-sdk.cfg`/bin
```

## Basic Commands

After installation, three new shell commands are available: `connectiq`, `monkeyc`, and `monkeydo`.

- `connectiq` launches the Connect IQ simulator, which can be used to run and test apps on your computer before running them on your device. In the simulator, your app will only have access to those APIs that are available on the currently simulated device. For example, an API only available in Connect IQ v2.2.x or higher, such as `PersistedContent`, will not be available on devices that run earlier versions of Connect IQ.
- `monkeyc` calls the Monkey C compiler. The compiler can take code from multiple files and link them together into a single Connect IQ executable (a PRG file). The usage is:

```
> monkeyc [-d <arg>] [-f <arg>] [-o <arg>] [-y <arg>]
```

| Argument | Definition |
| --- | --- |
| `-d ` | Target device |
| `-f ` | Jungle files |
| `-o ` | Output file to create |
| `-y ` | Private key to sign builds with |

**Note:** For more information on all the command line options, refer to the Compiler Options section in the Monkey C guide.

- `monkeydo` runs a Connect IQ executable in the simulator. You must have previously started the simulator with `connectiq`. The usage is:

```
monkeydo [executable] [device_id] [-n] [-t | -t test_name]
```

| Argument | Definition |
| --- | --- |
| `executable` | A Connect IQ executable (PRG) to run |
| `device_id` | The device to simulate (e.g. "fenix5plus") |
| `-n` | Runs the app in sensor native pairing mode |
| `-t` | Execute Run No Evil unit tests. Supply an optional test method or class name to only run that test or set of tests. |

Here is an example of a basic build and run cycle from the command line:

```
// Launch the simulator:
> connectiq

// Compile the executable:
> monkeyc -d fenix5plus -f /path/to/monkey.jungle -o project_name.prg -y /path/to/Dev_Key

// Run in the simulator
> monkeydo myApp.prg fenix5plus
```

**Note:** For more information on the `-f` option and the Jungle build framework, see the Overriding Resources section of this guide.

## Generating a Key Using OpenSSL

If you're working from the command line you can generate a RSA key using OpenSSL. The following command will generate a valid signing key.

```
> openssl genrsa -out developer_key.pem 4096
> openssl pkcs8 -topk8 -inform PEM -outform DER -in developer_key.pem -out developer_key.der -nocrypt
```

This developer key, `developer_key.der`, is passed to the compiler using the `-y` command line option.

eisbaw/garmin_nix

Monkey C - Visual Studio Marketplace


I found the latest SDK is 9.2.0. The SDK Manager download URL is on the page. I could try to download the Linux SDK Manager and use it. But it requires a Garmin account login for the SDK Manager. Also, building requires a developer key for signed builds, though sideloading a debug build might not need it.

Given the time, I think it's better to provide build instructions than to attempt a full SDK install. The user can use VS Code with the Monkey C extension.

However, I could attempt a command-line build if I can get the SDK. The SDK Manager Linux download URL is probably `https://developer.garmin.com/downloads/connect-iq/sdk-manager/connectiq-sdk-manager-linux.zip` or similar. Let me check by fetching the SDK page. But the page says "Accept & Download for Linux" which is probably a button that triggers a download.

Actually, I can see from the homebrew cask that the SDK itself (not manager) is at:
`https://developer.garmin.com/downloads/connect-iq/sdks/connectiq-sdk-mac-9.2.0-2026-06-09-92a1605b2.dmg`

For Linux, it might be:
`https://developer.garmin.com/downloads/connect-iq/sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2.zip`

Let me try to download the Linux SDK directly. But it's probably large. And I need device files for fēnix 3, which are separate downloads.

Actually, I recall that the SDK Manager downloads SDKs and devices. The SDK alone doesn't include device files. So I'd need to run the SDK Manager, which requires GUI or maybe headless with credentials.

Given the complexity, I'll skip local compilation and instead do a careful code review. The user can compile with the SDK Manager.

But wait - I should at least try to generate a developer key and set up the basic command-line build. Let me see if I can download the Linux SDK.

Actually, the SDK Manager for Linux might be runnable headless. Let me try to download it.

But this might be over-engineering. Let me focus on delivering high-quality code and instructions. The user can build it.

Let me do one more code review and then add a `.gitignore` and maybe a VS Code settings recommendation.

Also, I should add a note about the developer key. For sideloading personal apps, you can build without a key sometimes, or generate a self-signed key.

Let me create a build script or Makefile to make it easier. Actually, a simple shell script would help:

```bash
#!/bin/bash
# build.sh for fēnix 3 breath-hold app

SDK=$(cat ~/.Garmin/ConnectIQ/current-sdk.cfg 2>/dev/null)
if [ -z "$SDK" ]; then
    echo "Connect IQ SDK not found. Install via SDK Manager first."
    exit 1
fi

KEY=${DEV_KEY:-developer_key.der}

if [ ! -f "$KEY" ]; then
    echo "Generating developer key..."
    openssl genrsa -out developer_key.pem 4096
    openssl pkcs8 -topk8 -inform PEM -outform DER -in developer_key.pem -out developer_key.der -nocrypt
fi

mkdir -p bin
$SDK/bin/monkeyc \
    -d fenix3 \
    -f monkey.jungle \
    -o bin/FreediveBreath_fenix3.prg \
    -y $KEY

echo "Built bin/FreediveBreath_fenix3.prg"
```

That's useful. Let me create it.

Also, a `.gitignore`:
```
bin/
*.prg
*.iq
developer_key.pem
developer_key.der
*.swp
```

Let me write these files.

write

#!/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."

# Build outputs
bin/
*.prg
*.iq

# Developer signing keys
*.pem
*.der

# Editor / OS noise
*.swp
*.swo
*~
.DS_Store
.vscode/settings.json


Successfully wrote 1467 bytes to code/garmin-freedive-app/build.sh


Successfully wrote 138 bytes to code/garmin-freedive-app/.gitignore


(no output)


Now let me do a final review of the project files. Let me list them and read the key files once more.

bash

cd code/garmin-freedive-app && find . -type f | sort


./build.sh
./.gitignore
./IMPLEMENTATION_PLAN.md
./manifest.xml
./monkey.jungle
./README.md
./resources/resources.xml
./source/BreathHoldApp.mc
./source/BreathHoldDelegate.mc
./source/BreathHoldView.mc


Good. Now let me think about whether there's a significant issue I missed.

One important thing: in the manifest, the `id` attribute. I used `a1b2c3d4e5f678901234567890123456`. Connect IQ manifest IDs are usually 32-character UUIDs without dashes. My string is 32 characters but the pattern is not a valid UUID. It might work, but to be safe, I should use a proper UUID format. Let me generate one.

Actually, the format is just 32 hex characters. `a1b2c3d4e5f678901234567890123456` has 32 hex chars. It's fine. But to be more realistic, let me use a random UUID-like string. I'll generate one.

Actually, I can use a known test pattern. It doesn't matter for sideloading. But let me use a proper random UUID.

python3 -c "import uuid; print(str(uuid.uuid4()).replace('-',''))"


edit

<iq:application entry="BreathHoldApp" id="b896271ba7e04e12988fd03fb8714fc3" minApiLevel="1.4.0" type="watch-app" name="@Strings.AppName">

<iq:application entry="BreathHoldApp" id="a1b2c3d4e5f678901234567890123456" minApiLevel="1.4.0" type="watch-app" name="@Strings.AppName">


Successfully replaced 1 block(s) in code/garmin-freedive-app/manifest.xml.

  1 <?xml version="1.0"?>
  2 <iq:manifest xmlns:iq="http://www.garmin.com/connect-iq" version="1">
- 3     <iq:application entry="BreathHoldApp" id="a1b2c3d4e5f678901234567890123456" minApiLevel="1.4.0" type="watch-app" name="@Strings.AppName">
+ 3     <iq:application entry="BreathHoldApp" id="b896271ba7e04e12988fd03fb8714fc3" minApiLevel="1.4.0" type="watch-app" name="@Strings.AppName">
  4         <iq:products>
  5             <iq:product id="fenix3"/>
  6             <iq:product id="fenix3_hr"/>
  7         </iq:products>
    ...

--- code/garmin-freedive-app/manifest.xml
+++ code/garmin-freedive-app/manifest.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <iq:manifest xmlns:iq="http://www.garmin.com/connect-iq" version="1">
-    <iq:application entry="BreathHoldApp" id="a1b2c3d4e5f678901234567890123456" minApiLevel="1.4.0" type="watch-app" name="@Strings.AppName">
+    <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>


Now, one more thought: the user said "focus on the fenix 3 with sideloading instructions". I have done that. The project is ready for them to build.

I should present a final summary to the user, explaining:
1. The pivot to fēnix 3-only breath-hold app
2. What I built
3. Where the files are
4. Next steps to build and sideload
5. Reminder that depth is not possible on fēnix 3

I should be clear and concise.

Also, I should note that I did not compile locally because the Connect IQ SDK is not installed in this environment, but the code is written for API 1.4 and should compile with the SDK Manager.

Let me also maybe add a note about trying to install the SDK. Actually, I can offer to help install it if needed. But the user has the instructions.

Let me write the final response.

Done. I pivoted the project to a **fēnix 3 / fēnix 3 HR breath-hold app** since raw depth measurement is impossible on that watch, and the Venu 2 has the ANT+/touchscreen problems you discovered.

## What I built

All files are in:

**`~/code/garmin-freedive-app/`**

| File | Purpose |
|------|---------|
| `manifest.xml` | Targets `fenix3` and `fenix3_hr`, API 1.4.0, with `Sensor`, `ActivityRecording`, `FitContributor` permissions |
| `monkey.jungle` | Build configuration |
| `source/BreathHoldApp.mc` | App entry point; enables heart-rate sensor |
| `source/BreathHoldView.mc` | UI and state machine: prep → hold → recovery → saved; records breath-hold time and last HR to FIT |
| `source/BreathHoldDelegate.mc` | Button handling (START, UP/DOWN for contractions, BACK to discard/exit) |
| `resources/resources.xml` | App name string |
| `build.sh` | Command-line builder; auto-generates a developer key if missing |
| `README.md` | Full usage and sideloading instructions |
| `IMPLEMENTATION_PLAN.md` | Feasibility analysis + revised scope |

## What the app does

- **Prep breathe** countdown (2 min, configurable)
- **Breath-hold** timer counts up; press **UP/DOWN** to count contractions
- **Recovery** countdown (2 min, configurable)
- **Heart rate** from HRM‑Tri / HRM‑Swim ANT+ strap (or fēnix 3 HR wrist sensor)
- Saves a FIT activity with custom fields:
  - `hold_time_s`
  - `last_hr_bpm`

## What it does **not** do

- **No depth measurement.** The fēnix 3 cannot expose raw ambient pressure, so this cannot be a dive computer.

## Build & sideload

1. Install the **Connect IQ SDK Manager** and download the latest SDK + **fēnix 3** / **fēnix 3 HR** device files.
2. Run `./build.sh` in the project folder.
3. Copy the generated `.PRG` to the watch’s `GARMIN/APPS/` folder over USB.
4. Launch **Freedive Breath** from the watch’s Apps menu.

Full details are in `README.md`.

I did not compile locally because the Connect IQ SDK is not installed in this environment, but the code is written specifically for API 1.4.0 and uses only APIs available on the fēnix 3. If you hit build errors, paste them here and I’ll fix them.


Create a structured implementation plan for: are you sure that the fenix does not measure the ambient pressure??

Follow this format:
1. **Goal** — one-sentence objective
2. **Constraints** — existing code, dependencies, tests, or assumptions to respect
3. **Approach** — high-level strategy and rationale
4. **Steps** — numbered, actionable tasks small enough to verify one at a time
5. **Verification** — how to confirm each step works (tests, logs, manual checks)
6. **Risks & Rollbacks** — what could go wrong and how to undo safely
7. **Open Questions** — anything that needs clarification before starting

Use the codebase context as needed. Do not write implementation code yet; only produce the plan.


The user wants a structured implementation plan for the question: "are you sure that the fenix does not measure the ambient pressure??" They want to verify/disprove my earlier claim that the fēnix 3 cannot measure ambient pressure accurately for depth calculation.

This is essentially a research/verification task. I need to:
1. Investigate whether fēnix 3 has ambient pressure measurement capabilities
2. Check if Sensor.Info.pressure on fēnix 3 gives raw or calibrated pressure
3. Check forum discussions about fēnix 3 raw pressure
4. Possibly test with actual data if available
5. Produce a structured plan following the requested format

Let me use context-mode tools for this research. I should search the indexed CIQ docs and also do web research.

I'll investigate thoroughly before writing the plan. Let me verify the fēnix 3 pressure API claims against the indexed docs and fresh sources.

echo "Searching indexed CIQ docs for fēnix 3 pressure capabilities..."

echo "Searching session memory for prior fēnix 3 pressure conclusions..."

fenix 3 raw ambient pressure Sensor.Info pressure

fēnix 3 depth pressure barometer calibrated sea level

Sensor.Info.pressure fēnix 3 altitude depth unreliable

fēnix 3 Connect IQ API 1.4 ambientPressure rawAmbientPressure supported devices


Executed 2 commands (14 lines, 0.3KB). Indexed 2 sections. Searched 4 queries.

## Commands

- indexed_fenix_pressure: `echo "Searching indexed CIQ docs for fēnix 3 pressure capabilities..."`
- session_fenix_pressure: `echo "Searching session memory for prior fēnix 3 pressure conclusions..."`

## Indexed Sections

- indexed_fenix_pressure (0.2KB)
- session_fenix_pressure (0.2KB)

## fenix 3 raw ambient pressure Sensor.Info pressure

### Class: Toybox.Activity.Info > Instance Attribute Details > var rawAmbientPressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null** (1)
### var rawAmbientPressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

The raw ambient pressure in Pascals (Pa).

This returns ambient (local) barometric pressure as measured by the internal pressure sensor. The data is the temperature compensated information read directly from the internal sensor.

Since:

API Level 2.4.0

Supported Devices:

### Class: Toybox.Sensor.Info > Instance Attribute Details > var pressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**
### var pressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

The barometric pressure in Pascals (Pa).

This returns barometric pressure calibrated to sea level. Since pressure varies dues to several factors, a GPS-based altitude must first be obtained, then the ambient (local) pressure is measured by the pressure sensor before conversion to a calibrated barometric pressure value.

Since:

API Level 1.0.0

Returns:

*   [Lang.Float](../../Toybox/Lang/Float.html)

### indexed_fenix_pressure
# indexed_fenix_pressure

$ echo "Searching indexed CIQ docs for fēnix 3 pressure capabilities..."

Searching indexed CIQ docs for fēnix 3 pressure capabilities...

## fēnix 3 depth pressure barometer calibrated sea level

### Class: Toybox.Activity.Info > Instance Attribute Details > var meanSeaLevelPressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null** (1)
### var meanSeaLevelPressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

The mean sea level barometric pressure in Pascals (Pa).

This returns barometric pressure calibrated to sea level. Since pressure varies dues to several factors, a GPS-based altitude must first be obtained, then the ambient (local) pressure is measured by the pressure sensor before conversion to a calibrated barometric pressure value.

Since:

API Level 2.4.0

Supported Devices:

### Class: Toybox.Sensor.Info > Instance Attribute Details > var pressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**
### var pressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

The barometric pressure in Pascals (Pa).

This returns barometric pressure calibrated to sea level. Since pressure varies dues to several factors, a GPS-based altitude must first be obtained, then the ambient (local) pressure is measured by the pressure sensor before conversion to a calibrated barometric pressure value.

Since:

API Level 1.0.0

Returns:

*   [Lang.Float](../../Toybox/Lang/Float.html)

### Class: Toybox.Sensor.Info > Instance Attribute Details > var altitude as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**
### var altitude as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

The altitude above mean sea level in meters (m).

Elevation is derived from the most accurate source: Barometer or GPS in order of descending accuracy. If no GPS is present, then barometer readings will be used.

Since:

API Level 1.0.0

See Also:

*   [Meters above sea level](https://en.wikipedia.org/wiki/Metres_above_sea_level)

*   [Positional Altitude](../../Toybox/Position/Info.html#altitude-var)

Returns:

*   [Lang.Float](../../Toybox/Lang/Float.html)

## Sensor.Info.pressure fēnix 3 altitude depth unreliable

### Class: Toybox.Activity.Info > Instance Attribute Details > var altitude as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**
### var altitude as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

The altitude above mean sea level in meters (m).

Elevation is derived from the most accurate source: Barometer or GPS

Since:

API Level 1.0.0

Returns:

*   [Lang.Float](../../Toybox/Lang/Float.html)

### Class: Toybox.Sensor.Info > Instance Attribute Details > var altitude as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**
### var altitude as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

The altitude above mean sea level in meters (m).

Elevation is derived from the most accurate source: Barometer or GPS in order of descending accuracy. If no GPS is present, then barometer readings will be used.

Since:

API Level 1.0.0

See Also:

*   [Meters above sea level](https://en.wikipedia.org/wiki/Metres_above_sea_level)

*   [Positional Altitude](../../Toybox/Position/Info.html#altitude-var)

Returns:

*   [Lang.Float](../../Toybox/Lang/Float.html)

### Class: Toybox.Sensor.Info > Instance Member Summary [collapse](#)
## Instance Member Summary [collapse](#)

*   [**accel**](#accel-var "accel (Var)") as [Lang.Array](../../Toybox/Lang/Array.html)<[Lang.Number](../../Toybox/Lang/Number.html)\> or **Null**

    The accelerometer reading of the x, y, and z axes as an Array of [Number](../../Toybox/Lang/Number.html) values in millig-units.

*   [**altitude**](#altitude-var "altitude (Var)") as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

    The altitude above mean sea level in meters (m).

*   [**cadence**](#cadence-var "cadence (Var)") as [Lang.Number](../../Toybox/Lang/Number.html) or **Null**

    The cadence in revolutions per minute (rpm).

*   [**heading**](#heading-var "heading (Var)") as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

    The true north referenced heading in radians.

*   [**heartRate**](#heartRate-var "heartRate (Var)") as [Lang.Number](../../Toybox/Lang/Number.html) or **Null**

    The heart rate in beats per minute (bpm).

*   [**mag**](#mag-var "mag (Var)") as [Lang.Array](../../Toybox/Lang/Array.html)<[Lang.Number](../../Toybox/Lang/Number.html)\> or **Null**

    The magnetometer reading of the x, y, and z axes as an Array of [Number](../../Toybox/Lang/Number.html) values in milliGauss (mG).

*   [**oxygenSaturation**](#oxygenSaturation-var "oxygenSaturation (Var)") as [Lang.Number](../../Toybox/Lang/Number.html) or **Null**

    The current oxygen saturation in percent (%).

*   [**power**](#power-var "power (Var)") as [Lang.Number](../../Toybox/Lang/Number.html) or **Null**

    The power in Watts (W).

*   [**pressure**](#pressure-var "pressure (Var)") as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

    The barometric pressure in Pascals (Pa).

*   [**speed**](#speed-var "speed (Var)") as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

    The speed in meters per second (m/s).

*   [**temperature**](#temperature-var "temperature (Var)") as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

    The temperature in degrees Celsius (C).

## fēnix 3 Connect IQ API 1.4 ambientPressure rawAmbientPressure supported devices

### activity_info_supported_devices
# activity_info_supported_devices

$ echo "Querying indexed CIQ Activity.Info API for rawAmbientPressure supported devices..."

Querying indexed CIQ Activity.Info API for rawAmbientPressure supported devices...

### Class: Toybox.Activity.Info > Instance Attribute Details > var rawAmbientPressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null** (1)
### var rawAmbientPressure as [Lang.Float](../../Toybox/Lang/Float.html) or **Null**

The raw ambient pressure in Pascals (Pa).

This returns ambient (local) barometric pressure as measured by the internal pressure sensor. The data is the temperature compensated information read directly from the internal sensor.

Since:

API Level 2.4.0

Supported Devices:

### Untitled (1)
Compatible Devices | Connect IQ | Garmin Developers

*   [Overview](/connect-iq/overview/)

*   [Compatible Devices](/connect-iq/compatible-devices/)

*   [API Docs](/connect-iq/api-docs/)

*   [Get the SDK](/connect-iq/sdk/)

*   [Submit an App](/connect-iq/submit-an-app/)

*   [Stay Informed](/connect-iq/stay-informed/)

*   [Connect IQ Basics](/connect-iq/connect-iq-basics/)

*   [Monkey C](/connect-iq/monkey-c/)

*   [Core Topics](/connect-iq/core-topics/)

*   [User Experience Guidelines](/connect-iq/user-experience-guidelines/)

*   [Personality Library](/connect-iq/personality-library/)

*   [Connect IQ FAQ](/connect-iq/connect-iq-faq/)

*   [Reference Guides](/connect-iq/reference-guides/)

*   [App Review Guidelines](/connect-iq/app-review-guidelines/)

*   [Monetization](/connect-iq/monetization/)

*   [Device Reference](/connect-iq/device-reference/)

MenuCompatible Devices

×

Device Type:

AllWatches/WearablesOutdoor HandheldsEdge

Supported CIQ Version:

All

*   [

    All](#)
*   [

    1.2](#)
*   [

    1.4](#)
*   [

    2.4](#)
*   [

    3.0](#)
*   [

    3.1](#)
*   [

    3.2](#)
*   [

    3.3](#)
*   [

    3.4](#)
*   [

    5.0](#)
*   [

    5.1](#)
*   [

    5.2](#)
*   [

    6.0](#)

| Watches/Wearables | Screen Resolution | Screen Shape | Screen Technology | API Level |
| --- | --- | --- | --- | --- |
|
![](https://res.garmin.com/en/products/010-03010-00/v/cf-sm.jpg)

Approach® S50

 | 390 x 390 | round | AMOLED | 5.1 |
|

![](https://static.garmincdn.com/en/products/010-01702-00/v/cf-sm.jpg)

Approach® S60

 | 240 x 240 | round | Memory-In-Pixel (64 colors) | 2.4 |
|

![](https://static.garmincdn.com/en/products/010-02200-00/v/cf-sm-bc8fd553-731f-4fc3-98dd-ef9eb4d7d597.jpg)

Approach® S62

 | 260 x 260 | round | Memory-In-Pixel (64 colors) | 3.0 |
|

![](https://res.garmin.com/en/products/010-02746-01/v/cf-sm.jpg)

Approach® S70 42mm

 | 390 x 390 | round | AMOLED | 5.1 |
|

![](https://res.garmin.com/en/products/010-02746-12/g/cf-sm.jpg)

Approach® S70 47mm

 | 454 x 454 | round | AMOLED | 5.1 |
|

![](https://static.garmincdn.com/en/products/010-02172-41/g/cf-sm-0a318bb6-9baa-4c5d-b344-19de0c656cd7.jpg)

Captain Marvel

 | 218 x 218 | round | Memory-In-Pixel (64 colors) | 3.3 |
|

![](https://static.garmincdn.com/en/products/010-02173-41/g/cf-sm-11d59984-bc91-49ed-bfe9-d2ddc7c627a8.jpg)

D2™ Air

 | 390 x 390 | round | AMOLED | 3.2 |
|

![](https://static.garmincdn.com/en/products/010-02496-09/v/cf-sm-f80be13b-f121-40f1-ba82-d2571df9c634.jpg)

D2™ Air X10

 | 416 x 416 | round | AMOLED | 5.0 |
|

![](https://static.garmincdn.com/en/products/010-01338-31/v/cf-sm.jpg)

D2™ Bravo

 | 218 x 218 | round | Memory-In-Pixel (16 colors) | 1.4 |
|

![](https://static.garmincdn.com/en/products/010-01338-36/v/cf-sm.jpg)

D2™ Bravo Titanium

 | 218 x 218 | round | Memory-In-Pixel (16 colors) | 1.4 |
|

![](https://static.garmincdn.com/en/products/010-01733-30/v…

> **Scope:** Queries searched the entire persistent index (query_scope: "global").


Question: Garmin Fenix 3 raw pressure Sensor.Info.pressure barometer depth freediving connect iq

Question: "fēnix 3" "raw ambient pressure" OR "Sensor.Info.pressure" depth freediving garmin forum

Question: Garmin Fenix 3 barometric sensor pressure altitude weather calibration Connect IQ API 1.4