diff options
author | Jean-François Milants <jf@codingfield.com> | 2021-02-02 22:09:00 +0100 |
---|---|---|
committer | Jean-François Milants <jf@codingfield.com> | 2021-02-02 22:09:00 +0100 |
commit | d90b7274fa8bbfa09f79660b45b550d91f7b0125 (patch) | |
tree | 434e4aa362b0083eb9df7bea4f1358787174e5b4 /src/libs/mynewt-nimble/apps/mesh_badge/src | |
parent | 9c35b6fe5dc889b589b979dd7c650c70f302854b (diff) |
Update to nimble 1.3 master branch commit 82153e744833821e20e9a8b0d61c38b2b0dbcfe1
WARNING : heartbeat task is disabled!
Diffstat (limited to 'src/libs/mynewt-nimble/apps/mesh_badge/src')
-rw-r--r-- | src/libs/mynewt-nimble/apps/mesh_badge/src/board.h | 15 | ||||
-rw-r--r-- | src/libs/mynewt-nimble/apps/mesh_badge/src/gatt_svr.c | 226 | ||||
-rw-r--r-- | src/libs/mynewt-nimble/apps/mesh_badge/src/main.c | 393 | ||||
-rw-r--r-- | src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.c | 313 | ||||
-rw-r--r-- | src/libs/mynewt-nimble/apps/mesh_badge/src/mesh.h | 14 | ||||
-rw-r--r-- | src/libs/mynewt-nimble/apps/mesh_badge/src/mesh_badge.h | 28 | ||||
-rw-r--r-- | src/libs/mynewt-nimble/apps/mesh_badge/src/reel_board.c | 508 |
7 files changed, 1497 insertions, 0 deletions
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; +} |