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

Battery model for mesh

Hey

I would like know how can i add battery model for mesh. I have seen the Battery indicator in normal BLE sdk, now I want to implement in mesh sdk in the form of model. How can i achieve it, I want the data to be sent form client to server. I am using light switch program

Thanking you

sumanth

  • Hi,

    The SDK documentation describe how to implement new models. There is a Generic Battery model described in the Mesh Model specifications, please see section 3.1.6.

    Best regards,
    Jørgen

  • Thank you for your response

    I followed the procedure of how to implement the new models in light switch client program. I created two new files battery_client_model.c and battery_client_model.h (attached below) and added the battery model(model id 0x100D) as generic_onoff_client model and respective call back functions,structures etc.

    I am unable to get that model in Nordic MESH app, only client model is visible. Please go through the code attached below and let me where i have done wrong

    #include "model_common.h"
    
    #include <stdint.h>
    #include <stddef.h>
    #include <string.h>
    #include "access.h"
    #include "access_config.h"
    #include "nrf_mesh_assert.h"
    #include "nrf_mesh_utils.h"
    #include "nordic_common.h"
    #include "app_onoff.h"
    #include "log.h"
    #include "generic_onoff_server.h"
    #include "generic_onoff_messages.h"
    #include "battery_client_model.h"
    
    #define GENERIC_ONOFF_OPCODE_SET22  0x00C1
    
    /*battery model*/
    #define BLE_UUID_BATTERY_MODEL_ID   0x100D //180F
    
    //uint32_t battery_double_init(generic_onoff_server_t * p_server, uint8_t element_index);
    
    static void battery_status_handle(access_model_handle_t handle, const access_message_rx_t * p_rx_msg, void * p_args)
    {
        generic_battery_client_t * p_client = (generic_battery_client_t *) p_args;
        generic_battery_status_params_t in_data = {0};
    
        if (p_rx_msg->length == GENERIC_ONOFF_STATUS_MINLEN || p_rx_msg->length == GENERIC_ONOFF_STATUS_MAXLEN)
        {
            generic_battery_status_msg_pkt_t * p_msg_params_packed = (generic_battery_status_msg_pkt_t *) p_rx_msg->p_data;
    
            if (p_rx_msg->length == GENERIC_ONOFF_STATUS_MINLEN)
            {
                in_data.battery_State = p_msg_params_packed->battery_State;
                in_data.remaining_time_ms = 0;
            }
            else
            {
                in_data.battery_State = p_msg_params_packed->battery_State;
                in_data.remaining_time_ms = model_transition_time_decode(p_msg_params_packed->remaining_time);
            }
    
            p_client->settings.p_callbacks->battery_status_cb(p_client, &p_rx_msg->meta_data, &in_data);
        }
    }
    
    /*
    uint32_t battery_init(app_onoff_server_t * p_battery, uint8_t element_index)
    {   
        uint32_t status;
        status = battery_double_init(&p_battery->server, element_index);
        if(status |= NRF_SUCCESS)
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Battery init error\n");
        }
        return status;
    }
    */
    
    static const access_opcode_handler_t m_opcode_handlers2[] =
    {
        {ACCESS_OPCODE_SIG(GENERIC_ONOFF_OPCODE_SET22), battery_status_handle},
    };
    
    uint32_t battery_double_init(generic_battery_client_t * p_client, uint8_t element_index)
    {
        if (p_client == NULL ||
            p_client->settings.p_callbacks == NULL ||
            p_client->settings.p_callbacks->battery_status_cb == NULL ||
            p_client->settings.p_callbacks->periodic_publish_cb == NULL)
        {
            return NRF_ERROR_NULL;
        }
    
        if (p_client->settings.timeout == 0)
        {
            p_client->settings.timeout= MODEL_ACKNOWLEDGED_TRANSACTION_TIMEOUT;
        }
    
        access_model_add_params_t init_params =
        {
            .model_id = ACCESS_MODEL_SIG(BLE_UUID_BATTERY_MODEL_ID),
            .element_index =  0,
            .p_opcode_handlers = &m_opcode_handlers2[0],
            .opcode_count = ARRAY_SIZE(m_opcode_handlers2),
            .p_args = p_client,
            .publish_timeout_cb = NULL
        };
    
        uint32_t status = access_model_add(&init_params, &p_client->model_handle);
    
        if (status == NRF_SUCCESS)
        {
            status = access_model_subscription_list_alloc(p_client->model_handle);
        }
        return status;
    }  
    battery_client_model.h
    /* 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, 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"
    
    /* Provisioning and configuration */
    #include "mesh_provisionee.h"
    #include "mesh_app_utils.h"
    
    /* Models */
    #include "generic_onoff_client.h"
    
    /* Logging and RTT */
    #include "log.h"
    #include "rtt_input.h"
    
    /* Example specific includes */
    #include "app_config.h"
    #include "nrf_mesh_config_examples.h"
    #include "light_switch_example_common.h"
    #include "example_common.h"
    #include "ble_softdevice_support.h"
    
    /*battery included*/
    #include "app_onoff.h"
    #include "battery_client_model.h"
    
    #define APP_STATE_OFF                (0)
    #define APP_STATE_ON                 (1)
    #define haha                         (5)   
    #define APP_UNACK_MSG_REPEAT_COUNT   (2)
    
    //edited
    #define ONOFF_SERVER_0_LED          (BSP_LED_0)
    #define BATTERY_ELEMENT_INDEX     (0)
    //#define APP_ONOFF_ELEMENT_INDEX   (0)
    
    static generic_onoff_client_t m_clients[CLIENT_MODEL_INSTANCE_COUNT];
    static bool                   m_device_provisioned;
    
    
    static generic_battery_client_t m_battery;
    
    /*
    static void battery_model_init(void)
    {
        // Instantiate onoff server on element index APP_ONOFF_ELEMENT_INDEX 
        battery_init(&battery_name, BATTERY_ELEMENT_INDEX);
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "App OnOff Model Handle: %d\n",battery_name.server.model_handle);
    }
    */
    
    /* Forward declaration */
    static void app_gen_onoff_client_publish_interval_cb(access_model_handle_t handle, void * p_self);
    static void app_generic_onoff_client_status_cb(const generic_onoff_client_t * p_self,
                                                   const access_message_rx_meta_t * p_meta,
                                                   const generic_onoff_status_params_t * p_in);
    static void app_gen_onoff_client_transaction_status_cb(access_model_handle_t model_handle,
                                                           void * p_args,
                                                           access_reliable_status_t status);
    static void app_generic_battery_client_status_cb(const generic_battery_client_t * p_self,
                                                   const access_message_rx_meta_t * p_meta,
                                                   const generic_battery_status_params_t * p_in);
    
    const generic_battery_client_callbacks_t battery_cbs=
    {
        .battery_status_cb = app_generic_battery_client_status_cb,
        .ack_transaction_status_cb = app_gen_onoff_client_transaction_status_cb,
        .periodic_publish_cb = app_gen_onoff_client_publish_interval_cb
    };
    
    const generic_onoff_client_callbacks_t client_cbs =
    {
        .onoff_status_cb = app_generic_onoff_client_status_cb,
        .ack_transaction_status_cb = app_gen_onoff_client_transaction_status_cb,
        .periodic_publish_cb = app_gen_onoff_client_publish_interval_cb
    };
    
    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);
    }
    
    /* This callback is called periodically if model is configured for periodic publishing */
    static void app_gen_onoff_client_publish_interval_cb(access_model_handle_t handle, void * p_self)
    {
         __LOG(LOG_SRC_APP, LOG_LEVEL_WARN, "Publish desired message here.\n");
    }
    
    /* Acknowledged transaction status callback, if acknowledged transfer fails, application can
    * determine suitable course of action (e.g. re-initiate previous transaction) by using this
    * callback.
    */
    static void app_gen_onoff_client_transaction_status_cb(access_model_handle_t model_handle,
                                                           void * p_args,
                                                           access_reliable_status_t status)
    {
        switch(status)
        {
            case ACCESS_RELIABLE_TRANSFER_SUCCESS:
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Acknowledged transfer success.\n");
                break;
    
            case ACCESS_RELIABLE_TRANSFER_TIMEOUT:
                hal_led_blink_ms(LEDS_MASK, LED_BLINK_SHORT_INTERVAL_MS, LED_BLINK_CNT_NO_REPLY);
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Acknowledged transfer timeout.\n");
                break;
    
            case ACCESS_RELIABLE_TRANSFER_CANCELLED:
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Acknowledged transfer cancelled.\n");
                break;
    
            default:
                ERROR_CHECK(NRF_ERROR_INTERNAL);
                break;
        }
    }
    
    /* Generic OnOff client model interface: Process the received status message in this callback */
    static void app_generic_onoff_client_status_cb(const generic_onoff_client_t * p_self,
                                                   const access_message_rx_meta_t * p_meta,
                                                   const generic_onoff_status_params_t * p_in)
    {
        if (p_in->remaining_time_ms > 0)
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "OnOff server: 0x%04x, Present OnOff: %d, Target OnOff: %d, Remaining Time: %d ms\n",
                  p_meta->src.value, p_in->present_on_off, p_in->target_on_off, p_in->remaining_time_ms);
        }
        else
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "OnOff server: 0x%04x, Present OnOff: %d\n",
                  p_meta->src.value, p_in->present_on_off);
        }
    }
    
    static void app_generic_battery_client_status_cb(const generic_battery_client_t * p_self,
                                                   const access_message_rx_meta_t * p_meta,
                                                   const generic_battery_status_params_t * p_in)
    {
        if (p_in->remaining_time_ms > 0)
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "battery server: 0x%04x, Present OnOff: %d, Remaining Time: %d ms\n",
                  p_meta->src.value, p_in->battery_State, p_in->remaining_time_ms);
        }
        else
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "battery server: 0x%04x, Present OnOff: %d\n",
                  p_meta->src.value, p_in->battery_State);
        }
    }
    
    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);
    
        uint32_t status = NRF_SUCCESS;
        generic_onoff_set_params_t set_params;
        model_transition_t transition_params;
        static uint8_t tid = 0;
    
        /* Button 1: On, Button 1: Off, Client[0]
         * Button 2: On, Button 3: Off, Client[1]
         */
    
        switch(button_number)
        {
            case 0:
            case 2:
                set_params.on_off = APP_STATE_ON;
                break;
    
            case 1:
            case 3:
                set_params.on_off = APP_STATE_OFF;
                break;
        }
    
        set_params.tid = tid++;
        transition_params.delay_ms = APP_CONFIG_ONOFF_DELAY_MS;
        transition_params.transition_time_ms = APP_CONFIG_ONOFF_TRANSITION_TIME_MS;
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Sending msg: ONOFF SET %d\n", set_params.on_off);
    
        switch (button_number)
        {
            case 0:
            case 1:
                /* Demonstrate acknowledged transaction, using 1st client model instance */
                /* In this examples, users will not be blocked if the model is busy */
                (void)access_model_reliable_cancel(m_clients[0].model_handle);
                status = generic_onoff_client_set(&m_clients[0], &set_params, &transition_params);
                hal_led_pin_set(BSP_LED_0, set_params.on_off);
                break;
    
            case 2:
            case 3:
                /* Demonstrate un-acknowledged transaction, using 2nd client model instance */
                status = generic_onoff_client_set_unack(&m_clients[1], &set_params,
                                                        &transition_params, APP_UNACK_MSG_REPEAT_COUNT);
                hal_led_pin_set(BSP_LED_1, set_params.on_off);
                break;
        }
    
        switch (status)
        {
            case NRF_SUCCESS:
                break;
    
            case NRF_ERROR_NO_MEM:
            case NRF_ERROR_BUSY:
            case NRF_ERROR_INVALID_STATE:
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Client %u cannot send\n", button_number);
                hal_led_blink_ms(LEDS_MASK, LED_BLINK_SHORT_INTERVAL_MS, LED_BLINK_CNT_NO_REPLY);
                break;
    
            case NRF_ERROR_INVALID_PARAM:
                /* Publication not enabled for this client. One (or more) of the following is wrong:
                 * - An application key is missing, or there is no application key bound to the model
                 * - The client does not have its publication state set
                 *
                 * It is the provisioner that adds an application key, binds it to the model and sets
                 * the model's publication state.
                 */
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Publication not configured for client %u\n", button_number);
                break;
    
            default:
                ERROR_CHECK(status);
                break;
        }
    }
    
    static void rtt_input_handler(int key)
    {
        if (key >= '0' && key <= '3')
        {
            uint32_t button_number = key - '0';
            button_event_handler(button_number);
        }
    }
    
    
    static void models_init_cb(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Initializing and adding models\n");
    
        for (uint32_t i = 0; i < CLIENT_MODEL_INSTANCE_COUNT; ++i)
        {
            m_clients[i].settings.p_callbacks = &client_cbs;
            m_clients[i].settings.timeout = 0;
            m_clients[i].settings.force_segmented = APP_CONFIG_FORCE_SEGMENTATION;
            m_clients[i].settings.transmic_size = APP_CONFIG_MIC_SIZE;
    
            ERROR_CHECK(generic_onoff_client_init(&m_clients[i], i + 1));
        }
    
        m_battery.settings.p_callbacks = &battery_cbs;
        m_battery.settings.timeout = 0;
        m_battery.settings.force_segmented = APP_CONFIG_FORCE_SEGMENTATION;
        m_battery.settings.transmic_size = APP_CONFIG_MIC_SIZE;
        ERROR_CHECK(battery_double_init(&m_battery,BATTERY_ELEMENT_INDEX));
    }
    
    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
        };
        ERROR_CHECK(mesh_stack_init(&init_params, &m_device_provisioned));
    }
    
    static void initialize(void)
    {
        __LOG_INIT(LOG_SRC_APP | LOG_SRC_ACCESS | LOG_SRC_BEARER, LOG_LEVEL_INFO, LOG_CALLBACK_DEFAULT);
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- BLE Mesh Light Switch Client 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(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_CLIENT
            };
            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();
        }
    }
    
    generic_onoff_common.hgeneric_onoff_messages.h

    The above attached files are modified files for adding the battery_model, where major changes are in main.c battery_client_model.c/.h and minor changes in generic_onoff_messages and common.h files.

    Regards

    sumanth

  • hi, could you pls update link for battery model implementation or examples which be useful as a references?

Related