Publishing for Bluetooth Asia 2018 developer session: mesh session. This repo is for GenericOnOff client side firmware.

EULA

PLEASE READ MESH_DEMO_TUTORIAL_EULA.TXT BEFORE START DEVELOPMENT.

Overview

This sample demonstrates Bluetooth Mesh functionality on micro:bit. It doesn't need provisioning because all the credential material (DevKey, NetKey, AppKey, Key Index, IVI and Unicast address) is pre-configured. This sample work as a GenericOnOff client:

  • Pressing Button A on micro:bit will send GenericOnOffSet access message with on or off state alternatively and specific group address;
  • Pressing Button B on micro:bit will switch the group address from 0xC000 to 0xC003 and back to 0xC000 again like round robin;

Requirements

micro:bit board

Building and Running

0. Download source code, it's zip file, the link is on this page, Download repository.

1. Following below link to set the development environment up on your Windows computer. http://docs.zephyrproject.org/getting_started/installation_win.html . Please make sure git checkout this commit, commit: 88dfd399f480b1593a8e13f5a68d512921a55502 , the detail is here, https://github.com/zephyrproject-rtos/zephyr/commit/88dfd399f480b1593a8e13f5a68d512921a55502

2. copy zip file into ./zephyr/sample/ in the Zephyr tree.

3. unzip the file by "Extract Here".

3. access into the extracted folder and rename prj_bbc_microbit.txt to prj_bbc_microbit.conf

4. if adopting Windows Command Prompt, use it to access into the unzip folder and type following commands on the console:

mkdir build & cd build

cmake -GNinja -DBOARD=bbc_microbit ..

ninja

if adopting MSYS2, use it to access into the unzip folder and type following commands on the console:

mkdir build & cd build

cmake -GNinja -DBOARD=bbc_microbit ..

ninja

5. connect micro:bit with your computer by USB cable, the board will be enumerated as a massive storage device;

6. drag the hex file (which is in ./zephyr/sample/[unzip folder]/build/zephyr/zephyr.hex) into Microbit massive storage device to flash the firmware;

7. micro:bit is ready to work as a GenericOnOff client.

Revision:
1:fb4893064dd4
Parent:
0:876e59f87d50
Child:
7:6512e150f5f6
--- a/src/main.c	Tue May 01 10:22:24 2018 +0000
+++ b/src/main.c	Wed May 02 04:10:40 2018 +0000
@@ -1,122 +1,244 @@
-/* main.c - Application main entry point */
-
 /*
- * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2018 Bluetooth SIG
  *
  * SPDX-License-Identifier: Apache-2.0
  */
 
-#include <stdio.h>
-
-#include <gpio.h>
-#include <board.h>
-
 #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>
 
-#include "board.h"
+#if !defined(NODE_ADDR)
+#define NODE_ADDR 0x0b02
+#endif
 
+#define CID_INTEL 0x0002
+#define MOD_INTEL 0x0000
+#define GROUP_ADDR 0xc000
+#define PUBLISHER_ADDR  0x000f
+
+/* 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)
 
-#define BUTTON_DEBOUNCE_DELAY_MS 250
-
+/* Publication Declarations */
+BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_cli, NULL, 2 + 2);
 
-static struct device *gpio;
-static u8_t genericOnOffValue;
-static u8_t trans_id = 0;
-static u32_t time, last_time;
-static struct k_work button_work;
+/* Button debounce timeout */
+#define BUTTON_DEBOUNCE_DELAY_MS 250
 
+/* LED matrix scroll speed */
+#define SCROLL_SPEED   K_MSEC(500)
+
+/* Buzzer definition, no used in this example. */
+#define BUZZER_PIN     EXT_P0_GPIO_PIN
+#define BEEP_DURATION  K_MSEC(60)
 
