1. How to properly override source .mc files for specific device in jungles? - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/306054/how-to-properly-override-source-mc-files-for-specific-device-in-jungles
   How to properly override source .mc files for specific device in jungles? - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- State Not Answered
- +1 person also asked this people also asked this
- Replies 8 replies
- Subscribers 22 subscribers
- Views 1792 views
- Users 0 members are here

Options

Related

# How to properly override source .mc files for specific device in jungles?

Hi,

I have a problem with overriding source .mc files. I'm trying to set source for low memory devices (fr45, swim2).

I created .mc files and set source path in .jungle file.

Looks like it works properly for fr45 but not for Swim2.

Is this a good way to override source files?

Any help would be appreciated. Thanks.

Here's part of my jungle file:

```
project.manifest = manifest.xml

# Reset the base source path to include only source files in the source folder. The
# default source path includes source files in the project folder and all subfolders.
base.sourcePath = source

# Low memory devices.
fr45.sourcePath = source-lowmem
garminswim2.sourcePath = source-lowmem
```

- Sign in to reply
- Cancel

Monkey doesn't support any kind of compiler directives in source code?

Monkey doesn't support any kind of compiler directives in source code?

If you mean line-by-line conditional compilation like C/C++/C#, the answer is no.

You can exclude modules, classes, methods and non-local variables using annotations and build exclusions in jungle files. This allows you to have multiple versions of the same method for different devices, for example. This isn't a perfect solution for removing code for low memory devices, because you end up having to implement functions with empty bodies (for example), which wastes a small amount of memory.

If you use build exclusions and empty functions, this third-party prettier extension may be able to optimize away the empty functions.

You could also roll your own line-by-line conditional compilation solution with the C preprocessor. For example:

You can also use jungles to include different mc files based on the device or feature. One example for me is for mapping, I have different files for devices with on-device maps and those where I draw a breadcrumb trail. Less clutter than annotations, no functions with empty bodies, and an easier way to reduce size.

Here's a bit of the monkey.jungle I use for maps/no maps:

```
project.manifest = manifest.xml
srcBase=source
resBase=resources

#maps Stuff
base.sourcePath=$(srcBase);source-nomaps
base.resourcePath=$(resBase)

# tested with maps
fenix5x.sourcePath = $(srcBase);source-maps
fenix5x.resourcePath = $(resBase);resources-maps
```

Different source for maps, as well as some additional resources I use with the maps.

You can also use jungles to include different mc files based on the device or feature. One example for me is for mapping, I have different files for devices with on-device maps and those where I draw a breadcrumb trail. Less clutter than annotations, no functions with empty bodies, and an easier way to reduce size.

That is literally what the original post said and they posted an example similar to yours. Not to mention that they said it didn't work for them with the garminswim2 device (for whatever reason).

The person who responded to that asked if Monkey C supports compiler directives, which implies that they understood the original post and they are asking for an alternative. I responded to that person.

No offense but do you bother to actually read any of the posts and understand the context in which questions are asked and answers are given?

Thank you all for the reply.

I have not tested on the real device but on the simulator I got:

```
Error: Out Of Memory Error
Details: Failed invoking <symbol>
Stack:
  - onUpdate() at ...\SimpleTDB\source-lowmem\SimpleTDBView.mc:140 0x100005b4
```

I thought that it doesn't work because I saw that memory usage on the simulator is higher on swim2 than fr45. But when I have read the source path in error stack I understood that swim2 uses correct source path (source-lowmem). I don't know why I didn't notice it before. I'm sorry for this.

But if the path is correct why fr45 consumes 41.8kb and swim2 - 46.5 and I got out of memory error on the sim? Looks like I missed something. Shouldn't the memory usage of these devices be the same?

I think I need more coffee and one day off to figure it out.

But if the path is correct why fr45 consumes 41.8kb and swim2 - 46.5 and I got out of memory error on the sim? Looks like I missed something. Shouldn't the memory usage of these devices be the same?

That's weird. For both devices, compiler.json and simulator.json are almost identical, except fr45 lacks antialiasing support. I built the Analog sample for both devices, and their memory usage is identical.

Thanks Jim! Very helpful

2. initial dev env setup on mac osx arm - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/423312/initial-dev-env-setup-on-mac-osx-arm
   initial dev env setup on mac osx arm - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- State Suggested Answer
- Replies 3 replies
- Answers 2 answers
- Subscribers 18 subscribers
- Views 635 views
- Users 0 members are here

Options

Related

# initial dev env setup on mac osx arm

Hi

I'm facing issues when trying to setup my dev environment

I'm on macbook pro, arm based, using vsc.

I installed connectIq, added the monkey C plugin to vsc

I created a der file and trying to build a simple hello world app using

> monkeyc -d venu2 -f monkey.jungle -o build/app.prg -y ./keys/developer_key.der

I join my monkey.jungle and manifest.xml

and... no way for me building my app

```
A valid 'project.manifest' property was not defined within a jungle file.
ERROR: .../monkey.jungle:3: no viable alternative at input 'project.sourcePath'
ERROR: .../monkey.jungle:4: no viable alternative at input 'project.resourcePath'
```

it is probably something stupid, but ...

thanks for reading

```
<?xml version="1.0"?>
<iq:manifest version="3" xmlns:iq="http://www.garmin.com/xml/connectiq">
  <iq:application
      id="08ea74b8-ca2b-493b-b858-56e249e64a62"
      type="watch-app"
      entry="TaddleApp"
      name="Taddle"
      minApiLevel="3.3.0">
    <iq:products>
      <iq:product id="venu2"/>
    </iq:products>
    <iq:permissions/>
    <iq:languages/>
    <iq:barrels/>
    <iq:trialMode enable="false">
        <iq:unlockURL>https://www.example.com/your_unlock_url</iq:unlockURL>
    </iq:trialMode>
    <iq:activityFilter/>
  </iq:application>
</iq:manifest>
```

```
project.manifest=manifest.xml

project.sourcePath=src
project.resourcePath=resources
// venu2.resourcePath=$(project.resourcePath); resources-venu2
```

- Sign in to reply
- Cancel

## Top Replies

- You may want to start with one of the samples in the samples folder in the sdkvsc command palette>monkey c: open samples folderor use one of the vsc templatesvsc command palette>monkey c: new pr…

## All Replies

You may want to start with one of the samples in the samples folder in the sdk

vsc command palette>monkey c: open samples folder

or use one of the vsc templates

vsc command palette>monkey c: new project

get one of this to compile without changing anything.

As the errors indicate (perhaps poorly), "project.sourcePath" and "project.resourcePath" are not valid project qualifiers in a jungle file. See: https://developer.garmin.com/connect-iq/reference-guides/jungle-reference/

The correct syntax for what you're trying to accomplish would be:

`project.manifest=manifest.xml``base.sourcePath=src``base.resourcePath=resources``venu2.resourcePath=$(base.resourcePath);resources-venu2`

or even better:

`project.manifest=manifest.xml``base.sourcePath=src``base.resourcePath=resources``venu2.resourcePath=$(venu2.resourcePath);resources-venu2`

The reason this is better is that venu2 has other built-in resource paths [*], like resources-round-416x416/ and resources-round/, which would be ignored the other way.

Such apparently recursive references are allowed in jungle files because when you type X=$(X) here, $(X) is evaluated as the value of X *prior* to assigning X's new value. This also means that the order of assignments in jungle files is significant. If you're familiar with makefiles, assignment in jungle files is like " simply expanded variable assignment" in makefiles, as opposed to " recursively expanded variable assignment")

However, base.sourcePath and base.resourcePath are already initialized by default as follows [*]:

`base.sourcePath = .\**.mc # this means all *.mc files in your project``base.resourcePath = resources`

---

So all you really need in your monkey.jungle is this:

`project.manifest=manifest.xml``venu2.resourcePath=$(venu2.resourcePath);resources-venu2`

---

[*] All of this can be seen by looking at the default.jungle file in the current SDK's bin/ folder. You need to successfully build at least once for this file to be auto-generated.

1) Open command palette (CMD-SHIFT-P on Mac)

2) Select "Monkey C: Open Samples Folder"

This opens CURRENT_SDK/samples/

3) Navigate up 1 parent folder so you're at CURRENT_SDK/. Navigate to the bin/ folder

4) Open default.jungle

Thank a lot. I started from scratch and now is correct

3. Relative paths in local variables in jungle files are resolved incorrectly. - Connect IQ Bug Reports - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/i/bug-reports/relative-paths-in-local-variables-in-jungle-files-are-resolved-incorrectly
   Relative paths in local variables in jungle files are resolved incorrectly. - Connect IQ Bug Reports - Connect IQ - Garmin Forums

Acknowledged

- View Status History
- More
- Cancel

# Relative paths in local variables in jungle files are resolved incorrectly.

According to the jungle reference(see the last paragraph), relative paths in jungle files are resolved relative to the jungle file they appear in. But relative paths in local variables are actually resolved relative to the manifest file. In the "default" setup, the manifest file is in the same directory as the jungle file, so this doesn't usually matter, but when they're not the same, it results in strange behavior.

eg, given a project with monkey.jungle at the root, manifest.xml in the sub-directory manifest, source files in source, and resource files in resources, the following jungle file works:

```
project.manifest = manifest/manifest.xml

base.sourcePath = ./source
base.resourcePath = ./resources

```

Note that we have to be explicit about the source and resource paths, because the paths in default.jungle are resolved against the manifest.xml file's directory. So in this case, without explicitly setting sourcePath and resourcePath, it would look for sources in manifest/**.mc, and resources in manifest/resources.

But if you rewrite that file using local variables:

```
project.manifest = manifest/manifest.xml

source = ./source
resources = ./resources

base.sourcePath = $(source)
base.resourcePath = $(resources)

```

it now looks for source files in manifest/source and resource files in manifest/resources. In order to make this jungle file work, we have to rewrite it as:

```
project.manifest = manifest/manifest.xml

source = ../source
resources = ../resources

base.sourcePath = $(source)
base.resourcePath = $(resources)

```

