Project Overview
This project involved developing a wearable ECG heart rate monitoring device for a medical technology company. Built on the Nordic nRF52840 high-performance Bluetooth chip and integrating the professional-grade ADS1293 ECG analog front-end, the device enables 24-hour continuous cardiac monitoring, real-time arrhythmia alerts, and cloud-based data analytics.
Core Technical Challenges
1. Medical-Grade ECG Signal Processing
Challenge:
- ECG signals are extremely weak (0.5-4mV) and susceptible to noise interference
- High-precision ADC sampling required (500Hz and above)
- Real-time signal processing must not interfere with Bluetooth transmission
Solution:
// ADS1293 ECG analog front-end configuration
#define ECG_SAMPLE_RATE 500 // 500Hz sampling rate
#define ECG_LEAD_COUNT 3 // 3-lead electrocardiogram
typedef struct {
int32_t lead_I;
int32_t lead_II;
int32_t lead_III;
uint32_t timestamp;
} ecg_sample_t;
// ECG digital filter (50Hz notch + 0.5Hz high-pass + 40Hz low-pass)
void ecg_filter_init(ecg_filter_t *filter) {
// 50Hz notch filter (eliminate power line interference)
filter->notch_coeff_b[0] = 0.9565;
filter->notch_coeff_b[1] = -1.9131;
filter->notch_coeff_b[2] = 0.9565;
filter->notch_coeff_a[1] = -1.9112;
filter->notch_coeff_a[2] = 0.9150;
// Butterworth high-pass filter (eliminate baseline drift)
filter->hp_cutoff = 0.5;
// Butterworth low-pass filter (eliminate high-frequency noise)
filter->lp_cutoff = 40.0;
}
int32_t ecg_apply_filter(ecg_filter_t *filter, int32_t raw_sample) {
// Notch filtering
float notch_out = filter->notch_coeff_b[0] * raw_sample +
filter->notch_coeff_b[1] * filter->notch_x[0] +
filter->notch_coeff_b[2] * filter->notch_x[1] -
filter->notch_coeff_a[1] * filter->notch_y[0] -
filter->notch_coeff_a[2] * filter->notch_y[1];
// Update delay line
filter->notch_x[1] = filter->notch_x[0];
filter->notch_x[0] = raw_sample;
filter->notch_y[1] = filter->notch_y[0];
filter->notch_y[0] = notch_out;
// High-pass filtering
float hp_out = apply_butterworth_hp(notch_out, &filter->hp_state);
// Low-pass filtering
float lp_out = apply_butterworth_lp(hp_out, &filter->lp_state);
return (int32_t)lp_out;
}
2. R-Wave Detection and Heart Rate Calculation
Implemented the Pan-Tompkins algorithm for real-time QRS complex detection:
// Pan-Tompkins QRS detection algorithm
typedef struct {
float derivative_buffer[5];
float squared_buffer[30];
float integrated_buffer[30];
float threshold;
uint32_t last_qrs_time;
uint32_t rr_interval_buffer[8];
uint8_t rr_index;
} qrs_detector_t;
bool qrs_detect(qrs_detector_t *detector, int32_t filtered_sample) {
// 1. Differentiation (emphasize high slopes)
float derivative = (2*filtered_sample + detector->derivative_buffer[0]
- detector->derivative_buffer[2]
- 2*detector->derivative_buffer[3]) / 8.0;
// 2. Squaring (amplify differences)
float squared = derivative * derivative;
// 3. Moving window integration (150ms window)
float integrated = moving_window_integration(squared, detector->integrated_buffer, 30);
// 4. Adaptive threshold detection
if (integrated > detector->threshold) {
uint32_t current_time = get_timestamp_ms();
uint32_t rr_interval = current_time - detector->last_qrs_time;
// Reject excessively short RR intervals (likely noise)
if (rr_interval > 200) { // Minimum 200ms (max heart rate 300bpm)
detector->rr_interval_buffer[detector->rr_index] = rr_interval;
detector->rr_index = (detector->rr_index + 1) % 8;
detector->last_qrs_time = current_time;
// Update adaptive threshold
update_adaptive_threshold(detector);
return true; // R-wave detected
}
}
return false;
}
uint16_t calculate_heart_rate(qrs_detector_t *detector) {
// Calculate average RR interval
uint32_t avg_rr = 0;
for (int i = 0; i < 8; i++) {
avg_rr += detector->rr_interval_buffer[i];
}
avg_rr /= 8;
// Heart rate (bpm) = 60000 / RR interval (ms)
uint16_t heart_rate = (avg_rr > 0) ? (60000 / avg_rr) : 0;
return heart_rate;
}
3. Arrhythmia Detection Algorithm
// Arrhythmia types
typedef enum {
RHYTHM_NORMAL, // Normal sinus rhythm
RHYTHM_BRADYCARDIA, // Bradycardia (<60 bpm)
RHYTHM_TACHYCARDIA, // Tachycardia (>100 bpm)
RHYTHM_IRREGULAR, // Irregular rhythm (excessive RR interval variability)
RHYTHM_AFIB, // Suspected atrial fibrillation
RHYTHM_PVC // Premature ventricular contraction
} cardiac_rhythm_t;
cardiac_rhythm_t detect_arrhythmia(qrs_detector_t *detector, uint16_t heart_rate) {
// 1. Bradycardia detection
if (heart_rate < 60) {
return RHYTHM_BRADYCARDIA;
}
// 2. Tachycardia detection
if (heart_rate > 100) {
return RHYTHM_TACHYCARDIA;
}
// 3. RR interval variability analysis (detect atrial fibrillation)
float rr_variance = calculate_rr_variance(detector->rr_interval_buffer, 8);
float rr_mean = calculate_rr_mean(detector->rr_interval_buffer, 8);
float cv = rr_variance / rr_mean; // Coefficient of variation
if (cv > 0.15) { // Coefficient of variation > 15%
// Further analysis for atrial fibrillation
bool is_afib = afib_classifier(detector);
if (is_afib) {
return RHYTHM_AFIB;
}
return RHYTHM_IRREGULAR;
}
// 4. Premature contraction detection (sudden RR shortening followed by compensatory pause)
if (detect_premature_contraction(detector)) {
return RHYTHM_PVC;
}
return RHYTHM_NORMAL;
}
4. Low-Power Design (7-Day Battery Life)
Power Optimization Strategy:
// Power management configuration
#define ECG_CONTINUOUS_MODE 0 // Continuous monitoring mode (high power)
#define ECG_SMART_MODE 1 // Smart monitoring mode (power saving)
typedef struct {
uint8_t mode;
uint16_t sampling_rate; // Current sampling rate
uint16_t ble_interval; // Bluetooth connection interval
bool motion_detected; // Motion detection flag
} power_config_t;
void optimize_power_consumption(power_config_t *config) {
// 1. Dynamic sampling rate adjustment
if (config->motion_detected) {
// During exercise: reduce sampling rate to save computation
config->sampling_rate = 250; // 250Hz (still meets medical standards)
} else {
// At rest: normal sampling rate
config->sampling_rate = 500; // 500Hz
}
// 2. Dynamic Bluetooth connection interval adjustment
if (config->mode == ECG_SMART_MODE) {
// Normal rhythm: extend Bluetooth interval
config->ble_interval = 1000; // Update once per second
} else {
// Anomaly detected: shorten interval for real-time alerts
config->ble_interval = 100; // 100ms update
}
// 3. Peripheral power saving
nrf_gpio_cfg_sense_input(MOTION_SENSOR_PIN,
NRF_GPIO_PIN_PULLUP,
NRF_GPIO_PIN_SENSE_LOW);
// 4. Enable nRF52 DC/DC converter (40% power reduction)
NRF_POWER->DCDCEN = 1;
}
// FreeRTOS task priority configuration
void create_rtos_tasks(void) {
// Highest priority: ECG sampling (cannot miss samples)
xTaskCreate(ecg_sampling_task, "ECG_SAMP", 512, NULL, 5, NULL);
// High priority: signal processing and QRS detection
xTaskCreate(ecg_processing_task, "ECG_PROC", 1024, NULL, 4, NULL);
// Medium priority: Bluetooth data transmission
xTaskCreate(ble_transmit_task, "BLE_TX", 512, NULL, 3, NULL);
// Low priority: storage and logging
xTaskCreate(data_logging_task, "LOG", 256, NULL, 2, NULL);
}
5. Bluetooth 5.0 High-Speed Transmission
// BLE ECG service definition (custom UUID)
#define BLE_UUID_ECG_SERVICE 0x181D // Health Thermometer Service Base
#define BLE_UUID_ECG_REALTIME_CHAR 0x2A1C // Custom ECG Realtime Data
#define BLE_UUID_ECG_STATS_CHAR 0x2A1D // Custom ECG Statistics
// ECG real-time data characteristic (supports Notification)
static ble_gatts_char_handles_t ecg_realtime_handles;
void ble_ecg_service_init(void) {
ble_uuid_t ble_uuid;
ble_gatts_char_md_t char_md;
ble_gatts_attr_t attr_char_value;
// Set characteristic properties
memset(&char_md, 0, sizeof(char_md));
char_md.char_props.notify = 1; // Enable Notification
char_md.char_props.read = 1;
// Set CCCD (Client Characteristic Configuration Descriptor)
ble_gatts_attr_md_t cccd_md;
memset(&cccd_md, 0, sizeof(cccd_md));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
cccd_md.vloc = BLE_GATTS_VLOC_STACK;
char_md.p_cccd_md = &cccd_md;
// Add characteristic
ble_uuid.type = BLE_UUID_TYPE_BLE;
ble_uuid.uuid = BLE_UUID_ECG_REALTIME_CHAR;
memset(&attr_char_value, 0, sizeof(attr_char_value));
attr_char_value.p_uuid = &ble_uuid;
attr_char_value.max_len = 20; // Maximum 20 bytes (BLE 4.2)
attr_char_value.init_len = 0;
sd_ble_gatts_characteristic_add(ecg_service_handle,
&char_md,
&attr_char_value,
&ecg_realtime_handles);
}
// Bluetooth 5.0 extended data length (maximum 251 bytes)
void enable_ble_data_length_extension(void) {
ble_opt_t opt;
opt.common_opt.conn_evt_ext.enable = 1;
sd_ble_opt_set(BLE_COMMON_OPT_CONN_EVT_EXT, &opt);
// Set PHY to 2Mbps (Bluetooth 5.0)
ble_gap_phys_t phys = {
.tx_phys = BLE_GAP_PHY_2MBPS,
.rx_phys = BLE_GAP_PHY_2MBPS
};
sd_ble_gap_phy_update(conn_handle, &phys);
}
// Real-time ECG data transmission (compressed format)
void send_ecg_data_via_ble(ecg_sample_t *samples, uint8_t count) {
uint8_t buffer[244]; // BLE 5.0 maximum payload
uint16_t offset = 0;
// Packet header
buffer[offset++] = 0xEC; // Magic byte
buffer[offset++] = count; // Sample count
// Differential encoding compression (reduce data volume)
int32_t prev_value = 0;
for (uint8_t i = 0; i < count; i++) {
int16_t diff = (samples[i].lead_I - prev_value) >> 2; // Divide by 4 to reduce precision
buffer[offset++] = (diff >> 8) & 0xFF;
buffer[offset++] = diff & 0xFF;
prev_value = samples[i].lead_I;
}
// Send Notification
ble_gatts_hvx_params_t hvx_params;
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = ecg_realtime_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = 0;
hvx_params.p_len = &offset;
hvx_params.p_data = buffer;
sd_ble_gatts_hvx(conn_handle, &hvx_params);
}
Project Results
Technical Metrics
- ECG sampling precision: 24-bit ADC, 500Hz sampling rate
- R-wave detection: Real-time QRS complex detection via Pan-Tompkins algorithm
- Heart rate measurement range: 30-250 bpm
- Bluetooth transmission: Bluetooth 5.0 2Mbps PHY
Innovation Highlights
- Medical-grade signal quality: TI ADS1293 professional ECG analog front-end
- Real-time arrhythmia alerts: Built-in detection for 5 types of cardiac anomalies with instant push notifications to mobile app
- Low-power design: Smart power-saving algorithms dynamically adjust sampling rate and Bluetooth connection interval
- Cloud data analysis: Integrated cloud model providing health data analytics
Technology Stack
Hardware Platform:
- Nordic nRF52840 (ARM Cortex-M4F 64MHz)
- TI ADS1293 (3-lead ECG analog front-end)
- InvenSense ICM-20948 (9-axis motion sensor)
- 300mAh lithium polymer battery
Firmware Development:
- C/C++ low-level driver development
- FreeRTOS real-time operating system
- Nordic SDK 17.1.0
- SoftDevice S140 Bluetooth protocol stack
Development Tools:
- Segger Embedded Studio
- nRF Connect SDK
- J-Link debugger
- Logic analyzer
Medical Certifications:
- IEC 60601-1 Electrical Safety Standard
- IEC 60601-2-27 ECG Equipment Specific Standard
- ISO 13485 Medical Device Quality Management System
Project Duration: August 2022 - March 2023 Technical Domains: Embedded Systems, Medical Electronics, Bluetooth Communication, Digital Signal Processing