Showing posts with label OpenThread. Show all posts
Showing posts with label OpenThread. Show all posts

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)

Friday, December 23, 2022

Matter/OpenThread Border Router/Gateway Reality in the field by end of 2022

It's almost in the end of 2022 and Matter/OpenThread definitely would be noted as one of biggest event in IOT. The design philosophy of Matter is the convergence of IOT so end user won't have panic selecting and using IOT appliances on the markets. Until now, there are lots of company announcing Matter Border Router/Gateway support. I try to figure out or test the reality of such event and hope can help others further understanding this topic.

1. Apple: support Matter to its devices with iOS 16.1, iPadOS 16.1, macOS Ventura, watchOS 9.1, tvOS 16.1, and HomePod 16.1 software updates on Nov. 2022.I try to build Matter/OpenThread light-app, Thermostat, and light-switch-app applications on Silicon Labs EFR32 SoC and all of them work fluently. Incredible! Don't have Matter/Wifi device to test if it work but I would guess it works since Matter/OpenThread works in my tests.

2. Google: release firmware update including the original Google Home speaker, Google Home Mini, Nest Mini, Nest Audio, Nest Hub (1st and 2nd gen), Nest Hub Max, and the new Nest Wifi Pro to support Matter on Dec. 2022. I also use Matter/OpenThread light-app, Thermostat, and light-switch-app applications on Silicon Labs EFR32 SoC to test this. However, it always stops at the end of commissioning process and I believe this is bug and compatibility issue from Google. Since Matter/OpenThread devuce cannot work, I won't expect Matter/Wifi device would work but I welcome someone provides information to prove it works.

Add on Jan. 30th 2023, you can add Matter/OpenThread light-app to Goggle Nest Hub Max by using Android 13 camera to scan QR-code on device.

3. Amazon:  rolling out Matter/Wifi support for 17 Echo devices on Dec. 2022 too and an Android phone is required for Matter setup. Amazon also says that Matter/OpenThread and iOS support will arrive sometime in 2023. Since I don't have Matter/Wifi device, I cannot prove if it works. I also welcome someone provides information to prove it works.

4. Philips: announce they will release new firmware for Hue Bridge in the first quarter of 2023.

5. Samsung: rolling out an OTA update for SmartThings v2 and v3 hubs to add Matter compatibility (via Samsung). The v2 hub will let you control Matter-compatible smart home devices over Wi-Fi and Ethernet, with Thread border router support limited to the v3 hub and SmartThings dongle. I don't have SmartThings hubs to test this and I don't see anyone reports it works by surfing/Google on Internet. I also welcome someone provides information to prove it works.

6.Aqara: users will first gain access to Matter in December 2022 via the Aqara Hub M2 through an OTA (over-the-air) update, which will allow existing Aqara Zigbee devices to become compatible with Matter. I don't have Aqara Hub M2 to test this and also don't see anyone reports this works by surfing/Google on Internet. I also welcome someone provides information to prove it works.

It seems lots of company announcing Matter support but it not actually happens in the field. Maybe it's still too early to judge anything on Matter protocol now. If you are interested in Matter device, I would suggest you to watch and monitor it for now. I would guess 2023 Q2 might be a good timing to check the Matter reality again.

By the way, you won't see much news or press from Apple Matter support but actually they integrate HomeKit with Matter/Thread seamlessly. I would give them a thumb up for solid technology to support this. If you have money, go to buy Apple stock before you buy Apple products.

Friday, October 28, 2022

Build and run Matter and OpenThread lighting-app on EFR32MG24 to work with Apple HomePod Mini and iPhone Home App.

The following steps show you how to build and run Matter and OpenThread lighting-app on EFR32MG24 to work with Apple HomePod Mini and iPhone Home App.

1. Refer to steps 1 and 2 in How to setup and build Matter/CHIP EFR32 Lighting Example on Ubuntu 20.04 to setup Ubuntu 20.04 build environment and git master Matter source codes.

2. Switch to v1.0-branch and workable commit.

    cd connectedhomeip/
    git fetch origin
    git branch -a
    git checkout -b v1.0 remotes/origin/v1.0-branch
    git submodule update --init --recursive
    git checkout 561d23d0db215a99705ff0696e73853c8edf11b2
 

3. Build lighting-app for BRD4186C

    ./scripts/examples/gn_efr32_example.sh ./examples/lighting-app/efr32/ ./out/lighting-app BRD4186C

4. Download chip-efr32-lighting-example.hex (can be found under out/lighting-app/BRD4186C) in to BRD4186C+BRD4001A.

5. Make sure your Apple HomePod Mini and iPhone are update to OS 16.1.

6. Start Home App on your iPhone and scan the QR code on LCD of BRD4186C+BRD4001A. Following Home App wizard to complete settings.


