Skip to content

Network Recovery Feature ESP32 Example #38554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
86 changes: 85 additions & 1 deletion examples/lighting-app/esp32/main/AppTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@

#include <app-common/zap-generated/attributes/Accessors.h>

#include <platform/CHIPDeviceConfig.h>
#include <platform/CHIPDeviceLayer.h>
#include <platform/KeyValueStoreManager.h>
#include <esp_wifi.h>
#include <esp_timer.h>
#include "Button.h"
#include <app-common/zap-generated/cluster-objects.h>
#include <app/clusters/general-commissioning-server/general-commissioning-server.h>

#define APP_TASK_NAME "APP"
#define APP_EVENT_QUEUE_SIZE 10
#define APP_TASK_STACK_SIZE (3072)
Expand All @@ -47,6 +56,71 @@ TaskHandle_t sAppTaskHandle;

AppTask AppTask::sAppTask;

#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
/* add the timer for checking WiFi state*/
static esp_timer_handle_t g_checking_state_polling_timer_handle = NULL;
static void checking_state_polling_cb(void *args)
{
static uint8_t timerCount = 0;
static bool lastWiFiStationConnected = true;
chip::app::Clusters::GeneralCommissioning::Attributes::NetworkRecoveryReason::TypeInfo::Type reason;
if (chip::DeviceLayer::ConnectivityMgr().IsWiFiStationConnected()){
timerCount = 0;
if (false == lastWiFiStationConnected){
int WiFiDisconnectFlagValue = 0;
//chip::DeviceLayer::PersistedStorage::KeyValueStoreMgr().Get("WiFiDisconnectFlag", &WiFiDisconnectFlagValue);
if (WiFiDisconnectFlagValue != 0x11){
lastWiFiStationConnected = true;
ESP_LOGI(TAG, "WiFi Connected");
chip::DeviceLayer::PlatformMgr().LockChipStack();
if (ConnectivityMgr().IsWiFiStationProvisioned()){
//chip::app::DataModel::Nullable<chip::app::Clusters::GeneralCommissioning::NetworkRecoveryReasonEnum> reason;
chip::app::Clusters::GeneralCommissioning::GetNetworkRecoveryReasonValue(reason);
if (!reason.IsNull()){
//chip::app::DataModel::Nullable<chip::app::Clusters::GeneralCommissioning::NetworkRecoveryReasonEnum> reason;
reason.SetNull();
//chip::app::Clusters::GeneralCommissioning::Attributes::NetworkRecoveryReason::Set(kRootEndpointId, reason);
chip::app::Clusters::GeneralCommissioning::SetNetworkRecoveryReasonValue(reason);
if (ConnectivityMgr().IsBLEAdvertising()){
ESP_LOGI(TAG, "Stop WiFi Network Recovery BLE Advertisement");
ConnectivityMgr().SetBLEAdvertisingEnabled(false);
}
}
}
chip::DeviceLayer::PlatformMgr().UnlockChipStack();
}
} else {
//Do nothing
}
} else {
if (lastWiFiStationConnected){
//wifi disconnected
if (timerCount < 10){
timerCount++; //checking ten seconds
ESP_LOGI(TAG, "WiFi Disonnected Count: %d",timerCount);
} else {
lastWiFiStationConnected = false;
using chip::DeviceLayer::ConnectivityMgr;
chip::DeviceLayer::PlatformMgr().LockChipStack();
if (ConnectivityMgr().IsWiFiStationProvisioned()){
ESP_LOGI(TAG, "Start WiFi Network Recovery BLE Advertisement");
chip::app::Clusters::GeneralCommissioning::GetNetworkRecoveryReasonValue(reason);
if (reason.IsNull()){
reason = chip::app::Clusters::GeneralCommissioning::NetworkRecoveryReasonEnum::kVisibility;
chip::app::Clusters::GeneralCommissioning::SetNetworkRecoveryReasonValue(reason);
}
ConnectivityMgr().SetBLEAdvertisingEnabled(true);
}
chip::DeviceLayer::PlatformMgr().UnlockChipStack();
}
} else {
timerCount = 0;
}
}
}
//end checking WiFi state
#endif //CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED

