This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Custom BLE service on nRF52840 (pca10056) with nRF5 SDK 15.2

Since APIs have changed as new nRF5 SDKs have released from time to time, it is not clear how custom service can be implemented on BLE peripheral for nRF52840 (pca10056) S140 Soft Device. I am using Nordic nRF52840 Development Kit with SEGGER Embedded Studio. Please help!

Parents
  • Here's some of what I'm currently using.

    From the header file:

    /* UUID E24AAE40-3F6A-4F5D-828F-1F1248F75962 reversed*/
    #define LDS_UUID_BASE     {0x62, 0x59, 0xF7, 0x48, 0x12, 0x1F, 0x8F, 0x82,  \
                               0x5D, 0x4F, 0x6A, 0x3F, 0x00, 0x00, 0x4A, 0xE2}
    #define LDS_UUID_SERVICE  0xAE40
    #define LDS_UUID_CHAR     0xAE41
    
    #define LDS_DATA_SIZE (10)
    
    //*****************************************************************************
    // Data Types and Enums
    //*****************************************************************************
    /* Forward declaration of the ble_lds_t type */
    typedef struct ble_lds_struct ble_lds_t;
    
    
    typedef void (*ble_lds_write_handler_t) (ble_lds_t * p_lbs, lds_drive_data_t data);
    
    /* options and data needed to initialize the service*/
    typedef struct
    {
      ble_lds_write_handler_t lds_write_handler; /* Called when Characteristic is written to by the client e.g. phone app. */
    } ble_lds_init_t;
      
    /* This structure contains various status information for the service. */
    typedef struct ble_lds_struct
    {
      uint16_t                    service_handle;    /* Handle of Live Monitor Service (as provided by the BLE stack). */
      ble_gatts_char_handles_t    lds_char_handles; /* Handles related to the Characteristic. */
      uint8_t                     uuid_type;         /* UUID type for the Service. In this case 128 bit. */
      ble_lds_write_handler_t     lds_write_handler; /* Event handler to be called when the Characteristic is written to by the client e.g. phone app. */
      uint16_t                    conn_handle;       /* Handle for the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection) */
    }ble_lds_struct_t;
    
    
    

    From the source code file:

    static uint32_t lds_CharAdd(ble_lds_t *p_lds, const ble_lds_init_t *p_lds_init);
    static ble_lds_t m_LiveDriveService;
    NRF_SDH_BLE_OBSERVER(m_LiveDriveService_obs, BLE_LDS_BLE_OBSERVER_PRIO, 
                          LDS_OnBLEEventHandler, &m_LiveDriveService);
                          
    static uint8_t m_scratch_buffer[BLE_GATT_ATT_MTU_DEFAULT-3];
    static lds_drive_data_t m_lds_data;
    
    ///############################################################################
    /// @fn              lds_CharAdd
    /// @remark          param[out] p_lds     Live Drive Service structure.
    ///                  param[in] p_lds_init Initial Live Drive Service data
    /// @return          NRF_SUCCESS on success, otherwise an error code from the
    ///                  SoftDevice
    /// @see Description Function for adding the Characteristic to the service
    /// @warning         SoftDevice MUST be loaded and initialised first
    ///#############################################################################
    static uint32_t lds_CharAdd(ble_lds_t * p_lds, const ble_lds_init_t *p_lds_init)
    {
        if (NULL == p_lds)
        {
            return NRF_ERROR_INTERNAL;
        }
        ble_add_char_params_t add_char;
        memset(&m_lds_data,0, sizeof(m_lds_data));
          
        memset(&add_char, 0, sizeof(add_char));
        add_char.uuid             = LDS_UUID_CHAR;
        add_char.uuid_type        = p_lds->uuid_type;
        add_char.init_len         = sizeof(ble_lds_struct_t);//sizeof(uint8_t);
        add_char.max_len          = sizeof(ble_lds_struct_t);
        add_char.char_props.read  = 1;
        add_char.char_props.write = 1;
        add_char.p_init_value     = (uint8_t*)&m_lds_data.command;
        add_char.is_var_len       = 0;
    
        add_char.read_access  = SEC_OPEN;
        add_char.write_access = SEC_OPEN;
    
        return characteristic_add(p_lds->service_handle, &add_char, &p_lds->lds_char_handles);
    
    }
    
    ///############################################################################
    /// @fn              lds_OnWrite
    /// @remark          param[in] p_lds      Live Drive Service structure.
    ///                  param[in] p_ble_evt  Event received from the BLE stack.
    /// @return          None
    /// @see Description Handles the characteristic write event
    /// @warning         None
    ///#############################################################################
    static void lds_OnWrite(ble_lds_t * p_lds, ble_evt_t const * p_ble_evt)
    {
        VERIFY_PARAM_NOT_NULL_VOID(p_lds);
        VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
    
        /* get the write event data */
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        lds_drive_data_t newData={0};
        /* confirm that characteristic was written to, data length is correct and
        that an event handler has been set */
        if ((p_evt_write->handle == p_lds->lds_char_handles.value_handle) && (sizeof(newData.command) <= p_evt_write->len) &&
            (p_lds->lds_write_handler != NULL))
        {
            memcpy(&newData.command, p_evt_write->data, sizeof(newData.command));
    
            /* pass write event data to the event handler */
            p_lds->lds_write_handler(p_lds, newData);
        }
    }
    
    ///############################################################################
    /// @fn              LDS_ServiceInit
    ///                  param[in] p_lds_init - Information needed to initialize the
    ///                                         service.
    /// @return          NRF_SUCCESS If the service was initialized, otherwise an
    ///                  error code is returned.
    /// @see Description Initialises the Live Drive Service
    /// @warning         SoftDevice MUST be loaded and initialised first
    ///#############################################################################
    uint32_t LDS_ServiceInit(const ble_lds_init_t * p_lds_init)
    {
      
      /* initialise module data */
      memset(m_scratch_buffer,0,sizeof(m_scratch_buffer));
      memset(&m_lds_data, 0, sizeof(m_lds_data));
      
        uint32_t err_code;
        ble_uuid_t ble_uuid;
        ble_uuid128_t base_uuid = {LDS_UUID_BASE};
    
        VERIFY_PARAM_NOT_NULL(p_lds_init);
        VERIFY_PARAM_NOT_NULL(p_lds_init->lds_write_handler);
    
        m_LiveDriveService.conn_handle = BLE_CONN_HANDLE_INVALID;
    
        /* Initialize service structure */
        m_LiveDriveService.lds_write_handler = p_lds_init->lds_write_handler; 
    
        /* Add vendor specific base UUID to BLE stack and update the service
         * structure UUID type*/
        err_code = sd_ble_uuid_vs_add(&base_uuid, &m_LiveDriveService.uuid_type);
        VERIFY_SUCCESS(err_code);
    
        /* Add service to SoftDevice & GATTs database and update the service
         * structure with the new service handle */
        ble_uuid.type = m_LiveDriveService.uuid_type;
        ble_uuid.uuid = LDS_UUID_SERVICE;
        err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, 
                                            &m_LiveDriveService.service_handle);
        VERIFY_SUCCESS(err_code);
    
        /* Add characteristics to service */   
        err_code = lds_CharAdd(&m_LiveDriveService, p_lds_init);
        VERIFY_SUCCESS(err_code);
    
        return NRF_SUCCESS;
    }
    
    ///############################################################################
    /// @fn              LDS_GetServiceData
    /// @return          A copy of the LMS service data structure ble_lds_t
    /// @see Description Used to obtain a copy of the service structure data
    ///#############################################################################
    ble_lds_t LDS_GetServiceData(void) 
    { 
      return m_LiveDriveService; 
    }
    
    ///############################################################################
    /// @fn              LMS_OnBLEEventHandler
    /// @remark          param[in] p_ble_evt  Event received from the BLE stack.
    ///                  param[in] p_lms  Live Monitor Service structure.
    /// @return          None
    /// @see Description This function handles all events from the BLE stack that
    ///                  are of interest to the Live Monitor Service.
    /// @warning         None
    ///#############################################################################
    void LDS_OnBLEEventHandler(const ble_evt_t * p_ble_evt, void * p_context)
    {
    
        VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
        VERIFY_PARAM_NOT_NULL_VOID(p_context);
    
        ble_lds_t * p_lds = (ble_lds_t *)p_context;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GATTS_EVT_WRITE:
            {
                lds_OnWrite(p_lds, p_ble_evt);
            }
            break;
    
            case BLE_GAP_EVT_CONNECTED:
            {
                m_LiveDriveService.conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            }
            break;
    
            case BLE_GAP_EVT_DISCONNECTED:
            {
                m_LiveDriveService.conn_handle = BLE_CONN_HANDLE_INVALID;
            }
            break;
            default:
                // No implementation needed.
                break;
        }
    }
    
    ///############################################################################
    /// @fn              LDS_RefreshBLEData
    /// @remark          None
    /// @return          Error codce
    /// @see Description Pushes characteristic data onto the BLE RF 
    /// @warning         Active connection required
    ///#############################################################################
    uint32_t LDS_RefreshBLEData(void)
    {
      uint32_t err_code = NRF_SUCCESS;
      /* prepare data for LED characteristic update */
      /* clean the scratch buffer */
      memset(m_scratch_buffer, 0, sizeof(m_scratch_buffer));
    
      mempcpy(m_scratch_buffer, &m_lds_data.command, sizeof(m_lds_data.command));
      m_scratch_buffer[sizeof(m_lds_data.command)] = m_lds_data.result;
      mempcpy(&m_scratch_buffer[sizeof(m_lds_data.command)+1], m_lds_data.data, sizeof(m_lds_data.data));
    
      /* set-up parameter data for BLE characteristic update notification */
      uint16_t len = sizeof(m_lds_data);
      ble_gatts_hvx_params_t params;
    
      memset(&params, 0, sizeof(params));
      params.type = BLE_GATT_HVX_NOTIFICATION;
      params.p_len = &len;
    
      /* update and notify for the characteristic */
      params.handle = m_LiveDriveService.lds_char_handles.value_handle;
      params.p_data = m_scratch_buffer;
    
      err_code = sd_ble_gatts_hvx(m_LiveDriveService.conn_handle, &params);
      if (NRF_SUCCESS != err_code)
      { /* notifications may not be enabled, attempt to update data */
          ble_gatts_value_t valueData;
          valueData.len = len;
          valueData.offset = 0;
          valueData.p_value = m_scratch_buffer;
          err_code =
              sd_ble_gatts_value_set(m_LiveDriveService.conn_handle,
                                     m_LiveDriveService.lds_char_handles.value_handle, &valueData);
          if (NRF_SUCCESS != err_code)
          {
              return err_code;
          }
      }  
      return err_code;
    }

    This gives me a service that allows me to push some data from a handset to the nRF52840 .

    Actual decoding of data is performed elsewhere.

    LDS_ServiceInit registers the service and characteristic UUIDs with the SoftDevice.

    The Bluetooth stack must be initialised before calling LDS_ServiceInit. This is done like so:

      /* Initialise Live Drive Service */
      ble_lds_init_t lds_init={0};
      lds_init.lds_write_handler = ble_LDS_CharWriteHandler; 
      err_code = LDS_ServiceInit(&lds_init);
      APP_ERROR_CHECK(err_code); 
     

    I would recommend taking an example BLE project then adding the above.

