Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions docs/architectures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ Single-chip, combined Matter + Zigbee (SoC)
===========================================

In this design, the Zigbee (ZBOSS) and Matter over Thread (OpenThread) stacks run on the same nRF SoC and share the single 802.15.4 radio through a coexistence layer, while a `SoftDevice Controller`_ instance handles Bluetooth LE (CHIPoBLE) commissioning.
Radio ownership is time-separated and persisted across reboots: the device boots as a Zigbee node, advertises for Matter commissioning over Bluetooth LE in parallel, and hands the 802.15.4 radio over to OpenThread once the first Matter CASE session is established.
Radio ownership is time-separated and persists across reboots.
The device boots on the configured default protocol (Zigbee by default).
While Zigbee is active, Matter advertises for commissioning over Bluetooth LE in parallel.
The 802.15.4 radio is handed over to OpenThread once Matter commissioning completes or you request a protocol switch.

.. list-table::
:header-rows: 1
Expand All @@ -100,8 +103,8 @@ Radio ownership is time-separated and persisted across reboots: the device boots
- Advantage
- Trade-off
* - Deployment model
- A single firmware image can start as Zigbee and migrate to Matter after commissioning, without reflashing.
- Switching back to Zigbee requires a Matter factory reset, which clears Matter commissioning data.
- A single firmware image can start as Zigbee and migrate to Matter after commissioning or a button-triggered switch, without reflashing.
- Switching back to Zigbee is supported by a long button press. Removing the last Matter fabric or triggering a Matter factory reset clears Matter commissioning data and resets the persisted protocol to the configured default.
* - Radio usage
- Bluetooth LE commissioning (CHIPoBLE) runs concurrently with Zigbee operation, so no separate commissioning device is needed.
- The 802.15.4 radio is used by one stack at a time. Zigbee operation pauses once the device switches to Matter.
Expand Down
7 changes: 4 additions & 3 deletions docs/includes/matter_extension_limitations.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
As a proof of concept, the Matter extension has the following limitations:

* The 802.15.4 radio is time-shared, never concurrent: once the device is provisioned to Matter, Zigbee is torn down, and returning to Zigbee requires a Matter factory reset (which also wipes Matter storage).
* The Matter extension cannot be combined with the :ref:`Multiprotocol Bluetooth LE extension <zigbee_light_switch_sample_nus>` (``overlay-multiprotocol_ble.conf``) or with the Zigbee-only FOTA build (``FILE_SUFFIX=fota``).
* The Matter shell (``CONFIG_CHIP_LIB_SHELL``) and the Matter test shell are disabled to avoid option-parsing conflicts with the Zigbee shell.
* The 802.15.4 radio is time-shared, never concurrent: once the device runs on Matter, Zigbee is torn down until the active protocol changes again.
To switch back to Zigbee, ensure the ``CONFIG_ZIGBEE_MATTER_COEXISTENCE_BUTTON_SWITCH`` Kconfig option is enabled, then long-press the button.
Removing all Matter fabrics or triggering a Matter factory reset wipes Matter storage and resets the persisted protocol to the value selected by the ``CONFIG_ZIGBEE_MATTER_PROTOCOL_STATE_DEFAULT_PROTOCOL`` Kconfig option.* The Matter extension cannot be combined with the :ref:`Multiprotocol Bluetooth LE extension <zigbee_light_switch_sample_nus>` (``overlay-multiprotocol_ble.conf``) or with the Zigbee-only FOTA build (``FILE_SUFFIX=fota``).
* You cannot combine the Matter extension with the :ref:`Multiprotocol Bluetooth LE extension <zigbee_light_switch_sample_nus>` (``overlay-multiprotocol_ble.conf``) or with the Zigbee-only FOTA build (``FILE_SUFFIX=fota``).
* The memory footprint is tuned empirically for ``nrf54lm20dk/nrf54lm20a/cpuapp`` and ``nrf54lm20dk/nrf54lm20b/cpuapp``; porting to other targets requires revisiting the libc heap, ZBOSS thread, system work queue and partition sizes used by :file:`prj_matter_fota.conf`.
16 changes: 16 additions & 0 deletions docs/includes/matter_extension_protocol_switch.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
The default protocol on a factory-fresh device is selected by the ``CONFIG_ZIGBEE_MATTER_PROTOCOL_STATE_DEFAULT_PROTOCOL`` Kconfig choice (Zigbee by default).
A Matter factory reset (including last-fabric removal when ``CONFIG_CHIP_LAST_FABRIC_REMOVED_ERASE_AND_REBOOT`` is enabled) applies the same default again and wipes Matter commissioning data.
After the first run, the persisted protocol state in settings takes precedence until a factory reset or explicit protocol switch changes it.

