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.



1 comment:

  1. Unfortunately Silicon Labs HomeKit SDK access in Simplicity Studio is restricted to MFi Licensee registered and verified members.

    ReplyDelete