7. You can have the Matter enable Light connecting with HomePod Mini and control it with iPhone Home App.

P.S. You can add the following code into AppTask::Init() in AppTask.c to print URL on RTT viewer output to generate QR code on browser.

PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE));

P.S. You can use the following command to build lock-app SED for low power device test.

./scripts/examples/gn_efr32_example.sh ./examples/lock-app/efr32/ ./out/lock-app_SED BRD4186C --sed "chip_detail_logging=false chip_automation_logging=false chip_progress_logging=false is_debug=false show_qr_code=true chip_build_libshell=false disable_lcd=false enable_openthread_cli=false"

Friday, February 11, 2022

Build and run OpenThread Border Router for NXP i.MX6ULL with Silicon Labs EFR32 as RCP

This tutorial shows you how to build and run OpenThread Border Router for NXP i.MX6ULL with Silicon Labs EFR32 as RCP.

1. Setup build environment and build OpenThread Border Router for NXP i.MX6ULL EVK.

 sudo apt-get install -y gcc make perl curl nodejs npm
  • 1.3 Setup Yocto build environment.
mkdir ~/bin
curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
export PATH=${PATH}:~/bin

sudo update-ca-certificates
mkdir yocto-otbr
cd yocto-otbr
git config --global user.email "youremail@email.com"
git config --global user.name "yourname"
repo init -u https://source.codeaurora.org/external/imx/imx-manifest -b imx-linux-hardknott -m imx-5.10.35-2.0.0.xml
repo sync
  • 1.4 Integrate the meta-matter recipe into the Yocto code base
cd yocto-otbr/sources
git clone https://github.com/NXPmicro/meta-matter.git
  • 1.5 Install necessary package to build the Yocto Project.
sudo apt-get install -y gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev pylint3 xterm
  • 1.6 Download 0001-Add-TUN-device-support-for-arm.patch into yocto-otbr/sources/meta-imx folder and run the following git command to patch it (This will build /dev/net/tun on i.MX6ULL system for otbr-agent).
git am -3 0001-Add-TUN-device-support-for-arm.patch
  • 1.7 Generate the Yocto image with OpenThread Border Router support.
MACHINE=imx6ullevk DISTRO=fsl-imx-xwayland source sources/meta-matter/tools/imx-iot-setup.sh bld-xwayland
bitbake imx-image-multimedia
  • 1.8 After previous two commands are excuted successfully, the Yocto image imx-image-multimedia-imx6ullevk.wic.bz2 will be generated under yocto-otbr/bld-xwayland/tmp/deploy/images/imx6ullevk/. imx-image-multimedia-imx6ullevk.wic.bz2 should be unzip to imx-image-multimedia-imx6ullevk.wic.
 
  • 1.9 Use "ls /dev/sd*" to check the sd card name. In my , it's "sdb" Use dd command to program imx-image-multimedia-imx6ullevk.wic.bz2 to a microSD.
 sudo dd if=imx-image-multimedia-imx6ullevk.wic of=/dev/sdb bs=4096 conv=fsync status=progress
  •  1.10 After previous step is done, the microSD card can be used to boot i.MX6ULL EVK with OpenThread Border Router capability.
 

2. Download OT-RCP into Silicon Labs BRD4161A (EFR32 radio board) using Simplicity Studio v5.

  • 2.1 Instead of building OT-RCP for Silicon Labs BRD4161A by myself, I install Simplicity Studio v5 and Gecko SDK Suite v3.2.3 with OpenThread 1.2.3.0
 


  • 2.2 Download prebuilt OT-RCP into Silicon Labs BRD4161A directly.


3. Connect NXP i.MX6ULL EVK with Silicon Labs EFR32 as RCP to test OpenThread Border Router.

  • 3.1 Connect Silicon Labs BRD4161A+BRD4001A to i.MX6ULL EVK with USB cable and put the the microSD (built in step 1) to power on i.MX6ULL EVK.
 

  • 3.2 Use root to login i.MX6ULL EVK debug console and run the following command to start otbr-agent
 otbr-agent -I wpan0 -B eth0 spinel+hdlc+uart:///dev/ttyACM0 &
 
P.S.Since we use Silicon Labs BRD4161A+BRD4001A, it should emulate ttyACM0 after connecting to i.MX6ULL EVK with USB cable. If you use othe OT-RCP hardware, you might need to check physical device name that is emulated by your hardware.
  • 3.3 Run ot-ctl, which is a command line utility provided with OTBR. It is used to communicate with the Thread PAN interface (default is wpan0) that otbr-agent is bound to in the RCP design, to start test everything.
 P.S. If you see "connection session failed: No such file or directory" problem when running ot-ctl, you can use "cat /var/log/syslog" to check what's wrong with otbr-agent.
 

  • 3.4 You can refer to step 5, 6, and 7 in this post to test OTBR and an OT-CLI-FTD node.

 