Reply
  • Here's some of what I'm currently using.

    From the header file:

    /* UUID E24AAE40-3F6A-4F5D-828F-1F1248F75962 reversed*/
    #define LDS_UUID_BASE     {0x62, 0x59, 0xF7, 0x48, 0x12, 0x1F, 0x8F, 0x82,  \
                               0x5D, 0x4F, 0x6A, 0x3F, 0x00, 0x00, 0x4A, 0xE2}
    #define LDS_UUID_SERVICE  0xAE40
    #define LDS_UUID_CHAR     0xAE41
    
    #define LDS_DATA_SIZE (10)
    
    //*****************************************************************************
    // Data Types and Enums
    //*****************************************************************************
    /* Forward declaration of the ble_lds_t type */
    typedef struct ble_lds_struct ble_lds_t;
    
    
    typedef void (*ble_lds_write_handler_t) (ble_lds_t * p_lbs, lds_drive_data_t data);
    
    /* options and data needed to initialize the service*/
    typedef struct
    {
      ble_lds_write_handler_t lds_write_handler; /* Called when Characteristic is written to by the client e.g. phone app. */
    } ble_lds_init_t;
      
    /* This structure contains various status information for the service. */
    typedef struct ble_lds_struct
    {
      uint16_t                    service_handle;    /* Handle of Live Monitor Service (as provided by the BLE stack). */
      ble_gatts_char_handles_t    lds_char_handles; /* Handles related to the Characteristic. */
      uint8_t                     uuid_type;         /* UUID type for the Service. In this case 128 bit. */
      ble_lds_write_handler_t     lds_write_handler; /* Event handler to be called when the Characteristic is written to by the client e.g. phone app. */
      uint16_t                    conn_handle;       /* Handle for the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection) */
    }ble_lds_struct_t;
    
    
    

    From the source code file:

    static uint32_t lds_CharAdd(ble_lds_t *p_lds, const ble_lds_init_t *p_lds_init);
    static ble_lds_t m_LiveDriveService;
    NRF_SDH_BLE_OBSERVER(m_LiveDriveService_obs, BLE_LDS_BLE_OBSERVER_PRIO, 
                          LDS_OnBLEEventHandler, &m_LiveDriveService);
                          
    static uint8_t m_scratch_buffer[BLE_GATT_ATT_MTU_DEFAULT-3];
    static lds_drive_data_t m_lds_data;
    
    ///############################################################################
    /// @fn              lds_CharAdd
    /// @remark          param[out] p_lds     Live Drive Service structure.
    ///                  param[in] p_lds_init Initial Live Drive Service data
    /// @return          NRF_SUCCESS on success, otherwise an error code from the
    ///                  SoftDevice
    /// @see Description Function for adding the Characteristic to the service
    /// @warning         SoftDevice MUST be loaded and initialised first
    ///#############################################################################
    static uint32_t lds_CharAdd(ble_lds_t * p_lds, const ble_lds_init_t *p_lds_init)
    {
        if (NULL == p_lds)
        {
            return NRF_ERROR_INTERNAL;
        }
        ble_add_char_params_t add_char;
        memset(&m_lds_data,0, sizeof(m_lds_data));
          
        memset(&add_char, 0, sizeof(add_char));
        add_char.uuid             = LDS_UUID_CHAR;
        add_char.uuid_type        = p_lds->uuid_type;
        add_char.init_len         = sizeof(ble_lds_struct_t);//sizeof(uint8_t);
        add_char.max_len          = sizeof(ble_lds_struct_t);
        add_char.char_props.read  = 1;
        add_char.char_props.write = 1;
        add_char.p_init_value     = (uint8_t*)&m_lds_data.command;
        add_char.is_var_len       = 0;
    
        add_char.read_access  = SEC_OPEN;
        add_char.write_access = SEC_OPEN;
    
        return characteristic_add(p_lds->service_handle, &add_char, &p_lds->lds_char_handles);
    
    }
    
    ///############################################################################
    /// @fn              lds_OnWrite
    /// @remark          param[in] p_lds      Live Drive Service structure.
    ///                  param[in] p_ble_evt  Event received from the BLE stack.
    /// @return          None
    /// @see Description Handles the characteristic write event
    /// @warning         None
    ///#############################################################################
    static void lds_OnWrite(ble_lds_t * p_lds, ble_evt_t const * p_ble_evt)
    {
        VERIFY_PARAM_NOT_NULL_VOID(p_lds);
        VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
    
        /* get the write event data */
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        lds_drive_data_t newData={0};
        /* confirm that characteristic was written to, data length is correct and
        that an event handler has been set */
        if ((p_evt_write->handle == p_lds->lds_char_handles.value_handle) && (sizeof(newData.command) <= p_evt_write->len) &&
            (p_lds->lds_write_handler != NULL))
        {
            memcpy(&newData.command, p_evt_write->data, sizeof(newData.command));
    
            /* pass write event data to the event handler */
            p_lds->lds_write_handler(p_lds, newData);
        }
    }
    
    ///############################################################################
    /// @fn              LDS_ServiceInit
    ///                  param[in] p_lds_init - Information needed to initialize the
    ///                                         service.
    /// @return          NRF_SUCCESS If the service was initialized, otherwise an
    ///                  error code is returned.
    /// @see Description Initialises the Live Drive Service
    /// @warning         SoftDevice MUST be loaded and initialised first
    ///#############################################################################
    uint32_t LDS_ServiceInit(const ble_lds_init_t * p_lds_init)
    {
      
      /* initialise module data */
      memset(m_scratch_buffer,0,sizeof(m_scratch_buffer));
      memset(&m_lds_data, 0, sizeof(m_lds_data));
      
        uint32_t err_code;
        ble_uuid_t ble_uuid;
        ble_uuid128_t base_uuid = {LDS_UUID_BASE};
    
        VERIFY_PARAM_NOT_NULL(p_lds_init);
        VERIFY_PARAM_NOT_NULL(p_lds_init->lds_write_handler);
    
        m_LiveDriveService.conn_handle = BLE_CONN_HANDLE_INVALID;
    
        /* Initialize service structure */
        m_LiveDriveService.lds_write_handler = p_lds_init->lds_write_handler; 
    
        /* Add vendor specific base UUID to BLE stack and update the service
         * structure UUID type*/
        err_code = sd_ble_uuid_vs_add(&base_uuid, &m_LiveDriveService.uuid_type);
        VERIFY_SUCCESS(err_code);
    
        /* Add service to SoftDevice & GATTs database and update the service
         * structure with the new service handle */
        ble_uuid.type = m_LiveDriveService.uuid_type;
        ble_uuid.uuid = LDS_UUID_SERVICE;
        err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, 
                                            &m_LiveDriveService.service_handle);
        VERIFY_SUCCESS(err_code);
    
        /* Add characteristics to service */   
        err_code = lds_CharAdd(&m_LiveDriveService, p_lds_init);
        VERIFY_SUCCESS(err_code);
    
        return NRF_SUCCESS;
    }
    
    ///############################################################################
    /// @fn              LDS_GetServiceData
    /// @return          A copy of the LMS service data structure ble_lds_t
    /// @see Description Used to obtain a copy of the service structure data
    ///#############################################################################
    ble_lds_t LDS_GetServiceData(void) 
    { 
      return m_LiveDriveService; 
    }
    
    ///############################################################################
    /// @fn              LMS_OnBLEEventHandler
    /// @remark          param[in] p_ble_evt  Event received from the BLE stack.
    ///                  param[in] p_lms  Live Monitor Service structure.
    /// @return          None
    /// @see Description This function handles all events from the BLE stack that
    ///                  are of interest to the Live Monitor Service.
    /// @warning         None
    ///#############################################################################
    void LDS_OnBLEEventHandler(const ble_evt_t * p_ble_evt, void * p_context)
    {
    
        VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
        VERIFY_PARAM_NOT_NULL_VOID(p_context);
    
        ble_lds_t * p_lds = (ble_lds_t *)p_context;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GATTS_EVT_WRITE:
            {
                lds_OnWrite(p_lds, p_ble_evt);
            }
            break;
    
            case BLE_GAP_EVT_CONNECTED:
            {
                m_LiveDriveService.conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            }
            break;
    
            case BLE_GAP_EVT_DISCONNECTED:
            {
                m_LiveDriveService.conn_handle = BLE_CONN_HANDLE_INVALID;
            }
            break;
            default:
                // No implementation needed.
                break;
        }
    }
    
    ///############################################################################
    /// @fn              LDS_RefreshBLEData
    /// @remark          None
    /// @return          Error codce
    /// @see Description Pushes characteristic data onto the BLE RF 
    /// @warning         Active connection required
    ///#############################################################################
    uint32_t LDS_RefreshBLEData(void)
    {
      uint32_t err_code = NRF_SUCCESS;
      /* prepare data for LED characteristic update */
      /* clean the scratch buffer */
      memset(m_scratch_buffer, 0, sizeof(m_scratch_buffer));
    
      mempcpy(m_scratch_buffer, &m_lds_data.command, sizeof(m_lds_data.command));
      m_scratch_buffer[sizeof(m_lds_data.command)] = m_lds_data.result;
      mempcpy(&m_scratch_buffer[sizeof(m_lds_data.command)+1], m_lds_data.data, sizeof(m_lds_data.data));
    
      /* set-up parameter data for BLE characteristic update notification */
      uint16_t len = sizeof(m_lds_data);
      ble_gatts_hvx_params_t params;
    
      memset(&params, 0, sizeof(params));
      params.type = BLE_GATT_HVX_NOTIFICATION;
      params.p_len = &len;
    
      /* update and notify for the characteristic */
      params.handle = m_LiveDriveService.lds_char_handles.value_handle;
      params.p_data = m_scratch_buffer;
    
      err_code = sd_ble_gatts_hvx(m_LiveDriveService.conn_handle, &params);
      if (NRF_SUCCESS != err_code)
      { /* notifications may not be enabled, attempt to update data */
          ble_gatts_value_t valueData;
          valueData.len = len;
          valueData.offset = 0;
          valueData.p_value = m_scratch_buffer;
          err_code =
              sd_ble_gatts_value_set(m_LiveDriveService.conn_handle,
                                     m_LiveDriveService.lds_char_handles.value_handle, &valueData);
          if (NRF_SUCCESS != err_code)
          {
              return err_code;
          }
      }  
      return err_code;
    }

    This gives me a service that allows me to push some data from a handset to the nRF52840 .

    Actual decoding of data is performed elsewhere.

    LDS_ServiceInit registers the service and characteristic UUIDs with the SoftDevice.

    The Bluetooth stack must be initialised before calling LDS_ServiceInit. This is done like so:

      /* Initialise Live Drive Service */
      ble_lds_init_t lds_init={0};
      lds_init.lds_write_handler = ble_LDS_CharWriteHandler; 
      err_code = LDS_ServiceInit(&lds_init);
      APP_ERROR_CHECK(err_code); 
     

    I would recommend taking an example BLE project then adding the above.

Children
No Data
Related