Skip to content

Commit

Permalink
Auto-commissioner: support secondary network interface commissioning
Browse files Browse the repository at this point in the history
  • Loading branch information
DejinChen committed Jun 7, 2024
1 parent 4239c88 commit 908f57b
Show file tree
Hide file tree
Showing 15 changed files with 227 additions and 26 deletions.
9 changes: 9 additions & 0 deletions examples/chip-tool/commands/pairing/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ class PairBleThread : public PairingCommand
{}
};

class PairBleWiFiOrThread : public PairingCommand
{
public:
PairBleWiFiOrThread(CredentialIssuerCommands * credsIssuerConfig) :
PairingCommand("ble-wifi-thread", PairingMode::Ble, PairingNetworkType::WiFiOrThread, credsIssuerConfig)
{}
};

class PairSoftAP : public PairingCommand
{
public:
Expand Down Expand Up @@ -233,6 +241,7 @@ void registerCommandsPairing(Commands & commands, CredentialIssuerCommands * cre
make_unique<PairCodeThread>(credsIssuerConfig),
make_unique<PairBleWiFi>(credsIssuerConfig),
make_unique<PairBleThread>(credsIssuerConfig),
make_unique<PairBleWiFiOrThread>(credsIssuerConfig),
make_unique<PairSoftAP>(credsIssuerConfig),
make_unique<PairAlreadyDiscovered>(credsIssuerConfig),
make_unique<PairAlreadyDiscoveredByIndex>(credsIssuerConfig),
Expand Down
4 changes: 4 additions & 0 deletions examples/chip-tool/commands/pairing/PairingCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ CommissioningParameters PairingCommand::GetCommissioningParameters()
case PairingNetworkType::Thread:
params.SetThreadOperationalDataset(mOperationalDataset);
break;
case PairingNetworkType::WiFiOrThread:
params.SetWiFiCredentials(Controller::WiFiCredentials(mSSID, mPassword));
params.SetThreadOperationalDataset(mOperationalDataset);
break;
case PairingNetworkType::None:
break;
}
Expand Down
6 changes: 6 additions & 0 deletions examples/chip-tool/commands/pairing/PairingCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum class PairingNetworkType
None,
WiFi,
Thread,
WiFiOrThread,
};

class PairingCommand : public CHIPCommand,
Expand Down Expand Up @@ -85,6 +86,11 @@ class PairingCommand : public CHIPCommand,
case PairingNetworkType::Thread:
AddArgument("operationalDataset", &mOperationalDataset);
break;
case PairingNetworkType::WiFiOrThread:
AddArgument("ssid", &mSSID);
AddArgument("password", &mPassword);
AddArgument("operationalDataset", &mOperationalDataset);
break;
}

switch (mode)
Expand Down
55 changes: 32 additions & 23 deletions src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,19 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag

