Skip to content

Commit

Permalink
[nrfconnect] samples: fixed the simultaneous lift/tilt move.
Browse files Browse the repository at this point in the history
Signed-off-by: Marcin Kajor <[email protected]>
  • Loading branch information
markaj-nordic committed Apr 27, 2022
1 parent a8187ee commit 2f8ff2c
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 76 deletions.
2 changes: 1 addition & 1 deletion examples/window-app/nrfconnect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ switched off LED with brightness level 0 indicates a fully opened shutter

- _Pressed for 6 s_ &mdash; Initiates the factory reset of the device.
Releasing the button within the 6-second window cancels the factory reset
procedure. **LEDs 1-4** blink in unison when the factory reset procedure is
procedure. **LED 1** and **LED 4** blink in unison when the factory reset procedure is
initiated.

- _Pressed for less than 3 s_ &mdash; Initiates the OTA software update
Expand Down
170 changes: 104 additions & 66 deletions examples/window-app/nrfconnect/main/WindowCovering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ using namespace ::chip::Credentials;
using namespace ::chip::DeviceLayer;
using namespace chip::app::Clusters::WindowCovering;

static k_timer sMoveTimer;
static k_timer sLiftTimer;
static k_timer sTiltTimer;
static constexpr uint32_t sMoveTimeoutMs{ 200 };

static constexpr uint32_t FromOneRangeToAnother(uint32_t aInMin, uint32_t aInMax, uint32_t aOutMin, uint32_t aOutMax,
Expand Down Expand Up @@ -62,67 +63,67 @@ WindowCovering::WindowCovering()
LOG_ERR("Cannot initialize the tilt indicator");
}

k_timer_init(&sMoveTimer, MoveTimerTimeoutCallback, nullptr);
k_timer_init(&sLiftTimer, MoveTimerTimeoutCallback, nullptr);
k_timer_init(&sTiltTimer, MoveTimerTimeoutCallback, nullptr);
}

void WindowCovering::MoveTimerTimeoutCallback(k_timer * aTimer)
{
if (!aTimer)
return;
chip::DeviceLayer::PlatformMgr().ScheduleWork(DriveCurrentPosition);

if (aTimer == &sLiftTimer)
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(DriveCurrentLiftPosition);
}
else if (aTimer == &sTiltTimer)
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(DriveCurrentTiltPosition);
}
}

void WindowCovering::DriveCurrentPosition(intptr_t)
void WindowCovering::DriveCurrentLiftPosition(intptr_t)
{
NPercent100ths current{};
NPercent100ths target{};
NPercent100ths positionToSet{};

if (Instance().mCurrentMoveType == MoveType::LIFT)
{
VerifyOrReturn(Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS);
VerifyOrReturn(Attributes::TargetPositionLiftPercent100ths::Get(Endpoint(), target) == EMBER_ZCL_STATUS_SUCCESS);
}
else if (Instance().mCurrentMoveType == MoveType::TILT)
{
VerifyOrReturn(Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS);
VerifyOrReturn(Attributes::TargetPositionTiltPercent100ths::Get(Endpoint(), target) == EMBER_ZCL_STATUS_SUCCESS);
}
VerifyOrReturn(Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS);
VerifyOrReturn(Attributes::TargetPositionLiftPercent100ths::Get(Endpoint(), target) == EMBER_ZCL_STATUS_SUCCESS);

Instance().UpdateOperationalStatus(Instance().mCurrentMoveType, ComputeOperationalState(target, current));
Instance().UpdateOperationalStatus(MoveType::LIFT, ComputeOperationalState(target, current));

positionToSet.SetNonNull(Instance().CalculateSingleStep());

if (Instance().mCurrentMoveType == MoveType::LIFT)
{
LiftPositionSet(Endpoint(), positionToSet);
}
else if (Instance().mCurrentMoveType == MoveType::TILT)
{
TiltPositionSet(Endpoint(), positionToSet);
}
positionToSet.SetNonNull(CalculateSingleStep(MoveType::LIFT));
LiftPositionSet(Endpoint(), positionToSet);

// assume single move completed
Instance().mInMove = false;
Instance().mInLiftMove = false;

VerifyOrReturn(Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS);

if (!Instance().TargetCompleted())
if (!Instance().TargetCompleted(current, target))
{
// continue to move
Instance().StartTimer(sMoveTimeoutMs);
StartTimer(MoveType::LIFT, sMoveTimeoutMs);
}
else
{
// the OperationalStatus should indicate no-movement after the target is completed
Instance().UpdateOperationalStatus(MoveType::NONE, ComputeOperationalState(target, current));
}
}

