Skip to content

Commit

Permalink
applications: Enabled using persistent storage in Matter bridge.
Browse files Browse the repository at this point in the history
The change includes the following commits:

1. applications: Created application bridged devices creator module.

The bridged devices adding and removing was handled in the shell
module, however it may be useful to trigger such actions from
a different parts of an application (e.g. app task).

Extracted methods to add and remove bridged devices and handle
Bluetooth LE connected callback to the dedicated bridged devices
creator module. It will be possible to use its API in shell and
in the different locations as well.

2. samples: matter: Refactored bridge manager API for adding devices

Changed bridged manager API and adding devices algorithm to allow
requesting usage of specific index and endpoint id.

3. applications: Enabled using persistent storage in Matter bridge

Added storing bridged devices in the persistent storage after
creation, removing them from the storage after removal and
loading them from the storage after boot.

4. samples: matter: Added BLE connections recover after bridge reboot

Implemented recovering Bluetooth LE connections to the stored
bridged devices after bridge reboot.

Additionally refactored structure of BLEConnectivityManager
and BLEBridgedDeviceProvider by moving the BLEBridgedDevice
objects ownership from manager to providers.

Signed-off-by: Kamil Kasperczyk <[email protected]>
  • Loading branch information
kkasperczyk-no authored and nordicjm committed Sep 8, 2023
1 parent a3ac791 commit 0204f74
Show file tree
Hide file tree
Showing 16 changed files with 1,157 additions and 371 deletions.
1 change: 1 addition & 0 deletions applications/matter_bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ target_sources(app PRIVATE
src/app_task.cpp
src/main.cpp
src/bridge_shell.cpp
src/bridged_devices_creator.cpp
${COMMON_ROOT}/src/bridge/bridge_manager.cpp
${COMMON_ROOT}/src/bridge/matter_bridged_device.cpp
${COMMON_ROOT}/src/bridge/bridge_storage_manager.cpp
Expand Down
2 changes: 1 addition & 1 deletion applications/matter_bridge/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ config BT_SCAN

# Configure how many Bluetooth LE devices the Matter bridge can support (the final number is given value - 1, as 1 connection is used for the Matter Bluetooth LE service).
config BT_MAX_CONN
default 2
default 3

config BT_SCAN_FILTER_ENABLE
default y
Expand Down
74 changes: 70 additions & 4 deletions applications/matter_bridge/src/app_task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "app_task.h"
#include "app_config.h"
#include "bridge_manager.h"
#include "bridge_storage_manager.h"
#include "bridged_devices_creator.h"
#include "led_util.h"

#ifdef CONFIG_BRIDGED_DEVICE_BT
Expand Down Expand Up @@ -154,13 +156,23 @@ CHIP_ERROR AppTask::Init()
ConfigurationMgr().LogDeviceConfig();
PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE));

/* Initialize bridge manager */
BridgeManager::Instance().Init();

#ifdef CONFIG_BRIDGED_DEVICE_BT
BLEConnectivityManager::Instance().Init(sUuidServices, kUuidServicesNumber);
/* Initialize BLE Connectivity Manager before the Bridge Manager, as it must be ready to recover devices loaded
* from persistent storaged during the bridge init. */
err = BLEConnectivityManager::Instance().Init(sUuidServices, kUuidServicesNumber);
if (err != CHIP_NO_ERROR) {
LOG_ERR("BLEConnectivityManager initialization failed");
return err;
}
#endif

/* Initialize bridge manager */
err = BridgeManager::Instance().Init(RestoreBridgedDevices);
if (err != CHIP_NO_ERROR) {
LOG_ERR("BridgeManager initialization failed");
return err;
}

