/** Waits for the specified type of message. Silently discards any other messages received. Received message will be in znpBuf[]. If the specified type of message isn't received then returns error code. @return 0 if success, -21 if timeout, -31 if received a message but it wasn't the type we're expecting. */ signed int waitForMessage(unsigned int messageType, unsigned char timeoutSecs) { unsigned long counter = 0; unsigned long timeout = (timeoutSecs / 2) * XTAL; //since loop takes more than 1 clock cycle while (SRDY_IS_HIGH() && (counter != timeout)) counter++; if (counter == timeout) return -21; spiPoll(); if ((znpBuf[SRSP_LENGTH_FIELD] > 0) && (CONVERT_TO_INT(znpBuf[2], znpBuf[1]) == messageType)) { #ifdef ZNP_INTERFACE_VERBOSE printf("Received expected message %04X\r\n", messageType); #endif return 0; } else { //unexpected message received //#ifdef ZNP_INTERFACE_VERBOSE printf("waitForMessage Unexpected Message: L%u, messageType = %04X\r\n", znpBuf[SRSP_LENGTH_FIELD] , (CONVERT_TO_INT(znpBuf[2], znpBuf[1]))); //#endif return -31; } }
/** Prints out the returned value of zdoNetworkAddressRequest() */ void printZdoNetworkAddressResponse(unsigned char* rsp) { printf("Status = %u; MAC:", rsp[0]); for (int i=1; i<9; i++) printf("%02X ", rsp[i]); printf("NwkAddr:%04X; StartIdx:%u, numAssocDev:%u\r\n", CONVERT_TO_INT(rsp[9], rsp[10]), rsp[11],rsp[12]); for (int j=0; j<rsp[12]; j++) printf("%04X ", CONVERT_TO_INT(rsp[(j+13)], rsp[(j+14)])); printf("\r\n"); }
/** Displays the header information in an AF_INCOMING_MSG. @param srsp a pointer to the buffer containing the message @return 0 if success, -1 if not a AF_INCOMING_MSG. */ signed int printAfIncomingMsgHeader(unsigned char* srsp) { if (CONVERT_TO_INT(srsp[SRSP_CMD_LSB_FIELD], srsp[SRSP_CMD_MSB_FIELD]) == AF_INCOMING_MSG) { printf("#%02u: Grp%04x Clus%04x, SrcAd%04x, SrcEnd%02x DestEnd%02x Bc%02x Lqi%02x Sec%02x Len%02u", srsp[SRSP_HEADER_SIZE+15], CONVERT_TO_INT(srsp[SRSP_HEADER_SIZE], srsp[SRSP_HEADER_SIZE+1]), CONVERT_TO_INT(srsp[SRSP_HEADER_SIZE+2], srsp[SRSP_HEADER_SIZE+3]), CONVERT_TO_INT(srsp[SRSP_HEADER_SIZE+4], srsp[SRSP_HEADER_SIZE+5]), srsp[SRSP_HEADER_SIZE+6], srsp[SRSP_HEADER_SIZE+7], srsp[SRSP_HEADER_SIZE+8], srsp[SRSP_HEADER_SIZE+9], srsp[SRSP_HEADER_SIZE+10], srsp[SRSP_HEADER_SIZE+16]); return 0; } else { return -1; } }
/** Private method used to wait until a message is received indicating that we are on the network. Exits if received message is a ZDO_STATE_CHANGE_IND and the state matches what we want. Else loops until timeout. @note Since this is basically a blocking wait, you can also implement this in your application. @param expectedState the deviceState we are expecting - DEV_ZB_COORD etc. @param timeoutMs the amount of milliseconds to wait before returning an error. Should be an integer multiple of WFDS_POLL_INTERVAL_MS. @todo modify this if using UART. */ static moduleResult_t waitForDeviceState(unsigned char expectedState, uint16_t timeoutMs) { RETURN_INVALID_PARAMETER_IF_TRUE( ((!(IS_VALID_DEVICE_STATE(expectedState))) || (timeoutMs < WFDS_POLL_INTERVAL_MS)), METHOD_WAIT_FOR_DEVICE_STATE); uint16_t intervals = timeoutMs / WFDS_POLL_INTERVAL_MS; // how many times to check uint8_t state = 0xFF; while (intervals--) { if (moduleHasMessageWaiting()) // If there's a message waiting for us { getMessage(); if (CONVERT_TO_INT(zmBuf[2], zmBuf[1]) == ZDO_STATE_CHANGE_IND) // if it's a state change message { state = zmBuf[SRSP_PAYLOAD_START]; printf("%s, ", getDeviceStateName(state)); // display the name of the state in the message if (state == expectedState) // if it's the state we're expecting return MODULE_SUCCESS; //Then we're done! } //else we received a different type of message so we just ignore it } delayMs(WFDS_POLL_INTERVAL_MS); } // We've completed the loop without receiving the sate that we want; so therefore we've timed out. RETURN_RESULT(TIMEOUT, METHOD_WAIT_FOR_DEVICE_STATE); }
/** Utility method to display stored network configuration parameters. These are the configuration parameters stored in NV memory and are used to initialize the ZNP. @post znpResult contains the error code, or ZNP_SUCCESS if success. */ void getNetworkConfigurationParameters() { printf("ZNP Configuration Parameters\r\n"); unsigned char* param; param = getConfigurationParameter(ZCD_NV_PANID); if (znpResult != 0) return; printf(" ZCD_NV_PANID %04X\r\n", (CONVERT_TO_INT(param[0], param[1]))); param = getConfigurationParameter(ZCD_NV_CHANLIST); if (znpResult != 0) return; printf(" ZCD_NV_CHANLIST %02X %02X %02X %02X\r\n", param[0], param[1], param[2], param[3]); param = getConfigurationParameter(ZCD_NV_SECURITY_MODE); if (znpResult != 0) return; printf(" ZCD_NV_SECURITY_MODE %02X\r\n", param[0]); param = getConfigurationParameter(ZCD_NV_PRECFGKEYS_ENABLE); if (znpResult != 0) return; printf(" ZCD_NV_PRECFGKEYS_ENABLE %02X\r\n", param[0]); param = getConfigurationParameter(ZCD_NV_PRECFGKEY); if (znpResult != 0) return; printf(" ZCD_NV_PRECFGKEY "); printHexBytes(param, ZCD_NV_PRECFGKEY_LEN); }
/** Retrieves a random number from the ZNP using SYS_RANDOM command. @post znpResult contains the error code, or ZNP_SUCCESS if success. @return the random number, or indeterminate data if error. */ unsigned int getRandom() { znpBuf[0] = SYS_RANDOM_PAYLOAD_LEN; znpBuf[1] = MSB(SYS_RANDOM); znpBuf[2] = LSB(SYS_RANDOM); znpResult = sendMessage(); return (CONVERT_TO_INT(znpBuf[SRSP_PAYLOAD_START], znpBuf[SRSP_PAYLOAD_START+1])); }
/** Displays the radio's device Information Properties. Device Information Properties include: - Device State indicates whether the ZNP is on a network or not, and what type of device it is. This is a handy thing to check if things aren't operating correctly. If the device is starting as a coordinator, you'll see states of 01, 08, 08, then 09 once it has fully started. - MAC Address (aka IEEE Address) is a globally unique serial number for this IC. - Device Short Address is a network address assigned by the coordinator, similar to an IP Address in DHCP. The Coordinator always has a Short Address of 0. - Parent MAC Address is the IEEE Address of this device's "parent", i.e. which device was used to join the network. For a router, once joined this parent MAC address is irrelevant. This DIP will NOT be updated if the network reforms. For an end-device then this parent MAC address will always specify which router the end-device is joined to. - Channel is which frequency channel the device is operating on. - PAN ID (Personal Area Network Identifier) of the network is a unique number shared for all devices on the same network. - Extended PAN ID of the network is the coordinator's MAC Address. If device is not connected to a network then the Short Address fields will be 0xFEFF, the Parent MAC Address and channel will be 0, and the Extended PAN ID will be this device's MAC Address. @post znpResult contains the error code, or ZNP_SUCCESS if success. */ void getDeviceInformation() { printf("Device Information Properties (MSB first)\r\n"); getDeviceInformationProperty(DIP_STATE); if (znpResult != 0) return; printf(" Device State: %s (%u)\r\n", getDeviceStateName(znpBuf[SRSP_DIP_VALUE_FIELD]), (znpBuf[SRSP_DIP_VALUE_FIELD])); getDeviceInformationProperty(DIP_MAC_ADDRESS); printf(" MAC Address: "); if (znpResult != 0) return; for (int i = SRSP_DIP_VALUE_FIELD+7; i>=SRSP_DIP_VALUE_FIELD; i--) printf("%02X ", znpBuf[i]); printf("\r\n"); getDeviceInformationProperty(DIP_SHORT_ADDRESS); if (znpResult != 0) return; printf(" Short Address: %04X\r\n", CONVERT_TO_INT(znpBuf[SRSP_DIP_VALUE_FIELD] , znpBuf[SRSP_DIP_VALUE_FIELD+1])); getDeviceInformationProperty(DIP_PARENT_SHORT_ADDRESS); if (znpResult != 0) return; printf(" Parent Short Address: %04X\r\n", CONVERT_TO_INT(znpBuf[SRSP_DIP_VALUE_FIELD] , znpBuf[SRSP_DIP_VALUE_FIELD+1])); getDeviceInformationProperty(DIP_PARENT_MAC_ADDRESS); if (znpResult != 0) return; printf(" Parent MAC Address: "); for (int i = SRSP_DIP_VALUE_FIELD+7; i>=SRSP_DIP_VALUE_FIELD; i--) printf("%02X ", znpBuf[i]); printf("\r\n"); getDeviceInformationProperty(DIP_CHANNEL); if (znpResult != 0) return; printf(" Device Channel: %u\r\n", znpBuf[SRSP_DIP_VALUE_FIELD]); getDeviceInformationProperty(DIP_PANID); if (znpResult != 0) return; printf(" PAN ID: %04X\r\n", CONVERT_TO_INT(znpBuf[SRSP_DIP_VALUE_FIELD], znpBuf[SRSP_DIP_VALUE_FIELD+1])); getDeviceInformationProperty(DIP_EXTENDED_PANID); if (znpResult != 0) return; printf(" Extended PAN ID: "); for (int i = SRSP_DIP_VALUE_FIELD+7; i>=SRSP_DIP_VALUE_FIELD; i--) printf("%02X ", znpBuf[i]); printf("\r\n"); }
/** Blocking wait until a message is received. Exits if received message is a ZDO_STATE_CHANGE_IND and the state matches what we want. Else loops. @param expectedState the deviceState we are expecting - DEV_ZB_COORD etc.*/ void waitForDeviceState(unsigned char expectedState) { unsigned char state = 0xFF; while (state != expectedState) { while (SRDY_IS_HIGH()); pollAndDisplay(); if (CONVERT_TO_INT(znpBuf[2], znpBuf[1]) == ZDO_STATE_CHANGE_IND) state = znpBuf[SRSP_PAYLOAD_START]; } }
/** Waits for SRDY to go low, indicating a message has arrived. Displays the msg to console, and find the device's MAC address using ZDO_IEEE_ADDR_REQ. Then, if FIND_NWK_ADDRESS was defined, uses the received MAC address to find the device's short address. The two should match. @pre callbacks have been enabled with enableCallbacks() - otherwise you will never receive the ZDO_IEEE_ADDR_RSP message. */ void displayReceivedMessagesAndFindDevice() { znpBuf[0] = 0; unsigned int shortAddress; while (1) { while (SRDY_IS_HIGH()); pollAndDisplay(); if (CONVERT_TO_INT(znpBuf[2], znpBuf[1]) == AF_INCOMING_MSG) { shortAddress = CONVERT_TO_INT(znpBuf[7], znpBuf[8]); zdoRequestIeeeAddress(shortAddress, SINGLE_DEVICE_RESPONSE, 0); if (znpResult == 0) { printf("MAC Address (LSB first) of sender is :"); printHexBytes(znpBuf+4, 8); } else { printf("IEEE Request Failed (%d)\r\n", znpResult); continue; } #ifdef FIND_NWK_ADDRESS /** Very simple example of zdoNetworkAddressRequest() */ unsigned char* response = zdoNetworkAddressRequest(znpBuf+4, SINGLE_DEVICE_RESPONSE, 0); //was SINGLE_DEVICE_RESPONSE if (znpResult == 0) { unsigned int sa = CONVERT_TO_INT(znpBuf[12], znpBuf[13]); printf("Short Address = %04X\r\n", sa); } else { printf("NWK Request Failed (%d)\r\n", znpResult); continue; } printZdoNetworkAddressResponse(response); #endif } } }
/** * Crea un message_t a partir de un stream de bytes apuntado por source. * @param source puntero al comienzo del mensaje serializado * @return un message_t con los datos parseados. * @todo verificar que lenData < MAX_DATA_MESSAGE */ struct message_t deserializeMessage(unsigned char* source) { unsigned char* sourcePtr = source; struct message_t im; // header im.sequence = CONVERT_TO_INT( (*sourcePtr), (*(sourcePtr+1)) ); sourcePtr += 2; for (int i = 0; i < 8; i++) im.mac[i] = *sourcePtr++; im.msgType = *sourcePtr++; im.lenData = *sourcePtr++; // data for (int i = 0; i < im.lenData; i++) im.data[i] = *sourcePtr++; return im; }
/** * Utility function only used when displaying the network topology. * Recursively iterates through the network and displays the children of a node and then the children * of those children, and then the children of those children, etc. using recursion. * @param shortAddress the root at which to start getting the topology. Use 0000 for the Coordinator. * @note does not retrieve children for the same node twice. Uses a Set to keep track of which nodes * have been searched. This is done because occasionally two nodes will list the same child. * @see http://en.wikipedia.org/wiki/Recursion_%28computer_science%29 * @return the number of children * @todo handle case where starting index != 0. We will have to keep track of which nodes had * additional children, and then call zdoRequestIeeeAddress() for the next group. */ static uint8_t displayChildren(uint16_t shortAddress) { printf("Device %04X has ", shortAddress); moduleResult_t result = zdoRequestIeeeAddress(shortAddress, INCLUDE_ASSOCIATED_DEVICES, 0); #define MAX_CHILDREN 5 // Max of 20 from Z-Stack. Of these 20, 7 can be routers, the rest end devices. // We reduce to fit in RAM on the LaunchPad since this is called recursively uint8_t childCount = 0; uint16_t children[MAX_CHILDREN]; if (result == MODULE_SUCCESS) { if (!(addToSet(&searchedSet, shortAddress))) { printf("[addToSet failed for 0x%04X\r\n]", shortAddress); } childCount = zmBuf[SRSP_PAYLOAD_START + ZDO_IEEE_ADDR_RSP_NUMBER_OF_ASSOCIATED_DEVICES_FIELD]; if (childCount > 0) { printf("%u children, starting with #%u:", childCount, zmBuf[SRSP_PAYLOAD_START + ZDO_IEEE_ADDR_RSP_START_INDEX_FIELD]); int i=0; int j; for (j = 0; j < (childCount*2) - 1; j += 2) // Iterate through all the children. Each is a 2B integer { children[i] = CONVERT_TO_INT(zmBuf[(j + SRSP_PAYLOAD_START + ZDO_IEEE_ADDR_RSP_ASSOCIATED_DEVICE_FIELD_START)], zmBuf[( j + SRSP_PAYLOAD_START + ZDO_IEEE_ADDR_RSP_ASSOCIATED_DEVICE_FIELD_START + 1)]); printf("<%04X> ", children[i]); i++; } printf("\r\n"); for (i=0; i<childCount; i++) // Iterate through all children { if (!setContains(&searchedSet, children[i])) // Don't process the same short address twice! { displayChildren(children[i]); // Note use of recursion here } } } else { printf("no children\r\n"); } } else { printf("Could not locate that device (Error Code 0x%02X)\r\n", result); } return childCount; }
/** Creates an infoMessage from the stream of bytes starting at source @param source the beginning of the serialized Info Message @param info the infoMessage to create @return the number of bytes deserialized if success, or else an error code < 0 */ int16_t deserializeInfoMessage(unsigned char* source, struct infoMessage* info) { unsigned char* sourcePtr = source; info->header = deserializeHeader(sourcePtr); // First, deserialize the header sourcePtr += (HEADER_SIZE); info->deviceType = *sourcePtr++; // Get the deviceType info->numParameters = *sourcePtr++; // ... and the number of Parameters if (info->numParameters > MAX_KVPS_IN_STATUS_MESSAGE) return -1; int i; for (i=0; i < info->numParameters; i++) //for each Parameter: { info->kvps[i].oid = *sourcePtr++; info->kvps[i].value = CONVERT_TO_INT( (*sourcePtr), (*(sourcePtr+1)) ); sourcePtr += 2; } return (sourcePtr - source); }
/** Wait until a message is received. Exits if received message is a ZDO_STATE_CHANGE_IND and the state matches what we want. Else loops. @param expectedState the deviceState we are expecting - DEV_ZB_COORD etc. @return 0 if success, -1 if timeout */ signed int waitForDeviceState(unsigned char expectedState) { printf("Waiting for network... "); unsigned char state = 0xFF; unsigned long timeout = WAIT_FOR_DEVICE_STATE_TIMEOUT; while (state != expectedState) { while ((SRDY_IS_HIGH()) && (timeout > 0)) timeout--; if (timeout == 0) return -1; //error spiPoll(); if (CONVERT_TO_INT(znpBuf[2], znpBuf[1]) == ZDO_STATE_CHANGE_IND) { state = znpBuf[SRSP_PAYLOAD_START]; printf("%s ", getDeviceStateName(state)); } } printf("\r\n"); return 0; }
/** Displays the type of message in zmBuf. Ignores the message if length = 0. @pre moduleHasMessageWaiting() is true @pre getMessage() called to get message into zmBuf @post zmBuf Length field = 0 */ void displayMessage() { if (zmBuf[SRSP_LENGTH_FIELD] > 0) { switch ( (CONVERT_TO_INT(zmBuf[SRSP_CMD_LSB_FIELD], zmBuf[SRSP_CMD_MSB_FIELD])) ) { case AF_DATA_CONFIRM: { printf("AF_DATA_CONFIRM\r\n"); break; } case AF_INCOMING_MSG: { printf("AF_INCOMING_MSG "); printAfIncomingMsgHeader(zmBuf); printf("\r\n"); #ifdef VERBOSE_MESSAGE_DISPLAY printf("Payload: "); printHexBytes(zmBuf+SRSP_HEADER_SIZE+17, zmBuf[SRSP_HEADER_SIZE+16]); //print out message payload #endif break; } case AF_INCOMING_MSG_EXT: { printf("AF_INCOMING_MSG_EXT\r\n"); uint16_t len = AF_INCOMING_MESSAGE_EXT_LENGTH(); printf("Extended Message Received, L%u ", len); break; } case ZDO_IEEE_ADDR_RSP: { printf("ZDO_IEEE_ADDR_RSP\r\n"); displayZdoAddressResponse(zmBuf + SRSP_PAYLOAD_START); break; } case ZDO_NWK_ADDR_RSP: { printf("ZDO_NWK_ADDR_RSP\r\n"); displayZdoAddressResponse(zmBuf + SRSP_PAYLOAD_START); break; } case ZDO_END_DEVICE_ANNCE_IND: { printf("ZDO_END_DEVICE_ANNCE_IND\r\n"); displayZdoEndDeviceAnnounce(zmBuf); break; } case ZB_FIND_DEVICE_CONFIRM: { printf("ZB_FIND_DEVICE_CONFIRM\r\n"); break; } case ZDO_MGMT_NWK_DISCOVERY_RSP: { printf("ZDO_MGMT_NWK_DISCOVERY_RSP\r\n"); displayZdoNetworkDiscoveryResponse(zmBuf + SRSP_PAYLOAD_START); break; } case ZDO_MGMT_LEAVE_RSP: { printf("ZDO_MGMT_LEAVE_RSP\r\n"); displayZdoManagementLeaveResponse(zmBuf); break; } case ZDO_JOIN_CNF: { printf("ZDO_JOIN_CNF\r\n"); displayZdoJoinConfirm(zmBuf); break; } case ZDO_STATE_CHANGE_IND: { printf("ZDO_STATE_CHANGE_IND\r\n"); displayZdoStateChangeInd(zmBuf); break; } default: { printf("Message received, type 0x%04X\r\n", (CONVERT_TO_INT(zmBuf[SRSP_CMD_LSB_FIELD], zmBuf[SRSP_CMD_MSB_FIELD]))); printHexBytes(zmBuf, (zmBuf[SRSP_LENGTH_FIELD] + SRSP_HEADER_SIZE)); } } zmBuf[SRSP_LENGTH_FIELD] = 0; } //ignore messages with length == 0 }
void stateMachine() { while (1) { switch (state) { case STATE_IDLE: { if (stateFlags & STATE_FLAG_SEND_INFO_MESSAGE) //if there is a pending info message to be sent { state = STATE_SEND_INFO_MESSAGE; //then send the message and clear the flag stateFlags &= ~STATE_FLAG_SEND_INFO_MESSAGE; } //note: other flags (for different messages or events) can be added here break; } case STATE_ZNP_STARTUP: { #define ZNP_START_DELAY_IF_FAIL_MS 5000 /* Start the network; if fails then wait a second and try again. */ signed int startResult = startZnp(END_DEVICE); while (startResult != ZNP_SUCCESS) { printf("FAILED. Error Code %i, ZNP Result %i. Retrying...\r\n", startResult, znpResult); delayMs(ZNP_START_DELAY_IF_FAIL_MS); startResult = startZnp(END_DEVICE); } printf("Success\r\n"); //ZNP Initialized so store MAC Address memcpy(hdr.mac, getMacAddress(), 8); #ifdef SEND_MESSAGE_ON_TIMER signed int timerResult = initTimer(4, WAKEUP_AFTER_TIMER); if (timerResult != 0) { printf("timerResult Error %i, STOPPING\r\n", timerResult); while (1); } #endif state = STATE_DISPLAY_NETWORK_INFORMATION; break; } case STATE_DISPLAY_NETWORK_INFORMATION: { printf("~ni~"); /* On network, display info about this network */ getNetworkConfigurationParameters(); getDeviceInformation(); state = STATE_SEND_INFO_MESSAGE; break; } case STATE_SEND_INFO_MESSAGE: { printf("~im~"); setLed(1); im.header->sequence = sequenceNumber++; im.cause = infoMessageCause; unsigned char* panid = getDeviceInformationProperty(DIP_PANID); im.parameters[0] = CONVERT_TO_INT(*panid, *(panid+1)); //PAN ID im.parameters[1] = getVcc3(); //VCC im.parameters[2] = getLightSensor(); //Light Sensor printInfoMessage(&im); #define ZNP_RESTART_DELAY_IF_MESSAGE_FAIL_MS 5000 unsigned char msgBuf[100]; serializeInfoMessage(&im, msgBuf); afSendData(DEFAULT_ENDPOINT,DEFAULT_ENDPOINT,0, INFO_MESSAGE_CLUSTER, msgBuf, getSizeOfInfoMessage(&im)); if (znpResult != ZNP_SUCCESS) { printf("afSendData error %i; restarting...\r\n", znpResult); delayMs(ZNP_RESTART_DELAY_IF_MESSAGE_FAIL_MS); //allow enough time for coordinator to fully restart, if that caused our problem state = STATE_ZNP_STARTUP; } else { state = STATE_IDLE; clearLeds(); HAL_SLEEP(); } break; } default: //should never happen { printf("UNKNOWN STATE\r\n"); state = STATE_ZNP_STARTUP; } break; } } }