This is clearly not what the documentation says, and is really surprising. You would expect local variables to just be substituted in, and not cause semantic differences.

I realize that changing behavior at this point could break existing projects - although it's unusual for the manifest to be in a different directory from the jungle. But if that's deemed to be too high risk, the documentation should be updated to reflect the actual behavior.

- Sep 17, 2024
- View Status History
- More
- Cancel

This was fixed.

The behavior was changed in 8.0.0 to match the documentation.

Or we could have our cake and eat it too, if they decided to both:

- document the existing behavior correctly

- add a new kind of variable substitution which is purely textual: e.g. ${FOO}

4. How to exclude files and folders with Jungles? - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/6376/how-to-exclude-files-and-folders-with-jungles
   How to exclude files and folders with Jungles? - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- Replies 12 replies
- Subscribers 15 subscribers
- Views 3509 views
- Users 0 members are here

Options

Related

# How to exclude files and folders with Jungles?

Hello!With Jungles, it's very clear how we can exclude annotations, but is it possible to exclude files and folders ?According to the developer documentation, Build Exclusions & Jungles are compatible, but : "Build exclusions provide a lot of the same capabilities as Jungle files, and will eventually be removed in a future Connect IQ release. If used in combination with a Jungle file, build exclusions will continue to work, but a warning will be generated indicating that they have been deprecated."I'm excluding files and folders using Build Exclusions and all is working fine (and I receive the proper warning :) ), but is there any "excludeFile" or "excludeDir" options for Jungles?Thank you! :cool:

- Sign in to reply
- Cancel

Hi MarkusHave you an example of jungle file, because i can implemente in my developement.an example with f5.Ann....=oldversion ....Thank'sDidier

Hi riedid74, excuse me, do you need an example of my current .jungle file and a part of the code ?My goal is to move a build exclusion file like the next one to a jungle file:

>

It's ok for "exclude annotiation" to "[model].excludeAnnotations", but I don't know if it's possible to move the "exclude dir" part ... (I imagine that is still not possible ...).

in build i don't have problem in implement the exclude annotation but i can't make it in a jungle file. If you have an example in jungle format of a build file convertion. Thank's Didier

you can't exclude files and folders, jungles use an inclusive pattern for files and folderseg:`fr735xt.sourcePath=$(fr735xt.sourcePath);sourcefolder1\source1.mc;sourcefolder1\source2.mc;sourcefolder2\*.mc` I also had to twist my brains as it takes some time adjusting to including from excluding, but the inclusive pattern actually makes more sense (to me) now.to exclude annotations use this:`fr735xt.excludeAnnotations=roundImpl`

Thank you! :cool:

Thank you peterdedecker to show me the direction!!!! :):)I just post the solution I used for my monkey.jungle because it can help others.In my project, I have many folders on the top level, and this is why I want all my parts of code under the "source" folder, with some parts distributed in subfolders.I have a "trash" folder which contains some code that I don't want to be compiled.It's just for classes that I only need for testing purposes and when I need one of them, I move it to the "source" folder.Regarding the default.jungle file (included with the sdk), I adapted the monkey.jungle file in my project and now I can exclude the folder "source/trash" for all the devices this way:`# Exclude "trash" folderbase.sourcePath = source/*.mc; source/class/*.mc`

I don't get it. I created folder on the same level as "source" dir, copied here class and immediately got error about duplicate classes. So from my testing I assuming that in base.sourcePath are all available *mc files in the project. So what is benefit of your first example with sourcefolder1? I get the usage of resoucePath, where you can override resources definitions by specific order. But you can't do it with sourcePath definition - so why to use it?

The problem is by default it looks for .mc files in general, so it's not just looking under source.

Here's a part of a jungles file where I have source, with the common stuff, and two other dirs at the same level - one for breadcrumbs, one for the mapping api.

```
srcBase=source
resBase=resources

base.sourcePath=$(srcBase);source-nomaps
base.resourcePath=$(resBase)

fenix5x.sourcePath = $(srcBase);source-maps
fenix5x.resourcePath = $(resBase);resources-maps
```

So, for the f5x, I use source-maps instead of souce-nomaps, and also use the resources for maps (really only the markers I use) in addition to the base resources

Thank you Jim for your example. This usage has much more sense. I was previously worried to override base.sourcePath, but hey, I should know best what code should be used in my project.

So, with this, everything but the f5x uses the breadcrumb code, and to add maps to other devices, I replicate the two lines for the f5x and change fenix5x to fenix5plus or whatever.

5. Monkey.jungle - sourcePath for product suddenly ignored - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
   URL: https://forums.garmin.com/developer/connect-iq/f/discussion/316609/monkey-jungle---sourcepath-for-product-suddenly-ignored
   Monkey.jungle - sourcePath for product suddenly ignored - Connect IQ App Development Discussion - Connect IQ - Garmin Forums

- State Suggested Answer
- Replies 8 replies
- Answers 1 answer
- Subscribers 17 subscribers
- Views 1695 views
- Users 0 members are here

Options

Related

# Monkey.jungle - sourcePath for product suddenly ignored

I have my monkey.jungle set up like this:

project.manifest = manifest.xml

base.sourcePath = source

fr645.sourcePath = $(fr645.sourcePath);source-fr645fr645m.sourcePath = $(fr645m.sourcePath);source-fr645

In source I have a .mc file containing this callback function:

myFunc() { qout("base source"); }

In source-fr645 I have another .mc file with an overwritten version:

myFunc() { qout("fr645 source"); }

Running a configuration for fr645 the console always shows:

base source

Strange thing is: Until spring it all worked like a charm. Now I needed to update to 2022 Eclipse version where I also installed the newest ConnectIQ SDKs. I re-opened my workspace, got some new warnings (the newer IDE seems to apply stricter rules in some cases) - and suddenly the code in my product folders is not called anymore. This applies to every product/configuration.

Any help would be highly appreciated since I'm stuck. I read all the docs and, more important, didn't change the monkey.jungle or the code.

- Sign in to reply
- Cancel

Just of out of curiosity: when the code was working, how were you able to avoid duplicate symbol errors during compilation? Did you also use exclude annotations?

What happens if you put a syntax error in one of the files under source-fr645? Does the build fail?

Good questions. Yes, syntax errors in the product files get detected. Duplicate symbols never were a problem - I understand that that is exactly the point (if I have a function in a product code it overrides the basic version). I use this to work with a basic setting of screen offsets for fields and modify them for various watch models.

I'm not exactly sure how that would work, unless you take steps to exclude specific base files or symbols from fr645.

Given two symbols with the same name, how would the compiler know which one takes precedence? Sure, you could say that it should use the symbol that was found in the source file that appears last in the list of folders, but what if you have the same symbol twice in the same folder?

e.g. What if you have myFunc() twice in source/foo.mc or twice in source-fr645/foo.mc?

I tried a really simple test as follows:

`monkey.jungle:`

`base.sourcePath = source``fr645.sourcePath = $(fr645.sourcePath);source-fr645`

`source/base.mc:``function foo() {``   return "base";``}`

`source-fr645/override.mc:`

`function foo() {``  return "fr645";``}`

As expected, the compiler gave me the following error when I built for fr645:

ERROR: ... Redefinition of 'foo' in '$'. Previous definition at ...

That's why I was asking how it worked before. I would probably need to see more details about your project. I guess the tough part is you have no way to demonstrate that it used to work, unless you are using Windows and you have a restore point from before Eclipse was upgraded. (But then again who knows if all the relevant files are even covered by the System Restore.)

If it was working before, perhaps it was a bug that has been fixed. However, I went back to SDK 4.0.6 and the build still fails the same way.

Yes, syntax errors in the product files get detected.

So that proves that the source path actually isn't ignored.

The real question is how is your project structured such that:

- you were able to "override" symbols without the use of exclude annotations and without running into duplicate symbol errors

- why is the compiler now choosing to use the base symbols (still without errors)

Right - or, how can I use annotations to achieve the former behaviour?

how can I use annotations to achieve the former behaviour?

You can do something like the following, but I'm still curious how you're finding yourself in your current situation. If there's some bug or quirk in your app, it might interfere with anything else you try to do here.

As far as I know, code symbols can't be simply overridden in the same way that resources can, hence the duplicate symbol errors. But maybe I'm missing something here.

e.g. Suppose I have 3 "versions" of code: base, fr245 family and fr645 family.

monkey.jungle:

```
base.excludeAnnotations = fr645_fam;fr245_fam
fr245.excludeAnnotations = default;fr645_fam
fr245m.excludeAnnotations = default;fr645_fam
fr645.excludeAnnotations = default;fr245_fam
fr645m.excludeAnnotations = default;fr245_fam
```