P.S. Thanks Elven Wang @NXP and Jonathan Hui @Google for their great help to make all of these work together. The followings are reference links of previous works that help me lots.

https://github.com/openthread/ot-br-posix/issues/1200#

https://github.com/NXPmicro/meta-matter

Tuesday, December 7, 2021

Prototype OpenThread/HomeKit SwitchBot using Silicon Labs EFR32

Apple releases HomePod Mini to support Thread protocol. The following steps show you how easily to prototype an OpenThread/HomeKit SwitchBot using Silicon Labs EFR32:

1. Connect SLWSTK6000B to USB port of Desktop to start Simplicity Studio V5 and create "HomeKit Lightbulb DMP" in Launcher Tab


2. Configure "HomeKit Lightbulb DMP" as Sleepy MTD for power saving.

2.1 Revise FTD to MTD


 

2.2 Set "Enable reception when sleeping" to Disable in "UARTDRV Core"

 


2.3 Revise the following configurations in app.h.

#define kAccessorySleepInterval ((HAPTime) 5*HAPSecond)

#define kThreadDeviceType (kHAPPlatformThreadDeviceCapabilities_MTD)

2.4 Revise the following configurations in App_Base.h.

#define THREAD_CHILD_TIMEOUT_IN_SECONDS 9

 

3. Add the following codes in app.c

#include "em_device.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_chip.h"
#include "em_gpio.h"
#include "em_timer.h"
//#include "bsp.h"

// Note: change this to set the desired output frequency in Hz
#define PWM_FREQ 50

// Note: change this to set the desired duty cycle (used to update CCVB value)
static volatile int dutyCyclePercent = 10;

// stores 1 msTicks from the SysTick timer
volatile uint32_t msTicks;

/**************************************************************************//**
 * @brief
 *    Interrupt handler for TIMER0 that changes the duty cycle
 *
 * @note
 *    This handler doesn't actually dynamically change the duty cycle. Instead,
 *    it acts as a template for doing so. Simply change the dutyCyclePercent
 *    global variable here to dynamically change the duty cycle.
 *****************************************************************************/
void TIMER0_IRQHandler(void)
{
  // Acknowledge the interrupt
  uint32_t flags = TIMER_IntGet(TIMER0);
  TIMER_IntClear(TIMER0, flags);

  // Update CCVB to alter duty cycle starting next period
  TIMER_CompareBufSet(TIMER0, 0, (TIMER_TopGet(TIMER0) * dutyCyclePercent) / 100);
}

/**************************************************************************//**
 * @brief GPIO initialization
 *****************************************************************************/
void initGpio(void)
{
  // Enable GPIO and clock
  CMU_ClockEnable(cmuClock_GPIO, true);

  // Configure PC10 (Expansion Header Pin 16) as output
  GPIO_PinModeSet(gpioPortC, 10, gpioModePushPull, 0);
}

/**************************************************************************//**
 * @brief
 *    TIMER initialization
 *****************************************************************************/
void initTimer(void)
{
  // Enable clock for TIMER0 module
  CMU_ClockEnable(cmuClock_TIMER0, true);

  // Configure TIMER0 Compare/Capture for output compare
  // Use PWM mode, which sets output on overflow and clears on compare events
  TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT;
  timerCCInit.mode = timerCCModePWM;
  TIMER_InitCC(TIMER0, 0, &timerCCInit);

  // Route TIMER0 CC0 to location 15 and enable CC0 route pin
  // TIM0_CC0 #15 is GPIO Pin PC10
  TIMER0->ROUTELOC0 |=  TIMER_ROUTELOC0_CC0LOC_LOC15;
  TIMER0->ROUTEPEN |= TIMER_ROUTEPEN_CC0PEN;

  // Set top value to overflow at the desired PWM_FREQ frequency
  TIMER_TopSet(TIMER0, CMU_ClockFreqGet(cmuClock_TIMER0) / (8 * PWM_FREQ));

  // Set compare value for initial duty cycle
  TIMER_CompareSet(TIMER0, 0, (TIMER_TopGet(TIMER0) * dutyCyclePercent) / 100);

  // Initialize the timer
  TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT;
  timerInit.prescale = timerPrescale8;
  TIMER_Init(TIMER0, &timerInit);

  // Enable TIMER0 compare event interrupts to update the duty cycle
  TIMER_IntEnable(TIMER0, TIMER_IEN_CC0);
  NVIC_EnableIRQ(TIMER0_IRQn);
}