CHIP_ERROR AppTask::StartAppTask()
{
sAppEventQueue = xQueueCreate(APP_EVENT_QUEUE_SIZE, sizeof(AppEvent));
Expand Down Expand Up @@ -109,13 +183,23 @@ CHIP_ERROR AppTask::Init()
CHIP_ERROR err = CHIP_NO_ERROR;

AppLED.Init();

#if CONFIG_HAVE_DISPLAY
InitDeviceDisplay();

AppLED.SetVLED(ScreenManager::AddVLED(TFT_YELLOW));
#endif

#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
/* add the timer for checking WiFi state*/
esp_timer_create_args_t checking_state_polling_timer;
checking_state_polling_timer.arg = NULL;
checking_state_polling_timer.callback = checking_state_polling_cb;
checking_state_polling_timer.dispatch_method = ESP_TIMER_TASK;
checking_state_polling_timer.name = "checking_state_polling_timer";
esp_timer_create(&checking_state_polling_timer, &g_checking_state_polling_timer_handle);
esp_timer_start_periodic(g_checking_state_polling_timer_handle, 1000000U);
//end
#endif //CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
return err;
}

Expand Down
4 changes: 4 additions & 0 deletions examples/lighting-app/esp32/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ menu "Demo"
help
To reduce wear and heat the M5Stack's Display is automatically switched off after a few seconds

config CHIP_NETWORK_RECOVERY_REQUIRED
bool "chip device network recovery by ble. When enable it, should disable USE_BLE_ONLY_FOR_COMMISSIONING."
default "y"
depends on RENDEZVOUS_MODE_BLE
endmenu

menu "PW RPC Debug channel"
Expand Down
34 changes: 33 additions & 1 deletion examples/lighting-app/lighting-common/lighting-app.zap
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,38 @@
"maxInterval": 65534,
"reportableChange": 0
},
{
"name": "RecoveryIdentifier",
"code": 11,
"mfgCode": null,
"side": "server",
"type": "octet_string",
"included": 1,
"storageOption": "NVM",
"singleton": 0,
"bounded": 0,
"defaultValue": "0000000",
"reportable": 1,
"minInterval": 0,
"maxInterval": 65534,
"reportableChange": 0
},
{
"name": "NetworkRecoveryReason",
"code": 12,
"mfgCode": null,
"side": "server",
"type": "NetworkRecoveryReasonEnum",
"included": 1,
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
"defaultValue": null,
"reportable": 1,
"minInterval": 0,
"maxInterval": 65534,
"reportableChange": 0
},
{
"name": "GeneratedCommandList",
"code": 65528,
Expand Down Expand Up @@ -1416,7 +1448,7 @@
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
"defaultValue": "0",
"defaultValue": "2",
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,16 @@ class GeneralCommissioningGlobalInstance : public AttributeAccessInterface, publ
GeneralCommissioningGlobalInstance() :
AttributeAccessInterface(Optional<EndpointId>::Missing(), GeneralCommissioning::Id),
CommandHandlerInterface(Optional<EndpointId>::Missing(), GeneralCommissioning::Id)
{}
{
#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
mNetworkRecoveryReasonValue.SetNull();
#endif //CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
}

CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;

#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
Attributes::NetworkRecoveryReason::TypeInfo::Type mNetworkRecoveryReasonValue;
#endif //CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
private:
CHIP_ERROR ReadIfSupported(CHIP_ERROR (ConfigurationManager::*getter)(uint8_t &), AttributeValueEncoder & aEncoder);
CHIP_ERROR ReadBasicCommissioningInfo(AttributeValueEncoder & aEncoder);
Expand Down Expand Up @@ -112,6 +118,9 @@ CHIP_ERROR GeneralCommissioningGlobalInstance::Read(const ConcreteReadAttributeP
#if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED
features.Set(GeneralCommissioning::Feature::kTermsAndConditions);
#endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED
#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
features.Set(GeneralCommissioning::Feature::kNetworkRecovery);
#endif // CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
return aEncoder.Encode(features);
}

Expand Down Expand Up @@ -179,6 +188,17 @@ CHIP_ERROR GeneralCommissioningGlobalInstance::Read(const ConcreteReadAttributeP
return aEncoder.Encode(outUpdateAcceptanceDeadline.Value());
}
#endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED
#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
case RecoveryIdentifier::Id: {
uint8_t buffer[8];
chip::MutableByteSpan identifierTemp(buffer, sizeof(buffer));
RecoveryIdentifier::Get(aPath.mEndpointId, identifierTemp);
return aEncoder.Encode(identifierTemp);
}
case NetworkRecoveryReason::Id: {
return aEncoder.Encode(mNetworkRecoveryReasonValue);
}
#endif // CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
default: {
break;
}
Expand Down Expand Up @@ -660,6 +680,16 @@ class GeneralCommissioningFabricTableDelegate : public chip::FabricTable::Delega
NotifyTermsAndConditionsAttributeChangeIfRequired(initialState, updatedState);
}
#endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED
#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
// If the FabricIndex matches the last remaining entry in the Fabrics list, then the device SHALL delete all Matter
// related data on the node which was created since it was commissioned.
if (Server::GetInstance().GetFabricTable().FabricCount() == 0)
{
//here should set the NetworkRecoveryReason to Null, and regenerate the RecoveryIdentifier.
gGeneralCommissioningInstance.mNetworkRecoveryReasonValue.SetNull();
chip::app::Clusters::GeneralCommissioning::GenerateAndSetRecoveryIdentifier();
}
#endif // CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
}
};

