Skip to content

Commit

Permalink
Added unlatch state on unlock supporting Ubolt and tdded ransition lo…
Browse files Browse the repository at this point in the history
…cked->unlatched->unlocked
  • Loading branch information
lpbeliveau-silabs committed May 23, 2024
1 parent 0c4287b commit 8ae4b23
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 8 deletions.
4 changes: 4 additions & 0 deletions examples/lock-app/silabs/include/AppConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
// state to another.
#define ACTUATOR_MOVEMENT_PERIOS_MS 10

// Time the device will be left in the unlatched state before sending it back to the unlocked state.
// Left at 100 ms for testing purposes.
#define UNLATCH_TIME_MS 100

#define ON_DEMO_BITMAP \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, \
Expand Down
7 changes: 7 additions & 0 deletions examples/lock-app/silabs/include/AppTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ class AppTask : public BaseApplication
*/
static void UpdateClusterState(intptr_t context);

/**
* @brief Update Cluster State After Unlatch
*
* @param context current context
*/
static void UpdateClusterStateAfterUnlatch(intptr_t context);

/**
* @brief Handle lock update event
*
Expand Down
26 changes: 26 additions & 0 deletions examples/lock-app/silabs/include/LockManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class LockManager
{
LOCK_ACTION = 0,
UNLOCK_ACTION,
UNLATCH_ACTION,

INVALID_ACTION
} Action;
Expand All @@ -128,7 +129,9 @@ class LockManager
kState_LockInitiated = 0,
kState_LockCompleted,
kState_UnlockInitiated,
kState_UnlatchInitiated,
kState_UnlockCompleted,
kState_UnlatchCompleted,
} State;

CHIP_ERROR Init(chip::app::DataModel::Nullable<chip::app::Clusters::DoorLock::DlLockState> state,
Expand Down Expand Up @@ -191,7 +194,30 @@ class LockManager

bool ReadConfigValues();

void UnlockAfterUnlatch();

private:
struct UnlatchContext
{
chip::EndpointId mEndpointId;
Nullable<chip::FabricIndex> mFabricIdx;
Nullable<chip::NodeId> mNodeId;
Optional<chip::ByteSpan> mPin;
OperationErrorEnum mErr;

void Update(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err)
{
mEndpointId = endpointId;
mFabricIdx = fabricIdx;
mNodeId = nodeId;
mPin = pin;
mErr = err;
}
};
UnlatchContext mUnlatchContext;
chip::EndpointId mCurrentEndpointId;

friend LockManager & LockMgr();
State_t mState;

Expand Down
2 changes: 1 addition & 1 deletion examples/lock-app/silabs/openthread.gni
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ openthread_external_platform =
sl_enable_test_event_trigger = true

# ICD Default configurations
chip_enable_icd_server = true
chip_enable_icd_server = false
chip_subscription_timeout_resumption = false
sl_use_subscription_syncing = true

Expand Down
50 changes: 50 additions & 0 deletions examples/lock-app/silabs/src/AppTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,45 @@ using namespace EFR32DoorLock::LockInitParams;

namespace {
LEDWidget sLockLED;
TimerHandle_t sUnlatchTimer;

void UpdateClusterStateAfterUnlatch(intptr_t context)
{
LockMgr().UnlockAfterUnlatch();
}

void UnlatchTimerCallback(TimerHandle_t xTimer)
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(UpdateClusterStateAfterUnlatch, reinterpret_cast<intptr_t>(nullptr));
}

void CancelUnlatchTimer(void)
{
if (xTimerStop(sUnlatchTimer, pdMS_TO_TICKS(0)) == pdFAIL)
{
SILABS_LOG("sUnlatchTimer stop() failed");
appError(APP_ERROR_STOP_TIMER_FAILED);
}
}

void StartUnlatchTimer(uint32_t timeoutMs)
{
if (xTimerIsTimerActive(sUnlatchTimer))
{
SILABS_LOG("app timer already started!");
CancelUnlatchTimer();
}

// timer is not active, change its period to required value (== restart).
// FreeRTOS- Block for a maximum of 100 ms if the change period command
// cannot immediately be sent to the timer command queue.
if (xTimerStart(sUnlatchTimer, pdMS_TO_TICKS(timeoutMs)) != pdPASS)
{
SILABS_LOG("sUnlatchTimer timer start() failed");
appError(APP_ERROR_START_TIMER_FAILED);
}
}

} // namespace

using namespace chip::TLV;
Expand Down Expand Up @@ -180,6 +219,8 @@ CHIP_ERROR AppTask::Init()
sLockLED.Init(LOCK_STATE_LED);
sLockLED.Set(state.Value() == DlLockState::kUnlocked);

sUnlatchTimer = xTimerCreate("UnlatchTimer", pdMS_TO_TICKS(UNLATCH_TIME_MS), pdFALSE, (void *) 0, UnlatchTimerCallback);

// Update the LCD with the Stored value. Show QR Code if not provisioned
#ifdef DISPLAY_ENABLED
GetLCD().WriteDemoUI(state.Value() != DlLockState::kUnlocked);
Expand Down Expand Up @@ -309,6 +350,10 @@ void AppTask::ActionInitiated(LockManager::Action_t aAction, int32_t aActor)
sAppTask.GetLCD().WriteDemoUI(locked);
#endif // DISPLAY_ENABLED
}
else if (aAction == LockManager::UNLATCH_ACTION)
{
SILABS_LOG("Unlatch Action has been initiated");
}

if (aActor == AppEvent::kEventType_Button)
{
Expand All @@ -325,6 +370,11 @@ void AppTask::ActionCompleted(LockManager::Action_t aAction)
{
SILABS_LOG("Lock Action has been completed")
}
else if (aAction == LockManager::UNLATCH_ACTION)
{
SILABS_LOG("Unlatch Action has been completed")
StartUnlatchTimer(UNLATCH_TIME_MS);
}
else if (aAction == LockManager::UNLOCK_ACTION)
{
SILABS_LOG("Unlock Action has been completed")
Expand Down
59 changes: 53 additions & 6 deletions examples/lock-app/silabs/src/LockManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,22 +186,24 @@ bool LockManager::InitiateAction(int32_t aActor, Action_t aAction)
State_t new_state;

// Initiate Turn Lock/Unlock Action only when the previous one is complete.
if (mState == kState_LockCompleted && aAction == UNLOCK_ACTION)
if ((mState == kState_LockCompleted || mState == kState_UnlatchCompleted) && (aAction == UNLOCK_ACTION))
{
action_initiated = true;

new_state = kState_UnlockInitiated;
new_state = kState_UnlockInitiated;
}
else if ((mState == kState_LockCompleted || mState == kState_UnlockCompleted) && (aAction == UNLATCH_ACTION))
{
action_initiated = true;
new_state = kState_UnlatchInitiated;
}
else if (mState == kState_UnlockCompleted && aAction == LOCK_ACTION)
{
action_initiated = true;

new_state = kState_LockInitiated;
new_state = kState_LockInitiated;
}

if (action_initiated)
{

StartTimer(ACTUATOR_MOVEMENT_PERIOS_MS);

// Since the timer started successfully, update the state and trigger callback
Expand Down Expand Up @@ -249,6 +251,23 @@ void LockManager::TimerEventHandler(void * timerCbArg)
event.Handler = ActuatorMovementTimerEventHandler;
AppTask::GetAppTask().PostEvent(&event);
}
void LockManager::UnlockAfterUnlatch()
{
// write the new lock value
bool succes = false;
if (mUnlatchContext.mEndpointId != kInvalidEndpointId)
{
succes = setLockState(mUnlatchContext.mEndpointId, mUnlatchContext.mFabricIdx, mUnlatchContext.mNodeId,
DlLockState::kUnlocked, mUnlatchContext.mPin, mUnlatchContext.mErr);
}

if (!succes)
{
SILABS_LOG("Failed to update the lock state after Unlatch");
}

InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION);
}

void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent)
{
Expand All @@ -261,6 +280,11 @@ void LockManager::ActuatorMovementTimerEventHandler(AppEvent * aEvent)
lock->mState = kState_LockCompleted;
actionCompleted = LOCK_ACTION;
}
else if (lock->mState == kState_UnlatchInitiated)
{
lock->mState = kState_UnlatchCompleted;
actionCompleted = UNLATCH_ACTION;
}
else if (lock->mState == kState_UnlockInitiated)
{
lock->mState = kState_UnlockCompleted;
Expand All @@ -285,6 +309,29 @@ bool LockManager::Lock(chip::EndpointId endpointId, const Nullable<chip::FabricI
bool LockManager::Unlock(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err)
{
if (DoorLockServer::Instance().SupportsUnbolt(endpointId))
{
// TODO: Our current implementation does not support multiple endpoint. This needs to be fixed in the future.
if (endpointId != mUnlatchContext.mEndpointId)
{
// If we get a request to unlock on a different endpoint while the current endpoint is in the middle of an action,
// we return false for now. This needs to be fixed in the future.
if (mState != kState_UnlockCompleted && mState != kState_LockCompleted)
{
ChipLogError(Zcl, "Cannot unlock while unlatch on another endpoint is in progress on anotther endpoint");
return false;
}
else
{
mUnlatchContext.Update(endpointId, fabricIdx, nodeId, pin, err);
return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlatched, pin, err);
}
}
else
{
return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlatched, pin, err);
}
}
return setLockState(endpointId, fabricIdx, nodeId, DlLockState::kUnlocked, pin, err);
}

Expand Down
9 changes: 8 additions & 1 deletion examples/lock-app/silabs/src/ZclCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,14 @@ bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const
bool status = LockMgr().Unlock(endpointId, fabricIdx, nodeId, pinCode, err);
if (status == true)
{
LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION);
if (DoorLockServer::Instance().SupportsUnbolt(endpointId))
{
LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLATCH_ACTION);
}
else
{
LockMgr().InitiateAction(AppEvent::kEventType_Lock, LockManager::UNLOCK_ACTION);
}
}

return status;
Expand Down

0 comments on commit 8ae4b23

Please sign in to comment.