void sg90_on(void){
  dutyCyclePercent=25;
  initTimer();
  sl_udelay_wait(110000);
  dutyCyclePercent=10;
  initTimer();
  sl_udelay_wait(110000);
  dutyCyclePercent=25;
  initTimer();
  sl_udelay_wait(110000);
  CMU_ClockEnable(cmuClock_TIMER0, false);
  GPIO_PinModeSet(gpioPortC, 10, gpioModePushPull, 0);
}
void sg90_off(void){
  dutyCyclePercent=25;
  initTimer();
  sl_udelay_wait(130000);
  dutyCyclePercent=45;
  initTimer();
  sl_udelay_wait(130000);
  dutyCyclePercent=25;
  initTimer();
  sl_udelay_wait(130000);
  CMU_ClockEnable(cmuClock_TIMER0, false);
  GPIO_PinModeSet(gpioPortC, 10, gpioModePushPull, 0);
}

4. Add the following code to AppInitialize(), TurnOnLightBulb(), and TurnOffLightBulb() in app.c

4.1 Add initGpio() in AppInitialize().

4.2 Add sg90_on() in  TurnOnLightBulb().

4.3 Add sg90_off() in  TurnOffLightBulb().

5. Rename HomeKit device name to "OT HomeKit SwitchBot" in HomeKit Configurator


6. Connect SG90 servo motor to EXP Header (pin15-PC10) of SLWSTK6000B and build/download application into SLWSTK6000.

 


7. You need to have a HomePod mini, which supports OpenThread, and provision "OT HomeKit SwitchBot" with iPhone Home App.


 

8. Test it with Siri to trigger "OT HomeKit SwitchBot" to turn off/on my office light, which you can refer to the following video.


9. In Energy Profiler, you can see low average power consumption on "OT HomeKit SwitchBot" which is configured as Sleepy MTD and perfectly suitable for battery power.



Thursday, July 1, 2021

How to build OpenThread Sleepy MTD CoAP temperature sensor using Simplicity Studio v5 and BRD4161A

The following steps show you how to build an OpenThread Sleepy MTD CoAP temperature sensor using Simplicity Studio v5 and BRD4161A

1. Start Simplicity Studio v5 and connect EFR32MG12 BRD4161A. Form Launcher tab, select sleepy-demo-mtd (I use OpenThread SDK 1.2.0.0 when I test this) to create the project.

 


2.  We will add temperature reading (from Si7021) related codes in this step.

2.1 In Simplicity IDE tab, open sleepy-demo-mtd.slcp to make sure you enable "Relative Humidity and Temperature sensor" and set proper I2C pins.



2.2 You also need to check sl_board_control_config.h and make sure SL_BOARD_ENABLE_SENSOR_RHT is defined as "1" and SL_BOARD_ENABLE_SENSOR_RHT_PORT/SL_BOARD_ENABLE_SENSOR_RHT_PIN are defined to correct port/pin which is PB10 on BRD4161A. 

2.3 Set "sl_i2cspm_t *i2cspm_sensor = sl_i2cspm_inst0;" in sl_sensor_select.c.

2.4 Create periodic event to read temperature from humidity/temperature sensor Si7021.

2.4.1 Add the following header files for humidity/temperature reading and timer event.

#include "sl_i2cspm_instances.h"
#include "sl_si70xx.h"
#include "sl_sleeptimer.h"
#include <stdio.h>
#include <string.h>


2.4.2 Add the following lines in app_init in app.c to initialize Si7021 humidity/temperature sensor.

    otCliOutputFormat("app_init: sl_si70xx_init\r\n");
    sl_si70xx_init(sl_i2cspm_inst0, SI7021_ADDR);
 


 2.4.3 Add timer related defines/variables/callback function and add timer start in app_init in app.c.

#define MEASUREMENT_INTERVAL_MS      5000
static sl_sleeptimer_timer_handle_t measurement_timer;



static void measurement_callback(sl_sleeptimer_timer_handle_t *handle, void *data)
{
  otCliOutputFormat("measurement_callback\r\n");
  sl_si70xx_measure_rh_and_temp(sl_i2cspm_inst0, SI7021_ADDR, &rh_data, &temp_data);
  memset(str_buf,0,64);
  sprintf(str_buf,"rh=%f, temperature=%f\r\n",((float)rh_data/1000.0),((float)temp_data/1000.0));
  otCliOutputFormat(str_buf);
}



void app_init(void)
{
... 
otCliOutputFormat("app_init: Setup periodic measurement timer \r\n");
sl_sleeptimer_start_periodic_timer_ms(&measurement_timer, MEASUREMENT_INTERVAL_MS, measurement_callback, NULL, 0, 0);
...
}


3.  Change related network settings in setNetworkConfiguration function (in sleepy-mtd.c) so the device can join matched OpenThread Border Router network (refer to here to setup OpenThread Border Router).



4. Implement CoAP related codes.

4.1 Add the following header files in app.c

#include <openthread/coap.h>
#include "utils/code_utils.h"


 4.2 Add CoAP related defines and variables.

