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

How to add mesh to BLE device

Hi,

I am currently working on a project, where I am porting an existing application to the NRF52840.
The porting of the functionality have been rather smooth, but i struggling now with adding MESH to my project and i wondoer if my use case is even possible to achieve.

The project is based on a copy of the enocean switch example from NRF MESH SDK. I use the following SDK versions: "nRF5 SDK 16.0.0" and "nrf5 SDK for Mesh v4.0.0"

The existing project is a BLE peripheral device, where we have a mobile app which can connect to the device and do commissioning and other setup of the device.
The device is capable of of exchanging data with other nearby devices, where we use the manufacturer specific data part of both the advertisement packet and scan response packet.
This sho9uld now be extended with MESH functionality. So taht the devices can be part of a BLE MESH network. The device must be able to function as node/relay node/proxy node.
Modelwise we currently only need generic on/off and generic level.

According to the following link, i get the impression that the above is possible:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.meshsdk.v4.0.0%2Fmd_doc_getting_started_how_to_nordicSDK.html&cp=7_2_1_8

Section: "Concurrent SoftDevice and mesh activity"

Quote: "When advertising with the SoftDevice, try using the highest advertising interval your usage scenario can tolerate.
If possible, turn off the SoftDevice advertiser when it is not needed, and activate it only when you expect to receive a connection request. If you only need to send non-connectable,
non-scannable advertisements (for example, for third party beacon protocols), use the Mesh Advertiser API, as it is optimized for minimal context switching when used together with the Mesh."

My application needs to transmit both connectable/scannable and non-connectable_scannble advertisment telegrams continually (no timeout), therefore is not possible to use the "Mesh Advertiser API" correct?
My application currently advertises with an interval of 200ms, so there should be time to do mesh advertisement.

I do as metioned here and use mesh rc callback:

"SoftDevice-based scanning has the biggest impact on the Mesh performance of all the SoftDevice activity.
The Mesh is not able to receive packets while the SoftDevice is scanning, so every SoftDevice scan window replaces Mesh scanning.
SoftDevice scanning should only be used when trying to establish connections or when active scanning is required.
If general passive BLE scanning is required (for listening for beacons or other third party activity), hook into the Mesh scanner by setting an RX callback with the nrf_mesh_rx_cb_set function."

The above has been has been tested on the DK and this seems to work.

It took sometime for me to understand that the nRF Mesh app, does not show devices which only uses ADV bearers. -> please mention that somewhere, like in the App.
I had to refer to the light switch demo to actually come to the conclusion that when i have configured my system using only ADV bearer, that it can be scanned and almost be provioned be the light switch provisoner.

As mentioned above the device must be able to function as a proxy node and there need to make the following settings: ( in my ADV bearer application those were set to 0 )

/**
 * @defgroup MESH_CONFIG_GATT GATT configuration defines
 * @{
 */
/** PB-GATT feature. To be enabled only in combination with linking GATT files. */
#define MESH_FEATURE_PB_GATT_ENABLED                    (1)
/** GATT proxy feature. To be enabled only in combination with linking GATT proxy files. */
#define MESH_FEATURE_GATT_PROXY_ENABLED                 (1)
/** @} end of MESH_CONFIG_GATT */

This compiles and runs, until app_error_handler is called from function mesh_adv_data_set() in mesh_adv.c with error code 4 (ERROR_NO_MEM). Looking at the code, I see that it uses the same API i use in the application to
do advertisement. Is that the reason for this or is there a setting somewhere i need to change/increase to get rid of this error. Note that from the description in the link above, I assume that the MESH code doers not run/advertise
while it is done in the BLE peripheral part.

