Thursday, April 13, 2023

How to add one extra endpoint for Matter/OpenThread Light.

The following steps show you how to add one extra endpoint for Matter/OpenThread Light with Silicon Labs EFR32MG24 BRD4187C radio board. 

1. Create MatterLightOverThread with Silicon Labs EFR32MG24 BRD4187C radio board first.

2. Start ZAP tool (Zigbee Cluster Configurator) and copy endpoint 1 to create endpoint 2.

3. Enable all required client->server commands for endpoint 2 of on/off and level cluster in ZAP tool.

 

4. Make a copy of LightingManager.h to LightingManager_ep2.h and LightingManager.cpp to LightingManager_ep2.cpp.

5. Add "#include "LightingManager_ep2.h"" and the following items for endpoint 2 in AppTask.h.

    static void ActionInitiated_ep2(LightingManager_ep2::Action_t aAction, int32_t aActor);
    static void ActionCompleted_ep2(LightingManager_ep2::Action_t aAction);
    static void UpdateClusterState_ep2(intptr_t context);

6. Add led2 for endpoint by clicking "Adding New Instances" in Simple LED SOFTWARE COMPONENTS and configure led2 to use PA05 which is pin 7 on EXP header of BRD4001A/BRD4002A mainboard.

7. Add "#define LIGHT_LED_EP2 &sl_led_led2" and "LEDWidget sLightLED_ep2;" for led light of endpoint 2 in AppTask.c.

8. Add implementation of ActionInitiated_ep2, ActionCompleted_ep2, and UpdateClusterState_ep2 in AppTask.c.

void AppTask::ActionInitiated_ep2(LightingManager_ep2::Action_t aAction, int32_t aActor)
{
    // Action initiated, update the light led
    bool lightOn = aAction == LightingManager_ep2::ON_ACTION;
    EFR32_LOG("Turning _ep2 light %s", (lightOn) ? "On" : "Off")

#ifdef ENABLE_WSTK_LEDS
    sLightLED_ep2.Set(lightOn);
#endif // ENABLE_WSTK_LEDS

#ifdef DISPLAY_ENABLED
    sAppTask.GetLCD().WriteDemoUI(lightOn);
#endif
#ifdef SL_CATALOG_SIMPLE_BUTTON_PRESENT
    if (aActor == AppEvent::kEventType_Button)
    {
        sAppTask.mSyncClusterToButtonAction = true;
    }
#endif
}

void AppTask::ActionCompleted_ep2(LightingManager_ep2::Action_t aAction)
{
    // action has been completed bon the light
    if (aAction == LightingManager_ep2::ON_ACTION)
    {
        EFR32_LOG("EP2 Light ON")
    }
    else if (aAction == LightingManager_ep2::OFF_ACTION)
    {
        EFR32_LOG("EP2 Light OFF")
    }
#ifdef SL_CATALOG_SIMPLE_BUTTON_PRESENT
    if (sAppTask.mSyncClusterToButtonAction)
    {
        chip::DeviceLayer::PlatformMgr().ScheduleWork(UpdateClusterState_ep2, reinterpret_cast<intptr_t>(nullptr));
        sAppTask.mSyncClusterToButtonAction = false;
    }
#endif
}