-static struct bt_mesh_cfg_srv cfg_srv = {
-    .relay = BT_MESH_RELAY_DISABLED,
-    .beacon = BT_MESH_BEACON_ENABLED,
-#if defined(CONFIG_BT_MESH_FRIEND)
-    .frnd = BT_MESH_FRIEND_ENABLED,
-#else
-    .frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
-#endif
-#if defined(CONFIG_BT_MESH_GATT_PROXY)
-    .gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
-#else
-    .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
-#endif
-    .default_ttl = 7,
+/* 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)
 
-    /* 3 transmissions with 20ms interval */
-    .net_transmit = BT_MESH_TRANSMIT(2, 20),
-    .relay_retransmit = BT_MESH_TRANSMIT(2, 20),
+/* 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,
 };
 
 
-static void gen_onoff_status(struct bt_mesh_model *model,
-                 struct bt_mesh_msg_ctx *ctx,
-                 struct net_buf_simple *buf)
-{
-    u8_t    state;
-
-    state = net_buf_simple_pull_u8(buf);
-
-    printk("Node 0x%04x OnOff status from 0x%04x with state 0x%02x",
-            model->elem->addr, ctx->addr, state);
-}
-
-static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
-    { BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, gen_onoff_status },
-    BT_MESH_MODEL_OP_END,
+/* group address string. */
+const char *groupAddress2String[] = {
+    "Bedroom",
+    "Living",
+    "Dining",
+    "Garage"
 };
 
-static struct bt_mesh_model root_models[] = {
-    BT_MESH_MODEL_CFG_SRV(&cfg_srv),
-    BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
-              &gen_onoff_pub_cli, NULL),
-};
+static struct device *gpio;
+static struct device *nvm;
+static struct device *pwm;
+
+static struct k_work button_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 = NODE_ADDR;
+u16_t groupAddress = GROUP_ADDR;
+static u32_t seq;
 
-static struct bt_mesh_elem elements[] = {
-    BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
-};
+/* Transaction ID for Generic OnOff Set */
+static u8_t trans_ID = 0;
+
+/* GenericOnOffSet state */
+u8_t OnOff_Value = 0;
+
+/* Model Operation decleration */
+static void gen_onoff_set(struct bt_mesh_model *model,
+              struct bt_mesh_msg_ctx *ctx,
+              struct net_buf_simple *buf);
+
+static void gen_onoff_set_unack(struct bt_mesh_model *model,
+              struct bt_mesh_msg_ctx *ctx,
+              struct net_buf_simple *buf);
+
+static void gen_onoff_get(struct bt_mesh_model *model,
+              struct bt_mesh_msg_ctx *ctx,
+              struct net_buf_simple *buf);
 
-static const struct bt_mesh_comp comp = {
-    .cid = BT_COMP_ID_LF,
-    .elem = elements,
-    .elem_count = ARRAY_SIZE(elements),
-};
+static void gen_onoff_status(struct bt_mesh_model *model,
+              struct bt_mesh_msg_ctx *ctx,
+              struct net_buf_simple *buf);
+static void genericOnOffSet(void);
+
 
-void genericOnOffSet(uint8_t value)
+/* switch group address like round robin. */
+u16_t board_set_target(void)
 {
+    switch (groupAddress) {
+    case GROUP_ADDR:
+        groupAddress++;
+        break;
+    case (GROUP_ADDR + 3):
+        groupAddress = GROUP_ADDR;
+        break;
+    default:
+        groupAddress++;
+        break;
+    }
 
-    if (gen_onoff_pub_cli.addr == BT_MESH_ADDR_UNASSIGNED) {
-        printk("No\n");
+    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;
     }
 
-    printk("Yes\n");
+    seq_map >>= 1;
 
-    bt_mesh_model_msg_init(gen_onoff_pub_cli.msg,
-                       BT_MESH_MODEL_OP_GEN_ONOFF_SET);
-    net_buf_simple_add_u8(gen_onoff_pub_cli.msg, value);
-    net_buf_simple_add_u8(gen_onoff_pub_cli.msg, trans_id++);
-    bt_mesh_model_publish(&root_models[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);
+    }
 }
 
-void button_send_pressed(struct k_work *work)
+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;
+            }
 
-    genericOnOffSet(genericOnOffValue);
-    printk("button_send_pressed( %d )\n", genericOnOffValue);
+            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;
 }
 
