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

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 again. 

    Sorry about the delay. 

    There shouldn't be anything that you need to change in the mobile app. The error should be derived from your app. 

    You mention that you are getting an NRF_ERROR_NOT_FOUND. Have you debugged your application to figure out exactly where this error is returned?

    Regards, 
    Joakim

  • Hi Aftab,

    I have just accomplished your first task - implementing SimpleOnOff Server into LightSwitchServer Example.

    I commented out the initialization of the GenericOnOffServer and I only initialize SimpleOnOffServer.

    In addition to modifying the main.c file, simple_on_off_server.c should be added to the folder structure of the solution and in project options include folder of SimpleOnOffServer must be added to user include directories of preprocessor. (I hope I did not forget anything).

    Below is the modified main.c file. All changes are noted with a comment //NOTE. Additionaly button 2 alternatively calls get and set callbacks for testing purposes.

    /* 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"
    #include "simple_on_off_server.h" //NOTE Added.
    
    /* 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"
    
    #define ONOFF_SERVER_0_LED          (BSP_LED_0)
    #define ONOFF_SERVER_2_LED          (BSP_LED_2) //NOTE Added.
    #define APP_ONOFF_ELEMENT_INDEX     (0)
    
    static bool m_device_provisioned;
    
    uint8_t my_test_uint8_t = 0; //NOTE Added for testing.
    
    /*************************************************************************************************/
    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);
    
    
    /* 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)
    
    /* 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);
    }
    
    /* 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_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);
    }
    
    
    // NOTE define set and get callbacks
    // This callback is called when a message with opcode set (0xC1) is received. 
    // It sets selected output to received bool value, and returns the new bool value
    static bool my_simple_onoff_server_set_cb(const simple_on_off_server_t * p_self, bool onoff)
    {
      /* If there are more servers, resolve it here. See above. */
      __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "MySimpleOnOffServer set CB setting GPIO value: %d\n", onoff);
      hal_led_pin_set(ONOFF_SERVER_2_LED, onoff);
      return hal_led_pin_get(ONOFF_SERVER_2_LED);
    }
    
    // This callback is called when a message with opcode get (0xC2) is received.
    // It returns the state of the output.
    static bool my_simple_onoff_server_get_cb(const simple_on_off_server_t * p_self)
    {
      /* If there are more servers, resolve it here. See above. */
      __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "MySimpleOnOffServer get CB reporting value: %d\n", hal_led_pin_get(ONOFF_SERVER_2_LED));
      return hal_led_pin_get(ONOFF_SERVER_2_LED);
    }
    
    //NOTE instantiate my_simple_onoff_server_0
    static simple_on_off_server_t my_simple_onoff_server_0 =
    {
    	.model_handle = NULL, //model_handle of type access_model_handle_t will be provided by init function.
    	.get_cb = my_simple_onoff_server_get_cb, //Pointer to get callback function.
    	.set_cb = my_simple_onoff_server_set_cb  //Pointer to set callback function.
    };
    
    
    /*************************************************************************************************/
    
    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);
                break;
            }
    
            /* NOTE: Added: "Button 2" is my test button. */
            case 1:
            {
                // Test what callbacks do:
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Test Button pressed: %u\n", ++my_test_uint8_t);
                switch (my_test_uint8_t & 1)
                {
                  case 0:
                  {
                    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Will call CB set with param: %u\n", !hal_led_pin_get(ONOFF_SERVER_2_LED));
                    my_simple_onoff_server_set_cb(&my_simple_onoff_server_0, !hal_led_pin_get(ONOFF_SERVER_2_LED));
                    break;
                  }
                  case 1:
                  {
                    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Will call CB get.\n");
                    bool my_test_bool;
                    my_test_bool = my_simple_onoff_server_get_cb(&my_simple_onoff_server_0);
                    __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "CB get reporting: %u\n", my_test_bool);
                    break;
                  }
                }
    
              break;
            }
    
    
            /* NOTE Changed: Pressing "Button 3" i.e. SW2 changes "LED3" i.e. LED2 (And sends status change msg TODO later) */
            case 2:
            {
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Custom User action button 2\n");
                hal_led_pin_set(ONOFF_SERVER_2_LED, !hal_led_pin_get(ONOFF_SERVER_2_LED));
                ERROR_CHECK(simple_on_off_server_status_publish(&my_simple_onoff_server_0, hal_led_pin_get(ONOFF_SERVER_2_LED)));
                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");
        //NOTE Only SimpleOnOffServer will be initialized. To use both models, constants in nrf_mesh_config.h should be adjusted to allow more models.
        //app_model_init();
        ERROR_CHECK(simple_on_off_server_init(&my_simple_onoff_server_0, APP_ONOFF_ELEMENT_INDEX)); //NOTE Added
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "My Simple OnOff Model Handle: %d\n", my_simple_onoff_server_0.model_handle);
    }
    
    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();
    
        for (;;)
        {
            (void)sd_app_evt_wait();
        }
    }
    

    I did not implement the Client yet.

    I hope this helps.

    Best regards,
    Matevz

  • Thanks Matevz,

    Using this method I have added simple_onoff_model and tested it, working fine. I also added simple_message_server which seems working as provisioning is successful and can send data. But the data is not as expected. I was expecting a string but some garbage is being received.

    On sending data, in the my_simple_message_server_set_cb() function, I get response on RTT like:

    <t:    3497967>, main.c,  136, MySimpleMessageServer set CB scr=, dst= data: °, len: 2

    I'm attaching the project and simple_message_model files.

    mesh_light_server_modified_code.zip

    I have also noticed that in the Vendor Model Controls (nRF Mesh mobile app), Parameters does not allow to send data more than one byte, how to send a string through it?

  • I have tried to translate what I receive, this is what I receive:

     2392286>, main.c,  137, Á

    It seems that I receive character Á in the data field which is equivalent to 0xC1 hex. Hence I'm receiving the Opcode sent from Mesh app instead of actual data. Any pointer why I'm getting this?

Related