Skip to content
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

[SL-UP] Button handling in dimmer-switch app #363

Closed
Closed
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
52 changes: 52 additions & 0 deletions examples/light-switch-app/silabs/include/AppTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,29 @@ class AppTask : public BaseApplication

static AppTask & GetAppTask() { return sAppTask; }

struct Timer
{
typedef void (*Callback)(Timer & timer);

Timer(uint32_t timeoutInMs, Callback callback, void * context);
~Timer();

void Start();
void Stop();
void Timeout();

Callback mCallback = nullptr;
void * mContext = nullptr;
bool mIsActive = false;

osTimerId_t mHandler = nullptr;

private:
static void TimerCallback(void * timerCbArg);
};

Timer * longPressTimer = nullptr;

/**
* @brief AppTask task main loop function
*
Expand All @@ -65,6 +88,19 @@ class AppTask : public BaseApplication
static void AppTaskMain(void * pvParameter);

CHIP_ERROR StartAppTask();
/**
* @brief Event handler when a button is pressed
* Function posts an event for button processing
*
* @param buttonHandle BTN0 or BTN1
* @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED,
* SL_SIMPLE_BUTTON_RELEASED or SL_SIMPLE_BUTTON_DISABLED
*/
static void ButtonEventHandler(uint8_t button, uint8_t btnAction);

AppEvent CreateNewEvent(AppEvent::AppEventTypes type);

static void AppEventHandler(AppEvent * aEvent);

private:
static AppTask sAppTask;
Expand All @@ -75,4 +111,20 @@ class AppTask : public BaseApplication
* @return CHIP_ERROR
*/
CHIP_ERROR Init();

/**
* @brief PB1 Button event processing function
* Function triggers a switch action sent to the CHIP task
*
* @param aEvent button event being processed
*/
static void SwitchActionEventHandler(AppEvent * aEvent);

static void OnLongPressTimeout(Timer & timer);

/**
* @brief This function will be called when PB1 is
* long-pressed to trigger the level-control action
*/
void HandleLongPress();
};
68 changes: 10 additions & 58 deletions examples/light-switch-app/silabs/include/LightSwitchMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,84 +52,36 @@ class LightSwitchMgr
.stepSize = 1, .transitionTime = 0, .optionsMask = 0, .optionsOverride = 0
};

struct Timer
{
typedef void (*Callback)(Timer & timer);

Timer(uint32_t timeoutInMs, Callback callback, void * context);
~Timer();

void Start();
void Stop();
void Timeout();

Callback mCallback = nullptr;
void * mContext = nullptr;
bool mIsActive = false;

osTimerId_t mHandler = nullptr;

private:
static void TimerCallback(void * timerCbArg);
};

CHIP_ERROR Init(chip::EndpointId lightSwitchEndpoint, chip::EndpointId genericSwitchEndpoint);

void GenericSwitchOnInitialPress();
void GenericSwitchOnShortRelease();

/**
* @brief Button event processing function
* Function triggers a switch action sent to the CHIP task
*
* @param aEvent button event being processed
*/
static void SwitchActionEventHandler(AppEvent * aEvent);

void TriggerLightSwitchAction(LightSwitchAction action, bool isGroupCommand = false);
void TriggerLevelControlAction(StepModeEnum stepMode, bool isGroupCommand = false);

StepModeEnum getStepMode();

AppEvent CreateNewEvent(AppEvent::AppEventTypes type);
void changeStepMode();

static LightSwitchMgr & GetInstance() { return sSwitch; }

/**
* @brief Event handler when a button is pressed
* Function posts an event for button processing
*
* @param button BUTTON0 or BUTTON1
* @param btnAction button action - SL_SIMPLE_BUTTON_PRESSED,
* SL_SIMPLE_BUTTON_RELEASED
*/
static void ButtonEventHandler(uint8_t button, uint8_t btnAction);

static void AppEventHandler(AppEvent * aEvent);

private:
static LightSwitchMgr sSwitch;

Timer * mLongPressTimer = nullptr;
bool mFunctionButtonPressed = false; // True when button0 is pressed, used to trigger factory reset
bool mActionButtonPressed = false; // True when button1 is pressed, used to initiate toggle or level-up/down
bool mActionButtonSuppressed = false; // True when both button0 and button1 are pressed, used to switch step direction
bool mResetWarning = false;

