I wanted stock prices on my wrist. Not a notification — an actual chart. So I built a custom Garmin watch face with live sparkline charts for 5 configurable assets. Here’s what I learned building StockFaceTC for the Garmin Venu 2 Plus. The Challenge Garmin watch faces are tiny — 124KB memory, 416×416 AMOLED display, and a language (Monkey C) that most devs have never heard of. No npm. No frameworks. No Stack Overflow answers. Oh, and the “simulator” can’t make web requests. You test API calls on real hardware or not at all. What I Built A watch face showing: Time, date, and health stats (heart rate, steps, floors) 5 polar sparkline charts in an outer ring — each showing 24 hours of price data Auto-updates every 15 minutes via Twelve Data API 6-hour weather forecast with custom-drawn icons Fully configurable tickers via the Garmin Connect app Default tickers: AAPL, TSLA, Gold, BTC/USD, ETH/USD The Font Problem (and How I Solved It) Garmin’s VectorFont API isn’t available on all devices. The Venu 2 Plus? Nope. So I built my own: PrimitiveFont — a complete vector font library using only drawLine() calls. Every character is defined on a 5×7 grid and rendered procedurally. // “A” on a 5×7 grid CHAR_A = [0,6, 0,2, 0,2, 2,0, 2,0, 4,2, 4,2, 4,6, 0,4, 4,4] Enter fullscreen mode Exit fullscreen mode Features: Full alphabet (A-Z, a-z, 0-9, specials) Proportional widths (Arial-like metrics — ‘i’ is narrow, ‘M’ is wide) Bold, italic, underline Arc text — characters curved along a circular path (for the ring labels!) Any size via simple scaling: sizePx / 7.0 The entire library is ~21KB — well within the 124KB budget. Architecture Garmin Background Service (every 15 min) → Twelve Data API (1h candles × 24 = full day) → Phone acts as transparent HTTP proxy → JSON parsed in background thread (64KB limit!) → Prices + display names stored in model → View renders sparklines + arc labels on update Enter fullscreen mode Exit fullscreen mode Key Constraint: 64KB Background Memory The background service that fetches data runs in a separate 64KB sandbox. You can’t access the main app’s memory. The only way to pass data: Background.exit(dictionary), which the main app receives in onBackgroundData(). This means: parse JSON, extract only what you need, pass a minimal dictionary. No room for raw API responses. Dynamic Labels from API One early mistake: I hardcoded display names like “XAU/USD” → “GOLD”. That doesn’t scale. Better approach: The Twelve Data API returns metadata with every response: “meta”: { “symbol”: “XAU/USD”, “currency_base”: “Gold Spot”, “type”: “Precious Metal” } Enter fullscreen mode Exit fullscreen mode Now labels come from the API itself: Stocks: meta.symbol → “AAPL” Crypto: meta.currency_base → “Bitcoin” → “BITCOIN” Commodities: meta.currency_base → “Gold Spot” → “GOLD” Zero hardcoded aliases. Add any ticker, get the right label automatically. Simulator Gotchas If you’re building Connect IQ apps, save yourself some pain: Properties are cached forever — changing properties.xml defaults does nothing after first run. Use “Reset All App Data” first. makeWebRequest() doesn’t work in the simulator — you need a real phone connected via Bluetooth. getSettingsView() must be implemented (even returning null) or the settings menu stays greyed out. No VectorFont on many devices — if you need custom text, roll your own like PrimitiveFont. API Budget Twelve Data free tier: 800 API calls/day. My budget: 5 symbols × 96 fetches/day (every 15 min) = 480 calls. Well within limits. The Code Source is on GitLab. Feel free to fork it, break it, or build something cooler. What’s the weirdest hardware you’ve built for? Drop it in the comments.
I Built a Garmin Watch Face with Live Stock Charts in Monkey C — Here’s What I Learned
Full Article
Original Source
Read the full article at Dev →KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.