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
  • is right. If you want to store some variables and update them from time to time, I suggest that you look into FDS. You may not be aware that the flash can only written to once. Then you need to erase entire flash pages before you write to them again. This means that you can't just update a specific address any number of times. FDS will handle the placement of the flash records for you.

Children
Related