CommissioningStage AutoCommissioner::GetNextCommissioningStageNetworkSetup(CommissioningStage currentStage, CHIP_ERROR & lastErr)
{
if (IsSecondaryNetworkSupported())
{
if (IsTriedSecondaryNetwork())
{
// Try secondary network interface.
return mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? CommissioningStage::kThreadNetworkSetup
: CommissioningStage::kWiFiNetworkSetup;
}
// Try primary network interface
return mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? CommissioningStage::kWiFiNetworkSetup
: CommissioningStage::kThreadNetworkSetup;
}

if (mParams.GetWiFiCredentials().HasValue() && mDeviceCommissioningInfo.network.wifi.endpoint != kInvalidEndpointId)
{
return CommissioningStage::kWiFiNetworkSetup;
Expand Down Expand Up @@ -455,35 +468,15 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
case CommissioningStage::kNeedsNetworkCreds:
return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
case CommissioningStage::kWiFiNetworkSetup:
if (mParams.GetThreadOperationalDataset().HasValue() &&
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId)
{
return CommissioningStage::kThreadNetworkSetup;
}
else
{
return CommissioningStage::kFailsafeBeforeWiFiEnable;
}
return CommissioningStage::kFailsafeBeforeWiFiEnable;
case CommissioningStage::kThreadNetworkSetup:
if (mParams.GetWiFiCredentials().HasValue() && mDeviceCommissioningInfo.network.wifi.endpoint != kInvalidEndpointId)
{
return CommissioningStage::kFailsafeBeforeWiFiEnable;
}
else
{
return CommissioningStage::kFailsafeBeforeThreadEnable;
}
return CommissioningStage::kFailsafeBeforeThreadEnable;
case CommissioningStage::kFailsafeBeforeWiFiEnable:
return CommissioningStage::kWiFiNetworkEnable;
case CommissioningStage::kFailsafeBeforeThreadEnable:
return CommissioningStage::kThreadNetworkEnable;
case CommissioningStage::kWiFiNetworkEnable:
if (mParams.GetThreadOperationalDataset().HasValue() &&
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId)
{
return CommissioningStage::kThreadNetworkEnable;
}
else if (mParams.GetSkipCommissioningComplete().ValueOr(false))
if (mParams.GetSkipCommissioningComplete().ValueOr(false))
{
SetCASEFailsafeTimerIfNeeded();
return CommissioningStage::kCleanup;
Expand All @@ -502,6 +495,10 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
return CommissioningStage::kEvictPreviousCaseSessions;
case CommissioningStage::kEvictPreviousCaseSessions:
return CommissioningStage::kFindOperationalForStayActive;
case CommissioningStage::kPrimaryOperationalNetworkFailed:
return CommissioningStage::kDisablePrimaryNetworkInterface;
case CommissioningStage::kDisablePrimaryNetworkInterface:
return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
case CommissioningStage::kFindOperationalForStayActive:
return CommissioningStage::kICDSendStayActive;
case CommissioningStage::kICDSendStayActive:
Expand Down Expand Up @@ -564,6 +561,8 @@ EndpointId AutoCommissioner::GetEndpoint(const CommissioningStage & stage) const
case CommissioningStage::kThreadNetworkSetup:
case CommissioningStage::kThreadNetworkEnable:
return mDeviceCommissioningInfo.network.thread.endpoint;
case CommissioningStage::kDisablePrimaryNetworkInterface:
return kRootEndpointId;
default:
return kRootEndpointId;
}
Expand Down Expand Up @@ -729,6 +728,16 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
report.stageCompleted = CommissioningStage::kScanNetworks;
}
}

if (err != CHIP_NO_ERROR && IsSecondaryNetworkSupported() && !IsTriedSecondaryNetwork() &&
completionStatus.failedStage.HasValue() && completionStatus.failedStage.Value() >= kWiFiNetworkSetup &&
completionStatus.failedStage.Value() <= kICDSendStayActive)
{
// Primary network failed, disable primary network interface and try secondary network interface.
SetTrySecondaryNetwork();
err = CHIP_NO_ERROR;
report.stageCompleted = CommissioningStage::kPrimaryOperationalNetworkFailed;
}
}
else
{
Expand Down
15 changes: 15 additions & 0 deletions src/controller/AutoCommissioner.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,21 @@ class AutoCommissioner : public CommissioningDelegate
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId));
};

// Helper function to Determine whether secondary network interface is supported.
// Only true if information is provided for both networks, and the target has endpoint
// for wifi and thread.
bool IsSecondaryNetworkSupported() const
{
return ((mParams.GetSupportsConcurrentConnection().ValueOr(false) && mParams.GetWiFiCredentials().HasValue() &&
mDeviceCommissioningInfo.network.wifi.endpoint != kInvalidEndpointId) &&
mParams.GetThreadOperationalDataset().HasValue() &&
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId);
}

void SetTrySecondaryNetwork() { mAttemptedSecondaryNetwork = true; }
bool IsTriedSecondaryNetwork() const { return mAttemptedSecondaryNetwork; }
bool mAttemptedSecondaryNetwork = false;

bool mStopCommissioning = false;

DeviceCommissioner * mCommissioner = nullptr;
Expand Down
39 changes: 39 additions & 0 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <app/server/Dnssd.h>
#include <controller/CurrentFabricRemover.h>
#include <controller/InvokeInteraction.h>
#include <controller/WriteInteraction.h>
#include <credentials/CHIPCert.h>
#include <credentials/DeviceAttestationCredsProvider.h>
#include <crypto/CHIPCryptoPAL.h>
Expand Down Expand Up @@ -1801,6 +1802,12 @@ void DeviceCommissioner::OnBasicSuccess(void * context, const chip::app::DataMod
commissioner->CommissioningStageComplete(CHIP_NO_ERROR);
}

void DeviceCommissioner::OnInterfaceEnableWriteSuccessResponse(void * context)
{
DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);
commissioner->CommissioningStageComplete(CHIP_NO_ERROR);
}

