summaryrefslogtreecommitdiff
path: root/src/libs/mynewt-nimble/apps/mesh_badge
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/mynewt-nimble/apps/mesh_badge')
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/README.md48
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/pkg.yml39
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/src/board.h15
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/src/gatt_svr.c226
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/src/main.c393
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.c313
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.h14
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/src/mesh_badge.h28
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/src/reel_board.c508
-rw-r--r--src/libs/mynewt-nimble/apps/mesh_badge/syscfg.yml58
10 files changed, 1642 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/README.md b/src/libs/mynewt-nimble/apps/mesh_badge/README.md
new file mode 100644
index 00000000..8d2b3fa3
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/README.md
@@ -0,0 +1,48 @@
+### Mesh Badge
+
+
+##### Overview
+********
+
+This sample app for the reel board showcases Bluetooth Mesh
+
+The app starts off as a regular Bluetooth GATT peripheral application.
+Install the the "nRF Connect" app on your phone (available both for
+Android and iOS) to access the service that the app exposes. The service
+can also be accessed with any Bluetooth LE GATT client from your PC,
+however these instructions focus on the necessary steps for phones.
+
+##### Steps to set up
+***************
+
+* On your phone, use the nRF Connect app to Scan for devices and look
+ for "reel board"
+* Connect to the device. You'll see a single service - select it
+* Request to write to the characteristic by pressing on the upward pointing
+ arrow symbol
+* Select "Text" to enter text instead of hex
+* Enter your name (or any other arbitrary text). Multiple words
+ separated by spaces are possible. The font used on the reel display
+ allows three rows of up to 12 characters
+ wide text. You can force line breaks with a comma.
+* Press "Send" - this will trigger pairing since this is a protected
+ characteristic. The passkey for the pairing will be shown on the board's
+ display. Enter the passkey in your phone.
+* Once pairing is complete the board will show the text you sent. If
+ you're not happy with it you can try writing something else.
+* When you're happy with the text, disconnect from the board (exit the app or
+ go back to the device scan page)
+* Once disconnected the board switches over to Bluetooth Mesh mode, and you
+ can't connect to it anymore over GATT.
+
+If you configure multiple boards like this they can communicate with
+each other over mesh: by pressing the user button on the board the first
+word (name) of the stored text will be sent to all other boards in
+the network and cause the other boards to display "<name> says hi!".
+
+To reset a board to its initial state (disable mesh, erase the stored
+text, and make it connectable over GATT):
+
+* Keep the user button pressed when powering on (or press the reset button
+ when powered)
+* Wait until "Reseting Device" is shown
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/pkg.yml b/src/libs/mynewt-nimble/apps/mesh_badge/pkg.yml
new file mode 100644
index 00000000..0718236f
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/pkg.yml
@@ -0,0 +1,39 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+pkg.name: apps/mesh_badge
+pkg.type: app
+pkg.description: Sample app for the reel board that showcases Bluetooth Mesh
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - "@apache-mynewt-core/hw/drivers/display/cfb"
+ - "@apache-mynewt-core/hw/drivers/display/ssd1673"
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/sys/console/full"
+ - "@apache-mynewt-core/sys/log/full"
+ - "@apache-mynewt-core/sys/log/modlog"
+ - "@apache-mynewt-core/sys/stats/full"
+ - "@apache-mynewt-core/sys/shell"
+ - nimble/controller
+ - nimble/host
+ - nimble/host/services/gap
+ - nimble/host/services/gatt
+ - nimble/host/store/config
+ - nimble/transport/ram
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/src/board.h b/src/libs/mynewt-nimble/apps/mesh_badge/src/board.h
new file mode 100644
index 00000000..af77e0b4
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/src/board.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2018 Phytec Messtechnik GmbH
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "mesh/mesh.h"
+
+void board_refresh_display(void);
+void board_show_text(const char *text, bool center, int32_t duration);
+void board_blink_leds(void);
+void board_add_hello(uint16_t addr, const char *name);
+void board_add_heartbeat(uint16_t addr, uint8_t hops);
+int board_init(void);
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/src/gatt_svr.c b/src/libs/mynewt-nimble/apps/mesh_badge/src/gatt_svr.c
new file mode 100644
index 00000000..c6005a68
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/src/gatt_svr.c
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "services/gap/ble_svc_gap.h"
+#include "bsp/bsp.h"
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+
+#include "mesh.h"
+#include "board.h"
+#include "mesh_badge.h"
+
+static const ble_uuid16_t gatt_cud_uuid = BLE_UUID16_INIT(0x2901);
+static const ble_uuid16_t gatt_cpf_uuid = BLE_UUID16_INIT(0x2904);
+
+/** @brief GATT Characteristic Presentation Format Attribute Value. */
+struct bt_gatt_cpf {
+ /** Format of the value of the characteristic */
+ uint8_t format;
+ /** Exponent field to determine how the value of this characteristic is further formatted */
+ int8_t exponent;
+ /** Unit of the characteristic */
+ uint16_t unit;
+ /** Name space of the description */
+ uint8_t name_space;
+ /** Description of the characteristic as defined in a higher layer profile */
+ uint16_t description;
+} __packed;
+
+#define CPF_FORMAT_UTF8 0x19
+
+static const struct bt_gatt_cpf name_cpf = {
+ .format = CPF_FORMAT_UTF8,
+};
+
+static const ble_uuid128_t name_uuid = BLE_UUID128_INIT(
+ 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
+ 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
+
+static const ble_uuid128_t name_enc_uuid = BLE_UUID128_INIT(
+ 0xf1, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
+ 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
+
+static int
+gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg);
+
+static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
+ {
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = &name_uuid.u,
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ .uuid = &name_enc_uuid.u,
+ .access_cb = gatt_svr_chr_access,
+ .flags = BLE_GATT_CHR_F_READ |
+ BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
+ .descriptors = (struct ble_gatt_dsc_def[]) { {
+ .uuid = &gatt_cud_uuid.u,
+ .access_cb = gatt_svr_chr_access,
+ .att_flags = BLE_ATT_F_READ,
+ }, {
+ .uuid = &gatt_cpf_uuid.u,
+ .access_cb = gatt_svr_chr_access,
+ .att_flags = BLE_ATT_F_READ,
+ }, {
+ 0, /* No more descriptors in this characteristic. */
+ } }
+ }, {
+ 0, /* No more characteristics in this service. */
+ } },
+ },
+
+ {
+ 0, /* No more services. */
+ },
+};
+
+static int read_name(struct os_mbuf *om)
+{
+ const char *value = bt_get_name();
+ int rc;
+
+ rc = os_mbuf_append(om, value, (uint16_t) strlen(value));
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+}
+
+static int write_name(struct os_mbuf *om)
+{
+ char name[MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH)];
+ uint16_t len;
+ uint16_t om_len;
+ int rc;
+
+ om_len = OS_MBUF_PKTLEN(om);
+ if (om_len >= sizeof(name)) {
+ return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
+ }
+
+ rc = ble_hs_mbuf_to_flat(om, name, sizeof(name) - 1, &len);
+ if (rc != 0) {
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+
+ name[len] = '\0';
+
+ rc = bt_set_name(name);
+ if (rc) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ board_refresh_display();
+
+ return 0;
+}
+
+static int
+gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt,
+ void *arg)
+{
+ const ble_uuid_t *uuid;
+ int rc;
+
+ uuid = ctxt->chr->uuid;
+
+ if (ble_uuid_cmp(uuid, &name_enc_uuid.u) == 0) {
+ switch (ctxt->op) {
+ case BLE_GATT_ACCESS_OP_READ_CHR:
+ rc = read_name(ctxt->om);
+ return rc;
+
+ case BLE_GATT_ACCESS_OP_WRITE_CHR:
+ rc = write_name(ctxt->om);
+ return rc;
+
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+ } else if (ble_uuid_cmp(uuid, &gatt_cud_uuid.u) == 0) {
+ rc = os_mbuf_append(ctxt->om, "Badge Name",
+ (uint16_t) strlen("Badge Name"));
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ } else if (ble_uuid_cmp(uuid, &gatt_cpf_uuid.u) == 0) {
+ rc = os_mbuf_append(ctxt->om, &name_cpf,
+ (uint16_t) sizeof(name_cpf));
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ /* Unknown characteristic; the nimble stack should not have called this
+ * function.
+ */
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+}
+
+void
+gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
+{
+ char buf[BLE_UUID_STR_LEN];
+
+ switch (ctxt->op) {
+ case BLE_GATT_REGISTER_OP_SVC:
+ MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
+ ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
+ ctxt->svc.handle);
+ break;
+
+ case BLE_GATT_REGISTER_OP_CHR:
+ MODLOG_DFLT(DEBUG, "registering characteristic %s with "
+ "def_handle=%d val_handle=%d\n",
+ ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
+ ctxt->chr.def_handle,
+ ctxt->chr.val_handle);
+ break;
+
+ case BLE_GATT_REGISTER_OP_DSC:
+ MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
+ ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
+ ctxt->dsc.handle);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+}
+
+int
+gatt_svr_init(void)
+{
+ int rc;
+
+ rc = ble_gatts_count_cfg(gatt_svr_svcs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = ble_gatts_add_svcs(gatt_svr_svcs);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/src/main.c b/src/libs/mynewt-nimble/apps/mesh_badge/src/main.c
new file mode 100644
index 00000000..d856d816
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/src/main.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "console/console.h"
+#include "host/ble_gap.h"
+#include "mesh/glue.h"
+#include "services/gap/ble_svc_gap.h"
+#include "base64/base64.h"
+
+#include "mesh_badge.h"
+#include "mesh.h"
+#include "board.h"
+
+static char badge_name[MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH)];
+
+#define MESH_BADGE_NAME_ENCODE_SIZE \
+ BASE64_ENCODE_SIZE(sizeof(badge_name))
+
+static bool reset_mesh;
+
+void print_addr(const void *addr)
+{
+ const uint8_t *u8p;
+
+ u8p = addr;
+ MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
+ u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
+}
+
+static void
+print_conn_desc(struct ble_gap_conn_desc *desc)
+{
+ MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
+ desc->conn_handle, desc->our_ota_addr.type);
+ print_addr(desc->our_ota_addr.val);
+ MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
+ desc->our_id_addr.type);
+ print_addr(desc->our_id_addr.val);
+ MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
+ desc->peer_ota_addr.type);
+ print_addr(desc->peer_ota_addr.val);
+ MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
+ desc->peer_id_addr.type);
+ print_addr(desc->peer_id_addr.val);
+ MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
+ "encrypted=%d authenticated=%d bonded=%d\n",
+ desc->conn_itvl, desc->conn_latency,
+ desc->supervision_timeout,
+ desc->sec_state.encrypted,
+ desc->sec_state.authenticated,
+ desc->sec_state.bonded);
+}
+
+static int gap_event(struct ble_gap_event *event, void *arg);
+
+static void advertise(void)
+{
+ uint8_t own_addr_type;
+ struct ble_gap_adv_params adv_params;
+ struct ble_hs_adv_fields fields;
+ const char *name;
+ int rc;
+
+ /* Figure out address to use while advertising (no privacy for now) */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
+ return;
+ }
+
+ /**
+ * Set the advertisement data included in our advertisements:
+ * o Flags (indicates advertisement type and other general info).
+ * o Advertising tx power.
+ * o Device name.
+ * o 16-bit service UUIDs (alert notifications).
+ */
+
+ memset(&fields, 0, sizeof fields);
+
+ /* Advertise two flags:
+ * o Discoverability in forthcoming advertisement (general)
+ * o BLE-only (BR/EDR unsupported).
+ */
+ fields.flags = BLE_HS_ADV_F_DISC_GEN |
+ BLE_HS_ADV_F_BREDR_UNSUP;
+
+#if 0
+ /* Indicate that the TX power level field should be included; have the
+ * stack fill this value automatically. This is done by assiging the
+ * special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
+ */
+ fields.tx_pwr_lvl_is_present = 1;
+ fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
+#endif
+
+ name = ble_svc_gap_device_name();
+ fields.name = (uint8_t *)name;
+ fields.name_len = (uint8_t) strlen(name);
+ fields.name_is_complete = 1;
+
+ rc = ble_gap_adv_set_fields(&fields);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
+ return;
+ }
+
+ /* Begin advertising. */
+ memset(&adv_params, 0, sizeof adv_params);
+ adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
+ adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
+ rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
+ &adv_params, gap_event, NULL);
+ if (rc != 0) {
+ MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
+ return;
+ }
+}
+
+static void passkey_display(uint16_t conn_handle)
+{
+ char buf[20];
+ struct ble_sm_io pk;
+ int rc;
+
+ bt_rand(&pk.passkey, sizeof(pk.passkey));
+ /* Max value is 999999 */
+ pk.passkey %= 1000000;
+ pk.action = BLE_SM_IOACT_DISP;
+
+ rc = ble_sm_inject_io(conn_handle, &pk);
+ assert(rc == 0);
+
+ snprintk(buf, sizeof(buf), "Passkey:\n%06lu", pk.passkey);
+
+ printk("%s\n", buf);
+ board_show_text(buf, false, K_FOREVER);
+}
+
+static void pairing_complete(uint16_t conn_handle, bool bonded)
+{
+ printk("Pairing Complete\n");
+ board_show_text("Pairing Complete", false, K_SECONDS(2));
+}
+
+static void pairing_failed(uint16_t conn_handle)
+{
+ printk("Pairing Failed\n");
+ board_show_text("Pairing Failed", false, K_SECONDS(2));
+}
+
+static void connected(uint16_t conn_handle, int err)
+{
+ printk("Connected (err 0x%02x)\n", err);
+
+ if (err) {
+ board_show_text("Connection failed", false, K_SECONDS(2));
+ } else {
+ board_show_text("Connected", false, K_FOREVER);
+ }
+}
+
+static void disconnected(uint16_t conn_handle, int reason)
+{
+ printk("Disconnected (reason 0x%02x)\n", reason);
+
+ if (strcmp(MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME), bt_get_name()) != 0 &&
+ !mesh_is_initialized()) {
+ /* Mesh will take over advertising control */
+ ble_gap_adv_stop();
+ mesh_start();
+ } else {
+ board_show_text("Disconnected", false, K_SECONDS(2));
+ }
+}
+
+static int gap_event(struct ble_gap_event *event, void *arg)
+{
+ struct ble_gap_conn_desc desc;
+ int rc;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ /* A new connection was established or a connection attempt failed. */
+ MODLOG_DFLT(INFO, "connection %s; status=%d ",
+ event->connect.status == 0 ? "established" : "failed",
+ event->connect.status);
+ if (event->connect.status == 0) {
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ print_conn_desc(&desc);
+ connected(event->connect.conn_handle,
+ event->connect.status);
+ }
+ MODLOG_DFLT(INFO, "\n");
+
+ if (event->connect.status != 0) {
+ /* Connection failed; resume advertising. */
+ advertise();
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
+ print_conn_desc(&event->disconnect.conn);
+ MODLOG_DFLT(INFO, "\n");
+
+ /* Connection terminated; resume advertising. */
+ advertise();
+
+ disconnected(event->disconnect.conn.conn_handle,
+ event->disconnect.reason);
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ /* The central has updated the connection parameters. */
+ MODLOG_DFLT(INFO, "connection updated; status=%d ",
+ event->conn_update.status);
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ print_conn_desc(&desc);
+ MODLOG_DFLT(INFO, "\n");
+ return 0;
+
+ case BLE_GAP_EVENT_ENC_CHANGE:
+ /* Encryption has been enabled or disabled for this connection. */
+ MODLOG_DFLT(INFO, "encryption change event; status=%d ",
+ event->enc_change.status);
+ rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ assert(rc == 0);
+ print_conn_desc(&desc);
+ MODLOG_DFLT(INFO, "\n");
+
+ if (desc.sec_state.bonded) {
+ pairing_complete(event->enc_change.conn_handle, true);
+ } else if(desc.sec_state.encrypted) {
+ pairing_complete(event->enc_change.conn_handle, false);
+ } else {
+ pairing_failed(event->enc_change.conn_handle);
+ }
+ return 0;
+
+ case BLE_GAP_EVENT_PASSKEY_ACTION:
+ MODLOG_DFLT(INFO, "passkey action event; conn_handle=%d action=%d numcmp=%d\n",
+ event->passkey.conn_handle,
+ event->passkey.params.action,
+ event->passkey.params.numcmp);
+ passkey_display(event->passkey.conn_handle);
+ return 0;
+
+ case BLE_GAP_EVENT_REPEAT_PAIRING:
+ /* We already have a bond with the peer, but it is attempting to
+ * establish a new secure link. This app sacrifices security for
+ * convenience: just throw away the old bond and accept the new link.
+ */
+
+ /* Delete the old bond. */
+ rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
+ assert(rc == 0);
+ ble_store_util_delete_peer(&desc.peer_id_addr);
+
+ /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
+ * continue with the pairing operation.
+ */
+ return BLE_GAP_REPEAT_PAIRING_RETRY;
+
+ }
+
+ return 0;
+}
+
+static void on_sync(void)
+{
+ int err;
+ ble_addr_t addr;
+
+ /* Use NRPA */
+ err = ble_hs_id_gen_rnd(1, &addr);
+ assert(err == 0);
+ err = ble_hs_id_set_rnd(addr.val);
+ assert(err == 0);
+
+ printk("Bluetooth initialized\n");
+
+ err = mesh_init(addr.type);
+ if (err) {
+ printk("Initializing mesh failed (err %d)\n", err);
+ return;
+ }
+
+ printk("Mesh initialized\n");
+
+ if (IS_ENABLED(CONFIG_SETTINGS)) {
+ settings_load();
+ }
+
+ if (reset_mesh) {
+ bt_mesh_reset();
+ reset_mesh = false;
+ }
+
+ if (!mesh_is_initialized()) {
+ advertise();
+ } else {
+ printk("Already provisioned\n");
+ ble_svc_gap_device_name_set(bt_get_name());
+ }
+
+ board_refresh_display();
+
+ printk("Board started\n");
+}
+
+void schedule_mesh_reset(void)
+{
+ reset_mesh = true;
+}
+
+static void on_reset(int reason)
+{
+ MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
+}
+
+const char *bt_get_name(void)
+{
+ char buf[MESH_BADGE_NAME_ENCODE_SIZE];
+ int rc, len;
+
+ rc = conf_get_stored_value("mesh_badge/badge_name",
+ buf, sizeof(buf));
+ if (rc == OS_ENOENT) {
+ bt_set_name(MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME));
+ } else {
+ assert(rc == 0);
+ }
+
+ memset(badge_name, '\0', sizeof(badge_name));
+ len = base64_decode(buf, badge_name);
+ if (len < 0) {
+ bt_set_name(MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME));
+ }
+
+ return badge_name;
+}
+
+int bt_set_name(const char *name)
+{
+ char buf[MESH_BADGE_NAME_ENCODE_SIZE];
+ int rc;
+
+ memset(badge_name, '\0', sizeof(badge_name));
+ memcpy(badge_name, name, strlen(name));
+ base64_encode(badge_name, sizeof(badge_name), buf, 1);
+ rc = conf_save_one("mesh_badge/badge_name", buf);
+ assert(rc == 0);
+
+ return 0;
+}
+
+int main(void)
+{
+ int err;
+
+ /* Initialize OS */
+ sysinit();
+
+ err = board_init();
+ if (err) {
+ printk("board init failed (err %d)\n", err);
+ assert(err == 0);
+ }
+
+ /* Initialize the NimBLE host configuration. */
+ ble_hs_cfg.reset_cb = on_reset;
+ ble_hs_cfg.sync_cb = on_sync;
+ ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+ ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY;
+
+ err = gatt_svr_init();
+ assert(err == 0);
+
+ /*
+ * As the last thing, process events from default event queue.
+ */
+ while (1) {
+ os_eventq_run(os_eventq_dflt_get());
+ }
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.c b/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.c
new file mode 100644
index 00000000..ee999172
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "console/console.h"
+#include "mesh/mesh.h"
+
+#include "mesh_badge.h"
+#include "mesh.h"
+#include "board.h"
+
+#define BT_COMP_ID_LF 0x05f1
+
+#define MOD_LF 0x0000
+#define OP_HELLO 0xbb
+#define OP_HEARTBEAT 0xbc
+#define OP_VND_HELLO BT_MESH_MODEL_OP_3(OP_HELLO, BT_COMP_ID_LF)
+#define OP_VND_HEARTBEAT BT_MESH_MODEL_OP_3(OP_HEARTBEAT, BT_COMP_ID_LF)
+
+#define DEFAULT_TTL 31
+#define GROUP_ADDR 0xc123
+#define NET_IDX 0x000
+#define APP_IDX 0x000
+#define FLAGS 0
+static struct ble_npl_callout hello_work;
+static struct ble_npl_callout mesh_start_work;
+
+static void heartbeat(const struct bt_mesh_hb_sub *sub, uint8_t hops,
+ uint16_t feat)
+{
+ board_show_text("Heartbeat Received", false, K_SECONDS(2));
+}
+
+static struct bt_mesh_cfg_cli cfg_cli = {
+};
+
+static void attention_on(struct bt_mesh_model *model)
+{
+ board_show_text("Attention!", false, K_SECONDS(2));
+}
+
+static void attention_off(struct bt_mesh_model *model)
+{
+ board_refresh_display();
+}
+
+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,
+};
+
+static struct os_mbuf *bt_mesh_pub_msg_health_pub;
+static struct bt_mesh_model_pub health_pub;
+
+static struct bt_mesh_model root_models[] = {
+ BT_MESH_MODEL_CFG_SRV,
+ BT_MESH_MODEL_CFG_CLI(&cfg_cli),
+ BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
+};
+
+static void vnd_hello(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ char str[32];
+ size_t len;
+
+ printk("Hello message from 0x%04x\n", ctx->addr);
+
+ if (ctx->addr == bt_mesh_model_elem(model)->addr) {
+ printk("Ignoring message from self\n");
+ return;
+ }
+
+ len = min(buf->om_len, 8);
+ memcpy(str, buf->om_data, len);
+ str[len] = '\0';
+
+ board_add_hello(ctx->addr, str);
+
+ strcpy(str + len, " says hi!");
+
+ board_show_text(str, false, K_SECONDS(3));
+
+ board_blink_leds();
+}
+
+static void vnd_heartbeat(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ uint8_t init_ttl, hops;
+
+ /* Ignore messages from self */
+ if (ctx->addr == bt_mesh_model_elem(model)->addr) {
+ return;
+ }
+
+ init_ttl = net_buf_simple_pull_u8(buf);
+ hops = init_ttl - ctx->recv_ttl + 1;
+
+ printk("Heartbeat from 0x%04x over %u hop%s\n", ctx->addr,
+ hops, hops == 1 ? "" : "s");
+
+ board_add_heartbeat(ctx->addr, hops);
+}
+
+static const struct bt_mesh_model_op vnd_ops[] = {
+ { OP_VND_HELLO, 1, vnd_hello },
+ { OP_VND_HEARTBEAT, 1, vnd_heartbeat },
+ BT_MESH_MODEL_OP_END,
+};
+
+static int pub_update(struct bt_mesh_model *mod)
+{
+ struct os_mbuf *msg = mod->pub->msg;
+
+ printk("Preparing to send heartbeat\n");
+
+ bt_mesh_model_msg_init(msg, OP_VND_HEARTBEAT);
+ net_buf_simple_add_u8(msg, DEFAULT_TTL);
+
+ return 0;
+}
+
+static struct os_mbuf *bt_mesh_pub_msg_vnd_pub;
+static struct bt_mesh_model_pub vnd_pub;
+
+static struct bt_mesh_model vnd_models[] = {
+ BT_MESH_MODEL_VND(BT_COMP_ID_LF, MOD_LF, vnd_ops, &vnd_pub, NULL),
+};
+
+static struct bt_mesh_elem elements[] = {
+ BT_MESH_ELEM(0, root_models, vnd_models),
+};
+
+static const struct bt_mesh_comp comp = {
+ .cid = BT_COMP_ID_LF,
+ .elem = elements,
+ .elem_count = ARRAY_SIZE(elements),
+};
+
+static size_t first_name_len(const char *name)
+{
+ size_t len;
+
+ for (len = 0; *name; name++, len++) {
+ switch (*name) {
+ case ' ':
+ case ',':
+ case '\n':
+ return len;
+ }
+ }
+
+ return len;
+}
+
+static void send_hello(struct ble_npl_event *work)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(3 + 8 + 4);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = NET_IDX,
+ .app_idx = APP_IDX,
+ .addr = GROUP_ADDR,
+ .send_ttl = DEFAULT_TTL,
+ };
+ const char *name = bt_get_name();
+
+ bt_mesh_model_msg_init(msg, OP_VND_HELLO);
+ net_buf_simple_add_mem(msg, name, first_name_len(name));
+
+ if (bt_mesh_model_send(&vnd_models[0], &ctx, msg, NULL, NULL) == 0) {
+ board_show_text("Saying \"hi!\" to everyone", false,
+ K_SECONDS(2));
+ } else {
+ board_show_text("Sending Failed!", false, K_SECONDS(2));
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+void mesh_send_hello(void)
+{
+ k_work_submit(&hello_work);
+}
+
+static int provision_and_configure(void)
+{
+ static const uint8_t net_key[16] = {
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ };
+ static const uint8_t app_key[16] = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ };
+ static const uint16_t iv_index;
+ struct bt_mesh_cfg_mod_pub pub = {
+ .addr = GROUP_ADDR,
+ .app_idx = APP_IDX,
+ .ttl = DEFAULT_TTL,
+ .period = BT_MESH_PUB_PERIOD_SEC(10),
+ };
+ uint8_t dev_key[16];
+ uint16_t addr;
+ int err;
+
+ err = bt_rand(dev_key, sizeof(dev_key));
+ if (err) {
+ return err;
+ }
+
+ do {
+ err = bt_rand(&addr, sizeof(addr));
+ if (err) {
+ return err;
+ }
+ } while (!addr);
+
+ /* Make sure it's a unicast address (highest bit unset) */
+ addr &= ~0x8000;
+
+ err = bt_mesh_provision(net_key, NET_IDX, FLAGS, iv_index, addr,
+ dev_key);
+ if (err) {
+ return err;
+ }
+
+ 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_vnd(NET_IDX, addr, addr, APP_IDX,
+ MOD_LF, BT_COMP_ID_LF, NULL);
+
+ /* Bind to Health model */
+ bt_mesh_cfg_mod_app_bind(NET_IDX, addr, addr, APP_IDX,
+ BT_MESH_MODEL_ID_HEALTH_SRV, NULL);
+
+ /* Add model subscription */
+ bt_mesh_cfg_mod_sub_add_vnd(NET_IDX, addr, addr, GROUP_ADDR,
+ MOD_LF, BT_COMP_ID_LF, NULL);
+
+ bt_mesh_cfg_mod_pub_set_vnd(NET_IDX, addr, addr, MOD_LF, BT_COMP_ID_LF,
+ &pub, NULL);
+
+ printk("Configuration complete\n");
+
+ return addr;
+}
+
+static void start_mesh(struct ble_npl_event *work)
+{
+ int err;
+
+ err = provision_and_configure();
+ if (err < 0) {
+ board_show_text("Starting Mesh Failed", false,
+ K_SECONDS(2));
+ } else {
+ char buf[32];
+
+ snprintk(buf, sizeof(buf),
+ "Mesh Started\nAddr: 0x%04x", err);
+ board_show_text(buf, false, K_SECONDS(4));
+ }
+}
+
+void mesh_start(void)
+{
+ k_work_submit(&mesh_start_work);
+}
+
+bool mesh_is_initialized(void)
+{
+ return bt_mesh_is_provisioned();
+}
+
+uint16_t mesh_get_addr(void)
+{
+ return elements[0].addr;
+}
+
+int mesh_init(uint8_t addr_type)
+{
+ static const uint8_t dev_uuid[16] = { 0xc0, 0xff, 0xee };
+ static const struct bt_mesh_prov prov = {
+ .uuid = dev_uuid,
+ };
+
+ hb_cb = { .recv = heartbeat };
+
+ bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(0);
+ health_pub.msg = bt_mesh_pub_msg_health_pub;
+
+ bt_mesh_pub_msg_vnd_pub = NET_BUF_SIMPLE(3 + 1);
+ vnd_pub.msg = bt_mesh_pub_msg_vnd_pub;
+ vnd_pub.update = pub_update;
+
+ k_work_init(&hello_work, send_hello);
+ k_work_init(&mesh_start_work, start_mesh);
+
+ return bt_mesh_init(addr_type, &prov, &comp);
+}
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.h b/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.h
new file mode 100644
index 00000000..33ee7cd2
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "mesh/mesh.h"
+
+void mesh_send_hello(void);
+
+uint16_t mesh_get_addr(void);
+bool mesh_is_initialized(void);
+void mesh_start(void);
+int mesh_init(uint8_t addr_type);
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh_badge.h b/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh_badge.h
new file mode 100644
index 00000000..a156f1ce
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/src/mesh_badge.h
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+struct ble_gatt_register_ctxt;
+
+void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
+int gatt_svr_init(void);
+
+void schedule_mesh_reset(void);
+const char *bt_get_name(void);
+int bt_set_name(const char *);
+
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/src/reel_board.c b/src/libs/mynewt-nimble/apps/mesh_badge/src/reel_board.c
new file mode 100644
index 00000000..bc822937
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/src/reel_board.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 2018 Phytec Messtechnik GmbH
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "os/mynewt.h"
+#include "bsp/bsp.h"
+#include "console/console.h"
+#include "hal/hal_flash.h"
+#include "hal/hal_gpio.h"
+#include "mesh/glue.h"
+#include "services/gap/ble_svc_gap.h"
+
+#include "mesh_badge.h"
+#include "display/cfb.h"
+#include "mesh.h"
+#include "board.h"
+
+#define printk console_printf
+
+enum font_size {
+ FONT_BIG = 0,
+ FONT_MEDIUM = 1,
+ FONT_SMALL = 2,
+};
+
+struct font_info {
+ uint8_t columns;
+} fonts[] = {
+ [FONT_BIG] = { .columns = 12 },
+ [FONT_MEDIUM] = { .columns = 16 },
+ [FONT_SMALL] = { .columns = 25 },
+};
+
+#define LONG_PRESS_TIMEOUT K_SECONDS(1)
+
+#define STAT_COUNT 128
+
+#define EDGE (GPIO_INT_EDGE | GPIO_INT_DOUBLE_EDGE)
+
+#ifdef SW0_GPIO_FLAGS
+#define PULL_UP SW0_GPIO_FLAGS
+#else
+#define PULL_UP 0
+#endif
+
+static struct os_dev *epd_dev;
+static bool pressed;
+static bool stats_view;
+static struct k_delayed_work epd_work;
+static struct k_delayed_work long_press_work;
+
+static struct {
+ int pin;
+} leds[] = {
+ { .pin = LED_1, },
+ { .pin = RGB_LED_RED, },
+ { .pin = RGB_LED_GRN, },
+ { .pin = RGB_LED_BLU, },
+};
+
+struct k_delayed_work led_timer;
+
+static size_t print_line(enum font_size font_size, int row, const char *text,
+ size_t len, bool center)
+{
+ uint8_t font_height, font_width;
+ char line[fonts[FONT_SMALL].columns + 1];
+ int pad;
+
+ cfb_framebuffer_set_font(epd_dev, font_size);
+
+ len = min(len, fonts[font_size].columns);
+ memcpy(line, text, len);
+ line[len] = '\0';
+
+ if (center) {
+ pad = (fonts[font_size].columns - len) / 2;
+ } else {
+ pad = 0;
+ }
+
+ cfb_get_font_size(epd_dev, font_size, &font_width, &font_height);
+
+ if (cfb_print(epd_dev, line, font_width * pad, font_height * row)) {
+ printk("Failed to print a string\n");
+ }
+
+ return len;
+}
+
+static size_t get_len(enum font_size font, const char *text)
+{
+ const char *space = NULL;
+ size_t i;
+
+ for (i = 0; i <= fonts[font].columns; i++) {
+ switch (text[i]) {
+ case '\n':
+ case '\0':
+ return i;
+ case ' ':
+ space = &text[i];
+ break;
+ default:
+ continue;
+ }
+ }
+
+ /* If we got more characters than fits a line, and a space was
+ * encountered, fall back to the last space.
+ */
+ if (space) {
+ return space - text;
+ }
+
+ return fonts[font].columns;
+}
+
+void board_blink_leds(void)
+{
+ k_delayed_work_submit(&led_timer, K_MSEC(100));
+}
+
+void board_show_text(const char *text, bool center, int32_t duration)
+{
+ int i;
+
+ cfb_framebuffer_clear(epd_dev, false);
+
+ for (i = 0; i < 3; i++) {
+ size_t len;
+
+ while (*text == ' ' || *text == '\n') {
+ text++;
+ }
+
+ len = get_len(FONT_BIG, text);
+ if (!len) {
+ break;
+ }
+
+ text += print_line(FONT_BIG, i, text, len, center);
+ if (!*text) {
+ break;
+ }
+ }
+
+ cfb_framebuffer_finalize(epd_dev);
+
+ if (duration != K_FOREVER) {
+ k_delayed_work_submit(&epd_work, duration);
+ }
+}
+
+static struct stat {
+ uint16_t addr;
+ char name[9];
+ uint8_t min_hops;
+ uint8_t max_hops;
+ uint16_t hello_count;
+ uint16_t heartbeat_count;
+} stats[STAT_COUNT] = {
+ [0 ... (STAT_COUNT - 1)] = {
+ .min_hops = BT_MESH_TTL_MAX,
+ .max_hops = 0,
+ },
+};
+
+static uint32_t stat_count;
+
+#define NO_UPDATE -1
+
+static int add_hello(uint16_t addr, const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stats); i++) {
+ struct stat *stat = &stats[i];
+
+ if (!stat->addr) {
+ stat->addr = addr;
+ strncpy(stat->name, name, sizeof(stat->name) - 1);
+ stat->hello_count = 1;
+ stat_count++;
+ return i;
+ }
+
+ if (stat->addr == addr) {
+ /* Update name, incase it has changed */
+ strncpy(stat->name, name, sizeof(stat->name) - 1);
+
+ if (stat->hello_count < 0xffff) {
+ stat->hello_count++;
+ return i;
+ }
+
+ return NO_UPDATE;
+ }
+ }
+
+ return NO_UPDATE;
+}
+
+static int add_heartbeat(uint16_t addr, uint8_t hops)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stats); i++) {
+ struct stat *stat = &stats[i];
+
+ if (!stat->addr) {
+ stat->addr = addr;
+ stat->heartbeat_count = 1;
+ stat->min_hops = hops;
+ stat->max_hops = hops;
+ stat_count++;
+ return i;
+ }
+
+ if (stat->addr == addr) {
+ if (hops < stat->min_hops) {
+ stat->min_hops = hops;
+ } else if (hops > stat->max_hops) {
+ stat->max_hops = hops;
+ }
+
+ if (stat->heartbeat_count < 0xffff) {
+ stat->heartbeat_count++;
+ return i;
+ }
+
+ return NO_UPDATE;
+ }
+ }
+
+ return NO_UPDATE;
+}
+
+void board_add_hello(uint16_t addr, const char *name)
+{
+ uint32_t sort_i;
+
+ sort_i = add_hello(addr, name);
+ if (sort_i != NO_UPDATE) {
+ }
+}
+
+void board_add_heartbeat(uint16_t addr, uint8_t hops)
+{
+ uint32_t sort_i;
+
+ sort_i = add_heartbeat(addr, hops);
+ if (sort_i != NO_UPDATE) {
+ }
+}
+
+static void show_statistics(void)
+{
+ int top[4] = { -1, -1, -1, -1 };
+ int len, i, line = 0;
+ struct stat *stat;
+ char str[32];
+
+ cfb_framebuffer_clear(epd_dev, false);
+
+ len = snprintk(str, sizeof(str),
+ "Own Address: 0x%04x", mesh_get_addr());
+ print_line(FONT_SMALL, line++, str, len, false);
+
+ len = snprintk(str, sizeof(str),
+ "Node Count: %lu", stat_count + 1);
+ print_line(FONT_SMALL, line++, str, len, false);
+
+ /* Find the top sender */
+ for (i = 0; i < ARRAY_SIZE(stats); i++) {
+ int j;
+
+ stat = &stats[i];
+ if (!stat->addr) {
+ break;
+ }
+
+ if (!stat->hello_count) {
+ continue;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(top); j++) {
+ if (top[j] < 0) {
+ top[j] = i;
+ break;
+ }
+
+ if (stat->hello_count <= stats[top[j]].hello_count) {
+ continue;
+ }
+
+ /* Move other elements down the list */
+ if (j < ARRAY_SIZE(top) - 1) {
+ memmove(&top[j + 1], &top[j],
+ ((ARRAY_SIZE(top) - j - 1) *
+ sizeof(top[j])));
+ }
+
+ top[j] = i;
+ break;
+ }
+ }
+
+ if (stat_count >= 0) {
+ len = snprintk(str, sizeof(str), "Most messages from:");
+ print_line(FONT_SMALL, line++, str, len, false);
+
+ for (i = 0; i < ARRAY_SIZE(top); i++) {
+ if (top[i] < 0) {
+ break;
+ }
+
+ stat = &stats[top[i]];
+
+ len = snprintk(str, sizeof(str), "%-3u 0x%04x %s",
+ stat->hello_count, stat->addr,
+ stat->name);
+ print_line(FONT_SMALL, line++, str, len, false);
+ }
+ }
+
+ cfb_framebuffer_finalize(epd_dev);
+}
+
+static void epd_update(struct ble_npl_event *work)
+{
+ char buf[MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH)];
+ int i;
+
+ if (stats_view) {
+ show_statistics();
+ return;
+ }
+
+ strncpy(buf, bt_get_name(), sizeof(buf));
+
+ /* Convert commas to newlines */
+ for (i = 0; buf[i] != '\0'; i++) {
+ if (buf[i] == ',') {
+ buf[i] = '\n';
+ }
+ }
+
+ board_show_text(buf, true, K_FOREVER);
+}
+
+static void long_press(struct ble_npl_event *work)
+{
+ /* Treat as release so actual release doesn't send messages */
+ pressed = false;
+ stats_view = !stats_view;
+ board_refresh_display();
+}
+
+static bool button_is_pressed(void)
+{
+ uint32_t val;
+
+ val = (uint32_t) hal_gpio_read(BUTTON_1);
+
+ return !val;
+}
+
+static void button_interrupt(struct os_event *ev)
+{
+ int pin_pos = (int ) ev->ev_arg;
+
+ if (button_is_pressed() == pressed) {
+ return;
+ }
+
+ pressed = !pressed;
+ printk("Button %s\n", pressed ? "pressed" : "released");
+
+ if (pressed) {
+ k_delayed_work_submit(&long_press_work, LONG_PRESS_TIMEOUT);
+ return;
+ }
+
+ k_delayed_work_cancel(&long_press_work);
+
+ if (!mesh_is_initialized()) {
+ return;
+ }
+
+ /* Short press does currently nothing in statistics view */
+ if (stats_view) {
+ return;
+ }
+
+ if (pin_pos == BUTTON_1) {
+ mesh_send_hello();
+ }
+}
+
+static struct os_event button_event;
+
+static void
+gpio_irq_handler(void *arg)
+{
+ button_event.ev_arg = arg;
+ os_eventq_put(os_eventq_dflt_get(), &button_event);
+}
+
+static int configure_button(void)
+{
+ button_event.ev_cb = button_interrupt;
+
+ hal_gpio_irq_init(BUTTON_1, gpio_irq_handler, (void *) BUTTON_1,
+ HAL_GPIO_TRIG_BOTH, HAL_GPIO_PULL_UP);
+ hal_gpio_irq_enable(BUTTON_1);
+
+ return 0;
+}
+
+static void led_timeout(struct ble_npl_event *work)
+{
+ static int led_cntr;
+ int i;
+
+ /* Disable all LEDs */
+ for (i = 0; i < ARRAY_SIZE(leds); i++) {
+ hal_gpio_write(leds[i].pin, 1);
+ }
+
+ /* Stop after 5 iterations */
+ if (led_cntr > (ARRAY_SIZE(leds) * 5)) {
+ led_cntr = 0;
+ return;
+ }
+
+ /* Select and enable current LED */
+ i = led_cntr++ % ARRAY_SIZE(leds);
+ hal_gpio_write(leds[i].pin, 0);
+
+ k_delayed_work_submit(&led_timer, K_MSEC(100));
+}
+
+static int configure_leds(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(leds); i++) {
+ hal_gpio_init_out(leds[i].pin, 1);
+ }
+
+ k_delayed_work_init(&led_timer, led_timeout);
+ return 0;
+}
+
+static int erase_storage(void)
+{
+ bt_set_name(MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME));
+ ble_store_clear();
+ schedule_mesh_reset();
+ return 0;
+}
+
+void board_refresh_display(void)
+{
+ k_delayed_work_submit(&epd_work, K_NO_WAIT);
+}
+
+int board_init(void)
+{
+ epd_dev = os_dev_lookup(MYNEWT_VAL(SSD1673_OS_DEV_NAME));
+ if (epd_dev == NULL) {
+ printk("SSD1673 device not found\n");
+ return -ENODEV;
+ }
+
+ if (cfb_framebuffer_init(epd_dev)) {
+ printk("Framebuffer initialization failed\n");
+ return -EIO;
+ }
+
+ cfb_framebuffer_clear(epd_dev, true);
+
+ if (configure_button()) {
+ printk("Failed to configure button\n");
+ return -EIO;
+ }
+
+ if (configure_leds()) {
+ printk("LED init failed\n");
+ return -EIO;
+ }
+
+ k_delayed_work_init(&epd_work, epd_update);
+ k_delayed_work_init(&long_press_work, long_press);
+
+ pressed = button_is_pressed();
+ if (pressed) {
+ printk("Erasing storage\n");
+ board_show_text("Resetting Device", false, K_SECONDS(4));
+ erase_storage();
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/apps/mesh_badge/syscfg.yml b/src/libs/mynewt-nimble/apps/mesh_badge/syscfg.yml
new file mode 100644
index 00000000..0c60c016
--- /dev/null
+++ b/src/libs/mynewt-nimble/apps/mesh_badge/syscfg.yml
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Package: apps/mesh_badge
+
+syscfg.vals:
+ # Enable the shell task.
+ SHELL_TASK: 1
+
+ # Set log level to info (disable debug logging).
+ LOG_LEVEL: 1
+
+ # Default task settings
+ OS_MAIN_STACK_SIZE: 768
+
+ # Newtmgr is not supported in this app, so disable newtmgr-over-shell.
+ SHELL_NEWTMGR: 0
+
+ REEL_BOARD_ENABLE_ACTIVE_MODE: 1
+ SPI_0_MASTER: 1
+
+ BLE_MESH: 1
+ MSYS_1_BLOCK_COUNT: 48
+
+ BLE_SVC_GAP_DEVICE_NAME: '"reel board"'
+
+ BLE_SM_SC: 1
+ BLE_SM_BONDING: 1
+ BLE_MESH_RELAY: 1
+ BLE_MESH_GATT_PROXY: 0
+ BLE_MESH_PB_ADV: 0
+ BLE_MESH_PB_GATT: 0
+ BLE_MESH_ADV_BUF_COUNT: 30
+ BLE_MESH_LABEL_COUNT: 0
+ BLE_MESH_CFG_CLI: 1
+ BLE_MESH_TX_SEG_MAX: 6
+ BLE_MESH_TX_SEG_MSG_COUNT: 3
+ BLE_MESH_RX_SEG_MSG_COUNT: 3
+ BLE_MESH_CRPL: 128
+ BLE_MESH_RPL_STORE_TIMEOUT: 120
+ BLE_MESH_SETTINGS: 1
+ CONFIG_NFFS: 1
+ BLE_MESH_PB_ADV: 1 \ No newline at end of file