| /* |
| * Copyright (c) 2020, 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.h" |
| #include "hal_common.h" |
| #include "openthread-system.h" |
| #include "platform-efr32.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/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 button" |
| #define FTD_MESSAGE "ftd button" |
| |
| // Types |
| typedef struct ButtonArray |
| { |
| GPIO_Port_TypeDef port; |
| unsigned int pin; |
| } ButtonArray_t; |
| |
| // Prototypes |
| 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); |
| |
| /** |
| * This function initializes the CLI app. |
| * |
| * @param[in] aInstance The OpenThread instance structure. |
| * |
| */ |
| extern void otAppCliInit(otInstance *aInstance); |
| |
| // 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); |
| |
| otAppCliInit(instance); |
| |
| otLinkSetPollPeriod(instance, SLEEPY_POLL_PERIOD_MS); |
| setNetworkConfiguration(instance); |
| otSetStateChangedCallback(instance, handleNetifStateChanged, instance); |
| |
| config.mRxOnWhenIdle = true; |
| config.mDeviceType = 0; |
| config.mNetworkData = 0; |
| otThreadSetLinkMode(instance, config); |
| |
| initUdp(); |
| otIp6SetEnabled(instance, true); |
| otThreadSetEnabled(instance, true); |
| efr32SetSleepCallback(sleepCb); |
| |
| while (!otSysPseudoResetWasRequested()) |
| { |
| otTaskletsProcess(instance); |
| otSysProcessDrivers(instance); |
| |
| applicationTick(); |
| |
| // Put the EFR32 into deep sleep if callback sleepCb permits. |
| efr32Sleep(); |
| } |
| |
| otInstanceFinalize(instance); |
| return 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, 0x12, 0x34, 0xC0, 0xDE}; |
| 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.mDeviceType = 0; |
| config.mNetworkData = 0; |
| otThreadSetLinkMode(instance, config); |
| sAllowDeepSleep = false; |
| 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(instance, &sMtdSocket, &sockaddr); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otUdpClose(instance, &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.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(instance, &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); |
| } |
| } |
| } |