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

Storing values using Flash-manager

Hi Team

I would like to store a simple integer value in flash and read it back after a power cycle. I already studied the library Flash-manager of the nRF5 SDK for Mesh v1.0.1. Since I found no example code this seems quite hard to do. Can you give us some example lines of code to write and read simple values to/from flash?

kind regards

Gerry

 

Parents
  • FormerMember
    +1 FormerMember

    Attached below is an example showing how to write and read custom data from flash using flash_manager:

    • SDK version: nrf5_SDK_for_Mesh_v1.0.1
    • The code should replace main.c in the following folder: ..\nrf5_SDK_for_Mesh_v1.0.1_src\examples\light_switch\client\src

    main.c

    /* Copyright (c) 2010 - 2017, 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 "nrf.h"
    #include "nrf_sdm.h"
    #include "boards.h"
    #include "nrf_mesh_sdk.h"
    #include "nrf_delay.h"
    
    /* Core */
    #include "nrf_mesh.h"
    #include "nrf_mesh_events.h"
    #include "nrf_mesh_prov.h"
    #include "nrf_mesh_assert.h"
    #include "log.h"
    
    #include "access.h"
    #include "access_config.h"
    #include "device_state_manager.h"
    
    #include "config_client.h"
    #include "health_client.h"
    #include "simple_on_off_client.h"
    
    #include "simple_hal.h"
    #include "provisioner.h"
    
    #include "light_switch_example_common.h"
    #include "rtt_input.h"
    
    #include "flash_manager.h" // For storing custom data in flash. 
    
    /*****************************************************************************
    * Custom data in flash
     *****************************************************************************/
     #define FLASH_CUSTOM_DATA_GROUP_ELEMENT 0x1ABC // A number in the range 0x0000 - 0x7EFF (flash_manager.h)
     #define CUSTOM_DATA_FLASH_PAGE_COUNT 1
    
     typedef struct 
     {
       uint32_t data[2];
     } custom_data_format_t; // Format for the custom data
    
     static flash_manager_t m_custom_data_flash_manager; // flash manager instance  
    
    
    
    /*****************************************************************************
     * Definitions
     *****************************************************************************/
    
    #define CLIENT_COUNT             (SERVER_COUNT + 1)
    #define GROUP_CLIENT_INDEX       (SERVER_COUNT)
    #define BUTTON_NUMBER_GROUP      (3)
    #define RTT_INPUT_POLL_PERIOD_MS (100)
    
    /*****************************************************************************
     * Static data
     *****************************************************************************/
    
    static const uint8_t m_netkey[NRF_MESH_KEY_SIZE] = NETKEY;
    static const uint8_t m_appkey[NRF_MESH_KEY_SIZE] = APPKEY;
    
    static dsm_handle_t m_netkey_handle;
    static dsm_handle_t m_appkey_handle;
    static dsm_handle_t m_devkey_handles[SERVER_COUNT];
    static dsm_handle_t m_server_handles[SERVER_COUNT];
    static dsm_handle_t m_group_handle;
    
    static simple_on_off_client_t m_clients[CLIENT_COUNT];
    static health_client_t m_health_client;
    
    static uint16_t m_provisioned_devices;
    static uint16_t m_configured_devices;
    
    /* Forward declarations */
    static void client_status_cb(const simple_on_off_client_t * p_self, simple_on_off_status_t status, uint16_t src);
    static void health_event_cb(const health_client_t * p_client, const health_client_evt_t * p_event);
    
    /*****************************************************************************
     * Static functions
     *****************************************************************************/
    
    /**
     * Retrieves stored device state manager configuration.
     * The number of provisioned devices is calculated from the number of device keys stored. The device
     * key for each server is stored on provisioning complete in the `provisioner_prov_complete_cb()`.
     *
     * @returns Number of provisioned devices.
     */
    static uint16_t provisioned_device_handles_load(void)
    {
        uint16_t provisioned_devices = 0;
    
        /* Load the key handles. */
        uint32_t count = 1;
        ERROR_CHECK(dsm_subnet_get_all(&m_netkey_handle, &count));
        count = 1;
        ERROR_CHECK(dsm_appkey_get_all(m_netkey_handle, &m_appkey_handle, &count));
    
        /* Load all the address handles. */
        dsm_handle_t address_handles[DSM_ADDR_MAX];
        count = DSM_NONVIRTUAL_ADDR_MAX;
        ERROR_CHECK(dsm_address_get_all(&address_handles[0], &count));
    
        for (uint32_t i = 0; i < count; ++i)
        {
            nrf_mesh_address_t address;
            ERROR_CHECK(dsm_address_get(address_handles[i], &address));
    
            /* If the address is a unicast address, it is one of the server's root element address and
             * we have should have a device key stored for it. If not, it is our GROUP_ADDRESS and we
             * load the handle for that.
             */
            if ((address.type == NRF_MESH_ADDRESS_TYPE_UNICAST) &&
                (dsm_devkey_handle_get(address.value, &m_devkey_handles[provisioned_devices]) == NRF_SUCCESS)
                && m_devkey_handles[provisioned_devices] != DSM_HANDLE_INVALID)
            {
                ERROR_CHECK(dsm_address_handle_get(&address, &m_server_handles[provisioned_devices]));
                provisioned_devices++;
            }
            else if (address.type == NRF_MESH_ADDRESS_TYPE_GROUP)
            {
                ERROR_CHECK(dsm_address_handle_get(&address, &m_group_handle));
            }
        }
    
        return provisioned_devices;
    }
    
    /**
     * Gets the number of configured devices.
     *
     * We exploit the fact that the publish address of the Simple OnOff clients is set at configuration
     * complete, i.e., in the `provisioner_config_successful_cb()`, and simply count the number of
     * clients with their publish address' set.
     */
    static uint16_t configured_devices_count_get(void)
    {
        uint16_t configured_devices = 0;
        for (uint32_t i = 0; i < SERVER_COUNT; ++i)
        {
            dsm_handle_t address_handle = DSM_HANDLE_INVALID;
            if ((access_model_publish_address_get(m_clients[i].model_handle,
                                                  &address_handle) == NRF_SUCCESS)
                && (DSM_HANDLE_INVALID != address_handle))
            {
                configured_devices++;
            }
            else
            {
                /* Clients are configured sequentially. */
                break;
            }
        }
    
        return configured_devices;
    }
    
    static void access_setup(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Setting up access layer and models\n");
    
        dsm_init();
        access_init();
    
        m_netkey_handle = DSM_HANDLE_INVALID;
        m_appkey_handle = DSM_HANDLE_INVALID;
        for (uint32_t i = 0; i < SERVER_COUNT; ++i)
        {
            m_devkey_handles[i] = DSM_HANDLE_INVALID;
            m_server_handles[i] = DSM_HANDLE_INVALID;
        }
        m_group_handle = DSM_HANDLE_INVALID;
    
        /* Initialize and enable all the models before calling ***_flash_config_load. */
        ERROR_CHECK(config_client_init(config_client_event_cb));
        ERROR_CHECK(health_client_init(&m_health_client, 0, health_event_cb));
    
        for (uint32_t i = 0; i < CLIENT_COUNT; ++i)
        {
            m_clients[i].status_cb = client_status_cb;
            ERROR_CHECK(simple_on_off_client_init(&m_clients[i], i));
        }
    
        if (dsm_flash_config_load())
        {
            m_provisioned_devices = provisioned_device_handles_load();
        }
        else
        {
            /* Set and add local addresses and keys, if flash recovery fails. */
            dsm_local_unicast_address_t local_address = {PROVISIONER_ADDRESS, ACCESS_ELEMENT_COUNT};
            ERROR_CHECK(dsm_local_unicast_addresses_set(&local_address));
            ERROR_CHECK(dsm_address_publish_add(GROUP_ADDRESS, &m_group_handle));
            ERROR_CHECK(dsm_subnet_add(0, m_netkey, &m_netkey_handle));
            ERROR_CHECK(dsm_appkey_add(0, m_netkey_handle, m_appkey, &m_appkey_handle));
        }
    
        if (access_flash_config_load())
        {
            m_configured_devices = configured_devices_count_get();
        }
        else
        {
            /* Bind the keys to the health client. */
            ERROR_CHECK(access_model_application_bind(m_health_client.model_handle, m_appkey_handle));
            ERROR_CHECK(access_model_publish_application_set(m_health_client.model_handle, m_appkey_handle));
    
            /* Bind the keys to the Simple OnOff clients. */
            for (uint32_t i = 0; i < SERVER_COUNT; ++i)
            {
                ERROR_CHECK(access_model_application_bind(m_clients[i].model_handle, m_appkey_handle));
                ERROR_CHECK(access_model_publish_application_set(m_clients[i].model_handle, m_appkey_handle));
            }
    
            ERROR_CHECK(access_model_application_bind(m_clients[GROUP_CLIENT_INDEX].model_handle, m_appkey_handle));
            ERROR_CHECK(access_model_publish_application_set(m_clients[GROUP_CLIENT_INDEX].model_handle, m_appkey_handle));
            ERROR_CHECK(access_model_publish_address_set(m_clients[GROUP_CLIENT_INDEX].model_handle, m_group_handle));
            access_flash_config_store();
        }
    
        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);
        }
    }
    
    static uint32_t server_index_get(const simple_on_off_client_t * p_client)
    {
        uint32_t index = (((uint32_t) p_client - ((uint32_t) &m_clients[0]))) / sizeof(m_clients[0]);
        NRF_MESH_ASSERT(index < SERVER_COUNT);
        return index;
    }
    
    static void client_status_cb(const simple_on_off_client_t * p_self, simple_on_off_status_t status, uint16_t src)
    {
        uint32_t server_index = server_index_get(p_self);
        switch (status)
        {
            case SIMPLE_ON_OFF_STATUS_ON:
                hal_led_pin_set(BSP_LED_0 + server_index, true);
                break;
    
            case SIMPLE_ON_OFF_STATUS_OFF:
                hal_led_pin_set(BSP_LED_0 + server_index, false);
                break;
    
            case SIMPLE_ON_OFF_STATUS_ERROR_NO_REPLY:
                hal_led_blink_ms(LEDS_MASK, 100, 6);
                break;
    
            default:
                NRF_MESH_ASSERT(false);
                break;
        }
    
        /* Set 4th LED on when all servers are on. */
        bool all_servers_on = true;
        for (uint32_t i = BSP_LED_0; i < BSP_LED_0 + m_configured_devices; ++i)
        {
            if (!hal_led_pin_get(i))
            {
                all_servers_on = false;
                break;
            }
        }
    
        hal_led_pin_set(BSP_LED_3, all_servers_on);
    }
    
    static void health_event_cb(const health_client_t * p_client, const health_client_evt_t * p_event)
    {
        switch (p_event->type)
        {
            case HEALTH_CLIENT_EVT_TYPE_CURRENT_STATUS_RECEIVED:
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Node 0x%04x alive with %u active fault(s), RSSI: %d\n",
                      p_event->p_meta_data->src.value, p_event->data.fault_status.fault_array_length,
                      p_event->p_meta_data->rssi);
                break;
            default:
                break;
        }
    }
    
    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;
    
        }
    
        if (status == NRF_ERROR_INVALID_STATE ||
            status == NRF_ERROR_NO_MEM ||
            status == NRF_ERROR_BUSY)
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Cannot send. Device is busy.\n");
            hal_led_blink_ms(LEDS_MASK, 50, 4);
        }
        else
        {
            ERROR_CHECK(status);
        }
    }
    
    /*****************************************************************************
     * Event callbacks from the provisioner
     *****************************************************************************/
    
    void provisioner_config_successful_cb(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Configuration of device %u successful\n", m_configured_devices);
    
        /* Set publish address for the client to the corresponding server. */
        ERROR_CHECK(access_model_publish_address_set(m_clients[m_configured_devices].model_handle,
                                                     m_server_handles[m_configured_devices]));
        access_flash_config_store();
    
        hal_led_pin_set(BSP_LED_0 + m_configured_devices, false);
        m_configured_devices++;
    
        if (m_configured_devices < SERVER_COUNT)
        {
            provisioner_wait_for_unprov(UNPROV_START_ADDRESS + m_provisioned_devices);
            hal_led_pin_set(BSP_LED_0 + m_configured_devices, true);
        }
        else
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "All servers provisioned\n");
            hal_led_blink_ms(LEDS_MASK, 100, 4);
        }
    }
    
    void provisioner_config_failed_cb(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Configuration of device %u failed\n", m_configured_devices);
    
        /* Delete key and address. */
        ERROR_CHECK(dsm_address_publish_remove(m_server_handles[m_configured_devices]));
        ERROR_CHECK(dsm_devkey_delete(m_devkey_handles[m_configured_devices]));
        provisioner_wait_for_unprov(UNPROV_START_ADDRESS + m_provisioned_devices);
    }
    
    void provisioner_prov_complete_cb(const nrf_mesh_prov_evt_complete_t * p_prov_data)
    {
        /* We should not get here if all servers are provisioned. */
        NRF_MESH_ASSERT(m_configured_devices < SERVER_COUNT);
    
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Provisioning complete. Adding address 0x%04x.\n", p_prov_data->address);
    
        /* Add to local storage. */
        ERROR_CHECK(dsm_address_publish_add(p_prov_data->address, &m_server_handles[m_provisioned_devices]));
        ERROR_CHECK(dsm_devkey_add(p_prov_data->address, m_netkey_handle, p_prov_data->p_devkey, &m_devkey_handles[m_provisioned_devices]));
    
        /* Bind the device key to the configuration server and set the new node as the active server. */
        ERROR_CHECK(config_client_server_bind(m_devkey_handles[m_provisioned_devices]));
        ERROR_CHECK(config_client_server_set(m_devkey_handles[m_provisioned_devices],
                                             m_server_handles[m_provisioned_devices]));
    
        m_provisioned_devices++;
    
        /* Move on to the configuration step. */
        provisioner_configure(UNPROV_START_ADDRESS + m_configured_devices);
    }
    
    static void rtt_input_handler(int key)
    {
        if (key >= '0' && key <= '3')
        {
            uint32_t button_number = key - '0';
            button_event_handler(button_number);
        }
    }
    
    int main(void)
    {
        uint32_t ret_code;
    
        __LOG_INIT(LOG_SRC_APP | LOG_SRC_ACCESS, LOG_LEVEL_INFO, LOG_CALLBACK_DEFAULT);
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- BLE Mesh Light Switch Client Demo -----\n");
    
        hal_leds_init();
        ERROR_CHECK(hal_buttons_init(button_event_handler));
    
        /* Set the first LED */
        hal_led_pin_set(BSP_LED_0, true);
        mesh_core_setup();
        access_setup();
        rtt_input_enable(rtt_input_handler, RTT_INPUT_POLL_PERIOD_MS);
    
        /* Adding custom data to flash for persistent storage */
    
        // 1) Flash manager is already initialized
    
        // 2) Add a new flash manager instance. NB: should not overlap (in region) the instance used by mesh.  
    
        flash_manager_config_t custom_data_manager_config;
        custom_data_manager_config.write_complete_cb = NULL; 
        custom_data_manager_config.invalidate_complete_cb = NULL; 
        custom_data_manager_config.remove_complete_cb = NULL; 
        custom_data_manager_config.min_available_space = WORD_SIZE;
    
        // The new instance of flash manager should use an unused region of flash:
        custom_data_manager_config.p_area = (const flash_manager_page_t *) (((const uint8_t *) dsm_flash_area_get()) - (ACCESS_FLASH_PAGE_COUNT * PAGE_SIZE) - (NET_FLASH_PAGE_COUNT * PAGE_SIZE) );
        
        custom_data_manager_config.page_count = CUSTOM_DATA_FLASH_PAGE_COUNT;
       
        ret_code = flash_manager_add(&m_custom_data_flash_manager, &custom_data_manager_config);
       
        if (NRF_SUCCESS != ret_code) {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Flash error: no memory\n",ret_code);
        
        }
       
        
        // 3) Write to Flash
    
        // a) allocate flash 
        fm_entry_t * p_entry = flash_manager_entry_alloc(&m_custom_data_flash_manager, FLASH_CUSTOM_DATA_GROUP_ELEMENT, sizeof(custom_data_format_t));
        if (p_entry == NULL)
        {
          return NRF_ERROR_BUSY;
        }
          else
        {
           
          custom_data_format_t * p_custom_data = (custom_data_format_t *) p_entry->data;
          p_custom_data->data[0] = 5;
          p_custom_data->data[1] = 9;
       
          // b) write to flash
          flash_manager_entry_commit(p_entry);
          __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "write:%x, %x\n",p_entry->data[0], p_entry->data[1]);
          
        }
        
        
        // 4) Wait for flash manager to finish.
        flash_manager_wait();
       
       
        
        // 5) Read from Flash
        const fm_entry_t * p_read_raw = flash_manager_entry_get(&m_custom_data_flash_manager, FLASH_CUSTOM_DATA_GROUP_ELEMENT);
        
        const custom_data_format_t * p_read_data = (const custom_data_format_t *) p_read_raw->data;
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "read:%x, %x\n",p_read_data->data[0], p_read_data->data[1]);
        
    
    
    
        while (true)
        {
            (void)sd_app_evt_wait();
        }
    }
    

     

    The code is provided as-is, without any warranty.

