Maximising littlefs flash storage space

I'm working on a project that uses BLE, NVS storage (for some serial number data and a few other variables) and littlefs, for storing log data that a client can then access over bluetooth via SMP.  I'm on nRF Connect v2.0.0 and am using an nRF52832.

But I'm coming into some problems with making maximum use of the available NVS storage for my information.  At the moment, this is how my static partitions are defined:

settings_storage:
  address: 0x7a000
  size: 0x2000
  placement:
    before: 
    - user_storage
  region: flash_primary
user_storage:
  address: 0x7c000
  size: 0x2000
  placement:
    before:
    - littlefs_storage
  region: flash_primary
littlefs_storage:
  address: 0x7e000
  size: 0x2000
  placement:
    before: 
    - end
  region: flash_primary

Ideally, I want to maximise the amount of littlefs storage, and reduce the settings and user storage to an absolute minimum.  But the user_storage needs at least 2 sectors, each of 4kB (a page) in size, even if I only need to store 200 bytes of info.  Similarly, it seems the settings area needs a similar amount as a minimum, because the moment I try and reduce that, my BLE functionality stops working.  This is only leaving me 8kB of littlefs storage.

Reading this, it seems its possible to combine the settings and user storage areas into one, which potentially would free up 8kB.  But I don't believe this has been implemented in v2.0.0, so I may need to upgrade to a newer SDK (I think its available in v2.1.2).  Question I have though is how do I actually implement this in my code?  I've just followed the nvs example to set up my user storage in my code, so it looks something like this:

static struct nvs_fs fs;

int16_t flash_initialise(void)
{
    int16_t rc;

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

	fs.offset = FLASH_AREA_OFFSET(user_storage);
	rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info);
	if (rc !=0) 
	{
			printk("Unable to get page info\n");
		return rc = -EINVAL;
	}

	fs.sector_size = info.size;
	fs.sector_count = NVS_SECTOR_COUNT;

	if(fs.sector_size*fs.sector_count > (FLASH_AREA_SIZE(user_storage)))
	{
		printk("ERROR: Area used by NVS is larger than user storage\n");
	}

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

Note that STORAGE_NODE_LABEL = settings_storage.

Alternatively, this seems to be indicating its possible to combine nvs and littlefs partitions into one.  It doesn't really elaborate on how to do this though.  Is this an easier option than trying to combine settings and user storage?

Are there any other approaches I can use to reduce the amount of flash taken up by the settings and user storage, and hence maximise the amount of littlefs storage space I have?

Cheers,

Mike

Parents
  • Hi Mike, 

    If you had a look at Simon example in the case you pointed to you can see that he added a patch to the Zephy's settings library to add this function get_nvs_fs_structure() 

    It's to to get the instance of nvs used by the settings module so that you can store your own nvs data. 

    In newer SDK (my test v2.1.2) this patch has already been implemented. You can call settings_nvs_storage_get() to get the nvs instance used by settings. After that you can nvs_mount the nvs instance that you get from the function. Please have a look at the example by Simon. 
    This way you don't need to have an extra user_settings and can save some space. 

    In the second ticket that you referred to I don't think the customer merged littlefs and nvs to use one partition, but it's more about not using nvs at all and simply use littlefs to store data instead of using nvs. 

    If you need more space for your own data, have you considered to move the settings partition lower in flash for example 0x70000 instead of 0x7a000 ? Of course the trade off is that you will have smaller MCUBoot primary and secondary partitions.

Reply
  • Hi Mike, 

    If you had a look at Simon example in the case you pointed to you can see that he added a patch to the Zephy's settings library to add this function get_nvs_fs_structure() 

    It's to to get the instance of nvs used by the settings module so that you can store your own nvs data. 

    In newer SDK (my test v2.1.2) this patch has already been implemented. You can call settings_nvs_storage_get() to get the nvs instance used by settings. After that you can nvs_mount the nvs instance that you get from the function. Please have a look at the example by Simon. 
    This way you don't need to have an extra user_settings and can save some space. 

    In the second ticket that you referred to I don't think the customer merged littlefs and nvs to use one partition, but it's more about not using nvs at all and simply use littlefs to store data instead of using nvs. 

    If you need more space for your own data, have you considered to move the settings partition lower in flash for example 0x70000 instead of 0x7a000 ? Of course the trade off is that you will have smaller MCUBoot primary and secondary partitions.

