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); }