initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -0,0 +1,102 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector sample application.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef MEDC_API_H
#define MEDC_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Profile identifier used for MedcSetProfile() */
enum
{
MEDC_ID_HRP, /*! Heart rate profile */
MEDC_ID_BLP, /*! Blood pressure profile */
MEDC_ID_GLP, /*! Glucose profile */
MEDC_ID_WSP, /*! Weight scale profile */
MEDC_ID_HTP, /*! Health thermometer profile */
MEDC_ID_PLX /*! Pulse oximeter profile */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void MedcStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void MedcHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void MedcHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Set the profile to be used by the application. This function is called internally
* by MedcHandlerInit() with a default value. It may also be called by the system
* to configure the profile after executing MedcHandlerInit() and before executing
* MedcStart().
*
* \param profile Profile identifier.
*
* \return None.
*/
/*************************************************************************************************/
void MedcSetProfile(uint8_t profile);
#ifdef __cplusplus
};
#endif
#endif /* MEDC_API_H */
@@ -0,0 +1,220 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Blood Pressure profile
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "svc_ch.h"
#include "blpc/blpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached blood pressure service handles; begins after DIS */
#define MEDC_DISC_BPS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_BPS_START + BLPC_BPS_HDL_LIST_LEN)
/*! Pointers into handle list for blood pressure service handles */
static uint16_t *pMedcBpsHdlList = &medcCb.hdlList[MEDC_DISC_BPS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgBpsList[] =
{
/* Read: Blood pressure feature */
{NULL, 0, BLPC_BPS_BPF_HDL_IDX},
/* Write: Blood pressure measurement CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), BLPC_BPS_BPM_CCC_HDL_IDX},
/* Write: Intermediate cuff pressure CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), BLPC_BPS_ICP_CCC_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_BPS_LIST_LEN (sizeof(medcCfgBpsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcBlpInit(void);
static bool_t medcBlpDiscover(dmConnId_t connId);
static void medcBlpConfigure(dmConnId_t connId, uint8_t status);
static void medcBlpProcMsg(wsfMsgHdr_t *pMsg);
static void medcBlpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcBlpIf =
{
medcBlpInit,
medcBlpDiscover,
medcBlpConfigure,
medcBlpProcMsg,
medcBlpBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBpsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* blood pressure */
if (BlpcBpsValueUpdate(pMedcBpsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBlpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
case ATTC_HANDLE_VALUE_IND:
medcBpsValueUpdate((attEvt_t *) pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBlpInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_BLOOD_PRESSURE_SERVICE;
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcBlpDiscover(dmConnId_t connId)
{
/* discover blood pressure service */
BlpcBpsDiscover(connId, pMedcBpsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBlpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure blood pressure service */
AppDiscConfigure(connId, status, MEDC_CFG_BPS_LIST_LEN,
(attcDiscCfg_t *) medcCfgBpsList,
BLPC_BPS_HDL_LIST_LEN, pMedcBpsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBlpBtn(dmConnId_t connId, uint8_t btn)
{
return;
}
@@ -0,0 +1,344 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, glucose profile
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "glpc/glpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached glucose service handles; begins after DIS */
#define MEDC_DISC_GLS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_GLS_START + GLPC_GLS_HDL_LIST_LEN)
/*! Pointers into handle list for glucose service handles */
static uint16_t *pMedcGlsHdlList = &medcCb.hdlList[MEDC_DISC_GLS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgGlsList[] =
{
/* Read: glucose feature */
{NULL, 0, GLPC_GLS_GLF_HDL_IDX},
/* Write: glucose measurement CCC descriptor */
{medcCccNtfVal, sizeof(medcCccIndVal), GLPC_GLS_GLM_CCC_HDL_IDX},
/* Write: glucose measurement context CCC descriptor */
{medcCccNtfVal, sizeof(medcCccIndVal), GLPC_GLS_GLMC_CCC_HDL_IDX},
/* Write: record access control point CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), GLPC_GLS_RACP_CCC_HDL_IDX}
};
/* Characteristic configuration list length */
#define MEDC_CFG_GLS_LIST_LEN (sizeof(medcCfgGlsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
wsfTimer_t racpTimer; /*! RACP procedure timer */
bool_t inProgress; /*! RACP procedure in progress */
} medcGlpCb;
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcGlpInit(void);
static bool_t medcGlpDiscover(dmConnId_t connId);
static void medcGlpConfigure(dmConnId_t connId, uint8_t status);
static void medcGlpProcMsg(wsfMsgHdr_t *pMsg);
static void medcGlpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcGlpIf =
{
medcGlpInit,
medcGlpDiscover,
medcGlpConfigure,
medcGlpProcMsg,
medcGlpBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* glucose */
if (GlpcGlsValueUpdate(pMedcGlsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlpInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_GLUCOSE_SERVICE;
/* initialize timer */
medcGlpCb.racpTimer.handlerId = medcCb.handlerId;
medcGlpCb.racpTimer.msg.event = MEDC_TIMER_IND;
/* initialize sequence number */
GlpcGlsSetLastSeqNum(1);
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcGlpDiscover(dmConnId_t connId)
{
/* discover glucose service */
GlpcGlsDiscover(connId, pMedcGlsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure glucose service */
AppDiscConfigure(connId, status, MEDC_CFG_GLS_LIST_LEN,
(attcDiscCfg_t *) medcCfgGlsList,
GLPC_GLS_HDL_LIST_LEN, pMedcGlsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
/* process value update */
medcGlsValueUpdate((attEvt_t *) pMsg);
break;
case ATTC_WRITE_RSP:
/* if write to RACP was successful, start procedure timer */
if ((((attEvt_t *) pMsg)->hdr.status == ATT_SUCCESS) &&
(((attEvt_t *) pMsg)->handle == pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX]))
{
medcGlpCb.inProgress = TRUE;
medcGlpCb.racpTimer.msg.param = pMsg->param; /* conn ID */
WsfTimerStartSec(&medcGlpCb.racpTimer, ATT_MAX_TRANS_TIMEOUT);
}
break;
case ATTC_HANDLE_VALUE_IND:
/* if procedure in progress stop procedure timer */
if (medcGlpCb.inProgress)
{
medcGlpCb.inProgress = FALSE;
WsfTimerStop(&medcGlpCb.racpTimer);
}
/* process value */
medcGlsValueUpdate((attEvt_t *) pMsg);
break;
case DM_CONN_CLOSE_IND:
/* if procedure in progress stop procedure timer */
if (medcGlpCb.inProgress)
{
medcGlpCb.inProgress = FALSE;
WsfTimerStop(&medcGlpCb.racpTimer);
}
break;
case MEDC_TIMER_IND:
/* if procedure in progress then close connection */
if (medcGlpCb.inProgress && pMsg->param != DM_CONN_ID_NONE)
{
medcGlpCb.inProgress = FALSE;
/* if configured to disconnect upon ATT transaction timeout */
if (pAppCfg->disconnect)
{
AppConnClose((dmConnId_t)pMsg->param);
}
}
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlpBtn(dmConnId_t connId, uint8_t btn)
{
glpcFilter_t filter;
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
/* handle must be set to send RACP command */
if (pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX] == ATT_HANDLE_NONE)
{
return;
}
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* report all records */
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_ALL, NULL);
break;
case APP_UI_BTN_1_MED:
/* report records greater than sequence number */
filter.type = CH_RACP_GLS_FILTER_SEQ;
filter.param.seqNum = GlpcGlsGetLastSeqNum();
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_GTEQ, &filter);
break;
case APP_UI_BTN_2_SHORT:
/* report number of records */
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_ALL, NULL);
break;
case APP_UI_BTN_2_MED:
/* report number of records greater than sequence number */
filter.type = CH_RACP_GLS_FILTER_SEQ;
filter.param.seqNum = GlpcGlsGetLastSeqNum();
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_GTEQ, &filter);
break;
case APP_UI_BTN_2_LONG:
/* abort */
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_ABORT, CH_RACP_OPERATOR_NULL, NULL);
break;
case APP_UI_BTN_2_EX_LONG:
/* delete all records */
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_DELETE, CH_RACP_OPERATOR_ALL, NULL);
break;
default:
break;
}
}
}
@@ -0,0 +1,223 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Heart Rate profile
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "svc_ch.h"
#include "hrpc/hrpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached heart rate service handles; begins after DIS */
#define MEDC_DISC_HRS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_HRS_START + HRPC_HRS_HDL_LIST_LEN)
/*! Pointers into handle list heart rate service handles */
static uint16_t *pMedcHrsHdlList = &medcCb.hdlList[MEDC_DISC_HRS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* HRS Control point "Reset Energy Expended" */
static const uint8_t medcHrsRstEnExp[] = {CH_HRCP_RESET_ENERGY_EXP};
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgHrsList[] =
{
/* Read: HRS Body sensor location */
{NULL, 0, HRPC_HRS_BSL_HDL_IDX},
/* Write: HRS Control point "Reset Energy Expended" */
{medcHrsRstEnExp, sizeof(medcHrsRstEnExp), HRPC_HRS_HRCP_HDL_IDX},
/* Write: HRS Heart rate measurement CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), HRPC_HRS_HRM_CCC_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_HRS_LIST_LEN (sizeof(medcCfgHrsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcHrpInit(void);
static bool_t medcHrpDiscover(dmConnId_t connId);
static void medcHrpConfigure(dmConnId_t connId, uint8_t status);
static void medcHrpProcMsg(wsfMsgHdr_t *pMsg);
static void medcHrpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcHrpIf =
{
medcHrpInit,
medcHrpDiscover,
medcHrpConfigure,
medcHrpProcMsg,
medcHrpBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* heart rate */
if (HrpcHrsValueUpdate(pMedcHrsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
case ATTC_HANDLE_VALUE_IND:
medcHrsValueUpdate((attEvt_t *) pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrpInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_HEART_RATE_SERVICE;
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcHrpDiscover(dmConnId_t connId)
{
/* discover heart rate service */
HrpcHrsDiscover(connId, pMedcHrsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure heart rate service */
AppDiscConfigure(connId, status, MEDC_CFG_HRS_LIST_LEN,
(attcDiscCfg_t *) medcCfgHrsList,
HRPC_HRS_HDL_LIST_LEN, pMedcHrsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrpBtn(dmConnId_t connId, uint8_t btn)
{
return;
}
@@ -0,0 +1,220 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Health Thermometer profile
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "svc_ch.h"
#include "htpc/htpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached health thermometer service handles; begins after DIS */
#define MEDC_DISC_HTS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_HTS_START + HTPC_HTS_HDL_LIST_LEN)
/*! Pointers into handle list for health thermometer service handles */
static uint16_t *pMedcHtsHdlList = &medcCb.hdlList[MEDC_DISC_HTS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgHtsList[] =
{
/* Read: Temperature type */
{NULL, 0, HTPC_HTS_TT_HDL_IDX},
/* Write: Temperature measurement CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), HTPC_HTS_TM_CCC_HDL_IDX},
/* Write: Intermediate temperature CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), HTPC_HTS_IT_CCC_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_HTS_LIST_LEN (sizeof(medcCfgHtsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcHtpInit(void);
static bool_t medcHtpDiscover(dmConnId_t connId);
static void medcHtpConfigure(dmConnId_t connId, uint8_t status);
static void medcHtpProcMsg(wsfMsgHdr_t *pMsg);
static void medcHtpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcHtpIf =
{
medcHtpInit,
medcHtpDiscover,
medcHtpConfigure,
medcHtpProcMsg,
medcHtpBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* health thermometer */
if (HtpcHtsValueUpdate(pMedcHtsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
case ATTC_HANDLE_VALUE_IND:
medcHtsValueUpdate((attEvt_t *) pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtpInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_HEALTH_THERM_SERVICE;
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcHtpDiscover(dmConnId_t connId)
{
/* discover health thermometer service */
HtpcHtsDiscover(connId, pMedcHtsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure health thermometer service */
AppDiscConfigure(connId, status, MEDC_CFG_HTS_LIST_LEN,
(attcDiscCfg_t *) medcCfgHtsList,
HTPC_HTS_HDL_LIST_LEN, pMedcHtsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtpBtn(dmConnId_t connId, uint8_t btn)
{
return;
}
@@ -0,0 +1,940 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector sample application for the following profiles:
* Heart Rate profile collector
* Blood Pressure profile collector
* Glucose profile collector
* Weight scale profile collector
* Health Thermometer profile collector
* Pulse Oximeter profile collector
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "smp_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "gatt/gatt_api.h"
#include "dis/dis_api.h"
#include "medc/medc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
Profile Configuration
**************************************************************************************************/
/* Heart rate profile included */
#ifndef MEDC_HRP_INCLUDED
#define MEDC_HRP_INCLUDED TRUE
#endif
/* Blood pressure profile included */
#ifndef MEDC_BLP_INCLUDED
#define MEDC_BLP_INCLUDED TRUE
#endif
/* Glucose profile included */
#ifndef MEDC_GLP_INCLUDED
#define MEDC_GLP_INCLUDED TRUE
#endif
/* Weight scale profile included */
#ifndef MEDC_WSP_INCLUDED
#define MEDC_WSP_INCLUDED TRUE
#endif
/* Health thermometer profile included */
#ifndef MEDC_HTP_INCLUDED
#define MEDC_HTP_INCLUDED TRUE
#endif
/* Pulse oximeter profile included */
#ifndef MEDC_PLX_INCLUDED
#define MEDC_PLX_INCLUDED TRUE
#endif
/* Default profile to use */
#ifndef MEDC_PROFILE
#define MEDC_PROFILE MEDC_ID_HRP
#endif
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for master */
const appMasterCfg_t medcMasterCfg =
{
96, /*! The scan interval, in 0.625 ms units */
48, /*! The scan window, in 0.625 ms units */
4000, /*! The scan duration in ms */
DM_DISC_MODE_NONE, /*! The GAP discovery mode */
DM_SCAN_TYPE_ACTIVE /*! The scan type (active or passive) */
};
/*! configurable parameters for security */
static const appSecCfg_t medcSecCfg =
{
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
0, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
FALSE /*! TRUE to initiate security upon connection */
};
/*! SMP security parameter configuration */
static const smpCfg_t medcSmpCfg =
{
500, /*! 'Repeated attempts' timeout in msec */
SMP_IO_DISP_ONLY, /*! I/O Capability */
7, /*! Minimum encryption key length */
16, /*! Maximum encryption key length */
1, /*! Attempts to trigger 'repeated attempts' timeout */
0, /*! Device authentication requirements */
64000, /*! Maximum repeated attempts timeout in msec */
64000, /*! Time msec before attemptExp decreases */
2 /*! Repeated attempts multiplier exponent */
};
/*! Connection parameters */
static const hciConnSpec_t medcConnCfg =
{
40, /*! Minimum connection interval in 1.25ms units */
40, /*! Maximum connection interval in 1.25ms units */
0, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
0, /*! Unused */
0 /*! Unused */
};
/*! Configurable parameters for service and characteristic discovery */
static const appDiscCfg_t medcDiscCfg =
{
FALSE, /*! TRUE to wait for a secure connection before initiating discovery */
FALSE /*! TRUE to fall back on database hash to verify handles when no bond exists. */
};
static const appCfg_t medcAppCfg =
{
TRUE, /*! TRUE to abort service discovery if service not found */
TRUE /*! TRUE to disconnect if ATT transaction times out */
};
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/*! Discovery states: enumeration of services to be discovered */
enum
{
MEDC_DISC_GATT_SVC, /*! GATT service */
MEDC_DISC_DIS_SVC, /*! Device Information service */
MEDC_DISC_MED_SVC, /*! Configured med. service */
MEDC_DISC_SVC_MAX /*! Discovery complete */
};
/*! Pointers into handle list for each service's handles */
uint16_t *pMedcGattHdlList = &medcCb.hdlList[MEDC_DISC_GATT_START];
uint16_t *pMedcDisHdlList = &medcCb.hdlList[MEDC_DISC_DIS_START];
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/*! Configuration states: enumeration of services to be discovered */
enum
{
MEDC_CFG_DIS_SVC, /*! DIS services */
MEDC_CFG_GATT_SVC, /*! GATT services */
MEDC_CFG_MED_SVC, /*! Configured med. service */
MEDC_CFG_SVC_MAX /*! Configuration complete */
};
/*
* Data for configuration after service discovery
*/
/* Default value for CCC indications */
const uint8_t medcCccIndVal[2] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_INDICATE)};
/* Default value for CCC notifications */
const uint8_t medcCccNtfVal[2] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_NOTIFY)};
/* Default value for Client Supported Features (enable Robust Caching) */
const uint8_t medcCsfVal[1] = {ATTS_CSF_ROBUST_CACHING};
/* HRS Control point "Reset Energy Expended" */
/* static const uint8_t medcHrsRstEnExp[] = {CH_HRCP_RESET_ENERGY_EXP}; */\
/* List of DIS characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgDisList[] =
{
/* Read: DIS Manufacturer name string */
{NULL, 0, DIS_MFNS_HDL_IDX},
/* Read: DIS Model number string */
{NULL, 0, DIS_MNS_HDL_IDX},
/* Read: DIS Serial number string */
{NULL, 0, DIS_SNS_HDL_IDX},
/* Read: DIS Hardware revision string */
{NULL, 0, DIS_HRS_HDL_IDX},
/* Read: DIS Firmware revision string */
{NULL, 0, DIS_FRS_HDL_IDX},
/* Read: DIS Software revision string */
{NULL, 0, DIS_SRS_HDL_IDX},
/* Read: DIS System ID */
{NULL, 0, DIS_SID_HDL_IDX}
};
/* List of GATT characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgGattList[] =
{
/* Write: GATT service changed ccc descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), GATT_SC_CCC_HDL_IDX},
/* Write: GATT client supported features */
{medcCsfVal, sizeof(medcCsfVal), GATT_CSF_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_GATT_LIST_LEN (sizeof(medcCfgGattList) / sizeof(attcDiscCfg_t))
#define MEDC_CFG_DIS_LIST_LEN (sizeof(medcCfgDisList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! application control block */
medcCb_t medcCb;
/*! connection control block */
typedef struct {
appDbHdl_t dbHdl; /*! Device database record handle type */
uint8_t addrType; /*! Type of address of device to connect to */
bdAddr_t addr; /*! Address of device to connect to */
bool_t doConnect; /*! TRUE to issue connect on scan complete */
} medcConnInfo_t;
medcConnInfo_t medcConnInfo;
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void medcDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
uint16_t reportLen;
len = DmSizeOfEvt(pDmEvt);
if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND)
{
reportLen = pDmEvt->scanReport.len;
}
else
{
reportLen = 0;
}
if ((pMsg = WsfMsgAlloc(len + reportLen)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND)
{
pMsg->scanReport.pData = (uint8_t *) ((uint8_t *) pMsg + len);
memcpy(pMsg->scanReport.pData, pDmEvt->scanReport.pData, reportLen);
}
WsfMsgSend(medcCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void medcAttCback(attEvt_t *pEvt)
{
attEvt_t *pMsg;
if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attEvt_t));
pMsg->pValue = (uint8_t *) (pMsg + 1);
memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen);
WsfMsgSend(medcCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Perform actions on scan start.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcScanStart(dmEvt_t *pMsg)
{
if (pMsg->hdr.status == HCI_SUCCESS)
{
medcCb.scanning = TRUE;
}
}
/*************************************************************************************************/
/*!
* \brief Perform actions on scan stop.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcScanStop(dmEvt_t *pMsg)
{
if (pMsg->hdr.status == HCI_SUCCESS)
{
medcCb.scanning = FALSE;
medcCb.autoConnect = FALSE;
/* Open connection */
if (medcConnInfo.doConnect)
{
APP_TRACE_INFO0("medcScanStop: Opening Connection");
AppConnOpen(medcConnInfo.addrType, medcConnInfo.addr, medcConnInfo.dbHdl);
medcConnInfo.doConnect = FALSE;
}
}
}
/*************************************************************************************************/
/*!
* \brief Handle a scan report.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcScanReport(dmEvt_t *pMsg)
{
uint8_t *pData;
uint8_t len;
appDbHdl_t dbHdl;
bool_t connect = FALSE;
/* disregard if not scanning or autoconnecting */
if (!medcCb.scanning || !medcCb.autoConnect)
{
return;
}
/* if we already have a bond with this device then connect to it */
if ((dbHdl = AppDbFindByAddr(pMsg->scanReport.addrType, pMsg->scanReport.addr)) != APP_DB_HDL_NONE)
{
connect = TRUE;
}
/* otherwise look for desired service in advertising data */
else
{
/* find Service UUID list; if full list not found search for partial */
if ((pData = DmFindAdType(DM_ADV_TYPE_16_UUID, pMsg->scanReport.len,
pMsg->scanReport.pData)) == NULL)
{
pData = DmFindAdType(DM_ADV_TYPE_16_UUID_PART, pMsg->scanReport.len,
pMsg->scanReport.pData);
}
/* if found and length checks out ok */
if (pData != NULL && pData[DM_AD_LEN_IDX] >= (ATT_16_UUID_LEN + 1))
{
len = pData[DM_AD_LEN_IDX] - 1;
pData += DM_AD_DATA_IDX;
while ((!connect) && (len >= ATT_16_UUID_LEN))
{
int8_t i;
for (i=0; i < MEDC_MAX_AUTO_UUID; i++)
{
if (medcCb.autoUuid[i] == 0)
{
continue;
}
if (BYTES_UINT16_CMP(pData, medcCb.autoUuid[i]))
{
connect = TRUE;
break;
}
}
pData += ATT_16_UUID_LEN;
len -= ATT_16_UUID_LEN;
}
}
}
if (connect)
{
APP_TRACE_INFO0("medcScanReport: Peer found.");
/* stop scanning and connect */
medcCb.autoConnect = FALSE;
AppScanStop();
/* Store peer information for connect on scan stop */
medcConnInfo.addrType = pMsg->scanReport.addrType;
memcpy(medcConnInfo.addr, pMsg->scanReport.addr, sizeof(bdAddr_t));
medcConnInfo.dbHdl = dbHdl;
medcConnInfo.doConnect = TRUE;
}
}
/*************************************************************************************************/
/*!
* \brief Perform UI actions on connection open.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcOpen(dmEvt_t *pMsg)
{
}
/*************************************************************************************************/
/*!
* \brief Set up procedures that need to be performed after device reset.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcSetup(dmEvt_t *pMsg)
{
medcCb.scanning = FALSE;
medcCb.autoConnect = FALSE;
medcConnInfo.doConnect = FALSE;
DmConnSetConnSpec((hciConnSpec_t *) &medcConnCfg);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBtnCback(uint8_t btn)
{
dmConnId_t connId;
/* button actions when connected */
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_LONG:
/* disconnect */
AppConnClose(connId);
break;
default:
/* all other button presses-- send to profile */
medcCb.pIf->btn(connId, btn);
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* if scanning cancel scanning */
if (medcCb.scanning)
{
AppScanStop();
}
/* else auto connect */
else if (!medcCb.autoConnect)
{
medcCb.autoConnect = TRUE;
medcConnInfo.doConnect = FALSE;
AppScanStart(medcMasterCfg.discMode, medcMasterCfg.scanType,
medcMasterCfg.scanDuration);
}
break;
case APP_UI_BTN_1_LONG:
/* clear all bonding info */
AppClearAllBondingInfo();
break;
default:
/* all other button presses-- send to profile */
medcCb.pIf->btn(connId, btn);
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Discovery callback.
*
* \param connId Connection identifier.
* \param status Service or configuration status.
*
* \return None.
*/
/*************************************************************************************************/
static void medcDiscCback(dmConnId_t connId, uint8_t status)
{
switch(status)
{
case APP_DISC_INIT:
/* set handle list when initialization requested */
AppDiscSetHdlList(connId, medcCb.hdlListLen, medcCb.hdlList);
break;
case APP_DISC_READ_DATABASE_HASH:
/* Read peer's database hash */
AppDiscReadDatabaseHash(connId);
break;
case APP_DISC_SEC_REQUIRED:
/* initiate security */
AppMasterSecurityReq(connId);
break;
case APP_DISC_START:
/* initialize discovery state */
medcCb.discState = MEDC_DISC_GATT_SVC;
/* discover GATT service */
GattDiscover(connId, pMedcGattHdlList);
break;
case APP_DISC_FAILED:
if (pAppCfg->abortDisc)
{
/* if discovery failed for desired service then disconnect */
if (medcCb.discState == MEDC_DISC_MED_SVC)
{
AppConnClose(connId);
break;
}
}
/* Else falls through. */
case APP_DISC_CMPL:
/* next discovery state */
medcCb.discState++;
if (medcCb.discState == MEDC_DISC_DIS_SVC)
{
/* discover device information service */
DisDiscover(connId, pMedcDisHdlList);
}
else if (medcCb.discState == MEDC_DISC_MED_SVC)
{
/* discover med profile service */
if (medcCb.pIf->discover(connId) == FALSE)
{
/* There are other profiles to discover. Stay in the MEDC_DISC_MED_SVC state */
medcCb.discState = MEDC_DISC_MED_SVC - 1;
}
}
else
{
/* discovery complete */
AppDiscComplete(connId, APP_DISC_CMPL);
/* start configuration: configure DIS service */
medcCb.cfgState = MEDC_CFG_DIS_SVC;
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_DIS_LIST_LEN,
(attcDiscCfg_t *) medcCfgDisList,
DIS_HDL_LIST_LEN, pMedcDisHdlList);
}
break;
case APP_DISC_CFG_START:
/* start configuration: configure DIS service */
medcCb.cfgState = MEDC_CFG_DIS_SVC;
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_DIS_LIST_LEN,
(attcDiscCfg_t *) medcCfgDisList,
DIS_HDL_LIST_LEN, pMedcDisHdlList);
break;
case APP_DISC_CFG_CMPL:
/* next configuration state */
medcCb.cfgState++;
if (medcCb.cfgState == MEDC_CFG_GATT_SVC)
{
/* configure GATT */
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_GATT_LIST_LEN,
(attcDiscCfg_t *) medcCfgGattList,
GATT_HDL_LIST_LEN, pMedcGattHdlList);
}
else if (medcCb.cfgState == MEDC_CFG_MED_SVC)
{
/* configure med profile service */
medcCb.pIf->configure(connId, APP_DISC_CFG_START);
}
else
{
AppDiscComplete(connId, status);
}
break;
case APP_DISC_CFG_CONN_START:
/* no connection setup configuration */
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcProcMsg(dmEvt_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch(pMsg->hdr.event)
{
case ATTC_READ_RSP:
case ATTC_WRITE_RSP:
case ATTC_HANDLE_VALUE_NTF:
case ATTC_HANDLE_VALUE_IND:
case MEDC_TIMER_IND:
medcCb.pIf->procMsg(&pMsg->hdr);
break;
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
medcSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_SCAN_START_IND:
medcScanStart(pMsg);
uiEvent = APP_UI_SCAN_START;
break;
case DM_SCAN_STOP_IND:
medcScanStop(pMsg);
uiEvent = APP_UI_SCAN_STOP;
break;
case DM_SCAN_REPORT_IND:
medcScanReport(pMsg);
break;
case DM_CONN_OPEN_IND:
medcOpen(pMsg);
uiEvent = APP_UI_CONN_OPEN;
break;
case DM_CONN_CLOSE_IND:
medcCb.pIf->procMsg(&pMsg->hdr);
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
uiEvent = APP_UI_SEC_PAIR_CMPL;
break;
case DM_SEC_PAIR_FAIL_IND:
uiEvent = APP_UI_SEC_PAIR_FAIL;
break;
case DM_SEC_ENCRYPT_IND:
medcCb.pIf->procMsg(&pMsg->hdr);
uiEvent = APP_UI_SEC_ENCRYPT;
break;
case DM_SEC_ENCRYPT_FAIL_IND:
medcCb.pIf->procMsg(&pMsg->hdr);
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
break;
case DM_SEC_AUTH_REQ_IND:
AppHandlePasskey(&pMsg->authReq);
break;
case DM_PRIV_CLEAR_RES_LIST_IND:
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
break;
case DM_VENDOR_SPEC_CMD_CMPL_IND:
{
#if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)
uint8_t *param_ptr = &pMsg->vendorSpecCmdCmpl.param[0];
switch (pMsg->vendorSpecCmdCmpl.opcode)
{
case 0xFC20: //read at address
{
uint32_t read_value;
BSTREAM_TO_UINT32(read_value, param_ptr);
APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status,
read_value);
}
break;
default:
APP_TRACE_INFO2("VSC 0x%0x complete status %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status);
break;
}
#endif
}
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void MedcHandlerInit(wsfHandlerId_t handlerId)
{
APP_TRACE_INFO0("MedcHandlerInit");
/* store handler ID */
medcCb.handlerId = handlerId;
/* Set configuration pointers */
pAppMasterCfg = (appMasterCfg_t *) &medcMasterCfg;
pAppSecCfg = (appSecCfg_t *) &medcSecCfg;
pAppDiscCfg = (appDiscCfg_t *) &medcDiscCfg;
pAppCfg = (appCfg_t *) &medcAppCfg;
/* Set stack configuration pointers */
pSmpCfg = (smpCfg_t *) &medcSmpCfg;
/* Initialize application framework */
AppMasterInit();
AppDiscInit();
/* Set default profile to use */
MedcSetProfile(MEDC_PROFILE);
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void MedcHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("Medc got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event <= ATT_CBACK_END)
{
/* process discovery-related ATT messages */
AppDiscProcAttMsg((attEvt_t *) pMsg);
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppMasterProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppMasterSecProcDmMsg((dmEvt_t *) pMsg);
/* process discovery-related messages */
AppDiscProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
medcProcMsg((dmEvt_t *) pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void MedcStart(void)
{
/* Register for stack callbacks */
DmRegister(medcDmCback);
DmConnRegister(DM_CLIENT_ID_APP, medcDmCback);
AttRegister(medcAttCback);
/* Register for app framework button callbacks */
AppUiBtnRegister(medcBtnCback);
/* Initialize attribute server database */
SvcCoreAddGroup();
/* Register for app framework discovery callbacks */
AppDiscRegister(medcDiscCback);
/* Reset the device */
DmDevReset();
}
/*************************************************************************************************/
/*!
* \brief Set the profile to be used by the application. This function is called internally
* by MedcHandlerInit() with a default value. It may also be called by the system
* to configure the profile after executing MedcHandlerInit() and before executing
* MedcStart().
*
* \param profile Profile identifier.
*
* \return None.
*/
/*************************************************************************************************/
void MedcSetProfile(uint8_t profile)
{
switch (profile)
{
#if MEDC_HRP_INCLUDED == TRUE
case MEDC_ID_HRP:
medcCb.pIf = &medcHrpIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_BLP_INCLUDED == TRUE
case MEDC_ID_BLP:
medcCb.pIf = &medcBlpIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_GLP_INCLUDED == TRUE
case MEDC_ID_GLP:
medcCb.pIf = &medcGlpIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_WSP_INCLUDED == TRUE
case MEDC_ID_WSP:
medcCb.pIf = &medcWspIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_HTP_INCLUDED == TRUE
case MEDC_ID_HTP:
medcCb.pIf = &medcHtpIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_PLX_INCLUDED == TRUE
case MEDC_ID_PLX:
medcCb.pIf = &medcPlxpIf;
medcCb.pIf->init();
break;
#endif
default:
APP_TRACE_WARN1("MedcSetProfile invalid profile:%d", profile);
break;
}
}
@@ -0,0 +1,138 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector sample application interface file.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef MEDC_MAIN_H
#define MEDC_MAIN_H
#include "gatt/gatt_api.h"
#include "dis/dis_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! the Client handle list, medcCb.hdlList[], is set as follows:
*
* ------------------------------- <- MEDC_DISC_GATT_START
* | GATT handles |
* |... |
* ------------------------------- <- MEDC_DISC_DIS_START
* | DIS handles |
* | ... |
* ------------------------------- <- Configured med service start
* | Med service handles |
* | ... |
* -------------------------------
*/
/*! Maximum number of Service UUID for autoconnect */
#define MEDC_MAX_AUTO_UUID 2
/*! Start of each service's handles in the the handle list */
#define MEDC_DISC_GATT_START 0
#define MEDC_DISC_DIS_START (MEDC_DISC_GATT_START + GATT_HDL_LIST_LEN)
/*! WSF message event starting value */
#define MEDC_MSG_START 0xA0
/*! WSF message event enumeration */
enum
{
MEDC_TIMER_IND = MEDC_MSG_START, /*! Timer expired */
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! profile interface callback functions */
typedef void (*medcInitCback_t)(void);
typedef bool_t (*medcDiscoverCback_t)(dmConnId_t connId);
typedef void (*medcConfigureCback_t)(dmConnId_t connId, uint8_t status);
typedef void (*medcProcMsgCback_t)(wsfMsgHdr_t *pMsg);
typedef void (*medcBtnCback_t)(dmConnId_t connId, uint8_t btn);
/*! profile interface structure */
typedef struct
{
medcInitCback_t init;
medcDiscoverCback_t discover;
medcConfigureCback_t configure;
medcProcMsgCback_t procMsg;
medcBtnCback_t btn;
} medcIf_t;
/*! application control block */
typedef struct
{
uint16_t hdlList[APP_DB_HDL_LIST_LEN]; /*! Cached handle list */
medcIf_t *pIf; /*! Profile interface */
wsfHandlerId_t handlerId; /*! WSF hander ID */
uint16_t autoUuid[MEDC_MAX_AUTO_UUID]; /*! Service UUID for autoconnect */
bool_t scanning; /*! TRUE if scanning */
bool_t autoConnect; /*! TRUE if auto-connecting */
uint8_t hdlListLen; /*! Cached handle list length */
uint8_t discState; /*! Service discovery state */
uint8_t cfgState; /*! Service configuration state */
} medcCb_t;
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! Default value for CCC indications and notifications */
extern const uint8_t medcCccIndVal[2];
/*! Default value for CCC notifications */
extern const uint8_t medcCccNtfVal[2];
/*! Pointers into handle list for GATT and DIS service handles */
extern uint16_t *pMedcGattHdlList;
extern uint16_t *pMedcDisHdlList;
/*! application control block */
extern medcCb_t medcCb;
/*! profile interface pointers */
extern medcIf_t medcHrpIf; /* heart rate profile */
extern medcIf_t medcBlpIf; /* blood pressure profile */
extern medcIf_t medcGlpIf; /* glucose profile */
extern medcIf_t medcWspIf; /* weight scale profile */
extern medcIf_t medcHtpIf; /* health thermometer profile */
extern medcIf_t medcPlxpIf; /* pulse oximeter profile */
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
#ifdef __cplusplus
};
#endif
#endif /* MEDC_MAIN_H */
@@ -0,0 +1,381 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Pulse Oximeter profile
*
* Copyright (c) 2016-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_main.h"
#include "svc_ch.h"
#include "plxpc/plxpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached health pulse oximeter service handles; begins after DIS */
#define MEDC_DISC_PLXS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_PLXS_START + PLXPC_PLXS_HDL_LIST_LEN)
/*! Pointers into handle list for health pulse oximeter service handles */
static uint16_t *pMedcPlxsHdlList = &medcCb.hdlList[MEDC_DISC_PLXS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/* Default MTU */
#define MEDC_PLX_DEFAULT_MTU 50
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! ATT configurable parameters (increase MTU) */
static const attCfg_t medcPlxpAttCfg =
{
15, /* ATT server service discovery connection idle timeout in seconds */
MEDC_PLX_DEFAULT_MTU, /* desired ATT MTU */
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
4 /* number of queued prepare writes supported by server */
};
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgPlxsList[] =
{
/* Read: Features */
{NULL, 0, PLXPC_PLXS_PLXF_HDL_IDX},
/* Write: Spot Check CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), PLXPC_PLXS_PLXSC_CCC_HDL_IDX},
/* Write: Continuous Measurement CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), PLXPC_PLXS_PLXC_CCC_HDL_IDX},
/* Write: Record Access Control Point CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), PLXPC_PLXS_RACP_CCC_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_PLXS_LIST_LEN (sizeof(medcCfgPlxsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
wsfTimer_t racpTimer; /*! RACP procedure timer */
bool_t inProgress; /*! RACP procedure in progress */
bool_t restoreConnection; /*! Indicates if a dropped connection should be restored */
} medcPlxCb;
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcPlxpInit(void);
static bool_t medcPlxpDiscover(dmConnId_t connId);
static void medcPlxpConfigure(dmConnId_t connId, uint8_t status);
static void medcPlxpProcMsg(wsfMsgHdr_t *pMsg);
static void medcPlxpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcPlxpIf =
{
medcPlxpInit,
medcPlxpDiscover,
medcPlxpConfigure,
medcPlxpProcMsg,
medcPlxpBtn
};
/**************************************************************************************************
External Variables
**************************************************************************************************/
/*! MEDC application master configuration */
extern const appMasterCfg_t medcMasterCfg;
/*! App connection control block */
extern appConnCb_t appConnCb[DM_CONN_MAX];
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* pulse oximeter */
if (PlxpcPlxsValueUpdate(pMedcPlxsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxpProcMsg(wsfMsgHdr_t *pMsg)
{
appConnCb_t *pCb;
switch(pMsg->event)
{
case DM_CONN_CLOSE_IND:
/* if procedure in progress stop procedure timer */
if (medcPlxCb.inProgress)
{
medcPlxCb.inProgress = FALSE;
WsfTimerStop(&medcPlxCb.racpTimer);
}
if (medcPlxCb.restoreConnection == TRUE)
{
medcCb.autoConnect = TRUE;
AppScanStart(medcMasterCfg.discMode, medcMasterCfg.scanType,
medcMasterCfg.scanDuration);
}
break;
case DM_SEC_ENCRYPT_IND:
if (medcPlxCb.restoreConnection == TRUE)
{
AppDiscConfigure((dmConnId_t) pMsg->param, HCI_SUCCESS, MEDC_CFG_PLXS_LIST_LEN,
(attcDiscCfg_t *) medcCfgPlxsList,
PLXPC_PLXS_HDL_LIST_LEN, pMedcPlxsHdlList);
medcPlxCb.restoreConnection = FALSE;
}
break;
case DM_SEC_ENCRYPT_FAIL_IND:
if (medcPlxCb.restoreConnection == TRUE)
{
/* look up app connection control block from DM connection ID */
pCb = &appConnCb[pMsg->param - 1];
pCb->initiatingSec = FALSE;
/* if database record handle valid */
if (pCb->dbHdl != APP_DB_HDL_NONE)
{
AppDbDeleteRecord(pCb->dbHdl);
pCb->dbHdl = APP_DB_HDL_NONE;
}
AppMasterSecurityReq((dmConnId_t) pMsg->param);
}
break;
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
medcPlxsValueUpdate((attEvt_t *) pMsg);
break;
case ATTC_WRITE_RSP:
/* if write to RACP was successful, start procedure timer */
if ((((attEvt_t *) pMsg)->hdr.status == ATT_SUCCESS) &&
(((attEvt_t *) pMsg)->handle == pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX]))
{
medcPlxCb.inProgress = TRUE;
medcPlxCb.racpTimer.msg.param = pMsg->param; /* conn ID */
WsfTimerStartSec(&medcPlxCb.racpTimer, ATT_MAX_TRANS_TIMEOUT);
}
break;
case ATTC_HANDLE_VALUE_IND:
/* if procedure in progress stop procedure timer */
if (medcPlxCb.inProgress)
{
medcPlxCb.inProgress = FALSE;
WsfTimerStop(&medcPlxCb.racpTimer);
}
medcPlxsValueUpdate((attEvt_t *) pMsg);
break;
case MEDC_TIMER_IND:
/* if procedure in progress then close connection */
if (medcPlxCb.inProgress && pMsg->param != DM_CONN_ID_NONE)
{
medcPlxCb.inProgress = FALSE;
/* if configured to disconnect upon ATT transaction timeout */
if (pAppCfg->disconnect)
{
AppConnClose((dmConnId_t)pMsg->param);
}
}
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxpInit(void)
{
/* Set configuration pointers */
pAttCfg = (attCfg_t *) &medcPlxpAttCfg;
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_PULSE_OXIMITER_SERVICE;
/* initialize timer */
medcPlxCb.racpTimer.handlerId = medcCb.handlerId;
medcPlxCb.racpTimer.msg.event = MEDC_TIMER_IND;
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcPlxpDiscover(dmConnId_t connId)
{
/* discover health pulse oximeter service */
PlxpcPlxsDiscover(connId, pMedcPlxsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure health pulse oximeter service */
AppDiscConfigure(connId, status, MEDC_CFG_PLXS_LIST_LEN,
(attcDiscCfg_t *) medcCfgPlxsList,
PLXPC_PLXS_HDL_LIST_LEN, pMedcPlxsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxpBtn(dmConnId_t connId, uint8_t btn)
{
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_MED:
medcPlxCb.restoreConnection = TRUE;
AppConnClose(connId);
break;
case APP_UI_BTN_2_SHORT:
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_DELETE, CH_RACP_OPERATOR_ALL);
break;
case APP_UI_BTN_2_MED:
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_ALL);
break;
case APP_UI_BTN_2_LONG:
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_ALL);
break;
case APP_UI_BTN_2_EX_LONG:
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_ABORT, CH_RACP_OPERATOR_ALL);
break;
}
}
}
@@ -0,0 +1,315 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Weight Scale profile
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "wspc/wspc_api.h"
#include "udsc/udsc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/*! Discovery states: enumeration of services to be discovered */
enum
{
MEDC_DISC_WSP_UDS_SVC, /*! User data service */
MEDC_DISC_WSP_WSS_SVC, /*! Weight scale service */
};
/* Start of cached weight scale service handles; begins after DIS */
#define MEDC_DISC_WSS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Start of User Data Service handles; begins after weight scale serivce */
#define MEDC_DISC_UDS_START (MEDC_DISC_WSS_START + WSPC_WSS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_UDS_START + UDSC_HDL_LIST_LEN)
/*! Pointers into handle list for weight scale service handles */
static uint16_t *pMedcWssHdlList = &medcCb.hdlList[MEDC_DISC_WSS_START];
/*! Pointers into handle list for weight scale service handles */
static uint16_t *pMedcUdsHdlList = &medcCb.hdlList[MEDC_DISC_UDS_START];
/*! Weight Scale Profile discovery state */
static uint8_t pMedcWspDiscState;
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgWssList[] =
{
/* Read: Weight scale feature */
{NULL, 0, MEDC_DISC_WSS_START + WSPC_WSS_WSF_HDL_IDX},
/* Write: Weight scale measurement CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), MEDC_DISC_WSS_START + WSPC_WSS_WSM_CCC_HDL_IDX},
/* Write: Database Change Interval CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), MEDC_DISC_UDS_START + UDSC_DCBI_CCC_HDL_IDX},
/* Write: User Data Service Control Point CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), MEDC_DISC_UDS_START + UDSC_UCP_CCC_HDL_IDX}
};
/* Characteristic configuration list length */
#define MEDC_CFG_WSS_LIST_LEN (sizeof(medcCfgWssList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcWspInit(void);
static bool_t medcWspDiscover(dmConnId_t connId);
static void medcWspConfigure(dmConnId_t connId, uint8_t status);
static void medcWspProcMsg(wsfMsgHdr_t *pMsg);
static void medcWspBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcWspIf =
{
medcWspInit,
medcWspDiscover,
medcWspConfigure,
medcWspProcMsg,
medcWspBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWssValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* weight scale */
if (WspcWssValueUpdate(pMedcWssHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* user data service */
if (UdscValueUpdate(pMedcUdsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWspProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case DM_CONN_CLOSE_IND:
/* initialize discovery state */
pMedcWspDiscState = MEDC_DISC_WSP_UDS_SVC;
/* Notify UDSC the connection closed */
UdscClose();
break;
case ATTC_HANDLE_VALUE_IND:
medcWssValueUpdate((attEvt_t *) pMsg);
break;
case MEDC_TIMER_IND:
APP_TRACE_INFO0("medcWspProcMsg - Timeout waiting for UDS control point response.");
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief UDS Control Point Response Callback.
*
* \param connId Connection ID.
* \param opcode Cmd opcode being responded to.
* \param response Response code.
* \param index USer index (only set when opcode is UDSC_UCP_OPCODE_RNU)
*
* \return None
*/
/*************************************************************************************************/
static void medcWspUdsRspCallback(dmConnId_t connId, uint8_t opcode, uint8_t response, uint8_t index)
{
APP_TRACE_INFO3("medcWspUdsRspCallback - op: %d rsp: %d index: %d", opcode, response, index);
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWspInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_WEIGHT_SCALE_SERVICE;
medcCb.autoUuid[1] = ATT_UUID_USER_DATA_SERVICE;
/* Register the UDS Control Point Callback */
UdscInit(medcCb.handlerId, MEDC_TIMER_IND, medcWspUdsRspCallback);
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcWspDiscover(dmConnId_t connId)
{
if (pMedcWspDiscState == MEDC_DISC_WSP_UDS_SVC)
{
/* user data service */
UdscDiscover(connId, pMedcUdsHdlList);
pMedcWspDiscState++;
return FALSE;
}
if (pMedcWspDiscState == MEDC_DISC_WSP_WSS_SVC)
{
/* discover weight scale service */
WspcWssDiscover(connId, pMedcWssHdlList);
pMedcWspDiscState++;
}
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWspConfigure(dmConnId_t connId, uint8_t status)
{
/* configure weight scale service */
AppDiscConfigure(connId, status, MEDC_CFG_WSS_LIST_LEN,
(attcDiscCfg_t *) medcCfgWssList,
WSPC_WSS_HDL_LIST_LEN, medcCb.hdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWspBtn(dmConnId_t connId, uint8_t btn)
{
switch (btn)
{
case APP_UI_BTN_2_SHORT:
UdscRegisterNewUser(connId, pMedcUdsHdlList[UDSC_UCP_IDX], 1234);
break;
case APP_UI_BTN_2_MED:
UdscConsent(connId, pMedcUdsHdlList[UDSC_UCP_IDX], 2, 1234);
break;
case APP_UI_BTN_2_LONG:
UdscDeleteUserData(connId, pMedcUdsHdlList[UDSC_UCP_IDX]);
break;
case APP_UI_BTN_2_EX_LONG:
UdscDeleteUserData(connId, pMedcUdsHdlList[UDSC_UCP_IDX]);
break;
case APP_UI_BTN_1_SHORT:
UdscReadDatabaseChangeIncrement(connId, pMedcUdsHdlList[UDSC_DBCI_HDL_IDX]);
break;
case APP_UI_BTN_1_MED:
UdscReadUserIndex(connId, pMedcUdsHdlList[UDSC_UI_HDL_IDX]);
break;
case APP_UI_BTN_1_EX_LONG:
UdscWriteDatabaseChangeIncrement(connId, pMedcUdsHdlList[UDSC_DBCI_HDL_IDX], 1);
break;
default:
break;
}
}