Building a BLE application on NCS - Comparing and contrasting to SoftDevice

Scope

This is a discussion on building a BLE application on our new NCS/Zephyr platform with the reflections to our nRF5 SDK which based on the SoftDevice BLE stack. The goal is to give you an overview of the architecture of the new application, how you interface with the BLE stack and how you relate it to your code in your previous/original application running on SoftDevice stack.

This tutorial based heavily on the sample codes provided in NCS SDK v1.3.1 located at \nrf\samples\bluetooth and  \zephyr\samples\bluetooth. Before I continue with the guide I would suggest to try running some of the samples to see how they work. Documentation about the samples can be found here.
It's assumed that you have gone through the SDK's getting started guide and know how to build and run an NCS application. 

1. Architecture

Bellow is a quick comparison of the architecture of a SoftDevice based application vs an application with the Zephyr BLE stack. It's quite similar, but we can see a clear split between the host and the controller on NCS/Zephyr, compared to the single combined host and controller stack in the SoftDevice. This split in the Zephyr stack allows the nRF5 device to work as a transceiver chip controlled via HCI directly from other hosts, for example the BlueZ stack on Linux. The split is even more clear on multicore devices such as the nRF5340 where you need to flash the controller stack (hci_rpmsg sample) separately on the network core, while running the host and the application in the application core. 

Another difference is that the SoftDevice is built as an RTOS agnostic stack. The SoftDevice API's are implemented using thread-safe Supervisor Calls (SVCs). All application interactions with the stack and libraries are asynchronous and event driven. There is no linking against a library needed when calling APIs from the application. This allows the SoftDevice to be provided as a pre-compiled stack and you don't have to link and rebuild the SoftDevice when you build your application. On the other hand, there is a RTOS at the core of Zephyr. The Zephyr BLE stack is one of the modules of the RTOS. Some of the main benefits of an RTOS include abstracting away HW and scheduling complexity, allowing critical processes to finish in a deterministic fashion.

In a traditional SoftDevice based application you have a main loop where you put the CPU to sleep and execute thread (main) level tasks, then you have event/interrupt handlers and SVC calls that run various event handlers whenever an event occurs. In Zephyr you can have multiple threads executing at the same time, and you might not even have a main loop. This makes it easier to handle more complex applications, and in fact there is no limitation on the maximum number of concurrent connections in Zephyr. You can organize multiple activities running at the same time without having to think of how to switch the execution between them as it's handled by the Zephyr OS automatically.

The following code is extracted from the peripheral_uart sample. It shows two threads configured to run simultaneously and there's no main() function. The first thread led_blink_thread() will initialize Bluetooth and setup GATT services and advertising. After that it would just blink the LED. The second thread ble_write_thread() would just wait for data from the UART and send it over BLE. It uses a semaphore to wait for the BLE stack to be ready before entering the loop. 

static void led_blink_thread(void)
{
	int    blink_status       = 0;
	int    err                = 0;

	printk("Starting Nordic UART service example\n");

	err = init_uart();
	if (err) {
		error();
	}

	configure_gpio();

	bt_conn_cb_register(&conn_callbacks);

	if (IS_ENABLED(CONFIG_BT_GATT_NUS_SECURITY_ENABLED)) {
		bt_conn_auth_cb_register(&conn_auth_callbacks);
	}

	err = bt_enable(NULL);
	if (err) {
		error();
	}

	printk("Bluetooth initialized\n");
	k_sem_give(&ble_init_ok);

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	err = bt_gatt_nus_init(&nus_cb);
	if (err) {
		printk("Failed to initialize UART service (err: %d)\n", err);
		return;
	}

	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
			      ARRAY_SIZE(sd));
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
	}

	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 initailized */
	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_gatt_nus_send(NULL, buf->data, buf->len)) {
			printk("Failed to send data over BLE connection\n");
		}

		k_free(buf);

		if (rx_disabled) {
			rx_disabled = false;
			uart_irq_rx_enable(uart);
		}
	}
}

K_THREAD_DEFINE(led_blink_thread_id, STACKSIZE, led_blink_thread, NULL, NULL,
		NULL, PRIORITY, 0, 0);

K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL,
		NULL, PRIORITY, 0, 0);

HW abstraction and modulization also helps; you may already have noticed that in NCS there is no predefined BLE stack for each HW chip like we had with with different SoftDevice that only offer a specific list of features and support only some specific chips, for example S132, S112, S140 etc . 

Next we will have a look at some of the main tasks of any BLE application including advertising, BLE event handling, API calls, bonding and more. 

2. Advertising

Let's have a look at a very simple application. The following code is taken from the Zephyr Bluetooth documentation here.

/*
 * Set Advertisement data. Based on the Eddystone specification:
 * https://github.com/google/eddystone/blob/master/protocol-specification.md
 * https://github.com/google/eddystone/tree/master/eddystone-url
 */
static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
	BT_DATA_BYTES(BT_DATA_SVC_DATA16,
		      0xaa, 0xfe, /* Eddystone UUID */
		      0x10, /* Eddystone-URL frame type */
		      0x00, /* Calibrated Tx power at 0m */
		      0x00, /* URL Scheme Prefix http://www. */
		      'z', 'e', 'p', 'h', 'y', 'r',
		      'p', 'r', 'o', 'j', 'e', 'c', 't',
		      0x08) /* .org */
};

/* Set Scan Response data */
static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};

static void bt_ready(int err)
{
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	/* Start advertising */
	err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad),
			      sd, ARRAY_SIZE(sd));
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
		return;
	}

	printk("Beacon started\n");
}

void main(void)
{
	int err;

	printk("Starting Beacon Demo\n");

	/* Initialize the Bluetooth Subsystem */
	err = bt_enable(bt_ready);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
	}
}

It's super simple. The only two BLE API calls are bt_enable() to enable Bluetooth and bt_le_adv_start() to start advertising. 

There is no configuration input to bt_enable() and you provide a callback (bt_ready()) to be called when the Bluetooth subsystem is ready. We will look into the stack configuration later. 

The bt_le_adv_start() function requires 3 inputs, the advertising parameters, the advertising data packet and the scan response packet.

For advertising parameters, you can find the list of pre-defined advertising parameters here. Let's take a look at one of them: BT_LE_ADV_CONN_NAME:

BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME, \
                                            BT_GAP_ADV_FAST_INT_MIN_2, \
                                            BT_GAP_ADV_FAST_INT_MAX_2, NULL)