chip::Percent100ths WindowCovering::CalculateSingleStep()
chip::Percent100ths WindowCovering::CalculateSingleStep(MoveType aMoveType)
{
EmberAfStatus status{};
chip::Percent100ths percent100ths{};
NPercent100ths current{};

if (mCurrentMoveType == MoveType::LIFT)
if (aMoveType == MoveType::LIFT)
{
status = Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), current);
}
else if (mCurrentMoveType == MoveType::TILT)
else if (aMoveType == MoveType::TILT)
{
status = Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), current);
}
Expand All @@ -140,65 +141,97 @@ chip::Percent100ths WindowCovering::CalculateSingleStep()
return percent100ths;
}

bool WindowCovering::TargetCompleted()
bool WindowCovering::TargetCompleted(NPercent100ths aCurrent, NPercent100ths aTarget)
{
NPercent100ths current{};
NPercent100ths target{};

if (mCurrentMoveType == MoveType::LIFT)
{
VerifyOrReturnError(Attributes::CurrentPositionLiftPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS,
false);
VerifyOrReturnError(Attributes::TargetPositionLiftPercent100ths::Get(Endpoint(), target) == EMBER_ZCL_STATUS_SUCCESS,
false);
}
else if (mCurrentMoveType == MoveType::TILT)
{
VerifyOrReturnError(Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS,
false);
VerifyOrReturnError(Attributes::TargetPositionTiltPercent100ths::Get(Endpoint(), target) == EMBER_ZCL_STATUS_SUCCESS,
false);
}

if (!current.IsNull() && !target.IsNull())
if (!aCurrent.IsNull() && !aTarget.IsNull())
{
switch (mOperationalStatus.global)
{
case OperationalState::MovingDownOrClose:
return (current.Value() >= target.Value());
return (aCurrent.Value() >= aTarget.Value());
case OperationalState::MovingUpOrOpen:
return (current.Value() <= target.Value());
return (aCurrent.Value() <= aTarget.Value());
default:
return true;
}
}
else
{
LOG_ERR("Cannot read the shutter position");
LOG_ERR("Invalid target/current positions");
}
return false;
}

void WindowCovering::StartTimer(uint32_t aTimeoutMs)
void WindowCovering::StartTimer(MoveType aMoveType, uint32_t aTimeoutMs)
{
if (aMoveType == MoveType::LIFT)
{
k_timer_start(&sLiftTimer, K_MSEC(sMoveTimeoutMs), K_NO_WAIT);
}
else if (aMoveType == MoveType::TILT)
{
k_timer_start(&sTiltTimer, K_MSEC(sMoveTimeoutMs), K_NO_WAIT);
}
}

void WindowCovering::DriveCurrentTiltPosition(intptr_t)
{
k_timer_start(&sMoveTimer, K_MSEC(sMoveTimeoutMs), K_NO_WAIT);
NPercent100ths current{};
NPercent100ths target{};
NPercent100ths positionToSet{};

VerifyOrReturn(Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS);
VerifyOrReturn(Attributes::TargetPositionTiltPercent100ths::Get(Endpoint(), target) == EMBER_ZCL_STATUS_SUCCESS);

Instance().UpdateOperationalStatus(MoveType::TILT, ComputeOperationalState(target, current));

positionToSet.SetNonNull(CalculateSingleStep(MoveType::TILT));
TiltPositionSet(Endpoint(), positionToSet);

// assume single move completed
Instance().mInTiltMove = false;

VerifyOrReturn(Attributes::CurrentPositionTiltPercent100ths::Get(Endpoint(), current) == EMBER_ZCL_STATUS_SUCCESS);

if (!Instance().TargetCompleted(current, target))
{
// continue to move
StartTimer(MoveType::TILT, sMoveTimeoutMs);
}
else
{
// the OperationalStatus should indicate no-movement after the target is completed
Instance().UpdateOperationalStatus(MoveType::TILT, ComputeOperationalState(target, current));
}
}

