ble mesh chat sample to send 255 bytes of data using nRF21540dk/nRF52840 where build 2 configurations for nRF2140dk/nRF52840, build3 is for enabling FEM using nRF52840dk/nRF52840 + shield nRF21540ek
This commit is contained in:
commit
b77fd5457c
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# editors
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
|
||||||
|
# build
|
||||||
|
/build*/
|
19
CMakeLists.txt
Normal file
19
CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(NONE)
|
||||||
|
|
||||||
|
# NORDIC SDK APP START
|
||||||
|
target_sources(app PRIVATE
|
||||||
|
src/main.c
|
||||||
|
src/model_handler.c
|
||||||
|
src/chat_cli.c)
|
||||||
|
target_include_directories(app PRIVATE include)
|
||||||
|
|
||||||
|
|
||||||
|
# NORDIC SDK APP END
|
33
Kconfig
Normal file
33
Kconfig
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2020 Nordic Semiconductor
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
source "Kconfig.zephyr"
|
||||||
|
|
||||||
|
menu "Nordic Bluetooth Mesh chat client sample"
|
||||||
|
|
||||||
|
config BT_MESH_CHAT_CLI_MESSAGE_LENGTH
|
||||||
|
int "Max length of the message to be sent over mesh"
|
||||||
|
default 90
|
||||||
|
range 0 255
|
||||||
|
help
|
||||||
|
Impacts on memory size occupied by the buffer of a message to be
|
||||||
|
published. This does not include null terminator. The message must also
|
||||||
|
fit into the application SDU. Adjust BT_MESH_TX_SEG_MAX and
|
||||||
|
BT_MESH_RX_SEG_MAX to be able to receive the longest messages.
|
||||||
|
|
||||||
|
config BT_MESH_CHAT_SAMPLE_PRESENCE_CACHE_SIZE
|
||||||
|
int "Presence cache size"
|
||||||
|
default 10
|
||||||
|
range 0 32767
|
||||||
|
help
|
||||||
|
Presence cache stores previously received presence of chat clients.
|
||||||
|
Recommended to be as big as number of chat clients in the mesh network.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
module = BT_MESH_CHAT_CLI
|
||||||
|
module-str = BT Mesh Chat Client model
|
||||||
|
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"
|
13
Kconfig.sysbuild
Normal file
13
Kconfig.sysbuild
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2023 Nordic Semiconductor
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
|
||||||
|
source "${ZEPHYR_BASE}/share/sysbuild/Kconfig"
|
||||||
|
|
||||||
|
config NRF_DEFAULT_IPC_RADIO
|
||||||
|
default y
|
||||||
|
|
||||||
|
config NETCORE_IPC_RADIO_BT_HCI_IPC
|
||||||
|
default y
|
21
README.rst
Normal file
21
README.rst
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.. _bt_mesh_chat:
|
||||||
|
|
||||||
|
Bluetooth Mesh: Chat
|
||||||
|
####################
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
:depth: 2
|
||||||
|
|
||||||
|
The Bluetooth® Mesh chat sample demonstrates how to use the mesh network to facilitate communication between nodes by text, using the :ref:`bt_mesh_chat_client_model`.
|
||||||
|
|
||||||
|
See the subpages for detailed documentation on the sample and its internal model.
|
||||||
|
|
||||||
|
.. _mesh_chat_subpages:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:caption: Subpages:
|
||||||
|
|
||||||
|
sample_description.rst
|
||||||
|
chat_cli.rst
|
5
VERSION
Normal file
5
VERSION
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
VERSION_MAJOR = 2
|
||||||
|
VERSION_MINOR = 9
|
||||||
|
PATCHLEVEL = 0
|
||||||
|
VERSION_TWEAK = 0
|
||||||
|
EXTRAVERSION =
|
9
boards/nrf52dk_nrf52832.conf
Normal file
9
boards/nrf52dk_nrf52832.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
# Application overlay - nrf52dk_nrf52832
|
||||||
|
|
||||||
|
CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE=n
|
19
boards/nrf54l15dk_nrf54l15_cpuapp.conf
Normal file
19
boards/nrf54l15dk_nrf54l15_cpuapp.conf
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
# Application overlay - nrf54l15
|
||||||
|
|
||||||
|
CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE=n
|
||||||
|
CONFIG_NVS=n
|
||||||
|
CONFIG_NVS_LOOKUP_CACHE=n
|
||||||
|
CONFIG_SETTINGS_NVS_NAME_CACHE=n
|
||||||
|
CONFIG_ZMS=y
|
||||||
|
CONFIG_SETTINGS_ZMS_SECTOR_COUNT=8
|
||||||
|
CONFIG_ZMS_LOOKUP_CACHE=y
|
||||||
|
CONFIG_ZMS_LOOKUP_CACHE_SIZE=512
|
||||||
|
CONFIG_SETTINGS_ZMS_NAME_CACHE=y
|
||||||
|
CONFIG_SETTINGS_ZMS_NAME_CACHE_SIZE=512
|
||||||
|
CONFIG_ZMS_LOOKUP_CACHE_FOR_SETTINGS=y
|
110
chat_cli.rst
Normal file
110
chat_cli.rst
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
.. _bt_mesh_chat_client_model:
|
||||||
|
|
||||||
|
Chat Client model
|
||||||
|
#################
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
:depth: 2
|
||||||
|
|
||||||
|
The Chat Client model is a vendor model that allows communication with other such models, by sending text messages and providing the presence of the model instance.
|
||||||
|
It demonstrates basics of a vendor model implementation.
|
||||||
|
The model doesn't have a limitation on per-node instantiations of the model, and therefore can be instantiated on each element of the node.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
********
|
||||||
|
|
||||||
|
In this section, you can find more detailed information about the following aspects of the Chat Client:
|
||||||
|
|
||||||
|
* `Composition data structure`_
|
||||||
|
* `Messages`_
|
||||||
|
|
||||||
|
.. _bt_mesh_chat_client_model_composition:
|
||||||
|
|
||||||
|
Composition data structure
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The Chat Client model is a vendor model, and therefore in the application, when defining the node composition data, it needs to be declared in the third argument in the :c:macro:`BT_MESH_ELEM` macro:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
static struct bt_mesh_elem elements[] = {
|
||||||
|
BT_MESH_ELEM(1,
|
||||||
|
BT_MESH_MODEL_LIST(
|
||||||
|
BT_MESH_MODEL_CFG_SRV(&cfg_srv),
|
||||||
|
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub)),
|
||||||
|
BT_MESH_MODEL_LIST(
|
||||||
|
BT_MESH_MODEL_CHAT_CLI(&chat))),
|
||||||
|
};
|
||||||
|
|
||||||
|
.. _bt_mesh_chat_client_model_messages:
|
||||||
|
|
||||||
|
Messages
|
||||||
|
========
|
||||||
|
|
||||||
|
The Chat Client model defines the following messages:
|
||||||
|
|
||||||
|
Presence
|
||||||
|
Used to report the current model presence.
|
||||||
|
When the model periodic publication is configured, the Chat Client model will publish its current presence, regardless of whether it has been changed or not.
|
||||||
|
Presence message has a defined length of 1 byte.
|
||||||
|
|
||||||
|
Presence Get
|
||||||
|
Used to retrieve the current model presence.
|
||||||
|
Upon receiving the Presence Get message, the Chat Client model will send the Presence message with the current model presence stored in the response.
|
||||||
|
The message doesn't have any payload.
|
||||||
|
|
||||||
|
Message
|
||||||
|
Used to send a non-private text message.
|
||||||
|
The payload consists of the text string terminated by ``\0``.
|
||||||
|
The length of the text string can be configured at the compile-time using :ref:`CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH <CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH>` option.
|
||||||
|
|
||||||
|
Private Message
|
||||||
|
Used to send a private text message.
|
||||||
|
When the model receives this message, it replies with the Message Reply.
|
||||||
|
The payload consists of the text string terminated by ``\0``.
|
||||||
|
The length of the text string can be configured at the compile-time using :ref:`CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH <CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH>` option.
|
||||||
|
|
||||||
|
Message Reply
|
||||||
|
Used to reply on the received Private Message to confirm the reception.
|
||||||
|
The message doesn't have any payload.
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
*************
|
||||||
|
|config|
|
||||||
|
|
||||||
|
Configuration options
|
||||||
|
=====================
|
||||||
|
|
||||||
|
The following configuration parameters are associated with the Chat Client model:
|
||||||
|
|
||||||
|
.. _CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH:
|
||||||
|
|
||||||
|
CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH - Message length configuration
|
||||||
|
Maximum length of the message to be sent over the mesh network.
|
||||||
|
|
||||||
|
.. _bt_mesh_chat_client_model_states:
|
||||||
|
|
||||||
|
States
|
||||||
|
******
|
||||||
|
|
||||||
|
The Chat Client model contains the following states:
|
||||||
|
|
||||||
|
Presence: ``bt_mesh_chat_cli_presence``:
|
||||||
|
The Chat Client model enables a user to set a current presence of the client instantiated on the element of the node.
|
||||||
|
It can have the following values:
|
||||||
|
|
||||||
|
* :c:enumerator:`BT_MESH_CHAT_CLI_PRESENCE_AVAILABLE` - The client is available.
|
||||||
|
* :c:enumerator:`BT_MESH_CHAT_CLI_PRESENCE_AWAY` - The client is away.
|
||||||
|
* :c:enumerator:`BT_MESH_CHAT_CLI_PRESENCE_INACTIVE` - The client is inactive.
|
||||||
|
* :c:enumerator:`BT_MESH_CHAT_CLI_PRESENCE_DO_NOT_DISTURB` - The client is in "do not disturb" state.
|
||||||
|
|
||||||
|
Extended models
|
||||||
|
***************
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
Persistent storage
|
||||||
|
******************
|
||||||
|
|
||||||
|
If :kconfig:option:`CONFIG_BT_SETTINGS` is enabled, the Chat Client stores its presence state.
|
222
include/chat_cli.h
Normal file
222
include/chat_cli.h
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @defgroup bt_mesh_chat_cli
|
||||||
|
* @{
|
||||||
|
* @brief API for the Bluetooth Mesh Chat Client model.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BT_MESH_CHAT_CLI_H__
|
||||||
|
#define BT_MESH_CHAT_CLI_H__
|
||||||
|
|
||||||
|
#include <zephyr/bluetooth/mesh.h>
|
||||||
|
#include <bluetooth/mesh/model_types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_1 */
|
||||||
|
/** Company ID of the Bluetooth Mesh Chat Client model. */
|
||||||
|
#define BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID CONFIG_BT_COMPANY_ID_NORDIC
|
||||||
|
|
||||||
|
/** Model ID of the Bluetooth Mesh Chat Client model. */
|
||||||
|
#define BT_MESH_CHAT_CLI_VENDOR_MODEL_ID 0x000A
|
||||||
|
|
||||||
|
/** Non-private message opcode. */
|
||||||
|
#define BT_MESH_CHAT_CLI_OP_MESSAGE BT_MESH_MODEL_OP_3(0x0A, \
|
||||||
|
BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID)
|
||||||
|
|
||||||
|
/** Private message opcode. */
|
||||||
|
#define BT_MESH_CHAT_CLI_OP_PRIVATE_MESSAGE BT_MESH_MODEL_OP_3(0x0B, \
|
||||||
|
BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID)
|
||||||
|
|
||||||
|
/** Message reply opcode. */
|
||||||
|
#define BT_MESH_CHAT_CLI_OP_MESSAGE_REPLY BT_MESH_MODEL_OP_3(0x0C, \
|
||||||
|
BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID)
|
||||||
|
|
||||||
|
/** Presence message opcode. */
|
||||||
|
#define BT_MESH_CHAT_CLI_OP_PRESENCE BT_MESH_MODEL_OP_3(0x0D, \
|
||||||
|
BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID)
|
||||||
|
|
||||||
|
/** Presence get message opcode. */
|
||||||
|
#define BT_MESH_CHAT_CLI_OP_PRESENCE_GET BT_MESH_MODEL_OP_3(0x0E, \
|
||||||
|
BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID)
|
||||||
|
/* .. include_endpoint_chat_cli_rst_1 */
|
||||||
|
|
||||||
|
#define BT_MESH_CHAT_CLI_MSG_MINLEN_MESSAGE 1
|
||||||
|
#define BT_MESH_CHAT_CLI_MSG_MAXLEN_MESSAGE (\
|
||||||
|
CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH \
|
||||||
|
+ 1) /* + \0 */
|
||||||
|
#define BT_MESH_CHAT_CLI_MSG_LEN_MESSAGE_REPLY 0
|
||||||
|
#define BT_MESH_CHAT_CLI_MSG_LEN_PRESENCE 1
|
||||||
|
#define BT_MESH_CHAT_CLI_MSG_LEN_PRESENCE_GET 0
|
||||||
|
|
||||||
|
/** Bluetooth Mesh Chat Client Presence values. */
|
||||||
|
enum bt_mesh_chat_cli_presence {
|
||||||
|
BT_MESH_CHAT_CLI_PRESENCE_AVAILABLE,
|
||||||
|
BT_MESH_CHAT_CLI_PRESENCE_AWAY,
|
||||||
|
BT_MESH_CHAT_CLI_PRESENCE_INACTIVE,
|
||||||
|
BT_MESH_CHAT_CLI_PRESENCE_DO_NOT_DISTURB
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Forward declaration of the Bluetooth Mesh Chat Client model context. */
|
||||||
|
struct bt_mesh_chat_cli;
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_2 */
|
||||||
|
/** @def BT_MESH_MODEL_CHAT_CLI
|
||||||
|
*
|
||||||
|
* @brief Bluetooth Mesh Chat Client model composition data entry.
|
||||||
|
*
|
||||||
|
* @param[in] _chat Pointer to a @ref bt_mesh_chat_cli instance.
|
||||||
|
*/
|
||||||
|
#define BT_MESH_MODEL_CHAT_CLI(_chat) \
|
||||||
|
BT_MESH_MODEL_VND_CB(BT_MESH_CHAT_CLI_VENDOR_COMPANY_ID, \
|
||||||
|
BT_MESH_CHAT_CLI_VENDOR_MODEL_ID, \
|
||||||
|
_bt_mesh_chat_cli_op, &(_chat)->pub, \
|
||||||
|
BT_MESH_MODEL_USER_DATA(struct bt_mesh_chat_cli, \
|
||||||
|
_chat), \
|
||||||
|
&_bt_mesh_chat_cli_cb)
|
||||||
|
/* .. include_endpoint_chat_cli_rst_2 */
|
||||||
|
|
||||||
|
/** Bluetooth Mesh Chat Client model handlers. */
|
||||||
|
struct bt_mesh_chat_cli_handlers {
|
||||||
|
/** @brief Called after the node has been provisioned, or after all
|
||||||
|
* mesh data has been loaded from persistent storage.
|
||||||
|
*
|
||||||
|
* @param[in] cli Chat Client instance that has been started.
|
||||||
|
*/
|
||||||
|
void (*const start)(struct bt_mesh_chat_cli *chat);
|
||||||
|
|
||||||
|
/** @brief Handler for a presence message.
|
||||||
|
*
|
||||||
|
* @param[in] cli Chat client instance that received the text message.
|
||||||
|
* @param[in] ctx Context of the incoming message.
|
||||||
|
* @param[in] presence Presence of a Chat Client that published
|
||||||
|
* the message.
|
||||||
|
*/
|
||||||
|
void (*const presence)(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
|
enum bt_mesh_chat_cli_presence presence);
|
||||||
|
|
||||||
|
/** @brief Handler for a non-private text message.
|
||||||
|
*
|
||||||
|
* @param[in] cli Chat client instance that received the text message.
|
||||||
|
* @param[in] ctx Context of the incoming message.
|
||||||
|
* @param[in] msg Pointer to a received text message terminated with
|
||||||
|
* a null character, '\0'.
|
||||||
|
*/
|
||||||
|
void (*const message)(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
|
const uint8_t *msg);
|
||||||
|
|
||||||
|
/** @brief Handler for a private message.
|
||||||
|
*
|
||||||
|
* @param[in] cli Chat client that received the text message.
|
||||||
|
* @param[in] ctx Context of the incoming message.
|
||||||
|
* @param[in] msg Pointer to a received text message terminated with
|
||||||
|
* a null character, '\0'.
|
||||||
|
*/
|
||||||
|
void (*const private_message)(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
|
const uint8_t *msg);
|
||||||
|
|
||||||
|
/** @brief Handler for a reply on a private message.
|
||||||
|
*
|
||||||
|
* @param[in] cli Chat client instance that received the reply.
|
||||||
|
* @param[in] ctx Context of the incoming message.
|
||||||
|
*/
|
||||||
|
void (*const message_reply)(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_3 */
|
||||||
|
/**
|
||||||
|
* Bluetooth Mesh Chat Client model context.
|
||||||
|
*/
|
||||||
|
struct bt_mesh_chat_cli {
|
||||||
|
/** Access model pointer. */
|
||||||
|
const struct bt_mesh_model *model;
|
||||||
|
/** Publish parameters. */
|
||||||
|
struct bt_mesh_model_pub pub;
|
||||||
|
/** Publication message. */
|
||||||
|
struct net_buf_simple pub_msg;
|
||||||
|
/** Publication message buffer. */
|
||||||
|
uint8_t buf[BT_MESH_MODEL_BUF_LEN(BT_MESH_CHAT_CLI_OP_MESSAGE,
|
||||||
|
BT_MESH_CHAT_CLI_MSG_MAXLEN_MESSAGE)];
|
||||||
|
/** Handler function structure. */
|
||||||
|
const struct bt_mesh_chat_cli_handlers *handlers;
|
||||||
|
/** Current Presence value. */
|
||||||
|
enum bt_mesh_chat_cli_presence presence;
|
||||||
|
};
|
||||||
|
/* .. include_endpoint_chat_cli_rst_3 */
|
||||||
|
|
||||||
|
/** @brief Set the client presence and publish the presence to the mesh network.
|
||||||
|
*
|
||||||
|
* @param[in] chat Chat Client model instance to set presence on.
|
||||||
|
* @param[in] presence Presence status to be published.
|
||||||
|
*
|
||||||
|
* @retval 0 Successfully set the preceive and sent the message.
|
||||||
|
* @retval -EADDRNOTAVAIL Publishing is not configured.
|
||||||
|
* @retval -EAGAIN The device has not been provisioned.
|
||||||
|
*/
|
||||||
|
int bt_mesh_chat_cli_presence_set(struct bt_mesh_chat_cli *chat,
|
||||||
|
enum bt_mesh_chat_cli_presence presence);
|
||||||
|
|
||||||
|
/** @brief Get current presence value of a chat client.
|
||||||
|
*
|
||||||
|
* @param[in] chat Chat Client model instance to send the message.
|
||||||
|
* @param[in] addr Address of the chat client to get presence value of.
|
||||||
|
*
|
||||||
|
* @retval 0 Successfully sent the message.
|
||||||
|
* @retval -EINVAL The model is not bound to an application key.
|
||||||
|
* @retval -EAGAIN The device has not been provisioned.
|
||||||
|
*/
|
||||||
|
int bt_mesh_chat_cli_presence_get(struct bt_mesh_chat_cli *chat,
|
||||||
|
uint16_t addr);
|
||||||
|
|
||||||
|
/** @brief Send a text message.
|
||||||
|
*
|
||||||
|
* @param[in] cli Chat Client model instance to send the message.
|
||||||
|
* @param[in] msg Pointer to a text message to send. Must be terminated with
|
||||||
|
* a null character, '\0'.
|
||||||
|
*
|
||||||
|
* @retval 0 Successfully sent the message.
|
||||||
|
* @retval -EADDRNOTAVAIL Publishing is not configured.
|
||||||
|
* @retval -EAGAIN The device has not been provisioned.
|
||||||
|
*/
|
||||||
|
int bt_mesh_chat_cli_message_send(struct bt_mesh_chat_cli *chat,
|
||||||
|
const uint8_t *msg);
|
||||||
|
|
||||||
|
/** @brief Send a text message to a specified destination.
|
||||||
|
*
|
||||||
|
* @param[in] cli Chat Client model instance to send the message.
|
||||||
|
* @param[in] addr Address of the chat client to send message to.
|
||||||
|
* @param[in] msg Pointer to a text message to send. Must be terminated with
|
||||||
|
* a null character, '\0'.
|
||||||
|
*
|
||||||
|
* @retval 0 Successfully sent the message.
|
||||||
|
* @retval -EINVAL The model is not bound to an application key.
|
||||||
|
* @retval -EAGAIN The device has not been provisioned.
|
||||||
|
*/
|
||||||
|
int bt_mesh_chat_cli_private_message_send(struct bt_mesh_chat_cli *chat,
|
||||||
|
uint16_t addr,
|
||||||
|
const uint8_t *msg);
|
||||||
|
|
||||||
|
/** @cond INTERNAL_HIDDEN */
|
||||||
|
extern const struct bt_mesh_model_op _bt_mesh_chat_cli_op[];
|
||||||
|
extern const struct bt_mesh_model_cb _bt_mesh_chat_cli_cb;
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BT_MESH_CHAT_CLI_H__ */
|
||||||
|
|
||||||
|
/** @} */
|
27
include/model_handler.h
Normal file
27
include/model_handler.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief Model handler
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODEL_HANDLER_H__
|
||||||
|
#define MODEL_HANDLER_H__
|
||||||
|
|
||||||
|
#include <zephyr/bluetooth/mesh.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct bt_mesh_comp *model_handler_init(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MODEL_HANDLER_H__ */
|
12
nrf52840dk_nrf52840.overlay
Normal file
12
nrf52840dk_nrf52840.overlay
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// To get started, press Ctrl+Space to bring up the completion menu and view the available nodes.
|
||||||
|
|
||||||
|
// You can also use the buttons in the sidebar to perform actions on nodes.
|
||||||
|
// Actions currently available include:
|
||||||
|
|
||||||
|
// * Enabling / disabling the node
|
||||||
|
// * Adding the bus to a bus
|
||||||
|
// * Removing the node
|
||||||
|
// * Connecting ADC channels
|
||||||
|
|
||||||
|
// For more help, browse the DeviceTree documentation at https://docs.zephyrproject.org/latest/guides/dts/index.html
|
||||||
|
// You can also visit the nRF DeviceTree extension documentation at https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/guides/ncs_configure_app.html#devicetree-support-in-the-extension
|
72
prj.conf
Normal file
72
prj.conf
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
#
|
||||||
|
CONFIG_NCS_SAMPLES_DEFAULTS=y
|
||||||
|
|
||||||
|
# Deferred logging helps improve LPN power consumption
|
||||||
|
# when friendship is established.
|
||||||
|
CONFIG_LOG_MODE_DEFERRED=y
|
||||||
|
|
||||||
|
# General configuration
|
||||||
|
CONFIG_NCS_APPLICATION_BOOT_BANNER_STRING="Mesh Chat"
|
||||||
|
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
|
||||||
|
CONFIG_FLASH=y
|
||||||
|
CONFIG_FLASH_MAP=y
|
||||||
|
CONFIG_NVS=y
|
||||||
|
CONFIG_SETTINGS=y
|
||||||
|
CONFIG_NVS_LOOKUP_CACHE=y
|
||||||
|
CONFIG_SETTINGS_NVS_NAME_CACHE=y
|
||||||
|
CONFIG_HWINFO=y
|
||||||
|
CONFIG_DK_LIBRARY=y
|
||||||
|
CONFIG_PM_SINGLE_IMAGE=y
|
||||||
|
CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE=0x8000
|
||||||
|
CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE=y
|
||||||
|
|
||||||
|
# Bluetooth configuration
|
||||||
|
CONFIG_BT=y
|
||||||
|
CONFIG_BT_DEVICE_NAME="Mesh Chat"
|
||||||
|
CONFIG_BT_L2CAP_TX_BUF_COUNT=8
|
||||||
|
CONFIG_BT_OBSERVER=y
|
||||||
|
CONFIG_BT_PERIPHERAL=y
|
||||||
|
CONFIG_BT_SETTINGS=y
|
||||||
|
|
||||||
|
# Disable unused Bluetooth features
|
||||||
|
CONFIG_BT_CTLR_LE_ENC=n
|
||||||
|
CONFIG_BT_PHY_UPDATE=n
|
||||||
|
CONFIG_BT_CTLR_MIN_USED_CHAN=n
|
||||||
|
CONFIG_BT_CTLR_PRIVACY=n
|
||||||
|
|
||||||
|
# Bluetooth Mesh configuration
|
||||||
|
CONFIG_BT_MESH=y
|
||||||
|
CONFIG_BT_MESH_RELAY=y
|
||||||
|
CONFIG_BT_MESH_FRIEND=y
|
||||||
|
CONFIG_BT_MESH_RX_SEG_MAX=22
|
||||||
|
CONFIG_BT_MESH_TX_SEG_MAX=22
|
||||||
|
CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH=255
|
||||||
|
CONFIG_BT_MESH_PB_GATT=y
|
||||||
|
CONFIG_BT_MESH_GATT_PROXY=y
|
||||||
|
CONFIG_BT_MESH_DK_PROV=y
|
||||||
|
|
||||||
|
# Enable Bluetooth Mesh models debug logs
|
||||||
|
CONFIG_BT_MESH_LOG_LEVEL_DBG=y
|
||||||
|
|
||||||
|
# Enable Shell module and use UART as a backend
|
||||||
|
CONFIG_SHELL=y
|
||||||
|
CONFIG_SHELL_BACKEND_SERIAL=y
|
||||||
|
CONFIG_FLASH_SHELL=n
|
||||||
|
|
||||||
|
CONFIG_LOG_BACKEND_RTT=n
|
||||||
|
|
||||||
|
# configure 21540dk and enable MPSL and FEM
|
||||||
|
CONFIG_MPSL=y
|
||||||
|
CONFIG_MPSL_FEM=y
|
||||||
|
CONFIG_FEM=y # not set for nrf21540dk
|
||||||
|
CONFIG_MPSL_FEM_NRF21540_GPIO_SPI=y
|
||||||
|
CONFIG_MPSL_FEM_NRF21540_TX_GAIN_DB=20
|
||||||
|
# CONFIG_BT_CTLR_TX_PWR_DBM=0 // not set for nrf21540dk
|
||||||
|
CONFIG_BT_CTLR_TX_PWR_ANTENNA=20
|
||||||
|
|
||||||
|
CONFIG_MPSL_FEM_POWER_MODEL=y
|
||||||
|
CONFIG_MPSL_FEM_POWER_MODEL_NRF21540_USE_BUILTIN=y
|
15
sample.yaml
Normal file
15
sample.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
sample:
|
||||||
|
description: Bluetooth Mesh Chat sample
|
||||||
|
name: Bluetooth Mesh Chat
|
||||||
|
tests:
|
||||||
|
sample.bluetooth.mesh.chat:
|
||||||
|
sysbuild: true
|
||||||
|
build_only: true
|
||||||
|
integration_platforms:
|
||||||
|
- nrf52dk/nrf52832
|
||||||
|
- nrf52840dk/nrf52840
|
||||||
|
- nrf21540dk/nrf52840
|
||||||
|
- nrf54l15dk/nrf54l15/cpuapp
|
||||||
|
platform_allow: nrf52dk/nrf52832 nrf52840dk/nrf52840 nrf21540dk/nrf52840
|
||||||
|
nrf54l15dk/nrf54l15/cpuapp
|
||||||
|
tags: bluetooth ci_build sysbuild
|
227
sample_description.rst
Normal file
227
sample_description.rst
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
.. _bt_mesh_chat_description:
|
||||||
|
|
||||||
|
Sample description
|
||||||
|
##################
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
:depth: 2
|
||||||
|
|
||||||
|
The Bluetooth® Mesh chat sample demonstrates how the mesh network can be used to facilitate communication between nodes by text, using the :ref:`bt_mesh_chat_client_model`.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
************
|
||||||
|
|
||||||
|
The sample supports the following development kits:
|
||||||
|
|
||||||
|
.. table-from-sample-yaml::
|
||||||
|
|
||||||
|
The sample also requires a smartphone with Nordic Semiconductor's nRF Mesh mobile app installed in one of the following versions:
|
||||||
|
|
||||||
|
* `nRF Mesh mobile app for Android`_
|
||||||
|
* `nRF Mesh mobile app for iOS`_
|
||||||
|
|
||||||
|
Overview
|
||||||
|
********
|
||||||
|
|
||||||
|
By means of the mesh network, the clients as mesh nodes can communicate with each other without the need of a server.
|
||||||
|
The mesh chat sample is mainly designed for group communication, but it also supports one-on-one communication, as well as sharing the nodes presence.
|
||||||
|
|
||||||
|
This sample is used in :ref:`ug_bt_mesh_vendor_model` as an example of how to implement a vendor model for the Bluetooth Mesh in |NCS|.
|
||||||
|
|
||||||
|
The clients are nodes with a provisionee role in a mesh network.
|
||||||
|
Provisioning is performed using the `nRF Mesh mobile app`_.
|
||||||
|
This mobile application is also used to configure key bindings, and publication and subscription settings of the Bluetooth Mesh model instances in the sample.
|
||||||
|
After provisioning and configuring the mesh models supported by the sample in the `nRF Mesh mobile app`_, you can communicate with other mesh nodes by sending text messages and obtaining their presence using the :ref:`shell module <shell_api>`.
|
||||||
|
|
||||||
|
Provisioning
|
||||||
|
============
|
||||||
|
|
||||||
|
The provisioning is handled by the :ref:`bt_mesh_dk_prov`.
|
||||||
|
It supports four types of out-of-band (OOB) authentication methods, and uses the Hardware Information driver to generate a deterministic UUID to uniquely represent the device.
|
||||||
|
|
||||||
|
Models
|
||||||
|
======
|
||||||
|
|
||||||
|
The following table shows the Bluetooth Mesh chat composition data for this sample:
|
||||||
|
|
||||||
|
+---------------+
|
||||||
|
| Element 1 |
|
||||||
|
+===============+
|
||||||
|
| Config Server |
|
||||||
|
+---------------+
|
||||||
|
| Health Server |
|
||||||
|
+---------------+
|
||||||
|
| Chat Client |
|
||||||
|
+---------------+
|
||||||
|
|
||||||
|
The models are used for the following purposes:
|
||||||
|
|
||||||
|
* The :ref:`bt_mesh_chat_client_model` instance in the first element is used to communicate with the other Chat Client models instantiated on the other mesh nodes.
|
||||||
|
* Config Server allows configurator devices to configure the node remotely.
|
||||||
|
* Health Server provides ``attention`` callbacks that are used during provisioning to call your attention to the device.
|
||||||
|
These callbacks trigger blinking of the LEDs.
|
||||||
|
|
||||||
|
The model handling is implemented in :file:`src/model_handler.c`.
|
||||||
|
|
||||||
|
User interface
|
||||||
|
**************
|
||||||
|
|
||||||
|
Buttons:
|
||||||
|
Can be used to input the OOB authentication value during provisioning.
|
||||||
|
All buttons have the same functionality during this procedure.
|
||||||
|
|
||||||
|
LEDs:
|
||||||
|
Show the OOB authentication value during provisioning if the "Push button" OOB method is used.
|
||||||
|
|
||||||
|
Terminal emulator:
|
||||||
|
Used for the interaction with the sample.
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
*************
|
||||||
|
|
||||||
|
|config|
|
||||||
|
|
||||||
|
Source file setup
|
||||||
|
=================
|
||||||
|
|
||||||
|
This sample is split into the following source files:
|
||||||
|
|
||||||
|
* A :file:`main.c` file to handle initialization.
|
||||||
|
* A file for handling the Chat Client model, :file:`chat_cli.c`.
|
||||||
|
* A file for handling Bluetooth Mesh models and communication with the :ref:`shell module <shell_api>`, :file:`model_handler.c`.
|
||||||
|
|
||||||
|
FEM support
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: /includes/sample_fem_support.txt
|
||||||
|
|
||||||
|
Building and running
|
||||||
|
********************
|
||||||
|
|
||||||
|
.. |sample path| replace:: :file:`samples/bluetooth/mesh/chat`
|
||||||
|
|
||||||
|
.. include:: /includes/build_and_run.txt
|
||||||
|
|
||||||
|
.. _bluetooth_mesh_chat_testing:
|
||||||
|
|
||||||
|
Testing
|
||||||
|
=======
|
||||||
|
|
||||||
|
After programming the sample to your development kit, you can test it by using a smartphone with `nRF Mesh mobile app`_ installed.
|
||||||
|
Testing consists of provisioning the device and configuring it for communication with other nodes.
|
||||||
|
|
||||||
|
After configuring the device, you can interact with the sample using the terminal emulator.
|
||||||
|
|
||||||
|
Provisioning the device
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. |device name| replace:: :guilabel:`Mesh Chat`
|
||||||
|
|
||||||
|
.. include:: /includes/mesh_device_provisioning.txt
|
||||||
|
|
||||||
|
Configuring models
|
||||||
|
------------------
|
||||||
|
|
||||||
|
See :ref:`ug_bt_mesh_model_config_app` for details on how to configure the mesh models with the nRF Mesh mobile app.
|
||||||
|
|
||||||
|
Create a new group and name it *Chat Channel*, then configure the Vendor model on the **Mesh Chat** node:
|
||||||
|
|
||||||
|
* Bind the model to **Application Key 1**.
|
||||||
|
* Set the publication parameters:
|
||||||
|
|
||||||
|
* Destination/publish address: Select the created group **Chat Channel**.
|
||||||
|
* Publication interval: Set the interval to recommended value of 10 seconds.
|
||||||
|
* Retransmit count: Change the count as preferred.
|
||||||
|
|
||||||
|
* Set the subscription parameters: Select the created group **Chat Channel**.
|
||||||
|
|
||||||
|
Interacting with the sample
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
1. Connect the development kit to the computer using a USB cable.
|
||||||
|
The development kit is assigned a COM port (Windows), ttyACM device (Linux) or tty.usbmodem (MacOS).
|
||||||
|
#. |connect_terminal_specific_ANSI|
|
||||||
|
#. Enable local echo in the terminal to see the text you are typing.
|
||||||
|
|
||||||
|
After completing the steps above, a command can be sent to the sample.
|
||||||
|
The sample supports the following commands:
|
||||||
|
|
||||||
|
chat \-\-help
|
||||||
|
Prints help message together with the list of supported commands.
|
||||||
|
|
||||||
|
chat presence set <presence>
|
||||||
|
Sets presence of the current client.
|
||||||
|
The following values are supported: available, away, dnd, inactive.
|
||||||
|
|
||||||
|
chat presence get <node>
|
||||||
|
Gets presence of a specified chat client.
|
||||||
|
|
||||||
|
chat private <node> <message>
|
||||||
|
Sends a private text message to a specified chat client.
|
||||||
|
Remember to wrap the message in double quotes if it has 2 or more words.
|
||||||
|
|
||||||
|
chat msg <message>
|
||||||
|
Sends a text message to the chat.
|
||||||
|
Remember to wrap the message in double quotes if it has 2 or more words.
|
||||||
|
|
||||||
|
Whenever the node changes its presence, or the local node receives another model's presence the first time, you will see the following message:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
<0x0002> is now available
|
||||||
|
|
||||||
|
When the model receives a message from another node, together with the message you will see the address of the element of the node that sent the message:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
<0x0002>: Hi there!
|
||||||
|
|
||||||
|
The messages posted by the local node will have ``<you>`` instead of the address of the element:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
<you>: Hello, 0x0002!
|
||||||
|
<you> are now away
|
||||||
|
|
||||||
|
Private messages can be identified by the address of the element of the node that posted the message (enclosed in asterisks):
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
<you>: *0x0004* See you!
|
||||||
|
<0x0004>: *you* Bye!
|
||||||
|
|
||||||
|
When the reply is received, you will see the following:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
<0x0004> received the message
|
||||||
|
|
||||||
|
Note that private messages are only seen by those the messages are addressed to.
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
************
|
||||||
|
|
||||||
|
This sample uses the following |NCS| libraries:
|
||||||
|
|
||||||
|
* :ref:`bt_mesh_dk_prov`
|
||||||
|
* :ref:`dk_buttons_and_leds_readme`
|
||||||
|
|
||||||
|
In addition, it uses the following Zephyr libraries:
|
||||||
|
|
||||||
|
* :ref:`zephyr:kernel_api`:
|
||||||
|
|
||||||
|
* :file:`include/kernel.h`
|
||||||
|
|
||||||
|
* :ref:`zephyr:shell_api`:
|
||||||
|
|
||||||
|
* :file:`include/shell.h`
|
||||||
|
* :file:`include/shell_uart.h`
|
||||||
|
|
||||||
|
* :ref:`zephyr:bluetooth_api`:
|
||||||
|
|
||||||
|
* :file:`include/bluetooth/bluetooth.h`
|
||||||
|
|
||||||
|
* :ref:`zephyr:bluetooth_mesh`:
|
||||||
|
|
||||||
|
* :file:`include/bluetooth/mesh.h`
|
320
src/chat_cli.c
Normal file
320
src/chat_cli.c
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/bluetooth/mesh.h>
|
||||||
|
#include "chat_cli.h"
|
||||||
|
#include "mesh/net.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_DECLARE(chat);
|
||||||
|
|
||||||
|
BUILD_ASSERT(BT_MESH_MODEL_BUF_LEN(BT_MESH_CHAT_CLI_OP_MESSAGE,
|
||||||
|
BT_MESH_CHAT_CLI_MSG_MAXLEN_MESSAGE) <=
|
||||||
|
BT_MESH_RX_SDU_MAX,
|
||||||
|
"The message must fit inside an application SDU.");
|
||||||
|
BUILD_ASSERT(BT_MESH_MODEL_BUF_LEN(BT_MESH_CHAT_CLI_OP_MESSAGE,
|
||||||
|
BT_MESH_CHAT_CLI_MSG_MAXLEN_MESSAGE) <=
|
||||||
|
BT_MESH_TX_SDU_MAX,
|
||||||
|
"The message must fit inside an application SDU.");
|
||||||
|
|
||||||
|
static void encode_presence(struct net_buf_simple *buf,
|
||||||
|
enum bt_mesh_chat_cli_presence presence)
|
||||||
|
{
|
||||||
|
bt_mesh_model_msg_init(buf, BT_MESH_CHAT_CLI_OP_PRESENCE);
|
||||||
|
net_buf_simple_add_u8(buf, presence);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t *extract_msg(struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
buf->data[buf->len - 1] = '\0';
|
||||||
|
return net_buf_simple_pull_mem(buf, buf->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_message(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||||||
|
struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
const uint8_t *msg;
|
||||||
|
|
||||||
|
msg = extract_msg(buf);
|
||||||
|
|
||||||
|
if (chat->handlers->message) {
|
||||||
|
chat->handlers->message(chat, ctx, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_1 */
|
||||||
|
static void send_message_reply(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx)
|
||||||
|
{
|
||||||
|
BT_MESH_MODEL_BUF_DEFINE(msg, BT_MESH_CHAT_CLI_OP_MESSAGE_REPLY,
|
||||||
|
BT_MESH_CHAT_CLI_MSG_LEN_MESSAGE_REPLY);
|
||||||
|
bt_mesh_model_msg_init(&msg, BT_MESH_CHAT_CLI_OP_MESSAGE_REPLY);
|
||||||
|
|
||||||
|
(void)bt_mesh_model_send(chat->model, ctx, &msg, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_private_message(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||||||
|
struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
const uint8_t *msg;
|
||||||
|
|
||||||
|
msg = extract_msg(buf);
|
||||||
|
|
||||||
|
if (chat->handlers->private_message) {
|
||||||
|
chat->handlers->private_message(chat, ctx, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
send_message_reply(chat, ctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* .. include_endpoint_chat_cli_rst_1 */
|
||||||
|
|
||||||
|
static int handle_message_reply(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||||||
|
struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
|
||||||
|
if (chat->handlers->message_reply) {
|
||||||
|
chat->handlers->message_reply(chat, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_presence(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||||||
|
struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
enum bt_mesh_chat_cli_presence presence;
|
||||||
|
|
||||||
|
presence = net_buf_simple_pull_u8(buf);
|
||||||
|
|
||||||
|
if (chat->handlers->presence) {
|
||||||
|
chat->handlers->presence(chat, ctx, presence);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_presence_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
|
||||||
|
struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
|
||||||
|
BT_MESH_MODEL_BUF_DEFINE(msg, BT_MESH_CHAT_CLI_OP_PRESENCE,
|
||||||
|
BT_MESH_CHAT_CLI_MSG_LEN_PRESENCE);
|
||||||
|
|
||||||
|
encode_presence(&msg, chat->presence);
|
||||||
|
|
||||||
|
(void)bt_mesh_model_send(chat->model, ctx, &msg, NULL, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_2 */
|
||||||
|
const struct bt_mesh_model_op _bt_mesh_chat_cli_op[] = {
|
||||||
|
{
|
||||||
|
BT_MESH_CHAT_CLI_OP_MESSAGE,
|
||||||
|
BT_MESH_LEN_MIN(BT_MESH_CHAT_CLI_MSG_MINLEN_MESSAGE),
|
||||||
|
handle_message
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BT_MESH_CHAT_CLI_OP_PRIVATE_MESSAGE,
|
||||||
|
BT_MESH_LEN_MIN(BT_MESH_CHAT_CLI_MSG_MINLEN_MESSAGE),
|
||||||
|
handle_private_message
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BT_MESH_CHAT_CLI_OP_MESSAGE_REPLY,
|
||||||
|
BT_MESH_LEN_EXACT(BT_MESH_CHAT_CLI_MSG_LEN_MESSAGE_REPLY),
|
||||||
|
handle_message_reply
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BT_MESH_CHAT_CLI_OP_PRESENCE,
|
||||||
|
BT_MESH_LEN_EXACT(BT_MESH_CHAT_CLI_MSG_LEN_PRESENCE),
|
||||||
|
handle_presence
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BT_MESH_CHAT_CLI_OP_PRESENCE_GET,
|
||||||
|
BT_MESH_LEN_EXACT(BT_MESH_CHAT_CLI_MSG_LEN_PRESENCE_GET),
|
||||||
|
handle_presence_get
|
||||||
|
},
|
||||||
|
BT_MESH_MODEL_OP_END,
|
||||||
|
};
|
||||||
|
/* .. include_endpoint_chat_cli_rst_2 */
|
||||||
|
|
||||||
|
static int bt_mesh_chat_cli_update_handler(const struct bt_mesh_model *model)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
|
||||||
|
/* Continue publishing current presence. */
|
||||||
|
encode_presence(model->pub->msg, chat->presence);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_3 */
|
||||||
|
#ifdef CONFIG_BT_SETTINGS
|
||||||
|
static int bt_mesh_chat_cli_settings_set(const struct bt_mesh_model *model,
|
||||||
|
const char *name,
|
||||||
|
size_t len_rd,
|
||||||
|
settings_read_cb read_cb,
|
||||||
|
void *cb_arg)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t bytes = read_cb(cb_arg, &chat->presence,
|
||||||
|
sizeof(chat->presence));
|
||||||
|
if (bytes < 0) {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes != 0 && bytes != sizeof(chat->presence)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* .. include_endpoint_chat_cli_rst_3 */
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_4 */
|
||||||
|
static int bt_mesh_chat_cli_init(const struct bt_mesh_model *model)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
|
||||||
|
chat->model = model;
|
||||||
|
|
||||||
|
net_buf_simple_init_with_data(&chat->pub_msg, chat->buf,
|
||||||
|
sizeof(chat->buf));
|
||||||
|
chat->pub.msg = &chat->pub_msg;
|
||||||
|
chat->pub.update = bt_mesh_chat_cli_update_handler;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* .. include_endpoint_chat_cli_rst_4 */
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_5 */
|
||||||
|
static int bt_mesh_chat_cli_start(const struct bt_mesh_model *model)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
|
||||||
|
if (chat->handlers->start) {
|
||||||
|
chat->handlers->start(chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* .. include_endpoint_chat_cli_rst_5 */
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_6 */
|
||||||
|
static void bt_mesh_chat_cli_reset(const struct bt_mesh_model *model)
|
||||||
|
{
|
||||||
|
struct bt_mesh_chat_cli *chat = model->rt->user_data;
|
||||||
|
|
||||||
|
chat->presence = BT_MESH_CHAT_CLI_PRESENCE_AVAILABLE;
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||||
|
(void) bt_mesh_model_data_store(model, true, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* .. include_endpoint_chat_cli_rst_6 */
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_7 */
|
||||||
|
const struct bt_mesh_model_cb _bt_mesh_chat_cli_cb = {
|
||||||
|
.init = bt_mesh_chat_cli_init,
|
||||||
|
.start = bt_mesh_chat_cli_start,
|
||||||
|
#ifdef CONFIG_BT_SETTINGS
|
||||||
|
.settings_set = bt_mesh_chat_cli_settings_set,
|
||||||
|
#endif
|
||||||
|
.reset = bt_mesh_chat_cli_reset,
|
||||||
|
};
|
||||||
|
/* .. include_endpoint_chat_cli_rst_7 */
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_8 */
|
||||||
|
int bt_mesh_chat_cli_presence_set(struct bt_mesh_chat_cli *chat,
|
||||||
|
enum bt_mesh_chat_cli_presence presence)
|
||||||
|
{
|
||||||
|
if (presence != chat->presence) {
|
||||||
|
chat->presence = presence;
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||||
|
(void) bt_mesh_model_data_store(chat->model, true,
|
||||||
|
NULL, &presence,
|
||||||
|
sizeof(chat->presence));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_presence(chat->model->pub->msg, chat->presence);
|
||||||
|
|
||||||
|
return bt_mesh_model_publish(chat->model);
|
||||||
|
}
|
||||||
|
/* .. include_endpoint_chat_cli_rst_8 */
|
||||||
|
|
||||||
|
int bt_mesh_chat_cli_presence_get(struct bt_mesh_chat_cli *chat,
|
||||||
|
uint16_t addr)
|
||||||
|
{
|
||||||
|
struct bt_mesh_msg_ctx ctx = {
|
||||||
|
.addr = addr,
|
||||||
|
.app_idx = chat->model->keys[0],
|
||||||
|
.send_ttl = BT_MESH_TTL_DEFAULT,
|
||||||
|
.send_rel = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_CHAT_CLI_OP_PRESENCE_GET,
|
||||||
|
BT_MESH_CHAT_CLI_MSG_LEN_PRESENCE_GET);
|
||||||
|
bt_mesh_model_msg_init(&buf, BT_MESH_CHAT_CLI_OP_PRESENCE_GET);
|
||||||
|
|
||||||
|
return bt_mesh_model_send(chat->model, &ctx, &buf, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_mesh_chat_cli_message_send(struct bt_mesh_chat_cli *chat,
|
||||||
|
const uint8_t *msg)
|
||||||
|
{
|
||||||
|
struct net_buf_simple *buf = chat->model->pub->msg;
|
||||||
|
|
||||||
|
bt_mesh_model_msg_init(buf, BT_MESH_CHAT_CLI_OP_MESSAGE);
|
||||||
|
|
||||||
|
net_buf_simple_add_mem(buf, msg,
|
||||||
|
strnlen(msg,
|
||||||
|
CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH));
|
||||||
|
net_buf_simple_add_u8(buf, '\0');
|
||||||
|
|
||||||
|
return bt_mesh_model_publish(chat->model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .. include_startingpoint_chat_cli_rst_9 */
|
||||||
|
int bt_mesh_chat_cli_private_message_send(struct bt_mesh_chat_cli *chat,
|
||||||
|
uint16_t addr,
|
||||||
|
const uint8_t *msg)
|
||||||
|
{
|
||||||
|
struct bt_mesh_msg_ctx ctx = {
|
||||||
|
.addr = addr,
|
||||||
|
.app_idx = chat->model->keys[0],
|
||||||
|
.send_ttl = BT_MESH_TTL_DEFAULT,
|
||||||
|
.send_rel = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_CHAT_CLI_OP_PRIVATE_MESSAGE,
|
||||||
|
BT_MESH_CHAT_CLI_MSG_MAXLEN_MESSAGE);
|
||||||
|
bt_mesh_model_msg_init(&buf, BT_MESH_CHAT_CLI_OP_PRIVATE_MESSAGE);
|
||||||
|
|
||||||
|
net_buf_simple_add_mem(&buf, msg,
|
||||||
|
strnlen(msg,
|
||||||
|
CONFIG_BT_MESH_CHAT_CLI_MESSAGE_LENGTH));
|
||||||
|
net_buf_simple_add_u8(&buf, '\0');
|
||||||
|
|
||||||
|
return bt_mesh_model_send(chat->model, &ctx, &buf, NULL, NULL);
|
||||||
|
}
|
||||||
|
/* .. include_endpoint_chat_cli_rst_9 */
|
68
src/main.c
Normal file
68
src/main.c
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Nordic Mesh light sample
|
||||||
|
*/
|
||||||
|
#include <zephyr/bluetooth/bluetooth.h>
|
||||||
|
#include <bluetooth/mesh/models.h>
|
||||||
|
#include <bluetooth/mesh/dk_prov.h>
|
||||||
|
#include <dk_buttons_and_leds.h>
|
||||||
|
#include "model_handler.h"
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(chat, CONFIG_LOG_DEFAULT_LEVEL);
|
||||||
|
|
||||||
|
static void bt_ready(int err)
|
||||||
|
{
|
||||||
|
if (err) {
|
||||||
|
printk("Bluetooth init failed (err %d)\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Bluetooth initialized\n");
|
||||||
|
|
||||||
|
err = dk_leds_init();
|
||||||
|
if (err) {
|
||||||
|
printk("Initializing LEDs failed (err %d)\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dk_buttons_init(NULL);
|
||||||
|
if (err) {
|
||||||
|
printk("Initializing buttons failed (err %d)\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bt_mesh_init(bt_mesh_dk_prov_init(), model_handler_init());
|
||||||
|
if (err) {
|
||||||
|
printk("Initializing mesh failed (err %d)\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_SETTINGS)) {
|
||||||
|
settings_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This will be a no-op if settings_load() loaded provisioning info */
|
||||||
|
bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
|
||||||
|
|
||||||
|
printk("Mesh initialized\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
printk("Initializing...\n");
|
||||||
|
|
||||||
|
err = bt_enable(bt_ready);
|
||||||
|
if (err) {
|
||||||
|
printk("Bluetooth init failed (err %d)\n", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
571
src/model_handler.c
Normal file
571
src/model_handler.c
Normal file
@ -0,0 +1,571 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <zephyr/bluetooth/bluetooth.h>
|
||||||
|
#include <bluetooth/mesh/models.h>
|
||||||
|
#include <dk_buttons_and_leds.h>
|
||||||
|
|
||||||
|
#include <zephyr/shell/shell.h>
|
||||||
|
#include <zephyr/shell/shell_uart.h>
|
||||||
|
|
||||||
|
#include "chat_cli.h"
|
||||||
|
#include "model_handler.h"
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_DECLARE(chat);
|
||||||
|
|
||||||
|
static const struct shell *chat_shell;
|
||||||
|
|
||||||
|
#define MAX_NODES 10
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/*************************** Health server setup ******************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
/* Set up a repeating delayed work to blink the DK's LEDs when attention is
|
||||||
|
* requested.
|
||||||
|
*/
|
||||||
|
static struct k_work_delayable attention_blink_work;
|
||||||
|
static bool attention;
|
||||||
|
static volatile bool message_acknowledged = false;
|
||||||
|
|
||||||
|
static void attention_blink(struct k_work *work)
|
||||||
|
{
|
||||||
|
static int idx;
|
||||||
|
const uint8_t pattern[] = {
|
||||||
|
BIT(0) | BIT(1),
|
||||||
|
BIT(1) | BIT(2),
|
||||||
|
BIT(2) | BIT(3),
|
||||||
|
BIT(3) | BIT(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (attention) {
|
||||||
|
dk_set_leds(pattern[idx++ % ARRAY_SIZE(pattern)]);
|
||||||
|
k_work_reschedule(&attention_blink_work, K_MSEC(30));
|
||||||
|
} else {
|
||||||
|
dk_set_leds(DK_NO_LEDS_MSK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attention_on(const struct bt_mesh_model *mod)
|
||||||
|
{
|
||||||
|
attention = true;
|
||||||
|
k_work_reschedule(&attention_blink_work, K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void attention_off(const struct bt_mesh_model *mod)
|
||||||
|
{
|
||||||
|
/* Will stop rescheduling blink timer */
|
||||||
|
attention = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bt_mesh_health_srv_cb health_srv_cb = {
|
||||||
|
.attn_on = attention_on,
|
||||||
|
.attn_off = attention_off,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct bt_mesh_health_srv health_srv = {
|
||||||
|
.cb = &health_srv_cb,
|
||||||
|
};
|
||||||
|
|
||||||
|
BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/***************************** Chat model setup *******************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
struct presence_cache {
|
||||||
|
uint16_t addr;
|
||||||
|
enum bt_mesh_chat_cli_presence presence;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Cache of Presence values of other chat clients. */
|
||||||
|
static struct presence_cache presence_cache[
|
||||||
|
CONFIG_BT_MESH_CHAT_SAMPLE_PRESENCE_CACHE_SIZE];
|
||||||
|
|
||||||
|
static const uint8_t *presence_string[] = {
|
||||||
|
[BT_MESH_CHAT_CLI_PRESENCE_AVAILABLE] = "available",
|
||||||
|
[BT_MESH_CHAT_CLI_PRESENCE_AWAY] = "away",
|
||||||
|
[BT_MESH_CHAT_CLI_PRESENCE_DO_NOT_DISTURB] = "dnd",
|
||||||
|
[BT_MESH_CHAT_CLI_PRESENCE_INACTIVE] = "inactive",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified address is an address of the local element.
|
||||||
|
*/
|
||||||
|
static bool address_is_local(const struct bt_mesh_model *mod, uint16_t addr)
|
||||||
|
{
|
||||||
|
return bt_mesh_model_elem(mod)->rt->addr == addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the provided address is unicast address.
|
||||||
|
*/
|
||||||
|
static bool address_is_unicast(uint16_t addr)
|
||||||
|
{
|
||||||
|
return (addr > 0) && (addr <= 0x7FFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the node is new or the presence status is different from
|
||||||
|
* the one stored in the cache.
|
||||||
|
*/
|
||||||
|
static bool presence_cache_entry_check_and_update(uint16_t addr,
|
||||||
|
enum bt_mesh_chat_cli_presence presence)
|
||||||
|
{
|
||||||
|
static size_t presence_cache_head;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* Find address in cache. */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(presence_cache); i++) {
|
||||||
|
if (presence_cache[i].addr == addr) {
|
||||||
|
if (presence_cache[i].presence == presence) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Break since the node in the cache. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not in cache. */
|
||||||
|
if (i == ARRAY_SIZE(presence_cache)) {
|
||||||
|
for (i = 0; i < ARRAY_SIZE(presence_cache); i++) {
|
||||||
|
if (!presence_cache[i].addr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cache is full. */
|
||||||
|
if (i == ARRAY_SIZE(presence_cache)) {
|
||||||
|
i = presence_cache_head;
|
||||||
|
presence_cache_head = (presence_cache_head + 1)
|
||||||
|
% CONFIG_BT_MESH_CHAT_SAMPLE_PRESENCE_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update cache. */
|
||||||
|
presence_cache[i].addr = addr;
|
||||||
|
presence_cache[i].presence = presence;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_client_status(void);
|
||||||
|
|
||||||
|
static void handle_chat_start(struct bt_mesh_chat_cli *chat)
|
||||||
|
{
|
||||||
|
print_client_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_chat_presence(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
|
enum bt_mesh_chat_cli_presence presence)
|
||||||
|
{
|
||||||
|
if (address_is_local(chat->model, ctx->addr)) {
|
||||||
|
if (address_is_unicast(ctx->recv_dst)) {
|
||||||
|
shell_print(chat_shell, "<you> are %s",
|
||||||
|
presence_string[presence]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (address_is_unicast(ctx->recv_dst)) {
|
||||||
|
shell_print(chat_shell, "<0x%04X> is %s", ctx->addr,
|
||||||
|
presence_string[presence]);
|
||||||
|
} else if (presence_cache_entry_check_and_update(ctx->addr,
|
||||||
|
presence)) {
|
||||||
|
shell_print(chat_shell, "<0x%04X> is now %s",
|
||||||
|
ctx->addr,
|
||||||
|
presence_string[presence]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_chat_message(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
|
const uint8_t *msg)
|
||||||
|
{
|
||||||
|
/* Don't print own messages. */
|
||||||
|
if (address_is_local(chat->model, ctx->addr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_print(chat_shell, "<0x%04X>: %s", ctx->addr, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node_message {
|
||||||
|
uint16_t addr;
|
||||||
|
uint8_t msg_count;
|
||||||
|
bool is_active;
|
||||||
|
int64_t last_seen;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static struct node_message node_stats[MAX_NODES];
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_rpl_full(void)
|
||||||
|
{
|
||||||
|
// Wait for any pending messages to complete
|
||||||
|
k_sleep(K_MSEC(1000));
|
||||||
|
|
||||||
|
// Reset node tracking
|
||||||
|
for (int i = 0; i < MAX_NODES; i++) {
|
||||||
|
node_stats[i].msg_count = 0;
|
||||||
|
node_stats[i].last_seen = k_uptime_get();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INF("RPL management completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_chat_private_message(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx,
|
||||||
|
const uint8_t *msg)
|
||||||
|
{
|
||||||
|
if (!ctx || !msg) {
|
||||||
|
LOG_ERR("Invalid message received");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address_is_local(chat->model, ctx->addr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t current_time = k_uptime_get();
|
||||||
|
int available_slot = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_NODES; i++) {
|
||||||
|
if (node_stats[i].addr == ctx->addr) {
|
||||||
|
if ((current_time - node_stats[i].last_seen) > 5000) {
|
||||||
|
LOG_INF("Node 0x%04X reconnected", ctx->addr);
|
||||||
|
}
|
||||||
|
node_stats[i].msg_count++;
|
||||||
|
node_stats[i].last_seen = current_time;
|
||||||
|
node_stats[i].is_active = true;
|
||||||
|
LOG_INF("Message from 0x%04X (#%d): %s",
|
||||||
|
ctx->addr, node_stats[i].msg_count, msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!node_stats[i].is_active && available_slot == -1) {
|
||||||
|
available_slot = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (available_slot != -1) {
|
||||||
|
node_stats[available_slot].addr = ctx->addr;
|
||||||
|
node_stats[available_slot].msg_count = 1;
|
||||||
|
node_stats[available_slot].last_seen = current_time;
|
||||||
|
node_stats[available_slot].is_active = true;
|
||||||
|
LOG_INF("New node 0x%04X registered: %s", ctx->addr, msg);
|
||||||
|
} else {
|
||||||
|
LOG_WRN("Node tracking full - Message from 0x%04X dropped", ctx->addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_chat_message_reply(struct bt_mesh_chat_cli *chat,
|
||||||
|
struct bt_mesh_msg_ctx *ctx)
|
||||||
|
{
|
||||||
|
message_acknowledged = true;
|
||||||
|
shell_print(chat_shell, "<0x%04X> received the message", ctx->addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bt_mesh_chat_cli_handlers chat_handlers = {
|
||||||
|
.start = handle_chat_start,
|
||||||
|
.presence = handle_chat_presence,
|
||||||
|
.message = handle_chat_message,
|
||||||
|
.private_message = handle_chat_private_message,
|
||||||
|
.message_reply = handle_chat_message_reply,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* .. include_startingpoint_model_handler_rst_1 */
|
||||||
|
static struct bt_mesh_chat_cli chat = {
|
||||||
|
.handlers = &chat_handlers,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct bt_mesh_elem elements[] = {
|
||||||
|
BT_MESH_ELEM(
|
||||||
|
1,
|
||||||
|
BT_MESH_MODEL_LIST(
|
||||||
|
BT_MESH_MODEL_CFG_SRV,
|
||||||
|
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub)),
|
||||||
|
BT_MESH_MODEL_LIST(BT_MESH_MODEL_CHAT_CLI(&chat))),
|
||||||
|
};
|
||||||
|
/* .. include_endpoint_model_handler_rst_1 */
|
||||||
|
|
||||||
|
static void print_client_status(void)
|
||||||
|
{
|
||||||
|
if (!bt_mesh_is_provisioned()) {
|
||||||
|
shell_print(chat_shell,
|
||||||
|
"The mesh node is not provisioned. Please provision the mesh node before using the chat.");
|
||||||
|
} else {
|
||||||
|
shell_print(chat_shell,
|
||||||
|
"The mesh node is provisioned. The client address is 0x%04x.",
|
||||||
|
bt_mesh_model_elem(chat.model)->rt->addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_print(chat_shell, "Current presence: %s",
|
||||||
|
presence_string[chat.presence]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bt_mesh_comp comp = {
|
||||||
|
.cid = CONFIG_BT_COMPANY_ID,
|
||||||
|
.elem = elements,
|
||||||
|
.elem_count = ARRAY_SIZE(elements),
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************** Chat shell **********************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
static int cmd_status(const struct shell *shell, size_t argc, char *argv[])
|
||||||
|
{
|
||||||
|
print_client_status();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_message(const struct shell *shell, size_t argc, char *argv[])
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bt_mesh_chat_cli_message_send(&chat, argv[1]);
|
||||||
|
if (err) {
|
||||||
|
LOG_WRN("Failed to send message: %d", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print own messages in the chat. */
|
||||||
|
shell_print(shell, "<you>: %s", argv[1]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_private_message(const struct shell *shell, size_t argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
uint16_t addr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (argc < 3) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = strtol(argv[1], NULL, 0);
|
||||||
|
|
||||||
|
/* Print own message to the chat. */
|
||||||
|
shell_print(shell, "%x: %s\n", addr, argv[2]);
|
||||||
|
|
||||||
|
err = bt_mesh_chat_cli_private_message_send(&chat, addr, argv[2]);
|
||||||
|
if (err) {
|
||||||
|
LOG_WRN("Failed to publish message: %d", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_presence_set(const struct shell *shell, size_t argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(presence_string); i++) {
|
||||||
|
if (!strcmp(argv[1], presence_string[i])) {
|
||||||
|
enum bt_mesh_chat_cli_presence presence;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
presence = i;
|
||||||
|
|
||||||
|
err = bt_mesh_chat_cli_presence_set(&chat, presence);
|
||||||
|
if (err) {
|
||||||
|
LOG_WRN("Failed to update presence: %d", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print own presence in the chat. */
|
||||||
|
shell_print(shell, "You are now %s",
|
||||||
|
presence_string[presence]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_print(shell,
|
||||||
|
"Unknown presence status: %s. Possible presence statuses:",
|
||||||
|
argv[1]);
|
||||||
|
for (i = 0; i < ARRAY_SIZE(presence_string); i++) {
|
||||||
|
shell_print(shell, "%s", presence_string[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_presence_get(const struct shell *shell, size_t argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
uint16_t addr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = strtol(argv[1], NULL, 0);
|
||||||
|
|
||||||
|
err = bt_mesh_chat_cli_presence_get(&chat, addr);
|
||||||
|
if (err) {
|
||||||
|
LOG_WRN("Failed to publish message: %d", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHELL_STATIC_SUBCMD_SET_CREATE(presence_cmds,
|
||||||
|
SHELL_CMD_ARG(set, NULL,
|
||||||
|
"Set presence of the current client <presence: available, away, dnd or inactive>",
|
||||||
|
cmd_presence_set, 2, 0),
|
||||||
|
SHELL_CMD_ARG(get, NULL,
|
||||||
|
"Get presence status of the remote node <node>",
|
||||||
|
cmd_presence_get, 2, 0),
|
||||||
|
SHELL_SUBCMD_SET_END
|
||||||
|
);
|
||||||
|
|
||||||
|
static int cmd_presence(const struct shell *shell, size_t argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc == 1) {
|
||||||
|
shell_help(shell);
|
||||||
|
/* shell returns 1 when help is printed */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHELL_STATIC_SUBCMD_SET_CREATE(chat_cmds,
|
||||||
|
SHELL_CMD_ARG(status, NULL, "Print client status", cmd_status, 1, 0),
|
||||||
|
SHELL_CMD(presence, &presence_cmds, "Presence commands", cmd_presence),
|
||||||
|
SHELL_CMD_ARG(private, NULL,
|
||||||
|
"Send a private text message to a client <node> <message>",
|
||||||
|
cmd_private_message, 3, 0),
|
||||||
|
SHELL_CMD_ARG(msg, NULL, "Send a text message to the chat <message>",
|
||||||
|
cmd_message, 2, 0),
|
||||||
|
SHELL_SUBCMD_SET_END
|
||||||
|
);
|
||||||
|
|
||||||
|
static int cmd_chat(const struct shell *shell, size_t argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc == 1) {
|
||||||
|
shell_help(shell);
|
||||||
|
/* shell returns 1 when help is printed */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_error(shell, "%s unknown parameter: %s", argv[0], argv[1]);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHELL_CMD_ARG_REGISTER(chat, &chat_cmds, "Bluetooth Mesh Chat Client commands",
|
||||||
|
cmd_chat, 1, 1);
|
||||||
|
|
||||||
|
|
||||||
|
static struct k_work_delayable message_timer;
|
||||||
|
|
||||||
|
static uint32_t msg_counter = 0;
|
||||||
|
|
||||||
|
static uint16_t get_node_address(void)
|
||||||
|
{
|
||||||
|
uint16_t addr = bt_mesh_model_elem(chat.model)->rt->addr;
|
||||||
|
LOG_INF("Node unicast address: 0x%04x", addr);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool has_publish_address(void)
|
||||||
|
{
|
||||||
|
const struct bt_mesh_model *model = chat.model;
|
||||||
|
if (model && model->pub && model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
|
||||||
|
LOG_INF("Publish address: 0x%04x", model->pub->addr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_periodic_message(struct k_work *work)
|
||||||
|
{
|
||||||
|
if (!has_publish_address()) {
|
||||||
|
LOG_INF("No publish address assigned yet");
|
||||||
|
k_work_reschedule(&message_timer, K_SECONDS(5));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char fixed_message[255]; // change the size as needed for payload
|
||||||
|
uint16_t target_addr = 0x0105;
|
||||||
|
|
||||||
|
snprintf(fixed_message, sizeof(fixed_message),
|
||||||
|
"%lu 0x%04x dammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsandammavalamsarathchandradammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsandammavalamsarathchandrasrivatsa", msg_counter, get_node_address());
|
||||||
|
|
||||||
|
|
||||||
|
// First attempt
|
||||||
|
message_acknowledged = false;
|
||||||
|
int err = bt_mesh_chat_cli_private_message_send(&chat, target_addr, fixed_message);
|
||||||
|
if (err) {
|
||||||
|
LOG_WRN("First attempt failed, retrying...");
|
||||||
|
k_sleep(K_MSEC(10000));
|
||||||
|
|
||||||
|
// Second attempt
|
||||||
|
message_acknowledged = false;
|
||||||
|
err = bt_mesh_chat_cli_private_message_send(&chat, target_addr, fixed_message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
msg_counter++;
|
||||||
|
|
||||||
|
|
||||||
|
if (message_acknowledged) {
|
||||||
|
shell_print(chat_shell, "Message delivered successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
k_work_reschedule(&message_timer, K_SECONDS(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// change array size and constant value for payload in 519 & 523.
|
||||||
|
// change publish time value in line 545
|
||||||
|
// comment lines 562 & 563 for receiver nodes 105
|
||||||
|
// uncomment lines 562 & 563 for transmitter nodes 120,121,122,123,124
|
||||||
|
//
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************** Public API **********************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
const struct bt_mesh_comp *model_handler_init(void)
|
||||||
|
{
|
||||||
|
k_work_init_delayable(&attention_blink_work, attention_blink);
|
||||||
|
|
||||||
|
// k_work_init_delayable(&message_timer, send_periodic_message);
|
||||||
|
// k_work_schedule(&message_timer, K_NO_WAIT);
|
||||||
|
|
||||||
|
chat_shell = shell_backend_uart_get_ptr();
|
||||||
|
printk(">>> Bluetooth Mesh Chat sample <<<\n");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return ∁
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user