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

How to notify several characteristics in central

Hi, I am using ble_app_hrs_c as the base for central. I have included 5 characteristics in the peripheral and central side. How to make ble_nus_c_rx_notif_enable() to notify when any of the 5 characteristics is updated? At the moment, ble_nus_c_rx_notif_enable has only this: return cccd_configure(p_ble_nus_c->conn_handle,p_ble_nus_c->handles.nus_rx_cccd_handle, true);

How do I change it so that I get notification even when nus_rx_wheel_status_handle also receives a notification? My ble_nus_c_handles_t looks like this:

typedef struct {
    uint16_t                nus_rx_wheel_control_handle;      

    uint16_t                nus_rx_wheel_control_cccd_handle; /**< Handle of the CCCD of the NUS RX characteristic as provided by a discovery. */
    uint16_t                nus_rx_wheel_status_handle;	
    uint16_t                nus_rx_wheel_status_cccd_handle;	
    uint16_t                nus_rx_wheel_battery_handle;	
    uint16_t                nus_rx_wheel_battery_cccd_handle;		
    uint16_t                nus_rx_wheel_information_handle;	
    uint16_t                nus_rx_wheel_information_cccd_handle;		
    uint16_t                nus_rx_wheel_debug_handle;	
    uint16_t                nus_rx_wheel_debug_cccd_handle;		
    uint16_t                nus_tx_handle_wheel_control;      /**< Handle of the NUS TX characteristic as provided by a discovery. */
    uint16_t                nus_tx_handle_wheel_status;	
    uint16_t                nus_tx_handle_wheel_battery;	
    uint16_t                nus_tx_handle_wheel_information;		
    uint16_t                nus_tx_handle_wheel_debug;		
} ble_nus_c_handles_t;