Expand All @@ -675,6 +705,18 @@ void MatterGeneralCommissioningPluginServerInitCallback()
DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler);

Server::GetInstance().GetFabricTable().AddFabricDelegate(&fabricDelegate);
#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
//chip::DeviceLayer::PlatformMgr().LockChipStack();
gGeneralCommissioningInstance.mNetworkRecoveryReasonValue.SetNull();
// If this is the first-time initialization without any fabric, regenerate the network recovery information
if (Server::GetInstance().GetFabricTable().FabricCount() == 0)
{
if (GetRecoveryIdentifier() == 0) {
chip::app::Clusters::GeneralCommissioning::GenerateAndSetRecoveryIdentifier();
}
}
//chip::DeviceLayer::PlatformMgr().UnlockChipStack();
#endif // CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
}

void MatterGeneralCommissioningPluginServerShutdownCallback()
Expand All @@ -694,6 +736,42 @@ void SetBreadcrumb(Attributes::Breadcrumb::TypeInfo::Type breadcrumb)
{
Breadcrumb::Set(0, breadcrumb);
}
#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
void SetNetworkRecoveryReasonValue(Attributes::NetworkRecoveryReason::TypeInfo::Type& value)
{
gGeneralCommissioningInstance.mNetworkRecoveryReasonValue = value;
}
void GetNetworkRecoveryReasonValue(Attributes::NetworkRecoveryReason::TypeInfo::Type& value)
{
value = gGeneralCommissioningInstance.mNetworkRecoveryReasonValue;
}
void GenerateAndSetRecoveryIdentifier(void)
{
uint8_t recoveryIdentiferSpan[8];
// Generate a random byte array for the recovery identifier
for (uint8_t i=0; i<3; i++)
{ //avoid the identifer is zero, when is zero, try more times.
Crypto::DRBG_get_bytes(reinterpret_cast<uint8_t *>(recoveryIdentiferSpan), sizeof(recoveryIdentiferSpan));
if (!chip::Encoding::BigEndian::Get64(recoveryIdentiferSpan))
{
break;
}
}
chip::MutableByteSpan identifierTemp(recoveryIdentiferSpan,sizeof(recoveryIdentiferSpan));
RecoveryIdentifier::Set(kRootEndpointId, identifierTemp);
}
uint64_t GetRecoveryIdentifier(void)
{
uint8_t buffer[8];
chip::MutableByteSpan identifierTemp(buffer, sizeof(buffer));
RecoveryIdentifier::Get(kRootEndpointId, identifierTemp);
if (identifierTemp.size() == 0)
{
return 0;
}
return chip::Encoding::BigEndian::Get64(identifierTemp.data());
}
#endif // CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
} // namespace GeneralCommissioning
} // namespace Clusters
} // namespace app
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <platform/CHIPDeviceConfig.h>
#include <app-common/zap-generated/cluster-objects.h>