When ``CONFIG_ZIGBEE_MATTER_COEXISTENCE_BUTTON_SWITCH`` is enabled (the default), you can long-press the selected button to switch the active protocol.
Configure the required hold time by setting the ``CONFIG_ZIGBEE_MATTER_COEXISTENCE_SWITCH_BUTTON_PRESS_TIME_SECONDS`` Kconfig option.
By default, this option is set to 5 seconds.
From Zigbee, the coexistence layer stops the ZBOSS stack and hands the 802.15.4 radio to OpenThread for Matter.
From Matter, the device persists Zigbee as the active protocol and reboots.
The protocol switch is ignored while the device is joining a Zigbee network or undergoing Matter commissioning.

Other Kconfig options in the coexistence layer:

* ``CONFIG_ZIGBEE_MATTER_COEXISTENCE_BT_ADV_WHILE_ZIGBEE`` (default enabled) — Matter may advertise for commissioning over Bluetooth LE while Zigbee owns the 802.15.4 radio.
When disabled, the sample must call ``zigbee_matter_coexistence_signal_matter_board_init()`` after Matter server init, and the Zigbee worker proceeds without waiting for CHIPoBLE advertising.
* ``CONFIG_ZIGBEE_MATTER_COEXISTENCE_BUTTON_SWITCH`` — Enable or disable the button-triggered protocol switch.
36 changes: 34 additions & 2 deletions include/zigbee/matter_coexistence.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
* and calling @ref zigbee_matter_coexistence_run from @c main().
*/

#include <stdbool.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif
Expand All @@ -40,13 +43,17 @@ struct zigbee_matter_coexistence_callbacks {
*/
int (*zigbee_start)(void);

/** Hook run on the Zigbee worker thread once the Matter board has
* finished initialisation (first CHIPoBLE advertising-started event).
/** Hook run on the Zigbee worker thread once Matter is ready for the
* Zigbee phase (CHIPoBLE advertising started when
* @kconfig{CONFIG_ZIGBEE_MATTER_COEXISTENCE_BT_ADV_WHILE_ZIGBEE},
* otherwise after the sample calls
* @ref zigbee_matter_coexistence_signal_matter_board_init).
* Typically used to register a chained Zigbee button handler with
* the DK buttons library after @c dk_buttons_init() has completed.
* May be NULL.
*/
void (*post_matter_board_init)(void);

};

/** @brief Start the coexistence runtime.
Expand All @@ -65,6 +72,31 @@ struct zigbee_matter_coexistence_callbacks {
*/
int zigbee_matter_coexistence_run(const struct zigbee_matter_coexistence_callbacks *cb);

/** @brief Notify the coexistence runtime that Matter board init is complete.
*
* Call from the Matter worker thread after @c Nrf::Matter::StartServer()
* returns successfully when
* @kconfig{CONFIG_ZIGBEE_MATTER_COEXISTENCE_BT_ADV_WHILE_ZIGBEE} is
* disabled. Unblocks the Zigbee worker so it can chain button handlers and
* start the Zigbee stack.
*/
void zigbee_matter_coexistence_signal_matter_board_init(void);