- Option: BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME : Enable connectable advertising and include device Name in advertising packet (don't include device name in your advertising packet if you already use this option).

- BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2: Advertising interval max and min (by default 150ms and 100ms respectively). This is a little bit different from our SoftDevice configuration, as we only have a single interval configuration when calling sd_ble_gap_adv_set_configure(). In Zephyr we provide the max and min interval and its up to the controller to decide the actual interval depends on the other activities. 

The content of advertising data packet (ad[]) and the scan response data packet (sd[]) needs to follow the Bluetooth specification on advertise packets (Chapter 11 Vol 3 Part C - Bluetooth Spec v5.2). You can use the BT_DATA_BYTES and BT_DATA macros to help encode the packets in compliance with the specification. 

The setup and initialization of advertising is pretty straight forward and we don't have a generic library to automate more complex advertising activities such as auto switch to slow advertising, advertising timeout etc as in the nRF5 SDK. 

We will cover advertising with whitelist and extended advertising later. 

3. BLE events from the stack. 

The next step is how to receive the events from the BLE stack when some activity has occurred. 

Here is a code excerpt from the peripheral_lbs sample in our NRF repository. The example shows how to register BLE events to be forwarded to the application and how it's handled. 

#define DEVICE_NAME             CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN         (sizeof(DEVICE_NAME) - 1)


#define RUN_STATUS_LED          DK_LED1
#define CON_STATUS_LED          DK_LED2
#define RUN_LED_BLINK_INTERVAL  1000

#define USER_LED                DK_LED3

#define USER_BUTTON             DK_BTN1_MSK

static bool app_button_state;

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

static void connected(struct bt_conn *conn, u8_t err)
{
	if (err) {
		printk("Connection failed (err %u)\n", err);
		return;
	}

	
	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
		
	printk("Connection established!		\n\
	Connected to: %s					\n\
	Role: %u							\n\
	Connection interval: %u				\n\
	Slave latency: %u					\n\
	Connection supervisory timeout: %u	\n"
	, addr, info.role, info.le.interval, info.le.latency, info.le.timeout);

	dk_set_led_on(CON_STATUS_LED);
}

static void disconnected(struct bt_conn *conn, u8_t reason)
{
	printk("Disconnected (reason %u)\n", reason);

	dk_set_led_off(CON_STATUS_LED);
}

#ifdef CONFIG_BT_GATT_LBS_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) {
		printk("Security changed: %s level %u\n", addr, level);
	} else {
		printk("Security failed: %s level %u err %d\n", addr, level,
			err);
	}
}
#endif

static struct bt_conn_cb conn_callbacks = {
	.connected        = connected,
	.disconnected     = disconnected,
#ifdef CONFIG_BT_GATT_LBS_SECURITY_ENABLED
	.security_changed = security_changed,
#endif
};

#if defined(CONFIG_BT_GATT_LBS_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));

	printk("Passkey for %s: %06u\n", addr, passkey);
}

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

	printk("Pairing cancelled: %s\n", addr);
}

static void pairing_confirm(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	bt_conn_auth_pairing_confirm(conn);

	printk("Pairing confirmed: %s\n", 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));

	printk("Pairing completed: %s, bonded: %d\n", 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));

	printk("Pairing failed conn: %s, reason %d\n", addr, reason);
}

static struct bt_conn_auth_cb conn_auth_callbacks = {
	.passkey_display = auth_passkey_display,
	.cancel = auth_cancel,
	.pairing_confirm = pairing_confirm,
	.pairing_complete = pairing_complete,
	.pairing_failed = pairing_failed
};
#else
static struct bt_conn_auth_cb conn_auth_callbacks;
#endif

static void app_led_cb(bool led_state)
{
	dk_set_led(USER_LED, led_state);
}

static bool app_button_cb(void)
{
	return app_button_state;
}

static struct bt_gatt_lbs_cb lbs_callbacs = {
	.led_cb    = app_led_cb,
	.button_cb = app_button_cb,
};

static void button_changed(u32_t button_state, u32_t has_changed)
{
	if (has_changed & USER_BUTTON) {
		bt_gatt_lbs_send_button_state(button_state);
		app_button_state = button_state ? true : false;
	}
}

static int init_button(void)
{
	int err;

	err = dk_buttons_init(button_changed);
	if (err) {
		printk("Cannot init buttons (err: %d)\n", err);
	}

	return err;
}

void main(void)
{
	int blink_status = 0;
	int err;

	printk("Starting Bluetooth Peripheral LBS example\n");

	err = dk_leds_init();
	if (err) {
		printk("LEDs init failed (err %d)\n", err);
		return;
	}

	err = init_button();
	if (err) {
		printk("Button init failed (err %d)\n", err);
		return;
	}

	bt_conn_cb_register(&conn_callbacks);
	if (IS_ENABLED(CONFIG_BT_GATT_LBS_SECURITY_ENABLED)) {
		bt_conn_auth_cb_register(&conn_auth_callbacks);
	}

	err = bt_enable(NULL);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	err = bt_gatt_lbs_init(&lbs_callbacs);
	if (err) {
		printk("Failed to init LBS (err:%d)\n", err);
		return;
	}

	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad),
			      sd, ARRAY_SIZE(sd));
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
		return;
	}

	printk("Advertising successfully started\n");

	for (;;) {
		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
	}
}

It's slightly different from the way it's handled by the SoftDevice. There you register an observer using the NRF_SDH_BLE_OBSERVER() macro to receive events, and usually in main.c you have a ble_evt_handler() function allowing the application to handle Bluetooth events not processed automatically by other SDK modules. It's quite similar in NCS, but you use bt_conn_cb_register() to register callbacks for specific events such as connected, disconnected, security_changed, le_data_len_updated, le_phy_updated, and more. 

Callbacks for authentication pairing events are registered separately using bt_conn_auth_cb_register(). Here you can register event callbacks about pairing_accept, passkey_display, passkey_entry, oob_data_request etc. Note that if your device IO capabilities doesn't support display for example, then you just simply don't declare passkey_display callback and the stack will automatically remove Display in the device's IO capabilities. 

In the peripheral_lbs example what the application does is to send a notification when the button is pressed, by having bt_gatt_lbs_send_button_state() called inside the button_changed() callback function. If you have a look inside bt_gatt_lbs_send_button_state(), the notification is sent by a bt_gatt_notify() call. The function is equivalent to the sd_ble_gatts_hvx() function in the SoftDevice. You need to supply the connection handle, the characteristic's value handle and the data you want to send. You may want to pay extra attention to the characteristics value handle as it's not calculated automatically by the stack like you might be used to from the SoftDevice, but needs to be kept track of it when the attribute table is declared. We will go into this in the service characteristic declaration discussion. 

Let's take a look inside dk_button_and_leds.c to find how the button_changed() handler is called when the button is pressed (with debouncing). It's actually executed at thread level instead of at an interrupt level. You can find buttons_scan_fn() is executed using a mechanism called k_work. This mechanism lets a higher priority thread off-load non-urgent processing to a lower priority thread. In Zephyr Bluetooth APIs should be called in thread level. You can either use a workqueue or you can use semaphore to wait in a thread before you execute a function after you receive an interrupt. Following is a code snippet showing the use of semaphore in a thread to wait for the interrupt when the temperature sampling is ready before sending it over BLE.  

static K_SEM_DEFINE(temp_sampling_ready, 0, 1);
void temp_handler(int32_t temperature)
{ 
    printk("temperature %d %d \n",temperature,temperature / 4);
    temp= temperature;    
    k_sem_give(&temp_sampling_ready);
}
void ble_temperature_send(void)
{
        int err = 0;
	/* Don't go any further until BLE is initailized */
	k_sem_take(&ble_init_ok, K_FOREVER);
    k_sem_give(&ble_init_ok);
	for (;;) {
                    err= nrfx_temp_measure();
                    if (err!=NRFX_SUCCESS) 
                    {
                        printk("error %d\n", err);
                    }
                    k_sem_take(&temp_sampling_ready, K_FOREVER);
                    if (my_connection!=NULL) 
                    {
                        my_service_temperature_send(my_connection,temp);
                    }
                    k_sleep(K_MSEC(100)); // 100ms
	}
}
K_THREAD_DEFINE(ble_temperature_send_id, STACKSIZE, ble_temperature_send, NULL, NULL,
		NULL, 7, 0, 0);

The temperature sampling was initialized using following code: 

        nrfx_temp_config_t config=NRFX_TEMP_DEFAULT_CONFIG;
        
        err = nrfx_temp_init(&config,temp_handler);
        IRQ_CONNECT(TEMP_IRQn,5,nrfx_temp_irq_handler,NULL,0);

 

4. GATT declaration and callbacks

Next, we find how we can declare services, characteristics, and the callbacks to handle read/write events.

