Adding Simple OnOff Model to Generic OnOff Server Example

Hello,

I'm using Mesh SDk4.0.0 and SD v7.0.1. I have tested the light switch client and server examples. 

Now I want to add some more models for additional functionality. I want to start with adding simple onOff model present in models\vendor\simple_on_off directory. But I could not find any documentation regarding the actual steps/process to achieve this. Can you guide me how to do that? 

I would also like to add simple message model for sending/receiving strings of data instead of 1/0. This will help me better expand the functionality. However, simple OnOff seems to be an easier point to start.

I have been reading how to create new model, it's a pretty good guide to get started with creating a model. But the guide doesn't describe how to integrate it into an app, or more favorably, how to create a new app from scratch and use this model, hence creating a base for a larger application.

  • Hi. 

    Apart from the documentation we have on the Simple On/Off model and Creating new models, we don't have any more detailed guide on how to create and implement the model. 

    If you are having any specific problems with creating a new model, you can post a ticket here on Devzone so that we can help you. 

    As for sending a string instead of a bool, you can take a look at these previous tickets on the same topic: 
    https://devzone.nordicsemi.com/f/nordic-q-a/29836/send-and-receive-a-string-via-access-layer 
    https://devzone.nordicsemi.com/f/nordic-q-a/46092/send-string-in-a-mesh-with-nrf52dk

    Best regards, 
    Joakim

  • Hello , Is there a guide/documentation to use Simple On/Off model in a mesh application instead of Generic On/Off model? I want to expand from there as Simple On/Off is easier to modify.

  • I tried to add another model to the light switch server example but I get error 5 while provisioning, the provision starts but disappears half way, the device re-appears in the scan window. I'm using Mesh SDK v4.0.0. 

    I'm adding new model like in attached main.c file. In my case, have made these changes to nrf_mesh_config_app.h:

    //#define ACCESS_MODEL_COUNT (3)
    #define ACCESS_MODEL_COUNT (4)
    
    /**
    * The number of elements in the application.
    *
    * @warning If the application is to support _multiple instances_ of the _same_ model, these instances
    * cannot be in the same element and a separate element is needed for each new instance of the same model.
    */
    //#define ACCESS_ELEMENT_COUNT (1)
    #define ACCESS_ELEMENT_COUNT (2)
    
    /**
    * The number of allocated subscription lists for the application.
    *
    * @note This value must equal @ref ACCESS_MODEL_COUNT minus the number of
    * models operating on shared states.
    */
    #define ACCESS_SUBSCRIPTION_LIST_COUNT (ACCESS_MODEL_COUNT)

    Following is the RTT view log:

    <t: 0>, main.c, 286, ----- BLE Mesh Light Switch Server Demo -----
    <t: 10731>, main.c, 255, Initializing and adding models
    <t: 10734>, main.c, 143, App OnOff Model Handle: 2
    <t: 10737>, main.c, 146, App OnOff Model Handle: 3
    <t:10770>, main.c, 274, Data in the persistent memory was corrupted. Device starts as unprovisioned.
    <t: 15638>, mesh_app_utils.c, 65, Device UUID (raw): 5611860C225C174DB4D8F89A59153D41
    <t: 15641>, mesh_app_utils.c, 70, Device UUID : 0C861156-5C22-4D17-B4D8-F89A59153D41
    <t: 682946>, ble_softdevice_support.c, 104, Successfully updated connection parameters
    <t: 758640>, ble_softdevice_support.c, 104, Successfully updated connection parameters
    <t: 957812>, app_error_weak.c, 119, Mesh error 5 at 0x00000000 (:0)

    Here is the main.c file:

    31364.main.c
    /* Copyright (c) 2010 - 2019, 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, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, 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.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
    *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, 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.
     */
    
    #include <stdint.h>
    #include <string.h>
    
    /* HAL */
    #include "boards.h"
    #include "simple_hal.h"
    #include "app_timer.h"
    
    /* Core */
    #include "nrf_mesh_config_core.h"
    #include "nrf_mesh_gatt.h"
    #include "nrf_mesh_configure.h"
    #include "nrf_mesh.h"
    #include "mesh_stack.h"
    #include "device_state_manager.h"
    #include "access_config.h"
    #include "proxy.h"
    
    /* Provisioning and configuration */
    #include "mesh_provisionee.h"
    #include "mesh_app_utils.h"
    
    /* Models */
    #include "generic_onoff_server.h"
    
    /* Logging and RTT */
    #include "log.h"
    #include "rtt_input.h"
    
    /* Example specific includes */
    #include "app_config.h"
    #include "example_common.h"
    #include "nrf_mesh_config_examples.h"
    #include "light_switch_example_common.h"
    #include "app_onoff.h"
    #include "ble_softdevice_support.h"
    
    #include "simple_on_off_server.h"
    
    #define ONOFF_SERVER_0_LED          (BSP_LED_0)
    #define ONOFF_SERVER_1_LED          (BSP_LED_1)
    #define APP_ONOFF_ELEMENT_INDEX     (0)
    
    static bool m_device_provisioned;
    
    /*************************************************************************************************/
    static void app_onoff_server_set_cb(const app_onoff_server_t * p_server, bool onoff);
    static void app_onoff_server_get_cb(const app_onoff_server_t * p_server, bool * p_present_onoff);
    
    static void app_onoff_server_set_cb_1(const app_onoff_server_t * p_server, bool onoff);
    static void app_onoff_server_get_cb_1(const app_onoff_server_t * p_server, bool * p_present_onoff);
    
    
    /* Generic OnOff server structure definition and initialization */
    APP_ONOFF_SERVER_DEF(m_onoff_server_0,
                         APP_CONFIG_FORCE_SEGMENTATION,
                         APP_CONFIG_MIC_SIZE, 
                         app_onoff_server_set_cb,
                         app_onoff_server_get_cb)
    
    APP_ONOFF_SERVER_DEF(m_onoff_server_1,
                         APP_CONFIG_FORCE_SEGMENTATION,
                         APP_CONFIG_MIC_SIZE, 
                         app_onoff_server_set_cb_1,
                         app_onoff_server_get_cb_1)
    
    
    /* Callback for updating the hardware state */
    static void app_onoff_server_set_cb(const app_onoff_server_t * p_server, bool onoff)
    {
        /* Resolve the server instance here if required, this example uses only 1 instance. */
    
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Setting GPIO value: %d\n", onoff)
    
        hal_led_pin_set(ONOFF_SERVER_0_LED, onoff);
    }
    
    static void app_onoff_server_set_cb_1(const app_onoff_server_t * p_server, bool onoff)
    {
        /* Resolve the server instance here if required, this example uses only 1 instance. */
    
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Setting GPIO value: %d\n", onoff)
    
        hal_led_pin_set(ONOFF_SERVER_1_LED, onoff);
    }
    
    /* Callback for reading the hardware state */
    static void app_onoff_server_get_cb(const app_onoff_server_t * p_server, bool * p_present_onoff)
    {
        /* Resolve the server instance here if required, this example uses only 1 instance. */
    
        *p_present_onoff = hal_led_pin_get(ONOFF_SERVER_0_LED);
    }
    
    static void app_onoff_server_get_cb_1(const app_onoff_server_t * p_server, bool * p_present_onoff)
    {
        /* Resolve the server instance here if required, this example uses only 1 instance. */
    
        *p_present_onoff = hal_led_pin_get(ONOFF_SERVER_1_LED);
    }
    
    static void app_model_init(void)
    {
        /* Instantiate onoff server on element index APP_ONOFF_ELEMENT_INDEX */
        ERROR_CHECK(app_onoff_init(&m_onoff_server_0, APP_ONOFF_ELEMENT_INDEX));
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "App OnOff Model Handle: %d\n", m_onoff_server_0.server.model_handle);
        
        ERROR_CHECK(app_onoff_init(&m_onoff_server_1, APP_ONOFF_ELEMENT_INDEX+1));
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "App OnOff Model Handle: %d\n", m_onoff_server_1.server.model_handle);
    }
    
    /*************************************************************************************************/
    
    static void node_reset(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- Node reset  -----\n");
        hal_led_blink_ms(LEDS_MASK, LED_BLINK_INTERVAL_MS, LED_BLINK_CNT_RESET);
        /* This function may return if there are ongoing flash operations. */
        mesh_stack_device_reset();
    }
    
    static void config_server_evt_cb(const config_server_evt_t * p_evt)
    {
        if (p_evt->type == CONFIG_SERVER_EVT_NODE_RESET)
        {
            node_reset();
        }
    }
    
    static void button_event_handler(uint32_t button_number)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Button %u pressed\n", button_number);
        switch (button_number)
        {
            /* Pressing SW1 on the Development Kit will result in LED state to toggle and trigger
            the STATUS message to inform client about the state change. This is a demonstration of
            state change publication due to local event. */
            case 0:
            {
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "User action \n");
                hal_led_pin_set(ONOFF_SERVER_0_LED, !hal_led_pin_get(ONOFF_SERVER_0_LED));
                app_onoff_status_publish(&m_onoff_server_0);
    
                hal_led_pin_set(ONOFF_SERVER_1_LED, !hal_led_pin_get(ONOFF_SERVER_1_LED));
                app_onoff_status_publish(&m_onoff_server_1);
    
                break;
            }
    
            /* Initiate node reset */
            case 3:
            {
                /* Clear all the states to reset the node. */
                if (mesh_stack_is_device_provisioned())
                {
    #if MESH_FEATURE_GATT_PROXY_ENABLED
                    (void) proxy_stop();
    #endif
                    mesh_stack_config_clear();
                    node_reset();
                }
                else
                {
                    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "The device is unprovisioned. Resetting has no effect.\n");
                }
                break;
            }
    
            default:
                break;
        }
    }
    
    static void app_rtt_input_handler(int key)
    {
        if (key >= '0' && key <= '4')
        {
            uint32_t button_number = key - '0';
            button_event_handler(button_number);
        }
    }
    
    static void device_identification_start_cb(uint8_t attention_duration_s)
    {
        hal_led_mask_set(LEDS_MASK, false);
        hal_led_blink_ms(BSP_LED_2_MASK  | BSP_LED_3_MASK,
                         LED_BLINK_ATTENTION_INTERVAL_MS,
                         LED_BLINK_ATTENTION_COUNT(attention_duration_s));
    }
    
    static void provisioning_aborted_cb(void)
    {
        hal_led_blink_stop();
    }
    
    static void provisioning_complete_cb(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Successfully provisioned\n");
    
    #if MESH_FEATURE_GATT_ENABLED
        /* Restores the application parameters after switching from the Provisioning
         * service to the Proxy  */
        gap_params_init();
        conn_params_init();
    #endif
    
        dsm_local_unicast_address_t node_address;
        dsm_local_unicast_addresses_get(&node_address);
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Node Address: 0x%04x \n", node_address.address_start);
    
        hal_led_blink_stop();
        hal_led_mask_set(LEDS_MASK, LED_MASK_STATE_OFF);
        hal_led_blink_ms(LEDS_MASK, LED_BLINK_INTERVAL_MS, LED_BLINK_CNT_PROV);
    }
    
    static void models_init_cb(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Initializing and adding models\n");
        app_model_init();
    }
    
    static void mesh_init(void)
    {
        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
        };
    
        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);
        }
    }
    
    static void initialize(void)
    {
        __LOG_INIT(LOG_SRC_APP | LOG_SRC_FRIEND, LOG_LEVEL_DBG1, LOG_CALLBACK_DEFAULT);
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- BLE Mesh Light Switch Server Demo -----\n");
    
        ERROR_CHECK(app_timer_init());
        hal_leds_init();
    
    #if BUTTON_BOARD
        ERROR_CHECK(hal_buttons_init(button_event_handler));
    #endif
    
        ble_stack_init();
    
    #if MESH_FEATURE_GATT_ENABLED
        gap_params_init();
        conn_params_init();
    #endif
    
        mesh_init();
    }
    
    static void start(void)
    {
        rtt_input_enable(app_rtt_input_handler, RTT_INPUT_POLL_PERIOD_MS);
    
        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 = device_identification_start_cb,
                .prov_device_identification_stop_cb = NULL,
                .prov_abort_cb = provisioning_aborted_cb,
                .p_device_uri = EX_URI_LS_SERVER
            };
            ERROR_CHECK(mesh_provisionee_prov_start(&prov_start_params));
        }
    
        mesh_app_uuid_print(nrf_mesh_configure_device_uuid_get());
    
        ERROR_CHECK(mesh_stack_start());
    
        hal_led_mask_set(LEDS_MASK, LED_MASK_STATE_OFF);
        hal_led_blink_ms(LEDS_MASK, LED_BLINK_INTERVAL_MS, LED_BLINK_CNT_START);
    }
    
    int main(void)
    {
        initialize();
        start();
    
        // Turn on the ckt for leds on hotshot board
        nrf_gpio_cfg_output(2);
        nrf_gpio_cfg_output(17);
        nrf_gpio_cfg_output(28);
        nrf_gpio_cfg_output(27);
    
        nrf_gpio_pin_set(2);        //Drive_enable_1
        nrf_gpio_pin_set(17);       //Drive_enable_2
        nrf_gpio_pin_set(28);       //LE1
        nrf_gpio_pin_clear(27);     //OE, active LOW
    
    
        for (;;)
        {
            (void)sd_app_evt_wait();
        }
    }
    

  • I have made a video of the provisioning process where it starts and collapses midway. Do I need to change something in the mobile app as well? Any thing to add to this?

Related