Part Number: CC2640R2F
i am using simple central and project zero as base , i have two launchpad . i am using custom 128bits UUID , after successful connection i am unable to gatt Read / write data .
i have done appropriate changes in the both project for custom 128bits . i have attched simple gatt profile .c file , where i have made few changes .
/******************************************************************************
@file simple_gatt_profile.c
@brief This file contains the Simple GATT profile sample GATT service profile
for use with the BLE sample application.
Group: WCS, BTS
Target Device: cc2640r2
******************************************************************************
Copyright (c) 2010-2021, Texas Instruments Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form 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.
* Neither the name of Texas Instruments Incorporated nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
******************************************************************************
*****************************************************************************/
/*********************************************************************
* INCLUDES
*/
#include <string.h>
#include <icall.h>
#include "util.h"
/* This Header file contains all BLE API and icall structure definition */
#include "icall_ble_api.h"
#include "simple_gatt_profile.h"
/*********************************************************************
* MACROS
*/
/*********************************************************************
* CONSTANTS
*/
#define SERVAPP_NUM_ATTR_SUPPORTED 17
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* GLOBAL VARIABLES
*/
//TODO : Change Macro
#define FMCU_CUSTOM
#ifdef FMCU_CUSTOM
*************************************************************/
// Simple GATT Profile Service UUID: 0xFFF0
CONST uint8 simpleProfileServUUID[ATT_UUID_SIZE] =
{
SIMPLEPROFILE_SERV_UUID_BASE128(SIMPLEPROFILE_SERV_UUID)
};
#else /********** NORMAL **************/
// Simple GATT Profile Service UUID: 0xFFF0
CONST uint8 simpleProfileServUUID[ATT_BT_UUID_SIZE] =
{
LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID)
};
#endif
/*********************************************************************
* EXTERNAL VARIABLES
*/
/*********************************************************************
* EXTERNAL FUNCTIONS
*/
/*********************************************************************
* LOCAL VARIABLES
*/
static simpleProfileCBs_t *simpleProfile_AppCBs = NULL;
/*********************************************************************
* Profile Attributes - variables
*/
// Simple Profile Service attribute
#ifdef FMCU_CUSTOM
static CONST gattAttrType_t simpleProfileService = { ATT_UUID_SIZE, simpleProfileServUUID };
#else
static CONST gattAttrType_t simpleProfileService = { ATT_BT_UUID_SIZE, simpleProfileServUUID };
#endif
// Simple Profile Characteristic 1 Properties
static uint8 simpleProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 1 Value
static uint8 simpleProfileChar1 = 0;
// Simple Profile Characteristic 1 User Description
static uint8 simpleProfileChar1UserDesp[17] = "Characteristic 1";
/*********************************************************************
* Profile Attributes - Table
*/
#ifdef FMCU_CUSTOM
static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =
{
// Simple Profile Service
{
{ ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
GATT_PERMIT_READ, /* permissions */
0, /* handle */
(uint8 *)&simpleProfileService /* pValue */
},
// Characteristic 1 Declaration
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&simpleProfileChar1Props
},
// Characteristic Value 1
{
{ ATT_BT_UUID_SIZE, simpleProfilechar1UUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
&simpleProfileChar1
},
// Characteristic 1 User Description
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
simpleProfileChar1UserDesp
},
};
#else /************Working *************/
static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =
{
// Simple Profile Service
{
{ ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
GATT_PERMIT_READ, /* permissions */
0, /* handle */
(uint8 *)&simpleProfileService /* pValue */
},
// Characteristic 1 Declaration
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&simpleProfileChar1Props
},
// Characteristic Value 1
{
{ ATT_BT_UUID_SIZE, simpleProfilechar1UUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
&simpleProfileChar1
},
// Characteristic 1 User Description
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
simpleProfileChar1UserDesp
},
};
#endif
/*********************************************************************
* LOCAL FUNCTIONS
*/
static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle,
gattAttribute_t *pAttr,
uint8_t *pValue, uint16_t *pLen,
uint16_t offset, uint16_t maxLen,
uint8_t method);
static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle,
gattAttribute_t *pAttr,
uint8_t *pValue, uint16_t len,
uint16_t offset, uint8_t method);
/*********************************************************************
* PROFILE CALLBACKS
*/
// Simple Profile Service Callbacks
// Note: When an operation on a characteristic requires authorization and
// pfnAuthorizeAttrCB is not defined for that characteristic's service, the
// Stack will report a status of ATT_ERR_UNLIKELY to the client. When an
// operation on a characteristic requires authorization the Stack will call
// pfnAuthorizeAttrCB to check a client's authorization prior to calling
// pfnReadAttrCB or pfnWriteAttrCB, so no checks for authorization need to be
// made within these functions.
CONST gattServiceCBs_t simpleProfileCBs =
{
simpleProfile_ReadAttrCB, // Read callback function pointer
simpleProfile_WriteAttrCB, // Write callback function pointer
NULL // Authorization callback function pointer
};
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/*********************************************************************
* @fn SimpleProfile_AddService
*
* @brief Initializes the Simple Profile service by registering
* GATT attributes with the GATT server.
*
* @param services - services to add. This is a bit map and can
* contain more than one service.
*
* @return Success or Failure
*/
bStatus_t SimpleProfile_AddService( uint32 services )
{
uint8 status;
// Allocate Client Characteristic Configuration table
simpleProfileChar4Config = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) *
linkDBNumConns );
if ( simpleProfileChar4Config == NULL )
{
return ( bleMemAllocError );
}
// Initialize Client Characteristic Configuration attributes
GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar4Config );
if ( services & SIMPLEPROFILE_SERVICE )
{
// Register GATT attribute list and CBs with GATT Server App
status = GATTServApp_RegisterService( simpleProfileAttrTbl,
GATT_NUM_ATTRS( simpleProfileAttrTbl ),
GATT_MAX_ENCRYPT_KEY_SIZE,
&simpleProfileCBs );
}
else
{
status = SUCCESS;
}
return ( status );
}
/*********************************************************************
* @fn SimpleProfile_RegisterAppCBs
*
* @brief Registers the application callback function. Only call
* this function once.
*
* @param callbacks - pointer to application callbacks.
*
* @return SUCCESS or bleAlreadyInRequestedMode
*/
bStatus_t SimpleProfile_RegisterAppCBs( simpleProfileCBs_t *appCallbacks )
{
if ( appCallbacks )
{
simpleProfile_AppCBs = appCallbacks;
return ( SUCCESS );
}
else
{
return ( bleAlreadyInRequestedMode );
}
}
/*********************************************************************
* @fn SimpleProfile_SetParameter
*
* @brief Set a Simple Profile parameter.
*
* @param param - Profile parameter ID
* @param len - length of data to write
* @param value - pointer to data to write. This is dependent on
* the parameter ID and WILL be cast to the appropriate
* data type (example: data type of uint16 will be cast to
* uint16 pointer).
*
* @return bStatus_t
*/
bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value )
{
bStatus_t ret = SUCCESS;
if ( len == sizeof ( uint8 ) )
{
simpleProfileChar1 = *((uint8*)value);
}
else
{
ret = bleInvalidRange;
}
return ( ret );
}
/*********************************************************************
* @fn SimpleProfile_GetParameter
*
* @brief Get a Simple Profile parameter.
*
* @param param - Profile parameter ID
* @param value - pointer to data to put. This is dependent on
* the parameter ID and WILL be cast to the appropriate
* data type (example: data type of uint16 will be cast to
* uint16 pointer).
*
* @return bStatus_t
*/
bStatus_t SimpleProfile_GetParameter( uint8 param, void *value )
{
bStatus_t ret = SUCCESS;
*((uint8*)value) = simpleProfileChar1;
return ( ret );
}
/*********************************************************************
* @fn simpleProfile_ReadAttrCB
*
* @brief Read an attribute.
*
* @param connHandle - connection message was received on
* @param pAttr - pointer to attribute
* @param pValue - pointer to data to be read
* @param pLen - length of data to be read
* @param offset - offset of the first octet to be read
* @param maxLen - maximum length of data to be read
* @param method - type of read message
*
* @return SUCCESS, blePending or Failure
*/
static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle,
gattAttribute_t *pAttr,
uint8_t *pValue, uint16_t *pLen,
uint16_t offset, uint16_t maxLen,
uint8_t method)
{
bStatus_t status = SUCCESS;
// Make sure it's not a blob operation (no attributes in the profile are long)
if ( offset > 0 )
{
return ( ATT_ERR_ATTR_NOT_LONG );
}
if (pAttr->type.len == ATT_BT_UUID_SIZE) //ATT_BT_UUID_SIZE
{
*pLen = 1;
pValue[0] = *pAttr->pValue;
}
if (pAttr->type.len == ATT_UUID_SIZE) //ATT_BT_UUID_SIZE
{
*pLen = 1;
pValue[0] = *pAttr->pValue;
}
else
{
// 128-bit UUID
*pLen = 0;
status = ATT_ERR_INVALID_HANDLE;
}
return ( status );
}
/*********************************************************************
* @fn simpleProfile_WriteAttrCB
*
* @brief Validate attribute data prior to a write operation
*
* @param connHandle - connection message was received on
* @param pAttr - pointer to attribute
* @param pValue - pointer to data to be written
* @param len - length of data
* @param offset - offset of the first octet to be written
* @param method - type of write message
*
* @return SUCCESS, blePending or Failure
*/
static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle,
gattAttribute_t *pAttr,
uint8_t *pValue, uint16_t len,
uint16_t offset, uint8_t method)
{
bStatus_t status = SUCCESS;
uint8 notifyApp = 0xFF;
if (pAttr->type.len == ATT_UUID_SIZE)//ATT_BT_UUID_SIZE
{
// 16-bit UUID
//uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
// 128-bit UUID
uint16 uuid = BUILD_UINT16( pAttr->type.uuid[12], pAttr->type.uuid[13]);
if ( offset == 0 )
{
if ( len != 1 )
{
status = ATT_ERR_INVALID_VALUE_SIZE;
Display_print1(dispHandle, 4, 0, "ATT_ERR_INVALID_VALUE_SIZE % d ", status);
}
}
else
{
status = ATT_ERR_ATTR_NOT_LONG;
Display_print1(dispHandle, 4, 0, "ATT_ERR_ATTR_NOT_LONG % d ", status);
}
//Write the value
if ( status == SUCCESS )
{
uint8 *pCurValue = (uint8 *)pAttr->pValue;
*pCurValue = pValue[0];
if( pAttr->pValue == &simpleProfileChar1 )
{
notifyApp = SIMPLEPROFILE_CHAR1;
}
else
{
notifyApp = SIMPLEPROFILE_CHAR3;
}
}
}
else
{
// 16-bit UUID
status = ATT_ERR_INVALID_HANDLE;
}
// If a characteristic value changed then callback function to notify application of change
if ( (notifyApp != 0xFF ) && simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange )
{
simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp );
}
return ( status );
}
/*********************************************************************
*********************************************************************/
In SimpleCentral_processGATTMsg(gattMsgEvent_t *pMsg) function pmsg-> method value is always coming 1 (ATT_ERROR_RSP).
/******************************************************************************
@file simple_central.c
@brief This file contains the Simple Central sample application for use
with the CC2650 Bluetooth Low Energy Protocol Stack.
Group: WCS, BTS
Target Device: cc2640r2
******************************************************************************
Copyright (c) 2013-2021, Texas Instruments Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form 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.
* Neither the name of Texas Instruments Incorporated nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
******************************************************************************
*****************************************************************************/
/*********************************************************************
* INCLUDES
*/
#include <string.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Queue.h>
#include <ti/display/Display.h>
#include "bcomdef.h"
#include <icall.h>
#include "util.h"
/* This Header file contains all BLE API and icall structure definition */
#include "icall_ble_api.h"
#include "board_key.h"
#include "board.h"
#include "ble_user_config.h"
#include "simple_gatt_profile.h"
#include <menu/two_btn_menu.h>
#include "simple_central.h"
#include "simple_central_menu.h"
//Mauf data
static const uint8_t ManufData[6] = {0x0D,0x00,0xC0,0xFF,0xEE};
/*********************************************************************
* MACROS
*/
CONST uint8 simpleProfileServUUID[ATT_UUID_SIZE] =
{
SIMPLEPROFILE_SERV_UUID_BASE128(SIMPLEPROFILE_SERV_UUID)
//LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID)
};
/*********************************************************************
* CONSTANTS
*/
// Application events
#define SC_EVT_KEY_CHANGE 0x01
#define SC_EVT_SCAN_ENABLED 0x02
#define SC_EVT_SCAN_DISABLED 0x03
#define SC_EVT_ADV_REPORT 0x04
#define SC_EVT_SVC_DISC 0x05
#define SC_EVT_READ_RSSI 0x06
#define SC_EVT_PAIR_STATE 0x07
#define SC_EVT_PASSCODE_NEEDED 0x08
#define SC_EVT_READ_RPA 0x09
#define SC_EVT_INSUFFICIENT_MEM 0x0A
// Simple Central Task Events
#define SC_ICALL_EVT ICALL_MSG_EVENT_ID // Event_Id_31
#define SC_QUEUE_EVT UTIL_QUEUE_EVENT_ID // Event_Id_30
#define SC_ALL_EVENTS (SC_ICALL_EVT | \
SC_QUEUE_EVT)
// Address mode of the local device
// Note: When using the DEFAULT_ADDRESS_MODE as ADDRMODE_RANDOM or
// ADDRMODE_RP_WITH_RANDOM_ID, GAP_DeviceInit() should be called with
// it's last parameter set to a static random address
#define DEFAULT_ADDRESS_MODE ADDRMODE_RP_WITH_PUBLIC_ID
// Default PHY for scanning and initiating
#define DEFAULT_SCAN_PHY SCAN_PRIM_PHY_1M
#define DEFAULT_INIT_PHY INIT_PHY_1M
// Default scan duration in 10 ms
#define DEFAULT_SCAN_DURATION 100 // 1 sec
// Default RSSI polling period in ms
#define DEFAULT_RSSI_PERIOD 3000
// TRUE to filter discovery results on desired service UUID
#define DEFAULT_DEV_DISC_BY_SVC_UUID TRUE
// Minimum connection interval (units of 1.25ms) if automatic parameter update
// request is enabled
#define DEFAULT_UPDATE_MIN_CONN_INTERVAL 400
// Maximum connection interval (units of 1.25ms) if automatic parameter update
// request is enabled
#define DEFAULT_UPDATE_MAX_CONN_INTERVAL 800
// Slave latency to use if automatic parameter update request is enabled
#define DEFAULT_UPDATE_SLAVE_LATENCY 0
// Supervision timeout value (units of 10ms) if automatic parameter update
// request is enabled
#define DEFAULT_UPDATE_CONN_TIMEOUT 600
// Supervision timeout conversion rate to miliseconds
#define CONN_TIMEOUT_MS_CONVERSION 10
// How often to read current current RPA (in ms)
#define SC_READ_RPA_PERIOD 3000
// Task configuration
#define SC_TASK_PRIORITY 1
#ifndef SC_TASK_STACK_SIZE
#define SC_TASK_STACK_SIZE 1024
#endif
// Advertising report fields to keep in the list
// Interested in only peer address type and peer address
#define SC_ADV_RPT_FIELDS (SCAN_ADVRPT_FLD_ADDRTYPE | SCAN_ADVRPT_FLD_ADDRESS)
// Size of string-converted device address ("0xXXXXXXXXXXXX")
#define SC_ADDR_STR_SIZE 15
// Row numbers for two-button menu
#define SC_ROW_SEPARATOR (TBM_ROW_APP + 0)
#define SC_ROW_CUR_CONN (TBM_ROW_APP + 1)
#define SC_ROW_ANY_CONN (TBM_ROW_APP + 2)
#define SC_ROW_NON_CONN (TBM_ROW_APP + 3)
#define SC_ROW_NUM_CONN (TBM_ROW_APP + 4)
#define SC_ROW_IDA (TBM_ROW_APP + 5)
#define SC_ROW_RPA (TBM_ROW_APP + 6)
// Spin if the expression is not true
#define SIMPLECENTRAL_ASSERT(expr) if (!(expr)) SimpleCentral_spin();
/*********************************************************************
* TYPEDEFS
*/
// Discovery states
enum
{
BLE_DISC_STATE_IDLE, // Idle
BLE_DISC_STATE_MTU, // Exchange ATT MTU size
BLE_DISC_STATE_SVC, // Service discovery
BLE_DISC_STATE_CHAR // Characteristic discovery
};
// App event passed from profiles.
typedef struct
{
appEvtHdr_t hdr; // event header
uint8_t *pData; // event data
} scEvt_t;
// Scanned device information record
typedef struct
{
uint8_t addrType; // Peer Device's Address Type
uint8_t addr[B_ADDR_LEN]; // Peer Device Address
} scanRec_t;
// Connected device information
typedef struct
{
uint16_t connHandle; // Connection Handle
uint8_t addr[B_ADDR_LEN]; // Peer Device Address
uint8_t charHandle; // Characteristic Handle
Clock_Struct *pRssiClock; // pointer to clock struct
} connRec_t;
// Container to store paring state info when passing from gapbondmgr callback
// to app event. See the pfnPairStateCB_t documentation from the gapbondmgr.h
// header file for more information on each parameter.
typedef struct
{
uint16_t connHandle;
uint8_t status;
} scPairStateData_t;
// Container to store passcode data when passing from gapbondmgr callback
// to app event. See the pfnPasscodeCB_t documentation from the gapbondmgr.h
// header file for more information on each parameter.
typedef struct
{
uint8_t deviceAddr[B_ADDR_LEN];
uint16_t connHandle;
uint8_t uiInputs;
uint8_t uiOutputs;
uint32_t numComparison;
} scPasscodeData_t;
/*********************************************************************
* GLOBAL VARIABLES
*/
// Display Interface
Display_Handle dispHandle = NULL;
/*********************************************************************
* EXTERNAL VARIABLES
*/
/*********************************************************************
* LOCAL VARIABLES
*/
// Entity ID globally used to check for source and/or destination of messages
static ICall_EntityID selfEntity;
// Event globally used to post local events and pend on system and
// local events.
static ICall_SyncHandle syncEvent;
// Queue object used for app messages
static Queue_Struct appMsg;
static Queue_Handle appMsgQueue;
// Task configuration
Task_Struct scTask;
#if defined __TI_COMPILER_VERSION__
#pragma DATA_ALIGN(scTaskStack, 8)
#else
#pragma data_alignment=8
#endif
uint8_t scTaskStack[SC_TASK_STACK_SIZE];
// GAP GATT Attributes
static const uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple Central";
#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
// Number of scan results filtered by Service UUID
static uint8_t numScanRes = 0;
// Scan results filtered by Service UUID
static scanRec_t scanList[DEFAULT_MAX_SCAN_RES];
#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
// Number of connected devices
static uint8_t numConn = 0;
// List of connections
static connRec_t connList[MAX_NUM_BLE_CONNS];
// Connection handle of current connection
static uint16_t scConnHandle = CONNHANDLE_INVALID;
// Accept or reject L2CAP connection parameter update request
static bool acceptParamUpdateReq = true;
// Discovery state
static uint8_t discState = BLE_DISC_STATE_IDLE;
// Discovered service start and end handle
static uint16_t svcStartHdl = 0;
static uint16_t svcEndHdl = 0;
// Value to write
static uint8_t charVal = 0;
// Maximum PDU size (default = 27 octets)
static uint16_t scMaxPduSize;
// Clock instance for RPA read events.
static Clock_Struct clkRpaRead;
// Address mode
static GAP_Addr_Modes_t addrMode = DEFAULT_ADDRESS_MODE;
#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG)
// Current Random Private Address
static uint8 rpa[B_ADDR_LEN] = {0};
#endif // PRIVACY_1_2_CFG
/*********************************************************************
* LOCAL FUNCTIONS
*/
static void SimpleCentral_init(void);
static void SimpleCentral_taskFxn(uintptr_t a0, uintptr_t a1);
static void SimpleCentral_handleKeys(uint8_t keys);
static uint8_t SimpleCentral_processStackMsg(ICall_Hdr *pMsg);
static void SimpleCentral_processGapMsg(gapEventHdr_t *pMsg);
static void SimpleCentral_processGATTMsg(gattMsgEvent_t *pMsg);
static void SimpleCentral_processAppMsg(scEvt_t *pMsg);
static void SimpleCentral_processGATTDiscEvent(gattMsgEvent_t *pMsg);
static void SimpleCentral_startSvcDiscovery(void);
#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
static bool SimpleCentral_findUuid(const uint8_t *uuid, const uint8_t *pManufData ,uint8_t manDataLen ,uint8_t *pData,
uint8_t dataLen);
/*
static bool SimpleCentral_findSvcUuid(uint16_t uuid, uint8_t *pData,
uint16_t dataLen);
*/
static void SimpleCentral_addScanInfo(uint8_t *pAddr, uint8_t addrType);
#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
static uint8_t SimpleCentral_addConnInfo(uint16_t connHandle, uint8_t *pAddr);
static uint8_t SimpleCentral_removeConnInfo(uint16_t connHandle);
static uint8_t SimpleCentral_getConnIndex(uint16_t connHandle);
#ifndef Display_DISABLE_ALL
static char* SimpleCentral_getConnAddrStr(uint16_t connHandle);
#endif
static void SimpleCentral_processPairState(uint8_t state,
scPairStateData_t* pPairStateData);
static void SimpleCentral_processPasscode(scPasscodeData_t *pData);
static void SimpleCentral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg);
static status_t SimpleCentral_StartRssi();
static status_t SimpleCentral_CancelRssi(uint16_t connHandle);
static void SimpleCentral_passcodeCb(uint8_t *deviceAddr, uint16_t connHandle,
uint8_t uiInputs, uint8_t uiOutputs,
uint32_t numComparison);
static void SimpleCentral_pairStateCb(uint16_t connHandle, uint8_t state,
uint8_t status);
static void SimpleCentral_keyChangeHandler(uint8 keys);
static void SimpleCentral_clockHandler(UArg arg);
static status_t SimpleCentral_enqueueMsg(uint8_t event, uint8_t status,
uint8_t *pData);
static void SimpleCentral_scanCb(uint32_t evt, void* msg, uintptr_t arg);
static void SimpleCentral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr,
tbmMenuObj_t* pMenuObjNext);
/*********************************************************************
* EXTERN FUNCTIONS
*/
extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);
/*********************************************************************
* PROFILE CALLBACKS
*/
// Bond Manager Callbacks
static gapBondCBs_t bondMgrCBs =
{
SimpleCentral_passcodeCb, // Passcode callback
SimpleCentral_pairStateCb // Pairing/Bonding state Callback
};
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/*********************************************************************
* @fn SimpleCentral_spin
*
* @brief Spin forever
*
* @param none
*/
static void SimpleCentral_spin(void)
{
volatile uint8_t x;
while(1)
{
x++;
}
}
/*********************************************************************
* @fn SimpleCentral_createTask
*
* @brief Task creation function for the Simple Central.
*
* @param none
*
* @return none
*/
void SimpleCentral_createTask(void)
{
Task_Params taskParams;
// Configure task
Task_Params_init(&taskParams);
taskParams.stack = scTaskStack;
taskParams.stackSize = SC_TASK_STACK_SIZE;
taskParams.priority = SC_TASK_PRIORITY;
Task_construct(&scTask, SimpleCentral_taskFxn, &taskParams, NULL);
}
/*********************************************************************
* @fn SimpleCentral_Init
*
* @brief Initialization function for the Simple Central App Task.
* This is called during initialization and should contain
* any application specific initialization (ie. hardware
* initialization/setup, table initialization, power up
* notification).
*
* @param none
*
* @return none
*/
static void SimpleCentral_init(void)
{
uint8_t i;
// ******************************************************************
// N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
// ******************************************************************
// Register the current thread as an ICall dispatcher application
// so that the application can send and receive messages.
ICall_registerApp(&selfEntity, &syncEvent);
// Create an RTOS queue for message from profile to be sent to app.
appMsgQueue = Util_constructQueue(&appMsg);
// Initialize internal data
for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
connList[i].connHandle = CONNHANDLE_INVALID;
connList[i].pRssiClock = NULL;
}
Board_initKeys(SimpleCentral_keyChangeHandler);
GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN,
(void *)attDeviceName);
//Set default values for Data Length Extension
//Extended Data Length Feature is already enabled by default
//in build_config.opt in stack project.
{
//Set initial values to maximum, RX is set to max. by default(251 octets, 2120us)
#define APP_SUGGESTED_PDU_SIZE 251 //default is 27 octets(TX)
#define APP_SUGGESTED_TX_TIME 2120 //default is 328us(TX)
//This API is documented in hci.h
//See the LE Data Length Extension section in the BLE5-Stack User's Guide for information on using this command:
//http://software-dl.ti.com/lprf/ble5stack-latest/
//HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE, APP_SUGGESTED_TX_TIME);
}
// Initialize GATT Client
VOID GATT_InitClient();
// Register to receive incoming ATT Indications/Notifications
GATT_RegisterForInd(selfEntity);
// Initialize GATT attributes
GGS_AddService(GATT_ALL_SERVICES); // GAP
GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes
// Register for GATT local events and ATT Responses pending for transmission
GATT_RegisterForMsgs(selfEntity);
// Set Bond Manager parameters
{
// Don't send a pairing request after connecting; the device waits for the
// application to start pairing
uint8_t pairMode = GAPBOND_PAIRING_MODE_INITIATE;
// Do not use authenticated pairing
uint8_t mitm = FALSE;
// This is a display only device
uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
// Create a bond during the pairing process
uint8_t bonding = TRUE;
GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
}
// Start Bond Manager and register callback
// This must be done before initialing the GAP layer
VOID GAPBondMgr_Register(&bondMgrCBs);
// Accept all parameter update requests
GAP_SetParamValue(GAP_PARAM_LINK_UPDATE_DECISION, GAP_UPDATE_REQ_ACCEPT_ALL);
// Register with GAP for HCI/Host messages (for RSSI)
GAP_RegisterForMsgs(selfEntity);
// Initialize GAP layer for Central role and register to receive GAP events
GAP_DeviceInit(GAP_PROFILE_CENTRAL, selfEntity, addrMode, NULL);
dispHandle = Display_open(Display_Type_UART , NULL);
// Disable all items in the main menu
tbm_setItemStatus(&scMenuMain, SC_ITEM_NONE, SC_ITEM_ALL);
// Initialize Two-button Menu
tbm_initTwoBtnMenu(dispHandle, &scMenuMain, 4, SimpleCentral_menuSwitchCb);
Display_printf(dispHandle, SC_ROW_SEPARATOR, 0, "====================");
}
/*********************************************************************
* @fn SimpleCentral_taskFxn
*
* @brief Application task entry point for the Simple Central.
*
* @param none
*
* @return events not processed
*/
static void SimpleCentral_taskFxn(uintptr_t a0, uintptr_t a1)
{
// Initialize application
SimpleCentral_init();
// Application main loop
for (;;)
{
uint32_t events;
events = Event_pend(syncEvent, Event_Id_NONE, SC_ALL_EVENTS,
ICALL_TIMEOUT_FOREVER);
if (events)
{
ICall_EntityID dest;
ICall_ServiceEnum src;
ICall_HciExtEvt *pMsg = NULL;
if (ICall_fetchServiceMsg(&src, &dest,
(void **)&pMsg) == ICALL_ERRNO_SUCCESS)
{
uint8 safeToDealloc = TRUE;
if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
{
ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
// Check for BLE stack events first
if (pEvt->signature != 0xffff)
{
// Process inter-task message
safeToDealloc = SimpleCentral_processStackMsg((ICall_Hdr *)pMsg);
}
}
if (pMsg && safeToDealloc)
{
ICall_freeMsg(pMsg);
}
}
// If RTOS queue is not empty, process app message
if (events & SC_QUEUE_EVT)
{
scEvt_t *pMsg;
while (pMsg = (scEvt_t *)Util_dequeueMsg(appMsgQueue))
{
// Process message
SimpleCentral_processAppMsg(pMsg);
// Free the space from the message
ICall_free(pMsg);
}
}
}
}
}
/*********************************************************************
* @fn SimpleCentral_processStackMsg
*
* @brief Process an incoming task message.
*
* @param pMsg - message to process
*
* @return TRUE if safe to deallocate incoming message, FALSE otherwise.
*/
static uint8_t SimpleCentral_processStackMsg(ICall_Hdr *pMsg)
{
uint8_t safeToDealloc = TRUE;
switch (pMsg->event)
{
case GAP_MSG_EVENT:
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "GAP_MSG_EVENT");
SimpleCentral_processGapMsg((gapEventHdr_t*) pMsg);
break;
case GATT_MSG_EVENT:
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "GATT_MSG_EVENT");
SimpleCentral_processGATTMsg((gattMsgEvent_t *)pMsg);
break;
case HCI_GAP_EVENT_EVENT:
{
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "HCI_GAP_EVENT_EVENT");
// Process HCI message
switch (pMsg->status)
{
case HCI_COMMAND_COMPLETE_EVENT_CODE:
SimpleCentral_processCmdCompleteEvt((hciEvt_CmdComplete_t *) pMsg);
break;
case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
break;
// HCI Commands Events
case HCI_COMMAND_STATUS_EVENT_CODE:
{
hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;
switch ( pMyMsg->cmdOpcode )
{
case HCI_LE_SET_PHY:
{
if (pMyMsg->cmdStatus ==
HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"PHY Change failure, peer does not support this");
}
else
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"PHY Update Status: 0x%02x",
pMyMsg->cmdStatus);
}
}
break;
case HCI_DISCONNECT:
break;
default:
{
Display_printf(dispHandle, SC_ROW_NON_CONN, 0,
"Unknown Cmd Status: 0x%04x::0x%02x",
pMyMsg->cmdOpcode, pMyMsg->cmdStatus);
}
break;
}
}
break;
// LE Events
case HCI_LE_EVENT_CODE:
{
hciEvt_BLEPhyUpdateComplete_t *pPUC
= (hciEvt_BLEPhyUpdateComplete_t*) pMsg;
if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
{
if (pPUC->status != SUCCESS)
{
Display_printf(dispHandle, SC_ROW_ANY_CONN, 0,
"%s: PHY change failure",
SimpleCentral_getConnAddrStr(pPUC->connHandle));
}
else
{
Display_printf(dispHandle, SC_ROW_ANY_CONN, 0,
"%s: PHY updated to %s",
SimpleCentral_getConnAddrStr(pPUC->connHandle),
// Only symmetrical PHY is supported.
// rxPhy should be equal to txPhy.
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1 Mbps" :
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2 Mbps" :
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value");
}
}
break;
}
default:
break;
}
break;
}
case L2CAP_SIGNAL_EVENT:
// place holder for L2CAP Connection Parameter Reply
break;
default:
break;
}
return (safeToDealloc);
}
/*********************************************************************
* @fn SimpleCentral_processAppMsg
*
* @brief Scanner application event processing function.
*
* @param pMsg - pointer to event structure
*
* @return none
*/
static void SimpleCentral_processAppMsg(scEvt_t *pMsg)
{
bool safeToDealloc = TRUE;
switch (pMsg->hdr.event)
{
case SC_EVT_KEY_CHANGE:
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "SC_EVT_KEY_CHANGE");
SimpleCentral_handleKeys(pMsg->hdr.state);
break;
case SC_EVT_ADV_REPORT:
{
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "SC_EVT_ADV_REPORT");
GapScan_Evt_AdvRpt_t* pAdvRpt = (GapScan_Evt_AdvRpt_t*) (pMsg->pData);
#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
// if (SimpleCentral_findSvcUuid(SIMPLEPROFILE_SERV_UUID,
// pAdvRpt->pData, pAdvRpt->dataLen))
if (SimpleCentral_findUuid(simpleProfileServUUID,//128bit
ManufData,
sizeof(ManufData),
pAdvRpt->pData,
pAdvRpt->dataLen))
{
SimpleCentral_addScanInfo(pAdvRpt->addr, pAdvRpt->addrType);
Display_printf(dispHandle, 4, 0, "Discovered: %s",
Util_convertBdAddr2Str(pAdvRpt->addr));
}
#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Discovered: %s",
Util_convertBdAddr2Str(pAdvRpt->addr));
#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
// Free report payload data
if (pAdvRpt->pData != NULL)
{
ICall_free(pAdvRpt->pData);
}
break;
}
case SC_EVT_SCAN_ENABLED:
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "SC_EVT_SCAN_ENABLED");
// Disable everything but "Stop Discovering" on the menu
tbm_setItemStatus(&scMenuMain, SC_ITEM_STOPDISC,(SC_ITEM_ALL & ~SC_ITEM_STOPDISC));
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Discovering...");
break;
case SC_EVT_SCAN_DISABLED:
{
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "SC_EVT_SCAN_DISABLED");
uint8_t numReport;
uint8_t i;
static uint8_t* pAddrs = NULL;
uint8_t* pAddrTemp;
uint16_t itemsToEnable = SC_ITEM_STARTDISC | SC_ITEM_SCANPHY;
#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
numReport = numScanRes;
#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
GapScan_Evt_AdvRpt_t advRpt;
numReport = ((GapScan_Evt_End_t*) (pMsg->pData))->numReport;
#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
Display_printf(dispHandle, SC_ROW_NON_CONN, 0,
"%d devices discovered", numReport);
if (numReport > 0)
{
// Also enable "Connect to"
itemsToEnable |= SC_ITEM_CONNECT;
}
if (numConn > 0)
{
// Also enable "Work with"
itemsToEnable |= SC_ITEM_SELECTCONN;
//todo
itemsToEnable |= SC_ITEM_GATTWRITE;
}
// Enable "Discover Devices", "Set Scanning PHY", and possibly
// "Connect to" and/or "Work with".
// Disable "Stop Discovering".
tbm_setItemStatus(&scMenuMain, itemsToEnable, SC_ITEM_STOPDISC);
// Allocate buffer to display addresses
if (pAddrs != NULL)
{
// A scan has been done previously, release the previously allocated buffer
ICall_free(pAddrs);
}
pAddrs = ICall_malloc(numReport * SC_ADDR_STR_SIZE);
if (pAddrs == NULL)
{
numReport = 0;
}
TBM_SET_NUM_ITEM(&scMenuConnect, numReport);
if (pAddrs != NULL)
{
pAddrTemp = pAddrs;
for (i = 0; i < numReport; i++, pAddrTemp += SC_ADDR_STR_SIZE)
{
#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
// Get the address from the list, convert it to string, and
// copy the string to the address buffer
memcpy(pAddrTemp, Util_convertBdAddr2Str(scanList[i].addr),
SC_ADDR_STR_SIZE);
#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
// Get the address from the report, convert it to string, and
// copy the string to the address buffer
GapScan_getAdvReport(i, &advRpt);
memcpy(pAddrTemp, Util_convertBdAddr2Str(advRpt.addr),
SC_ADDR_STR_SIZE);
#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
// Assign the string to the corresponding action description of the menu
TBM_SET_ACTION_DESC(&scMenuConnect, i, pAddrTemp);
}
// Disable any non-active scan results
for (; i < DEFAULT_MAX_SCAN_RES; i++)
{
tbm_setItemStatus(&scMenuConnect, TBM_ITEM_NONE, (1 << i));
}
// Note: pAddrs is not freed since it will be used by the two button menu
// to display the discovered address.
// This implies that at least the last discovered addresses
// will be maintained until a new scan is done.
}
break;
}
case SC_EVT_SVC_DISC:
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "SC_EVT_SVC_DISC");
SimpleCentral_startSvcDiscovery();
break;
// case SC_EVT_READ_RSSI:
// {
// Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "SC_EVT_READ_RSSI");
// uint8_t connIndex = pMsg->hdr.state;
// uint16_t connHandle = connList[connIndex].connHandle;
//
// // If link is still valid
// if (connHandle != CONNHANDLE_INVALID)
// {
// // Restart timer
// Util_startClock(connList[connIndex].pRssiClock);
//
// // Read RSSI
// VOID HCI_ReadRssiCmd(connHandle);
// }
// break;
//}
// // Pairing event
// case SC_EVT_PAIR_STATE:
// {
// Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "SC_EVT_PAIR_STATE");
// SimpleCentral_processPairState(pMsg->hdr.state,
// (scPairStateData_t*) (pMsg->pData));
// break;
// }
//
// // Passcode event
// case SC_EVT_PASSCODE_NEEDED:
// {
// Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "SC_EVT_PASSCODE_NEEDED");
// SimpleCentral_processPasscode((scPasscodeData_t *)(pMsg->pData));
// break;
// }
#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG)
// case SC_EVT_READ_RPA:
// {
// uint8_t* pRpaNew;
// Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Inside SC_EVT_READ_RP");
//
// // Read the current RPA.
// pRpaNew = GAP_GetDevAddress(FALSE);
//
// if (memcmp(pRpaNew, rpa, B_ADDR_LEN))
// {
// // If the RPA has changed, update the display
// Display_printf(dispHandle, SC_ROW_RPA, 0, "RP Addr: %s",Util_convertBdAddr2Str(pRpaNew));
// memcpy(rpa, pRpaNew, B_ADDR_LEN);
// }
// break;
// }
#endif // PRIVACY_1_2_CFG
// Insufficient memory
case SC_EVT_INSUFFICIENT_MEM:
{
// We are running out of memory.
Display_printf(dispHandle, SC_ROW_ANY_CONN, 0, "Insufficient Memory");
// We might be in the middle of scanning, try stopping it.
GapScan_disable();
break;
}
default:
// Do nothing.
break;
}
if ((safeToDealloc == TRUE) && (pMsg->pData != NULL))
{
ICall_free(pMsg->pData);
}
}
/*********************************************************************
* @fn SimpleCentral_processGapMsg
*
* @brief GAP message processing function.
*
* @param pMsg - pointer to event message structure
*
* @return none
*/
static void SimpleCentral_processGapMsg(gapEventHdr_t *pMsg)
{
switch (pMsg->opcode)
{
case GAP_DEVICE_INIT_DONE_EVENT:
{
uint8_t temp8;
uint16_t temp16;
gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;
// Setup scanning
// For more information, see the GAP section in the User's Guide:
// http://software-dl.ti.com/lprf/ble5stack-latest/
// Register callback to process Scanner events
GapScan_registerCb(SimpleCentral_scanCb, NULL);
// Set Scanner Event Mask
GapScan_setEventMask(GAP_EVT_SCAN_ENABLED | GAP_EVT_SCAN_DISABLED | GAP_EVT_ADV_REPORT);
// Set Scan PHY parameters
// GapScan_setPhyParams(DEFAULT_SCAN_PHY, SCAN_TYPE_PASSIVE,SCAN_PARAM_DFLT_INTERVAL, SCAN_PARAM_DFLT_INTERVAL);
GapScan_setPhyParams(DEFAULT_SCAN_PHY, SCAN_TYPE_ACTIVE,SCAN_PARAM_DFLT_INTERVAL, SCAN_PARAM_DFLT_INTERVAL); //changed by me
// Set Advertising report fields to keep
temp16 = SC_ADV_RPT_FIELDS;
GapScan_setParam(SCAN_PARAM_RPT_FIELDS, &temp16);
// Set Scanning Primary PHY
temp8 = DEFAULT_SCAN_PHY;
GapScan_setParam(SCAN_PARAM_PRIM_PHYS, &temp8);
// Set LL Duplicate Filter
temp8 = SCAN_FLT_DUP_ENABLE;
GapScan_setParam(SCAN_PARAM_FLT_DUP, &temp8);
// Set PDU type filter -
// Only 'Connectable' and 'Complete' packets are desired.
// It doesn't matter if received packets are
// whether Scannable or Non-Scannable, whether Directed or Undirected,
// whether Scan_Rsp's or Advertisements, and whether Legacy or Extended.
temp16 = SCAN_FLT_PDU_CONNECTABLE_ONLY | SCAN_FLT_PDU_COMPLETE_ONLY;
//GapScan_setParam(SCAN_PARAM_FLT_PDU_TYPE, &temp16);
GapScan_setParam(SCAN_PARAM_FLT_PDU_TYPE, &temp16);
// SCAN_PARAM_FLT_PDU_TYPE - Filter by PDU Type
// SCAN_PARAM_FLT_MIN_RSSI - Filter by Minimum RSSI
// SCAN_PARAM_FLT_DISC_MODE - Filter by Discoverable Mode
// SCAN_PARAM_RPT_FIELDS - Advertising Report Fields
scMaxPduSize = pPkt->dataPktLen;
// Enable "Discover Devices", "Set Scanning PHY", and "Set Address Type"
// in the main menu
tbm_setItemStatus(&scMenuMain,
SC_ITEM_STARTDISC | SC_ITEM_SCANPHY, SC_ITEM_NONE);
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Initialized");
Display_printf(dispHandle, SC_ROW_NUM_CONN, 0, "Num Conns: %d", numConn);
// Display device address
Display_printf(dispHandle, SC_ROW_IDA, 0, "%s Addr: %s",
(addrMode <= ADDRMODE_RANDOM) ? "Dev" : "ID",
Util_convertBdAddr2Str(pPkt->devAddr));
#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG)
if (addrMode > ADDRMODE_RANDOM)
{
// Update the current RPA.
memcpy(rpa, GAP_GetDevAddress(FALSE), B_ADDR_LEN);
Display_printf(dispHandle, SC_ROW_RPA, 0, "RP Addr: %s",
Util_convertBdAddr2Str(rpa));
// Create one-shot clock for RPA check event.
Util_constructClock(&clkRpaRead, SimpleCentral_clockHandler,
SC_READ_RPA_PERIOD, 0, true, SC_EVT_READ_RPA);
}
#endif // PRIVACY_1_2_CFG
break;
}
case GAP_CONNECTING_CANCELLED_EVENT:
{
uint16_t itemsToEnable = SC_ITEM_SCANPHY | SC_ITEM_STARTDISC |
SC_ITEM_CONNECT;
if (numConn > 0)
{
itemsToEnable |= SC_ITEM_SELECTCONN;
}
Display_printf(dispHandle, SC_ROW_NON_CONN, 0,"Connecting attempt cancelled");
// Enable "Discover Devices", "Connect To", and "Set Scanning PHY"
// and disable everything else.
tbm_setItemStatus(&scMenuMain,
itemsToEnable, SC_ITEM_ALL & ~itemsToEnable);
break;
}
case GAP_LINK_ESTABLISHED_EVENT:
{
uint16_t connHandle = ((gapEstLinkReqEvent_t*) pMsg)->connectionHandle;
uint8_t* pAddr = ((gapEstLinkReqEvent_t*) pMsg)->devAddr;
uint8_t connIndex;
uint32_t itemsToDisable = SC_ITEM_STOPDISC | SC_ITEM_CANCELCONN;
uint8_t* pStrAddr;
uint8_t i;
uint8_t numConnectable = 0;
// Add this connection info to the list
connIndex = SimpleCentral_addConnInfo(connHandle, pAddr);
// connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
connList[connIndex].charHandle = 0;
pStrAddr = (uint8_t*) Util_convertBdAddr2Str(connList[connIndex].addr);
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Connected to %s", pStrAddr);
Display_printf(dispHandle, SC_ROW_NUM_CONN, 0, "Num Conns: %d", numConn);
// Disable "Connect To" until another discovery is performed
itemsToDisable |= SC_ITEM_CONNECT;
// If we already have maximum allowed number of connections,
// disable device discovery and additional connection making.
if (numConn >= MAX_NUM_BLE_CONNS)
{
itemsToDisable |= SC_ITEM_SCANPHY | SC_ITEM_STARTDISC;
}
for (i = 0; i < TBM_GET_NUM_ITEM(&scMenuConnect); i++)
{
if (!memcmp(TBM_GET_ACTION_DESC(&scMenuConnect, i), pStrAddr,
SC_ADDR_STR_SIZE))
{
// Disable this device from the connection choices
tbm_setItemStatus(&scMenuConnect, SC_ITEM_NONE, 1 << i);
}
else if (TBM_IS_ITEM_ACTIVE(&scMenuConnect, i))
{
numConnectable++;
}
}
// Enable/disable Main menu items properly
tbm_setItemStatus(&scMenuMain,SC_ITEM_ALL & ~(itemsToDisable), itemsToDisable);
break;
}
case GAP_LINK_TERMINATED_EVENT:
{
uint16_t connHandle = ((gapTerminateLinkEvent_t*) pMsg)->connectionHandle;
uint8_t connIndex;
uint32_t itemsToEnable = SC_ITEM_STARTDISC | SC_ITEM_SCANPHY;
uint8_t* pStrAddr;
uint8_t i;
uint8_t numConnectable = 0;
// Cancel timers
SimpleCentral_CancelRssi(connHandle);
// Mark this connection deleted in the connected device list.
connIndex = SimpleCentral_removeConnInfo(connHandle);
// connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
pStrAddr = (uint8_t*) Util_convertBdAddr2Str(connList[connIndex].addr);
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "%s is disconnected",
pStrAddr);
Display_printf(dispHandle, SC_ROW_NUM_CONN, 0, "Num Conns: %d", numConn);
for (i = 0; i < TBM_GET_NUM_ITEM(&scMenuConnect); i++)
{
if (!memcmp(TBM_GET_ACTION_DESC(&scMenuConnect, i), pStrAddr,
SC_ADDR_STR_SIZE))
{
// Enable this device in the connection choices
tbm_setItemStatus(&scMenuConnect, 1 << i, SC_ITEM_NONE);
}
if (TBM_IS_ITEM_ACTIVE(&scMenuConnect, i))
{
numConnectable++;
}
}
if (numConn > 0)
{
// There still is an active connection to select
itemsToEnable |= SC_ITEM_SELECTCONN;
}
// Enable/disable items properly.
tbm_setItemStatus(&scMenuMain,
itemsToEnable, SC_ITEM_ALL & ~itemsToEnable);
// If we are in the context which the teminated connection was associated
// with, go to main menu.
if (connHandle == scConnHandle)
{
tbm_goTo(&scMenuMain);
}
break;
}
case GAP_UPDATE_LINK_PARAM_REQ_EVENT:
{
gapUpdateLinkParamReqReply_t rsp;
gapUpdateLinkParamReq_t *pReq;
pReq = &((gapUpdateLinkParamReqEvent_t *)pMsg)->req;
rsp.connectionHandle = pReq->connectionHandle;
rsp.signalIdentifier = pReq->signalIdentifier;
if (acceptParamUpdateReq)
{
rsp.intervalMin = pReq->intervalMin;
rsp.intervalMax = pReq->intervalMax;
rsp.connLatency = pReq->connLatency;
rsp.connTimeout = pReq->connTimeout;
rsp.accepted = TRUE;
}
else
{
// Reject the request.
rsp.accepted = FALSE;
}
// Send Reply
VOID GAP_UpdateLinkParamReqReply(&rsp);
break;
}
case GAP_LINK_PARAM_UPDATE_EVENT:
{
gapLinkUpdateEvent_t *pPkt = (gapLinkUpdateEvent_t *)pMsg;
// Get the address from the connection handle
linkDBInfo_t linkInfo;
if (linkDB_GetInfo(pPkt->connectionHandle, &linkInfo) == SUCCESS)
{
if(pPkt->status == SUCCESS)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Updated: %s, connTimeout:%d",
Util_convertBdAddr2Str(linkInfo.addr),
linkInfo.connTimeout*CONN_TIMEOUT_MS_CONVERSION);
}
else
{
// Display the address of the connection update failure
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Update Failed 0x%h: %s", pPkt->opcode,
Util_convertBdAddr2Str(linkInfo.addr));
}
}
break;
}
default:
break;
}
}
/*********************************************************************
* @fn SimpleCentral_handleKeys
*
* @brief Handles all key events for this device.
*
* @param keys - bit field for key events. Valid entries:
* KEY_LEFT
* KEY_RIGHT
*
* @return none
*/
static void SimpleCentral_handleKeys(uint8_t keys)
{
if (keys & KEY_LEFT)
{
// Check if the key is still pressed. Workaround for possible bouncing.
if (PIN_getInputValue(Board_PIN_BUTTON0) == 0)
{
tbm_buttonLeft();
}
}
else if (keys & KEY_RIGHT)
{
// Check if the key is still pressed. Workaround for possible bouncing.
if (PIN_getInputValue(Board_PIN_BUTTON1) == 0)
{
tbm_buttonRight();
}
}
}
/*********************************************************************
* @fn SimpleCentral_processGATTMsg
*
* @brief Process GATT messages and events.
*
* @return none
*/
static void SimpleCentral_processGATTMsg(gattMsgEvent_t *pMsg)
{
if (linkDB_Up(pMsg->connHandle))
{
// See if GATT server was unable to transmit an ATT response
if (pMsg->hdr.status == blePending)
{
// No HCI buffer was available. App can try to retransmit the response
// on the next connection event. Drop it for now.
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"ATT Rsp dropped %d", pMsg->method);
}
else if ((pMsg->method == ATT_READ_RSP) ||
((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ)))
{
if (pMsg->method == ATT_ERROR_RSP)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Read Error %d", pMsg->msg.errorRsp.errCode);
}
else
{
// After a successful read, display the read value
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Read rsp: 0x%02x", pMsg->msg.readRsp.pValue[0]);
}
}
else if ((pMsg->method == ATT_WRITE_RSP) ||
((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ)))
{
if (pMsg->method == ATT_ERROR_RSP)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Write Error %d", pMsg->msg.errorRsp.errCode);
}
else
{
// After a successful write, display the value that was written and
// increment value
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"Write sent: 0x%02x", charVal);
}
tbm_goTo(&scMenuPerConn);
}
else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
{
// ATT request-response or indication-confirmation flow control is
// violated. All subsequent ATT requests or indications will be dropped.
// The app is informed in case it wants to drop the connection.
// Display the opcode of the message that caused the violation.
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
}
else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
{
// MTU size updated
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"MTU Size: %d", pMsg->msg.mtuEvt.MTU);
}
else if (discState != BLE_DISC_STATE_IDLE)
{
SimpleCentral_processGATTDiscEvent(pMsg);
}
} // else - in case a GATT message came after a connection has dropped, ignore it.
// Needed only for ATT Protocol messages
GATT_bm_free(&pMsg->msg, pMsg->method);
}
/*********************************************************************
* @fn SimpleCentral_processCmdCompleteEvt
*
* @brief Process an incoming OSAL HCI Command Complete Event.
*
* @param pMsg - message to process
*
* @return none
*/
static void SimpleCentral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg)
{
switch (pMsg->cmdOpcode)
{
case HCI_READ_RSSI:
{
#ifndef Display_DISABLE_ALL
uint16_t connHandle = BUILD_UINT16(pMsg->pReturnParam[1],
pMsg->pReturnParam[2]);
int8 rssi = (int8)pMsg->pReturnParam[3];
Display_printf(dispHandle, SC_ROW_ANY_CONN, 0, "%s: RSSI %d dBm",
SimpleCentral_getConnAddrStr(connHandle), rssi);
#endif
break;
}
default:
break;
}
}
/*********************************************************************
* @fn SimpleCentral_StartRssi
*
* @brief Start periodic RSSI reads on the current link.
*
* @return SUCCESS: RSSI Read timer started
* bleIncorrectMode: Aready started
* bleNoResources: No resources
*/
static status_t SimpleCentral_StartRssi(void)
{
uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
// connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
// If already running
if (connList[connIndex].pRssiClock != NULL)
{
return bleIncorrectMode;
}
// Create a clock object and start
connList[connIndex].pRssiClock
= (Clock_Struct*) ICall_malloc(sizeof(Clock_Struct));
if (connList[connIndex].pRssiClock)
{
Util_constructClock(connList[connIndex].pRssiClock,
SimpleCentral_clockHandler,
DEFAULT_RSSI_PERIOD, 0, true,
(connIndex << 8) | SC_EVT_READ_RSSI);
}
else
{
return bleNoResources;
}
return SUCCESS;
}
/*********************************************************************
* @fn SimpleCentral_CancelRssi
*
* @brief Cancel periodic RSSI reads on a link.
*
* @param connection handle
*
* @return SUCCESS: Operation successful
* bleIncorrectMode: Has not started
*/
static status_t SimpleCentral_CancelRssi(uint16_t connHandle)
{
uint8_t connIndex = SimpleCentral_getConnIndex(connHandle);
// connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
// If already running
if (connList[connIndex].pRssiClock == NULL)
{
return bleIncorrectMode;
}
// Stop timer
Util_stopClock(connList[connIndex].pRssiClock);
// Destroy the clock object
Clock_destruct(connList[connIndex].pRssiClock);
// Free clock struct
ICall_free(connList[connIndex].pRssiClock);
connList[connIndex].pRssiClock = NULL;
Display_clearLine(dispHandle, SC_ROW_ANY_CONN);
return SUCCESS;
}
/*********************************************************************
* @fn SimpleCentral_processPairState
*
* @brief Process the new paring state.
*
* @return none
*/
static void SimpleCentral_processPairState(uint8_t state,
scPairStateData_t* pPairData)
{
uint8_t status = pPairData->status;
if (state == GAPBOND_PAIRING_STATE_STARTED)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Pairing started");
}
else if (state == GAPBOND_PAIRING_STATE_COMPLETE)
{
if (status == SUCCESS)
{
linkDBInfo_t linkInfo;
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Pairing success");
#if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG)
if (linkDB_GetInfo(pPairData->connHandle, &linkInfo) == SUCCESS)
{
// If the peer was using private address, update with ID address
if ((linkInfo.addrType == ADDRTYPE_PUBLIC_ID ||
linkInfo.addrType == ADDRTYPE_RANDOM_ID) &&
!Util_isBufSet(linkInfo.addrPriv, 0, B_ADDR_LEN))
{
// Update the address of the peer to the ID address
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Addr updated: %s",
Util_convertBdAddr2Str(linkInfo.addr));
// Update the connection list with the ID address
uint8_t i = SimpleCentral_getConnIndex(pPairData->connHandle);
SIMPLECENTRAL_ASSERT(i < MAX_NUM_BLE_CONNS);
memcpy(connList[i].addr, linkInfo.addr, B_ADDR_LEN);
}
}
#endif // PRIVACY_1_2_CFG
}
else
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Pairing fail: %d", status);
}
}
else if (state == GAPBOND_PAIRING_STATE_ENCRYPTED)
{
if (status == SUCCESS)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Encryption success");
}
else
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Encryption failed: %d", status);
}
}
else if (state == GAPBOND_PAIRING_STATE_BOND_SAVED)
{
if (status == SUCCESS)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Bond save success");
}
else
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Bond save failed: %d", status);
}
}
}
/*********************************************************************
* @fn SimpleCentral_processPasscode
*
* @brief Process the Passcode request.
*
* @return none
*/
static void SimpleCentral_processPasscode(scPasscodeData_t *pData)
{
// Display passcode to user
if (pData->uiOutputs != 0)
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Passcode: %d",
B_APP_DEFAULT_PASSCODE);
}
// Send passcode response
GAPBondMgr_PasscodeRsp(pData->connHandle, SUCCESS, B_APP_DEFAULT_PASSCODE);
}
/*********************************************************************
* @fn SimpleCentral_startSvcDiscovery
*
* @brief Start service discovery.
*
* @return none
*/
static void SimpleCentral_startSvcDiscovery(void)
{
attExchangeMTUReq_t req;
// Initialize cached handles
svcStartHdl = svcEndHdl = 0;
discState = BLE_DISC_STATE_MTU;
// Discover GATT Server's Rx MTU size
req.clientRxMTU = scMaxPduSize - L2CAP_HDR_SIZE;
// ATT MTU size should be set to the minimum of the Client Rx MTU
// and Server Rx MTU values
VOID GATT_ExchangeMTU(scConnHandle, &req, selfEntity);
}
/*********************************************************************
* @fn SimpleCentral_processGATTDiscEvent
*
* @brief Process GATT discovery event
*
* @return none
*/
static void SimpleCentral_processGATTDiscEvent(gattMsgEvent_t *pMsg)
{
//TESTTTTT
// Now we can use GATT Read/Write
tbm_setItemStatus(&scMenuPerConn,SC_ITEM_GATTREAD | SC_ITEM_GATTWRITE, SC_ITEM_NONE);
if (discState == BLE_DISC_STATE_MTU)
{
// MTU size response received, discover simple service
if (pMsg->method == ATT_EXCHANGE_MTU_RSP)
{
/* uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),
HI_UINT16(SIMPLEPROFILE_SERV_UUID) };*/
uint8_t uuid[ATT_UUID_SIZE] = {simpleProfileServUUID};
discState = BLE_DISC_STATE_SVC;
// Discovery simple service
VOID GATT_DiscPrimaryServiceByUUID(pMsg->connHandle, uuid,ATT_UUID_SIZE, selfEntity);
}
}
else if (discState == BLE_DISC_STATE_SVC)
{
// Service found, store handles
if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
pMsg->msg.findByTypeValueRsp.numInfo > 0)
{
svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
}
// If procedure complete
if (((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP) &&
(pMsg->hdr.status == bleProcedureComplete)) ||
(pMsg->method == ATT_ERROR_RSP))
{
if (svcStartHdl != 0)
{
attReadByTypeReq_t req;
// Discover characteristic
discState = BLE_DISC_STATE_CHAR;
req.startHandle = svcStartHdl;
req.endHandle = svcEndHdl;
req.type.len = ATT_UUID_SIZE;
req.type.uuid[12] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
req.type.uuid[13] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
VOID GATT_DiscCharsByUUID(pMsg->connHandle, &req, selfEntity);
}
}
}
else if (discState == BLE_DISC_STATE_CHAR)
{
// Characteristic found, store handle
if ((pMsg->method == ATT_READ_BY_TYPE_RSP) &&
(pMsg->msg.readByTypeRsp.numPairs > 0))
{
uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
// connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
// Store the handle of the simpleprofile characteristic 1 value
connList[connIndex].charHandle
= BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[3],
pMsg->msg.readByTypeRsp.pDataList[4]);
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Simple Svc Found");
// Now we can use GATT Read/Write
tbm_setItemStatus(&scMenuPerConn,SC_ITEM_GATTREAD | SC_ITEM_GATTWRITE, SC_ITEM_NONE);
}
discState = BLE_DISC_STATE_IDLE;
}
}
#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
/**********************************************************************
* @fn SimpleCentral_findUuid
*
* @brief Find a given UUID in an advertiser's service UUID list.
*
* @return TRUE if service UUID found
*/
static bool SimpleCentral_findUuid(const uint8_t *uuid, const uint8_t *pManufData ,uint8_t manDataLen ,uint8_t *pData,
uint8_t dataLen)
{
uint8_t matchingIdUuid =0;
uint8_t adLen;
uint8_t adType;
uint8_t *pEnd;
pEnd = pData + dataLen - 1;
// while end of data not reached
while ((dataLen > 0) && (pData <pEnd))
{
// Get length of next AD item
adLen = *pData++;
if (adLen > 0)
{
adType = *pData;
// If AD type is for 128-bit service UUID
if ((adType == GAP_ADTYPE_128BIT_MORE) ||
(adType == GAP_ADTYPE_128BIT_COMPLETE))
{
pData++;
adLen--;
// For each UUID in list
while (adLen >= 2 && pData < pEnd)
{
// Check for match
if(!memcmp(pData,uuid,ATT_UUID_SIZE))
{
matchingIdUuid++;
}
// Go to next
pData += 2;
adLen -= 2;
}
// Handle possible erroneous extra byte in UUID list
if (adLen == 1)
{
pData++;
}
}
if(adType== GAP_ADTYPE_MANUFACTURER_SPECIFIC)
{
uint8_t len = adLen;
if(manDataLen < len)
{
len = manDataLen;
}
// Check for match
if(memcmp(pManufData,pData,len))
{
matchingIdUuid++;
}
}
if(matchingIdUuid<2) // 2 when we are cheking manuf data
{
// Go to next item
pData += adLen;
}
else
{
return TRUE;
}
}
}
// Match not found
return FALSE;
}
///*********************************************************************
// * @fn SimpleCentral_findSvcUuid
// *
// * @brief Find a given UUID in an advertiser's service UUID list.
// *
// * @return TRUE if service UUID found
// */
//static bool SimpleCentral_findSvcUuid(uint16_t uuid, uint8_t *pData,
// uint16_t dataLen)
//{
// uint8_t adLen;
// uint8_t adType;
// uint8_t *pEnd;
//
// if (dataLen > 0)
// {
// pEnd = pData + dataLen - 1;
//
// // While end of data not reached
// while (pData < pEnd)
// {
// // Get length of next AD item
// adLen = *pData++;
// if (adLen > 0)
// {
// adType = *pData;
//
// // If AD type is for 16-bit service UUID
// if ((adType == GAP_ADTYPE_16BIT_MORE) ||
// (adType == GAP_ADTYPE_16BIT_COMPLETE))
// {
// pData++;
// adLen--;
//
// // For each UUID in list
// while (adLen >= 2 && pData < pEnd)
// {
// // Check for match
// if ((pData[0] == LO_UINT16(uuid)) && (pData[1] == HI_UINT16(uuid)))
// {
// // Match found
// return TRUE;
// }
//
// // Go to next
// pData += 2;
// adLen -= 2;
// }
//
// // Handle possible erroneous extra byte in UUID list
// if (adLen == 1)
// {
// pData++;
// }
// }
// else
// {
// // Go to next item
// pData += adLen;
// }
// }
// }
// }
//
// // Match not found
// return FALSE;
//}
/*********************************************************************
* @fn SimpleCentral_addScanInfo
*
* @brief Add a device to the scanned device list
*
* @return none
*/
static void SimpleCentral_addScanInfo(uint8_t *pAddr, uint8_t addrType)
{
uint8_t i;
// If result count not at max
if (numScanRes < DEFAULT_MAX_SCAN_RES)
{
// Check if device is already in scan results
for (i = 0; i < numScanRes; i++)
{
if (memcmp(pAddr, scanList[i].addr , B_ADDR_LEN) == 0)
{
return;
}
}
// Add addr to scan result list
memcpy(scanList[numScanRes].addr, pAddr, B_ADDR_LEN);
scanList[numScanRes].addrType = addrType;
// Increment scan result count
numScanRes++;
}
}
#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
/*********************************************************************
* @fn SimpleCentral_addConnInfo
*
* @brief Add a device to the connected device list
*
* @return index of the connected device list entry where the new connection
* info is put in.
* if there is no room, MAX_NUM_BLE_CONNS will be returned.
*/
static uint8_t SimpleCentral_addConnInfo(uint16_t connHandle, uint8_t *pAddr)
{
uint8_t i;
for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
if (connList[i].connHandle == CONNHANDLE_INVALID)
{
// Found available entry to put a new connection info in
connList[i].connHandle = connHandle;
memcpy(connList[i].addr, pAddr, B_ADDR_LEN);
numConn++;
break;
}
}
return i;
}
/*********************************************************************
* @fn SimpleCentral_removeConnInfo
*
* @brief Remove a device from the connected device list
*
* @return index of the connected device list entry where the new connection
* info is removed from.
* if connHandle is not found, MAX_NUM_BLE_CONNS will be returned.
*/
static uint8_t SimpleCentral_removeConnInfo(uint16_t connHandle)
{
uint8_t i;
for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
if (connList[i].connHandle == connHandle)
{
// Found the entry to mark as deleted
connList[i].connHandle = CONNHANDLE_INVALID;
numConn--;
break;
}
}
return i;
}
/*********************************************************************
* @fn SimpleCentral_getConnIndex
*
* @brief Find index in the connected device list by connHandle
*
* @return the index of the entry that has the given connection handle.
* if there is no match, MAX_NUM_BLE_CONNS will be returned.
*/
static uint8_t SimpleCentral_getConnIndex(uint16_t connHandle)
{
uint8_t i;
for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
if (connList[i].connHandle == connHandle)
{
break;
}
}
return i;
}
#ifndef Display_DISABLE_ALL
/*********************************************************************
* @fn SimpleCentral_getConnAddrStr
*
* @brief Return, in string form, the address of the peer associated with
* the connHandle.
*
* @return A null-terminated string of the address.
* if there is no match, NULL will be returned.
*/
static char* SimpleCentral_getConnAddrStr(uint16_t connHandle)
{
uint8_t i;
for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
if (connList[i].connHandle == connHandle)
{
return Util_convertBdAddr2Str(connList[i].addr);
}
}
return NULL;
}
#endif
/*********************************************************************
* @fn SimpleCentral_pairStateCb
*
* @brief Pairing state callback.
*
* @return none
*/
static void SimpleCentral_pairStateCb(uint16_t connHandle, uint8_t state,
uint8_t status)
{
scPairStateData_t *pData;
// Allocate space for the event data.
if ((pData = ICall_malloc(sizeof(scPairStateData_t))))
{
pData->connHandle = connHandle;
pData->status = status;
// Queue the event.
if(SimpleCentral_enqueueMsg(SC_EVT_PAIR_STATE, state, (uint8_t*) pData) != SUCCESS)
{
ICall_free(pData);
}
}
}
/*********************************************************************
* @fn SimpleCentral_passcodeCb
*
* @brief Passcode callback.
*
* @param deviceAddr - pointer to device address
*
* @param connHandle - the connection handle
*
* @param uiInputs - pairing User Interface Inputs
*
* @param uiOutputs - pairing User Interface Outputs
*
* @param numComparison - numeric Comparison 20 bits
*
* @return none
*/
static void SimpleCentral_passcodeCb(uint8_t *deviceAddr, uint16_t connHandle,
uint8_t uiInputs, uint8_t uiOutputs,
uint32_t numComparison)
{
scPasscodeData_t *pData = ICall_malloc(sizeof(scPasscodeData_t));
// Allocate space for the passcode event.
if (pData)
{
pData->connHandle = connHandle;
memcpy(pData->deviceAddr, deviceAddr, B_ADDR_LEN);
pData->uiInputs = uiInputs;
pData->uiOutputs = uiOutputs;
pData->numComparison = numComparison;
// Enqueue the event.
if (SimpleCentral_enqueueMsg(SC_EVT_PASSCODE_NEEDED, 0,(uint8_t *) pData) != SUCCESS)
{
ICall_free(pData);
}
}
}
/*********************************************************************
* @fn SimpleCentral_keyChangeHandler
*
* @brief Key event handler function
*
* @param a0 - ignored
*
* @return none
*/
static void SimpleCentral_keyChangeHandler(uint8 keys)
{
SimpleCentral_enqueueMsg(SC_EVT_KEY_CHANGE, keys, NULL);
}
/*********************************************************************
* @fn SimpleCentral_clockHandler
*
* @brief clock handler function
*
* @param arg - argument from the clock initiator
*
* @return none
*/
void SimpleCentral_clockHandler(UArg arg)
{
uint8_t evtId = (uint8_t) (arg & 0xFF);
switch (evtId)
{
case SC_EVT_READ_RSSI:
SimpleCentral_enqueueMsg(SC_EVT_READ_RSSI, (uint8_t) (arg >> 8) , NULL);
break;
case SC_EVT_READ_RPA:
// Restart timer
Util_startClock(&clkRpaRead);
// Let the application handle the event
SimpleCentral_enqueueMsg(SC_EVT_READ_RPA, 0, NULL);
break;
default:
break;
}
}
/*********************************************************************
* @fn SimpleCentral_enqueueMsg
*
* @brief Creates a message and puts the message in RTOS queue.
*
* @param event - message event.
* @param state - message state.
* @param pData - message data pointer.
*
* @return TRUE or FALSE
*/
static status_t SimpleCentral_enqueueMsg(uint8_t event, uint8_t state,
uint8_t *pData)
{
uint8_t success;
scEvt_t *pMsg = ICall_malloc(sizeof(scEvt_t));
// Create dynamic pointer to message.
if (pMsg)
{
pMsg->hdr.event = event;
pMsg->hdr.state = state;
pMsg->pData = pData;
// Enqueue the message.
success = Util_enqueueMsg(appMsgQueue, syncEvent, (uint8_t *)pMsg);
return (success) ? SUCCESS : FAILURE;
}
return(bleMemAllocError);
}
/*********************************************************************
* @fn SimpleCentral_scanCb
*
* @brief Callback called by GapScan module
*
* @param evt - event
* @param msg - message coming with the event
* @param arg - user argument
*
* @return none
*/
void SimpleCentral_scanCb(uint32_t evt, void* pMsg, uintptr_t arg)
{
uint8_t event;
if (evt & GAP_EVT_ADV_REPORT)
{
event = SC_EVT_ADV_REPORT;
}
else if (evt & GAP_EVT_SCAN_ENABLED)
{
event = SC_EVT_SCAN_ENABLED;
}
else if (evt & GAP_EVT_SCAN_DISABLED)
{
event = SC_EVT_SCAN_DISABLED;
}
else if (evt & GAP_EVT_INSUFFICIENT_MEMORY)
{
event = SC_EVT_INSUFFICIENT_MEM;
}
else
{
return;
}
if(SimpleCentral_enqueueMsg(event, SUCCESS, pMsg) != SUCCESS)
{
ICall_free(pMsg);
}
}
/*********************************************************************
* @fn SimpleCentral_doSetScanPhy
*
* @brief Set PHYs for scanning.
*
* @param index - 0: 1M PHY
* 1: CODED PHY (Long range)
*
* @return always true
*/
bool SimpleCentral_doSetScanPhy(uint8_t index)
{
uint8_t temp8;
if (index == 0)
{
temp8 = SCAN_PRIM_PHY_1M;
}
else
{
temp8 = SCAN_PRIM_PHY_CODED;
}
// Set scanning primary PHY
GapScan_setParam(SCAN_PARAM_PRIM_PHYS, &temp8);
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Primary Scan PHY: %s",
TBM_GET_ACTION_DESC(&scMenuScanPhy, index));
tbm_goTo(&scMenuMain);
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doDiscoverDevices
*
* @brief Enables scanning
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doDiscoverDevices(uint8_t index)
{
(void) index;
#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
// Scanning for DEFAULT_SCAN_DURATION x 10 ms.
// The stack does not need to record advertising reports
// since the application will filter them by Service UUID and save.
// Reset number of scan results to 0 before starting scan
numScanRes = 0;
GapScan_enable(0, DEFAULT_SCAN_DURATION, 0);
#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
// Scanning for DEFAULT_SCAN_DURATION x 10 ms.
// Let the stack record the advertising reports as many as up to DEFAULT_MAX_SCAN_RES.
GapScan_enable(0, DEFAULT_SCAN_DURATION, DEFAULT_MAX_SCAN_RES);
#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
// Enable only "Stop Discovering" and disable all others in the main menu
tbm_setItemStatus(&scMenuMain, SC_ITEM_STOPDISC,
(SC_ITEM_ALL & ~SC_ITEM_STOPDISC));
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doStopDiscovering
*
* @brief Stop on-going scanning
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doStopDiscovering(uint8_t index)
{
(void) index;
GapScan_disable();
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doEstablishLink
*
* @brief Establish a link to a peer device
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doConnect(uint8_t index)
{
#if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
GapInit_connect(scanList[index].addrType & MASK_ADDRTYPE_ID,
scanList[index].addr, DEFAULT_INIT_PHY, 0);
#else // !DEFAULT_DEV_DISC_BY_SVC_UUID
GapScan_Evt_AdvRpt_t advRpt;
GapScan_getAdvReport(index, &advRpt);
GapInit_connect(advRpt.addrType & MASK_ADDRTYPE_ID,
advRpt.addr, DEFAULT_INIT_PHY, 0);
#endif // DEFAULT_DEV_DISC_BY_SVC_UUID
// Enable only "Cancel Connecting" and disable all others in the main menu
tbm_setItemStatus(&scMenuMain, SC_ITEM_CANCELCONN,
(SC_ITEM_ALL & ~SC_ITEM_CANCELCONN));
Display_printf(dispHandle, SC_ROW_NON_CONN, 0, "Connecting...");
tbm_goTo(&scMenuMain);
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doCancelConnecting
*
* @brief Cancel on-going connection attempt
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doCancelConnecting(uint8_t index)
{
(void) index;
GapInit_cancelConnect();
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doSelectConn
*
* @brief Select a connection to communicate with
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doSelectConn(uint8_t index)
{
uint32_t itemsToDisable = SC_ITEM_NONE;
// index cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(index < MAX_NUM_BLE_CONNS);
scConnHandle = connList[index].connHandle;
if (connList[index].charHandle == 0)
{
// Initiate service discovery
SimpleCentral_enqueueMsg(SC_EVT_SVC_DISC, 0, NULL);
// Diable GATT Read/Write until simple service is found
itemsToDisable = SC_ITEM_GATTREAD | SC_ITEM_GATTWRITE;
}
// Set the menu title and go to this connection's context
TBM_SET_TITLE(&scMenuPerConn, TBM_GET_ACTION_DESC(&scMenuSelectConn, index));
// Set RSSI items properly depending on current state
if (connList[index].pRssiClock == NULL)
{
tbm_setItemStatus(&scMenuPerConn,
SC_ITEM_STRTRSSI, SC_ITEM_STOPRSSI | itemsToDisable);
}
else
{
tbm_setItemStatus(&scMenuPerConn,
SC_ITEM_STOPRSSI, SC_ITEM_STRTRSSI | itemsToDisable);
}
// Clear non-connection-related message
Display_clearLine(dispHandle, SC_ROW_NON_CONN);
tbm_goTo(&scMenuPerConn);
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doGattRead
*
* @brief GATT Read
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doGattRead(uint8_t index)
{
attReadReq_t req;
uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
// connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
req.handle = connList[connIndex].charHandle;
GATT_ReadCharValue(scConnHandle, &req, selfEntity);
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doGattWrite
*
* @brief GATT Write
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doGattWrite(uint8_t index)
{
status_t status;
uint8_t charVals[4] = { 0x00, 0x55, 0xAA, 0xFF }; // Should be consistent with
// those in scMenuGattWrite
attWriteReq_t req;
req.pValue = GATT_bm_alloc(scConnHandle, ATT_WRITE_REQ, 1, NULL);
if ( req.pValue != NULL )
{
uint8_t connIndex = SimpleCentral_getConnIndex(scConnHandle);
// connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
SIMPLECENTRAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
req.handle = connList[connIndex].charHandle;
req.len = 1;
charVal = charVals[index];
req.pValue[0] = charVal;
req.sig = 0;
req.cmd = 0;
status = GATT_WriteCharValue(scConnHandle, &req, selfEntity);
if ( status != SUCCESS )
{
GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
}
}
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doRssiRead
*
* @brief Toggle RSSI Read
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doRssiRead(uint8_t index)
{
status_t status;
if ((1 << index) == SC_ITEM_STRTRSSI)
{
if ((status = SimpleCentral_StartRssi()) == SUCCESS)
{
tbm_setItemStatus(&scMenuPerConn, SC_ITEM_STOPRSSI, SC_ITEM_STRTRSSI);
}
}
else // SC_ITEM_STOP_RSSI
{
if ((status = SimpleCentral_CancelRssi(scConnHandle)) == SUCCESS)
{
tbm_setItemStatus(&scMenuPerConn, SC_ITEM_STRTRSSI, SC_ITEM_STOPRSSI);
}
}
return ((status == SUCCESS) ? true : false);
}
/*********************************************************************
* @fn SimpleCentral_doConnUpdate
*
* @brief Initiate Connection Update procedure
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doConnUpdate(uint8_t index)
{
gapUpdateLinkParamReq_t params;
(void) index;
params.connectionHandle = scConnHandle;
params.intervalMin = DEFAULT_UPDATE_MIN_CONN_INTERVAL;
params.intervalMax = DEFAULT_UPDATE_MAX_CONN_INTERVAL;
params.connLatency = DEFAULT_UPDATE_SLAVE_LATENCY;
linkDBInfo_t linkInfo;
if (linkDB_GetInfo(scConnHandle, &linkInfo) == SUCCESS)
{
if (linkInfo.connTimeout == DEFAULT_UPDATE_CONN_TIMEOUT)
{
params.connTimeout = DEFAULT_UPDATE_CONN_TIMEOUT + 200;
}
else
{
params.connTimeout = DEFAULT_UPDATE_CONN_TIMEOUT;
}
}
else
{
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0,
"update :%s, Unable to find link information",
Util_convertBdAddr2Str(linkInfo.addr));
}
GAP_UpdateLinkParamReq(¶ms);
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "Param update Request:connTimeout =%d",
params.connTimeout*CONN_TIMEOUT_MS_CONVERSION);
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doSetConnPhy
*
* @brief Set Connection PHY preference.
*
* @param index - 0: 1M PHY
* 1: 2M PHY
* 2: 1M + 2M PHY
* 3: CODED PHY (Long range)
* 4: 1M + 2M + CODED PHY
*
* @return always true
*/
bool SimpleCentral_doSetConnPhy(uint8_t index)
{
static uint8_t phy[] = {
HCI_PHY_1_MBPS, HCI_PHY_2_MBPS, HCI_PHY_1_MBPS | HCI_PHY_2_MBPS,
HCI_PHY_CODED, HCI_PHY_1_MBPS | HCI_PHY_2_MBPS | HCI_PHY_CODED,
};
// Set Phy Preference on the current connection. Apply the same value
// for RX and TX. For more information, see the LE 2M PHY section in the User's Guide:
// http://software-dl.ti.com/lprf/ble5stack-latest/
// Note PHYs are already enabled by default in build_config.opt in stack project.
HCI_LE_SetPhyCmd(scConnHandle, 0, phy[index], phy[index], 0);
Display_printf(dispHandle, SC_ROW_CUR_CONN, 0, "PHY preference: %s",
TBM_GET_ACTION_DESC(&scMenuConnPhy, index));
return (true);
}
/*********************************************************************
* @fn SimpleCentral_doDisconnect
*
* @brief Disconnect the specified link
*
* @param index - item index from the menu
*
* @return always true
*/
bool SimpleCentral_doDisconnect(uint8_t index)
{
(void) index;
GAP_TerminateLinkReq(scConnHandle, HCI_DISCONNECT_REMOTE_USER_TERM);
return (true);
}
/*********************************************************************
* @fn SimpleCentral_menuSwitchCb
*
* @brief Detect menu context switching
*
* @param pMenuObjCurr - the current menu object
* @param pMenuObjNext - the menu object the context is about to switch to
*
* @return none
*/
static void SimpleCentral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr,
tbmMenuObj_t* pMenuObjNext)
{
// interested in only the events of
// entering scMenuConnect, scMenuSelectConn, and scMenuMain for now
if (pMenuObjNext == &scMenuConnect)
{
uint8_t i, j;
uint32_t itemsToDisable = SC_ITEM_NONE;
for (i = 0; i < TBM_GET_NUM_ITEM(&scMenuConnect); i++)
{
for (j = 0; j < MAX_NUM_BLE_CONNS; j++)
{
if ((connList[j].connHandle != CONNHANDLE_INVALID) &&
!memcmp(TBM_GET_ACTION_DESC(&scMenuConnect, i),
Util_convertBdAddr2Str(connList[j].addr),
SC_ADDR_STR_SIZE))
{
// Already connected. Add to the set to be disabled.
itemsToDisable |= (1 << i);
}
}
}
// Eventually only non-connected device addresses will be displayed.
tbm_setItemStatus(&scMenuConnect,
SC_ITEM_ALL & ~itemsToDisable, itemsToDisable);
}
else if (pMenuObjNext == &scMenuSelectConn)
{
static uint8_t* pAddrs;
uint8_t* pAddrTemp;
if (pAddrs != NULL)
{
ICall_free(pAddrs);
}
// Allocate buffer to display addresses
pAddrs = ICall_malloc(numConn * SC_ADDR_STR_SIZE);
if (pAddrs == NULL)
{
TBM_SET_NUM_ITEM(&scMenuSelectConn, 0);
}
else
{
uint8_t i;
TBM_SET_NUM_ITEM(&scMenuSelectConn, MAX_NUM_BLE_CONNS);
pAddrTemp = pAddrs;
// Add active connection info to the menu object
for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
if (connList[i].connHandle != CONNHANDLE_INVALID)
{
// This connection is active. Set the corresponding menu item with
// the address of this connection and enable the item.
memcpy(pAddrTemp, Util_convertBdAddr2Str(connList[i].addr),
SC_ADDR_STR_SIZE);
TBM_SET_ACTION_DESC(&scMenuSelectConn, i, pAddrTemp);
tbm_setItemStatus(&scMenuSelectConn, (1 << i), SC_ITEM_NONE);
pAddrTemp += SC_ADDR_STR_SIZE;
}
else
{
// This connection is not active. Disable the corresponding menu item.
tbm_setItemStatus(&scMenuSelectConn, SC_ITEM_NONE, (1 << i));
}
}
}
}
else if (pMenuObjNext == &scMenuMain)
{
// Now we are not in a specific connection's context
scConnHandle = CONNHANDLE_INVALID;
// Clear connection-related message
Display_clearLine(dispHandle, SC_ROW_CUR_CONN);
}
}
/*********************************************************************
*********************************************************************/
Please let me know what more changes require so , i can read /write GATT msgs .
Modified Project zero .
/*
******************************************************************************
*****************************************************************************/
/*******************************************************************************
* INCLUDES
*/
#define FMCU_APP
#define DEVINFO_SYSTEM_ID_LEN 8
#include <string.h>
#if !(defined __TI_COMPILER_VERSION__)
#include <intrinsics.h>
#endif
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/BIOS.h>
#include <ti/drivers/UART.h>
#include <ti/drivers/uart/UARTCC26X2.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Queue.h>
#include <ti/drivers/utils/List.h>
//#include <xdc/runtime/Log.h> // Comment this in to use xdc.runtime.Log
#include <uartlog/UartLog.h> // Comment out if using xdc Log
#include <ti/display/AnsiColor.h>
#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
#include <icall.h>
#include <bcomdef.h>
/* This Header file contains all BLE API and icall structure definition */
#include <icall_ble_api.h>
/* Bluetooth Profiles */
//#include <devinfoservice.h>
#ifndef FMCU_APP
#include <services/button_service.h>
#include <services/led_service.h>
#endif
#define USE_RCOSC
/* Stack size in bytes */
#define THREADSTACKSIZE 1024
UART_Handle temp_handle;
uint8_t Start_timer_100msec=0;
#include <services/data_service.h>
/* Application specific includes */
#include <Board.h>
#include <project_zero.h>
#include <util.h>
#ifdef USE_RCOSC
#include "rcosc_calibration.h"
#endif //USE_RCOSC
/*********************************************************************
* MACROS
*/
// Spin if the expression is not true
#define APP_ASSERT(expr) if(!(expr)) {project_zero_spin();}
#define UTIL_ARRTOHEX_REVERSE 1
#define UTIL_ARRTOHEX_NO_REVERSE 0
/*********************************************************************
* CONSTANTS
*/
uint8_t Wake_Event_Sts=0;
uint8_t TX_Count=0;
// Task configuration
#define PZ_TASK_PRIORITY 1
#ifndef PZ_TASK_STACK_SIZE
#define PZ_TASK_STACK_SIZE 2048
#endif
// Internal Events for RTOS application
#define PZ_ICALL_EVT ICALL_MSG_EVENT_ID // Event_Id_31
#define PZ_APP_MSG_EVT Event_Id_30
// Bitwise OR of all RTOS events to pend on
#define PZ_ALL_EVENTS ( PZ_ICALL_EVT | PZ_APP_MSG_EVT )
// Types of messages that can be sent to the user application task from other
// tasks or interrupts. Note: Messages from BLE Stack are sent differently.
#define PZ_SERVICE_WRITE_EVT 0 /* A characteristic value has been written */
#define PZ_SERVICE_CFG_EVT 1 /* A characteristic configuration has changed */
#define PZ_UPDATE_CHARVAL_EVT 2 /* Request from ourselves to update a value */
#define PZ_BUTTON_DEBOUNCED_EVT 3 /* A button has been debounced with new value */
#define PZ_PAIRSTATE_EVT 4 /* The pairing state is updated */
#define PZ_PASSCODE_EVT 5 /* A pass-code/PIN is requested during pairing */
#define PZ_ADV_EVT 6 /* A subscribed advertisement activity */
#define PZ_START_ADV_EVT 7 /* Request advertisement start from task ctx */
#define PZ_SEND_PARAM_UPD_EVT 8 /* Request parameter update req be sent */
#define PZ_CONN_EVT 9 /* Connection Event End notice */
// General discoverable mode: advertise indefinitely
#define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL
// Minimum connection interval (units of 1.25ms, 80=100ms) for parameter update request
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 12
// Maximum connection interval (units of 1.25ms, 800=1000ms) for parameter update request
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 36
// Slave latency to use for parameter update request
#define DEFAULT_DESIRED_SLAVE_LATENCY 0
// Supervision timeout value (units of 10ms, 1000=10s) for parameter update request
#define DEFAULT_DESIRED_CONN_TIMEOUT 200
// Supervision timeout conversion rate to miliseconds
#define CONN_TIMEOUT_MS_CONVERSION 10
// Connection interval conversion rate to miliseconds
#define CONN_INTERVAL_MS_CONVERSION 1.25
// Pass parameter updates to the app for it to decide.
#define DEFAULT_PARAM_UPDATE_REQ_DECISION GAP_UPDATE_REQ_PASS_TO_APP
// Delay (in ms) after connection establishment before sending a parameter update requst
#define PZ_SEND_PARAM_UPDATE_DELAY 6000
/*********************************************************************
* TYPEDEFS
*/
// Struct for messages sent to the application task
typedef struct
{
uint8_t event;
void *pData;
} pzMsg_t;
// Struct for messages about characteristic data
typedef struct
{
uint16_t svcUUID; // UUID of the service
uint16_t dataLen; //
uint8_t paramID; // Index of the characteristic
uint8_t data[]; // Flexible array member, extended to malloc - sizeof(.)
} pzCharacteristicData_t;
// Struct for message about sending/requesting passcode from peer.
typedef struct
{
uint16_t connHandle;
uint8_t uiInputs;
uint8_t uiOutputs;
uint32_t numComparison;
} pzPasscodeReq_t;
// Struct for message about a pending parameter update request.
typedef struct
{
uint16_t connHandle;
} pzSendParamReq_t;
#ifndef FMCU_APP
// Struct for message about button state
typedef struct
{
PIN_Id pinId;
uint8_t state;
} pzButtonState_t;
#endif
// Container to store passcode data when passing from gapbondmgr callback
// to app event. See the pfnPairStateCB_t documentation from the gapbondmgr.h
// header file for more information on each parameter.
typedef struct
{
uint8_t state;
uint16_t connHandle;
uint8_t status;
} pzPairStateData_t;
// Container to store passcode data when passing from gapbondmgr callback
// to app event. See the pfnPasscodeCB_t documentation from the gapbondmgr.h
// header file for more information on each parameter.
typedef struct
{
uint8_t deviceAddr[B_ADDR_LEN];
uint16_t connHandle;
uint8_t uiInputs;
uint8_t uiOutputs;
uint32_t numComparison;
} pzPasscodeData_t;
// Container to store advertising event data when passing from advertising
// callback to app event. See the respective event in GapAdvScan_Event_IDs
// in gap_advertiser.h for the type that pBuf should be cast to.
typedef struct
{
uint32_t event;
void *pBuf;
} pzGapAdvEventData_t;
// List element for parameter update and PHY command status lists
typedef struct
{
List_Elem elem;
uint16_t *connHandle;
} pzConnHandleEntry_t;
// Connected device information
typedef struct
{
uint16_t connHandle; // Connection Handle
Clock_Struct* pUpdateClock; // pointer to clock struct
bool phyCngRq; // Set to true if PHY change request is in progress
uint8_t currPhy; // The active PHY for a connection
uint8_t rqPhy; // The requested PHY for a connection
uint8_t phyRqFailCnt; // PHY change request fail count
} pzConnRec_t;
/*********************************************************************
* GLOBAL VARIABLES
*/
// Task configuration
Task_Struct pzTask;
#if defined __TI_COMPILER_VERSION__
#pragma DATA_ALIGN(appTaskStack, 8)
#else
#pragma data_alignment=8
#endif
uint8_t appTaskStack[PZ_TASK_STACK_SIZE];
static uint8_t ResponseStatus=0;
/*********************************************************************
* LOCAL VARIABLES
*/
// Entity ID globally used to check for source and/or destination of messages
static ICall_EntityID selfEntity;
// Event globally used to post local events and pend on system and
// local events.
static ICall_SyncHandle syncEvent;
// Queue object used for app messages
static Queue_Struct appMsgQueue;
static Queue_Handle appMsgQueueHandle;
// GAP GATT Attributes
static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "FMCU MML";
/*
// Advertisement data
static uint8_t advertData[] =
{
0x02, // length of this data
GAP_ADTYPE_FLAGS,
DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
// complete name
14, // length of this data
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'T',
'T',
'T',
'T',
'-',
'B',
'L',
'E',
'-',
'T',
'T',
'T',
'T',
};
// Scan Response Data
static uint8_t scanRspData[] =
{
// service UUID, to notify central devices what services are included
// in this peripheral
//(ATT_UUID_SIZE + 0x01), // length of this data, LED service UUID + header
//GAP_ADTYPE_128BIT_MORE, // some of the UUID's, but not all
// (ATT_UUID_SIZE),
// GAP_ADTYPE_128BIT_COMPLETE,
// DATA_SERVICE_SERV_UUID_BASE128(DATA_SERVICE_SERV_UUID),
};
*/
// Advertisement data
static uint8_t advertData[] =
{
0x02, // length of this data
GAP_ADTYPE_FLAGS,
DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
// service UUID, to notify central devices what services are included
// in this peripheral
0x03, // length of this data //0x03
GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all //GAP_ADTYPE_16BIT_MORE
//GAP_ADTYPE_16BIT_COMPLETE,
//DATA_SERVICE_SERV_UUID_BASE128(DATA_SERVICE_SERV_UUID),
LO_UINT16(DATA_SERVICE_SERV_UUID),
HI_UINT16(DATA_SERVICE_SERV_UUID)
};
// Scan Response Data
static uint8_t scanRspData[] =
{
// complete name
14, // length of this data
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'T',
'T',
'T',
'T',
'-',
'B',
'L',
'E',
'-',
'T',
'T',
'T',
'T',
// connection interval range
5, // length of this data
GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL), // 100ms
HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),
LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL), // 1s
HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),
// Tx power level
2, // length of this data
GAP_ADTYPE_POWER_LEVEL,
0 // 0dBm
};
// Advertising handles
static uint8_t advHandleLegacy;
// Per-handle connection info
static pzConnRec_t connList[MAX_NUM_BLE_CONNS];
// List to store connection handles for set phy command status's
static List_List setPhyCommStatList;
// List to store connection handles for queued param updates
static List_List paramUpdateList;
#ifndef FMCU_APP
/* Pin driver handles */
static PIN_Handle buttonPinHandle;
static PIN_Handle ledPinHandle;
/* Global memory storage for a PIN_Config table */
static PIN_State buttonPinState;
static PIN_State ledPinState;
#endif
//#ifdef FMCU_APP_WAKEUP
static PIN_Handle ledPinHandle;
static PIN_State ledPinState;
//#endif
/*
* Initial LED pin configuration table
* - LEDs Board_PIN_LED0 & Board_PIN_LED1 are off.
*/
PIN_Config ledPinTable[] =
{
Board_PIN_RLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
Board_PIN_GLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
WakeUp_Event | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
PIN_TERMINATE
};
#ifdef FMCU_APP
/*
* Application button pin configuration table:
* - Buttons interrupts are configured to trigger on falling edge.
*/
PIN_Config buttonPinTable[] = {
Board_PIN_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
Board_PIN_BUTTON1 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
PIN_TERMINATE
};
// Clock objects for debouncing the buttons
static Clock_Struct button0DebounceClock;
static Clock_Struct button1DebounceClock;
static Clock_Handle button0DebounceClockHandle;
static Clock_Handle button1DebounceClockHandle;
// State of the buttons
static uint8_t button0State = 0;
static uint8_t button1State = 0;
#endif
static Clock_Struct Wait_Timer_100ms_Clock;
static Clock_Handle Wait_Timer_100ms;
// Create the FMCU WakeUp Event clock objects
static void WakeupEventFunction();
static void WakeupEventFunction()
{
Start_timer_100msec=0;
}
//Util_startClock((Clock_Struct *)LeadMe_WakeUpClockHandle);
//Util_startClock((Clock_Struct *)EngineStart_WakeUpClockHandle);
/*********************************************************************
* LOCAL FUNCTIONS
*/
void Send_Packet(UART_Handle handle);
char* itoa(int num, char* str, int base);
/* Task functions */
static void ProjectZero_init(void);
static void ProjectZero_taskFxn(UArg a0,UArg a1);
/* Event message processing functions */
static void ProjectZero_processStackEvent(uint32_t stack_event);
//static void ProjectZero_processApplicationMessage(pzMsg_t *pMsg,UART_Handle handle);
static void ProjectZero_processApplicationMessage(pzMsg_t *pMsg);
static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
static void ProjectZero_processGapMessage(gapEventHdr_t *pMsg);
static void ProjectZero_processHCIMsg(ICall_HciExtEvt *pMsg);
//static void ProjectZero_processHCIMsg(ICall_HciExtEvt *pMsg,UART_Handle Uart_handle);
static void ProjectZero_processPairState(pzPairStateData_t *pPairState);
static void ProjectZero_processPasscode(pzPasscodeReq_t *pReq);
static void ProjectZero_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg);
//static void ProjectZero_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg,UART_Handle Uart_handle);
static void ProjectZero_processAdvEvent(pzGapAdvEventData_t *pEventData);
/* Profile value change handlers */
static void ProjectZero_updateCharVal(pzCharacteristicData_t *pCharData);
#ifdef FMCU_APP
static void ProjectZero_LedService_ValueChangeHandler(pzCharacteristicData_t *pCharData);
static void ProjectZero_ButtonService_CfgChangeHandler(pzCharacteristicData_t *pCharData);
#endif
static void ProjectZero_DataService_ValueChangeHandler(pzCharacteristicData_t *pCharData);
//static void ProjectZero_DataService_ValueChangeHandler(pzCharacteristicData_t *pCharData,UART_Handle handle);
static void ProjectZero_DataService_CfgChangeHandler(pzCharacteristicData_t *pCharData);
/* Stack or profile callback function */
static void ProjectZero_advCallback(uint32_t event,
void *pBuf,
uintptr_t arg);
static void ProjectZero_passcodeCb(uint8_t *pDeviceAddr,
uint16_t connHandle,
uint8_t uiInputs,
uint8_t uiOutputs,
uint32_t numComparison);
static void ProjectZero_pairStateCb(uint16_t connHandle,
uint8_t state,
uint8_t status);
#ifndef FMCU_APP
static void ProjectZero_LedService_ValueChangeCB(uint16_t connHandle,
uint8_t paramID,
uint16_t len,
uint8_t *pValue);
#endif
static void ProjectZero_DataService_ValueChangeCB(uint16_t connHandle,
uint8_t paramID,
uint16_t len,
uint8_t *pValue);
#ifndef FMCU_APP
static void ProjectZero_ButtonService_CfgChangeCB(uint16_t connHandle,
uint8_t paramID,
uint16_t len,
uint8_t *pValue);
#endif
static void ProjectZero_DataService_CfgChangeCB(uint16_t connHandle,
uint8_t paramID,
uint16_t len,
uint8_t *pValue);
/* Connection handling functions */
static uint8_t ProjectZero_getConnIndex(uint16_t connHandle);
static uint8_t ProjectZero_clearConnListEntry(uint16_t connHandle);
static uint8_t ProjectZero_addConn(uint16_t connHandle);
static uint8_t ProjectZero_removeConn(uint16_t connHandle);
static void ProjectZero_updatePHYStat(uint16_t eventCode,uint8_t *pMsg);
static void ProjectZero_handleUpdateLinkParamReq(gapUpdateLinkParamReqEvent_t *pReq);
static void ProjectZero_sendParamUpdate(uint16_t connHandle);
static void ProjectZero_handleUpdateLinkEvent(gapLinkUpdateEvent_t *pEvt);
static void ProjectZero_paramUpdClockHandler(UArg arg);
static void ProjectZero_processConnEvt(Gap_ConnEventRpt_t *pReport);
#ifndef FMCU_APP
/* Button handling functions */
static void buttonDebounceSwiFxn(UArg buttonId);
static void buttonCallbackFxn(PIN_Handle handle,PIN_Id pinId);
static void ProjectZero_handleButtonPress(pzButtonState_t *pState);
#endif
/* Utility functions */
static status_t ProjectZero_enqueueMsg(uint8_t event,void *pData);
static char * util_arrtohex(uint8_t const *src,
uint8_t src_len,
uint8_t *dst,
uint8_t dst_len,
uint8_t reverse);
static char * util_getLocalNameStr(const uint8_t *advData, uint8_t len);
static void ProjectZero_processL2CAPMsg(l2capSignalEvent_t *pMsg);
/*********************************************************************
* EXTERN FUNCTIONS
*/
extern void AssertHandler(uint8_t assertCause,
uint8_t assertSubcause);
/*********************************************************************
* PROFILE CALLBACKS
*/
// GAP Bond Manager Callbacks
static gapBondCBs_t ProjectZero_BondMgrCBs =
{
ProjectZero_passcodeCb, // Passcode callback
ProjectZero_pairStateCb // Pairing/Bonding state Callback
};
/*
* Callbacks in the user application for events originating from BLE services.
*/
#ifndef FMCU_APP
// LED Service callback handler.
// The type LED_ServiceCBs_t is defined in led_service.h
static LedServiceCBs_t ProjectZero_LED_ServiceCBs =
{
.pfnChangeCb = ProjectZero_LedService_ValueChangeCB, // Characteristic value change callback handler
.pfnCfgChangeCb = NULL, // No notification-/indication enabled chars in LED Service
};
// Button Service callback handler.
// The type Button_ServiceCBs_t is defined in button_service.h
static ButtonServiceCBs_t ProjectZero_Button_ServiceCBs =
{
.pfnChangeCb = NULL, // No writable chars in Button Service, so no change handler.
.pfnCfgChangeCb = ProjectZero_ButtonService_CfgChangeCB, // Noti/ind configuration callback handler
};
#endif
// Data Service callback handler.
// The type Data_ServiceCBs_t is defined in data_service.h
static DataServiceCBs_t ProjectZero_Data_ServiceCBs =
{
.pfnChangeCb = ProjectZero_DataService_ValueChangeCB, // Characteristic value change callback handler
.pfnCfgChangeCb = ProjectZero_DataService_CfgChangeCB, // Noti/ind configuration callback handler
};
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/*********************************************************************
* @fn project_zero_spin
*
* @brief Spin forever
*/
static void project_zero_spin(void)
{
volatile uint8_t x = 0;;
while(1)
{
x++;
}
}
/*********************************************************************
* @fn ProjectZero_createTask
*
* @brief Task creation function for the Project Zero.
*/
void ProjectZero_createTask()
{
Task_Params taskParams;
// Configure task
Task_Params_init(&taskParams);
taskParams.stack = appTaskStack;
taskParams.stackSize = PZ_TASK_STACK_SIZE;
taskParams.priority = PZ_TASK_PRIORITY;
Task_construct(&pzTask, ProjectZero_taskFxn, &taskParams, NULL);
}
/*********************************************************************
* @fn ProjectZero_init
*
* @brief Called during initialization and contains application
* specific initialization (ie. hardware initialization/setup,
* table initialization, power up notification, etc), and
* profile initialization/setup.
*/
static void ProjectZero_init(void)
{
//Log_info0( "Entering in ProjectZero_init");
// ******************************************************************
// NO STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
// ******************************************************************
// Register the current thread as an ICall dispatcher application
// so that the application can send and receive messages.
ICall_registerApp(&selfEntity, &syncEvent);
// Initialize queue for application messages.
// Note: Used to transfer control to application thread from e.g. interrupts.
Queue_construct(&appMsgQueue, NULL);
appMsgQueueHandle = Queue_handle(&appMsgQueue);
// ******************************************************************
// Hardware initialization
// ******************************************************************
#ifdef USE_RCOSC
RCOSC_enableCalibration();
#endif // USE_RCOSC
#ifndef FMCU_APP_WAKEUP
Wait_Timer_100ms = Util_constructClock(&Wait_Timer_100ms_Clock,
WakeupEventFunction,100,
0,
0,
0);
#endif
// Set the Device Name characteristic in the GAP GATT Service
// For more information, see the section in the User's Guide:
// http://software-dl.ti.com/lprf/ble5stack-latest/
// Log_info1("Line 636","Entering in GGS_SetParameter");
GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
// Configure GAP for param update
{
uint16_t paramUpdateDecision = DEFAULT_PARAM_UPDATE_REQ_DECISION;
// Pass all parameter update requests to the app for it to decide
GAP_SetParamValue(GAP_PARAM_LINK_UPDATE_DECISION, paramUpdateDecision);
}
// Setup the GAP Bond Manager. For more information see the GAP Bond Manager
// section in the User's Guide:
// http://software-dl.ti.com/lprf/ble5stack-latest/
{
// Don't send a pairing request after connecting (the peer device must
// initiate pairing)
uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
// Use authenticated pairing: require passcode.
uint8_t mitm = TRUE;
// This device only has display capabilities. Therefore, it will display the
// passcode during pairing. However, since the default passcode is being
// used, there is no need to display anything.
uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
// Request bonding (storing long-term keys for re-encryption upon subsequent
// connections without repairing)
uint8_t bonding = TRUE;
// For Authentication
uint8_t gapbondSecure = GAPBOND_SECURE_CONNECTION_ONLY;//Only Secure connection only
GAPBondMgr_SetParameter(GAPBOND_SECURE_CONNECTION, sizeof(uint8_t), &gapbondSecure);
GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
}
// ******************************************************************
// BLE Service initialization
// ******************************************************************
// Log_info0("BLE Service initialization");
GGS_AddService(GATT_ALL_SERVICES); // GAP GATT Service
GATTServApp_AddService(GATT_ALL_SERVICES); // GATT Service
#ifndef FMCU_APP
//DevInfo_AddService(); // Device Information Service
// Add services to GATT server and give ID of this task for Indication acks.
LedService_AddService(selfEntity);
ButtonService_AddService(selfEntity);
#endif
//DevInfo_AddService(); // Device Information Service
//Log_info0(" Entering in DataService_AddService");
DataService_AddService(selfEntity);
#ifndef FMCU_APP
// Register callbacks with the generated services that
// can generate events (writes received) to the application
LedService_RegisterAppCBs(&ProjectZero_LED_ServiceCBs);
ButtonService_RegisterAppCBs(&ProjectZero_Button_ServiceCBs);
#endif
//Log_info0("Entering in DataService_RegisterAppCBs");
DataService_RegisterAppCBs(&ProjectZero_Data_ServiceCBs);
// Placeholder variable for characteristic intialization
uint8_t initVal[40] = {0};
uint8_t initString[] = "This is a pretty long string, isn't it!";
#ifndef FMCU_APP
// Initalization of characteristics in LED_Service that can provide data.
LedService_SetParameter(LS_LED0_ID, LS_LED0_LEN, initVal);
LedService_SetParameter(LS_LED1_ID, LS_LED1_LEN, initVal);
// Initalization of characteristics in Button_Service that can provide data.
ButtonService_SetParameter(BS_BUTTON0_ID, BS_BUTTON0_LEN, initVal);
ButtonService_SetParameter(BS_BUTTON1_ID, BS_BUTTON1_LEN, initVal);
#endif
// Initalization of characteristics in Data_Service that can provide data.
//DataService_SetParameter(DS_STRING_ID, sizeof(initString), initString);
//Log_info0("Entering in DataService_SetParameter");
DataService_SetParameter(DS_STREAM_ID, DS_STREAM_LEN, initVal);
// Start Bond Manager and register callback
VOID GAPBondMgr_Register(&ProjectZero_BondMgrCBs);
// Register with GAP for HCI/Host messages. This is needed to receive HCI
// events. For more information, see the HCI section in the User's Guide:
// http://software-dl.ti.com/lprf/ble5stack-latest/
GAP_RegisterForMsgs(selfEntity);
// Register for GATT local events and ATT Responses pending for transmission
GATT_RegisterForMsgs(selfEntity);
// Set default values for Data Length Extension
// Extended Data Length Feature is already enabled by default
{
// Set initial values to maximum, RX is set to max. by default(251 octets, 2120us)
// Some brand smartphone is essentially needing 251/2120, so we set them here.
#define APP_SUGGESTED_PDU_SIZE 251 //default is 27 octets(TX)
#define APP_SUGGESTED_TX_TIME 2120 //default is 328us(TX)
// This API is documented in hci.h
// See the LE Data Length Extension section in the BLE5-Stack User's Guide for information on using this command:
// http://software-dl.ti.com/lprf/ble5stack-latest/
HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE, APP_SUGGESTED_TX_TIME);
}
// Initialize GATT Client, used by GAPBondMgr to look for RPAO characteristic for network privacy
GATT_InitClient();
// Initialize Connection List
ProjectZero_clearConnListEntry(CONNHANDLE_ALL);
//Initialize GAP layer for Peripheral role and register to receive GAP events
GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, selfEntity, ADDRMODE_PUBLIC, NULL);
}
/*********************************************************************
* @fn ProjectZero_taskFxn
*
* @brief Application task entry point for the Project Zero.
*
* @param a0, a1 - not used.
*/
Semaphore_Struct sem;
Semaphore_Handle hSem;
static void uartRxCb(UART_Handle handle, void *buf, size_t count);
static void ProjectZero_taskFxn(UArg a0, UArg a1)
{
// Initialize application
ProjectZero_init();
Semaphore_Params sParams;
Semaphore_Params_init(&sParams);
sParams.mode = Semaphore_Mode_BINARY;
Semaphore_construct(&sem, 0, &sParams);
hSem = Semaphore_handle(&sem);
// Application main loop
for(;; )
{
uint32_t events;
// Waits for an event to be posted associated with the calling thread.
// Note that an event associated with a thread is posted when a
// message is queued to the message receive queue of the thread
events = Event_pend(syncEvent, Event_Id_NONE, PZ_ALL_EVENTS,ICALL_TIMEOUT_FOREVER);
if(events)
{
// Log_info1("Events %d %s",events);
ICall_EntityID dest;
ICall_ServiceEnum src;
ICall_HciExtEvt *pMsg = NULL;
// Fetch any available messages that might have been sent from the stack
if(ICall_fetchServiceMsg(&src, &dest,
(void **)&pMsg) == ICALL_ERRNO_SUCCESS)
{
uint8_t safeToDealloc = TRUE;
if((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
{
ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
// Check for BLE stack events first
if(pEvt->signature == 0xffff)
{
// Process stack events
ProjectZero_processStackEvent(pEvt->event_flag);
}
else
{
switch(pMsg->hdr.event)
{
case GAP_MSG_EVENT:
// Process GAP message
// Log_info0(" Entering ProjectZero_processGapMessage "); //only fmcu debug
ProjectZero_processGapMessage((gapEventHdr_t*) pMsg);
// Log_info0(" Exit from ProjectZero_processGapMessage "); //only fmcu debug
break;
case GATT_MSG_EVENT:
// Process GATT message
// Log_info0(" Entering ProjectZero_processGATTMsg "); //only fmcu debug
safeToDealloc = ProjectZero_processGATTMsg((gattMsgEvent_t *)pMsg);
// Log_info0(" Exit from ProjectZero_processGATTMsg "); //only fmcu debug
break;
case HCI_GAP_EVENT_EVENT:
ProjectZero_processHCIMsg(pMsg);//,hUART);
break;
case L2CAP_SIGNAL_EVENT:
// Process L2CAP free buffer notification
ProjectZero_processL2CAPMsg((l2capSignalEvent_t *)pMsg);
break;
default:
// do nothing
break;
}
}
}
if(pMsg && safeToDealloc)
{
ICall_freeMsg(pMsg);
}
}
// Process messages sent from another task or another context.
while(!Queue_empty(appMsgQueueHandle))
{
pzMsg_t *pMsg = (pzMsg_t *)Util_dequeueMsg(appMsgQueueHandle);
if(pMsg)
{
// Log_info0(" Entering ProjectZero_processApplicationMessage "); //only fmcu debug
// Process application-layer message probably sent from ourselves.
//ProjectZero_processApplicationMessage(pMsg,hUART);
ProjectZero_processApplicationMessage(pMsg);
// Free the received message.
ICall_free(pMsg);
}
}
}
}
}
/*********************************************************************
* @fn ProjectZero_processL2CAPMsg
*
* @brief Process L2CAP messages and events.
*
* @param pMsg - L2CAP signal buffer from stack
*
* @return None
*/
static void ProjectZero_processL2CAPMsg(l2capSignalEvent_t *pMsg)
{
switch(pMsg->opcode)
{
case L2CAP_NUM_CTRL_DATA_PKT_EVT:
break;
default:
break;
}
}
/*********************************************************************
* @fn ProjectZero_processStackEvent
*
* @brief Process stack event. The event flags received are user-selected
* via previous calls to stack APIs.
*
* @param stack_event - mask of events received
*
* @return none
*/
static void ProjectZero_processStackEvent(uint32_t stack_event)
{
// Intentionally blank
}
/*********************************************************************
* @fn ProjectZero_processGATTMsg
*
* @brief Process GATT messages and events.
*
* @param pMsg - message to process
*
* @return TRUE if safe to deallocate incoming message, FALSE otherwise.
*/
static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg)
{
if(pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
{
// ATT request-response or indication-confirmation flow control is
// violated. All subsequent ATT requests or indications will be dropped.
// The app is informed in case it wants to drop the connection.
// Display the opcode of the message that caused the violation.
Log_error1("FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
}
else if(pMsg->method == ATT_MTU_UPDATED_EVENT)
{
// MTU size updated
Log_info1("MTU Size: %d", pMsg->msg.mtuEvt.MTU);
}
// Free message payload. Needed only for ATT Protocol messages
GATT_bm_free(&pMsg->msg, pMsg->method);
// It's safe to free the incoming message
return(TRUE);
}
/*********************************************************************
* @fn ProjectZero_processApplicationMessage
*
* @brief Handle application messages
*
* These are messages not from the BLE stack, but from the
* application itself.
*
* For example, in a Software Interrupt (Swi) it is not possible to
* call any BLE APIs, so instead the Swi function must send a message
* to the application Task for processing in Task context.
*
* @param pMsg Pointer to the message of type pzMsg_t.
*/
//static void ProjectZero_processApplicationMessage(pzMsg_t *pMsg,UART_Handle handle)
static void ProjectZero_processApplicationMessage(pzMsg_t *pMsg)
{
// Cast to pzCharacteristicData_t* here since it's a common message pdu type.
pzCharacteristicData_t *pCharData = (pzCharacteristicData_t *)pMsg->pData;
Log_info2("Inside Process application message %s %d ",pMsg->event,pMsg->event);
switch(pMsg->event)
{
case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
break;
case PZ_SERVICE_WRITE_EVT: /* Message about received value write */
/* Call different handler per service */
Log_info0("PZ_SERVICE_WRITE_EVT");
switch(pCharData->svcUUID)
{
#ifndef FMCU_APP
case LED_SERVICE_SERV_UUID:
ProjectZero_LedService_ValueChangeHandler(pCharData);
break;
#endif
case DATA_SERVICE_SERV_UUID:
//Log_info1("Line 956","Entering in ProjectZero_DataService_ValueChangeHandler");
Log_info0("Entering in ProjectZero_DataService_ValueChangeHandler ");
ProjectZero_DataService_ValueChangeHandler(pCharData);
break;
}
/****Added RSSI Read Value ***/
// HCI_ReadRssiCmd(advHandleLegacy);
break;
case PZ_SERVICE_CFG_EVT: /* Message about received CCCD write */
/* Call different handler per service */
switch(pCharData->svcUUID)
{
#ifndef FMCU_APP
case BUTTON_SERVICE_SERV_UUID:
ProjectZero_ButtonService_CfgChangeHandler(pCharData);
break;
#endif
case DATA_SERVICE_SERV_UUID:
Log_info1("Line 972","Entering in ProjectZero_DataService_CfgChangeHandler");
ProjectZero_DataService_CfgChangeHandler(pCharData);
break;
}
break;
case PZ_UPDATE_CHARVAL_EVT: /* Message from ourselves to send */
ProjectZero_updateCharVal(pCharData);
break;
#ifndef FMCU_APP
case PZ_BUTTON_DEBOUNCED_EVT: /* Message from swi about pin change */
{
pzButtonState_t *pButtonState = (pzButtonState_t *)pMsg->pData;
ProjectZero_handleButtonPress(pButtonState);
}
break;
#endif
case PZ_ADV_EVT:
ProjectZero_processAdvEvent((pzGapAdvEventData_t*)(pMsg->pData));
break;
case PZ_SEND_PARAM_UPD_EVT:
{
// Send connection parameter update
pzSendParamReq_t* req = (pzSendParamReq_t *)pMsg->pData;
ProjectZero_sendParamUpdate(req->connHandle);
}
break;
case PZ_START_ADV_EVT:
if(linkDB_NumActive() < MAX_NUM_BLE_CONNS)
{
// Enable advertising if there is room for more connections
GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX, 0);
}
break;
case PZ_PAIRSTATE_EVT: /* Message about the pairing state */
ProjectZero_processPairState((pzPairStateData_t*)(pMsg->pData));
break;
case PZ_PASSCODE_EVT: /* Message about pairing PIN request */
{
pzPasscodeReq_t *pReq = (pzPasscodeReq_t *)pMsg->pData;
ProjectZero_processPasscode(pReq);
}
break;
case PZ_CONN_EVT:
ProjectZero_processConnEvt((Gap_ConnEventRpt_t *)(pMsg->pData));
break;
default:
break;
}
if(pMsg->pData != NULL)
{
ICall_free(pMsg->pData);
}
}
/*********************************************************************
* @fn ProjectZero_processGapMessage
*
* @brief Process an incoming GAP event.
*
* @param pMsg - message to process
*/
static void ProjectZero_processGapMessage(gapEventHdr_t *pMsg)
{
switch(pMsg->opcode)
{
case GAP_DEVICE_INIT_DONE_EVENT:
{
bStatus_t status = FAILURE;
gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;
if(pPkt->hdr.status == SUCCESS)
{
//Store the system ID
uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
// use 6 bytes of device address for 8 bytes of system ID value
systemId[0] = pPkt->devAddr[0];
systemId[1] = pPkt->devAddr[1];
systemId[2] = pPkt->devAddr[2];
// set middle bytes to zero
systemId[4] = 0x00;
systemId[3] = 0x00;
// shift three bytes up
systemId[7] = pPkt->devAddr[5];
systemId[6] = pPkt->devAddr[4];
systemId[5] = pPkt->devAddr[3];
// Set Device Info Service Parameter
//DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN,systemId);
// Display device address
// Need static so string persists until printed in idle thread.
static uint8_t addrStr[3 * B_ADDR_LEN + 1];
util_arrtohex(pPkt->devAddr, B_ADDR_LEN, addrStr, sizeof addrStr,
UTIL_ARRTOHEX_REVERSE);
Log_info1("GAP is started. Our address: " \
ANSI_COLOR(FG_GREEN) "%s" ANSI_COLOR(ATTR_RESET),
(uintptr_t)addrStr);
// Setup and start Advertising
// For more information, see the GAP section in the User's Guide:
// http://software-dl.ti.com/lprf/ble5stack-latest/
// Temporary memory for advertising parameters for set #1. These will be copied
// by the GapAdv module
GapAdv_params_t advParamLegacy = GAPADV_PARAMS_LEGACY_SCANN_CONN;
// Create Advertisement set #1 and assign handle
status = GapAdv_create(&ProjectZero_advCallback, &advParamLegacy,
&advHandleLegacy);
APP_ASSERT(status == SUCCESS);
Log_info1("Name in advertData array: " \
ANSI_COLOR(FG_YELLOW) "%s" ANSI_COLOR(ATTR_RESET),
(uintptr_t)util_getLocalNameStr(advertData,
sizeof(advertData)));
// Load advertising data for set #1 that is statically allocated by the app
status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_ADV,
sizeof(advertData), advertData);
APP_ASSERT(status == SUCCESS);
// Load scan response data for set #1 that is statically allocated by the app
status =
GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP,
sizeof(scanRspData),
scanRspData);
APP_ASSERT(status == SUCCESS);
// Set event mask for set #1
status = GapAdv_setEventMask(advHandleLegacy,
GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
GAP_ADV_EVT_MASK_SET_TERMINATED);
// Enable legacy advertising for set #1
status =
GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX,
0);
APP_ASSERT(status == SUCCESS);
}
break;
}
case GAP_LINK_ESTABLISHED_EVENT:
{
gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;
// Display the amount of current connections
Log_info2("Link establish event, status 0x%02x. Num Conns: %d",
pPkt->hdr.status,
linkDB_NumActive());
if(pPkt->hdr.status == SUCCESS)
{
// Add connection to list
ProjectZero_addConn(pPkt->connectionHandle);
// Display the address of this connection
static uint8_t addrStr[3 * B_ADDR_LEN + 1];
util_arrtohex(pPkt->devAddr, B_ADDR_LEN, addrStr, sizeof addrStr,
UTIL_ARRTOHEX_REVERSE);
// Log_info1("Connected. Peer address: " \
// ANSI_COLOR(FG_GREEN)"%s"ANSI_COLOR(ATTR_RESET),
// (uintptr_t)addrStr);
//Set Dio_7 High to Know FMCU that mobile is connected
PIN_setOutputValue(ledPinHandle, WakeUp_Event,1);
}
if(linkDB_NumActive() < MAX_NUM_BLE_CONNS)
{
// Log_info1("Continue to Advertise, %d possible connection remain", MAX_NUM_BLE_CONNS - linkDB_NumActive());
// Start advertising since there is room for more connections
GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX, 0);
}
else
{
// Log_info1("Max Number of Connection reach: %d, Adv. will not be enable again", linkDB_NumActive());
}
}
break;
case GAP_LINK_TERMINATED_EVENT:
{
gapTerminateLinkEvent_t *pPkt = (gapTerminateLinkEvent_t *)pMsg;
// Set DIO_7 Low For FMCU to know device is disconnected or out of range
PIN_setOutputValue(ledPinHandle, WakeUp_Event,0);
// Display the amount of current connections
Log_info0("Device Disconnected!");
Log_info1("Num Conns: %d", linkDB_NumActive());
// Remove the connection from the list and disable RSSI if needed
ProjectZero_removeConn(pPkt->connectionHandle);
// GapAdv_enable will return success only if the maximum number of connections
// has been reached, and adv was not re-enable in GAP_LINK_ESTABLISHED_EVENT
// switch case.
// If less connection were in used, Advertisement will have been restart in
// the GAP_LINK_ESTABLISHED_EVENT switch case and calling GapAdv_enable will
// just return an error.
if ( GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX, 0) == SUCCESS)
{
// Log_info1("Restart Advertising, %d possible connection remain", MAX_NUM_BLE_CONNS - linkDB_NumActive());
}
}
break;
case GAP_UPDATE_LINK_PARAM_REQ_EVENT:
// Log_info0("GAP_UPDATE_LINK_PARAM_REQ_EVENT");
ProjectZero_handleUpdateLinkParamReq((gapUpdateLinkParamReqEvent_t *)pMsg);
break;
case GAP_LINK_PARAM_UPDATE_EVENT:
// Log_info0("GAP_LINK_PARAM_UPDATE_EVENT");//Only Debug FMCU Mux
ProjectZero_handleUpdateLinkEvent((gapLinkUpdateEvent_t *)pMsg);
//Log_info0(" coming out of GAP_LINK_PARAM_UPDATE_EVENT");//Only Debug FMCU Mux
break;
case GAP_PAIRING_REQ_EVENT:
// Disable advertising so that the peer device can be added to
// the resolving list
GapAdv_disable(advHandleLegacy);
break;
default:
break;
}
}
void ProjectZero_processHCIMsg(ICall_HciExtEvt *pEvt)
//void ProjectZero_processHCIMsg(ICall_HciExtEvt *pEvt,UART_Handle hUART)
{
ICall_Hdr *pMsg = (ICall_Hdr *)pEvt;
// Process HCI message
switch(pMsg->status)
{
case HCI_COMMAND_COMPLETE_EVENT_CODE:
// Process HCI Command Complete Events here
ProjectZero_processCmdCompleteEvt((hciEvt_CmdComplete_t *) pMsg);
//UART_write(hUART,"Going",5);
//ProjectZero_processCmdCompleteEvt((hciEvt_CmdComplete_t *) pMsg, hUART);
break;
case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
break;
// HCI Commands Events
case HCI_COMMAND_STATUS_EVENT_CODE:
{
hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;
switch(pMyMsg->cmdOpcode)
{
case HCI_LE_SET_PHY:
{
if(pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
{
// Log_info0("PHY Change failure, peer does not support this");
}
else
{
// Log_info1("PHY Update Status Event: 0x%x",
// pMyMsg->cmdStatus);
}
ProjectZero_updatePHYStat(HCI_LE_SET_PHY, (uint8_t *)pMsg);
}
break;
default:
break;
}
}
break;
// LE Events
case HCI_LE_EVENT_CODE:
{
hciEvt_BLEPhyUpdateComplete_t *pPUC =
(hciEvt_BLEPhyUpdateComplete_t*) pMsg;
// A Phy Update Has Completed or Failed
if(pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
{
if(pPUC->status != SUCCESS)
{
// Log_info0("PHY Change failure");
}
else
{
// Only symmetrical PHY is supported.
// rxPhy should be equal to txPhy.
Log_info1("PHY Updated to %s",
(uintptr_t)((pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1M" :
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2M" :
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value"));
}
ProjectZero_updatePHYStat(HCI_BLE_PHY_UPDATE_COMPLETE_EVENT,
(uint8_t *)pMsg);
}
}
break;
default:
break;
}
}
/*********************************************************************
* @fn ProjectZero_processAdvEvent
*
* @brief Process advertising event in app context
*
* @param pEventData
*
* @return TRUE if safe to deallocate incoming message, FALSE otherwise.
*/
static void ProjectZero_processAdvEvent(pzGapAdvEventData_t *pEventData)
{
switch(pEventData->event)
{
/* Sent on the first advertisement after a GapAdv_enable */
case GAP_EVT_ADV_START_AFTER_ENABLE:
// Log_info1("Adv Set %d Enabled", *(uint8_t *)(pEventData->pBuf));
break;
/* Sent after advertising stops due to a GapAdv_disable */
case GAP_EVT_ADV_END_AFTER_DISABLE:
// Log_info1("Adv Set %d Disabled", *(uint8_t *)(pEventData->pBuf));
break;
/* Sent at the beginning of each advertisement. (Note that this event
* is not enabled by default, see GapAdv_setEventMask). */
case GAP_EVT_ADV_START:
break;
/* Sent after each advertisement. (Note that this event is not enabled
* by default, see GapAdv_setEventMask). */
case GAP_EVT_ADV_END:
break;
/* Sent when an advertisement set is terminated due to a
* connection establishment */
case GAP_EVT_ADV_SET_TERMINATED:
{
GapAdv_setTerm_t *advSetTerm = (GapAdv_setTerm_t *)(pEventData->pBuf);
// Log_info2("Adv Set %d disabled after conn %d",
// advSetTerm->handle, advSetTerm->connHandle);
}
break;
/* Sent when a scan request is received. (Note that this event
* is not enabled by default, see GapAdv_setEventMask). */
case GAP_EVT_SCAN_REQ_RECEIVED:
break;
/* Sent when an operation could not complete because of a lack of memory.
This message is not allocated on the heap and must not be freed */
case GAP_EVT_INSUFFICIENT_MEMORY:
break;
default:
break;
}
// All events have associated memory to free except the insufficient memory
// event
if (pEventData->event != GAP_EVT_INSUFFICIENT_MEMORY)
{
ICall_free(pEventData->pBuf);
}
}
/*********************************************************************
* @fn ProjectZero_processPairState
*
* @brief Process the new paring state.
*
* @param pPairData - pointer to pair state data container
*/
static void ProjectZero_processPairState(pzPairStateData_t *pPairData)
{
uint8_t state = pPairData->state;
uint8_t status = pPairData->status;
switch(state)
{
case GAPBOND_PAIRING_STATE_STARTED:
Log_info0("Pairing started");
break;
case GAPBOND_PAIRING_STATE_COMPLETE:
if(status == SUCCESS)
{
// Log_info0("Pairing success");
}
else
{
// Log_info1("Pairing fail: %d", status);
}
break;
case GAPBOND_PAIRING_STATE_ENCRYPTED:
if(status == SUCCESS)
{
// Log_info0("Encryption success");
}
else
{
// Log_info1("Encryption failed: %d", status);
}
break;
case GAPBOND_PAIRING_STATE_BOND_SAVED:
if(status == SUCCESS)
{
// Log_info0("Bond save success");
}
else
{
// Log_info1("Bond save failed: %d", status);
}
break;
default:
break;
}
}
/*********************************************************************
* @fn ProjectZero_processPasscode
*
* @brief Process the Passcode request.
*
* @param pReq - pointer to passcode req
*/
static void ProjectZero_processPasscode(pzPasscodeReq_t *pReq)
{
// Log_info2("BondMgr Requested passcode. We are %s passcode %06d",
// (uintptr_t)(pReq->uiInputs ? "Sending" : "Displaying"),
// B_APP_DEFAULT_PASSCODE);
// Send passcode response.
GAPBondMgr_PasscodeRsp(pReq->connHandle, SUCCESS, B_APP_DEFAULT_PASSCODE);
}
/*********************************************************************
* @fn ProjectZero_processConnEvt
*
* @brief Process connection event.
*
* @param pReport pointer to connection event report
*/
static void ProjectZero_processConnEvt(Gap_ConnEventRpt_t *pReport)
{
// Log_info1("Connection event done for connHandle: %d", pReport->handle);
}
char rev[5];
// Implementation of itoa()
char* itoa(int num, char* str, int base)
{
int i = 0;
bool isNegative = false;
/* Handle 0 explicitely, otherwise empty string is printed for 0 */
if (num == 0)
{
str[i++] = '0';
str[i] = '\0';
return str;
}
// In standard itoa(), negative numbers are handled only with
// base 10. Otherwise numbers are considered unsigned.
if (num < 0 && base == 10)
{
isNegative = true;
num = -num;
}
// Process individual digits
while (num != 0)
{
int rem = num % base;
str[i++] = (rem > 9)? (rem-10) + 'a' : rem + '0';
num = num/base;
}
// If number is negative, append '-'
if (isNegative)
str[i++] = '-';
str[i] = '\0'; // Append string terminator
// Reverse the string
//reverse(str, i);
return str;
}
/*********************************************************************
* @fn ProjectZero_processCmdCompleteEvt
*
* @brief Process an incoming OSAL HCI Command Complete Event.
*
* @param pMsg - message to process
*/
//static void ProjectZero_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg,UART_Handle hUART)
static void ProjectZero_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg)
{
uint8_t status = pMsg->pReturnParam[0];
//Find which command this command complete is for
switch(pMsg->cmdOpcode)
{
case HCI_READ_RSSI:
{
char buf [5];
int8 rssi = (int8)pMsg->pReturnParam[3];
// Display RSSI value, if RSSI is higher than threshold, change to faster PHY
if(status == SUCCESS)
{
uint16_t handle = BUILD_UINT16(pMsg->pReturnParam[1],
pMsg->pReturnParam[2]);
Log_info2("RSSI:%d, connHandle %d",
(uint32_t)(rssi),
(uint32_t)handle);
//UART_write(hUART,"Inside :",8);
itoa(rssi,buf,10);
int length=4,k=0;
int j= length-1;
//reversing the string by swapping
for (k = 0; k < length; k++)
{
rev[k] = buf[j];
j--;
}
rev[k] = '\0';
//UART_write(hUART,rev,5);
//UART_write(hUART,"ok",2);
} // end of if (status == SUCCESS)
break;
}
case HCI_LE_READ_PHY:
{
if(status == SUCCESS)
{
// Log_info2("RXPh: %d, TXPh: %d",
// pMsg->pReturnParam[3], pMsg->pReturnParam[4]);
}
break;
}
default:
break;
} // end of switch (pMsg->cmdOpcode)
}
/*********************************************************************
* @fn ProjectZero_handleUpdateLinkParamReq
*
* @brief Receive and respond to a parameter update request sent by
* a peer device
*
* @param pReq - pointer to stack request message
*/
static void ProjectZero_handleUpdateLinkParamReq(gapUpdateLinkParamReqEvent_t *pReq)
{
// Log_info0("Inside the ProjectZero_handleUpdateLinkParamReq");
gapUpdateLinkParamReqReply_t rsp;
rsp.connectionHandle = pReq->req.connectionHandle;
rsp.signalIdentifier = pReq->req.signalIdentifier;
// Only accept connection intervals with slave latency of 0
// This is just an example of how the application can send a response
if(pReq->req.connLatency == 0)
{
rsp.intervalMin = pReq->req.intervalMin;
rsp.intervalMax = pReq->req.intervalMax;
rsp.connLatency = pReq->req.connLatency;
rsp.connTimeout = pReq->req.connTimeout;
rsp.accepted = TRUE;
}
else
{
Log_info0("rsp_accepted = FALSE");//Only Debug FMCU Mux
rsp.accepted = FALSE;
}
// Send Reply
// Log_info0("Entering GAP_UpdateLinkParamReqReply ");//Only Debug FMCU Mux
VOID GAP_UpdateLinkParamReqReply(&rsp);
// Log_info0("Exiting GAP_UpdateLinkParamReqReply ");//Only Debug FMCU Mux
}
/*********************************************************************
* @fn ProjectZero_handleUpdateLinkEvent
*
* @brief Receive and parse a parameter update that has occurred.
*
* @param pEvt - pointer to stack event message
*/
static void ProjectZero_handleUpdateLinkEvent(gapLinkUpdateEvent_t *pEvt)
{
// Log_info0("Entering ProjectZero_handleUpdateLinkEvent ");//Only Debug FMCU Mux
// Get the address from the connection handle
linkDBInfo_t linkInfo;
linkDB_GetInfo(pEvt->connectionHandle, &linkInfo);
// Log_info1("Link DB_info0 %d ",(uintptr_t)pEvt->connectionHandle);//Only Debug FMCU Mux
static uint8_t addrStr[3 * B_ADDR_LEN + 1];
util_arrtohex(linkInfo.addr, B_ADDR_LEN, addrStr, sizeof addrStr,
UTIL_ARRTOHEX_REVERSE);
if(pEvt->status == SUCCESS)
{
//Log_info0("Entered ProjectZero_handleUpdateLinkEvent" );//Only Debug FMCU Mux
uint8_t ConnIntervalFracture = 25*(pEvt->connInterval % 4);
// Display the address of the connection update
/* Log_info5(
"Updated params for %s, interval: %d.%d ms, latency: %d, timeout: %d ms",
(uintptr_t)addrStr,
(uintptr_t)(pEvt->connInterval*CONN_INTERVAL_MS_CONVERSION),
ConnIntervalFracture,
pEvt->connLatency,
pEvt->connTimeout*CONN_TIMEOUT_MS_CONVERSION);*/
}
else
{
// Display the address of the connection update failure
// Log_info2("Update Failed 0x%02x: %s", pEvt->opcode, (uintptr_t)addrStr);
}
// Check if there are any queued parameter updates
pzConnHandleEntry_t *connHandleEntry = (pzConnHandleEntry_t *)List_get(¶mUpdateList);
if(connHandleEntry != NULL)
{
// Attempt to send queued update now
ProjectZero_sendParamUpdate(*(connHandleEntry->connHandle));
// Free list element
ICall_free(connHandleEntry->connHandle);
ICall_free(connHandleEntry);
//Log_info0("Exit ProjectZero_handleUpdateLinkEvent ");//Only Debug FMCU Mux
}
}
/*********************************************************************
* @fn ProjectZero_addConn
*
* @brief Add a device to the connected device list
*
* @param connHandle - connection handle
*
* @return bleMemAllocError if a param update event could not be sent. Else SUCCESS.
*/
static uint8_t ProjectZero_addConn(uint16_t connHandle)
{
uint8_t i;
uint8_t status = bleNoResources;
// Try to find an available entry
for(i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
if(connList[i].connHandle == CONNHANDLE_INVALID)
{
// Found available entry to put a new connection info in
connList[i].connHandle = connHandle;
// Create a clock object and start
connList[i].pUpdateClock
= (Clock_Struct*) ICall_malloc(sizeof(Clock_Struct));
if (connList[i].pUpdateClock)
{
Util_constructClock(connList[i].pUpdateClock,
ProjectZero_paramUpdClockHandler,
PZ_SEND_PARAM_UPDATE_DELAY, 0, true,
(uintptr_t)connHandle);
}
// Set default PHY to 1M
connList[i].currPhy = HCI_PHY_1_MBPS; // TODO: Is this true, neccessarily?
break;
}
}
return(status);
}
/*********************************************************************
* @fn ProjectZero_getConnIndex
*
* @brief Find index in the connected device list by connHandle
*
* @param connHandle - connection handle
*
* @return the index of the entry that has the given connection handle.
* if there is no match, MAX_NUM_BLE_CONNS will be returned.
*/
static uint8_t ProjectZero_getConnIndex(uint16_t connHandle)
{
uint8_t i;
for(i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
if(connList[i].connHandle == connHandle)
{
// Log_info1("Inside ProjectZero_getConnIndex ",connList[i].connHandle); //Only for FMCU_Debug
return(i);
}
}
return(MAX_NUM_BLE_CONNS);
}
/*********************************************************************
* @fn ProjectZero_clearConnListEntry
*
* @brief Clear the connection information structure held locally.
*
* @param connHandle - connection handle
*
* @return SUCCESS if connHandle found valid index or bleInvalidRange
* if index wasn't found. LINKDB_CONNHANDLE_ALL will always succeed.
*/
static uint8_t ProjectZero_clearConnListEntry(uint16_t connHandle)
{
// Log_info0("Inside ProjectZero_clearConnListEntry ");// only for FMCU_Debug
uint8_t i;
// Set to invalid connection index initially
uint8_t connIndex = MAX_NUM_BLE_CONNS;
if(connHandle != CONNHANDLE_ALL)
{
// Get connection index from handle
connIndex = ProjectZero_getConnIndex(connHandle);
if(connIndex >= MAX_NUM_BLE_CONNS)
{
return(bleInvalidRange);
}
}
// Clear specific handle or all handles
for(i = 0; i < MAX_NUM_BLE_CONNS; i++)
{
if((connIndex == i) || (connHandle == CONNHANDLE_ALL))
{
connList[i].connHandle = CONNHANDLE_INVALID;
connList[i].currPhy = 0;
connList[i].phyCngRq = 0;
connList[i].phyRqFailCnt = 0;
connList[i].rqPhy = 0;
}
}
return(SUCCESS);
}
/*********************************************************************
* @fn ProjectZero_removeConn
*
* @brief Remove a device from the connected device list
*
* @param connHandle - connection handle
*
* @return index of the connected device list entry where the new connection
* info is removed from.
* if connHandle is not found, MAX_NUM_BLE_CONNS will be returned.
*/
static uint8_t ProjectZero_removeConn(uint16_t connHandle)
{
// Log_info0("Inside ProjectZero_removeConn ");// only for FMCU_Debug
uint8_t connIndex = ProjectZero_getConnIndex(connHandle);
if(connIndex < MAX_NUM_BLE_CONNS)
{
Clock_Struct* pUpdateClock = connList[connIndex].pUpdateClock;
if (pUpdateClock != NULL)
{
// Stop and destruct the RTOS clock if it's still alive
if (Util_isActive(pUpdateClock))
{
Util_stopClock(pUpdateClock);
}
// Destruct the clock object
Clock_destruct(pUpdateClock);
// Free clock struct
ICall_free(pUpdateClock);
}
// Clear Connection List Entry
ProjectZero_clearConnListEntry(connHandle);
}
return connIndex;
}
/*********************************************************************
* @fn ProjectZero_sendParamUpdate
*
* @brief Remove a device from the connected device list
*
* @param connHandle - connection handle
*/
static void ProjectZero_sendParamUpdate(uint16_t connHandle)
{
gapUpdateLinkParamReq_t req;
uint8_t connIndex;
req.connectionHandle = connHandle;
req.connLatency = DEFAULT_DESIRED_SLAVE_LATENCY;
req.connTimeout = DEFAULT_DESIRED_CONN_TIMEOUT;
req.intervalMin = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
req.intervalMax = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
connIndex = ProjectZero_getConnIndex(connHandle);
APP_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
// Deconstruct the clock object
Clock_destruct(connList[connIndex].pUpdateClock);
// Free clock struct
ICall_free(connList[connIndex].pUpdateClock);
connList[connIndex].pUpdateClock = NULL;
// Send parameter update
bStatus_t status = GAP_UpdateLinkParamReq(&req);
// If there is an ongoing update, queue this for when the update completes
if(status == bleAlreadyInRequestedMode)
{
pzConnHandleEntry_t *connHandleEntry =
ICall_malloc(sizeof(pzConnHandleEntry_t));
if(connHandleEntry)
{
connHandleEntry->connHandle = ICall_malloc(sizeof(uint16_t));
if(connHandleEntry->connHandle)
{
*(connHandleEntry->connHandle) = connHandle;
List_put(¶mUpdateList, (List_Elem *)&connHandleEntry);
}
}
}
}
/*********************************************************************
* @fn ProjectZero_updatePHYStat
*
* @brief Update the auto phy update state machine
*
* @param eventCode - HCI LE Event code
* pMsg - message to process
*/
static void ProjectZero_updatePHYStat(uint16_t eventCode, uint8_t *pMsg)
{
uint8_t connIndex;
pzConnHandleEntry_t *connHandleEntry;
switch(eventCode)
{
case HCI_LE_SET_PHY:
{
// Get connection handle from list
connHandleEntry = (pzConnHandleEntry_t *)List_get(&setPhyCommStatList);
if(connHandleEntry)
{
// Get index from connection handle
connIndex = ProjectZero_getConnIndex(*(connHandleEntry->connHandle));
APP_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
ICall_free(connHandleEntry->connHandle);
ICall_free(connHandleEntry);
hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg;
if(pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
{
// Update the phy change request status for active RSSI tracking connection
connList[connIndex].phyCngRq = FALSE;
connList[connIndex].phyRqFailCnt++;
}
}
break;
}
// LE Event - a Phy update has completed or failed
case HCI_BLE_PHY_UPDATE_COMPLETE_EVENT:
{
hciEvt_BLEPhyUpdateComplete_t *pPUC =
(hciEvt_BLEPhyUpdateComplete_t*) pMsg;
if(pPUC)
{
// Get index from connection handle
uint8_t index = ProjectZero_getConnIndex(pPUC->connHandle);
APP_ASSERT(index < MAX_NUM_BLE_CONNS);
// Update the phychange request status for active RSSI tracking connection
connList[index].phyCngRq = FALSE;
if(pPUC->status == SUCCESS)
{
connList[index].currPhy = pPUC->rxPhy;
}
if(pPUC->rxPhy != connList[index].rqPhy)
{
connList[index].phyRqFailCnt++;
}
else
{
// Reset the request phy counter and requested phy
connList[index].phyRqFailCnt = 0;
connList[index].rqPhy = 0;
}
}
break;
}
default:
break;
} // end of switch (eventCode)
}
//static void uartRxCb(UART_Handle handle, void *buf, size_t count)
//{
// //Copy rxBuf to txBuf
// memset(txBuf, 0, BUFSIZE);
//
// memcpy(txBuf, rxBuf, count);
// //Wake task to echo
// Semaphore_post(hSem);
//}
//MobileApp Button data
char EngineStart[8]={36, 172, 4, 36, 173, 7, 32, 0}; //EngineStart String 36 172 4 36 173 7 32 0
char EngineStop[8]= {36, 172, 4, 36, 173, 7, 32, 1}; //EngineSTOP 36 172 4 36 173 7 32 1
char LeadMe_ON[8]= {36, 172, 4, 36, 173, 1, 0, 138}; //LEAD ME ON 36 172 4 36 173 1 0 138
char LeadMe_OFF[8]= {36, 172, 4, 36, 173, 1, 0, 139}; //LEAD ME OFF 36 172 4 36 173 1 0 139
#define Rx_Size 4
#define LeadMeButton_ON 10
#define LeadMeButton_OFF 20
#define EngineStart_Button 30
#define EngineStop_Button 40
char rxBuf[Rx_Size];
char txBuf[Rx_Size];
static uint8_t Button=0;
uint8_t RXcount=0;
uint8_t ACK_Flag = 0;
uint8_t Counter_retry=0;
#define Maximum_Number_of_Try 3
char RxResponse[Rx_Size];
char PACK[Rx_Size] = {"PACK"};
char NACK[Rx_Size] = {"NACK"};
//char PACK[Rx_Size] = {0x41,0x41};
//char NACK[Rx_Size] = {0x4B,0x4B};
//
static void uartRxCb(UART_Handle handle, void *buf, size_t count)
{
if(count>=Rx_Size)
{
//Clear RxResponse
memset(RxResponse, 0, count);
//Store rx data in other memory
memcpy(RxResponse, buf, count);
//UART_write(handle,RxString,sizeof(RxString));
//UART_write(handle,RxString, count);
}
else
memcpy(RxResponse, "NO-DATA", count);
Semaphore_post(hSem);
}
//**************************************************************************************
/*
* PACKET STRUTURE FROM BLE TO FMCU
* Start_Byte : 0x23 (1 Byte)
* Packet_Length : 0x31 0x36 (1 Byte) (16 bytes of data by default)
* DATA_START_ID : 0x3C (1 Byte)
* PayLoad : xxxx (16 Bytes)
* DATA_STOP_ID : 0x3E (1 Byte)
**************************************************************************************/
void Send_Packet(UART_Handle handle)
{
//0x23 ----> #
//0x31 0x36 ----> data of 16 bytes
//0x3C ------> <
//0x3E -----> >
static uint8_t Packet_array[3]={0X23,16,0X3C};
UART_write(handle,&Packet_array[0],3);
}
void ProjectZero_DataService_ValueChangeHandler( pzCharacteristicData_t *pCharData)
{
// Log_info0(" Inside ProjectZero_DataService_ValueChangeHandler ");
// Value to hold the received string for printing via Log, as Log printouts
// happen in the Idle task, and so need to refer to a global/static variable.
// static uint8_t received_string[DS_STRING_LEN] = {0};
// static uint8_t received_string[DS_STREAM_LEN] = {0};
// Rx buffer size
uint8_t RxDataLength = pCharData->dataLen;
// Buffer to hold RX data from Mobile App
uint8_t Buffer[(RxDataLength + 1)];
uint8_t RxCount = 0;
switch(pCharData->paramID)
{
case DS_STREAM_ID:
// Log_info0("Value Change msg:");
memset(Buffer,0,RxDataLength);
for(int i=0;i<RxDataLength;i++)
{
Buffer[i]=pCharData->data[i];
//Log_info1("%02x",pCharData->data[i]);
}
// UART_write(handle,&Buffer[0],8);
// // Display rx data in array
// for(int i=0;i<RxDataLength;i++) // 36 172 4 36 173 1 0 138
// {
// // Log_info1("Received data : %d ",Buffer[i]);
// }
//UART initialization
/* Call driver init functions */
UART_init();
UART_Params params;
UART_Params_init(¶ms);
UART_Handle handle;
uint32_t timeoutUs = 100000; //1second // 5000; // 5ms timeout, default timeout is no timeout (BIOS_WAIT_FOREVER)
params.readMode = UART_MODE_CALLBACK;
params.writeMode = UART_MODE_BLOCKING;
params.readCallback = uartRxCb;
params.readTimeout = timeoutUs / ClockP_getSystemTickPeriod(); // Default tick period is 10us
params.baudRate = 115200;
handle = UART_open(Board_UART0, ¶ms);
// Enable RETURN_PARTIAL
// UART_control(hUART, UARTCC26X2_CMD_RETURN_PARTIAL_ENABLE, NULL);
if(handle==NULL)
{
}
/****Added RSSI Read Value ***/
HCI_ReadRssiCmd(advHandleLegacy);
// int Pcount=0;
// char Lead[16]={"24AC"};
// for(int i=0;i<4;i++)
// {
// if(Buffer[i]==Lead[i])
// Pcount++;
// else
// Pcount=0;
// }
// if (Pcount>=3)
// UART_write(handle,"Hello",5);
//
// Pcount=0;
// }//remove me
//}//remove me
//Check for Button Press ?
if((memcmp(LeadMe_ON,Buffer,RxDataLength))==0)
{
Button=LeadMeButton_ON;
}
else if((memcmp(LeadMe_OFF,Buffer,RxDataLength))==0)
{
Button=LeadMeButton_OFF;
}
else if((memcmp(EngineStart,Buffer,RxDataLength))==0)
{
Button=EngineStart_Button;
}
else if((memcmp(EngineStop,Buffer,RxDataLength))==0)
{
Button=EngineStop_Button;
}
//Start Process
switch(Button)
{
case LeadMeButton_ON :
{
Start_timer_100msec=0;
Counter_retry=0;
ACK_Flag=0;
// Max 500msec wait time
while(ACK_Flag == 0)
{
if((Start_timer_100msec == 0) && (Counter_retry < 5))
{
Counter_retry++;
Start_timer_100msec = 1;
UART_write(handle,"#<24AC0424AD01008A>",19);
//Start a timer of 100 msec
Util_startClock((Clock_Struct *)Wait_Timer_100ms);
}
else if(Counter_retry >= 5)
{
ACK_Flag = 1;
Start_timer_100msec=0;
Counter_retry=0;
}
//Start Reading Response
UART_read(handle, rxBuf, sizeof(rxBuf));
//Wait for Acknowledgment from FMCU side
//Semaphore_pend(hSem, BIOS_WAIT_FOREVER);
//Check the Received Data is ACK ?
if((memcmp(PACK,RxResponse,sizeof(PACK)))==0)
{
PIN_setOutputValue(ledPinHandle, Board_PIN_RLED,1); // DIO 16 LED ON
ACK_Flag=1;
Start_timer_100msec=0;
Counter_retry=0;
}
//Check the Received Data is NACK ?
else if((memcmp(NACK,RxResponse,sizeof(NACK)))==0)
{
//Send the 1st default packet Received String to FMCU
PIN_setOutputValue(ledPinHandle, Board_PIN_RLED,0); // DIO 16 LED OFF
ACK_Flag=0;
}
memset(Buffer,0,RxDataLength);
memset(RxResponse,0,sizeof(NACK));
}
UART_close(handle);
break;
}
case LeadMeButton_OFF :
{
Start_timer_100msec=0;
Counter_retry=0;
ACK_Flag=0;
// Max 500msec wait time
while(ACK_Flag == 0)
{
if((Start_timer_100msec == 0) && (Counter_retry < 5))
{
Counter_retry++;
Start_timer_100msec = 1;
UART_write(handle,"#<24AC0424AD01008B>",19);
//Start a timer of 100 msec
Util_startClock((Clock_Struct *)Wait_Timer_100ms);
}
else if(Counter_retry >=5)
{
ACK_Flag = 1;
Start_timer_100msec=0;
Counter_retry=0;
}
//Start Reading Response
UART_read(handle, rxBuf, sizeof(rxBuf));
//Wait for Acknowledgment from FMCU side
//Semaphore_pend(hSem, BIOS_WAIT_FOREVER);
//Check the Received Data is ACK ?
if((memcmp(PACK,RxResponse,sizeof(PACK)))==0)
{
PIN_setOutputValue(ledPinHandle, Board_PIN_RLED,1); // DIO 16 LED ON
ACK_Flag=1;
Start_timer_100msec=0;
Counter_retry=0;
}
//Check the Received Data is NACK ?
else if((memcmp(NACK,RxResponse,sizeof(NACK)))==0)
{
//Send the 1st default packet Received String to FMCU
PIN_setOutputValue(ledPinHandle, Board_PIN_RLED,0); // DIO 16 LED OFF
ACK_Flag=0;
}
memset(Buffer,0,RxDataLength);
memset(RxResponse,0,sizeof(NACK));
}
UART_close(handle);
break;
}
case EngineStart_Button :
{
Start_timer_100msec=0;
Counter_retry=0;
ACK_Flag=0;
// Max 500msec wait time
while(ACK_Flag == 0)
{
if((Start_timer_100msec == 0) && (Counter_retry < 5))
{
Counter_retry++;
Start_timer_100msec = 1;
UART_write(handle,"#<24AC0424AD072000>",19);
//Start a timer of 100 msec
Util_startClock((Clock_Struct *)Wait_Timer_100ms);
}
else if(Counter_retry >=5)
{
ACK_Flag = 1;
Start_timer_100msec=0;
Counter_retry=0;
}
//Start Reading Response
UART_read(handle, rxBuf, sizeof(rxBuf));
//Wait for Acknowledgment from FMCU side
//Semaphore_pend(hSem, BIOS_WAIT_FOREVER);
//Check the Received Data is ACK ?
if((memcmp(PACK,RxResponse,sizeof(PACK)))==0)
{
PIN_setOutputValue(ledPinHandle, Board_PIN_RLED,1); // DIO 16 LED ON
ACK_Flag=1;
Start_timer_100msec=0;
Counter_retry=0;
}
//Check the Received Data is NACK ?
else if((memcmp(NACK,RxResponse,sizeof(NACK)))==0)
{
//Send the 1st default packet Received String to FMCU
PIN_setOutputValue(ledPinHandle, Board_PIN_RLED,0); // DIO 16 LED OFF
ACK_Flag=0;
}
memset(Buffer,0,RxDataLength);
memset(RxResponse,0,sizeof(NACK));
}
UART_close(handle);
break;
}
case EngineStop_Button :
{
Start_timer_100msec=0;
Counter_retry=0;
ACK_Flag=0;
// Max 500msec wait time
while(ACK_Flag == 0)
{
if((Start_timer_100msec == 0) && (Counter_retry < 5))
{
Counter_retry++;
Start_timer_100msec = 1;
UART_write(handle,"#<24AC0424AD072001>",19);
//Start a timer of 100 msec
Util_startClock((Clock_Struct *)Wait_Timer_100ms);
}
else if(Counter_retry >= 5)
{
ACK_Flag = 1;
Start_timer_100msec=0;
Counter_retry=0;
}
//Start Reading Response
UART_read(handle, rxBuf, sizeof(rxBuf));
//Wait for Acknowledgment from FMCU side
//Semaphore_pend(hSem, BIOS_WAIT_FOREVER);
//Check the Received Data is ACK ?
if((memcmp(PACK,RxResponse,sizeof(PACK)))==0)
{
PIN_setOutputValue(ledPinHandle, Board_PIN_RLED,1); // DIO 16 LED ON
ACK_Flag=1;
Start_timer_100msec=0;
Counter_retry=0;
}
//Check the Received Data is NACK ?
else if((memcmp(NACK,RxResponse,sizeof(NACK)))==0)
{
//Send the 1st default packet Received String to FMCU
PIN_setOutputValue(ledPinHandle, Board_PIN_RLED,0); // DIO 16 LED OFF
ACK_Flag=0;
}
memset(Buffer,0,RxDataLength);
memset(RxResponse,0,sizeof(NACK));
}
UART_close(handle);
break;
}
default:
return;
}
default:
return;
} // switch statement end
} //Function end
/*
* @brief Handle a CCCD (configuration change) write received from a peer
* device. This tells us whether the peer device wants us to send
* Notifications or Indications.
*
* @param pCharData pointer to malloc'd char write data
*
* @return None.
*/
void ProjectZero_DataService_CfgChangeHandler(pzCharacteristicData_t *pCharData)
{
Log_info0(" Inside ProjectZero_DataService_CfgChangeHandler ");
// Cast received data to uint16, as that's the format for CCCD writes.
uint16_t configValue = *(uint16_t *)pCharData->data;
char *configValString;
// Determine what to tell the user
switch(configValue)
{
case GATT_CFG_NO_OPERATION:
configValString = "Noti/Ind disabled";
break;
case GATT_CLIENT_CFG_NOTIFY:
configValString = "Notifications enabled";
break;
case GATT_CLIENT_CFG_INDICATE:
configValString = "Indications enabled";
break;
default:
configValString = "Unsupported operation";
}
switch(pCharData->paramID)
{
case DS_STREAM_ID:
Log_info3("CCCD Change msg: %s %s: %s",
(uintptr_t)"Data Service",
(uintptr_t)"Stream",
(uintptr_t)configValString);
// -------------------------
// Do something useful with configValue here. It tells you whether someone
// wants to know the state of this characteristic.
// ...
break;
}
}
/*
* @brief Convenience function for updating characteristic data via pzCharacteristicData_t
* structured message.
*
* @note Must run in Task context in case BLE Stack APIs are invoked.
*
* @param *pCharData Pointer to struct with value to update.
*/
static void ProjectZero_updateCharVal(pzCharacteristicData_t *pCharData)
{
Log_info0(" Inside ProjectZero_updateCharVal ");
switch(pCharData->svcUUID)
{
#ifndef FMCU_APP
case LED_SERVICE_SERV_UUID:
LedService_SetParameter(pCharData->paramID, pCharData->dataLen,
pCharData->data);
break;
case BUTTON_SERVICE_SERV_UUID:
ButtonService_SetParameter(pCharData->paramID, pCharData->dataLen,
pCharData->data);
break;
#endif
}
}
/******************************************************************************
*****************************************************************************
*
* Handlers of direct system callbacks.
*
* Typically enqueue the information or request as a message for the
* application Task for handling.
*
****************************************************************************
*****************************************************************************/
/*
* Callbacks from the Stack Task context (GAP or Service changes)
*****************************************************************************/
/*********************************************************************
* @fn ProjectZero_advCallback
*
* @brief GapAdv module callback
*
* @param pMsg - message to process
* pBuf - data potentially accompanying event
* arg - not used
*/
static void ProjectZero_advCallback(uint32_t event, void *pBuf, uintptr_t arg)
{
// Log_info0(" Inside ProjectZero_advCallback ");
pzGapAdvEventData_t *eventData = ICall_malloc(sizeof(pzGapAdvEventData_t));
if(eventData != NULL)
{
eventData->event = event;
eventData->pBuf = pBuf;
if(ProjectZero_enqueueMsg(PZ_ADV_EVT, eventData) != SUCCESS)
{
ICall_free(eventData);
}
}
}
/*********************************************************************
* @fn ProjectZero_pairStateCb
*
* @brief Pairing state callback.
*
* @param connHandle - connection handle
* state - pair state
* status - pair status
*/
static void ProjectZero_pairStateCb(uint16_t connHandle, uint8_t state,
uint8_t status)
{
Log_info0(" Inside ProjectZero_pairStateCb ");
pzPairStateData_t *pairState =
(pzPairStateData_t *)ICall_malloc(sizeof(pzPairStateData_t));
if(pairState != NULL)
{
pairState->state = state;
pairState->connHandle = connHandle;
pairState->status = status;
if(ProjectZero_enqueueMsg(PZ_PAIRSTATE_EVT, pairState) != SUCCESS)
{
ICall_free(pairState);
}
}
}
/*********************************************************************
* @fn ProjectZero_passcodeCb
*
* @brief Passcode callback.
*
* @param pDeviceAddr - not used
* connHandle - connection handle
* uiInpuits - if TRUE, the local device should accept a passcode input
* uiOutputs - if TRUE, the local device should display the passcode
* numComparison - the code that should be displayed for numeric
* comparison pairing. If this is zero, then passcode pairing is occurring.
*/
static void ProjectZero_passcodeCb(uint8_t *pDeviceAddr,
uint16_t connHandle,
uint8_t uiInputs,
uint8_t uiOutputs,
uint32_t numComparison)
{
Log_info0(" Inside ProjectZero_passcodeCb ");
pzPasscodeReq_t *req =
(pzPasscodeReq_t *)ICall_malloc(sizeof(pzPasscodeReq_t));
if(req != NULL)
{
req->connHandle = connHandle;
req->uiInputs = uiInputs;
req->uiOutputs = uiOutputs;
req->numComparison = numComparison;
if(ProjectZero_enqueueMsg(PZ_PASSCODE_EVT, req) != SUCCESS)
{
ICall_free(req);
}
}
;
}
/*********************************************************************
* @fn ProjectZero_DataService_ValueChangeCB
*
* @brief Callback for characteristic change when a peer writes to us
*
* @param connHandle - connection handle
* paramID - the parameter ID maps to the characteristic written to
* len - length of the data written
* pValue - pointer to the data written
*/
static void ProjectZero_DataService_ValueChangeCB(uint16_t connHandle,
uint8_t paramID, uint16_t len,
uint8_t *pValue)
{
// See the service header file to compare paramID with characteristic.
// Log_info1("(CB) Data Svc Characteristic value change: paramID(%d). "
// "Sending msg to app.", paramID);
pzCharacteristicData_t *pValChange =
ICall_malloc(sizeof(pzCharacteristicData_t) + len);
if(pValChange != NULL)
{
pValChange->svcUUID = DATA_SERVICE_SERV_UUID;
pValChange->paramID = paramID;
memcpy(pValChange->data, pValue, len);
pValChange->dataLen = len;
if(ProjectZero_enqueueMsg(PZ_SERVICE_WRITE_EVT, pValChange) != SUCCESS)
{
ICall_free(pValChange);
}
}
}
/*********************************************************************
* @fn ProjectZero_DataService_CfgChangeCB
*
* @brief Callback for when a peer enables or disables the CCCD attribute,
* indicating they are interested in notifications or indications.
*
* @param connHandle - connection handle
* paramID - the parameter ID maps to the characteristic written to
* len - length of the data written
* pValue - pointer to the data written
*/
static void ProjectZero_DataService_CfgChangeCB(uint16_t connHandle,
uint8_t paramID, uint16_t len,
uint8_t *pValue)
{
Log_info1("(CB) Data Svc Char config change paramID(%d). "
"Sending msg to app.", paramID);
pzCharacteristicData_t *pValChange =
ICall_malloc(sizeof(pzCharacteristicData_t) + len);
if(pValChange != NULL)
{
pValChange->svcUUID = DATA_SERVICE_SERV_UUID;
pValChange->paramID = paramID;
memcpy(pValChange->data, pValue, len);
pValChange->dataLen = len;
if(ProjectZero_enqueueMsg(PZ_SERVICE_CFG_EVT, pValChange) != SUCCESS)
{
ICall_free(pValChange);
}
}
}
/*
* Callbacks from Swi-context
*****************************************************************************/
/*********************************************************************
* @fn ProjectZero_paramUpdClockHandler
*
* @brief Handler function for clock timeouts.
*
* @param arg - app message pointer
*/
static void ProjectZero_paramUpdClockHandler(UArg arg)
{
pzSendParamReq_t *req =
(pzSendParamReq_t *)ICall_malloc(sizeof(pzSendParamReq_t));
if(req)
{
req->connHandle = (uint16_t)arg;
if(ProjectZero_enqueueMsg(PZ_SEND_PARAM_UPD_EVT, req) != SUCCESS)
{
ICall_free(req);
}
}
}
/******************************************************************************
*****************************************************************************
*
* Utility functions
*
****************************************************************************
*****************************************************************************/
/*********************************************************************
* @fn ProjectZero_enqueueMsg
*
* @brief Utility function that sends the event and data to the application.
* Handled in the task loop.
*
* @param event Event type
* @param pData Pointer to message data
*/
static status_t ProjectZero_enqueueMsg(uint8_t event, void *pData)
{
uint8_t success;
pzMsg_t *pMsg = ICall_malloc(sizeof(pzMsg_t));
if(pMsg)
{
pMsg->event = event;
pMsg->pData = pData;
success = Util_enqueueMsg(appMsgQueueHandle, syncEvent, (uint8_t *)pMsg);
return (success) ? SUCCESS : FAILURE;
}
return(bleMemAllocError);
}
/*********************************************************************
* @fn util_arrtohex
*
* @brief Convert {0x01, 0x02} to "01:02"
*
* @param src - source byte-array
* @param src_len - length of array
* @param dst - destination string-array
* @param dst_len - length of array
*
* @return array as string
*/
char * util_arrtohex(uint8_t const *src, uint8_t src_len,
uint8_t *dst, uint8_t dst_len, uint8_t reverse)
{
char hex[] = "0123456789ABCDEF";
uint8_t *pStr = dst;
uint8_t avail = dst_len - 1;
int8_t inc = 1;
if(reverse)
{
src = src + src_len - 1;
inc = -1;
}
memset(dst, 0, avail);
while(src_len && avail > 3)
{
if(avail < dst_len - 1)
{
*pStr++ = ':';
avail -= 1;
}
*pStr++ = hex[*src >> 4];
*pStr++ = hex[*src & 0x0F];
src += inc;
avail -= 2;
src_len--;
}
if(src_len && avail)
{
*pStr++ = ':'; // Indicate not all data fit on line.
}
return((char *)dst);
}
/*********************************************************************
* @fn util_getLocalNameStr
*
* @brief Extract the LOCALNAME from Scan/AdvData
*
* @param data - Pointer to the advertisement or scan response data
* @param len - Length of advertisment or scan repsonse data
*
* @return Pointer to null-terminated string with the adv local name.
*/
static char * util_getLocalNameStr(const uint8_t *data, uint8_t len)
{
uint8_t nuggetLen = 0;
uint8_t nuggetType = 0;
uint8_t advIdx = 0;
static char localNameStr[32] = { 0 };
memset(localNameStr, 0, sizeof(localNameStr));
for(advIdx = 0; advIdx < len; )
{
nuggetLen = data[advIdx++];
nuggetType = data[advIdx];
if((nuggetType == GAP_ADTYPE_LOCAL_NAME_COMPLETE ||
nuggetType == GAP_ADTYPE_LOCAL_NAME_SHORT) )
{
uint8_t len_temp = nuggetLen < (sizeof(localNameStr)-1)? (nuggetLen - 1):(sizeof(localNameStr)-2);
// Only copy the first 31 characters, if name bigger than 31.
memcpy(localNameStr, &data[advIdx + 1], len_temp);
break;
}
else
{
advIdx += nuggetLen;
}
}
return(localNameStr);
}
/*********************************************************************
*********************************************************************/