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

Mesh API for PB-GATT provisioning, proxy, Sample code

Hello,

I had a look at the brand new mesh 2.0 SDK, this looks very promising. I've been looking for some protocol description for the GATT connection to do the provisioning and the proxy functions, maybe I just hadn't looked at the right place, but I have no idea which data I have to send over the GATT from a samrtphone or a linux host to start the provisioning and talk to the mesh nodes. Is this a special API, or is this protocol the part of the bluetooth spec.? Could I use some Android-apps from another vendors to get it running? Did anyone got some sample code to get the mesh funtionality to work on a linux system? Will nordic deplay a sample Android-app for the mesh? How can I test the mesh setup?

I would like to start with one node which is talking to the linux system. Then I would like to do a provisioning and take a dedicated mesh-node into the system, and the next and the next... Then I have to communicate with all the provisioned nodes. What do I have to do to get such a setup working?

Thanks in advance, and have a nice weekend...

Parents
  • Hi rolliG,

    Glad to hear you’re interested in the nRF5 SDK for Mesh. First off, you can find information about the GATT connection in Bluetooth Mesh in Chapter 6 and 7 of the Mesh Profile specification. It’s not a bad read, but to summarize it, the data exchanged over the GATT connection is just raw Bluetooth Mesh data. I.e., the GATT connection is just acting as a dumb transport and you have to have a full mesh stack on both sides of the connection.

    We’re in the process of releasing apps for Android and iOS, but in the meantime you can use the Light switch provisioning example to quickly get started.

    We also provide an interactive Python interface to our serial example which has provisioning and configuration capabilities. Follow the Interactive provisioning and configuration tutorial to get started.

    Another alternative is to use BlueZ. BlueZ uses the PB-GATT and GATT proxy to provision and configure devices. You’ll need to download the latest version and compile it from source, the version shipped with the most common Linux distros isn’t usually very up to date. Follow the installation guide, as far as I remember, this should work:

    cd bluez-5.49
    ./configure --enable-mesh
    make
    make install
    

    Move to the mesh directory and run meshctl. Note that you might to install a few build dependencies in order to compile it from source.

    If you don’t have a Bluetooth controller that supports BLE, you can use a devkit as a HCI controller by following this guide: https://devzone.nordicsemi.com/b/blog/posts/nrf5x-support-within-the-zephyr-project-rtos

    BlueZ’s meshctl is very limited compared to the Interactive PyACI and does not support the full range of Configuration Client commands. Extending it is also more difficult.

    I’d recommend starting with a few devkits with the serial, light switch server and light switch client examples programmed and run through the Interactive provisioning and configuration tutorial.

    Edit: If you're trying out BlueZ, you'll have to provide static authentication data during the provisioning process. For the Light switch examples it can be found under examples/light_switch/include/light_switch_example_common.h and is 6E6F726469635F6578616D706C655F31. Unfortunately we cannot guarantee any support for BlueZ. but we have been able to do provisioning and configuration using BlueZ 5.49 and the nRF5 SDK for Mesh 2.0.

    Hope this helps,
    Thomas

Reply
  • Hi rolliG,

    Glad to hear you’re interested in the nRF5 SDK for Mesh. First off, you can find information about the GATT connection in Bluetooth Mesh in Chapter 6 and 7 of the Mesh Profile specification. It’s not a bad read, but to summarize it, the data exchanged over the GATT connection is just raw Bluetooth Mesh data. I.e., the GATT connection is just acting as a dumb transport and you have to have a full mesh stack on both sides of the connection.

    We’re in the process of releasing apps for Android and iOS, but in the meantime you can use the Light switch provisioning example to quickly get started.

    We also provide an interactive Python interface to our serial example which has provisioning and configuration capabilities. Follow the Interactive provisioning and configuration tutorial to get started.

    Another alternative is to use BlueZ. BlueZ uses the PB-GATT and GATT proxy to provision and configure devices. You’ll need to download the latest version and compile it from source, the version shipped with the most common Linux distros isn’t usually very up to date. Follow the installation guide, as far as I remember, this should work:

    cd bluez-5.49
    ./configure --enable-mesh
    make
    make install
    

    Move to the mesh directory and run meshctl. Note that you might to install a few build dependencies in order to compile it from source.

    If you don’t have a Bluetooth controller that supports BLE, you can use a devkit as a HCI controller by following this guide: https://devzone.nordicsemi.com/b/blog/posts/nrf5x-support-within-the-zephyr-project-rtos

    BlueZ’s meshctl is very limited compared to the Interactive PyACI and does not support the full range of Configuration Client commands. Extending it is also more difficult.

    I’d recommend starting with a few devkits with the serial, light switch server and light switch client examples programmed and run through the Interactive provisioning and configuration tutorial.

    Edit: If you're trying out BlueZ, you'll have to provide static authentication data during the provisioning process. For the Light switch examples it can be found under examples/light_switch/include/light_switch_example_common.h and is 6E6F726469635F6578616D706C655F31. Unfortunately we cannot guarantee any support for BlueZ. but we have been able to do provisioning and configuration using BlueZ 5.49 and the nRF5 SDK for Mesh 2.0.

    Hope this helps,
    Thomas