Please advise, thank you.

  • Here is my complete initialization code, am I missing something. Please let me know if more is needed:

    uint8_t HalBleHandlerStart( uint16_t usCompanyId, uint8_t * pBleMacAddr  )
    {
    	s_CompanyId = usCompanyId;
    
    	bleperipheral_init( usCompanyId, pBleMacAddr );
    	HalMeshHandler_init( scan_mesh_rx_cb );
    	HalMeshHandler_start();
    
    	( void ) sd_ble_gap_addr_get( &s_DeviceBleAddr );
    
    	return 1u;
    }
    
    /**
     *
     * @param pBleMacAddr
     */
    void bleperipheral_init( uint16_t usCompanyId, uint8_t * pBleMacAddr )
    {
    	s_CompanyId = usCompanyId;
    
    	bleperipheral_stack_init();
    
    	if ( pBleMacAddr != NULL )
    	{
    		// Set MAC address
    		uint32_t err_code;
    		ble_gap_addr_t BleAddr;
    		BleAddr.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC; // Public address type
    
    		for ( uint8_t i = 0; i < BLE_GAP_ADDR_LEN; i++ )
    		{
    			BleAddr.addr[ i ] = pBleMacAddr[ BLE_GAP_ADDR_LEN - i - 1 ];
    		}
    
    		do
    		{
    			err_code = sd_ble_gap_addr_set( &BleAddr );
    		} while ( err_code == NRF_ERROR_BUSY );
    
    		APP_ERROR_CHECK(err_code);
    	}
    
    	bleperipheral_gap_params_init();
    	bleperipheral_gatt_init();
    	advertising_init( 0u );
    	bleperipheral_conn_params_init();
    	bleperipheral_services_init();
    	advertising_start();
    }
    
    void bleperipheral_gap_params_init( void )
    {
        uint32_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 *) "",
                                              0 );
        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 library. */
    void bleperipheral_gatt_init( void )
    {
        ret_code_t err_code;
    
        err_code = nrf_ble_gatt_init( &m_bleperipheral_gatt, bleperipheral_gatt_evt_handler );
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_ble_gatt_att_mtu_periph_set( &m_bleperipheral_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE );
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for initializing the Advertising functionality.
     */
    static void advertising_init( uint8_t bUpdate )
    {
    	uint32_t               err_code;
    
        s_gap_adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
        s_gap_adv_data.adv_data.p_data = (s_gap_adv_data.adv_data.p_data == s_adv_packet_2 ? s_adv_packet : s_adv_packet_2);
        s_gap_adv_data.scan_rsp_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
        s_gap_adv_data.scan_rsp_data.p_data = (s_gap_adv_data.scan_rsp_data.p_data == s_scan_rsp_packet_2 ? s_scan_rsp_packet : s_scan_rsp_packet_2);
    
    
        ble_gap_adv_params_t advparams;
        ble_advdata_t advdata;
        ble_advdata_t srdata;
        memset( &advdata, 0, sizeof( advdata ) );
        memset( &srdata, 0, sizeof( srdata ) ) ;
        memset( &advparams, 0, sizeof( advparams ) );
    
        if ( m_conn_handle == BLE_CONN_HANDLE_INVALID )
        {
        	advparams.properties.type 	= BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
        }
        else
        {
        	// In connected state we can't advertise in connectable mode
        	advparams.properties.type 	= BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED;
        }
        advparams.duration			= BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED;
        advparams.filter_policy		= BLE_GAP_ADV_FP_ANY;   /**< Allow scan requests and connect requests from any device. */
        advparams.primary_phy 		= BLE_GAP_PHY_AUTO;
        advparams.secondary_phy		= BLE_GAP_PHY_AUTO;
        advparams.interval			= 320;	// 200 ms adv interval = 320 * 0,625 ms
    
        advdata.flags 				= BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
        advdata.name_type			= BLE_ADVDATA_NO_NAME;
        advdata.include_appearance 	= false;
        advdata.include_ble_device_addr = false;
    
        // Advertisement will include manufacturer specific data part.
    	s_manuf_data_adv.company_identifier = s_CompanyId;
    	s_manuf_data_adv.data.p_data = ( uint8_t * ) &s_manuf_data_adv_buf[ 0u ];
    	s_manuf_data_adv.data.size = MANUF_DATA_SIZE;
    	advdata.p_manuf_specific_data = &s_manuf_data_adv;
    
    	srdata.name_type 			= BLE_ADVDATA_NO_NAME;
    	srdata.include_appearance 	= false;
    	// Scan response will include manufacturer specific data part.
    	s_manuf_data_scanrsp.company_identifier = s_CompanyId;
    	s_manuf_data_scanrsp.data.p_data = ( uint8_t * ) &s_manuf_data_scan_rsp_buf[ 0u ];
    	s_manuf_data_scanrsp.data.size = MANUF_DATA_SIZE;
    	srdata.p_manuf_specific_data = &s_manuf_data_scanrsp;
    
    	err_code = ble_advdata_encode( &srdata, s_gap_adv_data.scan_rsp_data.p_data, &s_gap_adv_data.scan_rsp_data.len );
    	APP_ERROR_CHECK(err_code);
    
    	err_code = ble_advdata_encode( &advdata, s_gap_adv_data.adv_data.p_data, &s_gap_adv_data.adv_data.len );
    	APP_ERROR_CHECK(err_code);
    
    	if ( bUpdate )
    	{
    		err_code = sd_ble_gap_adv_set_configure( &m_advertising, &s_gap_adv_data, NULL );
    	}
    	else
    	{
    		err_code = sd_ble_gap_adv_set_configure( &m_advertising, &s_gap_adv_data, &advparams );
    	}
    	APP_ERROR_CHECK(err_code);
    }
    
    void bleperipheral_conn_params_init( void )
    {
        uint32_t               err_code;
        ble_conn_params_init_t cp_init;
    
        memset(&cp_init, 0, sizeof(cp_init));
    
        // NOTE: conn_params already set in gap_params_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                    = bleperipheral_on_conn_params_evt;
        cp_init.error_handler                  = bleperipheral_conn_params_error_handler;
    
        err_code = ble_conn_params_init(&cp_init);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for initializing services that will be used by the application.
     */
    static void bleperipheral_services_init(void)
    {
        uint32_t           err_code;
        ble_scomm_init_t     scomm_init;
    
        // Initialize custom service
        memset(&scomm_init, 0, sizeof(scomm_init));
    
        scomm_init.data_handler = scomm_data_handler;
    
        err_code = ble_scomm_init(&m_scomm, &scomm_init);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for starting advertising.
     */
    static void advertising_start(void)
    {
        uint32_t err_code = sd_ble_gap_adv_start( m_advertising, APP_BLE_CONN_CFG_TAG );
        APP_ERROR_CHECK(err_code);
    }
    
    // public available functions
    void HalMeshHandler_init( nrf_mesh_rx_cb_t rx_cb )
    {
        mesh_stack_init_params_t init_params =
        {
            .core.irq_priority       				= NRF_MESH_IRQ_PRIORITY_LOWEST,
            .core.lfclksrc           				= DEV_BOARD_LF_CLK_CFG,
            .core.p_uuid             				= NULL,
            .models.models_init_cb   				= models_init_cb,
            .models.config_server_cb 				= config_server_evt_cb,
    		.models.health_server_num_selftests 	= 0,
    		.models.p_health_server_selftest_array 	= NULL,
    		.models.health_server_attention_cb 		= health_server_attention_cb
        };
    
        uint32_t status = mesh_stack_init( &init_params, &m_device_provisioned );
        switch (status)
        {
            case NRF_ERROR_INVALID_DATA:
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Data in the persistent memory was corrupted. Device starts as unprovisioned.\n");
                break;
            case NRF_SUCCESS:
                break;
            default:
                ERROR_CHECK(status);
        }
    
        if ( rx_cb )
        {
        	s_scan_cb = rx_cb;
        }
        else
        {
        	s_scan_cb = NULL;
        }
    
        // Enabling ECDH offloading
        ERROR_CHECK( mesh_opt_prov_ecdh_offloading_set( true ) );
    
        /* Register event handler to receive NRF_MESH_EVT_FLASH_STABLE. Application functionality will
        be started after this event */
        nrf_mesh_evt_handler_add( &m_mesh_core_event_handler );
    }
    
    #include "example_common.h"
    void HalMeshHandler_start( void )
    {
    	uint32_t ret = NRF_SUCCESS;
        if ( !m_device_provisioned )
        {
            static const uint8_t static_auth_data[ NRF_MESH_KEY_SIZE ] = STATIC_AUTH_DATA;
            mesh_provisionee_start_params_t prov_start_params =
            {
                .p_static_data    = static_auth_data,
                .prov_complete_cb = provisioning_complete_cb,
                .prov_device_identification_start_cb = NULL,
                .prov_device_identification_stop_cb = NULL,
                .prov_abort_cb = NULL,
    			.p_device_uri = EX_URI_LS_CLIENT
            };
            ret = mesh_provisionee_prov_start( &prov_start_params );
            ERROR_CHECK( ret );
        }
    
        ret = mesh_stack_start();
        ERROR_CHECK( ret );
    }

  • Hi,

    As far as I can see this should be possible to do.

    My application needs to transmit both connectable/scannable and non-connectable_scannble advertisment telegrams continually (no timeout), therefore is not possible to use the "Mesh Advertiser API" correct?

    Yes, that is correct.

    It took sometime for me to understand that the nRF Mesh app, does not show devices which only uses ADV bearers. -> please mention that somewhere, like in the App.

    I will suggest to the developers to add something that mention this in the app.

    You will receive error code 4 (ERROR_NO_MEM) when you run out of memory, you can try allocate more memory, adjust the RAM size. See if that helps.

    I assume that the MESH code doers not run/advertise
    while it is done in the BLE peripheral part.

    Your assumption is correct. While the Softdevice is advertising the Mesh won’t be able to advertise. Like it is mentioned in the documentation, the SoftDevice activity will reduce the amount of time the Mesh gets on-air, and in order to maintain a consistently good Mesh performance, the SoftDevice radio parameters must be set as conservatively as possible.

    I also suggest you have a look at the coexistence examples if you haven’t already.

Related