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

Is it possible that two characteristics are notifying at the same time?

I've create a custom service with two characteristics. I need that the values of these characteristics are notifying at the same time. Is it possible? In this case, one of the two characteristics is notifying one time for four times that the other characteristic does it.

  • If you mean in the same connection interval the answer is yes, but only if you can send more than one packet in a connection interval. What is your central device? I'm not sure what "one of the two characteristics is notifying one time for four times that the other characteristic does it." means. Could you please edit your question and elaborate? Maybe include some code that shows how you are doing this? And how you are testing this?

  • Yes, in the same connection interval. I'm testing in Master Control Panel. In this case, I'm modifying the heart rate program. I 've added a custom service that it notifies sensor data. The sensor gets more than 18 bytes through i2c communication, then I need two characteristics. The first characteristic shows the values once every four intervals and the second characteristic shows the values once every interval.

    static uint32_t bno_measurement_char_add(ble_bno_t            * p_bno,
                                             const ble_bno_init_t * p_bno_init)
    {
        ble_gatts_char_md_t char_md;
        ble_gatts_attr_md_t cccd_md;
        ble_gatts_attr_t    attr_char_value;
        ble_uuid_t          ble_uuid;
        ble_gatts_attr_md_t attr_md;
        uint8_t             encoded_initial_bnom[MAX_BNOM_LEN];
    
        memset(&cccd_md, 0, sizeof(cccd_md));
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
        cccd_md.write_perm = p_bno_init->bno_hrm_attr_md.cccd_write_perm;
        cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
    
        memset(&char_md, 0, sizeof(char_md));
    
        char_md.char_props.notify = 1;
        char_md.p_char_user_desc  = NULL;
        char_md.p_char_pf         = NULL;
        char_md.p_user_desc_md    = NULL;
        char_md.p_cccd_md         = &cccd_md;
        char_md.p_sccd_md         = NULL;
    
        BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BNO055_MESAUREMENT_CHAR);
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        attr_md.read_perm  = p_bno_init->bno_hrm_attr_md.read_perm;
        attr_md.write_perm = p_bno_init->bno_hrm_attr_md.write_perm;
        attr_md.vloc       = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth    = 0;
        attr_md.wr_auth    = 0;
        attr_md.vlen       = 1;
    
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        attr_char_value.p_uuid    = &ble_uuid;
        attr_char_value.p_attr_md = &attr_md;
        attr_char_value.init_len  = bnom_encode(p_bno, INITIAL_VALUE_BNOM, encoded_initial_bnom);
        attr_char_value.init_offs = 0;
        attr_char_value.max_len   = MAX_BNOM_LEN;
        attr_char_value.p_value   = encoded_initial_bnom;
    
        return sd_ble_gatts_characteristic_add(p_bno->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               &p_bno->bnom_handles);
    }
    
    static uint32_t body_sensor_location_char_add(ble_bno_t * p_bno, const ble_bno_init_t * p_bno_init)
    {
        ble_gatts_char_md_t char_md;
    		ble_gatts_attr_md_t cccd_md;																															//Add 05.08.2015
        ble_gatts_attr_t    attr_char_value;
        ble_uuid_t          ble_uuid;
        ble_gatts_attr_md_t attr_md;
    		uint8_t             encoded_initial_bnom[MAX_BNOM_LEN];																		//Add 05.08.2015
    
        //memset(&char_md, 0, sizeof(char_md));																										//Del 05.08.2015
    		memset(&cccd_md, 0, sizeof(cccd_md));																											//Add 05.08.2015
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);																				//Add 05.08.2015
        cccd_md.write_perm = p_bno_init->bno_hrm_attr_md.cccd_write_perm;													//Add 05.08.2015
        cccd_md.vloc       = BLE_GATTS_VLOC_STACK;																								//Add 05.08.2015
    
        memset(&char_md, 0, sizeof(char_md));																											//Add 05.08.2015
    	
        //char_md.char_props.read  = 1;																														//Del 05.08.2015
    		char_md.char_props.notify = 1;																														//Add 05.08.2015
        char_md.p_char_user_desc = NULL;
        char_md.p_char_pf        = NULL;
        char_md.p_user_desc_md   = NULL;
        //char_md.p_cccd_md        = NULL;																												//Del 05.08.2015
    		char_md.p_cccd_md        = &cccd_md;																											//Add 05.08.2015
        char_md.p_sccd_md        = NULL;
    
        BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BODY_SENSOR_LOCATION_CHAR);
    
        memset(&attr_md, 0, sizeof(attr_md));
    
        attr_md.read_perm  = p_bno_init->bno_bsl_attr_md.read_perm;
        attr_md.write_perm = p_bno_init->bno_bsl_attr_md.write_perm;
        attr_md.vloc       = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth    = 0;
        attr_md.wr_auth    = 0;
        //attr_md.vlen       = 0;																																	//Del 05.08.2015
    		attr_md.vlen       = 1;																																		//Add 05.08.2015
    
        memset(&attr_char_value, 0, sizeof(attr_char_value));
    
        attr_char_value.p_uuid    = &ble_uuid;
        attr_char_value.p_attr_md = &attr_md;
        //attr_char_value.init_len  = sizeof (uint8_t);																						//Del 05.08.2015
    		attr_char_value.init_len  = bnom_encode(p_bno, INITIAL_VALUE_BNOM, encoded_initial_bnom);	//Add 05.08.2015
        attr_char_value.init_offs = 0;
        //attr_char_value.max_len   = sizeof (uint8_t);																						//Del 05.08.2015
    		attr_char_value.max_len   = MAX_BNOM_LEN;																									//Add 05.08.2015
        //attr_char_value.p_value   = p_bno_init->p_body_sensor_location;													//Del 05.08.2015
    		attr_char_value.p_value   = encoded_initial_bnom;																					//Add 05.08.2015
    
        return sd_ble_gatts_characteristic_add(p_bno->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               &p_bno->bsl_handles);
    }
    
    
    uint32_t ble_bno_measurement_send(ble_bno_t * p_bno, uint16_t bno_measurement)
    {
        uint32_t err_code;
    
        // Send value if connected and notifying
        if (p_bno->conn_handle != BLE_CONN_HANDLE_INVALID)
        {
            uint8_t                encoded_hrm[MAX_BNOM_LEN];
            uint16_t               len;
            uint16_t               hvx_len;
            ble_gatts_hvx_params_t hvx_params;
    
            len     = bnom_encode(p_bno, bno_measurement, encoded_hrm);
            hvx_len = len;
    			
            memset(&hvx_params, 0, sizeof(hvx_params));
    
            hvx_params.handle = p_bno->bnom_handles.value_handle;
            hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
            hvx_params.offset = 0;
            hvx_params.p_len  = &hvx_len;
            hvx_params.p_data = encoded_hrm;
    
            err_code = sd_ble_gatts_hvx(p_bno->conn_handle, &hvx_params);
            if ((err_code == NRF_SUCCESS) && (hvx_len != len))
            {
                err_code = NRF_ERROR_DATA_SIZE;
            }
        }
        else
        {
            err_code = NRF_ERROR_INVALID_STATE;
        }
    
        return err_code;
    }
    
    uint32_t ble_eul_measurement_send(ble_bno_t * p_bno, uint16_t bno_measurement)
    {
        uint32_t err_code;
    
        // Send value if connected and notifying
        if (p_bno->conn_handle != BLE_CONN_HANDLE_INVALID)
        {
            uint8_t                encoded_hrm[MAX_BNOM_LEN];
            uint16_t               len;
            uint16_t               hvx_len;
            ble_gatts_hvx_params_t hvx_params;
    
            len     = bnom_encode(p_bno, bno_measurement, encoded_hrm);
            hvx_len = len;
    			
            memset(&hvx_params, 0, sizeof(hvx_params));
    
            hvx_params.handle = p_bno->bsl_handles.value_handle;
            hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
            hvx_params.offset = 0;
            hvx_params.p_len  = &hvx_len;
            hvx_params.p_data = encoded_hrm;
    
            err_code = sd_ble_gatts_hvx(p_bno->conn_handle, &hvx_params);
            if ((err_code == NRF_SUCCESS) && (hvx_len != len))
            {
                err_code = NRF_ERROR_DATA_SIZE;
            }
        }
        else
        {
            err_code = NRF_ERROR_INVALID_STATE;
        }
    
        return err_code;
    }
    
    static uint8_t bnom_encode(ble_bno_t * p_bno, uint16_t bno_measurement, uint8_t * p_encoded_buffer)
    {
    		s8 iError;	
    		
    		uint16_t i = 0;
    		
    		for(i=0;i<18;i++){
    		p_encoded_buffer[i] = 0;
    		}
    		
    		iError = BNO055_I2C_bus_read(BNO055_I2C_ADDR2<<1, BNO055_ACCEL_DATA_X_LSB_ADDR, p_encoded_buffer, 18);
    
    		uint8_t len = 18;
        return len;
    
    }
    
    static void bno_meas_timeout_handler(void * p_context)
    {
        static uint32_t cnt = 0;
        uint32_t        err_code;
        uint16_t        heart_rate;
    
        UNUSED_PARAMETER(p_context);
    
        heart_rate = (uint16_t)sensorsim_measure(&m_bno_sim_state, &m_bno_sim_cfg);
    
        cnt++;
        err_code = ble_bno_measurement_send(&m_bno, heart_rate);
        if ((err_code != NRF_SUCCESS) &&
            (err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != BLE_ERROR_NO_TX_BUFFERS) &&
            (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
            )
        {
            APP_ERROR_HANDLER(err_code);
        }
    
        // Disable RR Interval recording every third heart rate measurement.
        // NOTE: An application will normally not do this. It is done here just for testing generation
        //       of messages without RR Interval measurements.
        //m_rr_interval_enabled = ((cnt % 3) != 0);
    }
    
    
    /**@brief Function for handling the Heart rate measurement timer timeout.
     *
     * @details This function will be called each time the heart rate measurement timer expires.
     *          It will exclude RR Interval data from every third measurement.
     *
     * @param[in] p_context  Pointer used for passing some arbitrary information (context) from the
     *                       app_start_timer() call to the timeout handler.
     */
    static void euler_meas_timeout_handler(void * p_context)
    {
        static uint32_t cnt = 0;
        uint32_t        err_code;
        uint16_t        heart_rate;
    
        UNUSED_PARAMETER(p_context);
    
        heart_rate = (uint16_t)sensorsim_measure(&m_bno_sim_state, &m_bno_sim_cfg);
    
        cnt++;
        err_code = ble_eul_measurement_send(&m_bno, heart_rate);
        if ((err_code != NRF_SUCCESS) &&
            (err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != BLE_ERROR_NO_TX_BUFFERS) &&
            (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
            )
        {
            APP_ERROR_HANDLER(err_code);
        }
    
        // Disable RR Interval recording every third heart rate measurement.
        // NOTE: An application will normally not do this. It is done here just for testing generation
        //       of messages without RR Interval measurements.
        //m_rr_interval_enabled = ((cnt % 3) != 0);
    }
    
    static void timers_init(void)
    {
        err_code = app_timer_create(&m_bno_timer_id,
                                    APP_TIMER_MODE_REPEATED,
                                    bno_meas_timeout_handler);
        APP_ERROR_CHECK(err_code);
    
    
        err_code = app_timer_create(&m_bno_timer_id,
                                    APP_TIMER_MODE_REPEATED,
                                    euler_meas_timeout_handler);
        APP_ERROR_CHECK(err_code);
    }
    

    In my tests, both characteristics should show the same values at the same time.

  • Would be nice if you would have edited your question, but ok. Are your timers running with the same interval? Where/when do you start the timers?

  • The problem has been solved using a new timer for the second characteristic. Thanks!

Related