Children
  • Hello Thomas,

    thank You very much, now I have a running BlueZ, I can start a successfull provisioning (with meshctl under linux). The other side is a PCA10040 evalboard with the Mesh-Stack 2.0 light_switch_proxy_server example directly from the Mesh-SDK 2.0. I have read some connection setup under linux with the meshctl-tool (from zephyr and silicon labs). But I cannot switch the LED on the board, I don't have found a description of the connection process into the mesh, I have also very much problems on the meshctl itself, I couldn't find a good documentation about the meshctl, would You like to help me on that? Maybe it is not possible to sent data over the meshctl, which tool under linux could I use instead?

    My steps are the following:

    1.start the meshctl

    2. discover-unprovisioned on

    3. provision 0059ffff00000000b78bcba7d90782c5 --> this is successfully after giving the 6E6F726469635F6578616D706C655F31 as pin

    4. menu configure

    5. target 0100 --> (0100 is the mesh address)

    6. appkey-add 1

    7. bind 0 1 0002 --> (Is that correct?)

    How does I have to proceed further to get the lights controlled ?

    Thanks in advance:

    Roland

  • Hi Roland,

    Glad to hear you’ve got BlueZ up and running.
    As in the previous reply, I strongly recommend using the interactive python shell over BlueZ for Mesh control and configuration.

    However, if you want to use meshctl to control the Simple OnOff server, you’ll have to convert the model to a Generic OnOff model. Luckily, they’re very much the same :) Applying the following diff should give you a limited Generic OnOff model:

    modified   models/simple_on_off/include/simple_on_off_client.h
    @@ -52,7 +52,7 @@
     
     
     /** Simple OnOff Client model ID. */
    -#define SIMPLE_ON_OFF_CLIENT_MODEL_ID (0x0001)
    +#define SIMPLE_ON_OFF_CLIENT_MODEL_ID (0x1001)
     
     /** Simple OnOff status codes. */
     typedef enum
    modified   models/simple_on_off/include/simple_on_off_common.h
    @@ -79,10 +79,10 @@
     /** Simple OnOff opcodes. */
     typedef enum
     {
    -    SIMPLE_ON_OFF_OPCODE_SET = 0xC1,            /**< Simple OnOff Acknowledged Set. */
    -    SIMPLE_ON_OFF_OPCODE_GET = 0xC2,            /**< Simple OnOff Get. */
    -    SIMPLE_ON_OFF_OPCODE_SET_UNRELIABLE = 0xC3, /**< Simple OnOff Set Unreliable. */
    -    SIMPLE_ON_OFF_OPCODE_STATUS = 0xC4          /**< Simple OnOff Status. */
    +    SIMPLE_ON_OFF_OPCODE_SET = 0x8202,            /**< Simple OnOff Acknowledged Set. */
    +    SIMPLE_ON_OFF_OPCODE_GET = 0x8201,            /**< Simple OnOff Get. */
    +    SIMPLE_ON_OFF_OPCODE_SET_UNRELIABLE = 0x8203, /**< Simple OnOff Set Unreliable. */
    +    SIMPLE_ON_OFF_OPCODE_STATUS = 0x8204          /**< Simple OnOff Status. */
     } simple_on_off_opcode_t;
     
     /** Message format for the Simple OnOff Set message. */
    modified   models/simple_on_off/include/simple_on_off_server.h
    @@ -50,7 +50,7 @@
      */
     
     /** Simple OnOff Server model ID. */
    -#define SIMPLE_ON_OFF_SERVER_MODEL_ID (0x0000)
    +#define SIMPLE_ON_OFF_SERVER_MODEL_ID (0x1000)
     
     /** Forward declaration. */
     typedef struct __simple_on_off_server simple_on_off_server_t;
    modified   models/simple_on_off/src/simple_on_off_client.c
    @@ -117,11 +117,11 @@ static uint32_t send_reliable_message(const simple_on_off_client_t * p_client,
         reliable.message.p_buffer = p_data;
         reliable.message.length = length;
         reliable.message.opcode.opcode = opcode;
    -    reliable.message.opcode.company_id = SIMPLE_ON_OFF_COMPANY_ID;
    +    reliable.message.opcode.company_id = ACCESS_COMPANY_ID_NONE;
         reliable.message.force_segmented = false;
         reliable.message.transmic_size = NRF_MESH_TRANSMIC_SIZE_DEFAULT;
         reliable.reply_opcode.opcode = SIMPLE_ON_OFF_OPCODE_STATUS;
    -    reliable.reply_opcode.company_id = SIMPLE_ON_OFF_COMPANY_ID;
    +    reliable.reply_opcode.company_id = ACCESS_COMPANY_ID_NONE;
         reliable.timeout = ACCESS_RELIABLE_TIMEOUT_MIN;
         reliable.status_cb = reliable_status_cb;
     
    @@ -151,7 +151,7 @@ static void handle_status_cb(access_model_handle_t handle, const access_message_
     
     static const access_opcode_handler_t m_opcode_handlers[] =
     {
    -    {{SIMPLE_ON_OFF_OPCODE_STATUS, SIMPLE_ON_OFF_COMPANY_ID}, handle_status_cb}
    +    {{SIMPLE_ON_OFF_OPCODE_STATUS, ACCESS_COMPANY_ID_NONE}, handle_status_cb}
     };
     
     static void handle_publish_timeout(access_model_handle_t handle, void * p_args)
    @@ -177,7 +177,7 @@ uint32_t simple_on_off_client_init(simple_on_off_client_t * p_client, uint16_t e
     
         access_model_add_params_t init_params;
         init_params.model_id.model_id = SIMPLE_ON_OFF_CLIENT_MODEL_ID;
    -    init_params.model_id.company_id = SIMPLE_ON_OFF_COMPANY_ID;
    +    init_params.model_id.company_id = ACCESS_COMPANY_ID_NONE;
         init_params.element_index = element_index;
         init_params.p_opcode_handlers = &m_opcode_handlers[0];
         init_params.opcode_count = sizeof(m_opcode_handlers) / sizeof(m_opcode_handlers[0]);
    @@ -220,7 +220,7 @@ uint32_t simple_on_off_client_set_unreliable(simple_on_off_client_t * p_client,
     
         access_message_tx_t message;
         message.opcode.opcode = SIMPLE_ON_OFF_OPCODE_SET_UNRELIABLE;
    -    message.opcode.company_id = SIMPLE_ON_OFF_COMPANY_ID;
    +    message.opcode.company_id = ACCESS_COMPANY_ID_NONE;
         message.p_buffer = (const uint8_t*) &set_unreliable;
         message.length = sizeof(set_unreliable);
         message.force_segmented = false;
    
    

    Note: This model is not completely following the Mesh Model specification, e.g., it’s not handling transition time and delay, but it should work for testing with meshctl.

    If you want to use the Interactive PyACI, here’s the corresponding Generic OnOff Client:

    # Copyright (c) 2010 - 2018, Nordic Semiconductor ASA
    # All rights reserved.
    #
    # Redistribution and use in source and binary forms, with or without
    # modification, are permitted provided that the following conditions are met:
    #
    # 1. Redistributions of source code must retain the above copyright notice, this
    #    list of conditions and the following disclaimer.
    #
    # 2. Redistributions in binary form must reproduce the above copyright
    #    notice, this list of conditions and the following disclaimer in the
    #    documentation and/or other materials provided with the distribution.
    #
    # 3. Neither the name of Nordic Semiconductor ASA nor the names of its
    #    contributors may be used to endorse or promote products derived from this
    #    software without specific prior written permission.
    #
    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    # IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
    # ARE DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
    # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    # POSSIBILITY OF SUCH DAMAGE.
    
    from mesh.access import Model, Opcode
    import struct
    
    
    class GenericOnOffClient(Model):
        GENERIC_ON_OFF_STATUS = Opcode(0x8204, None, "Generic OnOff Status")
        GENERIC_ON_OFF_SET = Opcode(0x8202, None, "Generic OnOff Set")
        GENERIC_ON_OFF_GET = Opcode(0x8201, None, "Generic OnOff Get")
        GENERIC_ON_OFF_SET_UNACKNOWLEDGED = Opcode(0x8203, None, "Generic OnOff Set Unacknowledged")
    
        def __init__(self):
            self.opcodes = [
                (self.GENERIC_ON_OFF_STATUS, self.__generic_on_off_status_handler)]
            self.__tid = 0
            super(GenericOnOffClient, self).__init__(self.opcodes)
    
        def set(self, state):
            message = bytearray()
            message += struct.pack("<BB", int(state), self._tid)
            self.send(self.GENERIC_ON_OFF_SET, message)
    
        def get(self):
            self.send(self.GENERIC_ON_OFF_GET)
    
        def unacknowledged_set(self, state):
            message = bytearray()
            message += struct.pack("<BB", int(state), self._tid)
            self.send(self.GENERIC_ON_OFF_SET_UNACKNOWLEDGED, message)
    
        @property
        def _tid(self):
            tid = self.__tid
            self.__tid += 1
            if self.__tid >= 255:
                self.__tid = 0
            return tid
    
        def __generic_on_off_status_handler(self, opcode, message):
            on_off = "on" if message.data[0] > 0 else "off"
            self.logger.info("Present value is %s", on_off)
    
    

    Regarding your configuration steps in BlueZ, the following should be correct:

    discover-unprovisioned on
    provision 0059ffff00000000b78bcba7d90782c5
    
    # Use 6E6F726469635F6578616D706C655F31 as static authentication data
    # Wait for re-connect
    
    menu config
    target 0100
    appkey-add 1
    bind 0 1 1000
    
    back
    menu onoff
    target 0100
    onoff 1
    

    The Model ID for the Generic OnOff Server is 0x1000 and the value used in the bind command.

    Hope this helps,
    Thomas

Related