/*
* Add CHIP event handler and start CHIP thread.
* Note that all the initialization code should happen prior to this point to avoid data races
Expand Down Expand Up @@ -360,3 +372,57 @@ void AppTask::DispatchEvent(const AppEvent &event)
LOG_INF("Event received with no handler. Dropping event.");
}
}

CHIP_ERROR AppTask::RestoreBridgedDevices()
{
uint8_t count;
uint8_t indexes[BridgeManager::kMaxBridgedDevices] = { 0 };
size_t indexesCount = 0;

if (!BridgeStorageManager::Instance().LoadBridgedDevicesCount(count)) {
LOG_INF("No bridged devices to load from the storage.");
return CHIP_NO_ERROR;
}

if (!BridgeStorageManager::Instance().LoadBridgedDevicesIndexes(indexes, BridgeManager::kMaxBridgedDevices,
indexesCount)) {
return CHIP_NO_ERROR;
}

/* Load all devices based on the read count number. */
for (size_t i = 0; i < indexesCount; i++) {
uint16_t endpointId;
char label[MatterBridgedDevice::kNodeLabelSize] = { 0 };
size_t labelSize;
uint16_t deviceType;

if (!BridgeStorageManager::Instance().LoadBridgedDeviceEndpointId(endpointId, indexes[i])) {
return CHIP_ERROR_NOT_FOUND;
}

/* Ignore an error, as node label is optional, so it may not be found. */
BridgeStorageManager::Instance().LoadBridgedDeviceNodeLabel(label, sizeof(label), labelSize,
indexes[i]);

if (!BridgeStorageManager::Instance().LoadBridgedDeviceType(deviceType, indexes[i])) {
return CHIP_ERROR_NOT_FOUND;
}

LOG_INF("Loaded bridged device on endpoint id %d from the storage", endpointId);

#ifdef CONFIG_BRIDGED_DEVICE_BT
bt_addr_le_t addr;

if (!BridgeStorageManager::Instance().LoadBtAddress(addr, indexes[i])) {
return CHIP_ERROR_NOT_FOUND;
}

BridgedDeviceCreator::CreateDevice(deviceType, label, addr, chip::Optional<uint8_t>(indexes[i]),
chip::Optional<uint16_t>(endpointId));
#else
BridgedDeviceCreator::CreateDevice(deviceType, label, chip::Optional<uint8_t>(indexes[i]),
chip::Optional<uint16_t>(endpointId));
#endif
}
return CHIP_NO_ERROR;
}
1 change: 1 addition & 0 deletions applications/matter_bridge/src/app_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class AppTask {
static void LEDStateUpdateHandler(LEDWidget &ledWidget);
static void FunctionTimerTimeoutCallback(k_timer *timer);
static void UpdateStatusLED();
static CHIP_ERROR RestoreBridgedDevices();

FunctionEvent mFunction = FunctionEvent::NoneSelected;
bool mFunctionTimerActive = false;
Expand Down
152 changes: 19 additions & 133 deletions applications/matter_bridge/src/bridge_shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,116 +5,19 @@
*/

#include "bridge_manager.h"
#include "bridged_device_factory.h"

#include <zephyr/logging/log.h>
#include <zephyr/shell/shell.h>
#include "bridged_devices_creator.h"

#ifdef CONFIG_BRIDGED_DEVICE_BT
#include "ble_bridged_device.h"
#include "ble_connectivity_manager.h"
#endif /* CONFIG_BRIDGED_DEVICE_BT */

LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL);

#ifdef CONFIG_BRIDGED_DEVICE_SIMULATED
static BridgedDeviceDataProvider *CreateSimulatedProvider(int deviceType)
{
return BridgeFactory::GetSimulatedDataProviderFactory().Create(
static_cast<MatterBridgedDevice::DeviceType>(deviceType), BridgeManager::HandleUpdate);
}
#endif /* CONFIG_BRIDGED_DEVICE_SIMULATED */

#ifdef CONFIG_BRIDGED_DEVICE_BT

static BridgedDeviceDataProvider *CreateBleProvider(int deviceType)
{
return BridgeFactory::GetBleDataProviderFactory().Create(
static_cast<MatterBridgedDevice::DeviceType>(deviceType), BridgeManager::HandleUpdate);
}