The following code is from the peripheral_lbs example in NCS: 

/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
 */

/** @file
 *  @brief LED Button Service (LBS) sample
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <zephyr.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>

#include <bluetooth/services/lbs.h>

#include <logging/log.h>

LOG_MODULE_REGISTER(bt_gatt_lbs, CONFIG_BT_GATT_LBS_LOG_LEVEL);

static bool                   notify_enabled;
static bool                   button_state;
static struct bt_gatt_lbs_cb       lbs_cb;

#define BT_UUID_LBS           BT_UUID_DECLARE_128(LBS_UUID_SERVICE)
#define BT_UUID_LBS_BUTTON    BT_UUID_DECLARE_128(LBS_UUID_BUTTON_CHAR)
#define BT_UUID_LBS_LED       BT_UUID_DECLARE_128(LBS_UUID_LED_CHAR)

static void lbslc_ccc_cfg_changed(const struct bt_gatt_attr *attr,
				  u16_t value)
{
	notify_enabled = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t write_led(struct bt_conn *conn,
			 const struct bt_gatt_attr *attr,
			 const void *buf,
			 u16_t len, u16_t offset, u8_t flags)
{
	LOG_DBG("Attribute write, handle: %u, conn: %p", attr->handle, conn);

	if (lbs_cb.led_cb) {
		lbs_cb.led_cb(*(bool *)buf);
	}

	return len;
}

#ifdef CONFIG_BT_GATT_LBS_POLL_BUTTON
static ssize_t read_button(struct bt_conn *conn,
			  const struct bt_gatt_attr *attr,
			  void *buf,
			  u16_t len,
			  u16_t offset)
{
	const char *value = attr->user_data;

	LOG_DBG("Attribute read, handle: %u, conn: %p", attr->handle, conn);

	if (lbs_cb.button_cb) {
		button_state = lbs_cb.button_cb();
		return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
					 sizeof(*value));
	}

	return 0;
}
#endif

/* LED Button Service Declaration */
BT_GATT_SERVICE_DEFINE(lbs_svc,
BT_GATT_PRIMARY_SERVICE(BT_UUID_LBS),
#ifdef CONFIG_BT_GATT_LBS_POLL_BUTTON
	BT_GATT_CHARACTERISTIC(BT_UUID_LBS_BUTTON,
			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_READ, read_button, NULL,
			       &button_state),
#else
	BT_GATT_CHARACTERISTIC(BT_UUID_LBS_BUTTON,
			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_READ, NULL, NULL, NULL),
#endif
	BT_GATT_CCC(lbslc_ccc_cfg_changed,
		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(BT_UUID_LBS_LED,
			       BT_GATT_CHRC_WRITE,
			       BT_GATT_PERM_WRITE,
			       NULL, write_led, NULL),
);

int bt_gatt_lbs_init(struct bt_gatt_lbs_cb *callbacks)
{
	if (callbacks) {
		lbs_cb.led_cb    = callbacks->led_cb;
		lbs_cb.button_cb = callbacks->button_cb;
	}

	return 0;
}

int bt_gatt_lbs_send_button_state(bool button_state)
{
	if (!notify_enabled) {
		return -EACCES;
	}

	return bt_gatt_notify(NULL, &lbs_svc.attrs[2],
			      &button_state,
			      sizeof(button_state));
}

Let's focus on the service and characteristic declaration: 

/* LED Button Service Declaration */
BT_GATT_SERVICE_DEFINE(lbs_svc,
BT_GATT_PRIMARY_SERVICE(BT_UUID_LBS),
	BT_GATT_CHARACTERISTIC(BT_UUID_LBS_BUTTON,
			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_READ, read_button, NULL,
			       &button_state),
	BT_GATT_CCC(lbslc_ccc_cfg_changed,
		    BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(BT_UUID_LBS_LED,
			       BT_GATT_CHRC_WRITE,
			       BT_GATT_PERM_WRITE,
			       NULL, write_led, NULL),
);

A service is defined using the BT_GATT_SERVICE_DEFINE() macro with the service UUID, followed by one or more characteristics definitions below the service. 

The characteristics are defined by the BT_GATT_CHARACTERISTIC() macro in which you need to provide the characteristic's UUID, the properties, the permission, event callbacks, and the user_data variable. 

You may notice that there is no "base UUID" like in the SoftDevices, and there is no longer a requirement to register the base UUID (128-bit) with the SoftDevice and then use the short UUID (16-bit) in the code. Here we simply use the long UUID to register each characteristic. 

The properties is as defined in the BLE specification, and you can combine multiple properties such as READ, NOTIFY etc in a single characteristic. 
The same goes for permissions; here you choose the level of security and which operations require which security settings. Note that if you want to have encryption with MITM level, you here need to use _AUTHEN permissions. As an example the BT_GATT_PERM_READ_AUTHEN permission is equivalent to the BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM() permission that was used for the SoftDevice. 

Next is the concept of read callbacks, write callbacks and user_data. They are a little bit different from the SoftDevice system. Instead of having to choose a location either in the SoftDevice or in the application memory space to store the characteristic value, a pointer must be provided to a user_data variable. This doesn't need to be the actual value of the characteristic but can be any data you plan to use. This user_data pointer will be provided to the read and write callbacks that you register in the characteristic declaration. 

In the read and write callbacks you will also receive a pointer to the buffer. In the read callback you would need to fill the buffer with the data you want to response to the read request. Here is a read callback from the lbs example above: 

static ssize_t read_button(struct bt_conn *conn,
			  const struct bt_gatt_attr *attr,
			  void *buf,
			  u16_t len,
			  u16_t offset)
{
	const char *value = attr->user_data;
	LOG_DBG("Attribute read, handle: %u, conn: %p", attr->handle, conn);
	if (lbs_cb.button_cb) {
                button_state = (char)lbs_cb.button_cb();
		return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
					 sizeof(*value));
	}

	return 0;
}

The bt_gatt_attr_read() function will do a memcpy of the value variable into the buffer and the buffer is then sent back to the peer as the read response. 

It's similar in the write callback, the value in the write request/command from the peer is provided in the buffer buf variable. In the following code, the data is copied back to the user_data pointer: 

static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
			 const void *buf, u16_t len, u16_t offset,
			 u8_t flags)
{
	u8_t *value = attr->user_data;

	if (offset + len > sizeof(vnd_value)) {
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
	}

	memcpy(value + offset, buf, len);

	return len;
}

The last function is for sending notifications/indications. This is done by calling bt_gatt_notify()/bt_gatt_indicate(). If you don't provide a connection handle in the call, the notification will be sent on all connections. In the function you need to identify the handle of either the characteristic or characteristic value attribute in the service attribute table. For example in the following service, the characteristic value of BT_UUID_MY_SERVICE_TX and BT_UUID_MY_SERVICE_TEMPERATURE are attrs[3] and attrs[6] respectively.

BT_GATT_SERVICE_DEFINE(my_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_MY_SERVICE),
BT_GATT_CHARACTERISTIC(BT_UUID_MY_SERVICE_RX,
			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, 
                   NULL, on_receive, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_MY_SERVICE_TX,
			       BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_READ,
                   NULL, NULL, NULL),
BT_GATT_CCC(on_cccd_changed,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
BT_GATT_CHARACTERISTIC(BT_UUID_MY_SERVICE_TEMPERATURE,
			       BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_READ,
                   NULL, NULL, NULL),

BT_GATT_CCC(on_cccd_changed,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)
);

It can be a little bit tricky to chose the correct index of the characteristic or characteristic value. You may need to check the spec on attribute table forming to get the handle of each record, in the above example we have the ATT table for the service as follows:

