初始版本
This commit is contained in:
143
components/ble/ble_services/experimental_nrf_ble_cgms/cgms_db.c
Normal file
143
components/ble/ble_services/experimental_nrf_ble_cgms/cgms_db.c
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "cgms_db.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool in_use_flag;
|
||||
ble_cgms_rec_t record;
|
||||
} database_entry_t;
|
||||
|
||||
static database_entry_t m_database[CGMS_DB_MAX_RECORDS];
|
||||
static uint8_t m_database_crossref[CGMS_DB_MAX_RECORDS];
|
||||
static uint16_t m_num_records;
|
||||
|
||||
|
||||
ret_code_t cgms_db_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CGMS_DB_MAX_RECORDS; i++)
|
||||
{
|
||||
m_database[i].in_use_flag = false;
|
||||
m_database_crossref[i] = 0xFF;
|
||||
}
|
||||
|
||||
m_num_records = 0;
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint16_t cgms_db_num_records_get(void)
|
||||
{
|
||||
return m_num_records;
|
||||
}
|
||||
|
||||
|
||||
ret_code_t cgms_db_record_get(uint8_t record_num, ble_cgms_rec_t * p_rec)
|
||||
{
|
||||
if ((record_num >= m_num_records) || (m_num_records == 0))
|
||||
{
|
||||
return NRF_ERROR_NOT_FOUND;
|
||||
}
|
||||
// copy record to the specified memory
|
||||
*p_rec = m_database[m_database_crossref[record_num]].record;
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
ret_code_t cgms_db_record_add(ble_cgms_rec_t * p_rec)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (m_num_records == CGMS_DB_MAX_RECORDS)
|
||||
{
|
||||
return NRF_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
// find next available database entry
|
||||
for (i = 0; i < CGMS_DB_MAX_RECORDS; i++)
|
||||
{
|
||||
if (!m_database[i].in_use_flag)
|
||||
{
|
||||
m_database[i].in_use_flag = true;
|
||||
m_database[i].record = *p_rec;
|
||||
|
||||
m_database_crossref[m_num_records] = i;
|
||||
m_num_records++;
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return NRF_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
|
||||
ret_code_t cgms_db_record_delete(uint8_t record_num)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (record_num >= m_num_records)
|
||||
{
|
||||
// Deleting a non-existent record is not an error
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
// free entry
|
||||
m_database[m_database_crossref[record_num]].in_use_flag = false;
|
||||
|
||||
// decrease number of records
|
||||
m_num_records--;
|
||||
|
||||
// remove cross reference index
|
||||
for (i = record_num; i < m_num_records; i++)
|
||||
{
|
||||
m_database_crossref[i] = m_database_crossref[i + 1];
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
118
components/ble/ble_services/experimental_nrf_ble_cgms/cgms_db.h
Normal file
118
components/ble/ble_services/experimental_nrf_ble_cgms/cgms_db.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/** @file
|
||||
*
|
||||
* @defgroup ble_sdk_srv_cgms_db Continuous Glucose Monitoring Service database
|
||||
* @{
|
||||
* @ingroup ble_cgms
|
||||
*
|
||||
* @brief Continuous Glucose Monitoring Service database module.
|
||||
*
|
||||
* @details This module implements a database of stored glucose measurement values.
|
||||
* This database is meant as an example of a database that the @ref ble_cgms can use.
|
||||
* Replace this module if this implementation does not suit
|
||||
* your application. Any replacement implementation should follow the API below to ensure
|
||||
* that the qualification of the @ref ble_cgms is not compromised.
|
||||
*/
|
||||
|
||||
#ifndef BLE_CGMS_DB_H__
|
||||
#define BLE_CGMS_DB_H__
|
||||
|
||||
#include "sdk_errors.h"
|
||||
#include "nrf_ble_cgms.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CGMS_DB_MAX_RECORDS 100 // !< Number of records that can be stored in the database.
|
||||
|
||||
|
||||
/**@brief Function for initializing the glucose record database.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the database was successfully initialized.
|
||||
*/
|
||||
ret_code_t cgms_db_init(void);
|
||||
|
||||
|
||||
/**@brief Function for getting the number of records in the database.
|
||||
*
|
||||
* @return The number of records in the database.
|
||||
*/
|
||||
uint16_t cgms_db_num_records_get(void);
|
||||
|
||||
|
||||
/**@brief Function for getting a specific record from the database.
|
||||
*
|
||||
* @param[in] record_num Index of the record to retrieve.
|
||||
* @param[out] p_rec Pointer to the record structure to which the retrieved record is copied.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the record was successfully retrieved.
|
||||
*/
|
||||
ret_code_t cgms_db_record_get(uint8_t record_num, ble_cgms_rec_t * p_rec);
|
||||
|
||||
|
||||
/**@brief Function for adding a record at the end of the database.
|
||||
*
|
||||
* @param[in] p_rec Pointer to the record to add to the database.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the record was successfully added to the database.
|
||||
*/
|
||||
ret_code_t cgms_db_record_add(ble_cgms_rec_t * p_rec);
|
||||
|
||||
|
||||
/**@brief Function for deleting a database entry.
|
||||
*
|
||||
* @details This call deletes an record from the database.
|
||||
*
|
||||
* @param[in] record_num Index of the record to delete.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the record was successfully deleted from the database.
|
||||
*/
|
||||
ret_code_t cgms_db_record_delete(uint8_t record_num);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // BLE_CGMS_DB_H__
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "ble.h"
|
||||
#include "sdk_macros.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "nrf_ble_cgms.h"
|
||||
#include "cgms_meas.h"
|
||||
#include "cgms_db.h"
|
||||
|
||||
/**@brief Function for encoding a Glucose measurement.
|
||||
*
|
||||
* @param[in] p_meas Measurement to be encoded.
|
||||
* @param[out] p_encoded_buffer Pointer to buffer where the encoded measurement is to be stored.
|
||||
*
|
||||
* @return Size of encoded measurement.
|
||||
*/
|
||||
static uint8_t cgms_meas_encode(nrf_ble_cgms_t * p_cgms,
|
||||
const nrf_ble_cgms_meas_t * p_meas,
|
||||
uint8_t * p_encoded_buffer)
|
||||
{
|
||||
uint8_t len = 2;
|
||||
|
||||
uint8_t flags = p_meas->flags;
|
||||
|
||||
len += uint16_encode(p_meas->glucose_concentration,
|
||||
&p_encoded_buffer[len]);
|
||||
len += uint16_encode(p_meas->time_offset,
|
||||
&p_encoded_buffer[len]);
|
||||
|
||||
if (p_meas->sensor_status_annunciation.warning != 0)
|
||||
{
|
||||
p_encoded_buffer[len++] = p_meas->sensor_status_annunciation.warning;
|
||||
flags |= NRF_BLE_CGMS_STATUS_FLAGS_WARNING_OCT_PRESENT;
|
||||
}
|
||||
|
||||
if (p_meas->sensor_status_annunciation.calib_temp != 0)
|
||||
{
|
||||
p_encoded_buffer[len++] = p_meas->sensor_status_annunciation.calib_temp;
|
||||
flags |= NRF_BLE_CGMS_STATUS_FLAGS_CALTEMP_OCT_PRESENT;
|
||||
}
|
||||
|
||||
if (p_meas->sensor_status_annunciation.status != 0)
|
||||
{
|
||||
p_encoded_buffer[len++] = p_meas->sensor_status_annunciation.status;
|
||||
flags |= NRF_BLE_CGMS_STATUS_FLAGS_STATUS_OCT_PRESENT;
|
||||
}
|
||||
|
||||
// Trend field
|
||||
if (p_cgms->feature.feature & NRF_BLE_CGMS_FEAT_CGM_TREND_INFORMATION_SUPPORTED)
|
||||
{
|
||||
if (flags & NRF_BLE_CGMS_FLAG_TREND_INFO_PRESENT)
|
||||
{
|
||||
len += uint16_encode(p_meas->trend, &p_encoded_buffer[len]);
|
||||
}
|
||||
}
|
||||
|
||||
// Quality field
|
||||
if (p_cgms->feature.feature & NRF_BLE_CGMS_FEAT_CGM_QUALITY_SUPPORTED)
|
||||
{
|
||||
if (flags & NRF_BLE_CGMS_FLAGS_QUALITY_PRESENT)
|
||||
{
|
||||
len += uint16_encode(p_meas->quality, &p_encoded_buffer[len]);
|
||||
}
|
||||
}
|
||||
|
||||
p_encoded_buffer[1] = flags;
|
||||
p_encoded_buffer[0] = len;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the Continuous Glucose Meter Measurement.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS if characteristic was successfully added, otherwise an error code.
|
||||
*/
|
||||
ret_code_t cgms_meas_char_add(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint8_t num_recs;
|
||||
uint8_t encoded_cgms_meas[NRF_BLE_CGMS_MEAS_LEN_MAX];
|
||||
ble_add_char_params_t add_char_params;
|
||||
ble_cgms_rec_t initial_cgms_rec_value;
|
||||
|
||||
memset(&add_char_params, 0, sizeof(add_char_params));
|
||||
memset(&initial_cgms_rec_value, 0, sizeof(ble_cgms_rec_t));
|
||||
|
||||
num_recs = cgms_db_num_records_get();
|
||||
if (num_recs > 0)
|
||||
{
|
||||
uint32_t err_code = cgms_db_record_get(num_recs - 1, &initial_cgms_rec_value);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
|
||||
add_char_params.uuid = BLE_UUID_CGM_MEASUREMENT;
|
||||
add_char_params.max_len = NRF_BLE_CGMS_MEAS_LEN_MAX;
|
||||
add_char_params.init_len = cgms_meas_encode(p_cgms,
|
||||
&initial_cgms_rec_value.meas,
|
||||
encoded_cgms_meas);
|
||||
add_char_params.p_init_value = encoded_cgms_meas;
|
||||
add_char_params.is_var_len = true;
|
||||
add_char_params.char_props.notify = true;
|
||||
add_char_params.cccd_write_access = SEC_JUST_WORKS;
|
||||
|
||||
|
||||
return characteristic_add(p_cgms->service_handle,
|
||||
&add_char_params,
|
||||
&p_cgms->char_handles.measurment);
|
||||
}
|
||||
|
||||
|
||||
ret_code_t cgms_meas_send(nrf_ble_cgms_t * p_cgms, ble_cgms_rec_t * p_rec, uint8_t * p_count)
|
||||
{
|
||||
|
||||
uint32_t err_code;
|
||||
uint8_t encoded_meas[NRF_BLE_CGMS_MEAS_LEN_MAX + NRF_BLE_CGMS_MEAS_REC_LEN_MAX];
|
||||
uint16_t len = 0;
|
||||
uint16_t hvx_len = NRF_BLE_CGMS_MEAS_LEN_MAX;
|
||||
int i;
|
||||
ble_gatts_hvx_params_t hvx_params;
|
||||
|
||||
for (i = 0; i < *p_count; i++)
|
||||
{
|
||||
uint8_t meas_len = cgms_meas_encode(p_cgms, &(p_rec[i].meas), (encoded_meas + len));
|
||||
if (len + meas_len >= NRF_BLE_CGMS_MEAS_LEN_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
len += meas_len;
|
||||
}
|
||||
*p_count = i;
|
||||
hvx_len = len;
|
||||
|
||||
memset(&hvx_params, 0, sizeof(hvx_params));
|
||||
|
||||
hvx_params.handle = p_cgms->char_handles.measurment.value_handle;
|
||||
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
|
||||
hvx_params.offset = 0;
|
||||
hvx_params.p_len = &hvx_len;
|
||||
hvx_params.p_data = encoded_meas;
|
||||
|
||||
err_code = sd_ble_gatts_hvx(p_cgms->conn_handle, &hvx_params);
|
||||
if (err_code == NRF_SUCCESS)
|
||||
{
|
||||
if (hvx_len != len)
|
||||
{
|
||||
err_code = NRF_ERROR_DATA_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Measurement successfully sent
|
||||
p_cgms->racp_data.racp_proc_records_reported += *p_count;
|
||||
}
|
||||
}
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the Glucose measurement CCCD write event.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
* @param[in] p_evt_write WRITE event to be handled.
|
||||
*/
|
||||
static void on_meas_cccd_write(nrf_ble_cgms_t * p_cgms, ble_gatts_evt_write_t const * p_evt_write)
|
||||
{
|
||||
if (p_evt_write->len == 2)
|
||||
{
|
||||
// CCCD written, update notification state
|
||||
if (p_cgms->evt_handler != NULL)
|
||||
{
|
||||
nrf_ble_cgms_evt_t evt;
|
||||
|
||||
if (ble_srv_is_notification_enabled(p_evt_write->data))
|
||||
{
|
||||
evt.evt_type = NRF_BLE_CGMS_EVT_NOTIFICATION_ENABLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
evt.evt_type = NRF_BLE_CGMS_EVT_NOTIFICATION_DISABLED;
|
||||
}
|
||||
|
||||
p_cgms->evt_handler(p_cgms, &evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the WRITE event.
|
||||
*
|
||||
* @details Handles WRITE events from the BLE stack.
|
||||
*
|
||||
* @param[in] p_cgms Glucose Service structure.
|
||||
* @param[in] p_ble_evt Event received from the BLE stack.
|
||||
*/
|
||||
void cgms_meas_on_write(nrf_ble_cgms_t * p_cgms, ble_gatts_evt_write_t const * p_evt_write)
|
||||
{
|
||||
|
||||
if (p_evt_write->handle == p_cgms->char_handles.measurment.cccd_handle)
|
||||
{
|
||||
on_meas_cccd_write(p_cgms, p_evt_write);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/** @file
|
||||
*
|
||||
* @defgroup ble_sdk_srv_cgms_meas Continuous Glucose Monitoring Service Measurement
|
||||
* @{
|
||||
* @ingroup ble_cgms
|
||||
* @brief Continuous Glucose Monitoring Service Measurement module.
|
||||
*
|
||||
* @details This module implements parts of the Continuous Glucose Monitoring that relate to the
|
||||
* Measurement characteristic. Events are propagated to this module from @ref ble_cgms
|
||||
* using @ref cgms_meas_on_write.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NRF_BLE_CGMS_MEAS_H__
|
||||
#define NRF_BLE_CGMS_MEAS_H__
|
||||
|
||||
#include "ble.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "sdk_errors.h"
|
||||
#include "nrf_ble_cgms.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the Continuous Glucose Monitoring Measurement.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the characteristic was successfully added.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t cgms_meas_char_add(nrf_ble_cgms_t * p_cgms);
|
||||
|
||||
/**@brief Function for sending a CGM Measurement.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] p_rec Measurement to be sent.
|
||||
* @param[in] count Number of measurements to encode.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the measurement was successfully sent.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t cgms_meas_send(nrf_ble_cgms_t * p_cgms, ble_cgms_rec_t * p_rec, uint8_t * count);
|
||||
|
||||
|
||||
/**@brief Function for handling the @ref BLE_GATTS_EVT_WRITE event from the BLE stack.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] p_evt_write Event received from the BLE stack.
|
||||
*/
|
||||
void cgms_meas_on_write(nrf_ble_cgms_t * p_cgms, ble_gatts_evt_write_t const * p_evt_write);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // NRF_BLE_CGMS_MEAS_H__
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,826 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "ble.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "cgms_racp.h"
|
||||
#include "cgms_db.h"
|
||||
#include "nrf_ble_gq.h"
|
||||
#include "cgms_meas.h"
|
||||
|
||||
#define OPERAND_LESS_GREATER_FILTER_TYPE_SIZE 1 // !< 1 byte.
|
||||
#define OPERAND_LESS_GREATER_FILTER_PARAM_SIZE 2 // !< 2 bytes.
|
||||
#define OPERAND_LESS_GREATER_SIZE OPERAND_LESS_GREATER_FILTER_TYPE_SIZE \
|
||||
+ OPERAND_LESS_GREATER_FILTER_PARAM_SIZE // !< Total size of the operand.
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the Record Access Control Point.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS if characteristic was successfully added, otherwise an error code.
|
||||
*/
|
||||
ret_code_t cgms_racp_char_add(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
ble_add_char_params_t add_char_params;
|
||||
|
||||
memset(&add_char_params, 0, sizeof(add_char_params));
|
||||
|
||||
add_char_params.uuid = BLE_UUID_RECORD_ACCESS_CONTROL_POINT_CHAR;
|
||||
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
|
||||
add_char_params.init_len = 0;
|
||||
add_char_params.p_init_value = 0;
|
||||
add_char_params.is_var_len = true;
|
||||
add_char_params.write_access = SEC_JUST_WORKS;
|
||||
add_char_params.char_props.write = true;
|
||||
add_char_params.char_props.indicate = true;
|
||||
add_char_params.cccd_write_access = SEC_JUST_WORKS;
|
||||
add_char_params.is_defered_write = 1;
|
||||
|
||||
return characteristic_add(p_cgms->service_handle,
|
||||
&add_char_params,
|
||||
&p_cgms->char_handles.racp);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for sending response from Specific Operation Control Point.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
* @param[in] p_racp_val RACP value to be sent.
|
||||
*/
|
||||
static void racp_send(nrf_ble_cgms_t * p_cgms, ble_racp_value_t * p_racp_val)
|
||||
{
|
||||
uint32_t err_code;
|
||||
uint8_t encoded_resp[25];
|
||||
uint16_t len;
|
||||
nrf_ble_gq_req_t cgms_req;
|
||||
|
||||
memset(&cgms_req, 0, sizeof(nrf_ble_gq_req_t));
|
||||
|
||||
// Send indication
|
||||
len = ble_racp_encode(p_racp_val, encoded_resp);
|
||||
|
||||
cgms_req.type = NRF_BLE_GQ_REQ_GATTS_HVX;
|
||||
cgms_req.error_handler.cb = p_cgms->gatt_err_handler;
|
||||
cgms_req.error_handler.p_ctx = p_cgms;
|
||||
cgms_req.params.gatts_hvx.type = BLE_GATT_HVX_INDICATION;
|
||||
cgms_req.params.gatts_hvx.handle = p_cgms->char_handles.racp.value_handle;
|
||||
cgms_req.params.gatts_hvx.offset = 0;
|
||||
cgms_req.params.gatts_hvx.p_data = encoded_resp;
|
||||
cgms_req.params.gatts_hvx.p_len = &len;
|
||||
|
||||
err_code = nrf_ble_gq_item_add(p_cgms->p_gatt_queue, &cgms_req, p_cgms->conn_handle);
|
||||
|
||||
// Report error to application
|
||||
if ((p_cgms->error_handler != NULL) &&
|
||||
(err_code != NRF_SUCCESS) &&
|
||||
(err_code != NRF_ERROR_INVALID_STATE))
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for sending a RACP response containing a Response Code Op Code and Response Code Value.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
* @param[in] opcode RACP Op Code.
|
||||
* @param[in] value RACP Response Code Value.
|
||||
*/
|
||||
static void racp_response_code_send(nrf_ble_cgms_t * p_cgms, uint8_t opcode, uint8_t value)
|
||||
{
|
||||
p_cgms->racp_data.pending_racp_response.opcode = RACP_OPCODE_RESPONSE_CODE;
|
||||
p_cgms->racp_data.pending_racp_response.operator = RACP_OPERATOR_NULL;
|
||||
p_cgms->racp_data.pending_racp_response.operand_len = 2;
|
||||
p_cgms->racp_data.pending_racp_response.p_operand =
|
||||
p_cgms->racp_data.pending_racp_response_operand;
|
||||
|
||||
p_cgms->racp_data.pending_racp_response_operand[0] = opcode;
|
||||
p_cgms->racp_data.pending_racp_response_operand[1] = value;
|
||||
|
||||
racp_send(p_cgms, &p_cgms->racp_data.pending_racp_response);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for responding to the ALL operation.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, otherwise an error code.
|
||||
*/
|
||||
static uint32_t racp_report_records_all(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint16_t total_records = cgms_db_num_records_get();
|
||||
uint16_t cur_nb_rec;
|
||||
uint8_t i;
|
||||
uint8_t nb_rec_to_send;
|
||||
|
||||
if (p_cgms->racp_data.racp_proc_record_ndx >= total_records)
|
||||
{
|
||||
p_cgms->racp_data.racp_procesing_active = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t err_code;
|
||||
ble_cgms_rec_t rec[NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX];
|
||||
|
||||
cur_nb_rec = total_records - p_cgms->racp_data.racp_proc_record_ndx;
|
||||
if (cur_nb_rec > NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX)
|
||||
{
|
||||
cur_nb_rec = NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX;
|
||||
}
|
||||
nb_rec_to_send = (uint8_t)cur_nb_rec;
|
||||
|
||||
for (i = 0; i < cur_nb_rec; i++)
|
||||
{
|
||||
err_code = cgms_db_record_get(p_cgms->racp_data.racp_proc_record_ndx + i, &(rec[i]));
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
err_code = cgms_meas_send(p_cgms, rec, &nb_rec_to_send);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
p_cgms->racp_data.racp_proc_record_ndx += nb_rec_to_send;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for responding to the FIRST or the LAST operation.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, otherwise an error code.
|
||||
*/
|
||||
static uint32_t racp_report_records_first_last(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint32_t err_code;
|
||||
ble_cgms_rec_t rec;
|
||||
uint16_t total_records;
|
||||
uint8_t nb_rec_to_send = 1;
|
||||
|
||||
total_records = cgms_db_num_records_get();
|
||||
|
||||
if ((p_cgms->racp_data.racp_proc_records_reported != 0) || (total_records == 0))
|
||||
{
|
||||
p_cgms->racp_data.racp_procesing_active = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p_cgms->racp_data.racp_proc_operator == RACP_OPERATOR_FIRST)
|
||||
{
|
||||
err_code = cgms_db_record_get(0, &rec);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
else if (p_cgms->racp_data.racp_proc_operator == RACP_OPERATOR_LAST)
|
||||
{
|
||||
err_code = cgms_db_record_get(total_records - 1, &rec);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
|
||||
err_code = cgms_meas_send(p_cgms, &rec, &nb_rec_to_send);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
p_cgms->racp_data.racp_proc_record_ndx++;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for responding to the LESS OR EQUAL operation.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, otherwise an error code.
|
||||
*/
|
||||
static ret_code_t racp_report_records_less_equal(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint16_t total_rec_nb_to_send;
|
||||
uint16_t rec_nb_left_to_send;
|
||||
uint8_t nb_rec_to_send;
|
||||
uint16_t i;
|
||||
|
||||
total_rec_nb_to_send = p_cgms->racp_data.racp_proc_records_ndx_last_to_send +1;
|
||||
|
||||
if (p_cgms->racp_data.racp_proc_record_ndx >= total_rec_nb_to_send)
|
||||
{
|
||||
p_cgms->racp_data.racp_procesing_active = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_code_t err_code;
|
||||
ble_cgms_rec_t rec[NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX];
|
||||
|
||||
rec_nb_left_to_send = total_rec_nb_to_send - p_cgms->racp_data.racp_proc_records_reported;
|
||||
|
||||
if (rec_nb_left_to_send > NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX)
|
||||
{
|
||||
nb_rec_to_send = NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
nb_rec_to_send = (uint8_t)rec_nb_left_to_send;
|
||||
}
|
||||
|
||||
for (i = 0; i < nb_rec_to_send; i++)
|
||||
{
|
||||
err_code = cgms_db_record_get(p_cgms->racp_data.racp_proc_record_ndx + i, &(rec[i]));
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
err_code = cgms_meas_send(p_cgms, rec, &nb_rec_to_send);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
p_cgms->racp_data.racp_proc_record_ndx += nb_rec_to_send;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for responding to the GREATER OR EQUAL operation.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS on success, otherwise an error code.
|
||||
*/
|
||||
static ret_code_t racp_report_records_greater_equal(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
uint16_t total_rec_nb = cgms_db_num_records_get();
|
||||
uint16_t rec_nb_left_to_send;
|
||||
uint8_t nb_rec_to_send;
|
||||
uint16_t i;
|
||||
|
||||
|
||||
total_rec_nb = cgms_db_num_records_get();
|
||||
if (p_cgms->racp_data.racp_proc_record_ndx >= total_rec_nb)
|
||||
{
|
||||
p_cgms->racp_data.racp_procesing_active = false;
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
ble_cgms_rec_t rec[NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX];
|
||||
|
||||
rec_nb_left_to_send = total_rec_nb - p_cgms->racp_data.racp_proc_record_ndx;
|
||||
if (rec_nb_left_to_send > NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX)
|
||||
{
|
||||
nb_rec_to_send = NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
nb_rec_to_send = (uint8_t)rec_nb_left_to_send;
|
||||
}
|
||||
|
||||
for (i = 0; i < nb_rec_to_send; i++)
|
||||
{
|
||||
err_code = cgms_db_record_get(p_cgms->racp_data.racp_proc_record_ndx + i, &(rec[i]));
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
err_code = cgms_meas_send(p_cgms, rec, &nb_rec_to_send);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
p_cgms->racp_data.racp_proc_record_ndx += nb_rec_to_send;
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for informing that the REPORT RECORDS procedure is completed.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*/
|
||||
static void racp_report_records_completed(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint8_t resp_code_value;
|
||||
|
||||
if (p_cgms->racp_data.racp_proc_records_reported > 0)
|
||||
{
|
||||
resp_code_value = RACP_RESPONSE_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
resp_code_value = RACP_RESPONSE_NO_RECORDS_FOUND;
|
||||
}
|
||||
|
||||
racp_response_code_send(p_cgms, RACP_OPCODE_REPORT_RECS, resp_code_value);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for the RACP report records procedure.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*/
|
||||
static void racp_report_records_procedure(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
ret_code_t err_code = NRF_SUCCESS;
|
||||
|
||||
while (p_cgms->racp_data.racp_procesing_active)
|
||||
{
|
||||
// Execute requested procedure
|
||||
switch (p_cgms->racp_data.racp_proc_operator)
|
||||
{
|
||||
case RACP_OPERATOR_ALL:
|
||||
err_code = racp_report_records_all(p_cgms);
|
||||
break;
|
||||
|
||||
case RACP_OPERATOR_FIRST:
|
||||
// Fall through.
|
||||
case RACP_OPERATOR_LAST:
|
||||
err_code = racp_report_records_first_last(p_cgms);
|
||||
break;
|
||||
case RACP_OPERATOR_GREATER_OR_EQUAL:
|
||||
err_code = racp_report_records_greater_equal(p_cgms);
|
||||
break;
|
||||
case RACP_OPERATOR_LESS_OR_EQUAL:
|
||||
err_code = racp_report_records_less_equal(p_cgms);
|
||||
break;
|
||||
default:
|
||||
// Report error to application
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(NRF_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
p_cgms->racp_data.racp_procesing_active = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Error handling
|
||||
switch (err_code)
|
||||
{
|
||||
case NRF_SUCCESS:
|
||||
if (!p_cgms->racp_data.racp_procesing_active)
|
||||
{
|
||||
racp_report_records_completed(p_cgms);
|
||||
}
|
||||
break;
|
||||
|
||||
case NRF_ERROR_RESOURCES:
|
||||
// Wait for TX_COMPLETE event to resume transmission.
|
||||
return;
|
||||
|
||||
case NRF_ERROR_INVALID_STATE:
|
||||
// Notification is probably not enabled. Ignore request.
|
||||
p_cgms->racp_data.racp_procesing_active = false;;
|
||||
return;
|
||||
|
||||
default:
|
||||
// Report error to application.
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
|
||||
// Make sure state machine returns to the default state.
|
||||
p_cgms->racp_data.racp_procesing_active = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for testing if the received request is to be executed.
|
||||
*
|
||||
* @param[in] p_racp_request Request to be checked.
|
||||
* @param[out] p_response_code Response code to be sent in case the request is rejected.
|
||||
* RACP_RESPONSE_RESERVED is returned if the received message is
|
||||
* to be rejected without sending a respone.
|
||||
*
|
||||
* @return TRUE if the request is to be executed, FALSE if it is to be rejected.
|
||||
* If it is to be rejected, p_response_code will contain the response code to be
|
||||
* returned to the central.
|
||||
*/
|
||||
static bool is_request_to_be_executed(nrf_ble_cgms_t * p_cgms,
|
||||
const ble_racp_value_t * p_racp_request,
|
||||
uint8_t * p_response_code)
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_RESERVED;
|
||||
|
||||
if (p_racp_request->opcode == RACP_OPCODE_ABORT_OPERATION)
|
||||
{
|
||||
if (p_cgms->racp_data.racp_procesing_active)
|
||||
{
|
||||
if (p_racp_request->operator != RACP_OPERATOR_NULL)
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_INVALID_OPERATOR;
|
||||
}
|
||||
else if (p_racp_request->operand_len != 0)
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_INVALID_OPERAND;
|
||||
}
|
||||
else
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_ABORT_FAILED;
|
||||
}
|
||||
}
|
||||
else if (p_cgms->racp_data.racp_procesing_active)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Supported opcodes
|
||||
else if ((p_racp_request->opcode == RACP_OPCODE_REPORT_RECS) ||
|
||||
(p_racp_request->opcode == RACP_OPCODE_REPORT_NUM_RECS))
|
||||
{
|
||||
switch (p_racp_request->operator)
|
||||
{
|
||||
// Operators without a filter.
|
||||
case RACP_OPERATOR_ALL:
|
||||
// Fall through.
|
||||
case RACP_OPERATOR_FIRST:
|
||||
// Fall through.
|
||||
case RACP_OPERATOR_LAST:
|
||||
if (p_racp_request->operand_len != 0)
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_INVALID_OPERAND;
|
||||
}
|
||||
break;
|
||||
|
||||
// Operators with a filter as part of the operand.
|
||||
case RACP_OPERATOR_LESS_OR_EQUAL:
|
||||
// Fall Through.
|
||||
case RACP_OPERATOR_GREATER_OR_EQUAL:
|
||||
if (*(p_racp_request->p_operand) == RACP_OPERAND_FILTER_TYPE_FACING_TIME)
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_PROCEDURE_NOT_DONE;
|
||||
}
|
||||
if (p_racp_request->operand_len != OPERAND_LESS_GREATER_SIZE)
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_INVALID_OPERAND;
|
||||
}
|
||||
break;
|
||||
|
||||
case RACP_OPERATOR_RANGE:
|
||||
*p_response_code = RACP_RESPONSE_OPERATOR_UNSUPPORTED;
|
||||
break;
|
||||
|
||||
// Invalid operators.
|
||||
case RACP_OPERATOR_NULL:
|
||||
// Fall through.
|
||||
default:
|
||||
*p_response_code = RACP_RESPONSE_INVALID_OPERATOR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Unsupported opcodes.
|
||||
else if (p_racp_request->opcode == RACP_OPCODE_DELETE_RECS)
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_OPCODE_UNSUPPORTED;
|
||||
}
|
||||
// Unknown opcodes.
|
||||
else
|
||||
{
|
||||
*p_response_code = RACP_RESPONSE_OPCODE_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return (*p_response_code == RACP_RESPONSE_RESERVED);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**@brief Function for getting a record with time offset less or equal to the input param.
|
||||
*
|
||||
* @param[in] offset The record that this function returns must have an time offset less or greater to this.
|
||||
* @param[out] record_num Pointer to the record index of the record that has the desired time offset.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the record was successfully retrieved.
|
||||
* @retval NRF_ERROR_NOT_FOUND A record with the desired offset does not exist in the database.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
static ret_code_t record_index_offset_less_or_equal_get(uint16_t offset, uint16_t * record_num)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
ble_cgms_rec_t rec;
|
||||
uint16_t upper_bound = cgms_db_num_records_get();
|
||||
|
||||
for((*record_num) = upper_bound; (*record_num)-- >0;)
|
||||
{
|
||||
err_code = cgms_db_record_get(*record_num, &rec);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
if (rec.meas.time_offset <= offset)
|
||||
{
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
}
|
||||
return NRF_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**@brief Function for getting a record with time offset greater or equal to the input param.
|
||||
*
|
||||
* @param[in] offset The record that this function returns must have an time offset equal or
|
||||
* greater to this.
|
||||
* @param[out] record_num Pointer to the record index of the record that has the desired time offset.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the record was successfully retrieved.
|
||||
* @retval NRF_ERROR_NOT_FOUND A record with the desired offset does not exist in the database.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
static ret_code_t record_index_offset_greater_or_equal_get(uint16_t offset, uint16_t * record_num)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
ble_cgms_rec_t rec;
|
||||
uint16_t upper_bound = cgms_db_num_records_get();
|
||||
|
||||
for(*record_num = 0; *record_num < upper_bound; (*record_num)++)
|
||||
{
|
||||
err_code = cgms_db_record_get(*record_num, &rec);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
if (rec.meas.time_offset >= offset)
|
||||
{
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
}
|
||||
return NRF_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for processing a REPORT RECORDS request.
|
||||
*
|
||||
* @details Set initial values before entering the state machine of racp_report_records_procedure().
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
* @param[in] p_racp_request Request to be executed.
|
||||
*/
|
||||
static void report_records_request_execute(nrf_ble_cgms_t * p_cgms,
|
||||
ble_racp_value_t * p_racp_request)
|
||||
{
|
||||
p_cgms->racp_data.racp_procesing_active = true;
|
||||
|
||||
p_cgms->racp_data.racp_proc_record_ndx = 0;
|
||||
p_cgms->racp_data.racp_proc_operator = p_racp_request->operator;
|
||||
p_cgms->racp_data.racp_proc_records_reported = 0;
|
||||
p_cgms->racp_data.racp_proc_records_ndx_last_to_send = 0;
|
||||
|
||||
if (p_cgms->racp_data.racp_proc_operator == RACP_OPERATOR_GREATER_OR_EQUAL)
|
||||
{
|
||||
uint16_t offset_requested = uint16_decode(&p_cgms->racp_data.racp_request.p_operand[OPERAND_LESS_GREATER_FILTER_TYPE_SIZE]);
|
||||
ret_code_t err_code = record_index_offset_greater_or_equal_get(offset_requested, &p_cgms->racp_data.racp_proc_record_ndx);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
racp_report_records_completed(p_cgms);
|
||||
}
|
||||
|
||||
}
|
||||
if (p_cgms->racp_data.racp_proc_operator == RACP_OPERATOR_LESS_OR_EQUAL)
|
||||
{
|
||||
uint16_t offset_requested = uint16_decode(&p_cgms->racp_data.racp_request.p_operand[OPERAND_LESS_GREATER_FILTER_TYPE_SIZE]);
|
||||
ret_code_t err_code = record_index_offset_less_or_equal_get(offset_requested,
|
||||
&p_cgms->racp_data.racp_proc_records_ndx_last_to_send);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
racp_report_records_completed(p_cgms);
|
||||
}
|
||||
}
|
||||
racp_report_records_procedure(p_cgms);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for processing a REPORT NUM RECORDS request.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
* @param[in] p_racp_request Request to be executed.
|
||||
*/
|
||||
static void report_num_records_request_execute(nrf_ble_cgms_t * p_cgms,
|
||||
ble_racp_value_t * p_racp_request)
|
||||
{
|
||||
uint16_t total_records;
|
||||
uint16_t num_records;
|
||||
|
||||
total_records = cgms_db_num_records_get();
|
||||
num_records = 0;
|
||||
|
||||
if (p_racp_request->operator == RACP_OPERATOR_ALL)
|
||||
{
|
||||
num_records = total_records;
|
||||
}
|
||||
else if ((p_racp_request->operator == RACP_OPERATOR_FIRST) ||
|
||||
(p_racp_request->operator == RACP_OPERATOR_LAST))
|
||||
{
|
||||
if (total_records > 0)
|
||||
{
|
||||
num_records = 1;
|
||||
}
|
||||
}
|
||||
else if (p_racp_request->operator == RACP_OPERATOR_GREATER_OR_EQUAL)
|
||||
{
|
||||
uint16_t index_of_offset;
|
||||
uint16_t offset_requested = uint16_decode(&p_cgms->racp_data.racp_request.p_operand[OPERAND_LESS_GREATER_FILTER_TYPE_SIZE]);
|
||||
ret_code_t err_code = record_index_offset_greater_or_equal_get(offset_requested, &index_of_offset);
|
||||
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
num_records = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
num_records = total_records - index_of_offset;
|
||||
}
|
||||
}
|
||||
|
||||
p_cgms->racp_data.pending_racp_response.opcode = RACP_OPCODE_NUM_RECS_RESPONSE;
|
||||
p_cgms->racp_data.pending_racp_response.operator = RACP_OPERATOR_NULL;
|
||||
p_cgms->racp_data.pending_racp_response.operand_len = sizeof(uint16_t);
|
||||
p_cgms->racp_data.pending_racp_response.p_operand =
|
||||
p_cgms->racp_data.pending_racp_response_operand;
|
||||
|
||||
p_cgms->racp_data.pending_racp_response_operand[0] = num_records & 0xFF;
|
||||
p_cgms->racp_data.pending_racp_response_operand[1] = num_records >> 8;
|
||||
|
||||
racp_send(p_cgms, &p_cgms->racp_data.pending_racp_response);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling a write event to the Record Access Control Point.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
* @param[in] p_evt_write WRITE event to be handled.
|
||||
*/
|
||||
static void on_racp_value_write(nrf_ble_cgms_t * p_cgms, ble_gatts_evt_write_t const * p_evt_write)
|
||||
{
|
||||
uint8_t response_code;
|
||||
|
||||
// set up reply to authorized write.
|
||||
ble_gatts_rw_authorize_reply_params_t auth_reply;
|
||||
uint32_t err_code;
|
||||
|
||||
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
|
||||
auth_reply.params.write.offset = 0;
|
||||
auth_reply.params.write.len = 0;
|
||||
auth_reply.params.write.p_data = NULL;
|
||||
|
||||
// Decode request.
|
||||
ble_racp_decode(p_evt_write->len, p_evt_write->data, &p_cgms->racp_data.racp_request);
|
||||
|
||||
// Check if request is to be executed
|
||||
if (is_request_to_be_executed(p_cgms, &p_cgms->racp_data.racp_request, &response_code))
|
||||
{
|
||||
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
auth_reply.params.write.update = 1;
|
||||
|
||||
err_code = sd_ble_gatts_rw_authorize_reply(p_cgms->conn_handle,
|
||||
&auth_reply);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute request
|
||||
if (p_cgms->racp_data.racp_request.opcode == RACP_OPCODE_REPORT_RECS)
|
||||
{
|
||||
report_records_request_execute(p_cgms, &p_cgms->racp_data.racp_request);
|
||||
}
|
||||
else if (p_cgms->racp_data.racp_request.opcode == RACP_OPCODE_REPORT_NUM_RECS)
|
||||
{
|
||||
report_num_records_request_execute(p_cgms, &p_cgms->racp_data.racp_request);
|
||||
}
|
||||
}
|
||||
else if (response_code != RACP_RESPONSE_RESERVED)
|
||||
{
|
||||
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
auth_reply.params.write.update = 1;
|
||||
err_code = sd_ble_gatts_rw_authorize_reply(p_cgms->conn_handle,
|
||||
&auth_reply);
|
||||
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Abort any running procedure
|
||||
p_cgms->racp_data.racp_procesing_active = false;
|
||||
|
||||
// Respond with error code
|
||||
racp_response_code_send(p_cgms, p_cgms->racp_data.racp_request.opcode, response_code);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ignore request
|
||||
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
auth_reply.params.write.update = 1;
|
||||
err_code = sd_ble_gatts_rw_authorize_reply(p_cgms->conn_handle,
|
||||
&auth_reply);
|
||||
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cgms_racp_on_rw_auth_req(nrf_ble_cgms_t * p_cgms,
|
||||
ble_gatts_evt_rw_authorize_request_t const * p_auth_req)
|
||||
{
|
||||
if (p_auth_req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
|
||||
{
|
||||
if (p_auth_req->request.write.handle == p_cgms->char_handles.racp.value_handle)
|
||||
{
|
||||
on_racp_value_write(p_cgms, &p_auth_req->request.write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling BLE_GATTS_EVT_HVN_TX_COMPLETE events.
|
||||
*
|
||||
* @param[in] p_cgms Glucose Service structure.
|
||||
*/
|
||||
void cgms_racp_on_tx_complete(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
if (p_cgms->racp_data.racp_procesing_active)
|
||||
{
|
||||
racp_report_records_procedure(p_cgms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/** @file
|
||||
*
|
||||
* @defgroup ble_sdk_srv_cgms_racp Record Access Control Point
|
||||
* @{
|
||||
* @ingroup ble_cgms
|
||||
* @brief Continuous Glucose Monitoring Service RACP module.
|
||||
*
|
||||
* @details This module implements parts of the Continuous Glucose Monitoring that relate to the
|
||||
* Record Access Control Point. Events are propagated to this module from @ref ble_cgms
|
||||
* using @ref cgms_racp_on_rw_auth_req and @ref cgms_racp_on_tx_complete.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NRF_BLE_CGMS_RACP_H__
|
||||
#define NRF_BLE_CGMS_RACP_H__
|
||||
|
||||
#include "ble.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "sdk_errors.h"
|
||||
#include "nrf_ble_cgms.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the Record Access Control Point.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the characteristic was successfully added.
|
||||
* @retval NRF_ERROR_NULL If any of the input parameters are NULL.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t cgms_racp_char_add(nrf_ble_cgms_t * p_cgms);
|
||||
|
||||
|
||||
/**@brief Function for handling @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST events.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] p_auth_req Authorize request event to be handled.
|
||||
*/
|
||||
void cgms_racp_on_rw_auth_req(nrf_ble_cgms_t * p_cgms,
|
||||
ble_gatts_evt_rw_authorize_request_t const * p_auth_req);
|
||||
|
||||
|
||||
/**@brief Function for handling @ref BLE_GATTS_EVT_HVN_TX_COMPLETE events.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
*/
|
||||
void cgms_racp_on_tx_complete(nrf_ble_cgms_t * p_cgms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // NRF_BLE_CGMS_RACP_H__
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "ble.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "cgms_sst.h"
|
||||
#include "cgms_socp.h"
|
||||
#include "nrf_ble_gq.h"
|
||||
|
||||
|
||||
#define NRF_BLE_CGMS_PLUS_INFINTE 0x07FE
|
||||
#define NRF_BLE_CGMS_MINUS_INFINTE 0x0802
|
||||
|
||||
/**@brief Specific Operation Control Point opcodes. */
|
||||
#define SOCP_OPCODE_RESERVED 0x00 /**< Specific Operation Control Point opcode - Reserved for future use. */
|
||||
#define SOCP_WRITE_CGM_COMMUNICATION_INTERVAL 0x01
|
||||
#define SOCP_READ_CGM_COMMUNICATION_INTERVAL 0x02
|
||||
#define SOCP_READ_CGM_COMMUNICATION_INTERVAL_RESPONSE 0x03
|
||||
#define SOCP_WRITE_GLUCOSE_CALIBRATION_VALUE 0x04
|
||||
#define SOCP_READ_GLUCOSE_CALIBRATION_VALUE 0x05
|
||||
#define SOCP_READ_GLUCOSE_CALIBRATION_VALUE_RESPONSE 0x06
|
||||
#define SOCP_WRITE_PATIENT_HIGH_ALERT_LEVEL 0x07
|
||||
#define SOCP_READ_PATIENT_HIGH_ALERT_LEVEL 0x08
|
||||
#define SOCP_READ_PATIENT_HIGH_ALERT_LEVEL_RESPONSE 0x09
|
||||
#define SOCP_WRITE_PATIENT_LOW_ALERT_LEVEL 0x0A
|
||||
#define SOCP_READ_PATIENT_LOW_ALERT_LEVEL 0x0B
|
||||
#define SOCP_READ_PATIENT_LOW_ALERT_LEVEL_RESPONSE 0x0C
|
||||
#define SOCP_SET_HYPO_ALERT_LEVEL 0x0D /**Set Hypo Alert Level Hypo Alert Level value in mg/dL The response to this control point is Response Code. */
|
||||
#define SOCP_GET_HYPO_ALERT_LEVEL 0x0E /**Get Hypo Alert Level N/A The normal response to this control point is Op Code 0x0F. For error conditions, the response is Response Code */
|
||||
#define SOCP_HYPO_ALERT_LEVEL_RESPONSE 0x0F /**Hypo Alert Level Response Hypo Alert Level value in mg/dL This is the normal response to Op Code 0x0E */
|
||||
#define SOCP_SET_HYPER_ALERT_LEVEL 0x10 /**Set Hyper Alert Level Hyper Alert Level value in mg/dL The response to this control point is Response Code. */
|
||||
#define SOCP_GET_HYPER_ALERT_LEVEL 0x11 /**Get Hyper Alert Level N/A The normal response to this control point is Op Code 0x12. For error conditions, the response is Response Code */
|
||||
#define SOCP_HYPER_ALERT_LEVEL_RESPONSE 0x12 /**Hyper Alert Level Response Hyper Alert Level value in mg/dL This is the normal response to Op Code 0x11 */
|
||||
#define SOCP_SET_RATE_OF_DECREASE_ALERT_LEVEL 0x13 /**Set Rate of Decrease Alert Level Rate of Decrease Alert Level value in mg/dL/min The response to this control point is Response Code. */
|
||||
#define SOCP_GET_RATE_OF_DECREASE_ALERT_LEVEL 0x14 /**Get Rate of Decrease Alert Level N/A The normal response to this control point is Op Code 0x15. For error conditions, the response is Response Code */
|
||||
#define SOCP_RATE_OF_DECREASE_ALERT_LEVEL_RESPONSE 0x15 /**Rate of Decrease Alert Level Response Rate of Decrease Alert Level value in mg/dL/min This is the normal response to Op Code 0x14 */
|
||||
#define SOCP_SET_RATE_OF_INCREASE_ALERT_LEVEL 0x16 /**Set Rate of Increase Alert Level Rate of Increase Alert Level value in mg/dL/min The response to this control point is Response Code. */
|
||||
#define SOCP_GET_RATE_OF_INCREASE_ALERT_LEVEL 0x17 /**Get Rate of Increase Alert Level N/A The normal response to this control point is Op Code 0x18. For error conditions, the response is Response Code */
|
||||
#define SOCP_RATE_OF_INCREASE_ALERT_LEVEL_RESPONSE 0x18 /**Rate of Increase Alert Level Response Rate of Increase Alert Level value in mg/dL/min This is the normal response to Op Code 0x17 */
|
||||
#define SOCP_RESET_DEVICE_SPECIFIC_ALERT 0x19 /**Reset Device Specific Alert N/A The response to this control point is Response Code. */
|
||||
|
||||
#define SOCP_START_THE_SESSION 0x1A
|
||||
#define SOCP_STOP_THE_SESSION 0x1B
|
||||
#define SOCP_RESPONSE_CODE 0x1C
|
||||
|
||||
#define SOCP_RSP_RESERVED_FOR_FUTURE_USE 0x00
|
||||
#define SOCP_RSP_SUCCESS 0x01
|
||||
#define SOCP_RSP_OP_CODE_NOT_SUPPORTED 0x02
|
||||
#define SOCP_RSP_INVALID_OPERAND 0x03
|
||||
#define SOCP_RSP_PROCEDURE_NOT_COMPLETED 0x04
|
||||
#define SOCP_RSP_OUT_OF_RANGE 0x05
|
||||
|
||||
static void ble_socp_decode(uint8_t data_len, uint8_t const * p_data, ble_cgms_socp_value_t * p_socp_val)
|
||||
{
|
||||
p_socp_val->opcode = 0xFF;
|
||||
p_socp_val->operand_len = 0;
|
||||
p_socp_val->p_operand = NULL;
|
||||
|
||||
if (data_len > 0)
|
||||
{
|
||||
p_socp_val->opcode = p_data[0];
|
||||
}
|
||||
if (data_len > 1)
|
||||
{
|
||||
p_socp_val->operand_len = data_len - 1;
|
||||
p_socp_val->p_operand = (uint8_t*)&p_data[1]; // lint !e416
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t ble_socp_encode(const ble_socp_rsp_t * p_socp_rsp, uint8_t * p_data)
|
||||
{
|
||||
uint8_t len = 0;
|
||||
int i;
|
||||
|
||||
|
||||
if (p_data != NULL)
|
||||
{
|
||||
p_data[len++] = p_socp_rsp->opcode;
|
||||
|
||||
if (
|
||||
(p_socp_rsp->opcode != SOCP_READ_CGM_COMMUNICATION_INTERVAL_RESPONSE)
|
||||
&& (p_socp_rsp->opcode != SOCP_READ_PATIENT_HIGH_ALERT_LEVEL_RESPONSE)
|
||||
&& (p_socp_rsp->opcode != SOCP_READ_PATIENT_LOW_ALERT_LEVEL_RESPONSE)
|
||||
&& (p_socp_rsp->opcode != SOCP_HYPO_ALERT_LEVEL_RESPONSE)
|
||||
&& (p_socp_rsp->opcode != SOCP_HYPER_ALERT_LEVEL_RESPONSE)
|
||||
&& (p_socp_rsp->opcode != SOCP_RATE_OF_DECREASE_ALERT_LEVEL_RESPONSE)
|
||||
&& (p_socp_rsp->opcode != SOCP_RATE_OF_INCREASE_ALERT_LEVEL_RESPONSE)
|
||||
&& (p_socp_rsp->opcode != SOCP_READ_GLUCOSE_CALIBRATION_VALUE_RESPONSE)
|
||||
)
|
||||
{
|
||||
p_data[len++] = p_socp_rsp->req_opcode;
|
||||
p_data[len++] = p_socp_rsp->rsp_code;
|
||||
}
|
||||
|
||||
for (i = 0; i < p_socp_rsp->size_val; i++)
|
||||
{
|
||||
p_data[len++] = p_socp_rsp->resp_val[i];
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the Specific Operations Control Point.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS if characteristic was successfully added, otherwise an error code.
|
||||
*/
|
||||
ret_code_t cgms_socp_char_add(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
ble_add_char_params_t add_char_params;
|
||||
|
||||
memset(&add_char_params, 0, sizeof(add_char_params));
|
||||
|
||||
add_char_params.uuid = BLE_UUID_CGM_SPECIFIC_OPS_CTRLPT;
|
||||
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
|
||||
add_char_params.init_len = 0;
|
||||
add_char_params.p_init_value = 0;
|
||||
add_char_params.is_var_len = true;
|
||||
add_char_params.char_props.indicate = true;
|
||||
add_char_params.char_props.write = true;
|
||||
add_char_params.write_access = SEC_JUST_WORKS;
|
||||
add_char_params.cccd_write_access = SEC_JUST_WORKS;
|
||||
add_char_params.is_defered_write = 1;
|
||||
|
||||
return characteristic_add(p_cgms->service_handle,
|
||||
&add_char_params,
|
||||
&p_cgms->char_handles.socp);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for sending a response from the Specific Operation Control Point.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*/
|
||||
static void socp_send(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint32_t err_code;
|
||||
uint8_t encoded_resp[25];
|
||||
uint16_t len;
|
||||
nrf_ble_gq_req_t cgms_req;
|
||||
|
||||
memset(&cgms_req, 0, sizeof(nrf_ble_gq_req_t));
|
||||
|
||||
// Send indication
|
||||
len = ble_socp_encode(&(p_cgms->socp_response), encoded_resp);
|
||||
|
||||
cgms_req.type = NRF_BLE_GQ_REQ_GATTS_HVX;
|
||||
cgms_req.error_handler.cb = p_cgms->gatt_err_handler;
|
||||
cgms_req.error_handler.p_ctx = p_cgms;
|
||||
cgms_req.params.gatts_hvx.type = BLE_GATT_HVX_INDICATION;
|
||||
cgms_req.params.gatts_hvx.handle = p_cgms->char_handles.socp.value_handle;
|
||||
cgms_req.params.gatts_hvx.offset = 0;
|
||||
cgms_req.params.gatts_hvx.p_data = encoded_resp;
|
||||
cgms_req.params.gatts_hvx.p_len = &len;
|
||||
|
||||
err_code = nrf_ble_gq_item_add(p_cgms->p_gatt_queue, &cgms_req, p_cgms->conn_handle);
|
||||
|
||||
// Report error to application
|
||||
if ((p_cgms->error_handler != NULL) &&
|
||||
(err_code != NRF_SUCCESS) &&
|
||||
(err_code != NRF_ERROR_INVALID_STATE))
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void encode_get_response(uint8_t rsp_code, ble_socp_rsp_t * p_rsp, uint16_t in_val)
|
||||
{
|
||||
p_rsp->opcode = rsp_code;
|
||||
p_rsp->rsp_code = SOCP_RSP_SUCCESS;
|
||||
p_rsp->size_val += uint16_encode(in_val, &(p_rsp->resp_val[p_rsp->size_val]));
|
||||
}
|
||||
|
||||
|
||||
void decode_set_opcode(nrf_ble_cgms_t * p_cgms,
|
||||
ble_cgms_socp_value_t * rcv_val,
|
||||
uint16_t min,
|
||||
uint16_t max,
|
||||
uint16_t * p_val)
|
||||
{
|
||||
uint16_t rcvd_val = uint16_decode(rcv_val->p_operand);
|
||||
|
||||
if ((rcvd_val == NRF_BLE_CGMS_PLUS_INFINTE)
|
||||
|| (rcvd_val == NRF_BLE_CGMS_MINUS_INFINTE)
|
||||
|| (rcvd_val > max)
|
||||
|| (rcvd_val < min))
|
||||
{
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_OUT_OF_RANGE;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_SUCCESS;
|
||||
*p_val = rcvd_val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool is_feature_present(nrf_ble_cgms_t * p_cgms, uint32_t feature)
|
||||
{
|
||||
return (p_cgms->feature.feature & feature);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling a write event to the Specific Operation Control Point.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
* @param[in] p_evt_write WRITE event to be handled.
|
||||
*/
|
||||
static void on_socp_value_write(nrf_ble_cgms_t * p_cgms, ble_gatts_evt_write_t const * p_evt_write)
|
||||
{
|
||||
ble_cgms_socp_value_t socp_request;
|
||||
nrf_ble_cgms_evt_t evt;
|
||||
ble_gatts_rw_authorize_reply_params_t auth_reply;
|
||||
uint32_t err_code;
|
||||
|
||||
memset(&auth_reply, 0, sizeof(auth_reply));
|
||||
|
||||
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
|
||||
|
||||
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
auth_reply.params.write.update = 1;
|
||||
|
||||
err_code = sd_ble_gatts_rw_authorize_reply(p_cgms->conn_handle, &auth_reply);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode request
|
||||
ble_socp_decode(p_evt_write->len, p_evt_write->data, &socp_request);
|
||||
|
||||
p_cgms->socp_response.opcode = SOCP_RESPONSE_CODE;
|
||||
p_cgms->socp_response.req_opcode = socp_request.opcode;
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_OP_CODE_NOT_SUPPORTED;
|
||||
p_cgms->socp_response.size_val = 0;
|
||||
|
||||
|
||||
switch (socp_request.opcode)
|
||||
{
|
||||
case SOCP_WRITE_CGM_COMMUNICATION_INTERVAL:
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_SUCCESS;
|
||||
p_cgms->comm_interval = socp_request.p_operand[0];
|
||||
evt.evt_type = NRF_BLE_CGMS_EVT_WRITE_COMM_INTERVAL;
|
||||
p_cgms->evt_handler(p_cgms, &evt);
|
||||
break;
|
||||
|
||||
case SOCP_READ_CGM_COMMUNICATION_INTERVAL:
|
||||
p_cgms->socp_response.opcode = SOCP_READ_CGM_COMMUNICATION_INTERVAL_RESPONSE;
|
||||
p_cgms->socp_response.resp_val[0] = p_cgms->comm_interval;
|
||||
p_cgms->socp_response.size_val++;
|
||||
break;
|
||||
|
||||
case SOCP_START_THE_SESSION:
|
||||
if (p_cgms->is_session_started)
|
||||
{
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_PROCEDURE_NOT_COMPLETED;
|
||||
}
|
||||
else if ((p_cgms->nb_run_session != 0) &&
|
||||
!(is_feature_present(p_cgms, NRF_BLE_CGMS_FEAT_MULTIPLE_SESSIONS_SUPPORTED)))
|
||||
{
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_PROCEDURE_NOT_COMPLETED;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_SUCCESS;
|
||||
p_cgms->is_session_started = true;
|
||||
p_cgms->nb_run_session++;
|
||||
|
||||
if (p_cgms->evt_handler != NULL)
|
||||
{
|
||||
evt.evt_type = NRF_BLE_CGMS_EVT_START_SESSION;
|
||||
p_cgms->evt_handler(p_cgms, &evt);
|
||||
}
|
||||
|
||||
ble_cgms_sst_t sst;
|
||||
memset(&sst, 0, sizeof(ble_cgms_sst_t));
|
||||
|
||||
err_code = cgms_sst_set(p_cgms, &sst);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
}
|
||||
p_cgms->sensor_status.time_offset = 0;
|
||||
p_cgms->sensor_status.status.status &= (~NRF_BLE_CGMS_STATUS_SESSION_STOPPED);
|
||||
|
||||
err_code = nrf_ble_cgms_update_status(p_cgms, &p_cgms->sensor_status);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SOCP_STOP_THE_SESSION:
|
||||
{
|
||||
nrf_ble_cgm_status_t status;
|
||||
memset(&status, 0, sizeof(nrf_ble_cgm_status_t));
|
||||
|
||||
p_cgms->evt_handler(p_cgms, &evt);
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_SUCCESS;
|
||||
p_cgms->is_session_started = false;
|
||||
|
||||
status.time_offset = p_cgms->sensor_status.time_offset;
|
||||
status.status.status = p_cgms->sensor_status.status.status |
|
||||
NRF_BLE_CGMS_STATUS_SESSION_STOPPED;
|
||||
|
||||
if (p_cgms->evt_handler != NULL)
|
||||
{
|
||||
evt.evt_type = NRF_BLE_CGMS_EVT_STOP_SESSION;
|
||||
p_cgms->evt_handler(p_cgms, &evt);
|
||||
}
|
||||
err_code = nrf_ble_cgms_update_status(p_cgms, &status);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
p_cgms->socp_response.rsp_code = SOCP_RSP_OP_CODE_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
socp_send(p_cgms);
|
||||
}
|
||||
|
||||
|
||||
void cgms_socp_on_rw_auth_req(nrf_ble_cgms_t * p_cgms,
|
||||
ble_gatts_evt_rw_authorize_request_t const * p_auth_req)
|
||||
{
|
||||
if (p_auth_req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
|
||||
{
|
||||
if (p_auth_req->request.write.handle == p_cgms->char_handles.socp.value_handle)
|
||||
{
|
||||
on_socp_value_write(p_cgms, &p_auth_req->request.write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/** @file
|
||||
*
|
||||
* @defgroup ble_sdk_srv_cgms_socp Specific Operations Control Point
|
||||
* @{
|
||||
* @ingroup ble_cgms
|
||||
* @brief Continuous Glucose Monitoring Service SOCP module.
|
||||
*
|
||||
* @details This module implements parts of the Continuous Glucose Monitoring that relate to the
|
||||
* Specific Operations Control Point. Events are propagated to this module from @ref ble_cgms
|
||||
* using @ref cgms_socp_on_rw_auth_req.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NRF_BLE_CGMS_SOCP_H__
|
||||
#define NRF_BLE_CGMS_SOCP_H__
|
||||
|
||||
#include "ble.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "sdk_errors.h"
|
||||
#include "nrf_ble_cgms.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**@brief Specific Operation Control Point value. */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t opcode; /**< Opcode. */
|
||||
uint8_t operand_len; /**< Length of the operand. */
|
||||
uint8_t * p_operand; /**< Pointer to the operand. */
|
||||
} ble_cgms_socp_value_t;
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the Specific Operations Control Point.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the characteristic was successfully added.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t cgms_socp_char_add(nrf_ble_cgms_t * p_cgms);
|
||||
|
||||
|
||||
/**@brief Function for handling @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST events.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] p_auth_req Authorize request event to be handled.
|
||||
*/
|
||||
void cgms_socp_on_rw_auth_req(nrf_ble_cgms_t * p_cgms,
|
||||
ble_gatts_evt_rw_authorize_request_t const * p_auth_req);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // NRF_BLE_CGMS_SOCP_H__
|
||||
|
||||
/** @} */
|
||||
235
components/ble/ble_services/experimental_nrf_ble_cgms/cgms_sst.c
Normal file
235
components/ble/ble_services/experimental_nrf_ble_cgms/cgms_sst.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "ble.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "nrf_ble_cgms.h"
|
||||
#include "cgms_sst.h"
|
||||
|
||||
|
||||
void sst_decode(ble_cgms_sst_t * p_sst, const uint8_t * p_data, const uint16_t len)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
if (len != NRF_BLE_CGMS_SST_LEN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
index = ble_date_time_decode(&p_sst->date_time, p_data);
|
||||
|
||||
p_sst->time_zone = p_data[index++];
|
||||
p_sst->dst = p_data[index++];
|
||||
}
|
||||
|
||||
|
||||
void convert_ble_time_c_time(ble_cgms_sst_t * p_sst, struct tm * p_c_time_date)
|
||||
{
|
||||
p_c_time_date->tm_sec = p_sst->date_time.seconds;
|
||||
p_c_time_date->tm_min = p_sst->date_time.minutes;
|
||||
p_c_time_date->tm_hour = p_sst->date_time.hours;
|
||||
p_c_time_date->tm_mday = p_sst->date_time.day;
|
||||
p_c_time_date->tm_mon = p_sst->date_time.month;
|
||||
p_c_time_date->tm_year = p_sst->date_time.year - 1900;
|
||||
|
||||
// Ignore daylight saving for this conversion.
|
||||
p_c_time_date->tm_isdst = 0;
|
||||
}
|
||||
|
||||
|
||||
void calc_sst(uint16_t offset, struct tm * p_c_time_date)
|
||||
{
|
||||
time_t c_time_in_sec;
|
||||
|
||||
c_time_in_sec = mktime(p_c_time_date);
|
||||
c_time_in_sec = c_time_in_sec - (offset * 60);
|
||||
*p_c_time_date = *(localtime(&c_time_in_sec));
|
||||
|
||||
if (p_c_time_date->tm_isdst == 1)
|
||||
{
|
||||
// Daylight saving time is not used and must be removed.
|
||||
p_c_time_date->tm_hour = p_c_time_date->tm_hour - 1;
|
||||
p_c_time_date->tm_isdst = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void convert_c_time_ble_time(ble_cgms_sst_t * p_sst, struct tm * p_c_time_date)
|
||||
{
|
||||
p_sst->date_time.seconds = p_c_time_date->tm_sec;
|
||||
p_sst->date_time.minutes = p_c_time_date->tm_min;
|
||||
p_sst->date_time.hours = p_c_time_date->tm_hour;
|
||||
p_sst->date_time.day = p_c_time_date->tm_mday;
|
||||
p_sst->date_time.month = p_c_time_date->tm_mon;
|
||||
p_sst->date_time.year = p_c_time_date->tm_year + 1900;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t sst_encode(ble_cgms_sst_t * p_sst, uint8_t * p_encoded_sst)
|
||||
{
|
||||
uint8_t len;
|
||||
|
||||
len = ble_date_time_encode(&p_sst->date_time, p_encoded_sst);
|
||||
|
||||
p_encoded_sst[len++] = p_sst->time_zone;
|
||||
p_encoded_sst[len++] = p_sst->dst;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static ret_code_t cgm_update_sst(nrf_ble_cgms_t * p_cgms, ble_gatts_evt_write_t const * p_evt_write)
|
||||
{
|
||||
ble_cgms_sst_t sst;
|
||||
struct tm c_time_and_date;
|
||||
|
||||
memset(&sst, 0, sizeof(ble_cgms_sst_t));
|
||||
|
||||
sst_decode(&sst, p_evt_write->data, p_evt_write->len);
|
||||
convert_ble_time_c_time(&sst, &c_time_and_date);
|
||||
calc_sst(p_cgms->sensor_status.time_offset, &c_time_and_date);
|
||||
convert_c_time_ble_time(&sst, &c_time_and_date);
|
||||
|
||||
return cgms_sst_set(p_cgms, &sst);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the Glucose session start time write event.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
* @param[in] p_evt_write WRITE event to be handled.
|
||||
*/
|
||||
static void on_sst_value_write(nrf_ble_cgms_t * p_cgms, ble_gatts_evt_write_t const * p_evt_write)
|
||||
{
|
||||
ble_gatts_rw_authorize_reply_params_t auth_reply;
|
||||
uint32_t err_code;
|
||||
|
||||
memset(&auth_reply, 0, sizeof(auth_reply));
|
||||
|
||||
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
|
||||
auth_reply.params.write.update = 1;
|
||||
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
|
||||
err_code = sd_ble_gatts_rw_authorize_reply(p_cgms->conn_handle, &auth_reply);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
}
|
||||
|
||||
err_code = cgm_update_sst(p_cgms, p_evt_write);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
if (p_cgms->error_handler != NULL)
|
||||
{
|
||||
p_cgms->error_handler(err_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the Session Start Time.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS if characteristic was successfully added, otherwise an error code.
|
||||
*/
|
||||
ret_code_t cgms_sst_char_add(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
ble_add_char_params_t add_char_params;
|
||||
uint8_t init_value[NRF_BLE_CGMS_SST_LEN] = {0};
|
||||
|
||||
memset(&add_char_params, 0, sizeof(add_char_params));
|
||||
|
||||
add_char_params.uuid = BLE_UUID_CGM_SESSION_START_TIME;
|
||||
add_char_params.max_len = NRF_BLE_CGMS_SST_LEN;
|
||||
|
||||
add_char_params.init_len = NRF_BLE_CGMS_SST_LEN;
|
||||
add_char_params.p_init_value = init_value;
|
||||
|
||||
add_char_params.read_access = SEC_JUST_WORKS;
|
||||
add_char_params.write_access = SEC_JUST_WORKS;
|
||||
add_char_params.is_defered_write = 1;
|
||||
add_char_params.char_props.write = true;
|
||||
add_char_params.char_props.read = true;
|
||||
|
||||
return characteristic_add(p_cgms->service_handle,
|
||||
&add_char_params,
|
||||
&p_cgms->char_handles.sst);
|
||||
}
|
||||
|
||||
|
||||
ret_code_t cgms_sst_set(nrf_ble_cgms_t * p_cgms, ble_cgms_sst_t * p_sst)
|
||||
{
|
||||
uint16_t conn_handle;
|
||||
uint16_t value_handle;
|
||||
ble_gatts_value_t sst_val;
|
||||
uint8_t encoded_start_session_time[NRF_BLE_CGMS_SST_LEN];
|
||||
uint8_t gatts_value_set_len = 0;
|
||||
|
||||
gatts_value_set_len = sst_encode(p_sst, encoded_start_session_time);
|
||||
conn_handle = p_cgms->conn_handle;
|
||||
value_handle = p_cgms->char_handles.sst.value_handle;
|
||||
memset(&sst_val, 0, sizeof(ble_gatts_value_t));
|
||||
sst_val.len = gatts_value_set_len;
|
||||
sst_val.p_value = encoded_start_session_time;
|
||||
sst_val.offset = 0;
|
||||
|
||||
return (sd_ble_gatts_value_set(conn_handle, value_handle, &sst_val));
|
||||
}
|
||||
|
||||
|
||||
void cgms_sst_on_rw_auth_req(nrf_ble_cgms_t * p_cgms,
|
||||
ble_gatts_evt_rw_authorize_request_t const * p_auth_req)
|
||||
{
|
||||
if (p_auth_req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
|
||||
{
|
||||
if (p_auth_req->request.write.handle == p_cgms->char_handles.sst.value_handle)
|
||||
{
|
||||
on_sst_value_write(p_cgms, &p_auth_req->request.write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
115
components/ble/ble_services/experimental_nrf_ble_cgms/cgms_sst.h
Normal file
115
components/ble/ble_services/experimental_nrf_ble_cgms/cgms_sst.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/** @file
|
||||
*
|
||||
* @defgroup ble_sdk_srv_cgms_sst Session Start Time
|
||||
* @{
|
||||
* @ingroup ble_cgms
|
||||
*
|
||||
* @brief Continuous Glucose Monitoring Service SST module.
|
||||
*
|
||||
* @details This module implements parts of the Continuous Glucose Monitoring that relate to the
|
||||
* Session Start Time characteristic. Events are propagated to this module from @ref ble_cgms
|
||||
* using @ref cgms_sst_on_rw_auth_req.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NRF_BLE_CGMS_SST_H__
|
||||
#define NRF_BLE_CGMS_SST_H__
|
||||
|
||||
|
||||
#include "ble.h"
|
||||
#include "ble_srv_common.h"
|
||||
#include "ble_date_time.h"
|
||||
#include "sdk_errors.h"
|
||||
#include "nrf_ble_cgms.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**@brief Required data for setting the SST characteristic value. */
|
||||
typedef struct
|
||||
{
|
||||
ble_date_time_t date_time; /**< Date and time. */
|
||||
uint8_t time_zone; /**< Time zone. */
|
||||
uint8_t dst; /**< Daylight saving time. */
|
||||
}ble_cgms_sst_t;
|
||||
|
||||
/**@brief Function for adding a characteristic for the Session Start Time.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the characteristic was successfully added.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t cgms_sst_char_add(nrf_ble_cgms_t * p_cgms);
|
||||
|
||||
|
||||
/**@brief Function for setting the Session Run Time attribute.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] p_sst Time and date that will be displayed in the session start time attribute.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the Session Run Time Attribute was successfully set.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t cgms_sst_set(nrf_ble_cgms_t * p_cgms, ble_cgms_sst_t * p_sst);
|
||||
|
||||
|
||||
/**@brief Function for handling @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST events.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] p_auth_req Authorize request event to be handled.
|
||||
*/
|
||||
void cgms_sst_on_rw_auth_req(nrf_ble_cgms_t * p_cgms,
|
||||
ble_gatts_evt_rw_authorize_request_t const * p_auth_req);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // NRF_BLE_CGMS_SST_H__
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,470 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include "ble_racp.h"
|
||||
#include "ble_srv_common.h"
|
||||
|
||||
#include "ble_date_time.h"
|
||||
#include "sdk_common.h"
|
||||
|
||||
#include "nrf_ble_cgms.h"
|
||||
#include "cgms_db.h"
|
||||
#include "cgms_meas.h"
|
||||
#include "cgms_racp.h"
|
||||
#include "cgms_socp.h"
|
||||
#include "cgms_sst.h"
|
||||
|
||||
#define OPERAND_FILTER_TYPE_RESV 0x00 /**< Filter type value reserved for future use. */
|
||||
#define OPERAND_FILTER_TYPE_SEQ_NUM 0x01 /**< Filter data using Sequence Number criteria. */
|
||||
#define OPERAND_FILTER_TYPE_FACING_TIME 0x02 /**< Filter data using User Facing Time criteria. */
|
||||
|
||||
|
||||
/**@brief Function for interception of GATT errors and @ref nrf_ble_gq errors.
|
||||
*
|
||||
* @param[in] nrf_error Error code.
|
||||
* @param[in] p_ctx Parameter from the event handler.
|
||||
* @param[in] conn_handle Connection handle.
|
||||
*/
|
||||
static void gatt_error_handler(uint32_t nrf_error,
|
||||
void * p_ctx,
|
||||
uint16_t conn_handle)
|
||||
{
|
||||
nrf_ble_cgms_t * p_cgms = (nrf_ble_cgms_t *)p_ctx;
|
||||
|
||||
if ((p_cgms->error_handler) != NULL && (nrf_error != NRF_ERROR_INVALID_STATE))
|
||||
{
|
||||
p_cgms->error_handler(nrf_error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for setting next sequence number by reading the last record in the data base.
|
||||
*
|
||||
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
|
||||
*/
|
||||
static uint32_t next_sequence_number_set(void)
|
||||
{
|
||||
uint16_t num_records;
|
||||
ble_cgms_rec_t rec;
|
||||
|
||||
num_records = cgms_db_num_records_get();
|
||||
if (num_records > 0)
|
||||
{
|
||||
// Get last record
|
||||
uint32_t err_code = cgms_db_record_get(num_records - 1, &rec);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint8_t encode_feature_location_type(uint8_t * p_out_buffer, nrf_ble_cgms_feature_t * p_in_feature)
|
||||
{
|
||||
uint8_t len = 0;
|
||||
|
||||
len += uint24_encode(p_in_feature->feature, &p_out_buffer[len]);
|
||||
p_out_buffer[len++] = (p_in_feature->sample_location << 4) | (p_in_feature->type & 0x0F);
|
||||
len += uint16_encode(0xFFFF, &p_out_buffer[len]);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the glucose feature.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS if characteristic was successfully added, otherwise an error code.
|
||||
*/
|
||||
static uint32_t glucose_feature_char_add(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint8_t init_value_len;
|
||||
uint8_t encoded_initial_feature[NRF_BLE_CGMS_FEATURE_LEN];
|
||||
ble_add_char_params_t add_char_params;
|
||||
|
||||
memset(&add_char_params, 0, sizeof(add_char_params));
|
||||
|
||||
init_value_len = encode_feature_location_type(encoded_initial_feature, &(p_cgms->feature));
|
||||
|
||||
add_char_params.uuid = BLE_UUID_CGM_FEATURE;
|
||||
add_char_params.max_len = init_value_len;
|
||||
add_char_params.init_len = init_value_len;
|
||||
add_char_params.p_init_value = encoded_initial_feature;
|
||||
add_char_params.read_access = SEC_JUST_WORKS;
|
||||
add_char_params.write_access = SEC_NO_ACCESS;
|
||||
|
||||
|
||||
return characteristic_add(p_cgms->service_handle,
|
||||
&add_char_params,
|
||||
&p_cgms->char_handles.feature);
|
||||
}
|
||||
|
||||
|
||||
uint8_t encode_status(uint8_t * p_out_buffer, nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint8_t len = 0;
|
||||
|
||||
len += uint16_encode(p_cgms->sensor_status.time_offset, &p_out_buffer[len]);
|
||||
|
||||
p_out_buffer[len++] = p_cgms->sensor_status.status.status;
|
||||
p_out_buffer[len++] = p_cgms->sensor_status.status.calib_temp;
|
||||
p_out_buffer[len++] = p_cgms->sensor_status.status.warning;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for adding a status characteristic for the CGMS.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS if characteristic was successfully added, otherwise an error code.
|
||||
*/
|
||||
static uint32_t status_char_add(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint8_t init_value_len;
|
||||
uint8_t encoded_initial_status[NRF_BLE_CGMS_STATUS_LEN];
|
||||
ble_add_char_params_t add_char_params;
|
||||
|
||||
memset(&add_char_params, 0, sizeof(add_char_params));
|
||||
|
||||
init_value_len = encode_status(encoded_initial_status, p_cgms);
|
||||
|
||||
add_char_params.uuid = BLE_UUID_CGM_STATUS;
|
||||
add_char_params.max_len = init_value_len;
|
||||
add_char_params.init_len = init_value_len;
|
||||
add_char_params.p_init_value = encoded_initial_status;
|
||||
add_char_params.read_access = SEC_JUST_WORKS;
|
||||
add_char_params.write_access = SEC_NO_ACCESS;
|
||||
|
||||
return characteristic_add(p_cgms->service_handle,
|
||||
&add_char_params,
|
||||
&p_cgms->char_handles.status);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for adding a characteristic for the Session Run Time.
|
||||
*
|
||||
* @param[in] p_cgms Service instance.
|
||||
*
|
||||
* @return NRF_SUCCESS if characteristic was successfully added, otherwise an error code.
|
||||
*/
|
||||
static uint32_t srt_char_add(nrf_ble_cgms_t * p_cgms)
|
||||
{
|
||||
uint8_t len = 0;
|
||||
uint8_t encoded_initial_srt[NRF_BLE_CGMS_SRT_LEN];
|
||||
ble_add_char_params_t add_char_params;
|
||||
|
||||
memset(&add_char_params, 0, sizeof(add_char_params));
|
||||
|
||||
|
||||
len += uint16_encode(p_cgms->session_run_time, &(encoded_initial_srt[len]));
|
||||
|
||||
add_char_params.uuid = BLE_UUID_CGM_SESSION_RUN_TIME;
|
||||
add_char_params.max_len = NRF_BLE_CGMS_SRT_LEN;
|
||||
add_char_params.init_len = len;
|
||||
add_char_params.p_init_value = encoded_initial_srt;
|
||||
add_char_params.read_access = SEC_JUST_WORKS;
|
||||
add_char_params.write_access = SEC_NO_ACCESS;
|
||||
add_char_params.char_props.read = true;
|
||||
|
||||
return characteristic_add(p_cgms->service_handle,
|
||||
&add_char_params,
|
||||
&p_cgms->char_handles.srt);
|
||||
}
|
||||
|
||||
|
||||
uint8_t init_calib_val[] = {
|
||||
0x3E,
|
||||
0x00,
|
||||
0x07,
|
||||
0x00,
|
||||
0x06,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
ret_code_t nrf_ble_cgms_init(nrf_ble_cgms_t * p_cgms, const nrf_ble_cgms_init_t * p_cgms_init)
|
||||
{
|
||||
VERIFY_PARAM_NOT_NULL(p_cgms);
|
||||
VERIFY_PARAM_NOT_NULL(p_cgms_init);
|
||||
VERIFY_PARAM_NOT_NULL(p_cgms_init->evt_handler);
|
||||
VERIFY_PARAM_NOT_NULL(p_cgms_init->p_gatt_queue);
|
||||
|
||||
uint32_t err_code;
|
||||
ble_uuid_t ble_uuid;
|
||||
|
||||
// Initialize data base
|
||||
err_code = cgms_db_init();
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
err_code = next_sequence_number_set();
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Initialize service structure
|
||||
p_cgms->evt_handler = p_cgms_init->evt_handler;
|
||||
p_cgms->error_handler = p_cgms_init->error_handler;
|
||||
p_cgms->p_gatt_queue = p_cgms_init->p_gatt_queue;
|
||||
p_cgms->feature = p_cgms_init->feature;
|
||||
p_cgms->sensor_status = p_cgms_init->initial_sensor_status;
|
||||
p_cgms->session_run_time = p_cgms_init->initial_run_time;
|
||||
p_cgms->is_session_started = false;
|
||||
p_cgms->nb_run_session = 0;
|
||||
p_cgms->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
p_cgms->gatt_err_handler = gatt_error_handler;
|
||||
|
||||
p_cgms->feature.feature = 0;
|
||||
p_cgms->feature.feature |= NRF_BLE_CGMS_FEAT_MULTIPLE_BOND_SUPPORTED;
|
||||
p_cgms->feature.feature |= NRF_BLE_CGMS_FEAT_MULTIPLE_SESSIONS_SUPPORTED;
|
||||
p_cgms->feature.type = NRF_BLE_CGMS_MEAS_TYPE_VEN_BLOOD;
|
||||
p_cgms->feature.sample_location = NRF_BLE_CGMS_MEAS_LOC_AST;
|
||||
p_cgms->feature.feature |= NRF_BLE_CGMS_FEAT_MULTIPLE_BOND_SUPPORTED;
|
||||
|
||||
memcpy(p_cgms->calibration_val[0].value, init_calib_val, NRF_BLE_CGMS_MAX_CALIB_LEN);
|
||||
|
||||
// Add service
|
||||
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_CGM_SERVICE);
|
||||
|
||||
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
|
||||
&ble_uuid,
|
||||
&p_cgms->service_handle);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Add glucose measurement characteristic
|
||||
err_code = cgms_meas_char_add(p_cgms);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Add glucose measurement feature characteristic
|
||||
err_code = glucose_feature_char_add(p_cgms);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Add glucose measurement status characteristic
|
||||
err_code = status_char_add(p_cgms);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Add record control access point characteristic
|
||||
err_code = cgms_racp_char_add(p_cgms);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Add Start Session Time characteristic
|
||||
err_code = cgms_sst_char_add(p_cgms);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Add Session Run Time characteristic
|
||||
err_code = srt_char_add(p_cgms);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
// Add Specific Operations Control Point characteristic
|
||||
err_code = cgms_socp_char_add(p_cgms);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the WRITE event.
|
||||
*
|
||||
* @details Handles WRITE events from the BLE stack.
|
||||
*
|
||||
* @param[in] p_cgms Glucose Service structure.
|
||||
* @param[in] p_ble_evt Event received from the BLE stack.
|
||||
*/
|
||||
static void on_write(nrf_ble_cgms_t * p_cgms, ble_evt_t const * p_ble_evt)
|
||||
{
|
||||
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
|
||||
|
||||
cgms_meas_on_write(p_cgms, p_evt_write);
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for handling the TX_COMPLETE event.
|
||||
*
|
||||
* @details Handles TX_COMPLETE events from the BLE stack.
|
||||
*
|
||||
* @param[in] p_cgms Glucose Service structure.
|
||||
* @param[in] p_ble_evt Event received from the BLE stack.
|
||||
*/
|
||||
static void on_tx_complete(nrf_ble_cgms_t * p_cgms, ble_evt_t const * p_ble_evt)
|
||||
{
|
||||
cgms_racp_on_tx_complete(p_cgms);
|
||||
}
|
||||
|
||||
|
||||
static void on_rw_authorize_request(nrf_ble_cgms_t * p_cgms, ble_gatts_evt_t const * p_gatts_evt)
|
||||
{
|
||||
ble_gatts_evt_rw_authorize_request_t const * p_auth_req =
|
||||
&p_gatts_evt->params.authorize_request;
|
||||
|
||||
cgms_racp_on_rw_auth_req(p_cgms, p_auth_req);
|
||||
cgms_socp_on_rw_auth_req(p_cgms, p_auth_req);
|
||||
cgms_sst_on_rw_auth_req(p_cgms, p_auth_req);
|
||||
}
|
||||
|
||||
|
||||
void nrf_ble_cgms_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
|
||||
{
|
||||
nrf_ble_cgms_t * p_cgms = (nrf_ble_cgms_t *)p_context;
|
||||
|
||||
switch (p_ble_evt->header.evt_id)
|
||||
{
|
||||
case BLE_GAP_EVT_CONNECTED:
|
||||
p_cgms->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
|
||||
break;
|
||||
|
||||
case BLE_GAP_EVT_DISCONNECTED:
|
||||
p_cgms->conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
break;
|
||||
|
||||
case BLE_GATTS_EVT_WRITE:
|
||||
on_write(p_cgms, p_ble_evt);
|
||||
break;
|
||||
|
||||
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
|
||||
on_tx_complete(p_cgms, p_ble_evt);
|
||||
break;
|
||||
|
||||
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
|
||||
on_rw_authorize_request(p_cgms, &p_ble_evt->evt.gatts_evt);
|
||||
break;
|
||||
|
||||
default:
|
||||
// No implementation needed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ret_code_t nrf_ble_cgms_meas_create(nrf_ble_cgms_t * p_cgms, ble_cgms_rec_t * p_rec)
|
||||
{
|
||||
uint32_t err_code = NRF_SUCCESS;
|
||||
uint8_t nb_rec_to_send = 1;
|
||||
|
||||
err_code = cgms_db_record_add(p_rec);
|
||||
if (err_code != NRF_SUCCESS)
|
||||
{
|
||||
return err_code;
|
||||
}
|
||||
if ((p_cgms->conn_handle != BLE_CONN_HANDLE_INVALID) && (p_cgms->comm_interval != 0))
|
||||
{
|
||||
err_code = cgms_meas_send(p_cgms, p_rec, &nb_rec_to_send);
|
||||
}
|
||||
return err_code;
|
||||
}
|
||||
|
||||
|
||||
ret_code_t nrf_ble_cgms_update_status(nrf_ble_cgms_t * p_cgms, nrf_ble_cgm_status_t * p_status)
|
||||
{
|
||||
uint8_t encoded_status[NRF_BLE_CGMS_STATUS_LEN];
|
||||
ble_gatts_value_t status_val;
|
||||
|
||||
memset(&status_val, 0, sizeof(status_val));
|
||||
p_cgms->sensor_status = *p_status;
|
||||
status_val.len = encode_status(encoded_status, p_cgms);
|
||||
status_val.p_value = encoded_status;
|
||||
status_val.offset = 0;
|
||||
|
||||
return (sd_ble_gatts_value_set(p_cgms->conn_handle, p_cgms->char_handles.status.value_handle,
|
||||
&status_val));
|
||||
}
|
||||
|
||||
|
||||
ret_code_t nrf_ble_cgms_conn_handle_assign(nrf_ble_cgms_t * p_cgms, uint16_t conn_handle)
|
||||
{
|
||||
VERIFY_PARAM_NOT_NULL(p_cgms);
|
||||
|
||||
p_cgms->conn_handle = conn_handle;
|
||||
|
||||
return nrf_ble_gq_conn_handle_register(p_cgms->p_gatt_queue, conn_handle);
|
||||
}
|
||||
|
||||
|
||||
ret_code_t nrf_ble_cgms_srt_set(nrf_ble_cgms_t * p_cgms, uint16_t run_time)
|
||||
{
|
||||
ble_gatts_value_t srt_val;
|
||||
uint8_t encoded_session_run_time[NRF_BLE_CGMS_SRT_LEN];
|
||||
uint8_t gatts_value_set_len = 0;
|
||||
|
||||
gatts_value_set_len = uint16_encode(run_time, encoded_session_run_time); // (p_sst, encoded_start_session_time);
|
||||
|
||||
memset(&srt_val, 0, sizeof(ble_gatts_value_t));
|
||||
srt_val.len = gatts_value_set_len;
|
||||
srt_val.p_value = encoded_session_run_time;
|
||||
srt_val.offset = 0;
|
||||
|
||||
return (sd_ble_gatts_value_set(p_cgms->conn_handle, p_cgms->char_handles.srt.value_handle,
|
||||
&srt_val));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/** @file
|
||||
*
|
||||
* @defgroup ble_cgms Continuous Glucose Monitoring Service
|
||||
* @{
|
||||
* @ingroup ble_sdk_srv
|
||||
* @brief Continuous Glucose Monitoring Service (CGMS) module.
|
||||
*
|
||||
* @details This module implements a sensor for the Continuous Glucose Monitoring Service.
|
||||
* The sensor is a GATT Server that sends CGM measurements to a connected CGMS
|
||||
* Collector. The CGMS Sensor stores records that can be accessed with the
|
||||
* Record Access Control Point (RACP). The collector can access the features and status
|
||||
* of the sensor. Session Run Time and Session Start Time can be used to convey timing
|
||||
* information between the sensor and the collector. The Specific Operations Control Point
|
||||
* is used to stop and start monitoring sessions, among other things.
|
||||
*
|
||||
* @note The application must register this module as BLE event observer using the
|
||||
* NRF_SDH_BLE_OBSERVER macro. Example:
|
||||
* @code
|
||||
* nrf_ble_cgms_t instance;
|
||||
* NRF_SDH_BLE_OBSERVER(anything, NRF_BLE_CGMS_BLE_OBSERVER_PRIO,
|
||||
* nrf_ble_cgms_on_ble_evt, &instance);
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#ifndef NRF_BLE_CGMS_H__
|
||||
#define NRF_BLE_CGMS_H__
|
||||
|
||||
#include "ble_srv_common.h"
|
||||
#include "sdk_errors.h"
|
||||
#include "ble_racp.h"
|
||||
#include "nrf_sdh_ble.h"
|
||||
#include "nrf_ble_gq.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**@brief Macro for defining a nrf_ble_cgms instance.
|
||||
*
|
||||
* @param _name Name of the instance.
|
||||
* @hideinitializer
|
||||
*/
|
||||
#define NRF_BLE_CGMS_DEF(_name) \
|
||||
static nrf_ble_cgms_t _name; \
|
||||
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
|
||||
NRF_BLE_CGMS_BLE_OBSERVER_PRIO, \
|
||||
nrf_ble_cgms_on_ble_evt, &_name)
|
||||
|
||||
/**@name CGM Feature characteristic defines
|
||||
* @{ */
|
||||
#define NRF_BLE_CGMS_FEAT_CALIBRATION_SUPPORTED (0x01 << 0) //!< Calibration supported.
|
||||
#define NRF_BLE_CGMS_FEAT_PATIENT_HIGH_LOW_ALERTS_SUPPORTED (0x01 << 1) //!< Patient High/Low Alerts supported.
|
||||
#define NRF_BLE_CGMS_FEAT_HYPO_ALERTS_SUPPORTED (0x01 << 2) //!< Hypo Alerts supported.
|
||||
#define NRF_BLE_CGMS_FEAT_HYPER_ALERTS_SUPPORTED (0x01 << 3) //!< Hyper Alerts supported.
|
||||
#define NRF_BLE_CGMS_FEAT_RATE_OF_INCREASE_DECREASE_ALERTS_SUPPORTED (0x01 << 4) //!< Rate of Increase/Decrease Alerts supported.
|
||||
#define NRF_BLE_CGMS_FEAT_DEVICE_SPECIFIC_ALERT_SUPPORTED (0x01 << 5) //!< Device Specific Alert supported.
|
||||
#define NRF_BLE_CGMS_FEAT_SENSOR_MALFUNCTION_DETECTION_SUPPORTED (0x01 << 6) //!< Sensor Malfunction Detection supported.
|
||||
#define NRF_BLE_CGMS_FEAT_SENSOR_TEMPERATURE_HIGH_LOW_DETECTION_SUPPORTED (0x01 << 7) //!< Sensor Temperature High-Low Detection supported.
|
||||
#define NRF_BLE_CGMS_FEAT_SENSOR_RESULT_HIGH_LOW_DETECTION_SUPPORTED (0x01 << 8) //!< Sensor Result High-Low Detection supported.
|
||||
#define NRF_BLE_CGMS_FEAT_LOW_BATTERY_DETECTION_SUPPORTED (0x01 << 9) //!< Low Battery Detection supported.
|
||||
#define NRF_BLE_CGMS_FEAT_SENSOR_TYPE_ERROR_DETECTION_SUPPORTED (0x01 << 10) //!< Sensor Type Error Detection supported.
|
||||
#define NRF_BLE_CGMS_FEAT_GENERAL_DEVICE_FAULT_SUPPORTED (0x01 << 11) //!< General Device Fault supported.
|
||||
#define NRF_BLE_CGMS_FEAT_E2E_CRC_SUPPORTED (0x01 << 12) //!< E2E-CRC supported.
|
||||
#define NRF_BLE_CGMS_FEAT_MULTIPLE_BOND_SUPPORTED (0x01 << 13) //!< Multiple Bond supported.
|
||||
#define NRF_BLE_CGMS_FEAT_MULTIPLE_SESSIONS_SUPPORTED (0x01 << 14) //!< Multiple Sessions supported.
|
||||
#define NRF_BLE_CGMS_FEAT_CGM_TREND_INFORMATION_SUPPORTED (0x01 << 15) //!< CGM Trend Information supported.
|
||||
#define NRF_BLE_CGMS_FEAT_CGM_QUALITY_SUPPORTED (0x01 << 16) //!< CGM Quality supported.
|
||||
/** @} */
|
||||
|
||||
/**@name Continuous Glucose Monitoring type
|
||||
* @{ */
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_CAP_BLOOD 0x01 //!< Capillary Whole blood.
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_CAP_PLASMA 0x02 //!< Capillary Plasma.
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_VEN_BLOOD 0x03 //!< Venous Whole blood.
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_VEN_PLASMA 0x04 //!< Venous Plasma.
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_ART_BLOOD 0x05 //!< Arterial Whole blood.
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_ART_PLASMA 0x06 //!< Arterial Plasma.
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_UNDET_BLOOD 0x07 //!< Undetermined Whole blood.
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_UNDET_PLASMA 0x08 //!< Undetermined Plasma.
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_FLUID 0x09 //!< Interstitial Fluid (ISF).
|
||||
#define NRF_BLE_CGMS_MEAS_TYPE_CONTROL 0x0A //!< Control Solution.
|
||||
/** @} */
|
||||
|
||||
/**@name CGM sample location
|
||||
* @{ */
|
||||
#define NRF_BLE_CGMS_MEAS_LOC_FINGER 0x01 //!< Finger.
|
||||
#define NRF_BLE_CGMS_MEAS_LOC_AST 0x02 //!< Alternate Site Test (AST).
|
||||
#define NRF_BLE_CGMS_MEAS_LOC_EAR 0x03 //!< Earlobe.
|
||||
#define NRF_BLE_CGMS_MEAS_LOC_CONTROL 0x04 //!< Control solution.
|
||||
#define NRF_BLE_CGMS_MEAS_LOC_SUB_TISSUE 0x05 //!< Subcutaneous tissue.
|
||||
#define NRF_BLE_CGMS_MEAS_LOC_NOT_AVAIL 0x0F //!< Sample Location value not available.
|
||||
/** @} */
|
||||
|
||||
/**@name CGM Measurement Sensor Status Annunciation
|
||||
* @{ */
|
||||
#define NRF_BLE_CGMS_STATUS_SESSION_STOPPED (0x01 << 0) //!< Status: Session Stopped.
|
||||
#define NRF_BLE_CGMS_STATUS_DEVICE_BATTERY_LOW (0x01 << 1) //!< Status: Device Battery Low.
|
||||
#define NRF_BLE_CGMS_STATUS_SENSOR_TYPE_INCORRECT_FOR_DEVICE (0x01 << 2) //!< Status: Sensor type incorrect for device.
|
||||
#define NRF_BLE_CGMS_STATUS_SENSOR_MALFUNCTION (0x01 << 3) //!< Status: Sensor malfunction.
|
||||
#define NRF_BLE_CGMS_STATUS_DEVICE_SPECIFIC_ALERT (0x01 << 4) //!< Status: Device Specific Alert.
|
||||
#define NRF_BLE_CGMS_STATUS_GENERAL_DEVICE_FAULT (0x01 << 5) //!< Status: General device fault has occurred in the sensor.
|
||||
/** @} */
|
||||
|
||||
/**@name CGM Measurement flags
|
||||
* @{ */
|
||||
#define NRF_BLE_CGMS_FLAG_TREND_INFO_PRESENT 0x01 //!< CGM Trend Information Present.
|
||||
#define NRF_BLE_CGMS_FLAGS_QUALITY_PRESENT 0x02 //!< CGM Quality Present.
|
||||
#define NRF_BLE_CGMS_STATUS_FLAGS_WARNING_OCT_PRESENT 0x20 //!< Sensor Status Annunciation Field, Warning-Octet present.
|
||||
#define NRF_BLE_CGMS_STATUS_FLAGS_CALTEMP_OCT_PRESENT 0x40 //!< Sensor Status Annunciation Field, Cal/Temp-Octet present.
|
||||
#define NRF_BLE_CGMS_STATUS_FLAGS_STATUS_OCT_PRESENT 0x80 //!< Sensor Status Annunciation Field, Status-Octet present.
|
||||
/** @} */
|
||||
|
||||
/**@name Byte length of various commands (used for validating, encoding, and decoding data).
|
||||
* @{ */
|
||||
#define NRF_BLE_CGMS_MEAS_OP_LEN 1 //!< Length of the opcode inside the Glucose Measurement packet.
|
||||
#define NRF_BLE_CGMS_MEAS_HANDLE_LEN 2 //!< Length of the handle inside the Glucose Measurement packet.
|
||||
#define NRF_BLE_CGMS_MEAS_LEN_MAX (BLE_GATT_ATT_MTU_DEFAULT - \
|
||||
NRF_BLE_CGMS_MEAS_OP_LEN - \
|
||||
NRF_BLE_CGMS_MEAS_HANDLE_LEN) //!< Maximum size of a transmitted Glucose Measurement.
|
||||
|
||||
#define NRF_BLE_CGMS_MEAS_REC_LEN_MAX 15 //!< Maximum length of one measurement record. Size 1 byte, flags 1 byte, glucose concentration 2 bytes, offset 2 bytes, status 3 bytes, trend 2 bytes, quality 2 bytes, CRC 2 bytes.
|
||||
#define NRF_BLE_CGMS_MEAS_REC_LEN_MIN 6 //!< Minimum length of one measurement record. Size 1 byte, flags 1 byte, glucose concentration 2 bytes, offset 2 bytes.
|
||||
#define NRF_BLE_CGMS_MEAS_REC_PER_NOTIF_MAX (NRF_BLE_CGMS_MEAS_LEN_MAX / \
|
||||
NRF_BLE_CGMS_MEAS_REC_LEN_MIN) //!< Maximum number of records per notification. We can send more than one measurement record per notification, but we do not want a a single record split over two notifications.
|
||||
|
||||
#define NRF_BLE_CGMS_SOCP_RESP_CODE_LEN 2 //!< Length of a response. Response code 1 byte, response value 1 byte.
|
||||
#define NRF_BLE_CGMS_FEATURE_LEN 6 //!< Length of a feature. Feature 3 bytes, type 4 bits, sample location 4 bits, CRC 2 bytes.
|
||||
#define NRF_BLE_CGMS_STATUS_LEN 7 //!< Length of a status. Offset 2 bytes, status 3 bytes, CRC 2 bytes.
|
||||
#define NRF_BLE_CGMS_MAX_CALIB_LEN 10 //!< Length of a calibration record. Concentration 2 bytes, time 2 bytes, calibration 4 bits, calibration sample location 4 bits, next calibration time 2 bytes, record number 2 bytes, calibration status 1 byte.
|
||||
#define NRF_BLE_CGMS_CALIBS_NB_MAX 5 //!< Maximum number of calibration values that can be stored.
|
||||
#define NRF_BLE_CGMS_SST_LEN 9 //!< Length of the start time. Date time 7 bytes, time zone 1 byte, DST 1 byte.
|
||||
#define NRF_BLE_CGMS_CRC_LEN 2 //!< Length of the CRC bytes (if used).
|
||||
#define NRF_BLE_CGMS_SRT_LEN 2 //!< Length of the Session Run Time attribute.
|
||||
|
||||
#define NRF_BLE_CGMS_SOCP_RESP_LEN (NRF_BLE_CGMS_MEAS_LEN_MAX - \
|
||||
NRF_BLE_CGMS_SOCP_RESP_CODE_LEN) //!< Max lenth of a SOCP response.
|
||||
|
||||
#define NRF_BLE_CGMS_RACP_PENDING_OPERANDS_MAX 2 // !< Maximum number of pending Record Access Control Point operations.
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup nrf_ble_cgms_enums Enumerations
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**@brief CGM Service events. */
|
||||
typedef enum
|
||||
{
|
||||
NRF_BLE_CGMS_EVT_NOTIFICATION_ENABLED, /**< Glucose value notification enabled. */
|
||||
NRF_BLE_CGMS_EVT_NOTIFICATION_DISABLED, /**< Glucose value notification disabled. */
|
||||
NRF_BLE_CGMS_EVT_START_SESSION, /**< Glucose value notification start session. */
|
||||
NRF_BLE_CGMS_EVT_STOP_SESSION, /**< Glucose value notification stop session. */
|
||||
NRF_BLE_CGMS_EVT_WRITE_COMM_INTERVAL, /**< Glucose value write communication interval. */
|
||||
} nrf_ble_cgms_evt_type_t;
|
||||
|
||||
|
||||
/** @} */ // End tag for Enumeration group.
|
||||
|
||||
/**
|
||||
* @defgroup nrf_ble_cgms_structs Structures
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**@brief CGM Service event. */
|
||||
typedef struct
|
||||
{
|
||||
nrf_ble_cgms_evt_type_t evt_type; /**< Type of event. */
|
||||
} nrf_ble_cgms_evt_t;
|
||||
|
||||
/** @} */ // End tag for Structure group.
|
||||
|
||||
/**
|
||||
* @defgroup nrf_ble_cgms_types Types
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**@brief Forward declaration of the nrf_ble_cgms_t type. */
|
||||
typedef struct ble_cgms_s nrf_ble_cgms_t;
|
||||
|
||||
|
||||
/**@brief CGM Service event handler type. */
|
||||
typedef void (* ble_cgms_evt_handler_t) (nrf_ble_cgms_t * p_cgms, nrf_ble_cgms_evt_t * p_evt);
|
||||
|
||||
/** @} */ // End tag for Types group.
|
||||
|
||||
/**
|
||||
* @addtogroup nrf_ble_cgms_structs
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**@brief CGM Measurement Sensor Status Annunciation. */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t warning; /**< Warning annunciation. */
|
||||
uint8_t calib_temp; /**< Calibration and Temperature annunciation. */
|
||||
uint8_t status; /**< Status annunciation. */
|
||||
} nrf_ble_cgms_sensor_annunc_t;
|
||||
|
||||
|
||||
/**@brief CGM measurement. */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t flags; /**< Indicates the presence of optional fields and the Sensor Status Annunciation field. */
|
||||
uint16_t glucose_concentration; /**< Glucose concentration. 16-bit word comprising 4-bit exponent and signed 12-bit mantissa. */
|
||||
uint16_t time_offset; /**< Time offset. Represents the time difference between measurements. */
|
||||
nrf_ble_cgms_sensor_annunc_t sensor_status_annunciation; /**< Sensor Status Annunciation. Variable length, can include Status, Cal/Temp, and Warning. */
|
||||
uint16_t trend; /**< Optional field that can include Trend Information. */
|
||||
uint16_t quality; /**< Optional field that includes the Quality of the measurement. */
|
||||
} nrf_ble_cgms_meas_t;
|
||||
|
||||
|
||||
/**@brief CGM Measurement record. */
|
||||
typedef struct
|
||||
{
|
||||
nrf_ble_cgms_meas_t meas; /**< CGM measurement. */
|
||||
} ble_cgms_rec_t;
|
||||
|
||||
|
||||
/**@brief Features supported by the CGM Service. */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t feature; /**< Information on supported features in the CGM Service. */
|
||||
uint8_t type; /**< Type. */
|
||||
uint8_t sample_location; /**< Sample location. */
|
||||
}nrf_ble_cgms_feature_t;
|
||||
|
||||
|
||||
/**@brief Status of the CGM measurement. */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t time_offset; /**< Time offset. */
|
||||
nrf_ble_cgms_sensor_annunc_t status; /**< Status. */
|
||||
} nrf_ble_cgm_status_t;
|
||||
|
||||
|
||||
/**@brief CGM Service initialization structure that contains all options and data needed for
|
||||
* initializing the service. */
|
||||
typedef struct
|
||||
{
|
||||
ble_cgms_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the CGM Service. */
|
||||
ble_srv_error_handler_t error_handler; /**< Function to be called when an error occurs. */
|
||||
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
|
||||
nrf_ble_cgms_feature_t feature; /**< Features supported by the service. */
|
||||
nrf_ble_cgm_status_t initial_sensor_status; /**< Sensor status. */
|
||||
uint16_t initial_run_time; /**< Run time. */
|
||||
} nrf_ble_cgms_init_t;
|
||||
|
||||
|
||||
/**@brief Specific Operation Control Point response structure. */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t opcode; /**< Opcode describing the response. */
|
||||
uint8_t req_opcode; /**< The original opcode for the request to which this response belongs. */
|
||||
uint8_t rsp_code; /**< Response code. */
|
||||
uint8_t resp_val[NRF_BLE_CGMS_SOCP_RESP_LEN]; /**< Array containing the response value. */
|
||||
uint8_t size_val; /**< Length of the response value. */
|
||||
} ble_socp_rsp_t;
|
||||
|
||||
|
||||
/**@brief Calibration value. */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t value[NRF_BLE_CGMS_MAX_CALIB_LEN]; /**< Array containing the calibration value. */
|
||||
} nrf_ble_cgms_calib_t;
|
||||
|
||||
|
||||
/**@brief Record Access Control Point transaction data. */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t racp_proc_operator; /**< Operator of the current request. */
|
||||
uint16_t racp_proc_record_ndx; /**< Current record index. */
|
||||
uint16_t racp_proc_records_ndx_last_to_send; /**< The last record to send, can be used together with racp_proc_record_ndx to determine a range of records to send. (used by greater/less filters). */
|
||||
uint16_t racp_proc_records_reported; /**< Number of reported records. */
|
||||
ble_racp_value_t racp_request; /**< RACP procedure that has been requested from the peer. */
|
||||
ble_racp_value_t pending_racp_response; /**< RACP response to be sent. */
|
||||
bool racp_procesing_active; /**< RACP processing active. */
|
||||
uint8_t pending_racp_response_operand[NRF_BLE_CGMS_RACP_PENDING_OPERANDS_MAX]; /**< Operand of the RACP response to be sent. */
|
||||
} nrf_ble_cgms_racp_t;
|
||||
|
||||
|
||||
/** @brief Handles related to CGM characteristics. */
|
||||
typedef struct
|
||||
{
|
||||
ble_gatts_char_handles_t measurment; /**< Handles related to the CGM Measurement characteristic. */
|
||||
ble_gatts_char_handles_t feature; /**< Handles related to the CGM Feature characteristic. */
|
||||
ble_gatts_char_handles_t sst; /**< Handles related to the CGM Session Start Time characteristic. */
|
||||
ble_gatts_char_handles_t racp; /**< Handles related to the CGM Record Access Control Point characteristic. */
|
||||
ble_gatts_char_handles_t srt; /**< Handles related to the CGM Session Run Time characteristic. */
|
||||
ble_gatts_char_handles_t socp; /**< Handles related to the CGM Specific Operations Control Point characteristic. */
|
||||
ble_gatts_char_handles_t status; /**< Handles related to the CGM Status characteristic. */
|
||||
} nrf_ble_cgms_char_handler_t;
|
||||
|
||||
|
||||
/**@brief Status information for the CGM Service. */
|
||||
struct ble_cgms_s
|
||||
{
|
||||
ble_cgms_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the CGM Service. */
|
||||
ble_srv_error_handler_t error_handler; /**< Function to be called if an error occurs. */
|
||||
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
|
||||
nrf_ble_gq_req_error_cb_t gatt_err_handler; /**< Error handler to be called in case of an error from SoftDevice. */
|
||||
uint16_t service_handle; /**< Handle of the CGM Service (as provided by the BLE stack). */
|
||||
nrf_ble_cgms_char_handler_t char_handles; /**< GATTS characteristic handles for the different characteristics in the service. */
|
||||
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack; @ref BLE_CONN_HANDLE_INVALID if not in a connection). */
|
||||
nrf_ble_cgms_feature_t feature; /**< Structure to store the value of the feature characteristic. */
|
||||
uint8_t comm_interval; /**< Variable to keep track of the communication interval. */
|
||||
ble_socp_rsp_t socp_response; /**< Structure containing reponse data to be indicated to the peer device. */
|
||||
nrf_ble_cgms_calib_t calibration_val[NRF_BLE_CGMS_CALIBS_NB_MAX]; /**< Calibration value. Can be read from and written to SOCP. */
|
||||
bool is_session_started; /**< Indicator if we are currently in a session. */
|
||||
uint8_t nb_run_session; /**< Variable to keep track of the number of sessions that were run. */
|
||||
uint16_t session_run_time; /**< Variable to store the expected run time of a session. */
|
||||
nrf_ble_cgm_status_t sensor_status; /**< Structure to keep track of the sensor status. */
|
||||
nrf_ble_cgms_racp_t racp_data; /**< Structure to manage Record Access requests. */
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup nrf_ble_cgms_functions Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**@brief Function for updating the status.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] p_status New status.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the status was updated successfully.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t nrf_ble_cgms_update_status(nrf_ble_cgms_t * p_cgms, nrf_ble_cgm_status_t * p_status);
|
||||
|
||||
|
||||
/**@brief Function for initializing the CGM Service.
|
||||
*
|
||||
* @param[out] p_cgms CGM Service structure. This structure must be supplied by
|
||||
* the application. It is initialized by this function and will later
|
||||
* be used to identify this particular service instance.
|
||||
* @param[in] p_cgms_init Information needed to initialize the service.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the service was initialized successfully.
|
||||
* @retval NRF_ERROR_NULL If any of the input parameters are NULL.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t nrf_ble_cgms_init(nrf_ble_cgms_t * p_cgms, const nrf_ble_cgms_init_t * p_cgms_init);
|
||||
|
||||
|
||||
/**@brief Function for handling the application's BLE stack events.
|
||||
*
|
||||
* @details Handles all events from the BLE stack that are of interest to the CGM Service.
|
||||
*
|
||||
* @param[in] p_ble_evt Event received from the BLE stack.
|
||||
* @param[in] p_context Instance of the CGM Service.
|
||||
*/
|
||||
void nrf_ble_cgms_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
|
||||
|
||||
|
||||
/**@brief Function for reporting a new glucose measurement to the CGM Service module.
|
||||
*
|
||||
* @details The application calls this function after having performed a new glucose measurement.
|
||||
* The new measurement is recorded in the RACP database.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] p_rec Pointer to the glucose record (measurement plus context).
|
||||
*
|
||||
* @retval NRF_SUCCESS If a measurement was successfully created.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t nrf_ble_cgms_meas_create(nrf_ble_cgms_t * p_cgms, ble_cgms_rec_t * p_rec);
|
||||
|
||||
|
||||
/**@brief Function for assigning a connection handle to a CGM Service instance.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] conn_handle Connection Handle to use for this instance of the CGM Service.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the connection handle was successfully stored in the CGM Service instance.
|
||||
* @retval NRF_ERROR_NULL If any of the input parameters are NULL.
|
||||
*/
|
||||
ret_code_t nrf_ble_cgms_conn_handle_assign(nrf_ble_cgms_t * p_cgms, uint16_t conn_handle);
|
||||
|
||||
|
||||
/**@brief Function for setting the Session Run Time attribute value.
|
||||
*
|
||||
* @param[in] p_cgms Instance of the CGM Service.
|
||||
* @param[in] run_time Run Time that will be displayed in the Session Run Time
|
||||
* attribute value.
|
||||
*
|
||||
* @retval NRF_SUCCESS If the Session Run Time attribute value was set successfully.
|
||||
* @return If functions from other modules return errors to this function,
|
||||
* the @ref nrf_error are propagated.
|
||||
*/
|
||||
ret_code_t nrf_ble_cgms_srt_set(nrf_ble_cgms_t * p_cgms, uint16_t run_time);
|
||||
|
||||
/** @} */ // End tag for Function group.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // NRF_BLE_CGMS_H__
|
||||
|
||||
/** @} */
|
||||
Reference in New Issue
Block a user