Suunto app Forum Suunto Community Forum
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Register
    • Login
    1. Home
    2. Popular
    Log in to post
    • All Time
    • Day
    • Week
    • Month
    • All Topics
    • New Topics
    • Watched Topics
    • Unreplied Topics

    • All categories
    • S

      [Question] No stupid questions - ask anything here

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Plus Development
      2
      3 Votes
      2 Posts
      52 Views
      Dimitrios KanellopoulosD
      Welcome from all of us to the Suunto plus development!
    • S

      Examples explained

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Plus Development
      2
      0 Votes
      2 Posts
      69 Views
      S
      FowlPlay FowlPlay is a quick, light‑hearted minigame perfect for killing time while waiting during any sport. Master the slingshot, tweak your trajectory and send your bird soaring toward its waiting pig friend. manifest.json The manifest for FowlPlay only defines the strictly necessary fields: name (that app’s name), description (a short description of the app; preferably under 22 characters), version (a unique short identifier; must be changed when reuploading to ApiZone), author (credit where credit is due), modificationTime (a Epoch Unix Timestamp in whole seconds representing modification time), type (the literal string “feature” for sport apps), usage (the literal string “workout”) and template (an array of html templates used by this app). manifest.json: { "name": "FowlPlay", "description": "Unite with the pig!!", "version": "1.0", "author": "Birdy B.", "modificationTime": 1770000000, "type": "feature", "usage": "workout", "template": [{"name": "g.html"}] } main.js For this app, all rendering and game logic live directly inside the HTML template (see below). Every app still needs a main.js file and must implement the global getUserInterface function that tells the watch which template to load. The function should return an object that includes at least a template field with template’s name. The file extension (.html) should be dropped here, even though the manifest lists full file names for templates. Here’s our minimal main.js file: main.js: function getUserInterface() { return { template: 'g' }; } g.html Finally, the HTML template! The file name should match the declaration in manifest.json and value returned by getUserInterface in main.js. For your convenience the entire code with comments is listed here and explained below: g.html: <uiView onLoad=" // Set constants for world gravity (g) and bird/enemy radius(r) var g = .2, r = 5; // Set the current state (one of 'sling', 'launched', 'won' or 'reset') and initialize sleep timer (s) var state = 'sling', s = 0; // Set variables for the launch force (f), launch angle (a) and current direction (d; angle) var f = 2, a = 0, d = 0; // Define the position and velocities for the bird and enemy var bird = {x: 20, y: 61.5, dx: 0, dy: 0}; var enemy = {x: 80, y: 80 - r, dx: 0, dy: 0}; /** @param { CanvasRenderingContext2D } ctx */ function renderGame(ctx) { // Viewport width and height from canvas width and height var vw = ctx.width / 100; var vh = ctx.height / 100; // Draw sky ctx.fillStyle = '#CCEEFF'; ctx.fillRect(0, 0, 100 * vw, 100 * vh); // Draw ground ctx.fillStyle = '#14B814'; ctx.fillRect(0, 80 * vh, 100 * vw, 20 * vh); // Draw slingshot ctx.strokeStyle = '#8A380F'; ctx.fillStyle = '#8A380F'; ctx.beginPath(); ctx.lineWidth = 3 * vh; ctx.lineCap = 'round' ctx.arc(20 * vw, 61.5 * vh, 7.5 * vh, 0, 3.15); ctx.stroke(); ctx.fillRect(18 * vw, 70 * vh, 4 * vw, 10 * vh); // Draw bird circle(ctx, '#F2DF0D', bird.x * vw, bird.y * vh, r * vh); // Calculate current direction d = (state == 'sling') ? a : (!!bird.dx ? (Math.atan(bird.dy / bird.dx) + .8) / -.2 : 0); // Draw beak ctx.fillStyle = '#F97706'; ctx.beginPath(); ctx.moveTo((bird.x + Math.cos(-d * .2 - 1 ) * r * .45) * vw, (bird.y + Math.sin(-d * .2 - 1 ) * r * .45) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - .4) * r * .95) * vw, (bird.y + Math.sin(-d * .2 - .4) * r * .95) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 + .2) * r * .45) * vw, (bird.y + Math.sin(-d * .2 + .2) * r * .45) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - .4) * r * .2 ) * vw, (bird.y + Math.sin(-d * .2 - .4) * r * .2 ) * vh); ctx.closePath(); ctx.fill(); // Draw eyes ctx.fillStyle = '#000000'; ctx.beginPath(); ctx.moveTo((bird.x + Math.cos(-d * .2 - 2.1) * r * .25) * vw, (bird.y + Math.sin(-d * .2 - 2.1) * r * .25) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - 1.4) * r * .5 ) * vw, (bird.y + Math.sin(-d * .2 - 1.4) * r * .5 ) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - 1.5) * r * .75) * vw, (bird.y + Math.sin(-d * .2 - 1.5) * r * .75) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - 1.2) * r * .75) * vw, (bird.y + Math.sin(-d * .2 - 1.2) * r * .75) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - 1.3) * r * .15) * vw, (bird.y + Math.sin(-d * .2 - 1.3) * r * .15) * vh); ctx.fill(); // Draw enemy circle(ctx, '#FF80D4', enemy.x * vw, enemy.y * vh, r * vh); // Eyes circle(ctx, '#ffffff', (enemy.x - 2) * vw, enemy.y * vh, (r - 3.5) * vh); circle(ctx, '#ffffff', (enemy.x + 2) * vw, enemy.y * vh, (r - 3.5) * vh); circle(ctx, '#000000', (enemy.x - 2) * vw, (enemy.y - .25) * vh, .25 * vh); circle(ctx, '#000000', (enemy.x + 2) * vw, (enemy.y - .25) * vh, .25 * vh); //Snout circle(ctx, '#FFCCEE', enemy.x * vw, (enemy.y + 1) * vh, (r - 3) * vh); circle(ctx, '#330022', (enemy.x - .75) * vw, (enemy.y + .5) * vh, .65 * vh); circle(ctx, '#330022', (enemy.x + .75) * vw, (enemy.y + .75) * vh, .45 * vh); // Draw predictions var pred = nextPos({x: bird.x, y: bird.y, dx: bird.dx, dy: bird.dy}); if (state == 'sling') for (var i = 0; i <= 5; ++i) { pred = nextPos(nextPos(pred)); circle(ctx, '#47b4eb', pred.x * vw, pred.y * vh, 1 * vh); } // Game info ctx.fillStyle = '#0d5173'; cText(ctx, 'FowlPlay', 50 * vw, 15 * vh); if (state == 'sling') cText(ctx, 'Force: ' + f, 50 * vw, 25 * vh); if (state == 'won') cText(ctx, 'Congrats, you win!', 50 * vw, 25 * vh); // Move bird and enemy if (state == 'launched') bird = nextPos(bird); enemy = nextPos(enemy); // Calculate new direction if (state == 'sling') { bird.dx = Math.cos(-a * .2 - .8) * f; bird.dy = Math.sin(-a * .2 - .8) * f; } // Check if bird and enemy overlap if ((bird.x - enemy.x) * (bird.x - enemy.x) + (bird.y - enemy.y) * (bird.y - enemy.y) < 4 * r * r) state = 'won'; // Next attempt if bird is still for 30 frames or game is reset if ((Math.abs(bird.dx) < 0.1 && Math.abs(bird.dy) < 0.1) || state == 'won') { ++s; } else { s = 0; } if ((s > 30 && state != 'sling')|| state == 'reset') { // Reset game state, bird position and get random location for enemy from current time state = 'sling'; bird = {x: 20, y: 61.5, dx: 0, dy: 0}; $.get('/Dev/Time/LocalTime', function(v){ enemy.x = parseInt(formatValue(v, 'time s')) % 11 * 4 + 40; }) } } // Helper function to center text function cText(ctx, text, x, y) { ctx.fillText(text, x - ctx.measureText(text).width / 2, y); } // Helper function to draw a circle function circle(ctx, color, x, y, rd) { ctx.fillStyle = color; ctx.beginPath(); ctx.arc(x, y, rd, 0, 6.3); ctx.closePath(); ctx.fill(); } // Helper method to calculate next position function nextPos(obj) { obj.dy += g; obj.x += obj.dx; obj.y += obj.dy; // Snap to ground if below ground surface after movement for simplicity if (obj.y >= 80 - r) { obj.y = 80 - r; obj.dy *= -.9; obj.dx *= .9; } return obj; } // Helper function to handle push button input function handleGameIO(v) { // Up and down button-click changes the launch angle if (v == 10 && state == 'sling') ++a; if (v == 20 && state == 'sling') --a; // Up-button-longpress changes the force of the slingshot if (v == 11 && state == 'sling') f = (f % 4) + 1; // Down-button-longpress launches the bird if (v == 21 && state == 'sling') state = 'launched'; if (v == 20 && state == 'launched') state = 'reset'; } " onActivate = "$.subscribe('/Dev/Time/Tick10hz', function(){ control('#cnv', 'REFRESH'); })" > <div id="suuntoplus"> <uiViewSet id="view"> <div id="welcome"> <div style="width: 100%; height: 50%; top: 0%; left: 0%;"><img src="btn-shape-top.png" class="c-yellow"></div> <div class="f-b-s cm-fgc" style="top: 13%; left: calc(79% - 100%e);" >CHANGE FORCE</div> <div style="width: 50px; height: 50px; top: calc(30% - 50%e); left: calc(90% - 50%e);"><img class="cm-bgc" src="hint-btn-top.png" /></div> <div class="p-m"> <span class="f-m">FowlPlay</span><br/> Click buttons to aim<br /> Longpress for action<br /> Launch away! </div> <div style="width: 100%; height: 50%; top: calc(100% - 100%e); left: 0%;"><img src="btn-shape-btm.png" class="c-yellow p-b"></div> <div class="f-b-s cm-fgc" style="top: calc(87% - 100%e); left: calc(79% - 100%e);" >LAUNCH BIRD</div> <div style="width: 50px; height: 50px; top: calc(70% - 50%e); left: calc(90% - 50%e);"><img class="cm-bgc" src="hint-btn-bottom.png" /></div> <userInput> <pushButton name="down" onLongPress="navigate('#view', 'game')"> </userInput> </div> <div id="game"> <object id="cnv" type="canvas" build="ctx => renderGame(ctx)" style="width: 100%; height: 100%; top: 0%; left: 0%;"/> <userInput> <pushButton name="up" onClick="handleGameIO(10)" onLongPressStart="handleGameIO(11)" /> <pushButton name="down" onClick="handleGameIO(20)" onLongPressStart="handleGameIO(21)" /> </userInput> </div> </uiViewSet> </div> </uiView> If you have the SuuntoPlusEditor-plugin installed in Visual Studio Code and your Suunto watch connected to your laptop you are now ready to test the app: Create an empty folder and open it in Visual Studio Code Create new files manifest.json, main.js and g.html Copy-paste the code snippets above to their respective files Navigate to the bottom of the Explorer view where these files are listed. Under the SuuntoPlus Apps section hover the mouse over the newly created app and click on the watch icon (Deploy to Watch). Wait for the building and uploading to finish; the app is now available on your watch under SuuntoPlus once you start a new exercise. Please note that the app does not work in the simulator. In this example, the g.html file handles everything from layout and user interactions (watch buttons) to game logic and rendering. Let’s remove the game-related code and focus on the layout and user interactions first. <uiView> <div id="suuntoplus"> <uiViewSet id="view"> <div id="welcome"> <div style="width: 100%; height: 50%; top: 0%; left: 0%;"><img src="btn-shape-top.png" class="c-yellow"></div> <div class="f-b-s cm-fgc" style="top: 13%; left: calc(79% - 100%e);" >CHANGE FORCE</div> <div style="width: 50px; height: 50px; top: calc(30% - 50%e); left: calc(90% - 50%e);"><img class="cm-bgc" src="hint-btn-top.png" /></div> <div class="p-m"> <span class="f-m">FowlPlay</span><br/> Click buttons to aim<br /> Longpress for action<br /> Launch away! </div> <div style="width: 100%; height: 50%; top: calc(100% - 100%e); left: 0%;"><img src="btn-shape-btm.png" class="c-yellow p-b"></div> <div class="f-b-s cm-fgc" style="top: calc(87% - 100%e); left: calc(79% - 100%e);" >LAUNCH BIRD</div> <div style="width: 50px; height: 50px; top: calc(70% - 50%e); left: calc(90% - 50%e);"><img class="cm-bgc" src="hint-btn-bottom.png" /></div> <userInput> <pushButton name="down" onLongPress="navigate('#view', 'game')"> </userInput> </div> <div id="game"> <!-- The game logic will be added here --> </div> </uiViewSet> </div> </uiView> Every sport app template has a <uiView> element representing the Suunto watch as their root element. It is good practice to wrap the content inside <uiView> in a <div> (you can think of these as the <html> and <body> elements of HTML). This app has two different screens: a welcome screen and the actual game. This can be achieved with a <uiViewSet> element containing two <div> elements. All three elements have an id attribute for easy referencing in the code. This can be used for example to change which of the <div> elements is displayed. This is done with: <userInput> <pushButton name="down" onLongPress="navigate('#view', 'game')"> </userInput> The <userInput> element can contain several <pushButton> elements. A <pushButton> has a name attribute to identify the button (here set to “down” for the bottom-right button) and event listeners for different types of user actions (here we listen to a longpress with onLongPress). Because the <userInput> element is defined inside the welcome screen’s <div> element, the interactions are captured only when the welcome screen is visible. navigate is a global built-in function of the watch that takes two parameters here: a query string to identify the targeted <uiViewSet> element and the id attribute of the <div> element to display. The rest of the <div> elements are used to set the position and dimensions (setting CSS styles (top/left/width/height) with the style attribute; % represents a percentage of the parent element’s width/height while %e represents a percentage of the current element’s width/height) as well as the font and color (through class names with the class attribute) of texts and images on the welcome screen. The class names and images used here are part of the watch and can be used without explicit definition or importing. The first letter of the class name typically defines its purpose: c is for colors (fg = foreground; bg = background; cm = current (light/dark) mode), f is for font styles and p is for positioning. The game logic is defined in an event listener attached to the <uiView>. Here we use the onLoad attribute (notice the uppercase L compared to the HTML standard) that behaves similarly to the onLoad function that could be defined in main.js except the symbols (functions and variables) defined here are in scope for (and thus can be used in) the entire HTML template. We take advantage of this for rendering. First we define a drawing surface in our layout using: <object id="cnv" type="canvas" build="ctx => renderGame(ctx)" style="width: 100%; height: 100%; top: 0%; left: 0%;"/> An <object> element with a type attribute of “canvas” works similarly to a HTML5 <canvas>. The content will be rendered using the anonymous function defined by the build attribute. Here we capture the argument (that is a modified version of a CanvasRenderingContext2D from the JavaScript Canvas API) and pass it onto the function renderGame. By setting the id and style attributes we ensure the canvas can be easily referenced later and takes up the entire screen respectively. This setup draws our game just once, but for a smooth gaming experience the screen needs to be updated continuously. This is achieved by adding the following attribute to the <uiView> element: onActivate = "$.subscribe('/Dev/Time/Tick10hz', function(){ control('#cnv', 'REFRESH'); })" Here the $ object allows us to access the devices resources. Here we use the subscribe method to attach a change listener to a resource. The '/Dev/Time/Tick10hz' resource will trigger the callback function approximately 10 times per second (this essentially works like setInterval in JavaScript). control is a global built-in function of the watch that takes two parameters: a query string to identify the target element and the control signal to send it. Here this causes the <canvas> to be refreshed (=rerendered). The actual rendering is handled by the renderGame function defined in the onLoad attribute of the <uiView>: function renderGame(ctx) { // Viewport width and height from canvas width and height var vw = ctx.width / 100; var vh = ctx.height / 100; // Draw sky ctx.fillStyle = '#CCEEFF'; ctx.fillRect(0, 0, 100 * vw, 100 * vh); // Draw ground ctx.fillStyle = '#14B814'; ctx.fillRect(0, 80 * vh, 100 * vw, 20 * vh); // Draw slingshot ctx.strokeStyle = '#8A380F'; ctx.fillStyle = '#8A380F'; ctx.beginPath(); ctx.lineWidth = 3 * vh; ctx.lineCap = 'round' ctx.arc(20 * vw, 61.5 * vh, 7.5 * vh, 0, 3.15); ctx.stroke(); ctx.fillRect(18 * vw, 70 * vh, 4 * vw, 10 * vh); // Draw bird circle(ctx, '#F2DF0D', bird.x * vw, bird.y * vh, r * vh); // Calculate current direction d = (state == 'sling') ? a : (!!bird.dx ? (Math.atan(bird.dy / bird.dx) + .8) / -.2 : 0); // Draw beak ctx.fillStyle = '#F97706'; ctx.beginPath(); ctx.moveTo((bird.x + Math.cos(-d * .2 - 1 ) * r * .45) * vw, (bird.y + Math.sin(-d * .2 - 1 ) * r * .45) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - .4) * r * .95) * vw, (bird.y + Math.sin(-d * .2 - .4) * r * .95) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 + .2) * r * .45) * vw, (bird.y + Math.sin(-d * .2 + .2) * r * .45) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - .4) * r * .2 ) * vw, (bird.y + Math.sin(-d * .2 - .4) * r * .2 ) * vh); ctx.closePath(); ctx.fill(); // Draw eyes ctx.fillStyle = '#000000'; ctx.beginPath(); ctx.moveTo((bird.x + Math.cos(-d * .2 - 2.1) * r * .25) * vw, (bird.y + Math.sin(-d * .2 - 2.1) * r * .25) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - 1.4) * r * .5 ) * vw, (bird.y + Math.sin(-d * .2 - 1.4) * r * .5 ) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - 1.5) * r * .75) * vw, (bird.y + Math.sin(-d * .2 - 1.5) * r * .75) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - 1.2) * r * .75) * vw, (bird.y + Math.sin(-d * .2 - 1.2) * r * .75) * vh); ctx.lineTo((bird.x + Math.cos(-d * .2 - 1.3) * r * .15) * vw, (bird.y + Math.sin(-d * .2 - 1.3) * r * .15) * vh); ctx.fill(); // Draw enemy circle(ctx, '#FF80D4', enemy.x * vw, enemy.y * vh, r * vh); // Eyes circle(ctx, '#ffffff', (enemy.x - 2) * vw, enemy.y * vh, (r - 3.5) * vh); circle(ctx, '#ffffff', (enemy.x + 2) * vw, enemy.y * vh, (r - 3.5) * vh); circle(ctx, '#000000', (enemy.x - 2) * vw, (enemy.y - .25) * vh, .25 * vh); circle(ctx, '#000000', (enemy.x + 2) * vw, (enemy.y - .25) * vh, .25 * vh); //Snout circle(ctx, '#FFCCEE', enemy.x * vw, (enemy.y + 1) * vh, (r - 3) * vh); circle(ctx, '#330022', (enemy.x - .75) * vw, (enemy.y + .5) * vh, .65 * vh); circle(ctx, '#330022', (enemy.x + .75) * vw, (enemy.y + .75) * vh, .45 * vh); // Draw predictions var pred = nextPos({x: bird.x, y: bird.y, dx: bird.dx, dy: bird.dy}); if (state == 'sling') for (var i = 0; i <= 5; ++i) { pred = nextPos(nextPos(pred)); circle(ctx, '#47b4eb', pred.x * vw, pred.y * vh, 1 * vh); } // Game info ctx.fillStyle = '#0d5173'; cText(ctx, 'FowlPlay', 50 * vw, 15 * vh); if (state == 'sling') cText(ctx, 'Force: ' + f, 50 * vw, 25 * vh); if (state == 'won') cText(ctx, 'Congrats, you win!', 50 * vw, 25 * vh); // Move bird and enemy if (state == 'launched') bird = nextPos(bird); enemy = nextPos(enemy); // Calculate new direction if (state == 'sling') { bird.dx = Math.cos(-a * .2 - .8) * f; bird.dy = Math.sin(-a * .2 - .8) * f; } // Check if bird and enemy overlap if ((bird.x - enemy.x) * (bird.x - enemy.x) + (bird.y - enemy.y) * (bird.y - enemy.y) < 4 * r * r) state = 'won'; // Next attempt if bird is still for 30 frames or game is reset if ((Math.abs(bird.dx) < 0.1 && Math.abs(bird.dy) < 0.1) || state == 'won') { ++s; } else { s = 0; } if ((s > 30 && state != 'sling')|| state == 'reset') { // Reset game state, bird position and get random location for enemy from current time state = 'sling'; bird = {x: 20, y: 61.5, dx: 0, dy: 0}; $.get('/Dev/Time/LocalTime', function(v){ enemy.x = parseInt(formatValue(v, 'time s')) % 11 * 4 + 40; }) } } The drawing is achieved by using methods familiar from the JavaScript Canvas API such as fillRect, beginPath, arc, moveTo, lineTo, fillText, measureText, stroke and fill as well as reading and modifying properties such as width, height, fillStyle, strokeStyle, lineWidth and lineCap. It is notable that the drawing context can be passed on to other function as is done in this example with the following helper functions:: // Helper function to center text function cText(ctx, text, x, y) { ctx.fillText(text, x - ctx.measureText(text).width / 2, y); } // Helper function to draw a circle function circle(ctx, color, x, y, rd) { ctx.fillStyle = color; ctx.beginPath(); ctx.arc(x, y, rd, 0, 6.3); ctx.closePath(); ctx.fill(); } We have also defined some constants and variables to manage the game’s physics and state (and once again defined a helper function to move the bird): // Set constants for world gravity (g) and bird/enemy radius(r) var g = .2, r = 5; // Set the current state (one of 'sling', 'launched', 'won' or 'reset') and initialize sleep timer (s) var state = 'sling', s = 0; // Set variables for the launch force (f), launch angle (a) and current direction (d; angle) var f = 2, a = 0, d = 0; // Define the position and velocities for the bird and enemy var bird = {x: 20, y: 61.5, dx: 0, dy: 0}; var enemy = {x: 80, y: 80 - r, dx: 0, dy: 0}; // Helper method to calculate next position function nextPos(obj) { obj.dy += g; obj.x += obj.dx; obj.y += obj.dy; // Snap to ground if below ground surface after movement for simplicity if (obj.y >= 80 - r) { obj.y = 80 - r; obj.dy *= -.9; obj.dx *= .9; } return obj; } Here a game object (the bird or pig) is defined by its x and y coordinates as well as its horizontal and vertical velocity (dx and dy). One quirk of the code is how the new pseudo-random location for the pig is calculated. $.get('/Dev/Time/LocalTime', function(v){ enemy.x = parseInt(formatValue(v, 'time s')) % 11 * 4 + 40; }) Here we fetch (the get method from the $ object works similarly to subscribe method except this is only executed once) the current time, treat the value as a time and format it to only include the current second (using the built-in function formatValue). Then we convert the seconds to a number and clamp it to fit our game world. The final step to complete the game is to attach event listeners to user input. This is similar to before except this time we use a helper function to handle the mapping of the users actions to correct changes to the game depending on the game’s state. <userInput> <pushButton name="up" onClick="handleGameIO(10)" onLongPressStart="handleGameIO(11)" /> <pushButton name="down" onClick="handleGameIO(20)" onLongPressStart="handleGameIO(21)" /> </userInput> // Helper function to handle push button input function handleGameIO(v) { // Up and down button-click changes the launch angle if (v == 10 && state == 'sling') ++a; if (v == 20 && state == 'sling') --a; // Up-button-longpress changes the force of the slingshot if (v == 11 && state == 'sling') f = (f % 4) + 1; // Down-button-longpress launches the bird if (v == 21 && state == 'sling') state = 'launched'; if (v == 20 && state == 'launched') state = 'reset'; } The app combines many different capabilities of Suunto watches. Feel free to ask any questions you might have about this example app. Hopefully it inspired you to create some awesome. Happy launching; may your idle moments forever be fowl‑filled!
    • matt-rM

      Suunto Race S – stress and HR issues after charging (firmware 2.50.x)

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Race S
      2
      0 Votes
      2 Posts
      162 Views
      Set_218S
      @matt-r The HR-problem is the same by my side since the last update. When i start a running or walking modus everything works fine. I go just to a shop or pick up my kids at school…. The hr is around 180-190bpm. I put off the watch from my wrist for some seconds. Put it back and it works. Sometimes the issue come back in a few minutes
    • P

      Erg mode for the Wahoo Kickr Core

      Watching Ignoring Scheduled Pinned Locked Moved SuuntoPlus™ Sports Apps
      2
      1 Votes
      2 Posts
      78 Views
      E
      @peringmar Its a more general issue, i have a Suito-T elite trainer and I also can’t control it, it would be nice to tackle this for all trainers
    • J

      Suunto Vertical vs Garmin Fenix 7

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Vertical
      24
      41 Votes
      24 Posts
      26k Views
      Gail HamiltonG
      I have face same issue on my client site.
    • withManishW

      Stuck to Power Save Mode

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Race S
      2
      1
      0 Votes
      2 Posts
      117 Views
      Horizontal_2H
      @withManish 12 s upper button could do tricks. Don’t have experience with your issue, though
    • kemetterK

      Suunto Routeplanner

      Watching Ignoring Scheduled Pinned Locked Moved Suunto app and other software services
      42
      1
      7 Votes
      42 Posts
      3k Views
      HonzaSH
      I wanted to try the route planner and found a bug that makes planning impossible. In the “Outdoor” map view, forest and dirt roads are displayed as regular roads. Only after zooming out to a certain level do they appear correctly as dashed green lines. However, at that zoom level not all paths are displayed anymore. The dark map mode works exactly the opposite way. Even at the highest zoom level, forest and dirt roads are displayed correctly as dashed lines, but from the same zoom-out level they start to be drawn as regular roads. [image: 1772911649342-b7009e92-e727-4c06-959a-edb5b376f284-image.png] [image: 1772911667337-231d2f3e-6ed0-4432-b3d3-a2288c117f3c-image.png] [image: 1772911725773-ad2281ea-65e3-4c51-836c-4858e1583a3e-image.png] [image: 1772911750528-87680971-631a-4a81-a595-7d997139edeb-image.png] In Suunto Android app, the same forest and dirt roads are displayed correctly as full or dashed green lines.
    • Christoph Van LaethemC

      Styd update Sync Vertical 2

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Vertical 2
      6
      0 Votes
      6 Posts
      409 Views
      Christoph Van LaethemC
      @Zdeněk-Hruška Thanks for the information, I sold my race 2, just before the last update from Stryd. I tested some other watches, Polar , Apple WU3 and I think I Will be going back to Suunto V2. So I hope it will work normal again
    • L

      Interval training. Next interval notification.

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Race 2
      15
      0 Votes
      15 Posts
      2k Views
      E
      I’d like to take this opportunity to ask a question: do these alerts sound on yours? Mine only gets a faint vibration (it would be nice if it also notified you when you leave Zonesense zones).
    • pavel.samokhaP

      Suunto Nautic S General Discussion

      Watching Ignoring Scheduled Pinned Locked Moved Nautic S
      49
      1
      5 Votes
      49 Posts
      5k Views
      A
      @Nandodiver i’m a 3-star CMAS diver. for training purposes, we dive to a maximum depth of 42 meters. they said that due to Suunto issues, their sensors were unreliable, but I really liked the Nautic S, especially its visible screen and design. thank you very much.
    • strainS

      Custom Alarms

      Watching Ignoring Scheduled Pinned Locked Moved Watches
      7
      5 Votes
      7 Posts
      415 Views
      Manuel ExtremeM
      @Mff73 In my opinion, the negative interpretation often depends on the stress level of the person reading. Firmware updates are something many of us users really look forward to, because besides fixing bugs they also bring improvements that let us enjoy our devices even more. Over the past weeks I’ve submitted several feature suggestions (like the one about customizable alarms and notifications), confirmed bugs reported by other users, and reported a few that I personally found. I also went back and read some older posts from two or three years ago, and many of the suggested features keep appearing again over time. So I’m not sure the next updates will actually resolve or improve that much. Still, as a user I hope they will, and as a fan I’m always curious and a bit impatient to see what the next update will bring.
    • Kraisun TuntaK

      Recovery watch & App

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Race 2
      19
      1 Votes
      19 Posts
      2k Views
      N
      @emkei For me, sleep tracking is better on my Race 2 than on my 7X.
    • mich hellerM

      yet another crash during workout

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Race 2 race 2 crash gps
      1
      2
      0 Votes
      1 Posts
      74 Views
      No one has replied
    • S

      [Tip] How to improve your design with built-in and custom images

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Plus Development tip image development icon design
      1
      3
      0 Votes
      1 Posts
      40 Views
      No one has replied
    • A

      Software Limitations of Suunto Nautic vs Garmin X50i

      Watching Ignoring Scheduled Pinned Locked Moved Nautic
      12
      3
      5 Votes
      12 Posts
      1k Views
      A
      User @divingpassion discovered that the tank pressure disappears under 30 bars. if you go below 30 bar, Suunto Nautic shows 0.0 minute gas consumption remaining. Source: https://forum.suunto.com/topic/14751/showing-bar-below-30
    • S

      [Tip] How to save/load persistent data

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Plus Development tip settings variables tutorial json manifest
      1
      0 Votes
      1 Posts
      34 Views
      No one has replied
    • S

      [Discussion] Simulator vs physical watch: key discrepancies & limitations

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Plus Development development simulator issues
      1
      0 Votes
      1 Posts
      32 Views
      No one has replied
    • S

      [Discussion] Share your projects

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Plus Development discussion peer-support
      1
      0 Votes
      1 Posts
      31 Views
      No one has replied
    • S

      [Inspiration] Share your app ideas

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Plus Development inspiration peer-support
      1
      1 Votes
      1 Posts
      35 Views
      No one has replied
    • Turo JantunenT

      Vo2Max and cycling

      Watching Ignoring Scheduled Pinned Locked Moved Suunto Race 2
      4
      0 Votes
      4 Posts
      387 Views
      L
      @Turo-Jantunen +1, I miss this. Is this in the product roadmap ?