#define TEMPERATURE_STATE_URI     "temp/celcius"
otCoapResource mResource_TEMPERATURE_STATE;
const char mTEMPERATUREStateUriPath[]=TEMPERATURE_STATE_URI;

4.3 Implement CoAP processing callback

static void temperature_state_coapHandler(void *aContext, otMessage *aMessage,
                             const otMessageInfo *aMessageInfo)
{
    otCliOutputFormat("sleepy-demo-mtd temperature_state_coapHandler\r\n");
    otError error = OT_ERROR_NONE;
    otMessage *responseMessage;
    otCoapCode responseCode = OT_COAP_CODE_CHANGED;
    otCoapCode messageCode = otCoapMessageGetCode(aMessage);
    otCoapType messageType = otCoapMessageGetType(aMessage);

    responseMessage = otCoapNewMessage((otInstance*)aContext, NULL);
    otEXPECT_ACTION(responseMessage != NULL, error = OT_ERROR_NO_BUFS);

    otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode);
    otCoapMessageSetToken(responseMessage, otCoapMessageGetToken(aMessage),
                         otCoapMessageGetTokenLength(aMessage));
    otCoapMessageSetPayloadMarker(responseMessage);

    if(OT_COAP_CODE_GET == messageCode)
    {
        memset(str_buf,0,64);
        sprintf(str_buf,"%f",((float)temp_data/1000.0));
         otCliOutputFormat("\r\nsleepy-demo-mtd coap get\r\n");
        otCliOutputFormat(str_buf);
        error = otMessageAppend(responseMessage, str_buf,
                                strlen((const char*)str_buf));
        otEXPECT(OT_ERROR_NONE == error);

        error = otCoapSendResponse((otInstance*)aContext, responseMessage,
                                   aMessageInfo);
        otEXPECT(OT_ERROR_NONE == error);
    }

exit:

    if (error != OT_ERROR_NONE && responseMessage != NULL)
    {
        otMessageFree(responseMessage);
    }
}


 4.4 Add CoAP start codes in app_init.

otCoapStart(otGetInstance(),OT_DEFAULT_COAP_PORT);

mResource_TEMPERATURE_STATE.mUriPath = mTEMPERATUREStateUriPath;    

mResource_TEMPERATURE_STATE.mContext = otGetInstance(); 

mResource_TEMPERATURE_STATE.mHandler = &temperature_state_coapHandler;

strncpy(mTEMPERATUREStateUriPath, TEMPERATURE_STATE_URI, sizeof(TEMPERATURE_STATE_URI));

otCoapAddResource(otGetInstance(),&mResource_TEMPERATURE_STATE);


 5. Build and download sleepy-demo-mtd.hex into your BRD4161A.


6.Reset BRD4161A to join OpenThread Border Router and use CLI command "ipaddr" to output IPv6 address of Sleepy End Device. 

 

7.Now, you can run coap-client (install libCoAP) on Raspberry Pi OTBR to get temperature reading through CoAP server running on BRD4161A Sleepy End Device.

 


 

Tuesday, June 29, 2021

What does Matter (formerly Project CHIP) matter?

The Zigbee Alliance officially opened the Project Connected Home over IP (Project CHIP) Working Group on January 17, 2020 and is in the process of drafting the specification. Recently, Zigbee Alliance renames itself as Connectivity Standards Alliance (CSA) and Project CHIP as "Matter". From any perspective, it's ambitious to announce and developing such a big picture to provide an Unifying, Interoperable, Ecosystem-Flexible, Easy to Use, Secure,..etc. solution in IOT world. 

Here, I would like to share my thoughts about "Matter". 

First of all, "Matter" is not equal to Thread protocol. "Matter" is application protocol and Thread is wireless IPv6 network protocol. "Matter" can run on any TCP/UDP going through IPv6 network protocols which is not limited to Thread.

Next, let's go to some other points:

Unifying/Interoperable/Ecosystem-Flexible/Easy to Use: This is the most primitive of Matter and let's have a look at the following CHIP pyramid.

 

Basically, Matter is an application protocol and I would say it is unified/interoperable in application point of view. Since Matter can run on TCP/UDP going through IPv6, there are different physical protocols, such as Thread/802.15.4, WiFi, Ethernet, or even BLE to provide such IPv6 capability. Let's imagine you buy a Smart Home gadget which claims it's Matter certified but doesn't mention which physical network protocol it use or it just mentions using Thread protocol. I would say any generic user won't know much about how to add such gadget into an existing Smart Home system. 

My point is Matter might unify application protocol but interoperability might be still an issue if different Matter devices use different physical network connection. Don't mean to add confusion but you must know you still need something like border router to forward or routing everything together.

For different ecosystems such as Google Nest and Apple HomeKit, I still don't see a clear picture that Matter can work to each other smoothly and seamlessly. Hopefully, I will see how different ecosystems work together soon and how easy-to-use of this.