Reply
  • FormerMember
    +1 FormerMember

    Attached below is an example showing how to write and read custom data from flash using flash_manager:

    • SDK version: nrf5_SDK_for_Mesh_v1.0.1
    • The code should replace main.c in the following folder: ..\nrf5_SDK_for_Mesh_v1.0.1_src\examples\light_switch\client\src

    main.c

    /* Copyright (c) 2010 - 2017, 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 "nrf.h"
    #include "nrf_sdm.h"
    #include "boards.h"
    #include "nrf_mesh_sdk.h"
    #include "nrf_delay.h"
    
    /* Core */
    #include "nrf_mesh.h"
    #include "nrf_mesh_events.h"
    #include "nrf_mesh_prov.h"
    #include "nrf_mesh_assert.h"
    #include "log.h"
    
    #include "access.h"
    #include "access_config.h"
    #include "device_state_manager.h"
    
    #include "config_client.h"
    #include "health_client.h"
    #include "simple_on_off_client.h"
    
    #include "simple_hal.h"
    #include "provisioner.h"
    
    #include "light_switch_example_common.h"
    #include "rtt_input.h"
    
    #include "flash_manager.h" // For storing custom data in flash. 
    
    /*****************************************************************************
    * Custom data in flash
     *****************************************************************************/
     #define FLASH_CUSTOM_DATA_GROUP_ELEMENT 0x1ABC // A number in the range 0x0000 - 0x7EFF (flash_manager.h)
     #define CUSTOM_DATA_FLASH_PAGE_COUNT 1
    
     typedef struct 
     {
       uint32_t data[2];
     } custom_data_format_t; // Format for the custom data
    
     static flash_manager_t m_custom_data_flash_manager; // flash manager instance  
    
    
    
    /*****************************************************************************
     * Definitions
     *****************************************************************************/
    
    #define CLIENT_COUNT             (SERVER_COUNT + 1)
    #define GROUP_CLIENT_INDEX       (SERVER_COUNT)
    #define BUTTON_NUMBER_GROUP      (3)
    #define RTT_INPUT_POLL_PERIOD_MS (100)
    
    /*****************************************************************************
     * Static data
     *****************************************************************************/
    
    static const uint8_t m_netkey[NRF_MESH_KEY_SIZE] = NETKEY;
    static const uint8_t m_appkey[NRF_MESH_KEY_SIZE] = APPKEY;
    
    static dsm_handle_t m_netkey_handle;
    static dsm_handle_t m_appkey_handle;
    static dsm_handle_t m_devkey_handles[SERVER_COUNT];
    static dsm_handle_t m_server_handles[SERVER_COUNT];
    static dsm_handle_t m_group_handle;
    
    static simple_on_off_client_t m_clients[CLIENT_COUNT];
    static health_client_t m_health_client;
    
    static uint16_t m_provisioned_devices;
    static uint16_t m_configured_devices;
    
    /* Forward declarations */
    static void client_status_cb(const simple_on_off_client_t * p_self, simple_on_off_status_t status, uint16_t src);
    static void health_event_cb(const health_client_t * p_client, const health_client_evt_t * p_event);
    
    /*****************************************************************************
     * Static functions
     *****************************************************************************/
    
    /**
     * Retrieves stored device state manager configuration.
     * The number of provisioned devices is calculated from the number of device keys stored. The device
     * key for each server is stored on provisioning complete in the `provisioner_prov_complete_cb()`.
     *
     * @returns Number of provisioned devices.
     */
    static uint16_t provisioned_device_handles_load(void)
    {
        uint16_t provisioned_devices = 0;
    
        /* Load the key handles. */
        uint32_t count = 1;
        ERROR_CHECK(dsm_subnet_get_all(&m_netkey_handle, &count));
        count = 1;
        ERROR_CHECK(dsm_appkey_get_all(m_netkey_handle, &m_appkey_handle, &count));
    
        /* Load all the address handles. */
        dsm_handle_t address_handles[DSM_ADDR_MAX];
        count = DSM_NONVIRTUAL_ADDR_MAX;
        ERROR_CHECK(dsm_address_get_all(&address_handles[0], &count));
    
        for (uint32_t i = 0; i < count; ++i)
        {
            nrf_mesh_address_t address;
            ERROR_CHECK(dsm_address_get(address_handles[i], &address));
    
            /* If the address is a unicast address, it is one of the server's root element address and
             * we have should have a device key stored for it. If not, it is our GROUP_ADDRESS and we
             * load the handle for that.
             */
            if ((address.type == NRF_MESH_ADDRESS_TYPE_UNICAST) &&
                (dsm_devkey_handle_get(address.value, &m_devkey_handles[provisioned_devices]) == NRF_SUCCESS)
                && m_devkey_handles[provisioned_devices] != DSM_HANDLE_INVALID)
            {
                ERROR_CHECK(dsm_address_handle_get(&address, &m_server_handles[provisioned_devices]));
                provisioned_devices++;
            }
            else if (address.type == NRF_MESH_ADDRESS_TYPE_GROUP)
            {
                ERROR_CHECK(dsm_address_handle_get(&address, &m_group_handle));
            }
        }
    
        return provisioned_devices;
    }
    
    /**
     * Gets the number of configured devices.
     *
     * We exploit the fact that the publish address of the Simple OnOff clients is set at configuration
     * complete, i.e., in the `provisioner_config_successful_cb()`, and simply count the number of
     * clients with their publish address' set.
     */
    static uint16_t configured_devices_count_get(void)
    {
        uint16_t configured_devices = 0;
        for (uint32_t i = 0; i < SERVER_COUNT; ++i)
        {
            dsm_handle_t address_handle = DSM_HANDLE_INVALID;
            if ((access_model_publish_address_get(m_clients[i].model_handle,
                                                  &address_handle) == NRF_SUCCESS)
                && (DSM_HANDLE_INVALID != address_handle))
            {
                configured_devices++;
            }
            else
            {
                /* Clients are configured sequentially. */
                break;
            }
        }
    
        return configured_devices;
    }
    
    static void access_setup(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Setting up access layer and models\n");
    
        dsm_init();
        access_init();
    
        m_netkey_handle = DSM_HANDLE_INVALID;
        m_appkey_handle = DSM_HANDLE_INVALID;
        for (uint32_t i = 0; i < SERVER_COUNT; ++i)
        {
            m_devkey_handles[i] = DSM_HANDLE_INVALID;
            m_server_handles[i] = DSM_HANDLE_INVALID;
        }
        m_group_handle = DSM_HANDLE_INVALID;
    
        /* Initialize and enable all the models before calling ***_flash_config_load. */
        ERROR_CHECK(config_client_init(config_client_event_cb));
        ERROR_CHECK(health_client_init(&m_health_client, 0, health_event_cb));
    
        for (uint32_t i = 0; i < CLIENT_COUNT; ++i)
        {
            m_clients[i].status_cb = client_status_cb;
            ERROR_CHECK(simple_on_off_client_init(&m_clients[i], i));
        }
    
        if (dsm_flash_config_load())
        {
            m_provisioned_devices = provisioned_device_handles_load();
        }
        else
        {
            /* Set and add local addresses and keys, if flash recovery fails. */
            dsm_local_unicast_address_t local_address = {PROVISIONER_ADDRESS, ACCESS_ELEMENT_COUNT};
            ERROR_CHECK(dsm_local_unicast_addresses_set(&local_address));
            ERROR_CHECK(dsm_address_publish_add(GROUP_ADDRESS, &m_group_handle));
            ERROR_CHECK(dsm_subnet_add(0, m_netkey, &m_netkey_handle));
            ERROR_CHECK(dsm_appkey_add(0, m_netkey_handle, m_appkey, &m_appkey_handle));
        }
    
        if (access_flash_config_load())
        {
            m_configured_devices = configured_devices_count_get();
        }
        else
        {
            /* Bind the keys to the health client. */
            ERROR_CHECK(access_model_application_bind(m_health_client.model_handle, m_appkey_handle));
            ERROR_CHECK(access_model_publish_application_set(m_health_client.model_handle, m_appkey_handle));
    
            /* Bind the keys to the Simple OnOff clients. */
            for (uint32_t i = 0; i < SERVER_COUNT; ++i)
            {
                ERROR_CHECK(access_model_application_bind(m_clients[i].model_handle, m_appkey_handle));
                ERROR_CHECK(access_model_publish_application_set(m_clients[i].model_handle, m_appkey_handle));
            }
    
            ERROR_CHECK(access_model_application_bind(m_clients[GROUP_CLIENT_INDEX].model_handle, m_appkey_handle));
            ERROR_CHECK(access_model_publish_application_set(m_clients[GROUP_CLIENT_INDEX].model_handle, m_appkey_handle));
            ERROR_CHECK(access_model_publish_address_set(m_clients[GROUP_CLIENT_INDEX].model_handle, m_group_handle));
            access_flash_config_store();
        }
    
        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);
        }
    }
    
    static uint32_t server_index_get(const simple_on_off_client_t * p_client)
    {
        uint32_t index = (((uint32_t) p_client - ((uint32_t) &m_clients[0]))) / sizeof(m_clients[0]);
        NRF_MESH_ASSERT(index < SERVER_COUNT);
        return index;
    }
    
    static void client_status_cb(const simple_on_off_client_t * p_self, simple_on_off_status_t status, uint16_t src)
    {
        uint32_t server_index = server_index_get(p_self);
        switch (status)
        {
            case SIMPLE_ON_OFF_STATUS_ON:
                hal_led_pin_set(BSP_LED_0 + server_index, true);
                break;
    
            case SIMPLE_ON_OFF_STATUS_OFF:
                hal_led_pin_set(BSP_LED_0 + server_index, false);
                break;
    
            case SIMPLE_ON_OFF_STATUS_ERROR_NO_REPLY:
                hal_led_blink_ms(LEDS_MASK, 100, 6);
                break;
    
            default:
                NRF_MESH_ASSERT(false);
                break;
        }
    
        /* Set 4th LED on when all servers are on. */
        bool all_servers_on = true;
        for (uint32_t i = BSP_LED_0; i < BSP_LED_0 + m_configured_devices; ++i)
        {
            if (!hal_led_pin_get(i))
            {
                all_servers_on = false;
                break;
            }
        }
    
        hal_led_pin_set(BSP_LED_3, all_servers_on);
    }
    
    static void health_event_cb(const health_client_t * p_client, const health_client_evt_t * p_event)
    {
        switch (p_event->type)
        {
            case HEALTH_CLIENT_EVT_TYPE_CURRENT_STATUS_RECEIVED:
                __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Node 0x%04x alive with %u active fault(s), RSSI: %d\n",
                      p_event->p_meta_data->src.value, p_event->data.fault_status.fault_array_length,
                      p_event->p_meta_data->rssi);
                break;
            default:
                break;
        }
    }
    
    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;
    
        }
    
        if (status == NRF_ERROR_INVALID_STATE ||
            status == NRF_ERROR_NO_MEM ||
            status == NRF_ERROR_BUSY)
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Cannot send. Device is busy.\n");
            hal_led_blink_ms(LEDS_MASK, 50, 4);
        }
        else
        {
            ERROR_CHECK(status);
        }
    }
    
    /*****************************************************************************
     * Event callbacks from the provisioner
     *****************************************************************************/
    
    void provisioner_config_successful_cb(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Configuration of device %u successful\n", m_configured_devices);
    
        /* Set publish address for the client to the corresponding server. */
        ERROR_CHECK(access_model_publish_address_set(m_clients[m_configured_devices].model_handle,
                                                     m_server_handles[m_configured_devices]));
        access_flash_config_store();
    
        hal_led_pin_set(BSP_LED_0 + m_configured_devices, false);
        m_configured_devices++;
    
        if (m_configured_devices < SERVER_COUNT)
        {
            provisioner_wait_for_unprov(UNPROV_START_ADDRESS + m_provisioned_devices);
            hal_led_pin_set(BSP_LED_0 + m_configured_devices, true);
        }
        else
        {
            __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "All servers provisioned\n");
            hal_led_blink_ms(LEDS_MASK, 100, 4);
        }
    }
    
    void provisioner_config_failed_cb(void)
    {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Configuration of device %u failed\n", m_configured_devices);
    
        /* Delete key and address. */
        ERROR_CHECK(dsm_address_publish_remove(m_server_handles[m_configured_devices]));
        ERROR_CHECK(dsm_devkey_delete(m_devkey_handles[m_configured_devices]));
        provisioner_wait_for_unprov(UNPROV_START_ADDRESS + m_provisioned_devices);
    }
    
    void provisioner_prov_complete_cb(const nrf_mesh_prov_evt_complete_t * p_prov_data)
    {
        /* We should not get here if all servers are provisioned. */
        NRF_MESH_ASSERT(m_configured_devices < SERVER_COUNT);
    
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Provisioning complete. Adding address 0x%04x.\n", p_prov_data->address);
    
        /* Add to local storage. */
        ERROR_CHECK(dsm_address_publish_add(p_prov_data->address, &m_server_handles[m_provisioned_devices]));
        ERROR_CHECK(dsm_devkey_add(p_prov_data->address, m_netkey_handle, p_prov_data->p_devkey, &m_devkey_handles[m_provisioned_devices]));
    
        /* Bind the device key to the configuration server and set the new node as the active server. */
        ERROR_CHECK(config_client_server_bind(m_devkey_handles[m_provisioned_devices]));
        ERROR_CHECK(config_client_server_set(m_devkey_handles[m_provisioned_devices],
                                             m_server_handles[m_provisioned_devices]));
    
        m_provisioned_devices++;
    
        /* Move on to the configuration step. */
        provisioner_configure(UNPROV_START_ADDRESS + m_configured_devices);
    }
    
    static void rtt_input_handler(int key)
    {
        if (key >= '0' && key <= '3')
        {
            uint32_t button_number = key - '0';
            button_event_handler(button_number);
        }
    }
    
    int main(void)
    {
        uint32_t ret_code;
    
        __LOG_INIT(LOG_SRC_APP | LOG_SRC_ACCESS, LOG_LEVEL_INFO, LOG_CALLBACK_DEFAULT);
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "----- BLE Mesh Light Switch Client Demo -----\n");
    
        hal_leds_init();
        ERROR_CHECK(hal_buttons_init(button_event_handler));
    
        /* Set the first LED */
        hal_led_pin_set(BSP_LED_0, true);
        mesh_core_setup();
        access_setup();
        rtt_input_enable(rtt_input_handler, RTT_INPUT_POLL_PERIOD_MS);
    
        /* Adding custom data to flash for persistent storage */
    
        // 1) Flash manager is already initialized
    
        // 2) Add a new flash manager instance. NB: should not overlap (in region) the instance used by mesh.  
    
        flash_manager_config_t custom_data_manager_config;
        custom_data_manager_config.write_complete_cb = NULL; 
        custom_data_manager_config.invalidate_complete_cb = NULL; 
        custom_data_manager_config.remove_complete_cb = NULL; 
        custom_data_manager_config.min_available_space = WORD_SIZE;
    
        // The new instance of flash manager should use an unused region of flash:
        custom_data_manager_config.p_area = (const flash_manager_page_t *) (((const uint8_t *) dsm_flash_area_get()) - (ACCESS_FLASH_PAGE_COUNT * PAGE_SIZE) - (NET_FLASH_PAGE_COUNT * PAGE_SIZE) );
        
        custom_data_manager_config.page_count = CUSTOM_DATA_FLASH_PAGE_COUNT;
       
        ret_code = flash_manager_add(&m_custom_data_flash_manager, &custom_data_manager_config);
       
        if (NRF_SUCCESS != ret_code) {
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "Flash error: no memory\n",ret_code);
        
        }
       
        
        // 3) Write to Flash
    
        // a) allocate flash 
        fm_entry_t * p_entry = flash_manager_entry_alloc(&m_custom_data_flash_manager, FLASH_CUSTOM_DATA_GROUP_ELEMENT, sizeof(custom_data_format_t));
        if (p_entry == NULL)
        {
          return NRF_ERROR_BUSY;
        }
          else
        {
           
          custom_data_format_t * p_custom_data = (custom_data_format_t *) p_entry->data;
          p_custom_data->data[0] = 5;
          p_custom_data->data[1] = 9;
       
          // b) write to flash
          flash_manager_entry_commit(p_entry);
          __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "write:%x, %x\n",p_entry->data[0], p_entry->data[1]);
          
        }
        
        
        // 4) Wait for flash manager to finish.
        flash_manager_wait();
       
       
        
        // 5) Read from Flash
        const fm_entry_t * p_read_raw = flash_manager_entry_get(&m_custom_data_flash_manager, FLASH_CUSTOM_DATA_GROUP_ELEMENT);
        
        const custom_data_format_t * p_read_data = (const custom_data_format_t *) p_read_raw->data;
        __LOG(LOG_SRC_APP, LOG_LEVEL_INFO, "read:%x, %x\n",p_read_data->data[0], p_read_data->data[1]);
        
    
    
    
        while (true)
        {
            (void)sd_app_evt_wait();
        }
    }
    

     

    The code is provided as-is, without any warranty.

Children
Related