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

Mesh node self provisioning

Hi all,

I want to self provision my node. I am using nrf52832 dev board, mesh SDK v 3.1.0, light switch client example. You may wonder why self provisioning, when there is nrf mesh provisioning mobile app, but it is the necessary intermediate step in the project where I combine the provisioner and client role in one node. The parts of code for self provisioning:

static uint8_t m_netkey; 
static uint8_t m_appkey;
static uint8_t m_self_devkey;

static dsm_handle_t m_netkey_handle;
static dsm_handle_t m_appkey_handle;
static dsm_handle_t m_self_devkey_handle;

static dsm_handle_t group_handle;
static generic_onoff_client_t m_client;

void self_provision()
{
    dsm_local_unicast_address_t local_address = {START_ADDRESS, ACCESS_ELEMENT_COUNT};
    ERROR_CHECK(dsm_local_unicast_addresses_set(&local_address));
    
    //hardcoded keys
    m_netkey = NETKEY;     
    m_appkey = APPKEY;
    m_devkey = DEVKEY;
    
    ERROR_CHECK(dsm_subnet_add(0, netkey, &m_netkey_handle));
    ERROR_CHECK(dsm_appkey_add(0, m_netkey_handle ,m_appkey, &m_appkey_handle));
    ERROR_CHECK(dsm_devkey_add(START_ADDRESS, m_netkey_handle, m_self_devkey, &m_self_devkey_handle));
    
    ERROR_CHECK(dsm_address_publish_add(FIRST_GROUP_ADDRESS, &group_handle));
}

void app_default_models_bind_setup(void)
{
    ERROR_CHECK(config_server_bind(m_self_devkey_handle));
    
    ERROR_CHECK(access_model_application_bind(m_client.model_handle, m_appkey_handle));
    ERROR_CHECK(access_model_publish_application_set(m_client.model_handle, m_appkey_handle));
    
    ERROR_CHECK(access_model_publish_address_set((m_client.model_handle, group_handle));
}

void models_init_cb(void)
{
    ERROR_CHECK(config_client_init(app_config_client_event_cb));
    ERROR_CHECK(generic_onoff_client_init(&m_client, FIRST_ELEMENT_INDEX));
}

To test if I correctly self provision the CLIENT node, I used the nrf mesh app to provision the SERVER node (lifgt switch server example), took the appkey and the netkey assigned by the mesh app, hardcoded them into the code for light switch client, configured the subscription address on the server node (using nrf mesh app) and watched, if the server reacts on the button presses on my client board. But I just watched the transfer timeout (transfer timeout - You can can object I use acknowledged messages in group messaging. I am aware of the fact, it is just temporary solution). 

My question is: Is there something missing in the code snippets above relating to be capable to self provision the node? I assume it has to do something with config client or config server model. Every help appreciated :) 

