/********************************************************************* * @fn SNP_processStateChangeEvt * * @brief Process a pending GAP Role state change event. * * @param newState - new state * * @return None. */ void SNP_processStateChangeEvt(gaprole_States_t newState) { static bool firstConnFlag = false; static gaprole_States_t currentState = GAPROLE_STARTED; static uint16_t currentConnectHandle; switch (newState) { case GAPROLE_STARTED: GAPRole_GetParameter(GAPROLE_CONNHANDLE, ¤tConnectHandle); break; case GAPROLE_ADVERTISING: break; case GAPROLE_ADVERTISING_NONCONN: /* After a connection is dropped a device will continue * sending non-connectable advertisements and shall sending this change of * state to the application. These are then disabled here so that sending * connectable advertisements can resume. */ { if(firstConnFlag) { // We were in a connection. uint8_t param[3]; param[0] = LO_UINT16(currentConnectHandle); param[1] = HI_UINT16(currentConnectHandle); GAPRole_GetParameter(GAPROLE_CONN_TERM_REASON, ¶m[2]); //reset the GATT state for this connection handle SNP_resetGATT(*((uint16_t*) ¶m[0])); //Connection Ended SNP_eventToHost_send(SNP_CONN_TERM_EVT, NULL, sizeof(param), param); } // Reset flag for next connection. firstConnFlag = false; } break; case GAPROLE_CONNECTED: if(firstConnFlag == false) { uint8_t peerAddress[B_ADDR_LEN]; GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress); GAPRole_GetParameter(GAPROLE_CONNHANDLE, ¤tConnectHandle); { uint8_t param[15]; GAPRole_GetParameter(GAPROLE_CONNHANDLE, ¶m[0]); GAPRole_GetParameter(GAPROLE_CONN_INTERVAL, ¶m[2]); GAPRole_GetParameter(GAPROLE_CONN_LATENCY, ¶m[4]); GAPRole_GetParameter(GAPROLE_CONN_TIMEOUT, ¶m[6]); GAPRole_GetParameter(GAPROLE_BD_ADDR_TYPE, ¶m[8]); memcpy(¶m[9], peerAddress, 6); //Advertisement Ended due to the connection SNP_eventToHost_send(SNP_CONN_EST_EVT, NULL, sizeof(param), param); } // if 4.1 feature are enable on the controller, // then the adv needs to be forced to // be non-connectable, since peripheral.c does not support multiple // connection. // Only turn advertising on for this state when we first connect // otherwise, when we go from connected_advertising back to this // state we will be turning advertising back on. firstConnFlag = true; } break; case GAPROLE_CONNECTED_ADV: break; case GAPROLE_WAITING: { if(firstConnFlag) { // We were in a connection. uint8_t param[3]; param[0] = LO_UINT16(currentConnectHandle); param[1] = HI_UINT16(currentConnectHandle); GAPRole_GetParameter(GAPROLE_CONN_TERM_REASON, ¶m[2]); //reset the GATT state for this connection handle SNP_resetGATT(*((uint16_t*)¶m[0])); //Connection Ended SNP_eventToHost_send(SNP_CONN_TERM_EVT, NULL, sizeof(param), param); // Reset flag for next connection. firstConnFlag = false; } } break; case GAPROLE_WAITING_AFTER_TIMEOUT: if(firstConnFlag) { uint8_t param[3]; param[0] = LO_UINT16(currentConnectHandle); param[1] = HI_UINT16(currentConnectHandle); GAPRole_GetParameter(GAPROLE_CONN_TERM_REASON, ¶m[2]); //reset the GATT state for this connection handle SNP_resetGATT(*((uint16_t*) ¶m[0])); //Connection Ended SNP_eventToHost_send(SNP_CONN_TERM_EVT, NULL, sizeof(param), param); } // Reset flag for next connection. firstConnFlag = false; break; case GAPROLE_ERROR: break; default: break; } SNP_AdvStateChange(newState, currentState); currentState = newState; }
/********************************************************************* * @fn Time_configNext() * * @brief Perform the characteristic configuration read or * write procedure. * * @param state - Configuration state. * * @return New configuration state. */ uint8_t Time_configNext(uint8_t state) { bool read; // Find next non-zero cached handle of interest while (state < TIME_CONFIG_MAX && Time_handleCache[Time_configList[state]] == 0) { state++; } // Return if reached end of list if (state >= TIME_CONFIG_MAX) { return TIME_CONFIG_CMPL; } // Determine what to do with characteristic switch (Time_configList[state]) { // Read these characteristics case HDL_CURR_TIME_CT_TIME_START: read = TRUE; break; // Set notification for these characteristics case HDL_CURR_TIME_CT_TIME_CCCD: read = FALSE; break; default: return state; } if(Time_configDone==TRUE) { return state; } // Do a GATT read or write if (read) { attReadReq_t readReq; readReq.handle = Time_handleCache[Time_configList[state]]; // Send the read request GATT_ReadCharValue(Time_connHandle, &readReq, ICall_getEntityId()); // Only reading time right now Time_configDone = TRUE; } else { attWriteReq_t writeReq; writeReq.pValue = GATT_bm_alloc(Time_connHandle, ATT_WRITE_REQ, 2, NULL); if (writeReq.pValue != NULL) { writeReq.len = 2; writeReq.pValue[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY); writeReq.pValue[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY); writeReq.sig = 0; writeReq.cmd = 0; writeReq.handle = Time_handleCache[Time_configList[state]]; // Send the read request if (GATT_WriteCharValue(Time_connHandle, &writeReq, ICall_getEntityId()) != SUCCESS) { GATT_bm_free((gattMsg_t *)&writeReq, ATT_WRITE_REQ); } } } return state; }
/********************************************************************* * @fn GlucoseDevInfoDisc * * @brief Current glucose service and characteristic discovery. * * @param state - Discovery state. * @param pMsg - GATT message. * * @return New discovery state. */ static uint8_t GlucoseDevInfoDisc(uint8_t state, gattMsgEvent_t *pMsg) { uint8_t newState = state; switch (state) { case DISC_DEVINFO_START: { uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(DEVINFO_SERV_UUID), HI_UINT16(DEVINFO_SERV_UUID) }; // Initialize service discovery variables glucoseSvcStartHdl = glucoseSvcEndHdl = 0; glucoseEndHdlIdx = 0; // Discover service by UUID GATT_DiscPrimaryServiceByUUID(glucCollConnHandle, uuid, ATT_BT_UUID_SIZE, glucCollTaskId); newState = DISC_DEVINFO_SVC; } break; case DISC_DEVINFO_SVC: // Service found, store handles if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP && pMsg->msg.findByTypeValueRsp.numInfo > 0) { glucoseSvcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0); glucoseSvcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0); } // If procedure complete if ((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP)) { // If service found if (glucoseSvcStartHdl != 0) { // Discover all characteristics GATT_DiscAllChars(glucCollConnHandle, glucoseSvcStartHdl, glucoseSvcEndHdl, glucCollTaskId); newState = DISC_DEVINFO_CHAR; } else { // Service not found newState = DISC_FAILED; } } break; case DISC_DEVINFO_CHAR: { uint8_t i; uint8_t *p; uint16_t handle; uint16_t uuid; // Characteristics found if (pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0 && pMsg->msg.readByTypeRsp.len == CHAR_DESC_HDL_UUID16_LEN) { // For each characteristic declaration p = pMsg->msg.readByTypeRsp.pDataList; for (i = pMsg->msg.readByTypeRsp.numPairs; i > 0; i--) { // Parse characteristic declaration handle = BUILD_UINT16(p[3], p[4]); uuid = BUILD_UINT16(p[5], p[6]); // If UUID is of interest, store handle switch (uuid) { case MANUFACTURER_NAME_UUID: glucoseHdlCache[HDL_DEVINFO_MANUFACTURER_NAME] = handle; break; case SYSTEM_ID_UUID: glucoseHdlCache[HDL_DEVINFO_SYSTEM_ID] = handle; break; case MODEL_NUMBER_UUID: glucoseHdlCache[HDL_DEVINFO_MODEL_NUM] = handle; break; default: break; } p += CHAR_DESC_HDL_UUID16_LEN; } } // If procedure complete if ((pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP)) { // If didn't find required device info if (glucoseHdlCache[HDL_DEVINFO_MANUFACTURER_NAME] == 0 || glucoseHdlCache[HDL_DEVINFO_SYSTEM_ID] == 0 || glucoseHdlCache[HDL_DEVINFO_MODEL_NUM] == 0) { newState = DISC_FAILED; } else { newState = DISC_IDLE; } } } break; default: break; } return newState; }
uint8 glucoseCtlPntWriteFilter(uint8 opcode, uint8 oper, uint8 filterType, void* param1, void* param2) { attWriteReq_t writeReq; uint8 status; writeReq.pValue = GATT_bm_alloc( glucCollConnHandle, ATT_WRITE_REQ, GLUCOSE_CTL_PNT_FILTER_LEN, NULL ); if (writeReq.pValue != NULL) { UTCTimeStruct *time1, *time2; uint16 *seqNum1, *seqNum2; uint8 *p = writeReq.pValue; *p++ = opcode; *p++ = oper; // The operator will tells us whether to include the filters or not // Note day and month are converted to date time struct values switch(oper) { case CTL_PNT_OPER_LESS_EQUAL: case CTL_PNT_OPER_GREATER_EQUAL: *p++ = filterType; if (filterType == CTL_PNT_FILTER_SEQNUM) { seqNum1 = param1; *p++ = LO_UINT16(*seqNum1); *p++ = HI_UINT16(*seqNum1); } else { time1 = param1; *p++ = LO_UINT16(time1->year); *p++ = HI_UINT16(time1->year); *p++ = (time1->month + 1); *p++ = (time1->day + 1); *p++ = time1->hour; *p++ = time1->minutes; *p++ = time1->seconds; } break; case CTL_PNT_OPER_RANGE: *p++ = filterType; if (filterType == CTL_PNT_FILTER_SEQNUM) { seqNum1 = param1; seqNum2 = param2; *p++ = LO_UINT16(*seqNum1); *p++ = HI_UINT16(*seqNum1); *p++ = LO_UINT16(*seqNum2); *p++ = HI_UINT16(*seqNum2); } else { time1 = param1; time2 = param2; *p++ = LO_UINT16(time1->year); *p++ = HI_UINT16(time1->year); *p++ = (time1->month + 1); *p++ = (time1->day + 1); *p++ = time1->hour; *p++ = time1->minutes; *p++ = time1->seconds; *p++ = LO_UINT16(time2->year); *p++ = HI_UINT16(time2->year); *p++ = (time2->month + 1); *p++ = (time2->day + 1); *p++ = time2->hour; *p++ = time2->minutes; *p++ = time2->seconds; } break; default: break; } writeReq.len = (p - writeReq.pValue); writeReq.sig = 0; writeReq.cmd = 0; writeReq.handle = glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_START]; // Send the write request status = GATT_WriteCharValue( glucCollConnHandle, &writeReq, glucCollTaskId ); if (status != SUCCESS) { GATT_bm_free( (gattMsg_t *)&writeReq, ATT_WRITE_REQ ); } } else { status = bleMemAllocError; } return status; }
#define PP_DEFAULT_PATH_LOSS 0x7F #define SERVAPP_NUM_ATTR_SUPPORTED 5 /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // Link Loss Service UUID CONST uint8 linkLossServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16( LINK_LOSS_SERV_UUID ), HI_UINT16( LINK_LOSS_SERV_UUID ) }; // Immediate Alert Service UUID CONST uint8 imAlertServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16( IMMEDIATE_ALERT_SERV_UUID ), HI_UINT16( IMMEDIATE_ALERT_SERV_UUID ) }; // Tx Power Level Service UUID CONST uint8 txPwrLevelServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16( TX_PWR_LEVEL_SERV_UUID ), HI_UINT16( TX_PWR_LEVEL_SERV_UUID ) }; // Alert Level Attribute UUID
* CONSTANTS */ #define SERVAPP_NUM_ATTR_SUPPORTED 18 /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // Simple GATT Profile Service UUID: 0xFFF0 CONST uint8 simpleProfileServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID) }; // Characteristic 1 UUID: 0xFFF1 CONST uint8 simpleProfilechar1UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_CHAR1_UUID), HI_UINT16(SIMPLEPROFILE_CHAR1_UUID) }; // Characteristic 2 UUID: 0xFFF2 CONST uint8 simpleProfilechar2UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_CHAR2_UUID), HI_UINT16(SIMPLEPROFILE_CHAR2_UUID) }; // Characteristic 3 UUID: 0xFFF3
uint8 glucoseCtlPntWriteFilter(uint8 opcode, uint8 oper, uint8 filterType, void* param1, void* param2) { attWriteReq_t writeReq; UTCTimeStruct *time1, *time2; uint16 *seqNum1, *seqNum2; uint8 *p = writeReq.value; *p++ = opcode; *p++ = oper; // The operator will tells us whether to include the filters or not // Note day and month are converted to date time struct values switch(oper) { case CTL_PNT_OPER_LESS_EQUAL: case CTL_PNT_OPER_GREATER_EQUAL: *p++ = filterType; if (filterType == CTL_PNT_FILTER_SEQNUM) { seqNum1 = param1; *p++ = LO_UINT16(*seqNum1); *p++ = HI_UINT16(*seqNum1); } else { time1 = param1; *p++ = LO_UINT16(time1->year); *p++ = HI_UINT16(time1->year); *p++ = (time1->month + 1); *p++ = (time1->day + 1); *p++ = time1->hour; *p++ = time1->minutes; *p++ = time1->seconds; } break; case CTL_PNT_OPER_RANGE: *p++ = filterType; if (filterType == CTL_PNT_FILTER_SEQNUM) { seqNum1 = param1; seqNum2 = param2; *p++ = LO_UINT16(*seqNum1); *p++ = HI_UINT16(*seqNum1); *p++ = LO_UINT16(*seqNum2); *p++ = HI_UINT16(*seqNum2); } else { time1 = param1; time2 = param2; *p++ = LO_UINT16(time1->year); *p++ = HI_UINT16(time1->year); *p++ = (time1->month + 1); *p++ = (time1->day + 1); *p++ = time1->hour; *p++ = time1->minutes; *p++ = time1->seconds; *p++ = LO_UINT16(time2->year); *p++ = HI_UINT16(time2->year); *p++ = (time2->month + 1); *p++ = (time2->day + 1); *p++ = time2->hour; *p++ = time2->minutes; *p++ = time2->seconds; } break; default: break; } writeReq.len = (p - writeReq.value); writeReq.sig = 0; writeReq.cmd = 0; writeReq.handle = glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_START]; return GATT_WriteCharValue( glucCollConnHandle, &writeReq, glucCollTaskId ); }
'm', 'e', 't', 'e', 'r', 'S', 'e', 'n', 's', 'o', 'r', // connection interval range 0x05, // 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 GAP_ADTYPE_POWER_LEVEL, 0 // 0dBm }; // Advertisement data static uint8 advertData[] = { // Flags; this sets the device to use limited discoverable // mode (advertises for 30 seconds at a time) instead of general // discoverable mode (advertises indefinitely) 0x02, // length of this data
/********************************************************************* * CONSTANTS */ /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // Lights GATT Profile Service UUID: 0xFFB0 CONST uint8 lightsProfileServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(LIGHTSPROFILE_SERV_UUID), HI_UINT16(LIGHTSPROFILE_SERV_UUID) }; // Characteristic Red UUID: 0xFFB1 CONST uint8 lightsProfileRedUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(LIGHTSPROFILE_RED_UUID), HI_UINT16(LIGHTSPROFILE_RED_UUID) }; // Characteristic Green UUID: 0xFFB2 CONST uint8 lightsProfileGreenUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(LIGHTSPROFILE_GREEN_UUID), HI_UINT16(LIGHTSPROFILE_GREEN_UUID) }; // Characteristic Blue UUID: 0xFFB3
/********************************************************************* * @fn thermometer_HandleKeys * * @brief Handles all key events for this device. * * @param shift - true if in shift/alt. * @param keys - bit field for key events. Valid entries: * HAL_KEY_SW_2 * HAL_KEY_SW_1 * * @return none */ static void thermometer_HandleKeys( uint8 shift, uint8 keys ) { bStatus_t status; uint8 notify_interval; if ( keys & HAL_KEY_SW_1 ) { // set simulated measurement flag index thermometerFlagsIdx+=1; if (thermometerFlagsIdx == FLAGS_IDX_MAX) { thermometerFlagsIdx = 0; } } //read stored interval value Thermometer_GetParameter( THERMOMETER_INTERVAL, ¬ify_interval ); if(notify_interval == 0) { thMeasTimerRunning = FALSE; } if ( keys & HAL_KEY_SW_2 ) { // if device is not in a connection, pressing the right key should toggle // advertising on and off. If timer is running, then will adv when meas is ready if((gapProfileState != GAPROLE_CONNECTED) && (thMeasTimerRunning == FALSE)) { uint8 current_adv_enabled_status; uint8 new_adv_enabled_status; //Find the current GAP advertisement status GAPRole_GetParameter( GAPROLE_ADVERT_ENABLED, ¤t_adv_enabled_status ); if ( current_adv_enabled_status == FALSE ) { new_adv_enabled_status = TRUE; } else { new_adv_enabled_status = FALSE; } //change the GAP advertisement status to opposite of current status GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &new_adv_enabled_status ); } else //timer is running, so allow simulated changes { //change temperature, remove single precision if((thermometerCelcius) < 0X000175) { thermometerCelcius +=1; } else { uint16 thInterval = 30; thermometerCelcius = 0X000173; //Simulate interval change Thermometer_SetParameter( THERMOMETER_INTERVAL, THERMOMETER_INTERVAL_LEN, &thInterval ); if(temperatureIntervalConfig == true) { attHandleValueInd_t intervalInd; intervalInd.pValue = GATT_bm_alloc( gapConnHandle, ATT_HANDLE_VALUE_IND, THERMOMETER_INTERVAL_LEN, NULL ); if ( intervalInd.pValue != NULL ) { intervalInd.len = THERMOMETER_INTERVAL_LEN; intervalInd.pValue[0] = LO_UINT16(thInterval); intervalInd.pValue[1] = HI_UINT16(thInterval); intervalInd.handle = THERMOMETER_INTERVAL_VALUE_POS; status = Thermometer_IntervalIndicate( gapConnHandle, &intervalInd, thermometerTaskId ); // we can fail if there was pending meas or not connected if (status != SUCCESS) { //queue indication thermometerStoreIndications(&intervalInd); } } } } } } }
/********************************************************************* * @fn thermometerMeasIndicate * * @brief Prepare and send a thermometer measurement indication * * @return none */ static void thermometerMeasIndicate(void) { // Thermometer measurement value stored in this structure. attHandleValueInd_t thermometerMeas; thermometerMeas.pValue = GATT_bm_alloc(gapConnHandle, ATT_HANDLE_VALUE_IND, THERMOMETER_MEAS_LEN, NULL); if (thermometerMeas.pValue != NULL) { // att value notification structure uint8 *p = thermometerMeas.pValue; // temperature uint32 temperature; //flags uint8 flags = thermometerFlags[thermometerFlagsIdx]; // flags 1 byte long *p++ = flags; if(flags & THERMOMETER_FLAGS_FARENHEIT) { temperature = (thermometerCelcius *9/5) +320; } else { temperature = thermometerCelcius; } temperature = 0xFF000000 | temperature; //osal_buffer_uint32 p = osal_buffer_uint32( p, temperature ); //timestamp if (flags & THERMOMETER_FLAGS_TIMESTAMP) { UTCTimeStruct time; // Get time structure from OSAL osal_ConvertUTCTime( &time, osal_getClock() ); *p++ = LO_UINT16(time.year); *p++ = HI_UINT16(time.year); *p++ = time.month; *p++ = time.day; *p++ = time.hour; *p++ = time.minutes; *p++ = time.seconds; } if(flags & THERMOMETER_FLAGS_TYPE) { uint8 site; Thermometer_GetParameter( THERMOMETER_TYPE, &site ); *p++ = site; } thermometerMeas.len = (uint8) (p - thermometerMeas.pValue); thermometerMeas.handle = THERMOMETER_TEMP_VALUE_POS; // Queue indication. thermometerStoreIndications( &thermometerMeas); //advertise measurement is ready thermometer_Advertise(); } }
/********************************************************************* * @fn thermometerMeasNotify * * @brief Prepare and send a thermometer measurement notification * * @return none */ static void thermometerImeasNotify(void) { if (temperatureIMeasCharConfig == true) { attHandleValueNoti_t thermometerIMeas; thermometerIMeas.pValue = GATT_bm_alloc( gapConnHandle, ATT_HANDLE_VALUE_NOTI, THERMOMETER_IMEAS_LEN, NULL ); if ( thermometerIMeas.pValue != NULL ) { // att value notification structure uint8 *p = thermometerIMeas.pValue; // temperature uint32 temperature; //flags uint8 flags = thermometerFlags[thermometerFlagsIdx]; // flags 1 byte long *p++ = flags; if(flags & THERMOMETER_FLAGS_FARENHEIT) { temperature = (thermometerCelcius *9/5) +320; } else { temperature = thermometerCelcius; } temperature = 0xFF000000 | temperature; //osal_buffer_uint32 p = osal_buffer_uint32( p, temperature ); //timestamp if (flags & THERMOMETER_FLAGS_TIMESTAMP) { UTCTimeStruct time; // Get time structure from OSAL osal_ConvertUTCTime( &time, osal_getClock() ); *p++ = LO_UINT16(time.year); *p++ = HI_UINT16(time.year); *p++ = time.month; *p++ = time.day; *p++ = time.hour; *p++ = time.minutes; *p++ = time.seconds; } if(flags & THERMOMETER_FLAGS_TYPE) { uint8 site; Thermometer_GetParameter( THERMOMETER_TYPE, &site ); *p++ = site; } thermometerIMeas.len = (uint8) (p - thermometerIMeas.pValue); if ( Thermometer_IMeasNotify( gapConnHandle, &thermometerIMeas) != SUCCESS ) { GATT_bm_free( (gattMsg_t *)&thermometerIMeas, ATT_HANDLE_VALUE_NOTI ); } } } }
's', 'u', 'r', 'e', ' ', 'S', 'e', 'n', 's', 'o', 'r', // connection interval range 0x05, // 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 ), 0x02, // length of this data GAP_ADTYPE_POWER_LEVEL, //TX Power Level 0 // 0dBm }; // Advertisement data static uint8 advertData[] = { // Flags; this sets the device to use limited discoverable // mode (advertises for 30 seconds at a time) instead of general // discoverable mode (advertises indefinitely) 0x02, // length of this data GAP_ADTYPE_FLAGS,
/********************************************************************* * CONSTANTS */ /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // Device information service CONST uint8 devInfoServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(DEVINFO_SERV_UUID), HI_UINT16(DEVINFO_SERV_UUID) }; // System ID CONST uint8 devInfoSystemIdUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(SYSTEM_ID_UUID), HI_UINT16(SYSTEM_ID_UUID) }; // Model Number String CONST uint8 devInfoModelNumberUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(MODEL_NUMBER_UUID), HI_UINT16(MODEL_NUMBER_UUID) }; // Serial Number String
's', 'o', 'r', }; static uint8 advertData[] = { // flags 0x02, GAP_ADTYPE_FLAGS, GAP_ADTYPE_FLAGS_GENERAL | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED, // service UUIDs 0x03, GAP_ADTYPE_16BIT_MORE, LO_UINT16(RSC_SERV_UUID), HI_UINT16(RSC_SERV_UUID), }; // Device name attribute value static uint8 attDeviceName[GAP_DEVICE_NAME_LEN] = "RSC Sensor"; // GAP connection handle static uint16 gapConnHandle; // Running measurement value stored in this structure static attHandleValueNoti_t sensorMeas; // Flags for simulated measurements static const uint8 sensorFlags[FLAGS_IDX_MAX] = { RSC_FLAGS_ALL,
/********************************************************************* * @fn SerialApp_ProcessMSGCmd * * @brief Data message processor callback. This function processes * any incoming data - probably from other devices. Based * on the cluster ID, perform the intended action. * * @param pkt - pointer to the incoming message packet * * @return TRUE if the 'pkt' parameter is being used and will be freed later, * FALSE otherwise. */ void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt ) { uint8 stat; uint8 seqnb; uint8 delay; switch ( pkt->clusterId ) { // A message with a serial data block to be transmitted on the serial port. case SERIALAPP_CLUSTERID1: seqnb = pkt->cmd.Data[0]; // Keep message if not a repeat packet if ( (seqnb > SerialApp_SeqRx) || // Normal ((seqnb < 0x80 ) && ( SerialApp_SeqRx > 0x80)) ) // Wrap-around { // Transmit the data on the serial port. if ( HalUARTWrite( SERIAL_APP_PORT, pkt->cmd.Data+1, (pkt->cmd.DataLength-1) ) ) { // Save for next incoming message SerialApp_SeqRx = seqnb; stat = OTA_SUCCESS; } else { stat = OTA_SER_BUSY; } } else { stat = OTA_DUP_MSG; } // Select approproiate OTA flow-control delay. delay = (stat == OTA_SER_BUSY) ? SERIALAPP_NAK_DELAY : SERIALAPP_ACK_DELAY; // Build & send OTA response message. rspBuf[0] = stat; rspBuf[1] = seqnb; rspBuf[2] = LO_UINT16( delay ); rspBuf[3] = HI_UINT16( delay ); stat = AF_DataRequest( &(pkt->srcAddr), (endPointDesc_t*)&SerialApp_epDesc, SERIALAPP_CLUSTERID2, SERIAL_APP_RSP_CNT , rspBuf, &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS ); if ( stat != afStatus_SUCCESS ) { osal_start_timerEx( SerialApp_TaskID, SERIALAPP_RSP_RTRY_EVT, SERIALAPP_RSP_RTRY_TIMEOUT ); // Store the address for the timeout retry. osal_memcpy(&SerialApp_RspDstAddr, &(pkt->srcAddr), sizeof( afAddrType_t )); } HalLedSet(HAL_LED_2,HAL_LED_MODE_ON); break; // A response to a received serial data block. case SERIALAPP_CLUSTERID2: if ( (pkt->cmd.Data[1] == SerialApp_SeqTx) && ((pkt->cmd.Data[0] == OTA_SUCCESS) || (pkt->cmd.Data[0] == OTA_DUP_MSG)) ) { // Remove timeout waiting for response from other device. osal_stop_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT ); FREE_OTABUF(); HalLedSet(HAL_LED_2,HAL_LED_MODE_ON); } else { delay = BUILD_UINT16( pkt->cmd.Data[2], pkt->cmd.Data[3] ); // Re-start timeout according to delay sent from other device. osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT, delay ); } break; #ifndef ZDO_COORDINATOR case SERIALAPP_CLUSTERID_CMD: CommandToBox(pkt->cmd.Data[1]); break; #endif default: break; } }
/********************************************************************* * @fn sensorMeasNotify * * @brief Prepare and send a RSC measurement notification * * @return none */ static void sensorMeasNotify(void) { static uint16 centimeters = 0; uint8 *p = sensorMeas.value; uint8 flags = sensorFlags[sensorFlagsIdx]; switch( motion ) { case STANDING_STILL: instSpeed = instCadence = instStrideLength = STANDING_STILL; // 0 for walking bit flags = flags & 0xFB; //0b1111 1011 break; case WALKING_MOTION: instStrideLength = STRIDE_LENGTH_WALKING; instCadence = WALKING_CADENCE; instSpeed = WALKING_SPEED; // 0 for walking bit flags = flags & 0xFB; break; case RUNNING_MOTION: instStrideLength = STRIDE_LENGTH_RUNNING; instCadence = RUNNING_CADENCE; instSpeed = RUNNING_SPEED; // set in 1 for walking bit. flags = flags | 0x04; break; default: // Do nothing break; } // Add distance centimeters += (uint16)DISTANCE_TRAVELED( instSpeed ); // If traveled at least a meter if ( centimeters >= 100 ) { // add distance, truncated to meters totalDistance += (centimeters / 100); // and continue to store the remaining centimeters centimeters %= 100; } // Build RSC measurement structure from simulated values // Flags simulate the isPresent bits. *p++ = flags; //Included regardless of flags. *p++ = LO_UINT16( instSpeed ); *p++ = HI_UINT16( instSpeed ); *p++ = instCadence; if (flags & RSC_FLAGS_STRIDE) { *p++ = LO_UINT16( instStrideLength ); *p++ = HI_UINT16( instStrideLength ); } if (flags & RSC_FLAGS_DIST) { *p++ = BREAK_UINT32(totalDistance, 0); *p++ = BREAK_UINT32(totalDistance, 1); *p++ = BREAK_UINT32(totalDistance, 2); *p++ = BREAK_UINT32(totalDistance, 3); } // Get length sensorMeas.len = (uint8) (p - sensorMeas.value); // Send to service to send the notification Running_MeasNotify( gapConnHandle, &sensorMeas ); }
/********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ /** * GATT Service */ // Immediate Alert Service UUID: 0xFF10 CONST uint8 imAlertServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(IM_ALERT_SERV_UUID), HI_UINT16(IM_ALERT_SERV_UUID) }; /** * GATT Characteristic Types */ // Alert Level UUID: 0xFF11 CONST uint8 imAlertLevelUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(IM_ALERT_LEVEL_UUID), HI_UINT16(IM_ALERT_LEVEL_UUID) }; /********************************************************************* * GLOBAL FUNCTIONS */
0x50, // 'P' 0x65, // 'e' 0x72, // 'r' 0x69, // 'i' 0x70, // 'p' 0x68, // 'h' 0x65, // 'e' 0x72, // 'r' 0x61, // 'a' 0x6c, // 'l' // connection interval range 0x05, // 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 0x02, // length of this data GAP_ADTYPE_POWER_LEVEL, 0 // 0dBm }; // GAP - Advertisement data (max size = 31 bytes, though this is // best kept short to conserve power while advertisting) static uint8 advertData[] = { // Flags; this sets the device to use limited discoverable // mode (advertises for 30 seconds at a time) instead of general
NULL // Authorization callback function pointer }; // reverse uuid CONST uint8 BXServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(BX_SERV_UUID), HI_UINT16(BX_SERV_UUID) }; CONST uint8 BXchar1UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(BX_CHAR1_UUID), HI_UINT16(BX_CHAR1_UUID) }; CONST uint8 BXchar2UUID[ATT_BT_UUID_SIZE] = { LO_UINT16(BX_CHAR2_UUID), HI_UINT16(BX_CHAR2_UUID) };
// Delay a time to make sure the coordinator can receive data // if not delay the coordinator can't receive data from other node #define MONITOR_SEND_DELAY (RESPONSE_POLL_RATE * 2) // resend delay #define MONITOR_RESEND_DELAY 10 /********************************************************************* * CONSTANTS */ // ACK const byte ACK[] = { 0xFE, 0x00, LO_UINT16(ACK_CMD), HI_UINT16(ACK_CMD), 0x00, 0x00, 0x00}; /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // This list should be filled with Application specific Cluster IDs. const cId_t Monitor_ClusterInList[Monitor_MAX_IN_CLUSTERS] = { ZIGBEE_COMMON_CLUSTER
/********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ /** * GATT Services */ // Generic Access Profile Service UUID CONST uint8 gapServiceUUID[ATT_BT_UUID_SIZE] = { LO_UINT16( GAP_SERVICE_UUID ), HI_UINT16( GAP_SERVICE_UUID ) }; // Generic Attribute Profile Service UUID CONST uint8 gattServiceUUID[ATT_BT_UUID_SIZE] = { LO_UINT16( GATT_SERVICE_UUID ), HI_UINT16( GATT_SERVICE_UUID ) }; /** * GATT Declarations */ // Primary Service UUID CONST uint8 primaryServiceUUID[ATT_BT_UUID_SIZE] = { LO_UINT16( GATT_PRIMARY_SERVICE_UUID ), HI_UINT16( GATT_PRIMARY_SERVICE_UUID )
/*************************************************************************************************** * @fn MT_AfIncomingMsg * * @brief Process the callback subscription for AF Incoming data. * * @param pkt - Incoming AF data. * * @return none ***************************************************************************************************/ void MT_AfIncomingMsg(afIncomingMSGPacket_t *pMsg) { #define MT_AF_INC_MSG_LEN 17 #define MT_AF_INC_MSG_EXT 10 uint16 dataLen = pMsg->cmd.DataLength; // Length of the data section in the response packet. uint16 respLen = MT_AF_INC_MSG_LEN + dataLen; uint8 cmd = MT_AF_INCOMING_MSG; uint8 *pRsp, *pTmp; mtAfInMsgList_t *pItem = NULL; #if defined INTER_PAN if (StubAPS_InterPan(pMsg->srcAddr.panId, pMsg->srcAddr.endPoint)) { cmd = MT_AF_INCOMING_MSG_EXT; } else #endif if ((pMsg->srcAddr.addrMode == afAddr64Bit) || (respLen > (uint16)(MT_RPC_DATA_MAX - MT_AF_INC_MSG_EXT))) { cmd = MT_AF_INCOMING_MSG_EXT; } if (cmd == MT_AF_INCOMING_MSG_EXT) { respLen += MT_AF_INC_MSG_EXT; } if (respLen > (uint16)MT_RPC_DATA_MAX) { if ((pItem = (mtAfInMsgList_t *)osal_mem_alloc(sizeof(mtAfInMsgList_t) + dataLen)) == NULL) { return; // If cannot hold a huge message, cannot give indication at all. } pItem->data = (uint8 *)(pItem+1); respLen -= dataLen; // Zero data bytes are sent with an over-sized incoming indication. } // Attempt to allocate memory for the response packet. if ((pRsp = osal_mem_alloc(respLen)) == NULL) { if (pItem != NULL) { (void)osal_mem_free(pItem); } return; } pTmp = pRsp; /* Group ID */ *pTmp++ = LO_UINT16(pMsg->groupId); *pTmp++ = HI_UINT16(pMsg->groupId); /* Cluster ID */ *pTmp++ = LO_UINT16(pMsg->clusterId); *pTmp++ = HI_UINT16(pMsg->clusterId); if (cmd == MT_AF_INCOMING_MSG_EXT) { *pTmp++ = pMsg->srcAddr.addrMode; if (pMsg->srcAddr.addrMode == afAddr64Bit) { (void)osal_memcpy(pTmp, pMsg->srcAddr.addr.extAddr, Z_EXTADDR_LEN); } else { pTmp[0] = LO_UINT16(pMsg->srcAddr.addr.shortAddr); pTmp[1] = HI_UINT16(pMsg->srcAddr.addr.shortAddr); } pTmp += Z_EXTADDR_LEN; *pTmp++ = pMsg->srcAddr.endPoint; #if defined INTER_PAN *pTmp++ = LO_UINT16(pMsg->srcAddr.panId); *pTmp++ = HI_UINT16(pMsg->srcAddr.panId); #else *pTmp++ = 0; *pTmp++ = 0; #endif } else { /* Source Address */ *pTmp++ = LO_UINT16(pMsg->srcAddr.addr.shortAddr); *pTmp++ = HI_UINT16(pMsg->srcAddr.addr.shortAddr); /* Source EP */ *pTmp++ = pMsg->srcAddr.endPoint; } /* Destination EP */ *pTmp++ = pMsg->endPoint; /* WasBroadCast */ *pTmp++ = pMsg->wasBroadcast; /* LinkQuality */ *pTmp++ = pMsg->LinkQuality; /* SecurityUse */ *pTmp++ = pMsg->SecurityUse; /* Timestamp */ *pTmp++ = BREAK_UINT32(pMsg->timestamp, 0); *pTmp++ = BREAK_UINT32(pMsg->timestamp, 1); *pTmp++ = BREAK_UINT32(pMsg->timestamp, 2); *pTmp++ = BREAK_UINT32(pMsg->timestamp, 3); /* Data Length */ if (cmd == MT_AF_INCOMING_MSG_EXT) { /* Z-Tool apparently takes the last Byte before the data buffer as the dynamic length and * ignores the bigger UInt16 length of an EXT incoming message. But no data bytes will be sent * with a huge message, so it's necessary to work-around and fake-out Z-Tool with a zero here. */ *pTmp++ = 0; // TODO - workaround Z-Tool shortcoming; should be: = pMsg->cmd.TransSeqNumber; *pTmp++ = LO_UINT16(dataLen); *pTmp++ = HI_UINT16(dataLen); } else { *pTmp++ = pMsg->cmd.TransSeqNumber; *pTmp++ = dataLen; } /* Data */ if (pItem != NULL) { // Enqueue the new huge incoming item. pItem->next = pMtAfInMsgList; pMtAfInMsgList = pItem; // Setup to time-out the huge incoming item if host does not MT_AF_DATA_RETRIEVE it. pItem->tick = MT_AF_EXEC_CNT; if (ZSuccess != osal_start_timerEx(MT_TaskID, MT_AF_EXEC_EVT, MT_AF_EXEC_DLY)) { (void)osal_set_event(MT_TaskID, MT_AF_EXEC_EVT); } pItem->timestamp = pMsg->timestamp; (void)osal_memcpy(pItem->data, pMsg->cmd.Data, dataLen); } else { (void)osal_memcpy(pTmp, pMsg->cmd.Data, dataLen); } /* Build and send back the response */ MT_BuildAndSendZToolResponse(((uint8)MT_RPC_CMD_AREQ|(uint8)MT_RPC_SYS_AF), cmd, respLen, pRsp); (void)osal_mem_free(pRsp); }
/********************************************************************* * @fn hidappDiscoverService * * @brief Discover service using UUID. * * @param connHandle - connection handle to do discovery on * @param svcUuid - service UUID to discover * * @return none */ static void hidappDiscoverService( uint16 connHandle, uint16 svcUuid ) { uint8 uuid[2] = {LO_UINT16(svcUuid), HI_UINT16(svcUuid)}; VOID GATT_DiscPrimaryServiceByUUID( connHandle, uuid, ATT_BT_UUID_SIZE, hidappTaskId ); }
'd' }; // Advertising data static uint8 advData[] = { // flags 0x02, // length of this data GAP_ADTYPE_FLAGS, GAP_ADTYPE_FLAGS_LIMITED | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED, // appearance 0x03, // length of this data GAP_ADTYPE_APPEARANCE, LO_UINT16(GAP_APPEARE_HID_KEYBOARD), HI_UINT16(GAP_APPEARE_HID_KEYBOARD), // service UUIDs 0x05, // length of this data GAP_ADTYPE_16BIT_MORE, LO_UINT16(HID_SERVICE_UUID), HI_UINT16(HID_SERVICE_UUID), LO_UINT16(BATT_SERVICE_UUID), HI_UINT16(BATT_SERVICE_UUID) }; // Device name attribute value static CONST uint8 attDeviceName[GAP_DEVICE_NAME_LEN] = "HID Keyboard"; // HID Dev configuration static hidDevCfg_t hidEmuKbdCfg =
static void hidappProcessGATTMsg( gattMsgEvent_t *pPkt ) { // Build the message first switch ( pPkt->method ) { case ATT_HANDLE_VALUE_NOTI: // First try to send out pending HID report if ( reportRetries > 0 ) { hidappSendInReport( &lastInReport ); reportRetries = 0; osal_stop_timerEx( hidappTaskId, HIDAPP_EVT_REPORT_RETRY ); } // Send incoming HID report if ( hidappSendInReport( &(pPkt->msg.handleValueNoti) ) == FALSE ) { // Save report for retries later osal_memcpy( &lastInReport, &(pPkt->msg.handleValueNoti), sizeof( attHandleValueNoti_t ) ); reportRetries = 1; osal_start_timerEx( hidappTaskId, HIDAPP_EVT_REPORT_RETRY, HIDAPP_INPUT_RETRY_TIMEOUT ); } break; case ATT_FIND_BY_TYPE_VALUE_RSP: // Response from GATT_DiscPrimaryServiceByUUID // Service found, store handles if ( pPkt->msg.findByTypeValueRsp.numInfo > 0 ) { serviceStartHandle = pPkt->msg.findByTypeValueRsp.handlesInfo[0].handle; serviceEndHandle = pPkt->msg.findByTypeValueRsp.handlesInfo[0].grpEndHandle; } // If procedure complete else if ( pPkt->hdr.status == bleProcedureComplete ) { if ( serviceStartHandle != 0 ) { if ( serviceToDiscover == GATT_SERVICE_UUID ) { // Begin the search for characteristic handle of the service readReq.startHandle = serviceStartHandle; readReq.endHandle = serviceEndHandle; readReqType.len = 2; readReqType.uuid[0] = LO_UINT16(SERVICE_CHANGED_UUID); readReqType.uuid[1] = HI_UINT16(SERVICE_CHANGED_UUID); readReq.type = readReqType; GATT_DiscCharsByUUID(connHandle, &readReq, hidappTaskId ); } else if ( serviceToDiscover == HID_SERVICE_UUID ) { // Discover all characteristics GATT_DiscAllChars( connHandle, serviceStartHandle, serviceEndHandle, hidappTaskId ); } } } break; case ATT_READ_BY_TYPE_RSP: // Response from Discover all Characteristics. // Success indicates packet with characteristic discoveries. if ( pPkt->hdr.status == SUCCESS ) { attReadByTypeRsp_t rsp = pPkt->msg.readByTypeRsp; uint8 idx = 0; if ( serviceToDiscover == GATT_SERVICE_UUID ) { // We have discovered the GATT Service Characteristic handle uint8 low = LO_UINT16(pPkt->msg.readByTypeRsp.dataList[3]); uint8 high = HI_UINT16(pPkt->msg.readByTypeRsp.dataList[4]); serviceChangeHandle = BUILD_UINT16(low, high); // Break to skip next part, it doesn't apply here break; } // Search characteristics for those with notification permissions. while( idx < ((rsp.numPairs * rsp.len ) - 1)) { // Check permissions of characteristic for notification permission if ( (rsp.dataList[idx+2] & GATT_PROP_NOTIFY ) ) { uint16* pHandle = (mouseCharHandle == GATT_INVALID_HANDLE) ? &mouseCharHandle : &keyCharHandle ; if ( pHandle == &keyCharHandle && consumerCtrlCharHandle == GATT_INVALID_HANDLE ) { pHandle = ( keyCharHandle == GATT_INVALID_HANDLE ) ? &keyCharHandle : &consumerCtrlCharHandle; } if ( *pHandle == GATT_INVALID_HANDLE ) { *pHandle = BUILD_UINT16( rsp.dataList[idx+3], rsp.dataList[idx+4] ); } } idx += rsp.len; } } // This indicates that there is no more characteristic data // to be discovered within the given handle range. else if ( pPkt->hdr.status == bleProcedureComplete ) { if ( serviceToDiscover == GATT_SERVICE_UUID ) { // Begin Service Discovery of HID Service serviceToDiscover = HID_SERVICE_UUID; hidappDiscoverService( connHandle, HID_SERVICE_UUID ); // Break to skip next part, it doesn't apply yet. break; } if ( enableCCCDs == TRUE ) { mouseCCCHandle = mouseCharHandle + 1; // Begin configuring the characteristics for notifications hidappEnableNotification( connHandle, mouseCCCHandle ); } else { serviceDiscComplete = TRUE; } } break; case ATT_WRITE_RSP: if ( pPkt->hdr.status == SUCCESS && !serviceDiscComplete ) { uint16 handle = ( keyCCCHandle == GATT_INVALID_HANDLE ) ? ( keyCCCHandle = keyCharHandle + 1 ) : ( consumerCtrlCCCHandle = consumerCtrlCharHandle + 1 ); hidappEnableNotification( connHandle, handle ); if ( consumerCtrlCCCHandle != GATT_INVALID_HANDLE) { serviceDiscComplete = TRUE; } } break; // Service Change indication case ATT_HANDLE_VALUE_IND: // Note: this logic assumes that the only indications that will be sent // will come from that GATT Service Changed Characteristic if ( pPkt->hdr.status == SUCCESS ) { serviceChange = CHANGE_OCCURED; // Acknowledge receipt of indication ATT_HandleValueCfm( pPkt->connHandle ); // Handles in server have changed while devices are connected if ( ( gapBondMgrState == PAIRED_BONDED_STATE ) && ( serviceChangeHandle == pPkt->msg.handleValueInd.handle ) ) { // Begin Service Discovery of HID Service serviceToDiscover = HID_SERVICE_UUID; hidappDiscoverService( connHandle, HID_SERVICE_UUID ); serviceChange = NO_CHANGE; } } break; default: // Unknown event break; } }
/********************************************************************* * @fn GlucoseDisc * * @brief Current glucose service and characteristic discovery. * * @param state - Discovery state. * @param pMsg - GATT message. * * @return New discovery state. */ static uint8_t GlucoseDisc(uint8_t state, gattMsgEvent_t *pMsg) { uint8_t newState = state; switch (state) { case DISC_GLUCOSE_START: { uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(GLUCOSE_SERV_UUID), HI_UINT16(GLUCOSE_SERV_UUID) }; // Initialize service discovery variables glucoseSvcStartHdl = glucoseSvcEndHdl = 0; glucoseEndHdlIdx = 0; // Discover service by UUID GATT_DiscPrimaryServiceByUUID(glucCollConnHandle, uuid, ATT_BT_UUID_SIZE, glucCollTaskId); newState = DISC_GLUCOSE_SVC; } break; case DISC_GLUCOSE_SVC: // Service found, store handles if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP && pMsg->msg.findByTypeValueRsp.numInfo > 0) { glucoseSvcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0); glucoseSvcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0); } // If procedure complete if ((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP)) { // If service found if (glucoseSvcStartHdl != 0) { // Discover all characteristics GATT_DiscAllChars(glucCollConnHandle, glucoseSvcStartHdl, glucoseSvcEndHdl, glucCollTaskId); newState = DISC_GLUCOSE_CHAR; } else { // Service not found newState = DISC_FAILED; } } break; case DISC_GLUCOSE_CHAR: { uint8_t i; uint8_t *p; uint16_t handle; uint16_t uuid; // Characteristics found if (pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->msg.readByTypeRsp.numPairs > 0 && pMsg->msg.readByTypeRsp.len == CHAR_DESC_HDL_UUID16_LEN) { // For each characteristic declaration p = pMsg->msg.readByTypeRsp.pDataList; for (i = pMsg->msg.readByTypeRsp.numPairs; i > 0; i--) { // Parse characteristic declaration handle = BUILD_UINT16(p[3], p[4]); uuid = BUILD_UINT16(p[5], p[6]); // If looking for end handle if (glucoseEndHdlIdx != 0) { // End handle is one less than handle of characteristic declaration glucoseHdlCache[glucoseEndHdlIdx] = BUILD_UINT16(p[0], p[1]) - 1; glucoseEndHdlIdx = 0; } // If UUID is of interest, store handle switch (uuid) { case GLUCOSE_MEAS_UUID: glucoseHdlCache[HDL_GLUCOSE_START] = handle; glucoseEndHdlIdx = HDL_GLUCOSE_END; break; case GLUCOSE_CONTEXT_UUID: glucoseHdlCache[HDL_GLUCOSE_CONTEXT_START] = handle; glucoseEndHdlIdx = HDL_GLUCOSE_CONTEXT_END; break; case RECORD_CTRL_PT_UUID: glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_START] = handle; glucoseEndHdlIdx = HDL_GLUCOSE_CTL_PNT_END; break; case GLUCOSE_FEATURE_UUID: glucoseHdlCache[HDL_GLUCOSE_FEATURE] = handle; break; default: break; } p += CHAR_DESC_HDL_UUID16_LEN; } } // If procedure complete if ((pMsg->method == ATT_READ_BY_TYPE_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP)) { // Special case of end handle at end of service if (glucoseEndHdlIdx != 0) { glucoseHdlCache[glucoseEndHdlIdx] = glucoseSvcEndHdl; glucoseEndHdlIdx = 0; } // If didn't find glucose characteristic if (glucoseHdlCache[HDL_GLUCOSE_START] == 0) { newState = DISC_FAILED; } else if (glucoseHdlCache[HDL_GLUCOSE_START] < glucoseHdlCache[HDL_GLUCOSE_END]) { // Discover characteristic descriptors GATT_DiscAllCharDescs(glucCollConnHandle, glucoseHdlCache[HDL_GLUCOSE_START] + 1, glucoseHdlCache[HDL_GLUCOSE_END], glucCollTaskId); newState = DISC_GLUCOSE_CCCD; } else { newState = DISC_IDLE; } } } break; case DISC_GLUCOSE_CCCD: { uint8_t i; // Characteristic descriptors found if (pMsg->method == ATT_FIND_INFO_RSP && pMsg->msg.findInfoRsp.numInfo > 0 && pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE) { attFindInfoRsp_t *pRsp = &(pMsg->msg.findInfoRsp); // For each handle/uuid pair for (i = 0; i < pRsp->numInfo; i++) { // Look for CCCD if ( ATT_BT_PAIR_UUID( pRsp->pInfo, i ) == GATT_CLIENT_CHAR_CFG_UUID ) { // CCCD found glucoseHdlCache[HDL_GLUCOSE_MEAS_CCCD] = ATT_BT_PAIR_HANDLE( pRsp->pInfo, i ); break; } } } // If procedure complete if ((pMsg->method == ATT_FIND_INFO_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP)) { // If CCCD found if (glucoseHdlCache[HDL_GLUCOSE_MEAS_CCCD] != 0) { // Should we look for unread category status CCCD if (glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_START] < glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_END]) { // Discover unread category status characteristic descriptors GATT_DiscAllCharDescs(glucCollConnHandle, glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_START] + 1, glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_END], glucCollTaskId); newState = DISC_GLUCOSE_CTL_PNT_CCCD; } else { // Missing required characteristic newState = DISC_FAILED; } } else { // Missing required characteristic descriptor glucoseHdlCache[HDL_GLUCOSE_MEAS_CCCD] = 0; newState = DISC_FAILED; } } } break; case DISC_GLUCOSE_CTL_PNT_CCCD: { uint8_t i; // Characteristic descriptors found if (pMsg->method == ATT_FIND_INFO_RSP && pMsg->msg.findInfoRsp.numInfo > 0 && pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE) { attFindInfoRsp_t *pRsp = &(pMsg->msg.findInfoRsp); // For each handle/uuid pair for (i = 0; i < pRsp->numInfo; i++) { // Look for CCCD if ( ATT_BT_PAIR_UUID( pRsp->pInfo, i ) == GATT_CLIENT_CHAR_CFG_UUID ) { // CCCD found glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_CCCD] = ATT_BT_PAIR_HANDLE( pRsp->pInfo, i ); break; } } } // If procedure complete if ((pMsg->method == ATT_FIND_INFO_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP)) { // If CCCD found if (glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_CCCD] != 0) { // Should we look for unread category status CCCD if (glucoseHdlCache[HDL_GLUCOSE_CONTEXT_START] < glucoseHdlCache[HDL_GLUCOSE_CONTEXT_END]) { // Discover unread category status characteristic descriptors GATT_DiscAllCharDescs(glucCollConnHandle, glucoseHdlCache[HDL_GLUCOSE_CONTEXT_START] + 1, glucoseHdlCache[HDL_GLUCOSE_CONTEXT_END], glucCollTaskId); newState = DISC_GLUCOSE_CONTEXT_CCCD; } else { // Done newState = DISC_IDLE; } } else { // Missing required characteristic descriptor glucoseHdlCache[HDL_GLUCOSE_CTL_PNT_CCCD] = 0; newState = DISC_FAILED; } } } break; case DISC_GLUCOSE_CONTEXT_CCCD: { uint8_t i; // Characteristic descriptors found if (pMsg->method == ATT_FIND_INFO_RSP && pMsg->msg.findInfoRsp.numInfo > 0 && pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE) { attFindInfoRsp_t *pRsp = &(pMsg->msg.findInfoRsp); // For each handle/uuid pair for (i = 0; i < pRsp->numInfo; i++) { // Look for CCCD if ( ATT_BT_PAIR_UUID( pRsp->pInfo, i ) == GATT_CLIENT_CHAR_CFG_UUID ) { // CCCD found glucoseHdlCache[HDL_GLUCOSE_CONTEXT_CCCD] = ATT_BT_PAIR_HANDLE( pRsp->pInfo, i ); break; } } } // If procedure complete if ((pMsg->method == ATT_FIND_INFO_RSP && pMsg->hdr.status == bleProcedureComplete) || (pMsg->method == ATT_ERROR_RSP)) { newState = DISC_IDLE; } } break; default: break; } return newState; }
/****************************************************************************** * @fn OTA_WriteHeader * * @brief Writes the OTA header to the output buffer. * * @param pHdr - pointer to the header information * @param pHdr - pointer to the output buffer * * @return none */ uint8 *OTA_WriteHeader(OTA_ImageHeader_t *pHdr, uint8 *pBuf) { uint8 i; // Output the Magic Number // osal_buffer_uint32 not available under windows //pBuf = osal_buffer_uint32(pBuf, pHdr->magicNumber); *pBuf++ = BREAK_UINT32(pHdr->magicNumber, 0); *pBuf++ = BREAK_UINT32(pHdr->magicNumber, 1); *pBuf++ = BREAK_UINT32(pHdr->magicNumber, 2); *pBuf++ = BREAK_UINT32(pHdr->magicNumber, 3); // Output the Header Version *pBuf++ = LO_UINT16(pHdr->headerVersion); *pBuf++ = HI_UINT16(pHdr->headerVersion); // Output the Header Length *pBuf++ = LO_UINT16(pHdr->headerLength); *pBuf++ = HI_UINT16(pHdr->headerLength); // Output the Field Control *pBuf++ = LO_UINT16(pHdr->fieldControl); *pBuf++ = HI_UINT16(pHdr->fieldControl); // Output the Manufacturer ID *pBuf++ = LO_UINT16(pHdr->fileId.manufacturer); *pBuf++ = HI_UINT16(pHdr->fileId.manufacturer); // Output the Image Type *pBuf++ = LO_UINT16(pHdr->fileId.type); *pBuf++ = HI_UINT16(pHdr->fileId.type); // Output the File Version // osal_buffer_uint32 not available under windows //pBuf = osal_buffer_uint32(pBuf, pHdr->fileId.version); *pBuf++ = BREAK_UINT32(pHdr->fileId.version, 0); *pBuf++ = BREAK_UINT32(pHdr->fileId.version, 1); *pBuf++ = BREAK_UINT32(pHdr->fileId.version, 2); *pBuf++ = BREAK_UINT32(pHdr->fileId.version, 3); // Output the Stack Version *pBuf++ = LO_UINT16(pHdr->stackVersion); *pBuf++ = HI_UINT16(pHdr->stackVersion); // Output the Header string for (i=0; i<OTA_HEADER_STR_LEN; i++) { *pBuf++ = pHdr->headerString[i]; } // Output the Image Size // osal_buffer_uint32 not available under windows //pBuf = osal_buffer_uint32(pBuf, pHdr->imageSize); *pBuf++ = BREAK_UINT32(pHdr->imageSize, 0); *pBuf++ = BREAK_UINT32(pHdr->imageSize, 1); *pBuf++ = BREAK_UINT32(pHdr->imageSize, 2); *pBuf++ = BREAK_UINT32(pHdr->imageSize, 3); // Output the Security Credential Version if (pHdr->fieldControl & OTA_FC_SCV_PRESENT) { *pBuf++ = pHdr->secCredentialVer; } // Output the Upgrade File Destination if (pHdr->fieldControl & OTA_FC_DSF_PRESENT) { for (i=0; i<Z_EXTADDR_LEN; i++) { *pBuf++ = pHdr->destIEEE[i]; } } // Output the Min and Max Hardware Versions if (pHdr->fieldControl & OTA_FC_HWV_PRESENT) { *pBuf++ = LO_UINT16(pHdr->minHwVer); *pBuf++ = HI_UINT16(pHdr->minHwVer); *pBuf++ = LO_UINT16(pHdr->maxHwVer); *pBuf++ = HI_UINT16(pHdr->maxHwVer); } return pBuf; }
/** \brief Processes the \ref GET_STATUS request (returns status for the specified recipient) * * The recipient bits in \ref USB_SETUP_HEADER.requestType specify the desired recipient. This is either the * (one and only) device, a specific interface, or a specific endpoint. Some of the status bits can be * changed with the SET_FEATURE and CLEAR_FEATURE requests. * * <b>Parameters</b>: * - VALUE: Always 0 * - INDEX: Depends upon the recipient: * - DEVICE: Always 0 * - INTERFACE: Interface number * - ENDPOINT: Endpoint address * - LENGTH: Always 2 * * <b>Data (IN)</b>: * Depends upon the recipient (the bit field illustrations are MSB first, LSB last): * - DEVICE: <tt>00000000.000000RS</tt>, where R(1) = DEVICE_REMOTE_WAKEUP and S(0) = SELF_POWERED * - INTERFACE: <tt>00000000.00000000</tt> (all bits are reserved) * - ENDPOINT: <tt>00000000.0000000H</tt>, where H(0) = ENDPOINT_HALT */ void usbsrGetStatus(void) { uint8 endpoint; static uint16 __xdata status; // Common sanity check if (usbSetupHeader.value || HI_UINT16(usbSetupHeader.index) || (usbSetupHeader.length != 2)) { usbfwData.ep0Status = EP_STALL; // Return status for device, interface, or endpoint } else { switch (usbSetupHeader.requestType) { // Device status: // Bit 0: Self powered // Bit 1: Remote wake-up allowed case RT_IN_DEVICE: // Sanity check if (LO_UINT16(usbSetupHeader.index)) { usbfwData.ep0Status = EP_STALL; // Get the bit values from the USBFW_DATA struct } else { // Self powered? status = usbfwData.selfPowered ? 0x0001 : 0x0000; // Remote wakeup? if (usbfwData.remoteWakeup) status |= 0x0002; } break; // Interface status: // All bits are reserved case RT_IN_INTERFACE: // Sanity check if (usbfwData.usbState != DEV_CONFIGURED) { usbfwData.ep0Status = EP_STALL; } else { status = 0x0000; } break; // Endpoint status: // Bit 0: Endpoint halted case RT_IN_ENDPOINT: endpoint = LO_UINT16(usbSetupHeader.index) & 0x7F; // Sanity check if ((usbfwData.usbState != DEV_CONFIGURED) || (endpoint > 5)) { usbfwData.ep0Status = EP_STALL; // Translate endpoint address to status index and return the status } else { // IN if (LO_UINT16(usbSetupHeader.index) & 0x80) { status = (usbfwData.pEpInStatus[endpoint - 1] == EP_HALT) ? 0x0001 : 0x0000; // OUT } else { status = (usbfwData.pEpOutStatus[endpoint - 1] == EP_HALT) ? 0x0001 : 0x0000; } } break; default: usbfwData.ep0Status = EP_STALL; break; } if (usbfwData.ep0Status != EP_STALL) { // Send it usbSetupData.pBuffer = (uint8 __xdata *)&status; usbSetupData.bytesLeft = 2; usbfwData.ep0Status = EP_TX; } } } // usbsrGetStatus
#define BATT_ADC_LEVEL_2V 273 #define BATT_LEVEL_VALUE_IDX 2 // Position of battery level in attribute array #define BATT_LEVEL_VALUE_CCCD_IDX 3 // Position of battery level CCCD in attribute array /********************************************************************* * TYPEDEFS */ /********************************************************************* * GLOBAL VARIABLES */ // Battery service CONST uint8 battServUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(BATT_SERVICE_UUID), HI_UINT16(BATT_SERVICE_UUID) }; // Battery level characteristic CONST uint8 battLevelUUID[ATT_BT_UUID_SIZE] = { LO_UINT16(BATT_LEVEL_UUID), HI_UINT16(BATT_LEVEL_UUID) }; /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */