mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-08-12 21:17:57 +00:00
apps/mesh_badge: Copy files from Zephyr
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
||||
project(NONE)
|
||||
|
||||
target_sources(app PRIVATE src/main.c src/mesh.c src/${BOARD}.c)
|
||||
@@ -0,0 +1,50 @@
|
||||
.. _mesh_badge:
|
||||
|
||||
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
|
||||
@@ -0,0 +1,66 @@
|
||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
|
||||
CONFIG_BT_RX_STACK_SIZE=4096
|
||||
|
||||
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
|
||||
CONFIG_BT_DEVICE_NAME_GATT_WRITABLE=y
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_PRIVACY=y
|
||||
CONFIG_BT_DEVICE_NAME="reel board"
|
||||
CONFIG_BT_DEVICE_NAME_MAX=32
|
||||
|
||||
CONFIG_BT_DEBUG_LOG=y
|
||||
CONFIG_BT_MESH_DEBUG=y
|
||||
#CONFIG_BT_DEBUG_HCI_CORE=y
|
||||
#CONFIG_BT_MESH_DEBUG_ADV=y
|
||||
|
||||
CONFIG_BT_OBSERVER=y
|
||||
CONFIG_BT_BROADCASTER=y
|
||||
CONFIG_BT_RX_BUF_COUNT=30
|
||||
CONFIG_BT_L2CAP_RX_MTU=69
|
||||
CONFIG_BT_L2CAP_TX_MTU=69
|
||||
CONFIG_BT_L2CAP_TX_BUF_COUNT=8
|
||||
|
||||
CONFIG_BT_MESH=y
|
||||
CONFIG_BT_MESH_RELAY=y
|
||||
#CONFIG_BT_MESH_GATT_PROXY=y
|
||||
CONFIG_BT_MESH_PB_ADV=n
|
||||
CONFIG_BT_MESH_PB_GATT=n
|
||||
CONFIG_BT_MESH_ADV_BUF_COUNT=30
|
||||
CONFIG_BT_MESH_LABEL_COUNT=0
|
||||
CONFIG_BT_MESH_CFG_CLI=y
|
||||
CONFIG_BT_MESH_TX_SEG_MAX=6
|
||||
CONFIG_BT_MESH_TX_SEG_MSG_COUNT=3
|
||||
CONFIG_BT_MESH_RX_SEG_MSG_COUNT=3
|
||||
CONFIG_BT_MESH_CRPL=128
|
||||
CONFIG_BT_MESH_RPL_STORE_TIMEOUT=120
|
||||
|
||||
CONFIG_PRINTK=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_STDOUT_CONSOLE=y
|
||||
|
||||
CONFIG_I2C=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_SENSOR=y
|
||||
|
||||
CONFIG_APDS9960=y
|
||||
|
||||
CONFIG_HDC1008=y
|
||||
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_ASYNC=y
|
||||
|
||||
CONFIG_HEAP_MEM_POOL_SIZE=16384
|
||||
CONFIG_DISPLAY=y
|
||||
CONFIG_SSD1673=y
|
||||
|
||||
CONFIG_CHARACTER_FRAMEBUFFER=y
|
||||
|
||||
CONFIG_BT_SETTINGS=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_PAGE_LAYOUT=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_FCB=y
|
||||
CONFIG_SETTINGS=y
|
||||
CONFIG_SETTINGS_FCB=y
|
||||
@@ -0,0 +1,8 @@
|
||||
sample:
|
||||
description: reel board mesh badge sample
|
||||
name: mesh badge
|
||||
tests:
|
||||
test:
|
||||
platform_whitelist: reel_board
|
||||
tags: samples sensor
|
||||
harness: sensor
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Phytec Messtechnik GmbH
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
void board_refresh_display(void);
|
||||
void board_show_text(const char *text, bool center, s32_t duration);
|
||||
void board_blink_leds(void);
|
||||
int board_init(void);
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <misc/printk.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <settings/settings.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/gatt.h>
|
||||
|
||||
#include "mesh.h"
|
||||
#include "board.h"
|
||||
|
||||
static const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
|
||||
};
|
||||
|
||||
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 ssize_t read_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
void *buf, u16_t len, u16_t offset)
|
||||
{
|
||||
const char *value = bt_get_name();
|
||||
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
|
||||
strlen(value));
|
||||
}
|
||||
|
||||
static ssize_t write_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
const void *buf, u16_t len, u16_t offset,
|
||||
u8_t flags)
|
||||
{
|
||||
char name[CONFIG_BT_DEVICE_NAME_MAX];
|
||||
int err;
|
||||
|
||||
if (offset) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
|
||||
if (len >= CONFIG_BT_DEVICE_NAME_MAX) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
}
|
||||
|
||||
memcpy(name, buf, len);
|
||||
name[len] = '\0';
|
||||
|
||||
err = bt_set_name(name);
|
||||
if (err) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
|
||||
}
|
||||
|
||||
mesh_set_name(name, first_name_len(name));
|
||||
board_refresh_display();
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct bt_uuid_128 name_uuid = BT_UUID_INIT_128(
|
||||
0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
||||
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
||||
|
||||
static struct bt_uuid_128 name_enc_uuid = BT_UUID_INIT_128(
|
||||
0xf1, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
||||
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
||||
|
||||
#define CPF_FORMAT_UTF8 0x19
|
||||
|
||||
static const struct bt_gatt_cpf name_cpf = {
|
||||
.format = CPF_FORMAT_UTF8,
|
||||
};
|
||||
|
||||
/* Vendor Primary Service Declaration */
|
||||
static struct bt_gatt_attr name_attrs[] = {
|
||||
/* Vendor Primary Service Declaration */
|
||||
BT_GATT_PRIMARY_SERVICE(&name_uuid),
|
||||
BT_GATT_CHARACTERISTIC(&name_enc_uuid.uuid,
|
||||
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
|
||||
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT,
|
||||
read_name, write_name, NULL),
|
||||
BT_GATT_CUD("Badge Name", BT_GATT_PERM_READ),
|
||||
BT_GATT_CPF(&name_cpf),
|
||||
};
|
||||
|
||||
static struct bt_gatt_service name_svc = BT_GATT_SERVICE(name_attrs);
|
||||
|
||||
static void passkey_display(struct bt_conn *conn, unsigned int passkey)
|
||||
{
|
||||
char buf[20];
|
||||
|
||||
snprintk(buf, sizeof(buf), "Passkey:\n%06u", passkey);
|
||||
|
||||
printk("%s\n", buf);
|
||||
board_show_text(buf, false, K_FOREVER);
|
||||
}
|
||||
|
||||
static void passkey_cancel(struct bt_conn *conn)
|
||||
{
|
||||
printk("Cancel\n");
|
||||
}
|
||||
|
||||
static void pairing_complete(struct bt_conn *conn, bool bonded)
|
||||
{
|
||||
printk("Pairing Complete\n");
|
||||
board_show_text("Pairing Complete", false, K_SECONDS(2));
|
||||
}
|
||||
|
||||
static void pairing_failed(struct bt_conn *conn)
|
||||
{
|
||||
printk("Pairing Failed\n");
|
||||
board_show_text("Pairing Failed", false, K_SECONDS(2));
|
||||
}
|
||||
|
||||
const struct bt_conn_auth_cb auth_cb = {
|
||||
.passkey_display = passkey_display,
|
||||
.cancel = passkey_cancel,
|
||||
.pairing_complete = pairing_complete,
|
||||
.pairing_failed = pairing_failed,
|
||||
};
|
||||
|
||||
static void connected(struct bt_conn *conn, u8_t 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(struct bt_conn *conn, u8_t reason)
|
||||
{
|
||||
printk("Disconnected (reason 0x%02x)\n", reason);
|
||||
|
||||
if (strcmp(CONFIG_BT_DEVICE_NAME, bt_get_name()) &&
|
||||
!mesh_is_initialized()) {
|
||||
/* Mesh will take over advertising control */
|
||||
bt_le_adv_stop();
|
||||
mesh_start();
|
||||
} else {
|
||||
board_show_text("Disconnected", false, K_SECONDS(2));
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_conn_cb conn_cb = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
static void bt_ready(int err)
|
||||
{
|
||||
if (err) {
|
||||
printk("Bluetooth init failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
err = mesh_init();
|
||||
if (err) {
|
||||
printk("Initializing mesh failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Mesh initialized\n");
|
||||
|
||||
bt_conn_cb_register(&conn_cb);
|
||||
bt_conn_auth_cb_register(&auth_cb);
|
||||
|
||||
bt_gatt_service_register(&name_svc);
|
||||
|
||||
if (IS_ENABLED(CONFIG_SETTINGS)) {
|
||||
settings_load();
|
||||
}
|
||||
|
||||
if (!mesh_is_initialized()) {
|
||||
/* Start advertising */
|
||||
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME,
|
||||
ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (err) {
|
||||
printk("Advertising failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const char *name = bt_get_name();
|
||||
|
||||
printk("Already provisioned\n");
|
||||
|
||||
mesh_set_name(name, first_name_len(name));
|
||||
}
|
||||
|
||||
board_refresh_display();
|
||||
|
||||
printk("Board started\n");
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = board_init();
|
||||
if (err) {
|
||||
printk("board init failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Starting Board Demo\n");
|
||||
|
||||
/* Initialize the Bluetooth Subsystem */
|
||||
err = bt_enable(bt_ready);
|
||||
if (err) {
|
||||
printk("Bluetooth init failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <string.h>
|
||||
#include <misc/printk.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/mesh.h>
|
||||
#include <bluetooth/hci.h>
|
||||
|
||||
#include "mesh.h"
|
||||
#include "board.h"
|
||||
|
||||
#define MOD_LF 0x0000
|
||||
#define OP_LF 0xbb
|
||||
#define OP_VENDOR_MSG BT_MESH_MODEL_OP_3(OP_LF, BT_COMP_ID_LF)
|
||||
|
||||
#define DEFAULT_TTL 7
|
||||
#define GROUP_ADDR 0xc123
|
||||
#define NET_IDX 0x000
|
||||
#define APP_IDX 0x000
|
||||
#define FLAGS 0
|
||||
|
||||
static struct k_work publish_work;
|
||||
static struct k_work mesh_start_work;
|
||||
|
||||
static void heartbeat(u8_t hops, u16_t feat)
|
||||
{
|
||||
board_show_text("Heartbeat Received", false, K_SECONDS(2));
|
||||
}
|
||||
|
||||
static struct bt_mesh_cfg_srv cfg_srv = {
|
||||
.relay = BT_MESH_RELAY_ENABLED,
|
||||
.beacon = BT_MESH_BEACON_ENABLED,
|
||||
.default_ttl = DEFAULT_TTL,
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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,
|
||||
};
|
||||
|
||||
BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
static void vnd_message(struct bt_mesh_model *model,
|
||||
struct bt_mesh_msg_ctx *ctx,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
char str[32];
|
||||
size_t len;
|
||||
|
||||
printk("Vendor 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->len, 8);
|
||||
memcpy(str, buf->data, len);
|
||||
strcpy(str + len, " says hi!");
|
||||
|
||||
board_show_text(str, false, K_SECONDS(3));
|
||||
|
||||
board_blink_leds();
|
||||
}
|
||||
|
||||
static const struct bt_mesh_model_op vnd_ops[] = {
|
||||
{ OP_VENDOR_MSG, 1, vnd_message },
|
||||
BT_MESH_MODEL_OP_END,
|
||||
};
|
||||
|
||||
/* Limit the payload to 11 bytes so that it doesn't get segmented */
|
||||
BT_MESH_MODEL_PUB_DEFINE(vnd_pub, NULL, 3 + 8);
|
||||
|
||||
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 void publish_message(struct k_work *work)
|
||||
{
|
||||
if (bt_mesh_model_publish(&vnd_models[0]) == 0) {
|
||||
board_show_text("Saying \"hi!\" to everyone", false,
|
||||
K_SECONDS(2));
|
||||
} else {
|
||||
board_show_text("Sending Failed!", false, K_SECONDS(2));
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_publish(void)
|
||||
{
|
||||
k_work_submit(&publish_work);
|
||||
}
|
||||
|
||||
static int provision_and_configure(void)
|
||||
{
|
||||
static const u8_t net_key[16] = {
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
|
||||
};
|
||||
static const u8_t app_key[16] = {
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
};
|
||||
static const u16_t iv_index;
|
||||
struct bt_mesh_cfg_mod_pub pub = {
|
||||
.addr = GROUP_ADDR,
|
||||
.app_idx = APP_IDX,
|
||||
.ttl = DEFAULT_TTL,
|
||||
};
|
||||
u8_t dev_key[16];
|
||||
u16_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 k_work *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);
|
||||
}
|
||||
|
||||
void mesh_set_name(const char *name, size_t len)
|
||||
{
|
||||
bt_mesh_model_msg_init(vnd_pub.msg, OP_VENDOR_MSG);
|
||||
len = min(len, net_buf_simple_tailroom(vnd_pub.msg));
|
||||
net_buf_simple_add_mem(vnd_pub.msg, name, len);
|
||||
}
|
||||
|
||||
bool mesh_is_initialized(void)
|
||||
{
|
||||
return elements[0].addr != BT_MESH_ADDR_UNASSIGNED;
|
||||
}
|
||||
|
||||
int mesh_init(void)
|
||||
{
|
||||
static const u8_t dev_uuid[16] = { 0xc0, 0xff, 0xee };
|
||||
static const struct bt_mesh_prov prov = {
|
||||
.uuid = dev_uuid,
|
||||
};
|
||||
|
||||
k_work_init(&publish_work, publish_message);
|
||||
k_work_init(&mesh_start_work, start_mesh);
|
||||
|
||||
return bt_mesh_init(&prov, &comp);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
void mesh_publish(void);
|
||||
void mesh_set_name(const char *name, size_t len);
|
||||
|
||||
bool mesh_is_initialized(void);
|
||||
void mesh_start(void);
|
||||
int mesh_init(void);
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Phytec Messtechnik GmbH
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <device.h>
|
||||
#include <gpio.h>
|
||||
#include <display/cfb.h>
|
||||
#include <misc/printk.h>
|
||||
#include <flash.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
||||
#include "mesh.h"
|
||||
#include "board.h"
|
||||
|
||||
#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
|
||||
|
||||
#define LINE_MAX 12
|
||||
|
||||
static struct device *epd_dev;
|
||||
static bool pressed;
|
||||
static struct device *gpio;
|
||||
static struct k_delayed_work epd_work;
|
||||
|
||||
static struct {
|
||||
struct device *dev;
|
||||
const char *name;
|
||||
u32_t pin;
|
||||
} leds[] = {
|
||||
{ .name = LED0_GPIO_CONTROLLER, .pin = LED0_GPIO_PIN, },
|
||||
{ .name = LED1_GPIO_CONTROLLER, .pin = LED1_GPIO_PIN, },
|
||||
{ .name = LED2_GPIO_CONTROLLER, .pin = LED2_GPIO_PIN, },
|
||||
{ .name = LED3_GPIO_CONTROLLER, .pin = LED3_GPIO_PIN, },
|
||||
};
|
||||
|
||||
struct k_delayed_work led_timer;
|
||||
|
||||
static size_t print_line(int row, const char *text, size_t len, bool center)
|
||||
{
|
||||
u8_t font_height, font_width;
|
||||
u8_t line[LINE_MAX + 1];
|
||||
int pad;
|
||||
|
||||
len = min(len, LINE_MAX);
|
||||
memcpy(line, text, len);
|
||||
line[len] = '\0';
|
||||
|
||||
if (center) {
|
||||
pad = (LINE_MAX - len) / 2;
|
||||
} else {
|
||||
pad = 0;
|
||||
}
|
||||
|
||||
cfb_get_font_size(epd_dev, 0, &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(const char *text)
|
||||
{
|
||||
const char *space = NULL;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i <= LINE_MAX; 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 LINE_MAX;
|
||||
}
|
||||
|
||||
void board_blink_leds(void)
|
||||
{
|
||||
k_delayed_work_submit(&led_timer, K_MSEC(100));
|
||||
}
|
||||
|
||||
void board_show_text(const char *text, bool center, s32_t duration)
|
||||
{
|
||||
int i;
|
||||
|
||||
cfb_framebuffer_set_font(epd_dev, 0);
|
||||
cfb_framebuffer_clear(epd_dev, false);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
size_t len;
|
||||
|
||||
while (*text == ' ' || *text == '\n') {
|
||||
text++;
|
||||
}
|
||||
|
||||
len = get_len(text);
|
||||
if (!len) {
|
||||
break;
|
||||
}
|
||||
|
||||
text += print_line(i, text, len, center);
|
||||
if (!*text) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cfb_framebuffer_finalize(epd_dev);
|
||||
|
||||
if (duration != K_FOREVER) {
|
||||
k_delayed_work_submit(&epd_work, duration);
|
||||
}
|
||||
}
|
||||
|
||||
static void epd_update(struct k_work *work)
|
||||
{
|
||||
char buf[CONFIG_BT_DEVICE_NAME_MAX];
|
||||
int i;
|
||||
|
||||
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 bool button_is_pressed(void)
|
||||
{
|
||||
u32_t val;
|
||||
|
||||
gpio_pin_read(gpio, SW0_GPIO_PIN, &val);
|
||||
|
||||
return !val;
|
||||
}
|
||||
|
||||
static void button_interrupt(struct device *dev, struct gpio_callback *cb,
|
||||
u32_t pins)
|
||||
{
|
||||
if (button_is_pressed() == pressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
pressed = !pressed;
|
||||
printk("Button %s\n", pressed ? "pressed" : "released");
|
||||
|
||||
/* We only care about button release for now */
|
||||
if (pressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mesh_is_initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pins & BIT(SW0_GPIO_PIN)) {
|
||||
mesh_publish();
|
||||
}
|
||||
}
|
||||
|
||||
static int configure_button(void)
|
||||
{
|
||||
static struct gpio_callback button_cb;
|
||||
|
||||
gpio = device_get_binding(GPIO_KEYS_BUTTON_0_GPIO_CONTROLLER);
|
||||
if (!gpio) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpio_pin_configure(gpio, SW0_GPIO_PIN,
|
||||
(GPIO_DIR_IN | GPIO_INT | PULL_UP | EDGE));
|
||||
|
||||
gpio_init_callback(&button_cb, button_interrupt, BIT(SW0_GPIO_PIN));
|
||||
gpio_add_callback(gpio, &button_cb);
|
||||
|
||||
gpio_pin_enable_callback(gpio, SW0_GPIO_PIN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void led_timeout(struct k_work *work)
|
||||
{
|
||||
static int led_cntr;
|
||||
int i;
|
||||
|
||||
/* Disable all LEDs */
|
||||
for (i = 0; i < ARRAY_SIZE(leds); i++) {
|
||||
gpio_pin_write(leds[i].dev, 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);
|
||||
gpio_pin_write(leds[i].dev, 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++) {
|
||||
leds[i].dev = device_get_binding(leds[i].name);
|
||||
if (!leds[i].dev) {
|
||||
printk("Failed to get %s device\n", leds[i].name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpio_pin_configure(leds[i].dev, leds[i].pin, GPIO_DIR_OUT);
|
||||
gpio_pin_write(leds[i].dev, leds[i].pin, 1);
|
||||
|
||||
}
|
||||
|
||||
k_delayed_work_init(&led_timer, led_timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int erase_storage(void)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = device_get_binding(FLASH_DEV_NAME);
|
||||
|
||||
return flash_erase(dev, FLASH_AREA_STORAGE_OFFSET,
|
||||
FLASH_AREA_STORAGE_SIZE);
|
||||
}
|
||||
|
||||
void board_refresh_display(void)
|
||||
{
|
||||
k_delayed_work_submit(&epd_work, K_NO_WAIT);
|
||||
}
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
epd_dev = device_get_binding(CONFIG_SSD1673_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);
|
||||
|
||||
pressed = button_is_pressed();
|
||||
if (pressed) {
|
||||
printk("Erasing storage\n");
|
||||
board_show_text("Resetting Device", false, K_SECONDS(4));
|
||||
erase_storage();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user