void DeviceCommissioner::OnBasicFailure(void * context, CHIP_ERROR error)
{
ChipLogProgress(Controller, "Received failure response %s\n", chip::ErrorStr(error));
Expand Down Expand Up @@ -2740,6 +2747,18 @@ DeviceCommissioner::SendCommissioningCommand(DeviceProxy * device, const Request
onFailureCb, NullOptional, timeout, (!fireAndForget) ? &mInvokeCancelFn : nullptr);
}

template <typename AttrType>
CHIP_ERROR DeviceCommissioner::SendCommissioningWriteRequest(DeviceProxy * device, EndpointId endpoint, ClusterId cluster,
AttributeId attribute, const AttrType & requestData,
WriteResponseSuccessCallback successCb,
WriteResponseFailureCallback failureCb)
{
auto onSuccessCb = [this, successCb](const app::ConcreteAttributePath & aPath) { successCb(this); };
auto onFailureCb = [this, failureCb](const app::ConcreteAttributePath * aPath, CHIP_ERROR aError) { failureCb(this, aError); };
return WriteAttribute(device->GetSecureSession().Value(), endpoint, cluster, attribute, requestData, onSuccessCb, onFailureCb,
NullOptional, nullptr, NullOptional);
}

void DeviceCommissioner::SendCommissioningReadRequest(DeviceProxy * proxy, Optional<System::Clock::Timeout> timeout,
app::AttributePathParams * readPaths, size_t readPathsSize)
{
Expand Down Expand Up @@ -3424,6 +3443,26 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
);
}
break;
case CommissioningStage::kPrimaryOperationalNetworkFailed: {
// nothing to do. This stage indicates that the primary operation network failed and the network interface should be
// disabled later.
break;
}
case CommissioningStage::kDisablePrimaryNetworkInterface: {
NetworkCommissioning::Attributes::InterfaceEnabled::TypeInfo::Type request = false;
CHIP_ERROR err = SendCommissioningWriteRequest(
proxy, endpoint, NetworkCommissioning::Attributes::InterfaceEnabled::TypeInfo::GetClusterId(),
NetworkCommissioning::Attributes::InterfaceEnabled::TypeInfo::GetAttributeId(), request,
OnInterfaceEnableWriteSuccessResponse, OnBasicFailure);
if (err != CHIP_NO_ERROR)
{
// We won't get any async callbacks here, so just complete our stage.
ChipLogError(Controller, "Failed to send InterfaceEnabled write request: %" CHIP_ERROR_FORMAT, err.Format());
CommissioningStageComplete(err);
return;
}
break;
}
case CommissioningStage::kICDSendStayActive: {
if (!(params.GetICDStayActiveDurationMsec().HasValue()))
{
Expand Down
6 changes: 6 additions & 0 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
OnICDManagementStayActiveResponse(void * context,
const app::Clusters::IcdManagement::Commands::StayActiveResponse::DecodableType & data);

static void OnInterfaceEnableWriteSuccessResponse(void * context);

/**
* @brief
* This function processes the CSR sent by the device.
Expand Down Expand Up @@ -1025,6 +1027,10 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
Optional<System::Clock::Timeout> timeout = NullOptional, bool fireAndForget = false);
void SendCommissioningReadRequest(DeviceProxy * proxy, Optional<System::Clock::Timeout> timeout,
app::AttributePathParams * readPaths, size_t readPathsSize);
template <typename AttrType>
CHIP_ERROR SendCommissioningWriteRequest(DeviceProxy * device, EndpointId endpoint, ClusterId cluster, AttributeId attribute,
const AttrType & requestData, WriteResponseSuccessCallback successCb,
WriteResponseFailureCallback failureCb);
void CancelCommissioningInteractions();
void CancelCASECallbacks();

Expand Down
6 changes: 6 additions & 0 deletions src/controller/CommissioningDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ const char * StageToString(CommissioningStage stage)
case kNeedsNetworkCreds:
return "NeedsNetworkCreds";

case kPrimaryOperationalNetworkFailed:
return "kPrimaryOperationalNetworkFailed";

case kDisablePrimaryNetworkInterface:
return "kDisablePrimaryNetworkInterface";

default:
return "???";
}
Expand Down
3 changes: 3 additions & 0 deletions src/controller/CommissioningDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ enum CommissioningStage : uint8_t
/// Call CHIPDeviceController::NetworkCredentialsReady() when CommissioningParameters is populated with
/// network credentials to use in kWiFiNetworkSetup or kThreadNetworkSetup steps.
kNeedsNetworkCreds,
kPrimaryOperationalNetworkFailed, ///< Indicate that the primary operational network (on root endpoint) failed, should disable
///< the primary network interface later.
kDisablePrimaryNetworkInterface, ///< Send InterfaceEnabled write request to the device to disable network interface.
};

enum class ICDRegistrationStrategy : uint8_t
Expand Down
4 changes: 4 additions & 0 deletions src/platform/ESP32/ConnectivityManagerImpl_WiFi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,10 @@ CHIP_ERROR ConnectivityManagerImpl::InitWiFi()
mWiFiStationMode = kWiFiStationMode_Disabled;
mWiFiStationState = kWiFiStationState_NotConnected;
mWiFiStationReconnectInterval = System::Clock::Milliseconds32(CHIP_DEVICE_CONFIG_WIFI_STATION_RECONNECT_INTERVAL);
if (!NetworkCommissioning::ESPWiFiDriver::GetInstance().GetEnabled())
{
mWiFiStationMode = kWiFiStationMode_ApplicationControlled;
}

#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP
mLastAPDemandTime = System::Clock::kZero;
Expand Down
49 changes: 49 additions & 0 deletions src/platform/ESP32/NetworkCommissioningDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ CHIP_ERROR ESPWiFiDriver::CommitConfiguration()
CHIP_ERROR ESPWiFiDriver::RevertConfiguration()
{
mStagingNetwork = mSavedNetwork;
if (!GetEnabled())
{
// When reverting, set InterfaceEnabled to default value (true).
ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Delete(kInterfaceEnabled));
}
return CHIP_NO_ERROR;
}

Expand Down Expand Up @@ -196,6 +201,12 @@ Status ESPWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableC

CHIP_ERROR ESPWiFiDriver::ConnectWiFiNetwork(const char * ssid, uint8_t ssidLen, const char * key, uint8_t keyLen)
{
if (!GetEnabled())
{
// Set InterfaceEnabled to default value (true).
ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Delete(kInterfaceEnabled));
}

// If device is already connected to WiFi, then disconnect the WiFi,
// clear the WiFi configurations and add the newly provided WiFi configurations.
if (chip::DeviceLayer::Internal::ESP32Utils::IsStationProvisioned())
Expand Down Expand Up @@ -307,6 +318,44 @@ void ESPWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callbac
}
}

CHIP_ERROR ESPWiFiDriver::SetEnabled(bool enabled)
{
if (enabled == GetEnabled())
{
return CHIP_NO_ERROR;
}

ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kInterfaceEnabled, &enabled, sizeof(enabled)));

if (!enabled)
{
if (chip::DeviceLayer::Internal::ESP32Utils::IsStationProvisioned())
{
ChipLogProgress(DeviceLayer, "Disconnecting WiFi station interface");
esp_err_t err = esp_wifi_disconnect();
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_disconnect() failed: %s", esp_err_to_name(err));
return chip::DeviceLayer::Internal::ESP32Utils::MapError(err);
}
return ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_ApplicationControlled);
}
}
else
{
ReturnErrorOnFailure(ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled));
}
return CHIP_NO_ERROR;
}

bool ESPWiFiDriver::GetEnabled()
{
bool value;
// InterfaceEnabled default value is true.
VerifyOrReturnValue(PersistedStorage::KeyValueStoreMgr().Get(kInterfaceEnabled, &value, sizeof(value)) == CHIP_NO_ERROR, true);
return value;
}

CHIP_ERROR ESPWiFiDriver::StartScanWiFiNetworks(ByteSpan ssid)
{
esp_err_t err = ESP_OK;
Expand Down
3 changes: 3 additions & 0 deletions src/platform/ESP32/NetworkCommissioningDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class ESPWiFiDriver final : public WiFiDriver
// BaseDriver
NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); }
CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) override;
CHIP_ERROR SetEnabled(bool enabled) override;
bool GetEnabled() override;
void Shutdown() override;

// WirelessDriver
Expand Down Expand Up @@ -131,6 +133,7 @@ class ESPWiFiDriver final : public WiFiDriver
}

private:
static constexpr const char * kInterfaceEnabled = "g/esp/en";
bool NetworkMatch(const WiFiNetwork & network, ByteSpan networkId);
CHIP_ERROR StartScanWiFiNetworks(ByteSpan ssid);

Expand Down
Loading

0 comments on commit 908f57b

Please sign in to comment.