Secure: since Matter runs on TCP/UDP going through IPv6, it's no problem to leverage modern security practices and protocols such as TLS/DTLS. However, most of IOT devices don't use secure boot and secure element to provide hardware/firmware protection. From my personal point of view, secure boot and security vault, such as Silicon Labs EFR32 series 2 chip provides, would be necessary to provide complete protection not only from protocol side itself.

Federated: No single entity serves as a throttle or a single-point-of-failure for root of trust but I think this is more related to Thread Protocol instead of Matter (maybe I am wrong).

Low Overhead: The alliance claims the protocols are practically implementable on low compute-resource devices, such as MCUs. From personal perspective, the minimal RAM/Flash footprint and MCU computing power are still larger than other low power wireless protocols such as Zigbee and Z-Wave.

Pervasive: The protocols are broadly deployable and accessible, thanks to leveraging IP and being implementable on low-capability devices.But, I would say current low-capability devices is not such low capability as you imagine now. Current SOC for Thread/Matter is very powerful from my point of view. For your reference, Zigbee and Z-Wave devices still can run on 8051 based MCU until today.

In the end, this is about benefiting/improving user experiences for anyone using IOT/Smart Home gadgets. Matter reveals a respectful vision to developing and achieving it. Hopefully, we can see it soon on the market.

 

Wednesday, February 24, 2021

Project Connected Home over IP (ProjectCHIP) demonstration with EFR32 as ProjectCHIP node, Raspberry Pi as OTBR, and Ubuntu VM as chip-tool

 The follow steps show you how to setup Project Connected Home over IP (ProjectCHIP) demonstration with EFR32 as ProjectCHIP node, Raspberry Pi as OTBR, and Ubuntu VM as chip-tool like in the diagram.



1. Install VirtualBox and Ubuntu 20.04 on your Desktop. Remember select "Bridged Adapter" in your network settings of VirtualBox/Ubuntu.

2. Setup environment (run the following commands in Ubuntu terminal) for building CHIP examples on Ubuntu VM.

    2.1 sudo apt-get install git gcc g++ python pkg-config libssl-dev libdbus-1-dev libglib2.0-dev libavahi-client-dev ninja-build python3-venv python3-dev unzip

    2.2.1 cd ~
    2.2.2 git clone https://github.com/SiliconLabs/sdk_support.git
    2.2.3 cd sdk_support
    2.2.4 git checkout ff45be117a5a1a20d27296628b0632523f65c66a

    2.3.1 cd ~
    2.3.2 git clone https://github.com/project-chip/connectedhomeip.git
    2.3.3 cd connectedhomeip
    2.3.4 git checkout 122da92801fd38f49b8fe3db397ccaa4eb0e4798
    2.3.5 source scripts/activate.sh
    2.3.6 gn gen out/host
    2.3.7 ninja -C out/host

3. Build and download CHIP lock-app for EFR32 (In my examples, I use BRD4180A with BRD4001A to act as Thread node running CHIP protocol)

    3.1 cd ~/connectedhomeip/examples/lock-app/efr32
    3.2 git submodule update --init
    3.3 source third_party/connectedhomeip/scripts/activate.sh
    3.4 export EFR32_SDK_ROOT=~/sdk_support
    3.5 export EFR32_BOARD=BRD4180A
    3.6 gn gen out/debug --args="efr32_sdk_root=\"${EFR32_SDK_ROOT}\" efr32_board=\"${EFR32_BOARD}\""
    3.7 ninja -C out/debug
    3.8 Using Simplicity Studio Commander to download chip-efr32-lock-example.s37 under out/debug folder into BRD4180A.

4. Refer to "Running OpenThread Border Router and device with Raspberry Pi and Silicon Labs EFR32 Kits" to setup OpenThread Border Router on Raspberry Pi.

5. Run the following commands in OpenThread Border Router terminal to setup Thread network for commission.

    5.1.1 sudo ot-ctl dataset init new
    5.1.2 sudo ot-ctl dataset channel 13
    5.1.3 sudo ot-ctl dataset panid 0xface
    5.1.4 sudo ot-ctl dataset extpanid face1111face2222
    5.1.5 sudo ot-ctl dataset networkname OpenThreadYKTest
    5.1.6 sudo ot-ctl dataset masterkey 00112233445566778899aabbccddeeff
    5.1.7 sudo ot-ctl dataset commit active

    5.2.1 sudo ot-ctl prefix add 2001:db8::/64 pasor
    5.2.2 sudo ot-ctl ifconfig up
    5.2.3 sudo ot-ctl thread start
    5.2.4 sudo ot-ctl netdata register
    5.2.5 sudo ot-ctl state
    5.2.6 sudo ot-ctl ipaddr


    5.3.1 sudo ip addr add dev eth0 2002::2/64

 