void AppTask::UpdateClusterState_ep2(intptr_t context)
{
    uint8_t newValue = LightMgr_ep2().IsLightOn();

    // write the new on/off value
    EmberAfStatus status = OnOffServer::Instance().setOnOffValue(2, newValue, false);

    if (status != EMBER_ZCL_STATUS_SUCCESS)
    {
        EFR32_LOG("ERR: updating ep2 on/off %x", status);
    }

9. Add kEventType_Light_ep2 enum behind kEventType_Light in AppEvent.h and use the following codes to replace original codes in LightActionEventHandler to support endpoint 2 light action.

    void AppTask::LightActionEventHandler(AppEvent * aEvent)
{
    bool initiated = false;
    LightingManager::Action_t action;
    LightingManager_ep2::Action_t action_ep2;
    int32_t actor;
    int32_t actor_ep2;
    CHIP_ERROR err = CHIP_NO_ERROR;

    if (aEvent->Type == AppEvent::kEventType_Light)
    {
        action = static_cast<LightingManager::Action_t>(aEvent->LightEvent.Action);
        actor  = aEvent->LightEvent.Actor;
    }
    else if (aEvent->Type == AppEvent::kEventType_Light_ep2)
      {
          action_ep2 = static_cast<LightingManager_ep2::Action_t>(aEvent->LightEvent.Action);
          actor_ep2  = aEvent->LightEvent.Actor;
      }
#ifdef SL_CATALOG_SIMPLE_BUTTON_PRESENT
    else if (aEvent->Type == AppEvent::kEventType_Button)
    {
        action = (LightMgr().IsLightOn()) ? LightingManager::OFF_ACTION : LightingManager::ON_ACTION;
        actor  = AppEvent::kEventType_Button;
    }
#endif
    else
    {
        err = APP_ERROR_UNHANDLED_EVENT;
    }

    if (err == CHIP_NO_ERROR)
    {
        if (aEvent->Type == AppEvent::kEventType_Light){
          initiated = LightMgr().InitiateAction(actor, action);

          if (!initiated)
          {
              EFR32_LOG("Action is already in progress or active.");
          }
        }
        else if (aEvent->Type == AppEvent::kEventType_Light_ep2)
        {
          initiated = LightMgr_ep2().InitiateAction(actor_ep2, action_ep2);

          if (!initiated)
          {
              EFR32_LOG("_ep2 Action is already in progress or active.");
          }
        }
    }
}

10. Add "#include "LightingManager_ep2.h"" ZclCallbacks.cpp and use the following codes in MatterPostAttributeChangeCallback function.

void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size,
                                       uint8_t * value)
{
    ClusterId clusterId     = attributePath.mClusterId;
    AttributeId attributeId = attributePath.mAttributeId;
    EndpointId endpoint = attributePath.mEndpointId;
    ChipLogProgress(Zcl, "Cluster callback: " ChipLogFormatMEI, ChipLogValueMEI(clusterId));

    if (clusterId == OnOff::Id && attributeId == OnOff::Attributes::OnOff::Id)
    {
        //ChipLogProgress(Zcl, "<---YK---> MatterPostAttributeChangeCallback Endpoint %d on/off value=%d", endpoint, value);
        if(endpoint==1)
          LightMgr().InitiateAction(AppEvent::kEventType_Light, *value ? LightingManager::ON_ACTION : LightingManager::OFF_ACTION);
        else if(endpoint==2)
          LightMgr_ep2().InitiateAction(AppEvent::kEventType_Light_ep2, *value ? LightingManager_ep2::ON_ACTION : LightingManager_ep2::OFF_ACTION);
    }
    else if (clusterId == LevelControl::Id)
    {
        ChipLogProgress(Zcl, "Level Control attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u",
                        ChipLogValueMEI(attributeId), type, *value, size);

        // WIP Apply attribute change to Light
    }
    else if (clusterId == ColorControl::Id)
    {
        ChipLogProgress(Zcl, "Color Control attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u",
                        ChipLogValueMEI(attributeId), type, *value, size);

        // WIP Apply attribute change to Light
    }
    else if (clusterId == OnOffSwitchConfiguration::Id)
    {
        ChipLogProgress(Zcl, "OnOff Switch Configuration attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u",
                        ChipLogValueMEI(attributeId), type, *value, size);

        // WIP Apply attribute change to Light
    }
    else if (clusterId == Identify::Id)
    {
        ChipLogProgress(Zcl, "Identify attribute ID: " ChipLogFormatMEI " Type: %u Value: %u, length %u",
                        ChipLogValueMEI(attributeId), type, *value, size);
    }
}

11. Build and download MatterLightOverThread into BRD4187C

12. Join the MatterLightOverThread device into Matter/Thread network of Apple Home mini with Hoe App and you can see two endpoints in Home App to control them separately.


 

 p.s. for your references, on/off command received flow in Matter source code is like the followings:

InteractionModelEngine::OnMessageReceived
ProcessInvokeRequest
ProcessCommandDataIB
DispatchCommand
DispatchSingleClusterCommand
DispatchServerCommand
emberAfOnOffClusterOffCallback
OnOffServer::Instance().offCommand(commandPath);
OnOffServer::setOnOffValue
Attributes::OnOff::Set
emberAfWriteServerAttribute [#define emberAfWriteServerAttribute emberAfWriteAttribute (af.h)]
emberAfWriteAttribute
emAfWriteAttribute
MatterPostAttributeChangeCallback (ZclCallbacks.cpp)

No comments:

Post a Comment