nvs_write Hangs in NVS Sample When BT Added to Project Configuration

I created a new project based on the nvs sample template under nRF Connect v2.0.2 for an nRF DK (PCA10040) board.  The source code looks like this:

#include <zephyr.h>

#include <sys/reboot.h>
#include <device.h>
#include <string.h>
#include <drivers/flash.h>
#include <storage/flash_map.h>
#include <fs/nvs.h>

#define STORAGE_NODE_LABEL storage

#include <zephyr/logging/log.h>

#define LOG_MODULE_NAME peripheral_uart
LOG_MODULE_REGISTER(LOG_MODULE_NAME);


static struct nvs_fs fs;
#define STORAGE_NODE_LABEL storage
#define BT_AD_ID 1
static K_SEM_DEFINE(sem_nvs_write, 0, 1);
static struct{
  uint16_t  id;
  uint8_t*  data;
  uint32_t  data_length;
}nvs_param;

#define NVS_THREAD_STACKSIZE 512
#define NVS_THREAD_PRIORITY    5

void nvs_write_thread(void)
{
    while(1)
    {
      k_sem_take(&sem_nvs_write, K_FOREVER);
      if (nvs_write(&fs, nvs_param.id, nvs_param.data, nvs_param.data_length) < 0)
        LOG_ERR("Couldn't write data to NVS\n");
      else
        LOG_INF("data written to NVS");
    }  
}

K_THREAD_DEFINE(nvs_write_thread_id, NVS_THREAD_STACKSIZE, nvs_write_thread, NULL, NULL, NULL, NVS_THREAD_PRIORITY, 0, 0);

void main(void)
{
    int rc = 0;
    char buf[16];
    struct flash_pages_info info;

    fs.flash_device = FLASH_AREA_DEVICE(STORAGE_NODE_LABEL);
    if (!device_is_ready(fs.flash_device)) {
        printk("Flash device %s is not ready\n", fs.flash_device->name);
        return;
    }
    fs.offset = FLASH_AREA_OFFSET(storage);
    rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info);
    if (rc) {
        printk("Unable to get page info\n");
        return;
    }
    fs.sector_size = info.size;
    fs.sector_count = 3U;

    rc = nvs_mount(&fs);
    if (rc) {
        printk("Flash Init failed\n");
        return;
    }

    nvs_delete(&fs, BT_AD_ID);
    while(1)
    {
 
        rc = nvs_read(&fs, BT_AD_ID, &buf, sizeof(buf));
        if (rc > 0) { /* item was found, show it */
            printk("Id: %d, BT Advertising string: %s\n", BT_AD_ID, buf);
			break;
        } else   {/* item was not found, add it */
            strcpy(buf, "nvsTest");
            printk("No ad string found, adding %s at id %d\n", buf,
                BT_AD_ID);
            nvs_param.id = BT_AD_ID;      
            nvs_param.data = buf;
            nvs_param.data_length = strlen(buf) + 1;                
            k_sem_give(&sem_nvs_write);
            k_msleep(1000);
        }
    }
}  

If I set a breakpoint at LOG_INF("data written to NVS"); in nvs_write_thread, I find that hits the breakpoint when I run VSC in Debug mode, as it should.
I then tried to run the same nvs_write_thread in code based on the peripheral_uart sample.  The Bluetooth receive callback bt_receive_cb in that code receives a command (ad=) to set the advertising string, which stores it in a buffer (bt_ad_str) then signals the nvs_write_thread to store it in NVS, so that the device's Bluetooth will start advertising with the new string even if the device is powered off and back on:

else if (memcmp(data, "ad=", sizeof("ad=")-1) == 0)
{
     snprintf(bt_ad_str, sizeof(bt_ad_str), "%s", &data[sizeof("ad=")-1]);
     nvs_param.id = BT_AD_ID;
     nvs_param.data = bt_ad_str;
     nvs_param.data_length = strlen(bt_ad_str) + 1;
     k_sem_give(&sem_nvs_write);
                   : 
The problem is the code hangs on nvs_write in the nvs_write_thread.  I found that this happens when CONFIG_BT=y is added to prj.conf for the nvs sample based code above and is presumably what is causing the problem with the peripheral_uart sample.
There appears to be some incompatibility between NVS and Bluetooth.  Is there some way I can get this to work? 

I would appreciate any advice you can give on this.

Alec 
  • Can't get peripheral_lbs_uart to build under nRF Connect 2.0.2.  I did try adding these lines in that source file (main.c) to my code:

    #define STORAGE_NODE DT_NODE_BY_FIXED_PARTITION_LABEL(userstorage)
    #define FLASH_NODE DT_MTD_FROM_FIXED_PARTITION(STORAGE_NODE)

    and also added nrf52dk_nrf52832.overlay for that project to mine.  What I get is this error when I try to build it:

    What is causing this?
  • I had to use the template for peripheral_lbs in nRF Connect 2.0.2 and merge in the nvs code in your source and the nvs lines in your proj.conf file, then change nvs_init to nvs_mount in main.c as the former had been deprecated.  I also copied nrf52dk_nrf52832.overlay to the project.  All of this was necessary to get it to build.

    What I found in debugging was the code would hang in nvs_mount, specifically as it tries to get the flash parameters.  The addresses for the nvs driver (soc_flash_nrf.c) functions are outside the addressable range of the MCU.  For example, according to zephyr.map, flash_nrf_get_parameters is supposed to be at 0x000000000001e160:  

    .text.flash_nrf_get_parameters
    0x000000000001e160 0x8 zephyr/drivers/flash/libdrivers__flash.a(soc_flash_nrf.c.obj)

    I have confirmed through the disassembly listing, that is the case.  However, in the flash.h z_impl_flash_get_parameters function, if I hover over api while stepping through the code in the debugger, I see its address is 0x8477020. 

    It looks like the issue is with the device driver.  In the nvs sample without Bluetooth enabled, which mounts successfully, I see the name of the device driver is "NRF_FLASH_DRV_NAME" while for this example with includes both Bluetooth and NVS, the name is "\035!\002".  It's as if the device driver could not be found in the device tree and garbage is being used.

    I'm not sure if the problem is in one of these macros in main.c

    #define STORAGE_NODE DT_NODE_BY_FIXED_PARTITION_LABEL(userstorage)
    #define FLASH_NODE DT_MTD_FROM_FIXED_PARTITION(STORAGE_NODE)

    or if I'm missing something.  Could you point me in the right direction?
    I've attached my source code.
    Thanks,
    Alec

    8168.peripheral_lbs_nvs.zip

  • The hang in nvs_mount was due to the peripheral_lbs_nvs code you had me try.  Here's a snippet from its main.c:


    flash_dev = DEVICE_DT_GET(FLASH_NODE);    
    if (!device_is_ready(flash_dev)) {        
         printk("Flash device %s is not ready\n",
    flash_dev->name);
    return;    
    }    
    fs.offset = FLASH_AREA_OFFSET(userstorage);    
    rc = flash_get_page_info_by_offs(flash_dev, fs.offset, &info);    
    if (rc) {        
        printk("Unable to get page info\n");
        return;    
    }    
    fs.sector_size = info.size;    
    fs.sector_count = 2U;    
    printk("Before nvs_init() from app\n");

    No where is fs.flash_device set to flash_dev prior to nvs_mount (for this older code, nvs_init).

    That's why the device does not show up as "NRF_FLASH_DRV_NAME".


Related