samples: onoff-app: Add OnOff Model for the Nordic nRF52840-PDK board

Signed-off-by: Steve Brown <sbrown@cortland.com>

---

The updates to BlueZ' meshctl have not been submitted.

I am submitting this PR only for review. It could be committed
after the patches for the meshctl commands are applied.
This commit is contained in:
Steve Brown 2017-12-08 03:08:19 -07:00 committed by Johan Hedberg
commit bce1b974f0
5 changed files with 911 additions and 0 deletions

View file

@ -0,0 +1,8 @@
set(QEMU_EXTRA_FLAGS -s)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(NONE)
target_link_libraries(app subsys__bluetooth)
target_sources(app PRIVATE src/main.c)

View file

@ -0,0 +1,101 @@
.. _bluetooth-mesh-onoff-sample:
Bluetooth: Mesh OnOff Model
###########################
Overview
********
This is a simple application demonstrating a Bluetooth mesh multi-element node.
Each element has a mesh onoff client and server
model which controls one of the 4 sets of buttons and LEDs .
Prior to provisioning, an unprovisioned beacon is broadcast that contains
a unique UUID. It is obtained from the device address set by Nordic in the
FICR. Each button controls the state of its
corresponding LED and does not initiate any mesh activity.
The models for button 1 and LED 1 are in the node's root element.
The 3 remaining button/LED pairs are in elements 1 through 3.
Assuming the provisioner assigns 0x100 to the root element,
the secondary elements will appear at 0x101, 0x102 and 0x103.
After provisioning, the button clients must
be configured to publish and the LED servers to subscribe.
If a LED server is provided with a publish address, it will
also publish its status on an onoff state change.
If a button is pressed only once within a 1 second interval, it sends an
"on" message. If it is pressed more than once, it
sends an "off" message. The buttons are quite noisy and are debounced in
the button_pressed() interrupt handler. An interrupt within 250ms of the
previous is discarded. This might seem a little clumsy, but the alternative of
using one button for "on" and another for "off" would reduce the number
of onoff clients from 4 to 2.
Requirements
************
This sample has been tested on the Nordic nRF52840-PDK board, but would
likely also run on the nrf52_pca10040 board.
Building and Running
********************
This sample can be found under :file:`samples/boards/nrf52/mesh/onoff-app` in the
Zephyr tree.
The following commands build the application.
.. zephyr-app-commands::
:zephyr-app: samples/boards/nrf52/mesh/onoff-app
:board: nrf52840_pca10056
:goals: build flash
:compact:
Prior to provisioning, each button controls its corresponding LED as one
would expect with an actual switch.
Provisioning is done using the BlueZ meshctl utility. Below is an example that
binds button 2 and LED 1 to application key 1. It then configures button 2
to publish to group 0xc000 and LED 1 to subscribe to that group.
.. code-block:: console
discover-unprovisioned on
provision <discovered UUID>
menu config
target 0100
appkey-add 1
bind 0 1 1000 # bind appkey 1 to LED server on element 0 (unicast 0100)
sub-add 0100 c000 1000 # add subscription to group address c000 to the LED server
bind 1 1 1001 # bind appkey 1 to button 2 on element 1 (unicast 0101)
pub-set 0101 c000 1 0 0 1001 # publish button 2 to group address c000
The meshctl utility maintains a persistent JSON database containing
the mesh configuration. As additional nodes (boards) are provisioned, it
assigns sequential unicast addresses based on the number of elements
supported by the node. This example supports 4 elements per node.
The first or root element of the node contains models for configuration,
health, and onoff. The secondary elements only
have models for onoff. The meshctl target for configuration must be the
root element's unicast address as it is the only one that has a
configuration server model.
If meshctl is gracefully exited, it can be restarted and reconnected to
network 0x0. The board configuration is volatile and if the board is reset,
power cycled, or reprogrammed, it will have to be provisioned and configured
again.
The meshctl utility also supports a onoff model client that can be used to
change the state of any LED that is bound to application key 0x1.
This is done by setting the target to the unicast address of the element
that has that LED's model and issuing the onoff command.
Group addresses are not supported.
This application was derived from the sample mesh skeleton at
:file:`samples/bluetooth/mesh`.
See :ref:`bluetooth setup section <bluetooth_setup>` for details.

View file

@ -0,0 +1,98 @@
#CONFIG_INIT_STACKS=y # if set, lots of noise from mesh/adv.c: STACK_ANALYZE
CONFIG_MAIN_STACK_SIZE=512
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_BOOT_BANNER=y
CONFIG_BUILD_TIMESTAMP=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_BROADCASTER=y
CONFIG_BT_CTLR_DUP_FILTER_LEN=0
CONFIG_BT_CTLR_LE_ENC=n
CONFIG_BT_CTLR_LE_PING=n
CONFIG_BT_CTLR_DATA_LENGTH=n
CONFIG_BT_CTLR_PHY=n
CONFIG_BT_CTLR_CHAN_SEL_2=n
CONFIG_BT_CTLR_MIN_USED_CHAN=n
CONFIG_BT_CTLR_ADV_EXT=n
CONFIG_BT_CTLR_PRIVACY=n
CONFIG_BT_PERIPHERAL=y
CONFIG_BT=y
CONFIG_BT_TINYCRYPT_ECC=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_LOW_POWER=n
CONFIG_BT_MESH_FRIEND=n
CONFIG_BT_MESH_FRIEND_QUEUE_SIZE=16
CONFIG_BT_MESH_ADV_BUF_COUNT=20
CONFIG_BT_MESH_PB_GATT=y
CONFIG_BT_MESH_PB_ADV=y
CONFIG_BT_MESH_GATT_PROXY=y
CONFIG_BT_MESH_CFG_CLI=y
#CONFIG_BT_MESH_LPN_SCAN_LATENCY=30
#CONFIG_BT_MESH_LPN_RECV_DELAY=40
#CONFIG_BT_MESH_LPN_POLL_TIMEOUT=300
CONFIG_BT_MESH_SUBNET_COUNT=2
CONFIG_BT_MESH_APP_KEY_COUNT=2
CONFIG_BT_MESH_MODEL_KEY_COUNT=2
CONFIG_BT_MESH_MODEL_GROUP_COUNT=2
CONFIG_BT_MESH_LABEL_COUNT=3
#CONFIG_BT_MESH_IV_UPDATE_TEST=y
CONFIG_UART_CONSOLE=y
# this outputs btmon formatted data to the serial port
#CONFIG_BT_DEBUG_MONITOR=y
#CONFIG_SYS_LOG=y
CONFIG_SYS_LOG_DEFAULT_LEVEL=4
CONFIG_SYS_LOG_EXT_HOOK=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_MESH_DEBUG=y
#CONFIG_BT_MESH_DEBUG_PROV=y
#CONFIG_BT_MESH_DEBUG_PROXY=y
#CONFIG_BT_MESH_DEBUG_BEACON=y
#CONFIG_BT_MESH_DEBUG_NET=y
#CONFIG_BT_MESH_DEBUG_TRANS=y
#CONFIG_BT_MESH_DEBUG_LOW_POWER=y
#CONFIG_BT_MESH_DEBUG_FRIEND=y
#CONFIG_BT_MESH_DEBUG_MODEL=y
#CONFIG_BT_MESH_DEBUG_ACCESS=y
#CONFIG_BT_MESH_DEBUG_CRYPTO=y
#CONFIG_BT_MESH_DEBUG_ADV=y
#CONFIG_BT_MESH_SELF_TEST=y
#CONFIG_BT_HCI_VS_EXT=n
#CONFIG_STACK_USAGE=y
CONFIG_BT_RX_STACK_SIZE=4096
#CONFIG_BT_DEBUG_HCI_DRIVER=y
#CONFIG_BT_DEBUG_HCI_CORE=y
#CONFIG_BT_DEBUG_GATT=y
#CONFIG_BT_DEBUG_CONN=y
CONFIG_BT_MAX_CONN=1
CONFIG_BT_CTLR_RX_BUFFERS=6
CONFIG_BT_CTLR_TX_BUFFERS=4
CONFIG_BT_HCI_CMD_COUNT=4
CONFIG_BT_ATT_PREPARE_COUNT=2
#CONFIG_NET_BUF_SIMPLE_LOG=y
#CONFIG_SYS_LOG_NET_BUF_LEVEL=4
#CONFIG_NET_BUF_LOG=y
#CONFIG_BT_CTLR_DEBUG_PINS=y

View file

@ -0,0 +1,7 @@
sample:
name: Bluetooth Mesh
tests:
- test:
build_only: true
platform_whitelist: nrf52840-pca10056
tags: bluetooth