-void button_pressed(struct device *dev, struct gpio_callback *cb,
+/* Callback function for button B pressed, it's scheduled by OS, out of interrupt routine
+ * it's safe to stay here longer. */
+static void button_send_pressed(struct k_work *work)
+{
+    struct mb_display *disp = mb_display_get();
+
+    printk("button_send_pressed()\n");
+    if(OnOff_Value % 2) {
+        mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, 100000,
+                                 "%s", groupAddress2String[groupAddress - GROUP_ADDR]);
+    }
+    else
+    {
+        mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, 100000,
+                                 "%s", " ");
+    }
+    genericOnOffSet();
+}
+
+/* button B callback, direct invoke by interrupt routine, need to be out of this callback ASAP. */
+static void button_pressed(struct device *dev, struct gpio_callback *cb,
                uint32_t pins)
 {
+    struct mb_display *disp = mb_display_get();
+
     /*
      * One button press within a 1 second interval sends an on message
      * More than one button press sends an off message
@@ -130,62 +252,129 @@
     }
 
     if (pins & BIT(SW0_GPIO_PIN)) {
-        genericOnOffValue = 1;
+        k_work_submit(&button_work);
+    }
+    else {
+        board_set_target();
+
+        mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED,
+                         "%s", groupAddress2String[groupAddress - GROUP_ADDR]);
     }
-    else{
-        genericOnOffValue = 0;
+    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;
     }
 
-    //submit to ksystem
-    k_work_submit(&button_work);
+    for (i = 0; i < ARRAY_SIZE(period_map); i++) {
+        if (period_map[i].note != note) {
+            continue;
+        }
 
-    last_time = time;
+        if (sharp) {
+            return period_map[i].sharp;
+        } else {
+            return period_map[i].period;
+        }
+    }
+
+    return 1500;
 }
 
 
-static int output_number(bt_mesh_output_action_t action, uint32_t number)
+void board_heartbeat(u8_t hops, u16_t feat)
 {
     struct mb_display *disp = mb_display_get();
-
-    printk("OOB Number: %u\n", number);
-    mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, -1, "%d", number);
-
-
-    return 0;
-}
+    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 })
+    };
 
-static int output_string(const char *str)
-{
-    struct mb_display *disp = mb_display_get();
+    printk("%u hops\n", hops);
 
-    printk("OOB String: %s\n", str);
-
-    mb_display_print(disp, MB_DISPLAY_MODE_SCROLL, K_SECONDS(1), "%s", str);
-
-    return 0;
+    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);
+    }
 }
 
-static void prov_complete(u16_t net_idx, u16_t addr)
+void board_attention(bool attention)
 {
-    //board_prov_complete();
-}
-
-static void prov_reset(void)
-{
-    bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
-}
+    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 }),
+    };
 
-static uint8_t dev_uuid[16] = { 0xdd, 0xdd };
-
-static const struct bt_mesh_prov prov = {
-        .uuid = dev_uuid,
-        .output_size = 4,
-        .output_actions = BT_MESH_DISPLAY_STRING,
-        .output_number = output_number,
-        .output_string = output_string,
-        .complete = prov_complete,
-        .reset = prov_reset,
-};
+    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)
 {
@@ -198,35 +387,195 @@
 
     gpio = device_get_binding(SW0_GPIO_NAME);
 
-    //initial button A
     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_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));
-
+               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(void)
+void board_init(u16_t *addr, u32_t *seq)
 {
-    gpio = device_get_binding(CONFIG_GPIO_NRF5_P0_DEV_NAME);
+    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", "Client", 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,
+};
+
+/*
+ * OnOff Model Client Op Dispatch Table
+ */
+
+static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
+    { BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, gen_onoff_status },
+    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_CLI, gen_onoff_cli_op,
+              &gen_onoff_pub_cli, NULL),
+};
+
+static struct bt_mesh_elem elements[] = {
+    BT_MESH_ELEM(0, root_models, /* 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),
+};
+
+/*
+ * Generic OnOff Model Server Message Handlers
+ *
+ * Mesh Model Specification 3.1.1
+ *
+ */
+
+static void gen_onoff_get(struct bt_mesh_model *model,
+              struct bt_mesh_msg_ctx *ctx,
+              struct net_buf_simple *buf)
+{
+    printk("gen_onoff_get...\n");
+}
+
+static void gen_onoff_set_unack(struct bt_mesh_model *model,
+                struct bt_mesh_msg_ctx *ctx,
+                struct net_buf_simple *buf)
+{
+    printk("gen_onoff_set_unack...\n");
+}
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+              struct bt_mesh_msg_ctx *ctx,
+              struct net_buf_simple *buf)
+{
+    printk("gen_onoff_set...\n");
+    
+}
+
+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...\n");
+}
+
+static void configure(void)
+{
+    int status;
+
+    printk("Configuring...\n");
+
+    /* Add Application Key */
+    status = bt_mesh_cfg_app_key_add(net_idx, addr, net_idx, app_idx, app_key, NULL);
+    printk("bt_mesh_cfg_app_key_add = %d \n", status);
+
+
+    /* Bind to Generic on/off client model */
+    status = bt_mesh_cfg_mod_app_bind(net_idx, addr, addr, app_idx,
+                 BT_MESH_MODEL_ID_GEN_ONOFF_CLI, NULL);     
+    printk("bt_mesh_cfg_mod_app_bind = %d \n", status);          
+    
+    status = bt_mesh_cfg_mod_sub_add(net_idx, addr, addr,
+                GROUP_ADDR, BT_MESH_MODEL_ID_GEN_ONOFF_CLI, NULL); 
+    printk("bt_mesh_cfg_mod_sub_add = %d \n", status);  
+
+
+        struct bt_mesh_cfg_hb_sub sub = {
+            .src = PUBLISHER_ADDR,
+            .dst = GROUP_ADDR,
+            .period = 0x10,
+        };
+
+        bt_mesh_cfg_hb_sub_set(net_idx, addr, &sub, NULL);
+        printk("Subscribing to heartbeat messages\n");
+
+
+    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)
 {
-    uint64_t uuidFICR = NRF_FICR->DEVICEADDR[0] | ( ( (uint64_t)(NRF_FICR->DEVICEADDR[01]) ) << 32 );
-
     if (err) {
         printk("Bluetooth init failed (err %d)\n", err);
         return;
@@ -234,40 +583,68 @@
 
     printk("Bluetooth initialized\n");
 
-    board_init();
-
-    /* Use identity address as device UUID */
-    memcpy(dev_uuid, &uuidFICR, sizeof(uint64_t));
-
     err = bt_mesh_init(&prov, &comp);
     if (err) {
         printk("Initializing mesh failed (err %d)\n", err);
         return;
     }
 
-    bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
+    printk("Mesh initialized\n");
 
-    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 board_prov_complete(void)
+static void genericOnOffSet(void)
 {
-    //gpio_pin_disable_callback(gpio, SW0_GPIO_PIN);
+    NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4);
+    struct bt_mesh_msg_ctx ctx = {
+        .net_idx = net_idx,
+        .app_idx = app_idx,
+        .addr = groupAddress,
+        .send_ttl = BT_MESH_TTL_DEFAULT,
+    };
+
+    /* Bind to Generic OnOff Model */
+    bt_mesh_model_msg_init(&msg, BT_MESH_MODEL_OP_GEN_ONOFF_SET );
+
+    net_buf_simple_add_u8(&msg, OnOff_Value % 2);
+    OnOff_Value++;
+    net_buf_simple_add_u8(&msg, trans_ID++);
+
+    if (bt_mesh_model_send(&root_models[3], &ctx, &msg, NULL, NULL)) {
+        printk("Unable to send Vendor Button message\n");
+    }
+
+    printk("Button message sent with addr 0x%04x\n", groupAddress);
 }
 
 void main(void)
 {
     int err;
-    struct mb_display *disp = mb_display_get();
+
+    printk("Initializing...\n");
 
-    printk("GenericOnOff Client Initializing...\n");
-    printk("UUID: 0x%08x%08X\n", NRF_FICR->DEVICEADDR[1], NRF_FICR->DEVICEADDR[0]);
-    mb_display_print(disp, MB_DISPLAY_MODE_SCROLL, K_MSEC(500), "%s", "Client");
+    board_init(&addr, &seq);
 
+    printk("Unicast address: 0x%04x, name: %s, seq 0x%06x\n", addr, groupAddress2String[groupAddress - GROUP_ADDR], seq);
 
     /* Initialize the Bluetooth Subsystem */
     err = bt_enable(bt_ready);
     if (err) {
         printk("Bluetooth init failed (err %d)\n", err);
     }
+
+    while (1) {
+
+    }
+
 }