blob: 5b88c539fa2fba267cb6d36b92eccddc4df18f0f [file] [log] [blame]
/*
* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <string.h>
#include "bsp.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "gpiointerrupt.h"
#include "hal-config-board.h"
#include "hal_common.h"
#include "openthread-system.h"
#include "platform-efr32.h"
#include "rtcdriver.h"
#include <common/logging.hpp>
#include <openthread-core-config.h>
#include <openthread/cli.h>
#include <openthread/config.h>
#include <openthread/dataset_ftd.h>
#include <openthread/diag.h>
#include <openthread/instance.h>
#include <openthread/link.h>
#include <openthread/message.h>
#include <openthread/tasklet.h>
#include <openthread/thread.h>
#include <openthread/udp.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/logging.h>
// Constants
#define MULTICAST_ADDR "ff03::1"
#define MULTICAST_PORT 123
#define RECV_PORT 234
#define SLEEPY_POLL_PERIOD_MS 5000
#define MTD_MESSAGE "mtd is awake"
#define FTD_MESSAGE "ftd button"
// Types
typedef struct ButtonArray
{
GPIO_Port_TypeDef port;
unsigned int pin;
} ButtonArray_t;
// Prototypes
void deviceOutOfSleepCb(void);
bool sleepCb(void);
void setNetworkConfiguration(otInstance *aInstance);
void handleNetifStateChanged(uint32_t aFlags, void *aContext);
void gpioInit(void (*gpioCallback)(uint8_t pin));
void buttonCallback(uint8_t pin);
void initUdp(void);
void applicationTick(void);
void mtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
// Variables
static otInstance * instance;
static otUdpSocket sMtdSocket;
static otSockAddr sMulticastSockAddr;
static const ButtonArray_t sButtonArray[BSP_BUTTON_COUNT] = BSP_BUTTON_INIT;
static bool sButtonPressed = false;
static bool sRxOnIdleButtonPressed = false;
static bool sLedOn = false;
static bool sAllowDeepSleep = false;
static bool sTaskletsPendingSem = true;
int main(int argc, char *argv[])
{
otLinkModeConfig config;
otSysInit(argc, argv);
gpioInit(buttonCallback);
instance = otInstanceInitSingle();
assert(instance);
otCliUartInit(instance);
otLinkSetPollPeriod(instance, SLEEPY_POLL_PERIOD_MS);
setNetworkConfiguration(instance);
otSetStateChangedCallback(instance, handleNetifStateChanged, instance);
config.mRxOnWhenIdle = true;
config.mSecureDataRequests = true;
config.mDeviceType = 0;
config.mNetworkData = 0;
otThreadSetLinkMode(instance, config);
initUdp();
otIp6SetEnabled(instance, true);
otThreadSetEnabled(instance, true);
efr32SetSleepCallback(sleepCb, deviceOutOfSleepCb);
while (!otSysPseudoResetWasRequested())
{
otTaskletsProcess(instance);
otSysProcessDrivers(instance);
applicationTick();
// Put the EFR32 into deep sleep if callback sleepCb permits.
efr32Sleep();
}
otInstanceFinalize(instance);
return 0;
}
void deviceOutOfSleepCb(void)
{
static uint32_t udpPacketSendTimer = 0;
if (udpPacketSendTimer == 0)
{
udpPacketSendTimer = otPlatAlarmMilliGetNow();
}
if ((otPlatAlarmMilliGetNow() - udpPacketSendTimer) >= 5000)
{
sButtonPressed = true;
udpPacketSendTimer = 0;
}
}
/*
* Callback from efr32Sleep to indicate if it is ok to go into sleep mode.
* This runs with interrupts disabled.
*/
bool sleepCb(void)
{
bool allow;
allow = (sAllowDeepSleep && !sTaskletsPendingSem);
sTaskletsPendingSem = false;
return allow;
}
void otTaskletsSignalPending(otInstance *aInstance)
{
(void)aInstance;
sTaskletsPendingSem = true;
}
/*
* Override default network settings, such as panid, so the devices can join a network
*/
void setNetworkConfiguration(otInstance *aInstance)
{
static char aNetworkName[] = "SleepyEFR32";
otOperationalDataset aDataset;
memset(&aDataset, 0, sizeof(otOperationalDataset));
/*
* Fields that can be configured in otOperationDataset to override defaults:
* Network Name, Mesh Local Prefix, Extended PAN ID, PAN ID, Delay Timer,
* Channel, Channel Mask Page 0, Network Master Key, PSKc, Security Policy
*/
aDataset.mActiveTimestamp = 1;
aDataset.mComponents.mIsActiveTimestampPresent = true;
/* Set Channel to 15 */
aDataset.mChannel = 15;
aDataset.mComponents.mIsChannelPresent = true;
/* Set Pan ID to 2222 */
aDataset.mPanId = (otPanId)0x2222;
aDataset.mComponents.mIsPanIdPresent = true;
/* Set Extended Pan ID to C0DE1AB5C0DE1AB5 */
uint8_t extPanId[OT_EXT_PAN_ID_SIZE] = {0xC0, 0xDE, 0x1A, 0xB5, 0xC0, 0xDE, 0x1A, 0xB5};
memcpy(aDataset.mExtendedPanId.m8, extPanId, sizeof(aDataset.mExtendedPanId));
aDataset.mComponents.mIsExtendedPanIdPresent = true;
/* Set master key to 1234C0DE1AB51234C0DE1AB51234C0DE */
uint8_t key[OT_MASTER_KEY_SIZE] = {0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5};
memcpy(aDataset.mMasterKey.m8, key, sizeof(aDataset.mMasterKey));
aDataset.mComponents.mIsMasterKeyPresent = true;
/* Set Network Name to SleepyEFR32 */
size_t length = strlen(aNetworkName);
assert(length <= OT_NETWORK_NAME_MAX_SIZE);
memcpy(aDataset.mNetworkName.m8, aNetworkName, length);
aDataset.mComponents.mIsNetworkNamePresent = true;
otDatasetSetActive(aInstance, &aDataset);
}
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);
sAllowDeepSleep = true;
break;
case OT_DEVICE_ROLE_DETACHED:
case OT_DEVICE_ROLE_DISABLED:
break;
}
}
}
/*
* Provide, if required an "otPlatLog()" function
*/
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
OT_UNUSED_VARIABLE(aFormat);
va_list ap;
va_start(ap, aFormat);
otCliPlatLogv(aLogLevel, aLogRegion, aFormat, ap);
va_end(ap);
}
#endif
void gpioInit(void (*callback)(uint8_t pin))
{
// set up button GPIOs to input with pullups
for (int i = 0; i < BSP_BUTTON_COUNT; i++)
{
GPIO_PinModeSet(sButtonArray[i].port, sButtonArray[i].pin, gpioModeInputPull, 1);
}
// set up interrupt based callback function on falling edge
GPIOINT_Init();
GPIOINT_CallbackRegister(sButtonArray[0].pin, callback);
GPIOINT_CallbackRegister(sButtonArray[1].pin, callback);
GPIO_IntConfig(sButtonArray[0].port, sButtonArray[0].pin, false, true, true);
GPIO_IntConfig(sButtonArray[1].port, sButtonArray[1].pin, false, true, true);
BSP_LedsInit();
BSP_LedClear(0);
BSP_LedClear(1);
}
void initUdp(void)
{
otError error;
otSockAddr sockaddr;
memset(&sMulticastSockAddr, 0, sizeof sMulticastSockAddr);
otIp6AddressFromString(MULTICAST_ADDR, &sMulticastSockAddr.mAddress);
sMulticastSockAddr.mPort = MULTICAST_PORT;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.mPort = RECV_PORT;
error = otUdpOpen(instance, &sMtdSocket, mtdReceiveCallback, NULL);
if (error != OT_ERROR_NONE)
{
return;
}
error = otUdpBind(&sMtdSocket, &sockaddr);
if (error != OT_ERROR_NONE)
{
otUdpClose(&sMtdSocket);
return;
}
}
void buttonCallback(uint8_t pin)
{
if ((pin & 0x01) == 0x01)
{
sButtonPressed = true;
}
else if ((pin & 0x01) == 0x00)
{
sRxOnIdleButtonPressed = true;
}
}
void applicationTick(void)
{
otError error = 0;
otMessageInfo messageInfo;
otMessage * message = NULL;
char * payload = MTD_MESSAGE;
otLinkModeConfig config;
if (sRxOnIdleButtonPressed == true)
{
sRxOnIdleButtonPressed = false;
sAllowDeepSleep = !sAllowDeepSleep;
config.mRxOnWhenIdle = !sAllowDeepSleep;
config.mSecureDataRequests = true;
config.mDeviceType = 0;
config.mNetworkData = 0;
otThreadSetLinkMode(instance, config);
}
if (sButtonPressed == true)
{
sButtonPressed = false;
memset(&messageInfo, 0, sizeof(messageInfo));
memcpy(&messageInfo.mPeerAddr, &sMulticastSockAddr.mAddress, sizeof messageInfo.mPeerAddr);
messageInfo.mPeerPort = sMulticastSockAddr.mPort;
message = otUdpNewMessage(instance, NULL);
if (message != NULL)
{
error = otMessageAppend(message, payload, (uint16_t)strlen(payload));
if (error == OT_ERROR_NONE)
{
error = otUdpSend(&sMtdSocket, message, &messageInfo);
if (error == OT_ERROR_NONE)
{
return;
}
}
}
if (message != NULL)
{
otMessageFree(message);
}
}
}
void mtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
OT_UNUSED_VARIABLE(aContext);
OT_UNUSED_VARIABLE(aMessage);
OT_UNUSED_VARIABLE(aMessageInfo);
uint8_t buf[1500];
int length;
length = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
buf[length] = '\0';
if (strcmp((char *)buf, FTD_MESSAGE) == 0)
{
sLedOn = !sLedOn;
if (sLedOn)
{
BSP_LedSet(0);
}
else
{
BSP_LedClear(0);
}
}
}