This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
您好、Sai、
请问您是否已尝试使用所需的传感器控制器示例遵循链接线程中提供的指导?
您能否指定您要阻止的步骤? 确保包括问题说明、如果相关、请提供有关错误的详细信息。
最后但同样重要的是、请确保指定您正在使用的 SDK 版本和使用的堆栈(BLEStack 或 BLE5STACK)。
此致、
感谢您的回复。
SDK 版本:5.30.00.03
ble5stack
是的、我使用传感器控制器示例尝试了链接线程中提供的指导、并成功获得闪烁 LED 示例的输出。 我需要定期读取 ADC。
我创建了新的服务和字符来读取16位 ADC 值。
SC 集成到 CCS 代码中,成功构建并上载,没有错误。 阈值的 LED 操作正常、但无法读取正确的 ADC 值。
我附加了 simpleperipheral.c 的以下代码
/****************************************************************************** @file simple_peripheral.c @brief This file contains the Simple Peripheral 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 "scif.h" #include "myData.h" #include "myData1.h" #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> #if !(defined __TI_COMPILER_VERSION__) #include <intrinsics.h> #endif #include <ti/drivers/utils/List.h> #include <icall.h> #include "util.h" #include <bcomdef.h> /* This Header file contains all BLE API and icall structure definition */ #include <icall_ble_api.h> #include <devinfoservice.h> #include <simple_gatt_profile.h> #ifdef USE_RCOSC #include <rcosc_calibration.h> #endif //USE_RCOSC #include <board.h> #include <board_key.h> #include <menu/two_btn_menu.h> #include "simple_peripheral_menu.h" #include "simple_peripheral.h" #ifdef PTM_MODE #include "npi_task.h" // To allow RX event registration #include "npi_ble.h" // To enable transmission of messages to UART #include "icall_hci_tl.h" // To allow ICall HCI Transport Layer #endif // PTM_MODE /********************************************************************* * MACROS */ /********************************************************************* * CONSTANTS */ // 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 //extern uint32_t myData; //extern uint32_t myData1; uint16_t myData; uint16_t myData1; #define BV(x) (1 << (x)) #define DEFAULT_ADDRESS_MODE ADDRMODE_PUBLIC // 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 80 // Maximum connection interval (units of 1.25ms, 104=130ms) for parameter update request #define DEFAULT_DESIRED_MAX_CONN_INTERVAL 104 // Slave latency to use for parameter update request #define DEFAULT_DESIRED_SLAVE_LATENCY 0 // Supervision timeout value (units of 10ms, 300=3s) for parameter update request #define DEFAULT_DESIRED_CONN_TIMEOUT 300 // Pass parameter updates to the app for it to decide. #define DEFAULT_PARAM_UPDATE_REQ_DECISION GAP_UPDATE_REQ_PASS_TO_APP // How often to perform periodic event (in ms) #define SP_PERIODIC_EVT_PERIOD 5000 // How often to read current current RPA (in ms) #define SP_READ_RPA_EVT_PERIOD 3000 // Delay (in ms) after connection establishment before sending a parameter update requst #define SP_SEND_PARAM_UPDATE_DELAY 6000 // Task configuration #define SP_TASK_PRIORITY 1 #ifndef SP_TASK_STACK_SIZE #define SP_TASK_STACK_SIZE 644 #endif // Application events #define SP_STATE_CHANGE_EVT 0 #define SP_CHAR_CHANGE_EVT 1 #define SP_KEY_CHANGE_EVT 2 #define SP_ADV_EVT 3 #define SP_PAIR_STATE_EVT 4 #define SP_PASSCODE_EVT 5 #define SP_PERIODIC_EVT 6 #define SP_READ_RPA_EVT 7 #define SP_SEND_PARAM_UPDATE_EVT 8 #define SP_CONN_EVT 9 #define APP_MSG_SC_TASK_ALERT 0x0099 // Internal Events for RTOS application #define SP_ICALL_EVT ICALL_MSG_EVENT_ID // Event_Id_31 #define SP_QUEUE_EVT UTIL_QUEUE_EVENT_ID // Event_Id_30 // Bitwise OR of all RTOS events to pend on #define SP_ALL_EVENTS (SP_ICALL_EVT | \ SP_QUEUE_EVT) // Size of string-converted device address ("0xXXXXXXXXXXXX") #define SP_ADDR_STR_SIZE 15 // Row numbers for two-button menu #define SP_ROW_SEPARATOR_1 (TBM_ROW_APP + 0) #define SP_ROW_STATUS_1 (TBM_ROW_APP + 1) #define SP_ROW_STATUS_2 (TBM_ROW_APP + 2) #define SP_ROW_CONNECTION (TBM_ROW_APP + 3) #define SP_ROW_ADVSTATE (TBM_ROW_APP + 4) #define SP_ROW_RSSI (TBM_ROW_APP + 5) #define SP_ROW_IDA (TBM_ROW_APP + 6) #define SP_ROW_RPA (TBM_ROW_APP + 7) #define SP_ROW_DEBUG (TBM_ROW_APP + 8) // For storing the active connections #define SP_RSSI_TRACK_CHNLS 1 // Max possible channels can be GAP_BONDINGS_MAX #define SP_MAX_RSSI_STORE_DEPTH 5 #define SP_INVALID_HANDLE 0xFFFF #define RSSI_2M_THRSHLD -30 #define RSSI_1M_THRSHLD -40 #define RSSI_S2_THRSHLD -50 #define RSSI_S8_THRSHLD -60 #define SP_PHY_NONE LL_PHY_NONE // No PHY set #define AUTO_PHY_UPDATE 0xFF // Spin if the expression is not true #define SIMPLEPERIPHERAL_ASSERT(expr) if (!(expr)) simple_peripheral_spin(); /********************************************************************* * TYPEDEFS */ // App event passed from stack modules. This type is defined by the application // since it can queue events to itself however it wants. /*typedef struct { APP_MSG_SC_CTRL_READY ; APP_MSG_SC_TASK_ALERT; } app_msg_types_t;*/ typedef struct { uint8_t event; // event type void *pData; // pointer to message } spEvt_t; // 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; } spPairStateData_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; } spPasscodeData_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; } spGapAdvEventData_t; // Container to store information from clock expiration using a flexible array // since data is not always needed typedef struct { uint8_t event; // uint8_t data[]; } spClockEventData_t; // List element for parameter update and PHY command status lists typedef struct { List_Elem elem; uint16_t connHandle; } spConnHandleEntry_t; // Connected device information typedef struct { uint16_t connHandle; // Connection Handle spClockEventData_t* pParamUpdateEventData; Clock_Struct* pUpdateClock; // pointer to clock struct int8_t rssiArr[SP_MAX_RSSI_STORE_DEPTH]; uint8_t rssiCntr; int8_t rssiAvg; bool phyCngRq; // Set to true if PHY change request is in progress uint8_t currPhy; uint8_t rqPhy; uint8_t phyRqFailCnt; // PHY change request count bool isAutoPHYEnable; // Flag to indicate auto phy change } spConnRec_t; /********************************************************************* * GLOBAL VARIABLES */ // Display Interface Display_Handle dispHandle = NULL; // Task configuration Task_Struct spTask; #if defined __TI_COMPILER_VERSION__ #pragma DATA_ALIGN(spTaskStack, 8) #else #pragma data_alignment=8 #endif uint8_t spTaskStack[SP_TASK_STACK_SIZE]; /********************************************************************* * 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; // Clock instance for internal periodic events. Only one is needed since // GattServApp will handle notifying all connected GATT clients static Clock_Struct clkPeriodic; // Clock instance for RPA read events. static Clock_Struct clkRpaRead; // Memory to pass periodic event ID to clock handler spClockEventData_t argPeriodic = { .event = SP_PERIODIC_EVT }; // Memory to pass RPA read event ID to clock handler spClockEventData_t argRpaRead = { .event = SP_READ_RPA_EVT }; // Per-handle connection info static spConnRec_t connList[MAX_NUM_BLE_CONNS]; // Current connection handle as chosen by menu static uint16_t menuConnHandle = CONNHANDLE_INVALID; // 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; // GAP GATT Attributes static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple Peripheral"; // 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 GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID) }; // Scan Response Data static uint8_t scanRspData[] = { // complete name 17, // length of this data GAP_ADTYPE_LOCAL_NAME_COMPLETE, 'S', 'i', 'm', 'p', 'l', 'e', 'P', 'e', 'r', 'i', 'p', 'h', 'e', 'r', 'a', 'l', // 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 advHandleLegacy; static uint8 advHandleLongRange; // 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 scCtrlReadyCallback(void); static void scTaskAlertCallback(void); static void processTaskAlert(void); static void SimplePeripheral_init( void ); static void SimplePeripheral_taskFxn(UArg a0, UArg a1); static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg); static uint8_t SimplePeripheral_processGATTMsg(gattMsgEvent_t *pMsg); static void SimplePeripheral_processGapMessage(gapEventHdr_t *pMsg); static void SimplePeripheral_advCallback(uint32_t event, void *pBuf, uintptr_t arg); static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData); static void SimplePeripheral_processAppMsg(spEvt_t *pMsg); static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId); static void SimplePeripheral_performPeriodicTask(void); #if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG) static void SimplePeripheral_updateRPA(void); #endif // PRIVACY_1_2_CFG static void SimplePeripheral_clockHandler(UArg arg); #if defined(GAP_BOND_MGR) static void SimplePeripheral_passcodeCb(uint8_t *pDeviceAddr, uint16_t connHandle, uint8_t uiInputs, uint8_t uiOutputs, uint32_t numComparison); static void SimplePeripheral_pairStateCb(uint16_t connHandle, uint8_t state, uint8_t status); #endif static void SimplePeripheral_processPairState(spPairStateData_t *pPairState); static void SimplePeripheral_processPasscode(spPasscodeData_t *pPasscodeData); static void SimplePeripheral_charValueChangeCB(uint8_t paramId); static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData); static void SimplePeripheral_keyChangeHandler(uint8 keys); static void SimplePeripheral_handleKeys(uint8_t keys); static void SimplePeripheral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg); static void SimplePeripheral_initPHYRSSIArray(void); static void SimplePeripheral_updatePHYStat(uint16_t eventCode, uint8_t *pMsg); static uint8_t SimplePeripheral_addConn(uint16_t connHandle); static uint8_t SimplePeripheral_getConnIndex(uint16_t connHandle); static uint8_t SimplePeripheral_removeConn(uint16_t connHandle); static void SimplePeripheral_processParamUpdate(uint16_t connHandle); static status_t SimplePeripheral_startAutoPhyChange(uint16_t connHandle); static status_t SimplePeripheral_stopAutoPhyChange(uint16_t connHandle); static status_t SimplePeripheral_setPhy(uint16_t connHandle, uint8_t allPhys, uint8_t txPhy, uint8_t rxPhy, uint16_t phyOpts); static uint8_t SimplePeripheral_clearConnListEntry(uint16_t connHandle); static void SimplePeripheral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr, tbmMenuObj_t* pMenuObjNext); static void SimplePeripheral_connEvtCB(Gap_ConnEventRpt_t *pReport); static void SimplePeripheral_processConnEvt(Gap_ConnEventRpt_t *pReport); #ifdef PTM_MODE void simple_peripheral_handleNPIRxInterceptEvent(uint8_t *pMsg); // Declaration static void simple_peripheral_sendToNPI(uint8_t *buf, uint16_t len); // Declaration #endif // PTM_MODE /********************************************************************* * EXTERN FUNCTIONS */ extern void AssertHandler(uint8 assertCause, uint8 assertSubcause); /********************************************************************* * PROFILE CALLBACKS */ #if defined(GAP_BOND_MGR) // GAP Bond Manager Callbacks static gapBondCBs_t SimplePeripheral_BondMgrCBs = { SimplePeripheral_passcodeCb, // Passcode callback SimplePeripheral_pairStateCb // Pairing/Bonding state Callback }; #endif // Simple GATT Profile Callbacks static simpleProfileCBs_t SimplePeripheral_simpleProfileCBs = { SimplePeripheral_charValueChangeCB // Simple GATT Characteristic value change callback }; /********************************************************************* * PUBLIC FUNCTIONS */ /********************************************************************* * @fn simple_peripheral_spin * * @brief Spin forever * * @param none */ void scCtrlReadyCallback(void) { // Notify application `Control READY` is active // user_enqueueRawAppMsg(APP_MSG_SC_CTRL_READY, NULL); } // scCtrlReadyCallback void scTaskAlertCallback(void) { //SimplePeripheral_enqueueMsg(SBP_SC_ALERT, NULL, NULL); DELETE ONE NULL SimplePeripheral_enqueueMsg(APP_MSG_SC_TASK_ALERT, NULL); } // scTaskAlertCallback void processTaskAlert(void) { // Clear the ALERT interrupt source scifClearAlertIntSource(); // Do SC Task processing here // Get 'state.high', and set highStr to appropriate string // uint8_t charValue1= scifTaskData.adcLevelTrigger.output; uint16_t myData =scifTaskData.adcLevelTrigger.output.adcValue; // uint8_t high = scifTaskData.adcLevelTrigger.state.high; // char *highStr = (high != 0) ? "HIGH" : "LOW"; // Set the highStr to the String characteristic in Data Service //DataService_SetParameter(DS_STRING_ID, strlen(highStr), highStr); // SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1,sizeof(uint8_t), // &charValue1); MyData_SetParameter(MYDATA_DATA_ID, MYDATA_DATA_LEN,&myData); Display_printf(dispHandle, 6, 0, "ADC value1: %d\n",myData); // MyData1_SetParameter(MYDATA1_DATA1_ID, MYDATA1_DATA1_LEN,&myData1); // Display_printf(dispHandle, 6, 0, "ADC value2: %d\n",myData1); // Set/clear red LED. // PIN_setOutputValue(ledPinHandle, Board_GPIO_RLED, high); // Acknowledge the ALERT event scifAckAlertEvents(); } // processTaskAlert static void simple_peripheral_spin(void) { volatile uint8_t x = 0; while(1) { x++; } } #ifdef PTM_MODE /********************************************************************* * @fn simple_peripheral_handleNPIRxInterceptEvent * * @brief Intercept an NPI RX serial message and queue for this application. * * @param pMsg - a NPIMSG_msg_t containing the intercepted message. * * @return none. */ void simple_peripheral_handleNPIRxInterceptEvent(uint8_t *pMsg) { // Send Command via HCI TL HCI_TL_SendToStack(((NPIMSG_msg_t *)pMsg)->pBuf); // The data is stored as a message, free this first. ICall_freeMsg(((NPIMSG_msg_t *)pMsg)->pBuf); // Free container. ICall_free(pMsg); } /********************************************************************* * @fn simple_peripheral_sendToNPI * * @brief Create an NPI packet and send to NPI to transmit. * * @param buf - pointer HCI event or data. * * @param len - length of buf in bytes. * * @return none */ static void simple_peripheral_sendToNPI(uint8_t *buf, uint16_t len) { npiPkt_t *pNpiPkt = (npiPkt_t *)ICall_allocMsg(sizeof(npiPkt_t) + len); if (pNpiPkt) { pNpiPkt->hdr.event = buf[0]; //Has the event status code in first byte of payload pNpiPkt->hdr.status = 0xFF; pNpiPkt->pktLen = len; pNpiPkt->pData = (uint8 *)(pNpiPkt + 1); memcpy(pNpiPkt->pData, buf, len); // Send to NPI // Note: there is no need to free this packet. NPI will do that itself. NPITask_sendToHost((uint8_t *)pNpiPkt); } } #endif // PTM_MODE /********************************************************************* * @fn SimplePeripheral_createTask * * @brief Task creation function for the Simple Peripheral. */ void SimplePeripheral_createTask(void) { Task_Params taskParams; // Configure task Task_Params_init(&taskParams); taskParams.stack = spTaskStack; taskParams.stackSize = SP_TASK_STACK_SIZE; taskParams.priority = SP_TASK_PRIORITY; Task_construct(&spTask, SimplePeripheral_taskFxn, &taskParams, NULL); } /********************************************************************* * @fn SimplePeripheral_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 SimplePeripheral_init(void) { // ****************************************************************** // 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); #ifdef USE_RCOSC RCOSC_enableCalibration(); #endif // USE_RCOSC // Create an RTOS queue for message from profile to be sent to app. appMsgQueueHandle = Util_constructQueue(&appMsgQueue); // Create one-shot clock for internal periodic events. Util_constructClock(&clkPeriodic, SimplePeripheral_clockHandler, SP_PERIODIC_EVT_PERIOD, 0, false, (UArg)&argPeriodic); // Set the Device Name characteristic in the GAP GATT Service // For more information, see the section in the User's Guide: // software-dl.ti.com/.../ GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName); // Configure GAP { 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); } #if defined(GAP_BOND_MGR) // Setup the GAP Bond Manager. For more information see the GAP Bond Manager // section in the User's Guide: // software-dl.ti.com/.../ { // 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; 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); } #endif // Initialize GATT attributes GGS_AddService(GATT_ALL_SERVICES); // GAP GATT Service GATTServApp_AddService(GATT_ALL_SERVICES); // GATT Service DevInfo_AddService(); // Device Information Service SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile MyData_AddService( selfEntity ); MyData1_AddService( selfEntity ); // Setup the SimpleProfile Characteristic Values // For more information, see the GATT and GATTServApp sections in the User's Guide: // software-dl.ti.com/.../ { uint8_t charValue1 = 0; uint8_t charValue2 = 2; uint8_t charValue3 = 3; uint8_t charValue4 = 4; uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 }; SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1,sizeof(uint8_t) , &charValue1); SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, sizeof(uint8_t), &charValue2); SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, sizeof(uint8_t), &charValue3); SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t), &charValue4); SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5); /* Add your new characteristic to the service. These names may vary */ uint8_t myData_data_initVal[MYDATA_DATA_LEN] = {0}; MyData_SetParameter(MYDATA_DATA_ID, MYDATA_DATA_LEN, myData_data_initVal); uint8_t myData1_data1_initVal[MYDATA1_DATA1_LEN] = {0}; MyData1_SetParameter(MYDATA1_DATA1_ID, MYDATA1_DATA1_LEN, myData1_data1_initVal); } // Register callback with SimpleGATTprofile SimpleProfile_RegisterAppCBs(&SimplePeripheral_simpleProfileCBs); #if defined(GAP_BOND_MGR) // Start Bond Manager and register callback VOID GAPBondMgr_Register(&SimplePeripheral_BondMgrCBs); #endif // 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: // software-dl.ti.com/.../ 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: // software-dl.ti.com/.../ HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE, APP_SUGGESTED_TX_TIME); } // Initialize GATT Client GATT_InitClient(); // Init key debouncer Board_initKeys(SimplePeripheral_keyChangeHandler); // Initialize Connection List SimplePeripheral_clearConnListEntry(CONNHANDLE_ALL); //Initialize GAP layer for Peripheral role and register to receive GAP events GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, selfEntity, addrMode, NULL); // Initialize array to store connection handle and RSSI values SimplePeripheral_initPHYRSSIArray(); // The type of display is configured based on the BOARD_DISPLAY_USE... // preprocessor definitions dispHandle = Display_open(Display_Type_ANY, NULL); // Initialize Two-Button Menu module TBM_SET_TITLE(&spMenuMain, "Simple Peripheral"); tbm_setItemStatus(&spMenuMain, TBM_ITEM_NONE, TBM_ITEM_ALL); tbm_initTwoBtnMenu(dispHandle, &spMenuMain, 2, SimplePeripheral_menuSwitchCb); Display_printf(dispHandle, SP_ROW_SEPARATOR_1, 0, "===================="); } /********************************************************************* * @fn SimplePeripheral_taskFxn * * @brief Application task entry point for the Simple Peripheral. * * @param a0, a1 - not used. */ static void SimplePeripheral_taskFxn(UArg a0, UArg a1) { // Initialize application SimplePeripheral_init(); // Initialize the Sensor Controller scifOsalInit(); scifOsalRegisterCtrlReadyCallback(scCtrlReadyCallback); scifOsalRegisterTaskAlertCallback(scTaskAlertCallback); scifInit(&scifDriverSetup); // Set the Sensor Controller task tick interval to 1 second uint32_t rtc_Hz = 1; // 1Hz RTC scifStartRtcTicksNow(0x00010000 / rtc_Hz); // Configure Sensor Controller tasks scifTaskData.adcLevelTrigger.cfg.threshold = 600; // Start Sensor Controller task scifStartTasksNbl(BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID)); // 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, SP_ALL_EVENTS, ICALL_TIMEOUT_FOREVER); if (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 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 = SimplePeripheral_processStackMsg((ICall_Hdr *)pMsg); } } if (pMsg && safeToDealloc) { ICall_freeMsg(pMsg); } } // If RTOS queue is not empty, process app message. if (events & SP_QUEUE_EVT) { while (!Queue_empty(appMsgQueueHandle)) { spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle); if (pMsg) { // Process message. SimplePeripheral_processAppMsg(pMsg); // Free the space from the message. ICall_free(pMsg); } } } } } } /********************************************************************* * @fn SimplePeripheral_processStackMsg * * @brief Process an incoming stack message. * * @param pMsg - message to process * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg) { // Always dealloc pMsg unless set otherwise uint8_t safeToDealloc = TRUE; switch (pMsg->event) { case GAP_MSG_EVENT: SimplePeripheral_processGapMessage((gapEventHdr_t*) pMsg); break; case GATT_MSG_EVENT: // Process GATT message safeToDealloc = SimplePeripheral_processGATTMsg((gattMsgEvent_t *)pMsg); break; case HCI_GAP_EVENT_EVENT: { // Process HCI message switch(pMsg->status) { case HCI_COMMAND_COMPLETE_EVENT_CODE: // Process HCI Command Complete Events here { SimplePeripheral_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, SP_ROW_STATUS_1, 0, "PHY Change failure, peer does not support this"); } else { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "PHY Update Status Event: 0x%x", pMyMsg->cmdStatus); } SimplePeripheral_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) { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "PHY Change failure"); } else { // Only symmetrical PHY is supported. // rxPhy should be equal to txPhy. Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "PHY Updated to %s", (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"); } SimplePeripheral_updatePHYStat(HCI_BLE_PHY_UPDATE_COMPLETE_EVENT, (uint8_t *)pMsg); } break; } default: break; } break; } default: // do nothing break; } #ifdef PTM_MODE // Check for NPI Messages hciPacket_t *pBuf = (hciPacket_t *)pMsg; // Serialized HCI Event if (pBuf->hdr.event == HCI_CTRL_TO_HOST_EVENT) { uint16_t len = 0; // Determine the packet length switch(pBuf->pData[0]) { case HCI_EVENT_PACKET: len = HCI_EVENT_MIN_LENGTH + pBuf->pData[2]; break; case HCI_ACL_DATA_PACKET: len = HCI_DATA_MIN_LENGTH + BUILD_UINT16(pBuf->pData[3], pBuf->pData[4]); break; default: break; } // Send to Remote Host. simple_peripheral_sendToNPI(pBuf->pData, len); // Free buffers if needed. switch (pBuf->pData[0]) { case HCI_ACL_DATA_PACKET: case HCI_SCO_DATA_PACKET: BM_free(pBuf->pData); default: break; } } #endif // PTM_MODE return (safeToDealloc); } /********************************************************************* * @fn SimplePeripheral_processGATTMsg * * @brief Process GATT messages and events. * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ static uint8_t SimplePeripheral_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. Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode); } else if (pMsg->method == ATT_MTU_UPDATED_EVENT) { // MTU size updated Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "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 SimplePeripheral_processAppMsg * * @brief Process an incoming callback from a profile. * * @param pMsg - message to process * * @return None. */ static void SimplePeripheral_processAppMsg(spEvt_t *pMsg) { bool dealloc = TRUE; switch (pMsg->event) { case SP_CHAR_CHANGE_EVT: SimplePeripheral_processCharValueChangeEvt(*(uint8_t*)(pMsg->pData)); break; case SP_KEY_CHANGE_EVT: SimplePeripheral_handleKeys(*(uint8_t*)(pMsg->pData)); break; case SP_ADV_EVT: SimplePeripheral_processAdvEvent((spGapAdvEventData_t*)(pMsg->pData)); break; case SP_PAIR_STATE_EVT: SimplePeripheral_processPairState((spPairStateData_t*)(pMsg->pData)); break; case SP_PASSCODE_EVT: SimplePeripheral_processPasscode((spPasscodeData_t*)(pMsg->pData)); break; case SP_PERIODIC_EVT: SimplePeripheral_performPeriodicTask(); break; #if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG) case SP_READ_RPA_EVT: SimplePeripheral_updateRPA(); break; #endif // PRIVACY_1_2_CFG case SP_SEND_PARAM_UPDATE_EVT: { // Extract connection handle from data uint16_t connHandle = *(uint16_t *)(((spClockEventData_t *)pMsg->pData)->data); SimplePeripheral_processParamUpdate(connHandle); // This data is not dynamically allocated dealloc = FALSE; break; } case SP_CONN_EVT: SimplePeripheral_processConnEvt((Gap_ConnEventRpt_t *)(pMsg->pData)); break; case APP_MSG_SC_TASK_ALERT: processTaskAlert(); break; default: // Do nothing. break; } // Free message data if it exists and we are to dealloc if ((dealloc == TRUE) && (pMsg->pData != NULL)) { ICall_free(pMsg->pData); } } /********************************************************************* * @fn SimplePeripheral_processGapMessage * * @brief Process an incoming GAP event. * * @param pMsg - message to process */ static void SimplePeripheral_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_printf(dispHandle, SP_ROW_STATUS_1, 0, "Initialized"); // Setup and start Advertising // For more information, see the GAP section in the User's Guide: // software-dl.ti.com/.../ // 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(&SimplePeripheral_advCallback, &advParamLegacy, &advHandleLegacy); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); // 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); SIMPLEPERIPHERAL_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); SIMPLEPERIPHERAL_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); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); // Use long range params to create long range set #2 GapAdv_params_t advParamLongRange = GAPADV_PARAMS_AE_LONG_RANGE_CONN; // Create Advertisement set #2 and assign handle status = GapAdv_create(&SimplePeripheral_advCallback, &advParamLongRange, &advHandleLongRange); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); // Load advertising data for set #2 that is statically allocated by the app status = GapAdv_loadByHandle(advHandleLongRange, GAP_ADV_DATA_TYPE_ADV, sizeof(advertData), advertData); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); // Set event mask for set #2 status = GapAdv_setEventMask(advHandleLongRange, GAP_ADV_EVT_MASK_START_AFTER_ENABLE | GAP_ADV_EVT_MASK_END_AFTER_DISABLE | GAP_ADV_EVT_MASK_SET_TERMINATED); // Enable long range advertising for set #2 status = GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); #ifdef PTM_MODE // Enable "Enable PTM Mode" option tbm_setItemStatus(&spMenuMain, SP_ITEM_PTM_ENBL, SP_ITEM_NONE); #endif // Display device address Display_printf(dispHandle, SP_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) { SimplePeripheral_updateRPA(); // Create one-shot clock for RPA check event. Util_constructClock(&clkRpaRead, SimplePeripheral_clockHandler, SP_READ_RPA_EVT_PERIOD, 0, true, (UArg) &argRpaRead); } #endif // PRIVACY_1_2_CFG } break; } case GAP_LINK_ESTABLISHED_EVENT: { gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg; // Display the amount of current connections uint8_t numActive = linkDB_NumActive(); Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Num Conns: %d", (uint16_t)numActive); if (pPkt->hdr.status == SUCCESS) { // Add connection to list and start RSSI SimplePeripheral_addConn(pPkt->connectionHandle); // Display the address of this connection Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connected to %s", Util_convertBdAddr2Str(pPkt->devAddr)); // Enable connection selection option tbm_setItemStatus(&spMenuMain, SP_ITEM_SELECT_CONN, TBM_ITEM_NONE); // Start Periodic Clock. Util_startClock(&clkPeriodic); } if (numActive < MAX_NUM_BLE_CONNS) { // Start advertising since there is room for more connections GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); } else { // Stop advertising since there is no room for more connections GapAdv_disable(advHandleLongRange); GapAdv_disable(advHandleLegacy); } break; } case GAP_LINK_TERMINATED_EVENT: { gapTerminateLinkEvent_t *pPkt = (gapTerminateLinkEvent_t *)pMsg; // Display the amount of current connections uint8_t numActive = linkDB_NumActive(); Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Device Disconnected!"); Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Num Conns: %d", (uint16_t)numActive); // Remove the connection from the list and disable RSSI if needed SimplePeripheral_removeConn(pPkt->connectionHandle); // If no active connections if (numActive == 0) { // Stop periodic clock Util_stopClock(&clkPeriodic); // Disable Connection Selection option tbm_setItemStatus(&spMenuMain, TBM_ITEM_NONE, SP_ITEM_SELECT_CONN); } // Start advertising since there is room for more connections GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); // Clear remaining lines Display_clearLine(dispHandle, SP_ROW_CONNECTION); break; } case GAP_UPDATE_LINK_PARAM_REQ_EVENT: { gapUpdateLinkParamReqReply_t rsp; gapUpdateLinkParamReqEvent_t *pReq = (gapUpdateLinkParamReqEvent_t *)pMsg; rsp.connectionHandle = pReq->req.connectionHandle; // 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 { 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; linkDB_GetInfo(pPkt->connectionHandle, &linkInfo); if(pPkt->status == SUCCESS) { // Display the address of the connection update Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Link Param Updated: %s", Util_convertBdAddr2Str(linkInfo.addr)); } else { // Display the address of the connection update failure Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Link Param Update Failed 0x%x: %s", pPkt->opcode, Util_convertBdAddr2Str(linkInfo.addr)); } // Check if there are any queued parameter updates spConnHandleEntry_t *connHandleEntry = (spConnHandleEntry_t *)List_get(¶mUpdateList); if (connHandleEntry != NULL) { // Attempt to send queued update now SimplePeripheral_processParamUpdate(connHandleEntry->connHandle); // Free list element ICall_free(connHandleEntry); } break; } default: Display_clearLines(dispHandle, SP_ROW_STATUS_1, SP_ROW_STATUS_2); break; } } /********************************************************************* * @fn SimplePeripheral_charValueChangeCB * * @brief Callback from Simple Profile indicating a characteristic * value change. * * @param paramId - parameter Id of the value that was changed. * * @return None. */ static void SimplePeripheral_charValueChangeCB(uint8_t paramId) { uint8_t *pValue = ICall_malloc(sizeof(uint8_t)); if (pValue) { *pValue = paramId; if (SimplePeripheral_enqueueMsg(SP_CHAR_CHANGE_EVT, pValue) != SUCCESS) { ICall_free(pValue); } } } /********************************************************************* * @fn SimplePeripheral_processCharValueChangeEvt * * @brief Process a pending Simple Profile characteristic value change * event. * * @param paramID - parameter ID of the value that was changed. */ static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId) { uint8_t newValue; switch(paramId) { case SIMPLEPROFILE_CHAR1: SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue); Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 1: %d", (uint16_t)newValue); break; case SIMPLEPROFILE_CHAR3: SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue); Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 3: %d", (uint16_t)newValue); break; default: // should not reach here! break; } } /********************************************************************* * @fn SimplePeripheral_performPeriodicTask * * @brief Perform a periodic application task. This function gets called * every five seconds (SP_PERIODIC_EVT_PERIOD). In this example, * the value of the third characteristic in the SimpleGATTProfile * service is retrieved from the profile, and then copied into the * value of the the fourth characteristic. * * @param None. * * @return None. */ static void SimplePeripheral_performPeriodicTask(void) { uint8_t valueToCopy; // Call to retrieve the value of the third characteristic in the profile if (SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &valueToCopy) == SUCCESS) { // Call to set that value of the fourth characteristic in the profile. // Note that if notifications of the fourth characteristic have been // enabled by a GATT client device, then a notification will be sent // every time this function is called. SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t), &valueToCopy); } } #if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG) /********************************************************************* * @fn SimplePeripheral_updateRPA * * @brief Read the current RPA from the stack and update display * if the RPA has changed. * * @param None. * * @return None. */ static void SimplePeripheral_updateRPA(void) { uint8_t* pRpaNew; // 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, SP_ROW_RPA, 0, "RP Addr: %s", Util_convertBdAddr2Str(pRpaNew)); memcpy(rpa, pRpaNew, B_ADDR_LEN); } } #endif // PRIVACY_1_2_CFG /********************************************************************* * @fn SimplePeripheral_clockHandler * * @brief Handler function for clock timeouts. * * @param arg - event type * * @return None. */ static void SimplePeripheral_clockHandler(UArg arg) { spClockEventData_t *pData = (spClockEventData_t *)arg; if (pData->event == SP_PERIODIC_EVT) { // Start the next period Util_startClock(&clkPeriodic); // Post event to wake up the application SimplePeripheral_enqueueMsg(SP_PERIODIC_EVT, NULL); } else if (pData->event == SP_READ_RPA_EVT) { // Start the next period Util_startClock(&clkRpaRead); // Post event to read the current RPA SimplePeripheral_enqueueMsg(SP_READ_RPA_EVT, NULL); } else if (pData->event == SP_SEND_PARAM_UPDATE_EVT) { // Send message to app SimplePeripheral_enqueueMsg(SP_SEND_PARAM_UPDATE_EVT, pData); } } /********************************************************************* * @fn SimplePeripheral_keyChangeHandler * * @brief Key event handler function * * @param keys - bitmap of pressed keys * * @return none */ static void SimplePeripheral_keyChangeHandler(uint8_t keys) { uint8_t *pValue = ICall_malloc(sizeof(uint8_t)); if (pValue) { *pValue = keys; if(SimplePeripheral_enqueueMsg(SP_KEY_CHANGE_EVT, pValue) != SUCCESS) { ICall_free(pValue); } } } /********************************************************************* * @fn SimplePeripheral_handleKeys * * @brief Handles all key events for this device. * * @param keys - bit field for key events. Valid entries: * KEY_LEFT * KEY_RIGHT */ static void SimplePeripheral_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 SimplePeripheral_doSetConnPhy * * @brief Set 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 SimplePeripheral_doSetConnPhy(uint8 index) { bool status = TRUE; 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, AUTO_PHY_UPDATE }; uint8_t connIndex = SimplePeripheral_getConnIndex(menuConnHandle); if (connIndex >= MAX_NUM_BLE_CONNS) { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!"); return FALSE; } // Set Phy Preference on the current connection. Apply the same value // for RX and TX. // If auto PHY update is not selected and if auto PHY update is enabled, then // stop auto PHY update // Note PHYs are already enabled by default in build_config.opt in stack project. if(phy[index] != AUTO_PHY_UPDATE) { // Cancel RSSI reading and auto phy changing SimplePeripheral_stopAutoPhyChange(connList[connIndex].connHandle); SimplePeripheral_setPhy(menuConnHandle, 0, phy[index], phy[index], 0); Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "PHY preference: %s", TBM_GET_ACTION_DESC(&spMenuConnPhy, index)); } else { // Start RSSI read for auto PHY update (if it is disabled) SimplePeripheral_startAutoPhyChange(menuConnHandle); } return status; } /********************************************************************* * @fn SimplePeripheral_advCallback * * @brief GapAdv module callback * * @param pMsg - message to process */ static void SimplePeripheral_advCallback(uint32_t event, void *pBuf, uintptr_t arg) { spGapAdvEventData_t *pData = ICall_malloc(sizeof(spGapAdvEventData_t)); if (pData) { pData->event = event; pData->pBuf = pBuf; if(SimplePeripheral_enqueueMsg(SP_ADV_EVT, pData) != SUCCESS) { ICall_free(pData); } } } /********************************************************************* * @fn SimplePeripheral_processAdvEvent * * @brief Process advertising event in app context * * @param pEventData */ static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData) { switch (pEventData->event) { case GAP_EVT_ADV_START_AFTER_ENABLE: Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Enabled", *(uint8_t *)(pEventData->pBuf)); break; case GAP_EVT_ADV_END_AFTER_DISABLE: Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Disabled", *(uint8_t *)(pEventData->pBuf)); break; case GAP_EVT_ADV_START: break; case GAP_EVT_ADV_END: break; case GAP_EVT_ADV_SET_TERMINATED: { #ifndef Display_DISABLE_ALL GapAdv_setTerm_t *advSetTerm = (GapAdv_setTerm_t *)(pEventData->pBuf); #endif Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d disabled after conn %d", advSetTerm->handle, advSetTerm->connHandle ); } break; case GAP_EVT_SCAN_REQ_RECEIVED: break; 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); } } #if defined(GAP_BOND_MGR) /********************************************************************* * @fn SimplePeripheral_pairStateCb * * @brief Pairing state callback. * * @return none */ static void SimplePeripheral_pairStateCb(uint16_t connHandle, uint8_t state, uint8_t status) { spPairStateData_t *pData = ICall_malloc(sizeof(spPairStateData_t)); // Allocate space for the event data. if (pData) { pData->state = state; pData->connHandle = connHandle; pData->status = status; // Queue the event. if(SimplePeripheral_enqueueMsg(SP_PAIR_STATE_EVT, pData) != SUCCESS) { ICall_free(pData); } } } /********************************************************************* * @fn SimplePeripheral_passcodeCb * * @brief Passcode callback. * * @return none */ static void SimplePeripheral_passcodeCb(uint8_t *pDeviceAddr, uint16_t connHandle, uint8_t uiInputs, uint8_t uiOutputs, uint32_t numComparison) { spPasscodeData_t *pData = ICall_malloc(sizeof(spPasscodeData_t)); // Allocate space for the passcode event. if (pData ) { pData->connHandle = connHandle; memcpy(pData->deviceAddr, pDeviceAddr, B_ADDR_LEN); pData->uiInputs = uiInputs; pData->uiOutputs = uiOutputs; pData->numComparison = numComparison; // Enqueue the event. if(SimplePeripheral_enqueueMsg(SP_PASSCODE_EVT, pData) != SUCCESS) { ICall_free(pData); } } } #endif /********************************************************************* * @fn SimplePeripheral_processPairState * * @brief Process the new paring state. * * @return none */ static void SimplePeripheral_processPairState(spPairStateData_t *pPairData) { uint8_t state = pPairData->state; uint8_t status = pPairData->status; switch (state) { case GAPBOND_PAIRING_STATE_STARTED: Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing started"); break; case GAPBOND_PAIRING_STATE_COMPLETE: if (status == SUCCESS) { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing success"); } else { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing fail: %d", status); } break; case GAPBOND_PAIRING_STATE_ENCRYPTED: if (status == SUCCESS) { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Encryption success"); } else { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Encryption failed: %d", status); } break; case GAPBOND_PAIRING_STATE_BOND_SAVED: if (status == SUCCESS) { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Bond save success"); } else { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Bond save failed: %d", status); } break; default: break; } } /********************************************************************* * @fn SimplePeripheral_processPasscode * * @brief Process the Passcode request. * * @return none */ static void SimplePeripheral_processPasscode(spPasscodeData_t *pPasscodeData) { // Display passcode to user if (pPasscodeData->uiOutputs != 0) { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Passcode: %d", B_APP_DEFAULT_PASSCODE); } #if defined(GAP_BOND_MGR) // Send passcode response GAPBondMgr_PasscodeRsp(pPasscodeData->connHandle , SUCCESS, B_APP_DEFAULT_PASSCODE); #endif } /********************************************************************* * @fn SimplePeripheral_connEvtCB * * @brief Connection event callback. * * @param pReport pointer to connection event report */ static void SimplePeripheral_connEvtCB(Gap_ConnEventRpt_t *pReport) { // Enqueue the event for processing in the app context. if(SimplePeripheral_enqueueMsg(SP_CONN_EVT, pReport) != SUCCESS) { ICall_free(pReport); } } /********************************************************************* * @fn SimplePeripheral_processConnEvt * * @brief Process connection event. * * @param pReport pointer to connection event report */ static void SimplePeripheral_processConnEvt(Gap_ConnEventRpt_t *pReport) { // Get index from handle uint8_t connIndex = SimplePeripheral_getConnIndex(pReport->handle); if (connIndex >= MAX_NUM_BLE_CONNS) { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!"); return; } // If auto phy change is enabled if (connList[connIndex].isAutoPHYEnable == TRUE) { // Read the RSSI HCI_ReadRssiCmd(pReport->handle); } } /********************************************************************* * @fn SimplePeripheral_enqueueMsg * * @brief Creates a message and puts the message in RTOS queue. * * @param event - message event. * @param state - message state. */ static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData) { uint8_t success; spEvt_t *pMsg = ICall_malloc(sizeof(spEvt_t)); // Create dynamic pointer to message. if(pMsg) { pMsg->event = event; pMsg->pData = pData; // Enqueue the message. success = Util_enqueueMsg(appMsgQueueHandle, syncEvent, (uint8_t *)pMsg); return (success) ? SUCCESS : FAILURE; } return(bleMemAllocError); } /********************************************************************* * @fn SimplePeripheral_doSelectConn * * @brief Select a connection to communicate with * * @param index - item index from the menu * * @return always true */ bool SimplePeripheral_doSelectConn(uint8_t index) { menuConnHandle = connList[index].connHandle; // Set the menu title and go to this connection's context TBM_SET_TITLE(&spMenuPerConn, TBM_GET_ACTION_DESC(&spMenuSelectConn, index)); // Clear non-connection-related message Display_clearLine(dispHandle, SP_ROW_CONNECTION); tbm_goTo(&spMenuPerConn); return (true); } /********************************************************************* * @fn SimplePeripheral_addConn * * @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 SimplePeripheral_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; // Allocate data to send through clock handler connList[i].pParamUpdateEventData = ICall_malloc(sizeof(spClockEventData_t) + sizeof (uint16_t)); if(connList[i].pParamUpdateEventData) { connList[i].pParamUpdateEventData->event = SP_SEND_PARAM_UPDATE_EVT; *((uint16_t *)connList[i].pParamUpdateEventData->data) = 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, SimplePeripheral_clockHandler, SP_SEND_PARAM_UPDATE_DELAY, 0, true, (UArg) (connList[i].pParamUpdateEventData)); } else { ICall_free(connList[i].pParamUpdateEventData); } } else { status = bleMemAllocError; } // Set default PHY to 1M connList[i].currPhy = HCI_PHY_1_MBPS; break; } } return status; } /********************************************************************* * @fn SimplePeripheral_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 SimplePeripheral_getConnIndex(uint16_t connHandle) { uint8_t i; for (i = 0; i < MAX_NUM_BLE_CONNS; i++) { if (connList[i].connHandle == connHandle) { return i; } } return(MAX_NUM_BLE_CONNS); } /********************************************************************* * @fn SimplePeripheral_getConnIndex * * @brief Find index in the connected device list by connHandle * * @return SUCCESS if connHandle found valid index or bleInvalidRange * if index wasn't found. CONNHANDLE_ALL will always succeed. */ static uint8_t SimplePeripheral_clearConnListEntry(uint16_t connHandle) { 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 = SimplePeripheral_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; memset(connList[i].rssiArr, 0, SP_MAX_RSSI_STORE_DEPTH); connList[i].rssiAvg = 0; connList[i].rssiCntr = 0; connList[i].isAutoPHYEnable = FALSE; } } return(SUCCESS); } /********************************************************************* * @fn SimplePeripheral_clearPendingParamUpdate * * @brief clean pending param update request in the paramUpdateList list * * @param connHandle - connection handle to clean * * @return none */ void SimplePeripheral_clearPendingParamUpdate(uint16_t connHandle) { List_Elem *curr; for (curr = List_head(¶mUpdateList); curr != NULL; curr = List_next(curr)) { if (((spConnHandleEntry_t *)curr)->connHandle == connHandle) { List_remove(¶mUpdateList, curr); } } } /********************************************************************* * @fn SimplePeripheral_removeConn * * @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 SimplePeripheral_removeConn(uint16_t connHandle) { uint8_t connIndex = SimplePeripheral_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); // Free ParamUpdateEventData ICall_free(connList[connIndex].pParamUpdateEventData); } // Clear pending update requests from paramUpdateList SimplePeripheral_clearPendingParamUpdate(connHandle); // Stop Auto PHY Change SimplePeripheral_stopAutoPhyChange(connHandle); // Clear Connection List Entry SimplePeripheral_clearConnListEntry(connHandle); } return connIndex; } /********************************************************************* * @fn SimplePeripheral_processParamUpdate * * @brief Process a parameters update request * * @return None */ static void SimplePeripheral_processParamUpdate(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 = SimplePeripheral_getConnIndex(connHandle); if (connIndex >= MAX_NUM_BLE_CONNS) { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!"); return; } // Deconstruct the clock object Clock_destruct(connList[connIndex].pUpdateClock); // Free clock struct, only in case it is not NULL if (connList[connIndex].pUpdateClock != NULL) { ICall_free(connList[connIndex].pUpdateClock); connList[connIndex].pUpdateClock = NULL; } // Free ParamUpdateEventData, only in case it is not NULL if (connList[connIndex].pParamUpdateEventData != NULL) ICall_free(connList[connIndex].pParamUpdateEventData); // Send parameter update bStatus_t status = GAP_UpdateLinkParamReq(&req); // If there is an ongoing update, queue this for when the udpate completes if (status == bleAlreadyInRequestedMode) { spConnHandleEntry_t *connHandleEntry = ICall_malloc(sizeof(spConnHandleEntry_t)); if (connHandleEntry) { connHandleEntry->connHandle = connHandle; List_put(¶mUpdateList, (List_Elem *)connHandleEntry); } } } /********************************************************************* * @fn SimpleCentral_processCmdCompleteEvt * * @brief Process an incoming OSAL HCI Command Complete Event. * * @param pMsg - message to process * * @return none */ static void SimplePeripheral_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: { 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]); uint8_t index = SimplePeripheral_getConnIndex(handle); SIMPLEPERIPHERAL_ASSERT(index < MAX_NUM_BLE_CONNS); if (rssi != LL_RSSI_NOT_AVAILABLE) { connList[index].rssiArr[connList[index].rssiCntr++] = rssi; connList[index].rssiCntr %= SP_MAX_RSSI_STORE_DEPTH; int16_t sum_rssi = 0; for(uint8_t cnt=0; cnt<SP_MAX_RSSI_STORE_DEPTH; cnt++) { sum_rssi += connList[index].rssiArr[cnt]; } connList[index].rssiAvg = (uint32_t)(sum_rssi/SP_MAX_RSSI_STORE_DEPTH); uint8_t phyRq = SP_PHY_NONE; uint8_t phyRqS = SP_PHY_NONE; uint8_t phyOpt = LL_PHY_OPT_NONE; if(connList[index].phyCngRq == FALSE) { if((connList[index].rssiAvg >= RSSI_2M_THRSHLD) && (connList[index].currPhy != HCI_PHY_2_MBPS) && (connList[index].currPhy != SP_PHY_NONE)) { // try to go to higher data rate phyRqS = phyRq = HCI_PHY_2_MBPS; } else if((connList[index].rssiAvg < RSSI_2M_THRSHLD) && (connList[index].rssiAvg >= RSSI_1M_THRSHLD) && (connList[index].currPhy != HCI_PHY_1_MBPS) && (connList[index].currPhy != SP_PHY_NONE)) { // try to go to legacy regular data rate phyRqS = phyRq = HCI_PHY_1_MBPS; } else if((connList[index].rssiAvg >= RSSI_S2_THRSHLD) && (connList[index].rssiAvg < RSSI_1M_THRSHLD) && (connList[index].currPhy != SP_PHY_NONE)) { // try to go to lower data rate S=2(500kb/s) phyRqS = HCI_PHY_CODED; phyOpt = LL_PHY_OPT_S2; phyRq = BLE5_CODED_S2_PHY; } else if(connList[index].rssiAvg < RSSI_S2_THRSHLD ) { // try to go to lowest data rate S=8(125kb/s) phyRqS = HCI_PHY_CODED; phyOpt = LL_PHY_OPT_S8; phyRq = BLE5_CODED_S8_PHY; } if((phyRq != SP_PHY_NONE) && // First check if the request for this phy change is already not honored then don't request for change (((connList[index].rqPhy == phyRq) && (connList[index].phyRqFailCnt < 2)) || (connList[index].rqPhy != phyRq))) { //Initiate PHY change based on RSSI SimplePeripheral_setPhy(connList[index].connHandle, 0, phyRqS, phyRqS, phyOpt); connList[index].phyCngRq = TRUE; // If it a request for different phy than failed request, reset the count if(connList[index].rqPhy != phyRq) { // then reset the request phy counter and requested phy connList[index].phyRqFailCnt = 0; } if(phyOpt == LL_PHY_OPT_NONE) { connList[index].rqPhy = phyRq; } else if(phyOpt == LL_PHY_OPT_S2) { connList[index].rqPhy = BLE5_CODED_S2_PHY; } else { connList[index].rqPhy = BLE5_CODED_S8_PHY; } } // end of if ((phyRq != SP_PHY_NONE) && ... } // end of if (connList[index].phyCngRq == FALSE) } // end of if (rssi != LL_RSSI_NOT_AVAILABLE) Display_printf(dispHandle, SP_ROW_RSSI, 0, "RSSI:%d dBm, AVG RSSI:%d dBm", (uint32_t)(rssi), connList[index].rssiAvg); } // end of if (status == SUCCESS) break; } case HCI_LE_READ_PHY: { if (status == SUCCESS) { Display_printf(dispHandle, SP_ROW_RSSI + 2, 0, "RXPh: %d, TXPh: %d", pMsg->pReturnParam[3], pMsg->pReturnParam[4]); } break; } default: break; } // end of switch (pMsg->cmdOpcode) } /********************************************************************* * @fn SimplePeripheral_initPHYRSSIArray * * @brief Initializes the array of structure/s to store data related * RSSI based auto PHy change * * @param connHandle - the connection handle * * @param addr - pointer to device address * * @return index of connection handle */ static void SimplePeripheral_initPHYRSSIArray(void) { //Initialize array to store connection handle and RSSI values memset(connList, 0, sizeof(connList)); for (uint8_t index = 0; index < MAX_NUM_BLE_CONNS; index++) { connList[index].connHandle = SP_INVALID_HANDLE; } } /********************************************************************* // Set default PHY to 1M * @fn SimplePeripheral_startAutoPhyChange * * @brief Start periodic RSSI reads on a link. * * @param connHandle - connection handle of link * @param devAddr - device address * * @return SUCCESS: Terminate started * bleIncorrectMode: No link * bleNoResources: No resources */ static status_t SimplePeripheral_startAutoPhyChange(uint16_t connHandle) { status_t status = FAILURE; // Get connection index from handle uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle); SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS); // Start Connection Event notice for RSSI calculation status = Gap_RegisterConnEventCb(SimplePeripheral_connEvtCB, GAP_CB_REGISTER, connHandle); // Flag in connection info if successful if (status == SUCCESS) { connList[connIndex].isAutoPHYEnable = TRUE; } return status; } /********************************************************************* * @fn SimplePeripheral_stopAutoPhyChange * * @brief Cancel periodic RSSI reads on a link. * * @param connHandle - connection handle of link * * @return SUCCESS: Operation successful * bleIncorrectMode: No link */ static status_t SimplePeripheral_stopAutoPhyChange(uint16_t connHandle) { // Get connection index from handle uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle); SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS); // Stop connection event notice Gap_RegisterConnEventCb(NULL, GAP_CB_UNREGISTER, connHandle); // Also update the phychange request status for active RSSI tracking connection connList[connIndex].phyCngRq = FALSE; connList[connIndex].isAutoPHYEnable = FALSE; return SUCCESS; } /********************************************************************* * @fn SimplePeripheral_setPhy * * @brief Call the HCI set phy API and and add the handle to a * list to match it to an incoming command status event */ static status_t SimplePeripheral_setPhy(uint16_t connHandle, uint8_t allPhys, uint8_t txPhy, uint8_t rxPhy, uint16_t phyOpts) { // Allocate list entry to store handle for command status spConnHandleEntry_t *connHandleEntry = ICall_malloc(sizeof(spConnHandleEntry_t)); if (connHandleEntry) { connHandleEntry->connHandle = connHandle; // Add entry to the phy command status list List_put(&setPhyCommStatList, (List_Elem *)connHandleEntry); // Send PHY Update HCI_LE_SetPhyCmd(connHandle, allPhys, txPhy, rxPhy, phyOpts); } return SUCCESS; } /********************************************************************* * @fn SimplePeripheral_updatePHYStat * * @brief Update the auto phy update state machine * * @param connHandle - the connection handle * * @return None */ static void SimplePeripheral_updatePHYStat(uint16_t eventCode, uint8_t *pMsg) { uint8_t connIndex; switch (eventCode) { case HCI_LE_SET_PHY: { // Get connection handle from list spConnHandleEntry_t *connHandleEntry = (spConnHandleEntry_t *)List_get(&setPhyCommStatList); if (connHandleEntry) { // Get index from connection handle connIndex = SimplePeripheral_getConnIndex(connHandleEntry->connHandle); ICall_free(connHandleEntry); // Is this connection still valid? if (connIndex < MAX_NUM_BLE_CONNS) { hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg; if (pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE) { // Update the phychange 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 connIndex = SimplePeripheral_getConnIndex(pPUC->connHandle); // Is this connection still valid? if (connIndex < MAX_NUM_BLE_CONNS) { // Update the phychange request status for active RSSI tracking connection connList[connIndex].phyCngRq = FALSE; if (pPUC->status == SUCCESS) { connList[connIndex].currPhy = pPUC->rxPhy; } if(pPUC->rxPhy != connList[connIndex].rqPhy) { connList[connIndex].phyRqFailCnt++; } else { // Reset the request phy counter and requested phy connList[connIndex].phyRqFailCnt = 0; connList[connIndex].rqPhy = 0; } } } break; } default: break; } // end of switch (eventCode) } /********************************************************************* * @fn SimplePeripheral_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 SimplePeripheral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr, tbmMenuObj_t* pMenuObjNext) { uint8_t NUMB_ACTIVE_CONNS = linkDB_NumActive(); // interested in only the events of // entering scMenuConnect, spMenuSelectConn, and scMenuMain for now if (pMenuObjNext == &spMenuSelectConn) { static uint8_t* pAddrs; uint8_t* pAddrTemp; if (pAddrs != NULL) { ICall_free(pAddrs); } // Allocate buffer to display addresses pAddrs = ICall_malloc(NUMB_ACTIVE_CONNS * SP_ADDR_STR_SIZE); if (pAddrs == NULL) { TBM_SET_NUM_ITEM(&spMenuSelectConn, 0); } else { uint8_t i; TBM_SET_NUM_ITEM(&spMenuSelectConn, 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) { // Get the address from the connection handle linkDBInfo_t linkInfo; linkDB_GetInfo(connList[i].connHandle, &linkInfo); // This connection is active. Set the corresponding menu item with // the address of this connection and enable the item. memcpy(pAddrTemp, Util_convertBdAddr2Str(linkInfo.addr), SP_ADDR_STR_SIZE); TBM_SET_ACTION_DESC(&spMenuSelectConn, i, pAddrTemp); tbm_setItemStatus(&spMenuSelectConn, (1 << i), SP_ITEM_NONE); pAddrTemp += SP_ADDR_STR_SIZE; } else { // This connection is not active. Disable the corresponding menu item. tbm_setItemStatus(&spMenuSelectConn, SP_ITEM_NONE, (1 << i)); } } } } else if (pMenuObjNext == &spMenuMain) { // Now we are not in a specific connection's context // Clear connection-related message Display_clearLine(dispHandle, SP_ROW_CONNECTION); } } #ifdef PTM_MODE /********************************************************************* * @fn SimplePeripheral_doEnablePTMMode * * @brief Stop advertising, configure & start PTM mode * * @param index - item index from the menu * * @return always true */ bool SimplePeripheral_doEnablePTMMode(uint8_t index) { // Clear Display Display_clearLines(dispHandle, 0, 15); // Indicate in screen that PTM Mode is initializing Display_printf(dispHandle, 1, 0, "PTM Mode initializing!\n\n\rPlease note UART feed will now stop..."); // Before starting the NPI task close Display driver to make sure there is no shared resource used by both Display_close(dispHandle); // Start NPI task NPITask_createTask(ICALL_SERVICE_CLASS_BLE); // Disable Advertising and destroy sets GapAdv_destroy(advHandleLegacy,GAP_ADV_FREE_OPTION_ALL_DATA); GapAdv_destroy(advHandleLongRange,GAP_ADV_FREE_OPTION_ALL_DATA); // Intercept NPI RX events. NPITask_registerIncomingRXEventAppCB(simple_peripheral_handleNPIRxInterceptEvent, INTERCEPT); // Register for Command Status information HCI_TL_Init(NULL, (HCI_TL_CommandStatusCB_t) simple_peripheral_sendToNPI, NULL, selfEntity); // Register for Events HCI_TL_getCmdResponderID(ICall_getLocalMsgEntityId(ICALL_SERVICE_CLASS_BLE_MSG, selfEntity)); // Inform Stack to Initialize PTM HCI_EXT_EnablePTMCmd(); // Open back the display to avoid crashes to future calls to Display_printf (even though they won't go through until reboot) dispHandle = Display_open(Display_Type_ANY, NULL); return TRUE; } #endif /********************************************************************* *********************************************************************/
您好、Sai、
感谢您提供的详细信息。 您看起来是在正确的方向上。
当您说"我无法读取正确的 ADC 值"时、这是否意味着该值卡在0 (或任何其他值)、或者该值不准确? 如果在 processTaskAlert()函数中停止执行,是否在 MyData 中有预期值?
如果前面的内容 没有帮助、我建议您首先将传感器控制器代码与不太复杂的项目(例如空项目)集成、这样您就可以验证 ADC 读取工作是否符合预期。
此致、
请参阅下面的博文。 博客中使用的 SDK 很旧、因此需要进行调整。
https://markelthinkslearnscreates.wordpress.com/2022/08/23/sensor-controller/
-kel
谢谢、我尝试了空项目...我能够读取预期值... 但无法在 BLE 项目中读取。
感谢您分享@Markel。 我跟在你的教学中。 我成功构建并运行了代码。 但我无法在 ble app 中读取 ADC 值、它显示为零。
以下是我的代码:
/****************************************************************************** @file simple_peripheral.c @brief This file contains the Simple Peripheral 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> #if !(defined __TI_COMPILER_VERSION__) #include <intrinsics.h> #endif #include <ti/drivers/utils/List.h> #include <icall.h> #include "util.h" #include <bcomdef.h> /* This Header file contains all BLE API and icall structure definition */ #include <icall_ble_api.h> #include <devinfoservice.h> #include <simple_gatt_profile.h> #include "myData.h" #include "myData1.h" #ifdef USE_RCOSC #include <rcosc_calibration.h> #endif //USE_RCOSC #include <board.h> #include <board_key.h> #include <scif.h> #include <menu/two_btn_menu.h> #include "simple_peripheral_menu.h" #include "simple_peripheral.h" #ifdef PTM_MODE #include "npi_task.h" // To allow RX event registration #include "npi_ble.h" // To enable transmission of messages to UART #include "icall_hci_tl.h" // To allow ICall HCI Transport Layer #endif // PTM_MODE /********************************************************************* * MACROS */ /********************************************************************* * CONSTANTS */ // 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 extern uint16_t myData; extern uint16_t myData1; #define DEFAULT_ADDRESS_MODE ADDRMODE_PUBLIC // 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 80 // Maximum connection interval (units of 1.25ms, 104=130ms) for parameter update request #define DEFAULT_DESIRED_MAX_CONN_INTERVAL 104 // Slave latency to use for parameter update request #define DEFAULT_DESIRED_SLAVE_LATENCY 0 // Supervision timeout value (units of 10ms, 300=3s) for parameter update request #define DEFAULT_DESIRED_CONN_TIMEOUT 300 // Pass parameter updates to the app for it to decide. #define DEFAULT_PARAM_UPDATE_REQ_DECISION GAP_UPDATE_REQ_PASS_TO_APP // How often to perform periodic event (in ms) #define SP_PERIODIC_EVT_PERIOD 5000 // How often to read current current RPA (in ms) #define SP_READ_RPA_EVT_PERIOD 3000 // Delay (in ms) after connection establishment before sending a parameter update requst #define SP_SEND_PARAM_UPDATE_DELAY 6000 // Task configuration #define SP_TASK_PRIORITY 1 #ifndef SP_TASK_STACK_SIZE #define SP_TASK_STACK_SIZE 644 #endif // Application events #define SP_STATE_CHANGE_EVT 0 #define SP_CHAR_CHANGE_EVT 1 #define SP_KEY_CHANGE_EVT 2 #define SP_ADV_EVT 3 #define SP_PAIR_STATE_EVT 4 #define SP_PASSCODE_EVT 5 #define SP_PERIODIC_EVT 6 #define SP_READ_RPA_EVT 7 #define SP_SEND_PARAM_UPDATE_EVT 8 #define SP_CONN_EVT 9 // Internal Events for RTOS application #define SP_ICALL_EVT ICALL_MSG_EVENT_ID // Event_Id_31 #define SP_QUEUE_EVT UTIL_QUEUE_EVENT_ID // Event_Id_30 #define SP_SCTASK_EVT Event_Id_04 // Bitwise OR of all RTOS events to pend on #define SP_ALL_EVENTS (SP_ICALL_EVT | \ SP_QUEUE_EVT | \ SP_SCTASK_EVT) // Size of string-converted device address ("0xXXXXXXXXXXXX") #define SP_ADDR_STR_SIZE 15 // Row numbers for two-button menu #define SP_ROW_SEPARATOR_1 (TBM_ROW_APP + 0) #define SP_ROW_STATUS_1 (TBM_ROW_APP + 1) #define SP_ROW_STATUS_2 (TBM_ROW_APP + 2) #define SP_ROW_CONNECTION (TBM_ROW_APP + 3) #define SP_ROW_ADVSTATE (TBM_ROW_APP + 4) #define SP_ROW_RSSI (TBM_ROW_APP + 5) #define SP_ROW_IDA (TBM_ROW_APP + 6) #define SP_ROW_RPA (TBM_ROW_APP + 7) #define SP_ROW_DEBUG (TBM_ROW_APP + 8) // For storing the active connections #define SP_RSSI_TRACK_CHNLS 1 // Max possible channels can be GAP_BONDINGS_MAX #define SP_MAX_RSSI_STORE_DEPTH 5 #define SP_INVALID_HANDLE 0xFFFF #define RSSI_2M_THRSHLD -30 #define RSSI_1M_THRSHLD -40 #define RSSI_S2_THRSHLD -50 #define RSSI_S8_THRSHLD -60 #define SP_PHY_NONE LL_PHY_NONE // No PHY set #define AUTO_PHY_UPDATE 0xFF // Spin if the expression is not true #define SIMPLEPERIPHERAL_ASSERT(expr) if (!(expr)) simple_peripheral_spin(); /********************************************************************* * TYPEDEFS */ // App event passed from stack modules. This type is defined by the application // since it can queue events to itself however it wants. typedef struct { uint8_t event; // event type void *pData; // pointer to message } spEvt_t; // 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; } spPairStateData_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; } spPasscodeData_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; } spGapAdvEventData_t; // Container to store information from clock expiration using a flexible array // since data is not always needed typedef struct { uint8_t event; // uint8_t data[]; } spClockEventData_t; // List element for parameter update and PHY command status lists typedef struct { List_Elem elem; uint16_t connHandle; } spConnHandleEntry_t; // Connected device information typedef struct { uint16_t connHandle; // Connection Handle spClockEventData_t* pParamUpdateEventData; Clock_Struct* pUpdateClock; // pointer to clock struct int8_t rssiArr[SP_MAX_RSSI_STORE_DEPTH]; uint8_t rssiCntr; int8_t rssiAvg; bool phyCngRq; // Set to true if PHY change request is in progress uint8_t currPhy; uint8_t rqPhy; uint8_t phyRqFailCnt; // PHY change request count bool isAutoPHYEnable; // Flag to indicate auto phy change } spConnRec_t; /********************************************************************* * GLOBAL VARIABLES */ // Display Interface Display_Handle dispHandle = NULL; // Task configuration Task_Struct spTask; #if defined __TI_COMPILER_VERSION__ #pragma DATA_ALIGN(spTaskStack, 8) #else #pragma data_alignment=8 #endif uint8_t spTaskStack[SP_TASK_STACK_SIZE]; /********************************************************************* * 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; // Clock instance for internal periodic events. Only one is needed since // GattServApp will handle notifying all connected GATT clients static Clock_Struct clkPeriodic; // Clock instance for RPA read events. static Clock_Struct clkRpaRead; // Memory to pass periodic event ID to clock handler spClockEventData_t argPeriodic = { .event = SP_PERIODIC_EVT }; // Memory to pass RPA read event ID to clock handler spClockEventData_t argRpaRead = { .event = SP_READ_RPA_EVT }; // Per-handle connection info static spConnRec_t connList[MAX_NUM_BLE_CONNS]; // Current connection handle as chosen by menu static uint16_t menuConnHandle = CONNHANDLE_INVALID; // 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; // GAP GATT Attributes static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple Peripheral"; static uint16_t adcData = 0; // added // 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 GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID) }; // Scan Response Data static uint8_t scanRspData[] = { // complete name 17, // length of this data GAP_ADTYPE_LOCAL_NAME_COMPLETE, 'S', 'i', 'm', 'p', 'l', 'e', 'P', 'e', 'r', 'i', 'p', 'h', 'e', 'r', 'a', 'l', // 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 advHandleLegacy; static uint8 advHandleLongRange; // 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 SimplePeripheral_init( void ); static void SimplePeripheral_taskFxn(UArg a0, UArg a1); static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg); static uint8_t SimplePeripheral_processGATTMsg(gattMsgEvent_t *pMsg); static void SimplePeripheral_processGapMessage(gapEventHdr_t *pMsg); static void SimplePeripheral_advCallback(uint32_t event, void *pBuf, uintptr_t arg); static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData); static void SimplePeripheral_processAppMsg(spEvt_t *pMsg); static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId); static void SimplePeripheral_performPeriodicTask(void); #if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG) static void SimplePeripheral_updateRPA(void); #endif // PRIVACY_1_2_CFG static void SimplePeripheral_clockHandler(UArg arg); #if defined(GAP_BOND_MGR) static void SimplePeripheral_passcodeCb(uint8_t *pDeviceAddr, uint16_t connHandle, uint8_t uiInputs, uint8_t uiOutputs, uint32_t numComparison); static void SimplePeripheral_pairStateCb(uint16_t connHandle, uint8_t state, uint8_t status); #endif static void SimplePeripheral_processPairState(spPairStateData_t *pPairState); static void SimplePeripheral_processPasscode(spPasscodeData_t *pPasscodeData); static void SimplePeripheral_charValueChangeCB(uint8_t paramId); static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData); static void SimplePeripheral_keyChangeHandler(uint8 keys); static void SimplePeripheral_handleKeys(uint8_t keys); static void SimplePeripheral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg); static void SimplePeripheral_initPHYRSSIArray(void); static void SimplePeripheral_updatePHYStat(uint16_t eventCode, uint8_t *pMsg); static uint8_t SimplePeripheral_addConn(uint16_t connHandle); static uint8_t SimplePeripheral_getConnIndex(uint16_t connHandle); static uint8_t SimplePeripheral_removeConn(uint16_t connHandle); static void SimplePeripheral_processParamUpdate(uint16_t connHandle); static status_t SimplePeripheral_startAutoPhyChange(uint16_t connHandle); static status_t SimplePeripheral_stopAutoPhyChange(uint16_t connHandle); static status_t SimplePeripheral_setPhy(uint16_t connHandle, uint8_t allPhys, uint8_t txPhy, uint8_t rxPhy, uint16_t phyOpts); static uint8_t SimplePeripheral_clearConnListEntry(uint16_t connHandle); static void SimplePeripheral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr, tbmMenuObj_t* pMenuObjNext); static void SimplePeripheral_connEvtCB(Gap_ConnEventRpt_t *pReport); static void SimplePeripheral_processConnEvt(Gap_ConnEventRpt_t *pReport); #ifdef PTM_MODE void simple_peripheral_handleNPIRxInterceptEvent(uint8_t *pMsg); // Declaration static void simple_peripheral_sendToNPI(uint8_t *buf, uint16_t len); // Declaration #endif // PTM_MODE /********************************************************************* * EXTERN FUNCTIONS */ extern void AssertHandler(uint8 assertCause, uint8 assertSubcause); /********************************************************************* * PROFILE CALLBACKS */ #if defined(GAP_BOND_MGR) // GAP Bond Manager Callbacks static gapBondCBs_t SimplePeripheral_BondMgrCBs = { SimplePeripheral_passcodeCb, // Passcode callback SimplePeripheral_pairStateCb // Pairing/Bonding state Callback }; #endif // Simple GATT Profile Callbacks static simpleProfileCBs_t SimplePeripheral_simpleProfileCBs = { SimplePeripheral_charValueChangeCB // Simple GATT Characteristic value change callback }; /********************************************************************* * PUBLIC FUNCTIONS */ /********************************************************************* * @fn SensorController_readyCallback * * @brief */ void SensorController_readyCallback(void){ } /********************************************************************* * @fn SensorController_taskalertCallback * * @brief */ void SensorController_taskalertCallback(void){ uint32_t bvAlertEvents = 0; // Clear the ALERT interrupt source scifClearAlertIntSource(); bvAlertEvents = scifGetAlertEvents(); if(bvAlertEvents & BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID)) { // ... Access Sensor Controller task data structures here ... // adcData = scifTaskData.adcLevelTrigger.output; adcData = scifTaskData.adcLevelTrigger.output.adcValue; } // Acknowledge the ALERT event scifAckAlertEvents(); Event_post(syncEvent,SP_SCTASK_EVT); } /********************************************************************* * @fn SensorController_adcSample * * @brief */ static void SensorController_adcSample(void){ while (scifWaitOnNbl(0) != SCIF_SUCCESS); scifResetTaskStructs(BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID),BV(SCIF_STRUCT_OUTPUT)); scifExecuteTasksOnceNbl(BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID)); } /********************************************************************* * @fn SensorController_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 SensorController_init(void){ // Initialize the SCIF operating system abstraction layer scifOsalInit(); scifOsalRegisterCtrlReadyCallback(SensorController_readyCallback); scifOsalRegisterTaskAlertCallback(SensorController_taskalertCallback); // Initialize the SCIF driver scifInit(&scifDriverSetup); // Enable RTC ticks, with N Hz tick interval scifStartRtcTicksNow(0x00010000 / 1); } /********************************************************************* * @fn simple_peripheral_spin * * @brief Spin forever * * @param none */ static void simple_peripheral_spin(void) { volatile uint8_t x = 0; while(1) { x++; } } #ifdef PTM_MODE /********************************************************************* * @fn simple_peripheral_handleNPIRxInterceptEvent * * @brief Intercept an NPI RX serial message and queue for this application. * * @param pMsg - a NPIMSG_msg_t containing the intercepted message. * * @return none. */ void simple_peripheral_handleNPIRxInterceptEvent(uint8_t *pMsg) { // Send Command via HCI TL HCI_TL_SendToStack(((NPIMSG_msg_t *)pMsg)->pBuf); // The data is stored as a message, free this first. ICall_freeMsg(((NPIMSG_msg_t *)pMsg)->pBuf); // Free container. ICall_free(pMsg); } /********************************************************************* * @fn simple_peripheral_sendToNPI * * @brief Create an NPI packet and send to NPI to transmit. * * @param buf - pointer HCI event or data. * * @param len - length of buf in bytes. * * @return none */ static void simple_peripheral_sendToNPI(uint8_t *buf, uint16_t len) { npiPkt_t *pNpiPkt = (npiPkt_t *)ICall_allocMsg(sizeof(npiPkt_t) + len); if (pNpiPkt) { pNpiPkt->hdr.event = buf[0]; //Has the event status code in first byte of payload pNpiPkt->hdr.status = 0xFF; pNpiPkt->pktLen = len; pNpiPkt->pData = (uint8 *)(pNpiPkt + 1); memcpy(pNpiPkt->pData, buf, len); // Send to NPI // Note: there is no need to free this packet. NPI will do that itself. NPITask_sendToHost((uint8_t *)pNpiPkt); } } #endif // PTM_MODE /********************************************************************* * @fn SimplePeripheral_createTask * * @brief Task creation function for the Simple Peripheral. */ void SimplePeripheral_createTask(void) { Task_Params taskParams; // Configure task Task_Params_init(&taskParams); taskParams.stack = spTaskStack; taskParams.stackSize = SP_TASK_STACK_SIZE; taskParams.priority = SP_TASK_PRIORITY; Task_construct(&spTask, SimplePeripheral_taskFxn, &taskParams, NULL); } /********************************************************************* * @fn SimplePeripheral_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 SimplePeripheral_init(void) { // ****************************************************************** // 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); #ifdef USE_RCOSC RCOSC_enableCalibration(); #endif // USE_RCOSC // Create an RTOS queue for message from profile to be sent to app. appMsgQueueHandle = Util_constructQueue(&appMsgQueue); // Create one-shot clock for internal periodic events. Util_constructClock(&clkPeriodic, SimplePeripheral_clockHandler, SP_PERIODIC_EVT_PERIOD, 0, false, (UArg)&argPeriodic); // Set the Device Name characteristic in the GAP GATT Service // For more information, see the section in the User's Guide: // software-dl.ti.com/.../ GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName); // Configure GAP { 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); } #if defined(GAP_BOND_MGR) // Setup the GAP Bond Manager. For more information see the GAP Bond Manager // section in the User's Guide: // software-dl.ti.com/.../ { // 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; 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); } SensorController_init(); #endif // Initialize GATT attributes GGS_AddService(GATT_ALL_SERVICES); // GAP GATT Service GATTServApp_AddService(GATT_ALL_SERVICES); // GATT Service DevInfo_AddService(); // Device Information Service SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile MyData_AddService( selfEntity ); MyData1_AddService( selfEntity ); // Setup the SimpleProfile Characteristic Values // For more information, see the GATT and GATTServApp sections in the User's Guide: // software-dl.ti.com/.../ { uint8_t charValue1 = 1; uint8_t charValue2 = 2; uint8_t charValue3 = 3; uint8_t charValue4 = 4; uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 }; SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, sizeof(uint8_t), &charValue1); SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, sizeof(uint8_t), &charValue2); SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, sizeof(uint8_t), &charValue3); SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t), &charValue4); SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5); uint8_t myData_data_initVal[MYDATA_DATA_LEN] = {0}; MyData_SetParameter(MYDATA_DATA_ID, MYDATA_DATA_LEN, myData_data_initVal); uint8_t myData1_data1_initVal[MYDATA1_DATA1_LEN] = {0}; MyData1_SetParameter(MYDATA1_DATA1_ID, MYDATA1_DATA1_LEN, myData1_data1_initVal); } // Register callback with SimpleGATTprofile SimpleProfile_RegisterAppCBs(&SimplePeripheral_simpleProfileCBs); #if defined(GAP_BOND_MGR) // Start Bond Manager and register callback VOID GAPBondMgr_Register(&SimplePeripheral_BondMgrCBs); #endif // 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: // software-dl.ti.com/.../ 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: // software-dl.ti.com/.../ HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE, APP_SUGGESTED_TX_TIME); } // Initialize GATT Client GATT_InitClient(); // Init key debouncer Board_initKeys(SimplePeripheral_keyChangeHandler); // Initialize Connection List SimplePeripheral_clearConnListEntry(CONNHANDLE_ALL); //Initialize GAP layer for Peripheral role and register to receive GAP events GAP_DeviceInit(GAP_PROFILE_PERIPHERAL, selfEntity, addrMode, NULL); // Initialize array to store connection handle and RSSI values SimplePeripheral_initPHYRSSIArray(); // The type of display is configured based on the BOARD_DISPLAY_USE... // preprocessor definitions dispHandle = Display_open(Display_Type_ANY, NULL); // Initialize Two-Button Menu module TBM_SET_TITLE(&spMenuMain, "Simple Peripheral"); tbm_setItemStatus(&spMenuMain, TBM_ITEM_NONE, TBM_ITEM_ALL); tbm_initTwoBtnMenu(dispHandle, &spMenuMain, 2, SimplePeripheral_menuSwitchCb); Display_printf(dispHandle, SP_ROW_SEPARATOR_1, 0, "===================="); } /********************************************************************* * @fn SimplePeripheral_taskFxn * * @brief Application task entry point for the Simple Peripheral. * * @param a0, a1 - not used. */ static void SimplePeripheral_taskFxn(UArg a0, UArg a1) { // Initialize application SimplePeripheral_init(); // 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, SP_ALL_EVENTS, ICALL_TIMEOUT_FOREVER); if (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 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 = SimplePeripheral_processStackMsg((ICall_Hdr *)pMsg); } } if (pMsg && safeToDealloc) { ICall_freeMsg(pMsg); } } // If RTOS queue is not empty, process app message. if (events & SP_QUEUE_EVT) { while (!Queue_empty(appMsgQueueHandle)) { spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle); if (pMsg) { // Process message. SimplePeripheral_processAppMsg(pMsg); // Free the space from the message. ICall_free(pMsg); } } } } } } /********************************************************************* * @fn SimplePeripheral_processStackMsg * * @brief Process an incoming stack message. * * @param pMsg - message to process * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ static uint8_t SimplePeripheral_processStackMsg(ICall_Hdr *pMsg) { // Always dealloc pMsg unless set otherwise uint8_t safeToDealloc = TRUE; switch (pMsg->event) { case GAP_MSG_EVENT: SimplePeripheral_processGapMessage((gapEventHdr_t*) pMsg); break; case GATT_MSG_EVENT: // Process GATT message safeToDealloc = SimplePeripheral_processGATTMsg((gattMsgEvent_t *)pMsg); break; case HCI_GAP_EVENT_EVENT: { // Process HCI message switch(pMsg->status) { case HCI_COMMAND_COMPLETE_EVENT_CODE: // Process HCI Command Complete Events here { SimplePeripheral_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, SP_ROW_STATUS_1, 0, "PHY Change failure, peer does not support this"); } else { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "PHY Update Status Event: 0x%x", pMyMsg->cmdStatus); } SimplePeripheral_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) { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "PHY Change failure"); } else { // Only symmetrical PHY is supported. // rxPhy should be equal to txPhy. Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "PHY Updated to %s", (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"); } SimplePeripheral_updatePHYStat(HCI_BLE_PHY_UPDATE_COMPLETE_EVENT, (uint8_t *)pMsg); } break; } default: break; } break; } default: // do nothing break; } #ifdef PTM_MODE // Check for NPI Messages hciPacket_t *pBuf = (hciPacket_t *)pMsg; // Serialized HCI Event if (pBuf->hdr.event == HCI_CTRL_TO_HOST_EVENT) { uint16_t len = 0; // Determine the packet length switch(pBuf->pData[0]) { case HCI_EVENT_PACKET: len = HCI_EVENT_MIN_LENGTH + pBuf->pData[2]; break; case HCI_ACL_DATA_PACKET: len = HCI_DATA_MIN_LENGTH + BUILD_UINT16(pBuf->pData[3], pBuf->pData[4]); break; default: break; } // Send to Remote Host. simple_peripheral_sendToNPI(pBuf->pData, len); // Free buffers if needed. switch (pBuf->pData[0]) { case HCI_ACL_DATA_PACKET: case HCI_SCO_DATA_PACKET: BM_free(pBuf->pData); default: break; } } #endif // PTM_MODE return (safeToDealloc); } /********************************************************************* * @fn SimplePeripheral_processGATTMsg * * @brief Process GATT messages and events. * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ static uint8_t SimplePeripheral_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. Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode); } else if (pMsg->method == ATT_MTU_UPDATED_EVENT) { // MTU size updated Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "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 SimplePeripheral_processAppMsg * * @brief Process an incoming callback from a profile. * * @param pMsg - message to process * * @return None. */ static void SimplePeripheral_processAppMsg(spEvt_t *pMsg) { bool dealloc = TRUE; switch (pMsg->event) { case SP_CHAR_CHANGE_EVT: SimplePeripheral_processCharValueChangeEvt(*(uint8_t*)(pMsg->pData)); break; case SP_KEY_CHANGE_EVT: SimplePeripheral_handleKeys(*(uint8_t*)(pMsg->pData)); break; case SP_ADV_EVT: SimplePeripheral_processAdvEvent((spGapAdvEventData_t*)(pMsg->pData)); break; case SP_PAIR_STATE_EVT: SimplePeripheral_processPairState((spPairStateData_t*)(pMsg->pData)); break; case SP_PASSCODE_EVT: SimplePeripheral_processPasscode((spPasscodeData_t*)(pMsg->pData)); break; case SP_PERIODIC_EVT: SimplePeripheral_performPeriodicTask(); break; case SP_SCTASK_EVT: MyData_SetParameter(MYDATA_DATA_ID, MYDATA_DATA_LEN, &adcData); break; #if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG) case SP_READ_RPA_EVT: SimplePeripheral_updateRPA(); break; #endif // PRIVACY_1_2_CFG case SP_SEND_PARAM_UPDATE_EVT: { // Extract connection handle from data uint16_t connHandle = *(uint16_t *)(((spClockEventData_t *)pMsg->pData)->data); SimplePeripheral_processParamUpdate(connHandle); // This data is not dynamically allocated dealloc = FALSE; break; } case SP_CONN_EVT: SimplePeripheral_processConnEvt((Gap_ConnEventRpt_t *)(pMsg->pData)); break; default: // Do nothing. break; } // Free message data if it exists and we are to dealloc if ((dealloc == TRUE) && (pMsg->pData != NULL)) { ICall_free(pMsg->pData); } } /********************************************************************* * @fn SimplePeripheral_processGapMessage * * @brief Process an incoming GAP event. * * @param pMsg - message to process */ static void SimplePeripheral_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_printf(dispHandle, SP_ROW_STATUS_1, 0, "Initialized"); // Setup and start Advertising // For more information, see the GAP section in the User's Guide: // software-dl.ti.com/.../ // 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(&SimplePeripheral_advCallback, &advParamLegacy, &advHandleLegacy); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); // 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); SIMPLEPERIPHERAL_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); SIMPLEPERIPHERAL_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); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); // Use long range params to create long range set #2 GapAdv_params_t advParamLongRange = GAPADV_PARAMS_AE_LONG_RANGE_CONN; // Create Advertisement set #2 and assign handle status = GapAdv_create(&SimplePeripheral_advCallback, &advParamLongRange, &advHandleLongRange); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); // Load advertising data for set #2 that is statically allocated by the app status = GapAdv_loadByHandle(advHandleLongRange, GAP_ADV_DATA_TYPE_ADV, sizeof(advertData), advertData); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); // Set event mask for set #2 status = GapAdv_setEventMask(advHandleLongRange, GAP_ADV_EVT_MASK_START_AFTER_ENABLE | GAP_ADV_EVT_MASK_END_AFTER_DISABLE | GAP_ADV_EVT_MASK_SET_TERMINATED); // Enable long range advertising for set #2 status = GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); SIMPLEPERIPHERAL_ASSERT(status == SUCCESS); #ifdef PTM_MODE // Enable "Enable PTM Mode" option tbm_setItemStatus(&spMenuMain, SP_ITEM_PTM_ENBL, SP_ITEM_NONE); #endif // Display device address Display_printf(dispHandle, SP_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) { SimplePeripheral_updateRPA(); // Create one-shot clock for RPA check event. Util_constructClock(&clkRpaRead, SimplePeripheral_clockHandler, SP_READ_RPA_EVT_PERIOD, 0, true, (UArg) &argRpaRead); } #endif // PRIVACY_1_2_CFG } break; } case GAP_LINK_ESTABLISHED_EVENT: { gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg; // Display the amount of current connections uint8_t numActive = linkDB_NumActive(); Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Num Conns: %d", (uint16_t)numActive); if (pPkt->hdr.status == SUCCESS) { // Add connection to list and start RSSI SimplePeripheral_addConn(pPkt->connectionHandle); // Display the address of this connection Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connected to %s", Util_convertBdAddr2Str(pPkt->devAddr)); // Enable connection selection option tbm_setItemStatus(&spMenuMain, SP_ITEM_SELECT_CONN, TBM_ITEM_NONE); // Start Periodic Clock. Util_startClock(&clkPeriodic); } if (numActive < MAX_NUM_BLE_CONNS) { // Start advertising since there is room for more connections GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); } else { // Stop advertising since there is no room for more connections GapAdv_disable(advHandleLongRange); GapAdv_disable(advHandleLegacy); } break; } case GAP_LINK_TERMINATED_EVENT: { gapTerminateLinkEvent_t *pPkt = (gapTerminateLinkEvent_t *)pMsg; // Display the amount of current connections uint8_t numActive = linkDB_NumActive(); Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Device Disconnected!"); Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Num Conns: %d", (uint16_t)numActive); // Remove the connection from the list and disable RSSI if needed SimplePeripheral_removeConn(pPkt->connectionHandle); // If no active connections if (numActive == 0) { // Stop periodic clock Util_stopClock(&clkPeriodic); // Disable Connection Selection option tbm_setItemStatus(&spMenuMain, TBM_ITEM_NONE, SP_ITEM_SELECT_CONN); } // Start advertising since there is room for more connections GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0); // Clear remaining lines Display_clearLine(dispHandle, SP_ROW_CONNECTION); break; } case GAP_UPDATE_LINK_PARAM_REQ_EVENT: { gapUpdateLinkParamReqReply_t rsp; gapUpdateLinkParamReqEvent_t *pReq = (gapUpdateLinkParamReqEvent_t *)pMsg; rsp.connectionHandle = pReq->req.connectionHandle; // 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 { 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; linkDB_GetInfo(pPkt->connectionHandle, &linkInfo); if(pPkt->status == SUCCESS) { // Display the address of the connection update Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Link Param Updated: %s", Util_convertBdAddr2Str(linkInfo.addr)); } else { // Display the address of the connection update failure Display_printf(dispHandle, SP_ROW_STATUS_2, 0, "Link Param Update Failed 0x%x: %s", pPkt->opcode, Util_convertBdAddr2Str(linkInfo.addr)); } // Check if there are any queued parameter updates spConnHandleEntry_t *connHandleEntry = (spConnHandleEntry_t *)List_get(¶mUpdateList); if (connHandleEntry != NULL) { // Attempt to send queued update now SimplePeripheral_processParamUpdate(connHandleEntry->connHandle); // Free list element ICall_free(connHandleEntry); } break; } default: Display_clearLines(dispHandle, SP_ROW_STATUS_1, SP_ROW_STATUS_2); break; } } /********************************************************************* * @fn SimplePeripheral_charValueChangeCB * * @brief Callback from Simple Profile indicating a characteristic * value change. * * @param paramId - parameter Id of the value that was changed. * * @return None. */ static void SimplePeripheral_charValueChangeCB(uint8_t paramId) { uint8_t *pValue = ICall_malloc(sizeof(uint8_t)); if (pValue) { *pValue = paramId; if (SimplePeripheral_enqueueMsg(SP_CHAR_CHANGE_EVT, pValue) != SUCCESS) { ICall_free(pValue); } } } /********************************************************************* * @fn SimplePeripheral_processCharValueChangeEvt * * @brief Process a pending Simple Profile characteristic value change * event. * * @param paramID - parameter ID of the value that was changed. */ static void SimplePeripheral_processCharValueChangeEvt(uint8_t paramId) { uint8_t newValue; switch(paramId) { case SIMPLEPROFILE_CHAR1: SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue); Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 1: %d", (uint16_t)newValue); break; case SIMPLEPROFILE_CHAR3: SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue); Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Char 3: %d", (uint16_t)newValue); break; default: // should not reach here! break; } } /********************************************************************* * @fn SimplePeripheral_performPeriodicTask * * @brief Perform a periodic application task. This function gets called * every five seconds (SP_PERIODIC_EVT_PERIOD). In this example, * the value of the third characteristic in the SimpleGATTProfile * service is retrieved from the profile, and then copied into the * value of the the fourth characteristic. * * @param None. * * @return None. */ static void SimplePeripheral_performPeriodicTask(void) { SensorController_adcSample(); /* uint8_t valueToCopy; // Call to retrieve the value of the third characteristic in the profile if (SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &valueToCopy) == SUCCESS) { // Call to set that value of the fourth characteristic in the profile. // Note that if notifications of the fourth characteristic have been // enabled by a GATT client device, then a notification will be sent // every time this function is called. SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t), &valueToCopy); }*/ } #if defined(BLE_V42_FEATURES) && (BLE_V42_FEATURES & PRIVACY_1_2_CFG) /********************************************************************* * @fn SimplePeripheral_updateRPA * * @brief Read the current RPA from the stack and update display * if the RPA has changed. * * @param None. * * @return None. */ static void SimplePeripheral_updateRPA(void) { uint8_t* pRpaNew; // 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, SP_ROW_RPA, 0, "RP Addr: %s", Util_convertBdAddr2Str(pRpaNew)); memcpy(rpa, pRpaNew, B_ADDR_LEN); } } #endif // PRIVACY_1_2_CFG /********************************************************************* * @fn SimplePeripheral_clockHandler * * @brief Handler function for clock timeouts. * * @param arg - event type * * @return None. */ static void SimplePeripheral_clockHandler(UArg arg) { spClockEventData_t *pData = (spClockEventData_t *)arg; if (pData->event == SP_PERIODIC_EVT) { // Start the next period Util_startClock(&clkPeriodic); // Post event to wake up the application SimplePeripheral_enqueueMsg(SP_PERIODIC_EVT, NULL); } else if (pData->event == SP_READ_RPA_EVT) { // Start the next period Util_startClock(&clkRpaRead); // Post event to read the current RPA SimplePeripheral_enqueueMsg(SP_READ_RPA_EVT, NULL); } else if (pData->event == SP_SEND_PARAM_UPDATE_EVT) { // Send message to app SimplePeripheral_enqueueMsg(SP_SEND_PARAM_UPDATE_EVT, pData); } } /********************************************************************* * @fn SimplePeripheral_keyChangeHandler * * @brief Key event handler function * * @param keys - bitmap of pressed keys * * @return none */ static void SimplePeripheral_keyChangeHandler(uint8_t keys) { uint8_t *pValue = ICall_malloc(sizeof(uint8_t)); if (pValue) { *pValue = keys; if(SimplePeripheral_enqueueMsg(SP_KEY_CHANGE_EVT, pValue) != SUCCESS) { ICall_free(pValue); } } } /********************************************************************* * @fn SimplePeripheral_handleKeys * * @brief Handles all key events for this device. * * @param keys - bit field for key events. Valid entries: * KEY_LEFT * KEY_RIGHT */ static void SimplePeripheral_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 SimplePeripheral_doSetConnPhy * * @brief Set 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 SimplePeripheral_doSetConnPhy(uint8 index) { bool status = TRUE; 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, AUTO_PHY_UPDATE }; uint8_t connIndex = SimplePeripheral_getConnIndex(menuConnHandle); if (connIndex >= MAX_NUM_BLE_CONNS) { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!"); return FALSE; } // Set Phy Preference on the current connection. Apply the same value // for RX and TX. // If auto PHY update is not selected and if auto PHY update is enabled, then // stop auto PHY update // Note PHYs are already enabled by default in build_config.opt in stack project. if(phy[index] != AUTO_PHY_UPDATE) { // Cancel RSSI reading and auto phy changing SimplePeripheral_stopAutoPhyChange(connList[connIndex].connHandle); SimplePeripheral_setPhy(menuConnHandle, 0, phy[index], phy[index], 0); Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "PHY preference: %s", TBM_GET_ACTION_DESC(&spMenuConnPhy, index)); } else { // Start RSSI read for auto PHY update (if it is disabled) SimplePeripheral_startAutoPhyChange(menuConnHandle); } return status; } /********************************************************************* * @fn SimplePeripheral_advCallback * * @brief GapAdv module callback * * @param pMsg - message to process */ static void SimplePeripheral_advCallback(uint32_t event, void *pBuf, uintptr_t arg) { spGapAdvEventData_t *pData = ICall_malloc(sizeof(spGapAdvEventData_t)); if (pData) { pData->event = event; pData->pBuf = pBuf; if(SimplePeripheral_enqueueMsg(SP_ADV_EVT, pData) != SUCCESS) { ICall_free(pData); } } } /********************************************************************* * @fn SimplePeripheral_processAdvEvent * * @brief Process advertising event in app context * * @param pEventData */ static void SimplePeripheral_processAdvEvent(spGapAdvEventData_t *pEventData) { switch (pEventData->event) { case GAP_EVT_ADV_START_AFTER_ENABLE: Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Enabled", *(uint8_t *)(pEventData->pBuf)); break; case GAP_EVT_ADV_END_AFTER_DISABLE: Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d Disabled", *(uint8_t *)(pEventData->pBuf)); break; case GAP_EVT_ADV_START: break; case GAP_EVT_ADV_END: break; case GAP_EVT_ADV_SET_TERMINATED: { #ifndef Display_DISABLE_ALL GapAdv_setTerm_t *advSetTerm = (GapAdv_setTerm_t *)(pEventData->pBuf); #endif Display_printf(dispHandle, SP_ROW_ADVSTATE, 0, "Adv Set %d disabled after conn %d", advSetTerm->handle, advSetTerm->connHandle ); } break; case GAP_EVT_SCAN_REQ_RECEIVED: break; 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); } } #if defined(GAP_BOND_MGR) /********************************************************************* * @fn SimplePeripheral_pairStateCb * * @brief Pairing state callback. * * @return none */ static void SimplePeripheral_pairStateCb(uint16_t connHandle, uint8_t state, uint8_t status) { spPairStateData_t *pData = ICall_malloc(sizeof(spPairStateData_t)); // Allocate space for the event data. if (pData) { pData->state = state; pData->connHandle = connHandle; pData->status = status; // Queue the event. if(SimplePeripheral_enqueueMsg(SP_PAIR_STATE_EVT, pData) != SUCCESS) { ICall_free(pData); } } } /********************************************************************* * @fn SimplePeripheral_passcodeCb * * @brief Passcode callback. * * @return none */ static void SimplePeripheral_passcodeCb(uint8_t *pDeviceAddr, uint16_t connHandle, uint8_t uiInputs, uint8_t uiOutputs, uint32_t numComparison) { spPasscodeData_t *pData = ICall_malloc(sizeof(spPasscodeData_t)); // Allocate space for the passcode event. if (pData ) { pData->connHandle = connHandle; memcpy(pData->deviceAddr, pDeviceAddr, B_ADDR_LEN); pData->uiInputs = uiInputs; pData->uiOutputs = uiOutputs; pData->numComparison = numComparison; // Enqueue the event. if(SimplePeripheral_enqueueMsg(SP_PASSCODE_EVT, pData) != SUCCESS) { ICall_free(pData); } } } #endif /********************************************************************* * @fn SimplePeripheral_processPairState * * @brief Process the new paring state. * * @return none */ static void SimplePeripheral_processPairState(spPairStateData_t *pPairData) { uint8_t state = pPairData->state; uint8_t status = pPairData->status; switch (state) { case GAPBOND_PAIRING_STATE_STARTED: Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing started"); break; case GAPBOND_PAIRING_STATE_COMPLETE: if (status == SUCCESS) { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing success"); } else { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Pairing fail: %d", status); } break; case GAPBOND_PAIRING_STATE_ENCRYPTED: if (status == SUCCESS) { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Encryption success"); } else { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Encryption failed: %d", status); } break; case GAPBOND_PAIRING_STATE_BOND_SAVED: if (status == SUCCESS) { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Bond save success"); } else { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Bond save failed: %d", status); } break; default: break; } } /********************************************************************* * @fn SimplePeripheral_processPasscode * * @brief Process the Passcode request. * * @return none */ static void SimplePeripheral_processPasscode(spPasscodeData_t *pPasscodeData) { // Display passcode to user if (pPasscodeData->uiOutputs != 0) { Display_printf(dispHandle, SP_ROW_CONNECTION, 0, "Passcode: %d", B_APP_DEFAULT_PASSCODE); } #if defined(GAP_BOND_MGR) // Send passcode response GAPBondMgr_PasscodeRsp(pPasscodeData->connHandle , SUCCESS, B_APP_DEFAULT_PASSCODE); #endif } /********************************************************************* * @fn SimplePeripheral_connEvtCB * * @brief Connection event callback. * * @param pReport pointer to connection event report */ static void SimplePeripheral_connEvtCB(Gap_ConnEventRpt_t *pReport) { // Enqueue the event for processing in the app context. if(SimplePeripheral_enqueueMsg(SP_CONN_EVT, pReport) != SUCCESS) { ICall_free(pReport); } } /********************************************************************* * @fn SimplePeripheral_processConnEvt * * @brief Process connection event. * * @param pReport pointer to connection event report */ static void SimplePeripheral_processConnEvt(Gap_ConnEventRpt_t *pReport) { // Get index from handle uint8_t connIndex = SimplePeripheral_getConnIndex(pReport->handle); if (connIndex >= MAX_NUM_BLE_CONNS) { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!"); return; } // If auto phy change is enabled if (connList[connIndex].isAutoPHYEnable == TRUE) { // Read the RSSI HCI_ReadRssiCmd(pReport->handle); } } /********************************************************************* * @fn SimplePeripheral_enqueueMsg * * @brief Creates a message and puts the message in RTOS queue. * * @param event - message event. * @param state - message state. */ static status_t SimplePeripheral_enqueueMsg(uint8_t event, void *pData) { uint8_t success; spEvt_t *pMsg = ICall_malloc(sizeof(spEvt_t)); // Create dynamic pointer to message. if(pMsg) { pMsg->event = event; pMsg->pData = pData; // Enqueue the message. success = Util_enqueueMsg(appMsgQueueHandle, syncEvent, (uint8_t *)pMsg); return (success) ? SUCCESS : FAILURE; } return(bleMemAllocError); } /********************************************************************* * @fn SimplePeripheral_doSelectConn * * @brief Select a connection to communicate with * * @param index - item index from the menu * * @return always true */ bool SimplePeripheral_doSelectConn(uint8_t index) { menuConnHandle = connList[index].connHandle; // Set the menu title and go to this connection's context TBM_SET_TITLE(&spMenuPerConn, TBM_GET_ACTION_DESC(&spMenuSelectConn, index)); // Clear non-connection-related message Display_clearLine(dispHandle, SP_ROW_CONNECTION); tbm_goTo(&spMenuPerConn); return (true); } /********************************************************************* * @fn SimplePeripheral_addConn * * @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 SimplePeripheral_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; // Allocate data to send through clock handler connList[i].pParamUpdateEventData = ICall_malloc(sizeof(spClockEventData_t) + sizeof (uint16_t)); if(connList[i].pParamUpdateEventData) { connList[i].pParamUpdateEventData->event = SP_SEND_PARAM_UPDATE_EVT; *((uint16_t *)connList[i].pParamUpdateEventData->data) = 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, SimplePeripheral_clockHandler, SP_SEND_PARAM_UPDATE_DELAY, 0, true, (UArg) (connList[i].pParamUpdateEventData)); } else { ICall_free(connList[i].pParamUpdateEventData); } } else { status = bleMemAllocError; } // Set default PHY to 1M connList[i].currPhy = HCI_PHY_1_MBPS; break; } } return status; } /********************************************************************* * @fn SimplePeripheral_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 SimplePeripheral_getConnIndex(uint16_t connHandle) { uint8_t i; for (i = 0; i < MAX_NUM_BLE_CONNS; i++) { if (connList[i].connHandle == connHandle) { return i; } } return(MAX_NUM_BLE_CONNS); } /********************************************************************* * @fn SimplePeripheral_getConnIndex * * @brief Find index in the connected device list by connHandle * * @return SUCCESS if connHandle found valid index or bleInvalidRange * if index wasn't found. CONNHANDLE_ALL will always succeed. */ static uint8_t SimplePeripheral_clearConnListEntry(uint16_t connHandle) { 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 = SimplePeripheral_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; memset(connList[i].rssiArr, 0, SP_MAX_RSSI_STORE_DEPTH); connList[i].rssiAvg = 0; connList[i].rssiCntr = 0; connList[i].isAutoPHYEnable = FALSE; } } return(SUCCESS); } /********************************************************************* * @fn SimplePeripheral_clearPendingParamUpdate * * @brief clean pending param update request in the paramUpdateList list * * @param connHandle - connection handle to clean * * @return none */ void SimplePeripheral_clearPendingParamUpdate(uint16_t connHandle) { List_Elem *curr; for (curr = List_head(¶mUpdateList); curr != NULL; curr = List_next(curr)) { if (((spConnHandleEntry_t *)curr)->connHandle == connHandle) { List_remove(¶mUpdateList, curr); } } } /********************************************************************* * @fn SimplePeripheral_removeConn * * @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 SimplePeripheral_removeConn(uint16_t connHandle) { uint8_t connIndex = SimplePeripheral_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); // Free ParamUpdateEventData ICall_free(connList[connIndex].pParamUpdateEventData); } // Clear pending update requests from paramUpdateList SimplePeripheral_clearPendingParamUpdate(connHandle); // Stop Auto PHY Change SimplePeripheral_stopAutoPhyChange(connHandle); // Clear Connection List Entry SimplePeripheral_clearConnListEntry(connHandle); } return connIndex; } /********************************************************************* * @fn SimplePeripheral_processParamUpdate * * @brief Process a parameters update request * * @return None */ static void SimplePeripheral_processParamUpdate(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 = SimplePeripheral_getConnIndex(connHandle); if (connIndex >= MAX_NUM_BLE_CONNS) { Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Connection handle is not in the connList !!!"); return; } // Deconstruct the clock object Clock_destruct(connList[connIndex].pUpdateClock); // Free clock struct, only in case it is not NULL if (connList[connIndex].pUpdateClock != NULL) { ICall_free(connList[connIndex].pUpdateClock); connList[connIndex].pUpdateClock = NULL; } // Free ParamUpdateEventData, only in case it is not NULL if (connList[connIndex].pParamUpdateEventData != NULL) ICall_free(connList[connIndex].pParamUpdateEventData); // Send parameter update bStatus_t status = GAP_UpdateLinkParamReq(&req); // If there is an ongoing update, queue this for when the udpate completes if (status == bleAlreadyInRequestedMode) { spConnHandleEntry_t *connHandleEntry = ICall_malloc(sizeof(spConnHandleEntry_t)); if (connHandleEntry) { connHandleEntry->connHandle = connHandle; List_put(¶mUpdateList, (List_Elem *)connHandleEntry); } } } /********************************************************************* * @fn SimpleCentral_processCmdCompleteEvt * * @brief Process an incoming OSAL HCI Command Complete Event. * * @param pMsg - message to process * * @return none */ static void SimplePeripheral_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: { 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]); uint8_t index = SimplePeripheral_getConnIndex(handle); SIMPLEPERIPHERAL_ASSERT(index < MAX_NUM_BLE_CONNS); if (rssi != LL_RSSI_NOT_AVAILABLE) { connList[index].rssiArr[connList[index].rssiCntr++] = rssi; connList[index].rssiCntr %= SP_MAX_RSSI_STORE_DEPTH; int16_t sum_rssi = 0; for(uint8_t cnt=0; cnt<SP_MAX_RSSI_STORE_DEPTH; cnt++) { sum_rssi += connList[index].rssiArr[cnt]; } connList[index].rssiAvg = (uint32_t)(sum_rssi/SP_MAX_RSSI_STORE_DEPTH); uint8_t phyRq = SP_PHY_NONE; uint8_t phyRqS = SP_PHY_NONE; uint8_t phyOpt = LL_PHY_OPT_NONE; if(connList[index].phyCngRq == FALSE) { if((connList[index].rssiAvg >= RSSI_2M_THRSHLD) && (connList[index].currPhy != HCI_PHY_2_MBPS) && (connList[index].currPhy != SP_PHY_NONE)) { // try to go to higher data rate phyRqS = phyRq = HCI_PHY_2_MBPS; } else if((connList[index].rssiAvg < RSSI_2M_THRSHLD) && (connList[index].rssiAvg >= RSSI_1M_THRSHLD) && (connList[index].currPhy != HCI_PHY_1_MBPS) && (connList[index].currPhy != SP_PHY_NONE)) { // try to go to legacy regular data rate phyRqS = phyRq = HCI_PHY_1_MBPS; } else if((connList[index].rssiAvg >= RSSI_S2_THRSHLD) && (connList[index].rssiAvg < RSSI_1M_THRSHLD) && (connList[index].currPhy != SP_PHY_NONE)) { // try to go to lower data rate S=2(500kb/s) phyRqS = HCI_PHY_CODED; phyOpt = LL_PHY_OPT_S2; phyRq = BLE5_CODED_S2_PHY; } else if(connList[index].rssiAvg < RSSI_S2_THRSHLD ) { // try to go to lowest data rate S=8(125kb/s) phyRqS = HCI_PHY_CODED; phyOpt = LL_PHY_OPT_S8; phyRq = BLE5_CODED_S8_PHY; } if((phyRq != SP_PHY_NONE) && // First check if the request for this phy change is already not honored then don't request for change (((connList[index].rqPhy == phyRq) && (connList[index].phyRqFailCnt < 2)) || (connList[index].rqPhy != phyRq))) { //Initiate PHY change based on RSSI SimplePeripheral_setPhy(connList[index].connHandle, 0, phyRqS, phyRqS, phyOpt); connList[index].phyCngRq = TRUE; // If it a request for different phy than failed request, reset the count if(connList[index].rqPhy != phyRq) { // then reset the request phy counter and requested phy connList[index].phyRqFailCnt = 0; } if(phyOpt == LL_PHY_OPT_NONE) { connList[index].rqPhy = phyRq; } else if(phyOpt == LL_PHY_OPT_S2) { connList[index].rqPhy = BLE5_CODED_S2_PHY; } else { connList[index].rqPhy = BLE5_CODED_S8_PHY; } } // end of if ((phyRq != SP_PHY_NONE) && ... } // end of if (connList[index].phyCngRq == FALSE) } // end of if (rssi != LL_RSSI_NOT_AVAILABLE) Display_printf(dispHandle, SP_ROW_RSSI, 0, "RSSI:%d dBm, AVG RSSI:%d dBm", (uint32_t)(rssi), connList[index].rssiAvg); } // end of if (status == SUCCESS) break; } case HCI_LE_READ_PHY: { if (status == SUCCESS) { Display_printf(dispHandle, SP_ROW_RSSI + 2, 0, "RXPh: %d, TXPh: %d", pMsg->pReturnParam[3], pMsg->pReturnParam[4]); } break; } default: break; } // end of switch (pMsg->cmdOpcode) } /********************************************************************* * @fn SimplePeripheral_initPHYRSSIArray * * @brief Initializes the array of structure/s to store data related * RSSI based auto PHy change * * @param connHandle - the connection handle * * @param addr - pointer to device address * * @return index of connection handle */ static void SimplePeripheral_initPHYRSSIArray(void) { //Initialize array to store connection handle and RSSI values memset(connList, 0, sizeof(connList)); for (uint8_t index = 0; index < MAX_NUM_BLE_CONNS; index++) { connList[index].connHandle = SP_INVALID_HANDLE; } } /********************************************************************* // Set default PHY to 1M * @fn SimplePeripheral_startAutoPhyChange * * @brief Start periodic RSSI reads on a link. * * @param connHandle - connection handle of link * @param devAddr - device address * * @return SUCCESS: Terminate started * bleIncorrectMode: No link * bleNoResources: No resources */ static status_t SimplePeripheral_startAutoPhyChange(uint16_t connHandle) { status_t status = FAILURE; // Get connection index from handle uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle); SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS); // Start Connection Event notice for RSSI calculation status = Gap_RegisterConnEventCb(SimplePeripheral_connEvtCB, GAP_CB_REGISTER, connHandle); // Flag in connection info if successful if (status == SUCCESS) { connList[connIndex].isAutoPHYEnable = TRUE; } return status; } /********************************************************************* * @fn SimplePeripheral_stopAutoPhyChange * * @brief Cancel periodic RSSI reads on a link. * * @param connHandle - connection handle of link * * @return SUCCESS: Operation successful * bleIncorrectMode: No link */ static status_t SimplePeripheral_stopAutoPhyChange(uint16_t connHandle) { // Get connection index from handle uint8_t connIndex = SimplePeripheral_getConnIndex(connHandle); SIMPLEPERIPHERAL_ASSERT(connIndex < MAX_NUM_BLE_CONNS); // Stop connection event notice Gap_RegisterConnEventCb(NULL, GAP_CB_UNREGISTER, connHandle); // Also update the phychange request status for active RSSI tracking connection connList[connIndex].phyCngRq = FALSE; connList[connIndex].isAutoPHYEnable = FALSE; return SUCCESS; } /********************************************************************* * @fn SimplePeripheral_setPhy * * @brief Call the HCI set phy API and and add the handle to a * list to match it to an incoming command status event */ static status_t SimplePeripheral_setPhy(uint16_t connHandle, uint8_t allPhys, uint8_t txPhy, uint8_t rxPhy, uint16_t phyOpts) { // Allocate list entry to store handle for command status spConnHandleEntry_t *connHandleEntry = ICall_malloc(sizeof(spConnHandleEntry_t)); if (connHandleEntry) { connHandleEntry->connHandle = connHandle; // Add entry to the phy command status list List_put(&setPhyCommStatList, (List_Elem *)connHandleEntry); // Send PHY Update HCI_LE_SetPhyCmd(connHandle, allPhys, txPhy, rxPhy, phyOpts); } return SUCCESS; } /********************************************************************* * @fn SimplePeripheral_updatePHYStat * * @brief Update the auto phy update state machine * * @param connHandle - the connection handle * * @return None */ static void SimplePeripheral_updatePHYStat(uint16_t eventCode, uint8_t *pMsg) { uint8_t connIndex; switch (eventCode) { case HCI_LE_SET_PHY: { // Get connection handle from list spConnHandleEntry_t *connHandleEntry = (spConnHandleEntry_t *)List_get(&setPhyCommStatList); if (connHandleEntry) { // Get index from connection handle connIndex = SimplePeripheral_getConnIndex(connHandleEntry->connHandle); ICall_free(connHandleEntry); // Is this connection still valid? if (connIndex < MAX_NUM_BLE_CONNS) { hciEvt_CommandStatus_t *pMyMsg = (hciEvt_CommandStatus_t *)pMsg; if (pMyMsg->cmdStatus == HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE) { // Update the phychange 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 connIndex = SimplePeripheral_getConnIndex(pPUC->connHandle); // Is this connection still valid? if (connIndex < MAX_NUM_BLE_CONNS) { // Update the phychange request status for active RSSI tracking connection connList[connIndex].phyCngRq = FALSE; if (pPUC->status == SUCCESS) { connList[connIndex].currPhy = pPUC->rxPhy; } if(pPUC->rxPhy != connList[connIndex].rqPhy) { connList[connIndex].phyRqFailCnt++; } else { // Reset the request phy counter and requested phy connList[connIndex].phyRqFailCnt = 0; connList[connIndex].rqPhy = 0; } } } break; } default: break; } // end of switch (eventCode) } /********************************************************************* * @fn SimplePeripheral_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 SimplePeripheral_menuSwitchCb(tbmMenuObj_t* pMenuObjCurr, tbmMenuObj_t* pMenuObjNext) { uint8_t NUMB_ACTIVE_CONNS = linkDB_NumActive(); // interested in only the events of // entering scMenuConnect, spMenuSelectConn, and scMenuMain for now if (pMenuObjNext == &spMenuSelectConn) { static uint8_t* pAddrs; uint8_t* pAddrTemp; if (pAddrs != NULL) { ICall_free(pAddrs); } // Allocate buffer to display addresses pAddrs = ICall_malloc(NUMB_ACTIVE_CONNS * SP_ADDR_STR_SIZE); if (pAddrs == NULL) { TBM_SET_NUM_ITEM(&spMenuSelectConn, 0); } else { uint8_t i; TBM_SET_NUM_ITEM(&spMenuSelectConn, 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) { // Get the address from the connection handle linkDBInfo_t linkInfo; linkDB_GetInfo(connList[i].connHandle, &linkInfo); // This connection is active. Set the corresponding menu item with // the address of this connection and enable the item. memcpy(pAddrTemp, Util_convertBdAddr2Str(linkInfo.addr), SP_ADDR_STR_SIZE); TBM_SET_ACTION_DESC(&spMenuSelectConn, i, pAddrTemp); tbm_setItemStatus(&spMenuSelectConn, (1 << i), SP_ITEM_NONE); pAddrTemp += SP_ADDR_STR_SIZE; } else { // This connection is not active. Disable the corresponding menu item. tbm_setItemStatus(&spMenuSelectConn, SP_ITEM_NONE, (1 << i)); } } } } else if (pMenuObjNext == &spMenuMain) { // Now we are not in a specific connection's context // Clear connection-related message Display_clearLine(dispHandle, SP_ROW_CONNECTION); } } #ifdef PTM_MODE /********************************************************************* * @fn SimplePeripheral_doEnablePTMMode * * @brief Stop advertising, configure & start PTM mode * * @param index - item index from the menu * * @return always true */ bool SimplePeripheral_doEnablePTMMode(uint8_t index) { // Clear Display Display_clearLines(dispHandle, 0, 15); // Indicate in screen that PTM Mode is initializing Display_printf(dispHandle, 1, 0, "PTM Mode initializing!\n\n\rPlease note UART feed will now stop..."); // Before starting the NPI task close Display driver to make sure there is no shared resource used by both Display_close(dispHandle); // Start NPI task NPITask_createTask(ICALL_SERVICE_CLASS_BLE); // Disable Advertising and destroy sets GapAdv_destroy(advHandleLegacy,GAP_ADV_FREE_OPTION_ALL_DATA); GapAdv_destroy(advHandleLongRange,GAP_ADV_FREE_OPTION_ALL_DATA); // Intercept NPI RX events. NPITask_registerIncomingRXEventAppCB(simple_peripheral_handleNPIRxInterceptEvent, INTERCEPT); // Register for Command Status information HCI_TL_Init(NULL, (HCI_TL_CommandStatusCB_t) simple_peripheral_sendToNPI, NULL, selfEntity); // Register for Events HCI_TL_getCmdResponderID(ICall_getLocalMsgEntityId(ICALL_SERVICE_CLASS_BLE_MSG, selfEntity)); // Inform Stack to Initialize PTM HCI_EXT_EnablePTMCmd(); // Open back the display to avoid crashes to future calls to Display_printf (even though they won't go through until reboot) dispHandle = Display_open(Display_Type_ANY, NULL); return TRUE; } #endif /********************************************************************* *********************************************************************/
您好、Sai、
您似乎已将传感器控制器项目正确集成到 CCS 项目中。 下面是一些问题。 您能否将非 BLE5简单外设示例程序作为基础进行尝试。 也不建议使用使用 CC2640R2F 的 BLE5堆栈。
博客文章中的程序是 simple_poter。 安装 SDKv3.30、并将应用程序和堆栈项目导入 CCSv9.20、同时使用复制到工作区选项。 它可以正常工作、我只是对它进行了测试。 简单电位器不使用基于 RTC 的执行调度。 它使用应用周期计时器通过传感器控制器读取电位计。
另一件事是、许多 TI BLE 项目或产品开发使用简单外设并进行修改。 我要做的是在将简单外设示例导入到 CCS 工程之前、将其正确重命名为简单电位器。 然后、我准备低功耗示例程序。 这似乎需要大量工作、但这是必要的。 我已经为此发布了一篇博文。
-kel
/********************************************************************* * @fn SensorController_taskalertCallback * * @brief */ void SensorController_taskalertCallback(void){ adcData = 0; uint32_t bvAlertEvents = 0; // Clear the ALERT interrupt source scifClearAlertIntSource(); bvAlertEvents = scifGetAlertEvents(); if(bvAlertEvents & BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID)) { // ... Access Sensor Controller task data structures here ... // adcData = scifTaskData.adcLevelTrigger.output; adcData = scifTaskData.adcLevelTrigger.output.adcValue; MyData_SetParameter(MYDATA_DATA_ID, MYDATA_DATA_LEN,&adcData); //ADDED } // Acknowledge the ALERT event scifAckAlertEvents(); Event_post(syncEvent,SP_SCTASK_EVT); }
使用传感器控制器集成有什么优点? 禁用 ADC 后、它是否会进入待机模式?
您好、Sai、
我再次查看了您的原始代码。 您所做的错误是处理 Event_post (syncEvent、SP_SCTASK_EVT);。 发布事件时,应 在 SimplePeripheral_taskFxn()中处理该事件,如下所示。
void SensorController_taskalertCallback(void){ uint32_t bvAlertEvents = 0; // Clear the ALERT interrupt source scifClearAlertIntSource(); bvAlertEvents = scifGetAlertEvents(); if(bvAlertEvents & BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID)) { // ... Access Sensor Controller task data structures here ... // adcData = scifTaskData.adcLevelTrigger.output; adcData = scifTaskData.adcLevelTrigger.output.adcValue; } // Acknowledge the ALERT event scifAckAlertEvents(); Event_post(syncEvent,SP_SCTASK_EVT); }
static void SimplePeripheral_taskFxn(UArg a0, UArg a1) { // Initialize application SimplePeripheral_init(); // 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, SP_ALL_EVENTS, ICALL_TIMEOUT_FOREVER); if (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 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 = SimplePeripheral_processStackMsg((ICall_Hdr *)pMsg); } } if (pMsg && safeToDealloc) { ICall_freeMsg(pMsg); } } // If RTOS queue is not empty, process app message. if (events & SP_QUEUE_EVT) { while (!Queue_empty(appMsgQueueHandle)) { spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle); if (pMsg) { // Process message. SimplePeripheral_processAppMsg(pMsg); // Free the space from the message. ICall_free(pMsg); } } if (events & SP_SCTASK_EVT) { MyData_SetParameter(MYDATA_DATA_ID, MYDATA_DATA_LEN,&adcData); //ADDED } } } }
传感器控制器可以与主应用并行运行。 使用传感器控制器非常有利、尤其是当您对模拟传感器进行多次采样并在将数据发送到主应用之前对其进行处理时。
当没有事件时、主应用程序将进入待机模式。 一个事件示例是蓝牙广播、应用事件。
-kel
我在 SimplePeripheral_taskFxn ()中添加了"MyData_SetParameter (MyData_data_ID、MyData_data_LEN、&adcData);//added "、但我无法在 BLE 应用中读取数据、只有零才会出现、这是因为我们将 char 值初始化为零。它不会更新。
adcData 需要是最上层的、并且对所有 c 函数都可见。
静态 uint16_t adcData = 0;//已添加
SimplePeripheral_taskFxn()中的代码需要与此类似
IF (事件和 SP_SCTASK_EVT)
{
MyData_SetParameter (MyData_data_ID、MyData_data_LEN、&adcData);//添加
}
您可以在 MyData_SetParameter 和 watch 变量 adcData 中添加断点。
-kel
是的、我将 adcData 初始化为全局变量。
我发现我们需要插入此代码,
"IF (事件和 SP_SCTASK_EVT)
{
MyData_SetParameter (MyData_data_ID、MyData_data_LEN、&adcData);//添加
}"
在该 IF 条件之后
"IF (事件和 SP_queue_EVT)
{
}"
您似乎错过了"IF (EVENT & SP_SCTASK_EVT)"之前的一个"}"
非常感谢 Markel 的帮助。
现在我能够读取 BLE 数据。
/********************************************************************* * @fn SimplePeripheral_taskFxn * * @brief Application task entry point for the Simple Peripheral. * * @param a0, a1 - not used. */ static void SimplePeripheral_taskFxn(UArg a0, UArg a1) { // Initialize application SimplePeripheral_init(); // 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, SP_ALL_EVENTS, ICALL_TIMEOUT_FOREVER); if (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 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 = SimplePeripheral_processStackMsg((ICall_Hdr *)pMsg); } } if (pMsg && safeToDealloc) { ICall_freeMsg(pMsg); } } // If RTOS queue is not empty, process app message. if (events & SP_QUEUE_EVT) { while (!Queue_empty(appMsgQueueHandle)) { spEvt_t *pMsg = (spEvt_t *)Util_dequeueMsg(appMsgQueueHandle); if (pMsg) { // Process message. SimplePeripheral_processAppMsg(pMsg); // Free the space from the message. ICall_free(pMsg); } } } if (events & SP_SCTASK_EVT) { MyData_SetParameter(MYDATA_DATA_ID, MYDATA_DATA_LEN,&adcData); //ADDED } } } }
您好!
我想降低 cc2640r2的功耗、因此我计划使用传感器控制器收集 ADC 数据20分钟、并在20分钟后通过 BLE 发送数据。我尝试了、但我正面临 issues..ca、您将指导我如何正确执行此操作。
谢谢。
您好、Sai、
我建议您创建有关此新问题的新帖子。
-kel