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

BLE service with multiple characteristics(client/switch and bulb/server)

I am new to C programming and BLE in general.I am trying to create a service with two characteristics,(the application simply is a modification of BLE bulb and switch). How the original application work is, on the client/switch if you push button 1, it turns on the LED 3 on the server/bulb, and when you push button 2 it turns it off.So it is only one service and one characteristic. The client acts as central and server acts as a peripheral.

What I want is to add another characteristic so that If I push button 3 on the client to turn on  LED 4 on the server .(So in short button 1 will turn LED 3 on and button 3 will turn LED4 on).So I added additional characteristic on both server and client, but the problem is I don't know how to check/distinguish whether the incoming request  on the server is which characteristic.

Question

1. How do I inspect the incoming request whether is coming from characteristic 1 or 2 in the server's main.c  led_write_handler function?

Here is the code

static void led_write_handler(uint16_t conn_handle, ble_led_service_t * p_led_service, uint8_t led_state)
{
   //HERE I WANT TO DO IF THE REQUEST IS CHARACTERSIC 1 TURN LED3 AND IF 2 TURN LED 4
     //NRF_LOG_INFO("led handled %d", p_led_service->led_3_char_handles);
    if (led_state)
    {
        bsp_board_led_on(LIGHTBULB_LED3);
        NRF_LOG_INFO("Received LED ON!");
    }
    else
    {
        bsp_board_led_off(LIGHTBULB_LED3);
        NRF_LOG_INFO("Received LED OFF!");
    }
}

  • Yes, I have two devkits ,one as a server and another as a client.

    I think I am updating the right characteristics, because in Led_service_server.c  on_write()  function     p_evt_write->handle reflects  16 for led_2_char_handles and 19 for led_3_char_handles.The issue only happens in main.c led_write_handler() function ( updating led_2_char_handles and led_3_char_handles both prints 16). What do you suspect is happening here?

  • Not knowing how much code re-use you have between your two units, could you have made a cut and paste error with the registration of the two characteristics on the one side or perhaps reregistered the same UUID to the two characteristics?

    Edit:

    I typed my reply in using a mobile. Now I've had a look at some of your code snippets I see that you don't appear to be registering anything for my_button_3_handles is this still the case.

    From your first snippet:

    characteristic_add(p_led_service->service_handle, &add_char, &p_led_service->my_char_button1_handle);
    
    
    // ... and later
    
    characteristic_add(p_led_service->service_handle, &add_char, &p_led_service->my_char_button2_handle);

  • Thanks cbd The code I shared above is for the server. I did register the Led_2 and Led_3. Here is the code registering led_handles in  led_service_server.c .

    static uint32_t led_2_char_add(ble_led_service_t * p_led_service)
    {
        ble_gatts_char_md_t char_md;
        ble_gatts_attr_t    attr_char_value;
        ble_gatts_attr_md_t attr_md;
        ble_uuid_t          ble_uuid;
    
        memset(&char_md, 0, sizeof(char_md));
        memset(&attr_md, 0, sizeof(attr_md));
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        char_md.char_props.read          = 1;
        char_md.char_props.write         = 1;
        char_md.p_char_user_desc         = LED2CharName;
        char_md.char_user_desc_size      = sizeof(LED2CharName);
        char_md.char_user_desc_max_size  = sizeof(LED2CharName);
        char_md.p_char_pf                = NULL;
        char_md.p_user_desc_md           = NULL;
        char_md.p_cccd_md                = NULL;
        char_md.p_sccd_md                = NULL;
    
        // Define the LED 2 Characteristic UUID
        ble_uuid.type = p_led_service->uuid_type;
        ble_uuid.uuid = BLE_UUID_LED_2_CHAR_UUID;
    
        // Set permissions on the Characteristic value
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    
        // Attribute Metadata settings
        attr_md.vloc       = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth    = 0;
        attr_md.wr_auth    = 0;
        attr_md.vlen       = 0;
    
        // Attribute Value settings
        attr_char_value.p_uuid       = &ble_uuid;
        attr_char_value.p_attr_md    = &attr_md;
        attr_char_value.init_len     = sizeof(uint8_t);
        attr_char_value.init_offs    = 0;
        attr_char_value.max_len      = sizeof(uint8_t);
        attr_char_value.p_value      = NULL;
    
        return sd_ble_gatts_characteristic_add(p_led_service->service_handle, &char_md,
                                               &attr_char_value,
                                               &p_led_service->led_2_char_handles);
    }
    
    static uint32_t led_3_char_add(ble_led_service_t * p_led_service)
    {
        ble_gatts_char_md_t char_md3;
        ble_gatts_attr_t    attr_char_value3;
        ble_gatts_attr_md_t attr_md3;
        ble_uuid_t          ble_uuid3;
    
        memset(&char_md3, 0, sizeof(char_md3));
        memset(&attr_md3, 0, sizeof(attr_md3));
        memset(&attr_char_value3, 0, sizeof(attr_char_value3));
    
        char_md3.char_props.read          = 1;
        char_md3.char_props.write         = 1;
        char_md3.p_char_user_desc         = LED3CharName;
        char_md3.char_user_desc_size      = sizeof(LED2CharName);
        char_md3.char_user_desc_max_size  = sizeof(LED2CharName);
        char_md3.p_char_pf                = NULL;
        char_md3.p_user_desc_md           = NULL;
        char_md3.p_cccd_md                = NULL;
        char_md3.p_sccd_md                = NULL;
    
        // Define the LED 2 Characteristic UUID
        ble_uuid3.type = p_led_service->uuid_type;
        ble_uuid3.uuid = BLE_UUID_LED_3_CHAR_UUID;
    
        // Set permissions on the Characteristic value
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md3.write_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md3.read_perm);
    
        // Attribute Metadata settings
        attr_md3.vloc       = BLE_GATTS_VLOC_STACK;
        attr_md3.rd_auth    = 0;
        attr_md3.wr_auth    = 0;
        attr_md3.vlen       = 0;
    
        // Attribute Value settings
        attr_char_value3.p_uuid       = &ble_uuid3;
        attr_char_value3.p_attr_md    = &attr_md3;
        attr_char_value3.init_len     = sizeof(uint8_t);
        attr_char_value3.init_offs    = 0;
        attr_char_value3.max_len      = sizeof(uint8_t);
        attr_char_value3.p_value      = NULL;
    
        return sd_ble_gatts_characteristic_add(p_led_service->service_handle, &char_md3,
                                               &attr_char_value3,
                                               &p_led_service->led_3_char_handles);
    }

    If you want to see the Client Code(Switch) here is full led_service_client.c

    static void tx_buffer_process(void)
    {
        if (m_tx_index != m_tx_insert_index)
        {
            uint32_t err_code;
    
            if (m_tx_buffer[m_tx_index].type == READ_REQ)
            {
                err_code = sd_ble_gattc_read(m_tx_buffer[m_tx_index].conn_handle,
                                             m_tx_buffer[m_tx_index].req.read_handle,
                                             0);
            }
            else
            {
                err_code = sd_ble_gattc_write(m_tx_buffer[m_tx_index].conn_handle,
                                              &m_tx_buffer[m_tx_index].req.write_req.gattc_params);
            }
            if (err_code == NRF_SUCCESS)
            {
                NRF_LOG_DEBUG("SD Read/Write API returns Success..");
                m_tx_index++;
                m_tx_index &= TX_BUFFER_MASK;
            }
            else
            {
                NRF_LOG_DEBUG("SD Read/Write API returns error. This message sending will be "
                    "attempted again..");
            }
        }
    }
    
    
    /**@brief Function for handling write response events.
     *
     * @param[in] p_ble_led_service_client Pointer to the Led Service Client structure.
     * @param[in] p_ble_evt                Pointer to the BLE event received.
     */
    static void on_write_rsp(ble_led_service_client_t * p_ble_led_service_client, ble_evt_t const * p_ble_evt)
    {
        // Check if the event if on the link for this instance
        if (p_ble_led_service_client->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
        {
            return;
        }
        // Check if there is any message to be sent across to the peer and send it.
        tx_buffer_process();
    }
    
    
    /**@brief Function for handling Disconnected event received from the SoftDevice.
     *
     * @details This function check if the disconnect event is happening on the link
     *          associated with the current instance of the module, if so it will set its
     *          conn_handle to invalid.
     *
     * @param[in] p_ble_led_service_client Pointer to the Led Service Client structure.
     * @param[in] p_ble_evt                Pointer to the BLE event received.
     */
    static void on_disconnected(ble_led_service_client_t * p_ble_led_service_client, ble_evt_t const * p_ble_evt)
    {
        if (p_ble_led_service_client->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
        {
            p_ble_led_service_client->conn_handle                    = BLE_CONN_HANDLE_INVALID;
            p_ble_led_service_client->peer_led_service_db.led2_handle         = BLE_GATT_HANDLE_INVALID;
            p_ble_led_service_client->peer_led_service_db.led3_handle         = BLE_GATT_HANDLE_INVALID;
        }
    }
    
    
    void ble_led_service_on_db_disc_evt(ble_led_service_client_t * p_ble_led_service_client, ble_db_discovery_evt_t const * p_evt)
    {
        // Check if the Led Button Service was discovered.
        if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
            p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_LED_SERVICE_UUID &&
            p_evt->params.discovered_db.srv_uuid.type == p_ble_led_service_client->uuid_type)
        {
            ble_led_service_client_evt_t evt;
    
            evt.evt_type    = BLE_LED_SERVICE_CLIENT_EVT_DISCOVERY_COMPLETE;
            evt.conn_handle = p_evt->conn_handle;
    
            for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
                switch (p_char->characteristic.uuid.uuid)
                {
                    case BLE_UUID_LED_2_CHAR_UUID:
                        NRF_LOG_INFO("in case UIID2 ",p_char->characteristic.uuid.uuid);
                        evt.peer_db.led2_handle = p_char->characteristic.handle_value;
                        break;
                   case BLE_UUID_LED_3_CHAR_UUID:
                    NRF_LOG_INFO("in case UIID3 ",p_char->characteristic.uuid.uuid);
                        evt.peer_db.led3_handle = p_char->characteristic.handle_value;
                        break;
    
                    default:
                        break;
                }
            }
    
            NRF_LOG_DEBUG("Led Service discovered at peer.");
            //If the instance has been assigned prior to db_discovery, assign the db_handles
            if (p_ble_led_service_client->conn_handle != BLE_CONN_HANDLE_INVALID)
            {
                if (p_ble_led_service_client->peer_led_service_db.led2_handle         == BLE_GATT_HANDLE_INVALID)
                {
                    p_ble_led_service_client->peer_led_service_db = evt.peer_db;
                }
                 if (p_ble_led_service_client->peer_led_service_db.led3_handle         == BLE_GATT_HANDLE_INVALID)
                {
                    p_ble_led_service_client->peer_led_service_db = evt.peer_db;
                }
            }
    
            p_ble_led_service_client->evt_handler(p_ble_led_service_client, &evt);
    
        }
    }
    
    
    uint32_t ble_led_service_client_init(ble_led_service_client_t * p_ble_led_service_client, ble_led_service_client_init_t * p_ble_led_service_client_init)
    {
        uint32_t      err_code;
        ble_uuid_t    led_service_uuid;
        ble_uuid128_t led_service_base_uuid = {BLE_UUID_LED_SERVICE_BASE_UUID};
    
        VERIFY_PARAM_NOT_NULL(p_ble_led_service_client);
        VERIFY_PARAM_NOT_NULL(p_ble_led_service_client_init);
        VERIFY_PARAM_NOT_NULL(p_ble_led_service_client_init->evt_handler);
    
        p_ble_led_service_client->peer_led_service_db.led2_handle   = BLE_GATT_HANDLE_INVALID;
        p_ble_led_service_client->peer_led_service_db.led3_handle   = BLE_GATT_HANDLE_INVALID;
        p_ble_led_service_client->conn_handle                      = BLE_CONN_HANDLE_INVALID;
        p_ble_led_service_client->evt_handler                      = p_ble_led_service_client_init->evt_handler;
    
        err_code = sd_ble_uuid_vs_add(&led_service_base_uuid, &p_ble_led_service_client->uuid_type);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
        VERIFY_SUCCESS(err_code);
    
        led_service_uuid.type = p_ble_led_service_client->uuid_type;
        led_service_uuid.uuid = BLE_UUID_LED_SERVICE_UUID;
    
        return ble_db_discovery_evt_register(&led_service_uuid);
    }
    
    void ble_led_service_client_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
    {
        if ((p_context == NULL) || (p_ble_evt == NULL))
        {
            return;
        }
    
        ble_led_service_client_t * p_ble_led_service_client = (ble_led_service_client_t *)p_context;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GATTC_EVT_WRITE_RSP:
                on_write_rsp(p_ble_led_service_client, p_ble_evt);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                on_disconnected(p_ble_led_service_client, p_ble_evt);
                break;
    
            default:
                break;
        }
    }
    
    
    uint32_t ble_led_service_led2_setting_send(ble_led_service_client_t * p_ble_led_service_client, uint8_t status)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_led_service_client);
    
        if (p_ble_led_service_client->conn_handle == BLE_CONN_HANDLE_INVALID)
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        NRF_LOG_DEBUG("writing LED2 status 0x%x", status);
      
    
        tx_message_t * p_msg;
    
        p_msg              = &m_tx_buffer[m_tx_insert_index++];
        m_tx_insert_index &= TX_BUFFER_MASK;
    
        p_msg->req.write_req.gattc_params.handle   = p_ble_led_service_client->peer_led_service_db.led2_handle;
        p_msg->req.write_req.gattc_params.len      = sizeof(status);
        p_msg->req.write_req.gattc_params.p_value  = p_msg->req.write_req.gattc_value;
        p_msg->req.write_req.gattc_params.offset   = 0;
        p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_CMD;
        p_msg->req.write_req.gattc_value[0]        = status;
        p_msg->conn_handle                         = p_ble_led_service_client->conn_handle;
        p_msg->type                                = WRITE_REQ;
    
        tx_buffer_process();
        return NRF_SUCCESS;
    }
    
    uint32_t ble_led_service_led3_setting_send(ble_led_service_client_t * p_ble_led_service_client, uint8_t status)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_led_service_client);
    
        if (p_ble_led_service_client->conn_handle == BLE_CONN_HANDLE_INVALID)
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
        NRF_LOG_DEBUG("writing LED3 status 0x%x", status);
        NRF_LOG_DEBUG("CON HANDLE %d", p_ble_led_service_client->conn_handle);
    
        tx_message_t * p_msg;
    
        p_msg              = &m_tx_buffer[m_tx_insert_index++];
        m_tx_insert_index &= TX_BUFFER_MASK;
    
        p_msg->req.write_req.gattc_params.handle   = p_ble_led_service_client->peer_led_service_db.led3_handle;
        p_msg->req.write_req.gattc_params.len      = sizeof(status);
        p_msg->req.write_req.gattc_params.p_value  = p_msg->req.write_req.gattc_value;
        p_msg->req.write_req.gattc_params.offset   = 0;
        p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_CMD;
        p_msg->req.write_req.gattc_value[0]        = status;
        p_msg->conn_handle                         = p_ble_led_service_client->conn_handle;
        p_msg->type                                = WRITE_REQ;
    
        tx_buffer_process();
        return NRF_SUCCESS;
    }
    
    uint32_t ble_led_service_client_handles_assign(ble_led_service_client_t    * p_ble_led_service_client,
                                                   uint16_t                      conn_handle,
                                                   const led_service_db_t      * p_peer_handles)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_led_service_client);
    
        p_ble_led_service_client->conn_handle = conn_handle;
        if (p_peer_handles != NULL)
        {
            p_ble_led_service_client->peer_led_service_db = *p_peer_handles;
        }
        return NRF_SUCCESS;
    }
    

  • Have you made this work? If not, please tell me, and I will look into it.

  • Hi Simon ,

    No not yet.The issue is only on led_write_handler() of main.c server side, on the led_service_server.c led_write_handler() everything shows up correctly.Below are the full code of main.c and led_service_server.c 

    main.c

    
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    
    #include "nordic_common.h"
    #include "nrf.h"
    #include "app_error.h"
    #include "ble.h"
    #include "ble_hci.h"
    #include "ble_srv_common.h"
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "nrf_sdh.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_sdh_ble.h"
    #include "app_timer.h"
    #include "fds.h"
    #include "peer_manager.h"
    #include "bsp_btn_ble.h"
    #include "sensorsim.h"
    #include "ble_conn_state.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_ble_qwr.h"
    #include "nrf_pwr_mgmt.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #include "ble_bas.h"
    #include "services/led_service.h"
    
    #include "Battery Level/battery_voltage.h"
    
    
    #define DEVICE_NAME                     "BLE_Lightbulb"                       /**< Name of device. Will be included in the advertising data. */
    #define MANUFACTURER_NAME               "NordicSemiconductor"                   /**< Manufacturer. Will be passed to Device Information Service. */
    #define APP_ADV_INTERVAL                300                                     /**< The advertising interval (in units of 0.625 ms. This value corresponds to 187.5 ms). */
    
    // Corresponds to LED2 on the development kit
    #define LIGHTBULB_LED                   BSP_BOARD_LED_1                         /**< LED to be toggled with the help of the LED Button Service. */
    #define LIGHTBULB_LED3                   BSP_BOARD_LED_2 
    
    #define APP_ADV_DURATION                18000                                   /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
    #define APP_BLE_OBSERVER_PRIO           3                                       /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    #define APP_BLE_CONN_CFG_TAG            1                                       /**< A tag identifying the SoftDevice BLE configuration. */
    
    #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(100, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.1 seconds). */
    #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(200, UNIT_1_25_MS)        /**< Maximum acceptable connection interval (0.2 second). */
    #define SLAVE_LATENCY                   0                                       /**< Slave latency. */
    #define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         /**< Connection supervisory timeout (4 seconds). */
    
    #define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                   /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                  /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT    3                                       /**< Number of attempts before giving up the connection parameter negotiation. */
    
    #define SEC_PARAM_BOND                  1                                       /**< Perform bonding. */
    #define SEC_PARAM_MITM                  0                                       /**< Man In The Middle protection not required. */
    #define SEC_PARAM_LESC                  0                                       /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS              0                                       /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
    #define SEC_PARAM_OOB                   0                                       /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE          7                                       /**< Minimum encryption key size. */
    #define SEC_PARAM_MAX_KEY_SIZE          16                                      /**< Maximum encryption key size. */
    
    #define DEAD_BEEF                       0xDEADBEEF                              /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */
    
    /**< Battery timer. */
    APP_TIMER_DEF(m_battery_timer_id);
    
    #define BATTERY_LEVEL_MEAS_INTERVAL     APP_TIMER_TICKS(30000)                 /**< Battery level measurement interval (ticks). */
    
    NRF_BLE_GATT_DEF(m_gatt);                                                       /**< GATT module instance. */
    NRF_BLE_QWR_DEF(m_qwr);                                                         /**< Context for the Queued Write module.*/
    BLE_ADVERTISING_DEF(m_advertising);                                             /**< Advertising module instance. */
    
    static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;                        /**< Handle of the current connection. */
    
    static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;                   /**< Advertising handle used to identify an advertising set. */
    static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX];                    /**< Buffer for storing an encoded advertising set. */
    static uint8_t m_enc_scan_response_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX];         /**< Buffer for storing an encoded scan data. */
    
    /**@brief Struct that contains pointers to the encoded advertising data. */
    static ble_gap_adv_data_t m_adv_data =
    {
        .adv_data =
        {
            .p_data = m_enc_advdata,
            .len    = BLE_GAP_ADV_SET_DATA_SIZE_MAX
        },
        .scan_rsp_data =
        {
            .p_data = m_enc_scan_response_data,
            .len    = BLE_GAP_ADV_SET_DATA_SIZE_MAX
    
        }
    };
    
    /**< Structure used to identify the battery service. */
    BLE_BAS_DEF(m_bas);
    
    /**< Structure used to identify the LED service. */
    BLE_LED_SERVICE_DEF(m_led_service);
    static void led_write_handler(uint16_t conn_handle, ble_led_service_t * p_led_service, uint8_t led_state);
    
    static void battery_level_meas_timeout_handler(void * p_context);
    static void battery_level_update(void);
    
    static ble_uuid_t m_adv_uuids[] =                                               /**< Universally unique service identifiers. */
    {
        {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}
    };
    
    static void advertising_start();
    
    
    /**@brief Callback function for asserts in the SoftDevice.
     *
     * @details This function will be called in case of an assert in the SoftDevice.
     *
     * @warning This handler is an example only and does not fit a final product. You need to analyze
     *          how your product is supposed to react in case of Assert.
     * @warning On assert from the SoftDevice, the system can only recover on reset.
     *
     * @param[in] line_num   Line number of the failing ASSERT call.
     * @param[in] file_name  File name of the failing ASSERT call.
     */
    void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
    {
        app_error_handler(DEAD_BEEF, line_num, p_file_name);
    }
    
    /**@brief Function for the Timer initialization.
     *
     * @details Initializes the timer module. This creates and starts application timers.
     */
    static void timers_init(void)
    {
        // Initialize timer module.
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
        
        // Create timers.
        err_code = app_timer_create(&m_battery_timer_id,
                                    APP_TIMER_MODE_REPEATED,
                                    battery_level_meas_timeout_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for starting application timers.
     */
    static void application_timers_start(void)
    {
        uint32_t err_code;
    
        // Start application timers.
        err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for the GAP initialization.
     *
     * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
     *          device including the device name, appearance, and the preferred connection parameters.
     */
    static void gap_params_init(void)
    {
        ret_code_t              err_code;
        ble_gap_conn_params_t   gap_conn_params;
        ble_gap_conn_sec_mode_t sec_mode;
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    
        err_code = sd_ble_gap_device_name_set(&sec_mode,
                                              (const uint8_t *)DEVICE_NAME,
                                              strlen(DEVICE_NAME));
        APP_ERROR_CHECK(err_code);
    
        err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_UNKNOWN);
        APP_ERROR_CHECK(err_code);
    
        memset(&gap_conn_params, 0, sizeof(gap_conn_params));
    
        gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
        gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
        gap_conn_params.slave_latency     = SLAVE_LATENCY;
        gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;
    
        err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the GATT module.
     */
    static void gatt_init(void)
    {
        ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling Queued Write Module errors.
     *
     * @details A pointer to this function will be passed to each service which may need to inform the
     *          application about an error.
     *
     * @param[in]   nrf_error   Error code containing information about what went wrong.
     */
    static void nrf_qwr_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    /**@brief Function for initializing services that will be used by the application.
     */
    static void services_init(void)
    {
        uint32_t       err_code;
        ble_bas_init_t bas_init;
        ble_led_service_init_t led_init;
    
        nrf_ble_qwr_init_t qwr_init = {0};
    
        // Initialize Queued Write Module.
        qwr_init.error_handler = nrf_qwr_error_handler;
    
        err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
        APP_ERROR_CHECK(err_code);
    
        // 1. Initialize the LED service
        led_init.led_write_handler = led_write_handler;
    
        err_code = ble_led_service_init(&m_led_service, &led_init);
        APP_ERROR_CHECK(err_code);
    
        // 2. Initialize Battery Service.
        memset(&bas_init, 0, sizeof(bas_init));
    
        // Here the sec level for the Battery Service can be changed/increased.
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.cccd_write_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.read_perm);
        BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&bas_init.battery_level_char_attr_md.write_perm);
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_report_read_perm);
    
        bas_init.evt_handler          = NULL;
        bas_init.support_notification = true;
        bas_init.p_report_ref         = NULL;
        bas_init.initial_batt_level   = 100;
    
        err_code = ble_bas_init(&m_bas, &bas_init);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling the Connection Parameters Module.
     *
     * @details This function will be called for all events in the Connection Parameters Module which
     *          are passed to the application.
     *          @note All this function does is to disconnect. This could have been done by simply
     *                setting the disconnect_on_fail config parameter, but instead we use the event
     *                handler mechanism to demonstrate its use.
     *
     * @param[in] p_evt  Event received from the Connection Parameters Module.
     */
    static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
    {
        ret_code_t err_code;
    
        if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
        {
            err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
            APP_ERROR_CHECK(err_code);
        }
    }
    
    
    /**@brief Function for handling a Connection Parameters error.
     *
     * @param[in] nrf_error  Error code containing information about what went wrong.
     */
    static void conn_params_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    
    /**@brief Function for initializing the Connection Parameters module.
     */
    static void conn_params_init(void)
    {
        ret_code_t             err_code;
        ble_conn_params_init_t cp_init;
    
        memset(&cp_init, 0, sizeof(cp_init));
    
        cp_init.p_conn_params                  = NULL;
        cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
        cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
        cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
        cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
        cp_init.disconnect_on_fail             = false;
        cp_init.evt_handler                    = on_conn_params_evt;
        cp_init.error_handler                  = conn_params_error_handler;
    
        err_code = ble_conn_params_init(&cp_init);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for putting the chip into sleep mode.
     *
     * @note This function will not return.
     */
    static void sleep_mode_enter(void)
    {
        ret_code_t err_code;
    
        err_code = bsp_indication_set(BSP_INDICATE_IDLE);
        APP_ERROR_CHECK(err_code);
    
        // Prepare wakeup buttons.
        err_code = bsp_btn_ble_sleep_mode_prepare();
        APP_ERROR_CHECK(err_code);
    
        // Go to system-off mode (this function will not return; wakeup will cause a reset).
        err_code = sd_power_system_off();
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ret_code_t err_code = NRF_SUCCESS;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected.");
                // LED indication will be changed when advertising starts.
                break;
    
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected.");
                err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
                APP_ERROR_CHECK(err_code);
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
                err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
                // Pairing not supported
                err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
                                                       BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
                                                       NULL,
                                                       NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
    
            case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    
            case BLE_GATTS_EVT_SYS_ATTR_MISSING:
                // No system attributes have been stored.
                err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("GATT Client Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                NRF_LOG_DEBUG("GATT Server Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    /**@brief Function for initializing the BLE stack.
     *
     * @details Initializes the SoftDevice and the BLE event interrupt.
     */
    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Register a handler for BLE events.
        NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
    }
    
    /**@brief Function for handling write events to the LED characteristic.
     *
     * @param[in] p_led_service  Instance of LED Service to which the write applies.
     * @param[in] led_state      Written/desired state of the LED.
     */
    static void led_write_handler(uint16_t conn_handle, ble_led_service_t * p_led_service, uint8_t led_state)
    {
         NRF_LOG_INFO("sevice= %d",p_led_service->uuid_type);
         //NRF_LOG_INFO("led handled %s", p_led_service->led_3_char_handles);
        if (led_state)
        {
            bsp_board_led_on(LIGHTBULB_LED3);
            NRF_LOG_INFO("Received LED ON!");
        }
        else
        {
            bsp_board_led_off(LIGHTBULB_LED3);
            NRF_LOG_INFO("Received LED OFF!");
        }
    }
    
    /**@brief Function for handling advertising events.
     *
     * @details This function will be called for advertising events which are passed to the application.
     *
     * @param[in] ble_adv_evt  Advertising event.
     */
    static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
    {
        ret_code_t err_code;
    
        switch (ble_adv_evt)
        {
            case BLE_ADV_EVT_FAST:
                NRF_LOG_INFO("Fast advertising.");
                err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_ADV_EVT_IDLE:
                sleep_mode_enter();
                break;
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for initializing the Advertising functionality.
     */
    static void advertising_init(void)
    {
        ret_code_t             err_code;
        ble_advertising_init_t init;
    
        memset(&init, 0, sizeof(init));
    
        init.advdata.name_type               = BLE_ADVDATA_FULL_NAME;
        init.advdata.include_appearance      = true;
        init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.advdata.uuids_complete.p_uuids  = m_adv_uuids;
    
        init.config.ble_adv_fast_enabled  = true;
        init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
        init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
    
        init.evt_handler = on_adv_evt;
    
        err_code = ble_advertising_init(&m_advertising, &init);
        APP_ERROR_CHECK(err_code);
    
        ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
    }
    
    /**@brief Function for handling events from the BSP module.
     *
     * @param[in]   event   Event generated when button is pressed.
     */
    static void bsp_event_handler(bsp_event_t event)
    {
        ret_code_t err_code;
    
        switch (event)
        {
            case BSP_EVENT_SLEEP:
                sleep_mode_enter();
                break; // BSP_EVENT_SLEEP
    
            case BSP_EVENT_DISCONNECT:
                err_code = sd_ble_gap_disconnect(m_conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                if (err_code != NRF_ERROR_INVALID_STATE)
                {
                    APP_ERROR_CHECK(err_code);
                }
                break; // BSP_EVENT_DISCONNECT
    
            case BSP_EVENT_WHITELIST_OFF:
                if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
                {
                    err_code = ble_advertising_restart_without_whitelist(&m_advertising);
                    if (err_code != NRF_ERROR_INVALID_STATE)
                    {
                        APP_ERROR_CHECK(err_code);
                    }
                }
                break; // BSP_EVENT_KEY_0
    
            default:
                break;
        }
    }
    
    
    /**@brief Function for initializing leds.
     *
     * @details Initializes all LEDs used by the application.
     */
    static void leds_init()
    {
        ret_code_t err_code;
        
        err_code = bsp_init(BSP_INIT_LEDS, bsp_event_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for initializing the nrf log module.
     */
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    
    /**@brief Function for initializing power management.
     */
    static void power_management_init(void)
    {
        ret_code_t err_code;
        err_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling the idle state (main loop).
     *
     * @details If there is no pending log operation, then sleep until next the next event occurs.
     */
    static void idle_state_handle(void)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }
    
    
    /**@brief Function for starting advertising.
     */
    static void advertising_start()
    {
        ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for handling the Battery measurement timer timeout.
     *
     * @details This function will be called each time the battery level measurement timer expires.
     *
     * @param[in] p_context  Pointer used for passing some arbitrary information (context) from the
     *                       app_start_timer() call to the timeout handler.
     */
    static void battery_level_meas_timeout_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        NRF_LOG_INFO("Battery Level timeout event");
    
        // Only send the battery level update if we are connected
        if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
        {
            battery_level_update();
        }
    }
    /**@brief Function for updating the Battery Level measurement*/
    static void battery_level_update(void)
    {
        ret_code_t err_code;
    
        uint8_t  battery_level;
        uint16_t vbatt;              // Variable to hold voltage reading
        battery_voltage_get(&vbatt); // Get new battery voltage
    
        battery_level = battery_level_in_percent(vbatt);          //Transform the millivolts value into battery level percent.
        printf("ADC result in percent: %d\r\n", battery_level);
    
        err_code = ble_bas_battery_level_update(&m_bas, battery_level, m_conn_handle);
        if ((err_code != NRF_SUCCESS) &&
            (err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != NRF_ERROR_RESOURCES) &&
            (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
           )
        {
            APP_ERROR_HANDLER(err_code);
        }
    }
    
    /**@brief Function for application main entry.
     */
    int main(void)
    {
        bool erase_bonds;
    
        // Initialize.
        log_init();
        timers_init();
        battery_voltage_init();
        leds_init();
        power_management_init();
        ble_stack_init();
        gap_params_init();
        gatt_init();
        services_init();
        advertising_init();
        conn_params_init();
        application_timers_start();
    
        // Start execution.
        NRF_LOG_INFO("BLE Lightbulb example started.");
       
        advertising_start();
    
        // Enter main loop.
        for (;;)
        {
            idle_state_handle();
        }
    }

    led_service_server.c

    #include <string.h>
    
    #include "nrf_log.h"
    #include "led_service.h"
    
    static const uint8_t LED2CharName[] = "LED 2";
    static const uint8_t LED3CharName[] = "LED 3";
    
    /**@brief Function for handling the Connect event.
     *
     * @param[in]   p_led_service  LED service structure.
     * @param[in]   p_ble_evt      Event received from the BLE stack.
     */
    static void on_connect(ble_led_service_t * p_led_service, ble_evt_t const * p_ble_evt)
    {
        p_led_service->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
    }
    
    /**@brief Function for handling the Disconnect event.
     *
     * @param[in]   p_bas       LED service structure.
     * @param[in]   p_ble_evt   Event received from the BLE stack.
     */
    static void on_disconnect(ble_led_service_t * p_led_service, ble_evt_t const * p_ble_evt)
    {
        UNUSED_PARAMETER(p_ble_evt);
        p_led_service->conn_handle = BLE_CONN_HANDLE_INVALID;
    }
    
    /**@brief Function for handling the Write event.
     *
     * @param[in] p_led_service   LED Service structure.
     * @param[in] p_ble_evt       Event received from the BLE stack.
     */
    static void on_write(ble_led_service_t * p_led_service, ble_evt_t const * p_ble_evt)
    {
        ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        NRF_LOG_INFO("handle",p_evt_write->handle);
    
        if (   (p_evt_write->handle == p_led_service->led_2_char_handles.value_handle)
            && (p_evt_write->len == 1)
            && (p_led_service->led_write_handler != NULL))
        {
            p_led_service->led_write_handler(p_ble_evt->evt.gap_evt.conn_handle, p_led_service, p_evt_write->data[0]);
        }
    
         if (   (p_evt_write->handle == p_led_service->led_3_char_handles.value_handle)
            && (p_evt_write->len == 1)
            && (p_led_service->led_write_handler != NULL))
        {
            p_led_service->led_write_handler(p_ble_evt->evt.gap_evt.conn_handle, p_led_service, p_evt_write->data[0]);
        }
    }
    
    /**@brief Function for adding the LED 2 characteristic.
     *
     */
    static uint32_t led_2_char_add(ble_led_service_t * p_led_service)
    {
        ble_gatts_char_md_t char_md;
        ble_gatts_attr_t    attr_char_value;
        ble_gatts_attr_md_t attr_md;
        ble_uuid_t          ble_uuid;
    
        memset(&char_md, 0, sizeof(char_md));
        memset(&attr_md, 0, sizeof(attr_md));
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        char_md.char_props.read          = 1;
        char_md.char_props.write         = 1;
        char_md.p_char_user_desc         = LED2CharName;
        char_md.char_user_desc_size      = sizeof(LED2CharName);
        char_md.char_user_desc_max_size  = sizeof(LED2CharName);
        char_md.p_char_pf                = NULL;
        char_md.p_user_desc_md           = NULL;
        char_md.p_cccd_md                = NULL;
        char_md.p_sccd_md                = NULL;
    
        // Define the LED 2 Characteristic UUID
        ble_uuid.type = p_led_service->uuid_type;
        ble_uuid.uuid = BLE_UUID_LED_2_CHAR_UUID;
    
        // Set permissions on the Characteristic value
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    
        // Attribute Metadata settings
        attr_md.vloc       = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth    = 0;
        attr_md.wr_auth    = 0;
        attr_md.vlen       = 0;
    
        // Attribute Value settings
        attr_char_value.p_uuid       = &ble_uuid;
        attr_char_value.p_attr_md    = &attr_md;
        attr_char_value.init_len     = sizeof(uint8_t);
        attr_char_value.init_offs    = 0;
        attr_char_value.max_len      = sizeof(uint8_t);
        attr_char_value.p_value      = NULL;
    
        return sd_ble_gatts_characteristic_add(p_led_service->service_handle, &char_md,
                                               &attr_char_value,
                                               &p_led_service->led_2_char_handles);
    }
    
    static uint32_t led_3_char_add(ble_led_service_t * p_led_service)
    {
        ble_gatts_char_md_t char_md3;
        ble_gatts_attr_t    attr_char_value3;
        ble_gatts_attr_md_t attr_md3;
        ble_uuid_t          ble_uuid3;
    
        memset(&char_md3, 0, sizeof(char_md3));
        memset(&attr_md3, 0, sizeof(attr_md3));
        memset(&attr_char_value3, 0, sizeof(attr_char_value3));
    
        char_md3.char_props.read          = 1;
        char_md3.char_props.write         = 1;
        char_md3.p_char_user_desc         = LED3CharName;
        char_md3.char_user_desc_size      = sizeof(LED2CharName);
        char_md3.char_user_desc_max_size  = sizeof(LED2CharName);
        char_md3.p_char_pf                = NULL;
        char_md3.p_user_desc_md           = NULL;
        char_md3.p_cccd_md                = NULL;
        char_md3.p_sccd_md                = NULL;
    
        // Define the LED 2 Characteristic UUID
        ble_uuid3.type = p_led_service->uuid_type;
        ble_uuid3.uuid = BLE_UUID_LED_3_CHAR_UUID;
    
        // Set permissions on the Characteristic value
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md3.write_perm);
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md3.read_perm);
    
        // Attribute Metadata settings
        attr_md3.vloc       = BLE_GATTS_VLOC_STACK;
        attr_md3.rd_auth    = 0;
        attr_md3.wr_auth    = 0;
        attr_md3.vlen       = 0;
    
        // Attribute Value settings
        attr_char_value3.p_uuid       = &ble_uuid3;
        attr_char_value3.p_attr_md    = &attr_md3;
        attr_char_value3.init_len     = sizeof(uint8_t);
        attr_char_value3.init_offs    = 0;
        attr_char_value3.max_len      = sizeof(uint8_t);
        attr_char_value3.p_value      = NULL;
    
        return sd_ble_gatts_characteristic_add(p_led_service->service_handle, &char_md3,
                                               &attr_char_value3,
                                               &p_led_service->led_3_char_handles);
    }
    
    
    uint32_t ble_led_service_init(ble_led_service_t * p_led_service, const ble_led_service_init_t * p_led_service_init)
    {
        uint32_t   err_code;
        uint32_t   err_code3;
        ble_uuid_t ble_uuid;
    
        // Initialize service structure
        p_led_service->conn_handle = BLE_CONN_HANDLE_INVALID;
    
        // Initialize service structure.
        p_led_service->led_write_handler = p_led_service_init->led_write_handler;
    
        // Add service UUID
        ble_uuid128_t base_uuid = {BLE_UUID_LED_SERVICE_BASE_UUID};
        err_code = sd_ble_uuid_vs_add(&base_uuid, &p_led_service->uuid_type);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        // Set up the UUID for the service (base + service-specific)
        ble_uuid.type = p_led_service->uuid_type;
        ble_uuid.uuid = BLE_UUID_LED_SERVICE_UUID;
    
        // Set up and add the service
        err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_led_service->service_handle);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
        // Add the different characteristics in the service:
        //   Button press characteristic:   E54B0002-67F5-479E-8711-B3B99198CE6C
        err_code = led_2_char_add(p_led_service);
        err_code3 = led_3_char_add(p_led_service);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    
         if (err_code3 != NRF_SUCCESS)
        {
            return err_code3;
        }
    
    
        return NRF_SUCCESS;
    }
    
    void ble_led_service_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ble_led_service_t * p_led_service = (ble_led_service_t *)p_context;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                 NRF_LOG_INFO("ON CONNECTED1 ",p_led_service->led_2_char_handles.value_handle);
                on_connect(p_led_service, p_ble_evt);
                break;
    
            case BLE_GATTS_EVT_WRITE:
                on_write(p_led_service, p_ble_evt);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                on_disconnect(p_led_service, p_ble_evt);
                break;
    
            default:
                // No implementation needed.
                break;
        }
    }

Related