struct BluetoothConnectionContext {
const struct shell *shell;
int deviceType;
char nodeLabel[MatterBridgedDevice::kNodeLabelSize];
BLEBridgedDeviceProvider *provider;
};
#endif /* CONFIG_BRIDGED_DEVICE_BT */

static void AddDevice(const struct shell *shell, int deviceType, const char *nodeLabel,
BridgedDeviceDataProvider *provider)
{
VerifyOrReturn(provider != nullptr, shell_fprintf(shell, SHELL_INFO, "No valid data provider!\n"));

CHIP_ERROR err;
if (deviceType == MatterBridgedDevice::DeviceType::EnvironmentalSensor) {
/* Handle special case for environmental sensor */
auto *temperatureBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create(
MatterBridgedDevice::DeviceType::TemperatureSensor, nodeLabel);

VerifyOrReturn(temperatureBridgedDevice != nullptr, delete provider,
shell_fprintf(shell, SHELL_INFO, "Cannot allocate Matter device of given type\n"));

auto *humidityBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create(
MatterBridgedDevice::DeviceType::HumiditySensor, nodeLabel);

VerifyOrReturn(humidityBridgedDevice != nullptr, delete provider, delete temperatureBridgedDevice,
shell_fprintf(shell, SHELL_INFO, "Cannot allocate Matter device of given type\n"));

MatterBridgedDevice *newBridgedDevices[] = { temperatureBridgedDevice, humidityBridgedDevice };
err = BridgeManager::Instance().AddBridgedDevices(newBridgedDevices, provider,
ARRAY_SIZE(newBridgedDevices));

} else {
auto *newBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create(
static_cast<MatterBridgedDevice::DeviceType>(deviceType), nodeLabel);

MatterBridgedDevice *newBridgedDevices[] = { newBridgedDevice };
VerifyOrReturn(newBridgedDevice != nullptr, delete provider,
shell_fprintf(shell, SHELL_INFO, "Cannot allocate Matter device of given type\n"));
err = BridgeManager::Instance().AddBridgedDevices(newBridgedDevices, provider,
ARRAY_SIZE(newBridgedDevices));
}

if (err == CHIP_NO_ERROR) {
shell_fprintf(shell, SHELL_INFO, "Done\n");
} else if (err == CHIP_ERROR_INVALID_STRING_LENGTH) {
shell_fprintf(shell, SHELL_ERROR, "Error: too long node label (max %d)\n",
MatterBridgedDevice::kNodeLabelSize);
} else if (err == CHIP_ERROR_NO_MEMORY) {
shell_fprintf(shell, SHELL_ERROR, "Error: no memory\n");
} else if (err == CHIP_ERROR_INVALID_ARGUMENT) {
shell_fprintf(shell, SHELL_ERROR, "Error: invalid device type\n");
} else {
shell_fprintf(shell, SHELL_ERROR, "Error: internal\n");
}
}

#ifdef CONFIG_BRIDGED_DEVICE_BT
static void BluetoothDeviceConnected(BLEBridgedDevice *device, bt_gatt_dm *discoveredData, bool discoverySucceeded,
void *context)
{
if (context && device && discoveredData && discoverySucceeded) {
BluetoothConnectionContext *ctx = reinterpret_cast<BluetoothConnectionContext *>(context);
chip::Platform::UniquePtr<BluetoothConnectionContext> ctxPtr(ctx);

if (ctxPtr->provider->MatchBleDevice(device)) {
return;
}

if (ctxPtr->provider->ParseDiscoveredData(discoveredData)) {
return;
}

AddDevice(ctx->shell, ctx->deviceType, ctx->nodeLabel, ctx->provider);
}
}
#endif /* CONFIG_BRIDGED_DEVICE_BT */
#include <zephyr/shell/shell.h>