/** @brief Process button events for user-triggered protocol switching.
*
* Detects a long press on @p switch_button and requests a protocol switch
* when @kconfig{CONFIG_ZIGBEE_MATTER_COEXISTENCE_SWITCH_BUTTON_PRESS_TIME_SECONDS}
* expires.
Comment thread
edmont marked this conversation as resolved.
*
* Call this before other sample-specific button checks.
*
* @param button_state Current button state bitmask.
* @param has_changed Button-change bitmask from DK callback.
* @param switch_button Bitmask of the switch button (one DK button).
*/
void zigbee_matter_coexistence_process_switch_button(uint32_t button_state, uint32_t has_changed,
uint32_t switch_button);

#ifdef __cplusplus
}
#endif
Expand Down
21 changes: 17 additions & 4 deletions include/zigbee/matter_protocol_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
* @brief Persistent "which protocol is active" state for samples that host
* both Matter and Zigbee on the same 802.15.4 radio.
*
* The application boots on Zigbee by default. Once Matter commissioning
* completes, the state flips to Matter and is persisted so that subsequent
* reboots resume Matter directly (and Zigbee stack initialization is skipped).
* A factory reset resets the state back to Zigbee.
* The default protocol on first boot is selected by Kconfig (see
* @kconfig{CONFIG_ZIGBEE_MATTER_PROTOCOL_STATE_DEFAULT_PROTOCOL}). Once Matter
* commissioning completes, the state flips to Matter and is persisted so that
* subsequent reboots resume Matter directly (and Zigbee stack initialization
* is skipped). A Matter factory reset resets the persisted protocol to the
* value selected by @kconfig{CONFIG_ZIGBEE_MATTER_PROTOCOL_STATE_DEFAULT_PROTOCOL}.
*/

