Good day!
I need to save sensor configs in persistent storage. What is better to use for this purpose: flash manager from nRF Mesh SDK or fstorage from nRF SDK?
I tried flash manager:
Initialization:
static uint8_t bsec_state[FLASH_MANAGER_ENTRY_MAX_SIZE] = {0};
static uint32_t bsec_state_len;
#define FLASH_CUSTOM_DATA_GROUP_ELEMENT 0x1500 // A number in the range 0x0000 - 0x7EFF (flash_manager.h)
#define CUSTOM_DATA_FLASH_PAGE_COUNT (1)
static flash_manager_t m_custom_data_flash_manager;
static fm_entry_t *p_entry;
static bool bsec_mem_allocated = false;
static bool bsec_persistent_flash_init(void)
{
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
mesh_config_backend_flash_usage_t tmp_f_usage;
mesh_config_backend_flash_usage_get(&tmp_f_usage);
//custom_data_manager_config.p_area = (const flash_manager_page_t *)((const uint8_t *)flash_manager_recovery_page_get() - (ACCESS_FLASH_PAGE_COUNT * PAGE_SIZE) - (DSM_FLASH_PAGE_COUNT * PAGE_SIZE));
//custom_data_manager_config.p_area = (const flash_manager_page_t *)flash_manager_recovery_page_get();
custom_data_manager_config.p_area = (const flash_manager_page_t *)tmp_f_usage.p_start;
custom_data_manager_config.page_count = CUSTOM_DATA_FLASH_PAGE_COUNT;
uint32_t ret_code = flash_manager_add(&m_custom_data_flash_manager, &custom_data_manager_config);
if (NRF_SUCCESS != ret_code) {
DEBUG_LOG("Flash init error %d: no memory\n",ret_code);
return false;
}
bsec_state_len = sizeof(bsec_state);
ret_code = flash_manager_entry_read(&m_custom_data_flash_manager, FLASH_CUSTOM_DATA_GROUP_ELEMENT, (void *)bsec_state, &bsec_state_len);
if (NRF_SUCCESS == ret_code) {
DEBUG_LOG("Stored data found and succesfully uploaded\n");
DEBUG_LOG("Test vals %d %d\n", bsec_state[0], bsec_state[1]);
return true;
}
DEBUG_LOG("No previous states found, allocating memory...\n");
p_entry = flash_manager_entry_alloc(&m_custom_data_flash_manager, FLASH_CUSTOM_DATA_GROUP_ELEMENT, bsec_state_len);
if (p_entry == NULL) {
DEBUG_LOG("Flash allocate error\n");
return false;
}
flash_manager_wait();
bsec_mem_allocated = true;
return true;
}
int main(void)
{
initialize();
start();
bsec_persistent_flash_init();
for (;;)
(void) sd_app_evt_wait();
}
As you can see commented lines 21 and 22 I tried different variants of initialization. Line 21 didn't work at all. Other lines starts fine and crashes while provisioning.
The questions are:
- What is better to use for this purpose: flash manager from nRF Mesh SDK or fstorage from nRF SDK?
- If Flash manager is prefered how to initialize it?
- How to check at startup that data was already stored?
nRF SDK for Mesh v 4.0, nRF SDK 16, nRF52840 (Linux, SeS)
UPD 24/04/20:
I've tried FDS solution. Fail.
static uint8_t bsec_state[FLASH_MANAGER_ENTRY_MAX_SIZE] = {0};
static uint32_t bsec_state_len = sizeof(bsec_state);
#define FLASH_CUSTOM_DATA_GROUP_ELEMENT 0x1500 // A number in the range 0x0000 - 0x7EFF (flash_manager.h)
#define CUSTOM_DATA_FLASH_PAGE_COUNT (1)
//static flash_manager_t m_custom_data_flash_manager;
//static fm_entry_t *p_entry;
static bool bsec_mem_allocated = false;
static uint8_t write_flag = 0;
static void my_fds_evt_handler(fds_evt_t const * const p_fds_evt)
{
switch (p_fds_evt->id)
{
case FDS_EVT_INIT:
if (p_fds_evt->result != NRF_SUCCESS)
{
// Initialization failed.
}
break;
case FDS_EVT_WRITE:
if (p_fds_evt->result == NRF_SUCCESS)
{
write_flag=1;
}
break;
default:
break;
}
}
static ret_code_t fds_test_write(void)
{
#define FILE_ID 0x1111
#define REC_KEY 0x2222
static uint32_t const m_deadbeef[2] = {0xDEADBEEF,0xBAADF00D};
fds_record_t record;
fds_record_desc_t record_desc;
//fds_record_chunk_t record_chunk;
// Set up data.
//record_chunk.p_data = m_deadbeef;
//record_chunk.length_words = 2;
// Set up record.
record.file_id = FILE_ID;
record.key = REC_KEY;
record.data.p_data = bsec_state;
record.data.length_words = (sizeof(bsec_state)/WORD_SIZE);
ret_code_t ret = fds_record_write(&record_desc, &record);
if (ret != NRF_SUCCESS)
{
return ret;
}
DEBUG_LOG("Writing Record ID = %d \r\n",record_desc.record_id);
return NRF_SUCCESS;
}
static ret_code_t fds_read(void)
{
#define FILE_ID 0x1111
#define REC_KEY 0x2222
fds_flash_record_t flash_record;
fds_record_desc_t record_desc;
fds_find_token_t ftok ={0};//Important, make sure you zero init the ftok token
uint8_t *data;
uint32_t err_code;
DEBUG_LOG("Start searching... \r\n");
// Loop until all records with the given key and file ID have been found.
while (fds_record_find(FILE_ID, REC_KEY, &record_desc, &ftok) == NRF_SUCCESS)
{
err_code = fds_record_open(&record_desc, &flash_record);
if ( err_code != NRF_SUCCESS)
{
return err_code;
}
DEBUG_LOG("Found Record ID = %d\r\n",record_desc.record_id);
DEBUG_LOG("Data = ");
data = (uint8_t *) flash_record.p_data;
for (uint8_t i=0;i<bsec_state_len;i++)
{
DEBUG_LOG("0x%8x \n",data[i]);
}
// Access the record through the flash_record structure.
// Close the record when done.
err_code = fds_record_close(&record_desc);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
return NRF_SUCCESS;
}
static ret_code_t fds_test_find_and_delete (void)
{
#define FILE_ID 0x1111
#define REC_KEY 0x2222
fds_record_desc_t record_desc;
fds_find_token_t ftok;
ftok.page=0;
ftok.p_addr=NULL;
// Loop and find records with same ID and rec key and mark them as deleted.
while (fds_record_find(FILE_ID, REC_KEY, &record_desc, &ftok) == NRF_SUCCESS)
{
fds_record_delete(&record_desc);
DEBUG_LOG("Deleted record ID: %d \r\n",record_desc.record_id);
}
// call the garbage collector to empty them, don't need to do this all the time, this is just for demonstration
ret_code_t ret = fds_gc();
if (ret != NRF_SUCCESS)
{
return ret;
}
return NRF_SUCCESS;
}
static ret_code_t fds_test_init (void)
{
ret_code_t ret = fds_register(my_fds_evt_handler);
if (ret != NRF_SUCCESS)
{
return ret;
}
ret = fds_init();
if (ret != NRF_SUCCESS)
{
return ret;
}
return NRF_SUCCESS;
}
UPD 24/04/20
I increased this value in sdk_config.h #define FDS_VIRTUAL_PAGES 8 and application started... and crashed after about 15 minutes.
So question is still opened
UPD 25/05/20
Solution based on Mesh config entry is working! That's great! But now I have new question:
what is the proper way to store kind of blob, 512bytes for example represented as array?
My solution:
/*** Save \ load state functions ***/
static uint32_t state_setter(mesh_config_entry_id_t id, const void * p_entry);
static void state_getter(mesh_config_entry_id_t id, void * p_entry);
#define LIB_STATE_FILE_ID 0x0220
MESH_CONFIG_FILE(m_state_file, LIB_STATE_FILE_ID, MESH_CONFIG_STRATEGY_CONTINUOUS);
enum
{
LIB_STATE_RECORD_1 = 0x0001,
LIB_STATE_RECORD_2,
LIB_STATE_RECORD_3,
LIB_STATE_RECORD_4,
LIB_STATE_RECORD_5,
LIB_STATE_RECORD_6,
LIB_STATE_RECORD_7,
LIB_STATE_RECORD_END
};
#define LIB_STATE_C1_ENTRY_ID MESH_CONFIG_ENTRY_ID(LIB_STATE_FILE_ID, LIB_STATE_RECORD_1)
#define LIB_STATE_C2_ENTRY_ID MESH_CONFIG_ENTRY_ID(LIB_STATE_FILE_ID, LIB_STATE_RECORD_2)
#define LIB_STATE_C3_ENTRY_ID MESH_CONFIG_ENTRY_ID(LIB_STATE_FILE_ID, LIB_STATE_RECORD_3)
#define LIB_STATE_C4_ENTRY_ID MESH_CONFIG_ENTRY_ID(LIB_STATE_FILE_ID, LIB_STATE_RECORD_4)
#define LIB_STATE_C5_ENTRY_ID MESH_CONFIG_ENTRY_ID(LIB_STATE_FILE_ID, LIB_STATE_RECORD_5)
#define LIB_STATE_C6_ENTRY_ID MESH_CONFIG_ENTRY_ID(LIB_STATE_FILE_ID, LIB_STATE_RECORD_6)
#define LIB_STATE_C7_ENTRY_ID MESH_CONFIG_ENTRY_ID(LIB_STATE_FILE_ID, LIB_STATE_RECORD_7)
#define LIB_STATE_C8_ENTRY_ID MESH_CONFIG_ENTRY_ID(LIB_STATE_FILE_ID, LIB_STATE_RECORD_END)
MESH_CONFIG_ENTRY(m_lib_state_1,
LIB_STATE_C1_ENTRY_ID,
1,
STATE_CHUNK_SIZE,
state_setter,
state_getter,
NULL, //deleter is unnecessary
false);
/** SAME FOR ALL OTHERS **/
MESH_CONFIG_ENTRY(m_lib_state_8,
LIB_STATE_C8_ENTRY_ID,
1,
LAST_CHUNK_SIZE,
state_setter,
state_getter,
NULL, //deleter is unnecessary
false);
static uint32_t state_setter(mesh_config_entry_id_t id, const void * p_entry)
{
NRF_MESH_ASSERT_DEBUG(LIB_STATE_FILE_ID == id.file);
uint8_t *tmp_p = (uint8_t *)p_entry;
uint8_t i = (id.record - 1);
if((LIB_STATE_RECORD_END-1) != i) { //all chunks are full size except last one
if (memcmp((p_state+i*STATE_CHUNK_SIZE), tmp_p, STATE_CHUNK_SIZE) != 0) {
memcpy((p_state+i*STATE_CHUNK_SIZE), tmp_p, STATE_CHUNK_SIZE);
}
} else {
if (memcmp((p_state+i*STATE_CHUNK_SIZE), tmp_p, LAST_CHUNK_SIZE) != 0) {
memcpy((p_state+i*STATE_CHUNK_SIZE), tmp_p, LAST_CHUNK_SIZE);
}
}
return NRF_SUCCESS;
}
uint32_t state_load(uint8_t *state_buffer)
{
mesh_config_entry_id_t id;
id.file = LIB_STATE_FILE_ID;
for (uint8_t i = 0; i < LIB_STATE_RECORD_END; i++) {
id.record = (i+1);
mesh_config_entry_get(id, (state_buffer + STATE_CHUNK_SIZE * i));
}
/* Return array length */
if (state[0] == 0) // if first byte is empty - whole buffer is empty < app specific
return 0;
return (i*STATE_CHUNK_SIZE + LAST_CHUNK_SIZE);
}