static int AddBridgedDeviceHandler(const struct shell *shell, size_t argc, char **argv)
{
int deviceType = strtoul(argv[1], NULL, 0);
char *nodeLabel = nullptr;
CHIP_ERROR result = CHIP_NO_ERROR;

#if defined(CONFIG_BRIDGED_DEVICE_BT)
int bleDeviceIndex = strtoul(argv[2], NULL, 0);
Expand All @@ -123,62 +26,45 @@ static int AddBridgedDeviceHandler(const struct shell *shell, size_t argc, char
nodeLabel = argv[3];
}

/* The device object can be created once the Bluetooth LE connection will be established. */
BluetoothConnectionContext *context = chip::Platform::New<BluetoothConnectionContext>();

if (!context) {
return -ENOMEM;
}

chip::Platform::UniquePtr<BluetoothConnectionContext> contextPtr(context);
contextPtr->shell = shell;
contextPtr->deviceType = deviceType;

if (nodeLabel) {
strcpy(contextPtr->nodeLabel, nodeLabel);
}

BridgedDeviceDataProvider *provider = CreateBleProvider(contextPtr->deviceType);

if (!provider) {
shell_fprintf(shell, SHELL_INFO, "Cannot allocate data provider of given type\n");
return -ENOMEM;
bt_addr_le_t address;
if (BLEConnectivityManager::Instance().GetScannedDeviceAddress(&address, bleDeviceIndex) != CHIP_NO_ERROR) {
shell_fprintf(shell, SHELL_ERROR, "Invalid Bluetooth LE device index.\n");
} else {
shell_fprintf(shell, SHELL_ERROR, "Found device address\n");
}

contextPtr->provider = static_cast<BLEBridgedDeviceProvider *>(provider);

CHIP_ERROR err = BLEConnectivityManager::Instance().Connect(
bleDeviceIndex, BluetoothDeviceConnected, contextPtr.get(), contextPtr->provider->GetServiceUuid());

if (err == CHIP_NO_ERROR) {
contextPtr.release();
}
result = BridgedDeviceCreator::CreateDevice(deviceType, nodeLabel, address);

#elif defined(CONFIG_BRIDGED_DEVICE_SIMULATED)

if (argv[2]) {
nodeLabel = argv[2];
}

/* The device is simulated, so it can be added immediately. */
BridgedDeviceDataProvider *provider = CreateSimulatedProvider(deviceType);
AddDevice(shell, deviceType, nodeLabel, provider);
result = BridgedDeviceCreator::CreateDevice(deviceType, nodeLabel);

#else
return -ENOTSUP;

#endif /* CONFIG_BRIDGED_DEVICE_BT */

if (result == CHIP_NO_ERROR) {
shell_fprintf(shell, SHELL_INFO, "Done\n");
} else {
shell_fprintf(shell, SHELL_ERROR, "Device add failed\n");
}

return 0;
}

static int RemoveBridgedDeviceHandler(const struct shell *shell, size_t argc, char **argv)
{
int endpointId = strtoul(argv[1], NULL, 0);

if (CHIP_NO_ERROR == BridgeManager::Instance().RemoveBridgedDevice(endpointId)) {
if (CHIP_NO_ERROR == BridgedDeviceCreator::RemoveDevice(endpointId)) {
shell_fprintf(shell, SHELL_INFO, "Done\n");
} else {
shell_fprintf(shell, SHELL_ERROR, "Error: device not found\n");
shell_fprintf(shell, SHELL_ERROR, "Error: device remove failed\n");
}
return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ bt_gatt_read_params BleEnvironmentalDataProvider::sHumidityReadParams{};

BleEnvironmentalDataProvider *GetProvider(bt_conn *conn)
{
BLEBridgedDevice *device = BLEConnectivityManager::Instance().FindBLEBridgedDevice(conn);
VerifyOrReturnValue(device, nullptr);
return reinterpret_cast<BleEnvironmentalDataProvider *>(device->mProvider);
return static_cast<BleEnvironmentalDataProvider *>(
BLEConnectivityManager::Instance().FindBLEProvider(*bt_conn_get_dst(conn)));
}

bt_uuid *BleEnvironmentalDataProvider::GetServiceUuid()
Expand All @@ -40,6 +39,7 @@ uint8_t BleEnvironmentalDataProvider::GattTemperatureNotifyCallback(bt_conn *con
const void *data, uint16_t length)
{
BleEnvironmentalDataProvider *provider = GetProvider(conn);

VerifyOrExit(provider, );
VerifyOrExit(data, );
VerifyOrExit(length == sizeof(mTemperatureValue), );
Expand Down Expand Up @@ -148,10 +148,10 @@ CHIP_ERROR BleEnvironmentalDataProvider::ParseHumidityCharacteristic(bt_gatt_dm

void BleEnvironmentalDataProvider::Subscribe()
{
VerifyOrReturn(mDevice && mDevice->mConn, LOG_ERR("Invalid connection object"));
VerifyOrReturn(mDevice.mConn, LOG_ERR("Invalid connection object"));

if (CheckSubscriptionParameters(&mGattTemperatureSubscribeParams)) {
int err = bt_gatt_subscribe(mDevice->mConn, &mGattTemperatureSubscribeParams);
int err = bt_gatt_subscribe(mDevice.mConn, &mGattTemperatureSubscribeParams);
if (err) {
LOG_ERR("Subscribe to temperature characteristic failed with error %d", err);
}
Expand All @@ -160,7 +160,7 @@ void BleEnvironmentalDataProvider::Subscribe()
}

if (CheckSubscriptionParameters(&mGattHumiditySubscribeParams)) {
int err = bt_gatt_subscribe(mDevice->mConn, &mGattHumiditySubscribeParams);
int err = bt_gatt_subscribe(mDevice.mConn, &mGattHumiditySubscribeParams);
if (err) {
LOG_ERR("Subscribe to humidity characteristic failed with error %d", err);
}
Expand All @@ -178,14 +178,14 @@ void BleEnvironmentalDataProvider::Subscribe()

void BleEnvironmentalDataProvider::Unsubscribe()
{
VerifyOrReturn(mDevice && mDevice->mConn, LOG_ERR("Invalid connection object"));
VerifyOrReturn(mDevice.mConn, LOG_ERR("Invalid connection object"));

int err = bt_gatt_unsubscribe(mDevice->mConn, &mGattTemperatureSubscribeParams);
int err = bt_gatt_unsubscribe(mDevice.mConn, &mGattTemperatureSubscribeParams);
if (err) {
LOG_INF("Cannot unsubscribe from temperature characteristic (error %d)", err);
}

err = bt_gatt_unsubscribe(mDevice->mConn, &mGattHumiditySubscribeParams);
err = bt_gatt_unsubscribe(mDevice.mConn, &mGattHumiditySubscribeParams);
if (err) {
LOG_INF("Cannot unsubscribe from humidity characteristic (error %d)", err);
}
Expand Down Expand Up @@ -233,7 +233,7 @@ void BleEnvironmentalDataProvider::HumidityTimerTimeoutCallback(k_timer *timer)

BleEnvironmentalDataProvider *provider = reinterpret_cast<BleEnvironmentalDataProvider *>(timer->user_data);
VerifyOrReturn(provider, LOG_ERR("Invalid provider object"));
VerifyOrReturn(provider->mDevice && provider->mDevice->mConn, LOG_ERR("Invalid connection object"));
VerifyOrReturn(provider->mDevice.mConn, LOG_ERR("Invalid connection object"));

DeviceLayer::PlatformMgr().ScheduleWork(ReadGATTHumidity, reinterpret_cast<intptr_t>(provider));
}
Expand All @@ -242,7 +242,7 @@ void BleEnvironmentalDataProvider::ReadGATTHumidity(intptr_t context)
{
BleEnvironmentalDataProvider *provider = reinterpret_cast<BleEnvironmentalDataProvider *>(context);

int err = bt_gatt_read(provider->mDevice->mConn, &sHumidityReadParams);
int err = bt_gatt_read(provider->mDevice.mConn, &sHumidityReadParams);
if (err) {
LOG_INF("GATT read failed (err %d)", err);
}
Expand Down
Loading

0 comments on commit 0204f74

Please sign in to comment.