The code works fine when I enable notification for one character at a time.

  • Hi Petter, Thanks for the reply. In the central code, in the ble_nus_c_rx_notif_enable, I have configured only one of the characteristics as shown below:

    uint32_t ble_nus_c_rx_notif_enable(ble_nus_c_t * p_ble_nus_c)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
    
        if ( (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
           ||(p_ble_nus_c->handles.nus_rx_debug_cccd_handle == BLE_GATT_HANDLE_INVALID)
    			||(p_ble_nus_c->handles.nus_rx_speed_cccd_handle == BLE_GATT_HANDLE_INVALID)
    			||(p_ble_nus_c->handles.nus_rx_information_cccd_handle == BLE_GATT_HANDLE_INVALID)
           )
        {
            return NRF_ERROR_INVALID_STATE;
        }
    
    		return cccd_configure(p_ble_nus_c->conn_handle,p_ble_nus_c->handles.nus_rx_speed_cccd_handle, true);
    
    }
    

    I have configured only for 1 characteristic in ble_nus_c_rx_notif_enable because, if I configure for more than 1 characteristic, I get NRF_ERROR_BUSY error. Also, upon discovery event, I assign cccd handles for all the characteristics as shown below:

    void ble_nus_c_on_db_disc_evt(ble_nus_c_t * p_ble_nus_c, ble_db_discovery_evt_t * p_evt)
    {
        ble_nus_c_evt_t nus_c_evt;
        memset(&nus_c_evt,0,sizeof(ble_nus_c_evt_t));
    
        ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;
    
        // Check if the NUS was discovered.
        if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
            p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_NUS_SERVICE &&
            p_evt->params.discovered_db.srv_uuid.type == p_ble_nus_c->uuid_type)
        {
    
            uint32_t i;
    
            for (i = 0; i < p_evt->params.discovered_db.char_count; i++)
            {
                switch (p_chars[i].characteristic.uuid.uuid)
                {
    
                    case BLE_UUID_NUS_RX_SPEED_CHARACTERISTIC:
                        nus_c_evt.handles.nus_rx_speed_handle = p_chars[i].characteristic.handle_value;
                        nus_c_evt.handles.nus_rx_speed_cccd_handle = p_chars[i].cccd_handle;
                        break;
    
                    case BLE_UUID_NUS_TX_SPEED_CHARACTERISTIC:
                        nus_c_evt.handles.nus_tx_handle_speed = p_chars[i].characteristic.handle_value;
                        break;
    								
                    case BLE_UUID_NUS_RX_INFORMATION_CHARACTERISTIC:
                        nus_c_evt.handles.nus_rx_information_handle = p_chars[i].characteristic.handle_value;
                        nus_c_evt.handles.nus_rx_information_cccd_handle = p_chars[i].cccd_handle;
                        break;
    
                    case BLE_UUID_NUS_TX_INFORMATION_CHARACTERISTIC:
                        nus_c_evt.handles.nus_tx_handle_information = p_chars[i].characteristic.handle_value;
                        break;								
    
    								
                    case BLE_UUID_NUS_RX_DEBUG_CHARACTERISTIC:
                        nus_c_evt.handles.nus_rx_debug_handle = p_chars[i].characteristic.handle_value;
                        nus_c_evt.handles.nus_rx_debug_cccd_handle = p_chars[i].cccd_handle;
                        break;
    
                    case BLE_UUID_NUS_TX_DEBUG_CHARACTERISTIC:
                        nus_c_evt.handles.nus_tx_handle_debug = p_chars[i].characteristic.handle_value;
                        break;		
    
                    case BLE_UUID_NUS_RX_CONTROL_CHARACTERISTIC:
                        nus_c_evt.handles.nus_rx_control_handle = p_chars[i].characteristic.handle_value;
                        nus_c_evt.handles.nus_rx_control_cccd_handle = p_chars[i].cccd_handle;
                        break;
    
                    case BLE_UUID_NUS_TX_CONTROL_CHARACTERISTIC:
                        nus_c_evt.handles.nus_tx_handle_control = p_chars[i].characteristic.handle_value;
                        break;									
    								
    
                    default:
                        break;
                }
            }
            if (p_ble_nus_c->evt_handler != NULL)
            {
                nus_c_evt.conn_handle = p_evt->conn_handle;
                nus_c_evt.evt_type    = BLE_NUS_C_EVT_DISCOVERY_COMPLETE;
                p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
            }
        }
    }
    

    Also I have used the following routines:

    static uint32_t cccd_configure(uint16_t conn_handle, uint16_t cccd_handle, bool enable)
    {
        uint8_t buf[BLE_CCCD_VALUE_LEN];
    
        buf[0] = enable ? BLE_GATT_HVX_NOTIFICATION : 0;
        buf[1] = 0;
    
        const ble_gattc_write_params_t write_params = {
            .write_op = BLE_GATT_OP_WRITE_REQ,
            .flags    = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE,
            .handle   = cccd_handle,
            .offset   = 0,
            .len      = sizeof(buf),
            .p_value  = buf
        };
    
        return sd_ble_gattc_write(conn_handle, &write_params);
    }
    
    uint32_t ble_nus_c_handles_assign(ble_nus_c_t * p_ble_nus,
                                      const uint16_t conn_handle,
                                      const ble_nus_c_handles_t * p_peer_handles)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_nus);
    
        p_ble_nus->conn_handle = conn_handle;
        if (p_peer_handles != NULL)
        {
            p_ble_nus->handles.nus_rx_control_cccd_handle = p_peer_handles->nus_rx_control_cccd_handle;
            p_ble_nus->handles.nus_rx_control_handle      = p_peer_handles->nus_rx_control_handle;
            p_ble_nus->handles.nus_tx_handle_control      = p_peer_handles->nus_tx_handle_control;
    			
            p_ble_nus->handles.nus_rx_speed_cccd_handle = p_peer_handles->nus_rx_speed_cccd_handle;
            p_ble_nus->handles.nus_rx_speed_handle      = p_peer_handles->nus_rx_speed_handle;
            p_ble_nus->handles.nus_tx_handle_speed      = p_peer_handles->nus_tx_handle_speed;		
    			
            p_ble_nus->handles.nus_rx_information_cccd_handle = p_peer_handles->nus_rx_information_cccd_handle;
            p_ble_nus->handles.nus_rx_information_handle      = p_peer_handles->nus_rx_information_handle;
            p_ble_nus->handles.nus_tx_handle_information      = p_peer_handles->nus_tx_handle_information;				
    			
    
            p_ble_nus->handles.nus_rx_debug_cccd_handle = p_peer_handles->nus_rx_debug_cccd_handle;
            p_ble_nus->handles.nus_rx_debug_handle      = p_peer_handles->nus_rx_debug_handle;
            p_ble_nus->handles.nus_tx_handle_debug      = p_peer_handles->nus_tx_handle_debug;			
        }
        return NRF_SUCCESS;
    }
    
  • Can you please try to use the 101010 button when you insert code? Then I don't need to edit every time.

    You probably get busy because you are trying to do several write requests at the same time. You can only send one write request at the time, then you have to wait for the response before you send a new one.

  • Hi Petter, thanks for the reply. Sorry about the indentation.

    1. By "You could of course try to use write command instead." do you mean write command without response? (reference: www.safaribooksonline.com/.../ch04.html).

    2. Could you please share a central and peripheral combo code that uses write command without response?

    3. If I use write command without response during declaration at periperals, will the central get an event when peripheral writes into that characteristics? Or, should the central do polling to find if a new data has been written to each characteristic (when peripheral uses write command without response during declaration)? If so, which register to poll at the central?

    4. If I wanted to use write with response, then, will the following logic work if I have 3 characteristics at the central and peripheral side?

    In the peripheral side, make sure that the 3 characteristics are defined with write and not write_wo_response:

    char_md.char_props.write         = 1;
    char_md.char_props.write_wo_resp = 0;
    

    The peripheral first writes to characteristic 1 and waits till BLE_GATTC_EVT_WRITE_RSP event has been got in the peripheral. The central first runs the routine: ble_nus_c_rx_notif_enable with cccd_configure with the handle of characteristic 1. This would mean that when peripheral writes data to characteristic1, the central would receive notification under : ble_nus_c_on_ble_evt routine with an event id : BLE_GATTC_EVT_HVX with p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_nus_c->handles.characteristic1. So, now the central can go ahead and read the data recieved in BLE_NUS_C_EVT_NUS_RX_EVT (in the routine: ble_nus_c_evt_handler). Once the central reads out the data sent by the peripheral, the peripheral would receive a BLE_GATTC_EVT_WRITE_RSP event.

    Now, the peripheral would send data in characteristic 2 and wait for BLE_GATTC_EVT_WRITE_RSP to be received. In the central side, once the central receives data in characteristic 1, it would call the routine: ble_nus_c_rx_notif_enable with cccd_configure with the handle of characteristic 2.

    Once central receives data in characteristic 2, (the peripheral would receive BLE_GATTC_EVT_WRITE_RSP at the same time), the central would call would call the routine: ble_nus_c_rx_notif_enable with cccd_configure with the handle of characteristic 3 and peripheral would write data to characteristic 3 and wait for BLE_GATTC_EVT_WRITE_RSP to happen, etc. Whenever the peripheral wants to write data to any of the characteristics, the above procedure would be followed. Even if the peripheral wants to write to only 1 characteristic (for example characteristic 3), the peripheral still needs to write dummy data to characteristics 1 and 2 so that the central and peripheral are in sync about which characteristic to be written (by peripheral) and read (by the central).

    Is the above logic correct?

  • The logic that I mentioned in the previous post did not work because, I was not able to sync between central and peripheral (meaning, peripheral transmits too soon and central misses some). The problem becomes worse when the number of peripherals increase. Please let me know if the following logic is correct:

    I have 1 central and 2 peripherals, each having 4 characteristic. My aim is to send data from each of the 4 characteristics from both the peripherals to central and vice versa. But, since the central can have only one characteristic to be notified at a time, I have come up with the following logic - so that I dont have to change the enable notification from one characteristic to another at the central end.

    1. Peripheral and central have 4 characteristics - the uuids of the characteristics match between central and peripheral.
    2. central has notification enabled only for charcteristic 4. In peripheral the first 3 characteristics have write without response and 4th characteristic has notification enabled.
    3. When I want to transmit data in one of the characteristic from peripheral to central, I write respective (different) data to all 4 characteristics. As soon as characteristic 4 is written in peripheral, central gets notified. Now, central reads the characteristic 4 in ble_nus_c_evt and first 3 characteristics using sd_ble_gattc_read. By this way, all 4 characteristics from both the peripherals would not be missed by the central.
    1. Yes, I mean write without response in GATT, which is write command in ATT.
    2. ble_app_uart_c uses write command, see ble_nus_c_string_send().
Related