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

write/read to the flash

Hi,

I am currently using SEGGER Embedded Studio version  4.12, nRF5 SDK version15.2.0,  nRF5 SDK for Mesh version 3.1.0,   Hardware BMD-300 Evaluation Board.

i using the uart to send data over the ble mesh and all work fine. now i want to write/read user data to/from the flash from specific address that i picked.

i found that code for write and read to/from the flash

/* 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();
    }
}

this code work fine, but i didn't understand how it pick the address and where it put the address(which variable)? and how i pick and where i put address that i want? and how i know that the address that i pick it a "good" address?

thanks! 

Parents Reply Children
No Data
Related