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.

CC2650: Calling SimpleBLEPeripheral_enqueueMsg from simple_gatt_profile.c

Part Number: CC2650
Other Parts Discussed in Thread: CC2640R2F,

Hi,

I have an application that started from the CC2650LP simple_peripheral example in BLE SDK 2.02.05.02.  I'm using the SCIF to interface to a sensor and detect an event.  When the event is detected, the SCIF code alerts the application in a function I put in simple_gatt_profile.c.   When the event occurs, I would send a notification on one of the characteristics, so I think I need to call SimpleBLEPeripheral_enqueueMsg.  I'm not sure how to accomplish this from simple_gatt_profile.c.  What do I use for event and state parameters to that function?

Alternatively, I could put the code from SimpleBLEPeripheral_enqueueMsg inside my ISR:

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);
}

but then I'm not sure where to get appMsgQueue or sem to "connect" them to the BLE task.

First time OS user here.  Any comments/hints appreciated.

Thanks,

Jim

  • Hi,

    You should just create a function for handling Sensor Controller task alert. You can see the Task Alert Callback C function for the Sensor Controller initialization code below.

    static void SensorController_Init(void)
    {
        uint32_t rtc_Hz = 1;  // 1Hz RTC
    
        scifOsalInit();
        scifOsalRegisterCtrlReadyCallback(SensorController_ReadyCallback);
        scifOsalRegisterTaskAlertCallback(SensorController_TaskAlertCallback);
        scifInit(&scifDriverSetup);
    
        // Set the Sensor Controller task tick interval to 1 second
        scifStartRtcTicksNow(0x00010000 / rtc_Hz);
    
        scifStartTasksNbl(BV(SCIF_ACCELEROMETER_TASK_ID));
        scifWaitOnNbl(50);
    
        // Start Sensor Controller task
        scifStartTasksNbl(BV(SCIF_SENSOR_MEASUREMENT_TASK_TASK_ID));
        scifWaitOnNbl(50);
    }

    -kel

  • Hello Jim,

    I believe Markel's solution should be helpful. Can you verify if you were able to implement it properly?

    Regards,

    Jan

  • Hi Jan and Markel,

    I already have a scTaskAlertCallback function and I think it is working.  When the SCIF interrupt happens, I'd like to send a BLE notify on Char4.  I see Char4 notifies happening every 5s from the simple_peripheral example code.  I've tried several approaches with no success:

    1) putting a call to SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4,sizeof(uint8),&notifyValue); in the SCIF callback:

     

    // calling SetParameter of the char with notify enabled causes BLE to be disconnected
    SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4,sizeof(uint8),&notifyValue);

    2) setting a flag in the SCIF callback, and then checking that flag in another task called ledTask.  If the flag is set, the other task calls SetParameter and then clears the flag.  This means I'd have to run the other task more often to reduce the latency.  The program hangs up when I cause the SCIF interrupt to happen.  I have another call to SetParameter in ledTask that gets executed every pass through, and seems to work.

    3) trying to send a message from the SCIF callback:

                if (pMsg = ICall_malloc(sizeof(sbpEvt_t)))
                {
                  pMsg->event = SBP_BEAT_DETECT_EVT;
                  pMsg->data = notifyValue;
    
                  // Enqueue the message.
                  Util_enqueueMsg(appMsgQueue, sem, (uint8*)pMsg);
                }
    

    I'm not sure how to declare the appMsgQueue or sem to "connect" this to the BLE task.

    I'm not sure which is the right approach or how to debug this.  I'm currently running on a BOOSTXL_CC2650MA since it is close to my target hardware, so the Display functions do not work.  I tried to add in the Uart but wasn't able to get that working.

    I can put in my entire simple_gatt_profile.c if that helps.

    Thanks for your help,

    Jim

  • Hi Jim,

    To send a notification you would need to use the GATT_Notification() function. The user's guide contains some information on how this function works and what is needed to use it. Let me know if that section is helpful. With regards to the UART, you may find out UART echo example helpful. Let me know if you have any issues.

    Regards,

    Jan

  • I see Char4 notifies happening every 5s from the simple_peripheral example code.

    Yes, this is the default implementation. If you do not need this to happen then remove it or comment it out.

    putting a call to SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4,sizeof(uint8),&notifyValue); in the SCIF callback:

    This should work. But normally, you should queue or post an event before calling this. The simple peripheral shows sample usage of queueing and posting an event.

    -kel

  • Thanks for the answer.  

    I don't understand why you would queue and post an event *before* the call to set parameter.   I would have thought that you need to get the data into Char4, then queue the message with the char change event.  Please correct my understanding.  Also, in the simple_peripheral example, the periodic notification of Char4 seems to happen just by calling the SetParameter.  Does this work because it's happening inside the BLE task?

    I put these lines into my simple_gatt_profile.c

                SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4,sizeof(uint8),&notifyValue);
                SimpleBLEPeripheral_enqueueMsg(SBP_CHAR_CHANGE_EVT, SIMPLEPROFILE_CHAR4);
    

    then I had to declare SimpleBLEPeripheral_enqueueMsg in simple_peripheral.h to make simple_gatt_profile.c compile.  But even with this I get an undefined symbol error from the linker for SimpleBLEPeripheral_enqueueMsg.  Apologies if this is a simple C or CCS gap in my knowledge!

    Any hints?

    Thanks!

    Jim

  • Hello Jim,

    The periodic task is triggered using a clock that is set up using the following line of code:

    Util_constructClock(&periodicClock, SimpleBLEPeripheral_clockHandler, SBP_PERIODIC_EVT_PERIOD, 0, false, SBP_PERIODIC_EVT);

    in the SimpleBLEPeripheral_init() function. Which means that every 5 seconds the periodic task will be posted and the SimpleBLEPeripheral_taskFxn() will process it and call the SimpleBLEPeripheral_performPeriodicTask(). 

    To help accomplish your goal, you may be able to create function in the simple_peripheral application file with the extern keyword which should allow it to be accessible to the simple_gatt_profile.c file.

    Regards,

    Jan

  • You queue or post an event so you process the sensor data in the task. Refer to attached moisture_sensor.c and review how I process the sensor controller task alert, then post an event, then write the data to Bluetooth characteristics. This is for cc2640r2f.

    simple_moisture.c
    /******************************************************************************
    
     @file  simple_moisture.c
    
     @brief This file contains the Simple Moisture sample application for use
            with the CC2640R2F Bluetooth Low Energy Protocol Stack.
    
     Group: WCS, BTS
     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>
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Event.h>
    #include <ti/sysbios/knl/Queue.h>
    #include <ti/display/Display.h>
    
    #include <icall.h>
    #include "util.h"
    #include "att_rsp.h"
    
    /* This Header file contains all BLE API and icall structure definition */
    #include "icall_ble_api.h"
    
    #include "devinfoservice.h"
    #include "moistureservice.h"
    #include "ll_common.h"
    
    #include "peripheral.h"
    
    #include "board_key.h"
    
    #include "scif.h"
    
    #include "Board.h"
    
    #include "simple_moisture.h"
    
    /*********************************************************************
     * CONSTANTS
     */
    
    // Advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL          160
    
    // General discoverable mode: advertise indefinitely
    #define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
    
    // Minimum connection interval (units of 1.25ms, 80=100ms) for automatic
    // parameter update request
    #define DEFAULT_DESIRED_MIN_CONN_INTERVAL     80
    
    // Maximum connection interval (units of 1.25ms, 800=1000ms) for automatic
    // parameter update request
    #define DEFAULT_DESIRED_MAX_CONN_INTERVAL     800
    
    // Slave latency to use for automatic parameter update request
    #define DEFAULT_DESIRED_SLAVE_LATENCY         0
    
    // Supervision timeout value (units of 10ms, 1000=10s) for automatic parameter
    // update request
    #define DEFAULT_DESIRED_CONN_TIMEOUT          1000
    
    // After the connection is formed, the peripheral waits until the central
    // device asks for its preferred connection parameters
    #define DEFAULT_ENABLE_UPDATE_REQUEST         GAPROLE_LINK_PARAM_UPDATE_WAIT_REMOTE_PARAMS
    
    // 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
    
    // Application specific event ID for HCI Connection Event End Events
    #define SBP_HCI_CONN_EVT_END_EVT              0x0001
    
    // Type of Display to open
    #if !defined(Display_DISABLE_ALL)
      #if defined(BOARD_DISPLAY_USE_LCD) && (BOARD_DISPLAY_USE_LCD!=0)
        #define SBP_DISPLAY_TYPE Display_Type_LCD
      #elif defined (BOARD_DISPLAY_USE_UART) && (BOARD_DISPLAY_USE_UART!=0)
        #define SBP_DISPLAY_TYPE Display_Type_UART
      #else // !BOARD_DISPLAY_USE_LCD && !BOARD_DISPLAY_USE_UART
        #define SBP_DISPLAY_TYPE 0 // Option not supported
      #endif // BOARD_DISPLAY_USE_LCD && BOARD_DISPLAY_USE_UART
    #else // BOARD_DISPLAY_USE_LCD && BOARD_DISPLAY_USE_UART
      #define SBP_DISPLAY_TYPE 0 // No Display
    #endif // !Display_DISABLE_ALL
    
    // Task configuration
    #define SBP_TASK_PRIORITY                     1
    
    #ifndef SBP_TASK_STACK_SIZE
    #define SBP_TASK_STACK_SIZE                   644
    #endif
    
    // Application events
    #define SBP_STATE_CHANGE_EVT                  0x0001
    #define SBP_CHAR_CHANGE_EVT                   0x0002
    #define SBP_PAIRING_STATE_EVT                 0x0004
    #define SBP_PASSCODE_NEEDED_EVT               0x0008
    #define SBP_CONN_EVT                          0x0010
    
    // Internal Events for RTOS application
    #define SBP_ICALL_EVT                         ICALL_MSG_EVENT_ID // Event_Id_31
    #define SBP_QUEUE_EVT                         UTIL_QUEUE_EVENT_ID // Event_Id_30
    #define SBP_PERIODIC_EVT                      Event_Id_00
    #define SBP_SCTASK_EVT                        Event_Id_01
    
    // Bitwise OR of all events to pend on
    #define SBP_ALL_EVENTS                        (SBP_ICALL_EVT        | \
                                                   SBP_QUEUE_EVT        | \
                                                   SBP_PERIODIC_EVT     | \
                                                   SBP_SCTASK_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
     */
    
    // App event passed from profiles.
    typedef struct
    {
      appEvtHdr_t hdr;  // event header.
      uint8_t *pData;  // event data
    } 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;
    
    // Event globally used to post local events and pend on system and
    // local events.
    static ICall_SyncHandle syncEvent;
    
    // Clock instances for internal periodic events.
    static Clock_Struct periodicClock;
    
    // Queue object used for app messages
    static Queue_Struct appMsg;
    static Queue_Handle appMsgQueue;
    
    // Task configuration
    Task_Struct sbpTask;
    Char sbpTaskStack[SBP_TASK_STACK_SIZE];
    
    // Scan response data (max size = 31 bytes)
    static uint8_t scanRspData[] =
    {
      // complete name
      0x0F,   // length of this data
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'S',
      'i',
      'm',
      'p',
      'l',
      'e',
      'M',
      'o',
      'i',
      's',
      't',
      'u',
      'r',
      'e',
    
      // 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
    };
    
    // Advertisement data (max size = 31 bytes, though this is
    // best kept short to conserve power while advertising)
    static uint8_t advertData[] =
    {
      // Flags: this field sets the device to use general discoverable
      // mode (advertises indefinitely) instead of general
      // discoverable mode (advertise for 30 seconds at a time)
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // service UUID, to notify central devices what services are included
      // in this peripheral
      0x03,   // length of this data
      GAP_ADTYPE_16BIT_MORE,      // some of the UUID's, but not all
      LO_UINT16(MOISTURESERVICE_SERV_UUID),
      HI_UINT16(MOISTURESERVICE_SERV_UUID)
    };
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple Moisture";
    
    uint8_t moistureService_MoistureDataCharVal[MOISTURESERVICE_MOISTUREDATACHAR_LEN] = {0,};
    
    static uint16_t moistureAnalog = 0;
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void SimpleMoisture_init( void );
    static void SimpleMoisture_taskFxn(UArg a0, UArg a1);
    
    static uint8_t SimpleMoisture_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t SimpleMoisture_processGATTMsg(gattMsgEvent_t *pMsg);
    static void SimpleMoisture_processAppMsg(sbpEvt_t *pMsg);
    static void SimpleMoisture_processStateChangeEvt(gaprole_States_t newState);
    static void SimpleMoisture_processCharValueChangeEvt(uint8_t paramID);
    static void SimpleMoisture_performPeriodicTask(void);
    static void SimpleMoisture_clockHandler(UArg arg);
    
    static void SimpleMoisture_passcodeCB(uint8_t *deviceAddr,
                                            uint16_t connHandle,
                                            uint8_t uiInputs, uint8_t uiOutputs,
                                            uint32_t numComparison);
    static void SimpleMoisture_pairStateCB(uint16_t connHandle, uint8_t state,
                                             uint8_t status);
    static void SimpleMoisture_processPairState(uint8_t state, uint8_t status);
    static void SimpleMoisture_processPasscode(uint8_t uiOutputs);
    
    static void SimpleMoisture_stateChangeCB(gaprole_States_t newState);
    static uint8_t SimpleMoisture_enqueueMsg(uint8_t event, uint8_t state,
                                                  uint8_t *pData);
    
    static void SimpleMoisture_connEvtCB(Gap_ConnEventRpt_t *pReport);
    static void SimpleMoisture_processConnEvt(Gap_ConnEventRpt_t *pReport);
    
    //Sensor Controller
    static void SensorController_init(void);
    static void SensorController_startMoistureTask(void);
    static void SensorController_stopMoistureTask(void);
    void SensorController_readyCallback(void);
    void SensorController_taskalertCallback(void);
    
    
    /*********************************************************************
     * EXTERN FUNCTIONS
     */
    extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // Peripheral GAPRole Callbacks
    static gapRolesCBs_t SimpleMoisture_gapRoleCBs =
    {
      SimpleMoisture_stateChangeCB     // GAPRole State Change Callbacks
    };
    
    // GAP Bond Manager Callbacks
    // These are set to NULL since they are not needed. The application
    // is set up to only perform justworks pairing.
    static gapBondCBs_t simplePeripheral_BondMgrCBs =
    {
      SimpleMoisture_passcodeCB,  // Passcode callback
      SimpleMoisture_pairStateCB  // Pairing / Bonding state Callback
    };
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*********************************************************************
     * The following typedef and global handle the registration to connection event
     */
    typedef enum
    {
       NOT_REGISTER       = 0,
       FOR_AOA_SCAN       = 1,
       FOR_ATT_RSP        = 2,
       FOR_AOA_SEND       = 4,
       FOR_TOF_SEND       = 8
    }connectionEventRegisterCause_u;
    
    // Handle the registration and un-registration for the connection event, since only one can be registered.
    uint32_t       connectionEventRegisterCauseBitMap = NOT_REGISTER; //see connectionEventRegisterCause_u
    
    /*********************************************************************
    * @fn      SensorController_readyCallback
    *
    * @brief
    *
    * @param   None.
    *
    * @return  None.
    */
    void SensorController_readyCallback(void)
    {
    
    }
    
    /*********************************************************************
    * @fn      SensorController_taskalertCallback
    *
    * @brief
    *
    * @param   None.
    *
    * @return  None.
    */
    void SensorController_taskalertCallback(void)
    {
      uint32_t bvAlertEvents = 0;
      // Clear the ALERT interrupt source
      scifClearAlertIntSource();
    
      bvAlertEvents = scifGetAlertEvents();
    
      if (bvAlertEvents & BV(SCIF_MOISTURE_TASK_TASK_ID))
      {
        //... Access Sensor Controller task data structures here ...
        moistureAnalog = scifTaskData.moistureTask.output.moistureValue;
      }
    
      // Acknowledge the ALERT event
      scifAckAlertEvents();
    
      Event_post(syncEvent, SBP_SCTASK_EVT);
    }
    
    /*********************************************************************
    * @fn      SensorController_startMoistureTask
    *
    * @brief
    *
    * @param   None.
    *
    * @return  None.
    */
    static void SensorController_startMoistureTask(void)
    {
      // Start the Potentiometer Sampler task
      while (scifWaitOnNbl(0) != SCIF_SUCCESS);
      // Reset all data structures except configuration
      scifResetTaskStructs(BV(SCIF_MOISTURE_TASK_TASK_ID), BV(SCIF_STRUCT_OUTPUT));
      // Start the "Moisture Task" Sensor Controller task
      scifStartTasksNbl(BV(SCIF_MOISTURE_TASK_TASK_ID));
    }
    
    /*********************************************************************
    * @fn      SensorController_stopMoistureTask
    *
    * @brief
    *
    * @param   None.
    *
    * @return  None.
    */
    static void SensorController_stopMoistureTask(void)
    {
      // Start the Potentiometer Sampler task
      while (scifWaitOnNbl(0) != SCIF_SUCCESS);
      scifStopTasksNbl(BV(SCIF_MOISTURE_TASK_TASK_ID));
    }
    
    
    /*********************************************************************
    * @fn      SensorController_init
    *
    * @brief
    *
    * @param   None.
    *
    * @return  None.
    */
    static void SensorController_init(void)
    {
      // Initialize the SCIF operating system abstraction layer
      scifOsalInit();
      scifOsalRegisterCtrlReadyCallback(SensorController_readyCallback);
      scifOsalRegisterTaskAlertCallback(SensorController_taskalertCallback);
    
      // Initialize the SCIF driver
      scifInit(&scifDriverSetup);
    
      // Enable RTC ticks
      scifStartRtcTicksNow(0x00050000);
    }
    
    
    /*********************************************************************
     * @fn      SimpleMoisture_RegistertToAllConnectionEvent()
     *
     * @brief   register to receive connection events for all the connection
     *
     * @param connectionEventRegisterCause represents the reason for registration
     *
     * @return @ref SUCCESS
     *
     */
    bStatus_t SimpleMoisture_RegistertToAllConnectionEvent (connectionEventRegisterCause_u connectionEventRegisterCause)
    {
      bStatus_t status = SUCCESS;
    
      // in case  there is no registration for the connection event, make the registration
      if (!CONNECTION_EVENT_IS_REGISTERED)
      {
        status = GAP_RegisterConnEventCb(SimpleMoisture_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);
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_UnRegistertToAllConnectionEvent()
     *
     * @brief   Unregister connection events
     *
     * @param connectionEventRegisterCause represents the reason for registration
     *
     * @return @ref SUCCESS
     *
     */
    bStatus_t SimpleMoisture_UnRegistertToAllConnectionEvent (connectionEventRegisterCause_u connectionEventRegisterCause)
    {
      bStatus_t status = SUCCESS;
    
      CONNECTION_EVENT_REGISTER_BIT_REMOVE(connectionEventRegisterCause);
      // in case  there is no more registration for the connection event than unregister
      if (!CONNECTION_EVENT_IS_REGISTERED)
      {
        GAP_RegisterConnEventCb(SimpleMoisture_connEvtCB, GAP_CB_UNREGISTER, LINKDB_CONNHANDLE_ALL);
      }
    
      return(status);
    }
    
     /*********************************************************************
     * @fn      SimpleMoisture_createTask
     *
     * @brief   Task creation function for the Simple Peripheral.
     *
     * @param   None.
     *
     * @return  None.
     */
    void SimpleMoisture_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, SimpleMoisture_taskFxn, &taskParams, NULL);
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_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 SimpleMoisture_init(void)
    {
      // ******************************************************************
      // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages.
      ICall_registerApp(&selfEntity, &syncEvent);
    
      // 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, SimpleMoisture_clockHandler,
                          SBP_PERIODIC_EVT_PERIOD, 0, false, SBP_PERIODIC_EVT);
    
    #if !defined(Display_DISABLE_ALL)
      dispHandle = Display_open(SBP_DISPLAY_TYPE, NULL);
    #endif
    
      // Set GAP Parameters: After a connection was established, delay in seconds
      // before sending when GAPRole_SetParameter(GAPROLE_PARAM_UPDATE_ENABLE,...)
      // uses GAPROLE_LINK_PARAM_UPDATE_INITIATE_BOTH_PARAMS or
      // GAPROLE_LINK_PARAM_UPDATE_INITIATE_APP_PARAMS
      // For current defaults, this has no effect.
      GAP_SetParamValue(TGAP_CONN_PAUSE_PERIPHERAL, DEFAULT_CONN_PAUSE_PERIPHERAL);
    
      // Start the Device:
      // Please Notice that in case of wanting to use the GAPRole_SetParameter
      // function with GAPROLE_IRK or GAPROLE_SRK parameter - Perform
      // these function calls before the GAPRole_StartDevice use.
      // (because Both cases are updating the gapRole_IRK & gapRole_SRK variables).
      VOID GAPRole_StartDevice(&SimpleMoisture_gapRoleCBs);
    
      // Setup the Peripheral GAPRole Profile. For more information see the User's
      // Guide:
      // http://software-dl.ti.com/lprf/sdg-latest/html/
      {
        // Device starts advertising upon initialization of GAP
        uint8_t initialAdvertEnable = TRUE;
    
        // 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 re-enabled by the application
        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 Peripheral GAPRole 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 Device Name characteristic in the GAP GATT Service
      // For more information, see the section in the User's Guide:
      // http://software-dl.ti.com/lprf/sdg-latest/html
      GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
      // Set GAP Parameters to set the advertising interval
      // For more information, see the GAP section of the User's Guide:
      // http://software-dl.ti.com/lprf/sdg-latest/html
      {
        // Use the same interval for general and limited advertising.
        // Note that only general advertising will occur based on the above configuration
        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. For more information see the section in the
      // User's Guide:
      // http://software-dl.ti.com/lprf/sdg-latest/html/
      {
        // Don't send a pairing request after connecting; the peer device must
        // initiate pairing
        uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        // Use authenticated pairing: require passcode.
        uint8_t mitm = TRUE;
        // This device only has display capabilities. Therefore, it will display the
        // passcode during pairing. However, since the default passcode is being
        // used, there is no need to display anything.
        uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
        // Request bonding (storing long-term keys for re-encryption upon subsequent
        // connections without repairing)
        uint8_t bonding = TRUE;
        // Whether to replace the least recently used entry when bond list is full,
        // and a new device is bonded.
        // Alternative is pairing succeeds but bonding fails, unless application has
        // manually erased at least one bond.
        uint8_t replaceBonds = FALSE;
    
        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);
        GAPBondMgr_SetParameter(GAPBOND_LRU_BOND_REPLACEMENT, sizeof(uint8_t), &replaceBonds);
      }
    
      SensorController_init();
    
      // Initialize GATT attributes
      GGS_AddService(GATT_ALL_SERVICES);           // GAP GATT Service
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT Service
      DevInfo_AddService();                        // Device Information Service
      MoistureService_AddService(selfEntity);
    
      // Initialization of characteristics in moistureService that are readable.
      MoistureService_SetParameter(MOISTURESERVICE_MOISTUREDATACHAR_ID,
                                   MOISTURESERVICE_MOISTUREDATACHAR_LEN,
                                   moistureService_MoistureDataCharVal);
    
      // Start Bond Manager and register callback
      VOID GAPBondMgr_Register(&simplePeripheral_BondMgrCBs);
    
      // Register with GAP for HCI/Host messages. This is needed to receive HCI
      // events. For more information, see the section in the User's Guide:
      // http://software-dl.ti.com/lprf/sdg-latest/html
      GAP_RegisterForMsgs(selfEntity);
    
      // Register for GATT local events and ATT Responses pending for transmission
      GATT_RegisterForMsgs(selfEntity);
    
      //Set default values for Data Length Extension
      {
        //Set initial values to maximum, RX is set to max. by default(251 octets, 2120us)
        #define APP_SUGGESTED_PDU_SIZE 251 //default is 27 octets(TX)
        #define APP_SUGGESTED_TX_TIME 2120 //default is 328us(TX)
    
        //This API is documented in hci.h
        //See the LE Data Length Extension section in the BLE-Stack User's Guide for information on using this command:
        //http://software-dl.ti.com/lprf/sdg-latest/html/cc2640/index.html
        //HCI_LE_WriteSuggestedDefaultDataLenCmd(APP_SUGGESTED_PDU_SIZE, APP_SUGGESTED_TX_TIME);
      }
    
    #if !defined (USE_LL_CONN_PARAM_UPDATE)
      // Get the currently set local supported LE features
      // The HCI will generate an HCI event that will get received in the main
      // loop
      HCI_LE_ReadLocalSupportedFeaturesCmd();
    #endif // !defined (USE_LL_CONN_PARAM_UPDATE)
    #if !defined(Display_DISABLE_ALL)
      Display_print0(dispHandle, 0, 0, "BLE Peripheral");
    #endif
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_taskFxn
     *
     * @brief   Application task entry point for the Simple Peripheral.
     *
     * @param   a0, a1 - not used.
     *
     * @return  None.
     */
    static void SimpleMoisture_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      SimpleMoisture_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, SBP_ALL_EVENTS,
                            ICALL_TIMEOUT_FOREVER);
    
        if (events)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          // Fetch any available messages that might have been sent from the stack
          if (ICall_fetchServiceMsg(&src, &dest,
                                    (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
          {
            uint8 safeToDealloc = TRUE;
    
            if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
            {
              ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
    
              if (pEvt->signature != 0xffff)
              {
                // Process inter-task message
                safeToDealloc = SimpleMoisture_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // If RTOS queue is not empty, process app message.
          if (events & SBP_QUEUE_EVT)
          {
            while (!Queue_empty(appMsgQueue))
            {
              sbpEvt_t *pMsg = (sbpEvt_t *)Util_dequeueMsg(appMsgQueue);
              if (pMsg)
              {
                // Process message.
                SimpleMoisture_processAppMsg(pMsg);
    
                // Free the space from the message.
                ICall_free(pMsg);
              }
            }
          }
    
          if (events & SBP_PERIODIC_EVT)
          {
            Util_startClock(&periodicClock);
    
            // Perform periodic application task
            SimpleMoisture_performPeriodicTask();
          }
    
          if (events & SBP_SCTASK_EVT)
          {
            MoistureService_SetParameter(MOISTURESERVICE_MOISTUREDATACHAR_ID,
                                         MOISTURESERVICE_MOISTUREDATACHAR_LEN,
                                         &moistureAnalog);
          }
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_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 SimpleMoisture_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = SimpleMoisture_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
                {
    
    #if !defined (USE_LL_CONN_PARAM_UPDATE)
                  // This code will disable the use of the LL_CONNECTION_PARAM_REQ
                  // control procedure (for connection parameter updates, the
                  // L2CAP Connection Parameter Update procedure will be used
                  // instead). To re-enable the LL_CONNECTION_PARAM_REQ control
                  // procedures, define the symbol USE_LL_CONN_PARAM_UPDATE
                  // The L2CAP Connection Parameter Update procedure is used to
                  // support a delta between the minimum and maximum connection
                  // intervals required by some iOS devices.
    
                  // Parse Command Complete Event for opcode and status
                  hciEvt_CmdComplete_t* command_complete = (hciEvt_CmdComplete_t*) pMsg;
                  uint8_t   pktStatus = command_complete->pReturnParam[0];
    
                  //find which command this command complete is for
                  switch (command_complete->cmdOpcode)
                  {
                    case HCI_LE_READ_LOCAL_SUPPORTED_FEATURES:
                      {
                        if (pktStatus == SUCCESS)
                        {
                          uint8_t featSet[8];
    
                          // Get current feature set from received event (bits 1-9
                          // of the returned data
                          memcpy( featSet, &command_complete->pReturnParam[1], 8 );
    
                          // Clear bit 1 of byte 0 of feature set to disable LL
                          // Connection Parameter Updates
                          CLR_FEATURE_FLAG( featSet[0], LL_FEATURE_CONN_PARAMS_REQ );
    
                          // Update controller with modified features
                          HCI_EXT_SetLocalSupportedFeaturesCmd( featSet );
                        }
                      }
                      break;
    
                    default:
                      //do nothing
                      break;
                  }
    #endif // !defined (USE_LL_CONN_PARAM_UPDATE)
    
                }
                break;
    
              case HCI_BLE_HARDWARE_ERROR_EVENT_CODE:
                AssertHandler(HAL_ASSERT_CAUSE_HARDWARE_ERROR,0);
                break;
    
              default:
                break;
            }
          }
          break;
    
          default:
            // do nothing
            break;
    
        }
    
      return (safeToDealloc);
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_processGATTMsg
     *
     * @brief   Process GATT messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t SimpleMoisture_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      // See if GATT server was unable to transmit an ATT response
      if (attRsp_isAttRsp(pMsg))
      {
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if( SimpleMoisture_RegistertToAllConnectionEvent(FOR_ATT_RSP) == SUCCESS)
        {
          // 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.
    #if !defined(Display_DISABLE_ALL)
        Display_print1(dispHandle, 5, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
    #endif
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
    #if !defined(Display_DISABLE_ALL)
        Display_print1(dispHandle, 5, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
    #endif
      }
    
      // 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      SimpleMoisture_processConnEvt
     *
     * @brief   Process connection event.
     *
     * @param pReport pointer to connection event report
     */
    static void SimpleMoisture_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)
        if (attRsp_sendAttRsp() == SUCCESS)
        {
            // Disable connection event end notice
            SimpleMoisture_UnRegistertToAllConnectionEvent (FOR_ATT_RSP);
        }
      }
    
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_processAppMsg
     *
     * @brief   Process an incoming callback from a profile.
     *
     * @param   pMsg - message to process
     *
     * @return  None.
     */
    static void SimpleMoisture_processAppMsg(sbpEvt_t *pMsg)
    {
      switch (pMsg->hdr.event)
      {
        case SBP_STATE_CHANGE_EVT:
          {
            SimpleMoisture_processStateChangeEvt((gaprole_States_t)pMsg->
                                                    hdr.state);
          }
          break;
    
        case SBP_CHAR_CHANGE_EVT:
          {
            SimpleMoisture_processCharValueChangeEvt(pMsg->hdr.state);
          }
          break;
    
        // Pairing event
        case SBP_PAIRING_STATE_EVT:
          {
            SimpleMoisture_processPairState(pMsg->hdr.state, *pMsg->pData);
    
            ICall_free(pMsg->pData);
            break;
          }
    
        // Passcode event
        case SBP_PASSCODE_NEEDED_EVT:
          {
            SimpleMoisture_processPasscode(*pMsg->pData);
    
            ICall_free(pMsg->pData);
            break;
          }
    
    	case SBP_CONN_EVT:
          {
            SimpleMoisture_processConnEvt((Gap_ConnEventRpt_t *)(pMsg->pData));
    
            ICall_free(pMsg->pData);
            break;
    	  }
    
        default:
          // Do nothing.
          break;
      }
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_stateChangeCB
     *
     * @brief   Callback from GAP Role indicating a role state change.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void SimpleMoisture_stateChangeCB(gaprole_States_t newState)
    {
      SimpleMoisture_enqueueMsg(SBP_STATE_CHANGE_EVT, newState, NULL);
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_processStateChangeEvt
     *
     * @brief   Process a pending GAP Role state change event.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void SimpleMoisture_processStateChangeEvt(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);
    
    #if !defined(Display_DISABLE_ALL)
            // Display device address
            Display_print0(dispHandle, 1, 0, Util_convertBdAddr2Str(ownAddress));
            Display_print0(dispHandle, 2, 0, "Initialized");
    #endif
          }
          break;
    
        case GAPROLE_ADVERTISING:
    #if !defined(Display_DISABLE_ALL)
          Display_print0(dispHandle, 2, 0, "Advertising");
    #endif
          break;
    
        case GAPROLE_CONNECTED:
          {
            linkDBInfo_t linkInfo;
            uint8_t numActive = 0;
    
            SensorController_startMoistureTask();
    
            Util_startClock(&periodicClock);
    
            numActive = linkDB_NumActive();
    
            // Use numActive to determine the connection handle of the last
            // connection
            if ( linkDB_GetInfo( numActive - 1, &linkInfo ) == SUCCESS )
            {
    #if !defined(Display_DISABLE_ALL)
              Display_print1(dispHandle, 2, 0, "Num Conns: %d", (uint16_t)numActive);
              Display_print0(dispHandle, 3, 0, Util_convertBdAddr2Str(linkInfo.addr));
    #endif
            }
            else
            {
              uint8_t peerAddress[B_ADDR_LEN];
    
              GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);
    #if !defined(Display_DISABLE_ALL)
              Display_print0(dispHandle, 2, 0, "Connected");
              Display_print0(dispHandle, 3, 0, Util_convertBdAddr2Str(peerAddress));
    #endif
            }
          }
          break;
    
        case GAPROLE_CONNECTED_ADV:
    #if !defined(Display_DISABLE_ALL)
          Display_print0(dispHandle, 2, 0, "Connected Advertising");
    #endif
          break;
    
        case GAPROLE_WAITING:
    
          SensorController_stopMoistureTask();
    
          Util_stopClock(&periodicClock);
          attRsp_freeAttRsp(bleNotConnected);
    #if !defined(Display_DISABLE_ALL)
          Display_print0(dispHandle, 2, 0, "Disconnected");
    
          // Clear remaining lines
          Display_clearLines(dispHandle, 3, 5);
    #endif
          break;
    
        case GAPROLE_WAITING_AFTER_TIMEOUT:
    
          SensorController_stopMoistureTask();
    
          attRsp_freeAttRsp(bleNotConnected);
    #if !defined(Display_DISABLE_ALL)
          Display_print0(dispHandle, 2, 0, "Timed Out");
    
          // Clear remaining lines
          Display_clearLines(dispHandle, 3, 5);
    #endif
         break;
    
        case GAPROLE_ERROR:
    #if !defined(Display_DISABLE_ALL)
          Display_print0(dispHandle, 2, 0, "Error");
    #endif
          break;
    
        default:
    #if !defined(Display_DISABLE_ALL)
          Display_clearLine(dispHandle, 2);
    #endif
          break;
      }
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_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 SimpleMoisture_processCharValueChangeEvt(uint8_t paramID)
    {
    
      switch(paramID)
      {
    
        default:
          // should not reach here!
          break;
      }
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_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 SimpleMoisture_performPeriodicTask(void)
    {
    
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_pairStateCB
     *
     * @brief   Pairing state callback.
     *
     * @return  none
     */
    static void SimpleMoisture_pairStateCB(uint16_t connHandle, uint8_t state,
                                                uint8_t status)
    {
      uint8_t *pData;
    
      // Allocate space for the event data.
      if ((pData = ICall_malloc(sizeof(uint8_t))))
      {
        *pData = status;
    
        // Queue the event.
        SimpleMoisture_enqueueMsg(SBP_PAIRING_STATE_EVT, state, pData);
      }
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_processPairState
     *
     * @brief   Process the new paring state.
     *
     * @return  none
     */
    static void SimpleMoisture_processPairState(uint8_t state, uint8_t status)
    {
      if (state == GAPBOND_PAIRING_STATE_STARTED)
      {
    #if !defined(Display_DISABLE_ALL)
        Display_print0(dispHandle, 2, 0, "Pairing started");
    #endif
      }
      else if (state == GAPBOND_PAIRING_STATE_COMPLETE)
      {
        if (status == SUCCESS)
        {
    #if !defined(Display_DISABLE_ALL)
          Display_print0(dispHandle, 2, 0, "Pairing success");
    #endif
        }
        else
        {
    #if !defined(Display_DISABLE_ALL)
          Display_print1(dispHandle, 2, 0, "Pairing fail: %d", status);
    #endif
        }
      }
      else if (state == GAPBOND_PAIRING_STATE_BONDED)
      {
        if (status == SUCCESS)
        {
    #if !defined(Display_DISABLE_ALL)
          Display_print0(dispHandle, 2, 0, "Bonding success");
    #endif
        }
      }
      else if (state == GAPBOND_PAIRING_STATE_BOND_SAVED)
      {
        if (status == SUCCESS)
        {
    #if !defined(Display_DISABLE_ALL)
          Display_print0(dispHandle, 2, 0, "Bond save success");
    #endif
        }
        else
        {
    #if !defined(Display_DISABLE_ALL)
          Display_print1(dispHandle, 2, 0, "Bond save failed: %d", status);
    #endif
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_passcodeCB
     *
     * @brief   Passcode callback.
     *
     * @return  none
     */
    static void SimpleMoisture_passcodeCB(uint8_t *deviceAddr,
                                            uint16_t connHandle,
                                            uint8_t uiInputs,
                                            uint8_t uiOutputs,
                                            uint32_t numComparison)
    {
      uint8_t *pData;
    
      // Allocate space for the passcode event.
      if ((pData = ICall_malloc(sizeof(uint8_t))))
      {
        *pData = uiOutputs;
    
        // Enqueue the event.
        SimpleMoisture_enqueueMsg(SBP_PASSCODE_NEEDED_EVT, 0, pData);
      }
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_processPasscode
     *
     * @brief   Process the Passcode request.
     *
     * @return  none
     */
    static void SimpleMoisture_processPasscode(uint8_t uiOutputs)
    {
      // This app uses a default passcode. A real-life scenario would handle all
      // pairing scenarios and likely generate this randomly.
      uint32_t passcode = B_APP_DEFAULT_PASSCODE;
    
      // Display passcode to user
      if (uiOutputs != 0)
      {
    #if !defined(Display_DISABLE_ALL)
        Display_print1(dispHandle, 4, 0, "Passcode: %d", passcode);
    #endif
      }
    
      uint16_t connectionHandle;
      GAPRole_GetParameter(GAPROLE_CONNHANDLE, &connectionHandle);
    
      // Send passcode response
      GAPBondMgr_PasscodeRsp(connectionHandle, SUCCESS, passcode);
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_clockHandler
     *
     * @brief   Handler function for clock timeouts.
     *
     * @param   arg - event type
     *
     * @return  None.
     */
    static void SimpleMoisture_clockHandler(UArg arg)
    {
      // Wake up the application.
      Event_post(syncEvent, arg);
    }
    
    /*********************************************************************
     * @fn      SimpleMoisture_connEvtCB
     *
     * @brief   Connection event callback.
     *
     * @param pReport pointer to connection event report
     */
    static void SimpleMoisture_connEvtCB(Gap_ConnEventRpt_t *pReport)
    {
      // Enqueue the event for processing in the app context.
      if( SimpleMoisture_enqueueMsg(SBP_CONN_EVT, 0 ,(uint8_t *) pReport) == FALSE)
      {
        ICall_free(pReport);
      }
    
    }
    
    /*********************************************************************
     *
     * @brief   Creates a message and puts the message in RTOS queue.
     *
     * @param   event - message event.
     * @param   state - message state.
     * @param   pData - message data pointer.
     *
     * @return  TRUE or FALSE
     */
    static uint8_t SimpleMoisture_enqueueMsg(uint8_t event, uint8_t state,
                                               uint8_t *pData)
    {
      sbpEvt_t *pMsg = ICall_malloc(sizeof(sbpEvt_t));
    
      // Create dynamic pointer to message.
      if (pMsg)
      {
        pMsg->hdr.event = event;
        pMsg->hdr.state = state;
        pMsg->pData = pData;
    
        // Enqueue the message.
        return Util_enqueueMsg(appMsgQueue, syncEvent, (uint8_t *)pMsg);
      }
    
      return FALSE;
    }
    /*********************************************************************
    *********************************************************************/
    

  • Hi Kel,

    Thanks for the simple_moisture code.  I moved my SCIF driver callback into simple_peripheral.c:

    // SCIF driver callback: Sensor Controller task code has generated an alert interrupt
    void scTaskAlertCallback(void) {
        uint8_t beatEnable;
    
        // Clear the ALERT interrupt source
        scifClearAlertIntSource();
    
        // Find which tasks have generated ALERT event
        uint32_t bvAlertEvents = scifGetAlertEvents();
    
        // If the "Accelerometer" task generated interrupt ...
        if (bvAlertEvents & (1 << SCIF_ACCELEROMETER_TASK_ID)) {
            
            // read the data from the SCIF...
            
                Event_post(syncEvent, SBP_BEAT_DETECT_EVT);
    //            Event_post(sem, SBP_BEAT_DETECT_EVT);
        }
    
        // If the "Sleep Mode" task generated interrupt ...
        if (bvAlertEvents & (1 << SCIF_SLEEP_MODE_TASK_ID)) {
            // wake up from sleep mode
        }
    
        // Acknowledge the ALERT event
        scifAckAlertEvents();
    }
    

    I can tell that this callback is happening.  With the call to Event_post in place, the code hangs up (my other task stops executing).  With it commented out, the code runs.

    I have this code in SimpleBLEPeripheral_taskFxn:

        if (events & SBP_BEAT_DETECT_EVT)
        {
            events &= ~SBP_BEAT_DETECT_EVT;
            // update characteristic
            SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4,sizeof(uint8),&beatData);
        }
    

    I notice that simple_moisture uses a SyncEvent while simple_peripheral uses a semaphore.  I've tried both ways with the same result.

    Can I send you my code to have a look at?  I have only modified simple_peripheral and simple_gatt_profile files from the simple_peripheral example.

    Thanks,

    Jim

  • Hi Jim,

    Attach your simple_peripheral.c here for review using the insert->file tool below. As, I have mentioned simple_moisture.c is for CC2640R2F, so some code implementations are different compared to CC2650.

    -kel

  • Here is a .zip with my changes to simple_peripheral.

    2021-04-09 10-28 AM.zip

    I'm planning to eventually move the 2640R2F.

    Thanks,

    Jim

  • Hello Jim,

    After scifAckAlertEvents();, you can post a sem with your event like this below. I am not sure if this will work, it has been a very long time since I worked with CC2650

      // Store the event.
      events |= SBP_BEAT_DETECT_EVT;
    
      // Wake up the application.
      Semaphore_post(sem);

    or you can queue, you need to study how the queueing works from the simple peripheral code.

    -kel

  • Thanks for the suggestion.  I tried it but it doesn't send the notification.  I can see that the interrupt is happening and that a value is coming back from the SCIF.  I'll do some reading on how queueing works in simple_peripheral.

    Jim

  • Can you put a breakpoint here, and see if it goes there.

        if (events & SBP_BEAT_DETECT_EVT)
        {
            events &= ~SBP_BEAT_DETECT_EVT;
            // update characteristic
            SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4,sizeof(uint8),&beatData);
        }

  • yes, i put a breakpoint at line 3 above, and it gets there when I cause the interrupt to happen.

    I keep thinking that this should be simple... the periodic event reads char3 and writes the value to char4 and i can see the resulting notification.  So I'm not sure why when I write to char4 (using SimpleProfile_SetParameter) elsewhere it doesn't trigger a notification.

    Jim

  • yes, i put a breakpoint at line 3 above, and it gets there when I cause the interrupt to happen.

    I keep thinking that this should be simple... the periodic event reads char3 and writes the value to char4 and i can see the resulting notification.  So I'm not sure why when I write to char4 (using SimpleProfile_SetParameter) elsewhere it doesn't trigger a notification

    This is good. You commented out the copying of char 3 to char 4. Rebuild All, there should be no errors or warnings. Use IOS LightBlue App or Android nRF Connect App for testing make sure you enable notification for Characteristics 4.

    -kel

  • Hi Kel,

    After a clean build, this is working!  So I think the fix was the use of Semaphore_post.  Now I see notifies happening in nRF Connect when the SCIF alert is triggered.

    Thanks for all of your suggestions!

    Jim