namespace chip {
Expand All @@ -23,7 +23,12 @@ namespace Clusters {
namespace GeneralCommissioning {

void SetBreadcrumb(Attributes::Breadcrumb::TypeInfo::Type breadcrumb);

#if CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
void SetNetworkRecoveryReasonValue(Attributes::NetworkRecoveryReason::TypeInfo::Type& value);
void GetNetworkRecoveryReasonValue(Attributes::NetworkRecoveryReason::TypeInfo::Type& value);
void GenerateAndSetRecoveryIdentifier(void);
uint64_t GetRecoveryIdentifier(void);
#endif // CHIP_DEVICE_CONFIG_NETWORK_RECOVERY_REQUIRED
} // namespace GeneralCommissioning
} // namespace Clusters
} // namespace app
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ limitations under the License.
<item name="Outdoor" value="0x1"/>
<item name="IndoorOutdoor" value="0x2"/>
</enum>
<enum name="NetworkRecoveryReasonEnum" type="enum8">
<cluster code="0x0030"/>
<item name="Unspecified" value="0x0"/>
<item name="Route" value="0x1"/>
<item name="ICD" value="0x2"/>
<item name="Auth" value="0x3"/>
<item name="Visibility" value="0x4"/>
</enum>
<struct name="BasicCommissioningInfo">
<cluster code="0x0030"/>
<item fieldId="0" name="FailSafeExpiryLengthSeconds" type="int16u"/>
Expand All @@ -51,6 +59,12 @@ limitations under the License.
<optionalConform/>
</otherwiseConform>
</feature>
<feature bit="1" code="NR" name="NetworkRecovery" summary="Supports Network Recovery" apiMaturity="provisional">
<otherwiseConform>
<provisionalConform/>
<optionalConform/>
</otherwiseConform>
</feature>
</features>
<attribute side="server" code="0x00" name="Breadcrumb" define="BREADCRUMB" type="int64u" writable="true" default="0x0000000000000000" optional="false">
<access op="read" privilege="view"/>
Expand Down Expand Up @@ -114,6 +128,24 @@ limitations under the License.
</mandatoryConform>
</otherwiseConform>
</attribute>
<attribute side="server" code="0x0B" name="RecoveryIdentifier" define="RECOVERY_IDENTIFIER" type="octet_string" length="8" writable="false" optional="true" apiMaturity="provisional">
<access op="read" privilege="manage"/>
<otherwiseConform>
<provisionalConform/>
<mandatoryConform>
<feature name="NR"/>
</mandatoryConform>
</otherwiseConform>
</attribute>
<attribute side="server" code="0x0C" name="NetworkRecoveryReason" define="NETWORK_RECOVERY_REASON" type="NetworkRecoveryReasonEnum" writable="false" optional="true" isNullable="true" apiMaturity="provisional">
<access op="read" privilege="manage"/>
<otherwiseConform>
<provisionalConform/>
<mandatoryConform>
<feature name="NR"/>
</mandatoryConform>
</otherwiseConform>
</attribute>
<command source="client" code="0x00" name="ArmFailSafe" response="ArmFailSafeResponse" optional="false" cli="chip fabric_commissioning armfailsafe">
<description>Arm the persistent fail-safe timer with an expiry time of now + ExpiryLengthSeconds using device clock</description>
<arg name="ExpiryLengthSeconds" type="int16u"/>
Expand Down
Loading
Loading