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.

CCS/BLE-STACK: Advertising with delays.

Part Number: BLE-STACK

Tool/software: Code Composer Studio

Hello, 

I am working on project_zero example, I have updated my DEFAULT_ADVERTISING_INTERVAL to 16000 (i.e. 10 seconds). and advertising mode is General. 

Basically I want some delay in advertising that is the device should advertise for 10sec then stop advertising then again advertise for 10sec.

The result which I observe is, the device is advertising continuously. So I am not getting the significance of that DEFAULT_ADVERTISING_INTERVAL , it is doing nothing.

So is there any way by which I can achieve that feature. (Limited mode is not suitable for my application as it stops advertising after 30sec)

I want periodic advertising with delay of 10sec or 20sec. How can I achieve this ? 

  • Dear Chaitanya,

    As in your example. I modified my DEFAULT_ADVERTISING_INTERVAL to 1600 (10s). Following the documentation from Debugging RF Output

    I mapped two different pins the RX and TX data. Viewing this info on a logic analyzer we can see that Adv events are happening every 10s. 

    So this is the purpose of the DEFAULT_ADVERTISING_INTERVAL. 

    If you wish to turn on advertisements for some period, then turn it off for some period. This will need to be done in your application. Take a look at this E2E post that shows how to do it. https://e2e.ti.com/support/wireless-connectivity/bluetooth/f/538/t/457269

    DEFAULT_ADVERTISING_INTERVAL is only meant to be the time between advertisements. Not between advertisement periods.

    I hope this helps!

    Best,

    Kris

  • /******************************************************************************
    
     @file  simple_peripheral.c
    
     @brief This file contains the Simple BLE Peripheral sample application for use
            with the CC2650 Bluetooth Low Energy Protocol Stack.
    
     Group: WCS, BTS
     Target Device: CC2650, CC2640, CC1350
    
     ******************************************************************************
     
     Copyright (c) 2013-2016, 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.
    
     ******************************************************************************
     Release Name: ble_sdk_2_02_01_18
     Release Date: 2016-10-26 15:20:04
     *****************************************************************************/
    
    /*********************************************************************
     * INCLUDES
     */
    #include <string.h>
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Queue.h>
    
    //------------------------------------------------------------------------------------------
    #include <ti/sysbios/hal/Seconds.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ti/drivers/PIN.h>
    #include <ti/drivers/GPIO.h>
    #include <ti/mw/display/Display.h>
    #include<xdc/runtime/System.h>
    //-------------------------------------------------------------------------------------
    
    #include "hci_tl.h"
    #include "gatt.h"
    #include "linkdb.h"
    #include "gapgattserver.h"
    #include "gattservapp.h"
    #include "devinfoservice.h"
    #include "simple_gatt_profile.h"
    
    #if defined(FEATURE_OAD) || defined(IMAGE_INVALIDATE)
    #include "oad_target.h"
    #include "oad.h"
    #endif //FEATURE_OAD || IMAGE_INVALIDATE
    
    #include "peripheral.h"
    #include "gapbondmgr.h"
    
    #include "osal_snv.h"
    #include "icall_apimsg.h"
    
    #include "util.h"
    
    #ifdef USE_RCOSC
    #include "rcosc_calibration.h"
    #endif //USE_RCOSC
    
    #include <ti/mw/display/Display.h>
    #include "board_key.h"
    
    #include "board.h"
    
    #include "simple_peripheral.h"
    
    #if defined( USE_FPGA ) || defined( DEBUG_SW_TRACE )
    #include <driverlib/ioc.h>
    #endif // USE_FPGA | DEBUG_SW_TRACE
    
    /*********************************************************************
     * CONSTANTS
     */
    
    // Advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL          160
    
    // Limited discoverable mode advertises for 30.72s, and then stops
    // General discoverable mode advertises indefinitely
    #define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
    
    #ifndef FEATURE_OAD
    // Minimum connection interval (units of 1.25ms, 80=100ms) if automatic
    // parameter update request is enabled
    #define DEFAULT_DESIRED_MIN_CONN_INTERVAL     80
    
    // Maximum connection interval (units of 1.25ms, 800=1000ms) if automatic
    // parameter update request is enabled
    #define DEFAULT_DESIRED_MAX_CONN_INTERVAL     800
    #else //!FEATURE_OAD
    // Minimum connection interval (units of 1.25ms, 8=10ms) if automatic
    // parameter update request is enabled
    #define DEFAULT_DESIRED_MIN_CONN_INTERVAL     8
    
    // Maximum connection interval (units of 1.25ms, 8=10ms) if automatic
    // parameter update request is enabled
    #define DEFAULT_DESIRED_MAX_CONN_INTERVAL     8
    #endif // FEATURE_OAD
    
    // Slave latency to use if automatic parameter update request is enabled
    #define DEFAULT_DESIRED_SLAVE_LATENCY         0
    
    // Supervision timeout value (units of 10ms, 1000=10s) if automatic parameter
    // update request is enabled
    #define DEFAULT_DESIRED_CONN_TIMEOUT          1000
    
    // Whether to enable automatic parameter update request when a connection is
    // formed
    #define DEFAULT_ENABLE_UPDATE_REQUEST         TRUE
    
    // Connection Pause Peripheral time value (in seconds)
    #define DEFAULT_CONN_PAUSE_PERIPHERAL         6
    
    // How often to perform periodic event (in msec)
    #define SBP_PERIODIC_EVT_PERIOD               5000
    
    #ifdef FEATURE_OAD
    // The size of an OAD packet.
    #define OAD_PACKET_SIZE                       ((OAD_BLOCK_SIZE) + 2)
    #endif // FEATURE_OAD
    
    // Task configuration
    #define SBP_TASK_PRIORITY                     1
    
    
    #ifndef SBP_TASK_STACK_SIZE
    #define SBP_TASK_STACK_SIZE                   644
    #endif
    
    // Internal Events for RTOS application
    #define SBP_STATE_CHANGE_EVT                  0x0001
    #define SBP_CHAR_CHANGE_EVT                   0x0002
    #define SBP_PERIODIC_EVT                      0x0004
    #define SBP_CONN_EVT_END_EVT                  0x0008
    
    uint8_t initialAdvertEnable = FALSE;
    
    /*********************************************************************
     * TYPEDEFS
     */
    
    // App event passed from profiles.
    typedef struct
    {
      appEvtHdr_t hdr;  // event header.
    } sbpEvt_t;
    
    /*********************************************************************
     * GLOBAL VARIABLES
     */
    
    // Display Interface
    Display_Handle dispHandle = NULL;
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Semaphore globally used to post events to the application thread
    static ICall_Semaphore sem;
    
    // Clock instances for internal periodic events.
    static Clock_Struct periodicClock;
    
    // Queue object used for app messages
    static Queue_Struct appMsg;
    static Queue_Handle appMsgQueue;
    
    #if defined(FEATURE_OAD)
    // Event data from OAD profile.
    static Queue_Struct oadQ;
    static Queue_Handle hOadQ;
    #endif //FEATURE_OAD
    
    // events flag for internal application events.
    static uint16_t events;
    
    // Task configuration
    Task_Struct sbpTask;
    Char sbpTaskStack[SBP_TASK_STACK_SIZE];
    
    // Profile state and parameters
    //static gaprole_States_t gapProfileState = GAPROLE_INIT;
    
    // GAP - SCAN RSP data (max size = 31 bytes)
    static uint8_t scanRspData[] =
    {
      // complete name
      0x14,   // length of this data
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'S',
      'i',
      'm',
      'p',
      'l',
      'e',
      'B',
      'L',
      'E',
      'P',
      'e',
      'r',
      'i',
      'p',
      'h',
      'e',
      'r',
      'a',
      '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_t 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,
      DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // service UUID, to notify central devices what services are included
      // in this peripheral
    #if !defined(FEATURE_OAD) || defined(FEATURE_OAD_ONCHIP)
      0x03,   // length of this data
    #else //OAD for external flash
      0x05,  // lenght of this data
    #endif //FEATURE_OAD
      GAP_ADTYPE_16BIT_MORE,      // some of the UUID's, but not all
    #ifdef FEATURE_OAD
      LO_UINT16(OAD_SERVICE_UUID),
      HI_UINT16(OAD_SERVICE_UUID),
    #endif //FEATURE_OAD
    #ifndef FEATURE_OAD_ONCHIP
      LO_UINT16(SIMPLEPROFILE_SERV_UUID),
      HI_UINT16(SIMPLEPROFILE_SERV_UUID)
    #endif //FEATURE_OAD_ONCHIP
    };
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple BLE Peripheral";
    
    // Globals used for ATT Response retransmission
    static gattMsgEvent_t *pAttRsp = NULL;
    static uint8_t rspTxRetry = 0;
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void SimpleBLEPeripheral_init( void );
    static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1);
    
    static uint8_t SimpleBLEPeripheral_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t SimpleBLEPeripheral_processGATTMsg(gattMsgEvent_t *pMsg);
    static void SimpleBLEPeripheral_processAppMsg(sbpEvt_t *pMsg);
    static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState);
    static void SimpleBLEPeripheral_processCharValueChangeEvt(uint8_t paramID);
    static void SimpleBLEPeripheral_performPeriodicTask(void);
    static void SimpleBLEPeripheral_clockHandler(UArg arg);
    
    static void SimpleBLEPeripheral_sendAttRsp(void);
    static void SimpleBLEPeripheral_freeAttRsp(uint8_t status);
    
    static void SimpleBLEPeripheral_stateChangeCB(gaprole_States_t newState);
    #ifndef FEATURE_OAD_ONCHIP
    static void SimpleBLEPeripheral_charValueChangeCB(uint8_t paramID);
    #endif //!FEATURE_OAD_ONCHIP
    static void SimpleBLEPeripheral_enqueueMsg(uint8_t event, uint8_t state);
    
    #ifdef FEATURE_OAD
    void SimpleBLEPeripheral_processOadWriteCB(uint8_t event, uint16_t connHandle,
                                               uint8_t *pData);
    #endif //FEATURE_OAD
    
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // GAP Role Callbacks
    static gapRolesCBs_t SimpleBLEPeripheral_gapRoleCBs =
    {
      SimpleBLEPeripheral_stateChangeCB     // Profile State Change Callbacks
    };
    
    // GAP Bond Manager Callbacks
    static gapBondCBs_t simpleBLEPeripheral_BondMgrCBs =
    {
      NULL, // Passcode callback (not used by application)
      NULL  // Pairing / Bonding state Callback (not used by application)
    };
    
    // Simple GATT Profile Callbacks
    #ifndef FEATURE_OAD_ONCHIP
    static simpleProfileCBs_t SimpleBLEPeripheral_simpleProfileCBs =
    {
      SimpleBLEPeripheral_charValueChangeCB // Characteristic value change callback
    };
    #endif //!FEATURE_OAD_ONCHIP
    
    #ifdef FEATURE_OAD
    static oadTargetCBs_t simpleBLEPeripheral_oadCBs =
    {
      SimpleBLEPeripheral_processOadWriteCB // Write Callback.
    };
    #endif //FEATURE_OAD
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_createTask
     *
     * @brief   Task creation function for the Simple BLE Peripheral.
     *
     * @param   None.
     *
     * @return  None.
     */
    void SimpleBLEPeripheral_createTask(void)
    {
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = sbpTaskStack;
      taskParams.stackSize = SBP_TASK_STACK_SIZE;
      taskParams.priority = SBP_TASK_PRIORITY;
    
      Task_construct(&sbpTask, SimpleBLEPeripheral_taskFxn, &taskParams, NULL);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_init
     *
     * @brief   Called during initialization and contains application
     *          specific initialization (ie. hardware initialization/setup,
     *          table initialization, power up notification, etc), and
     *          profile initialization/setup.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_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, &sem);
    
    #ifdef USE_RCOSC
      RCOSC_enableCalibration();
    #endif // USE_RCOSC
    
    #if defined( USE_FPGA )
      // configure RF Core SMI Data Link
      IOCPortConfigureSet(IOID_12, IOC_PORT_RFC_GPO0, IOC_STD_OUTPUT);
      IOCPortConfigureSet(IOID_11, IOC_PORT_RFC_GPI0, IOC_STD_INPUT);
    
      // configure RF Core SMI Command Link
      IOCPortConfigureSet(IOID_10, IOC_IOCFG0_PORT_ID_RFC_SMI_CL_OUT, IOC_STD_OUTPUT);
      IOCPortConfigureSet(IOID_9, IOC_IOCFG0_PORT_ID_RFC_SMI_CL_IN, IOC_STD_INPUT);
    
      // configure RF Core tracer IO
      IOCPortConfigureSet(IOID_8, IOC_PORT_RFC_TRC, IOC_STD_OUTPUT);
    #else // !USE_FPGA
      #if defined( DEBUG_SW_TRACE )
        // configure RF Core tracer IO
        IOCPortConfigureSet(IOID_8, IOC_PORT_RFC_TRC, IOC_STD_OUTPUT | IOC_CURRENT_4MA | IOC_SLEW_ENABLE);
      #endif // DEBUG_SW_TRACE
    #endif // USE_FPGA
    
      // Create an RTOS queue for message from profile to be sent to app.
      appMsgQueue = Util_constructQueue(&appMsg);
    
      // Create one-shot clocks for internal periodic events.
      Util_constructClock(&periodicClock, SimpleBLEPeripheral_clockHandler,
                          SBP_PERIODIC_EVT_PERIOD, 0, false, SBP_PERIODIC_EVT);
    
      dispHandle = Display_open(Display_Type_LCD, NULL);
    
      // Setup the GAP
      GAP_SetParamValue(TGAP_CONN_PAUSE_PERIPHERAL, DEFAULT_CONN_PAUSE_PERIPHERAL);
    
      // Setup the GAP Peripheral Role Profile
      {
        // For all hardware platforms, device starts advertising upon initialization
      //  uint8_t initialAdvertEnable = FALSE;
    
        // By setting this to zero, the device will go into the waiting state after
        // being discoverable for 30.72 second, and will not being advertising again
        // until the enabler is set back to TRUE
        uint16_t advertOffTime = 0;
    
        uint8_t enableUpdateRequest = DEFAULT_ENABLE_UPDATE_REQUEST;
        uint16_t desiredMinInterval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
        uint16_t desiredMaxInterval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
        uint16_t desiredSlaveLatency = DEFAULT_DESIRED_SLAVE_LATENCY;
        uint16_t desiredConnTimeout = DEFAULT_DESIRED_CONN_TIMEOUT;
    
        // Set the GAP Role Parameters
        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                             &initialAdvertEnable);
        GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
                             &advertOffTime);
    
        GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData),
                             scanRspData);
        GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
    
        GAPRole_SetParameter(GAPROLE_PARAM_UPDATE_ENABLE, sizeof(uint8_t),
                             &enableUpdateRequest);
        GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t),
                             &desiredMinInterval);
        GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t),
                             &desiredMaxInterval);
        GAPRole_SetParameter(GAPROLE_SLAVE_LATENCY, sizeof(uint16_t),
                             &desiredSlaveLatency);
        GAPRole_SetParameter(GAPROLE_TIMEOUT_MULTIPLIER, sizeof(uint16_t),
                             &desiredConnTimeout);
      }
    
      // Set the GAP Characteristics
      GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
      // Set advertising interval
      {
        uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
    
        GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt);
        GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt);
      }
    
      // Setup the GAP Bond Manager
      {
        uint32_t passkey = 0; // passkey "000000"
        uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        uint8_t mitm = TRUE;
        uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
        uint8_t bonding = TRUE;
    
        GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t),
                                &passkey);
        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);
      }
    
       // Initialize GATT attributes
      GGS_AddService(GATT_ALL_SERVICES);           // GAP
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
      DevInfo_AddService();                        // Device Information Service
    
    #ifndef FEATURE_OAD_ONCHIP
      SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
    #endif //!FEATURE_OAD_ONCHIP
    
    #ifdef FEATURE_OAD
      VOID OAD_addService();                 // OAD Profile
      OAD_register((oadTargetCBs_t *)&simpleBLEPeripheral_oadCBs);
      hOadQ = Util_constructQueue(&oadQ);
    #endif //FEATURE_OAD
    
    #ifdef IMAGE_INVALIDATE
      Reset_addService();
    #endif //IMAGE_INVALIDATE
    
    
    #ifndef FEATURE_OAD_ONCHIP
      // Setup the SimpleProfile Characteristic Values
      {
        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);
      }
    
      // Register callback with SimpleGATTprofile
      SimpleProfile_RegisterAppCBs(&SimpleBLEPeripheral_simpleProfileCBs);
    #endif //!FEATURE_OAD_ONCHIP
    
      // Start the Device
      VOID GAPRole_StartDevice(&SimpleBLEPeripheral_gapRoleCBs);
    
      // Start Bond Manager
      VOID GAPBondMgr_Register(&simpleBLEPeripheral_BondMgrCBs);
    
      // Register with GAP for HCI/Host messages
      GAP_RegisterForMsgs(selfEntity);
    
      // Register for GATT local events and ATT Responses pending for transmission
      GATT_RegisterForMsgs(selfEntity);
    
    //  HCI_LE_ReadMaxDataLenCmd();
      Util_startClock(&periodicClock);
    
    #if defined FEATURE_OAD
    #if defined (HAL_IMAGE_A)
      Display_print0(dispHandle, 0, 0, "BLE Peripheral A");
    #else
      Display_print0(dispHandle, 0, 0, "BLE Peripheral B");
    #endif // HAL_IMAGE_A
    #else
      Display_print0(dispHandle, 0, 0, "BLE Peripheral");
    #endif // FEATURE_OAD
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_taskFxn
     *
     * @brief   Application task entry point for the Simple BLE Peripheral.
     *
     * @param   a0, a1 - not used.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      SimpleBLEPeripheral_init();
    
      // Application main loop
      for (;;)
      {
        // Waits for a signal to the semaphore associated with the calling thread.
        // Note that the semaphore associated with a thread is signaled when a
        // message is queued to the message receive queue of the thread or when
        // ICall_signal() function is called onto the semaphore.
        ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER);
    
        if (errno == ICALL_ERRNO_SUCCESS)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          if (ICall_fetchServiceMsg(&src, &dest,
                                    (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
          {
            uint8 safeToDealloc = TRUE;
    
            if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
            {
              ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
    
              // Check for BLE stack events first
              if (pEvt->signature == 0xffff)
              {
                if (pEvt->event_flag & SBP_CONN_EVT_END_EVT)
                {
                  // Try to retransmit pending ATT Response (if any)
                  SimpleBLEPeripheral_sendAttRsp();
                }
              }
              else
              {
                // Process inter-task message
                safeToDealloc = SimpleBLEPeripheral_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // If RTOS queue is not empty, process app message.
          while (!Queue_empty(appMsgQueue))
          {
            sbpEvt_t *pMsg = (sbpEvt_t *)Util_dequeueMsg(appMsgQueue);
            if (pMsg)
            {
              // Process message.
              SimpleBLEPeripheral_processAppMsg(pMsg);
    
              // Free the space from the message.
              ICall_free(pMsg);
            }
          }
        }
    
        if (events & SBP_PERIODIC_EVT)
        {
          events &= ~SBP_PERIODIC_EVT;
    
          Util_startClock(&periodicClock);
    
    
               initialAdvertEnable ^= 1; //ZH
    
               GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initialAdvertEnable);
    
    
             }
    
          // Perform periodic application task
         // SimpleBLEPeripheral_performPeriodicTask();
        }
    
    #ifdef FEATURE_OAD
        while (!Queue_empty(hOadQ))
        {
          oadTargetWrite_t *oadWriteEvt = Queue_get(hOadQ);
    
          // Identify new image.
          if (oadWriteEvt->event == OAD_WRITE_IDENTIFY_REQ)
          {
            OAD_imgIdentifyWrite(oadWriteEvt->connHandle, oadWriteEvt->pData);
          }
          // Write a next block request.
          else if (oadWriteEvt->event == OAD_WRITE_BLOCK_REQ)
          {
            OAD_imgBlockWrite(oadWriteEvt->connHandle, oadWriteEvt->pData);
          }
    
          // Free buffer.
          ICall_free(oadWriteEvt);
        }
    #endif //FEATURE_OAD
      }
    
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_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 SimpleBLEPeripheral_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = SimpleBLEPeripheral_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 Event
                break;
    
              default:
                break;
            }
          }
          break;
    
        default:
          // do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processGATTMsg
     *
     * @brief   Process GATT messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t SimpleBLEPeripheral_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      // See if GATT server was unable to transmit an ATT response
      if (pMsg->hdr.status == blePending)
      {
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if (HCI_EXT_ConnEventNoticeCmd(pMsg->connHandle, selfEntity,
                                       SBP_CONN_EVT_END_EVT) == SUCCESS)
        {
          // First free any pending response
          SimpleBLEPeripheral_freeAttRsp(FAILURE);
    
          // Hold on to the response message for retransmission
          pAttRsp = pMsg;
    
          // Don't free the response message yet
          return (FALSE);
        }
      }
      else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
      {
        // ATT request-response or indication-confirmation flow control is
        // violated. All subsequent ATT requests or indications will be dropped.
        // The app is informed in case it wants to drop the connection.
    
        // Display the opcode of the message that caused the violation.
        Display_print1(dispHandle, 5, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
        Display_print1(dispHandle, 5, 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      SimpleBLEPeripheral_sendAttRsp
     *
     * @brief   Send a pending ATT response message.
     *
     * @param   none
     *
     * @return  none
     */
    static void SimpleBLEPeripheral_sendAttRsp(void)
    {
      // See if there's a pending ATT Response to be transmitted
      if (pAttRsp != NULL)
      {
        uint8_t status;
    
        // Increment retransmission count
        rspTxRetry++;
    
        // Try to retransmit ATT response till either we're successful or
        // the ATT Client times out (after 30s) and drops the connection.
        status = GATT_SendRsp(pAttRsp->connHandle, pAttRsp->method, &(pAttRsp->msg));
        if ((status != blePending) && (status != MSG_BUFFER_NOT_AVAIL))
        {
          // Disable connection event end notice
          HCI_EXT_ConnEventNoticeCmd(pAttRsp->connHandle, selfEntity, 0);
    
          // We're done with the response message
          SimpleBLEPeripheral_freeAttRsp(status);
        }
        else
        {
          // Continue retrying
          Display_print1(dispHandle, 5, 0, "Rsp send retry: %d", rspTxRetry);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_freeAttRsp
     *
     * @brief   Free ATT response message.
     *
     * @param   status - response transmit status
     *
     * @return  none
     */
    static void SimpleBLEPeripheral_freeAttRsp(uint8_t status)
    {
      // See if there's a pending ATT response message
      if (pAttRsp != NULL)
      {
        // See if the response was sent out successfully
        if (status == SUCCESS)
        {
          Display_print1(dispHandle, 5, 0, "Rsp sent retry: %d", rspTxRetry);
        }
        else
        {
          // Free response payload
          GATT_bm_free(&pAttRsp->msg, pAttRsp->method);
    
          Display_print1(dispHandle, 5, 0, "Rsp retry failed: %d", rspTxRetry);
        }
    
        // Free response message
        ICall_freeMsg(pAttRsp);
    
        // Reset our globals
        pAttRsp = NULL;
        rspTxRetry = 0;
      }
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processAppMsg
     *
     * @brief   Process an incoming callback from a profile.
     *
     * @param   pMsg - message to process
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_processAppMsg(sbpEvt_t *pMsg)
    {
      switch (pMsg->hdr.event)
      {
        case SBP_STATE_CHANGE_EVT:
          SimpleBLEPeripheral_processStateChangeEvt((gaprole_States_t)pMsg->
                                                    hdr.state);
          break;
    
        case SBP_CHAR_CHANGE_EVT:
          SimpleBLEPeripheral_processCharValueChangeEvt(pMsg->hdr.state);
          break;
    
        default:
          // Do nothing.
          break;
      }
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_stateChangeCB
     *
     * @brief   Callback from GAP Role indicating a role state change.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_stateChangeCB(gaprole_States_t newState)
    {
      SimpleBLEPeripheral_enqueueMsg(SBP_STATE_CHANGE_EVT, newState);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processStateChangeEvt
     *
     * @brief   Process a pending GAP Role state change event.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState)
    {
    #ifdef PLUS_BROADCASTER
      static bool firstConnFlag = false;
    #endif // PLUS_BROADCASTER
    
      switch ( newState )
      {
        case GAPROLE_STARTED:
          {
            uint8_t ownAddress[B_ADDR_LEN];
            uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
    
            GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);
    
            // use 6 bytes of device address for 8 bytes of system ID value
            systemId[0] = ownAddress[0];
            systemId[1] = ownAddress[1];
            systemId[2] = ownAddress[2];
    
            // set middle bytes to zero
            systemId[4] = 0x00;
            systemId[3] = 0x00;
    
            // shift three bytes up
            systemId[7] = ownAddress[5];
            systemId[6] = ownAddress[4];
            systemId[5] = ownAddress[3];
    
            DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
    
            // Display device address
            Display_print0(dispHandle, 1, 0, Util_convertBdAddr2Str(ownAddress));
            Display_print0(dispHandle, 2, 0, "Initialized");
          }
          break;
    
        case GAPROLE_ADVERTISING:
          Display_print0(dispHandle, 2, 0, "Advertising");
          break;
    
    #ifdef PLUS_BROADCASTER
        /* After a connection is dropped a device in PLUS_BROADCASTER 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.
         */
        case GAPROLE_ADVERTISING_NONCONN:
          {
            uint8_t advertEnabled = FALSE;
    
            // Disable non-connectable advertising.
            GAPRole_SetParameter(GAPROLE_ADV_NONCONN_ENABLED, sizeof(uint8_t),
                               &advertEnabled);
    
            advertEnabled = TRUE;
    
            // Enabled connectable advertising.
            GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                                 &advertEnabled);
    
            // Reset flag for next connection.
            firstConnFlag = false;
    
            SimpleBLEPeripheral_freeAttRsp(bleNotConnected);
          }
          break;
    #endif //PLUS_BROADCASTER
    
        case GAPROLE_CONNECTED:
          {
            linkDBInfo_t linkInfo;
            uint8_t numActive = 0;
    
            Util_startClock(&periodicClock);
    
            numActive = linkDB_NumActive();
    
            // Use numActive to determine the connection handle of the last
            // connection
            if ( linkDB_GetInfo( numActive - 1, &linkInfo ) == SUCCESS )
            {
              Display_print1(dispHandle, 2, 0, "Num Conns: %d", (uint16_t)numActive);
              Display_print0(dispHandle, 3, 0, Util_convertBdAddr2Str(linkInfo.addr));
            }
            else
            {
              uint8_t peerAddress[B_ADDR_LEN];
    
              GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);
    
              Display_print0(dispHandle, 2, 0, "Connected");
              Display_print0(dispHandle, 3, 0, Util_convertBdAddr2Str(peerAddress));
            }
    
            #ifdef PLUS_BROADCASTER
              // 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.
              if (firstConnFlag == false)
              {
                uint8_t advertEnabled = FALSE; // Turn on Advertising
    
                // Disable connectable advertising.
                GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                                     &advertEnabled);
    
                // Set to true for non-connectabel advertising.
                advertEnabled = TRUE;
    
                // Enable non-connectable advertising.
                GAPRole_SetParameter(GAPROLE_ADV_NONCONN_ENABLED, sizeof(uint8_t),
                                     &advertEnabled);
                firstConnFlag = true;
              }
            #endif // PLUS_BROADCASTER
          }
          break;
    
        case GAPROLE_CONNECTED_ADV:
          Display_print0(dispHandle, 2, 0, "Connected Advertising");
          break;
    
        case GAPROLE_WAITING:
          Util_stopClock(&periodicClock);
          SimpleBLEPeripheral_freeAttRsp(bleNotConnected);
    
          Display_print0(dispHandle, 2, 0, "Disconnected");
    
          // Clear remaining lines
          Display_clearLines(dispHandle, 3, 5);
          break;
    
        case GAPROLE_WAITING_AFTER_TIMEOUT:
          SimpleBLEPeripheral_freeAttRsp(bleNotConnected);
    
          Display_print0(dispHandle, 2, 0, "Timed Out");
    
          // Clear remaining lines
          Display_clearLines(dispHandle, 3, 5);
    
          #ifdef PLUS_BROADCASTER
            // Reset flag for next connection.
            firstConnFlag = false;
          #endif //#ifdef (PLUS_BROADCASTER)
          break;
    
        case GAPROLE_ERROR:
          Display_print0(dispHandle, 2, 0, "Error");
          break;
    
        default:
          Display_clearLine(dispHandle, 2);
          break;
      }
    
      // Update the state
      //gapProfileState = newState;
    }
    
    #ifndef FEATURE_OAD_ONCHIP
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_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 SimpleBLEPeripheral_charValueChangeCB(uint8_t paramID)
    {
      SimpleBLEPeripheral_enqueueMsg(SBP_CHAR_CHANGE_EVT, paramID);
    }
    #endif //!FEATURE_OAD_ONCHIP
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processCharValueChangeEvt
     *
     * @brief   Process a pending Simple Profile characteristic value change
     *          event.
     *
     * @param   paramID - parameter ID of the value that was changed.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_processCharValueChangeEvt(uint8_t paramID)
    {
    #ifndef FEATURE_OAD_ONCHIP
      uint8_t newValue;
    
      switch(paramID)
      {
        case SIMPLEPROFILE_CHAR1:
          SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);
    
          Display_print1(dispHandle, 4, 0, "Char 1: %d", (uint16_t)newValue);
          break;
    
        case SIMPLEPROFILE_CHAR3:
          SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);
    
          Display_print1(dispHandle, 4, 0, "Char 3: %d", (uint16_t)newValue);
          break;
    
        default:
          // should not reach here!
          break;
      }
    #endif //!FEATURE_OAD_ONCHIP
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_performPeriodicTask
     *
     * @brief   Perform a periodic application task. This function gets called
     *          every five seconds (SBP_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 SimpleBLEPeripheral_performPeriodicTask(void)
    {
    #ifndef FEATURE_OAD_ONCHIP
      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);
      }
    #endif //!FEATURE_OAD_ONCHIP
    }
    
    
    #ifdef FEATURE_OAD
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processOadWriteCB
     *
     * @brief   Process a write request to the OAD profile.
     *
     * @param   event      - event type:
     *                       OAD_WRITE_IDENTIFY_REQ
     *                       OAD_WRITE_BLOCK_REQ
     * @param   connHandle - the connection Handle this request is from.
     * @param   pData      - pointer to data for processing and/or storing.
     *
     * @return  None.
     */
    void SimpleBLEPeripheral_processOadWriteCB(uint8_t event, uint16_t connHandle,
                                               uint8_t *pData)
    {
      oadTargetWrite_t *oadWriteEvt = ICall_malloc( sizeof(oadTargetWrite_t) + \
                                                 sizeof(uint8_t) * OAD_PACKET_SIZE);
    
      if ( oadWriteEvt != NULL )
      {
        oadWriteEvt->event = event;
        oadWriteEvt->connHandle = connHandle;
    
        oadWriteEvt->pData = (uint8_t *)(&oadWriteEvt->pData + 1);
        memcpy(oadWriteEvt->pData, pData, OAD_PACKET_SIZE);
    
        Queue_put(hOadQ, (Queue_Elem *)oadWriteEvt);
    
        // Post the application's semaphore.
        Semaphore_post(sem);
      }
      else
      {
        // Fail silently.
      }
    }
    #endif //FEATURE_OAD
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_clockHandler
     *
     * @brief   Handler function for clock timeouts.
     *
     * @param   arg - event type
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_clockHandler(UArg arg)
    {
      // Store the event.
      events |= arg;
    
      // Wake up the application.
      Semaphore_post(sem);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_enqueueMsg
     *
     * @brief   Creates a message and puts the message in RTOS queue.
     *
     * @param   event - message event.
     * @param   state - message state.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_enqueueMsg(uint8_t event, uint8_t state)
    {
      sbpEvt_t *pMsg;
    
      // Create dynamic pointer to message.
      if ((pMsg = ICall_malloc(sizeof(sbpEvt_t))))
      {
        pMsg->hdr.event = event;
        pMsg->hdr.state = state;
    
        // Enqueue the message.
        Util_enqueueMsg(appMsgQueue, sem, (uint8*)pMsg);
      }
    }
    
    /*********************************************************************
    *********************************************************************/
    
    Hello,

    I have made the changes as per provided in your post. 

    But actually the device is advertising for 10 seconds and then stop advertising never start again until you press reset button on launchpad.

    How can I get that functionality done?

  • Chaitanya,

    May I ask how you are verifying this 10s long period? Can you redirect the RX and TX pins to the LEDs (pins 6 and 7) or if you have access to a logic analyzer then you can use like I did pins 26 and 27.

    I'm a bit confused still how you are achieving a 10s period with the modified simpleBLEPeripheral.c you provided. You are using the periodicClock to invert the initialAdvertEnable variable before it is set with GAPRole_SetParameter. Looks fine. Where I'm confused is that the periodicClock is being constructed with a 5s period. So if anything was to be happening. I would think that it would be advertising for 5 seconds only.

    Can you please identify to me how you are verifying the advertising period of 10s? From what I can tell you should have a 5 second period. What other changes have you made?

    Can you tell me the SDK that you are basing your work out of so that I can make sure I am comparing apples to apples as well please.


    Best,
    Kris
  • Hello,
    I am using ble_sdk_2_02_01_18 and ccs6.2 with tirtos_cc13xx_cc26xx_2_20_01_08 installed.

    Actually I am not receiving 10sec period nor 5sec periods. The advertising stops after some seconds and doesn't start again until reset.
    I am observing on BLEScanner app that board advertise for some random time and then never start again until reset.
    Instead it should advertise on and off with regular interval.
  • I want a feature which will advertise periodically on and off. And I want to know where to write that code i.e. area of the simple_peripheral code in which the changes should be made so that that part runs continuously without affecting other part of the code such as services and other applications made into the projects.

    For Eg. The health meters and mi bands and many other BLE device I see they advertise for  some fix seconds then stop advertising then again advertise..

    How may I achieve this feature? and it should work without affecting other part of code.

    Any exact implementation of such code will be very helpful for me.

  • Chaitanya,

    This functionality can be achieved by following the thread I posted above. 

    I've done the same and found these results.

    Here are the changes I made to

    /******************************************************************************
    
     @file  project_zero.c
    
     @brief This file contains the Project Zero sample application implementation
    
     Group: CMCU, LPRF
     Target Device: cc2640r2
    
     ******************************************************************************
     
     Copyright (c) 2013-2019, 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>
    
    //#define xdc_runtime_Log_DISABLE_ALL 1  // Add to disable logs from this file
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Event.h>
    #include <ti/sysbios/knl/Queue.h>
    #include <ti/sysbios/knl/Clock.h>
    
    #include <ti/drivers/PIN.h>
    #include <ti/display/Display.h>
    
    #include <xdc/runtime/Diags.h>
    #include <uartlog/UartLog.h>
    
    /* This Header file contains all BLE API and icall structure definition */
    #include "icall_ble_api.h"
    #include <icall.h>
    
    #include <osal_snv.h>
    #include <peripheral.h>
    #include <devinfoservice.h>
    
    #include "util.h"
    
    #include "Board.h"
    #include "project_zero.h"
    
    // Bluetooth Developer Studio services
    #include "led_service.h"
    #include "button_service.h"
    #include "data_service.h"
    
    /*********************************************************************
     * CONSTANTS
     */
    
    // Advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL          160
    
    // Limited discoverable mode advertises for 30.72s, and then stops
    // General discoverable mode advertises indefinitely
    #define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
    
    // Default pass-code used for pairing.
    #define DEFAULT_PASSCODE                      000000
    
    // Task configuration
    #define PRZ_TASK_PRIORITY                     1
    
    #ifndef PRZ_TASK_STACK_SIZE
    #define PRZ_TASK_STACK_SIZE                   800
    #endif
    
    // Internal Events for RTOS application
    #define PRZ_ICALL_EVT                         ICALL_MSG_EVENT_ID  // Event_Id_31
    #define PRZ_QUEUE_EVT                         UTIL_QUEUE_EVENT_ID // Event_Id_30
    #define PRZ_STATE_CHANGE_EVT                  Event_Id_00
    #define PRZ_CHAR_CHANGE_EVT                   Event_Id_01
    #define PRZ_PERIODIC_EVT                      Event_Id_02
    #define PRZ_APP_MSG_EVT                       Event_Id_03
    #define PRZ_ADV_TOGGLE_EVT                    Event_Id_04
    
    #define PRZ_ALL_EVENTS                       (PRZ_ICALL_EVT        | \
                                                  PRZ_QUEUE_EVT        | \
                                                  PRZ_STATE_CHANGE_EVT | \
                                                  PRZ_CHAR_CHANGE_EVT  | \
                                                  PRZ_PERIODIC_EVT     | \
                                                  PRZ_ADV_TOGGLE_EVT     | \
                                                  PRZ_APP_MSG_EVT)
    
    // Set the register cause to the registration bit-mask
    #define CONNECTION_EVENT_REGISTER_BIT_SET(registerCause) (connectionEventRegisterCauseBitMap |= registerCause )
    
    // Remove the register cause from the registration bit-mask
    #define CONNECTION_EVENT_REGISTER_BIT_REMOVE(registerCause) (connectionEventRegisterCauseBitMap &= (~registerCause) )
    
    // Gets whether the current App is registered to the receive connection events
    #define CONNECTION_EVENT_IS_REGISTERED (connectionEventRegisterCauseBitMap > 0)
    
    // Gets whether the registerCause was registered to recieve connection event
    #define CONNECTION_EVENT_REGISTRATION_CAUSE(registerCause) (connectionEventRegisterCauseBitMap & registerCause )
    
    /*********************************************************************
     * TYPEDEFS
     */
    // Types of messages that can be sent to the user application task from other
    // tasks or interrupts. Note: Messages from BLE Stack are sent differently.
    typedef enum
    {
      APP_MSG_SERVICE_WRITE = 0,   /* A characteristic value has been written     */
      APP_MSG_SERVICE_CFG,         /* A characteristic configuration has changed  */
      APP_MSG_UPDATE_CHARVAL,      /* Request from ourselves to update a value    */
      APP_MSG_GAP_STATE_CHANGE,    /* The GAP / connection state has changed      */
      APP_MSG_BUTTON_DEBOUNCED,    /* A button has been debounced with new value  */
      APP_MSG_SEND_PASSCODE,       /* A pass-code/PIN is requested during pairing */
      APP_MSG_PRZ_CONN_EVT,        /* Connection Event finished report            */
      APP_MSG_PRZ_ADV_TOGGLE_EVT,
    } app_msg_types_t;
    
    // Struct for messages sent to the application task
    typedef struct
    {
      Queue_Elem       _elem;
      app_msg_types_t  type;
      uint8_t          pdu[];
    } app_msg_t;
    
    // Struct for messages about characteristic data
    typedef struct
    {
      uint16_t svcUUID; // UUID of the service
      uint16_t dataLen; //
      uint8_t  paramID; // Index of the characteristic
      uint8_t  data[];  // Flexible array member, extended to malloc - sizeof(.)
    } char_data_t;
    
    // Struct for message about sending/requesting passcode from peer.
    typedef struct
    {
      uint16_t connHandle;
      uint8_t  uiInputs;
      uint8_t  uiOutputs;
      uint32   numComparison;
    } passcode_req_t;
    
    // Struct for message about button state
    typedef struct
    {
      PIN_Id   pinId;
      uint8_t  state;
    } button_state_t;
    
    /*********************************************************************
     * 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 application messages.
    static Queue_Struct applicationMsgQ;
    static Queue_Handle hApplicationMsgQ;
    
    // Task configuration
    Task_Struct przTask;
    Char przTaskStack[PRZ_TASK_STACK_SIZE];
    
    
    // GAP - SCAN RSP data (max size = 31 bytes)
    static uint8_t scanRspData[] =
    {
      // No scan response data provided.
      0x00 // Placeholder to keep the compiler happy.
    };
    
    // GAP - Advertisement data (max size = 31 bytes, though this is
    // best kept short to conserve power while advertisting)
    static uint8_t advertData[] =
    {
      // Flags; this sets the device to use limited discoverable
      // mode (advertises for 30 seconds at a time) or general
      // discoverable mode (advertises indefinitely), depending
      // on the DEFAULT_DISCOVERY_MODE define.
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // complete name
      16,
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'P', 'r', 'o', 'j', 'e', 'c', 't', ' ', 'Z', 'e', 'r', 'o', ' ','R','2',
    
    };
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Project Zero R2";
    
    // Globals used for ATT Response retransmission
    static gattMsgEvent_t *pAttRsp = NULL;
    static uint8_t rspTxRetry = 0;
    
    
    /* Pin driver handles */
    static PIN_Handle buttonPinHandle;
    static PIN_Handle ledPinHandle;
    
    /* Global memory storage for a PIN_Config table */
    static PIN_State buttonPinState;
    static PIN_State ledPinState;
    
    /*
     * Initial LED pin configuration table
     *   - LEDs Board_LED0 & Board_LED1 are off.
     */
    PIN_Config ledPinTable[] = {
      Board_RLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
      Board_GLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
      PIN_TERMINATE
    };
    
    /*
     * Application button pin configuration table:
     *   - Buttons interrupts are configured to trigger on falling edge.
     */
    PIN_Config buttonPinTable[] = {
        Board_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        Board_BUTTON1 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        PIN_TERMINATE
    };
    
    // Clock objects for debouncing the buttons
    static Clock_Struct button0DebounceClock;
    static Clock_Struct button1DebounceClock;
    static Clock_Struct advertToggleClock;
    
    // State of the buttons
    static uint8_t button0State = 0;
    static uint8_t button1State = 0;
    
    // Global display handle
    Display_Handle dispHandle;
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void ProjectZero_init( void );
    static void ProjectZero_taskFxn(UArg a0, UArg a1);
    
    static void user_processApplicationMessage(app_msg_t *pMsg);
    static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
    
    static void ProjectZero_sendAttRsp(void);
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
    static void ProjectZero_freeAttRsp(uint8_t status);
    
    static void ProjectZero_connEvtCB(Gap_ConnEventRpt_t *pReport);
    static void ProjectZero_processConnEvt(Gap_ConnEventRpt_t *pReport);
    
    static void user_processGapStateChangeEvt(gaprole_States_t newState);
    static void user_gapStateChangeCB(gaprole_States_t newState);
    static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                           uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison);
    static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state,
                                            uint8_t status);
    
    static void buttonDebounceSwiFxn(UArg buttonId);
    static void advertToggleClockFxn(void);
    static void user_handleButtonPress(button_state_t *pState);
    
    // Generic callback handlers for value changes in services.
    static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len );
    static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len );
    
    // Task context handlers for generated services.
    static void user_LedService_ValueChangeHandler(char_data_t *pCharData);
    static void user_ButtonService_CfgChangeHandler(char_data_t *pCharData);
    static void user_DataService_ValueChangeHandler(char_data_t *pCharData);
    static void user_DataService_CfgChangeHandler(char_data_t *pCharData);
    
    // Task handler for sending notifications.
    static void user_updateCharVal(char_data_t *pCharData);
    
    // Utility functions
    static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData, uint16_t len );
    static void user_enqueueCharDataMsg(app_msg_types_t appMsgType, uint16_t connHandle,
                                        uint16_t serviceUUID, uint8_t paramID,
                                        uint8_t *pValue, uint16_t len);
    static void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId);
    
    static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len,
                                              uint8_t *dst, uint8_t dst_len);
    static char *Util_getLocalNameStr(const uint8_t *data);
    
    /*********************************************************************
     * EXTERN FUNCTIONS
     */
    extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // GAP Role Callbacks
    static gapRolesCBs_t user_gapRoleCBs =
    {
      user_gapStateChangeCB     // Profile State Change Callbacks
    };
    
    // GAP Bond Manager Callbacks
    static gapBondCBs_t user_bondMgrCBs =
    {
      user_gapBondMgr_passcodeCB, // Passcode callback
      user_gapBondMgr_pairStateCB // Pairing / Bonding state Callback
    };
    
    /*
     * Callbacks in the user application for events originating from BLE services.
     */
    // LED Service callback handler.
    // The type LED_ServiceCBs_t is defined in led_service.h
    static LedServiceCBs_t user_LED_ServiceCBs =
    {
      .pfnChangeCb    = user_service_ValueChangeCB, // Characteristic value change callback handler
      .pfnCfgChangeCb = NULL, // No notification-/indication enabled chars in LED Service
    };
    
    // Button Service callback handler.
    // The type Button_ServiceCBs_t is defined in button_service.h
    static ButtonServiceCBs_t user_Button_ServiceCBs =
    {
      .pfnChangeCb    = NULL, // No writable chars in Button Service, so no change handler.
      .pfnCfgChangeCb = user_service_CfgChangeCB, // Noti/ind configuration callback handler
    };
    
    // Data Service callback handler.
    // The type Data_ServiceCBs_t is defined in data_service.h
    static DataServiceCBs_t user_Data_ServiceCBs =
    {
      .pfnChangeCb    = user_service_ValueChangeCB, // Characteristic value change callback handler
      .pfnCfgChangeCb = user_service_CfgChangeCB, // Noti/ind configuration callback handler
    };
    
    /*********************************************************************
     * The following typedef and global handle the registration to connection event
     */
    typedef enum
    {
       NONE_REGISTERED    = 0,
       FOR_ATT_RSP        = 1,
    } connectionEventRegisterCause_u;
    
    // Handle the registration and un-registration for the connection event, since only one can be registered.
    uint32_t connectionEventRegisterCauseBitMap = NONE_REGISTERED; // See connectionEventRegisterCause_u
    
    
    /*
     * @brief  Register to receive connection event reports for all the connections
     *
     * @param  connectionEventRegisterCause  Represents the reason for registration
     *
     * @return @ref SUCCESS
     */
    bStatus_t ProjectZero_RegistertToAllConnectionEvent(connectionEventRegisterCause_u connectionEventRegisterCause)
    {
      bStatus_t status = SUCCESS;
    
      // In case  there is no registration for the connection event, register for report
      if (!CONNECTION_EVENT_IS_REGISTERED)
      {
        status = GAP_RegisterConnEventCb(ProjectZero_connEvtCB, GAP_CB_REGISTER, LINKDB_CONNHANDLE_ALL);
      }
      
      if(status == SUCCESS)
      {
        // Add the reason bit to the bitamap.
        CONNECTION_EVENT_REGISTER_BIT_SET(connectionEventRegisterCause);
      }
    
      return(status);
    }
    
    /*
     * @brief   Unregister connection events
     *
     * @param connectionEventRegisterCause represents the reason for registration
     *
     * @return @ref SUCCESS
     *
     */
    bStatus_t ProjectZero_UnRegistertToAllConnectionEvent (connectionEventRegisterCause_u connectionEventRegisterCause)
    {
      bStatus_t status = SUCCESS;
    
      CONNECTION_EVENT_REGISTER_BIT_REMOVE(connectionEventRegisterCause);
      
      // In case there are no more subscribers for the connection event then unregister for report
      if (!CONNECTION_EVENT_IS_REGISTERED)
      {
        GAP_RegisterConnEventCb(ProjectZero_connEvtCB, GAP_CB_UNREGISTER, LINKDB_CONNHANDLE_ALL);
      }
    
      return(status);
    }
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*
     * @brief   Task creation function for the user task.
     *
     * @param   None.
     *
     * @return  None.
     */
    void ProjectZero_createTask(void)
    {
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = przTaskStack;
      taskParams.stackSize = PRZ_TASK_STACK_SIZE;
      taskParams.priority = PRZ_TASK_PRIORITY;
    
      Task_construct(&przTask, ProjectZero_taskFxn, &taskParams, NULL);
    }
    
    /*
     * @brief   Called before the task loop and contains application-specific
     *          initialization of the BLE stack, hardware setup, power-state
     *          notification if used, and BLE profile/service initialization.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void ProjectZero_init(void)
    {
      // ******************************************************************
      // NO STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages via ICall to Stack.
      ICall_registerApp(&selfEntity, &syncEvent);
    
      Log_info0("Initializing the user task, hardware, BLE stack and services.");
    
      // Open display. By default this is disabled via the predefined symbol Display_DISABLE_ALL.
      dispHandle = Display_open(Display_Type_UART, NULL);
    
      // Initialize queue for application messages.
      // Note: Used to transfer control to application thread from e.g. interrupts.
      Queue_construct(&applicationMsgQ, NULL);
      hApplicationMsgQ = Queue_handle(&applicationMsgQ);
    
      // ******************************************************************
      // Hardware initialization
      // ******************************************************************
    
      // Open LED pins
      ledPinHandle = PIN_open(&ledPinState, ledPinTable);
      if(!ledPinHandle) {
        Log_error0("Error initializing board LED pins");
        Task_exit();
      }
    
      buttonPinHandle = PIN_open(&buttonPinState, buttonPinTable);
      if(!buttonPinHandle) {
        Log_error0("Error initializing button pins");
        Task_exit();
      }
    
      // Setup callback for button pins
      if (PIN_registerIntCb(buttonPinHandle, &buttonCallbackFxn) != 0) {
        Log_error0("Error registering button callback function");
        Task_exit();
      }
    
      // Create the debounce clock objects for Button 0 and Button 1
      Clock_Params clockParams;
      Clock_Params_init(&clockParams);
    
      // Both clock objects use the same callback, so differentiate on argument
      // given to the callback in Swi context
      clockParams.arg = Board_BUTTON0;
    
      // Initialize to 50 ms timeout when Clock_start is called.
      // Timeout argument is in ticks, so convert from ms to ticks via tickPeriod.
      Clock_construct(&button0DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
      // Second button
      clockParams.arg = Board_BUTTON1;
      Clock_construct(&button1DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
      // Advertising Toggle Clock
      Clock_construct(&advertToggleClock, (ti_sysbios_knl_Clock_FuncPtr) advertToggleClockFxn,
                      10000 * (1000/Clock_tickPeriod), // Ten seconds
                      &clockParams);
    
      // ******************************************************************
      // BLE Stack initialization
      // ******************************************************************
    
    //  // Setup the GAP Peripheral Role Profile
    //  uint8_t initialAdvertEnable = TRUE;  // Advertise on power-up
    //
    //  // By setting this to zero, the device will go into the waiting state after
    //  // being discoverable. Otherwise wait this long [ms] before advertising again.
    //  uint16_t advertOffTime = 0; // miliseconds
    //
    //  // Set advertisement enabled.
    //  GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
    //                       &initialAdvertEnable);
    //
    //  // Configure the wait-time before restarting advertisement automatically
    //  GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
    //                       &advertOffTime);
      Clock_start(Clock_handle(&advertToggleClock));
    
      // Initialize Scan Response data
      GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
    
      // Initialize Advertisement data
      GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
    
      Log_info1("Name in advertData array: \x1b[33m%s\x1b[0m",
                (IArg)Util_getLocalNameStr(advertData));
    
      // Set advertising interval
      uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
    
      GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt);
      GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt);
      GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt);
      GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt);
    
      // Set duration of advertisement before stopping in Limited adv mode.
      //GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, 30); // Seconds
    
      // ******************************************************************
      // BLE Bond Manager initialization
      // ******************************************************************
      uint32_t passkey = 0; // passkey "000000"
      uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
      uint8_t mitm = TRUE;
      uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
      uint8_t bonding = TRUE;
    
      GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t),
                              &passkey);
      GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
      GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
      GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
      GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
    
      // ******************************************************************
      // BLE Service initialization
      // ******************************************************************
    
      // Add services to GATT server
      GGS_AddService(GATT_ALL_SERVICES);           // GAP
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
      DevInfo_AddService();                        // Device Information Service
    
      // Set the device name characteristic in the GAP Profile
      GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
      // Add services to GATT server and give ID of this task for Indication acks.
      LedService_AddService( selfEntity );
      ButtonService_AddService( selfEntity );
      DataService_AddService( selfEntity );
    
      // Register callbacks with the generated services that
      // can generate events (writes received) to the application
      LedService_RegisterAppCBs( &user_LED_ServiceCBs );
      ButtonService_RegisterAppCBs( &user_Button_ServiceCBs );
      DataService_RegisterAppCBs( &user_Data_ServiceCBs );
    
      // Placeholder variable for characteristic intialization
      uint8_t initVal[40] = {0};
      uint8_t initString[] = "This is a pretty long string, isn't it!";
    
      // Initalization of characteristics in LED_Service that can provide data.
      LedService_SetParameter(LS_LED0_ID, LS_LED0_LEN, initVal);
      LedService_SetParameter(LS_LED1_ID, LS_LED1_LEN, initVal);
    
      // Initalization of characteristics in Button_Service that can provide data.
      ButtonService_SetParameter(BS_BUTTON0_ID, BS_BUTTON0_LEN, initVal);
      ButtonService_SetParameter(BS_BUTTON1_ID, BS_BUTTON1_LEN, initVal);
    
      // Initalization of characteristics in Data_Service that can provide data.
      DataService_SetParameter(DS_STRING_ID, sizeof(initString), initString);
      DataService_SetParameter(DS_STREAM_ID, DS_STREAM_LEN, initVal);
    
      // Start the stack in Peripheral mode.
      VOID GAPRole_StartDevice(&user_gapRoleCBs);
    
      // Start Bond Manager
      VOID GAPBondMgr_Register(&user_bondMgrCBs);
    
      // Register with GAP for HCI/Host messages
      GAP_RegisterForMsgs(selfEntity);
    
      // Register for GATT local events and ATT Responses pending for transmission
      GATT_RegisterForMsgs(selfEntity);
    }
    
    
    /*
     * @brief   Application task entry point.
     *
     *          Invoked by TI-RTOS when BIOS_start is called. Calls an init function
     *          and enters an infinite loop waiting for messages.
     *
     *          Messages can be either directly from the BLE stack or from user code
     *          like Hardware Interrupt (Hwi) or a callback function.
     *
     *          The reason for sending messages to this task from e.g. Hwi's is that
     *          some RTOS and Stack APIs are not available in callbacks and so the
     *          actions that may need to be taken is dispatched to this Task.
     *
     * @param   a0, a1 - not used.
     *
     * @return  None.
     */
    static void ProjectZero_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      ProjectZero_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, PRZ_ALL_EVENTS,
                            ICALL_TIMEOUT_FOREVER);
    
        if (events)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          // Check if we got a signal because of a stack message
          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;
    
              if (pEvt->signature != 0xffff)
              {
                // Process inter-task message
                safeToDealloc = ProjectZero_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // Process messages sent from another task or another context.
          while (!Queue_empty(hApplicationMsgQ))
          {
            app_msg_t *pMsg = Queue_dequeue(hApplicationMsgQ);
    
            // Process application-layer message probably sent from ourselves.
            user_processApplicationMessage(pMsg);
    
            // Free the received message.
            ICall_free(pMsg);
          }
        }
      }
    }
    
    
    /*
     * @brief   Handle application messages
     *
     *          These are messages not from the BLE stack, but from the
     *          application itself.
     *
     *          For example, in a Software Interrupt (Swi) it is not possible to
     *          call any BLE APIs, so instead the Swi function must send a message
     *          to the application Task for processing in Task context.
     *
     * @param   pMsg  Pointer to the message of type app_msg_t.
     *
     * @return  None.
     */
    static void user_processApplicationMessage(app_msg_t *pMsg)
    {
      char_data_t *pCharData = (char_data_t *)pMsg->pdu;
    
      switch (pMsg->type)
      {
        case APP_MSG_SERVICE_WRITE: /* Message about received value write */
          /* Call different handler per service */
          switch(pCharData->svcUUID) {
            case LED_SERVICE_SERV_UUID:
              user_LedService_ValueChangeHandler(pCharData);
              break;
            case DATA_SERVICE_SERV_UUID:
              user_DataService_ValueChangeHandler(pCharData);
              break;
    
          }
          break;
    
        case APP_MSG_SERVICE_CFG: /* Message about received CCCD write */
          /* Call different handler per service */
          switch(pCharData->svcUUID) {
            case BUTTON_SERVICE_SERV_UUID:
              user_ButtonService_CfgChangeHandler(pCharData);
              break;
            case DATA_SERVICE_SERV_UUID:
              user_DataService_CfgChangeHandler(pCharData);
              break;
          }
          break;
    
       case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
         AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
         break;
    
        case APP_MSG_UPDATE_CHARVAL: /* Message from ourselves to send  */
          user_updateCharVal(pCharData);
          break;
    
        case APP_MSG_GAP_STATE_CHANGE: /* Message that GAP state changed  */
          user_processGapStateChangeEvt( *(gaprole_States_t *)pMsg->pdu );
          break;
    
        case APP_MSG_SEND_PASSCODE: /* Message about pairing PIN request */
          {
            passcode_req_t *pReq = (passcode_req_t *)pMsg->pdu;
            Log_info2("BondMgr Requested passcode. We are %s passcode %06d",
                      (IArg)(pReq->uiInputs?"Sending":"Displaying"),
                      DEFAULT_PASSCODE);
            // Send passcode response.
            GAPBondMgr_PasscodeRsp(pReq->connHandle, SUCCESS, DEFAULT_PASSCODE);
          }
          break;
    
        case APP_MSG_BUTTON_DEBOUNCED: /* Message from swi about pin change */
          {
            button_state_t *pButtonState = (button_state_t *)pMsg->pdu;
            user_handleButtonPress(pButtonState);
          }
          break;
    
        case APP_MSG_PRZ_CONN_EVT:
        {
            ProjectZero_processConnEvt((Gap_ConnEventRpt_t *)pMsg->pdu);
            break;
        }
        case APP_MSG_PRZ_ADV_TOGGLE_EVT:
        {
            uint8_t advertEnable = 0;
    
            GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &advertEnable);
            advertEnable = !advertEnable;
            GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertEnable);
    
            Clock_start(Clock_handle(&advertToggleClock));
            break;
        }
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Handlers of system/application events deferred to the user Task context.
     *  Invoked from the application Task function above.
     *
     *  Further down you can find the callback handler section containing the
     *  functions that defer their actions via messages to the application task.
     *
     ****************************************************************************
     *****************************************************************************/
    
    
    /*
     * @brief   Process a pending GAP Role state change event.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void user_processGapStateChangeEvt(gaprole_States_t newState)
    {
      switch ( newState )
      {
        case GAPROLE_STARTED:
          {
            uint8_t ownAddress[B_ADDR_LEN];
            uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
    
            GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);
    
            // use 6 bytes of device address for 8 bytes of system ID value
            systemId[0] = ownAddress[0];
            systemId[1] = ownAddress[1];
            systemId[2] = ownAddress[2];
    
            // set middle bytes to zero
            systemId[4] = 0x00;
            systemId[3] = 0x00;
    
            // shift three bytes up
            systemId[7] = ownAddress[5];
            systemId[6] = ownAddress[4];
            systemId[5] = ownAddress[3];
    
            DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
    
            // Display device address
            char *cstr_ownAddress = Util_convertBdAddr2Str(ownAddress);
            Log_info1("GAP is started. Our address: \x1b[32m%s\x1b[0m", (IArg)cstr_ownAddress);
          }
          break;
    
        case GAPROLE_ADVERTISING:
          Log_info0("Advertising");
          break;
    
        case GAPROLE_CONNECTED:
          {
            uint8_t peerAddress[B_ADDR_LEN];
    
            GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);
    
            char *cstr_peerAddress = Util_convertBdAddr2Str(peerAddress);
            Log_info1("Connected. Peer address: \x1b[32m%s\x1b[0m", (IArg)cstr_peerAddress);
           }
          break;
    
        case GAPROLE_CONNECTED_ADV:
          Log_info0("Connected and advertising");
          break;
    
        case GAPROLE_WAITING:
          Log_info0("Disconnected / Idle");
          break;
    
        case GAPROLE_WAITING_AFTER_TIMEOUT:
          Log_info0("Connection timed out");
          break;
    
        case GAPROLE_ERROR:
          Log_info0("Error");
          break;
    
        default:
          break;
      }
    }
    
    
    /*
     * @brief   Handle a debounced button press or release in Task context.
     *          Invoked by the taskFxn based on a message received from a callback.
     *
     * @see     buttonDebounceSwiFxn
     * @see     buttonCallbackFxn
     *
     * @param   pState  pointer to button_state_t message sent from debounce Swi.
     *
     * @return  None.
     */
    static void user_handleButtonPress(button_state_t *pState)
    {
      Log_info2("%s %s",
        (IArg)(pState->pinId == Board_BUTTON0?"Button 0":"Button 1"),
        (IArg)(pState->state?"\x1b[32mpressed\x1b[0m":
                             "\x1b[33mreleased\x1b[0m"));
    
      // Update the service with the new value.
      // Will automatically send notification/indication if enabled.
      switch (pState->pinId)
      {
        case Board_BUTTON0:
          ButtonService_SetParameter(BS_BUTTON0_ID,
                                     sizeof(pState->state),
                                     &pState->state);
          break;
        case Board_BUTTON1:
          ButtonService_SetParameter(BS_BUTTON1_ID,
                                     sizeof(pState->state),
                                     &pState->state);
          break;
      }
    }
    
    /*
     * @brief   Handle a write request sent from a peer device.
     *
     *          Invoked by the Task based on a message received from a callback.
     *
     *          When we get here, the request has already been accepted by the
     *          service and is valid from a BLE protocol perspective as well as
     *          having the correct length as defined in the service implementation.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_LedService_ValueChangeHandler(char_data_t *pCharData)
    {
      static uint8_t pretty_data_holder[16]; // 5 bytes as hex string "AA:BB:CC:DD:EE"
      Util_convertArrayToHexString(pCharData->data, pCharData->dataLen,
                                   pretty_data_holder, sizeof(pretty_data_holder));
    
      switch (pCharData->paramID)
      {
        case LS_LED0_ID:
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"LED Service",
                    (IArg)"LED0",
                    (IArg)pretty_data_holder);
    
          // Do something useful with pCharData->data here
          // -------------------------
          // Set the output value equal to the received value. 0 is off, not 0 is on
          PIN_setOutputValue(ledPinHandle, Board_RLED, pCharData->data[0]);
          Log_info2("Turning %s %s",
                    (IArg)"\x1b[31mLED0\x1b[0m",
                    (IArg)(pCharData->data[0]?"on":"off"));
          break;
    
        case LS_LED1_ID:
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"LED Service",
                    (IArg)"LED1",
                    (IArg)pretty_data_holder);
    
          // Do something useful with pCharData->data here
          // -------------------------
          // Set the output value equal to the received value. 0 is off, not 0 is on
          PIN_setOutputValue(ledPinHandle, Board_GLED, pCharData->data[0]);
          Log_info2("Turning %s %s",
                    (IArg)"\x1b[32mLED1\x1b[0m",
                    (IArg)(pCharData->data[0]?"on":"off"));
          break;
    
      default:
        return;
      }
    }
    
    
    /*
     * @brief   Handle a CCCD (configuration change) write received from a peer
     *          device. This tells us whether the peer device wants us to send
     *          Notifications or Indications.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_ButtonService_CfgChangeHandler(char_data_t *pCharData)
    {
      // Cast received data to uint16, as that's the format for CCCD writes.
      uint16_t configValue = *(uint16_t *)pCharData->data;
      char *configValString;
    
      // Determine what to tell the user
      switch(configValue)
      {
      case GATT_CFG_NO_OPERATION:
        configValString = "Noti/Ind disabled";
        break;
      case GATT_CLIENT_CFG_NOTIFY:
        configValString = "Notifications enabled";
        break;
      case GATT_CLIENT_CFG_INDICATE:
        configValString = "Indications enabled";
        break;
      }
    
      switch (pCharData->paramID)
      {
        case BS_BUTTON0_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Button Service",
                    (IArg)"BUTTON0",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
    
        case BS_BUTTON1_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Button Service",
                    (IArg)"BUTTON1",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
      }
    }
    
    /*
     * @brief   Handle a write request sent from a peer device.
     *
     *          Invoked by the Task based on a message received from a callback.
     *
     *          When we get here, the request has already been accepted by the
     *          service and is valid from a BLE protocol perspective as well as
     *          having the correct length as defined in the service implementation.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_DataService_ValueChangeHandler(char_data_t *pCharData)
    {
      // Value to hold the received string for printing via Log, as Log printouts
      // happen in the Idle task, and so need to refer to a global/static variable.
      static uint8_t received_string[DS_STRING_LEN] = {0};
    
      switch (pCharData->paramID)
      {
        case DS_STRING_ID:
          // Do something useful with pCharData->data here
          // -------------------------
          // Copy received data to holder array, ensuring NULL termination.
          memset(received_string, 0, DS_STRING_LEN);
          memcpy(received_string, pCharData->data, DS_STRING_LEN-1);
          // Needed to copy before log statement, as the holder array remains after
          // the pCharData message has been freed and reused for something else.
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"Data Service",
                    (IArg)"String",
                    (IArg)received_string);
          break;
    
        case DS_STREAM_ID:
          Log_info3("Value Change msg: Data Service Stream: %02x:%02x:%02x...",
                    (IArg)pCharData->data[0],
                    (IArg)pCharData->data[1],
                    (IArg)pCharData->data[2]);
          // -------------------------
          // Do something useful with pCharData->data here
          break;
    
      default:
        return;
      }
    }
    
    /*
     * @brief   Handle a CCCD (configuration change) write received from a peer
     *          device. This tells us whether the peer device wants us to send
     *          Notifications or Indications.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_DataService_CfgChangeHandler(char_data_t *pCharData)
    {
      // Cast received data to uint16, as that's the format for CCCD writes.
      uint16_t configValue = *(uint16_t *)pCharData->data;
      char *configValString;
    
      // Determine what to tell the user
      switch(configValue)
      {
      case GATT_CFG_NO_OPERATION:
        configValString = "Noti/Ind disabled";
        break;
      case GATT_CLIENT_CFG_NOTIFY:
        configValString = "Notifications enabled";
        break;
      case GATT_CLIENT_CFG_INDICATE:
        configValString = "Indications enabled";
        break;
      }
    
      switch (pCharData->paramID)
      {
        case DS_STREAM_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Data Service",
                    (IArg)"Stream",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
      }
    }
    
    
    /*
     * @brief   Process an incoming BLE stack message.
     *
     *          This could be a GATT message from a peer device like acknowledgement
     *          of an Indication we sent, or it could be a response from the stack
     *          to an HCI message that the user application sent.
     *
     * @param   pMsg - message to process
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = ProjectZero_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 Event
                Log_info0("HCI Command Complete Event received");
                break;
    
              default:
                break;
            }
          }
          break;
    
        default:
          // do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    
    /*
     * @brief   Process GATT messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      // See if GATT server was unable to transmit an ATT response
      if (pMsg->hdr.status == blePending)
      {
        Log_warning1("Outgoing RF FIFO full. Re-schedule transmission of msg with opcode 0x%02x",
          pMsg->method);
    
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if(ProjectZero_RegistertToAllConnectionEvent(FOR_ATT_RSP) == SUCCESS)
        {
          // First free any pending response
          ProjectZero_freeAttRsp(FAILURE);
    
          // Hold on to the response message for retransmission
          pAttRsp = pMsg;
    
          // Don't free the response message yet
          return (FALSE);
        }
      }
      else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
      {
        // ATT request-response or indication-confirmation flow control is
        // violated. All subsequent ATT requests or indications will be dropped.
        // The app is informed in case it wants to drop the connection.
    
        // Log the opcode of the message that caused the violation.
        Log_error1("Flow control violated. Opcode of offending ATT msg: 0x%02x",
          pMsg->msg.flowCtrlEvt.opcode);
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
        Log_info1("MTU Size change: %d bytes", pMsg->msg.mtuEvt.MTU);
      }
      else
      {
        // Got an expected GATT message from a peer.
        Log_info1("Recevied GATT Message. Opcode: 0x%02x", pMsg->method);
      }
    
      // Free message payload. Needed only for ATT Protocol messages
      GATT_bm_free(&pMsg->msg, pMsg->method);
    
      // It's safe to free the incoming message
      return (TRUE);
    }
    
    /*********************************************************************
     * @fn      ProjectZero_processConnEvt
     *
     * @brief   Process connection event.
     *
     * @param pReport pointer to connection event report
     */
    static void ProjectZero_processConnEvt(Gap_ConnEventRpt_t *pReport)
    {
    
      if( CONNECTION_EVENT_REGISTRATION_CAUSE(FOR_ATT_RSP))
      {
        // The GATT server might have returned a blePending as it was trying
        // to process an ATT Response. Now that we finished with this
        // connection event, let's try sending any remaining ATT Responses
        // on the next connection event.
        // Try to retransmit pending ATT Response (if any)
        ProjectZero_sendAttRsp();
      }
    
    }
    
    
    
    /*
     *  Application error handling functions
     *****************************************************************************/
    
    /*
     * @brief   Send a pending ATT response message.
     *
     *          The message is one that the stack was trying to send based on a
     *          peer request, but the response couldn't be sent because the
     *          user application had filled the TX queue with other data.
     *
     * @param   none
     *
     * @return  none
     */
    static void ProjectZero_sendAttRsp(void)
    {
      // See if there's a pending ATT Response to be transmitted
      if (pAttRsp != NULL)
      {
        uint8_t status;
    
        // Increment retransmission count
        rspTxRetry++;
    
        // Try to retransmit ATT response till either we're successful or
        // the ATT Client times out (after 30s) and drops the connection.
        status = GATT_SendRsp(pAttRsp->connHandle, pAttRsp->method, &(pAttRsp->msg));
        if ((status != blePending) && (status != MSG_BUFFER_NOT_AVAIL))
        {
          // Disable connection event end notice
          ProjectZero_UnRegistertToAllConnectionEvent (FOR_ATT_RSP);
    
          // We're done with the response message
          ProjectZero_freeAttRsp(status);
        }
        else
        {
          // Continue retrying
          Log_warning2("Retrying message with opcode 0x%02x. Attempt %d",
            pAttRsp->method, rspTxRetry);
        }
      }
    }
    
    /*
     * @brief   Free ATT response message.
     *
     * @param   status - response transmit status
     *
     * @return  none
     */
    static void ProjectZero_freeAttRsp(uint8_t status)
    {
      // See if there's a pending ATT response message
      if (pAttRsp != NULL)
      {
        // See if the response was sent out successfully
        if (status == SUCCESS)
        {
          Log_info2("Sent message with opcode 0x%02x. Attempt %d",
            pAttRsp->method, rspTxRetry);
        }
        else
        {
          Log_error2("Gave up message with opcode 0x%02x. Status: %d",
            pAttRsp->method, status);
    
          // Free response payload
          GATT_bm_free(&pAttRsp->msg, pAttRsp->method);
        }
    
        // Free response message
        ICall_freeMsg(pAttRsp);
    
        // Reset our globals
        pAttRsp = NULL;
        rspTxRetry = 0;
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Handlers of direct system callbacks.
     *
     *  Typically enqueue the information or request as a message for the
     *  application Task for handling.
     *
     ****************************************************************************
     *****************************************************************************/
    
    /*********************************************************************
     * @fn      ProjectZero_connEvtCB
     *
     * @brief   Connection event callback.
     *
     * @param pReport pointer to connection event report
     */
    static void ProjectZero_connEvtCB(Gap_ConnEventRpt_t *pReport)
    {
      // Enqueue the event for processing in the app context.
      user_enqueueRawAppMsg(APP_MSG_PRZ_CONN_EVT, (uint8_t *)pReport, sizeof(pReport));
      ICall_free(pReport);
    }
    
    /*
     *  Callbacks from the Stack Task context (GAP or Service changes)
     *****************************************************************************/
    
    /**
     * Callback from GAP Role indicating a role state change.
     */
    static void user_gapStateChangeCB(gaprole_States_t newState)
    {
      Log_info1("(CB) GAP State change: %d, Sending msg to app.", (IArg)newState);
      user_enqueueRawAppMsg( APP_MSG_GAP_STATE_CHANGE, (uint8_t *)&newState, sizeof(newState) );
    }
    
    /*
     * @brief   Passcode callback.
     *
     * @param   connHandle - connection handle
     * @param   uiInputs   - input passcode?
     * @param   uiOutputs  - display passcode?
     * @param   numComparison - numeric comparison value
     *
     * @return  none
     */
    static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                           uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison)
    {
      passcode_req_t req =
      {
        .connHandle = connHandle,
        .uiInputs = uiInputs,
        .uiOutputs = uiOutputs,
        .numComparison = numComparison
      };
    
      // Defer handling of the passcode request to the application, in case
      // user input is required, and because a BLE API must be used from Task.
      user_enqueueRawAppMsg(APP_MSG_SEND_PASSCODE, (uint8_t *)&req, sizeof(req));
    }
    
    /*
     * @brief   Pairing state callback.
     *
     * @param   connHandle - connection handle
     * @param   state      - pairing state
     * @param   status     - pairing status
     *
     * @return  none
     */
    static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state,
                                            uint8_t status)
    {
      if (state == GAPBOND_PAIRING_STATE_STARTED)
      {
        Log_info0("Pairing started");
      }
      else if (state == GAPBOND_PAIRING_STATE_COMPLETE)
      {
        if (status == SUCCESS)
        {
          Log_info0("Pairing completed successfully.");
        }
        else
        {
          Log_error1("Pairing failed. Error: %02x", status);
        }
      }
      else if (state == GAPBOND_PAIRING_STATE_BONDED)
      {
        if (status == SUCCESS)
        {
         Log_info0("Re-established pairing from stored bond info.");
        }
      }
    }
    
    /**
     * Callback handler for characteristic value changes in services.
     */
    static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid,
                                            uint8_t paramID, uint8_t *pValue,
                                            uint16_t len )
    {
      // See the service header file to compare paramID with characteristic.
      Log_info2("(CB) Characteristic value change: svc(0x%04x) paramID(%d). "
                "Sending msg to app.", (IArg)svcUuid, (IArg)paramID);
      user_enqueueCharDataMsg(APP_MSG_SERVICE_WRITE, connHandle, svcUuid, paramID,
                              pValue, len);
    }
    
    /**
     * Callback handler for characteristic configuration changes in services.
     */
    static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid,
                                          uint8_t paramID, uint8_t *pValue,
                                          uint16_t len )
    {
      Log_info2("(CB) Char config change: svc(0x%04x) paramID(%d). "
                "Sending msg to app.", (IArg)svcUuid, (IArg)paramID);
      user_enqueueCharDataMsg(APP_MSG_SERVICE_CFG, connHandle, svcUuid,
                              paramID, pValue, len);
    }
    
    /*
     *  Callbacks from Swi-context
     *****************************************************************************/
    
    /*
     * @brief  Callback from Clock module on timeout
     *
     *         Determines new state after debouncing
     *
     * @param  buttonId    The pin being debounced
     */
    static void buttonDebounceSwiFxn(UArg buttonId)
    {
      // Used to send message to app
      button_state_t buttonMsg = { .pinId = buttonId };
      uint8_t        sendMsg   = FALSE;
    
      // Get current value of the button pin after the clock timeout
      uint8_t buttonPinVal = PIN_getInputValue(buttonId);
    
      // Set interrupt direction to opposite of debounced state
      // If button is now released (button is active low, so release is high)
      if (buttonPinVal)
      {
        // Enable negative edge interrupts to wait for press
        PIN_setConfig(buttonPinHandle, PIN_BM_IRQ, buttonId | PIN_IRQ_NEGEDGE);
      }
      else
      {
        // Enable positive edge interrupts to wait for relesae
        PIN_setConfig(buttonPinHandle, PIN_BM_IRQ, buttonId | PIN_IRQ_POSEDGE);
      }
    
      switch(buttonId)
      {
        case Board_BUTTON0:
          // If button is now released (buttonPinVal is active low, so release is 1)
          // and button state was pressed (buttonstate is active high so press is 1)
          if (buttonPinVal && button0State)
          {
            // Button was released
            buttonMsg.state = button0State = 0;
            sendMsg = TRUE;
          }
          else if (!buttonPinVal && !button0State)
          {
            // Button was pressed
            buttonMsg.state = button0State = 1;
            sendMsg = TRUE;
          }
          break;
    
        case Board_BUTTON1:
          // If button is now released (buttonPinVal is active low, so release is 1)
          // and button state was pressed (buttonstate is active high so press is 1)
          if (buttonPinVal && button1State)
          {
            // Button was released
            buttonMsg.state = button1State = 0;
            sendMsg = TRUE;
          }
          else if (!buttonPinVal && !button1State)
          {
            // Button was pressed
            buttonMsg.state = button1State = 1;
            sendMsg = TRUE;
          }
          break;
      }
    
      if (sendMsg == TRUE)
      {
        user_enqueueRawAppMsg(APP_MSG_BUTTON_DEBOUNCED,
                          (uint8_t *)&buttonMsg, sizeof(buttonMsg));
      }
    }
    
    static void advertToggleClockFxn(void)
    {
        static uint8_t count = 0;
    
        user_enqueueRawAppMsg(APP_MSG_PRZ_ADV_TOGGLE_EVT,
                          (uint8_t *)&count, sizeof(count));
    
        count++;
    }
    
    /*
     *  Callbacks from Hwi-context
     *****************************************************************************/
    
    /*
     * @brief  Callback from PIN driver on interrupt
     *
     *         Sets in motion the debouncing.
     *
     * @param  handle    The PIN_Handle instance this is about
     * @param  pinId     The pin that generated the interrupt
     */
    static void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId)
    {
      Log_info1("Button interrupt: %s",
                (IArg)((pinId == Board_BUTTON0)?"Button 0":"Button 1"));
    
      // Disable interrupt on that pin for now. Re-enabled after debounce.
      PIN_setConfig(handle, PIN_BM_IRQ, pinId | PIN_IRQ_DIS);
    
      // Start debounce timer
      switch (pinId)
      {
        case Board_BUTTON0:
          Clock_start(Clock_handle(&button0DebounceClock));
          break;
        case Board_BUTTON1:
          Clock_start(Clock_handle(&button1DebounceClock));
          break;
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Utility functions
     *
     ****************************************************************************
     *****************************************************************************/
    
    /*
     * @brief  Generic message constructor for characteristic data.
     *
     *         Sends a message to the application for handling in Task context where
     *         the message payload is a char_data_t struct.
     *
     *         From service callbacks the appMsgType is APP_MSG_SERVICE_WRITE or
     *         APP_MSG_SERVICE_CFG, and functions running in another context than
     *         the Task itself, can set the type to APP_MSG_UPDATE_CHARVAL to
     *         make the user Task loop invoke user_updateCharVal function for them.
     *
     * @param  appMsgType    Enumerated type of message being sent.
     * @param  connHandle    GAP Connection handle of the relevant connection
     * @param  serviceUUID   16-bit part of the relevant service UUID
     * @param  paramID       Index of the characteristic in the service
     * @oaram  *pValue       Pointer to characteristic value
     * @param  len           Length of characteristic data
     */
    static void user_enqueueCharDataMsg( app_msg_types_t appMsgType,
                                         uint16_t connHandle,
                                         uint16_t serviceUUID, uint8_t paramID,
                                         uint8_t *pValue, uint16_t len )
    {
      // Called in Stack's Task context, so can't do processing here.
      // Send message to application message queue about received data.
      uint16_t readLen = len; // How much data was written to the attribute
    
      // Allocate memory for the message.
      // Note: The pCharData message doesn't have to contain the data itself, as
      //       that's stored in a variable in the service implementation.
      //
      //       However, to prevent data loss if a new value is received before the
      //       service's container is read out via the GetParameter API is called,
      //       we copy the characteristic's data now.
      app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + sizeof(char_data_t) +
                                      readLen );
    
      if (pMsg != NULL)
      {
        pMsg->type = appMsgType;
    
        char_data_t *pCharData = (char_data_t *)pMsg->pdu;
        pCharData->svcUUID = serviceUUID; // Use 16-bit part of UUID.
        pCharData->paramID = paramID;
        // Copy data from service now.
        memcpy(pCharData->data, pValue, readLen);
        // Update pCharData with how much data we received.
        pCharData->dataLen = readLen;
        // Enqueue the message using pointer to queue node element.
        Queue_enqueue(hApplicationMsgQ, &pMsg->_elem);
      // Let application know there's a message.
      Event_post(syncEvent, PRZ_APP_MSG_EVT);
      }
    }
    
    /*
     * @brief  Generic message constructor for application messages.
     *
     *         Sends a message to the application for handling in Task context.
     *
     * @param  appMsgType    Enumerated type of message being sent.
     * @oaram  *pValue       Pointer to characteristic value
     * @param  len           Length of characteristic data
     */
    static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData,
                                      uint16_t len)
    {
      // Allocate memory for the message.
      app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + len );
    
      if (pMsg != NULL)
      {
        pMsg->type = appMsgType;
    
        // Copy data into message
        memcpy(pMsg->pdu, pData, len);
    
        // Enqueue the message using pointer to queue node element.
        Queue_enqueue(hApplicationMsgQ, &pMsg->_elem);
    //    // Let application know there's a message.
        Event_post(syncEvent, PRZ_APP_MSG_EVT);
      }
    }
    
    
    /*
     * @brief  Convenience function for updating characteristic data via char_data_t
     *         structured message.
     *
     * @note   Must run in Task context in case BLE Stack APIs are invoked.
     *
     * @param  *pCharData  Pointer to struct with value to update.
     */
    static void user_updateCharVal(char_data_t *pCharData)
    {
      switch(pCharData->svcUUID) {
        case LED_SERVICE_SERV_UUID:
          LedService_SetParameter(pCharData->paramID, pCharData->dataLen,
                                  pCharData->data);
        break;
    
        case BUTTON_SERVICE_SERV_UUID:
          ButtonService_SetParameter(pCharData->paramID, pCharData->dataLen,
                                     pCharData->data);
        break;
    
      }
    }
    
    /*
     * @brief   Convert {0x01, 0x02} to "01:02"
     *
     * @param   src - source byte-array
     * @param   src_len - length of array
     * @param   dst - destination string-array
     * @param   dst_len - length of array
     *
     * @return  array as string
     */
    static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len,
                                              uint8_t *dst, uint8_t dst_len)
    {
      char        hex[] = "0123456789ABCDEF";
      uint8_t     *pStr = dst;
      uint8_t     avail = dst_len-1;
    
      memset(dst, 0, avail);
    
      while (src_len && avail > 3)
      {
        if (avail < dst_len-1) { *pStr++ = ':'; avail -= 1; };
        *pStr++ = hex[*src >> 4];
        *pStr++ = hex[*src++ & 0x0F];
        avail -= 2;
        src_len--;
      }
    
      if (src_len && avail)
        *pStr++ = ':'; // Indicate not all data fit on line.
    
      return (char *)dst;
    }
    
    /*
     * @brief   Extract the LOCALNAME from Scan/AdvData
     *
     * @param   data - Pointer to the advertisement or scan response data
     *
     * @return  Pointer to null-terminated string with the adv local name.
     */
    static char *Util_getLocalNameStr(const uint8_t *data) {
      uint8_t nuggetLen = 0;
      uint8_t nuggetType = 0;
      uint8_t advIdx = 0;
    
      static char localNameStr[32] = { 0 };
      memset(localNameStr, 0, sizeof(localNameStr));
    
      for (advIdx = 0; advIdx < 32;) {
        nuggetLen = data[advIdx++];
        nuggetType = data[advIdx];
        if ( (nuggetType == GAP_ADTYPE_LOCAL_NAME_COMPLETE ||
              nuggetType == GAP_ADTYPE_LOCAL_NAME_SHORT) && nuggetLen < 31) {
          memcpy(localNameStr, &data[advIdx + 1], nuggetLen - 1);
          break;
        } else {
          advIdx += nuggetLen;
        }
      }
    
      return localNameStr;
    }
    
    /*********************************************************************
    *********************************************************************/
    
    to make this happen. Here is the file so you can see for yourself. It doesn't have to be done exactly like this. But this was my proof of concept. 

    First add a #define for the clock event and add it to ALL_EVENTS. Here mine is called PRZ_ADV_TOGGLE_EVT

    #define PRZ_ADV_TOGGLE_EVT                    Event_Id_04
    
    #define PRZ_ALL_EVENTS                       (PRZ_ICALL_EVT        | \
                                                  PRZ_QUEUE_EVT        | \
                                                  PRZ_STATE_CHANGE_EVT | \
                                                  PRZ_CHAR_CHANGE_EVT  | \
                                                  PRZ_PERIODIC_EVT     | \
                                                  PRZ_ADV_TOGGLE_EVT     | \
                                                  PRZ_APP_MSG_EVT)

    Add an event for an APP MSG event. Here mine is called APP_MSG_PRZ_ADV_TOGGLE_EVT.

    typedef enum
    {
      APP_MSG_SERVICE_WRITE = 0,   /* A characteristic value has been written     */
      APP_MSG_SERVICE_CFG,         /* A characteristic configuration has changed  */
      APP_MSG_UPDATE_CHARVAL,      /* Request from ourselves to update a value    */
      APP_MSG_GAP_STATE_CHANGE,    /* The GAP / connection state has changed      */
      APP_MSG_BUTTON_DEBOUNCED,    /* A button has been debounced with new value  */
      APP_MSG_SEND_PASSCODE,       /* A pass-code/PIN is requested during pairing */
      APP_MSG_PRZ_CONN_EVT,        /* Connection Event finished report            */
      APP_MSG_PRZ_ADV_TOGGLE_EVT,
    } app_msg_types_t;

    Add a new clock definition.

    // Clock objects for debouncing the buttons
    static Clock_Struct button0DebounceClock;
    static Clock_Struct button1DebounceClock;
    static Clock_Struct advertToggleClock;

    Add a function prototype for our clockFxn.

    static void advertToggleClockFxn(void);

    Then within Project_Zero_init(), add a call to Clock_construct for our new Clock object  as well as start the clock instead of setting up advertisements in this function. (I've commented out the old advertisement initialization code)

      // Initialize to 50 ms timeout when Clock_start is called.
      // Timeout argument is in ticks, so convert from ms to ticks via tickPeriod.
      Clock_construct(&button0DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
      // Second button
      clockParams.arg = Board_BUTTON1;
      Clock_construct(&button1DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
      // Advertising Toggle Clock
      Clock_construct(&advertToggleClock, (ti_sysbios_knl_Clock_FuncPtr) advertToggleClockFxn,
                      10000 * (1000/Clock_tickPeriod), // Ten seconds
                      &clockParams); // We won't actually be using this param

      // ******************************************************************
      // BLE Stack initialization
      // ******************************************************************
    
    //  // Setup the GAP Peripheral Role Profile
    //  uint8_t initialAdvertEnable = TRUE;  // Advertise on power-up
    //
    //  // By setting this to zero, the device will go into the waiting state after
    //  // being discoverable. Otherwise wait this long [ms] before advertising again.
    //  uint16_t advertOffTime = 0; // miliseconds
    //
    //  // Set advertisement enabled.
    //  GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
    //                       &initialAdvertEnable);
    //
    //  // Configure the wait-time before restarting advertisement automatically
    //  GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
    //                       &advertOffTime);
      Clock_start(Clock_handle(&advertToggleClock));

    Add a case statement for our new APP_MSG event within user_processApplicationMessage().

    case APP_MSG_PRZ_ADV_TOGGLE_EVT:
        {
            uint8_t advertEnable = 0;
    
            GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &advertEnable);
            advertEnable = !advertEnable;
            GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertEnable);
    
            Clock_start(Clock_handle(&advertToggleClock));
            break;
        }

    Finish declaring the clockFxn we made a prototype for earlier. The function simply enqueues a msg with a type of APP_MSG_PRZ_ADV_TOGGLE_EVT which we created earlier as well. The parameter 'count' here does nothing but shows how you could add a msg parameter.

    static void advertToggleClockFxn(void)
    {
        static uint8_t count = 0;
    
        user_enqueueRawAppMsg(APP_MSG_PRZ_ADV_TOGGLE_EVT,
                          (uint8_t *)&count, sizeof(count));
    
        count++;
    }

    That's it. With those changes and a DEFAULT_ADVERTISING_INTERVAL == 160 // 100ms  you can get 10s of advertisements and then 10s of no advertisements.

    The key here is to create a clock with a 10s period. When the clock is triggered, which will be in a SWI context, post an APP event outside of the SWI context so that we can call any functions we want. 

    Catch this APP event and perform the actual ENABLING/DISABLING of the advertisements. 

    Finally, restart the clock so that we can ENABLE/DISABLE the advertisements again in 10 seconds.

  • Hello,

    Your Post helped me a lot achieving results.

    I wanted to know that how do you measure on oscilloscope? where to connect +ve and -ve terminal of oscilloscope probe ? Is there any GPIO assigned for it ? 

    Because on BLE Scanner app I observed the functionality it is not showing linear 10sec period. There are some variations in on period & off period, Do you know something about this / why that is happening?

  • Chaitanya,

    I'm glad this has been helpful so far. I'm not using an oscilloscope but instead a logic analyzer. I actually linked the resource I used to add the GPIO triggering in my first post to this thread.  It is Debugging RF Output. Re read my first post to understand what I did in my test setup though.

    I use a saleae Logic 8 logic analyzer, however, you can find much cheaper clones of similar devices. I would highly recommend you pick up an analyzer as it allows you to make these measurements which will help you understand the behavior of your application. In particular I find it useful for characterizing RF behavior. 

    With regards to the BLE Scanner app not observing a perfect 10s period. The phone can not guarantee that it will catch every adv packet 100% of the time. That's why adv are periodic so that we can greater the chance that a central device will hear the adv while not spitting adv so often to where it is killing the battery too much.  Again, being able to accurately characterize RF output here will help you verify your understandings about what is actually happening. It sounds to me like the phone simply isn't catching every single adv packet and is only reporting to you what it finds. 

    Hope this helps!

    Best,

    Kris

  • Hello,

    I referred your code and had this changes in my code attached below.

    But still it is not working because the on time is very long and off time is just 1-2 seconds. I also observed it on LED (using Debugging RF Output post), I am not getting uniform periodic advertisement.

    Please suggest any changes or corrections. 

    Thank you.

    /*
     * Copyright (c) 2016, 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>
    
    //#define xdc_runtime_Log_DISABLE_ALL 1  // Add to disable logs from this file
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Queue.h>
    
    // externally added/////////////////////////////////////////////////
    #include <ti/sysbios/hal/Seconds.h>
    #include <time.h>
    #include <stdlib.h>
    
    ////////////////////////////////////////////////////////////////////
    #include <ti/drivers/PIN.h>
    #include <ti/mw/display/Display.h>
    
    
    
    #include <xdc/runtime/Log.h>
    #include <xdc/runtime/Diags.h>
    
    //externally added//////////////////////////////////////////////
    #include<xdc/runtime/System.h>
    ////////////////////////////////////////////////////////////////
    
    // Stack headers
    #include <hci_tl.h>
    #include <gap.h>
    #include <gatt.h>
    #include <gapgattserver.h>
    #include <gattservapp.h>
    #include <osal_snv.h>
    #include <gapbondmgr.h>
    #include <peripheral.h>
    #include <icall_apimsg.h>
    
    #include <devinfoservice.h>
    
    #include "util.h"
    
    #include "Board.h"
    #include "project_zero.h"
    
    // Bluetooth Developer Studio services
    #include "led_service.h"
    #include "button_service.h"
    #include "data_service.h"
    
    
    
    /*********************************************************************
     * CONSTANTS
     */
    // Advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL          160
    
    // Limited discoverable mode advertises for 30.72s, and then stops
    // General discoverable mode advertises indefinitely
    #define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
    
    // Default pass-code used for pairing.
    #define DEFAULT_PASSCODE                      000000
    
    // Task configuration
    #define PRZ_TASK_PRIORITY                     1
    
    #ifndef PRZ_TASK_STACK_SIZE
    #define PRZ_TASK_STACK_SIZE                   800
    #endif
    
    // Internal Events for RTOS application
    #define PRZ_STATE_CHANGE_EVT                  0x0001
    #define PRZ_CHAR_CHANGE_EVT                   0x0002
    #define PRZ_PERIODIC_EVT                      0x0004
    #define PRZ_CONN_EVT_END_EVT                  0x0008
    
    /*********************************************************************
     * TYPEDEFS
     */
    // Types of messages that can be sent to the user application task from other
    // tasks or interrupts. Note: Messages from BLE Stack are sent differently.
    typedef enum
    {
      APP_MSG_SERVICE_WRITE = 0,   /* A characteristic value has been written     */
      APP_MSG_SERVICE_CFG,         /* A characteristic configuration has changed  */
      APP_MSG_UPDATE_CHARVAL,      /* Request from ourselves to update a value    */
      APP_MSG_GAP_STATE_CHANGE,    /* The GAP / connection state has changed      */
      APP_MSG_BUTTON_DEBOUNCED,    /* A button has been debounced with new value  */
      APP_MSG_SEND_PASSCODE,       /* A pass-code/PIN is requested during pairing */
      APP_MSG_PRZ_ADV_TOGGLE_EVT,
    } app_msg_types_t;
    
    // Struct for messages sent to the application task
    typedef struct
    {
      Queue_Elem       _elem;
      app_msg_types_t  type;
      uint8_t          pdu[];
    } app_msg_t;
    
    // Struct for messages about characteristic data
    typedef struct
    {
      uint16_t svcUUID; // UUID of the service
      uint16_t dataLen; //
      uint8_t  paramID; // Index of the characteristic
      uint8_t  data[];  // Flexible array member, extended to malloc - sizeof(.)
    } char_data_t;
    
    // Struct for message about sending/requesting passcode from peer.
    typedef struct
    {
      uint16_t connHandle;
      uint8_t  uiInputs;
      uint8_t  uiOutputs;
      uint32   numComparison;
    } passcode_req_t;
    
    // Struct for message about button state
    typedef struct
    {
      PIN_Id   pinId;
      uint8_t  state;
    } button_state_t;
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Semaphore globally used to post events to the application thread
    static ICall_Semaphore sem;
    
    // Queue object used for application messages.
    static Queue_Struct applicationMsgQ;
    static Queue_Handle hApplicationMsgQ;
    
    extern uint32_t k1;
    extern uint8_t tempArr[10];
    // Task configuration
    Task_Struct przTask;
    Char przTaskStack[PRZ_TASK_STACK_SIZE];
    
    
    // GAP - SCAN RSP data (max size = 31 bytes)
    static uint8_t scanRspData[] =
    {
      // No scan response data provided.
      0x00 // Placeholder to keep the compiler happy.
    };
    
    // GAP - Advertisement data (max size = 31 bytes, though this is
    // best kept short to conserve power while advertisting)
    static uint8_t advertData[] =
    {
      // Flags; this sets the device to use limited discoverable
      // mode (advertises for 30 seconds at a time) or general
      // discoverable mode (advertises indefinitely), depending
      // on the DEFAULT_DISCOVERY_MODE define.
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // complete name
      13,
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'P', 'r', 'o', 'j', 'e', 'c', 't', ' ', 'Z', 'e', 'r', 'o',
    
    };
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Project Zero";
    
    // Globals used for ATT Response retransmission
    static gattMsgEvent_t *pAttRsp = NULL;
    static uint8_t rspTxRetry = 0;
    
    
    /* Pin driver handles */
    static PIN_Handle buttonPinHandle;
    static PIN_Handle ledPinHandle;
    
    /* Global memory storage for a PIN_Config table */
    static PIN_State buttonPinState;
    static PIN_State ledPinState;
    
    /*
     * Initial LED pin configuration table
     *   - LEDs Board_LED0 & Board_LED1 are off.
     */
    PIN_Config ledPinTable[] = {
      Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
      Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
      PIN_TERMINATE
    };
    
    /*
     * Application button pin configuration table:
     *   - Buttons interrupts are configured to trigger on falling edge.
     */
    PIN_Config buttonPinTable[] = {
        Board_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        Board_BUTTON1 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        PIN_TERMINATE
    };
    
    // Clock objects for debouncing the buttons
    static Clock_Struct button0DebounceClock;
    static Clock_Struct button1DebounceClock;
    static Clock_Struct advertToggleClock;
    
    // State of the buttons
    static uint8_t button0State = 0;
    static uint8_t button1State = 0;
    
    
    // Global display handle
    Display_Handle dispHandle;
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void ProjectZero_init( void );
    static void ProjectZero_taskFxn(UArg a0, UArg a1);
    
    static void user_processApplicationMessage(app_msg_t *pMsg);
    static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
    
    static void ProjectZero_sendAttRsp(void);
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
    static void ProjectZero_freeAttRsp(uint8_t status);
    
    static void user_processGapStateChangeEvt(gaprole_States_t newState);
    static void user_gapStateChangeCB(gaprole_States_t newState);
    static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                           uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison);
    static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state,
                                            uint8_t status);
    
    static void buttonDebounceSwiFxn(UArg buttonId);
    static void user_handleButtonPress(button_state_t *pState);
    
    // Generic callback handlers for value changes in services.
    static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len );
    static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len );
    
    // Task context handlers for generated services.
    static void user_LedService_ValueChangeHandler(char_data_t *pCharData);
    static void user_ButtonService_CfgChangeHandler(char_data_t *pCharData);
    static void user_DataService_ValueChangeHandler(char_data_t *pCharData);
    static void user_DataService_CfgChangeHandler(char_data_t *pCharData);
    
    // Task handler for sending notifications.
    static void user_updateCharVal(char_data_t *pCharData);
    
    // Utility functions
    static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData, uint16_t len );
    static void user_enqueueCharDataMsg(app_msg_types_t appMsgType, uint16_t connHandle,
                                        uint16_t serviceUUID, uint8_t paramID,
                                        uint8_t *pValue, uint16_t len);
    static void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId);
    
    static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len,
                                              uint8_t *dst, uint8_t dst_len);
    static char *Util_getLocalNameStr(const uint8_t *data);
    
    
    static char* getTimestamp(); //Externally added to get string of current time///////////////////////////
    
    static void advertToggleClockFxn(void); ///////////////////////////////////////////////////toggle clock function
    static uint8_t advertEnable = 0;    ////////////////////advertEnable variable
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // GAP Role Callbacks
    static gapRolesCBs_t user_gapRoleCBs =
    {
      user_gapStateChangeCB     // Profile State Change Callbacks
    };
    
    // GAP Bond Manager Callbacks
    static gapBondCBs_t user_bondMgrCBs =
    {
      user_gapBondMgr_passcodeCB, // Passcode callback
      user_gapBondMgr_pairStateCB // Pairing / Bonding state Callback
    };
    
    /*
     * Callbacks in the user application for events originating from BLE services.
     */
    // LED Service callback handler.
    // The type LED_ServiceCBs_t is defined in led_service.h
    static LedServiceCBs_t user_LED_ServiceCBs =
    {
      .pfnChangeCb    = user_service_ValueChangeCB, // Characteristic value change callback handler
      .pfnCfgChangeCb = NULL, // No notification-/indication enabled chars in LED Service
    };
    
    // Button Service callback handler.
    // The type Button_ServiceCBs_t is defined in button_service.h
    static ButtonServiceCBs_t user_Button_ServiceCBs =
    {
      .pfnChangeCb    = NULL, // No writable chars in Button Service, so no change handler.
      .pfnCfgChangeCb = user_service_CfgChangeCB, // Noti/ind configuration callback handler
    };
    
    // Data Service callback handler.
    // The type Data_ServiceCBs_t is defined in data_service.h
    static DataServiceCBs_t user_Data_ServiceCBs =
    {
      .pfnChangeCb    = user_service_ValueChangeCB, // Characteristic value change callback handler
      .pfnCfgChangeCb = user_service_CfgChangeCB, // Noti/ind configuration callback handler
    };
    
    
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*
     * @brief   Task creation function for the user task.
     *
     * @param   None.
     *
     * @return  None.
     */
    void ProjectZero_createTask(void)
    {
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = przTaskStack;
      taskParams.stackSize = PRZ_TASK_STACK_SIZE;
      taskParams.priority = PRZ_TASK_PRIORITY;
    
      Task_construct(&przTask, ProjectZero_taskFxn, &taskParams, NULL);
    }
    
    /*
     * @brief   Called before the task loop and contains application-specific
     *          initialization of the BLE stack, hardware setup, power-state
     *          notification if used, and BLE profile/service initialization.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void ProjectZero_init(void)
    {
    
      // ******************************************************************
      // NO STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages via ICall to Stack.
      ICall_registerApp(&selfEntity, &sem);
    
      Log_info0("Initializing the user task, hardware, BLE stack and services.");
    
      // Open display. By default this is disabled via the predefined symbol Display_DISABLE_ALL.
      dispHandle = Display_open(Display_Type_LCD, NULL);
    
      // Initialize queue for application messages.
      // Note: Used to transfer control to application thread from e.g. interrupts.
      Queue_construct(&applicationMsgQ, NULL);
      hApplicationMsgQ = Queue_handle(&applicationMsgQ);
    
      // ******************************************************************
      // Hardware initialization
      // ******************************************************************
    
      // Open LED pins
      ledPinHandle = PIN_open(&ledPinState, ledPinTable);
      if(!ledPinHandle) {
        Log_error0("Error initializing board LED pins");
        Task_exit();
      }
    
      buttonPinHandle = PIN_open(&buttonPinState, buttonPinTable);
      if(!buttonPinHandle) {
        Log_error0("Error initializing button pins");
        Task_exit();
      }
    
      // Setup callback for button pins
      if (PIN_registerIntCb(buttonPinHandle, &buttonCallbackFxn) != 0) {
        Log_error0("Error registering button callback function");
        Task_exit();
      }
    
      // Create the debounce clock objects for Button 0 and Button 1
      Clock_Params clockParams;
      Clock_Params_init(&clockParams);
    
      // Both clock objects use the same callback, so differentiate on argument
      // given to the callback in Swi context
      clockParams.arg = Board_BUTTON0;
    
      // Initialize to 50 ms timeout when Clock_start is called.
      // Timeout argument is in ticks, so convert from ms to ticks via tickPeriod.
      Clock_construct(&button0DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
      // Second button
      clockParams.arg = Board_BUTTON1;
      Clock_construct(&button1DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
    
      Clock_construct(&advertToggleClock, (ti_sysbios_knl_Clock_FuncPtr) advertToggleClockFxn,
                      10000 * (1000/Clock_tickPeriod), // Ten seconds
                      &clockParams);
    
      // ******************************************************************
      // BLE Stack initialization
      // ******************************************************************
    /*
      // Setup the GAP Peripheral Role Profile
      uint8_t initialAdvertEnable = TRUE;  // Advertise on power-up
    
      // By setting this to zero, the device will go into the waiting state after
      // being discoverable. Otherwise wait this long [ms] before advertising again.
      uint16_t advertOffTime = 0; // miliseconds
    
      // Set advertisement enabled.
      GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                           &initialAdvertEnable);
    
      // Configure the wait-time before restarting advertisement automatically
      GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
                           &advertOffTime);
    */
      Clock_start(Clock_handle(&advertToggleClock));
      // Initialize Scan Response data
      GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
    
      // Initialize Advertisement data
      GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
    
      Log_info1("Name in advertData array: \x1b[33m%s\x1b[0m",
                (IArg)Util_getLocalNameStr(advertData));
    
      // Set advertising interval
      uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
    
      GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt);
      GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt);
      GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt);
      GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt);
    
      // Set duration of advertisement before stopping in Limited adv mode.
     // GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, 30); // Seconds
    
      // ******************************************************************
      // BLE Bond Manager initialization
      // ******************************************************************
      uint32_t passkey = 0; // passkey "000000"
      uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
      uint8_t mitm = TRUE;
      uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
      uint8_t bonding = TRUE;
    
      GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t),
                              &passkey);
      GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
      GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
      GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
      GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
    
      // ******************************************************************
      // BLE Service initialization
      // ******************************************************************
    
      // Add services to GATT server
      GGS_AddService(GATT_ALL_SERVICES);           // GAP
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
      DevInfo_AddService();                        // Device Information Service
    
      // Set the device name characteristic in the GAP Profile
      GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
      // Add services to GATT server and give ID of this task for Indication acks.
      LedService_AddService( selfEntity );
      ButtonService_AddService( selfEntity );
      DataService_AddService( selfEntity );
    
    
      // Register callbacks with the generated services that
      // can generate events (writes received) to the application
      LedService_RegisterAppCBs( &user_LED_ServiceCBs );
      ButtonService_RegisterAppCBs( &user_Button_ServiceCBs );
      DataService_RegisterAppCBs( &user_Data_ServiceCBs );
    
    
      // Placeholder variable for characteristic intialization
      uint8_t initVal[40] = {0};
      //uint8_t initString[] = "This is a pretty long string, isn't it!";
      //int i;
      //for(i=0;i<40;i++)
      //{
    	 // System_printf("%s \t" , initString);
      //}
    
      // Initalization of characteristics in LED_Service that can provide data.
      LedService_SetParameter(LS_LED0_ID, LS_LED0_LEN, initVal);
      LedService_SetParameter(LS_LED1_ID, LS_LED1_LEN, initVal);
    
      // Initalization of characteristics in Button_Service that can provide data.
      ButtonService_SetParameter(BS_BUTTON0_ID, BS_BUTTON0_LEN, initVal);
      ButtonService_SetParameter(BS_BUTTON1_ID, BS_BUTTON1_LEN, initVal);
    
      // Initalization of characteristics in Data_Service that can provide data.
      // DataService_SetParameter(DS_STRING_ID, sizeof(initString), initString);
      DataService_SetParameter(DS_STREAM_ID, DS_STREAM_LEN, initVal);
    
      // Start the stack in Peripheral mode.
      VOID GAPRole_StartDevice(&user_gapRoleCBs);
    
      // Start Bond Manager
      VOID GAPBondMgr_Register(&user_bondMgrCBs);
    
      // Register with GAP for HCI/Host messages
      GAP_RegisterForMsgs(selfEntity);
    
      // Register for GATT local events and ATT Responses pending for transmission
      GATT_RegisterForMsgs(selfEntity);
    }
    
    
    /*
     * @brief   Application task entry point.
     *
     *          Invoked by TI-RTOS when BIOS_start is called. Calls an init function
     *          and enters an infinite loop waiting for messages.
     *
     *          Messages can be either directly from the BLE stack or from user code
     *          like Hardware Interrupt (Hwi) or a callback function.
     *
     *          The reason for sending messages to this task from e.g. Hwi's is that
     *          some RTOS and Stack APIs are not available in callbacks and so the
     *          actions that may need to be taken is dispatched to this Task.
     *
     * @param   a0, a1 - not used.
     *
     * @return  None.
     */
    
    
    static void ProjectZero_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      ProjectZero_init();
    
      // Application main loop
      for (;;)
      {
    	 char *curTime; //To get display on console
    	 curTime = getTimestamp(); // Getting display on console
    	 System_flush();
    
    	 /*
    	 uint_t i, number;
    	 uint8_t rstring[10] = {"1544786591"};
    	 for(i=0;i<10;i++)
    	 {
    		 System_printf("%d \t ", (int)rstring[i]);
    	 }
    
    	 number= atoi(rstring);
    	 System_printf("%d",number);
    
    	 */
    
    	 // DataService_SetParameter(DS_STRING_ID, 32, curTime);
    	  /*
    	  time_t t;
    	    struct tm *ltm;
    	    char *curTime;
    	    t = time(NULL);
    
    	    ltm = localtime(&t);
    	    curTime = asctime(ltm);
    
    	    System_printf("Time(GMT): %s\n", curTime);
    	    System_flush();
    
    	    DataService_SetParameter(DS_STRING_ID, 32, curTime);
    
    
    
    	    		  	  	 /* int  num, binary_val, decimal_val = 0,
    	    		  	  	          num=t;
    	    		  	  	          binary_val = num;
    	    		  	  	          while (num > 0)
    	    		  	  	          {
    	    		  	  	              rem = num % 10;
    	    		  	  	              decimal_val = decimal_val + rem * base;
    	    		  	  	              num = num / 10 ;
    	    		  	  	              base = base * 2;
    	    		  	  	          }
    
    	    		  	  	 */
    
    
    
        // Waits for a signal to the semaphore associated with the calling thread.
        // Note that the semaphore associated with a thread is signaled when a
        // message is queued to the message receive queue of the thread or when
        // ICall_signal() function is called onto the semaphore.
    
    	ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER);
    
        if (errno == ICALL_ERRNO_SUCCESS)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          // Check if we got a signal because of a stack message
          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 event flags received (event signature 0xffff)
              if (pEvt->signature == 0xffff)
              {
                // Event received when a connection event is completed
                if (pEvt->event_flag & PRZ_CONN_EVT_END_EVT)
                {
                  // Try to retransmit pending ATT Response (if any)
                  ProjectZero_sendAttRsp();
                }
              }
              else // It's a message from the stack and not an event.
              {
                // Process inter-task message
                safeToDealloc = ProjectZero_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // Process messages sent from another task or another context.
          while (!Queue_empty(hApplicationMsgQ))
          {
            app_msg_t *pMsg = Queue_dequeue(hApplicationMsgQ);
    
            // Process application-layer message probably sent from ourselves.
            user_processApplicationMessage(pMsg);
    
            // Free the received message.
            ICall_free(pMsg);
          }
        }
      }
    }
    
    // The timestamp function written externally for getting time string
     char* getTimestamp()
    {
    	        time_t t;
    		    struct tm *ltm;
    		    char *curTime;
    
    		    t = time(NULL);
    		    ltm = localtime(&t);
    		    curTime = asctime(ltm);
    		    System_printf("Time(IST): %s\n", curTime);
    		    return curTime;
    }
    
    
    /*
     * @brief   Handle application messages
     *
     *          These are messages not from the BLE stack, but from the
     *          application itself.
     *
     *          For example, in a Software Interrupt (Swi) it is not possible to
     *          call any BLE APIs, so instead the Swi function must send a message
     *          to the application Task for processing in Task context.
     *
     * @param   pMsg  Pointer to the message of type app_msg_t.
     *
     * @return  None.
     */
    static void user_processApplicationMessage(app_msg_t *pMsg)
    {
      char_data_t *pCharData = (char_data_t *)pMsg->pdu;
    
      switch (pMsg->type)
      {
        case APP_MSG_SERVICE_WRITE: /* Message about received value write */
          /* Call different handler per service */
          switch(pCharData->svcUUID) {
            case LED_SERVICE_SERV_UUID:
              user_LedService_ValueChangeHandler(pCharData);
              break;
            case DATA_SERVICE_SERV_UUID:
              user_DataService_ValueChangeHandler(pCharData);
              break;
    
          }
          break;
    
        case APP_MSG_SERVICE_CFG: /* Message about received CCCD write */
          /* Call different handler per service */
          switch(pCharData->svcUUID) {
            case BUTTON_SERVICE_SERV_UUID:
              user_ButtonService_CfgChangeHandler(pCharData);
              break;
            case DATA_SERVICE_SERV_UUID:
              user_DataService_CfgChangeHandler(pCharData);
              break;
    
          }
          break;
    
        case APP_MSG_UPDATE_CHARVAL: /* Message from ourselves to send  */
          user_updateCharVal(pCharData);
          break;
    
        case APP_MSG_GAP_STATE_CHANGE: /* Message that GAP state changed  */
          user_processGapStateChangeEvt( *(gaprole_States_t *)pMsg->pdu );
          break;
    
        case APP_MSG_SEND_PASSCODE: /* Message about pairing PIN request */
          {
            passcode_req_t *pReq = (passcode_req_t *)pMsg->pdu;
            Log_info2("BondMgr Requested passcode. We are %s passcode %06d",
                      (IArg)(pReq->uiInputs?"Sending":"Displaying"),
                      DEFAULT_PASSCODE);
            // Send passcode response.
            GAPBondMgr_PasscodeRsp(pReq->connHandle, SUCCESS, DEFAULT_PASSCODE);
          }
          break;
    
        case APP_MSG_BUTTON_DEBOUNCED: /* Message from swi about pin change */
          {
            button_state_t *pButtonState = (button_state_t *)pMsg->pdu;
            user_handleButtonPress(pButtonState);
          }
          break;
        case APP_MSG_PRZ_ADV_TOGGLE_EVT:
            {
            	uint8_t advertEnable=0;
                GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &advertEnable);
                advertEnable = !advertEnable;
                GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertEnable);
    
                Clock_start(Clock_handle(&advertToggleClock));
                break;
            }
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Handlers of system/application events deferred to the user Task context.
     *  Invoked from the application Task function above.
     *
     *  Further down you can find the callback handler section containing the
     *  functions that defer their actions via messages to the application task.
     *
     ****************************************************************************
     *****************************************************************************/
    
    
    /*
     * @brief   Process a pending GAP Role state change event.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void user_processGapStateChangeEvt(gaprole_States_t newState)
    {
      switch ( newState )
      {
        case GAPROLE_STARTED:
          {
            uint8_t ownAddress[B_ADDR_LEN];
            uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
    
            GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);
    
            // use 6 bytes of device address for 8 bytes of system ID value
            systemId[0] = ownAddress[0];
            systemId[1] = ownAddress[1];
            systemId[2] = ownAddress[2];
    
            // set middle bytes to zero
            systemId[4] = 0x00;
            systemId[3] = 0x00;
    
            // shift three bytes up
            systemId[7] = ownAddress[5];
            systemId[6] = ownAddress[4];
            systemId[5] = ownAddress[3];
    
            DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
    
            // Display device address
            char *cstr_ownAddress = Util_convertBdAddr2Str(ownAddress);
            Log_info1("GAP is started. Our address: \x1b[32m%s\x1b[0m", (IArg)cstr_ownAddress);
          }
          break;
    
        case GAPROLE_ADVERTISING:
          Log_info0("Advertising");
          break;
    
        case GAPROLE_CONNECTED:
          {
            uint8_t peerAddress[B_ADDR_LEN];
    
            GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);
    
            char *cstr_peerAddress = Util_convertBdAddr2Str(peerAddress);
            Log_info1("Connected. Peer address: \x1b[32m%s\x1b[0m", (IArg)cstr_peerAddress);
           }
          break;
    
        case GAPROLE_CONNECTED_ADV:
          Log_info0("Connected and advertising");
          break;
    
        case GAPROLE_WAITING:
          Log_info0("Disconnected / Idle");
          break;
    
        case GAPROLE_WAITING_AFTER_TIMEOUT:
          Log_info0("Connection timed out");
          break;
    
        case GAPROLE_ERROR:
          Log_info0("Error");
          break;
    
        default:
          break;
      }
    }
    
    
    /*
     * @brief   Handle a debounced button press or release in Task context.
     *          Invoked by the taskFxn based on a message received from a callback.
     *
     * @see     buttonDebounceSwiFxn
     * @see     buttonCallbackFxn
     *
     * @param   pState  pointer to button_state_t message sent from debounce Swi.
     *
     * @return  None.
     */
    static void user_handleButtonPress(button_state_t *pState)
    {
      Log_info2("%s %s",
        (IArg)(pState->pinId == Board_BUTTON0?"Button 0":"Button 1"),
        (IArg)(pState->state?"\x1b[32mpressed\x1b[0m":
                             "\x1b[33mreleased\x1b[0m"));
    
      // Update the service with the new value.
      // Will automatically send notification/indication if enabled.
      switch (pState->pinId)
      {
        case Board_BUTTON0:
          ButtonService_SetParameter(BS_BUTTON0_ID,
                                     sizeof(pState->state),
                                     &pState->state);
          break;
        case Board_BUTTON1:
          ButtonService_SetParameter(BS_BUTTON1_ID,
                                     sizeof(pState->state),
                                     &pState->state);
          break;
      }
    }
    
    static void advertToggleClockFxn(void)  ////////////////////////////////////toggleClock function
    {
        static uint8_t count = 0;
    
        user_enqueueRawAppMsg(APP_MSG_PRZ_ADV_TOGGLE_EVT,
                          (uint8_t *)&count, sizeof(count));
    
        count++;
    }
    /*
     * @brief   Handle a write request sent from a peer device.
     *
     *          Invoked by the Task based on a message received from a callback.
     *
     *          When we get here, the request has already been accepted by the
     *          service and is valid from a BLE protocol perspective as well as
     *          having the correct length as defined in the service implementation.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_LedService_ValueChangeHandler(char_data_t *pCharData)
    {
      static uint8_t pretty_data_holder[16]; // 5 bytes as hex string "AA:BB:CC:DD:EE"
      Util_convertArrayToHexString(pCharData->data, pCharData->dataLen,
                                   pretty_data_holder, sizeof(pretty_data_holder));
    
      switch (pCharData->paramID)
      {
        case LS_LED0_ID:
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"LED Service",
                    (IArg)"LED0",
                    (IArg)pretty_data_holder);
    
          // Do something useful with pCharData->data here
          // -------------------------
          // Set the output value equal to the received value. 0 is off, not 0 is on
          PIN_setOutputValue(ledPinHandle, Board_LED0, pCharData->data[0]);
          Log_info2("Turning %s %s",
                    (IArg)"\x1b[31mLED0\x1b[0m",
                    (IArg)(pCharData->data[0]?"on":"off"));
          break;
    
        case LS_LED1_ID:
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"LED Service",
                    (IArg)"LED1",
                    (IArg)pretty_data_holder);
    
          // Do something useful with pCharData->data here
          // -------------------------
          // Set the output value equal to the received value. 0 is off, not 0 is on
          PIN_setOutputValue(ledPinHandle, Board_LED1, pCharData->data[0]);
          Log_info2("Turning %s %s",
                    (IArg)"\x1b[32mLED1\x1b[0m",
                    (IArg)(pCharData->data[0]?"on":"off"));
          break;
    
      default:
        return;
      }
    }
    
    
    /*
     * @brief   Handle a CCCD (configuration change) write received from a peer
     *          device. This tells us whether the peer device wants us to send
     *          Notifications or Indications.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_ButtonService_CfgChangeHandler(char_data_t *pCharData)
    {
      // Cast received data to uint16, as that's the format for CCCD writes.
      uint16_t configValue = *(uint16_t *)pCharData->data;
      char *configValString;
    
      // Determine what to tell the user
      switch(configValue)
      {
      case GATT_CFG_NO_OPERATION:
        configValString = "Noti/Ind disabled";
        break;
      case GATT_CLIENT_CFG_NOTIFY:
        configValString = "Notifications enabled";
        break;
      case GATT_CLIENT_CFG_INDICATE:
        configValString = "Indications enabled";
        break;
      }
    
      switch (pCharData->paramID)
      {
        case BS_BUTTON0_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Button Service",
                    (IArg)"BUTTON0",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
    
        case BS_BUTTON1_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Button Service",
                    (IArg)"BUTTON1",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
      }
    }
    
    /*
     * @brief   Handle a write request sent from a peer device.
     *
     *          Invoked by the Task based on a message received from a callback.
     *
     *          When we get here, the request has already been accepted by the
     *          service and is valid from a BLE protocol perspective as well as
     *          having the correct length as defined in the service implementation.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_DataService_ValueChangeHandler(char_data_t *pCharData)
    {
      // Value to hold the received string for printing via Log, as Log printouts
      // happen in the Idle task, and so need to refer to a global/static variable.
      static uint8_t received_string[DS_STRING_LEN] = {0};
      int sec_num;
    
      switch (pCharData->paramID)
      {
        case DS_STRING_ID:
         // Do something useful with pCharData->data here
          // -------------------------
          // Copy received data to holder array, ensuring NULL termination.
          memset(received_string, 0, DS_STRING_LEN);
          memcpy(received_string, pCharData->data, DS_STRING_LEN-1);
    
    ///////////////////This function takes that distributed ASCII data from data elements and club them in single integer//////////////////////////
          sec_num= atoi(pCharData->data);
          Seconds_set(sec_num);
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
          // Needed to copy before log statement, as the holder array remains after
          // the pCharData message has been freed and reused for something else.
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"Data Service",
                    (IArg)"String",
                    (IArg)received_string);
          break;
    
        	case DS_STREAM_ID:
        	Log_info3("Value Change msg: Data Service Stream: %02x:%02x:%02x...",
                    (IArg)pCharData->data[0],
                    (IArg)pCharData->data[1],
                    (IArg)pCharData->data[2]);
          // -------------------------
          // Do something useful with pCharData->data here
          break;
    
      default:
        return;
      }
    }
    
    /*
     * @brief   Handle a CCCD (configuration change) write received from a peer
     *          device. This tells us whether the peer device wants us to send
     *          Notifications or Indications.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_DataService_CfgChangeHandler(char_data_t *pCharData)
    {
      // Cast received data to uint16, as that's the format for CCCD writes.
      uint16_t configValue = *(uint16_t *)pCharData->data;
      char *configValString;
    
      // Determine what to tell the user
      switch(configValue)
      {
      case GATT_CFG_NO_OPERATION:
        configValString = "Noti/Ind disabled";
        break;
      case GATT_CLIENT_CFG_NOTIFY:
        configValString = "Notifications enabled";
        break;
      case GATT_CLIENT_CFG_INDICATE:
        configValString = "Indications enabled";
        break;
      }
    
      switch (pCharData->paramID)
      {
        case DS_STREAM_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Data Service",
                    (IArg)"Stream",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
      }
    }
    
    /*
     * @brief   Process an incoming BLE stack message.
     *
     *          This could be a GATT message from a peer device like acknowledgement
     *          of an Indication we sent, or it could be a response from the stack
     *          to an HCI message that the user application sent.
     *
     * @param   pMsg - message to process
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = ProjectZero_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 Event
                Log_info0("HCI Command Complete Event received");
                break;
    
              default:
                break;
            }
          }
          break;
    
        default:
          // do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    
    /*
     * @brief   Process GATT messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      // See if GATT server was unable to transmit an ATT response
      if (pMsg->hdr.status == blePending)
      {
        Log_warning1("Outgoing RF FIFO full. Re-schedule transmission of msg with opcode 0x%02x",
          pMsg->method);
    
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if (HCI_EXT_ConnEventNoticeCmd(pMsg->connHandle, selfEntity,
                                       PRZ_CONN_EVT_END_EVT) == SUCCESS)
        {
          // First free any pending response
          ProjectZero_freeAttRsp(FAILURE);
    
          // Hold on to the response message for retransmission
          pAttRsp = pMsg;
    
          // Don't free the response message yet
          return (FALSE);
        }
      }
      else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
      {
        // ATT request-response or indication-confirmation flow control is
        // violated. All subsequent ATT requests or indications will be dropped.
        // The app is informed in case it wants to drop the connection.
    
        // Log the opcode of the message that caused the violation.
        Log_error1("Flow control violated. Opcode of offending ATT msg: 0x%02x",
          pMsg->msg.flowCtrlEvt.opcode);
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
        Log_info1("MTU Size change: %d bytes", pMsg->msg.mtuEvt.MTU);
      }
      else
      {
        // Got an expected GATT message from a peer.
        Log_info1("Recevied GATT Message. Opcode: 0x%02x", pMsg->method);
      }
    
      // 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);
    }
    
    
    
    
    /*
     *  Application error handling functions
     *****************************************************************************/
    
    /*
     * @brief   Send a pending ATT response message.
     *
     *          The message is one that the stack was trying to send based on a
     *          peer request, but the response couldn't be sent because the
     *          user application had filled the TX queue with other data.
     *
     * @param   none
     *
     * @return  none
     */
    static void ProjectZero_sendAttRsp(void)
    {
      // See if there's a pending ATT Response to be transmitted
      if (pAttRsp != NULL)
      {
        uint8_t status;
    
        // Increment retransmission count
        rspTxRetry++;
    
        // Try to retransmit ATT response till either we're successful or
        // the ATT Client times out (after 30s) and drops the connection.
        status = GATT_SendRsp(pAttRsp->connHandle, pAttRsp->method, &(pAttRsp->msg));
        if ((status != blePending) && (status != MSG_BUFFER_NOT_AVAIL))
        {
          // Disable connection event end notice
          HCI_EXT_ConnEventNoticeCmd(pAttRsp->connHandle, selfEntity, 0);
    
          // We're done with the response message
          ProjectZero_freeAttRsp(status);
        }
        else
        {
          // Continue retrying
          Log_warning2("Retrying message with opcode 0x%02x. Attempt %d",
            pAttRsp->method, rspTxRetry);
        }
      }
    }
    
    /*
     * @brief   Free ATT response message.
     *
     * @param   status - response transmit status
     *
     * @return  none
     */
    static void ProjectZero_freeAttRsp(uint8_t status)
    {
      // See if there's a pending ATT response message
      if (pAttRsp != NULL)
      {
        // See if the response was sent out successfully
        if (status == SUCCESS)
        {
          Log_info2("Sent message with opcode 0x%02x. Attempt %d",
            pAttRsp->method, rspTxRetry);
        }
        else
        {
          Log_error2("Gave up message with opcode 0x%02x. Status: %d",
            pAttRsp->method, status);
    
          // Free response payload
          GATT_bm_free(&pAttRsp->msg, pAttRsp->method);
        }
    
        // Free response message
        ICall_freeMsg(pAttRsp);
    
        // Reset our globals
        pAttRsp = NULL;
        rspTxRetry = 0;
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Handlers of direct system callbacks.
     *
     *  Typically enqueue the information or request as a message for the
     *  application Task for handling.
     *
     ****************************************************************************
     *****************************************************************************/
    
    
    /*
     *  Callbacks from the Stack Task context (GAP or Service changes)
     *****************************************************************************/
    
    /**
     * Callback from GAP Role indicating a role state change.
     */
    static void user_gapStateChangeCB(gaprole_States_t newState)
    {
      Log_info1("(CB) GAP State change: %d, Sending msg to app.", (IArg)newState);
      user_enqueueRawAppMsg( APP_MSG_GAP_STATE_CHANGE, (uint8_t *)&newState, sizeof(newState) );
    }
    
    /*
     * @brief   Passcode callback.
     *
     * @param   connHandle - connection handle
     * @param   uiInputs   - input passcode?
     * @param   uiOutputs  - display passcode?
     * @param   numComparison - numeric comparison value
     *
     * @return  none
     */
    static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                           uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison)
    {
      passcode_req_t req =
      {
        .connHandle = connHandle,
        .uiInputs = uiInputs,
        .uiOutputs = uiOutputs,
        .numComparison = numComparison
      };
    
      // Defer handling of the passcode request to the application, in case
      // user input is required, and because a BLE API must be used from Task.
      user_enqueueRawAppMsg(APP_MSG_SEND_PASSCODE, (uint8_t *)&req, sizeof(req));
    }
    
    /*
     * @brief   Pairing state callback.
     *
     * @param   connHandle - connection handle
     * @param   state      - pairing state
     * @param   status     - pairing status
     *
     * @return  none
     */
    static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state,
                                            uint8_t status)
    {
      if (state == GAPBOND_PAIRING_STATE_STARTED)
      {
        Log_info0("Pairing started");
      }
      else if (state == GAPBOND_PAIRING_STATE_COMPLETE)
      {
        if (status == SUCCESS)
        {
          Log_info0("Pairing completed successfully.");
        }
        else
        {
          Log_error1("Pairing failed. Error: %02x", status);
        }
      }
      else if (state == GAPBOND_PAIRING_STATE_BONDED)
      {
        if (status == SUCCESS)
        {
         Log_info0("Re-established pairing from stored bond info.");
        }
      }
    }
    
    /**
     * Callback handler for characteristic value changes in services.
     */
    static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid,
                                            uint8_t paramID, uint8_t *pValue,
                                            uint16_t len )
    {
    
    
      // See the service header file to compare paramID with characteristic.
      Log_info2("(CB) Characteristic value change: svc(0x%04x) paramID(%d). "
                "Sending msg to app.", (IArg)svcUuid, (IArg)paramID);
    
    
      user_enqueueCharDataMsg(APP_MSG_SERVICE_WRITE, connHandle, svcUuid, paramID,
                              pValue, len);
    }
    
    /**
     * Callback handler for characteristic configuration changes in services.
     */
    static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid,
                                          uint8_t paramID, uint8_t *pValue,
                                          uint16_t len )
    {
      Log_info2("(CB) Char config change: svc(0x%04x) paramID(%d). "
                "Sending msg to app.", (IArg)svcUuid, (IArg)paramID);
      user_enqueueCharDataMsg(APP_MSG_SERVICE_CFG, connHandle, svcUuid,
                              paramID, pValue, len);
    }
    
    /*
     *  Callbacks from Swi-context
     *****************************************************************************/
    
    /*
     * @brief  Callback from Clock module on timeout
     *
     *         Determines new state after debouncing
     *
     * @param  buttonId    The pin being debounced
     */
    static void buttonDebounceSwiFxn(UArg buttonId)
    {
      // Used to send message to app
      button_state_t buttonMsg = { .pinId = buttonId };
      uint8_t        sendMsg   = FALSE;
    
      // Get current value of the button pin after the clock timeout
      uint8_t buttonPinVal = PIN_getInputValue(buttonId);
    
      // Set interrupt direction to opposite of debounced state
      // If button is now released (button is active low, so release is high)
      if (buttonPinVal)
      {
        // Enable negative edge interrupts to wait for press
        PIN_setConfig(buttonPinHandle, PIN_BM_IRQ, buttonId | PIN_IRQ_NEGEDGE);
      }
      else
      {
        // Enable positive edge interrupts to wait for relesae
        PIN_setConfig(buttonPinHandle, PIN_BM_IRQ, buttonId | PIN_IRQ_POSEDGE);
      }
    
      switch(buttonId)
      {
        case Board_BUTTON0:
          // If button is now released (buttonPinVal is active low, so release is 1)
          // and button state was pressed (buttonstate is active high so press is 1)
          if (buttonPinVal && button0State)
          {
            // Button was released
            buttonMsg.state = button0State = 0;
            sendMsg = TRUE;
          }
          else if (!buttonPinVal && !button0State)
          {
            // Button was pressed
            buttonMsg.state = button0State = 1;
            sendMsg = TRUE;
          }
          break;
    
        case Board_BUTTON1:
          // If button is now released (buttonPinVal is active low, so release is 1)
          // and button state was pressed (buttonstate is active high so press is 1)
          if (buttonPinVal && button1State)
          {
            // Button was released
            buttonMsg.state = button1State = 0;
            sendMsg = TRUE;
          }
          else if (!buttonPinVal && !button1State)
          {
            // Button was pressed
            buttonMsg.state = button1State = 1;
            sendMsg = TRUE;
          }
          break;
      }
    
      if (sendMsg == TRUE)
      {
        user_enqueueRawAppMsg(APP_MSG_BUTTON_DEBOUNCED,
                          (uint8_t *)&buttonMsg, sizeof(buttonMsg));
      }
    }
    
    /*
     *  Callbacks from Hwi-context
     *****************************************************************************/
    
    /*
     * @brief  Callback from PIN driver on interrupt
     *
     *         Sets in motion the debouncing.
     *
     * @param  handle    The PIN_Handle instance this is about
     * @param  pinId     The pin that generated the interrupt
     */
    static void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId)
    {
      Log_info1("Button interrupt: %s",
                (IArg)((pinId == Board_BUTTON0)?"Button 0":"Button 1"));
    
      // Disable interrupt on that pin for now. Re-enabled after debounce.
      PIN_setConfig(handle, PIN_BM_IRQ, pinId | PIN_IRQ_DIS);
    
      // Start debounce timer
      switch (pinId)
      {
        case Board_BUTTON0:
          Clock_start(Clock_handle(&button0DebounceClock));
          break;
        case Board_BUTTON1:
          Clock_start(Clock_handle(&button1DebounceClock));
          break;
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Utility functions
     *
     ****************************************************************************
     *****************************************************************************/
    
    /*
     * @brief  Generic message constructor for characteristic data.
     *
     *         Sends a message to the application for handling in Task context where
     *         the message payload is a char_data_t struct.
     *
     *         From service callbacks the appMsgType is APP_MSG_SERVICE_WRITE or
     *         APP_MSG_SERVICE_CFG, and functions running in another context than
     *         the Task itself, can set the type to APP_MSG_UPDATE_CHARVAL to
     *         make the user Task loop invoke user_updateCharVal function for them.
     *
     * @param  appMsgType    Enumerated type of message being sent.
     * @param  connHandle    GAP Connection handle of the relevant connection
     * @param  serviceUUID   16-bit part of the relevant service UUID
     * @param  paramID       Index of the characteristic in the service
     * @oaram  *pValue       Pointer to characteristic value
     * @param  len           Length of characteristic data
     */
    static void user_enqueueCharDataMsg( app_msg_types_t appMsgType,
                                         uint16_t connHandle,
                                         uint16_t serviceUUID, uint8_t paramID,
                                         uint8_t *pValue, uint16_t len )
    {
      // Called in Stack's Task context, so can't do processing here.
      // Send message to application message queue about received data.
      uint16_t readLen = len; // How much data was written to the attribute
    
      // Allocate memory for the message.
      // Note: The pCharData message doesn't have to contain the data itself, as
      //       that's stored in a variable in the service implementation.
      //
      //       However, to prevent data loss if a new value is received before the
      //       service's container is read out via the GetParameter API is called,
      //       we copy the characteristic's data now.
      app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + sizeof(char_data_t) +
                                      readLen );
    
      if (pMsg != NULL)
      {
        pMsg->type = appMsgType;
    
        char_data_t *pCharData = (char_data_t *)pMsg->pdu;
        pCharData->svcUUID = serviceUUID; // Use 16-bit part of UUID.
        pCharData->paramID = paramID;
        // Copy data from service now.
        memcpy(pCharData->data, pValue, readLen);
        // Update pCharData with how much data we received.
        pCharData->dataLen = readLen;
        // Enqueue the message using pointer to queue node element.
        Queue_enqueue(hApplicationMsgQ, &pMsg->_elem);
        // Let application know there's a message.
        Semaphore_post(sem);
      }
    }
    
    /*
     * @brief  Generic message constructor for application messages.
     *
     *         Sends a message to the application for handling in Task context.
     *
     * @param  appMsgType    Enumerated type of message being sent.
     * @oaram  *pValue       Pointer to characteristic value
     * @param  len           Length of characteristic data
     */
    static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData,
                                      uint16_t len)
    {
      // Allocate memory for the message.
      app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + len );
    
      if (pMsg != NULL)
      {
        pMsg->type = appMsgType;
    
        // Copy data into message
        memcpy(pMsg->pdu, pData, len);
    
        // Enqueue the message using pointer to queue node element.
        Queue_enqueue(hApplicationMsgQ, &pMsg->_elem);
        // Let application know there's a message.
        Semaphore_post(sem);
      }
    }
    
    
    /*
     * @brief  Convenience function for updating characteristic data via char_data_t
     *         structured message.
     *
     * @note   Must run in Task context in case BLE Stack APIs are invoked.
     *
     * @param  *pCharData  Pointer to struct with value to update.
     */
    static void user_updateCharVal(char_data_t *pCharData)
    {
    
    
      switch(pCharData->svcUUID) {
        case LED_SERVICE_SERV_UUID:
          LedService_SetParameter(pCharData->paramID, pCharData->dataLen,
                                  pCharData->data);
        break;
    
        case BUTTON_SERVICE_SERV_UUID:
          ButtonService_SetParameter(pCharData->paramID, pCharData->dataLen,
                                     pCharData->data);
        break;
    
       case DATA_SERVICE_SERV_UUID:
    
        	DataService_SetParameter(pCharData->paramID, pCharData->dataLen,
                    pCharData->data);
        	break;
      }
    }
    
    /*
     * @brief   Convert {0x01, 0x02} to "01:02"
     *
     * @param   src - source byte-array
     * @param   src_len - length of array
     * @param   dst - destination string-array
     * @param   dst_len - length of array
     *
     * @return  array as string
     */
    static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len,
                                              uint8_t *dst, uint8_t dst_len)
    {
      char        hex[] = "0123456789ABCDEF";
      uint8_t     *pStr = dst;
      uint8_t     avail = dst_len-1;
    
      memset(dst, 0, avail);
    
      while (src_len && avail > 3)
      {
        if (avail < dst_len-1) { *pStr++ = ':'; avail -= 1; };
        *pStr++ = hex[*src >> 4];
        *pStr++ = hex[*src++ & 0x0F];
        avail -= 2;
        src_len--;
      }
    
      if (src_len && avail)
        *pStr++ = ':'; // Indicate not all data fit on line.
    
      return (char *)dst;
    }
    
    /*
     * @brief   Extract the LOCALNAME from Scan/AdvData
     *
     * @param   data - Pointer to the advertisement or scan response data
     *
     * @return  Pointer to null-terminated string with the adv local name.
     */
    static char *Util_getLocalNameStr(const uint8_t *data) {
      uint8_t nuggetLen = 0;
      uint8_t nuggetType = 0;
      uint8_t advIdx = 0;
    
      static char localNameStr[32] = { 0 };
      memset(localNameStr, 0, sizeof(localNameStr));
    
      for (advIdx = 0; advIdx < 32;) {
        nuggetLen = data[advIdx++];
        nuggetType = data[advIdx];
        if ( (nuggetType == GAP_ADTYPE_LOCAL_NAME_COMPLETE ||
              nuggetType == GAP_ADTYPE_LOCAL_NAME_SHORT) && nuggetLen < 31) {
          memcpy(localNameStr, &data[advIdx + 1], nuggetLen - 1);
          break;
        } else {
          advIdx += nuggetLen;
        }
      }
    
      return localNameStr;
    }
    
    /*********************************************************************
    *********************************************************************/
    
     

  • Chaitanya,

    I will look into your issue further. While I do that. Can I ask that you start from a fresh project with no changes other than what I've explained above? I want to make sure there are no other changes that could be effecting the results.

    Best,
    Kris
  • /*
     * Copyright (c) 2016, 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>
    
    //#define xdc_runtime_Log_DISABLE_ALL 1  // Add to disable logs from this file
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Queue.h>
    
    #include <ti/drivers/PIN.h>
    #include <ti/mw/display/Display.h>
    
    #include <xdc/runtime/Log.h>
    #include <xdc/runtime/Diags.h>
    
    // Stack headers
    #include <hci_tl.h>
    #include <gap.h>
    #include <gatt.h>
    #include <gapgattserver.h>
    #include <gattservapp.h>
    #include <osal_snv.h>
    #include <gapbondmgr.h>
    #include <peripheral.h>
    #include <icall_apimsg.h>
    
    #include <devinfoservice.h>
    
    #include "util.h"
    
    #include "Board.h"
    #include "project_zero.h"
    
    // Bluetooth Developer Studio services
    #include "led_service.h"
    #include "button_service.h"
    #include "data_service.h"
    
    
    /*********************************************************************
     * CONSTANTS
     */
    // Advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL          160
    
    // Limited discoverable mode advertises for 30.72s, and then stops
    // General discoverable mode advertises indefinitely
    #define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
    
    // Default pass-code used for pairing.
    #define DEFAULT_PASSCODE                      000000
    
    // Task configuration
    #define PRZ_TASK_PRIORITY                     1
    
    #ifndef PRZ_TASK_STACK_SIZE
    #define PRZ_TASK_STACK_SIZE                   800
    #endif
    
    // Internal Events for RTOS application
    #define PRZ_STATE_CHANGE_EVT                  0x0001
    #define PRZ_CHAR_CHANGE_EVT                   0x0002
    #define PRZ_PERIODIC_EVT                      0x0004
    #define PRZ_CONN_EVT_END_EVT                  0x0008
    
    /*********************************************************************
     * TYPEDEFS
     */
    // Types of messages that can be sent to the user application task from other
    // tasks or interrupts. Note: Messages from BLE Stack are sent differently.
    typedef enum
    {
      APP_MSG_SERVICE_WRITE = 0,   /* A characteristic value has been written     */
      APP_MSG_SERVICE_CFG,         /* A characteristic configuration has changed  */
      APP_MSG_UPDATE_CHARVAL,      /* Request from ourselves to update a value    */
      APP_MSG_GAP_STATE_CHANGE,    /* The GAP / connection state has changed      */
      APP_MSG_BUTTON_DEBOUNCED,    /* A button has been debounced with new value  */
      APP_MSG_SEND_PASSCODE,       /* A pass-code/PIN is requested during pairing */
      APP_MSG_PRZ_ADV_TOGGLE_EVT,
    } app_msg_types_t;
    
    // Struct for messages sent to the application task
    typedef struct
    {
      Queue_Elem       _elem;
      app_msg_types_t  type;
      uint8_t          pdu[];
    } app_msg_t;
    
    // Struct for messages about characteristic data
    typedef struct
    {
      uint16_t svcUUID; // UUID of the service
      uint16_t dataLen; //
      uint8_t  paramID; // Index of the characteristic
      uint8_t  data[];  // Flexible array member, extended to malloc - sizeof(.)
    } char_data_t;
    
    // Struct for message about sending/requesting passcode from peer.
    typedef struct
    {
      uint16_t connHandle;
      uint8_t  uiInputs;
      uint8_t  uiOutputs;
      uint32   numComparison;
    } passcode_req_t;
    
    // Struct for message about button state
    typedef struct
    {
      PIN_Id   pinId;
      uint8_t  state;
    } button_state_t;
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Semaphore globally used to post events to the application thread
    static ICall_Semaphore sem;
    
    // Queue object used for application messages.
    static Queue_Struct applicationMsgQ;
    static Queue_Handle hApplicationMsgQ;
    
    // Task configuration
    Task_Struct przTask;
    Char przTaskStack[PRZ_TASK_STACK_SIZE];
    
    
    // GAP - SCAN RSP data (max size = 31 bytes)
    static uint8_t scanRspData[] =
    {
      // No scan response data provided.
      0x00 // Placeholder to keep the compiler happy.
    };
    
    // GAP - Advertisement data (max size = 31 bytes, though this is
    // best kept short to conserve power while advertisting)
    static uint8_t advertData[] =
    {
      // Flags; this sets the device to use limited discoverable
      // mode (advertises for 30 seconds at a time) or general
      // discoverable mode (advertises indefinitely), depending
      // on the DEFAULT_DISCOVERY_MODE define.
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // complete name
      13,
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'P', 'r', 'o', 'j', 'e', 'c', 't', ' ', 'Z', 'e', 'r', 'o',
    
    };
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Project Zero";
    
    // Globals used for ATT Response retransmission
    static gattMsgEvent_t *pAttRsp = NULL;
    static uint8_t rspTxRetry = 0;
    
    
    /* Pin driver handles */
    static PIN_Handle buttonPinHandle;
    static PIN_Handle ledPinHandle;
    
    /* Global memory storage for a PIN_Config table */
    static PIN_State buttonPinState;
    static PIN_State ledPinState;
    
    /*
     * Initial LED pin configuration table
     *   - LEDs Board_LED0 & Board_LED1 are off.
     */
    PIN_Config ledPinTable[] = {
      Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
      Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
      PIN_TERMINATE
    };
    
    /*
     * Application button pin configuration table:
     *   - Buttons interrupts are configured to trigger on falling edge.
     */
    PIN_Config buttonPinTable[] = {
        Board_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        Board_BUTTON1 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        PIN_TERMINATE
    };
    
    // Clock objects for debouncing the buttons
    static Clock_Struct button0DebounceClock;
    static Clock_Struct button1DebounceClock;
    static Clock_Struct advertToggleClock;
    
    // State of the buttons
    static uint8_t button0State = 0;
    static uint8_t button1State = 0;
    
    // Global display handle
    Display_Handle dispHandle;
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void ProjectZero_init( void );
    static void ProjectZero_taskFxn(UArg a0, UArg a1);
    
    static void user_processApplicationMessage(app_msg_t *pMsg);
    static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
    
    static void ProjectZero_sendAttRsp(void);
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
    static void ProjectZero_freeAttRsp(uint8_t status);
    
    static void user_processGapStateChangeEvt(gaprole_States_t newState);
    static void user_gapStateChangeCB(gaprole_States_t newState);
    static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                           uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison);
    static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state,
                                            uint8_t status);
    
    static void buttonDebounceSwiFxn(UArg buttonId);
    static void user_handleButtonPress(button_state_t *pState);
    
    // Generic callback handlers for value changes in services.
    static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len );
    static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len );
    
    // Task context handlers for generated services.
    static void user_LedService_ValueChangeHandler(char_data_t *pCharData);
    static void user_ButtonService_CfgChangeHandler(char_data_t *pCharData);
    static void user_DataService_ValueChangeHandler(char_data_t *pCharData);
    static void user_DataService_CfgChangeHandler(char_data_t *pCharData);
    
    // Task handler for sending notifications.
    static void user_updateCharVal(char_data_t *pCharData);
    
    // Utility functions
    static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData, uint16_t len );
    static void user_enqueueCharDataMsg(app_msg_types_t appMsgType, uint16_t connHandle,
                                        uint16_t serviceUUID, uint8_t paramID,
                                        uint8_t *pValue, uint16_t len);
    static void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId);
    
    static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len,
                                              uint8_t *dst, uint8_t dst_len);
    static char *Util_getLocalNameStr(const uint8_t *data);
    
    
    static void advertToggleClockFxn(void); ///////////////////////////////////////////////////toggle clock function
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // GAP Role Callbacks
    static gapRolesCBs_t user_gapRoleCBs =
    {
      user_gapStateChangeCB     // Profile State Change Callbacks
    };
    
    // GAP Bond Manager Callbacks
    static gapBondCBs_t user_bondMgrCBs =
    {
      user_gapBondMgr_passcodeCB, // Passcode callback
      user_gapBondMgr_pairStateCB // Pairing / Bonding state Callback
    };
    
    /*
     * Callbacks in the user application for events originating from BLE services.
     */
    // LED Service callback handler.
    // The type LED_ServiceCBs_t is defined in led_service.h
    static LedServiceCBs_t user_LED_ServiceCBs =
    {
      .pfnChangeCb    = user_service_ValueChangeCB, // Characteristic value change callback handler
      .pfnCfgChangeCb = NULL, // No notification-/indication enabled chars in LED Service
    };
    
    // Button Service callback handler.
    // The type Button_ServiceCBs_t is defined in button_service.h
    static ButtonServiceCBs_t user_Button_ServiceCBs =
    {
      .pfnChangeCb    = NULL, // No writable chars in Button Service, so no change handler.
      .pfnCfgChangeCb = user_service_CfgChangeCB, // Noti/ind configuration callback handler
    };
    
    // Data Service callback handler.
    // The type Data_ServiceCBs_t is defined in data_service.h
    static DataServiceCBs_t user_Data_ServiceCBs =
    {
      .pfnChangeCb    = user_service_ValueChangeCB, // Characteristic value change callback handler
      .pfnCfgChangeCb = user_service_CfgChangeCB, // Noti/ind configuration callback handler
    };
    
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*
     * @brief   Task creation function for the user task.
     *
     * @param   None.
     *
     * @return  None.
     */
    void ProjectZero_createTask(void)
    {
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = przTaskStack;
      taskParams.stackSize = PRZ_TASK_STACK_SIZE;
      taskParams.priority = PRZ_TASK_PRIORITY;
    
      Task_construct(&przTask, ProjectZero_taskFxn, &taskParams, NULL);
    }
    
    /*
     * @brief   Called before the task loop and contains application-specific
     *          initialization of the BLE stack, hardware setup, power-state
     *          notification if used, and BLE profile/service initialization.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void ProjectZero_init(void)
    {
      // ******************************************************************
      // NO STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages via ICall to Stack.
      ICall_registerApp(&selfEntity, &sem);
    
      Log_info0("Initializing the user task, hardware, BLE stack and services.");
    
      // Open display. By default this is disabled via the predefined symbol Display_DISABLE_ALL.
      dispHandle = Display_open(Display_Type_LCD, NULL);
    
      // Initialize queue for application messages.
      // Note: Used to transfer control to application thread from e.g. interrupts.
      Queue_construct(&applicationMsgQ, NULL);
      hApplicationMsgQ = Queue_handle(&applicationMsgQ);
    
      // ******************************************************************
      // Hardware initialization
      // ******************************************************************
    
      // Open LED pins
      ledPinHandle = PIN_open(&ledPinState, ledPinTable);
      if(!ledPinHandle) {
        Log_error0("Error initializing board LED pins");
        Task_exit();
      }
    
      buttonPinHandle = PIN_open(&buttonPinState, buttonPinTable);
      if(!buttonPinHandle) {
        Log_error0("Error initializing button pins");
        Task_exit();
      }
    
      // Setup callback for button pins
      if (PIN_registerIntCb(buttonPinHandle, &buttonCallbackFxn) != 0) {
        Log_error0("Error registering button callback function");
        Task_exit();
      }
    
      // Create the debounce clock objects for Button 0 and Button 1
      Clock_Params clockParams;
      Clock_Params_init(&clockParams);
    
      // Both clock objects use the same callback, so differentiate on argument
      // given to the callback in Swi context
      clockParams.arg = Board_BUTTON0;
    
      // Initialize to 50 ms timeout when Clock_start is called.
      // Timeout argument is in ticks, so convert from ms to ticks via tickPeriod.
      Clock_construct(&button0DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
      // Second button
      clockParams.arg = Board_BUTTON1;
      Clock_construct(&button1DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
    
      Clock_construct(&advertToggleClock, (ti_sysbios_knl_Clock_FuncPtr) advertToggleClockFxn,
                      10000 * (1000/Clock_tickPeriod), // Ten seconds
                      &clockParams);
    
      // ******************************************************************
      // BLE Stack initialization
      // ******************************************************************
    /*
      // Setup the GAP Peripheral Role Profile
      uint8_t initialAdvertEnable = TRUE;  // Advertise on power-up
    
      // By setting this to zero, the device will go into the waiting state after
      // being discoverable. Otherwise wait this long [ms] before advertising again.
      uint16_t advertOffTime = 0; // miliseconds
    
      // Set advertisement enabled.
      GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                           &initialAdvertEnable);
    
      // Configure the wait-time before restarting advertisement automatically
      GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
                           &advertOffTime);
    */
      Clock_start(Clock_handle(&advertToggleClock));
      // Initialize Scan Response data
      GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
    
      // Initialize Advertisement data
      GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
    
      Log_info1("Name in advertData array: \x1b[33m%s\x1b[0m",
                (IArg)Util_getLocalNameStr(advertData));
    
      // Set advertising interval
      uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
    
      GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt);
      GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt);
      GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt);
      GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt);
    
      // Set duration of advertisement before stopping in Limited adv mode.
     // GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, 30); // Seconds
    
      // ******************************************************************
      // BLE Bond Manager initialization
      // ******************************************************************
      uint32_t passkey = 0; // passkey "000000"
      uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
      uint8_t mitm = TRUE;
      uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
      uint8_t bonding = TRUE;
    
      GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t),
                              &passkey);
      GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
      GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
      GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
      GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
    
      // ******************************************************************
      // BLE Service initialization
      // ******************************************************************
    
      // Add services to GATT server
      GGS_AddService(GATT_ALL_SERVICES);           // GAP
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
      DevInfo_AddService();                        // Device Information Service
    
      // Set the device name characteristic in the GAP Profile
      GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
      // Add services to GATT server and give ID of this task for Indication acks.
      LedService_AddService( selfEntity );
      ButtonService_AddService( selfEntity );
      DataService_AddService( selfEntity );
    
      // Register callbacks with the generated services that
      // can generate events (writes received) to the application
      LedService_RegisterAppCBs( &user_LED_ServiceCBs );
      ButtonService_RegisterAppCBs( &user_Button_ServiceCBs );
      DataService_RegisterAppCBs( &user_Data_ServiceCBs );
    
      // Placeholder variable for characteristic intialization
      uint8_t initVal[40] = {0};
      uint8_t initString[] = "This is a pretty long string, isn't it!";
    
      // Initalization of characteristics in LED_Service that can provide data.
      LedService_SetParameter(LS_LED0_ID, LS_LED0_LEN, initVal);
      LedService_SetParameter(LS_LED1_ID, LS_LED1_LEN, initVal);
    
      // Initalization of characteristics in Button_Service that can provide data.
      ButtonService_SetParameter(BS_BUTTON0_ID, BS_BUTTON0_LEN, initVal);
      ButtonService_SetParameter(BS_BUTTON1_ID, BS_BUTTON1_LEN, initVal);
    
      // Initalization of characteristics in Data_Service that can provide data.
      DataService_SetParameter(DS_STRING_ID, sizeof(initString), initString);
      DataService_SetParameter(DS_STREAM_ID, DS_STREAM_LEN, initVal);
    
      // Start the stack in Peripheral mode.
      VOID GAPRole_StartDevice(&user_gapRoleCBs);
    
      // Start Bond Manager
      VOID GAPBondMgr_Register(&user_bondMgrCBs);
    
      // Register with GAP for HCI/Host messages
      GAP_RegisterForMsgs(selfEntity);
    
      // Register for GATT local events and ATT Responses pending for transmission
      GATT_RegisterForMsgs(selfEntity);
    }
    
    
    /*
     * @brief   Application task entry point.
     *
     *          Invoked by TI-RTOS when BIOS_start is called. Calls an init function
     *          and enters an infinite loop waiting for messages.
     *
     *          Messages can be either directly from the BLE stack or from user code
     *          like Hardware Interrupt (Hwi) or a callback function.
     *
     *          The reason for sending messages to this task from e.g. Hwi's is that
     *          some RTOS and Stack APIs are not available in callbacks and so the
     *          actions that may need to be taken is dispatched to this Task.
     *
     * @param   a0, a1 - not used.
     *
     * @return  None.
     */
    static void ProjectZero_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      ProjectZero_init();
    
      // Application main loop
      for (;;)
      {
        // Waits for a signal to the semaphore associated with the calling thread.
        // Note that the semaphore associated with a thread is signaled when a
        // message is queued to the message receive queue of the thread or when
        // ICall_signal() function is called onto the semaphore.
        ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER);
    
        if (errno == ICALL_ERRNO_SUCCESS)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          // Check if we got a signal because of a stack message
          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 event flags received (event signature 0xffff)
              if (pEvt->signature == 0xffff)
              {
                // Event received when a connection event is completed
                if (pEvt->event_flag & PRZ_CONN_EVT_END_EVT)
                {
                  // Try to retransmit pending ATT Response (if any)
                  ProjectZero_sendAttRsp();
                }
              }
              else // It's a message from the stack and not an event.
              {
                // Process inter-task message
                safeToDealloc = ProjectZero_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // Process messages sent from another task or another context.
          while (!Queue_empty(hApplicationMsgQ))
          {
            app_msg_t *pMsg = Queue_dequeue(hApplicationMsgQ);
    
            // Process application-layer message probably sent from ourselves.
            user_processApplicationMessage(pMsg);
    
            // Free the received message.
            ICall_free(pMsg);
          }
        }
      }
    }
    
    
    /*
     * @brief   Handle application messages
     *
     *          These are messages not from the BLE stack, but from the
     *          application itself.
     *
     *          For example, in a Software Interrupt (Swi) it is not possible to
     *          call any BLE APIs, so instead the Swi function must send a message
     *          to the application Task for processing in Task context.
     *
     * @param   pMsg  Pointer to the message of type app_msg_t.
     *
     * @return  None.
     */
    static void user_processApplicationMessage(app_msg_t *pMsg)
    {
      char_data_t *pCharData = (char_data_t *)pMsg->pdu;
    
      switch (pMsg->type)
      {
        case APP_MSG_SERVICE_WRITE: /* Message about received value write */
          /* Call different handler per service */
          switch(pCharData->svcUUID) {
            case LED_SERVICE_SERV_UUID:
              user_LedService_ValueChangeHandler(pCharData);
              break;
            case DATA_SERVICE_SERV_UUID:
              user_DataService_ValueChangeHandler(pCharData);
              break;
    
          }
          break;
    
        case APP_MSG_SERVICE_CFG: /* Message about received CCCD write */
          /* Call different handler per service */
          switch(pCharData->svcUUID) {
            case BUTTON_SERVICE_SERV_UUID:
              user_ButtonService_CfgChangeHandler(pCharData);
              break;
            case DATA_SERVICE_SERV_UUID:
              user_DataService_CfgChangeHandler(pCharData);
              break;
          }
          break;
    
        case APP_MSG_UPDATE_CHARVAL: /* Message from ourselves to send  */
          user_updateCharVal(pCharData);
          break;
    
        case APP_MSG_GAP_STATE_CHANGE: /* Message that GAP state changed  */
          user_processGapStateChangeEvt( *(gaprole_States_t *)pMsg->pdu );
          break;
    
        case APP_MSG_SEND_PASSCODE: /* Message about pairing PIN request */
          {
            passcode_req_t *pReq = (passcode_req_t *)pMsg->pdu;
            Log_info2("BondMgr Requested passcode. We are %s passcode %06d",
                      (IArg)(pReq->uiInputs?"Sending":"Displaying"),
                      DEFAULT_PASSCODE);
            // Send passcode response.
            GAPBondMgr_PasscodeRsp(pReq->connHandle, SUCCESS, DEFAULT_PASSCODE);
          }
          break;
    
        case APP_MSG_BUTTON_DEBOUNCED: /* Message from swi about pin change */
          {
            button_state_t *pButtonState = (button_state_t *)pMsg->pdu;
            user_handleButtonPress(pButtonState);
          }
          break;
        case APP_MSG_PRZ_ADV_TOGGLE_EVT:
            {
                uint8_t advertEnable=0;
                GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &advertEnable);
                advertEnable = !advertEnable;
                GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertEnable);
    
                Clock_start(Clock_handle(&advertToggleClock));
                break;
            }
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Handlers of system/application events deferred to the user Task context.
     *  Invoked from the application Task function above.
     *
     *  Further down you can find the callback handler section containing the
     *  functions that defer their actions via messages to the application task.
     *
     ****************************************************************************
     *****************************************************************************/
    
    
    /*
     * @brief   Process a pending GAP Role state change event.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void user_processGapStateChangeEvt(gaprole_States_t newState)
    {
      switch ( newState )
      {
        case GAPROLE_STARTED:
          {
            uint8_t ownAddress[B_ADDR_LEN];
            uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
    
            GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);
    
            // use 6 bytes of device address for 8 bytes of system ID value
            systemId[0] = ownAddress[0];
            systemId[1] = ownAddress[1];
            systemId[2] = ownAddress[2];
    
            // set middle bytes to zero
            systemId[4] = 0x00;
            systemId[3] = 0x00;
    
            // shift three bytes up
            systemId[7] = ownAddress[5];
            systemId[6] = ownAddress[4];
            systemId[5] = ownAddress[3];
    
            DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
    
            // Display device address
            char *cstr_ownAddress = Util_convertBdAddr2Str(ownAddress);
            Log_info1("GAP is started. Our address: \x1b[32m%s\x1b[0m", (IArg)cstr_ownAddress);
          }
          break;
    
        case GAPROLE_ADVERTISING:
          Log_info0("Advertising");
          break;
    
        case GAPROLE_CONNECTED:
          {
            uint8_t peerAddress[B_ADDR_LEN];
    
            GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);
    
            char *cstr_peerAddress = Util_convertBdAddr2Str(peerAddress);
            Log_info1("Connected. Peer address: \x1b[32m%s\x1b[0m", (IArg)cstr_peerAddress);
           }
          break;
    
        case GAPROLE_CONNECTED_ADV:
          Log_info0("Connected and advertising");
          break;
    
        case GAPROLE_WAITING:
          Log_info0("Disconnected / Idle");
          break;
    
        case GAPROLE_WAITING_AFTER_TIMEOUT:
          Log_info0("Connection timed out");
          break;
    
        case GAPROLE_ERROR:
          Log_info0("Error");
          break;
    
        default:
          break;
      }
    }
    
    
    /*
     * @brief   Handle a debounced button press or release in Task context.
     *          Invoked by the taskFxn based on a message received from a callback.
     *
     * @see     buttonDebounceSwiFxn
     * @see     buttonCallbackFxn
     *
     * @param   pState  pointer to button_state_t message sent from debounce Swi.
     *
     * @return  None.
     */
    static void user_handleButtonPress(button_state_t *pState)
    {
      Log_info2("%s %s",
        (IArg)(pState->pinId == Board_BUTTON0?"Button 0":"Button 1"),
        (IArg)(pState->state?"\x1b[32mpressed\x1b[0m":
                             "\x1b[33mreleased\x1b[0m"));
    
      // Update the service with the new value.
      // Will automatically send notification/indication if enabled.
      switch (pState->pinId)
      {
        case Board_BUTTON0:
          ButtonService_SetParameter(BS_BUTTON0_ID,
                                     sizeof(pState->state),
                                     &pState->state);
          break;
        case Board_BUTTON1:
          ButtonService_SetParameter(BS_BUTTON1_ID,
                                     sizeof(pState->state),
                                     &pState->state);
          break;
      }
    }
    
    static void advertToggleClockFxn(void)  ////////////////////////////////////toggleClock function
    {
        static uint8_t count = 0;
    
        user_enqueueRawAppMsg(APP_MSG_PRZ_ADV_TOGGLE_EVT,
                          (uint8_t *)&count, sizeof(count));
    
        count++;
    }
    /*
     * @brief   Handle a write request sent from a peer device.
     *
     *          Invoked by the Task based on a message received from a callback.
     *
     *          When we get here, the request has already been accepted by the
     *          service and is valid from a BLE protocol perspective as well as
     *          having the correct length as defined in the service implementation.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_LedService_ValueChangeHandler(char_data_t *pCharData)
    {
      static uint8_t pretty_data_holder[16]; // 5 bytes as hex string "AA:BB:CC:DD:EE"
      Util_convertArrayToHexString(pCharData->data, pCharData->dataLen,
                                   pretty_data_holder, sizeof(pretty_data_holder));
    
      switch (pCharData->paramID)
      {
        case LS_LED0_ID:
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"LED Service",
                    (IArg)"LED0",
                    (IArg)pretty_data_holder);
    
          // Do something useful with pCharData->data here
          // -------------------------
          // Set the output value equal to the received value. 0 is off, not 0 is on
          PIN_setOutputValue(ledPinHandle, Board_LED0, pCharData->data[0]);
          Log_info2("Turning %s %s",
                    (IArg)"\x1b[31mLED0\x1b[0m",
                    (IArg)(pCharData->data[0]?"on":"off"));
          break;
    
        case LS_LED1_ID:
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"LED Service",
                    (IArg)"LED1",
                    (IArg)pretty_data_holder);
    
          // Do something useful with pCharData->data here
          // -------------------------
          // Set the output value equal to the received value. 0 is off, not 0 is on
          PIN_setOutputValue(ledPinHandle, Board_LED1, pCharData->data[0]);
          Log_info2("Turning %s %s",
                    (IArg)"\x1b[32mLED1\x1b[0m",
                    (IArg)(pCharData->data[0]?"on":"off"));
          break;
    
      default:
        return;
      }
    }
    
    
    /*
     * @brief   Handle a CCCD (configuration change) write received from a peer
     *          device. This tells us whether the peer device wants us to send
     *          Notifications or Indications.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_ButtonService_CfgChangeHandler(char_data_t *pCharData)
    {
      // Cast received data to uint16, as that's the format for CCCD writes.
      uint16_t configValue = *(uint16_t *)pCharData->data;
      char *configValString;
    
      // Determine what to tell the user
      switch(configValue)
      {
      case GATT_CFG_NO_OPERATION:
        configValString = "Noti/Ind disabled";
        break;
      case GATT_CLIENT_CFG_NOTIFY:
        configValString = "Notifications enabled";
        break;
      case GATT_CLIENT_CFG_INDICATE:
        configValString = "Indications enabled";
        break;
      }
    
      switch (pCharData->paramID)
      {
        case BS_BUTTON0_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Button Service",
                    (IArg)"BUTTON0",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
    
        case BS_BUTTON1_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Button Service",
                    (IArg)"BUTTON1",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
      }
    }
    
    /*
     * @brief   Handle a write request sent from a peer device.
     *
     *          Invoked by the Task based on a message received from a callback.
     *
     *          When we get here, the request has already been accepted by the
     *          service and is valid from a BLE protocol perspective as well as
     *          having the correct length as defined in the service implementation.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_DataService_ValueChangeHandler(char_data_t *pCharData)
    {
      // Value to hold the received string for printing via Log, as Log printouts
      // happen in the Idle task, and so need to refer to a global/static variable.
      static uint8_t received_string[DS_STRING_LEN] = {0};
    
      switch (pCharData->paramID)
      {
        case DS_STRING_ID:
          // Do something useful with pCharData->data here
          // -------------------------
          // Copy received data to holder array, ensuring NULL termination.
          memset(received_string, 0, DS_STRING_LEN);
          memcpy(received_string, pCharData->data, DS_STRING_LEN-1);
          // Needed to copy before log statement, as the holder array remains after
          // the pCharData message has been freed and reused for something else.
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"Data Service",
                    (IArg)"String",
                    (IArg)received_string);
          break;
    
        case DS_STREAM_ID:
          Log_info3("Value Change msg: Data Service Stream: %02x:%02x:%02x...",
                    (IArg)pCharData->data[0],
                    (IArg)pCharData->data[1],
                    (IArg)pCharData->data[2]);
          // -------------------------
          // Do something useful with pCharData->data here
          break;
    
      default:
        return;
      }
    }
    
    /*
     * @brief   Handle a CCCD (configuration change) write received from a peer
     *          device. This tells us whether the peer device wants us to send
     *          Notifications or Indications.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_DataService_CfgChangeHandler(char_data_t *pCharData)
    {
      // Cast received data to uint16, as that's the format for CCCD writes.
      uint16_t configValue = *(uint16_t *)pCharData->data;
      char *configValString;
    
      // Determine what to tell the user
      switch(configValue)
      {
      case GATT_CFG_NO_OPERATION:
        configValString = "Noti/Ind disabled";
        break;
      case GATT_CLIENT_CFG_NOTIFY:
        configValString = "Notifications enabled";
        break;
      case GATT_CLIENT_CFG_INDICATE:
        configValString = "Indications enabled";
        break;
      }
    
      switch (pCharData->paramID)
      {
        case DS_STREAM_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Data Service",
                    (IArg)"Stream",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
      }
    }
    
    
    /*
     * @brief   Process an incoming BLE stack message.
     *
     *          This could be a GATT message from a peer device like acknowledgement
     *          of an Indication we sent, or it could be a response from the stack
     *          to an HCI message that the user application sent.
     *
     * @param   pMsg - message to process
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = ProjectZero_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 Event
                Log_info0("HCI Command Complete Event received");
                break;
    
              default:
                break;
            }
          }
          break;
    
        default:
          // do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    
    /*
     * @brief   Process GATT messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      // See if GATT server was unable to transmit an ATT response
      if (pMsg->hdr.status == blePending)
      {
        Log_warning1("Outgoing RF FIFO full. Re-schedule transmission of msg with opcode 0x%02x",
          pMsg->method);
    
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if (HCI_EXT_ConnEventNoticeCmd(pMsg->connHandle, selfEntity,
                                       PRZ_CONN_EVT_END_EVT) == SUCCESS)
        {
          // First free any pending response
          ProjectZero_freeAttRsp(FAILURE);
    
          // Hold on to the response message for retransmission
          pAttRsp = pMsg;
    
          // Don't free the response message yet
          return (FALSE);
        }
      }
      else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
      {
        // ATT request-response or indication-confirmation flow control is
        // violated. All subsequent ATT requests or indications will be dropped.
        // The app is informed in case it wants to drop the connection.
    
        // Log the opcode of the message that caused the violation.
        Log_error1("Flow control violated. Opcode of offending ATT msg: 0x%02x",
          pMsg->msg.flowCtrlEvt.opcode);
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
        Log_info1("MTU Size change: %d bytes", pMsg->msg.mtuEvt.MTU);
      }
      else
      {
        // Got an expected GATT message from a peer.
        Log_info1("Recevied GATT Message. Opcode: 0x%02x", pMsg->method);
      }
    
      // 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);
    }
    
    
    
    
    /*
     *  Application error handling functions
     *****************************************************************************/
    
    /*
     * @brief   Send a pending ATT response message.
     *
     *          The message is one that the stack was trying to send based on a
     *          peer request, but the response couldn't be sent because the
     *          user application had filled the TX queue with other data.
     *
     * @param   none
     *
     * @return  none
     */
    static void ProjectZero_sendAttRsp(void)
    {
      // See if there's a pending ATT Response to be transmitted
      if (pAttRsp != NULL)
      {
        uint8_t status;
    
        // Increment retransmission count
        rspTxRetry++;
    
        // Try to retransmit ATT response till either we're successful or
        // the ATT Client times out (after 30s) and drops the connection.
        status = GATT_SendRsp(pAttRsp->connHandle, pAttRsp->method, &(pAttRsp->msg));
        if ((status != blePending) && (status != MSG_BUFFER_NOT_AVAIL))
        {
          // Disable connection event end notice
          HCI_EXT_ConnEventNoticeCmd(pAttRsp->connHandle, selfEntity, 0);
    
          // We're done with the response message
          ProjectZero_freeAttRsp(status);
        }
        else
        {
          // Continue retrying
          Log_warning2("Retrying message with opcode 0x%02x. Attempt %d",
            pAttRsp->method, rspTxRetry);
        }
      }
    }
    
    /*
     * @brief   Free ATT response message.
     *
     * @param   status - response transmit status
     *
     * @return  none
     */
    static void ProjectZero_freeAttRsp(uint8_t status)
    {
      // See if there's a pending ATT response message
      if (pAttRsp != NULL)
      {
        // See if the response was sent out successfully
        if (status == SUCCESS)
        {
          Log_info2("Sent message with opcode 0x%02x. Attempt %d",
            pAttRsp->method, rspTxRetry);
        }
        else
        {
          Log_error2("Gave up message with opcode 0x%02x. Status: %d",
            pAttRsp->method, status);
    
          // Free response payload
          GATT_bm_free(&pAttRsp->msg, pAttRsp->method);
        }
    
        // Free response message
        ICall_freeMsg(pAttRsp);
    
        // Reset our globals
        pAttRsp = NULL;
        rspTxRetry = 0;
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Handlers of direct system callbacks.
     *
     *  Typically enqueue the information or request as a message for the
     *  application Task for handling.
     *
     ****************************************************************************
     *****************************************************************************/
    
    
    /*
     *  Callbacks from the Stack Task context (GAP or Service changes)
     *****************************************************************************/
    
    /**
     * Callback from GAP Role indicating a role state change.
     */
    static void user_gapStateChangeCB(gaprole_States_t newState)
    {
      Log_info1("(CB) GAP State change: %d, Sending msg to app.", (IArg)newState);
      user_enqueueRawAppMsg( APP_MSG_GAP_STATE_CHANGE, (uint8_t *)&newState, sizeof(newState) );
    }
    
    /*
     * @brief   Passcode callback.
     *
     * @param   connHandle - connection handle
     * @param   uiInputs   - input passcode?
     * @param   uiOutputs  - display passcode?
     * @param   numComparison - numeric comparison value
     *
     * @return  none
     */
    static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                           uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison)
    {
      passcode_req_t req =
      {
        .connHandle = connHandle,
        .uiInputs = uiInputs,
        .uiOutputs = uiOutputs,
        .numComparison = numComparison
      };
    
      // Defer handling of the passcode request to the application, in case
      // user input is required, and because a BLE API must be used from Task.
      user_enqueueRawAppMsg(APP_MSG_SEND_PASSCODE, (uint8_t *)&req, sizeof(req));
    }
    
    /*
     * @brief   Pairing state callback.
     *
     * @param   connHandle - connection handle
     * @param   state      - pairing state
     * @param   status     - pairing status
     *
     * @return  none
     */
    static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state,
                                            uint8_t status)
    {
      if (state == GAPBOND_PAIRING_STATE_STARTED)
      {
        Log_info0("Pairing started");
      }
      else if (state == GAPBOND_PAIRING_STATE_COMPLETE)
      {
        if (status == SUCCESS)
        {
          Log_info0("Pairing completed successfully.");
        }
        else
        {
          Log_error1("Pairing failed. Error: %02x", status);
        }
      }
      else if (state == GAPBOND_PAIRING_STATE_BONDED)
      {
        if (status == SUCCESS)
        {
         Log_info0("Re-established pairing from stored bond info.");
        }
      }
    }
    
    /**
     * Callback handler for characteristic value changes in services.
     */
    static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid,
                                            uint8_t paramID, uint8_t *pValue,
                                            uint16_t len )
    {
      // See the service header file to compare paramID with characteristic.
      Log_info2("(CB) Characteristic value change: svc(0x%04x) paramID(%d). "
                "Sending msg to app.", (IArg)svcUuid, (IArg)paramID);
      user_enqueueCharDataMsg(APP_MSG_SERVICE_WRITE, connHandle, svcUuid, paramID,
                              pValue, len);
    }
    
    /**
     * Callback handler for characteristic configuration changes in services.
     */
    static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid,
                                          uint8_t paramID, uint8_t *pValue,
                                          uint16_t len )
    {
      Log_info2("(CB) Char config change: svc(0x%04x) paramID(%d). "
                "Sending msg to app.", (IArg)svcUuid, (IArg)paramID);
      user_enqueueCharDataMsg(APP_MSG_SERVICE_CFG, connHandle, svcUuid,
                              paramID, pValue, len);
    }
    
    /*
     *  Callbacks from Swi-context
     *****************************************************************************/
    
    /*
     * @brief  Callback from Clock module on timeout
     *
     *         Determines new state after debouncing
     *
     * @param  buttonId    The pin being debounced
     */
    static void buttonDebounceSwiFxn(UArg buttonId)
    {
      // Used to send message to app
      button_state_t buttonMsg = { .pinId = buttonId };
      uint8_t        sendMsg   = FALSE;
    
      // Get current value of the button pin after the clock timeout
      uint8_t buttonPinVal = PIN_getInputValue(buttonId);
    
      // Set interrupt direction to opposite of debounced state
      // If button is now released (button is active low, so release is high)
      if (buttonPinVal)
      {
        // Enable negative edge interrupts to wait for press
        PIN_setConfig(buttonPinHandle, PIN_BM_IRQ, buttonId | PIN_IRQ_NEGEDGE);
      }
      else
      {
        // Enable positive edge interrupts to wait for relesae
        PIN_setConfig(buttonPinHandle, PIN_BM_IRQ, buttonId | PIN_IRQ_POSEDGE);
      }
    
      switch(buttonId)
      {
        case Board_BUTTON0:
          // If button is now released (buttonPinVal is active low, so release is 1)
          // and button state was pressed (buttonstate is active high so press is 1)
          if (buttonPinVal && button0State)
          {
            // Button was released
            buttonMsg.state = button0State = 0;
            sendMsg = TRUE;
          }
          else if (!buttonPinVal && !button0State)
          {
            // Button was pressed
            buttonMsg.state = button0State = 1;
            sendMsg = TRUE;
          }
          break;
    
        case Board_BUTTON1:
          // If button is now released (buttonPinVal is active low, so release is 1)
          // and button state was pressed (buttonstate is active high so press is 1)
          if (buttonPinVal && button1State)
          {
            // Button was released
            buttonMsg.state = button1State = 0;
            sendMsg = TRUE;
          }
          else if (!buttonPinVal && !button1State)
          {
            // Button was pressed
            buttonMsg.state = button1State = 1;
            sendMsg = TRUE;
          }
          break;
      }
    
      if (sendMsg == TRUE)
      {
        user_enqueueRawAppMsg(APP_MSG_BUTTON_DEBOUNCED,
                          (uint8_t *)&buttonMsg, sizeof(buttonMsg));
      }
    }
    
    /*
     *  Callbacks from Hwi-context
     *****************************************************************************/
    
    /*
     * @brief  Callback from PIN driver on interrupt
     *
     *         Sets in motion the debouncing.
     *
     * @param  handle    The PIN_Handle instance this is about
     * @param  pinId     The pin that generated the interrupt
     */
    static void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId)
    {
      Log_info1("Button interrupt: %s",
                (IArg)((pinId == Board_BUTTON0)?"Button 0":"Button 1"));
    
      // Disable interrupt on that pin for now. Re-enabled after debounce.
      PIN_setConfig(handle, PIN_BM_IRQ, pinId | PIN_IRQ_DIS);
    
      // Start debounce timer
      switch (pinId)
      {
        case Board_BUTTON0:
          Clock_start(Clock_handle(&button0DebounceClock));
          break;
        case Board_BUTTON1:
          Clock_start(Clock_handle(&button1DebounceClock));
          break;
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Utility functions
     *
     ****************************************************************************
     *****************************************************************************/
    
    /*
     * @brief  Generic message constructor for characteristic data.
     *
     *         Sends a message to the application for handling in Task context where
     *         the message payload is a char_data_t struct.
     *
     *         From service callbacks the appMsgType is APP_MSG_SERVICE_WRITE or
     *         APP_MSG_SERVICE_CFG, and functions running in another context than
     *         the Task itself, can set the type to APP_MSG_UPDATE_CHARVAL to
     *         make the user Task loop invoke user_updateCharVal function for them.
     *
     * @param  appMsgType    Enumerated type of message being sent.
     * @param  connHandle    GAP Connection handle of the relevant connection
     * @param  serviceUUID   16-bit part of the relevant service UUID
     * @param  paramID       Index of the characteristic in the service
     * @oaram  *pValue       Pointer to characteristic value
     * @param  len           Length of characteristic data
     */
    static void user_enqueueCharDataMsg( app_msg_types_t appMsgType,
                                         uint16_t connHandle,
                                         uint16_t serviceUUID, uint8_t paramID,
                                         uint8_t *pValue, uint16_t len )
    {
      // Called in Stack's Task context, so can't do processing here.
      // Send message to application message queue about received data.
      uint16_t readLen = len; // How much data was written to the attribute
    
      // Allocate memory for the message.
      // Note: The pCharData message doesn't have to contain the data itself, as
      //       that's stored in a variable in the service implementation.
      //
      //       However, to prevent data loss if a new value is received before the
      //       service's container is read out via the GetParameter API is called,
      //       we copy the characteristic's data now.
      app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + sizeof(char_data_t) +
                                      readLen );
    
      if (pMsg != NULL)
      {
        pMsg->type = appMsgType;
    
        char_data_t *pCharData = (char_data_t *)pMsg->pdu;
        pCharData->svcUUID = serviceUUID; // Use 16-bit part of UUID.
        pCharData->paramID = paramID;
        // Copy data from service now.
        memcpy(pCharData->data, pValue, readLen);
        // Update pCharData with how much data we received.
        pCharData->dataLen = readLen;
        // Enqueue the message using pointer to queue node element.
        Queue_enqueue(hApplicationMsgQ, &pMsg->_elem);
        // Let application know there's a message.
        Semaphore_post(sem);
      }
    }
    
    /*
     * @brief  Generic message constructor for application messages.
     *
     *         Sends a message to the application for handling in Task context.
     *
     * @param  appMsgType    Enumerated type of message being sent.
     * @oaram  *pValue       Pointer to characteristic value
     * @param  len           Length of characteristic data
     */
    static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData,
                                      uint16_t len)
    {
      // Allocate memory for the message.
      app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + len );
    
      if (pMsg != NULL)
      {
        pMsg->type = appMsgType;
    
        // Copy data into message
        memcpy(pMsg->pdu, pData, len);
    
        // Enqueue the message using pointer to queue node element.
        Queue_enqueue(hApplicationMsgQ, &pMsg->_elem);
        // Let application know there's a message.
        Semaphore_post(sem);
      }
    }
    
    
    /*
     * @brief  Convenience function for updating characteristic data via char_data_t
     *         structured message.
     *
     * @note   Must run in Task context in case BLE Stack APIs are invoked.
     *
     * @param  *pCharData  Pointer to struct with value to update.
     */
    static void user_updateCharVal(char_data_t *pCharData)
    {
      switch(pCharData->svcUUID) {
        case LED_SERVICE_SERV_UUID:
          LedService_SetParameter(pCharData->paramID, pCharData->dataLen,
                                  pCharData->data);
        break;
    
        case BUTTON_SERVICE_SERV_UUID:
          ButtonService_SetParameter(pCharData->paramID, pCharData->dataLen,
                                     pCharData->data);
        break;
    
      }
    }
    
    /*
     * @brief   Convert {0x01, 0x02} to "01:02"
     *
     * @param   src - source byte-array
     * @param   src_len - length of array
     * @param   dst - destination string-array
     * @param   dst_len - length of array
     *
     * @return  array as string
     */
    static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len,
                                              uint8_t *dst, uint8_t dst_len)
    {
      char        hex[] = "0123456789ABCDEF";
      uint8_t     *pStr = dst;
      uint8_t     avail = dst_len-1;
    
      memset(dst, 0, avail);
    
      while (src_len && avail > 3)
      {
        if (avail < dst_len-1) { *pStr++ = ':'; avail -= 1; };
        *pStr++ = hex[*src >> 4];
        *pStr++ = hex[*src++ & 0x0F];
        avail -= 2;
        src_len--;
      }
    
      if (src_len && avail)
        *pStr++ = ':'; // Indicate not all data fit on line.
    
      return (char *)dst;
    }
    
    /*
     * @brief   Extract the LOCALNAME from Scan/AdvData
     *
     * @param   data - Pointer to the advertisement or scan response data
     *
     * @return  Pointer to null-terminated string with the adv local name.
     */
    static char *Util_getLocalNameStr(const uint8_t *data) {
      uint8_t nuggetLen = 0;
      uint8_t nuggetType = 0;
      uint8_t advIdx = 0;
    
      static char localNameStr[32] = { 0 };
      memset(localNameStr, 0, sizeof(localNameStr));
    
      for (advIdx = 0; advIdx < 32;) {
        nuggetLen = data[advIdx++];
        nuggetType = data[advIdx];
        if ( (nuggetType == GAP_ADTYPE_LOCAL_NAME_COMPLETE ||
              nuggetType == GAP_ADTYPE_LOCAL_NAME_SHORT) && nuggetLen < 31) {
          memcpy(localNameStr, &data[advIdx + 1], nuggetLen - 1);
          break;
        } else {
          advIdx += nuggetLen;
        }
      }
    
      return localNameStr;
    }
    
    /*********************************************************************
    *********************************************************************/
    

    Chaitanya,

    I've attached a reduced version of the file you sent me. I removed all of the changes you made that do not pertain to this goal.

    With this I see 10s on 10s off. I can see this even easier by looking at the UART output.

    Here you can see a 10s period of advertising and 10s period of being Idle. 

    If you are still having problems I again suggest you start with a clean project as it is most likely changes you have made to the project other than the changes I've suggested you make. 

    Best,

    Kris

  • Kristopher, thank you for the example code.

    the PRZ_ADV_TOGGLE_EVT is defined, but it seems never be used it in the provided clock code by search the page content. is the PRZ_ADV_TOGGLE_EVT define used elsewhere not shown in the code?

  • I observed on PUTTY that advertising is Happening ON and OFF periodically.

    But, Still I am getting same results on BLE SCANNER app and LightBlue app.
    1. Does advertising is still ON continuously and also the radio RF antenna is ON thats why I am getting that ?

    2. Also one thing I want to know is that what is the role of DEFAULT_ADVERTISING_INTERVAL which is set to 160 i.e. 10ms ? which is initialized at start ? 

  • Chaitanya,

    In reply to your first question about "PRZ_ADV_TOGGLE_EVT ". I would urge you to look at the code that I sent in my previous post again. 

    Here it is again. 

    /*
     * Copyright (c) 2016, 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>
    
    //#define xdc_runtime_Log_DISABLE_ALL 1  // Add to disable logs from this file
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Queue.h>
    
    #include <ti/drivers/PIN.h>
    #include <ti/mw/display/Display.h>
    
    #include <xdc/runtime/Log.h>
    #include <xdc/runtime/Diags.h>
    
    // Stack headers
    #include <hci_tl.h>
    #include <gap.h>
    #include <gatt.h>
    #include <gapgattserver.h>
    #include <gattservapp.h>
    #include <osal_snv.h>
    #include <gapbondmgr.h>
    #include <peripheral.h>
    #include <icall_apimsg.h>
    
    #include <devinfoservice.h>
    
    #include "util.h"
    
    #include "Board.h"
    #include "project_zero.h"
    
    // Bluetooth Developer Studio services
    #include "led_service.h"
    #include "button_service.h"
    #include "data_service.h"
    
    
    /*********************************************************************
     * CONSTANTS
     */
    // Advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL          160
    
    // Limited discoverable mode advertises for 30.72s, and then stops
    // General discoverable mode advertises indefinitely
    #define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
    
    // Default pass-code used for pairing.
    #define DEFAULT_PASSCODE                      000000
    
    // Task configuration
    #define PRZ_TASK_PRIORITY                     1
    
    #ifndef PRZ_TASK_STACK_SIZE
    #define PRZ_TASK_STACK_SIZE                   800
    #endif
    
    // Internal Events for RTOS application
    #define PRZ_STATE_CHANGE_EVT                  0x0001
    #define PRZ_CHAR_CHANGE_EVT                   0x0002
    #define PRZ_PERIODIC_EVT                      0x0004
    #define PRZ_CONN_EVT_END_EVT                  0x0008
    
    /*********************************************************************
     * TYPEDEFS
     */
    // Types of messages that can be sent to the user application task from other
    // tasks or interrupts. Note: Messages from BLE Stack are sent differently.
    typedef enum
    {
      APP_MSG_SERVICE_WRITE = 0,   /* A characteristic value has been written     */
      APP_MSG_SERVICE_CFG,         /* A characteristic configuration has changed  */
      APP_MSG_UPDATE_CHARVAL,      /* Request from ourselves to update a value    */
      APP_MSG_GAP_STATE_CHANGE,    /* The GAP / connection state has changed      */
      APP_MSG_BUTTON_DEBOUNCED,    /* A button has been debounced with new value  */
      APP_MSG_SEND_PASSCODE,       /* A pass-code/PIN is requested during pairing */
      APP_MSG_PRZ_ADV_TOGGLE_EVT,
    } app_msg_types_t;
    
    // Struct for messages sent to the application task
    typedef struct
    {
      Queue_Elem       _elem;
      app_msg_types_t  type;
      uint8_t          pdu[];
    } app_msg_t;
    
    // Struct for messages about characteristic data
    typedef struct
    {
      uint16_t svcUUID; // UUID of the service
      uint16_t dataLen; //
      uint8_t  paramID; // Index of the characteristic
      uint8_t  data[];  // Flexible array member, extended to malloc - sizeof(.)
    } char_data_t;
    
    // Struct for message about sending/requesting passcode from peer.
    typedef struct
    {
      uint16_t connHandle;
      uint8_t  uiInputs;
      uint8_t  uiOutputs;
      uint32   numComparison;
    } passcode_req_t;
    
    // Struct for message about button state
    typedef struct
    {
      PIN_Id   pinId;
      uint8_t  state;
    } button_state_t;
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Semaphore globally used to post events to the application thread
    static ICall_Semaphore sem;
    
    // Queue object used for application messages.
    static Queue_Struct applicationMsgQ;
    static Queue_Handle hApplicationMsgQ;
    
    // Task configuration
    Task_Struct przTask;
    Char przTaskStack[PRZ_TASK_STACK_SIZE];
    
    
    // GAP - SCAN RSP data (max size = 31 bytes)
    static uint8_t scanRspData[] =
    {
      // No scan response data provided.
      0x00 // Placeholder to keep the compiler happy.
    };
    
    // GAP - Advertisement data (max size = 31 bytes, though this is
    // best kept short to conserve power while advertisting)
    static uint8_t advertData[] =
    {
      // Flags; this sets the device to use limited discoverable
      // mode (advertises for 30 seconds at a time) or general
      // discoverable mode (advertises indefinitely), depending
      // on the DEFAULT_DISCOVERY_MODE define.
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // complete name
      13,
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'P', 'r', 'o', 'j', 'e', 'c', 't', ' ', 'Z', 'e', 'r', 'o',
    
    };
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Project Zero";
    
    // Globals used for ATT Response retransmission
    static gattMsgEvent_t *pAttRsp = NULL;
    static uint8_t rspTxRetry = 0;
    
    
    /* Pin driver handles */
    static PIN_Handle buttonPinHandle;
    static PIN_Handle ledPinHandle;
    
    /* Global memory storage for a PIN_Config table */
    static PIN_State buttonPinState;
    static PIN_State ledPinState;
    
    /*
     * Initial LED pin configuration table
     *   - LEDs Board_LED0 & Board_LED1 are off.
     */
    PIN_Config ledPinTable[] = {
      Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
      Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
      PIN_TERMINATE
    };
    
    /*
     * Application button pin configuration table:
     *   - Buttons interrupts are configured to trigger on falling edge.
     */
    PIN_Config buttonPinTable[] = {
        Board_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        Board_BUTTON1 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
        PIN_TERMINATE
    };
    
    // Clock objects for debouncing the buttons
    static Clock_Struct button0DebounceClock;
    static Clock_Struct button1DebounceClock;
    static Clock_Struct advertToggleClock;
    
    // State of the buttons
    static uint8_t button0State = 0;
    static uint8_t button1State = 0;
    
    // Global display handle
    Display_Handle dispHandle;
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void ProjectZero_init( void );
    static void ProjectZero_taskFxn(UArg a0, UArg a1);
    
    static void user_processApplicationMessage(app_msg_t *pMsg);
    static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
    
    static void ProjectZero_sendAttRsp(void);
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg);
    static void ProjectZero_freeAttRsp(uint8_t status);
    
    static void user_processGapStateChangeEvt(gaprole_States_t newState);
    static void user_gapStateChangeCB(gaprole_States_t newState);
    static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                           uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison);
    static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state,
                                            uint8_t status);
    
    static void buttonDebounceSwiFxn(UArg buttonId);
    static void user_handleButtonPress(button_state_t *pState);
    
    // Generic callback handlers for value changes in services.
    static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len );
    static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid, uint8_t paramID, uint8_t *pValue, uint16_t len );
    
    // Task context handlers for generated services.
    static void user_LedService_ValueChangeHandler(char_data_t *pCharData);
    static void user_ButtonService_CfgChangeHandler(char_data_t *pCharData);
    static void user_DataService_ValueChangeHandler(char_data_t *pCharData);
    static void user_DataService_CfgChangeHandler(char_data_t *pCharData);
    
    // Task handler for sending notifications.
    static void user_updateCharVal(char_data_t *pCharData);
    
    // Utility functions
    static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData, uint16_t len );
    static void user_enqueueCharDataMsg(app_msg_types_t appMsgType, uint16_t connHandle,
                                        uint16_t serviceUUID, uint8_t paramID,
                                        uint8_t *pValue, uint16_t len);
    static void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId);
    
    static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len,
                                              uint8_t *dst, uint8_t dst_len);
    static char *Util_getLocalNameStr(const uint8_t *data);
    
    
    static void advertToggleClockFxn(void); ///////////////////////////////////////////////////toggle clock function
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // GAP Role Callbacks
    static gapRolesCBs_t user_gapRoleCBs =
    {
      user_gapStateChangeCB     // Profile State Change Callbacks
    };
    
    // GAP Bond Manager Callbacks
    static gapBondCBs_t user_bondMgrCBs =
    {
      user_gapBondMgr_passcodeCB, // Passcode callback
      user_gapBondMgr_pairStateCB // Pairing / Bonding state Callback
    };
    
    /*
     * Callbacks in the user application for events originating from BLE services.
     */
    // LED Service callback handler.
    // The type LED_ServiceCBs_t is defined in led_service.h
    static LedServiceCBs_t user_LED_ServiceCBs =
    {
      .pfnChangeCb    = user_service_ValueChangeCB, // Characteristic value change callback handler
      .pfnCfgChangeCb = NULL, // No notification-/indication enabled chars in LED Service
    };
    
    // Button Service callback handler.
    // The type Button_ServiceCBs_t is defined in button_service.h
    static ButtonServiceCBs_t user_Button_ServiceCBs =
    {
      .pfnChangeCb    = NULL, // No writable chars in Button Service, so no change handler.
      .pfnCfgChangeCb = user_service_CfgChangeCB, // Noti/ind configuration callback handler
    };
    
    // Data Service callback handler.
    // The type Data_ServiceCBs_t is defined in data_service.h
    static DataServiceCBs_t user_Data_ServiceCBs =
    {
      .pfnChangeCb    = user_service_ValueChangeCB, // Characteristic value change callback handler
      .pfnCfgChangeCb = user_service_CfgChangeCB, // Noti/ind configuration callback handler
    };
    
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*
     * @brief   Task creation function for the user task.
     *
     * @param   None.
     *
     * @return  None.
     */
    void ProjectZero_createTask(void)
    {
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = przTaskStack;
      taskParams.stackSize = PRZ_TASK_STACK_SIZE;
      taskParams.priority = PRZ_TASK_PRIORITY;
    
      Task_construct(&przTask, ProjectZero_taskFxn, &taskParams, NULL);
    }
    
    /*
     * @brief   Called before the task loop and contains application-specific
     *          initialization of the BLE stack, hardware setup, power-state
     *          notification if used, and BLE profile/service initialization.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void ProjectZero_init(void)
    {
      // ******************************************************************
      // NO STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages via ICall to Stack.
      ICall_registerApp(&selfEntity, &sem);
    
      Log_info0("Initializing the user task, hardware, BLE stack and services.");
    
      // Open display. By default this is disabled via the predefined symbol Display_DISABLE_ALL.
      dispHandle = Display_open(Display_Type_LCD, NULL);
    
      // Initialize queue for application messages.
      // Note: Used to transfer control to application thread from e.g. interrupts.
      Queue_construct(&applicationMsgQ, NULL);
      hApplicationMsgQ = Queue_handle(&applicationMsgQ);
    
      // ******************************************************************
      // Hardware initialization
      // ******************************************************************
    
      // Open LED pins
      ledPinHandle = PIN_open(&ledPinState, ledPinTable);
      if(!ledPinHandle) {
        Log_error0("Error initializing board LED pins");
        Task_exit();
      }
    
      buttonPinHandle = PIN_open(&buttonPinState, buttonPinTable);
      if(!buttonPinHandle) {
        Log_error0("Error initializing button pins");
        Task_exit();
      }
    
      // Setup callback for button pins
      if (PIN_registerIntCb(buttonPinHandle, &buttonCallbackFxn) != 0) {
        Log_error0("Error registering button callback function");
        Task_exit();
      }
    
      // Create the debounce clock objects for Button 0 and Button 1
      Clock_Params clockParams;
      Clock_Params_init(&clockParams);
    
      // Both clock objects use the same callback, so differentiate on argument
      // given to the callback in Swi context
      clockParams.arg = Board_BUTTON0;
    
      // Initialize to 50 ms timeout when Clock_start is called.
      // Timeout argument is in ticks, so convert from ms to ticks via tickPeriod.
      Clock_construct(&button0DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
      // Second button
      clockParams.arg = Board_BUTTON1;
      Clock_construct(&button1DebounceClock, buttonDebounceSwiFxn,
                      50 * (1000/Clock_tickPeriod),
                      &clockParams);
    
    
      Clock_construct(&advertToggleClock, (ti_sysbios_knl_Clock_FuncPtr) advertToggleClockFxn,
                      10000 * (1000/Clock_tickPeriod), // Ten seconds
                      &clockParams);
    
      // ******************************************************************
      // BLE Stack initialization
      // ******************************************************************
    /*
      // Setup the GAP Peripheral Role Profile
      uint8_t initialAdvertEnable = TRUE;  // Advertise on power-up
    
      // By setting this to zero, the device will go into the waiting state after
      // being discoverable. Otherwise wait this long [ms] before advertising again.
      uint16_t advertOffTime = 0; // miliseconds
    
      // Set advertisement enabled.
      GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                           &initialAdvertEnable);
    
      // Configure the wait-time before restarting advertisement automatically
      GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
                           &advertOffTime);
    */
      Clock_start(Clock_handle(&advertToggleClock));
      // Initialize Scan Response data
      GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
    
      // Initialize Advertisement data
      GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
    
      Log_info1("Name in advertData array: \x1b[33m%s\x1b[0m",
                (IArg)Util_getLocalNameStr(advertData));
    
      // Set advertising interval
      uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
    
      GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt);
      GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt);
      GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt);
      GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt);
    
      // Set duration of advertisement before stopping in Limited adv mode.
     // GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, 30); // Seconds
    
      // ******************************************************************
      // BLE Bond Manager initialization
      // ******************************************************************
      uint32_t passkey = 0; // passkey "000000"
      uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
      uint8_t mitm = TRUE;
      uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
      uint8_t bonding = TRUE;
    
      GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t),
                              &passkey);
      GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
      GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
      GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
      GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
    
      // ******************************************************************
      // BLE Service initialization
      // ******************************************************************
    
      // Add services to GATT server
      GGS_AddService(GATT_ALL_SERVICES);           // GAP
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
      DevInfo_AddService();                        // Device Information Service
    
      // Set the device name characteristic in the GAP Profile
      GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
      // Add services to GATT server and give ID of this task for Indication acks.
      LedService_AddService( selfEntity );
      ButtonService_AddService( selfEntity );
      DataService_AddService( selfEntity );
    
      // Register callbacks with the generated services that
      // can generate events (writes received) to the application
      LedService_RegisterAppCBs( &user_LED_ServiceCBs );
      ButtonService_RegisterAppCBs( &user_Button_ServiceCBs );
      DataService_RegisterAppCBs( &user_Data_ServiceCBs );
    
      // Placeholder variable for characteristic intialization
      uint8_t initVal[40] = {0};
      uint8_t initString[] = "This is a pretty long string, isn't it!";
    
      // Initalization of characteristics in LED_Service that can provide data.
      LedService_SetParameter(LS_LED0_ID, LS_LED0_LEN, initVal);
      LedService_SetParameter(LS_LED1_ID, LS_LED1_LEN, initVal);
    
      // Initalization of characteristics in Button_Service that can provide data.
      ButtonService_SetParameter(BS_BUTTON0_ID, BS_BUTTON0_LEN, initVal);
      ButtonService_SetParameter(BS_BUTTON1_ID, BS_BUTTON1_LEN, initVal);
    
      // Initalization of characteristics in Data_Service that can provide data.
      DataService_SetParameter(DS_STRING_ID, sizeof(initString), initString);
      DataService_SetParameter(DS_STREAM_ID, DS_STREAM_LEN, initVal);
    
      // Start the stack in Peripheral mode.
      VOID GAPRole_StartDevice(&user_gapRoleCBs);
    
      // Start Bond Manager
      VOID GAPBondMgr_Register(&user_bondMgrCBs);
    
      // Register with GAP for HCI/Host messages
      GAP_RegisterForMsgs(selfEntity);
    
      // Register for GATT local events and ATT Responses pending for transmission
      GATT_RegisterForMsgs(selfEntity);
    }
    
    
    /*
     * @brief   Application task entry point.
     *
     *          Invoked by TI-RTOS when BIOS_start is called. Calls an init function
     *          and enters an infinite loop waiting for messages.
     *
     *          Messages can be either directly from the BLE stack or from user code
     *          like Hardware Interrupt (Hwi) or a callback function.
     *
     *          The reason for sending messages to this task from e.g. Hwi's is that
     *          some RTOS and Stack APIs are not available in callbacks and so the
     *          actions that may need to be taken is dispatched to this Task.
     *
     * @param   a0, a1 - not used.
     *
     * @return  None.
     */
    static void ProjectZero_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      ProjectZero_init();
    
      // Application main loop
      for (;;)
      {
        // Waits for a signal to the semaphore associated with the calling thread.
        // Note that the semaphore associated with a thread is signaled when a
        // message is queued to the message receive queue of the thread or when
        // ICall_signal() function is called onto the semaphore.
        ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER);
    
        if (errno == ICALL_ERRNO_SUCCESS)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          // Check if we got a signal because of a stack message
          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 event flags received (event signature 0xffff)
              if (pEvt->signature == 0xffff)
              {
                // Event received when a connection event is completed
                if (pEvt->event_flag & PRZ_CONN_EVT_END_EVT)
                {
                  // Try to retransmit pending ATT Response (if any)
                  ProjectZero_sendAttRsp();
                }
              }
              else // It's a message from the stack and not an event.
              {
                // Process inter-task message
                safeToDealloc = ProjectZero_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // Process messages sent from another task or another context.
          while (!Queue_empty(hApplicationMsgQ))
          {
            app_msg_t *pMsg = Queue_dequeue(hApplicationMsgQ);
    
            // Process application-layer message probably sent from ourselves.
            user_processApplicationMessage(pMsg);
    
            // Free the received message.
            ICall_free(pMsg);
          }
        }
      }
    }
    
    
    /*
     * @brief   Handle application messages
     *
     *          These are messages not from the BLE stack, but from the
     *          application itself.
     *
     *          For example, in a Software Interrupt (Swi) it is not possible to
     *          call any BLE APIs, so instead the Swi function must send a message
     *          to the application Task for processing in Task context.
     *
     * @param   pMsg  Pointer to the message of type app_msg_t.
     *
     * @return  None.
     */
    static void user_processApplicationMessage(app_msg_t *pMsg)
    {
      char_data_t *pCharData = (char_data_t *)pMsg->pdu;
    
      switch (pMsg->type)
      {
        case APP_MSG_SERVICE_WRITE: /* Message about received value write */
          /* Call different handler per service */
          switch(pCharData->svcUUID) {
            case LED_SERVICE_SERV_UUID:
              user_LedService_ValueChangeHandler(pCharData);
              break;
            case DATA_SERVICE_SERV_UUID:
              user_DataService_ValueChangeHandler(pCharData);
              break;
    
          }
          break;
    
        case APP_MSG_SERVICE_CFG: /* Message about received CCCD write */
          /* Call different handler per service */
          switch(pCharData->svcUUID) {
            case BUTTON_SERVICE_SERV_UUID:
              user_ButtonService_CfgChangeHandler(pCharData);
              break;
            case DATA_SERVICE_SERV_UUID:
              user_DataService_CfgChangeHandler(pCharData);
              break;
          }
          break;
    
        case APP_MSG_UPDATE_CHARVAL: /* Message from ourselves to send  */
          user_updateCharVal(pCharData);
          break;
    
        case APP_MSG_GAP_STATE_CHANGE: /* Message that GAP state changed  */
          user_processGapStateChangeEvt( *(gaprole_States_t *)pMsg->pdu );
          break;
    
        case APP_MSG_SEND_PASSCODE: /* Message about pairing PIN request */
          {
            passcode_req_t *pReq = (passcode_req_t *)pMsg->pdu;
            Log_info2("BondMgr Requested passcode. We are %s passcode %06d",
                      (IArg)(pReq->uiInputs?"Sending":"Displaying"),
                      DEFAULT_PASSCODE);
            // Send passcode response.
            GAPBondMgr_PasscodeRsp(pReq->connHandle, SUCCESS, DEFAULT_PASSCODE);
          }
          break;
    
        case APP_MSG_BUTTON_DEBOUNCED: /* Message from swi about pin change */
          {
            button_state_t *pButtonState = (button_state_t *)pMsg->pdu;
            user_handleButtonPress(pButtonState);
          }
          break;
        case APP_MSG_PRZ_ADV_TOGGLE_EVT:
            {
                uint8_t advertEnable=0;
                GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &advertEnable);
                advertEnable = !advertEnable;
                GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertEnable);
    
                Clock_start(Clock_handle(&advertToggleClock));
                break;
            }
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Handlers of system/application events deferred to the user Task context.
     *  Invoked from the application Task function above.
     *
     *  Further down you can find the callback handler section containing the
     *  functions that defer their actions via messages to the application task.
     *
     ****************************************************************************
     *****************************************************************************/
    
    
    /*
     * @brief   Process a pending GAP Role state change event.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void user_processGapStateChangeEvt(gaprole_States_t newState)
    {
      switch ( newState )
      {
        case GAPROLE_STARTED:
          {
            uint8_t ownAddress[B_ADDR_LEN];
            uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
    
            GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);
    
            // use 6 bytes of device address for 8 bytes of system ID value
            systemId[0] = ownAddress[0];
            systemId[1] = ownAddress[1];
            systemId[2] = ownAddress[2];
    
            // set middle bytes to zero
            systemId[4] = 0x00;
            systemId[3] = 0x00;
    
            // shift three bytes up
            systemId[7] = ownAddress[5];
            systemId[6] = ownAddress[4];
            systemId[5] = ownAddress[3];
    
            DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
    
            // Display device address
            char *cstr_ownAddress = Util_convertBdAddr2Str(ownAddress);
            Log_info1("GAP is started. Our address: \x1b[32m%s\x1b[0m", (IArg)cstr_ownAddress);
          }
          break;
    
        case GAPROLE_ADVERTISING:
          Log_info0("Advertising");
          break;
    
        case GAPROLE_CONNECTED:
          {
            uint8_t peerAddress[B_ADDR_LEN];
    
            GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);
    
            char *cstr_peerAddress = Util_convertBdAddr2Str(peerAddress);
            Log_info1("Connected. Peer address: \x1b[32m%s\x1b[0m", (IArg)cstr_peerAddress);
           }
          break;
    
        case GAPROLE_CONNECTED_ADV:
          Log_info0("Connected and advertising");
          break;
    
        case GAPROLE_WAITING:
          Log_info0("Disconnected / Idle");
          break;
    
        case GAPROLE_WAITING_AFTER_TIMEOUT:
          Log_info0("Connection timed out");
          break;
    
        case GAPROLE_ERROR:
          Log_info0("Error");
          break;
    
        default:
          break;
      }
    }
    
    
    /*
     * @brief   Handle a debounced button press or release in Task context.
     *          Invoked by the taskFxn based on a message received from a callback.
     *
     * @see     buttonDebounceSwiFxn
     * @see     buttonCallbackFxn
     *
     * @param   pState  pointer to button_state_t message sent from debounce Swi.
     *
     * @return  None.
     */
    static void user_handleButtonPress(button_state_t *pState)
    {
      Log_info2("%s %s",
        (IArg)(pState->pinId == Board_BUTTON0?"Button 0":"Button 1"),
        (IArg)(pState->state?"\x1b[32mpressed\x1b[0m":
                             "\x1b[33mreleased\x1b[0m"));
    
      // Update the service with the new value.
      // Will automatically send notification/indication if enabled.
      switch (pState->pinId)
      {
        case Board_BUTTON0:
          ButtonService_SetParameter(BS_BUTTON0_ID,
                                     sizeof(pState->state),
                                     &pState->state);
          break;
        case Board_BUTTON1:
          ButtonService_SetParameter(BS_BUTTON1_ID,
                                     sizeof(pState->state),
                                     &pState->state);
          break;
      }
    }
    
    static void advertToggleClockFxn(void)  ////////////////////////////////////toggleClock function
    {
        static uint8_t count = 0;
    
        user_enqueueRawAppMsg(APP_MSG_PRZ_ADV_TOGGLE_EVT,
                          (uint8_t *)&count, sizeof(count));
    
        count++;
    }
    /*
     * @brief   Handle a write request sent from a peer device.
     *
     *          Invoked by the Task based on a message received from a callback.
     *
     *          When we get here, the request has already been accepted by the
     *          service and is valid from a BLE protocol perspective as well as
     *          having the correct length as defined in the service implementation.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_LedService_ValueChangeHandler(char_data_t *pCharData)
    {
      static uint8_t pretty_data_holder[16]; // 5 bytes as hex string "AA:BB:CC:DD:EE"
      Util_convertArrayToHexString(pCharData->data, pCharData->dataLen,
                                   pretty_data_holder, sizeof(pretty_data_holder));
    
      switch (pCharData->paramID)
      {
        case LS_LED0_ID:
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"LED Service",
                    (IArg)"LED0",
                    (IArg)pretty_data_holder);
    
          // Do something useful with pCharData->data here
          // -------------------------
          // Set the output value equal to the received value. 0 is off, not 0 is on
          PIN_setOutputValue(ledPinHandle, Board_LED0, pCharData->data[0]);
          Log_info2("Turning %s %s",
                    (IArg)"\x1b[31mLED0\x1b[0m",
                    (IArg)(pCharData->data[0]?"on":"off"));
          break;
    
        case LS_LED1_ID:
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"LED Service",
                    (IArg)"LED1",
                    (IArg)pretty_data_holder);
    
          // Do something useful with pCharData->data here
          // -------------------------
          // Set the output value equal to the received value. 0 is off, not 0 is on
          PIN_setOutputValue(ledPinHandle, Board_LED1, pCharData->data[0]);
          Log_info2("Turning %s %s",
                    (IArg)"\x1b[32mLED1\x1b[0m",
                    (IArg)(pCharData->data[0]?"on":"off"));
          break;
    
      default:
        return;
      }
    }
    
    
    /*
     * @brief   Handle a CCCD (configuration change) write received from a peer
     *          device. This tells us whether the peer device wants us to send
     *          Notifications or Indications.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_ButtonService_CfgChangeHandler(char_data_t *pCharData)
    {
      // Cast received data to uint16, as that's the format for CCCD writes.
      uint16_t configValue = *(uint16_t *)pCharData->data;
      char *configValString;
    
      // Determine what to tell the user
      switch(configValue)
      {
      case GATT_CFG_NO_OPERATION:
        configValString = "Noti/Ind disabled";
        break;
      case GATT_CLIENT_CFG_NOTIFY:
        configValString = "Notifications enabled";
        break;
      case GATT_CLIENT_CFG_INDICATE:
        configValString = "Indications enabled";
        break;
      }
    
      switch (pCharData->paramID)
      {
        case BS_BUTTON0_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Button Service",
                    (IArg)"BUTTON0",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
    
        case BS_BUTTON1_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Button Service",
                    (IArg)"BUTTON1",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
      }
    }
    
    /*
     * @brief   Handle a write request sent from a peer device.
     *
     *          Invoked by the Task based on a message received from a callback.
     *
     *          When we get here, the request has already been accepted by the
     *          service and is valid from a BLE protocol perspective as well as
     *          having the correct length as defined in the service implementation.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_DataService_ValueChangeHandler(char_data_t *pCharData)
    {
      // Value to hold the received string for printing via Log, as Log printouts
      // happen in the Idle task, and so need to refer to a global/static variable.
      static uint8_t received_string[DS_STRING_LEN] = {0};
    
      switch (pCharData->paramID)
      {
        case DS_STRING_ID:
          // Do something useful with pCharData->data here
          // -------------------------
          // Copy received data to holder array, ensuring NULL termination.
          memset(received_string, 0, DS_STRING_LEN);
          memcpy(received_string, pCharData->data, DS_STRING_LEN-1);
          // Needed to copy before log statement, as the holder array remains after
          // the pCharData message has been freed and reused for something else.
          Log_info3("Value Change msg: %s %s: %s",
                    (IArg)"Data Service",
                    (IArg)"String",
                    (IArg)received_string);
          break;
    
        case DS_STREAM_ID:
          Log_info3("Value Change msg: Data Service Stream: %02x:%02x:%02x...",
                    (IArg)pCharData->data[0],
                    (IArg)pCharData->data[1],
                    (IArg)pCharData->data[2]);
          // -------------------------
          // Do something useful with pCharData->data here
          break;
    
      default:
        return;
      }
    }
    
    /*
     * @brief   Handle a CCCD (configuration change) write received from a peer
     *          device. This tells us whether the peer device wants us to send
     *          Notifications or Indications.
     *
     * @param   pCharData  pointer to malloc'd char write data
     *
     * @return  None.
     */
    void user_DataService_CfgChangeHandler(char_data_t *pCharData)
    {
      // Cast received data to uint16, as that's the format for CCCD writes.
      uint16_t configValue = *(uint16_t *)pCharData->data;
      char *configValString;
    
      // Determine what to tell the user
      switch(configValue)
      {
      case GATT_CFG_NO_OPERATION:
        configValString = "Noti/Ind disabled";
        break;
      case GATT_CLIENT_CFG_NOTIFY:
        configValString = "Notifications enabled";
        break;
      case GATT_CLIENT_CFG_INDICATE:
        configValString = "Indications enabled";
        break;
      }
    
      switch (pCharData->paramID)
      {
        case DS_STREAM_ID:
          Log_info3("CCCD Change msg: %s %s: %s",
                    (IArg)"Data Service",
                    (IArg)"Stream",
                    (IArg)configValString);
          // -------------------------
          // Do something useful with configValue here. It tells you whether someone
          // wants to know the state of this characteristic.
          // ...
          break;
      }
    }
    
    
    /*
     * @brief   Process an incoming BLE stack message.
     *
     *          This could be a GATT message from a peer device like acknowledgement
     *          of an Indication we sent, or it could be a response from the stack
     *          to an HCI message that the user application sent.
     *
     * @param   pMsg - message to process
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t ProjectZero_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = ProjectZero_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 Event
                Log_info0("HCI Command Complete Event received");
                break;
    
              default:
                break;
            }
          }
          break;
    
        default:
          // do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    
    /*
     * @brief   Process GATT messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t ProjectZero_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      // See if GATT server was unable to transmit an ATT response
      if (pMsg->hdr.status == blePending)
      {
        Log_warning1("Outgoing RF FIFO full. Re-schedule transmission of msg with opcode 0x%02x",
          pMsg->method);
    
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if (HCI_EXT_ConnEventNoticeCmd(pMsg->connHandle, selfEntity,
                                       PRZ_CONN_EVT_END_EVT) == SUCCESS)
        {
          // First free any pending response
          ProjectZero_freeAttRsp(FAILURE);
    
          // Hold on to the response message for retransmission
          pAttRsp = pMsg;
    
          // Don't free the response message yet
          return (FALSE);
        }
      }
      else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
      {
        // ATT request-response or indication-confirmation flow control is
        // violated. All subsequent ATT requests or indications will be dropped.
        // The app is informed in case it wants to drop the connection.
    
        // Log the opcode of the message that caused the violation.
        Log_error1("Flow control violated. Opcode of offending ATT msg: 0x%02x",
          pMsg->msg.flowCtrlEvt.opcode);
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
        Log_info1("MTU Size change: %d bytes", pMsg->msg.mtuEvt.MTU);
      }
      else
      {
        // Got an expected GATT message from a peer.
        Log_info1("Recevied GATT Message. Opcode: 0x%02x", pMsg->method);
      }
    
      // 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);
    }
    
    
    
    
    /*
     *  Application error handling functions
     *****************************************************************************/
    
    /*
     * @brief   Send a pending ATT response message.
     *
     *          The message is one that the stack was trying to send based on a
     *          peer request, but the response couldn't be sent because the
     *          user application had filled the TX queue with other data.
     *
     * @param   none
     *
     * @return  none
     */
    static void ProjectZero_sendAttRsp(void)
    {
      // See if there's a pending ATT Response to be transmitted
      if (pAttRsp != NULL)
      {
        uint8_t status;
    
        // Increment retransmission count
        rspTxRetry++;
    
        // Try to retransmit ATT response till either we're successful or
        // the ATT Client times out (after 30s) and drops the connection.
        status = GATT_SendRsp(pAttRsp->connHandle, pAttRsp->method, &(pAttRsp->msg));
        if ((status != blePending) && (status != MSG_BUFFER_NOT_AVAIL))
        {
          // Disable connection event end notice
          HCI_EXT_ConnEventNoticeCmd(pAttRsp->connHandle, selfEntity, 0);
    
          // We're done with the response message
          ProjectZero_freeAttRsp(status);
        }
        else
        {
          // Continue retrying
          Log_warning2("Retrying message with opcode 0x%02x. Attempt %d",
            pAttRsp->method, rspTxRetry);
        }
      }
    }
    
    /*
     * @brief   Free ATT response message.
     *
     * @param   status - response transmit status
     *
     * @return  none
     */
    static void ProjectZero_freeAttRsp(uint8_t status)
    {
      // See if there's a pending ATT response message
      if (pAttRsp != NULL)
      {
        // See if the response was sent out successfully
        if (status == SUCCESS)
        {
          Log_info2("Sent message with opcode 0x%02x. Attempt %d",
            pAttRsp->method, rspTxRetry);
        }
        else
        {
          Log_error2("Gave up message with opcode 0x%02x. Status: %d",
            pAttRsp->method, status);
    
          // Free response payload
          GATT_bm_free(&pAttRsp->msg, pAttRsp->method);
        }
    
        // Free response message
        ICall_freeMsg(pAttRsp);
    
        // Reset our globals
        pAttRsp = NULL;
        rspTxRetry = 0;
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Handlers of direct system callbacks.
     *
     *  Typically enqueue the information or request as a message for the
     *  application Task for handling.
     *
     ****************************************************************************
     *****************************************************************************/
    
    
    /*
     *  Callbacks from the Stack Task context (GAP or Service changes)
     *****************************************************************************/
    
    /**
     * Callback from GAP Role indicating a role state change.
     */
    static void user_gapStateChangeCB(gaprole_States_t newState)
    {
      Log_info1("(CB) GAP State change: %d, Sending msg to app.", (IArg)newState);
      user_enqueueRawAppMsg( APP_MSG_GAP_STATE_CHANGE, (uint8_t *)&newState, sizeof(newState) );
    }
    
    /*
     * @brief   Passcode callback.
     *
     * @param   connHandle - connection handle
     * @param   uiInputs   - input passcode?
     * @param   uiOutputs  - display passcode?
     * @param   numComparison - numeric comparison value
     *
     * @return  none
     */
    static void user_gapBondMgr_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                           uint8_t uiInputs, uint8_t uiOutputs, uint32 numComparison)
    {
      passcode_req_t req =
      {
        .connHandle = connHandle,
        .uiInputs = uiInputs,
        .uiOutputs = uiOutputs,
        .numComparison = numComparison
      };
    
      // Defer handling of the passcode request to the application, in case
      // user input is required, and because a BLE API must be used from Task.
      user_enqueueRawAppMsg(APP_MSG_SEND_PASSCODE, (uint8_t *)&req, sizeof(req));
    }
    
    /*
     * @brief   Pairing state callback.
     *
     * @param   connHandle - connection handle
     * @param   state      - pairing state
     * @param   status     - pairing status
     *
     * @return  none
     */
    static void user_gapBondMgr_pairStateCB(uint16_t connHandle, uint8_t state,
                                            uint8_t status)
    {
      if (state == GAPBOND_PAIRING_STATE_STARTED)
      {
        Log_info0("Pairing started");
      }
      else if (state == GAPBOND_PAIRING_STATE_COMPLETE)
      {
        if (status == SUCCESS)
        {
          Log_info0("Pairing completed successfully.");
        }
        else
        {
          Log_error1("Pairing failed. Error: %02x", status);
        }
      }
      else if (state == GAPBOND_PAIRING_STATE_BONDED)
      {
        if (status == SUCCESS)
        {
         Log_info0("Re-established pairing from stored bond info.");
        }
      }
    }
    
    /**
     * Callback handler for characteristic value changes in services.
     */
    static void user_service_ValueChangeCB( uint16_t connHandle, uint16_t svcUuid,
                                            uint8_t paramID, uint8_t *pValue,
                                            uint16_t len )
    {
      // See the service header file to compare paramID with characteristic.
      Log_info2("(CB) Characteristic value change: svc(0x%04x) paramID(%d). "
                "Sending msg to app.", (IArg)svcUuid, (IArg)paramID);
      user_enqueueCharDataMsg(APP_MSG_SERVICE_WRITE, connHandle, svcUuid, paramID,
                              pValue, len);
    }
    
    /**
     * Callback handler for characteristic configuration changes in services.
     */
    static void user_service_CfgChangeCB( uint16_t connHandle, uint16_t svcUuid,
                                          uint8_t paramID, uint8_t *pValue,
                                          uint16_t len )
    {
      Log_info2("(CB) Char config change: svc(0x%04x) paramID(%d). "
                "Sending msg to app.", (IArg)svcUuid, (IArg)paramID);
      user_enqueueCharDataMsg(APP_MSG_SERVICE_CFG, connHandle, svcUuid,
                              paramID, pValue, len);
    }
    
    /*
     *  Callbacks from Swi-context
     *****************************************************************************/
    
    /*
     * @brief  Callback from Clock module on timeout
     *
     *         Determines new state after debouncing
     *
     * @param  buttonId    The pin being debounced
     */
    static void buttonDebounceSwiFxn(UArg buttonId)
    {
      // Used to send message to app
      button_state_t buttonMsg = { .pinId = buttonId };
      uint8_t        sendMsg   = FALSE;
    
      // Get current value of the button pin after the clock timeout
      uint8_t buttonPinVal = PIN_getInputValue(buttonId);
    
      // Set interrupt direction to opposite of debounced state
      // If button is now released (button is active low, so release is high)
      if (buttonPinVal)
      {
        // Enable negative edge interrupts to wait for press
        PIN_setConfig(buttonPinHandle, PIN_BM_IRQ, buttonId | PIN_IRQ_NEGEDGE);
      }
      else
      {
        // Enable positive edge interrupts to wait for relesae
        PIN_setConfig(buttonPinHandle, PIN_BM_IRQ, buttonId | PIN_IRQ_POSEDGE);
      }
    
      switch(buttonId)
      {
        case Board_BUTTON0:
          // If button is now released (buttonPinVal is active low, so release is 1)
          // and button state was pressed (buttonstate is active high so press is 1)
          if (buttonPinVal && button0State)
          {
            // Button was released
            buttonMsg.state = button0State = 0;
            sendMsg = TRUE;
          }
          else if (!buttonPinVal && !button0State)
          {
            // Button was pressed
            buttonMsg.state = button0State = 1;
            sendMsg = TRUE;
          }
          break;
    
        case Board_BUTTON1:
          // If button is now released (buttonPinVal is active low, so release is 1)
          // and button state was pressed (buttonstate is active high so press is 1)
          if (buttonPinVal && button1State)
          {
            // Button was released
            buttonMsg.state = button1State = 0;
            sendMsg = TRUE;
          }
          else if (!buttonPinVal && !button1State)
          {
            // Button was pressed
            buttonMsg.state = button1State = 1;
            sendMsg = TRUE;
          }
          break;
      }
    
      if (sendMsg == TRUE)
      {
        user_enqueueRawAppMsg(APP_MSG_BUTTON_DEBOUNCED,
                          (uint8_t *)&buttonMsg, sizeof(buttonMsg));
      }
    }
    
    /*
     *  Callbacks from Hwi-context
     *****************************************************************************/
    
    /*
     * @brief  Callback from PIN driver on interrupt
     *
     *         Sets in motion the debouncing.
     *
     * @param  handle    The PIN_Handle instance this is about
     * @param  pinId     The pin that generated the interrupt
     */
    static void buttonCallbackFxn(PIN_Handle handle, PIN_Id pinId)
    {
      Log_info1("Button interrupt: %s",
                (IArg)((pinId == Board_BUTTON0)?"Button 0":"Button 1"));
    
      // Disable interrupt on that pin for now. Re-enabled after debounce.
      PIN_setConfig(handle, PIN_BM_IRQ, pinId | PIN_IRQ_DIS);
    
      // Start debounce timer
      switch (pinId)
      {
        case Board_BUTTON0:
          Clock_start(Clock_handle(&button0DebounceClock));
          break;
        case Board_BUTTON1:
          Clock_start(Clock_handle(&button1DebounceClock));
          break;
      }
    }
    
    
    /******************************************************************************
     *****************************************************************************
     *
     *  Utility functions
     *
     ****************************************************************************
     *****************************************************************************/
    
    /*
     * @brief  Generic message constructor for characteristic data.
     *
     *         Sends a message to the application for handling in Task context where
     *         the message payload is a char_data_t struct.
     *
     *         From service callbacks the appMsgType is APP_MSG_SERVICE_WRITE or
     *         APP_MSG_SERVICE_CFG, and functions running in another context than
     *         the Task itself, can set the type to APP_MSG_UPDATE_CHARVAL to
     *         make the user Task loop invoke user_updateCharVal function for them.
     *
     * @param  appMsgType    Enumerated type of message being sent.
     * @param  connHandle    GAP Connection handle of the relevant connection
     * @param  serviceUUID   16-bit part of the relevant service UUID
     * @param  paramID       Index of the characteristic in the service
     * @oaram  *pValue       Pointer to characteristic value
     * @param  len           Length of characteristic data
     */
    static void user_enqueueCharDataMsg( app_msg_types_t appMsgType,
                                         uint16_t connHandle,
                                         uint16_t serviceUUID, uint8_t paramID,
                                         uint8_t *pValue, uint16_t len )
    {
      // Called in Stack's Task context, so can't do processing here.
      // Send message to application message queue about received data.
      uint16_t readLen = len; // How much data was written to the attribute
    
      // Allocate memory for the message.
      // Note: The pCharData message doesn't have to contain the data itself, as
      //       that's stored in a variable in the service implementation.
      //
      //       However, to prevent data loss if a new value is received before the
      //       service's container is read out via the GetParameter API is called,
      //       we copy the characteristic's data now.
      app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + sizeof(char_data_t) +
                                      readLen );
    
      if (pMsg != NULL)
      {
        pMsg->type = appMsgType;
    
        char_data_t *pCharData = (char_data_t *)pMsg->pdu;
        pCharData->svcUUID = serviceUUID; // Use 16-bit part of UUID.
        pCharData->paramID = paramID;
        // Copy data from service now.
        memcpy(pCharData->data, pValue, readLen);
        // Update pCharData with how much data we received.
        pCharData->dataLen = readLen;
        // Enqueue the message using pointer to queue node element.
        Queue_enqueue(hApplicationMsgQ, &pMsg->_elem);
        // Let application know there's a message.
        Semaphore_post(sem);
      }
    }
    
    /*
     * @brief  Generic message constructor for application messages.
     *
     *         Sends a message to the application for handling in Task context.
     *
     * @param  appMsgType    Enumerated type of message being sent.
     * @oaram  *pValue       Pointer to characteristic value
     * @param  len           Length of characteristic data
     */
    static void user_enqueueRawAppMsg(app_msg_types_t appMsgType, uint8_t *pData,
                                      uint16_t len)
    {
      // Allocate memory for the message.
      app_msg_t *pMsg = ICall_malloc( sizeof(app_msg_t) + len );
    
      if (pMsg != NULL)
      {
        pMsg->type = appMsgType;
    
        // Copy data into message
        memcpy(pMsg->pdu, pData, len);
    
        // Enqueue the message using pointer to queue node element.
        Queue_enqueue(hApplicationMsgQ, &pMsg->_elem);
        // Let application know there's a message.
        Semaphore_post(sem);
      }
    }
    
    
    /*
     * @brief  Convenience function for updating characteristic data via char_data_t
     *         structured message.
     *
     * @note   Must run in Task context in case BLE Stack APIs are invoked.
     *
     * @param  *pCharData  Pointer to struct with value to update.
     */
    static void user_updateCharVal(char_data_t *pCharData)
    {
      switch(pCharData->svcUUID) {
        case LED_SERVICE_SERV_UUID:
          LedService_SetParameter(pCharData->paramID, pCharData->dataLen,
                                  pCharData->data);
        break;
    
        case BUTTON_SERVICE_SERV_UUID:
          ButtonService_SetParameter(pCharData->paramID, pCharData->dataLen,
                                     pCharData->data);
        break;
    
      }
    }
    
    /*
     * @brief   Convert {0x01, 0x02} to "01:02"
     *
     * @param   src - source byte-array
     * @param   src_len - length of array
     * @param   dst - destination string-array
     * @param   dst_len - length of array
     *
     * @return  array as string
     */
    static char *Util_convertArrayToHexString(uint8_t const *src, uint8_t src_len,
                                              uint8_t *dst, uint8_t dst_len)
    {
      char        hex[] = "0123456789ABCDEF";
      uint8_t     *pStr = dst;
      uint8_t     avail = dst_len-1;
    
      memset(dst, 0, avail);
    
      while (src_len && avail > 3)
      {
        if (avail < dst_len-1) { *pStr++ = ':'; avail -= 1; };
        *pStr++ = hex[*src >> 4];
        *pStr++ = hex[*src++ & 0x0F];
        avail -= 2;
        src_len--;
      }
    
      if (src_len && avail)
        *pStr++ = ':'; // Indicate not all data fit on line.
    
      return (char *)dst;
    }
    
    /*
     * @brief   Extract the LOCALNAME from Scan/AdvData
     *
     * @param   data - Pointer to the advertisement or scan response data
     *
     * @return  Pointer to null-terminated string with the adv local name.
     */
    static char *Util_getLocalNameStr(const uint8_t *data) {
      uint8_t nuggetLen = 0;
      uint8_t nuggetType = 0;
      uint8_t advIdx = 0;
    
      static char localNameStr[32] = { 0 };
      memset(localNameStr, 0, sizeof(localNameStr));
    
      for (advIdx = 0; advIdx < 32;) {
        nuggetLen = data[advIdx++];
        nuggetType = data[advIdx];
        if ( (nuggetType == GAP_ADTYPE_LOCAL_NAME_COMPLETE ||
              nuggetType == GAP_ADTYPE_LOCAL_NAME_SHORT) && nuggetLen < 31) {
          memcpy(localNameStr, &data[advIdx + 1], nuggetLen - 1);
          break;
        } else {
          advIdx += nuggetLen;
        }
      }
    
      return localNameStr;
    }
    
    /*********************************************************************
    *********************************************************************/
    

    No where in the code is "PRZ_ADV_TOGGLE_EVT " defined. There is however a definition for "APP_MSG_PRZ_ADV_TOGGLE_EVT " .

    typedef enum
    {
      APP_MSG_SERVICE_WRITE = 0,   /* A characteristic value has been written     */
      APP_MSG_SERVICE_CFG,         /* A characteristic configuration has changed  */
      APP_MSG_UPDATE_CHARVAL,      /* Request from ourselves to update a value    */
      APP_MSG_GAP_STATE_CHANGE,    /* The GAP / connection state has changed      */
      APP_MSG_BUTTON_DEBOUNCED,    /* A button has been debounced with new value  */
      APP_MSG_SEND_PASSCODE,       /* A pass-code/PIN is requested during pairing */
      APP_MSG_PRZ_ADV_TOGGLE_EVT,
    } app_msg_types_t;

    This value is indeed used in the file as well.

    Here in user_processApplicationMessage() where the event is handled.

    case APP_MSG_PRZ_ADV_TOGGLE_EVT:
            {
                uint8_t advertEnable=0;
                GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &advertEnable);
                advertEnable = !advertEnable;
                GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertEnable);
    
                Clock_start(Clock_handle(&advertToggleClock));
                break;
            }

    And here in advertToggleClockFxn() where the event is posted.

    static void advertToggleClockFxn(void)  ////////////////////////////////////toggleClock function
    {
        static uint8_t count = 0;
    
        user_enqueueRawAppMsg(APP_MSG_PRZ_ADV_TOGGLE_EVT,
                          (uint8_t *)&count, sizeof(count));
    
        count++;
    }

    So please take a close look at the file once more and try to follow the changes that I have provided. 

    Again, if you have not done so already. Start from a fresh project and make these changes only. I cannot control or explain things that are happening from changes outside of these.

    Can you further explain how you are seeing poor results with LightBlue. I used LightBlue in with the code provided above in order to see that advertisements do not appear when Idle and advertisements do appear when the device is advertising.  I did this by continually sorting by signal strength in LightBlue with the icon to the right of the search icon. If I tapped this sort button repeatedly I could see project_zero advertising when it should have been and then it wouldn't appear when it shouldn't have been.

    I will get out the BLE sniffer to show the advertisements as well and reply with my results when I have them. In the meantime though.

    To answer your question about DEFAULT_ADVERTISING_INTERVAL. This was apart of your first post I believe. Please refer again to the first reply I made in this thread. It explains that the DEFAULT_ADVERTISING_INTERVAL  is the interval between adv events. So in our case.(10s on ... 10s off). During the 10s on period. Every 10ms an adv is sent on the primary channels.

    Best,

    Kris

  • Chaitanya,

    Lastly, I don't have control over your phone or the apps on it. So what you may be seeing is the app caching the adv data in the list you are seeing. I'm not sure why you would be seeing the adv past 10s otherwise.

    Best,
    Kris
  • Chaitanya,

    I hope this provides the proof you need to believe this implementation. I have used a sniffer to capture the following data over the air.

    Here is the same image but concentrated on a smaller section.

    Here you can see the sniffer is identifying a device advertising as "Project Zero" (I verified the BLE address is also correct for my device) and you can see the data being sent in intervals of 10s on / 10s off. If I zoom in all the way you will see that each event advertises on all three primary channels.

    And if I zoom out slightly to see two of these adv events we can see that the interval between the two events is following the DEFAULT_ADVERTISING_INTERVAL  of which you are questioning. Please, if you have not already take a look at the Advertisement SLA. I think you could make use of the information the SLA's provide!

    Now you can be certain that the rf output is what you desire. Furthermore the phone apps may be doing any number of things in the background before reporting results as well as the fact that a phone is not guaranteed to receive every adv packet that may be sent out. It is best to use a BLE sniffer such as the Ellisys as I have to capture real rf results. 

    I hope this has been helpful,

    Best,

    Kris