0 Service declare
1 RX Char declare
2 RX Char value
3 TX Char declare
4 TX Char value
5 TX CCCD
6 MY_TEMPERATURE char declare
7 MY_TEMPERATURE char value
8 MY_TEMPERATURE CCCD.

5. Project Configuration 

As mentioned in #2 , the bt_enable() call doesn't contain any configuration of the stack as it's usually configured in project config file prj.conf. This gives the flexibility to the Zephyr BLE stack compare to SoftDevices. Features can be added or removed based on the requirement of the application, allowing optimization of the memory footprint of the stack. 

Following is the project configuration of the peripheral_hids_mouse example: 

CONFIG_BT=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_MAX_CONN=2
CONFIG_BT_MAX_PAIRED=2
CONFIG_BT_SMP=y
CONFIG_BT_L2CAP_TX_BUF_COUNT=5
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="Nordic_HIDS_mouse"
CONFIG_BT_DEVICE_APPEARANCE=962

CONFIG_BT_GATT_BAS=y
CONFIG_BT_GATT_HIDS=y
CONFIG_BT_GATT_HIDS_MAX_CLIENT_COUNT=2
CONFIG_BT_GATT_UUID16_POOL_SIZE=40
CONFIG_BT_GATT_CHRC_POOL_SIZE=20

CONFIG_BT_CONN_CTX=y

CONFIG_BT_GATT_DIS=y
CONFIG_BT_GATT_DIS_PNP=y
CONFIG_BT_GATT_DIS_MANUF="NordicSemiconductor"
CONFIG_BT_GATT_DIS_PNP_VID_SRC=2
CONFIG_BT_GATT_DIS_PNP_VID=0x1915
CONFIG_BT_GATT_DIS_PNP_PID=0xEEEE
CONFIG_BT_GATT_DIS_PNP_VER=0x0100

CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y

CONFIG_DK_LIBRARY=y

Most of the configuration are self explanatory and you can read the documentation to find more information about each of them here. There are some important ones: 

CONFIG_BT_SMP: Allow pairing and bonding

If bonding (storing bond info) is needed these settings should be enabled: 
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y

CONFIG_BT_TINYCRYPT_ECC: needed to enable LE Secure connection.

CONFIG_BT_PRIVACY: Enable/Disable privacy mode, this will enable/disable using random address on every boot. 

CONFIG_BT_SCAN_WITH_IDENTITY: Enable/Disable privacy mode (random address) as a scanner.
Note that the configuration can also be set (select) inside kconfig file as well. 

Further reading

Some tutorials that I would suggest to go though to get familiar with the Zephyr BLE stack:

 

https://devzone.nordicsemi.com/nordic/nrf-connect-sdk-guides/b/getting-started/posts/nus-on-ncs-the-nordic-uart-service-with-the-nrf-connect-sdk

https://devzone.nordicsemi.com/nordic/nrf-connect-sdk-guides/b/getting-started/posts/ncs-ble-tutorial-part-1-custom-service-in-peripheral-role

If you have any request on other related topic, please leave a comment bellow. I will try to keep this blog updated. Some topics in the timeline are whitelisting, directed advertising, central features, service discovery.