(Note that the device-specific excludeAnnotations are written so they do *not* preserve their default values, because you don't want them to receive the value of base.excludeAnnotations.)

data field view (I will set a different label for each family)

```
import Toybox.Activity;
import Toybox.Lang;
import Toybox.Time;
import Toybox.WatchUi;

class TestDataFieldView extends WatchUi.SimpleDataField {

    (:default) function getLabel() { return "default"; }
    (:fr645_fam) function getLabel() { return "fr645_fam"; }
    (:fr245_fam) function getLabel() { return "fr245_fam"; }

    // Set the label of the data field here.
    function initialize() {
        SimpleDataField.initialize();
        label = getLabel();
    }

    function compute(info as Activity.Info) as Numeric or Duration or String or Null {
        return 0.0;

    }

}
```

A different way of doing things would be to cleanly split the code into folders:

- shared source

- non-645 / default source

- 645 source

e.g.

monkey.jungle:

`base.source = source;source-default``fr645.source = source;source-645``fr645m.source = source;source-645`

This would be a lot simpler than exclude annotations, especially if you start supporting differences between additional "families"

- source = source that everyone wants

- source-default = source that only non-overridden devices want

Might not be feasible depending on how your code is structured, unless you want to duplicate code or make extensive use of class inheritance. (Both of which may use more memory than the alternative.)

The splitting into folders is what I was actually doing. To make things more understandable I have restructured the folders like this:

- source (contains all the common code)

- source-default

- source-products

- source-vivoactive-hr (one of many prooducts)

There is a set of global vars with (dummy) screen coordinates, i.e.

var offset_X_altitude = 0;var offset_Y_altitude = 0;var offset_X_a2 = 0;

in the main module under source.

There is also a callback function under source-default

function setDeviceParameters(dc) { qout("setDeviceParameters global");}

which does actually nothing except a debug output.

The same function is in each of the product directories, i.e.

function setDeviceParameters(dc) {

qout("setDeviceParameters VIVOACTIVE_HR");

offset_X_altitude = 106; offset_Y_altitude = 24; offset_X_a2 = 147;}

where it is supposed to override the default coordinates.

Monkey.jungle says this now:

base.sourcePath = source;source-default

vivoactive_hr.sourcePath = source;source-products/source-vivoactive_hr

This works now!


It seems jungle files don't support env var substitution directly. So the simplest approach is to keep modifying the SDK default.jungle to point to the local Devices path, which is what I did.

For portability, I could have build_fenix5.sh override default.jungle when using a local .Garmin copy. But modifying SDK files during build is messy.

Given that the user is testing with a copied .Garmin, I think the current setup is acceptable. I'll just document what was done.

Actually, an alternative: since build_fenix5.sh now auto-discovers SDK, and we know the local Devices path, we can pass `--devices` option to monkeyc? Let me check monkeyc options.

Actually, I think monkeyc uses the jungle file for devicesPath. But there might be a `-i` or `--devices` flag. Let me check.

SDK=$(cat /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/current-sdk.cfg)
"$SDK/bin/monkeyc" --help 2>&1 | head -40


```shell
SDK=$(cat /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/current-sdk.cfg)
"$SDK/bin/monkeyc" --help 2>&1 | head -40
```

usage: monkeyc [-a <arg>] [-b <arg>] [--build-stats <arg>] [-d <arg>]
       [--debug-log-level <arg>] [--debug-log-output <arg>]
       [--disable-api-has-check-removal] [--disable-v2-opcodes] [-e]
       [--Eno-invalid-symbol] [-f <arg>] [-g] [-h] [-i <arg>] [-k] [-l <arg>]
       [-m <arg>] [--no-gen-styles] [-o <arg>] [-O <arg>] [-p <arg>] [-r] [-t]
       [-u <arg>] [-v] [-w] [-x <arg>] [-y <arg>] [-z <arg>]
-a,--apidb <arg>                  API import file
-b,--apimir <arg>                 API MIR file
   --build-stats <arg>            Print build stats [0=basic]
-d,--device <arg>                 Target device
   --debug-log-level <arg>        Debug logging verbosity [0=errors, 1=basic,
                                  2=intermediate, 3=verbose]
   --debug-log-output <arg>       Output log zip file
   --disable-api-has-check-removalDo not optimize out API has checks
   --disable-v2-opcodes           Do not use the v2 opcodes
-e,--package-app                  Create an application package.
   --Eno-invalid-symbol           Do not error when a symbol is found to be
                                  invalid
-f,--jungles <arg>                Jungle files
-g,--debug                        Print debug output
-h,--help                         Prints help information
-i,--import-dbg <arg>             Import api.debug.xml
-k,--profile                      Enable profiling support
-l,--typecheck <arg>              Type check [0=off, 1=gradual, 2=informative,
                                  3=strict]
-m,--manifest <arg>               Manifest file (deprecated)
   --no-gen-styles                Do not generate Rez.Styles module
-o,--output <arg>                 Output file to create
-O,--optimization <arg>           Optimization level [0=none, 1=basic, 2=fast
                                  optimizations, 3=slow optimizations]
                                  [p=optimize performance, z=optimize code
                                  space]
-p,--project-info <arg>           projectInfo.xml file to use when compiling
-r,--release                      Strip debug information
-t,--unit-test                    Enables compilation of unit tests
-u,--devices <arg>                devices.xml file to use when compiling
                                  (deprecated)
-v,--version                      Prints the compiler version
-w,--warn                         Show compiler warnings
-x,--excludes <arg>               Add annotations to the exclude list


There is `-u,--devices <arg>` but it's deprecated. Better to use jungle devicesPath.

Given the complexity, I'll leave the default.jungle absolute path as is and just make sure current-sdk.cfg points correctly. For the user's testing, this is sufficient.

Now I should also update the monkeyc wrapper script to be more robust. Let me check what I changed.


#!/bin/bash

MB_HOME="$( cd "$( dirname "$0" )" && pwd )"

# Prefer Java 17 for SDK compatibility (OpenJDK 26 can crash monkeybrains.jar).
if [ -x "/usr/lib/jvm/java-17-openjdk/bin/java" ]; then
    JAVA_BIN="/usr/lib/jvm/java-17-openjdk/bin/java"
else
    JAVA_BIN="java"
fi

# If a local .Garmin/ConnectIQ folder exists in the current working directory,
# use it as the user home so the SDK finds device files and config there.
if [ -d "$PWD/.Garmin/ConnectIQ" ]; then
    USER_HOME="$PWD"
else
    USER_HOME="$HOME"
fi

"$JAVA_BIN" -Xms1g -Dfile.encoding=UTF-8 -Dapple.awt.UIElement=true -Duser.home="$USER_HOME" -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.Monkeybrains "$@"


The monkeyc wrapper looks good. It prefers Java 17 and uses local .Garmin if present.

Now I think I've done enough for step 1. Let me summarize the work and ask the user if they want me to proceed with simulator testing (which requires GUI on their machine) or sideloading.

Actually, I can also do a quick static check on the build output. But the build is clean.

Let me also check git status to see all modified files.

cd /home/liph/code/garmin-freedive-app
git status --short


```shell
cd /home/liph/code/garmin-freedive-app
git status --short
```

Exit code: 128

stdout:

stderr:
fatal: not a git repository (or any parent up to mount point /mnt/flash1)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).


Not a git repo. That's fine. Let me just summarize.

Now, I should report to the user:
1. Build succeeded for fenix5plus
2. Fixed the Method() type errors by casting to Lang.Method in timer.start calls
3. Fixed deprecation warnings by switching to Application.Storage and Toybox.Activity constants
4. Updated build_fenix5.sh to support local .Garmin copy and auto-discover SDK
5. Updated current-sdk.cfg and SDK wrapper for Java 17 / local home
6. Simulator testing requires GUI, can't do headless

I should be clear that the SDK files inside .Garmin were modified, and that's tied to this project copy.

Also, I should mention the remaining minor warnings (container access) are benign.

Let me compose a concise response.

Done — `bin/Freedive_fenix5plus.prg` builds successfully.

## What was fixed

- **`method(...)` / Timer type errors**
  Added `using Toybox.Lang;` to the delegate files and cast every `method(:callback)` passed to `Timer.start(...)` as `Lang.Method` (e.g. `method(:onTimer) as Lang.Method`).

- **Deprecation warnings**
  - Replaced `Application.getApp().getProperty()` / `setProperty()` with `Application.Storage.getValue()` / `setValue()`.
  - Replaced `ActivityRecording.SPORT_*` / `SUB_SPORT_*` with `Toybox.Activity.SPORT_*` / `SUB_SPORT_*`.
  - Removed the unused `restS` local in `BreathingView`.

- **Build script / SDK setup**
  - `build_fenix5.sh` now prefers a local `.Garmin/ConnectIQ` folder in the project, auto-discovers the SDK under `.Garmin/ConnectIQ/Sdks/` if `current-sdk.cfg` is stale, and checks that `bin/monkeyc` is actually executable.
  - Updated `.Garmin/ConnectIQ/current-sdk.cfg` to point at the local SDK copy.
  - Updated the SDK `bin/monkeyc` wrapper to use Java 17 and to set `-Duser.home` to the project directory when a local `.Garmin/ConnectIQ` exists.
  - Updated `README.md` with Java 17 / local `.Garmin` copy notes.

## Current build output

```text
$ ./build_fenix5.sh
Building for fenix5plus...
BUILD SUCCESSFUL
Built: bin/Freedive_fenix5plus.prg
```

Three benign container-access warnings remain (`Cannot determine if container access is using container type`). They don't block the build or the app.

## Note on the simulator

The Connect IQ simulator needs a GUI, so it can't run in this headless environment. The next step is to test the `.prg` on your build machine with:

```bash
monkeydo bin/Freedive_fenix5plus.prg fenix5plus
```

or sideload it to the watch:

```bash
cp bin/Freedive_fenix5plus.prg /path/to/GARMIN/APPS/
```

Want me to do anything else on the code side before you try it on the watch/simulator?


❯ monkeydo bin/Freedive_fenix5plus.prg fenix5plus
zsh: command not found: monkeydo


`monkeydo` is in the SDK `bin` directory next to `monkeyc`, but it isn’t on your shell `PATH`. The `build_fenix5.sh` script only mentions copying the `.prg`, so the next step is to add simulator instructions to the README.

First, let me confirm where `monkeydo` lives and that it runs.

SDK=$(cat /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/current-sdk.cfg)
echo "SDK path: $SDK"
ls -la "$SDK/bin/monkeydo" 2>/dev/null || echo "monkeydo not found"
file "$SDK/bin/monkeydo" 2>/dev/null


```shell
SDK=$(cat /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/current-sdk.cfg)
echo "SDK path: $SDK"
ls -la "$SDK/bin/monkeydo" 2>/dev/null || echo "monkeydo not found"
file "$SDK/bin/monkeydo" 2>/dev/null
```

SDK path: /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2
-rwxr--r-- 1 liph liph 2773 Jul 27 07:33 /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2/bin/monkeydo
/home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2/bin/monkeydo: Bourne-Again shell script, ASCII text executable