View file

@ -0,0 +1,697 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This application is specific to the Nordic nRF52840-PDK board.
*
* It supports the 4 buttons and 4 LEDs as mesh clients and servers.
*
* Prior to provisioning, a button inverts the state of the
* corresponding LED.
*
* The unprovisioned beacon uses the device address set by Nordic
* in the FICR as its UUID and is presumed unique.
*
* Button and LED 1 are in the root node.
* The 3 remaining button/LED pairs are in element 1 through 3.
* Assuming the provisioner assigns 0x100 to the root node,
* the secondary elements will appear at 0x101, 0x102 and 0x103.
*
* It's anticipated that after provisioning, the button clients would
* be configured to publish and the LED servers to subscribe.
*
* If a LED server is provided with a publish address, it will
* also publish its status on a state change.
*
* Messages from a button to its corresponding LED are ignored as
* the LED's state has already been changed locally by the button client.
*
* The buttons are debounced at a nominal 250ms. That value can be
* changed as needed.
*
*/
#include <misc/printk.h>
#include <misc/byteorder.h>
#include <nrf.h>
#include <device.h>
#include <gpio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/hci.h>
#include <bluetooth/mesh.h>
#include <stdio.h>
#include <board.h>
#define CID_INTEL 0x0002
/*
* The include must follow the define for it to take effect.
* If it isn't, the domain defaults to "general"
*/
#define SYS_LOG_DOMAIN "OnOff"
#include <logging/sys_log.h>
/* Model Operation Codes */
#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf);
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf);
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf);
static void gen_onoff_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf);
/*
* Server Configuration Declaration
*/
static struct bt_mesh_cfg_srv cfg_srv = {
.relay = BT_MESH_RELAY_DISABLED,
.beacon = BT_MESH_BEACON_ENABLED,
#if defined(CONFIG_BT_MESH_FRIEND)
.frnd = BT_MESH_FRIEND_ENABLED,
#else
.frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
#endif
#if defined(CONFIG_BT_MESH_GATT_PROXY)
.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
#else
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
.default_ttl = 7,
/* 3 transmissions with 20ms interval */
.net_transmit = BT_MESH_TRANSMIT(2, 20),
.relay_retransmit = BT_MESH_TRANSMIT(2, 20),
};
/*
* Client Configuration Declaration
*/
static struct bt_mesh_cfg_cli cfg_cli = {
};
/*
* Health Server Declaration
*/
static struct bt_mesh_health_srv health_srv = {
};
/*
* Publication Declarations
*
* The publication messages are initialized to the
* the size of the opcode + content
*
* The messages are in static storage because NET_BUF_SIMPLE()
* only allocates on the stack if called within a function.
* For publication, the message must be in static or global as
* it is re-transmitted several times. This occurs
* after the function that called bt_mesh_model_publish() has
* exited and the stack is no longer valid.
*
* Note that the additional 4 bytes for the AppMIC is not needed
* because it is added to a stack variable at the time a
* transmission occurs.
*
*/
static struct bt_mesh_model_pub health_pub = {
.msg = BT_MESH_HEALTH_FAULT_MSG(0),
};
static struct bt_mesh_model_pub gen_onoff_pub_srv = {
.msg = NET_BUF_SIMPLE(2 + 2),
};
static struct bt_mesh_model_pub gen_onoff_pub_cli = {
.msg = NET_BUF_SIMPLE(2 + 2),
};
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_0 = {
.msg = NET_BUF_SIMPLE(2 + 2),
};
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_0 = {
.msg = NET_BUF_SIMPLE(2 + 2),
};
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_1 = {
.msg = NET_BUF_SIMPLE(2 + 2),
};
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_1 = {
.msg = NET_BUF_SIMPLE(2 + 2),
};
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_2 = {
.msg = NET_BUF_SIMPLE(2 + 2),
};
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_2 = {
.msg = NET_BUF_SIMPLE(2 + 2),
};
/*
* Models in an element must have unique op codes.
*
* The mesh stack dispatches a message to the first model in an element
* that is also bound to an app key and supports the op code in the
* received message.
*
*/
/*
* OnOff Model Server Op Dispatch Table
*
*/
static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
{ BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get },
{ BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set },
{ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack },
BT_MESH_MODEL_OP_END,
};
/*
* OnOff Model Client Op Dispatch Table
*/
static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
{ BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, gen_onoff_status },
BT_MESH_MODEL_OP_END,
};
struct onoff_state {
u8_t current;
u8_t previous;
u8_t led_gpio_pin;
struct device *led_device;
};
/*
* Declare and Initialize Element Contexts
* Change to select different GPIO output pins
*/
static struct onoff_state onoff_state[] = {
{ .led_gpio_pin = LED0_GPIO_PIN },
{ .led_gpio_pin = LED1_GPIO_PIN },
{ .led_gpio_pin = LED2_GPIO_PIN },
{ .led_gpio_pin = LED3_GPIO_PIN },
};
/*
*
* Element Model Declarations
*
* Element 0 Root Models
*/
static struct bt_mesh_model root_models[] = {
BT_MESH_MODEL_CFG_SRV(&cfg_srv),
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv, &onoff_state[0]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli, &onoff_state[0]),
};
/*
* Element 1 Models
*/
static struct bt_mesh_model secondary_0_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_0, &onoff_state[1]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_0, &onoff_state[1]),
};
/*
* Element 2 Models
*/
static struct bt_mesh_model secondary_1_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_1, &onoff_state[2]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_1, &onoff_state[2]),
};
/*
* Element 3 Models
*/
static struct bt_mesh_model secondary_2_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_2, &onoff_state[3]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_2, &onoff_state[3]),
};
/*
* Button to Client Model Assignments
*/
struct bt_mesh_model *mod_cli_sw[] = {
&root_models[4],
&secondary_0_models[1],
&secondary_1_models[1],
&secondary_2_models[1],
};
/*
* LED to Server Model Assigmnents
*/
struct bt_mesh_model *mod_srv_sw[] = {
&root_models[3],
&secondary_0_models[0],
&secondary_1_models[0],
&secondary_2_models[0],
};
/*
* Root and Secondary Element Declarations
*/
static struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_0_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_1_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_2_models, BT_MESH_MODEL_NONE),
};
static const struct bt_mesh_comp comp = {
.cid = CID_INTEL,
.elem = elements,
.elem_count = ARRAY_SIZE(elements),
};
struct device *sw_device;
struct sw {
u8_t sw_num;
u8_t onoff_state;
struct k_work button_work;
struct k_timer button_timer;
};
static u8_t button_press_cnt;
static struct sw sw;
static struct gpio_callback button_cb;
static u8_t trans_id;
static u32_t time, last_time;
static u16_t primary_addr;
static u16_t primary_net_idx;
/*
* Generic OnOff Model Server Message Handlers
*
* Mesh Model Specification 3.1.1
*
*/
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct net_buf_simple *msg = NET_BUF_SIMPLE(2 + 1 + 4);
struct onoff_state *onoff_state = model->user_data;
SYS_LOG_INF("addr 0x%04x onoff 0x%02x",
model->elem->addr, onoff_state->current);
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
net_buf_simple_add_u8(msg, onoff_state->current);
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
SYS_LOG_ERR("Unable to send On Off Status response");
}
}
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct net_buf_simple *msg = model->pub->msg;
struct onoff_state *onoff_state = model->user_data;
int err;
onoff_state->current = net_buf_simple_pull_u8(buf);
SYS_LOG_INF("addr 0x%02x state 0x%02x",
model->elem->addr, onoff_state->current);
/* Pin set low turns on LED's on the nrf52840-pca10056 board */
gpio_pin_write(onoff_state->led_device,
onoff_state->led_gpio_pin,
onoff_state->current ? 0 : 1);
/*
* If a server has a publish address, it is required to
* publish status on a state change
*
* See Mesh Profile Specification 3.7.6.1.2
*
* Only publish if there is an assigned address
*/
if (onoff_state->previous != onoff_state->current &&
model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
SYS_LOG_INF("publish last 0x%02x cur 0x%02x",
onoff_state->previous,
onoff_state->current);
onoff_state->previous = onoff_state->current;
bt_mesh_model_msg_init(msg,
BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
net_buf_simple_add_u8(msg, onoff_state->current);
err = bt_mesh_model_publish(model);
if (err) {
SYS_LOG_ERR("bt_mesh_model_publish err %d", err);
}
}
}
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
SYS_LOG_INF("");
gen_onoff_set_unack(model, ctx, buf);
gen_onoff_get(model, ctx, buf);
}
static void gen_onoff_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
u8_t state;
state = net_buf_simple_pull_u8(buf);
SYS_LOG_INF("Node 0x%04x OnOff status from 0x%04x with state 0x%02x",
model->elem->addr, ctx->addr, state);
}
static int output_number(bt_mesh_output_action_t action, uint32_t number)
{
SYS_LOG_INF("OOB Number %u", number);
return 0;
}
static int output_string(const char *str)
{
SYS_LOG_INF("OOB String %s", str);
return 0;
}
static void prov_complete(u16_t net_idx, u16_t addr)
{
SYS_LOG_INF("provisioning complete for net_idx 0x%04x addr 0x%04x",
net_idx, addr);
primary_addr = addr;
primary_net_idx = net_idx;
}
static void prov_reset(void)
{
bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
}
static u8_t dev_uuid[16] = { 0xdd, 0xdd };
#define BUTTON_DEBOUNCE_DELAY_MS 250
/*
* Map GPIO pins to button number
* Change to select different GPIO input pins
*/
static uint8_t pin_to_sw(uint32_t pin_pos)
{
switch (pin_pos) {
case BIT(SW0_GPIO_PIN): return 0;
case BIT(SW1_GPIO_PIN): return 1;
case BIT(SW2_GPIO_PIN): return 2;
case BIT(SW3_GPIO_PIN): return 3;
}
SYS_LOG_ERR("No match for GPIO pin 0x%08x", pin_pos);
return 0;
}
static void button_pressed(struct device *dev, struct gpio_callback *cb,
uint32_t pin_pos)
{
/*
* One button press within a 1 second interval sends an on message
* More than one button press sends an off message
*/
time = k_uptime_get_32();
/* debounce the switch */
if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) {
last_time = time;
return;
}
if (button_press_cnt == 0) {
k_timer_start(&sw.button_timer, K_SECONDS(1), 0);
}
SYS_LOG_INF("button_press_cnt 0x%02x", button_press_cnt);
button_press_cnt++;
/* The variable pin_pos is the pin position in the GPIO register,
* not the pin number. It's assumed that only one bit is set.
*/
sw.sw_num = pin_to_sw(pin_pos);
last_time = time;
}
/*
* Button Count Timer Worker
*/
static void button_cnt_timer(struct k_timer *work)
{
struct sw *button_sw = CONTAINER_OF(work, struct sw, button_timer);
button_sw->onoff_state = button_press_cnt == 1 ? 1 : 0;
SYS_LOG_INF("button_press_cnt 0x%02x onoff_state 0x%02x",
button_press_cnt, button_sw->onoff_state);
button_press_cnt = 0;
k_work_submit(&sw.button_work);
}
/*
* Button Pressed Worker Task
*/
static void button_pressed_worker(struct k_work *work)
{
struct bt_mesh_model *mod_cli, *mod_srv;
struct bt_mesh_model_pub *pub_cli, *pub_srv;
struct sw *sw = CONTAINER_OF(work, struct sw, button_work);
int err;
u8_t sw_idx = sw->sw_num;
mod_cli = mod_cli_sw[sw_idx];
pub_cli = mod_cli->pub;
mod_srv = mod_srv_sw[sw_idx];
pub_srv = mod_srv->pub;
/* If unprovisioned, just call the set function.
* The intent is to have switch-like behavior
* prior to provisioning. Once provisioned,
* the button and its corresponding led are no longer
* associated and act independently. So, if a button is to
* control its associated led after provisioning, the button
* must be configured to either publish to the led's unicast
* address or a group to which the led is subscribed.
*/
if (primary_addr == BT_MESH_ADDR_UNASSIGNED) {
struct net_buf_simple *msg = NET_BUF_SIMPLE(1);
struct bt_mesh_msg_ctx ctx = {
.addr = sw_idx + primary_addr,
};
/* This is a dummy message sufficient
* for the led server
*/
net_buf_simple_init(msg, 0);
net_buf_simple_add_u8(msg, sw->onoff_state);
gen_onoff_set_unack(mod_srv, &ctx, msg);
return;
}
if (pub_cli->addr == BT_MESH_ADDR_UNASSIGNED) {
return;
}
SYS_LOG_INF("publish to 0x%04x onoff 0x%04x sw_idx 0x%04x",
pub_cli->addr, sw->onoff_state, sw_idx);
bt_mesh_model_msg_init(pub_cli->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET);
net_buf_simple_add_u8(pub_cli->msg, sw->onoff_state);
net_buf_simple_add_u8(pub_cli->msg, trans_id++);
err = bt_mesh_model_publish(mod_cli);
if (err) {
SYS_LOG_ERR("bt_mesh_model_publish err %d", err);
}
}
/* Disable OOB security for SILabs Android app */
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
#if 1
.output_size = 6,
.output_actions = (BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING),
.output_number = output_number,
.output_string = output_string,
#else
.output_size = 0,
.output_actions = 0,
.output_number = 0,
#endif
.complete = prov_complete,
.reset = prov_reset,
};
/*
* Bluetooth Ready Callback
*/
static void bt_ready(int err)
{
struct bt_le_oob oob;
if (err) {
SYS_LOG_ERR("Bluetooth init failed (err %d", err);
return;
}
SYS_LOG_INF("Bluetooth initialized");
/* Use identity address as device UUID */
if (bt_le_oob_get_local(&oob)) {
SYS_LOG_ERR("Identity Address unavailable");
} else {
memcpy(dev_uuid, oob.addr.a.val, 6);
}
err = bt_mesh_init(&prov, &comp);
if (err) {
SYS_LOG_ERR("Initializing mesh failed (err %d)", err);
return;
}
bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
SYS_LOG_INF("Mesh initialized");
}
void init_led(u8_t dev, const char *port, u32_t pin_num)
{
onoff_state[dev].led_device = device_get_binding(port);
gpio_pin_configure(onoff_state[dev].led_device,
pin_num, GPIO_DIR_OUT | GPIO_PUD_PULL_UP);
gpio_pin_write(onoff_state[dev].led_device, pin_num, 1);
}
/*
* Log functions to identify output with the node
*/
void log_cbuf_put(const char *format, ...)
{
va_list args;
char buf[250];
va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
printk("[%04x] %s", primary_addr, buf);
}
void main(void)
{
int err;
/*
* Initialize the logger hook
*/
syslog_hook_install(log_cbuf_put);
SYS_LOG_INF("Initializing...");
/* Initialize the button debouncer */
last_time = k_uptime_get_32();
/* Initialize button worker task*/
k_work_init(&sw.button_work, button_pressed_worker);
/* Initialize button count timer */
k_timer_init(&sw.button_timer, button_cnt_timer, NULL);
sw_device = device_get_binding(SW0_GPIO_NAME);
gpio_pin_configure(sw_device, SW0_GPIO_PIN,
(GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
GPIO_INT_ACTIVE_LOW | GPIO_PUD_PULL_UP));
gpio_pin_configure(sw_device, SW1_GPIO_PIN,
(GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
GPIO_INT_ACTIVE_LOW | GPIO_PUD_PULL_UP));
gpio_pin_configure(sw_device, SW2_GPIO_PIN,
(GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
GPIO_INT_ACTIVE_LOW | GPIO_PUD_PULL_UP));
gpio_pin_configure(sw_device, SW3_GPIO_PIN,
(GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
GPIO_INT_ACTIVE_LOW | GPIO_PUD_PULL_UP));
gpio_init_callback(&button_cb, button_pressed,
BIT(SW0_GPIO_PIN) | BIT(SW1_GPIO_PIN) |
BIT(SW2_GPIO_PIN) | BIT(SW3_GPIO_PIN));
gpio_add_callback(sw_device, &button_cb);
gpio_pin_enable_callback(sw_device, SW0_GPIO_PIN);
gpio_pin_enable_callback(sw_device, SW1_GPIO_PIN);
gpio_pin_enable_callback(sw_device, SW2_GPIO_PIN);
gpio_pin_enable_callback(sw_device, SW3_GPIO_PIN);
/* Initialize LED's */
init_led(0, LED0_GPIO_PORT, LED0_GPIO_PIN);
init_led(1, LED1_GPIO_PORT, LED1_GPIO_PIN);
init_led(2, LED2_GPIO_PORT, LED2_GPIO_PIN);
init_led(3, LED3_GPIO_PORT, LED3_GPIO_PIN);
/* Initialize the Bluetooth Subsystem */
err = bt_enable(bt_ready);
if (err) {
SYS_LOG_ERR("Bluetooth init failed (err %d)", err);
}
}