Anonymous
  • sorry i' using west to try to compile for nrf9160

    -- west build: generating a build system
    -- Application: /home/novello/nRF/nrf/samples/nrf9160/at_client
    -- Zephyr version: 2.4.0 (/home/novello/nRF/zephyr)
    -- Found Python3: /usr/bin/python3.6 (found suitable exact version "3.6.9") found components: Interpreter
    -- Found west (found suitable version "0.8.0", minimum required is "0.7.1")
    -- Board: nrf9160dk_nrf9160ns
    -- Cache files will be written to: /home/novello/.cache/zephyr
    -- Found toolchain: zephyr (/home/novello/zephyr-sdk-0.11.3)
    -- Found dtc: /home/novello/zephyr-sdk-0.11.3/sysroots/x86_64-pokysdk-linux/usr/bin/dtc (found suitable version "1.5.0", minimum required is "1.4.6")
    -- Found BOARD.dts: /home/novello/nRF/zephyr/boards/arm/nrf9160dk_nrf9160/nrf9160dk_nrf9160ns.dts
    -- Generated zephyr.dts: /home/novello/nRF/nrf/samples/nrf9160/build/zephyr/zephyr.dts
    -- Generated devicetree_unfixed.h: /home/novello/nRF/nrf/samples/nrf9160/build/zephyr/include/generated/devicetree_unfixed.h
    Parsing /home/novello/nRF/zephyr/Kconfig
    Loaded configuration '/home/novello/nRF/zephyr/boards/arm/nrf9160dk_nrf9160/nrf9160dk_nrf9160ns_defconfig'
    Merged configuration '/home/novello/nRF/nrf/samples/nrf9160/at_client/prj.conf'
    Configuration saved to '/home/novello/nRF/nrf/samples/nrf9160/build/zephyr/.config'
    Kconfig header saved to '/home/novello/nRF/nrf/samples/nrf9160/build/zephyr/include/generated/autoconf.h'
    -- The C compiler identification is GNU 9.2.0
    -- The CXX compiler identification is GNU 9.2.0
    -- The ASM compiler identification is GNU
    -- Found assembler: /home/novello/zephyr-sdk-0.11.3/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc
    -- Application: /home/novello/nRF/nrf/samples/spm
    -- Zephyr version: 2.4.0 (/home/novello/nRF/zephyr)
    -- Found Python3: /usr/bin/python3.6 (found suitable exact version "3.6.9") found components: Interpreter
    -- Found west (found suitable version "0.8.0", minimum required is "0.7.1")
    -- Board: nrf9160dk_nrf9160
    -- Cache files will be written to: /home/novello/.cache/zephyr
    -- Found toolchain: zephyr (/home/novello/zephyr-sdk-0.11.3)
    -- Found dtc: /home/novello/zephyr-sdk-0.11.3/sysroots/x86_64-pokysdk-linux/usr/bin/dtc (found suitable version "1.5.0", minimum required is "1.4.6")
    -- Found BOARD.dts: /home/novello/nRF/zephyr/boards/arm/nrf9160dk_nrf9160/nrf9160dk_nrf9160.dts
    -- Found devicetree overlay: /home/novello/nRF/nrf/samples/spm/nrf9160dk_nrf9160.overlay
    -- Generated zephyr.dts: /home/novello/nRF/nrf/samples/nrf9160/build/spm/zephyr/zephyr.dts
    -- Generated devicetree_unfixed.h: /home/novello/nRF/nrf/samples/nrf9160/build/spm/zephyr/include/generated/devicetree_unfixed.h
    Parsing /home/novello/nRF/zephyr/Kconfig
    Loaded configuration '/home/novello/nRF/zephyr/boards/arm/nrf9160dk_nrf9160/nrf9160dk_nrf9160_defconfig'
    Merged configuration '/home/novello/nRF/nrf/samples/spm/prj.conf'
    Merged configuration '/home/novello/nRF/nrf/samples/spm/boards/nrf9160dk_nrf9160.conf'
    Merged configuration '/home/novello/nRF/nrf/samples/nrf9160/build/spm/zephyr/misc/generated/extra_kconfig_options.conf'
    Configuration saved to '/home/novello/nRF/nrf/samples/nrf9160/build/spm/zephyr/.config'
    Kconfig header saved to '/home/novello/nRF/nrf/samples/nrf9160/build/spm/zephyr/include/generated/autoconf.h'
    -- The C compiler identification is GNU 9.2.0
    -- The CXX compiler identification is GNU 9.2.0
    -- The ASM compiler identification is GNU
    -- Found assembler: /home/novello/zephyr-sdk-0.11.3/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/novello/nRF/nrf/samples/nrf9160/build/spm
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/novello/nRF/nrf/samples/nrf9160/build
    -- west build: building application
    [1/175] Preparing syscall dependency handling

    [2/175] Creating directories for 'spm_subimage'
    [3/175] No download step for 'spm_subimage'
    [4/175] No update step for 'spm_subimage'
    [5/175] No patch step for 'spm_subimage'
    [6/175] No configure step for 'spm_subimage'
    [6/175] Performing build step for 'spm_subimage'
    [1/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/cc310/CMakeFiles/mbedcrypto_cc3xx_noglue.dir/home/novello/nRF/zephyr/misc/empty_file.c.obj
    [2/196] Preparing syscall dependency handling

    [3/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/shared/CMakeFiles/mbedcrypto_shared.dir/home/novello/nRF/zephyr/misc/empty_file.c.obj
    [4/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/shared/CMakeFiles/mbedcrypto_shared.dir/home/novello/nRF/mbedtls/library/sha512.c.obj
    [5/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_external.dir/home/novello/nRF/zephyr/misc/empty_file.c.obj
    [6/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/cc310/CMakeFiles/mbedcrypto_cc3xx.dir/home/novello/nRF/zephyr/misc/empty_file.c.obj
    [7/196] Linking C static library modules/nrfxlib/nrf_security/src/mbedtls/libmbedtls_external.a
    [8/196] Linking C static library modules/nrfxlib/nrf_security/src/mbedtls/cc310/libmbedcrypto_cc3xx_noglue.a
    [9/196] Linking C static library modules/nrfxlib/nrf_security/src/mbedtls/shared/libmbedcrypto_shared.a
    [10/196] Linking C static library modules/nrfxlib/nrf_security/src/mbedtls/cc310/libmbedcrypto_cc3xx.a
    [11/196] Generating misc/generated/syscalls_subdirs.trigger
    [12/196] Generating misc/generated/syscalls.json, misc/generated/struct_tags.json
    [13/196] Generating include/generated/driver-validation.h
    [14/196] Generating include/generated/syscall_dispatch.c, include/generated/syscall_list.h
    [15/196] Generating include/generated/kobj-types-enum.h, include/generated/otype-to-str.h
    [16/196] Building C object zephyr/CMakeFiles/offsets.dir/arch/arm/core/offsets/offsets.c.obj
    [17/196] Generating include/generated/offsets.h
    [18/196] Building C object CMakeFiles/app.dir/src/main.c.obj
    [19/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/crc8_sw.c.obj
    [20/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/crc16_sw.c.obj
    [21/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/crc7_sw.c.obj
    [22/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/crc32_sw.c.obj
    [23/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/dec.c.obj
    [24/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/hex.c.obj
    [25/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/fdtable.c.obj
    [26/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/notify.c.obj
    [27/196] Linking C static library app/libapp.a
    [28/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/mempool.c.obj
    [29/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/printk.c.obj
    [30/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/rb.c.obj
    [31/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/onoff.c.obj
    [32/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/timeutil.c.obj
    [33/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/sem.c.obj
    [34/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/thread_entry.c.obj
    [35/196] Building C object zephyr/CMakeFiles/zephyr.dir/misc/generated/configs.c.obj
    [36/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/work_q.c.obj
    [37/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/heap.c.obj
    [38/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/heap-validate.c.obj
    [39/196] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/prf.c.obj
    [40/196] Building C object zephyr/CMakeFiles/zephyr.dir/soc/arm/nordic_nrf/nrf91/soc.c.obj
    [41/196] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/power/power.c.obj
    [42/196] Building C object zephyr/CMakeFiles/zephyr.dir/soc/arm/nordic_nrf/validate_enabled_instances.c.obj
    [43/196] Building C object zephyr/CMakeFiles/zephyr.dir/soc/arm/nordic_nrf/nrf91/power.c.obj
    [44/196] Building C object zephyr/CMakeFiles/zephyr.dir/soc/arm/nordic_nrf/validate_base_addresses.c.obj
    [45/196] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/power/reboot.c.obj
    [46/196] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/power/policy/policy_residency.c.obj
    [47/196] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/console/uart_console.c.obj
    [48/196] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/timer/sys_clock_init.c.obj
    [49/196] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/clock_control/clock_control_nrf.c.obj
    [50/196] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/timer/nrf_rtc_timer.c.obj
    [51/196] Building C object zephyr/CMakeFiles/zephyr.dir/home/novello/nRF/nrf/subsys/spm/spm.c.obj
    [52/196] Building C object zephyr/CMakeFiles/zephyr.dir/home/novello/nRF/nrf/subsys/spm/secure_services.c.obj
    [53/196] Building C object zephyr/CMakeFiles/zephyr.dir/home/novello/nRF/nrfxlib/crypto/nrf_cc310_platform/src/nrf_cc3xx_platform_abort_zephyr.c.obj
    [54/196] Building C object zephyr/CMakeFiles/zephyr.dir/home/novello/nRF/nrfxlib/crypto/nrf_cc310_platform/src/nrf_cc3xx_platform_mutex_zephyr.c.obj
    [55/196] Building C object zephyr/arch/common/CMakeFiles/arch__common.dir/sw_isr_common.c.obj
    [56/196] Building C object zephyr/arch/common/CMakeFiles/isr_tables.dir/isr_tables.c.obj
    [57/196] Generating linker.cmd
    [58/196] Building ASM object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/swap_helper.S.obj
    [59/196] Building C object zephyr/CMakeFiles/zephyr_prebuilt.dir/misc/empty_file.c.obj
    [60/196] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/swap.c.obj
    [61/196] Building ASM object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/cpu_idle.S.obj
    [62/196] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/thread.c.obj
    [63/196] Building ASM object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/nmi_on_reset.S.obj
    [64/196] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/irq_manage.c.obj
    [65/196] Linking C static library zephyr/libzephyr.a
    [66/196] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/fatal.c.obj
    [67/196] Building ASM object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/isr_wrapper.S.obj
    [68/196] Building ASM object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/vector_table.S.obj
    [69/196] Linking C static library zephyr/arch/common/libisr_tables.a
    [70/196] Building ASM object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/fault_s.S.obj
    [71/196] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/nmi.c.obj
    [72/196] Building ASM object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/reset.S.obj
    [73/196] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/prep_c.c.obj
    [74/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/pkparse.c.obj
    [75/196] Building ASM object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/exc_exit.S.obj
    [76/196] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/scb.c.obj
    [77/196] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/fault.c.obj
    [78/196] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/irq_init.c.obj
    [79/196] Linking C static library zephyr/arch/common/libarch__common.a
    [80/196] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/thread_abort.c.obj
    [81/196] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/mpu/CMakeFiles/arch__arm__core__aarch32__cortex_m__mpu.dir/arm_core_mpu.c.obj
    [82/196] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/cmse/CMakeFiles/arch__arm__core__aarch32__cortex_m__cmse.dir/arm_core_cmse.c.obj
    [83/196] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/tz/CMakeFiles/arch__arm__core__aarch32__cortex_m__tz.dir/arm_core_tz.c.obj
    [84/196] Linking C static library zephyr/arch/arch/arm/core/aarch32/libarch__arm__core__aarch32.a
    [85/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/atoi.c.obj
    [86/196] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/mpu/CMakeFiles/arch__arm__core__aarch32__cortex_m__mpu.dir/arm_mpu.c.obj
    [87/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/strtol.c.obj
    [88/196] Linking C static library zephyr/arch/arch/arm/core/aarch32/cortex_m/libarch__arm__core__aarch32__cortex_m.a
    [89/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/strtoul.c.obj
    [90/196] Linking C static library zephyr/arch/arch/arm/core/aarch32/cortex_m/tz/libarch__arm__core__aarch32__cortex_m__tz.a
    [91/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/bsearch.c.obj
    [92/196] Linking C static library zephyr/arch/arch/arm/core/aarch32/cortex_m/cmse/libarch__arm__core__aarch32__cortex_m__cmse.a
    [93/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/string/strstr.c.obj
    [94/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/string/strncasecmp.c.obj
    [95/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/exit.c.obj
    [96/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/malloc.c.obj
    [97/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/string/string.c.obj
    [98/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/string/strspn.c.obj
    [99/196] Linking C static library zephyr/arch/arch/arm/core/aarch32/cortex_m/mpu/libarch__arm__core__aarch32__cortex_m__mpu.a
    [100/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdout/sprintf.c.obj
    [101/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdout/fprintf.c.obj
    [102/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdout/stdout_console.c.obj
    [103/196] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/time/gmtime.c.obj
    [104/196] Building C object zephyr/lib/posix/CMakeFiles/lib__posix.dir/nanosleep.c.obj
    [105/196] Building C object zephyr/soc/arm/common/cortex_m/CMakeFiles/soc__arm__common__cortex_m.dir/arm_mpu_regions.c.obj
    [106/196] Building C object zephyr/lib/posix/CMakeFiles/lib__posix.dir/pthread_common.c.obj
    [107/196] Building C object zephyr/drivers/serial/CMakeFiles/drivers__serial.dir/uart_nrfx_uarte.c.obj
    [108/196] Building C object modules/nrf/lib/fatal_error/CMakeFiles/..__nrf__lib__fatal_error.dir/fatal_error.c.obj
    [109/196] Building C object modules/nrf/subsys/fw_info/CMakeFiles/..__nrf__subsys__fw_info.dir/fw_info.c.obj
    [110/196] Linking C static library zephyr/lib/libc/minimal/liblib__libc__minimal.a
    [111/196] Linking C static library zephyr/soc/arm/common/cortex_m/libsoc__arm__common__cortex_m.a
    [112/196] Linking C static library zephyr/lib/posix/liblib__posix.a
    [113/196] Linking C static library zephyr/drivers/serial/libdrivers__serial.a
    [114/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/aesni.c.obj
    [115/196] Linking C static library modules/nrf/lib/fatal_error/lib..__nrf__lib__fatal_error.a
    [116/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/arc4.c.obj
    [117/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/aria.c.obj
    [118/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/asn1parse.c.obj
    [119/196] Linking C static library modules/nrf/subsys/fw_info/lib..__nrf__subsys__fw_info.a
    [120/196] Building C object modules/nrf/drivers/hw_cc310/CMakeFiles/..__nrf__drivers__hw_cc310.dir/hw_cc310.c.obj
    [121/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/asn1write.c.obj
    [122/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/base64.c.obj
    [123/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/camellia.c.obj
    [124/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/bignum.c.obj
    [125/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/blowfish.c.obj
    [126/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/ctr_drbg.c.obj
    [127/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/cipher.c.obj
    [128/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/cipher_wrap.c.obj
    [129/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/des.c.obj
    [130/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/entropy.c.obj
    [131/196] Linking C static library modules/nrf/drivers/hw_cc310/lib..__nrf__drivers__hw_cc310.a
    [132/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/entropy_poll.c.obj
    [133/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/error.c.obj
    [134/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/gcm.c.obj
    [135/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/havege.c.obj
    [136/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/hkdf.c.obj
    [137/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/hmac_drbg.c.obj
    [138/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/md.c.obj
    [139/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/md2.c.obj
    [140/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/md4.c.obj
    [141/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/nist_kw.c.obj
    [142/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/md5.c.obj
    [143/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/padlock.c.obj
    [144/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/pk_wrap.c.obj
    [145/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/oid.c.obj
    [146/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/pk.c.obj
    [147/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/pkcs12.c.obj
    [148/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/pkcs5.c.obj
    [149/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/platform.c.obj
    [150/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/pkwrite.c.obj
    [151/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/platform_util.c.obj
    [152/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/ripemd160.c.obj
    [153/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/timing.c.obj
    [154/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/version.c.obj
    [155/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/version_features.c.obj
    [156/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/home/novello/nRF/mbedtls/library/xtea.c.obj
    [157/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/replacements/pem.c.obj
    [158/196] Building C object modules/nrfxlib/nrf_security/src/mbedtls/CMakeFiles/mbedtls_base_vanilla.dir/replacements/memory_buffer_alloc.c.obj
    [159/196] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx/mdk/system_nrf9160.c.obj
    [160/196] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx_glue.c.obj
    [161/196] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx/drivers/src/nrfx_clock.c.obj
    [162/196] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx/drivers/src/nrfx_nvmc.c.obj
    [163/196] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx/drivers/src/nrfx_uarte.c.obj
    [164/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/device.c.obj
    [165/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/errno.c.obj
    [166/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/idle.c.obj
    [167/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/fatal.c.obj
    [168/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/kheap.c.obj
    [169/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/init.c.obj
    [170/196] Linking C static library modules/nrfxlib/nrf_security/src/mbedtls/libmbedtls_base_vanilla.a
    [171/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/mem_slab.c.obj
    [172/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/mailbox.c.obj
    [173/196] Linking C static library modules/nordic/lib..__modules__hal__nordic.a
    [174/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/msg_q.c.obj
    [175/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/pipes.c.obj
    [176/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/mutex.c.obj
    [177/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/sem.c.obj
    [178/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/queue.c.obj
    [179/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/version.c.obj
    [180/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/sched.c.obj
    [181/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/stack.c.obj
    [182/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/system_work_q.c.obj
    [183/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/work_q.c.obj
    [184/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/thread.c.obj
    [185/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/smp.c.obj
    [186/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/thread_abort.c.obj
    [187/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/timeout.c.obj
    [188/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/mempool.c.obj
    [189/196] Building C object zephyr/kernel/CMakeFiles/kernel.dir/timer.c.obj
    [190/196] Linking C static library zephyr/kernel/libkernel.a
    [191/196] Linking C executable zephyr/zephyr_prebuilt.elf
    Memory region         Used Size  Region Size  %age Used
               FLASH:         32 KB        48 KB     66.67%
                SRAM:        5552 B        64 KB      8.47%
            IDT_LIST:          40 B         2 KB      1.95%
    [192/196] Generating linker_pass_final.cmd
    [193/196] Generating isr_tables.c
    [194/196] Building C object zephyr/CMakeFiles/zephyr_final.dir/misc/empty_file.c.obj
    [195/196] Building C object zephyr/CMakeFiles/zephyr_final.dir/isr_tables.c.obj
    [196/196] Linking C executable zephyr/zephyr.elf
    [12/175] Generating include/generated/kobj-types-enum.h, include/generated/otype-to-str.h
    [14/175] No install step for 'spm_subimage'
    [15/175] Completed 'spm_subimage'
    [16/175] Building C object zephyr/CMakeFiles/offsets.dir/arch/arm/core/offsets/offsets.c.obj
    [17/175] Generating include/generated/offsets.h
    [18/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/crc32_sw.c.obj
    [19/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/crc16_sw.c.obj
    [20/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/crc7_sw.c.obj
    [21/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/crc8_sw.c.obj
    [22/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/dec.c.obj
    [23/175] Building C object CMakeFiles/app.dir/src/main.c.obj
    [24/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/hex.c.obj
    [25/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/fdtable.c.obj
    [26/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/mempool.c.obj
    [27/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/notify.c.obj
    [28/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/printk.c.obj
    [29/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/onoff.c.obj
    [30/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/sem.c.obj
    [31/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/rb.c.obj
    [32/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/timeutil.c.obj
    [33/175] Linking C static library app/libapp.a
    [34/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/thread_entry.c.obj
    [35/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/work_q.c.obj
    [36/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/heap.c.obj
    [37/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/prf.c.obj
    [38/175] Building C object zephyr/CMakeFiles/zephyr.dir/misc/generated/configs.c.obj
    [39/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/assert.c.obj
    [40/175] Building C object zephyr/CMakeFiles/zephyr.dir/lib/os/heap-validate.c.obj
    [41/175] Building C object zephyr/CMakeFiles/zephyr.dir/soc/arm/nordic_nrf/nrf91/soc.c.obj
    [42/175] Building C object zephyr/CMakeFiles/zephyr.dir/soc/arm/nordic_nrf/nrf91/power.c.obj
    [43/175] Building C object zephyr/CMakeFiles/zephyr.dir/soc/arm/nordic_nrf/validate_base_addresses.c.obj
    [44/175] Building C object zephyr/CMakeFiles/zephyr.dir/soc/arm/nordic_nrf/validate_enabled_instances.c.obj
    [45/175] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/net/lib/utils/addr_utils.c.obj
    [46/175] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/net/lib/sockets/sockets.c.obj
    [47/175] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/net/lib/sockets/getaddrinfo.c.obj
    [48/175] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/net/lib/sockets/sockets_select.c.obj
    [49/175] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/net/lib/sockets/socket_offload.c.obj
    [50/175] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/power/power.c.obj
    [51/175] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/power/reboot.c.obj
    [52/175] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/clock_control/clock_control_nrf.c.obj
    [53/175] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/console/uart_console.c.obj
    [54/175] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/timer/sys_clock_init.c.obj
    [55/175] Building C object zephyr/CMakeFiles/zephyr.dir/subsys/power/policy/policy_residency.c.obj
    [56/175] Building C object zephyr/arch/common/CMakeFiles/isr_tables.dir/isr_tables.c.obj
    [57/175] Building C object zephyr/CMakeFiles/zephyr.dir/drivers/timer/nrf_rtc_timer.c.obj
    [58/175] Building C object zephyr/CMakeFiles/zephyr.dir/home/novello/nRF/nrf/subsys/nonsecure/secure_services_ns.c.obj
    [59/175] Building C object zephyr/arch/common/CMakeFiles/arch__common.dir/sw_isr_common.c.obj
    [60/175] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/swap.c.obj
    [61/175] Generating linker.cmd
    [62/175] Building ASM object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/swap_helper.S.obj
    [63/175] Linking C static library zephyr/arch/common/libisr_tables.a
    [64/175] Building C object zephyr/CMakeFiles/zephyr_prebuilt.dir/misc/empty_file.c.obj
    [65/175] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/thread.c.obj
    [66/175] Building ASM object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/cpu_idle.S.obj
    [67/175] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/irq_manage.c.obj
    [68/175] Building ASM object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/nmi_on_reset.S.obj
    [69/175] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/fatal.c.obj
    [70/175] Linking C static library zephyr/arch/common/libarch__common.a
    [71/175] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/nmi.c.obj
    [72/175] Building ASM object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/isr_wrapper.S.obj
    [73/175] Building ASM object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/vector_table.S.obj
    [74/175] Building ASM object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/fault_s.S.obj
    [75/175] Building ASM object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/reset.S.obj
    [76/175] Building C object zephyr/arch/arch/arm/core/aarch32/CMakeFiles/arch__arm__core__aarch32.dir/prep_c.c.obj
    [77/175] Building ASM object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/exc_exit.S.obj
    [78/175] Linking C static library zephyr/libzephyr.a
    [79/175] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/fault.c.obj
    [80/175] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/scb.c.obj
    [81/175] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/irq_init.c.obj
    [82/175] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/CMakeFiles/arch__arm__core__aarch32__cortex_m.dir/thread_abort.c.obj
    [83/175] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/mpu/CMakeFiles/arch__arm__core__aarch32__cortex_m__mpu.dir/arm_core_mpu.c.obj
    [84/175] Linking C static library zephyr/arch/arch/arm/core/aarch32/libarch__arm__core__aarch32.a
    [85/175] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/mpu/CMakeFiles/arch__arm__core__aarch32__cortex_m__mpu.dir/arm_mpu.c.obj
    [86/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/strtol.c.obj
    [87/175] Building C object zephyr/arch/arch/arm/core/aarch32/cortex_m/cmse/CMakeFiles/arch__arm__core__aarch32__cortex_m__cmse.dir/arm_core_cmse.c.obj
    [88/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/atoi.c.obj
    [89/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/strtoul.c.obj
    [90/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/bsearch.c.obj
    [91/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/string/strncasecmp.c.obj
    [92/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/exit.c.obj
    [93/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/string/strstr.c.obj
    [94/175] Linking C static library zephyr/arch/arch/arm/core/aarch32/cortex_m/libarch__arm__core__aarch32__cortex_m.a
    [95/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdlib/malloc.c.obj
    [96/175] Linking C static library zephyr/arch/arch/arm/core/aarch32/cortex_m/cmse/libarch__arm__core__aarch32__cortex_m__cmse.a
    [97/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/string/string.c.obj
    [98/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/string/strspn.c.obj
    [99/175] Linking C static library zephyr/arch/arch/arm/core/aarch32/cortex_m/mpu/libarch__arm__core__aarch32__cortex_m__mpu.a
    [100/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdout/stdout_console.c.obj
    [101/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdout/sprintf.c.obj
    [102/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/stdout/fprintf.c.obj
    [103/175] Building C object zephyr/lib/libc/minimal/CMakeFiles/lib__libc__minimal.dir/source/time/gmtime.c.obj
    [104/175] Building C object zephyr/soc/arm/common/cortex_m/CMakeFiles/soc__arm__common__cortex_m.dir/arm_mpu_regions.c.obj
    [105/175] Building C object zephyr/lib/posix/CMakeFiles/lib__posix.dir/pthread_common.c.obj
    [106/175] Building C object zephyr/lib/posix/CMakeFiles/lib__posix.dir/nanosleep.c.obj
    [107/175] Linking C static library zephyr/lib/libc/minimal/liblib__libc__minimal.a
    [108/175] Building C object zephyr/subsys/net/ip/CMakeFiles/subsys__net__ip.dir/net_if.c.obj
    [109/175] Building C object zephyr/subsys/net/ip/CMakeFiles/subsys__net__ip.dir/net_core.c.obj
    [110/175] Building C object zephyr/subsys/net/CMakeFiles/subsys__net.dir/buf.c.obj
    [111/175] Linking C static library zephyr/soc/arm/common/cortex_m/libsoc__arm__common__cortex_m.a
    [112/175] Linking C static library zephyr/lib/posix/liblib__posix.a
    [113/175] Building C object zephyr/subsys/net/ip/CMakeFiles/subsys__net__ip.dir/utils.c.obj
    [114/175] Building C object zephyr/drivers/gpio/CMakeFiles/drivers__gpio.dir/gpio_nrfx.c.obj
    [115/175] Building C object zephyr/drivers/serial/CMakeFiles/drivers__serial.dir/uart_nrfx_uarte.c.obj
    [116/175] Building C object zephyr/drivers/entropy/CMakeFiles/drivers__entropy.dir/home/novello/nRF/nrf/drivers/entropy/entropy_cc310.c.obj
    [117/175] Linking C static library zephyr/subsys/net/libsubsys__net.a
    [118/175] Building C object zephyr/subsys/random/CMakeFiles/subsys__random.dir/rand32_entropy_device.c.obj
    [119/175] Linking C static library zephyr/subsys/net/ip/libsubsys__net__ip.a
    [120/175] Building C object modules/nrf/lib/bsdlib/CMakeFiles/..__nrf__lib__bsdlib.dir/bsdlib.c.obj
    [121/175] Linking C static library zephyr/drivers/gpio/libdrivers__gpio.a
    [122/175] Linking C static library zephyr/drivers/serial/libdrivers__serial.a
    [123/175] Linking C static library zephyr/drivers/entropy/libdrivers__entropy.a
    [124/175] Building C object modules/nrf/lib/bsdlib/CMakeFiles/..__nrf__lib__bsdlib.dir/bsd_os.c.obj
    [125/175] Linking C static library zephyr/subsys/random/libsubsys__random.a
    [126/175] Building C object modules/nrf/lib/at_cmd/CMakeFiles/..__nrf__lib__at_cmd.dir/at_cmd.c.obj
    [127/175] Building C object modules/nrf/lib/at_notif/CMakeFiles/..__nrf__lib__at_notif.dir/at_notif.c.obj
    [128/175] Building C object modules/nrf/lib/bsdlib/CMakeFiles/..__nrf__lib__bsdlib.dir/nrf91_sockets.c.obj
    [129/175] Building C object modules/nrf/lib/fatal_error/CMakeFiles/..__nrf__lib__fatal_error.dir/fatal_error.c.obj
    [130/175] Building C object modules/nrf/lib/at_host/CMakeFiles/..__nrf__lib__at_host.dir/at_host.c.obj
    [131/175] Building C object modules/nrf/subsys/fw_info/CMakeFiles/..__nrf__subsys__fw_info.dir/fw_info.c.obj
    [132/175] Linking C static library modules/nrf/lib/bsdlib/lib..__nrf__lib__bsdlib.a
    [133/175] Linking C static library modules/nrf/lib/at_cmd/lib..__nrf__lib__at_cmd.a
    [134/175] Linking C static library modules/nrf/lib/at_notif/lib..__nrf__lib__at_notif.a
    [135/175] Linking C static library modules/nrf/subsys/fw_info/lib..__nrf__subsys__fw_info.a
    [136/175] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx/mdk/system_nrf9160.c.obj
    [137/175] Linking C static library modules/nrf/lib/fatal_error/lib..__nrf__lib__fatal_error.a
    [138/175] Linking C static library modules/nrf/lib/at_host/lib..__nrf__lib__at_host.a
    [139/175] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx_glue.c.obj
    [140/175] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx/drivers/src/nrfx_clock.c.obj
    [141/175] Building C object modules/nordic/CMakeFiles/..__modules__hal__nordic.dir/nrfx/drivers/src/nrfx_nvmc.c.obj
    [142/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/device.c.obj
    [143/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/errno.c.obj
    [144/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/idle.c.obj
    [145/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/fatal.c.obj
    [146/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/init.c.obj
    [147/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/kheap.c.obj
    [148/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/mailbox.c.obj
    [149/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/mutex.c.obj
    [150/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/mem_slab.c.obj
    [151/175] Linking C static library modules/nordic/lib..__modules__hal__nordic.a
    [152/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/msg_q.c.obj
    [153/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/pipes.c.obj
    [154/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/sched.c.obj
    [155/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/system_work_q.c.obj
    [156/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/stack.c.obj
    [157/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/version.c.obj
    [158/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/queue.c.obj
    [159/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/sem.c.obj
    [160/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/thread.c.obj
    [161/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/thread_abort.c.obj
    [162/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/work_q.c.obj
    [163/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/smp.c.obj
    [164/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/poll.c.obj
    [165/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/timeout.c.obj
    [166/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/timer.c.obj
    [167/175] Building C object zephyr/kernel/CMakeFiles/kernel.dir/mempool.c.obj
    [168/175] Linking C static library zephyr/kernel/libkernel.a
    [169/175] Linking C executable zephyr/zephyr_prebuilt.elf
    FAILED: zephyr/zephyr_prebuilt.elf
    : && ccache /home/novello/zephyr-sdk-0.11.3/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc   zephyr/CMakeFiles/zephyr_prebuilt.dir/misc/empty_file.c.obj -o zephyr/zephyr_prebuilt.elf  -Wl,-T  zephyr/linker.cmd  -Wl,-Map=/home/novello/nRF/nrf/samples/nrf9160/build/zephyr/zephyr_prebuilt.map  -Wl,--whole-archive  app/libapp.a  zephyr/libzephyr.a  zephyr/arch/common/libarch__common.a  zephyr/arch/arch/arm/core/aarch32/libarch__arm__core__aarch32.a  zephyr/arch/arch/arm/core/aarch32/cortex_m/libarch__arm__core__aarch32__cortex_m.a  zephyr/arch/arch/arm/core/aarch32/cortex_m/mpu/libarch__arm__core__aarch32__cortex_m__mpu.a  zephyr/arch/arch/arm/core/aarch32/cortex_m/cmse/libarch__arm__core__aarch32__cortex_m__cmse.a  zephyr/lib/libc/minimal/liblib__libc__minimal.a  zephyr/lib/posix/liblib__posix.a  zephyr/soc/arm/common/cortex_m/libsoc__arm__common__cortex_m.a  zephyr/subsys/net/libsubsys__net.a  zephyr/subsys/net/ip/libsubsys__net__ip.a  zephyr/subsys/random/libsubsys__random.a  zephyr/drivers/gpio/libdrivers__gpio.a  zephyr/drivers/serial/libdrivers__serial.a  zephyr/drivers/entropy/libdrivers__entropy.a  modules/nrf/lib/bsdlib/lib..__nrf__lib__bsdlib.a  modules/nrf/lib/at_cmd/lib..__nrf__lib__at_cmd.a  modules/nrf/lib/at_notif/lib..__nrf__lib__at_notif.a  modules/nrf/lib/at_host/lib..__nrf__lib__at_host.a  modules/nrf/lib/fatal_error/lib..__nrf__lib__fatal_error.a  modules/nrf/subsys/fw_info/lib..__nrf__subsys__fw_info.a  /home/novello/nRF/nrfxlib/bsdlib/lib/cortex-m33/hard-float/libbsd_nrf9160_xxaa.a  modules/nordic/lib..__modules__hal__nordic.a  -Wl,--no-whole-archive  zephyr/kernel/libkernel.a  zephyr/CMakeFiles/offsets.dir/./arch/arm/core/offsets/offsets.c.obj  -L"/home/novello/zephyr-sdk-0.11.3/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/9.2.0/thumb/v8-m.main+fp/hard"  -L/home/novello/nRF/nrf/samples/nrf9160/build/zephyr  -lgcc  -Wl,--print-memory-usage  zephyr/arch/common/libisr_tables.a  -mcpu=cortex-m33  -mthumb  -mabi=aapcs  -Wl,--gc-sections  -Wl,--build-id=none  -Wl,--sort-common=descending  -Wl,--sort-section=alignment  -Wl,-u,_OffsetAbsSyms  -Wl,-u,_ConfigAbsSyms  -nostdlib  -static  -no-pie  -Wl,-X  -Wl,-N  -Wl,--orphan-handling=warn  spm/libspmsecureentries.a && :
    arm-zephyr-eabi-gcc: error: spm/libspmsecureentries.a: No such file or directory
    ninja: build stopped: subcommand failed.

    west build -b nrf9160dk_nrf9160ns at_client/ |tee log

    why i have this error