#!/bin/bash
# Check for proper arguments. If the arguments aren't valid print the usage.
if [ -z "$2" ] || [ "$2" == "-n" ] || [ "$2" == "-a" ] || [ "$2" == "-t" ] || [ "$1" == "-h" ] || [ "$1" == "--help" ]; then

    echo "Pushes and runs a Connect IQ executable on the Connect IQ simulator. The simulator must be running for this command to succeed."
    echo
    echo "Usage: monkeydo executable device_id [-n] [-a additional_file] [-t | -t test_name]"
    echo "    executable - A Connect IQ executable to run."
    echo "    device_id  - The device to simulate."
    echo "    n          - When provided, the app will be run in Native Pairing mode."
    echo "    additional_file - When providing the additionalFile flag you must specify one or more additional files to send to the simulator in the form of '<source>:<dest>'."
    echo "    test_name  - When providing the test flag you may specify a specific test name."

else

    # Set the required variables
    MB_HOME="$( cd "$( dirname "$0" )" && pwd )"
    PRG_PATH="$1"
    FILE=`echo $(basename "$PRG_PATH" | tr '[:lower:]' '[:upper:]')`
    DEVICE_ID="$2"

    # Create variables for the optional arguments
    TEST_NAME=""
    TEST_FLAG=""
    ADDITIONAL_FILE_FLAG=""
    ADDITIONAL_FILE=""
    RUN_NATIVE_PAIRING_FLAG=""

    # Set the optional arguments
    if [ ! -z "$3" ]; then
        # Get the run native pairing argument
        if [ "$3" == "-n"  ]; then
            RUN_NATIVE_PAIRING_FLAG="-n"
            ADDITIONAL_FILE_FLAG=""
            TEST_FLAG=""
        # Get the addtional file arguments
        elif [ "$3" == "-a"  ]; then
            ADDITIONAL_FILE_FLAG="-a"
            index=4
            numberInputs=$#
            while [ $index -le $numberInputs ];
            do
               if [ "$4" == "-t" ]; then
                   TEST_FLAG="-t"
                   shift
                   shift
                   shift
                   shift
                   TEST_NAME="$@"
                   break;
                else
                   ADDITIONAL_FILE="${ADDITIONAL_FILE} $4"
                   let "index=index+1"
                   shift
                fi
            done
        # Get the test arguments
        elif [ "$3" == "-t"  ]; then
            TEST_FLAG="-t"
            if [ ! -z "$4" ]; then
                shift
                shift
                shift
                TEST_NAME="$@"
            fi
        else
            echo "ILLEGAL ARGUMENT: $3"
        fi
    fi

    # Push the executable to the simulator
    java -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.monkeydodeux.MonkeyDoDeux -f "$PRG_PATH" -d $DEVICE_ID -s "$MB_HOME"/shell $RUN_NATIVE_PAIRING_FLAG $ADDITIONAL_FILE_FLAG $ADDITIONAL_FILE $TEST_FLAG $TEST_NAME
fi


edit

else

    # Set the required variables
    MB_HOME="$( cd "$( dirname "$0" )" && pwd )"

    # Prefer Java 17 for SDK compatibility (OpenJDK 26 can crash monkeybrains.jar).
    if [ -x "/usr/lib/jvm/java-17-openjdk/bin/java" ]; then
        JAVA_BIN="/usr/lib/jvm/java-17-openjdk/bin/java"
    else
        JAVA_BIN="java"
    fi

    # If a local .Garmin/ConnectIQ folder exists in the current working directory,
    # use it as the user home so the SDK finds device files and config there.
    if [ -d "$PWD/.Garmin/ConnectIQ" ]; then
        USER_HOME="$PWD"
    else
        USER_HOME="$HOME"
    fi

    PRG_PATH="$1"

else

    # Set the required variables
    MB_HOME="$( cd "$( dirname "$0" )" && pwd )"
    PRG_PATH="$1"

    # Push the executable to the simulator
    "$JAVA_BIN" -Duser.home="$USER_HOME" -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.monkeydodeux.MonkeyDoDeux -f "$PRG_PATH" -d $DEVICE_ID -s "$MB_HOME"/shell $RUN_NATIVE_PAIRING_FLAG $ADDITIONAL_FILE_FLAG $ADDITIONAL_FILE $TEST_FLAG $TEST_NAME
fi

    # Push the executable to the simulator
    java -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.monkeydodeux.MonkeyDoDeux -f "$PRG_PATH" -d $DEVICE_ID -s "$MB_HOME"/shell $RUN_NATIVE_PAIRING_FLAG $ADDITIONAL_FILE_FLAG $ADDITIONAL_FILE $TEST_FLAG $TEST_NAME
fi


Successfully replaced 2 block(s) in .Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2/bin/monkeydo.

    ...
 14 else
 15
 16     # Set the required variables
 17     MB_HOME="$( cd "$( dirname "$0" )" && pwd )"
+18
+19     # Prefer Java 17 for SDK compatibility (OpenJDK 26 can crash monkeybrains.jar).
+20     if [ -x "/usr/lib/jvm/java-17-openjdk/bin/java" ]; then
+21         JAVA_BIN="/usr/lib/jvm/java-17-openjdk/bin/java"
+22     else
+23         JAVA_BIN="java"
+24     fi
+25
+26     # If a local .Garmin/ConnectIQ folder exists in the current working directory,
+27     # use it as the user home so the SDK finds device files and config there.
+28     if [ -d "$PWD/.Garmin/ConnectIQ" ]; then
+29         USER_HOME="$PWD"
+30     else
+31         USER_HOME="$HOME"
+32     fi
+33
 18     PRG_PATH="$1"
 19     FILE=`echo $(basename "$PRG_PATH" | tr '[:lower:]' '[:upper:]')`
 20     DEVICE_ID="$2"
 21
    ...
 68         fi
 69     fi
 70
 71     # Push the executable to the simulator
-72     java -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.monkeydodeux.MonkeyDoDeux -f "$PRG_PATH" -d $DEVICE_ID -s "$MB_HOME"/shell $RUN_NATIVE_PAIRING_FLAG $ADDITIONAL_FILE_FLAG $ADDITIONAL_FILE $TEST_FLAG $TEST_NAME
+88     "$JAVA_BIN" -Duser.home="$USER_HOME" -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.monkeydodeux.MonkeyDoDeux -f "$PRG_PATH" -d $DEVICE_ID -s "$MB_HOME"/shell $RUN_NATIVE_PAIRING_FLAG $ADDITIONAL_FILE_FLAG $ADDITIONAL_FILE $TEST_FLAG $TEST_NAME
 73 fi

--- .Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2/bin/monkeydo
+++ .Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2/bin/monkeydo
@@ -14,8 +14,24 @@
 else

     # Set the required variables
     MB_HOME="$( cd "$( dirname "$0" )" && pwd )"
+
+    # Prefer Java 17 for SDK compatibility (OpenJDK 26 can crash monkeybrains.jar).
+    if [ -x "/usr/lib/jvm/java-17-openjdk/bin/java" ]; then
+        JAVA_BIN="/usr/lib/jvm/java-17-openjdk/bin/java"
+    else
+        JAVA_BIN="java"
+    fi
+
+    # If a local .Garmin/ConnectIQ folder exists in the current working directory,
+    # use it as the user home so the SDK finds device files and config there.
+    if [ -d "$PWD/.Garmin/ConnectIQ" ]; then
+        USER_HOME="$PWD"
+    else
+        USER_HOME="$HOME"
+    fi
+
     PRG_PATH="$1"
     FILE=`echo $(basename "$PRG_PATH" | tr '[:lower:]' '[:upper:]')`
     DEVICE_ID="$2"

@@ -68,6 +84,6 @@
         fi
     fi

     # Push the executable to the simulator
-    java -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.monkeydodeux.MonkeyDoDeux -f "$PRG_PATH" -d $DEVICE_ID -s "$MB_HOME"/shell $RUN_NATIVE_PAIRING_FLAG $ADDITIONAL_FILE_FLAG $ADDITIONAL_FILE $TEST_FLAG $TEST_NAME
+    "$JAVA_BIN" -Duser.home="$USER_HOME" -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.monkeydodeux.MonkeyDoDeux -f "$PRG_PATH" -d $DEVICE_ID -s "$MB_HOME"/shell $RUN_NATIVE_PAIRING_FLAG $ADDITIONAL_FILE_FLAG $ADDITIONAL_FILE $TEST_FLAG $TEST_NAME
 fi


#!/bin/bash
# Check for proper arguments. If the arguments aren't valid print the usage.
if [ -z "$2" ] || [ "$2" == "-n" ] || [ "$2" == "-a" ] || [ "$2" == "-t" ] || [ "$1" == "-h" ] || [ "$1" == "--help" ]; then

    echo "Pushes and runs a Connect IQ executable on the Connect IQ simulator. The simulator must be running for this command to succeed."
    echo
    echo "Usage: monkeydo executable device_id [-n] [-a additional_file] [-t | -t test_name]"
    echo "    executable - A Connect IQ executable to run."
    echo "    device_id  - The device to simulate."
    echo "    n          - When provided, the app will be run in Native Pairing mode."
    echo "    additional_file - When providing the additionalFile flag you must specify one or more additional files to send to the simulator in the form of '<source>:<dest>'."
    echo "    test_name  - When providing the test flag you may specify a specific test name."