Patrik

  • Hi Patrik, 

    A good source of reference is to have a look at our Mesh SDK v1.0.1's light switch example. In that example we have the provisioner and the client combined. And we actually have hardcoded provisioning in that example. 

    It looks pretty good in your code, I don't think there is any thing missing. I would suggest to debug on access layer , if you look at the code you can find where we printout "TX:" and "RX:" in access.c (Inside mesh_msg_handle() and packet_tx(). 

    Could you try to printout the message  data and meta data on the client when you press the button (check TX) and on the server when there is any message arrives (check RX) 

    I would also suggest to double check the netkey and app key received on the server after you provision the node using the phone, just to double check if they are identical to what you have on the client. 

  • Thank you for the reply, Hung. I printed the TX and RX info in both of the functions mesh_msg_handle() and packet_tx(). I found out, that the self provisioned client is sending the message on the air. To confirm I am really sending messages on the air, in the access.c  in mesh_evt_cb() I added printing functions:

    static void mesh_evt_cb(const nrf_mesh_evt_t * p_evt)
    {
        switch (p_evt->type)
        {
            case NRF_MESH_EVT_MESSAGE_RECEIVED:
                SEGGER_RTT_printf(0, "%s(): Received message from address 0x%04x\n", __func__, p_evt->params.message.src.value);
                mesh_msg_handle(&p_evt->params.message);
                break;
             case NRF_MESH_EVT_TX_COMPLETE:
                SEGGER_RTT_printf(0, "%s(): Message has been sent on the air with token number %u\n", __func__, p_evt->params.tx_complete.token);
                break;
            default:
                /* Ignore */
                break;
        }
    
    }

     

    On the self provisioned client side: checked the NRF_MESH_EVT_TX_COMPLETE event, because this event happens when the packet is sent on the air. I could also watch in packet_tx() sending to destination address, the source address and the whole message:

    static uint32_t packet_tx(access_model_handle_t handle,
                              const access_message_tx_t * p_tx_message,
                              const access_message_rx_t * p_rx_message,
                              const uint8_t *p_access_payload,
                              uint16_t access_payload_len)
    {
        ....
        ....
        ....
        status = nrf_mesh_packet_send(&tx_params, NULL);
        if (status == NRF_SUCCESS)
        {
            __LOG(LOG_SRC_ACCESS, LOG_LEVEL_DBG1, "TX: [aop: 0x%04x] \n", p_tx_message->opcode.opcode);
            __LOG_XB(LOG_SRC_ACCESS, LOG_LEVEL_DBG1, "TX: Msg", p_tx_message->p_buffer, p_tx_message->length);
            
            SEGGER_RTT_printf(0, "TX: Sending message to address 0x%04x\n", tx_params.dst.value);
            SEGGER_RTT_printf(0, "TX: The source address of message: 0x%04x\n", tx_params.src);
        }
    }
    

    On the server side, which I provisioned and configured by nrf mesh app: checked NRF_MESH_EVT_MESSAGE_RECEIVED event, which is triggered when incomming message.

    In my case the event NRF_MESH_EVT_TX_COMPLETE occurs every time on the client side, but the event NRF_MESH_EVT_MESSAGE_RECEIVED is not occuring on the server side. I am working with group address, publication group address on the client side and subscription address on the server side set the same, checked many times and confirmed, that on both side set the same netkey, appkey, iv index. Not sure if necessary, but in my case on both sides set the same appkey index and netkey index.

    To simulate my case, please try the light switch client example from mesh v1.0.1 on one board and the light switch server example from mesh version 3.1.0 on the second board. The server example I provisioned with nrf mesh app. In the client example I commented the following lines (to prevent provisioning of external nodes): 

    static void button_event_handler(uint32_t button_number)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Button %u pressed\n", button_number);
    //    if (m_configured_devices == 0)
    //    {
    //        __LOG(LOG_SRC_APP, LOG_LEVEL_WARN, "No devices provisioned\n");
    //        return;
    //    }
    //    else if (m_configured_devices <= button_number && button_number != BUTTON_NUMBER_GROUP)
    //    {
    //        __LOG(LOG_SRC_APP, LOG_LEVEL_WARN, "Device %u not provisioned yet.\n", button_number);
    //        return;
    //    }
    
        uint32_t status = NRF_SUCCESS;
        switch (button_number)
        {
            case 0:
            case 1:
            case 2:
                /* Invert LED. */
                status = simple_on_off_client_set(&m_clients[button_number],
                                                  !hal_led_pin_get(BSP_LED_0 + button_number));
                break;
            case 3:
                /* Group message: invert all LEDs. */
                status = simple_on_off_client_set_unreliable(&m_clients[GROUP_CLIENT_INDEX],
                                                             !hal_led_pin_get(BSP_LED_0 + button_number), 3);
                break;
            default:
                break;
        }
        
        ....
        ....
        ....
    }
    
    static void access_setup(void)
    {
        ....
        ....
        ....
        
    //    provisioner_init();
    //    if (m_configured_devices < m_provisioned_devices)
    //    {
    //        provisioner_configure(UNPROV_START_ADDRESS + m_configured_devices);
    //    }
    //    else if (m_provisioned_devices < SERVER_COUNT)
    //    {
    //        provisioner_wait_for_unprov(UNPROV_START_ADDRESS + m_provisioned_devices);
    //    }
    
    }

    I know that the server example contains the generic on off client model and the server example contains the simple on off server model, two different models, but up to the access layer the message should be processed even if not the same appkeys but the publication group address on the client side and subscription address on the server side set the same, network key set the same on both sides, iv index the same on both sides, not sure for appkey index and network index but in my case set the same on both sides.  -- > in this case NRF_MESH_EVT_MESSAGE_RECEIVED  event should occur on the server side when sending messages from client node. I tried it, but this event is not triggered. Dont know why ... Please, try to simulate that case :) 

  • Hi Patrik, 

    We spotted that you used the wrong key defines. They should be uint8_t[16] an array, not just an uint8_t. 

    In addition the IV Index need to be set as well (you can try to set it to 0 ).

    We have this function here that apply provisioning data after received from provisioner that you may want to have a look. 

  • Ouu, yes all the keys are arrays. It is wrong above in the code snippet, but in my code it is ok. I made the mistake by editing to devzone. Double checked the rest of the code above and it is identical with my code. 

  • netkey set to zero, but the result is the same

Related