GlanceHR - Heart Rate Glanceο
Overviewο
The GlanceHR app is a simple wearable application designed to display real-time heart rate data in a compact glance view. It provides continuous heart rate monitoring with a clean, minimal interface that shows the current heart rate value alongside an icon. The app integrates heart rate sensor data and presents it through the UNA SDKβs glance framework, making it easily accessible from the watch face.
The application follows a service-only architecture with no separate GUI component, communicating directly with the glance system through the UNA SDKβs kernel infrastructure. It supports real-time heart rate updates and handles sensor connection management automatically.
Key features include:
Real-time heart rate monitoring with live display
Automatic sensor connection and data processing
Clean glance interface with heart rate icon and value
Graceful handling of sensor availability and data validity
Low-power operation suitable for continuous monitoring
Architectureο
The GlanceHR app follows a service-only architecture pattern where the service component handles all functionality including sensor integration, data processing, and glance UI management. Communication occurs through the UNA SDKβs kernel infrastructure and glance messaging system.
High-Level Componentsο
Service Layer: Core business logic, sensor integration, data processing, and glance UI management
SDK Integration: Kernel, sensor layer, glance framework, messaging
Glance Framework: UNA SDKβs glance system for compact UI display
Component Interactionο
[Hardware Sensors] <-> [Sensor Layer] <-> [Service] <-> [Glance Framework]
^ ^ ^
| | |
[Kernel Messages] <-- [Message System] --> [Glance Updates]
The service runs as a separate process, continuously processing heart rate sensor data and updating the glance display. The glance framework handles the actual rendering and user interaction.
Service Backendο
The service backend is implemented in Service.hpp and Service.cpp, providing the core functionality for heart rate monitoring and glance display.
Core Classes and Structuresο
Service Classο
The main service class inherits from no base class but implements lifecycle management and messaging through the kernel.
class Service {
public:
Service(SDK::Kernel &kernel);
virtual ~Service();
void run();
private:
// Implementation details
};
Key Data Structuresο
Heart Rate Data:
SDK::Sensor::Connection mSensorHR; // Heart rate sensor connection
float mHrValue; // Current heart rate value
bool mIsValid; // Data validity flag
Glance UI Components:
SDK::Glance::Form mGlanceUI; // Main glance form
SDK::Glance::ControlText mGlanceTitle; // "Live HR" title
SDK::Glance::ControlText mGlanceValue; // Heart rate value display
Sensor Integrationο
The service manages a single heart rate sensor connection:
Heart Rate Sensor (
SDK::Sensor::Type::HEART_RATE): Provides BPM measurements with trust levels
The sensor is represented by an SDK::Sensor::Connection object with appropriate sampling periods and latencies.
Data Processing Pipelineο
The data processing pipeline is streamlined for heart rate display, focusing on real-time updates and validity checking.
1. Sensor Data Receptionο
Heart rate data arrives through the kernelβs message system. The handleSensorsData() method processes the sensor data:
void Service::handleSensorsData(uint16_t handle, SDK::Sensor::DataBatch& data) {
if (mSensorHR.matchesDriver(handle)) {
SDK::SensorDataParser::HeartRate p(data[0]);
float newValue = p.getBpm();
LOG_DEBUG("hr = %.2f\n", newValue);
if (static_cast<uint32_t>(newValue) != static_cast<uint32_t>(mHrValue)) {
mHrValue = newValue;
glanceUpdate();
}
}
}
Each sensor connection has a matchesDriver() method to identify the source of the data batch.
2. Data Validation and Filteringο
Raw heart rate data undergoes validation to ensure accuracy:
Heart Rate Validation:
if (mHrValue > 1.0 && !mIsValid) {
mIsValid = true;
// Switch to valid display mode
} else if (mHrValue <= 1.0 && mIsValid) {
mIsValid = false;
// Switch to calculating display mode
}
The app distinguishes between valid heart rate readings (> 1.0 BPM) and invalid/calculating states.
3. Glance Update Logicο
The glanceUpdate() method manages the display state based on data validity:
void Service::glanceUpdate() {
if (mHrValue > 1.0 && !mIsValid) {
mIsValid = true;
mGlanceValue.pos({ 80, 28 }, { 160, 34 })
.font(GlanceFont_t::GLANCE_FONT_POPPINS_SEMIBOLD_30)
.setText("")
.alignment(GlanceAlignH_t::GLANCE_ALIGN_H_CENTER);
} else if (mHrValue <= 1.0 && mIsValid) {
mIsValid = false;
mGlanceValue.pos({ 81, 34 }, { 130, 23 })
.font(GlanceFont_t::GLANCE_FONT_POPPINS_SEMIBOLD_18)
.setText(skTextCalculating)
.alignment(GlanceAlignH_t::GLANCE_ALIGN_H_LEFT);
}
if (mIsValid) {
mGlanceValue.print("%.0f ", mHrValue);
}
}
Activity State Managementο
The service maintains glance lifecycle states:
Glance Start: Initialize UI and connect sensors
Glance Stop: Disconnect sensors and cleanup
Glance Tick: Periodic UI updates and refresh
State transitions are handled by message processing in the main run loop.
Glance UI Managementο
The app creates a simple glance interface with three components:
Glance Form Layout:
void Service::createGuiControls() {
// Heart rate icon (60x60 pixels)
mGlanceUI.createImage().init({20, 0}, {60, 60}, ICON_60X60_ABGR2222);
// Title text "Live HR"
mGlanceTitle = mGlanceUI.createText();
mGlanceTitle.pos({ 70, 0 }, { 100, 25 })
.font(GlanceFont_t::GLANCE_FONT_POPPINS_SEMIBOLD_20)
.color(GlanceColor_t::GLANCE_COLOR_TEAL)
.setText("Live HR")
.alignment(GlanceAlignH_t::GLANCE_ALIGN_H_CENTER);
// Heart rate value display
mGlanceValue = mGlanceUI.createText();
mGlanceValue.pos({ 81, 34 }, { 172, 23 })
.font(GlanceFont_t::GLANCE_FONT_POPPINS_SEMIBOLD_18)
.color(GlanceColor_t::GLANCE_COLOR_WHITE)
.setText(skTextCalculating)
.alignment(GlanceAlignH_t::GLANCE_ALIGN_H_LEFT);
}
Settings and Configurationο
The app configures the glance display through the UNA SDKβs glance configuration system:
bool Service::configGui() {
bool status = false;
if (auto gc = SDK::make_msg<SDK::Message::RequestGlanceConfig>(mKernel)) {
if (gc.send(100) && gc.ok()) {
if (gc->maxControls >= 3) {
mGlanceUI.setWidth(gc->width);
mGlanceUI.setHeight(gc->height);
status = true;
}
}
}
return status;
}
The configuration ensures the glance has sufficient space for the three UI controls (icon, title, value).
Sensor Integrationο
The GlanceHR app integrates a single physiological sensor through the UNA SDKβs sensor layer.
Heart Rate Sensorο
Heart Rate Sensor (SDK::Sensor::Type::HEART_RATE):
Provides BPM measurements with trust levels
Used for real-time heart rate display
Trust levels: 1-3 (higher is better)
Only valid data is displayed
Sensor Data Processing Architectureο
The sensor data processing system uses specialized parsers for heart rate data extraction and validation.
Parser Classesο
Heart Rate Parser:
SDK::SensorDataParser::HeartRate parser(data[0]);
if (parser.isDataValid()) {
float bpm = parser.getBpm();
uint8_t trustLevel = parser.getTrustLevel();
// Process heart rate data
}
Data Validationο
Heart Rate Validity Checking:
bool isValidHeartRate = (mHrValue > 1.0 && mHrValue <= 300.0); // Reasonable BPM range
if (isValidHeartRate) {
// Display heart rate
} else {
// Show calculating state
}
Sensor Sampling Strategyο
Adaptive Sampling Rates:
Heart Rate: 1 Hz sampling for real-time display
Latency: 1000ms latency balancing responsiveness and power consumption
Error Handling and Data Validationο
Sensor Availability Management:
void Service::connect() {
if (!mSensorHR.isConnected()) {
LOG_DEBUG("Connect to sensors...\n");
mSensorHR.connect();
}
}
void Service::disconnect() {
if (mSensorHR.isConnected()) {
LOG_DEBUG("Disconnect from sensors...\n");
mSensorHR.disconnect();
}
}
Sensor connections are managed based on glance lifecycle events.
Glance Framework Integrationο
The app uses the UNA SDKβs glance framework for compact UI display:
Glance Message Systemο
Glance Update Messages:
void Service::onGlanceTick() {
if (mGlanceUI.isInvalid()) {
if (auto upd = SDK::make_msg<SDK::Message::RequestGlanceUpdate>(mKernel)) {
upd->name = APP_NAME;
upd->controls = mGlanceUI.data();
upd->controlsNumber = static_cast<uint32_t>(mGlanceUI.size());
upd.send(100);
}
mGlanceUI.setValid();
}
}
The glance system handles periodic updates and invalidation management.
Control Typesο
Text Controls:
Title control with fixed βLive HRβ text
Value control with dynamic heart rate display
Font and color customization through SDK enums
Image Controls:
Heart rate icon display using pre-compiled bitmap data
Positioned alongside text controls
Message Handling Systemο
The service implements message-based communication for lifecycle and data events:
switch (msg->getType()) {
case SDK::MessageType::EVENT_GLANCE_START:
// Initialize glance and connect sensors
break;
case SDK::MessageType::EVENT_GLANCE_STOP:
// Disconnect sensors and cleanup
break;
case SDK::MessageType::EVENT_GLANCE_TICK:
// Periodic UI refresh
break;
case SDK::MessageType::EVENT_SENSOR_LAYER_DATA:
// Process sensor data
break;
}
Build and Setupο
The GlanceHR app uses CMake for cross-platform builds targeting embedded hardware.
Build System Overviewο
Primary Build File: CMakeLists.txt in GlanceHR-CMake/
# App configuration
set(APP_NAME "GlanceHR")
set(APP_USER_NAME "Live HR")
set(APP_TYPE "Glance")
set(DEV_ID "UNA")
set(APP_ID "A1358F7C2E9D4BA6")
# Include SDK build scripts
include($ENV{UNA_SDK}/cmake/una-app.cmake)
include($ENV{UNA_SDK}/cmake/una-sdk.cmake)
Build Targetsο
Service Build:
set(SERVICE_SOURCES
${LIBS_SOURCES}
${UNA_SDK_SOURCES_COMMON}
${UNA_SDK_SOURCES_SERVICE}
)
una_app_build_service(${APP_NAME}Service.elf)
Complete App:
una_app_build_app()
Dependenciesο
SDK Components:
UNA SDK common and service sources
Sensor layer interfaces
Kernel and messaging systems
Glance framework components
Build Processο
CMake Configuration: Sets up toolchain and paths
Source Collection: Gathers service source files
Compilation: Builds service executable
Packaging: Combines into deployable glance app package
Development Setupο
See SDK Setup and Build Overview for comprehensive development environment setup, build instructions, and toolchain requirements.
Conclusionο
The GlanceHR app demonstrates a focused implementation of a heart rate monitoring glance for wearable devices. Its service-only architecture provides efficient real-time heart rate display with minimal resource usage.
Key architectural strengths include:
Simplicity: Focused on single responsibility of heart rate display
Efficiency: Low-power operation suitable for continuous monitoring
Integration: Seamless integration with UNA SDK glance framework
Reliability: Robust sensor connection and data validation
User Experience: Clean, intuitive interface for quick heart rate checks
The implementation showcases effective use of the UNA SDKβs glance system and sensor layer for building lightweight, purpose-driven wearable applications. The app successfully provides real-time heart rate monitoring while maintaining the performance characteristics expected of glance interfaces.