else

    # Set the required variables
    MB_HOME="$( cd "$( dirname "$0" )" && pwd )"

    # Prefer Java 17 for SDK compatibility (OpenJDK 26 can crash monkeybrains.jar).
    if [ -x "/usr/lib/jvm/java-17-openjdk/bin/java" ]; then
        JAVA_BIN="/usr/lib/jvm/java-17-openjdk/bin/java"
    else
        JAVA_BIN="java"
    fi

    # If a local .Garmin/ConnectIQ folder exists in the current working directory,
    # use it as the user home so the SDK finds device files and config there.
    if [ -d "$PWD/.Garmin/ConnectIQ" ]; then
        USER_HOME="$PWD"
    else
        USER_HOME="$HOME"
    fi

    PRG_PATH="$1"
    FILE=`echo $(basename "$PRG_PATH" | tr '[:lower:]' '[:upper:]')`
    DEVICE_ID="$2"

    # Create variables for the optional arguments
    TEST_NAME=""
    TEST_FLAG=""
    ADDITIONAL_FILE_FLAG=""
    ADDITIONAL_FILE=""
    RUN_NATIVE_PAIRING_FLAG=""

    # Set the optional arguments
    if [ ! -z "$3" ]; then
        # Get the run native pairing argument
        if [ "$3" == "-n"  ]; then
            RUN_NATIVE_PAIRING_FLAG="-n"
            ADDITIONAL_FILE_FLAG=""
            TEST_FLAG=""
        # Get the addtional file arguments
        elif [ "$3" == "-a"  ]; then
            ADDITIONAL_FILE_FLAG="-a"
            index=4
            numberInputs=$#
            while [ $index -le $numberInputs ];
            do
               if [ "$4" == "-t" ]; then
                   TEST_FLAG="-t"
                   shift
                   shift
                   shift
                   shift
                   TEST_NAME="$@"
                   break;
                else
                   ADDITIONAL_FILE="${ADDITIONAL_FILE} $4"
                   let "index=index+1"
                   shift
                fi
            done
        # Get the test arguments
        elif [ "$3" == "-t"  ]; then
            TEST_FLAG="-t"
            if [ ! -z "$4" ]; then
                shift
                shift
                shift
                TEST_NAME="$@"
            fi
        else
            echo "ILLEGAL ARGUMENT: $3"
        fi
    fi

    # Push the executable to the simulator
    "$JAVA_BIN" -Duser.home="$USER_HOME" -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.monkeydodeux.MonkeyDoDeux -f "$PRG_PATH" -d $DEVICE_ID -s "$MB_HOME"/shell $RUN_NATIVE_PAIRING_FLAG $ADDITIONAL_FILE_FLAG $ADDITIONAL_FILE $TEST_FLAG $TEST_NAME
fi