6. Run the following commands on terminal of EFR32 CHIP lock-app device (build and download in step 3)

    6.1 factoryreset
    6.2 dataset channel 13
    6.3 dataset panid 0xface
    6.4 dataset masterkey 00112233445566778899aabbccddeeff
    6.5 dataset commit active
    6.6 ifconfig up
    6.7 thread start
    6.8 ipaddr

7. Try to ping EFR32 CHIP lock-app device from Raspberry Pi border router to make sure it respond.


8. Add route (enp0s3 might be different on your VM) on Ubuntu VM to access to EFR32 CHIP lock-app device

    8.1 sudo ifconfig enp0s3 inet6 add 2002::1/64
    8.2 sudo ip route add 2001:db8:0:0::/64 via 2002::2
    8.3 sudo ip route add fd38:117c:3b66::/64 via 2002::2


     8.4Try to ping EFR32 CHIP lock-app device from Ununtu VM terminal this time to make sure it respond.

 


9. Build and run chip-tool on Ubuntu VM

    9.1 cd ~/connectedhomeip/examples/chip-tool
    9.2 git submodule update --init
    9.3 source third_party/connectedhomeip/scripts/activate.sh
    9.4 gn gen out/debug
    9.5 ninja -C out/debug

    9.6 run "./chip-tool onoff on 2001:DB8::C6CC:14E9:E7D1:A3EA 11097 1" to send onoff command on Ubuntu VM terminal to control EFR32 CHIP lock-app device

 


    9.7 Using J-link RTT Viewer to check if EFR32 CHIP lock-app device receives CHIP command.


P.S. This demostartion is based on steps in https://www.silabs.com/documents/public/training/wireless/chip-connected-home-over-ip-lab.pdf. However, there are some typo and ambiguous in the document and here I share the whole steps again according to my test.

Friday, September 4, 2020

Build an OpenThread Sleepy End Device Doorlock Prototype using Simplicity Studio v5 and EFR32XG21

The following steps show you how to build an OpenThread Sleepy End Device Doorlock Prototype using Simplicity Studio v5 and EFR32XG21

1. Start Simplicity Studio v5 and connect EFR32XG21 BRD4180A. Form Launcher tab, select sleepy-demo-mtd to create the project.


2. In Simplicity IDE tab, open sleepy-demo-mtd.slcp and click "FORCE GENERATION" button to generate related codes.


3. Change related network settings in setNetworkConfiguration function (main.c) so the device can join matched OpenThread Border Router network (refer to here to setup OpenThread Border Router).


4. Implement handleNetifStateChanged and call "otSetStateChangedCallback(instance, handleNetifStateChanged, instance);" after setNetworkConfiguration to setup sleepy end device doing polling.

void handleNetifStateChanged(uint32_t aFlags, void *aContext)
{
    otLinkModeConfig config;

    if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
    {
        otDeviceRole changedRole = otThreadGetDeviceRole(aContext);

        switch (changedRole)
        {
        case OT_DEVICE_ROLE_LEADER:
        case OT_DEVICE_ROLE_ROUTER:
            break;

        case OT_DEVICE_ROLE_CHILD:
            config.mRxOnWhenIdle       = 0;
            config.mSecureDataRequests = true;
            config.mDeviceType         = 0;
            config.mNetworkData        = 0;
            otThreadSetLinkMode(instance, config);
            sAllowSleep = true;
            break;

        case OT_DEVICE_ROLE_DETACHED:
        case OT_DEVICE_ROLE_DISABLED:
            break;
        }
    }
}



5. Create coap server with resource URI "ddl/state".

5.1 Add the following includes, defines and global variables in main.c

#include <openthread/coap.h>
#include "utils/code_utils.h"

#define DDL_STATE_URI     "ddl/state"
#define DDL_STATE_LOCK    "lock"
#define DDL_STATE_UNLOCK  "unlock"
otCoapResource mResource_DDL_STATE;
const char mDDLStateUriPath[]=DDL_STATE_URI;
static uint8_t ddlState[15] = DDL_STATE_UNLOCK;

5.2 Add the following coap handler function ddl_state_coapHandler.