#include <stdbool.h>
Expand All @@ -43,6 +45,17 @@ typedef enum {
*/
int protocol_state_init(void);

/**
* @brief Get the configured default protocol.
*
* Returns the value selected by
* @kconfig{CONFIG_ZIGBEE_MATTER_PROTOCOL_STATE_DEFAULT_PROTOCOL}. Used on
* first boot when no state is stored yet and after a Matter factory reset.
*
* @return PROTOCOL_ZIGBEE or PROTOCOL_MATTER
*/
active_protocol_t protocol_state_get_default(void);

/**
* @brief Get the currently active protocol.
* @return PROTOCOL_ZIGBEE or PROTOCOL_MATTER
Expand Down
21 changes: 19 additions & 2 deletions include/zigbee/zigbee_app_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
#include <stdint.h>
#include <stdbool.h>

#include <zboss_api.h>

/** @file zigbee_app_utils.h
*
* @defgroup zigbee_app_utils Zigbee application utilities library.
Expand All @@ -27,6 +25,8 @@
extern "C" {
#endif

#include <zboss_api.h>

/**@brief Function for setting the Erase persistent storage,
* depending on the erase pin.
*
Expand All @@ -40,6 +40,23 @@ extern "C" {
*/
void zigbee_erase_persistent_storage(zb_bool_t erase);

/** @brief Whether a Zigbee network join/commissioning procedure is active.
*
* True only while top-level BDB commissioning (initialization, steering,
* formation, Touchlink, or Trust Center rejoin) is running on the radio.
* False when the device is idle and not joined, including between automatic
* rejoin attempts.
*/
bool zigbee_network_join_commissioning_active(void);

/** @brief Mark Zigbee network join/commissioning as active or idle.
*
* Samples should set this to true before starting Touchlink or other
* application-triggered commissioning not covered by the default signal
* handler.
*/
void zigbee_network_join_commissioning_set_active(bool active);

/**@brief Function for converting an input buffer to a hex string.
*
* @param[out] out Pointer to the output buffer.
Expand Down
24 changes: 18 additions & 6 deletions samples/light_bulb/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ It is supported only on the ``nrf54lm20dk/nrf54lm20a/cpuapp`` and ``nrf54lm20dk/

The sample-specific behavior is:

* On first boot, the device is a standard :ref:`Zigbee Router <zigbee_roles>` exposing the Dimmable Light device, while the Matter stack advertises for commissioning over Bluetooth LE (CHIPoBLE).
* On first boot, the device starts on the protocol selected by ``CONFIG_ZIGBEE_MATTER_PROTOCOL_STATE_DEFAULT_PROTOCOL`` (Zigbee by default).
When Zigbee is active, it is a standard :ref:`Zigbee Router <zigbee_roles>` exposing the Dimmable Light device, while the Matter stack advertises for commissioning over Bluetooth LE (CHIPoBLE).
* After successful Matter commissioning, the device is converted to a Matter Dimmable Light endpoint that serves the On/Off and Level Control clusters and drives the same LED through PWM.
It can then be controlled by any device on the Matter fabric bound to it (for example, the :ref:`zigbee_light_switch_sample` built with the :ref:`zigbee_light_switch_sample_matter`).
* On subsequent boots, the device starts directly as a Matter Dimmable Light.
A Matter factory reset reverts it to a fresh Zigbee Router.
* On subsequent boots, the device resumes the persisted protocol.
When Matter was selected, it starts directly as a Matter Dimmable Light.
A Matter factory reset resets the persisted protocol to the configured default and wipes Matter commissioning data.

The radio hand-over, persistent protocol state, factory-reset behavior and onboarding-data generation are common to both combined samples and are described in detail in the :ref:`zigbee_light_switch_sample_matter` section of the Light switch sample.
The radio hand-over, persistent protocol state, user-triggered protocol switch, factory-reset behavior and onboarding-data generation are common to both combined samples and are described in detail in the :ref:`zigbee_light_switch_sample_matter` section of the Light switch sample.

.. _zigbee_light_bulb_sample_touchlink:

Expand Down Expand Up @@ -115,6 +117,9 @@ User interface
LED 2:
Turns on when the light bulb joins the network.

Button 2:
If ``CONFIG_ZIGBEE_MATTER_COEXISTENCE_BUTTON_SWITCH`` is enabled (default), it triggers a protocol switch after a long press (``CONFIG_ZIGBEE_MATTER_COEXISTENCE_SWITCH_BUTTON_PRESS_TIME_SECONDS``, 5 s by default).

Button 3:
Depending on how long the button is pressed:

Expand Down Expand Up @@ -241,15 +246,22 @@ Complete the following steps to exercise the full Zigbee-to-Matter flow:
The two devices form a distributed-security Zigbee network without a Zigbee Coordinator, and the light switch finds and controls the light bulb.

While the device is still a Zigbee Router, it also advertises for Matter commissioning over Bluetooth LE.
#. Optionally, long-press Button 2 for ``CONFIG_ZIGBEE_MATTER_COEXISTENCE_SWITCH_BUTTON_PRESS_TIME_SECONDS`` to switch to Matter.
The Zigbee stack is stopped and the radio is handed to OpenThread.
Skip the next step if you use this path and Matter was already commissioned in a previous session.
#. Commission the device using the onboarding payload produced by the Matter factory data build (QR code or manual pairing code).
After the Matter CASE session is established, the light bulb hands the radio over to Thread and stops participating in the Zigbee network.
#. Drive the light bulb from a Matter peer:

* From the controller directly, with ``chip-tool onoff toggle …`` or ``chip-tool levelcontrol move-to-level …``.
* Or by binding a Matter switch to the light bulb and using the switch's dimmer button.

#. To return the device to Zigbee operation, trigger a Matter factory reset from the controller (for example, ``chip-tool pairing unpair …``).
The device reboots as a fresh Zigbee Router with Matter Bluetooth LE advertising active again.
#. To return the device to Zigbee operation, use one of the following:

* Long-press Button 2 for ``CONFIG_ZIGBEE_MATTER_COEXISTENCE_SWITCH_BUTTON_PRESS_TIME_SECONDS``.
The device reboots and resumes as a Zigbee Router.
* Or trigger a Matter factory reset from the controller (for example, ``chip-tool pairing unpair …``).
The device reboots as a fresh Zigbee Router with Matter Bluetooth LE advertising active again, and Matter storage is cleared.

Dependencies
************
Expand Down
1 change: 1 addition & 0 deletions samples/light_bulb/prj_matter_fota.conf
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ CONFIG_DK_LIBRARY=y
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

CONFIG_ZBOSS_OSIF_LOG_LEVEL_INF=y
CONFIG_ZIGBEE_MATTER_COEXISTENCE_LOG_LEVEL_INF=y

#######################
# Zigbee FOTA Configuration
Expand Down
14 changes: 13 additions & 1 deletion samples/light_bulb/src/app_task_matter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
#include <app/server/Server.h>
#include <setup_payload/OnboardingCodesUtil.h>

#if defined(CONFIG_ZIGBEE_MATTER_COEXISTENCE) && \
!defined(CONFIG_ZIGBEE_MATTER_COEXISTENCE_BT_ADV_WHILE_ZIGBEE)
#include <zigbee/matter_coexistence.h>
#endif

#include <zephyr/logging/log.h>

LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL);
Expand Down Expand Up @@ -213,7 +218,14 @@ CHIP_ERROR AppTask::Init() {

ReturnErrorOnFailure(sIdentifyCluster.Init());

return Nrf::Matter::StartServer();
ReturnErrorOnFailure(Nrf::Matter::StartServer());

#if defined(CONFIG_ZIGBEE_MATTER_COEXISTENCE) && \
!defined(CONFIG_ZIGBEE_MATTER_COEXISTENCE_BT_ADV_WHILE_ZIGBEE)
zigbee_matter_coexistence_signal_matter_board_init();
#endif

return CHIP_NO_ERROR;
}

CHIP_ERROR AppTask::StartApp() {
Expand Down
12 changes: 12 additions & 0 deletions samples/light_bulb/src/app_task_zigbee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "app_task_zigbee.h"

#ifdef CONFIG_ZIGBEE_MATTER_COEXISTENCE
#include <zigbee/matter_coexistence.h>
#include <zigbee/matter_protocol_state.h>
#endif

Expand All @@ -20,6 +21,7 @@
#include <zephyr/drivers/pwm.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#ifdef CONFIG_ZIGBEE_SCENES
#include <zephyr/settings/settings.h>
#endif
Expand Down Expand Up @@ -88,6 +90,11 @@ extern "C" {
*/
#define BULB_LED DK_LED4

#if defined(CONFIG_ZIGBEE_MATTER_COEXISTENCE_BUTTON_SWITCH)
/* Long-press to switch active protocol. */
#define PROTOCOL_SWITCH_BUTTON DK_BTN3_MSK
#endif

/* Button used to enter the Bulb into the Identify mode. */
#define IDENTIFY_MODE_BUTTON DK_BTN4_MSK

Expand Down Expand Up @@ -213,6 +220,11 @@ static void start_identifying(zb_bufid_t bufid) {
*/
static void zb_button_handler_impl(uint32_t button_state, uint32_t has_changed) {
#ifdef CONFIG_ZIGBEE_MATTER_COEXISTENCE
#ifdef CONFIG_ZIGBEE_MATTER_COEXISTENCE_BUTTON_SWITCH
(void)zigbee_matter_coexistence_process_switch_button(
button_state, has_changed, PROTOCOL_SWITCH_BUTTON);
#endif

if (!protocol_is_zigbee_active()) {
return;
}
Expand Down
Loading
Loading