HRMonitor - Heart Rate Monitorο
Overviewο
The HRMonitor app is a dedicated heart rate monitoring application designed for wearable devices, specifically targeting continuous cardiac monitoring. It provides real-time display of heart rate measurements with trust level assessment, persistent data recording in FIT file format, and a clean TouchGFX-based user interface optimized for wearable screens.
The application follows a modular architecture with separate service and GUI components, communicating through a custom message system using the UNA SDKβs kernel infrastructure. It supports continuous heart rate tracking with data export capabilities and automatic activity session management.
Key features include:
Real-time heart rate monitoring with trust level display
Continuous data recording in FIT file format
Activity session tracking with summary statistics
Simple, focused user interface for wearable devices
Automatic sensor connection and data validation
Architectureο
The HRMonitor app follows a client-server architecture pattern where the service component handles all backend logic, sensor integration, and data processing, while the GUI component manages user interaction and display. Communication between these components occurs through a message-based system using the UNA SDKβs kernel infrastructure.
High-Level Componentsο
Service Layer: Core business logic, heart rate sensor integration, data processing
GUI Layer: TouchGFX-based user interface, heart rate display
SDK Integration: Kernel, sensor layer, file system, messaging
Data Persistence: FIT file format for heart rate data
Component Interactionο
[Heart Rate Sensor] <-> [Sensor Layer] <-> [Service]
^ ^ ^
| | |
[File System] <-- [Activity Writer] --> [FIT Recording]
^ ^ ^
| | |
[Kernel Messages] <-- [Message System] --> [GUI]
The service runs as a separate process/thread, continuously processing heart rate sensor data and maintaining activity state. The GUI runs on the TouchGFX framework, handling user input and displaying heart rate information received from the service.
Service Backendο
The service backend is implemented in Service.hpp and Service.cpp, providing the core functionality for heart rate monitoring and data recording.
Core Classes and Structuresο
Service Classο
The main service class inherits from no base class but implements several interfaces for lifecycle management and messaging.
class Service {
public:
Service(SDK::Kernel &kernel);
virtual ~Service();
void run();
private:
// Implementation details
};
Key Data Structuresο
Heart Rate Data Structure:
struct {
float mHR; // Current heart rate value
float mHRTL; // Trust level (1-3 scale)
} mHeartRateData;
Activity Writer Integration:
ActivityWriter mActivityWriter; // Handles FIT file recording
Sensor Integrationο
The service manages heart rate sensor connection:
Heart Rate Sensor (
SDK::Sensor::Type::HEART_RATE):Provides BPM measurements with trust levels
Trust levels: 1-3 (higher is better)
1-second sampling period with 2-second latency
Each sensor is represented by an SDK::Sensor::Connection object with specific sampling periods and latencies.
Data Processing Pipelineο
The data processing pipeline handles heart rate data reception, validation, and recording:
1. Sensor Data Receptionο
Heart rate data arrives through the kernelβs message system. The onSdlNewData() method processes the sensor data:
void Service::onSdlNewData(uint16_t handle, SDK::Sensor::DataBatch& data) {
if (mSensorHR.matchesDriver(handle)) {
if (mGUIStarted) {
SDK::SensorDataParser::HeartRate parser(data[0]);
if (parser.isDataValid()) {
mHR = parser.getBpm();
mHRTL = parser.getTrustLevel();
mSender.updateHeartRate(mHR, mHRTL);
}
}
}
}
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 quality:
Trust Level Filtering:
if (parser.isDataValid()) {
mHR = parser.getBpm();
mHRTL = parser.getTrustLevel(); // 1-3 scale
// Only send valid data to GUI
mSender.updateHeartRate(mHR, mHRTL);
}
3. FIT File Recordingο
Heart rate data is recorded in FIT (Flexible and Interoperable Data Transfer) format every second during active monitoring:
ActivityWriter::RecordData fitRecord {};
fitRecord.timestamp = utc;
fitRecord.heartRate = static_cast<uint8_t>(mHR);
fitRecord.trustLevel = static_cast<uint8_t>(mHRTL);
mActivityWriter.addRecord(fitRecord);
FIT records include heart rate and trust level data for interoperability with fitness applications.
Activity State Managementο
The service maintains activity state and handles lifecycle events:
Active Monitoring: Continuously collects heart rate data when GUI is active
Data Recording: Writes FIT records every second
Session Management: Tracks activity start/end times
Statistics Calculation: Computes average and maximum heart rates
Settings and Persistenceο
Activity data is persisted in FIT format with session summaries:
Heart rate records with timestamps
Trust level information
Session start/end times
Average and maximum heart rate calculations
GUI Implementationο
The GUI is built using TouchGFX framework, providing a simple, focused interface for heart rate monitoring.
Model-View-Presenter Patternο
The GUI follows MVP architecture:
Model:
Model.hpp/cpp- Data management and service communicationView: MainView - UI rendering
Presenter: MainPresenter - Logic binding model and view
Key GUI Componentsο
Model Classο
The Model class serves as the central data hub:
class Model : public touchgfx::UIEventListener,
public SDK::Interface::IGuiLifeCycleCallback,
public SDK::Interface::ICustomMessageHandler {
public:
void bind(ModelListener *listener);
void tick();
void handleKeyEvent(uint8_t c);
// Heart rate management methods
void exitApp();
};
Key responsibilities:
Lifecycle management (onStart, onResume, onSuspend, onStop)
Message handling from service
Application exit coordination
View Classesο
MainView: Primary heart rate display screen
Shows current heart rate value
Displays trust level indicator
Minimal button layout with exit functionality
Message Handling Systemο
The Model implements ICustomMessageHandler to receive heart rate updates from the service:
bool Model::customMessageHandler(SDK::MessageBase *msg) {
switch (msg->getType()) {
case CustomMessage::HR_VALUES: {
auto* m = static_cast<CustomMessage::HRValues*>(msg);
modelListener->updateHR(m->heartRate, m->trustLevel);
} break;
default:
break;
}
return true;
}
Custom Containers Implementationο
The app uses minimal custom containers for the heart rate display:
Main Screen Layout:
Large heart rate number display
Trust level indicator
Exit button (R2)
Input Handling Architectureο
User input is minimal, focused on application exit:
Button Mapping:
R2: Exit application
Data Formatting and Unitsο
The GUI handles heart rate display formatting:
Heart Rate Display:
Unicode::snprintfFloat(textHRBuffer, TEXTHR_SIZE, "%.0f", hr);
textHR.invalidate();
Trust Level Display:
Unicode::snprintfFloat(textTRLBuffer, TEXTTRL_SIZE, "%.0f", tl);
textTRL.invalidate();
Sensor Integrationο
The HRMonitor app integrates heart rate 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
Trust levels indicate signal quality (1-3 scale)
Continuous monitoring during app active state
Sensor Data Processing Architectureο
The sensor data processing system uses specialized parsers for heart rate data extraction:
Parser Classesο
Heart Rate Parser:
SDK::SensorDataParser::HeartRate parser(data[0]);
if (parser.isDataValid()) {
mHR = parser.getBpm();
mHRTL = parser.getTrustLevel();
}
Data Validationο
Trust Level Assessment:
Trust levels 1-3 indicate signal quality
Higher trust levels represent better signal quality
Data is always sent to GUI regardless of trust level
Sensor Sampling Strategyο
Heart Rate Sampling:
1 Hz sampling for real-time display
2-second latency for power optimization
Continuous sampling during active monitoring
TouchGFX Portο
The HRMonitor app uses TouchGFX for its graphical user interface, providing a clean, minimal design for wearable devices.
TouchGFX Project Structureο
TouchGFX-GUI/
βββ application.config # Application configuration
βββ target.config # Target hardware settings
βββ touchgfx.cmake # CMake integration
βββ gui/ # Generated and custom GUI code
βββ assets/ # Images, fonts, texts (minimal)
βββ generated/ # Auto-generated code
GUI Architectureο
Screens and Transitions:
Single main screen for heart rate display
No screen transitions required
Direct exit functionality
Custom Containers:
Minimal container usage
Direct text and button widgets
TouchGFX Integration with UNA SDKο
The integration uses standard TouchGFXCommandProcessor and KernelProviderGUI patterns.
Custom Message Systemο
The app defines custom message types for service-GUI communication:
namespace CustomMessage {
constexpr SDK::MessageType::Type HR_VALUES = 0x00000001;
struct HRValues : public SDK::MessageBase {
float heartRate;
float trustLevel;
};
}
Message Sender Classesο
GUISender provides type-safe message sending:
class GUISender {
public:
bool updateHeartRate(float value, float trustLevel) {
if (auto req = SDK::make_msg<CustomMessage::HRValues>(mKernel)) {
req->heartRate = value;
req->trustLevel = trustLevel;
return req.send();
}
return false;
}
};
Asset Managementο
Images: Minimal icon assets for the application Fonts: Standard TouchGFX fonts for text display Texts: Localized strings (minimal text content)
Build and Setupο
The HRMonitor app uses CMake for cross-platform builds targeting embedded hardware and simulation.
Build System Overviewο
Primary Build File: CMakeLists.txt in HRMonitor-CMake/
# App configuration
set(APP_NAME "HRMonitor")
set(APP_TYPE "Activity")
set(DEV_ID "UNA")
set(APP_ID "F1E2D3C448669780")
# 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)
GUI Build:
set(GUI_SOURCES
${TOUCHGFX_SOURCES}
${UNA_SDK_SOURCES_COMMON}
${UNA_SDK_SOURCES_GUI}
)
una_app_build_gui(${APP_NAME}GUI.elf)
Complete App:
una_app_build_app()
Dependenciesο
SDK Components:
UNA SDK common, service, and GUI sources
Sensor layer interfaces
Kernel and messaging systems
External Libraries:
TouchGFX framework
Custom app libraries (ActivityWriter)
Development Setupο
See SDK Setup and Build Overview for comprehensive development environment setup, build instructions, and toolchain requirements.
Simulator Buildο
TouchGFX provides simulator builds for PC development:
Visual Studio project for Windows
Includes mock hardware interfaces
Conclusionο
The HRMonitor app demonstrates a focused implementation of a heart rate monitoring application on wearable devices. Its modular architecture separates concerns effectively between service logic and user interface, enabling reliable sensor integration and data recording.
Key architectural strengths include:
Separation of Concerns: Clear division between service and GUI
Message-Based Communication: Loose coupling between components
Sensor Integration: Robust heart rate sensor handling
Data Persistence: FIT file format ensures interoperability
Minimal UI: Focused design optimized for wearable use
The implementation showcases real-time systems programming, embedded GUI development, and sensor data processing. The app successfully integrates heart rate monitoring with persistent storage while maintaining a clean, focused user experience.
Future enhancements could include additional physiological metrics, enhanced trust level visualization, and expanded activity tracking features while maintaining the core architectural principles established in this implementation.