Children
  • Hi Hung,

    Thanks for input.  I did have a look through that example, but there was a fair bit going on around the flash memory stuff, and I was struggling to isolate what was relevant, and what wasn't.

    Anyway, I've made some progress.  Had to upgrade to v2.1.2, which isn't without its challenges.

    I now have the following flash initialisation, which I've effectively copied from Simons example:

    #include <drivers/flash.h>
    #include <fs/nvs.h>
    #include <storage/flash_map.h>
    #include <settings/settings_nvs.h>
    
    const struct device *flash_dev;
    static struct nvs_fs *fs;
    
    struct config_nvs 
    {
    	struct nvs_fs *cf_nvs;
    	uint16_t last_name_id;
    	const char *flash_dev_name;
    };
    
    int16_t nvs_config_init(void)
    {
       int rc = 0;
    	/*  Define the nvs file system by settings with:
    	 *	sector_size equal to the pagesize,
    	 *	2 sectors
    	 *	starting at FLASH_AREA_OFFSET(storage)
    	 */
       static struct config_nvs default_config_nvs;
       const struct flash_area *fa;
       struct flash_sector hw_flash_sector;
       uint32_t sector_cnt = 1;
       uint16_t cnt = 0;
       size_t nvs_sector_size, nvs_size = 0;
    
       rc = flash_area_open(STORAGE_NODE_LABEL, &fa);
       if (rc) 
       {
    	  	#ifdef DEBUG_NVS
    			printk("ERROR: Can't open flash area\n");
    		#endif
          	return -1;
       }
    
       rc = flash_area_get_sectors(STORAGE_NODE_LABEL, &sector_cnt, &hw_flash_sector);
       if (rc == -ENODEV) 
       {
    	  	#ifdef DEBUG_NVS
    			printk("ERROR: No device\n");
    		#endif
    		return -1;
       } 
       else if (rc != 0 && rc != -ENOMEM) 
       {
    		#ifdef DEBUG_NVS
    			printk("ERROR: No memory\n");
    		#endif
        	return -1;
       }
       else
       {
          	#ifdef DEBUG_NVS
    			printk("INFO: Sector info success\n");
    		#endif
       }
    
    	nvs_sector_size = CONFIG_SETTINGS_NVS_SECTOR_SIZE_MULT * hw_flash_sector.fs_size;
    
    	if (nvs_sector_size > UINT16_MAX) 
       {
        	#ifdef DEBUG_NVS
    			printk("ERROR: Area used by NVS is larger than available\n");
    		#endif
    		return -1;
    	}
    
    	while (cnt < CONFIG_SETTINGS_NVS_SECTOR_COUNT) 
       {
    	   nvs_size += nvs_sector_size;
    	   if (nvs_size > fa->fa_size) 
    	   {
    		 	break;
    	   }
    		cnt++;
    	}
    
    	default_config_nvs.cf_nvs = settings_nvs_storage_get();
    	default_config_nvs.flash_dev_name = fa->fa_dev->name;
    
    	#ifdef DEBUG_NVS
    		printk("INFO: Flash offset %d sector size %d cnt %d", fa->fa_off, nvs_sector_size, cnt);
    	#endif
    
    	default_config_nvs.cf_nvs->flash_device = device_get_binding(default_config_nvs.flash_dev_name);
    	if (default_config_nvs.cf_nvs->flash_device == NULL) 
       	{
    		#ifdef DEBUG_NVS
     			printk("ERROR: Flash device %s is not ready\n", default_config_nvs.flash_dev_name);
     		#endif
    		return -1;
    	}
    
    	rc = nvs_mount(default_config_nvs.cf_nvs);
    	if (rc) 
       	{
    		#ifdef DEBUG_NVS
     			printk("ERROR: Flash mount failed\n");
     		#endif
    		return -1;
       	}
        fs = default_config_nvs.cf_nvs;
    
       	// Check if data is stored in flash
    	rc = check_for_nvs_key();
    	return rc;
    }

    However, I am having issues with the call to 

    default_config_nvs.cf_nvs = settings_nvs_storage_get();
    The compiler isn't able to find the API settings_nvs_storage_get(); even though I've got settings/settings_nvs.h included.
    I'm also not clear on what I should be passing to settings_nvs_storage_get(); to get it to return the correct info.
    I may actually be on the totally wrong path here as well!
    Cheers,
    Mike
  • Hi Mike, 

    My bad, settings_nvs_storage_get was a static function for settings_nvs.c 
    You should call 
    settings_storage_get() instead. 
    So the following code seems to work for me: 

    static struct nvs_fs *fs;

        settings_load();
    	settings_storage_get(&fs);
    		rc = nvs_mount(fs);
    		if (rc) {
    			printk("Flash Init failed\n");
    			return;
    		}
    		rc = nvs_read(fs, ADDRESS_ID, &buf, sizeof(buf));
    	if (rc > 0) { /* item was found, show it */
    		printk("Id: %d, Address: %s\n", ADDRESS_ID, buf);
    	} else   {/* item was not found, add it */
    		strcpy(buf, "192.168.1.1");
    		printk("No address found, adding %s at id %d\n", buf,
    		       ADDRESS_ID);
    		(void)nvs_write(fs, ADDRESS_ID, &buf, strlen(buf)+1);
    	}
    	/* KEY_ID is used to store a key, lets see if we can read it from flash
    	 */
    	rc = nvs_read(fs, KEY_ID, &key, sizeof(key));
    	if (rc > 0) { /* item was found, show it */
    		printk("Id: %d, Key: ", KEY_ID);
    		for (int n = 0; n < 8; n++) {
    			printk("%x ", key[n]);
    		}
    		printk("\n");
    	} else   {/* item was not found, add it */
    		printk("No key found, adding it at id %d\n", KEY_ID);
    		key[0] = 0xFF;
    		key[1] = 0xFE;
    		key[2] = 0xFD;
    		key[3] = 0xFC;
    		key[4] = 0xFB;
    		key[5] = 0xFA;
    		key[6] = 0xF9;
    		key[7] = 0xF8;
    		(void)nvs_write(fs, KEY_ID, &key, sizeof(key));
    	}
    	/* RBT_CNT_ID is used to store the reboot counter, lets see
    	 * if we can read it from flash
    	 */
    	rc = nvs_read(fs, RBT_CNT_ID, &reboot_counter, sizeof(reboot_counter));
    	if (rc > 0) { /* item was found, show it */
    		printk("Id: %d, Reboot_counter: %d\n",
    			RBT_CNT_ID, reboot_counter);
    	} else   {/* item was not found, add it */
    		printk("No Reboot counter found, adding it at id %d\n",
    		       RBT_CNT_ID);
    		(void)nvs_write(fs, RBT_CNT_ID, &reboot_counter,
    			  sizeof(reboot_counter));
    	}
    	reboot_counter++;
    	(void)nvs_write(fs, RBT_CNT_ID, &reboot_counter,					sizeof(reboot_counter));
    	 }

    See the modified main.c from peripheral_uart in SDK v2.1.2

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    /** @file
     *  @brief Nordic UART Bridge Service (NUS) sample
     */
    #include "uart_async_adapter.h"
    
    #include <zephyr/types.h>
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/uart.h>
    #include <zephyr/usb/usb_device.h>
    
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <soc.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/hci.h>
    
    #include <bluetooth/services/nus.h>
    
    #include <dk_buttons_and_leds.h>
    
    #include <zephyr/settings/settings.h>
    
    #include <stdio.h>
    
    #include <zephyr/logging/log.h>
    #include <fs/nvs.h>
    #define LOG_MODULE_NAME peripheral_uart
    LOG_MODULE_REGISTER(LOG_MODULE_NAME);
    
    #define STACKSIZE CONFIG_BT_NUS_THREAD_STACK_SIZE
    #define PRIORITY 7
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN	(sizeof(DEVICE_NAME) - 1)
    
    #define RUN_STATUS_LED DK_LED1
    #define RUN_LED_BLINK_INTERVAL 1000
    
    #define CON_STATUS_LED DK_LED2
    
    #define KEY_PASSKEY_ACCEPT DK_BTN1_MSK
    #define KEY_PASSKEY_REJECT DK_BTN2_MSK
    
    #define UART_BUF_SIZE CONFIG_BT_NUS_UART_BUFFER_SIZE
    #define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
    #define UART_WAIT_FOR_RX CONFIG_BT_NUS_UART_RX_WAIT_TIME
    
    static K_SEM_DEFINE(ble_init_ok, 0, 1);
    
    static struct bt_conn *current_conn;
    static struct bt_conn *auth_conn;
    
    static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));
    static struct k_work_delayable uart_work;
    
    struct uart_data_t {
    	void *fifo_reserved;
    	uint8_t data[UART_BUF_SIZE];
    	uint16_t len;
    };
    
    static K_FIFO_DEFINE(fifo_uart_tx_data);
    static K_FIFO_DEFINE(fifo_uart_rx_data);
    
    static const struct bt_data ad[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    static const struct bt_data sd[] = {
    	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
    };
    
    #if CONFIG_BT_NUS_UART_ASYNC_ADAPTER
    UART_ASYNC_ADAPTER_INST_DEFINE(async_adapter);
    #else
    static const struct device *const async_adapter;
    #endif
    
    static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    {
    	ARG_UNUSED(dev);
    
    	static size_t aborted_len;
    	struct uart_data_t *buf;
    	static uint8_t *aborted_buf;
    	static bool disable_req;
    
    	switch (evt->type) {
    	case UART_TX_DONE:
    		LOG_DBG("UART_TX_DONE");
    		if ((evt->data.tx.len == 0) ||
    		    (!evt->data.tx.buf)) {
    			return;
    		}
    
    		if (aborted_buf) {
    			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    					   data);
    			aborted_buf = NULL;
    			aborted_len = 0;
    		} else {
    			buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t,
    					   data);
    		}
    
    		k_free(buf);
    
    		buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
    		if (!buf) {
    			return;
    		}
    
    		if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS)) {
    			LOG_WRN("Failed to send data over UART");
    		}
    
    		break;
    
    	case UART_RX_RDY:
    		LOG_DBG("UART_RX_RDY");
    		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data);
    		buf->len += evt->data.rx.len;
    
    		if (disable_req) {
    			return;
    		}
    
    		if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
    		    (evt->data.rx.buf[buf->len - 1] == '\r')) {
    			disable_req = true;
    			uart_rx_disable(uart);
    		}
    
    		break;
    
    	case UART_RX_DISABLED:
    		LOG_DBG("UART_RX_DISABLED");
    		disable_req = false;
    
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    			return;
    		}
    
    		uart_rx_enable(uart, buf->data, sizeof(buf->data),
    			       UART_WAIT_FOR_RX);
    
    		break;
    
    	case UART_RX_BUF_REQUEST:
    		LOG_DBG("UART_RX_BUF_REQUEST");
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    		}
    
    		break;
    
    	case UART_RX_BUF_RELEASED:
    		LOG_DBG("UART_RX_BUF_RELEASED");
    		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
    				   data);
    
    		if (buf->len > 0) {
    			k_fifo_put(&fifo_uart_rx_data, buf);
    		} else {
    			k_free(buf);
    		}
    
    		break;
    
    	case UART_TX_ABORTED:
    		LOG_DBG("UART_TX_ABORTED");
    		if (!aborted_buf) {
    			aborted_buf = (uint8_t *)evt->data.tx.buf;
    		}
    
    		aborted_len += evt->data.tx.len;
    		buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    				   data);
    
    		uart_tx(uart, &buf->data[aborted_len],
    			buf->len - aborted_len, SYS_FOREVER_MS);
    
    		break;
    
    	default:
    		break;
    	}
    }
    
    static void uart_work_handler(struct k_work *item)
    {
    	struct uart_data_t *buf;
    
    	buf = k_malloc(sizeof(*buf));
    	if (buf) {
    		buf->len = 0;
    	} else {
    		LOG_WRN("Not able to allocate UART receive buffer");
    		k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    		return;
    	}
    
    	uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX);
    }
    
    static bool uart_test_async_api(const struct device *dev)
    {
    	const struct uart_driver_api *api =
    			(const struct uart_driver_api *)dev->api;
    
    	return (api->callback_set != NULL);
    }
    
    static int uart_init(void)
    {
    	int err;
    	int pos;
    	struct uart_data_t *rx;
    	struct uart_data_t *tx;
    
    	if (!device_is_ready(uart)) {
    		return -ENODEV;
    	}
    
    	if (IS_ENABLED(CONFIG_USB_DEVICE_STACK)) {
    		err = usb_enable(NULL);
    		if (err && (err != -EALREADY)) {
    			LOG_ERR("Failed to enable USB");
    			return err;
    		}
    	}
    
    	rx = k_malloc(sizeof(*rx));
    	if (rx) {
    		rx->len = 0;
    	} else {
    		return -ENOMEM;
    	}
    
    	k_work_init_delayable(&uart_work, uart_work_handler);
    
    
    	if (IS_ENABLED(CONFIG_BT_NUS_UART_ASYNC_ADAPTER) && !uart_test_async_api(uart)) {
    		/* Implement API adapter */
    		uart_async_adapter_init(async_adapter, uart);
    		uart = async_adapter;
    	}
    
    	err = uart_callback_set(uart, uart_cb, NULL);
    	if (err) {
    		LOG_ERR("Cannot initialize UART callback");
    		return err;
    	}
    
    	if (IS_ENABLED(CONFIG_UART_LINE_CTRL)) {
    		LOG_INF("Wait for DTR");
    		while (true) {
    			uint32_t dtr = 0;
    
    			uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr);
    			if (dtr) {
    				break;
    			}
    			/* Give CPU resources to low priority threads. */
    			k_sleep(K_MSEC(100));
    		}
    		LOG_INF("DTR set");
    		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DCD, 1);
    		if (err) {
    			LOG_WRN("Failed to set DCD, ret code %d", err);
    		}
    		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DSR, 1);
    		if (err) {
    			LOG_WRN("Failed to set DSR, ret code %d", err);
    		}
    	}
    
    	tx = k_malloc(sizeof(*tx));
    
    	if (tx) {
    		pos = snprintf(tx->data, sizeof(tx->data),
    			       "Starting Nordic UART service example\r\n");
    
    		if ((pos < 0) || (pos >= sizeof(tx->data))) {
    			k_free(tx);
    			LOG_ERR("snprintf returned %d", pos);
    			return -ENOMEM;
    		}
    
    		tx->len = pos;
    	} else {
    		return -ENOMEM;
    	}
    
    	err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    	if (err) {
    		LOG_ERR("Cannot display welcome message (err: %d)", err);
    		return err;
    	}
    
    	return uart_rx_enable(uart, rx->data, sizeof(rx->data), 50);
    }
    
    static void connected(struct bt_conn *conn, uint8_t err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	if (err) {
    		LOG_ERR("Connection failed (err %u)", err);
    		return;
    	}
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    	LOG_INF("Connected %s", addr);
    
    	current_conn = bt_conn_ref(conn);
    
    	dk_set_led_on(CON_STATUS_LED);
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Disconnected: %s (reason %u)", addr, reason);
    
    	if (auth_conn) {
    		bt_conn_unref(auth_conn);
    		auth_conn = NULL;
    	}
    
    	if (current_conn) {
    		bt_conn_unref(current_conn);
    		current_conn = NULL;
    		dk_set_led_off(CON_STATUS_LED);
    	}
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void security_changed(struct bt_conn *conn, bt_security_t level,
    			     enum bt_security_err err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (!err) {
    		LOG_INF("Security changed: %s level %u", addr, level);
    	} else {
    		LOG_WRN("Security failed: %s level %u err %d", addr,
    			level, err);
    	}
    }
    #endif
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected    = connected,
    	.disconnected = disconnected,
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	.security_changed = security_changed,
    #endif
    };
    
    #if defined(CONFIG_BT_NUS_SECURITY_ENABLED)
    static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Passkey for %s: %06u", addr, passkey);
    }
    
    static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	auth_conn = bt_conn_ref(conn);
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Passkey for %s: %06u", addr, passkey);
    	LOG_INF("Press Button 1 to confirm, Button 2 to reject.");
    }
    
    
    static void auth_cancel(struct bt_conn *conn)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing cancelled: %s", addr);
    }
    
    
    static void pairing_complete(struct bt_conn *conn, bool bonded)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing completed: %s, bonded: %d", addr, bonded);
    }
    
    
    static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	LOG_INF("Pairing failed conn: %s, reason %d", addr, reason);
    }
    
    
    static struct bt_conn_auth_cb conn_auth_callbacks = {
    	.passkey_display = auth_passkey_display,
    	.passkey_confirm = auth_passkey_confirm,
    	.cancel = auth_cancel,
    };
    
    static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
    	.pairing_complete = pairing_complete,
    	.pairing_failed = pairing_failed
    };
    #else
    static struct bt_conn_auth_cb conn_auth_callbacks;
    #endif
    
    static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data,
    			  uint16_t len)
    {
    	int err;
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr));
    
    	LOG_INF("Received data from: %s", addr);
    
    	for (uint16_t pos = 0; pos != len;) {
    		struct uart_data_t *tx = k_malloc(sizeof(*tx));
    
    		if (!tx) {
    			LOG_WRN("Not able to allocate UART send data buffer");
    			return;
    		}
    
    		/* Keep the last byte of TX buffer for potential LF char. */
    		size_t tx_data_size = sizeof(tx->data) - 1;
    
    		if ((len - pos) > tx_data_size) {
    			tx->len = tx_data_size;
    		} else {
    			tx->len = (len - pos);
    		}
    
    		memcpy(tx->data, &data[pos], tx->len);
    
    		pos += tx->len;
    
    		/* Append the LF character when the CR character triggered
    		 * transmission from the peer.
    		 */
    		if ((pos == len) && (data[len - 1] == '\r')) {
    			tx->data[tx->len] = '\n';
    			tx->len++;
    		}
    
    		err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    		if (err) {
    			k_fifo_put(&fifo_uart_tx_data, tx);
    		}
    	}
    }
    
    static struct bt_nus_cb nus_cb = {
    	.received = bt_receive_cb,
    };
    
    void error(void)
    {
    	dk_set_leds_state(DK_ALL_LEDS_MSK, DK_NO_LEDS_MSK);
    
    	while (true) {
    		/* Spin for ever */
    		k_sleep(K_MSEC(1000));
    	}
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void num_comp_reply(bool accept)
    {
    	if (accept) {
    		bt_conn_auth_passkey_confirm(auth_conn);
    		LOG_INF("Numeric Match, conn %p", (void *)auth_conn);
    	} else {
    		bt_conn_auth_cancel(auth_conn);
    		LOG_INF("Numeric Reject, conn %p", (void *)auth_conn);
    	}
    
    	bt_conn_unref(auth_conn);
    	auth_conn = NULL;
    }
    
    void button_changed(uint32_t button_state, uint32_t has_changed)
    {
    	uint32_t buttons = button_state & has_changed;
    
    	if (auth_conn) {
    		if (buttons & KEY_PASSKEY_ACCEPT) {
    			num_comp_reply(true);
    		}
    
    		if (buttons & KEY_PASSKEY_REJECT) {
    			num_comp_reply(false);
    		}
    	}
    }
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    static void configure_gpio(void)
    {
    	int err;
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	err = dk_buttons_init(button_changed);
    	if (err) {
    		LOG_ERR("Cannot init buttons (err: %d)", err);
    	}
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    	err = dk_leds_init();
    	if (err) {
    		LOG_ERR("Cannot init LEDs (err: %d)", err);
    	}
    }
    static struct nvs_fs *fs;
    #define ADDRESS_ID 1
    #define KEY_ID 2
    #define RBT_CNT_ID 3
    #define STRING_ID 4
    #define LONG_ID 5
    void main(void)
    {
    	int blink_status = 0;
    	int err = 0;
    	int rc = 0, cnt = 0, cnt_his = 0;
    	char buf[16];
    	uint8_t key[8], longarray[128];
    	uint32_t reboot_counter = 0U, reboot_counter_his;
    	configure_gpio();
    
    	err = uart_init();
    	if (err) {
    		error();
    	}
    
    	if (IS_ENABLED(CONFIG_BT_NUS_SECURITY_ENABLED)) {
    		err = bt_conn_auth_cb_register(&conn_auth_callbacks);
    		if (err) {
    			printk("Failed to register authorization callbacks.\n");
    			return;
    		}
    
    		err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    		if (err) {
    			printk("Failed to register authorization info callbacks.\n");
    			return;
    		}
    	 }
    
    	err = bt_enable(NULL);
    	if (err) {
    		error();
    	}
    
    	LOG_INF("Bluetooth initialized");
    
    	k_sem_give(&ble_init_ok);
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    		settings_storage_get(&fs);
    		rc = nvs_mount(fs);
    		if (rc) {
    			printk("Flash Init failed\n");
    			return;
    		}
    		rc = nvs_read(fs, ADDRESS_ID, &buf, sizeof(buf));
    	if (rc > 0) { /* item was found, show it */
    		printk("Id: %d, Address: %s\n", ADDRESS_ID, buf);
    	} else   {/* item was not found, add it */
    		strcpy(buf, "192.168.1.1");
    		printk("No address found, adding %s at id %d\n", buf,
    		       ADDRESS_ID);
    		(void)nvs_write(fs, ADDRESS_ID, &buf, strlen(buf)+1);
    	}
    	/* KEY_ID is used to store a key, lets see if we can read it from flash
    	 */
    	rc = nvs_read(fs, KEY_ID, &key, sizeof(key));
    	if (rc > 0) { /* item was found, show it */
    		printk("Id: %d, Key: ", KEY_ID);
    		for (int n = 0; n < 8; n++) {
    			printk("%x ", key[n]);
    		}
    		printk("\n");
    	} else   {/* item was not found, add it */
    		printk("No key found, adding it at id %d\n", KEY_ID);
    		key[0] = 0xFF;
    		key[1] = 0xFE;
    		key[2] = 0xFD;
    		key[3] = 0xFC;
    		key[4] = 0xFB;
    		key[5] = 0xFA;
    		key[6] = 0xF9;
    		key[7] = 0xF8;
    		(void)nvs_write(fs, KEY_ID, &key, sizeof(key));
    	}
    	/* RBT_CNT_ID is used to store the reboot counter, lets see
    	 * if we can read it from flash
    	 */
    	rc = nvs_read(fs, RBT_CNT_ID, &reboot_counter, sizeof(reboot_counter));
    	if (rc > 0) { /* item was found, show it */
    		printk("Id: %d, Reboot_counter: %d\n",
    			RBT_CNT_ID, reboot_counter);
    	} else   {/* item was not found, add it */
    		printk("No Reboot counter found, adding it at id %d\n",
    		       RBT_CNT_ID);
    		(void)nvs_write(fs, RBT_CNT_ID, &reboot_counter,
    			  sizeof(reboot_counter));
    	}
    	reboot_counter++;
    	(void)nvs_write(fs, RBT_CNT_ID, &reboot_counter,					sizeof(reboot_counter));
    	
    
    	 }
    
    	err = bt_nus_init(&nus_cb);
    	if (err) {
    		LOG_ERR("Failed to initialize UART service (err: %d)", err);
    		return;
    	}
    
    	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
    			      ARRAY_SIZE(sd));
    	if (err) {
    		LOG_ERR("Advertising failed to start (err %d)", err);
    		return;
    	}
    
    	for (;;) {
    		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    	}
    }
    
    void ble_write_thread(void)
    {
    	/* Don't go any further until BLE is initialized */
    	k_sem_take(&ble_init_ok, K_FOREVER);
    
    	for (;;) {
    		/* Wait indefinitely for data to be sent over bluetooth */
    		struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
    						     K_FOREVER);
    
    		if (bt_nus_send(NULL, buf->data, buf->len)) {
    			LOG_WRN("Failed to send data over BLE connection");
    		}
    
    		k_free(buf);
    	}
    }
    
    K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL,
    		NULL, PRIORITY, 0, 0);
    

  • Hi Hung,

    Thanks - I've tried to implement this in my code, with SDK v2.1.2, but moving from v2.0.0 to v2.1.2 seems to cause my code to stop working as expected, so I'm not able to progress things.

    I'll need to do some more investigating as to what the issue is with the move from v2.0.0 to v2.1.2. before I can check if the above works for me

    Cheers,

    Mike

  • Hi Hung,

    Managed to update to NCS V2.2.0 and after much stuffing about, now have my code building.

    However, when I go to make the above changes to enable me to try and combine the settings and user storage into the one partition, I get the following build warnings:

    c:\Nordic\Development\LSR-MAX\LSR-MAX_V0_09\src\nvs_app.c:250:30: warning: passing argument 1 of 'settings_storage_get' from incompatible pointer type [-Wincompatible-pointer-types]
      250 |         settings_storage_get(&fs);
          |                              ^~~
          |                              |
          |                              struct nvs_fs **
    In file included from C:\Nordic\v2.2.0\zephyr\include\zephyr\storage\flash_map.h:261,
                     from c:\Nordic\Development\LSR-MAX\LSR-MAX_V0_09\src\nvs_app.h:17,
                     from c:\Nordic\Development\LSR-MAX\LSR-MAX_V0_09\src\nvs_app.c:14:
    C:\Nordic\v2.2.0\nrf\include\flash_map_pm.h:31:17: note: expected 'void **' but argument is of type 'struct nvs_fs **'
       31 | #define storage settings_storage
    C:\Nordic\v2.2.0\zephyr\include\zephyr\settings\settings.h:606:33: note: in expansion of macro 'storage'
      606 | int settings_storage_get(void **storage);
          |                                 ^~~~~~~
    [288/309] Linking C executable zephyr\zephyr_pre0.elf

    My code is set up as follows:

    static struct nvs_fs *fs;

    settings_load();
    settings_storage_get(&fs);
    
    rc = nvs_mount(fs);
    if (rc !=0) 
    {
    	printk("ERROR: Flash Init failed\n");
    	return rc;
    }

    If I ignore the warnings and flash my code to my device, it just goes through an endless reboot cycle (I suspect I'm trying to write/read somewhere I shouldn't)

    Any ideas on what's going wrong?

    Cheers,

    Mike

  • Hi Mike, 
    As far as I can see it's OK to have the warning as the input for settings_storage_get() expecting type void ** and we will assign it to the  nvs_fs * in the function so it's fine. The function doesn't know which backend will be used so they use void. 
    Have you tried with my example ? you can copy it to peripheral_uart project. 

Related