/**@brief Function for handling the Heart rate measurement timer time-out. * * @details This function will be called each time the heart rate measurement timer expires. * It will exclude RR Interval data from every third measurement. * * @param[in] xTimer Handler to the timer that called this function. * You may get identifier given to the function xTimerCreate using pvTimerGetTimerID. */ static void heart_rate_meas_timeout_handler(TimerHandle_t xTimer) { static uint32_t cnt = 0; uint32_t err_code; uint16_t heart_rate; UNUSED_PARAMETER(xTimer); heart_rate = (uint16_t)sensorsim_measure(&m_heart_rate_sim_state, &m_heart_rate_sim_cfg); cnt++; err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, heart_rate); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_PACKETS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } // Disable RR Interval recording every third heart rate measurement. // NOTE: An application will normally not do this. It is done here just for testing generation // of messages without RR Interval measurements. m_rr_interval_enabled = ((cnt % 3) != 0); }
/**@brief Function for populating simulated health thermometer measurement. */ static void hts_sim_measurement(ble_hts_meas_t * p_meas) { static ble_date_time_t time_stamp = { 2012, 12, 5, 11, 50, 0 }; uint32_t celciusX100; p_meas->temp_in_fahr_units = false; p_meas->time_stamp_present = true; p_meas->temp_type_present = (TEMP_TYPE_AS_CHARACTERISTIC ? false : true); celciusX100 = sensorsim_measure(&m_temp_celcius_sim_state, &m_temp_celcius_sim_cfg); p_meas->temp_in_celcius.exponent = -2; p_meas->temp_in_celcius.mantissa = celciusX100; p_meas->temp_in_fahr.exponent = -2; p_meas->temp_in_fahr.mantissa = (32 * 100) + ((celciusX100 * 9) / 5); p_meas->time_stamp = time_stamp; p_meas->temp_type = BLE_HTS_TEMP_TYPE_FINGER; // update simulated time stamp time_stamp.seconds += 27; if (time_stamp.seconds > 59) { time_stamp.seconds -= 60; time_stamp.minutes++; if (time_stamp.minutes > 59) { time_stamp.minutes = 0; } } }
void ant_hrm_simulator_one_iteration(ant_hrm_simulator_t * p_simulator) { if (p_simulator->_cb.auto_change) { UNUSED_PARAMETER(sensorsim_measure(&(p_simulator->_cb.sensorsim_state), &(p_simulator->_cb.sensorsim_cfg))); } // @note: Take a local copy within scope in order to assist the compiler in variable register // allocation. const uint32_t computed_heart_rate_value = p_simulator->_cb.sensorsim_state.current_val; // @note: This implementation assumes that the current instantaneous heart can vary and this // function is called with static frequency. // value and the heart rate pulse interval is derived from it. The computation is based on 60 // seconds in a minute and the used time base is 1/1024 seconds. const uint32_t current_hb_pulse_interval = (60u * 1024u) / computed_heart_rate_value; // update time from last hb detected p_simulator->_cb.time_since_last_hb += ITERATION_PERIOD; // extended celculadion by fraction make calculating accurat in long time perspective p_simulator->_cb.fraction_since_last_hb += ITERATION_FRACTION; uint32_t add_period = p_simulator->_cb.fraction_since_last_hb / ANT_CLOCK_FREQUENCY; if (add_period > 0) { p_simulator->_cb.time_since_last_hb++; p_simulator->_cb.fraction_since_last_hb %= ANT_CLOCK_FREQUENCY; } // calc number of hb as will fill uint32_t new_beats = p_simulator->_cb.time_since_last_hb / current_hb_pulse_interval; uint32_t add_event_time = new_beats * current_hb_pulse_interval; if (new_beats > 0) { p_simulator->p_profile->HRM_PROFILE_computed_heart_rate = (uint8_t)computed_heart_rate_value; // Current heart beat event time is the previous event time added with the current heart rate // pulse interval. uint32_t current_heart_beat_event_time = p_simulator->p_profile->HRM_PROFILE_beat_time + add_event_time; // Set current event time. p_simulator->p_profile->HRM_PROFILE_beat_time = current_heart_beat_event_time; // <- B<4,5> <- // Set previous event time. // p4.B<2,3> <- B<4,5> p_simulator->p_profile->HRM_PROFILE_prev_beat = p_simulator->p_profile->HRM_PROFILE_beat_time - current_hb_pulse_interval; // Event count. p_simulator->p_profile->HRM_PROFILE_beat_count += new_beats; // B<6> p_simulator->_cb.time_since_last_hb -= add_event_time; } }
/**@brief Function for populating simulated cycling speed and cadence measurements. */ static void csc_sim_measurement(ble_cscs_meas_t * p_measurement) { static uint16_t cumulative_crank_revs = 0; static uint16_t event_time = 0; static uint16_t wheel_revolution_mm = 0; static uint16_t crank_rev_degrees = 0; uint16_t mm_per_sec; uint16_t degrees_per_sec; uint16_t event_time_inc; // Per specification event time is in 1/1024th's of a second. event_time_inc = (1024 * SPEED_AND_CADENCE_MEAS_INTERVAL) / 1000; // Calculate simulated wheel revolution values. p_measurement->is_wheel_rev_data_present = true; mm_per_sec = KPH_TO_MM_PER_SEC * sensorsim_measure(&m_speed_kph_sim_state, &m_speed_kph_sim_cfg); wheel_revolution_mm += mm_per_sec * SPEED_AND_CADENCE_MEAS_INTERVAL / 1000; m_cumulative_wheel_revs += wheel_revolution_mm / WHEEL_CIRCUMFERENCE_MM; wheel_revolution_mm %= WHEEL_CIRCUMFERENCE_MM; p_measurement->cumulative_wheel_revs = m_cumulative_wheel_revs; p_measurement->last_wheel_event_time = event_time + (event_time_inc * (mm_per_sec - wheel_revolution_mm) / mm_per_sec); // Calculate simulated cadence values. p_measurement->is_crank_rev_data_present = true; degrees_per_sec = RPM_TO_DEGREES_PER_SEC * sensorsim_measure(&m_crank_rpm_sim_state, &m_crank_rpm_sim_cfg); crank_rev_degrees += degrees_per_sec * SPEED_AND_CADENCE_MEAS_INTERVAL / 1000; cumulative_crank_revs += crank_rev_degrees / DEGREES_PER_REVOLUTION; crank_rev_degrees %= DEGREES_PER_REVOLUTION; p_measurement->cumulative_crank_revs = cumulative_crank_revs; p_measurement->last_crank_event_time = event_time + (event_time_inc * (degrees_per_sec - crank_rev_degrees) / degrees_per_sec); event_time += event_time_inc; }
/**@brief Function for populating simulated running speed and cadence measurement. */ static void rsc_sim_measurement(ble_rscs_meas_t * p_measurement) { p_measurement->is_inst_stride_len_present = true; p_measurement->is_total_distance_present = false; p_measurement->is_running = false; p_measurement->inst_speed = sensorsim_measure(&m_speed_mps_sim_state, &m_speed_mps_sim_cfg); p_measurement->inst_cadence = sensorsim_measure(&m_cadence_rpm_sim_state, &m_cadence_rpm_sim_cfg); p_measurement->inst_stride_length = sensorsim_measure(&m_cadence_stl_sim_state, &m_cadence_stl_sim_cfg); if (p_measurement->inst_speed > (uint32_t)(MIN_RUNNING_SPEED * 256)) { p_measurement->is_running = true; } }
/**@brief Function for handling the RR interval timer timeout. * * @details This function will be called each time the RR interval timer expires. * * @param[in] p_context Pointer used for passing some arbitrary information (context) from the * app_start_timer() call to the timeout handler. */ static void rr_interval_timeout_handler(void * p_context) { UNUSED_PARAMETER(p_context); if (m_rr_interval_enabled) { uint16_t rr_interval; rr_interval = (uint16_t)sensorsim_measure(&m_rr_interval_sim_state, &m_rr_interval_sim_cfg); ble_hrs_rr_interval_add(&m_hrs, rr_interval); rr_interval = (uint16_t)sensorsim_measure(&m_rr_interval_sim_state, &m_rr_interval_sim_cfg); ble_hrs_rr_interval_add(&m_hrs, rr_interval); rr_interval = (uint16_t)sensorsim_measure(&m_rr_interval_sim_state, &m_rr_interval_sim_cfg); ble_hrs_rr_interval_add(&m_hrs, rr_interval); rr_interval = (uint16_t)sensorsim_measure(&m_rr_interval_sim_state, &m_rr_interval_sim_cfg); ble_hrs_rr_interval_add(&m_hrs, rr_interval); rr_interval = (uint16_t)sensorsim_measure(&m_rr_interval_sim_state, &m_rr_interval_sim_cfg); ble_hrs_rr_interval_add(&m_hrs, rr_interval); rr_interval = (uint16_t)sensorsim_measure(&m_rr_interval_sim_state, &m_rr_interval_sim_cfg); ble_hrs_rr_interval_add(&m_hrs, rr_interval); } }
/**@brief Function for performing battery measurement and updating the Battery Level characteristic * in Battery Service. */ static void battery_level_update(void) { uint32_t err_code; uint8_t battery_level; battery_level = (uint8_t)sensorsim_measure(&m_battery_sim_state, &m_battery_sim_cfg); err_code = ble_bas_battery_level_update(&m_bas, battery_level); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_PACKETS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } }
/**@brief Function for handling the Heart rate measurement timer timeout. * * @details This function will be called each time the heart rate measurement timer expires. * It will exclude RR Interval data from every third measurement. * * @param[in] p_context Pointer used for passing some arbitrary information (context) from the * app_start_timer() call to the timeout handler. */ static void heart_rate_meas_timeout_handler(void * p_context) { #if !defined(__RING_SUPPORT__) static uint32_t cnt = 0; uint16_t heart_rate; #endif uint32_t err_code; UNUSED_PARAMETER(p_context); #if defined(__RING_SUPPORT__) err_code = ble_sensor_data_send(&m_hrs); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_BUFFERS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } #else heart_rate = (uint16_t)sensorsim_measure(&m_heart_rate_sim_state, &m_heart_rate_sim_cfg); cnt++; err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, heart_rate); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_BUFFERS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } // Disable RR Interval recording every third heart rate measurement. // NOTE: An application will normally not do this. It is done here just for testing generation // of messages without RR Interval measurements. m_rr_interval_enabled = ((cnt % 3) != 0); #endif }
void ant_sdm_simulator_one_iteration(ant_sdm_simulator_t * p_simulator) { if (p_simulator->_cb.auto_change) { UNUSED_PARAMETER(sensorsim_measure(&(p_simulator->_cb.sensorsim_state), &(p_simulator->_cb.sensorsim_cfg))); } p_simulator->_cb.time += SIMULATOR_TIME_INCREMENT; p_simulator->_cb.stride_incr += p_simulator->_cb.sensorsim_state.current_val * SIMULATOR_TIME_INCREMENT; p_simulator->p_profile->SDM_PROFILE_strides += p_simulator->_cb.stride_incr / SIMULATOR_STRIDE_UNIT_REVERSAL; p_simulator->_cb.stride_incr = p_simulator->_cb.stride_incr % SIMULATOR_STRIDE_UNIT_REVERSAL; uint32_t distance = value_rescale( p_simulator->p_profile->SDM_PROFILE_strides * p_simulator->_cb.stride_length, SIMULATOR_STRIDE_LENGTH_UNIT_REVERSAL, ANT_SDM_DISTANCE_UNIT_REVERSAL); if (p_simulator->p_profile->SDM_PROFILE_capabilities.cadency_is_valid) { p_simulator->p_profile->SDM_PROFILE_cadence = p_simulator->_cb.sensorsim_state.current_val; } if (p_simulator->p_profile->SDM_PROFILE_capabilities.speed_is_valid) { p_simulator->p_profile->SDM_PROFILE_speed = value_rescale( p_simulator->_cb.sensorsim_state.current_val * p_simulator->_cb.stride_length, ANT_SDM_CADENCE_UNIT_REVERSAL * SIMULATOR_STRIDE_LENGTH_UNIT_REVERSAL * SEC_PER_MIN, ANT_SDM_SPEED_UNIT_REVERSAL); } if (p_simulator->p_profile->SDM_PROFILE_capabilities.distance_is_valid) { p_simulator->p_profile->SDM_PROFILE_distance = distance; } if (p_simulator->p_profile->SDM_PROFILE_capabilities.calorie_is_valid) { p_simulator->p_profile->SDM_PROFILE_calories = value_rescale(distance, SIMULATOR_BURN_RATE_UNIT * ANT_SDM_DISTANCE_UNIT_REVERSAL, p_simulator->_cb.burn_rate); } if (p_simulator->p_profile->SDM_PROFILE_capabilities.time_is_valid) { p_simulator->p_profile->SDM_PROFILE_time = value_rescale(p_simulator->_cb.time, SIMULATOR_TIME_UNIT_REVERSAL, ANT_SDM_TIME_UNIT_REVERSAL); } if (p_simulator->p_profile->SDM_PROFILE_capabilities.latency_is_valid) { p_simulator->p_profile->SDM_PROFILE_update_latency = ROUNDED_DIV(((uint64_t)p_simulator->_cb.stride_incr * ANT_SDM_UPDATE_LATENCY_UNIT_REVERSAL), (uint64_t)SIMULATOR_TIME_UNIT_REVERSAL * p_simulator->_cb.sensorsim_state.current_val); } }
/**@brief Function for handling Peer Manager events. * * @param[in] p_evt Peer Manager event. */ static void pm_evt_handler(pm_evt_t const * p_evt) { ret_code_t err_code; switch (p_evt->evt_id) { case PM_EVT_BONDED_PEER_CONNECTED: { NRF_LOG_INFO("Connected to a previously bonded device.\r\n"); } break; case PM_EVT_CONN_SEC_SUCCEEDED: { NRF_LOG_INFO("Connection secured. Role: %d. conn_handle: %d, Procedure: %d\r\n", ble_conn_state_role(p_evt->conn_handle), p_evt->conn_handle, p_evt->params.conn_sec_succeeded.procedure); } break; case PM_EVT_CONN_SEC_FAILED: { /* Often, when securing fails, it shouldn't be restarted, for security reasons. * Other times, it can be restarted directly. * Sometimes it can be restarted, but only after changing some Security Parameters. * Sometimes, it cannot be restarted until the link is disconnected and reconnected. * Sometimes it is impossible, to secure the link, or the peer device does not support it. * How to handle this error is highly application dependent. */ } break; case PM_EVT_CONN_SEC_CONFIG_REQ: { // Reject pairing request from an already bonded peer. pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false}; pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config); } break; case PM_EVT_STORAGE_FULL: { // Run garbage collection on the flash. err_code = fds_gc(); if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES) { // Retry. } else { APP_ERROR_CHECK(err_code); } } break; case PM_EVT_PEERS_DELETE_SUCCEEDED: { advertising_start(); } break; case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED: { // The local database has likely changed, send service changed indications. pm_local_database_has_changed(); } break; case PM_EVT_PEER_DATA_UPDATE_FAILED: { // Assert. APP_ERROR_CHECK(p_evt->params.peer_data_update_failed.error); } break; case PM_EVT_PEER_DELETE_FAILED: { // Assert. APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error); } break; case PM_EVT_PEERS_DELETE_FAILED: { // Assert. APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error); } break; case PM_EVT_ERROR_UNEXPECTED: { // Assert. APP_ERROR_CHECK(p_evt->params.error_unexpected.error); } break; case PM_EVT_CONN_SEC_START: case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: case PM_EVT_PEER_DELETE_SUCCEEDED: case PM_EVT_LOCAL_DB_CACHE_APPLIED: case PM_EVT_SERVICE_CHANGED_IND_SENT: case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED: default: break; } } /**@brief Function for performing battery measurement and updating the Battery Level characteristic * in Battery Service. */ static void battery_level_update(void) { uint32_t err_code; uint8_t battery_level; battery_level = (uint8_t)sensorsim_measure(&m_battery_sim_state, &m_battery_sim_cfg); err_code = ble_bas_battery_level_update(&m_bas, battery_level); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_PACKETS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } }