Kai Ren / server

Files at this revision

API Documentation at this revision

Comitter:
krenbluetoothsig
Date:
Wed May 02 05:14:13 2018 +0000
Child:
1:5186eefd678a
Commit message:
server; 1. NEW: project created.

Changed in this revision

CMakeLists.txt Show annotated file Show diff for this revision Revisions of this file
README.txt Show annotated file Show diff for this revision Revisions of this file
prj_bbc_microbit.txt Show annotated file Show diff for this revision Revisions of this file
src/main.c Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CMakeLists.txt	Wed May 02 05:14:13 2018 +0000
@@ -0,0 +1,10 @@
+set(QEMU_EXTRA_FLAGS -s)
+
+include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
+project(NONE)
+
+target_sources(app PRIVATE src/main.c)
+
+if(NODE_ADDR)
+  zephyr_compile_definitions(NODE_ADDR=${NODE_ADDR})
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.txt	Wed May 02 05:14:13 2018 +0000
@@ -0,0 +1,43 @@
+This is a README for implementing micro:bit Bluetooth mesh demo. 
+
+http://docs.zephyrproject.org/getting_started/getting_started.html#set-up-the-development-environment 
+
+.. _ble_mesh:
+
+Bluetooth: Mesh
+###############
+
+Overview
+********
+
+This sample demonstrates Bluetooth Mesh functionality. It has several
+standard Mesh models, and supports provisioning over both the
+Advertising and the GATT Provisioning Bearers (i.e. PB-ADV and PB-GATT).
+The application also needs a functioning serial console, since that's
+used for the Out-of-Band provisioning procedure.
+
+Requirements
+************
+
+* A board with Bluetooth LE support, or
+* QEMU with BlueZ running on the host
+
+Building and Running
+********************
+
+This sample can be found under :file:`samples/bluetooth/mesh` in the
+Zephyr tree.
+
+See :ref:`bluetooth setup section <bluetooth_setup>` for details on how
+to run the sample inside QEMU.
+
+For other boards, build and flash the application as follows:
+
+.. zephyr-app-commands::
+   :zephyr-app: samples/bluetooth/mesh
+   :board: <board>
+   :goals: flash
+   :compact:
+
+Refer to your :ref:`board's documentation <boards>` for alternative
+flash instructions if your board doesn't support the ``flash`` target.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/prj_bbc_microbit.txt	Wed May 02 05:14:13 2018 +0000
@@ -0,0 +1,41 @@
+#CONFIG_INIT_STACKS=y
+CONFIG_MAIN_STACK_SIZE=512
+#CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1280
+CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
+CONFIG_SYS_CLOCK_TICKS_PER_SEC=250
+CONFIG_GPIO=y
+CONFIG_FLASH=y
+CONFIG_SOC_FLASH_NRF5=y
+CONFIG_MICROBIT_DISPLAY=y
+CONFIG_PWM=y
+CONFIG_PWM_NRF5_SW=y
+
+CONFIG_BT=y
+CONFIG_BT_RX_STACK_SIZE=1280
+CONFIG_BT_CTLR_DUP_FILTER_LEN=0
+CONFIG_BT_OBSERVER=y
+CONFIG_BT_BROADCASTER=y
+CONFIG_BT_PRIVACY=n
+
+CONFIG_BT_MESH=y
+CONFIG_BT_MESH_RELAY=y
+CONFIG_BT_MESH_RX_SDU_MAX=36
+CONFIG_BT_MESH_SUBNET_COUNT=1
+CONFIG_BT_MESH_APP_KEY_COUNT=2
+CONFIG_BT_MESH_MODEL_GROUP_COUNT=2
+CONFIG_BT_MESH_ADV_BUF_COUNT=10
+CONFIG_BT_MESH_PB_ADV=n
+CONFIG_BT_MESH_CFG_CLI=y
+
+CONFIG_BT_DEBUG_LOG=n 
+CONFIG_BT_MESH_DEBUG=y
+#CONFIG_BT_MESH_DEBUG_NET=y
+CONFIG_BT_MESH_DEBUG_MODEL=y
+#CONFIG_BT_MESH_DEBUG_TRANS=y
+#CONFIG_BT_MESH_SELF_TEST=y
+#CONFIG_BT_MESH_IV_UPDATE_TEST=y
+#CONFIG_BT_MESH_DEBUG_PROV=y
+#CONFIG_BT_MESH_DEBUG_BEACON=y
+#CONFIG_BT_MESH_DEBUG_CRYPTO=y
+#CONFIG_BT_MESH_DEBUG_ADV=y
+#CONFIG_BT_MESH_DEBUG_ACCESS=y
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main.c	Wed May 02 05:14:13 2018 +0000
@@ -0,0 +1,618 @@
+/*
+ * Copyright (c) 2018 Bluetooth SIG
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <misc/printk.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/mesh.h>
+#include <gpio.h>
+#include <board.h>
+#include <soc.h>
+#include <misc/printk.h>
+#include <ctype.h>
+#include <flash.h>
+#include <gpio.h>
+#include <pwm.h>
+
+#include <display/mb_display.h>
+
+#include <bluetooth/mesh.h>
+
+#define GROUP_ADDR 0xc000
+#define PUBLISHER_ADDR  0x000f
+#if !defined(NODE_ADDR)
+#define NODE_ADDR 0x0b02
+#endif
+
+/* Model Operation Codes */
+#define BT_MESH_MODEL_OP_GEN_ONOFF_GET      BT_MESH_MODEL_OP_2(0x82, 0x01)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_SET      BT_MESH_MODEL_OP_2(0x82, 0x02)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK    BT_MESH_MODEL_OP_2(0x82, 0x03)
+#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS   BT_MESH_MODEL_OP_2(0x82, 0x04)
+
+
+/* Button debounce timeout */
+#define BUTTON_DEBOUNCE_DELAY_MS 250
+
+/* LED matrix scroll speed */
+#define SCROLL_SPEED   K_MSEC(500)
+
+/* LED matrix scroll speed */
+#define BUZZER_PIN     EXT_P0_GPIO_PIN
+#define BEEP_DURATION  K_MSEC(60)
+
+/* NVM offset */
+#define SEQ_PER_BIT  976
+#define SEQ_PAGE     (NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 1))
+#define SEQ_MAX      (NRF_FICR->CODEPAGESIZE * 8 * SEQ_PER_BIT)
+
+/* Key definition, it's pre-configured, not need to do provisioning. */
+static const u8_t net_key[16] = {
+    0xf1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+};
+static const u8_t dev_key[16] = {
+    0xf1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+};
+static const u8_t app_key[16] = {
+    0xf1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+};
+
+/* group address string. */
+const char *groupAddress2String[] = {
+    "Bedroom",
+    "Living",
+    "Dining",
+    "Garage"
+};
+
+static struct device *gpio;
+static struct device *nvm;
+static struct device *pwm;
+
+static struct k_work button_work;
+static struct k_work subscribe_work;
+static u32_t time, last_time;
+
+const u16_t net_idx;
+const u16_t app_idx;
+const u32_t iv_index;
+u8_t flags;
+u16_t addr;
+static u32_t seq;
+
+/* group address, initialized */
+u16_t groupAddress = GROUP_ADDR; 
+
+u16_t board_set_target(void)
+{
+    switch (groupAddress) {
+    case GROUP_ADDR:
+        groupAddress++ ;
+        break;
+    case (GROUP_ADDR + 3):
+        groupAddress = GROUP_ADDR;
+        break;
+    default:
+        groupAddress++;
+        break;
+    }
+
+    return groupAddress;
+}
+
+void board_seq_update(u32_t seq)
+{
+    u32_t loc, seq_map;
+    int err;
+
+    if (seq % SEQ_PER_BIT) {
+        return;
+    }
+
+    loc = (SEQ_PAGE + ((seq / SEQ_PER_BIT) / 32));
+
+    err = flash_read(nvm, loc, &seq_map, sizeof(seq_map));
+    if (err) {
+        printk("flash_read err %d\n", err);
+        return;
+    }
+
+    seq_map >>= 1;
+
+    flash_write_protection_set(nvm, false);
+    err = flash_write(nvm, loc, &seq_map, sizeof(seq_map));
+    flash_write_protection_set(nvm, true);
+    if (err) {
+        printk("flash_write err %d\n", err);
+    }
+}
+
+static u32_t get_seq(void)
+{
+    u32_t seq_map, seq = 0;
+    int err, i;
+
+    for (i = 0; i < NRF_FICR->CODEPAGESIZE / sizeof(seq_map); i++) {
+        err = flash_read(nvm, SEQ_PAGE + (i * sizeof(seq_map)),
+                 &seq_map, sizeof(seq_map));
+        if (err) {
+            printk("flash_read err %d\n", err);
+            return seq;
+        }
+
+        printk("seq_map 0x%08x\n", seq_map);
+
+        if (seq_map) {
+            seq = ((i * 32) +
+                   (32 - popcount(seq_map))) * SEQ_PER_BIT;
+            if (!seq) {
+                return 0;
+            }
+
+            break;
+        }
+    }
+
+    seq += SEQ_PER_BIT;
+    if (seq >= SEQ_MAX) {
+        seq = 0;
+    }
+
+    if (seq) {
+        seq_map >>= 1;
+        flash_write_protection_set(nvm, false);
+        err = flash_write(nvm, SEQ_PAGE + (i * sizeof(seq_map)),
+                  &seq_map, sizeof(seq_map));
+        flash_write_protection_set(nvm, true);
+        if (err) {
+            printk("flash_write err %d\n", err);
+        }
+    } else {
+        printk("Performing flash erase of page 0x%08x\n", SEQ_PAGE);
+        err = flash_erase(nvm, SEQ_PAGE, NRF_FICR->CODEPAGESIZE);
+        if (err) {
+            printk("flash_erase err %d\n", err);
+        }
+    }
+
+    return seq;
+}
+
+static void buttonA_pressed(struct k_work *work)
+{
+    struct mb_display *disp = mb_display_get();
+
+    printk("buttonA_pressed\n");
+    mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED, "%s",
+            groupAddress2String[groupAddress - GROUP_ADDR]);
+}
+
+static void buttonB_resubscribe(struct k_work *work)
+{
+    struct mb_display *disp = mb_display_get();
+
+    printk("change new group address to 0x%04x\n", board_set_target());
+
+    mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED, "%s",
+        groupAddress2String[groupAddress - GROUP_ADDR]);
+
+    // change model subscription
+    bt_mesh_cfg_mod_sub_overwrite(net_idx, addr, addr, groupAddress,
+                            BT_MESH_MODEL_ID_GEN_ONOFF_SRV, NULL);
+
+    {
+        struct bt_mesh_cfg_hb_sub sub = {
+            .src = PUBLISHER_ADDR,
+            .dst = groupAddress,
+            .period = 0x10,
+        };
+
+        bt_mesh_cfg_hb_sub_set(net_idx, addr, &sub, NULL /*&status*/);
+    }
+
+}
+
+static void button_pressed(struct device *dev, struct gpio_callback *cb,
+               uint32_t pins)
+{
+
+    /*
+     * One button press within a 1 second interval sends an on message
+     * More than one button press sends an off message
+     */
+    time = k_uptime_get_32();
+
+    /* debounce the switch */
+    if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) {
+        last_time = time;
+        return;
+    }
+
+    if (pins & BIT(SW0_GPIO_PIN)) {
+        k_work_submit(&button_work);
+    }
+    else {
+            k_work_submit(&subscribe_work);
+    }
+
+    last_time = time;
+
+}
+
+static const struct {
+    char  note;
+    u32_t period;
+    u32_t sharp;
+} period_map[] = {
+    { 'C',  3822,  3608 },
+    { 'D',  3405,  3214 },
+    { 'E',  3034,  3034 },
+    { 'F',  2863,  2703 },
+    { 'G',  2551,  2407 },
+    { 'A',  2273,  2145 },
+    { 'B',  2025,  2025 },
+};
+
+static u32_t get_period(char note, bool sharp)
+{
+    int i;
+
+    if (note == ' ') {
+        return 0;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(period_map); i++) {
+        if (period_map[i].note != note) {
+            continue;
+        }
+
+        if (sharp) {
+            return period_map[i].sharp;
+        } else {
+            return period_map[i].period;
+        }
+    }
+
+    return 1500;
+}
+
+void board_heartbeat(u8_t hops, u16_t feat)
+{
+    struct mb_display *disp = mb_display_get();
+    const struct mb_image hops_img[] = {
+        MB_IMAGE({ 1, 1, 1, 1, 1 },
+             { 1, 1, 1, 1, 1 },
+             { 1, 1, 1, 1, 1 },
+             { 1, 1, 1, 1, 1 },
+             { 1, 1, 1, 1, 1 }),
+        MB_IMAGE({ 1, 1, 1, 1, 1 },
+             { 1, 1, 1, 1, 1 },
+             { 1, 1, 0, 1, 1 },
+             { 1, 1, 1, 1, 1 },
+             { 1, 1, 1, 1, 1 }),
+        MB_IMAGE({ 1, 1, 1, 1, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 1, 1, 1, 1 }),
+        MB_IMAGE({ 1, 1, 1, 1, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 1, 1, 1, 1 }),
+        MB_IMAGE({ 1, 0, 1, 0, 1 },
+             { 0, 0, 0, 0, 0 },
+             { 1, 0, 0, 0, 1 },
+             { 0, 0, 0, 0, 0 },
+             { 1, 0, 1, 0, 1 })
+    };
+
+    printk("%u hops\n", hops);
+
+    if (hops) {
+        hops = min(hops, ARRAY_SIZE(hops_img));
+        mb_display_image(disp, MB_DISPLAY_MODE_SINGLE, K_SECONDS(2),
+                 &hops_img[hops - 1], 1);
+    }
+}
+
+void board_other_dev_pressed(u16_t addr)
+{
+    struct mb_display *disp = mb_display_get();
+
+    printk("board_other_dev_pressed(0x%04x)\n", addr);
+
+    mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, K_SECONDS(2),
+             "%c", groupAddress2String[groupAddress - GROUP_ADDR][0]);
+}
+
+void board_attention(bool attention)
+{
+    struct mb_display *disp = mb_display_get();
+    static const struct mb_image attn_img[] = {
+        MB_IMAGE({ 0, 0, 0, 0, 0 },
+             { 0, 0, 0, 0, 0 },
+             { 0, 0, 1, 0, 0 },
+             { 0, 0, 0, 0, 0 },
+             { 0, 0, 0, 0, 0 }),
+        MB_IMAGE({ 0, 0, 0, 0, 0 },
+             { 0, 1, 1, 1, 0 },
+             { 0, 1, 1, 1, 0 },
+             { 0, 1, 1, 1, 0 },
+             { 0, 0, 0, 0, 0 }),
+        MB_IMAGE({ 1, 1, 1, 1, 1 },
+             { 1, 1, 1, 1, 1 },
+             { 1, 1, 0, 1, 1 },
+             { 1, 1, 1, 1, 1 },
+             { 1, 1, 1, 1, 1 }),
+        MB_IMAGE({ 1, 1, 1, 1, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 0, 0, 0, 1 },
+             { 1, 1, 1, 1, 1 }),
+    };
+
+    if (attention) {
+        mb_display_image(disp,
+                 MB_DISPLAY_MODE_DEFAULT | MB_DISPLAY_FLAG_LOOP,
+                 K_MSEC(150), attn_img, ARRAY_SIZE(attn_img));
+    } else {
+        mb_display_stop(disp);
+    }
+}
+
+static void configure_button(void)
+{
+    static struct gpio_callback button_cb;
+
+    /* Initialize the button debouncer */
+    last_time = k_uptime_get_32();
+
+    k_work_init(&button_work, buttonA_pressed);
+    k_work_init(&subscribe_work, buttonB_resubscribe);
+
+    gpio = device_get_binding(SW0_GPIO_NAME);
+
+    gpio_pin_configure(gpio, SW0_GPIO_PIN,
+               (GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
+                GPIO_INT_ACTIVE_LOW));
+    gpio_pin_configure(gpio, SW1_GPIO_PIN,
+               (GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
+                GPIO_INT_ACTIVE_LOW));
+
+    gpio_init_callback(&button_cb, button_pressed,
+               BIT(SW0_GPIO_PIN) | BIT(SW1_GPIO_PIN));
+    gpio_add_callback(gpio, &button_cb);
+
+    gpio_pin_enable_callback(gpio, SW0_GPIO_PIN);
+    gpio_pin_enable_callback(gpio, SW1_GPIO_PIN);
+}
+
+void board_init(u16_t *addr, u32_t *seq)
+{
+    struct mb_display *disp = mb_display_get();
+
+    printk("SEQ_PAGE 0x%08x\n", SEQ_PAGE);
+
+    nvm = device_get_binding(FLASH_DEV_NAME);
+    pwm = device_get_binding(CONFIG_PWM_NRF5_SW_0_DEV_NAME);
+
+    *addr = ( (u16_t)NRF_FICR->DEVICEADDR[0] ) & 0x7fff;
+
+    printk("FICR 0x%02x\n", *addr);
+
+    *seq = get_seq();
+
+    mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED,
+             "%s %s", "Server", groupAddress2String[groupAddress - GROUP_ADDR]);
+
+    configure_button();
+}
+
+static void heartbeat(u8_t hops, u16_t feat)
+{
+    board_heartbeat(hops, feat);
+}
+
+static struct bt_mesh_cfg_srv cfg_srv = {
+#if defined(CONFIG_BOARD_BBC_MICROBIT)
+    .relay = BT_MESH_RELAY_ENABLED,
+    .beacon = BT_MESH_BEACON_DISABLED,
+#else
+    .relay = BT_MESH_RELAY_ENABLED,
+    .beacon = BT_MESH_BEACON_ENABLED,
+#endif
+    .frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
+    .default_ttl = 7,
+
+    /* 3 transmissions with 20ms interval */
+    .net_transmit = BT_MESH_TRANSMIT(2, 20),
+    .relay_retransmit = BT_MESH_TRANSMIT(3, 20),
+
+    .hb_sub.func = heartbeat,
+};
+
+static struct bt_mesh_cfg_cli cfg_cli = {
+};
+
+static void attention_on(struct bt_mesh_model *model)
+{
+    printk("attention_on()\n");
+    board_attention(true);
+}
+
+static void attention_off(struct bt_mesh_model *model)
+{
+    printk("attention_off()\n");
+    board_attention(false);
+}
+
+static const struct bt_mesh_health_srv_cb health_srv_cb = {
+    .attn_on = attention_on,
+    .attn_off = attention_off,
+};
+
+static struct bt_mesh_health_srv health_srv = {
+    .cb = &health_srv_cb,
+};
+
+BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
+BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_srv, NULL, 2 + 2);
+BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_cli, NULL, 2 + 2);
+
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+              struct bt_mesh_msg_ctx *ctx,
+              struct net_buf_simple *buf)
+{
+    struct mb_display *disp = mb_display_get();
+    uint8_t value, trans;
+    value = *buf->data++;
+    trans = *buf->data++;
+    
+    printk("gen_onoff_set model 0x%04x, src 0x%04x, value:0x%02x, trans: 0x%02x\n",
+            model->id, ctx->addr, value, trans);
+    if(value){
+        mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, -1,
+                     "%c", groupAddress2String[groupAddress - GROUP_ADDR][0]);
+    }
+    else{
+        mb_display_stop(disp);
+    }
+
+}
+
+static void gen_onoff_status(struct bt_mesh_model *model,
+                 struct bt_mesh_msg_ctx *ctx,
+                 struct net_buf_simple *buf)
+{
+    printk("gen_onoff_status modelId 0x%04x, src 0x%04x\n", model->id, ctx->addr);
+}
+
+/*
+ * OnOff Model Server Op Dispatch Table
+ *
+ */
+
+static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
+    //{ BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get },
+    { BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set },
+    //{ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack },
+    BT_MESH_MODEL_OP_END,
+};
+
+static struct bt_mesh_model root_models[] = {
+    BT_MESH_MODEL_CFG_SRV(&cfg_srv),
+    BT_MESH_MODEL_CFG_CLI(&cfg_cli),
+    BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
+    BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
+                  &gen_onoff_pub_srv, NULL),
+};
+
+static struct bt_mesh_elem elements[] = {
+    BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
+    //BT_MESH_ELEM(0, vnd_models, BT_MESH_MODEL_NONE),
+};
+
+static const struct bt_mesh_comp comp = {
+    .cid = BT_COMP_ID_LF,
+    .elem = elements,
+    .elem_count = ARRAY_SIZE(elements),
+};
+
+
+static void configure(void)
+{
+    printk("Configuring...\n");
+
+    /* Add Application Key */
+    bt_mesh_cfg_app_key_add(net_idx, addr, net_idx, app_idx, app_key, NULL);
+
+    /* Bind to vendor model */
+    bt_mesh_cfg_mod_app_bind(net_idx, addr, addr, app_idx,
+                     BT_MESH_MODEL_ID_GEN_ONOFF_SRV, NULL);
+
+    /* Bind to Health model */
+    bt_mesh_cfg_mod_app_bind(net_idx, addr, addr, app_idx,
+                 BT_MESH_MODEL_ID_HEALTH_SRV, NULL);
+
+    bt_mesh_cfg_mod_sub_add(net_idx, addr, addr, groupAddress,
+                    BT_MESH_MODEL_ID_GEN_ONOFF_SRV, NULL);
+
+    {
+        struct bt_mesh_cfg_hb_sub sub = {
+            .src = PUBLISHER_ADDR,
+            .dst = groupAddress,
+            .period = 0x10,
+        };
+
+        bt_mesh_cfg_hb_sub_set(net_idx, addr, &sub, NULL);
+        printk("Subscribing to heartbeat messages, group address: 0x%04x, %s\n",
+            groupAddress, groupAddress2String[groupAddress - GROUP_ADDR]);
+    }
+
+    printk("Configuration complete\n");
+
+}
+
+static const u8_t dev_uuid[16] = { 0xdd, 0xdd };
+
+static const struct bt_mesh_prov prov = {
+    .uuid = dev_uuid,
+};
+
+static void bt_ready(int err)
+{
+    if (err) {
+        printk("Bluetooth init failed (err %d)\n", err);
+        return;
+    }
+
+    printk("Bluetooth initialized\n");
+
+    err = bt_mesh_init(&prov, &comp);
+    if (err) {
+        printk("Initializing mesh failed (err %d)\n", err);
+        return;
+    }
+
+    printk("Mesh initialized\n");
+
+    err = bt_mesh_provision(net_key, net_idx, flags, iv_index, seq, addr,
+                dev_key);
+    if (err) {
+        printk("Provisioning failed (err %d)\n", err);
+        return;
+    }
+
+    printk("Provisioning completed\n");
+
+    configure();
+}
+
+void main(void)
+{
+    int err;
+
+    printk("Initializing...\n");
+
+    board_init(&addr, &seq);
+
+    printk("Unicast address: 0x%04x, seq 0x%06x\n", addr, seq);
+
+    /* Initialize the Bluetooth Subsystem */
+    err = bt_enable(bt_ready);
+    if (err) {
+        printk("Bluetooth init failed (err %d)\n", err);
+    }
+
+    while (1) {
+    }
+
+}