From ca38d81b22e82de5010e8b66dfdb3765644dc3d9 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Fri, 19 Jun 2020 20:54:37 +0200 Subject: [PATCH] EZSP milestone 2 --- tasmota/i18n.h | 3 + tasmota/xdrv_23_zigbee_0_constants.ino | 503 +++++++++++++ tasmota/xdrv_23_zigbee_7_statemachine.ino | 114 ++- tasmota/xdrv_23_zigbee_8_parsers.ino | 84 +++ tasmota/xdrv_23_zigbee_9_serial.ino | 696 ++++++++++++++++++ ...e_9_impl.ino => xdrv_23_zigbee_A_impl.ino} | 515 +------------ 6 files changed, 1399 insertions(+), 516 deletions(-) create mode 100644 tasmota/xdrv_23_zigbee_9_serial.ino rename tasmota/{xdrv_23_zigbee_9_impl.ino => xdrv_23_zigbee_A_impl.ino} (70%) diff --git a/tasmota/i18n.h b/tasmota/i18n.h index ccbb6b399..b3537b59c 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -506,13 +506,16 @@ #define D_JSON_ZIGBEE_CC2530 "CC2530" #define D_CMND_ZIGBEEZNPRECEIVE "ZNPReceive" // only for debug #define D_CMND_ZIGBEE_EZSP_RECEIVE "EZSPReceive" // only for debug +#define D_CMND_ZIGBEE_EZSP_RECEIVE_RAW "EZSPReceiveRaw" // only for debug #define D_CMND_ZIGBEEZNPSEND "ZNPSend" #define D_CMND_ZIGBEE_EZSP_SEND "EZSPSend" +#define D_CMND_ZIGBEE_EZSP_SEND_RAW "EZSPSendRaw" #define D_JSON_ZIGBEE_STATE "ZbState" #define D_JSON_ZIGBEEZNPRECEIVED "ZbZNPReceived" #define D_JSON_ZIGBEE_EZSP_RECEIVED "ZbEZSPReceived" #define D_JSON_ZIGBEEZNPSENT "ZbZNPSent" #define D_JSON_ZIGBEE_EZSP_SENT "ZbEZSPSent" + #define D_JSON_ZIGBEE_EZSP_SENT_RAW "ZbEZSPSentRaw" #define D_JSON_ZIGBEEZCL_RECEIVED "ZbZCLReceived" #define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZbZCLRawReceived" #define D_JSON_ZIGBEE_DEVICE "Device" diff --git a/tasmota/xdrv_23_zigbee_0_constants.ino b/tasmota/xdrv_23_zigbee_0_constants.ino index 92e88c708..467f70e35 100644 --- a/tasmota/xdrv_23_zigbee_0_constants.ino +++ b/tasmota/xdrv_23_zigbee_0_constants.ino @@ -55,6 +55,509 @@ enum ZnpSubsystem { }; #endif // USE_ZIGBEE_ZNP +#ifdef USE_ZIGBEE_EZSP + +enum EZSPCondigId { + EZSP_CONFIG_PACKET_BUFFER_COUNT = 0x01, + EZSP_CONFIG_NEIGHBOR_TABLE_SIZE = 0x02, + EZSP_CONFIG_APS_UNICAST_MESSAGE_COUNT = 0x03, + EZSP_CONFIG_BINDING_TABLE_SIZE = 0x04, + EZSP_CONFIG_ADDRESS_TABLE_SIZE = 0x05, + EZSP_CONFIG_MULTICAST_TABLE_SIZE = 0x06, + EZSP_CONFIG_ROUTE_TABLE_SIZE = 0x07, + EZSP_CONFIG_DISCOVERY_TABLE_SIZE = 0x08, + EZSP_CONFIG_STACK_PROFILE = 0x0C, + EZSP_CONFIG_SECURITY_LEVEL = 0x0D, + EZSP_CONFIG_MAX_HOPS = 0x10, + EZSP_CONFIG_MAX_END_DEVICE_CHILDREN = 0x11, + EZSP_CONFIG_INDIRECT_TRANSMISSION_TIMEOUT = 0x12, + EZSP_CONFIG_END_DEVICE_POLL_TIMEOUT = 0x13, + EZSP_CONFIG_TX_POWER_MODE = 0x17, + EZSP_CONFIG_DISABLE_RELAY = 0x18, + EZSP_CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE = 0x19, + EZSP_CONFIG_SOURCE_ROUTE_TABLE_SIZE = 0x1A, + EZSP_CONFIG_FRAGMENT_WINDOW_SIZE = 0x1C, + EZSP_CONFIG_FRAGMENT_DELAY_MS = 0x1D, + EZSP_CONFIG_KEY_TABLE_SIZE = 0x1E, + EZSP_CONFIG_APS_ACK_TIMEOUT = 0x1F, + EZSP_CONFIG_BEACON_JITTER_DURATION = 0x20, + EZSP_CONFIG_END_DEVICE_BIND_TIMEOUT = 0x21, + EZSP_CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD = 0x22, + EZSP_CONFIG_REQUEST_KEY_TIMEOUT = 0x24, + EZSP_CONFIG_CERTIFICATE_TABLE_SIZE = 0x29, + EZSP_CONFIG_APPLICATION_ZDO_FLAGS = 0x2A, + EZSP_CONFIG_BROADCAST_TABLE_SIZE = 0x2B, + EZSP_CONFIG_MAC_FILTER_TABLE_SIZE = 0x2C, + EZSP_CONFIG_SUPPORTED_NETWORKS = 0x2D, + EZSP_CONFIG_SEND_MULTICASTS_TO_SLEEPY_ADDRESS = 0x2E, + EZSP_CONFIG_ZLL_GROUP_ADDRESSES = 0x2F, + EZSP_CONFIG_ZLL_RSSI_THRESHOLD = 0x30, + EZSP_CONFIG_MTORR_FLOW_CONTROL = 0x33, + EZSP_CONFIG_RETRY_QUEUE_SIZE = 0x34, + EZSP_CONFIG_NEW_BROADCAST_ENTRY_THRESHOLD = 0x35, + EZSP_CONFIG_BROADCAST_MIN_ACKS_NEEDED = 0x37, + EZSP_CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S = 0x38, + EZSP_CONFIG_CTUNE_VALUE = 0x39 +}; + +enum EZSPValueId { + EZSP_VALUE_TOKEN_STACK_NODE_DATA = 0x00, + EZSP_VALUE_MAC_PASSTHROUGH_FLAGS = 0x01, + EZSP_VALUE_EMBERNET_PASSTHROUGH_SOURCE_ADDRESS = 0x02, + EZSP_VALUE_FREE_BUFFERS = 0x03, + EZSP_VALUE_UART_SYNCH_CALLBACKS = 0x04, + EZSP_VALUE_MAXIMUM_INCOMING_TRANSFER_SIZE = 0x05, + EZSP_VALUE_MAXIMUM_OUTGOING_TRANSFER_SIZE = 0x06, + EZSP_VALUE_STACK_TOKEN_WRITING = 0x07, + EZSP_VALUE_STACK_IS_PERFORMING_REJOIN = 0x08, + EZSP_VALUE_MAC_FILTER_LIST = 0x09, + EZSP_VALUE_EXTENDED_SECURITY_BITMASK = 0x0A, + EZSP_VALUE_NODE_SHORT_ID = 0x0B, + EZSP_VALUE_DESCRIPTOR_CAPABILITY = 0x0C, + EZSP_VALUE_STACK_DEVICE_REQUEST_SEQUENCE_NUMBER = 0x0D, + EZSP_VALUE_RADIO_HOLD_OFF = 0x0E, + EZSP_VALUE_ENDPOINT_FLAGS = 0x0F, + EZSP_VALUE_MFG_SECURITY_CONFIG = 0x10, + EZSP_VALUE_VERSION_INFO = 0x11, + EZSP_VALUE_NEXT_HOST_REJOIN_REASON = 0x12, + EZSP_VALUE_LAST_REJOIN_REASON = 0x13, + EZSP_VALUE_NEXT_ZIGBEE_SEQUENCE_NUMBER = 0x14, + EZSP_VALUE_CCA_THRESHOLD = 0x15, + EZSP_VALUE_SET_COUNTER_THRESHOLD = 0x17, + EZSP_VALUE_RESET_COUNTER_THRESHOLDS = 0x18, + EZSP_VALUE_CLEAR_COUNTERS = 0x19, + EZSP_VALUE_CERTIFICATE_283K1 = 0x1A, + EZSP_VALUE_PUBLIC_KEY_283K1 = 0x1B, + EZSP_VALUE_PRIVATE_KEY_283K1 = 0x1C, + EZSP_VALUE_NWK_FRAME_COUNTER = 0x23, + EZSP_VALUE_APS_FRAME_COUNTER = 0x24, + EZSP_VALUE_RETRY_DEVICE_TYPE = 0x25, + EZSP_VALUE_ENABLE_R21_BEHAVIOR = 0x29, + EZSP_VALUE_ANTENNA_MODE = 0x30, + EZSP_VALUE_ENABLE_PTA = 0x31, + EZSP_VALUE_PTA_OPTIONS = 0x32, + EZSP_VALUE_MFGLIB_OPTIONS = 0x33, + EZSP_VALUE_USE_NEGOTIATED_POWER_BY_LPD = 0x34, + EZSP_VALUE_PTA_PWM_OPTIONS = 0x35, + EZSP_VALUE_PTA_DIRECTIONAL_PRIORITY_PULSE_WIDTH = 0x36, + EZSP_VALUE_PTA_PHY_SELECT_TIMEOUT = 0x37, + EZSP_VALUE_ANTENNA_RX_MODE = 0x38, + EZSP_VALUE_NWK_KEY_TIMEOUT = 0x39, + EZSP_VALUE_FORCE_TX_AFTER_FAILED_CCA_ATTEMPTS = 0x3A, + EZSP_VALUE_TRANSIENT_KEY_TIMEOUT_S = 0x3B, + ZSP_VALUE_COULOMB_COUNTER_USAGE = 0x3C, + EZSP_VALUE_MAX_BEACONS_TO_STORE = 0x3D, + EZSP_VALUE_END_DEVICE_TIMEOUT_OPTIONS_MASK = 0x3E, + EZSP_VALUE_END_DEVICE_KEEP_ALIVE_SUPPORT_MODE = 0x3F, + EZSP_VALUE_GPIO_RADIO_POWER_MASK = 0x40, + EZSP_VALUE_ACTIVE_RADIO_CONFIG = 0x41 +}; + +enum EZSPEmberStatusId { + EMBER_SUCCESS = 0x00, + EMBER_ERR_FATAL = 0x01, + EMBER_BAD_ARGUMENT = 0x02, + EMBER_EEPROM_MFG_STACK_VERSION_MISMATCH = 0x04, + EMBER_INCOMPATIBLE_STATIC_MEMORY_DEFINITIONS = 0x05, + EMBER_EEPROM_MFG_VERSION_MISMATCH = 0x06, + EMBER_EEPROM_STACK_VERSION_MISMATCH = 0x07, + EMBER_NO_BUFFERS = 0x18, + EMBER_SERIAL_INVALID_BAUD_RATE = 0x20, + EMBER_SERIAL_INVALID_PORT = 0x21, + EMBER_SERIAL_TX_OVERFLOW = 0x22, + EMBER_SERIAL_RX_OVERFLOW = 0x23, + EMBER_SERIAL_RX_FRAME_ERROR = 0x24, + EMBER_SERIAL_RX_PARITY_ERROR = 0x25, + EMBER_SERIAL_RX_EMPTY = 0x26, + EMBER_SERIAL_RX_OVERRUN_ERROR = 0x27, + EMBER_MAC_TRANSMIT_QUEUE_FULL = 0x39, + EMBER_MAC_UNKNOWN_HEADER_TYPE = 0x3A, + EMBER_MAC_SCANNING = 0x3D, + EMBER_MAC_NO_DATA = 0x31, + EMBER_MAC_JOINED_NETWORK = 0x32, + EMBER_MAC_BAD_SCAN_DURATION = 0x33, + EMBER_MAC_INCORRECT_SCAN_TYPE = 0x34, + EMBER_MAC_INVALID_CHANNEL_MASK = 0x35, + EMBER_MAC_COMMAND_TRANSMIT_FAILURE = 0x36, + EMBER_MAC_NO_ACK_RECEIVED = 0x40, + EMBER_MAC_INDIRECT_TIMEOUT = 0x42, + EMBER_SIM_EEPROM_ERASE_PAGE_GREEN = 0x43, + EMBER_SIM_EEPROM_ERASE_PAGE_RED = 0x44, + EMBER_SIM_EEPROM_FULL = 0x45, + EMBER_ERR_FLASH_WRITE_INHIBITED = 0x46, + EMBER_ERR_FLASH_VERIFY_FAILED = 0x47, + EMBER_SIM_EEPROM_INIT_1_FAILED = 0x48, + EMBER_SIM_EEPROM_INIT_2_FAILED = 0x49, + EMBER_SIM_EEPROM_INIT_3_FAILED = 0x4A, + EMBER_ERR_FLASH_PROG_FAIL = 0x4B, + EMBER_ERR_FLASH_ERASE_FAIL = 0x4C, + EMBER_ERR_BOOTLOADER_TRAP_TABLE_BAD = 0x58, + EMBER_ERR_BOOTLOADER_TRAP_UNKNOWN = 0x59, + EMBER_ERR_BOOTLOADER_NO_IMAGE = 0x5A, + EMBER_DELIVERY_FAILED = 0x66, + EMBER_BINDING_INDEX_OUT_OF_RANGE = 0x69, + EMBER_ADDRESS_TABLE_INDEX_OUT_OF_RANGE = 0x6A, + EMBER_INVALID_BINDING_INDEX = 0x6C, + EMBER_INVALID_CALL = 0x70, + EMBER_COST_NOT_KNOWN = 0x71, + EMBER_MAX_MESSAGE_LIMIT_REACHED = 0x72, + EMBER_MESSAGE_TOO_LONG = 0x74, + EMBER_BINDING_IS_ACTIVE = 0x75, + EMBER_ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x76, + EMBER_ADC_CONVERSION_DONE = 0x80, + EMBER_ADC_CONVERSION_BUSY = 0x81, + EMBER_ADC_CONVERSION_DEFERRED = 0x82, + EMBER_ADC_NO_CONVERSION_PENDING = 0x84, + EMBER_SLEEP_INTERRUPTED = 0x85, + EMBER_PHY_TX_UNDERFLOW = 0x88, + EMBER_PHY_TX_INCOMPLETE = 0x89, + EMBER_PHY_INVALID_CHANNEL = 0x8A, + EMBER_PHY_INVALID_POWER = 0x8B, + EMBER_PHY_TX_BUSY = 0x8C, + EMBER_PHY_TX_CCA_FAIL = 0x8D, + EMBER_PHY_OSCILLATOR_CHECK_FAILED = 0x8E, + EMBER_PHY_ACK_RECEIVED = 0x8F, + EMBER_NETWORK_UP = 0x90, + EMBER_NETWORK_DOWN = 0x91, + EMBER_JOIN_FAILED = 0x94, + EMBER_MOVE_FAILED = 0x96, + EMBER_CANNOT_JOIN_AS_ROUTER = 0x98, + EMBER_NODE_ID_CHANGED = 0x99, + EMBER_PAN_ID_CHANGED = 0x9A, + EMBER_NO_BEACONS = 0xAB, + EMBER_RECEIVED_KEY_IN_THE_CLEAR = 0xAC, + EMBER_NO_NETWORK_KEY_RECEIVED = 0xAD, + EMBER_NO_LINK_KEY_RECEIVED = 0xAE, + EMBER_PRECONFIGURED_KEY_REQUIRED = 0xAF, + EMBER_NOT_JOINED = 0x93, + EMBER_INVALID_SECURITY_LEVEL = 0x95, + EMBER_NETWORK_BUSY = 0xA1, + EMBER_INVALID_ENDPOINT = 0xA3, + EMBER_BINDING_HAS_CHANGED = 0xA4, + EMBER_INSUFFICIENT_RANDOM_DATA = 0xA5, + EMBER_APS_ENCRYPTION_ERROR = 0xA6, + EMBER_SECURITY_STATE_NOT_SET = 0xA8, + EMBER_KEY_TABLE_INVALID_ADDRESS = 0xB3, + EMBER_SECURITY_CONFIGURATION_INVALID = 0xB7, + EMBER_TOO_SOON_FOR_SWITCH_KEY = 0xB8, + EMBER_KEY_NOT_AUTHORIZED = 0xBB, + EMBER_SECURITY_DATA_INVALID = 0xBD, + EMBER_SOURCE_ROUTE_FAILURE = 0xA9, + EMBER_MANY_TO_ONE_ROUTE_FAILURE = 0xAA, + EMBER_STACK_AND_HARDWARE_MISMATCH = 0xB0, + EMBER_INDEX_OUT_OF_RANGE = 0xB1, + EMBER_TABLE_FULL = 0xB4, + EMBER_TABLE_ENTRY_ERASED = 0xB6, + EMBER_LIBRARY_NOT_PRESENT = 0xB5, + EMBER_OPERATION_IN_PROGRESS = 0xBA, +}; + +enum EZSPStatusId { + EZSP_SUCCESS = 0x00, + EZSP_SPI_ERR_FATAL = 0x10, + EZSP_SPI_ERR_NCP_RESET = 0x11, + EZSP_SPI_ERR_OVERSIZED_EZSP_FRAME = 0x12, + EZSP_SPI_ERR_ABORTED_TRANSACTION = 0x13, + EZSP_SPI_ERR_MISSING_FRAME_TERMINATOR = 0x14, + EZSP_SPI_ERR_WAIT_SECTION_TIMEOUT = 0x15, + EZSP_SPI_ERR_NO_FRAME_TERMINATOR = 0x16, + EZSP_SPI_ERR_EZSP_COMMAND_OVERSIZED = 0x17, + EZSP_SPI_ERR_EZSP_RESPONSE_OVERSIZED = 0x18, + EZSP_SPI_WAITING_FOR_RESPONSE = 0x19, + EZSP_SPI_ERR_HANDSHAKE_TIMEOUT = 0x1A, + EZSP_SPI_ERR_STARTUP_TIMEOUT = 0x1B, + EZSP_SPI_ERR_STARTUP_FAIL = 0x1C, + EZSP_SPI_ERR_UNSUPPORTED_SPI_COMMAND = 0x1D, + EZSP_ASH_IN_PROGRESS = 0x20, + EZSP_HOST_FATAL_ERROR = 0x21, + EZSP_ASH_NCP_FATAL_ERROR = 0x22, + EZSP_DATA_FRAME_TOO_LONG = 0x23, + EZSP_DATA_FRAME_TOO_SHORT = 0x24, + EZSP_NO_TX_SPACE = 0x25, + EZSP_NO_RX_SPACE = 0x26, + EZSP_NO_RX_DATA = 0x27, + EZSP_NOT_CONNECTED = 0x28, + EZSP_ERROR_VERSION_NOT_SET = 0x30, + EZSP_ERROR_INVALID_FRAME_ID = 0x31, + EZSP_ERROR_WRONG_DIRECTION = 0x32, + EZSP_ERROR_TRUNCATED = 0x33, + EZSP_ERROR_OVERFLOW = 0x34, + EZSP_ERROR_OUT_OF_MEMORY = 0x35, + EZSP_ERROR_INVALID_VALUE = 0x36, + EZSP_ERROR_INVALID_ID = 0x37, + EZSP_ERROR_INVALID_CALL = 0x38, + EZSP_ERROR_NO_RESPONSE = 0x39, + EZSP_ERROR_COMMAND_TOO_LONG = 0x40, + EZSP_ERROR_QUEUE_FULL = 0x41, + EZSP_ERROR_COMMAND_FILTERED = 0x42, + EZSP_ERROR_SECURITY_KEY_ALREADY_SET = 0x43, + EZSP_ERROR_SECURITY_TYPE_INVALID = 0x44, + EZSP_ERROR_SECURITY_PARAMETERS_INVALID = 0x45, + EZSP_ERROR_SECURITY_PARAMETERS_ALREADY_SET = 0x46, + EZSP_ERROR_SECURITY_KEY_NOT_SET = 0x47, + EZSP_ERROR_SECURITY_PARAMETERS_NOT_SET = 0x48, + EZSP_ERROR_UNSUPPORTED_CONTROL = 0x49, + EZSP_ERROR_UNSECURE_FRAME = 0x4A, + EZSP_NO_ERROR = 0xFF +}; + +enum EZSP_Commands { + EZSP_version = 0x0000, + EZSP_getLibraryStatus = 0x0001, + EZSP_addEndpoint = 0x0002, + EZSP_getExtendedValue = 0x0003, + EZSP_getNextBeacon = 0x0004, + EZSP_nop = 0x0005, + EZSP_callback = 0x0006, + EZSP_noCallbacks = 0x0007, + EZSP_getNumStoredBeacons = 0x0008, + EZSP_setToken = 0x0009, + EZSP_getToken = 0x000A, + EZSP_getMfgToken = 0x000B, + EZSP_setMfgToken = 0x000C, + EZSP_stackTokenChangedHandler = 0x000D, + EZSP_setTimer = 0x000E, + EZSP_timerHandler = 0x000F, + EZSP_setConcentrator = 0x0010, + EZSP_setBrokenRouteErrorCode = 0x0011, + EZSP_debugWrite = 0x0012, + EZSP_getXncpInfo = 0x0013, + EZSP_requestLinkKey = 0x0014, + EZSP_setManufacturerCode = 0x0015, + EZSP_setPowerDescriptor = 0x0016, + EZSP_networkInit = 0x0017, + EZSP_networkState = 0x0018, + EZSP_stackStatusHandler = 0x0019, + EZSP_startScan = 0x001A, + EZSP_networkFoundHandler = 0x001B, + EZSP_scanCompleteHandler = 0x001C, + EZSP_stopScan = 0x001D, + EZSP_formNetwork = 0x001E, + EZSP_joinNetwork = 0x001F, + EZSP_leaveNetwork = 0x0020, + EZSP_findAndRejoinNetwork = 0x0021, + EZSP_permitJoining = 0x0022, + EZSP_childJoinHandler = 0x0023, + EZSP_trustCenterJoinHandler = 0x0024, + EZSP_zllClearTokens = 0x0025, + EZSP_getEui64 = 0x0026, + EZSP_getNodeId = 0x0027, + EZSP_getNetworkParameters = 0x0028, + EZSP_getParentChildParameters = 0x0029, + EZSP_clearBindingTable = 0x002A, + EZSP_setBinding = 0x002B, + EZSP_getBinding = 0x002C, + EZSP_deleteBinding = 0x002D, + EZSP_bindingIsActive = 0x002E, + EZSP_getBindingRemoteNodeId = 0x002F, + EZSP_setBindingRemoteNodeId = 0x0030, + EZSP_remoteSetBindingHandler = 0x0031, + EZSP_remoteDeleteBindingHandler = 0x0032, + EZSP_maximumPayloadLength = 0x0033, + EZSP_sendUnicast = 0x0034, + EZSP_getDutyCycleState = 0x0035, + EZSP_sendBroadcast = 0x0036, + EZSP_proxyBroadcast = 0x0037, + EZSP_sendMulticast = 0x0038, + EZSP_sendReply = 0x0039, + EZSP_sendMulticastWithAlias = 0x003A, + EZSP_joinNetworkDirectly = 0x003B, + EZSP_clearStoredBeacons = 0x003C, + EZSP_getFirstBeacon = 0x003D, + EZSP_getNeighborFrameCounter = 0x003E, + EZSP_messageSentHandler = 0x003F, + EZSP_setDutyCycleLimitsInStack = 0x0040, + EZSP_sendManyToOneRouteRequest = 0x0041, + EZSP_pollForData = 0x0042, + EZSP_pollCompleteHandler = 0x0043, + EZSP_pollHandler = 0x0044, + EZSP_incomingMessageHandler = 0x0045, + EZSP_macFilterMatchMessageHandler = 0x0046, + EZSP_customFrame = 0x0047, + EZSP_energyScanResultHandler = 0x0048, + EZSP_getRandomNumber = 0x0049, + EZSP_getChildData = 0x004A, + EZSP_getDutyCycleLimits = 0x004B, + EZSP_getCurrentDutyCycle = 0x004C, + EZSP_dutyCycleHandler = 0x004D, + EZSP_getTimer = 0x004E, + EZSP_getTrueRandomEntropySource = 0x004F, + EZSP_unicastCurrentNetworkKey = 0x0050, + EZSP_sendRawMessageExtended = 0x0051, + EZSP_getConfigurationValue = 0x0052, + EZSP_setConfigurationValue = 0x0053, + EZSP_customFrameHandler = 0x0054, + EZSP_setPolicy = 0x0055, + EZSP_getPolicy = 0x0056, + EZSP_invalidCommand = 0x0058, + EZSP_setSourceRouteDiscoveryMode = 0x005A, + EZSP_addressTableEntryIsActive = 0x005B, + EZSP_setAddressTableRemoteEui64 = 0x005C, + EZSP_setAddressTableRemoteNodeId = 0x005D, + EZSP_getAddressTableRemoteEui64 = 0x005E, + EZSP_getAddressTableRemoteNodeId = 0x005F, + EZSP_lookupNodeIdByEui64 = 0x0060, + EZSP_lookupEui64ByNodeId = 0x0061, + EZSP_incomingSenderEui64Handler = 0x0062, + EZSP_getMulticastTableEntry = 0x0063, + EZSP_setMulticastTableEntry = 0x0064, + EZSP_readAndClearCounters = 0x0065, + EZSP_addOrUpdateKeyTableEntry = 0x0066, + EZSP_sendTrustCenterLinkKey = 0x0067, + EZSP_setInitialSecurityState = 0x0068, + EZSP_getCurrentSecurityState = 0x0069, + EZSP_getKey = 0x006A, + EZSP_clearTransientLinkKeys = 0x006B, + EZSP_updateTcLinkKey = 0x006C, + EZSP_getTransientKeyTableEntry = 0x006D, + EZSP_switchNetworkKeyHandler = 0x006E, + EZSP_aesMmoHash = 0x006F, + EZSP_gpSinkTableInit = 0x0070, + EZSP_getKeyTableEntry = 0x0071, + EZSP_setKeyTableEntry = 0x0072, + EZSP_broadcastNextNetworkKey = 0x0073, + EZSP_broadcastNetworkKeySwitch = 0x0074, + EZSP_findKeyTableEntry = 0x0075, + EZSP_eraseKeyTableEntry = 0x0076, + EZSP_becomeTrustCenter = 0x0077, + EZSP_dsaVerifyHandler = 0x0078, + EZSP_getNeighbor = 0x0079, + EZSP_neighborCount = 0x007A, + EZSP_getRouteTableEntry = 0x007B, + EZSP_idConflictHandler = 0x007C, + EZSP_incomingManyToOneRouteRequestHandler = 0x007D, + EZSP_setExtendedTimeout = 0x007E, + EZSP_getExtendedTimeout = 0x007F, + EZSP_incomingRouteErrorHandler = 0x0080, + EZSP_echo = 0x0081, + EZSP_replaceAddressTableEntry = 0x0082, + EZSP_mfglibStart = 0x0083, + EZSP_mfglibEnd = 0x0084, + EZSP_mfglibStartTone = 0x0085, + EZSP_mfglibStopTone = 0x0086, + EZSP_mfglibStartStream = 0x0087, + EZSP_mfglibStopStream = 0x0088, + EZSP_mfglibSendPacket = 0x0089, + EZSP_mfglibSetChannel = 0x008A, + EZSP_mfglibGetChannel = 0x008B, + EZSP_mfglibSetPower = 0x008C, + EZSP_mfglibGetPower = 0x008D, + EZSP_mfglibRxHandler = 0x008E, + EZSP_launchStandaloneBootloader = 0x008F, + EZSP_sendBootloadMessage = 0x0090, + EZSP_getStandaloneBootloaderVersionPlatMicroPhy = 0x0091, + EZSP_incomingBootloadMessageHandler = 0x0092, + EZSP_bootloadTransmitCompleteHandler = 0x0093, + EZSP_aesEncrypt = 0x0094, + EZSP_overrideCurrentChannel = 0x0095, + EZSP_sendRawMessage = 0x0096, + EZSP_macPassthroughMessageHandler = 0x0097, + EZSP_rawTransmitCompleteHandler = 0x0098, + EZSP_setRadioPower = 0x0099, + EZSP_setRadioChannel = 0x009A, + EZSP_zigbeeKeyEstablishmentHandler = 0x009B, + EZSP_energyScanRequest = 0x009C, + EZSP_delayTest = 0x009D, + EZSP_generateCbkeKeysHandler = 0x009E, + EZSP_calculateSmacs = 0x009F, + EZSP_calculateSmacsHandler = 0x00A0, + EZSP_clearTemporaryDataMaybeStoreLinkKey = 0x00A1, + EZSP_setPreinstalledCbkeData = 0x00A2, + EZSP_dsaVerify = 0x00A3, + EZSP_generateCbkeKeys = 0x00A4, + EZSP_getCertificate = 0x00A5, + EZSP_dsaSign = 0x00A6, + EZSP_dsaSignHandler = 0x00A7, + EZSP_removeDevice = 0x00A8, + EZSP_unicastNwkKeyUpdate = 0x00A9, + EZSP_getValue = 0x00AA, + EZSP_setValue = 0x00AB, + EZSP_setGpioCurrentConfiguration = 0x00AC, + EZSP_setGpioPowerUpDownConfiguration = 0x00AD, + EZSP_setGpioRadioPowerMask = 0x00AE, + EZSP_addTransientLinkKey = 0x00AF, + EZSP_dsaVerify283k1 = 0x00B0, + EZSP_clearKeyTable = 0x00B1, + EZSP_zllNetworkOps = 0x00B2, + EZSP_zllSetInitialSecurityState = 0x00B3, + EZSP_zllStartScan = 0x00B4, + EZSP_zllSetRxOnWhenIdle = 0x00B5, + EZSP_zllNetworkFoundHandler = 0x00B6, + EZSP_zllScanCompleteHandler = 0x00B7, + EZSP_zllAddressAssignmentHandler = 0x00B8, + EZSP_setLogicalAndRadioChannel = 0x00B9, + EZSP_getLogicalChannel = 0x00BA, + EZSP_zllTouchLinkTargetHandler = 0x00BB, + EZSP_zllGetTokens = 0x00BC, + EZSP_zllSetDataToken = 0x00BD, + EZSP_isZllNetwork = 0x00BE, + EZSP_zllSetNonZllNetwork = 0x00BF, + EZSP_gpProxyTableLookup = 0x00C0, + EZSP_getSourceRouteTableEntry = 0x00C1, + EZSP_getSourceRouteTableFilledSize = 0x00C2, + EZSP_getSourceRouteTableTotalSize = 0x00C3, + EZSP_gpepIncomingMessageHandler = 0x00C5, + EZSP_dGpSend = 0x00C6, + EZSP_dGpSentHandler = 0x00C7, + EZSP_gpProxyTableGetEntry = 0x00C8, + EZSP_gpProxyTableProcessGpPairing = 0x00C9, + EZSP_setSecurityKey = 0x00CA, + EZSP_setSecurityParameters = 0x00CB, + EZSP_resetToFactoryDefaults = 0x00CC, + EZSP_getSecurityKeyStatus = 0x00CD, + EZSP_getTransientLinkKey = 0x00CE, + EZSP_zllSetSecurityStateWithoutKey = 0x00CF, + EZSP_setRoutingShortcutThreshold = 0x00D0, + EZSP_getRoutingShortcutThreshold = 0x00D1, + EZSP_unusedPanIdFoundHandler = 0x00D2, + EZSP_findUnusedPanId = 0x00D3, + EZSP_zllSetRadioIdleMode = 0x00D4, + EZSP_setZllNodeType = 0x00D5, + EZSP_setZllAdditionalState = 0x00D6, + EZSP_zllOperationInProgress = 0x00D7, + EZSP_zllRxOnWhenIdleGetActive = 0x00D8, + EZSP_getZllPrimaryChannelMask = 0x00D9, + EZSP_getZllSecondaryChannelMask = 0x00DA, + EZSP_setZllPrimaryChannelMask = 0x00DB, + EZSP_setZllSecondaryChannelMask = 0x00DC, + EZSP_gpSinkTableGetEntry = 0x00DD, + EZSP_gpSinkTableLookup = 0x00DE, + EZSP_gpSinkTableSetEntry = 0x00DF, + EZSP_gpSinkTableRemoveEntry = 0x00E0, + EZSP_gpSinkTableFindOrAllocateEntry = 0x00E1, + EZSP_gpSinkTableClearAll = 0x00E2, + EZSP_setLongUpTime = 0x00E3, + EZSP_setHubConnectivity = 0x00E4, + EZSP_isUpTimeLong = 0x00E5, + EZSP_isHubConnected = 0x00E6, + EZSP_setParentClassificationEnabled = 0x00E7, + EZSP_generateCbkeKeys283k1 = 0x00E8, + EZSP_generateCbkeKeysHandler283k1 = 0x00E9, + EZSP_calculateSmacs283k1 = 0x00EA, + EZSP_calculateSmacsHandler283k1 = 0x00EB, + EZSP_getCertificate283k1 = 0x00EC, + EZSP_savePreinstalledCbkeData283k1 = 0x00ED, + EZSP_clearTemporaryDataMaybeStoreLinkKey283k1 = 0x00EE, + EZSP_setBeaconClassificationParams = 0x00EF, + EZSP_getParentClassificationEnabled = 0x00F0, + EZSP_readCounters = 0x00F1, + EZSP_counterRolloverHandler = 0x00F2, + EZSP_getBeaconClassificationParams = 0x00F3, + EZSP_setMacPollFailureWaitTime = 0x00F4, + EZSP_sendLinkPowerDeltaRequest = 0x00F7, + EZSP_multiPhyStart = 0x00F8, + EZSP_multiPhyStop = 0x00F9, + EZSP_multiPhySetRadioPower = 0x00FA, + EZSP_multiPhySetRadioChannel = 0x00FB, + EZSP_getPhyInterfaceCount = 0x00FC, + EZSP_getRadioParameters = 0x00FD, + EZSP_writeNodeData = 0x00FE, + // Tasmota specifics + EZSP_rstAck = 0xFFFE, +}; + +#endif // USE_ZIGBEE_EZSP + // Commands in the SYS subsystem enum SysCommand { SYS_RESET = 0x00, diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index cbfe822ab..16896fd0c 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -622,17 +622,124 @@ void Z_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_pani } +// patterns for EZSP + +// wait for RSTACK, meaning the device booted +ZBM(ZBR_RSTACK, Z_B0(EZSP_rstAck), Z_B1(EZSP_rstAck)) // FEFF - internal code for RSTACK + +// call version() and ask for EZSP v8 +ZBM(ZBS_VERSION, EZSP_version, 0x00, 0x08) // 000008 +ZBM(ZBR_VERSION, EZSP_version, 0x00, 0x08, 0x02) // 00000802 - expect v8, proto v2 + +// general configuration +// inspired from bellows: https://github.com/zigpy/bellows/blob/dev/bellows/config/ezsp.py +ZBM(ZBS_SET_ADDR_TABLE, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_KEY_TABLE_SIZE, 0x04, 0x00) // 53001E0400 +ZBM(ZBS_SET_MCAST_TABLE, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_MULTICAST_TABLE_SIZE, 0x10, 0x00) // 5300061000 +ZBM(ZBS_SET_STK_PROF, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_STACK_PROFILE, 0x02, 0x00) // 53000C0200 +ZBM(ZBS_SET_SEC_LEVEL, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_SECURITY_LEVEL, 0x05, 0x00) // 53000D0500 +ZBM(ZBS_SET_MAX_DEVICES, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_MAX_END_DEVICE_CHILDREN, 0x18, 0x00) // 5300111800 +ZBM(ZBS_SET_INDIRECT_TMO, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 0x00, 0x1E) // 530012001E +ZBM(ZBS_SET_TC_CACHE, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 0x02, 0x00) // 5300190200 +ZBM(ZBS_SET_ROUTE_TBL, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_SOURCE_ROUTE_TABLE_SIZE, 0x10, 0x00) // 53001A1000 +ZBM(ZBS_SET_KEY_TBL, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_KEY_TABLE_SIZE, 0x04, 0x00) // 53001E0400 +ZBM(ZBS_SET_PANID_CNFLCT, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 0x02, 0x00)// 5300220200 +// TODO APP_RECEIVES_SUPPORTED_ZDO_REQUESTS +ZBM(ZBS_SET_ZDO_REQ, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_APPLICATION_ZDO_FLAGS, 0x03, 0x00) // 53002A0300 +ZBM(ZBS_SET_NETWORKS, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_SUPPORTED_NETWORKS, 0x01, 0x00) // 53002D0100 +ZBM(ZBS_SET_PACKET_BUF, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_PACKET_BUFFER_COUNT, 0xFF, 0x00) // 530001FF00 + +ZBM(ZBR_SET_OK, EZSP_setConfigurationValue, 0x00 /*high*/, 0x00 /*ok*/) // 530000 +ZBM(ZBR_SET_OK2, 0x00, 0x00 /*high*/, 0x00 /*ok*/) // 000000 - TODO why does setting EZSP_CONFIG_PACKET_BUFFER_COUNT has a different response? + +// Read some configuration values +ZBM(ZBS_GET_APS_UNI, EZSP_getConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_APS_UNICAST_MESSAGE_COUNT) // 520003 +ZBM(ZBR_GET_OK, EZSP_getConfigurationValue, 0x00 /*high*/, 0x00 /*ok*/) // 5200 - followed by the value + +// Add Endpoints +// ZBM(ZBS_ADD_ENDPOINT1, EZSP_addEndpoint, 0x00 /*high*/, 0x01 /*ep*/, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), +// 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, +// 0x0E /* inputClusterCount */, // actually all clusters will be received +// 0X00 /* outputClusterCount */, +// 0x00,0x00, 0x04,0x00, 0x05,0x00, 0x06,0x00, // 0x0000, 0x0004, 0x0005, 0x0006 +// 0x07,0x00, 0x08,0x00, 0x0A,0x00, 0x02,0x01, // 0x0007, 0x0008, 0x000A, 0X0102 +// 0x00,0x03, 0x00,0x04, 0x02,0x04, 0x03,0x04, // 0x0300, 0x0400, 0x0402, 0x0403 +// 0x05,0x04, 0x06,0x04, // 0x0405, 0x0406 +// ) +ZBM(ZBS_ADD_ENDPOINT1, EZSP_addEndpoint, 0x00 /*high*/, 0x01 /*ep*/, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, + 0x00 /* inputClusterCount */, // actually all clusters will be received + 0X00 /* outputClusterCount */ ) // 02000104010500000000 +ZBM(ZBS_ADD_ENDPOINTB, EZSP_addEndpoint, 0x00 /*high*/, 0x0B /*ep*/, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, + 0x00 /* inputClusterCount */, // actually all clusters will be received + 0X00 /* outputClusterCount */ ) // 02000B04010500000000 +ZBM(ZBR_ADD_ENDPOINT, EZSP_addEndpoint, 0x00 /*high*/, 0x00 /*ok*/) // 020000 + +// set concentrator false +ZBM(ZBS_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 /*high*/, 0x00 /*false*/, 0xF9,0xFF /*HIGH_RAM_CONCENTRATOR*/, + 0x58,0x02 /*minTime*/, 0x08,0x07 /*maxTime*/, 0x02 /*errThr*/, 0x05 /*failThr*/, 0x00 /*maxHops*/) // 100000F9FF58020807020500 +ZBM(ZBR_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 /*high*/, 0x00 /*ok*/) // 100000 + +//False, , 600, 1800, 2, 5, 0) + +const char kResetingDevice[] PROGMEM = D_LOG_ZIGBEE "resetting EZSP device"; +const char kAbort[] PROGMEM = "Abort"; +const char kZigbeeAbort[] PROGMEM = D_LOG_ZIGBEE "Abort"; + static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_LABEL(0) ZI_NOOP() ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) - // ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default) - // ZI_WAIT(10500) // wait for 10 seconds for Tasmota to stabilize + ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default) + ZI_WAIT(10500) // wait for 10 seconds for Tasmota to stabilize + + // Hardware reset + ZI_LOG(LOG_LEVEL_INFO, kResetingDevice) // Log Debug: resetting EZSP device + ZI_CALL(&Z_Reset_Device, 0) // LOW = reset + ZI_WAIT(100) // wait for .1 second + ZI_CALL(&Z_Reset_Device, 1) // HIGH = release reset + + // wait for device to start + ZI_WAIT_UNTIL(5000, ZBR_RSTACK) // wait for RSTACK message + + // Init device and probe version + ZI_SEND(ZBS_VERSION) ZI_WAIT_RECV(1000, ZBR_VERSION) // check EXT PAN ID + + // configure EFR32 + ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) // Address table size + ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_SEC_LEVEL) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_MAX_DEVICES) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_INDIRECT_TMO) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_TC_CACHE) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_ROUTE_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_KEY_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_PANID_CNFLCT) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_ZDO_REQ) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_NETWORKS) ZI_WAIT_RECV(500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(500, ZBR_SET_OK2) + + // read configuration + ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(500, ZBR_GET_OK, &Z_ReadAPSUnicastMessage) + + // add endpoint 0x01 and 0x0B + ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT) + ZI_SEND(ZBS_ADD_ENDPOINTB) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT) + + // set Concentrator + ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(500, ZBR_SET_CONCENTRATOR) ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) ZI_WAIT_FOREVER() ZI_GOTO(ZIGBEE_LABEL_READY) + + // Abort state machine, general error + ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort + ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, kAbort) + ZI_LOG(LOG_LEVEL_ERROR, kZigbeeAbort) + ZI_STOP(ZIGBEE_LABEL_ABORT) }; #endif // USE_ZIGBEE_EZSP @@ -799,6 +906,9 @@ void ZigbeeStateMachine_Run(void) { #ifdef USE_ZIGBEE_ZNP ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 /* len */); #endif // USE_ZIGBEE_ZNP +#ifdef USE_ZIGBEE_EZSP + ZigbeeEZSPSendCmd((uint8_t*) cur_ptr1, cur_d8 /* len */, true); // send cancel byte +#endif // USE_ZIGBEE_EZSP break; case ZGB_INSTR_WAIT_UNTIL: zigbee.recv_until = true; // and reuse ZGB_INSTR_WAIT_RECV diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 7e9129d64..d7e1ba565 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -19,6 +19,81 @@ #ifdef USE_ZIGBEE +#ifdef USE_ZIGBEE_EZSP +/*********************************************************************************************\ + * Parsers for incoming EZSP messages +\*********************************************************************************************/ + +// EZSP: received ASH RSTACK frame, indicating that the MCU finished boot +int32_t Z_EZSP_RSTACK(uint8_t reset_code) { + const char *reason_str; + + switch (reset_code) { + case 0x01: reason_str = PSTR("External"); break; + case 0x02: reason_str = PSTR("Power-on"); break; + case 0x03: reason_str = PSTR("Watchdog"); break; + case 0x06: reason_str = PSTR("Assert"); break; + case 0x09: reason_str = PSTR("Bootloader"); break; + case 0x0B: reason_str = PSTR("Software"); break; + case 0x00: + default: reason_str = PSTR("Unknown"); break; + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\"EFR32 booted\",\"RestartReason\":\"%s\"" + ",\"Code\":%d}}"), + ZIGBEE_STATUS_BOOT, reason_str, reset_code); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); +} + +// EZSP: received ASH ERROR frame, indicating that the MCU finished boot +int32_t Z_EZSP_ERROR(uint8_t error_code) { + const char *reason_str; + + switch (error_code) { + case 0x51: reason_str = PSTR("ACK timeout"); break; + default: reason_str = PSTR("Unknown"); break; + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\"Failed state\",\"Error\":\"%s\"" + ",\"Code\":%d}}"), + ZIGBEE_STATUS_ABORT, reason_str, error_code); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); +} + +/*********************************************************************************************\ + * Default resolver +\*********************************************************************************************/ + +int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { + // Default message handler for new messages + if (zigbee.init_phase) { + // if still during initialization phase, ignore any unexpected message + return -1; // ignore message + } else { + // TODO + // for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) { + // if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { + // (*Z_DispatchTable[i].func)(res, buf); + // } + // } + return -1; + } +} + +int32_t Z_ReadAPSUnicastMessage(int32_t res, class SBuffer &buf) { + // Called when receiving a response from getConfigurationValue + // Value is in bytes 2+3 + uint16_t value = buf.get16(2); + return res; +} + + +#endif // USE_ZIGBEE_EZSP + /*********************************************************************************************\ * Parsers for incoming ZNP messages \*********************************************************************************************/ @@ -597,6 +672,15 @@ void Z_SendAFInfoRequest(uint16_t shortaddr) { * Send specific EZS¨ messages \*********************************************************************************************/ +// +// Callback for loading Zigbee configuration from Flash, called by the state machine +// +int32_t Z_Reset_Device(uint8_t value) { + // TODO - GPIO is hardwired to GPIO4 + digitalWrite(4, value ? HIGH : LOW); + return 0; // continue +} + // // Send ZDO_IEEE_ADDR_REQ request to get IEEE long address // diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino new file mode 100644 index 000000000..5c492e007 --- /dev/null +++ b/tasmota/xdrv_23_zigbee_9_serial.ino @@ -0,0 +1,696 @@ +/* + xdrv_23_zigbee_9_serial.ino - zigbee: serial communication with MCU + + Copyright (C) 2020 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +#ifdef USE_ZIGBEE_ZNP +const uint32_t ZIGBEE_BUFFER_SIZE = 256; // Max ZNP frame is SOF+LEN+CMD1+CMD2+250+FCS = 255 +const uint8_t ZIGBEE_SOF = 0xFE; +const uint8_t ZIGBEE_SOF_ALT = 0xFF; +#endif // USE_ZIGBEE_ZNP + +#ifdef USE_ZIGBEE_EZSP +const uint32_t ZIGBEE_BUFFER_SIZE = 256; +const uint8_t ZIGBEE_EZSP_CANCEL = 0x1A; // cancel byte +const uint8_t ZIGBEE_EZSP_EOF = 0x7E; // end of frame +const uint8_t ZIGBEE_EZSP_ESCAPE = 0x7D; // escape byte + +class EZSP_Serial_t { +public: + uint8_t to_ack = 0; // 0..7, frame number of next id to send + uint8_t from_ack = 0; // 0..7, frame to ack + uint8_t ezsp_seq = 0; // 0..255, EZSP sequence number +}; + +EZSP_Serial_t EZSP_Serial; + +#endif // USE_ZIGBEE_EZSP + +#include +TasmotaSerial *ZigbeeSerial = nullptr; + +/********************************************************************************************/ +// +// Called at event loop, checks for incoming data from the CC2530 +// +void ZigbeeInputLoop(void) { + +#ifdef USE_ZIGBEE_ZNP + static uint32_t zigbee_polling_window = 0; // number of milliseconds since first byte + static uint8_t fcs = ZIGBEE_SOF; + static uint32_t zigbee_frame_len = 5; // minimal zigbee frame length, will be updated when buf[1] is read + // Receive only valid ZNP frames: + // 00 - SOF = 0xFE + // 01 - Length of Data Field - 0..250 + // 02 - CMD1 - first byte of command + // 03 - CMD2 - second byte of command + // 04..FD - Data Field + // FE (or last) - FCS Checksum + + while (ZigbeeSerial->available()) { + yield(); + uint8_t zigbee_in_byte = ZigbeeSerial->read(); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZbInput byte=%d len=%d"), zigbee_in_byte, zigbee_buffer->len()); + + if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized + zigbee_frame_len = 5; + fcs = ZIGBEE_SOF; + // there is a rare race condition when an interrupt occurs when receiving the first byte + // in this case the first bit (lsb) is missed and Tasmota receives 0xFF instead of 0xFE + // We forgive this mistake, and next bytes are automatically resynchronized + if (ZIGBEE_SOF_ALT == zigbee_in_byte) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte); + zigbee_in_byte = ZIGBEE_SOF; + } + } + + if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { + // waiting for SOF (Start Of Frame) byte, discard anything else + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte); + continue; // discard + } + + if (zigbee_buffer->len() < zigbee_frame_len) { + zigbee_buffer->add8(zigbee_in_byte); + zigbee_polling_window = millis(); // Wait for more data + fcs ^= zigbee_in_byte; + } + + if (zigbee_buffer->len() >= zigbee_frame_len) { + zigbee_polling_window = 0; // Publish now + break; + } + + // recalculate frame length + if (02 == zigbee_buffer->len()) { + // We just received the Lenght byte + uint8_t len_byte = zigbee_buffer->get8(1); + if (len_byte > 250) len_byte = 250; // ZNP spec says len is 250 max + + zigbee_frame_len = len_byte + 5; // SOF + LEN + CMD1 + CMD2 + FCS = 5 bytes overhead + } + } + + if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { + char hex_char[(zigbee_buffer->len() * 2) + 2]; + ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); + // buffer received, now check integrity + if (zigbee_buffer->len() != zigbee_frame_len) { + // Len is not correct, log and reject frame + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len); + } else if (0x00 != fcs) { + // FCS is wrong, packet is corrupt, log and reject frame + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs); + } else { + // frame is correct + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received correct frame %s"), hex_char); + + SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); // remove SOF, LEN and FCS + + ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); + } + // now process the message + ZigbeeProcessInput(znp_buffer); + } + zigbee_buffer->setLen(0); // empty buffer + } +#endif // USE_ZIGBEE_ZNP + +#ifdef USE_ZIGBEE_EZSP + static uint32_t zigbee_polling_window = 0; // number of milliseconds since first byte + static bool escape = false; // was the previous byte an escape? + bool frame_complete = false; // frame is ready and complete + // Receive only valid EZSP frames: + // 1A - Cancel - cancel all previous bytes + // 7D - Escape byte - following byte is escaped + // 7E - end of frame + + while (ZigbeeSerial->available()) { + yield(); + uint8_t zigbee_in_byte = ZigbeeSerial->read(); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: ZbInput byte=0x%02X len=%d"), zigbee_in_byte, zigbee_buffer->len()); + + // if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized + // escape = false; + // frame_complete = false; + // } + + if ((0x11 == zigbee_in_byte) || (0x13 == zigbee_in_byte)) { + continue; // ignore reserved bytes XON/XOFF + } + + if (ZIGBEE_EZSP_ESCAPE == zigbee_in_byte) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: Escape byte received")); + escape = true; + continue; + } + + if (ZIGBEE_EZSP_CANCEL == zigbee_in_byte) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: ZbInput byte=0x1A, cancel byte received, discarding %d bytes"), zigbee_buffer->len()); + zigbee_buffer->setLen(0); // empty buffer + escape = false; + frame_complete = false; + continue; // re-loop + } + + if (ZIGBEE_EZSP_EOF == zigbee_in_byte) { + // end of frame + frame_complete = true; + break; + } + + if (zigbee_buffer->len() < ZIGBEE_BUFFER_SIZE) { + if (escape) { + // invert bit 5 + zigbee_in_byte ^= 0x20; + escape = false; + } + + zigbee_buffer->add8(zigbee_in_byte); + zigbee_polling_window = millis(); // Wait for more data + } // adding bytes + } // while (ZigbeeSerial->available()) + + uint32_t frame_len = zigbee_buffer->len(); + if (frame_complete || (frame_len && (millis() > (zigbee_polling_window + ZIGBEE_POLLING)))) { + char hex_char[frame_len * 2 + 2]; + ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); + if ((frame_complete) && (frame_len >= 3)) { + // frame received and has at least 3 bytes (without EOF), checking CRC + // AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": received raw frame %s"), hex_char); + uint16_t crc = 0xFFFF; // frame CRC + // compute CRC + for (uint32_t i=0; iget8(i) << 8); + for (uint32_t i=0; i<8; i++) { + if (crc & 0x8000) { + crc = (crc << 1) ^ 0x1021; // polynom is x^16 + x^12 + x^5 + 1, CCITT standard + } else { + crc <<= 1; + } + } + } + + uint16_t crc_received = zigbee_buffer->get8(frame_len - 2) << 8 | zigbee_buffer->get8(frame_len - 1); + // remove 2 last bytes + + if (crc_received != crc) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": bad crc (received 0x%04X, computed 0x%04X) %s"), crc_received, crc, hex_char); + } else { + // copy buffer + SBuffer ezsp_buffer = zigbee_buffer->subBuffer(0, frame_len - 2); // CRC + + // CRC is correct, apply de-stuffing if DATA frame + if (0 == (ezsp_buffer.get8(0) & 0x80)) { + // DATA frame + uint8_t rand = 0x42; + for (uint32_t i=1; i> 1) ^ 0xB8; } + else { rand = (rand >> 1); } + } + } + + ToHex_P((unsigned char*)ezsp_buffer.getBuffer(), ezsp_buffer.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_EZSP_RECEIVED "2\":\"%s\"}"), hex_char); + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); // TODO move to LOG_LEVEL_DEBUG when stable + } + // now process the message + ZigbeeProcessInputRaw(ezsp_buffer); + } + } else { + // the buffer timed-out, print error and discard + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": time-out, discarding %s, %d"), hex_char); + } + zigbee_buffer->setLen(0); // empty buffer + escape = false; + frame_complete = false; + } + +#endif // USE_ZIGBEE_EZSP + +} + +/********************************************************************************************/ + +// Initialize internal structures +void ZigbeeInitSerial(void) +{ +// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem1 = %d"), ESP_getFreeHeap()); + zigbee.active = false; + if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX)) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX)); + // if seriallog_level is 0, we allow GPIO 13/15 to switch to Hardware Serial + ZigbeeSerial = new TasmotaSerial(Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX), seriallog_level ? 1 : 2, 0, 256); // set a receive buffer of 256 bytes + ZigbeeSerial->begin(115200); + if (ZigbeeSerial->hardwareSerial()) { + ClaimSerial(); + uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3; + zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer); + } else { +// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem2 = %d"), ESP_getFreeHeap()); + zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); +// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem3 = %d"), ESP_getFreeHeap()); + } + zigbee.active = true; + zigbee.init_phase = true; // start the state machine + zigbee.state_machine = true; // start the state machine + ZigbeeSerial->flush(); + } +// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem9 = %d"), ESP_getFreeHeap()); +} + +#ifdef USE_ZIGBEE_ZNP + +void ZigbeeZNPSend(const uint8_t *msg, size_t len) { + if ((len < 2) || (len > 252)) { + // abort, message cannot be less than 2 bytes for CMD1 and CMD2 + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len); + return; + } + uint8_t data_len = len - 2; // removing CMD1 and CMD2 + + if (ZigbeeSerial) { + uint8_t fcs = data_len; + + ZigbeeSerial->write(ZIGBEE_SOF); // 0xFE + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF); + ZigbeeSerial->write(data_len); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len); + for (uint32_t i = 0; i < len; i++) { + uint8_t b = pgm_read_byte(msg + i); + ZigbeeSerial->write(b); + fcs ^= b; + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b); + } + ZigbeeSerial->write(fcs); // finally send fcs checksum byte + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs); + } + // Now send a MQTT message to report the sent message + char hex_char[(len * 2) + 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"), + ToHex_P(msg, len, hex_char, sizeof(hex_char))); +} + +// +// Same code for `ZbZNPSend` and `ZbZNPReceive` +// building the complete message (intro, length) +// +void CmndZbZNPSendOrReceive(bool send) +{ + if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int32_t size = strlen(XdrvMailbox.data); + + SBuffer buf((size+1)/2); + + while (size > 1) { + char stemp[3]; + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, nullptr, 16); + buf.add8(code); + size -= 2; + codes += 2; + } + if (send) { + // Command was `ZbZNPSend` + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + } else { + // Command was `ZbZNPReceive` + ZigbeeProcessInput(buf); + } + } + ResponseCmndDone(); +} + +// For debug purposes only, simulates a message received +void CmndZbZNPReceive(void) +{ + CmndZbZNPSendOrReceive(false); +} + +void CmndZbZNPSend(void) +{ + CmndZbZNPSendOrReceive(true); +} + +#endif // USE_ZIGBEE_ZNP + +#ifdef USE_ZIGBEE_EZSP + +// internal function to output a byte, and escape it (stuffing) if needed +void ZigbeeEZSPSend_Out(uint8_t out_byte) { + switch (out_byte) { + case 0x7E: // Flag byte + case 0x11: // XON + case 0x13: // XOFF + case 0x18: // Substitute byte + case 0x1A: // Cancel byte + case 0x7D: // Escape byte + // case 0xFF: // special wake-up + ZigbeeSerial->write(ZIGBEE_EZSP_ESCAPE); // send Escape byte 0x7D + ZigbeeSerial->write(out_byte ^ 0x20); // send with bit 5 inverted + break; + default: + ZigbeeSerial->write(out_byte); // send unchanged + break; + } +} +// Send low-level EZSP frames +// +// The frame should contain the Control Byte and Data Field +// The frame shouldn't be escaped, nor randomized +// +// Before sending: +// - send Cancel byte (0x1A) if requested +// - randomize Data Field if DATA Frame +// - compute CRC16 +// - escape (stuff) reserved bytes +// - add EOF (0x7E) +// - send frame +// send_cancel: should we first send a EZSP_CANCEL (0x1A) before the message to clear any leftover +void ZigbeeEZSPSendRaw(const uint8_t *msg, size_t len, bool send_cancel) { + if ((len < 1) || (len > 252)) { + // abort, message cannot be less than 2 bytes for CMD1 and CMD2 + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEE_EZSP_SENT ": bad message len %d"), len); + return; + } + uint8_t data_len = len - 2; // removing CMD1 and CMD2 + + if (ZigbeeSerial) { + if (send_cancel) { + ZigbeeSerial->write(ZIGBEE_EZSP_CANCEL); // 0x1A + } + + bool data_frame = (0 == (msg[0] & 0x80)); + uint8_t rand = 0x42; // pseudo-randomizer initial value + uint16_t crc = 0xFFFF; // CRC16 CCITT initialization + + for (uint32_t i=0; i 0)) { + out_byte ^= rand; + if (rand & 1) { rand = (rand >> 1) ^ 0xB8; } + else { rand = (rand >> 1); } + } + + // compute CRC + crc = crc ^ ((uint16_t)out_byte << 8); + for (uint32_t i=0; i<8; i++) { + if (crc & 0x8000) { + crc = (crc << 1) ^ 0x1021; // polynom is x^16 + x^12 + x^5 + 1, CCITT standard + } else { + crc <<= 1; + } + } + + // output byte + ZigbeeEZSPSend_Out(out_byte); + } + // send CRC16 in big-endian + ZigbeeEZSPSend_Out(crc >> 8); + ZigbeeEZSPSend_Out(crc & 0xFF); + + // finally send End of Frame + ZigbeeSerial->write(ZIGBEE_EZSP_EOF); // 0x1A + } + // Now send a MQTT message to report the sent message + char hex_char[(len * 2) + 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEE_EZSP_SENT_RAW " %s"), + ToHex_P(msg, len, hex_char, sizeof(hex_char))); +} + +// Send an EZSP command and data +// Ex: Version with min v8 = 000008 +void ZigbeeEZSPSendCmd(const uint8_t *msg, size_t len, bool send_cancel) { + char hex_char[len*2 + 2]; + ToHex_P(msg, len, hex_char, sizeof(hex_char)); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbEZSPSend %s"), hex_char); + + SBuffer cmd(len+3); // prefix with seq number (1 byte) and frame control bytes (2 bytes) + + cmd.add8(EZSP_Serial.ezsp_seq++); + cmd.add8(0x00); // Low byte of Frame Control + cmd.add8(0x01); // High byte of Frame Control, frameFormatVersion = 1 + cmd.addBuffer(msg, len); + + // send + ZigbeeEZSPSendDATA(cmd.getBuffer(), cmd.len(), send_cancel); +} + +// Send an EZSP DATA frame, automatically calculating the correct frame numbers +void ZigbeeEZSPSendDATA(const uint8_t *msg, size_t len, bool send_cancel) { + uint8_t control_byte = ((EZSP_Serial.to_ack & 0x07) << 4) + (EZSP_Serial.from_ack & 0x07); + // increment to_ack + EZSP_Serial.to_ack = (EZSP_Serial.to_ack + 1) & 0x07; + // build complete frame + SBuffer buf(len+1); + buf.add8(control_byte); + buf.addBuffer(msg, len); + // send + ZigbeeEZSPSendRaw(buf.getBuffer(), buf.len(), send_cancel); +} + +// Receive a high-level EZSP command/response, starting with 16-bits frame ID +int32_t ZigbeeProcessInputEZSP(class SBuffer &buf) { + // verify errors in first 2 bytes. + // TODO + // uint8_t sequence_num = buf.get8(0); + uint16_t frame_control = buf.get16(1); + bool truncated = frame_control & 0x02; + bool overflow = frame_control & 0x01; + bool callbackPending = frame_control & 0x04; + bool security_enabled = frame_control & 0x8000; + if (frame_control != 0x0180) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: specific frame_control 0x%04X"), frame_control); + } + + // remove first 2 bytes, be + for (uint32_t i=0; i> 4) + 1) & 0x07; + uint8_t ack_byte = 0x80 | EZSP_Serial.from_ack; + ZigbeeEZSPSendRaw(&ack_byte, 1, false); // send a 1-byte ACK + + // build the EZSP frame + // remove first byte + for (uint8_t i=0; i 0)) { + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int32_t size = strlen(XdrvMailbox.data); + + SBuffer buf((size+1)/2); + + while (size > 1) { + char stemp[3]; + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, nullptr, 16); + buf.add8(code); + size -= 2; + codes += 2; + } + if (send) { + // Command was `ZbEZSPSend` + if (2 == XdrvMailbox.index) { ZigbeeEZSPSendDATA(buf.getBuffer(), buf.len(), true); } + else if (3 == XdrvMailbox.index) { ZigbeeEZSPSendRaw(buf.getBuffer(), buf.len(), true); } + else { ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len(), true); } + + } else { + // Command was `ZbEZSPReceive` + if (2 == XdrvMailbox.index) { ZigbeeProcessInput(buf); } + else if (3 == XdrvMailbox.index) { ZigbeeProcessInputRaw(buf); } + else { ZigbeeProcessInputEZSP(buf); } // TODO + } + } + ResponseCmndDone(); +} +// Variants with managed ASH frame numbers +// For debug purposes only, simulates a message received +void CmndZbEZSPReceive(void) +{ + CmndZbEZSPSendOrReceive(false); +} + +void CmndZbEZSPSend(void) +{ + CmndZbEZSPSendOrReceive(true); +} +#endif // USE_ZIGBEE_EZSP + +// +// Internal function, send the low-level frame +// Input: +// - shortaddr: 16-bits short address, or 0x0000 if group address +// - groupaddr: 16-bits group address, or 0x0000 if unicast using shortaddr +// - clusterIf: 16-bits cluster number +// - endpoint: 8-bits target endpoint (source is always 0x01), unused for group addresses. Should not be 0x00 except when sending to group address. +// - cmdId: 8-bits ZCL command number +// - clusterSpecific: boolean, is the message general cluster or cluster specific, used to create the FC byte of ZCL +// - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr +// - len: length of the 'msg' payload +// - needResponse: boolean, true = we ask the target to respond, false = the target should not respond +// - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number +// Returns: None +// +void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { + +#ifdef USE_ZIGBEE_ZNP + SBuffer buf(32+len); + buf.add8(Z_SREQ | Z_AF); // 24 + buf.add8(AF_DATA_REQUEST_EXT); // 02 + if (BAD_SHORTADDR == shortaddr) { // if no shortaddr we assume group address + buf.add8(Z_Addr_Group); // 01 + buf.add64(groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded + buf.add8(0xFF); // dest endpoint is not used for group addresses + } else { + buf.add8(Z_Addr_ShortAddress); // 02 + buf.add64(shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded + buf.add8(endpoint); // dest endpoint + } + buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan + buf.add8(0x01); // source endpoint + buf.add16(clusterId); + buf.add8(transacId); // transacId + buf.add8(0x30); // 30 options + buf.add8(0x1E); // 1E radius + + buf.add16(3 + len + (manuf ? 2 : 0)); + buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field + if (manuf) { + buf.add16(manuf); // add Manuf Id if not null + } + buf.add8(transacId); // Transaction Sequance Number + buf.add8(cmdId); + if (len > 0) { + buf.addBuffer(msg, len); // add the payload + } + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +#endif // USE_ZIGBEE_ZNP +} + +#endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino similarity index 70% rename from tasmota/xdrv_23_zigbee_9_impl.ino rename to tasmota/xdrv_23_zigbee_A_impl.ino index 1cc5f6efc..bfb3015d4 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -21,23 +21,6 @@ #define XDRV_23 23 -#ifdef USE_ZIGBEE_ZNP -const uint32_t ZIGBEE_BUFFER_SIZE = 256; // Max ZNP frame is SOF+LEN+CMD1+CMD2+250+FCS = 255 -const uint8_t ZIGBEE_SOF = 0xFE; -const uint8_t ZIGBEE_SOF_ALT = 0xFF; -#endif // USE_ZIGBEE_ZNP - -#ifdef USE_ZIGBEE_EZSP -const uint32_t ZIGBEE_BUFFER_SIZE = 256; -const uint8_t ZIGBEE_EZSP_CANCEL = 0x1A; // cancel byte -const uint8_t ZIGBEE_EZSP_EOF = 0x7E; // end of frame -const uint8_t ZIGBEE_EZSP_ESCAPE = 0x7D; // escape byte -#endif // USE_ZIGBEE_EZSP - -#include -TasmotaSerial *ZigbeeSerial = nullptr; - - const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix #ifdef USE_ZIGBEE_ZNP D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEEZNPRECEIVE "|" @@ -68,221 +51,6 @@ void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbConfig, }; -// -// Called at event loop, checks for incoming data from the CC2530 -// -void ZigbeeInputLoop(void) { - -#ifdef USE_ZIGBEE_ZNP - static uint32_t zigbee_polling_window = 0; // number of milliseconds since first byte - static uint8_t fcs = ZIGBEE_SOF; - static uint32_t zigbee_frame_len = 5; // minimal zigbee frame length, will be updated when buf[1] is read - // Receive only valid ZNP frames: - // 00 - SOF = 0xFE - // 01 - Length of Data Field - 0..250 - // 02 - CMD1 - first byte of command - // 03 - CMD2 - second byte of command - // 04..FD - Data Field - // FE (or last) - FCS Checksum - - while (ZigbeeSerial->available()) { - yield(); - uint8_t zigbee_in_byte = ZigbeeSerial->read(); - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZbInput byte=%d len=%d"), zigbee_in_byte, zigbee_buffer->len()); - - if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized - zigbee_frame_len = 5; - fcs = ZIGBEE_SOF; - // there is a rare race condition when an interrupt occurs when receiving the first byte - // in this case the first bit (lsb) is missed and Tasmota receives 0xFF instead of 0xFE - // We forgive this mistake, and next bytes are automatically resynchronized - if (ZIGBEE_SOF_ALT == zigbee_in_byte) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte); - zigbee_in_byte = ZIGBEE_SOF; - } - } - - if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { - // waiting for SOF (Start Of Frame) byte, discard anything else - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte); - continue; // discard - } - - if (zigbee_buffer->len() < zigbee_frame_len) { - zigbee_buffer->add8(zigbee_in_byte); - zigbee_polling_window = millis(); // Wait for more data - fcs ^= zigbee_in_byte; - } - - if (zigbee_buffer->len() >= zigbee_frame_len) { - zigbee_polling_window = 0; // Publish now - break; - } - - // recalculate frame length - if (02 == zigbee_buffer->len()) { - // We just received the Lenght byte - uint8_t len_byte = zigbee_buffer->get8(1); - if (len_byte > 250) len_byte = 250; // ZNP spec says len is 250 max - - zigbee_frame_len = len_byte + 5; // SOF + LEN + CMD1 + CMD2 + FCS = 5 bytes overhead - } - } - - if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { - char hex_char[(zigbee_buffer->len() * 2) + 2]; - ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); - // buffer received, now check integrity - if (zigbee_buffer->len() != zigbee_frame_len) { - // Len is not correct, log and reject frame - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len); - } else if (0x00 != fcs) { - // FCS is wrong, packet is corrupt, log and reject frame - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs); - } else { - // frame is correct - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received correct frame %s"), hex_char); - - SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); // remove SOF, LEN and FCS - - ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); - Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); - if (Settings.flag3.tuya_serial_mqtt_publish) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - } else { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); - } - // now process the message - ZigbeeProcessInput(znp_buffer); - } - zigbee_buffer->setLen(0); // empty buffer - } -#endif // USE_ZIGBEE_ZNP - -#ifdef USE_ZIGBEE_EZSP - static uint32_t zigbee_polling_window = 0; // number of milliseconds since first byte - bool escape = false; // was the previous byte an escape? - bool frame_complete = false; // frame is ready and complete - // Receive only valid EZSP frames: - // 1A - Cancel - cancel all previous bytes - // 7D - Escape byte - following byte is escaped - // 7E - end of frame - - while (ZigbeeSerial->available()) { - yield(); - uint8_t zigbee_in_byte = ZigbeeSerial->read(); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: ZbInput byte=0x%02X len=%d"), zigbee_in_byte, zigbee_buffer->len()); - - // if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized - // escape = false; - // frame_complete = false; - // } - - if ((0x11 == zigbee_in_byte) || (0x13 == zigbee_in_byte)) { - continue; // ignore reserved bytes XON/XOFF - } - - if (ZIGBEE_EZSP_ESCAPE == zigbee_in_byte) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: Escape byte received")); - escape = true; - continue; - } - - if (ZIGBEE_EZSP_CANCEL == zigbee_in_byte) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: ZbInput byte=0x1A, cancel byte received, discarding %d bytes"), zigbee_buffer->len()); - zigbee_buffer->setLen(0); // empty buffer - escape = false; - frame_complete = false; - continue; // re-loop - } - - if (ZIGBEE_EZSP_EOF == zigbee_in_byte) { - // end of frame - frame_complete = true; - break; - } - - if (zigbee_buffer->len() < ZIGBEE_BUFFER_SIZE) { - if (escape) { - // invert bit 5 - zigbee_in_byte ^= 0x20; - escape = false; - } - - zigbee_buffer->add8(zigbee_in_byte); - zigbee_polling_window = millis(); // Wait for more data - } // adding bytes - } // while (ZigbeeSerial->available()) - - uint32_t frame_len = zigbee_buffer->len(); - if (frame_complete || (frame_len && (millis() > (zigbee_polling_window + ZIGBEE_POLLING)))) { - char hex_char[frame_len * 2 + 2]; - ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); - if ((frame_complete) && (frame_len >= 3)) { - // frame received and has at least 3 bytes (without EOF), checking CRC - // AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": received raw frame %s"), hex_char); - uint16_t crc = 0xFFFF; // frame CRC - // compute CRC - for (uint32_t i=0; iget8(i) << 8); - for (uint32_t i=0; i<8; i++) { - if (crc & 0x8000) { - crc = (crc << 1) ^ 0x1021; // polynom is x^16 + x^12 + x^5 + 1, CCITT standard - } else { - crc <<= 1; - } - } - } - - uint16_t crc_received = zigbee_buffer->get8(frame_len - 2) << 8 | zigbee_buffer->get8(frame_len - 1); - // remove 2 last bytes - - if (crc_received != crc) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": bad crc (received 0x%04X, computed 0x%04X) %s"), crc_received, crc, hex_char); - } else { - // copy buffer - SBuffer ezsp_buffer = zigbee_buffer->subBuffer(0, frame_len - 2); // CRC - - // CRC is correct, apply de-stuffing if DATA frame - if (0 == (ezsp_buffer.get8(0) & 0x80)) { - // DATA frame - uint8_t rand = 0x42; - for (uint32_t i=1; i> 1) ^ 0xB8; } - else { rand = (rand >> 1); } - } - } - - ToHex_P((unsigned char*)ezsp_buffer.getBuffer(), ezsp_buffer.len(), hex_char, sizeof(hex_char)); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_EZSP_RECEIVED "\":\"%s\"}"), hex_char); - if (Settings.flag3.tuya_serial_mqtt_publish) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - } else { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); // TODO move to LOG_LEVEL_DEBUG when stable - } - // now process the message - ZigbeeProcessInput(ezsp_buffer); - } - } else { - // the buffer timed-out, print error and discard - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": time-out, discarding %s, %d"), hex_char); - } - zigbee_buffer->setLen(0); // empty buffer - escape = false; - frame_complete = false; - } - -#endif // USE_ZIGBEE_EZSP - -} - /********************************************************************************************/ // Initialize internal structures @@ -301,28 +69,7 @@ void ZigbeeInit(void) // update commands with the current settings Z_UpdateConfig(Settings.zb_channel, Settings.zb_pan_id, Settings.zb_ext_panid, Settings.zb_precfgkey_l, Settings.zb_precfgkey_h); -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem1 = %d"), ESP_getFreeHeap()); - zigbee.active = false; - if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX)) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX)); - // if seriallog_level is 0, we allow GPIO 13/15 to switch to Hardware Serial - ZigbeeSerial = new TasmotaSerial(Pin(GPIO_ZIGBEE_RX), Pin(GPIO_ZIGBEE_TX), seriallog_level ? 1 : 2, 0, 256); // set a receive buffer of 256 bytes - ZigbeeSerial->begin(115200); - if (ZigbeeSerial->hardwareSerial()) { - ClaimSerial(); - uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3; - zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer); - } else { -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem2 = %d"), ESP_getFreeHeap()); - zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem3 = %d"), ESP_getFreeHeap()); - } - zigbee.active = true; - zigbee.init_phase = true; // start the state machine - zigbee.state_machine = true; // start the state machine - ZigbeeSerial->flush(); - } -// AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem9 = %d"), ESP_getFreeHeap()); + ZigbeeInitSerial(); } /*********************************************************************************************\ @@ -366,266 +113,6 @@ void CmndZbReset(void) { } } -#ifdef USE_ZIGBEE_ZNP - -void ZigbeeZNPSend(const uint8_t *msg, size_t len) { - if ((len < 2) || (len > 252)) { - // abort, message cannot be less than 2 bytes for CMD1 and CMD2 - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len); - return; - } - uint8_t data_len = len - 2; // removing CMD1 and CMD2 - - if (ZigbeeSerial) { - uint8_t fcs = data_len; - - ZigbeeSerial->write(ZIGBEE_SOF); // 0xFE - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF); - ZigbeeSerial->write(data_len); - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len); - for (uint32_t i = 0; i < len; i++) { - uint8_t b = pgm_read_byte(msg + i); - ZigbeeSerial->write(b); - fcs ^= b; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b); - } - ZigbeeSerial->write(fcs); // finally send fcs checksum byte - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs); - } - // Now send a MQTT message to report the sent message - char hex_char[(len * 2) + 2]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"), - ToHex_P(msg, len, hex_char, sizeof(hex_char))); -} - -// -// Same code for `ZbZNPSend` and `ZbZNPReceive` -// building the complete message (intro, length) -// -void CmndZbZNPSendOrReceive(bool send) -{ - if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { - uint8_t code; - - char *codes = RemoveSpace(XdrvMailbox.data); - int32_t size = strlen(XdrvMailbox.data); - - SBuffer buf((size+1)/2); - - while (size > 1) { - char stemp[3]; - strlcpy(stemp, codes, sizeof(stemp)); - code = strtol(stemp, nullptr, 16); - buf.add8(code); - size -= 2; - codes += 2; - } - if (send) { - // Command was `ZbZNPSend` - ZigbeeZNPSend(buf.getBuffer(), buf.len()); - } else { - // Command was `ZbZNPReceive` - ZigbeeProcessInput(buf); - } - } - ResponseCmndDone(); -} - -// For debug purposes only, simulates a message received -void CmndZbZNPReceive(void) -{ - CmndZbZNPSendOrReceive(false); -} - -void CmndZbZNPSend(void) -{ - CmndZbZNPSendOrReceive(true); -} - -#endif // USE_ZIGBEE_ZNP - -#ifdef USE_ZIGBEE_EZSP - -// internal function to output a byte, and escape it (stuffing) if needed -void ZigbeeEZSPSend_Out(uint8_t out_byte) { - switch (out_byte) { - case 0x7E: // Flag byte - case 0x11: // XON - case 0x13: // XOFF - case 0x18: // Substitute byte - case 0x1A: // Cancel byte - case 0x7D: // Escape byte - ZigbeeSerial->write(ZIGBEE_EZSP_ESCAPE); // send Escape byte 0x7D - ZigbeeSerial->write(out_byte ^ 0x20); // send with bit 5 inverted - break; - default: - ZigbeeSerial->write(out_byte); // send unchanged - break; - } -} -// Send low-level EZSP frames -// -// The frame should contain the Control Byte and Data Field -// The frame shouldn't be escaped, nor randomized -// -// Before sending: -// - send Cancel byte (0x1A) if requested -// - randomize Data Field if DATA Frame -// - compute CRC16 -// - escape (stuff) reserved bytes -// - add EOF (0x7E) -// - send frame -// send_cancel: should we first send a EZSP_CANCEL (0x1A) before the message to clear any leftover -void ZigbeeEZSPSend(const uint8_t *msg, size_t len, bool send_cancel = false) { - if ((len < 1) || (len > 252)) { - // abort, message cannot be less than 2 bytes for CMD1 and CMD2 - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEE_EZSP_SENT ": bad message len %d"), len); - return; - } - uint8_t data_len = len - 2; // removing CMD1 and CMD2 - - if (ZigbeeSerial) { - if (send_cancel) { - ZigbeeSerial->write(ZIGBEE_EZSP_CANCEL); // 0x1A - } - - bool data_frame = (0 == (msg[0] & 0x80)); - uint8_t rand = 0x42; // pseudo-randomizer initial value - uint16_t crc = 0xFFFF; // CRC16 CCITT initialization - - for (uint32_t i=0; i 0)) { - out_byte ^= rand; - if (rand & 1) { rand = (rand >> 1) ^ 0xB8; } - else { rand = (rand >> 1); } - } - - // compute CRC - crc = crc ^ ((uint16_t)out_byte << 8); - for (uint32_t i=0; i<8; i++) { - if (crc & 0x8000) { - crc = (crc << 1) ^ 0x1021; // polynom is x^16 + x^12 + x^5 + 1, CCITT standard - } else { - crc <<= 1; - } - } - - // output byte - ZigbeeEZSPSend_Out(out_byte); - } - // send CRC16 in big-endian - ZigbeeEZSPSend_Out(crc >> 8); - ZigbeeEZSPSend_Out(crc & 0xFF); - - // finally send End of Frame - ZigbeeSerial->write(ZIGBEE_EZSP_EOF); // 0x1A - } - // Now send a MQTT message to report the sent message - char hex_char[(len * 2) + 2]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEE_EZSP_SENT " %s"), - ToHex_P(msg, len, hex_char, sizeof(hex_char))); -} - -// -// Same code for `ZbZNPSend` and `ZbZNPReceive` -// building the complete message (intro, length) -// -void CmndZbEZSPSendOrReceive(bool send) -{ - if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { - uint8_t code; - - char *codes = RemoveSpace(XdrvMailbox.data); - int32_t size = strlen(XdrvMailbox.data); - - SBuffer buf((size+1)/2); - - while (size > 1) { - char stemp[3]; - strlcpy(stemp, codes, sizeof(stemp)); - code = strtol(stemp, nullptr, 16); - buf.add8(code); - size -= 2; - codes += 2; - } - if (send) { - // Command was `ZbEZSPSend` - ZigbeeEZSPSend(buf.getBuffer(), buf.len()); - } else { - // Command was `ZbEZSPReceive` - ZigbeeProcessInput(buf); - } - } - ResponseCmndDone(); -} - -// For debug purposes only, simulates a message received -void CmndZbEZSPReceive(void) -{ - CmndZbEZSPSendOrReceive(false); -} - -void CmndZbEZSPSend(void) -{ - CmndZbEZSPSendOrReceive(true); -} -#endif // USE_ZIGBEE_EZSP - -// -// Internal function, send the low-level frame -// Input: -// - shortaddr: 16-bits short address, or 0x0000 if group address -// - groupaddr: 16-bits group address, or 0x0000 if unicast using shortaddr -// - clusterIf: 16-bits cluster number -// - endpoint: 8-bits target endpoint (source is always 0x01), unused for group addresses. Should not be 0x00 except when sending to group address. -// - cmdId: 8-bits ZCL command number -// - clusterSpecific: boolean, is the message general cluster or cluster specific, used to create the FC byte of ZCL -// - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr -// - len: length of the 'msg' payload -// - needResponse: boolean, true = we ask the target to respond, false = the target should not respond -// - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number -// Returns: None -// -void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { - -#ifdef USE_ZIGBEE_ZNP - SBuffer buf(32+len); - buf.add8(Z_SREQ | Z_AF); // 24 - buf.add8(AF_DATA_REQUEST_EXT); // 02 - if (BAD_SHORTADDR == shortaddr) { // if no shortaddr we assume group address - buf.add8(Z_Addr_Group); // 01 - buf.add64(groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded - buf.add8(0xFF); // dest endpoint is not used for group addresses - } else { - buf.add8(Z_Addr_ShortAddress); // 02 - buf.add64(shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded - buf.add8(endpoint); // dest endpoint - } - buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan - buf.add8(0x01); // source endpoint - buf.add16(clusterId); - buf.add8(transacId); // transacId - buf.add8(0x30); // 30 options - buf.add8(0x1E); // 1E radius - - buf.add16(3 + len + (manuf ? 2 : 0)); - buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field - if (manuf) { - buf.add16(manuf); // add Manuf Id if not null - } - buf.add8(transacId); // Transaction Sequance Number - buf.add8(cmdId); - if (len > 0) { - buf.addBuffer(msg, len); // add the payload - } - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); -#endif // USE_ZIGBEE_ZNP -} - /********************************************************************************************/ // // High-level function