mirror of https://github.com/arendst/Tasmota.git
Merge pull request #8739 from s-hadinger/zigbee_ezsp_2
Zigbee EZSP milestone 2
This commit is contained in:
commit
1b2cf36e82
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, <EmberConcentratorType.HIGH_RAM_CONCENTRATOR: 65529>, 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
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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.h>
|
||||
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; i<frame_len-2; i++) {
|
||||
crc = crc ^ ((uint16_t)zigbee_buffer->get8(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<ezsp_buffer.len(); i++) {
|
||||
ezsp_buffer.set8(i, ezsp_buffer.get8(i) ^ rand);
|
||||
if (rand & 1) { rand = (rand >> 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<len; i++) {
|
||||
uint8_t out_byte = msg[i];
|
||||
|
||||
// apply randomization if DATA field
|
||||
if (data_frame && (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<buf.len()-3; i++) {
|
||||
buf.set8(i, buf.get8(i+3));
|
||||
}
|
||||
buf.setLen(buf.len() - 3);
|
||||
|
||||
char hex_char[buf.len()*2 + 2];
|
||||
|
||||
// log message
|
||||
ToHex_P((unsigned char*)buf.getBuffer(), buf.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
|
||||
}
|
||||
|
||||
// Pass message to state machine
|
||||
ZigbeeProcessInput(buf);
|
||||
}
|
||||
|
||||
|
||||
// Receive raw ASH frame (CRC was removed, data unstuffed) but still contains frame numbers
|
||||
int32_t ZigbeeProcessInputRaw(class SBuffer &buf) {
|
||||
uint8_t control_byte = buf.get8(0);
|
||||
uint8_t ack_num = control_byte & 0x07; // keep 3 LSB
|
||||
if (control_byte & 0x80) {
|
||||
|
||||
// non DATA frame
|
||||
uint8_t frame_type = control_byte & 0xE0; // keep 3 MSB
|
||||
if (frame_type == 0x80) {
|
||||
|
||||
// ACK
|
||||
EZSP_Serial.from_ack = ack_num; // update ack num
|
||||
} else if (frame_type == 0xA0) {
|
||||
|
||||
// NAK
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Received NAK %d, resending not implemented"), ack_num);
|
||||
} else if (control_byte == 0xC1) {
|
||||
|
||||
// RSTACK
|
||||
// received just after boot, either because of Power up, hardware reset or RST
|
||||
Z_EZSP_RSTACK(buf.get8(2));
|
||||
EZSP_Serial.from_ack = 0;
|
||||
EZSP_Serial.to_ack = 0;
|
||||
|
||||
// pass it to state machine with a special 0xFFFE frame code (EZSP_RSTACK_ID)
|
||||
buf.set8(0, Z_B0(EZSP_rstAck));
|
||||
buf.set8(1, Z_B1(EZSP_rstAck));
|
||||
// keep byte #2 with code
|
||||
buf.setLen(3);
|
||||
ZigbeeProcessInput(buf);
|
||||
} else if (control_byte == 0xC2) {
|
||||
|
||||
// ERROR
|
||||
Z_EZSP_ERROR(buf.get8(2));
|
||||
zigbee.active = false; // stop all zigbee activities
|
||||
} else {
|
||||
|
||||
// Unknown
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Received unknown control byte 0x%02X"), control_byte);
|
||||
}
|
||||
} else {
|
||||
|
||||
// DATA Frame
|
||||
// check the frame number, and send ACK or NAK
|
||||
if ((control_byte & 0x07) != EZSP_Serial.to_ack) {
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: wrong ack, received %d, expected %d"), control_byte & 0x07, EZSP_Serial.to_ack);
|
||||
//EZSP_Serial.to_ack = control_byte & 0x07;
|
||||
}
|
||||
// MCU acknowledged the correct frame
|
||||
// we acknowledge the frame too
|
||||
EZSP_Serial.from_ack = ((control_byte >> 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<buf.len()-1; i++) {
|
||||
buf.set8(i, buf.get8(i+1));
|
||||
}
|
||||
buf.setLen(buf.len()-1);
|
||||
// pass to next level
|
||||
ZigbeeProcessInputEZSP(buf);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Same code for `ZbEZSPSend` and `ZbEZSPReceive`
|
||||
// building the complete message (intro, length)
|
||||
//
|
||||
// ZbEZSPSend1 = high level EZSP command
|
||||
// ZbEZSPSend2 = low level EZSP DATA frame (with sequence numbers)
|
||||
// ZbEZSPSend3 = low level ASH frame
|
||||
//
|
||||
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`
|
||||
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
|
|
@ -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.h>
|
||||
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; i<frame_len-2; i++) {
|
||||
crc = crc ^ ((uint16_t)zigbee_buffer->get8(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<ezsp_buffer.len(); i++) {
|
||||
ezsp_buffer.set8(i, ezsp_buffer.get8(i) ^ rand);
|
||||
if (rand & 1) { rand = (rand >> 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<len; i++) {
|
||||
uint8_t out_byte = msg[i];
|
||||
|
||||
// apply randomization if DATA field
|
||||
if (data_frame && (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
|
Loading…
Reference in New Issue