// Default Step direction for Level control
StepModeEnum stepDirection = StepModeEnum::kUp;

static void OnLongPressTimeout(Timer & timer);
LightSwitchMgr() = default;

/**
* @brief This function will be called when PB0 is
* long-pressed to trigger the factory-reset
*/
void HandleLongPress();
LightSwitchMgr() = default;

static void GenericSwitchWorkerFunction(intptr_t context);

chip::EndpointId mLightSwitchEndpoint = chip::kInvalidEndpointId;
chip::EndpointId mGenericSwitchEndpoint = chip::kInvalidEndpointId;

/**
* @brief Button event processing function
* Function triggers a switch action sent to the CHIP task
*
* @param aEvent button event being processed
*/
static void SwitchActionEventHandler(AppEvent * aEvent);
};
201 changes: 200 additions & 1 deletion examples/light-switch-app/silabs/src/AppTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,15 @@ using namespace ::chip::DeviceLayer;
*********************************************************/

AppTask AppTask::sAppTask;
bool sFunctionButtonPressed = false; // True when button0 is pressed, used to trigger factory reset
bool sActionButtonPressed = false; // True when button1 is pressed, used to initiate toggle or level-up/down
bool sActionButtonSuppressed = false; // True when both button0 and button1 are pressed, used to switch step direction
bool sIsButtonEventTriggered = false; // True when button0 press event is posted to BaseApplication

CHIP_ERROR AppTask::Init()
{
CHIP_ERROR err = CHIP_NO_ERROR;
chip::DeviceLayer::Silabs::GetPlatform().SetButtonsCb(LightSwitchMgr::ButtonEventHandler);
chip::DeviceLayer::Silabs::GetPlatform().SetButtonsCb(AppTask::ButtonEventHandler);

#ifdef DISPLAY_ENABLED
GetLCD().Init((uint8_t *) "Light Switch");
Expand All @@ -91,9 +95,98 @@ CHIP_ERROR AppTask::Init()
appError(err);
}

longPressTimer = new Timer(LONG_PRESS_TIMEOUT, OnLongPressTimeout, this);

return err;
}

void AppTask::Timer::Start()
{
// Starts or restarts the function timer
if (osTimerStart(mHandler, pdMS_TO_TICKS(LONG_PRESS_TIMEOUT)) != osOK)
{
SILABS_LOG("Timer start() failed");
appError(CHIP_ERROR_INTERNAL);
}

mIsActive = true;
}

void AppTask::Timer::Timeout()
{
mIsActive = false;
if (mCallback)
{
mCallback(*this);
}
}

void AppTask::HandleLongPress()
{
AppEvent event;
event.Handler = AppTask::AppEventHandler;

if (sActionButtonPressed)
{
sActionButtonSuppressed = true;
// Long press button up : Trigger Level Control Action
event.Type = AppEvent::kEventType_TriggerLevelControlAction;
AppTask::GetAppTask().PostEvent(&event);
}
}

void AppTask::OnLongPressTimeout(AppTask::Timer & timer)
{
AppTask * app = static_cast<AppTask *>(timer.mContext);
if (app)
{
app->HandleLongPress();
}
}

AppTask::Timer::Timer(uint32_t timeoutInMs, Callback callback, void * context) : mCallback(callback), mContext(context)
{
mHandler = osTimerNew(TimerCallback, // timer callback handler
osTimerOnce, // no timer reload (one-shot timer)
this, // pass the app task obj context
NULL // No osTimerAttr_t to provide.
);

if (mHandler == NULL)
{
SILABS_LOG("Timer create failed");
appError(CHIP_ERROR_INTERNAL);
}
}

AppTask::Timer::~Timer()
{
if (mHandler)
{
osTimerDelete(mHandler);
mHandler = nullptr;
}
}

void AppTask::Timer::Stop()
{
mIsActive = false;
if (osTimerStop(mHandler) == osError)
{
SILABS_LOG("Timer stop() failed");
appError(CHIP_ERROR_INTERNAL);
}
}

void AppTask::Timer::TimerCallback(void * timerCbArg)
{
Timer * timer = reinterpret_cast<Timer *>(timerCbArg);
if (timer)
{
timer->Timeout();
}
}

