Suunto app Forum Suunto Community Forum
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Register
    • Login

    [Question] No stupid questions - ask anything here

    Scheduled Pinned Locked Moved Suunto Plus Development
    142 Posts 40 Posters 14.9k Views 39 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D Offline
      DonTomGot @matram
      last edited by

      @matram Fantastic! many thanks!

      1 Reply Last reply Reply Quote 0
      • Łukasz SzmigielŁ Offline
        Łukasz Szmigiel
        last edited by

        Hi,

        I’m seeing a reproducible issue with a SuuntoPlus app during active navigation, and I’d like to ask what the recommended mitigation is.

        The app is called Constantin. It uses multiple UI templates for different workout views:

        • td.html - drift / combined view
        • tp.html - pace-focused view
        • th.html - heart-rate-focused view

        The switching logic follows the documented pattern from the SuuntoPlus examples:

        • the HTML template only sends a /Zapp/{zapp_index}/Event
        • main.js handles the event in onEvent()
        • main.js updates currentTemplate
        • main.js calls unload('_cm')
        • getUserInterface() returns the selected template

        Simplified version:

        var currentTemplate = 'td';
        
        var changeTemplate = function(template) {
          if (currentTemplate === template) return;
          currentTemplate = template;
          unload('_cm');
        };
        
        function onEvent(input, output, eventId) {
          if (eventId === 1) {
            if (currentTemplate === 'td') changeTemplate('tp');
            else if (currentTemplate === 'tp') changeTemplate('th');
            else changeTemplate('td');
            return;
          }
        
          if (eventId === 2) {
            if (currentTemplate === 'td') changeTemplate('th');
            else if (currentTemplate === 'th') changeTemplate('tp');
            else changeTemplate('td');
          }
        }
        
        function getUserInterface() {
          return { template: currentTemplate || 'td' };
        }
        

        In the templates I use a mechanical button like this:

        <pushButton
          name="up"
          type="lock"
          longType="lock"
          onClick="$.put('/Zapp/{zapp_index}/Event', 1, null, 'int32');"
          onLongPressStart="$.put('/Zapp/{zapp_index}/Event', 2, null, 'int32');"
        />
        

        The UI itself is intentionally split into separate templates instead of one large uiViewSet, because earlier versions had UI/Duktape memory pressure on the physical watch, especially when running together with ZoneSense and navigation. Splitting into separate templates reduced active subscriptions, active evals, and canvas/UI load.

        The problem:

        When active route navigation is running, pressing the upper button to cycle templates causes a system navigation/POI notification to appear at the bottom of the screen. Sometimes it only flashes, but sometimes it stays visible indefinitely on top of the SuuntoPlus app.

        The app itself continues to work. The template changes correctly. Data updates continue. But the system POI/navigation overlay remains visible.

        The overlay disappears if I leave Constantin and switch to another screen/app, then return to Constantin. The problem appears only when cycling templates with the upper button while navigation is active.

        Things already tested:

        • removed delayed/postponed rendering from the UI
        • removed postpone-rendering:true
        • reduced canvas refreshes and subscriptions
        • added type="lock" / longType="lock" to the up button
        • removed a full-screen black root background
        • changed the root element id from a generic suuntoplus to unique ids per template
        • verified that the canvas is not full-screen and should not be covering the whole display

        None of these mitigated the navigation/POI overlay issue.

        So my current suspicion is that this is not caused by canvas rendering or a full-screen DOM element. It looks more like an interaction between:

        • the physical upper button
        • active navigation / POI system UI
        • immediate unload('_cm') from onEvent()
        • template reloading during the same button event

        Questions:

        1. Is using the upper button for SuuntoPlus template switching expected to conflict with active navigation or POI notifications?
        2. Is type="lock" supposed to prevent this kind of system overlay interaction, or does it only affect button lock behavior?
        3. Is calling unload('_cm') directly inside onEvent() considered safe during active navigation?
        4. Would it be better to defer the actual unload('_cm') to the next evaluate() cycle after receiving the button event?
        5. Is uiViewSet / navigate() preferred over template switching for this case, even if it increases active UI memory pressure?
        6. Is there a recommended button for cycling SuuntoPlus views during navigation, for example down or next instead of up?

        What I’m trying to achieve is a stable way to cycle between 3 lightweight SuuntoPlus workout views on the physical watch, while active navigation and another S+ app such as ZoneSense are running, without triggering or leaving behind the system navigation/POI overlay.

        Any guidance on the recommended architecture or mitigation would be appreciated.

        S9PP 2.50.28

        1 Reply Last reply Reply Quote 1
        • M Offline
          matram @Tomas5
          last edited by

          @Tomas5
          Trying to do the same thing here, with the same result. Works OK in simulator, not on RACE S.

          #3384733 28.05.2026 18:55:06 : ERR WBMAIN : *0: app 717 Event 37 80915318
          #3384734 28.05.2026 18:55:06 : ERR WBMAIN : 1: mea 79 Wait 0 00000000
          

          Some clues. Initially this manifested as a problem with low heap memory after only a few second of HR data.

          #3368541 28.05.2026 13:12:03 : ERR APPLICATION : Zapp:relMemCb (exec:ui)
          #3368542 28.05.2026 13:12:03 : ERR APPLICATION : Zapp:RelMem->None avail
          #3368543 28.05.2026 13:12:03 : ERR DUKTAPE : JSalloc:16
          

          That was fixed by using a Uint8Array(400) and pruning data. That removed the “relMemCb” fault but instead I got the “ERR WBMAIN”…

          It appears some resource is exhausted, and the watch becomes very sluggish after a few minutes. UI is almost frozen.

          Inserting some systemEvent() calls did not reveal any specific line in the code as the culprit.

          I tried performance.memory.usedJSHeapSize, but that is not supported it seems.

          It would be good to have more information about available memory (or other limited resources) and some guidance on how to interpret the error messages.

          Have a new Race2 on its way to me, will check if that makes any difference.

          1 Reply Last reply Reply Quote 0
          • Łukasz SzmigielŁ Offline
            Łukasz Szmigiel
            last edited by Łukasz Szmigiel

            Play stupid games, win stupid prizes xd

            catastrophicfailure.jpg

            TIL: the Duktape JS heap is 133,120 bytes (~130 KB), shared across all loaded zapps. At last in S9PP that is.

            Learned this the hard way today — Constantin crashed my watch three times, once into a full firmware restore.

            I merged my three view templates into one to fix a button-lock bug during route navigation. Bad idea. Each template is ~30–45 KB; all three loaded simultaneously at onLoad = ~130 KB = 99.4% of the entire heap before ZoneSense even shows up. The system tries to recover by force-unloading the offending zapp, which then reloads, fills the heap again, repeat, until Duktape can’t allocate 1392 bytes and crashes:

            WRN UI_FRAMEWORK : JsTotMem 132296/133120
            ERR APPLICATION : Zapp: releaseMemoryCb → Zapp 3: ReleaseMem -> unload  [×3]
            ERR APPLICATION : Zapp: ReleaseMem -> None avail.
            ERR DUKTAPE : JSalloc:1392  [×11]
            ERR FAULT : A302:duktapeFunctionCombiner.cpp
            EVT BOOTLOOP : Bootloop detected, sysmode 5->17
            

            Good to know: ≤ 30–40 KB per feature zapp, one template at a time. With ZoneSense (~30 KB) and Weather (~15 KB) running alongside, headroom is very tight.

            The watch is fine. I’ve reverted to the multi-template build. The original nav overlay bug is still open — if anyone’s solved the unload/type="lock" gap problem without blowing the heap, I’d love to hear it.

            S9PP 2.50.28

            1 Reply Last reply Reply Quote 1
            • Łukasz SzmigielŁ Offline
              Łukasz Szmigiel
              last edited by

              How can I read current wind gusts in a SuuntoPlus app?

              Hi @suuntopartnerteam,

              I’m working on a SuuntoPlus app that displays current weather and forecast weather data. I’m trying to show wind gusts for the current weather, but on the actual watch I only get --.

              In my template I currently read current weather like this:

              $.get('/Weather/Current', function(v) {
                gWxNow = v;
                renderNow();
              });
              

              and then I try to render current gusts with:

              setText('#nowWg', windStr(v.windGust));
              

              This works only if /Weather/Current returns a windGust field. On the watch, however, this seems to be undefined, so my formatter returns --.

              What confused me is that in the SuuntoPlus VS Code simulator internals, the weather mock seems to return a richer weather object. In suunto-internals, getWeatherData() includes fields such as:

              {
                humidity: 43,
                feelsLike: 293.15,
                temp: 293.15,
                weatherType: 2,
                windDeg: 230,
                windType: 5,
                windGust: 8,
                windSpeed: 6,
                pop: .23,
                rain: 1.2
              }
              

              So in the simulator, v.windGust can exist on the weather object.

              But from the documented data paths, /Weather/Current seems to expose only something like:

              {
                temp,
                weatherType,
                windSpeed,
                windType
              }
              

              and the listed current paths appear to be:

              /Weather/Current
              /Weather/Current.temp
              /Weather/Current.weatherType
              /Weather/Current.windSpeed
              /Weather/Current.windType
              

              I can read forecast gusts separately with a path like:

              /Weather/Future/N.windGust
              

              or at least this seems to work on the watch for forecast data. But I have not found an equivalent reliable path for current wind gusts, such as:

              /Weather/Current.windGust
              

              My main question is: is there any supported way to read current wind gusts in a SuuntoPlus app on the actual watch?

              I also have a related question about forecast index 0.

              What is the intended meaning of:

              /Weather/Future/0
              

              How is /Weather/Future/0 different from /Weather/Current?

              Is Future/0 the nearest hourly forecast slot, the current forecast hour, or something else? I’m asking because if current gusts are not available, one possible fallback would be to use /Weather/Future/0.windGust, but I’m not sure whether that would be semantically correct.

              More specifically:

              1. Does /Weather/Current.windGust exist on hardware?
              2. Should current gusts be read from another path, for example Observation data?
              3. Is windGust only available for forecast data, not current weather?
              4. Is the richer weather object in the VS Code simulator only mock data and not representative of the real watch API?
              5. What exactly is the difference between /Weather/Current and /Weather/Future/0?

              Right now, on the watch, current weather gusts display as --, while forecast gusts can be displayed.

              Tested on hardware, not only in the simulator. The issue is that the simulator mock appears to include windGust in the current weather object, but the actual watch does not seem to expose that field.

              Thanks for any clarification.

              S9PP 2.50.28

              1 Reply Last reply Reply Quote 0
              • withManishW Offline
                withManish
                last edited by

                I have a couple of questions.

                1. Is there a way to copy the built-in workout to a new workout while keeping the modified personal workout settings?

                I do understand that the app does not allow modifying any of the in-built workouts, but it’s great to be able to copy them as new personal workout settings and modify them as you want, rather than creating them from scratch.

                1. In-built options for workouts on the watch must be able to modify and save for next use, especially for the battery. As of now, battery mode reverts to Performance, even though I don’t need it in my regular usage. This is an extra checkpoint you have to complete before you start your workout every time.

                2. Even if I change battery mode, it won’t let me change what display setting I want.

                3. The map gets disabled on the watch. The map is already downloaded on the watch; you are just showing me where I am. This does not make any sense to disable map display.

                By the way, I use Race S as of now. My old Ambit 3 still lets me modify every setting as needed. I don’t understand why I am forced to use a performance mood every single time, even though I don’t need to.

                www.withManish.com

                sky-runnerS 1 Reply Last reply Reply Quote 0
                • sky-runnerS Offline
                  sky-runner Platinum Member @withManish
                  last edited by

                  @withManish said:

                  The map gets disabled on the watch. The map is already downloaded on the watch; you are just showing me where I am. This does not make any sense to disable map display.

                  The map is enabled only in Performance mode. According to discussions that I’ve seen in this forum, the cost of preloading loading map segments from the storage is such that if it was enabled in modes other than Performance, the battery use would still be almost the same as in Performance mode.

                  @withManish said:

                  I don’t understand why I am forced to use a performance mood every single time, even though I don’t need to.

                  Honestly I don’t understand why anyone would want to use a battery mode other than Performance. We are talking about 30 hours in Performance mode vs. 40 in Endurance. For everyday training that doesn’t make much difference because the majority of the battery use comes from the smartwatch usage outside of activity, which comes close to roughly 10% per day with raise to wake display activation.

                  Let’s just assume that someone trains 1 hour per day with GPS. When using Performance mode, in a week 70% of use will come smartphone mode and 23% from recording activities. And if someone uses Endurance mode - 17% of use would be from recording activities. So at the end, it is a difference of 93% vs. 87% - not a significant difference at all.

                  When I used Race S I didn’t bother and just used the Performance mode every single time. The only case when I’d consider Endurance mode is if I was doing a 100 mile ultramarathon with Race S.

                  Suunto: Ambit, Ambit 3 Peak, 9 Baro, Race S, Race Ti, Vertical 2 Ti
                  Garmin: Forerunner 210, Forerunner 610, Fenix 6X, Fenix 7X Ti

                  withManishW 1 Reply Last reply Reply Quote 0
                  • withManishW Offline
                    withManish @sky-runner
                    last edited by

                    @sky-runner Thanks for explaining battery usage and map/GPS, but every % of power counts in remote places, where charging would be an issue, unlike at home.

                    If loading maps consumes the same amount of power even though they are preloaded on the device, what is the point of having them preloaded? It is important that you are not always on the map; at times, check where your location is or where you are heading.

                    So it might use a little more battery while checking maps, and again, back to endurance mode to gain more power backup. My question is: why forcibly disable instead of giving a prompt and letting them continue with the map? It’s a matter of safety, too; you want to know whether you’re where you are or headed in the right direction.

                    And an important question: where is all this information mentioned in the manual? When I asked this question to a support person on chat, there was silence, and no response came for a very long time.

                    Anyway, nice to get a response from you and make me understand.

                    www.withManish.com

                    1 Reply Last reply Reply Quote 0
                    • D Offline
                      DonTomGot @matram
                      last edited by

                      @matram I tried including your brilliant design - but - I am very new to Javascript (all modern languages in fact). What pieces of code are supposed to go where in terms of .js files etc?

                      M 1 Reply Last reply Reply Quote 0
                      • M Offline
                        matram @DonTomGot
                        last edited by

                        @DonTomGot
                        No problem, I am more of a Swift guy myself.

                        The functions arcSegment, drawGaugeBackground, pointer and renderGauges are declared as in the onLoad method in t.html.

                        onActivate is also in t.html, same as the onLoad method.

                        The canvas declaration goes at the end of the HTML code in the same file.

                        Good luck 👍

                        1 Reply Last reply Reply Quote 0

                        Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                        Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                        With your input, this post could be even better 💗

                        Register Login
                        • First post
                          Last post

                        Suunto Terms | Privacy Policy