ELF>P2@P@8
@)(@@@����   U@U@pppJJP�P�P�`X�X�X�00888  XXXDDS�td888  P�td�z�z�z��Q�tdR�tdP�P�P���/lib64/ld-linux-x86-64.so.2GNU�GNU����'���Q�-r����o�GNUO��OQ(��e�mgUa@�G�,�]m�zX$F����Whct�t�#x���`�8�N�gd2���'����_�2+��}R�8��E ����Q �- �+��p�z"n��libpthread.so.0_ITM_deregisterTMCloneTable_ITM_registerTMCloneTable__errno_locationsendacceptpthread_mutex_destroypthread_joinpthread_cond_waitpthread_createpthread_cond_signalpthread_mutex_lockpthread_mutex_unlockpthread_cond_initpthread_cond_destroyrecvpthread_mutex_initconnectpthread_cond_timedwaitlibstdc++.so.6__gmon_start___ZdlPvm_ZTVN10__cxxabiv117__class_type_infoE_ZTVN10__cxxabiv120__si_class_type_infoE__cxa_pure_virtual__gxx_personality_v0_Znwmlibgcc_s.so.1_Unwind_Resumelibc.so.6socketfflush__printf_chkexithtonsfopenstrrchrftellstrncpy__stack_chk_failputcharlistenstdinstrftimefeoffgetscallocstrlenmemsetbindfseekshutdownvsnprintfstdoutinet_addrfputsstrtok_rmemcpyfclosemallocstrcasecmpgetenv__strncat_chkstrncasecmpstrncatgetcwdfwritefreadgettimeofday__memcpy_chkatoi__fread_chklocaltimestrchrsleep__cxa_finalizememmove__libc_start_mainferrorfreeGCC_3.0CXXABI_1.3.9GLIBCXX_3.4CXXABI_1.3GLIBC_2.2.5GLIBC_2.3.2GLIBC_2.4GLIBC_2.14GLIBC_2.7GLIBC_2.3.4./
� P&y�B@yѯ
�t)�	�ӯk�0ui	�ri	�ii
�����ii
ui	�ti	P�03X��2h�@�p��Mx�VN��FO��lO���O���O��Q���P��;P���PК�Rؚ�R���R�R��0�8� xH�@xP�0���@�W_H��YP��ZX��_`� ��J�J �J(�J0�@�3ПP؟D��G�I�K�Bp�O��Q����������ȝН؝��	�
����
��� �(�0�8�@�H�P�X�`�h�p�x������� ��!��"��#��$��%��&Ȟ'О(؞)��*�+�,��-�.�/�0�1 �2(�40�58�6@�7H�8P�9X�:`�;h�<p�=x�>��?��@��A��C��E��F��H��L��MȟN��H��H��H��t��H����5j}��%k}��h���������h���������h���������h��������h��������h��������h��������h��q������h��a������h	��Q������h
��A������h��1������h��!������h
��������h��������h���������h���������h���������h���������h��������h��������h��������h��������h��q������h��a������h��Q������h��A������h��1������h��!������h��������h��������h���������h ���������h!���������h"���������h#��������h$��������h%��������h&��������h'��q������h(��a������h)��Q������h*��A������h+��1������h,��!������h-��������h.��������h/���������h0���������h1���������h2���������h3��������h4��������h5��������h6��������h7��q������h8��a������h9��Q������h:��A������h;��1������h<��!������h=��������h>��������h?���������h@���������hA���������hB���������hC��������hD��������hE����������%5{D����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%�xD����%}xD����%uxD����%mxD����%exD����%]xD����%UxD����%MxD����%ExD����%=xD����%5xD����%-xD����%%xD����%xD����%xD����%
xD����%xD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%�wD����%}wD����%uwD����%mwD����%ewD����%]wD����%UwD����%MwD����%EwD����%=wD����%5wD����%-wD����%%wD����%wD����%wD����%
wD����%wD����%�vD����%�vD����%�vD����%�vD����%�vD����%�vD����%�vD��AW���&AVAUATUSH��xdH�%(H��$h1�L�D$0H�awH�NwL���H����H�^�B�A�L�l�H��f�H�}H�������M�dI9�u�L���L���H�D$0H�D$XH�D$�4H�|$0L��H���H���H�|$0L��H�5H�4���H��I9���H�+�}-uƀ}-u�L�}�=L���G���H���5L�pL)�H�5�FL��H��H�$����H�$����H�5�FL���f������f���H�|$����L�������n������s���H�D$0fD��$W��H�5��L�-�u����H��uI)���D��$X1�L�|$8�+�|$8�'L��L����������H��H��L9�tYL�#M��t�E��t�H�=iLH��H���$���H�S��1�H�5VF�}���H�C��P H�=7L�����D��$XL9�u���$X�SH�=Fu��H�=6F�����H�-uH�|$X�P0H�uH��H����������H�T$0�@yH����$Y�ExH�JH�EH�H��t�:H�pHE�H�EH��
1�1�H�E�1�H�H�EH��H�E H��H�E(H�]0�E8����H�EH�EP�H�EXH����H�E`H�|$H��1�H�EhH��Ep�X���A�Ņ���H�|$A��!H�tA��8��$X��E1�H�|$t0H�=EH��s�m���H�=�sH��t�SP��D���@H�=�sH��t�o���H��sH�|$0H��t�U���H��$hdH3%(�!H��xD��[]A\A]A^A_úH�5�CL���U�������Ƅ$W�l���H�=yEE1������H�=�E����H�=�E����H�=\C����H�=�E�����
�'���H�=TC�{���H�=TC�o���H�=�E�c���H�=F�W���H�=8F�K���H�=1C�?���H�=XF�3���H�=�F�'���H�=�F����H�=�F����H�=C����H�=G�����H�=HG�����H�=�B�����H�=�B�����H�=lG�����H�=�G�����
�Q���H�=�G����H�=�G����H�=�B�����
�#���H�=H�w���H�=@H�k���H�=tH�_��������H�|$8� � L���s��������L��H�5�B�1�����H�H��q�-����H�5�AL���u�����t)�	H�5�AL���]������]���Ƅ$Y�t���Ƅ$X�g���H�}X��:������H�T$0H��t	�:�jL��$`��H�]@1�L���H����E8A�H���1�L��L���H�=�pH�����3&���|L��<�U���H��H�����4H�|$ H��@H��H��pH�$�
��ukH�|$ �1�����H�|$ �v���H�|$ 1�1�I������L���]���I��H��t/H�L$ L��H������H�<$L��H��H���%L���������1�L���H�H��o�L������H�����>L������I��H�������H���[H�5�@�H��I��1�����H��@L��H���	�������H�=�@�����E8��������H�|$A��~��������H�T$8H�5eF�1��Q���A��N���H�=o���������H�#oH�t$ �H�H�L$ �D$(H���PHH�|$H��uH�}XH��t�oH����������1�H��n����H�=�?�U���H�=�nH��u@��/����E1��H��H���1�H���H�=�n����#�H�=�E�����L����SP�H�=�EA�������+����������1�I��^H��H���PTL��-H�
_-H�=�����bm��H�=�mH��mH9�tH�>mH��t	�����H�=�mH�5�mH)�H��H��?H��H�H��tH�mH��t��fD�����=�mu+UH�=�lH��tH�=�l�y����d����em]������w�������Fm�����:m���fD��H��H��H�0� 1�H���DAVAUATUH��S�_hD�glD9����I������I��H����A)ܸL�E`L��A����DG�D��H���
���H9�u0DehD��L��H��L���K"��xL���W���1�[]A\A]A^�@H�}`����H��H�5�;��1��S"L���$���[�]A\A]A^�fDH�`�'���[H�E`1�H�Eh�Ep]A\A]A^�D1�H�5�;H����!L���������h���1�H�5X;H����!��M���f����������AUATUH��SH��(�ZdH�%(H�D$1�����H�O`H�����zA��I���H�L������I9�ux�Eh1�؉Ehk�d�ul9Ep�/L�l$�D$�L��H�L��H�D$A�T$H�����El9Ehtb1�H�L$dH3%(�H��([]A\A]ÐH�}`����H�5�:H���1��� ��DH�5<� ��DH�}`����H�E`L��H�	H�Eh�L���EpH�D$�D$A�T$H���N���1�H�5<H���F ��5���D1�H�5�;H���& �����D�Ep��H�5�;H��1�� ����D1�H�5�9���������C������F�GH�F�GL�F�GPH�X�w	���S�8�t'H��H�=�9�F���H�{@�����C8����H�C@H�5v9�1�����H�=Ti����1�[�ff.�ATI��US������<u@A�D$I�������<t�< t����<
��L�����
t
�PH�����u��A�$L���u�1D��t(H���S<%H�k��u��2u�}0t�EH�k��u�L��[]A\�@�E� H���l���H�sH��H�P������E�L��닐��AWAVAUA��ATI��USH��H��8dH�%(H�D$(1�@��9G8��L��1�������t��A�<
@��A�����{x�L�l$1�L�t$1�L��f�T$&L�|$H�$H�D$f�D$H�D$�����L���c���H��7�
L��H���|���L��I���A���H��7�
L��H���Z���L��L��H�5O7����I��H����@�����{yt"L��L��H�5v71���t����f�@��t�
�����@�kyH�=%g�����H�D$(dH3%(��H��8[]A\A]A^A_�DL��H�5$7�1������H��t��{yu=L��H�5�6�1������L��H�5�61������I��H���_����
�����M��L��L���H�5�61�������H�={6�$���H�s@L����������������ff.���H����u#H�TfH��tH���PP1��*���f.�H�=	f�����H�=
f����H�=f�����H�=�eH�������ff.����AWAVAUATUSH��H�$H��dH�%(H��$1��>/I��I��I����H��H������H��tTH��H����������!�%����t��L�������D�H�SHDډ�@�H������H)�H)�L�xL9�sDL��H�527�1��>����H��$dH3%(uwH��[]A\A]A^A_�@H�|�H�55�������L��L��H)�H�|�����L��H���w���I�EH��������H��L���U���I�E�������f.���AVAUATUH��S�BH�Ӆ�������H�zL�������L��I�������I��H��t	�8�~I�~L��1��\HǃH���Iǆ�H)������H�L���?���H����H�p�L��M���R���H��3H�}`L�������L�����u+�C[�Eh�Ep�El]1�A\A]A^����L��H�5�5H��1���[]A\A]A^�@H�zI������H��I�������H�}`H�m3L���Z�����u�H�}`�1�����H�}`�M���H�}`1�1��El�m����El�H���CL��CH�H�A�T$HA���D;C�\����Eh[L��H���Ep]A\A]A^�����\H�������H��������x������\L�������H��t>H�p�L�������������L��L��M��������v�����L��L�������Y���ff.���I��H��H��L������1�H�������������'�����UH��H�}�H�u�H�E�H�H��uH�E�H�U�H�PH�E�H�PH�E�H��,H�E�H�@H�U�H�P`H�E�H�PH�E�H�PXH�E�H�U�H�P�]���UH��H�}�H�u�H�E�H�H�E�H�@H9�u H�E�H�@H�E�H�PH�E�H��H�E�H�H9E�uH�E�H�H�P`H�E�H��vH�E�H�@H9E�uH�E�H�@H�PXH�E�H�P�RH�E�H�H�@`H�E�H�E�H;E�u+H�E�H�@XH�U�H�R`H�P`H�E�H�@`H�U�H�RXH�PX��H�E�H�@`H�E��]���UH��H��0H�}�u�U�H�Mؿ@����H�E�H�E�H�@H�E�H�PH�E�H�H�E�H���H������H�E��U��P8H�E�����UH��H�� H�}�H�E�H��H���z���H�E��@8H�E�H�H�E��BH�E�H���V���H�E�H��(H���&���H�E�H������H�E�H�@H9E�tH�E�H�@`H�E�H�}�u���H�E�H��H���V��������UH��H��H�}�H�E�H��H�������H�E��@8H�E�H��H�����������UH��H��H�}�H�E�H�H��t��!H�E�H��H�������H�E�H������������UH��H��H�}��u�U�H�E��ѺH�ƿ�����UH��H�����\���H��P�����X�����L���dH�%(H�E�1�ǅl���������\���u
�`��ǅd����.��d���H��H��P���H�H�H��H���������d�����d���;�\���r�H�E��H������H�E�H��(�H������ǅd����]��d���H��H��P���H�H�H�U�H��H��������d���H��H��P���H�H��@8��t
ǅl�����d�����d���;�\���r���l������H�E�H���������L������H�E��H���g���H�E�Hi����L���H�Hi�@BH�H��x���H�E�H��p���H��x���H=�ɚ;~uH��p���H��x���H����&�.H��H��H��H��H��?H)�H��H�H��p���H��x���H����&�.H��H��H��H��H��?H)�H��Hi�ʚ;H)�H��H��x���ǅd����.��d���H��H��P���H�H�H��H���A�����d�����d���;�\���ră�L����uH�E�H�U�H��(H��H���������l����$H��p���H�E�H�M�H��(H��H���������l�����l���nuǅh���@���l���t
ǅh���`H�E�H������ǅd����.��d���H��H��P���H�H�H��H���)�����d�����d���;�\���r�ǅd����_��d���H��H��P���H�H�H�U�H��H���������d���H��H��P���H�H��@8��t��d�����h�����d�����d���;�\���r�ǅd����.��d���H��H��P���H�H�H��H��������d�����d���;�\���r�H�E�H������H�E�H��(H���'�����h���H�u�dH34%(t�-�������UH��H��0H�}�H�u��U�H�E� �H������H�}�u%H�E�H�P�E�H�։���E��}�u��H�E�H�U�H�PH�E�U܉P�����UH��H��H�}�H�E�H��H���H�E�� �H���?��������UH��SH��H�}�H�E�XH�E�H����)É؃�H��[]���UH��H��H�}�H�E�H�������UH��H��0H�}�H�u��U�H�E��E��H�E�@9�s'�U�H�E�H�HH�E���H�H�E�H��H���j����aH�E�PH�E�)ЉE��U�H�E�H�HH�E���H�H�E�H��H���/����E�+E���H�E�H�@�M�H�U�H�H��H��H�����������UH��H�}�u��E��IH�E�H�E�@9�u
H�E��@�6H�E��PH�E�H�E�H�E�@9�r
H�E���E��E�;E�r�]���UH��H�}�H�E�@���E�H�E�@9E�r�E�H�E�9E���]���UH��H�}�H�u��U�H�E�H�@H��u
���E��uH�E�PH�E�@��9�sH�E�@�����E�H�E�9E�uH�E��@��?�U�H�E�H�H�E�H�PH�E�@��H���H�E�U��P�E��E�;E�r��]���UH��H�}���E�H�E�H�@H��u��lH�E�PH�E�@��9�sH�E�@�����E�H�E�9E�uH�E��@��)H�E�H�PH�E�@��H��E�H�E�U��P�]���UH��H�}�H�E�PH�E�)ЉE��}�yH�E�P�E�ЉE��E�]���UH��H���}�H�u�H�E�H��E�H�������H��H�E�H�H�E�H�H��u�������UH��H��H�}�H�E�H�H��tH�E�H�H���C���H�E�H����Ð��UH��H��H�}�H��PH�E�H�H�E��@H�E�H��H������������UH��H��H�}�H�E�H������H�E��0H�������Ð��UH��H��H�}��u�H�LPH�E�H�H�E��@�E�H�U�H�J�¾H�������������tH�E��@��Ð��UH��H��0H�}�H�u��U��E��E�H�E�@��t
�������}�
���H�E�H��H���V����U�9�sH�E�H��H���?�����E܉E��}�utH�E�@��t
������H�E�H�H�� H�H�E�H����H�E�@��t
������H�E�H��H��������U�9�sH�E�H��H���������E܉E�놋U�H�E�H�HH�E�H��H��������E�H�U�H����H���d����}�~H�E�H�H��H�H�E�H���ҋE�����UH��H��0H�}�H�u��U܉ȈE��E��E�H�E�@��t
������ �}�
��H�E�P(�E�9�s
�������H�E�H��H��������U�9�sH�E�H��H��������E܉E��E�;E�}w�}�tqH�E�@��t
������H�E�H�H��(H�H�E�H����H�E�@��t������{H�E�H��H���H����U�9�sH�E�H��H���1�����E܉E�끋U�H�E�H�HH�E�H��H�������E��}�u�E������}�~H�E�H�H��H�H�E�H���ҋE�����UH��H��H�}�H�}�t2H�E�H�H��t&H�E�H�H��tH�H��H�H����H�E�H���Ð��UH��H��H�}�H�sLH�E�H�H�E��@H�E�H���+H�E�H���EH�E�H�@0H�������H�E�H�@8H�������H�E�H������������UH��H��H�}�H�E�H���t���H�E��@H���-����Ð��UH��SH��H�}�u�H�E�U��H�������H��KH�E�H�H�E�H�@0H�E�H�@8���������H�U�H�B0���������H�U�H�B8H�E�H�@0H�����H��H�E�H�������H��H���b���H��[]Ð��UH��H��H�}�H�E�H�@0H�����������Ð��UH��H��H�}�H�E�H�@8H����������Ð��UH��H��H�}�H�E�H�@0�����H�����������UH��H��H�}�H�E�H�@8�����H�����������UH��ATSH���}�@����H�ËE��H���x���H�����I�ľ@H������L��H���n���H��[A\]���UH��H��0H�}�H�u�H�U�H�E�H�E�H�E�H�E�H�E�L�H�E��@��
��H�E��@��H�E�H�H�E��@��H�E�H��A�и����UH��H�� H�}�H�u�H�U�H�E�H�PH�E�H��������UH��H�� H�}�H�u�H�U�H�E�H�5p'H�Ǹ�H�|OH���t��������UH��H��0H�}�H�u�H�U�H�E�H�E�H�E�H�E�H�E��pH�E��HH�E��PH�E�A��H�5"'H�Ǹ�H�E��@��H�E�H�H�E��PH�E�H�5'H�Ǹ��H�E�A���H�5�&H�Ǹ��H�E��@��tH�E�H�5�&H�Ǹ�H�E�H�HH�U�H�E�H��H���Ѹ����UH��H�� H�}�H�u�H�U�H�E�H�5�&H�Ǹ�RH�E�H�PH�E�H���Ҹ����UH��H�� H�}�H�E�H�E�\H���Z���H�E�H�}�tH�E�H�E�����UH��H�� H�}�H�u�H�U�H�E�L�@H�U�H�M�H�E�H��H��A������UH��H�� H�}�H�u�H�U�H�E�L�@ H�U�H�M�H�E�H��H��A������UH��H�� H�}�H�u�H�U�H�E�L�@(H�U�H�M�H�E�H��H��A������UH��H��@H�}�H�uЉU�H�M�D�E�H�E�H�E��E�H�H��H���G���H�E�H�E��Ủ�EȍPH�E��PH�E��@�E�Hc�H�E��@��H�E�H�H�E�H��H������H�E�L�@HH�E��@��H�M�H�E�H��H��A�ЉE�}��u)H�E�H�5<%H�Ǹ�H�E�H�PPH�E�H����H�E�H���M����E�����UH��H��PH�}�H�u�dH�%(H�E�1��E�H�E�H�E�H�E�H�5�$H�Ǹ�(H�E�L�@@H�M�H�E��H��H��A�ЉẼ}�%�U�H�E�H�5�$H�Ǹ���E��,�E��H�������H�E�H�}�u$�U�H�E�H�5�$H�Ǹ��E���H�E�H�U�H��U�P�E��tXH�E�H�H@�E����H�E�H�pH�E�H���щẼ}�.�U�H�E�H�5U$H�Ǹ�AH�E�H�������E���E��H��H�4EH�H�E�H�}�t0H�U�H�M�H�E�L�E�H��H��A�ЈEˀ}�t)H�E�H�������,�U�H�E�H�5�#H�Ǹ��H�E�H����������H�E�H�5�#H�Ǹ��E�H�M�dH3%(t���������UH��H�� H�}�H�u�H�U�M�M�H�U�H�u�H�E�A��H�ѺH�����������UH��H�� H�}�H�u�H�U�M�M�H�U�H�u�H�E�A��H�ѺH����������UH��H���H�����H�����H��`���H��h���L��p���L��x�����t )E�)M�)U�)]�)e�)m�)u�)}�dH�%(H��H���1�ǅ0���ǅ4���0H�EH��8���H��P���H��@���H��0���H�����H��H�¾�������$���ǅ0���ǅ4���0H�EH��8���H��P���H��@�����$�����H�H���$���H��(�����$�����Hc�H��0���H�����H��(���H������H�����L���$����HH��(���H������H��A��H��(���H���i����H��H���dH3%(t�������UH��H�� �}�E��������E��E������E�����UH��}��}����]���UH���r����]���UH��]���UH��]���UH��H��0H�}�H�u��U�H�E�H�E��E܉E��E�Hc�H�E��@`H�u����r����E�E���r�����t������%�}�u�������E�)E��E�H�HE��}���E�����UH��H�� H�}�H�E�H�E�H�E��@`������������UH��H��0H�}�H�u��U�H�E�H�E��E��E�Hc�H�E��@`H�u��������E�E���������tH�E��@`�������������E�E��E�H�HE��E�;E�|��E�����UH��H����F��u�W��u
����F�h�����H�E�H�E�H�����H�P@H�E�H� ���H�PHH�E�H�����H�PPH�E�H�����H�PXH�E��@`������E���������H�U��B`H�E��@`���u%��E������H�E�H��������������H�E�����UH��H��PH������dH�%(H�E�1������H������fǅ�����H��������H�������H��������H�5�H������H��������H�����������H�������:H������H������H������tYH������H��H�������f������f������u	fǅ�����H������H+�����H��H������H������H��H���
����H������H��������H��H�������������u
��H������H���������H�����������H9�����u*H������H��H�=��������b�����Z�h����H������H������H�D���H�P@H������H�����H�PHH������H�����H�PPH������H�����H�PXH�������@`��������|���H�������B`H�������@`��������t����H������H��������H��������H�������fǅ����H�������������������������f������H�������@`H�������H�Ή��+�����������t,H�������@`�����������H������H���������H������H�M�dH3%(t�i�������UH��H��@�}�dH�%(H�E�1��E�H�E��H�������f�E��E��E���������f�E�3BH�M�H�Ή��������>�����u�E��E�H�U�dH3%(t���������UH��H�� �E��E�H�E�������u�E�����������A��A���������t
������E���H�=m�����H�E�H�}�t%H�E�H������f�E�f�}�t�E��������E�}�u5�E��&�E���������E�}�u�`�����bu
�E��}�~Ԁ}�u:�A����������������t��@��������<�����E����@������������E�����UH��H�=������H�=�����H�=�����]���UH��A��u1�y@���t&�n@���`���������@�N@�����]���AWL�=s:AVI��AUI��ATA��UH�-d:SL)�H�������H��t1��L��L��D��A��H��H9�u�H��[]A\A]A^A_�ff.������H��H���Memory error
Error reading file: %d
Error sending file part.
File is not opened
Error writing to file %d
Closing file:>Writing bytes%T%D[%s %s][%s %s] %s/rbw+btransporttransport_argshelplist_transportstimestamp                 [--help]Parameters:	 transport:	 transport_args:	 list_transports:		 if this is found.	 help:	 the shell command.Transport name: %s
Found Transport: %s
Connecting...Saving to %s
wbError opening fileClosing shell and port Protocol error - no file part payload
Copying file... %d%% complete
Error sending file part ack packet.
Error sending file copy complete packet.
Error opening file: %s. Filename too long
Could not open file: %s Error: %d
Usage: shell.exe [--transport=<transport>]                 [--transport_args=<transport_args>]                 [--list_transports]                 [shell command arguments]		 (Optional) Transport to use. The list_transports		 parameter can be used to list the available transports.		 Defaults to the first transport found if not passed.		 (Optional) Arguments to pass to the transport being used. This		 is a free form string passed to the transport.  Use the		 list_transports parameter to list the available		 transports and the arguments that they support.		 (Optional) List the available transports and the arguments		 that they support and exits.  All other parameters are ignored		 (Optional) Prints out this help message and exits. All other		 parameters are ignored if this is found.	 Any arguments passed other than these are interpreted as	 the shell command that is to be run and any parameters for	 If there are no remaining arguments passed an interactive	 shell session will be started where commands can be entered	 manually from a command prompt.-------------------------------------------------------------No transport found matching: %s
Error creating server version eventTimeout waiting for device infoN5SHELL11ManagedFIFOEN5SHELL18WindowsManagedFIFOEDevice Requested Disconnection
Device Version %d.%d.%d
Device id %u name "%s"
Shell Version %d.%d.%d
Major version incompatibilty, shell likely to malfunction
File pushed successfully
send failed:
Connecting to device...
Socket Error in packet header %d
Malloc Failure for size %d
Transport Error %d
Unhandled packet type %d
Connection Finished
127.0.0.1Invalid socket address: %sSHELL_SERVER_PORTThis transport takes an optional parameter to specify theip address and portto connect to.  If not passed it will default to127.0.0.1:1234;�T|�����������\�������������4����Hܸ��`����,�������,���(����D\���t����������\���(�������������������]���<:���\����|=������������������!�������<����\���| ���������C����}����4�������<���\N���|��������������	T��� 	����@	����`	B����	�����	�����	�����	����
����0
���P
>���p
�����
�����
)����
o����
a�������<����\���|V���������t����e������������<m���\����|��������������������Y���
����<
���\
����|
�����
}����
�����
�����
,�������dzRx����/D$4����pFJw�?:*3$"\���tئ��`�`����l���HR`�t���>B�B�B �A(�D0��
(A BBBEe
(F BBBGJ
(Z BBBFP���	80L����F�B�A �D(�DP�
(A ABBBl��������UE�O,�@����B�D�A ��
ABEH����"F�B�B �E(�D0�A8�GpM
8A0A(B BBBFĺ��dHWL0���VF�B�B �B(�A0�A8�G� I� �
8A0A(B BBBE`�,���UF�B�B �A(�D0��
(R DBBH\
(A BBBE�
(N BBBE�(���KQ�0���	,���	L$����H	F�I�B �B(�A0�A8�G�

8D0A(B BBBAtѽ��hE�C
_�����E�C
��־��hE�C
_�����E�C
������BE�C
y����FE�C
}4���0E�C
gT���,E�C
#t����vE�C
m�S���9E�C
p�l���2E�C
E�d�~���E�C
U�|����E�C
����kE�C
b4_���:E�C
qTy����E�C
�t����E�C
������:E�C
q�����ME�C
D�����;E�C
rzPLRx��� $����<�E�C
s8����/E�C
fX����]E�C
Tx,���BE�C
9�N���_E�C
V �����LE�C
C �����p�E�C
g���/E�C
f$0�����E�C
E��H����%E�C
\h����%E�C
\�����(E�C
_�����(E�C
_(�����YE�C
G��I�����gE�C
^:���+E�C
b4E���FE�C
}Tk����E�C
�t=���HE�C
�e���?E�C
v�����7E�C
n�����7E�C
n�����7E�C
n	�����E�C
�4	�����E�C
�T	a���?E�C
vt	����?E�C
v�	�����E�C
��		���,A�C
g�	���E�C
K�		���E�C
H
����E�C
F4

[Showing lines 1-83 of 715 (50.0KB limit). Use offset=84 to continue.]


cd /home/liph/code/garmin-freedive-app
SDK=$(cat .Garmin/ConnectIQ/current-sdk.cfg)
"$SDK/bin/monkeydo" bin/Freedive_fenix5plus.prg fenix5plus 2>&1 | head -30


```shell
cd /home/liph/code/garmin-freedive-app
SDK=$(cat .Garmin/ConnectIQ/current-sdk.cfg)
"$SDK/bin/monkeydo" bin/Freedive_fenix5plus.prg fenix5plus 2>&1 | head -30
```

Unable to connect to simulator.


`monkeydo` works, but it needs the Connect IQ simulator to be running first. The simulator is a GUI app, so it won’t start in this headless environment. I’ll update `README.md` with clear instructions for both the simulator and the command-not-found issue.


- **Distance** — separate horizontal-distance mode for pool dynamic apnea (bi-fins / monofin / no-fins). Manual lengths + configurable DOWN button (strokes or contractions).
- **Static Apnoe** — breath-up / hold / recovery timer with HR.
- **Breathing Trainer** — land-based CO₂/O₂ tables with decreasing rest times.

The design is in [`APP_DESIGN_V2.md`](APP_DESIGN_V2.md). The fēnix 5 source files are the `Fenix5App`, `MainMenu*`, `SubMenu*`, `Depth*`, `Distance*`, `Static*`, `Breathing*`, `Settings*`, `AppSettings`, `Common`, and `DepthModel` classes.

### Build the fēnix 5 v2 app

```bash
./build_fenix5.sh
```

This produces `bin/Freedive_fenix5plus.prg`. Sideload it to `GARMIN/APPS/` over USB.

> **Java version:** the Connect IQ SDK 9.x `monkeybrains.jar` crashes on OpenJDK 26. Use **OpenJDK 17** for building. On Arch: `sudo pacman -S jdk17-openjdk`.

> **Headless/container builds:** the SDK Manager itself needs a GUI, but once the SDK and device files are downloaded, you can copy the `.Garmin/ConnectIQ` folder into the project root and run `./build_fenix5.sh`. The script will use the local copy if `project/.Garmin/ConnectIQ/current-sdk.cfg` exists.

### fēnix 5 v2 buttons

| Context | Button | Action |
|---------|--------|--------|
| Main menu | UP/DOWN | Highlight mode |
| Main menu | START | Select mode |
| Main menu | BACK long | Open settings |
| Sub-menu | UP/DOWN | Highlight item |
| Sub-menu | START | Select |
| Sub-menu | BACK | Go back |
| Active session | START | Skip breath-up / start dive / end dive / save |
| Active session (Depth/Distance) | UP | Count a pool length |
| Active session (Distance) | DOWN | Count stroke **or** contraction (configurable in settings) |
| Active session | BACK short | Discard and return to menu |
| Active session | BACK long | Open settings (from breath-up/setup only) |

---

## 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

[112 more lines in file. Use offset=170 to continue.]


### Build the fēnix 5 v2 app

```bash
./build_fenix5.sh
```

This produces `bin/Freedive_fenix5plus.prg`. Sideload it to `GARMIN/APPS/` over USB.

> **Java version:** the Connect IQ SDK 9.x `monkeybrains.jar` crashes on OpenJDK 26. Use **OpenJDK 17** for building. On Arch: `sudo pacman -S jdk17-openjdk`.

> **Headless/container builds:** the SDK Manager itself needs a GUI, but once the SDK and device files are downloaded, you can copy the `.Garmin/ConnectIQ` folder into the project root and run `./build_fenix5.sh`. The script will use the local copy if `project/.Garmin/ConnectIQ/current-sdk.cfg` exists.

### Test in the fēnix 5 v2 simulator

The SDK commands `monkeyc` and `monkeydo` live inside the SDK `bin` directory, not on your shell `PATH` by default. Use their full path, or add the SDK `bin` directory to your `PATH`.

1. Start the Connect IQ simulator:

   ```bash
   SDK=$(cat ~/.Garmin/ConnectIQ/current-sdk.cfg)
   "$SDK/bin/simulator"
   ```

2. In another terminal, push the built app to the simulator:

   ```bash
   SDK=$(cat ~/.Garmin/ConnectIQ/current-sdk.cfg)
   "$SDK/bin/monkeydo" bin/Freedive_fenix5plus.prg fenix5plus
   ```

The simulator needs a graphical display. It will not run on a headless server without X11, Xvfb, or VNC.

### fēnix 5 v2 buttons

### Build the fēnix 5 v2 app

```bash
./build_fenix5.sh
```

This produces `bin/Freedive_fenix5plus.prg`. Sideload it to `GARMIN/APPS/` over USB.

> **Java version:** the Connect IQ SDK 9.x `monkeybrains.jar` crashes on OpenJDK 26. Use **OpenJDK 17** for building. On Arch: `sudo pacman -S jdk17-openjdk`.

> **Headless/container builds:** the SDK Manager itself needs a GUI, but once the SDK and device files are downloaded, you can copy the `.Garmin/ConnectIQ` folder into the project root and run `./build_fenix5.sh`. The script will use the local copy if `project/.Garmin/ConnectIQ/current-sdk.cfg` exists.

### fēnix 5 v2 buttons


Successfully replaced 1 block(s) in /home/liph/code/garmin-freedive-app/README.md.

     ...
 124 > **Java version:** the Connect IQ SDK 9.x `monkeybrains.jar` crashes on OpenJDK 26. Use **OpenJDK 17** for building. On Arch: `sudo pacman -S jdk17-openjdk`.
 125
 126 > **Headless/container builds:** the SDK Manager itself needs a GUI, but once the SDK and device files are downloaded, you can copy the `.Garmin/ConnectIQ` folder into the project root and run `./build_fenix5.sh`. The script will use the local copy if `project/.Garmin/ConnectIQ/current-sdk.cfg` exists.
 127
+128 ### Test in the fēnix 5 v2 simulator
+129
+130 The SDK commands `monkeyc` and `monkeydo` live inside the SDK `bin` directory, not on your shell `PATH` by default. Use their full path, or add the SDK `bin` directory to your `PATH`.
+131
+132 1. Start the Connect IQ simulator:
+133
+134    ```bash
+135    SDK=$(cat ~/.Garmin/ConnectIQ/current-sdk.cfg)
+136    "$SDK/bin/simulator"
+137    ```
+138
+139 2. In another terminal, push the built app to the simulator:
+140
+141    ```bash
+142    SDK=$(cat ~/.Garmin/ConnectIQ/current-sdk.cfg)
+143    "$SDK/bin/monkeydo" bin/Freedive_fenix5plus.prg fenix5plus
+144    ```
+145
+146 The simulator needs a graphical display. It will not run on a headless server without X11, Xvfb, or VNC.
+147
 128 ### fēnix 5 v2 buttons
 129
 130 | Context | Button | Action |
 131 |---------|--------|--------|
     ...

--- /home/liph/code/garmin-freedive-app/README.md
+++ /home/liph/code/garmin-freedive-app/README.md
@@ -124,8 +124,28 @@
 > **Java version:** the Connect IQ SDK 9.x `monkeybrains.jar` crashes on OpenJDK 26. Use **OpenJDK 17** for building. On Arch: `sudo pacman -S jdk17-openjdk`.

 > **Headless/container builds:** the SDK Manager itself needs a GUI, but once the SDK and device files are downloaded, you can copy the `.Garmin/ConnectIQ` folder into the project root and run `./build_fenix5.sh`. The script will use the local copy if `project/.Garmin/ConnectIQ/current-sdk.cfg` exists.

+### Test in the fēnix 5 v2 simulator
+
+The SDK commands `monkeyc` and `monkeydo` live inside the SDK `bin` directory, not on your shell `PATH` by default. Use their full path, or add the SDK `bin` directory to your `PATH`.
+
+1. Start the Connect IQ simulator:
+
+   ```bash
+   SDK=$(cat ~/.Garmin/ConnectIQ/current-sdk.cfg)
+   "$SDK/bin/simulator"
+   ```
+
+2. In another terminal, push the built app to the simulator:
+
+   ```bash
+   SDK=$(cat ~/.Garmin/ConnectIQ/current-sdk.cfg)
+   "$SDK/bin/monkeydo" bin/Freedive_fenix5plus.prg fenix5plus
+   ```
+
+The simulator needs a graphical display. It will not run on a headless server without X11, Xvfb, or VNC.
+
 ### fēnix 5 v2 buttons

 | Context | Button | Action |
 |---------|--------|--------|


SDK=$(cat /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/current-sdk.cfg)
ls -la "$SDK/bin/simulator" 2>/dev/null || ls -la "$SDK/bin/" | grep -i sim


```shell
SDK=$(cat /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/current-sdk.cfg)
ls -la "$SDK/bin/simulator" 2>/dev/null || ls -la "$SDK/bin/" | grep -i sim
```

-rwxr--r-- 1 liph liph 29165936 Jul 27 07:33 /home/liph/code/garmin-freedive-app/.Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2/bin/simulator


# Prefer Java 17 for SDK compatibility (OpenJDK 26 can crash monkeybrains.jar).
if [ -x "/usr/lib/jvm/java-17-openjdk/bin/java" ]; then
    JAVA_BIN="/usr/lib/jvm/java-17-openjdk/bin/java"
else
    JAVA_BIN="java"
fi

# If a local .Garmin/ConnectIQ folder exists in the current working directory,
# use it as the user home so the SDK finds device files and config there.
if [ -d "$PWD/.Garmin/ConnectIQ" ]; then
    USER_HOME="$PWD"
else
    USER_HOME="$HOME"
fi

# Determine the directory this script is in
MB_HOME="$( cd "$( dirname "$0" )" && pwd )"

"$JAVA_BIN" -Duser.home="$USER_HOME" -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.simulator.Simulator "$@"

# Determine the directory this script is in
MB_HOME="$( cd "$( dirname "$0" )" && pwd )"

java -classpath "$MB_HOME"/monkeybrains.jar com.garmin.monkeybrains.simulator.Simulator "$@"


Could not find the exact text in .Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-9.2.0-2026-06-09-92a1605b2/bin/simulator. The old text must match exactly including all whitespace and newlines.