CHIP_ERROR AppTask::StartAppTask()
{
return BaseApplication::StartAppTask(AppTaskMain);
Expand Down Expand Up @@ -126,3 +219,109 @@ void AppTask::AppTaskMain(void * pvParameter)
}
}
}

AppEvent AppTask::CreateNewEvent(AppEvent::AppEventTypes type)
{
AppEvent aEvent;
aEvent.Type = type;
aEvent.Handler = AppTask::AppEventHandler;
return aEvent;
}

void AppTask::ButtonEventHandler(uint8_t button, uint8_t btnAction)
{
AppEvent event = {};
if (btnAction == to_underlying(SilabsPlatform::ButtonAction::ButtonPressed))
{
event = AppTask::GetAppTask().CreateNewEvent(button ? AppEvent::kEventType_ActionButtonPressed
: AppEvent::kEventType_FunctionButtonPressed);
}
else
{
event = AppTask::GetAppTask().CreateNewEvent(button ? AppEvent::kEventType_ActionButtonReleased
: AppEvent::kEventType_FunctionButtonReleased);
}
AppTask::GetAppTask().PostEvent(&event);
}

void AppTask::AppEventHandler(AppEvent * aEvent)
{
switch (aEvent->Type)
{
case AppEvent::kEventType_FunctionButtonPressed:
sFunctionButtonPressed = true;
if (sActionButtonPressed)
{
sActionButtonSuppressed = true;
LightSwitchMgr::GetInstance().changeStepMode();
ChipLogProgress(AppServer, "Step direction changed. Current Step Direction : %s",
((LightSwitchMgr::GetInstance().getStepMode() == StepModeEnum::kUp) ? "kUp" : "kDown"));
}
else
{
sIsButtonEventTriggered = true;
// Post button press event to BaseApplication
AppEvent button_event = {};
button_event.Type = AppEvent::kEventType_Button;
button_event.ButtonEvent.Action = static_cast<uint8_t>(SilabsPlatform::ButtonAction::ButtonPressed);
button_event.Handler = BaseApplication::ButtonHandler;
AppTask::GetAppTask().PostEvent(&button_event);
}
break;
case AppEvent::kEventType_FunctionButtonReleased: {
sFunctionButtonPressed = false;
if (sIsButtonEventTriggered)
{
sIsButtonEventTriggered = false;
// Post button release event to BaseApplication
AppEvent button_event = {};
button_event.Type = AppEvent::kEventType_Button;
button_event.ButtonEvent.Action = static_cast<uint8_t>(SilabsPlatform::ButtonAction::ButtonReleased);
button_event.Handler = BaseApplication::ButtonHandler;
AppTask::GetAppTask().PostEvent(&button_event);
}
break;
}
case AppEvent::kEventType_ActionButtonPressed:
sActionButtonPressed = true;
aEvent->Handler = LightSwitchMgr::SwitchActionEventHandler;
AppTask::GetAppTask().PostEvent(aEvent);
if (sFunctionButtonPressed)
{
sActionButtonSuppressed = true;
LightSwitchMgr::GetInstance().changeStepMode();
ChipLogProgress(AppServer, "Step direction changed. Current Step Direction : %s",
((LightSwitchMgr::GetInstance().getStepMode() == StepModeEnum::kUp) ? "kUp" : "kDown"));
}
else if (sAppTask.longPressTimer)
{
sAppTask.longPressTimer->Start();
}
break;
case AppEvent::kEventType_ActionButtonReleased:
sActionButtonPressed = false;
if (sAppTask.longPressTimer)
{
sAppTask.longPressTimer->Stop();
}
if (sActionButtonSuppressed)
{
sActionButtonSuppressed = false;
}
else
{
aEvent->Type = AppEvent::kEventType_TriggerToggle;
aEvent->Handler = LightSwitchMgr::SwitchActionEventHandler;
AppTask::GetAppTask().PostEvent(aEvent);
}
aEvent->Type = AppEvent::kEventType_ActionButtonReleased;
aEvent->Handler = LightSwitchMgr::SwitchActionEventHandler;
AppTask::GetAppTask().PostEvent(aEvent);
break;
case AppEvent::kEventType_TriggerLevelControlAction:
aEvent->Handler = LightSwitchMgr::SwitchActionEventHandler;
AppTask::GetAppTask().PostEvent(aEvent);
default:
break;
}
}
Loading
Loading