void WindowCovering::StartMove(MoveType aMoveType)
{
// drop if already in move
if (!mInMove)
switch (aMoveType)
{
mCurrentMoveType = aMoveType;
mInMove = true;
StartTimer(sMoveTimeoutMs);
}
case MoveType::LIFT:
if (!mInLiftMove)
{
mInLiftMove = true;
StartTimer(aMoveType, sMoveTimeoutMs);
}
break;
case MoveType::TILT:
if (!mInTiltMove)
{
mInTiltMove = true;
StartTimer(aMoveType, sMoveTimeoutMs);
}
break;
default:
break;
};
}

void WindowCovering::SetSingleStepTarget(OperationalState aDirection)
{
UpdateOperationalStatus(mCurrentMoveType, aDirection);
SetTargetPosition(aDirection, CalculateSingleStep());
UpdateOperationalStatus(mCurrentUIMoveType, aDirection);
SetTargetPosition(aDirection, CalculateSingleStep(mCurrentUIMoveType));
}

void WindowCovering::UpdateOperationalStatus(MoveType aMoveType, OperationalState aDirection)
Expand All @@ -215,6 +248,11 @@ void WindowCovering::UpdateOperationalStatus(MoveType aMoveType, OperationalStat
mOperationalStatus.global = mOperationalStatus.tilt;
mOperationalStatus.lift = OperationalState::Stall;
break;
case MoveType::NONE:
mOperationalStatus.tilt = OperationalState::Stall;
mOperationalStatus.global = OperationalState::Stall;
mOperationalStatus.lift = OperationalState::Stall;
break;
default:
break;
}
Expand All @@ -224,11 +262,11 @@ void WindowCovering::UpdateOperationalStatus(MoveType aMoveType, OperationalStat
void WindowCovering::SetTargetPosition(OperationalState aDirection, chip::Percent100ths aPosition)
{
EmberAfStatus status{};
if (Instance().mCurrentMoveType == MoveType::LIFT)
if (Instance().mCurrentUIMoveType == MoveType::LIFT)
{
status = Attributes::TargetPositionLiftPercent100ths::Set(Endpoint(), aPosition);
}
else if (Instance().mCurrentMoveType == MoveType::TILT)
else if (Instance().mCurrentUIMoveType == MoveType::TILT)
{
status = Attributes::TargetPositionTiltPercent100ths::Set(Endpoint(), aPosition);
}
Expand Down
19 changes: 10 additions & 9 deletions examples/window-app/nrfconnect/main/include/WindowCovering.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ class WindowCovering

void StartMove(MoveType aMoveType);
void SetSingleStepTarget(OperationalState aDirection);
void SetMoveType(MoveType aMoveType) { mCurrentMoveType = aMoveType; }
MoveType GetMoveType() { return mCurrentMoveType; }
void SetMoveType(MoveType aMoveType) { mCurrentUIMoveType = aMoveType; }
MoveType GetMoveType() { return mCurrentUIMoveType; }
void PositionLEDUpdate(MoveType aMoveType);

static constexpr chip::EndpointId Endpoint() { return 1; };
Expand All @@ -55,20 +55,21 @@ class WindowCovering
void SetBrightness(MoveType aMoveType, uint16_t aPosition);
void SetTargetPosition(OperationalState aDirection, chip::Percent100ths aPosition);
void UpdateOperationalStatus(MoveType aMoveType, OperationalState aDirection);
void StartTimer(uint32_t aTimeoutMs);
uint8_t PositionToBrightness(uint16_t aPosition, MoveType aMoveType);
bool TargetCompleted();
chip::Percent100ths CalculateSingleStep();
bool TargetCompleted(NPercent100ths aCurrent, NPercent100ths aTarget);

static void DriveCurrentPosition(intptr_t);
static void StartTimer(MoveType aMoveType, uint32_t aTimeoutMs);
static chip::Percent100ths CalculateSingleStep(MoveType aMoveType);
static void DriveCurrentLiftPosition(intptr_t);
static void DriveCurrentTiltPosition(intptr_t);
static void MoveTimerTimeoutCallback(k_timer * aTimer);

OperationalStatus mOperationalStatus;
MoveType mCurrentMoveType;
MoveType mCurrentUIMoveType;
LEDWidget mLiftLED;
LEDWidget mTiltLED;
PWMDevice mLiftIndicator;
PWMDevice mTiltIndicator;
k_timer mMoveTimer;
bool mInMove{ false };
bool mInLiftMove{ false };
bool mInTiltMove{ false };
};

0 comments on commit 2f8ff2c

Please sign in to comment.