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:
Sarath_Chandra_D 2025-02-18 11:23:10 +05:30
commit b77fd5457c
18 changed files with 1769 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# editors
*.swp
*~
# build
/build*/

19
CMakeLists.txt Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,5 @@
VERSION_MAJOR = 2
VERSION_MINOR = 9
PATCHLEVEL = 0
VERSION_TWEAK = 0
EXTRAVERSION =

View 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

View 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
View 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
View 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
View 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__ */

View 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
View 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
View 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
View 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
View 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
View 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
View 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 &comp;
}