static void ddl_state_coapHandler(void *aContext, otMessage *aMessage,
                             const otMessageInfo *aMessageInfo)
{
    otError error = OT_ERROR_NONE;
    otMessage *responseMessage;
    otCoapCode responseCode = OT_COAP_CODE_CHANGED;
    otCoapCode messageCode = otCoapMessageGetCode(aMessage);
    otCoapType messageType = otCoapMessageGetType(aMessage);

    responseMessage = otCoapNewMessage((otInstance*)aContext, NULL);
    otEXPECT_ACTION(responseMessage != NULL, error = OT_ERROR_NO_BUFS);

    otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode);
    otCoapMessageSetToken(responseMessage, otCoapMessageGetToken(aMessage),
                         otCoapMessageGetTokenLength(aMessage));
    otCoapMessageSetPayloadMarker(responseMessage);

    if(OT_COAP_CODE_GET == messageCode)
    {
        error = otMessageAppend(responseMessage, ddlState,
                                strlen((const char*)ddlState));
        otEXPECT(OT_ERROR_NONE == error);

        error = otCoapSendResponse((otInstance*)aContext, responseMessage,
                                   aMessageInfo);
        otEXPECT(OT_ERROR_NONE == error);
    }
    else if(OT_COAP_CODE_POST == messageCode)
    {
        char data[32];
        uint16_t offset = otMessageGetOffset(aMessage);
        uint16_t read = otMessageRead(aMessage, offset, data, sizeof(data) - 1);
        data[read] = '\0';

        /* process message */
        if(strcmp(DDL_STATE_LOCK, data) == 0)
        {
            /* update the attribute state */
            strcpy((char *)ddlState, DDL_STATE_LOCK);
        }
        else if(strcmp(DDL_STATE_UNLOCK, data) == 0)
        {
            /* update the attribute state */
            strcpy((char *)ddlState, DDL_STATE_UNLOCK);
        }
        else
        {
            /* no valid body, fail without response */
            otEXPECT_ACTION(false, error = OT_ERROR_NO_BUFS);
        }

        if (OT_COAP_TYPE_CONFIRMABLE == messageType)
        {
            error = otMessageAppend(responseMessage, ddlState,
                                    strlen((const char*)ddlState));
            otEXPECT(OT_ERROR_NONE == error);

            error = otCoapSendResponse((otInstance*)aContext,
                                       responseMessage, aMessageInfo);
            otEXPECT(OT_ERROR_NONE == error);
        }
    }

exit:

    if (error != OT_ERROR_NONE && responseMessage != NULL)
    {
        otMessageFree(responseMessage);
    }
}

5.3 Add the following code to start coap service and resource right after calling "otThreadSetEnabled(instance, true);" in main function.

    otCoapStart(instance,OT_DEFAULT_COAP_PORT);
    mResource_DDL_STATE.mUriPath = mDDLStateUriPath;
    mResource_DDL_STATE.mContext = instance;
    mResource_DDL_STATE.mHandler = &ddl_state_coapHandler;
    strncpy(mDDLStateUriPath, DDL_STATE_URI, sizeof(DDL_STATE_URI));
    otCoapAddResource(instance,&mResource_DDL_STATE);

 

6. Add the capability to output IPv6 addree of Sleepy End Device.

6.1 Add the following two global variables in main.c

char str[256];
int str_idx;

6.2 Add the following funtions in main.c

inline uint16_t Swap16(uint16_t v)
{
    return (((v & 0x00ffU) << 8) & 0xff00) | (((v & 0xff00U) >> 8) & 0x00ff);
}

inline uint16_t HostSwap16(uint16_t v)
{
    return Swap16(v);
}

void printIPv6Addr(void)
{
  const otNetifAddress *unicastAddrs;

  str_idx=0;
  memset(str,0x0,256);
  sprintf(str,"OpenThread Doorlock COAP Demo\r\nipaddr\r\n");
  str_idx=strlen(str);
  unicastAddrs = otIp6GetUnicastAddresses(instance);
  const otNetifAddress *addr = unicastAddrs;
  for (; addr; addr = addr->mNext){
      str_idx=strlen(str);
      sprintf(&str[str_idx],"%x:%x:%x:%x:%x:%x:%x:%x\r\n",
              HostSwap16(addr->mAddress.mFields.m16[0]), HostSwap16(addr->mAddress.mFields.m16[1]),
              HostSwap16(addr->mAddress.mFields.m16[2]), HostSwap16(addr->mAddress.mFields.m16[3]), HostSwap16(addr->mAddress.mFields.m16[4]),
              HostSwap16(addr->mAddress.mFields.m16[5]), HostSwap16(addr->mAddress.mFields.m16[6]), HostSwap16(addr->mAddress.mFields.m16[7])
             );
  }
  otPlatUartSend(str,sizeof(str));
}

 

6.3 Add calling printIPv6Addr in sl_button_on_change to print IPv6 address when button 1 is pressed.



7. Build and download sleepy-demo-mtd.hex into your EFR32XG21 BRD4180A.

 


8. Reset EFR32XG21 BRD4180A to join OpenThread Border Router and press PB1 on BRD4001A to output IPv6 address of Sleepy End Device.



9. Now, you can use libcoap to get and set doorlock state through coap server running on EFR32XG21 BRD4180A Sleepy End Device.



P.S. You can use Simplicity Studio Energy Profiler to check power consumption of Sleepy End Device and make sure it goes to sleep mode when not doing polling.