Hello,
I recently bought a nrf52840 dev kit + dongle to test the functionalities of Matter.
To commission devices "On network" without the use of Bluetooth, i wanted to use the Thread commissioner/joiner mechanism, based on a pre-shared key or the EUI+64 of the device.
I started from the matter light bulb example, and added the necessary flags in prj.conf.
# Enable CHIP CONFIG_CHIP=y CONFIG_CHIP_PROJECT_CONFIG="src/chip_project_config.h" # 32773 == 0x8005 (example lighting-app) CONFIG_CHIP_DEVICE_PRODUCT_ID=32773 CONFIG_STD_CPP17=y # Enable Matter pairing automatically on application start. CONFIG_CHIP_ENABLE_PAIRING_AUTOSTART=y # Enable Matter extended announcement and increase duration to 1 hour. CONFIG_CHIP_BLE_EXT_ADVERTISING=y CONFIG_CHIP_BLE_ADVERTISING_DURATION=60 # Add support for LEDs and buttons on Nordic development kits CONFIG_DK_LIBRARY=y CONFIG_PWM=y # Bluetooth Low Energy configuration CONFIG_BT_DEVICE_NAME="MatterLight" # Other settings CONFIG_THREAD_NAME=y CONFIG_MPU_STACK_GUARD=y CONFIG_RESET_ON_FATAL_ERROR=n CONFIG_CHIP_LIB_SHELL=y CONFIG_NCS_SAMPLE_MATTER_TEST_SHELL=y # Reduce application size CONFIG_USE_SEGGER_RTT=n # Enable Factory Data feature CONFIG_CHIP_FACTORY_DATA=y CONFIG_CHIP_FACTORY_DATA_BUILD=y # extra: mbedTLS CONFIG_MBEDTLS_BUILTIN=y CONFIG_MBEDTLS_AES_C=y CONFIG_MBEDTLS_LEGACY_CRYPTO_C=y CONFIG_NRF_SECURITY=y CONFIG_MBEDTLS=y CONFIG_MBEDTLS_CMAC_C=y CONFIG_MBEDTLS_SSL_PROTO_DTLS=y CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED=y CONFIG_MBEDTLS_ECJPAKE_C=y # Enable elliptic curve cryptography # CONFIG_MBEDTLS_ECP=y # Enable specific elliptic curves (example: CURVE25519 and CURVE448) CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y CONFIG_MBEDTLS_ECP_DP_CURVE448_ENABLED=y # extra: Thread # Enable OpenThread support CONFIG_OPENTHREAD=y CONFIG_OPENTHREAD_THREAD_VERSION_1_3=y # start the joiner CONFIG_OPENTHREAD_JOINER=y CONFIG_CHIP_OPENTHREAD_JOINER_ENABLED=y # Enable Thread networking CONFIG_NET_L2_OPENTHREAD=y CONFIG_OPENTHREAD_FTD=y # Full Thread Device (use MTD for Minimal Thread Device) # Enable IPv6 support (required for Thread) CONFIG_NETWORKING=y CONFIG_NET_IPV6=y CONFIG_NET_IPV6_MLD=y CONFIG_NET_IPV6_NBR_CACHE=y CONFIG_NET_IPV6_ND=y # Network shell CONFIG_SHELL=y CONFIG_OPENTHREAD_SHELL=y CONFIG_SHELL_ARGC_MAX=26 CONFIG_SHELL_CMD_BUFF_SIZE=416
in my src/main.cpp code i adjusted the program to print the EUI64 in the console and start the thread joiner.
#include "app_task.h"
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <openthread/thread.h>
#include <openthread/joiner.h>
#include <openthread/platform/radio.h> // For otPlatRadioGetIeeeEui64
#define THREAD_JOINER_TIMEOUT 900 // 15 minutes in seconds
// Global flag to indicate Thread Joiner completion
static volatile bool is_thread_joiner_complete = false;
static const struct gpio_dt_spec button1 = GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios);
static struct gpio_callback button1_cb_data;
LOG_MODULE_REGISTER(app, CONFIG_CHIP_APP_LOG_LEVEL);
// Callback for Thread Joiner completion
void JoinerCallback(otError result, void *context)
{
if (result == OT_ERROR_NONE)
{
LOG_INF("Thread Joiner completed successfully");
is_thread_joiner_complete = true;
}
else
{
LOG_ERR("Thread Joiner failed: %d", result);
}
}
void StartThreadJoiner()
{
otInstance *instance = openthread_get_default_instance();
if (instance == NULL)
{
LOG_ERR("Failed to get OpenThread instance");
return;
}
// Retrieve the EUI64
uint8_t eui64[OT_EXT_ADDRESS_SIZE];
otPlatRadioGetIeeeEui64(instance, eui64);
// Print the EUI64 to the console
LOG_INF("Device EUI64: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
eui64[0], eui64[1], eui64[2], eui64[3],
eui64[4], eui64[5], eui64[6], eui64[7]);
// Start the Thread Joiner
const char *pskd = NULL; // No PSKd since you're using EUI64
otError error = otJoinerStart(instance, pskd, NULL, NULL, NULL, NULL, NULL, JoinerCallback, NULL);
if (error == OT_ERROR_NONE)
{
LOG_INF("Thread Joiner started successfully");
}
else
{
LOG_ERR("Failed to start Thread Joiner: %d", error);
}
}
void button1_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
LOG_INF("Button 1 (sw1 alias) pressed, starting Thread Joiner...");
StartThreadJoiner();
}
void init_buttons()
{
if (!device_is_ready(button1.port)) {
LOG_ERR("Button 1 GPIO port not ready");
return;
}
gpio_pin_configure_dt(&button1, GPIO_INPUT);
gpio_pin_interrupt_configure_dt(&button1, GPIO_INT_EDGE_TO_ACTIVE);
gpio_init_callback(&button1_cb_data, button1_pressed, BIT(button1.pin));
gpio_add_callback(button1.port, &button1_cb_data);
LOG_INF("Button 1 configured successfully");
}
int main()
{
// Initialize Button 1 for Thread Joiner
init_buttons();
LOG_INF("Waiting for Thread Joiner to complete...");
while (!is_thread_joiner_complete)
{
k_sleep(K_SECONDS(1)); // Polling to check if Joiner has completed
}
LOG_INF("Starting Matter application...");
CHIP_ERROR err = AppTask::Instance().StartApp();
LOG_ERR("Exited with code %" CHIP_ERROR_FORMAT, err.Format());
return err == CHIP_NO_ERROR ? EXIT_SUCCESS : EXIT_FAILURE;
}
there is an issue with the link to the Openthread libraries though, because i get the error:
C:/ncs/workspace/light_bulb/src/main.cpp:62: undefined reference to `otJoinerStart'
Has anyone tried this before?
Do i need to add extra lines in the config file or is it not possible to use both Opentread and Matter due to conflicts?
Thanks in advance for your help!