Ken Muse

Swift, Workouts, and Bluetooth Low Energy


This is a post in the series Building a Workout App for watchOS in Swift. The posts in this series include:

As part of my recovery, I decided to be more focused on keeping up a healthy exercise routine. While my underdesk treadmill has served me well, it isn’t a smart treadmill. As a result, it doesn’t play nicely with my Apple devices. Instead, Apple’s fitness apps try to approximate the speed a distance using other sensors. It estimates my cadence and stride length, then uses that to determine the distance I’ve travelled. Unfortunately, the estimates from my devices are consistently off by a substantial amount. I’ve seen the forums, so I know that I’m not the only person that’s noticed this.

I decided it was time to find a solution. I invested in the North Pole Engineering (NPE) Runn treadmill sensor to capture information about my speed, cadence, and distance from a device that could more accurately capture the details. It monitors the treadmill belt and reports the information via Bluetooth Low Energy (BLE). In short, it turns the treadmill into a smart device. Problem solved? Not entirely.

It turns out that Apple only natively supports one particular BLE specification for monitoring exercise – Cycling Speed and Cadence (CSC). Treadmill devices typically rely on the Fitness Machine Service (FTMS) service, so it isn’t natively supported. Hopefully Apple will update the OS to support standards-based connections and not just GymKit. Until then, Runn provides a watchOS application that can create workouts and monitor the FTMS services. Ah! A solution.

Not exactly.

Turns out the application has known issues, including providing incorrect data, failing to start, losing connections with the device, and failing to connect to the device. In short — it is too unreliable at the moment to be a viable solution. NPE claims to be working on fixes. Thankfully, there is still a path forward.

The joy of standards

The BLE specification includes numerous standards for creating interoperable devices with well-defined service specifications. In fact, it has LOTS of services including fitness, battery level, body composition and weight measurement, blood pressure measurements, and cadence. For any service that isn’t standardized, vendors are allowed to implement custom Bluetooth services. These services are prt of the Generic Attribute Profile (GATT) specification. Each service is assigned a universally unique identifier (UUID) to make it easy to discover. A service can than provide additional information – characteristics – to connected clients.

The unfortunate truth is that most devices implement private, proprietary services. This means that the devices require the vendor’s application (which often has questionable data and privacy policies). I’m a big believer in supporting companies that choose to adopt industry standards for interoperability. If you’ve ever worked with IoT (or started to upgrade yourself to a “smart home”), you’ve seen the benefit. Devices that adopt standards put the customer first and choose to compete on quality rather than lock-in. I like these vendors.

I’m in no way trying to promote a specific product. I believe that devices should adopt existing standards and support customers using those standardized services. I chose the NPE Runn specifically because it provides a GATT server that exposes some standardized BLE services:

  • Fitness Machine Service
  • Running and Speed Cadence
  • Battery Service

Unfortunately, they do have some proprietary services. The first of those is well-documented for users. The Device Firmware Update (DFU) service. It uses Nordic’s assigned 16-bit UUID, 0xFE59 and exposes a characteristic,0x8EC90001-F315-4F60-9FB8-838830DAEA50. It also exposes two other UUIDs that are not publicly documented. After reaching out to NPE, they informed me those are proprietary and details are only provided by NDA. Based on their applications, those services seem to support features such as resetting the device, performing calibration, and controlling the power switch functionality.

As a comparison, I purchased a weight scale with body composition support that supports BLE. Unfortunately, ALL of its services are proprietary. As a result, I have no way to reliably read the information it’s providing (unless I want to start analyzing its packets). That means the only way to get the information is to use the vendor’s application and trust how they handle my personal data. That’s not a great way to treat customers.

Because NPE adopted BLE standards, it means I have other options available to me. I could find an application that does what I need and implements a client for those services. Alternatively, I could build my own application in any language or platform (as long as it supports BLE).

The start of a journey

It’s been a while since I developed anything for Apple devices. This seemed like a good excuse to dive back in and spend some quality time with the Swift programming language. While I was able to put together a prototype application in an evening, it didn’t follow practices that would make it easy to maintain over time. Over the coming weeks, I’ll share my journey in building an application for the Apple Watch in Swift that integrates with the Runn BLE. Hopefully, I can provide some development insights and demystify some of the development process.

Implementing my application does require a bit of knowledge about Bluetooth. I’ll walk through that more in future posts. In those posts, I’ll reference some specifications:

Based on those, I’ll be implementing an application that can read those values, display them on the watch, and capture that information into a workout.