Merge branch 'development' into rtsp_auth

This commit is contained in:
Nemobi 2022-05-20 13:25:42 +00:00
commit 8e4dcd7b62
21 changed files with 420 additions and 350 deletions

View File

@ -11,7 +11,7 @@ All notable changes to this project will be documented in this file.
### Fixed ### Fixed
- Possible pin output toggle after power on (#15630)
### Removed ### Removed

View File

@ -127,6 +127,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
- Improv initial or erase device installation failing to provide Configure WiFi option - Improv initial or erase device installation failing to provide Configure WiFi option
- SCD40 start low power command [#15361](https://github.com/arendst/Tasmota/issues/15361) - SCD40 start low power command [#15361](https://github.com/arendst/Tasmota/issues/15361)
- BL09xx negative power presentation [#15374](https://github.com/arendst/Tasmota/issues/15374) - BL09xx negative power presentation [#15374](https://github.com/arendst/Tasmota/issues/15374)
- Possible pin output toggle after power on [#15630](https://github.com/arendst/Tasmota/issues/15630)
### Removed ### Removed
- Arduino IDE support - Arduino IDE support

46
boards/esp32s3usb.json Normal file
View File

@ -0,0 +1,46 @@
{
"build": {
"arduino":{
"ldscript": "esp32s3_out.ld",
"memory_type": "qspi_qspi"
},
"core": "esp32",
"extra_flags": "-DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DUSE_USB_SERIAL_CONSOLE -DESP32_4M -DESP32S3",
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"mcu": "esp32s3",
"variant": "esp32s3",
"partitions": "partitions/esp32_partition_app2880k_fs320k.csv"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"espidf",
"arduino"
],
"name": "Espressif Generic ESP32-S3 4M Flash, Tasmota 2880k Code/OTA, 320k FS",
"upload": {
"arduino": {
"flash_extra_images": [
[
"0x10000",
"variants/tasmota/tasmota32s3usb-safeboot.bin"
]
]
},
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/",
"vendor": "Espressif"
}

View File

@ -1,11 +1,6 @@
# OneWire library # OneWire library
A modification of the Arduino OneWire library maintained by @PaulStoffregen. This modifications supports the ESP32 under the Arduino-esp32 Environment. A @stickbreaker @arendst modified version of the Arduino OneWire library maintained by @PaulStoffregen.
This modifications supports the ESP8266, ESP32, ESP32c3, ESP32s2 and ESP32s3 under the Arduino-esp32 Environment.
No changes are required for compatibility with Arduino coding.
Original Source is Paul's 2.3 version. Forked 28DEC2017 Original Source is Paul's 2.3 version.
@stickbreaker
V2.3.1 30APR2018 add IRAM_ATTR to read_bit() write_bit() to solve ICache miss timing failure.
thanks @everslick re: https://github.com/espressif/arduino-esp32/issues/1335
V2.3 28DEC2017 original mods to support ESP32

View File

@ -28,7 +28,7 @@
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
* Use online command Prefix to translate cmnd, stat and tele. * Use online command Prefix to translate cmnd, stat and tele.
* *
* Updated until v10.1.0.7 - Last update 04.02.2022 * Updated until v11.1.0.3 - Last update 17.05.2022
\*********************************************************************/ \*********************************************************************/
//#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@ -140,7 +140,7 @@
#define D_PASSWORD "Hasło" #define D_PASSWORD "Hasło"
#define D_PH "pH" #define D_PH "pH"
#define D_MQ "MQ" #define D_MQ "MQ"
#define D_PARTITION "Partition" // As in flash and firmware partition #define D_PARTITION "Partycja" // As in flash and firmware partition
#define D_PORT "Port" #define D_PORT "Port"
#define D_POWER_FACTOR "Cosinus fi" #define D_POWER_FACTOR "Cosinus fi"
#define D_POWERUSAGE "Moc" #define D_POWERUSAGE "Moc"
@ -184,7 +184,7 @@
#define D_UPGRADE "aktualizacji" #define D_UPGRADE "aktualizacji"
#define D_UPLOAD "Wgraj" #define D_UPLOAD "Wgraj"
#define D_UPTIME "Czas pracy" #define D_UPTIME "Czas pracy"
#define D_USED "used" #define D_USED "użyte"
#define D_USER "Użytkownik" #define D_USER "Użytkownik"
#define D_UTC_TIME "UTC" #define D_UTC_TIME "UTC"
#define D_UV_INDEX "Indeks UV" #define D_UV_INDEX "Indeks UV"
@ -203,8 +203,8 @@
#define D_WEIGHT "Waga" #define D_WEIGHT "Waga"
#define D_WARMLIGHT "Temperatura światła" #define D_WARMLIGHT "Temperatura światła"
#define D_WEB_SERVER "Serwer Web" #define D_WEB_SERVER "Serwer Web"
#define D_SOC "State of Charge" #define D_SOC "Stan naładowania"
#define D_SOH "State of Health" #define D_SOH "Kondycja"
// tasmota.ino // tasmota.ino
#define D_WARNING_MINIMAL_VERSION "UWAGA Ta wersja nie obsługuje zapisu ustawień" #define D_WARNING_MINIMAL_VERSION "UWAGA Ta wersja nie obsługuje zapisu ustawień"

View File

@ -1326,10 +1326,16 @@ void SetPin(uint32_t lpin, uint32_t gpio) {
TasmotaGlobal.gpio_pin[lpin] = gpio; TasmotaGlobal.gpio_pin[lpin] = gpio;
} }
void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) {
{ static uint32_t pinmode_init[2] = { 0 }; // Pins 0 to 63 !!!
if (PinUsed(gpio_pin, index)) { if (PinUsed(gpio_pin, index)) {
digitalWrite(Pin(gpio_pin, index), state &1); uint32_t pin = Pin(gpio_pin, index) & 0x3F; // Fix possible overflow over 63 gpios
if (!bitRead(pinmode_init[pin / 32], pin % 32)) {
bitSet(pinmode_init[pin / 32], pin % 32);
pinMode(pin, OUTPUT);
}
digitalWrite(pin, state &1);
} }
} }

View File

@ -2048,10 +2048,7 @@ void GpioInit(void)
#endif // ESP8266 #endif // ESP8266
} }
if (PinUsed(GPIO_HEARTBEAT)) { DigitalWrite(GPIO_HEARTBEAT, 0, TasmotaGlobal.heartbeat_inverted);
pinMode(Pin(GPIO_HEARTBEAT), OUTPUT);
digitalWrite(Pin(GPIO_HEARTBEAT), TasmotaGlobal.heartbeat_inverted);
}
// Digital input // Digital input
for (uint32_t i = 0; i < MAX_SWITCHES; i++) { for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
@ -2106,10 +2103,8 @@ void GpioInit(void)
for (uint32_t i = 0; i < MAX_RELAYS; i++) { for (uint32_t i = 0; i < MAX_RELAYS; i++) {
if (PinUsed(GPIO_REL1, i)) { if (PinUsed(GPIO_REL1, i)) {
TasmotaGlobal.devices_present++; TasmotaGlobal.devices_present++;
pinMode(Pin(GPIO_REL1, i), OUTPUT);
#ifdef ESP8266 #ifdef ESP8266
if (EXS_RELAY == TasmotaGlobal.module_type) { if (EXS_RELAY == TasmotaGlobal.module_type) {
digitalWrite(Pin(GPIO_REL1, i), bitRead(TasmotaGlobal.rel_inverted, i) ? 1 : 0);
if (i &1) { TasmotaGlobal.devices_present--; } if (i &1) { TasmotaGlobal.devices_present--; }
} }
#endif // ESP8266 #endif // ESP8266
@ -2124,17 +2119,13 @@ void GpioInit(void)
} else { } else {
#endif #endif
TasmotaGlobal.leds_present++; TasmotaGlobal.leds_present++;
pinMode(Pin(GPIO_LED1, i), OUTPUT); DigitalWrite(GPIO_LED1, i, bitRead(TasmotaGlobal.led_inverted, i));
digitalWrite(Pin(GPIO_LED1, i), bitRead(TasmotaGlobal.led_inverted, i));
#ifdef USE_ARILUX_RF #ifdef USE_ARILUX_RF
} }
#endif #endif
} }
} }
if (PinUsed(GPIO_LEDLNK)) { DigitalWrite(GPIO_LEDLNK, 0, TasmotaGlobal.ledlnk_inverted);
pinMode(Pin(GPIO_LEDLNK), OUTPUT);
digitalWrite(Pin(GPIO_LEDLNK), TasmotaGlobal.ledlnk_inverted);
}
#ifdef USE_PWM_DIMMER #ifdef USE_PWM_DIMMER
if (PWM_DIMMER == TasmotaGlobal.module_type && PinUsed(GPIO_REL1)) { TasmotaGlobal.devices_present--; } if (PWM_DIMMER == TasmotaGlobal.module_type && PinUsed(GPIO_REL1)) { TasmotaGlobal.devices_present--; }

View File

@ -240,6 +240,7 @@ struct TasmotaGlobal_t {
uint8_t discovery_counter; // Delayed discovery counter uint8_t discovery_counter; // Delayed discovery counter
#ifdef USE_PWM_DIMMER #ifdef USE_PWM_DIMMER
uint8_t restore_powered_off_led_counter; // Seconds before powered-off LED (LEDLink) is restored uint8_t restore_powered_off_led_counter; // Seconds before powered-off LED (LEDLink) is restored
uint8_t pwm_dimmer_led_bri; // Adjusted brightness LED level
#endif // USE_PWM_DIMMER #endif // USE_PWM_DIMMER
#ifndef SUPPORT_IF_STATEMENT #ifndef SUPPORT_IF_STATEMENT

View File

@ -2168,7 +2168,10 @@ void LightSetOutputs(const uint16_t *cur_col_10) {
#ifdef USE_PWM_DIMMER #ifdef USE_PWM_DIMMER
// Animate brightness LEDs to follow PWM dimmer brightness // Animate brightness LEDs to follow PWM dimmer brightness
if (PWM_DIMMER == TasmotaGlobal.module_type) PWMDimmerSetBrightnessLeds(change10to8(max_col)); if (PWM_DIMMER == TasmotaGlobal.module_type) {
TasmotaGlobal.pwm_dimmer_led_bri = change10to8(max_col);
PWMDimmerSetBrightnessLeds(-1);
}
#endif // USE_PWM_DIMMER #endif // USE_PWM_DIMMER
} }
// char msg[24]; // char msg[24];

View File

@ -30,43 +30,6 @@
// contains some definitions for functions used before their declarations // contains some definitions for functions used before their declarations
//
// structure containing all needed information to send a ZCL packet
//
class ZCLMessage {
public:
ZCLMessage(void); // allocate 16 bytes vy default
ZCLMessage(size_t size);
inline bool validShortaddr(void) const { return BAD_SHORTADDR != shortaddr; }
inline bool validGroupaddr(void) const { return 0 != groupaddr; }
inline bool validCluster(void) const { return 0xFFFF != cluster; }
inline bool validEndpoint(void) const { return 0x00 != endpoint; }
inline bool validCmd(void) const { return 0xFF != cmd; }
inline void setTransac(uint8_t _transac) { transac = _transac; transacSet = true; }
uint16_t shortaddr = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
uint16_t groupaddr = 0x0000; // group address valid only if device == BAD_SHORTADDR
uint16_t cluster = 0xFFFF; // no default
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
uint8_t cmd = 0xFF; // 0xFF is invalid command number
uint16_t manuf = 0x0000; // default manuf id
bool clusterSpecific = false;
bool needResponse = true;
bool direct = false; // true if direct, false if discover router
bool transacSet = false; // is transac already set
uint8_t transac = 0; // ZCL transaction number
SBuffer buf;
// const uint8_t *msg = nullptr;
// size_t len = 0;
};
// define constructor seperately to avoid inlining and reduce Flash size
ZCLMessage::ZCLMessage(void) : buf(12) {};
ZCLMessage::ZCLMessage(size_t size) : buf(size) {};
typedef int32_t (*ZB_Func)(uint8_t value); typedef int32_t (*ZB_Func)(uint8_t value);
typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const SBuffer &buf); typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const SBuffer &buf);
@ -150,8 +113,8 @@ public:
struct ZigbeeStatus zigbee; struct ZigbeeStatus zigbee;
SBuffer *zigbee_buffer = nullptr; SBuffer *zigbee_buffer = nullptr;
void zigbeeZCLSendCmd(ZCLMessage &msg); void zigbeeZCLSendCmd(ZCLFrame &msg);
void ZigbeeZCLSend_Raw(const ZCLMessage &zcl); void ZigbeeZCLSend_Raw(const ZCLFrame &zcl);
bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok = false); bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok = false);
// parse Hex formatted attribute names like '0301/0001" // parse Hex formatted attribute names like '0301/0001"

View File

@ -975,7 +975,7 @@ public:
void clean(void); // avoid writing to flash the last changes void clean(void); // avoid writing to flash the last changes
// Find device by name, can be short_addr, long_addr, number_in_array or name // Find device by name, can be short_addr, long_addr, number_in_array or name
Z_Device & parseDeviceFromName(const char * param, uint16_t * parsed_shortaddr = nullptr); Z_Device & parseDeviceFromName(const char * param, uint16_t * parsed_shortaddr = nullptr, int32_t mailbox_payload = 0);
bool isTuyaProtocol(uint16_t shortaddr, uint8_t ep = 0) const; bool isTuyaProtocol(uint16_t shortaddr, uint8_t ep = 0) const;
@ -983,7 +983,7 @@ private:
LList<Z_Device> _devices; // list of devices LList<Z_Device> _devices; // list of devices
LList<Z_Deferred> _deferred; // list of deferred calls LList<Z_Deferred> _deferred; // list of deferred calls
uint32_t _saveTimer = 0; uint32_t _saveTimer = 0;
uint8_t _seqNumber = 0; // global seqNumber if device is unknown uint8_t _seqnumber = 0; // global seqNumber if device is unknown
//int32_t findShortAddrIdx(uint16_t shortaddr) const; //int32_t findShortAddrIdx(uint16_t shortaddr) const;
// Create a new entry in the devices list - must be called if it is sure it does not already exist // Create a new entry in the devices list - must be called if it is sure it does not already exist
@ -991,6 +991,13 @@ private:
void freeDeviceEntry(Z_Device *device); void freeDeviceEntry(Z_Device *device);
}; };
/*********************************************************************************************\
* Berry support
\*********************************************************************************************/
#ifdef USE_BERRY
extern "C" void callBerryZigbeeDispatcher(const char* type, ZCLFrame* zcl_received);
#endif // USE_BERRY
/*********************************************************************************************\ /*********************************************************************************************\
* Singleton variable * Singleton variable
\*********************************************************************************************/ \*********************************************************************************************/

View File

@ -345,8 +345,8 @@ uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
device.seqNumber += 1; device.seqNumber += 1;
return device.seqNumber; return device.seqNumber;
} else { } else {
_seqNumber += 1; _seqnumber += 1;
return _seqNumber; return _seqnumber;
} }
} }
@ -622,7 +622,7 @@ void Z_Devices::clean(void) {
// - a friendly name, between quotes, example: "Room_Temp" // - a friendly name, between quotes, example: "Room_Temp"
// //
// In case the device is not found, the parsed 0x.... short address is passed to *parsed_shortaddr // In case the device is not found, the parsed 0x.... short address is passed to *parsed_shortaddr
Z_Device & Z_Devices::parseDeviceFromName(const char * param, uint16_t * parsed_shortaddr) { Z_Device & Z_Devices::parseDeviceFromName(const char * param, uint16_t * parsed_shortaddr, int32_t mailbox_payload) {
if (nullptr == param) { return device_unk; } if (nullptr == param) { return device_unk; }
size_t param_len = strlen(param); size_t param_len = strlen(param);
char dataBuf[param_len + 1]; char dataBuf[param_len + 1];
@ -632,8 +632,8 @@ Z_Device & Z_Devices::parseDeviceFromName(const char * param, uint16_t * parsed_
if ((dataBuf[0] >= '0') && (dataBuf[0] <= '9') && (strlen(dataBuf) < 4)) { if ((dataBuf[0] >= '0') && (dataBuf[0] <= '9') && (strlen(dataBuf) < 4)) {
// simple number 0..99 // simple number 0..99
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) { if ((mailbox_payload > 0) && (mailbox_payload <= 99)) {
return isKnownIndexDevice(XdrvMailbox.payload - 1); return isKnownIndexDevice(mailbox_payload - 1);
} else { } else {
return device_unk; return device_unk;
} }

View File

@ -722,18 +722,25 @@ const __FlashStringHelper* zigbeeFindAttributeById(uint16_t cluster, uint16_t at
class ZCLFrame { class ZCLFrame {
public: public:
// constructor used when creating a message from scratch to send it later
ZCLFrame(void); // allocate 16 bytes by default
ZCLFrame(size_t size);
// constructore used when receiving a Zigbee frame and populating the class
ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id,
const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupaddr, const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupaddr,
uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber): uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber):
_manuf_code(manuf_code), _transact_seq(transact_seq), _cmd_id(cmd_id), manuf(manuf_code), transactseq(transact_seq), cmd(cmd_id),
_payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough
_cluster_id(clusterid), _groupaddr(groupaddr), cluster(clusterid), groupaddr(groupaddr),
_srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), shortaddr(srcaddr), _srcendpoint(srcendpoint), dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast),
_linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber) _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber)
{ {
_frame_control.d8 = frame_control; _frame_control.d8 = frame_control;
_payload.addBuffer(buf, buf_len); clusterSpecific = (_frame_control.b.frame_type != 0);
needResponse = !_frame_control.b.disable_def_resp;
payload.addBuffer(buf, buf_len);
}; };
@ -746,13 +753,13 @@ public:
"\"frametype\":%d,\"direction\":%d,\"disableresp\":%d," "\"frametype\":%d,\"direction\":%d,\"disableresp\":%d,"
"\"manuf\":\"0x%04X\",\"transact\":%d," "\"manuf\":\"0x%04X\",\"transact\":%d,"
"\"cmdid\":\"0x%02X\",\"payload\":\"%_B\"}}"), "\"cmdid\":\"0x%02X\",\"payload\":\"%_B\"}}"),
_groupaddr, _cluster_id, _srcaddr, groupaddr, cluster, shortaddr,
_srcendpoint, _dstendpoint, _wasbroadcast, _srcendpoint, dstendpoint, _wasbroadcast,
_linkquality, _securityuse, _seqnumber, _linkquality, _securityuse, _seqnumber,
_frame_control, _frame_control,
_frame_control.b.frame_type, _frame_control.b.direction, _frame_control.b.disable_def_resp, _frame_control.b.frame_type, _frame_control.b.direction, _frame_control.b.disable_def_resp,
_manuf_code, _transact_seq, _cmd_id, manuf, transactseq, cmd,
&_payload); &payload);
if (Settings->flag3.tuya_serial_mqtt_publish) { if (Settings->flag3.tuya_serial_mqtt_publish) {
MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR)); MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR));
} else { } else {
@ -788,6 +795,7 @@ public:
return _frame_control.b.frame_type & 1; return _frame_control.b.frame_type & 1;
} }
// parsers for received messages
void parseReportAttributes(Z_attribute_list& attr_list); void parseReportAttributes(Z_attribute_list& attr_list);
void generateSyntheticAttributes(Z_attribute_list& attr_list); void generateSyntheticAttributes(Z_attribute_list& attr_list);
void removeInvalidAttributes(Z_attribute_list& attr_list); void removeInvalidAttributes(Z_attribute_list& attr_list);
@ -813,47 +821,59 @@ public:
void autoResponder(const uint16_t *attr_list_ids, size_t attr_len); void autoResponder(const uint16_t *attr_list_ids, size_t attr_len);
inline void setGroupId(uint16_t groupid) { inline void setGroupId(uint16_t groupid) {
_groupaddr = groupid; groupaddr = groupid;
} }
inline void setClusterId(uint16_t clusterid) { inline void setClusterId(uint16_t clusterid) {
_cluster_id = clusterid; cluster = clusterid;
} }
inline uint16_t getSrcAddr(void) const { return _srcaddr; } inline uint16_t getSrcAddr(void) const { return shortaddr; }
inline uint16_t getGroupAddr(void) const { return _groupaddr; } inline uint16_t getGroupAddr(void) const { return groupaddr; }
inline uint16_t getClusterId(void) const { return _cluster_id; } inline uint16_t getClusterId(void) const { return cluster; }
inline uint8_t getLinkQuality(void) const { return _linkquality; } inline uint8_t getLinkQuality(void) const { return _linkquality; }
inline uint8_t getCmdId(void) const { return _cmd_id; } inline uint8_t getCmdId(void) const { return cmd; }
inline uint16_t getSrcEndpoint(void) const { return _srcendpoint; } inline uint16_t getSrcEndpoint(void) const { return _srcendpoint; }
const SBuffer &getPayload(void) const { return payload; }
uint16_t getManufCode(void) const { return manuf; }
const SBuffer &getPayload(void) const { inline void setTransac(uint8_t _transac) { transactseq = _transac; transacSet = true; }
return _payload;
}
uint16_t getManufCode(void) const { inline bool validShortaddr(void) const { return BAD_SHORTADDR != shortaddr; }
return _manuf_code; inline bool validCluster(void) const { return 0xFFFF != cluster; }
} inline bool validEndpoint(void) const { return 0x00 != dstendpoint; }
inline bool validCmd(void) const { return 0xFF != cmd; }
public:
private: uint16_t manuf = 0; // optional
ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; uint8_t transactseq = 0; // transaction sequence number
uint16_t _manuf_code = 0; // optional uint8_t cmd = 0;
uint8_t _transact_seq = 0; // transaction sequence number SBuffer payload;
uint8_t _cmd_id = 0; uint16_t cluster = 0;
SBuffer _payload; uint16_t groupaddr = 0;
uint16_t _cluster_id = 0;
uint16_t _groupaddr = 0;
// information from decoded ZCL frame // information from decoded ZCL frame
uint16_t _srcaddr; uint16_t shortaddr = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
uint8_t _srcendpoint; uint8_t dstendpoint = 0x00; // 0x00 is invalid for the dst endpoint
uint8_t _dstendpoint; // attributes used in send-only mode
uint8_t _wasbroadcast; bool clusterSpecific = false;
uint8_t _linkquality; bool needResponse = true;
uint8_t _securityuse; bool direct = false; // true if direct, false if discover router
uint8_t _seqnumber; bool transacSet = false; // is transac already set
// below private attributes are not used when sending a message
private:
uint8_t _srcendpoint = 0x00; // 0x00 is invalid for the src endpoint
ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 };
bool _wasbroadcast = false;
uint8_t _linkquality = 0x00;
uint8_t _securityuse = 0; // not used by Z2T, logging only
uint8_t _seqnumber = 0; // not used by Z2T, logging only
}; };
// define constructor seperately to avoid inlining and reduce Flash size
ZCLFrame::ZCLFrame(void) : payload(12) {};
ZCLFrame::ZCLFrame(size_t size) : payload(size) {};
// Zigbee ZCL converters // Zigbee ZCL converters
// from https://github.com/Koenkk/zigbee-shepherd-converters/blob/638d29f0cace6343052b9a4e7fd60980fa785479/converters/fromZigbee.js#L55 // from https://github.com/Koenkk/zigbee-shepherd-converters/blob/638d29f0cace6343052b9a4e7fd60980fa785479/converters/fromZigbee.js#L55
@ -1197,45 +1217,45 @@ uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf,
// First pass, parse all attributes in their native format // First pass, parse all attributes in their native format
void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) { void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) {
uint32_t i = 0; uint32_t i = 0;
uint32_t len = _payload.len(); uint32_t len = payload.len();
if (ZCL_WRITE_ATTRIBUTES == getCmdId()) { if (ZCL_WRITE_ATTRIBUTES == getCmdId()) {
attr_list.addAttribute(PSTR("Command"), true).setStr(PSTR("Write")); attr_list.addAttribute(PSTR("Command"), true).setStr(PSTR("Write"));
} }
while (len >= i + 3) { while (len >= i + 3) {
uint16_t attrid = _payload.get16(i); uint16_t attrid = payload.get16(i);
i += 2; i += 2;
// exception for Xiaomi lumi.weather - specific field to be treated as octet and not char // exception for Xiaomi lumi.weather - specific field to be treated as octet and not char
if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { if ((0x0000 == cluster) && (0xFF01 == attrid)) {
if (0x42 == _payload.get8(i)) { if (0x42 == payload.get8(i)) {
_payload.set8(i, 0x41); // change type from 0x42 to 0x41 payload.set8(i, 0x41); // change type from 0x42 to 0x41
} }
} }
// TODO look for suffix // TODO look for suffix
Z_attribute & attr = attr_list.addAttribute(_cluster_id, attrid); Z_attribute & attr = attr_list.addAttribute(cluster, attrid);
i += parseSingleAttribute(attr, _payload, i); i += parseSingleAttribute(attr, payload, i);
} }
// Issue Philips outdoor motion sensor SML002, see https://github.com/Koenkk/zigbee2mqtt/issues/897 // Issue Philips outdoor motion sensor SML002, see https://github.com/Koenkk/zigbee2mqtt/issues/897
// The sensor expects the coordinator to send a Default Response to acknowledge the attribute reporting // The sensor expects the coordinator to send a Default Response to acknowledge the attribute reporting
if (0 == _frame_control.b.disable_def_resp) { if (0 == _frame_control.b.disable_def_resp) {
// the device expects a default response // the device expects a default response
ZCLMessage zcl(2); // message is 2 bytes ZCLFrame zcl(2); // message is 2 bytes
zcl.shortaddr = _srcaddr; zcl.shortaddr = shortaddr;
zcl.cluster = _cluster_id; zcl.cluster = cluster;
zcl.endpoint = _srcendpoint; zcl.dstendpoint = _srcendpoint;
zcl.cmd = ZCL_DEFAULT_RESPONSE; zcl.cmd = ZCL_DEFAULT_RESPONSE;
zcl.manuf = _manuf_code; zcl.manuf = manuf;
zcl.clusterSpecific = false; /* not cluster specific */ zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */ zcl.needResponse = false; /* noresponse */
zcl.direct = true; /* direct no retry */ zcl.direct = true; /* direct no retry */
zcl.setTransac(_transact_seq); zcl.setTransac(transactseq);
zcl.buf.add8(_cmd_id); zcl.payload.add8(cmd);
zcl.buf.add8(0); // Status = OK zcl.payload.add8(0); // Status = OK
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }
@ -1295,8 +1315,8 @@ void ZCLFrame::removeInvalidAttributes(Z_attribute_list& attr_list) {
// Note: both function are now split to compute on extracted attributes // Note: both function are now split to compute on extracted attributes
// //
void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) { void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
const Z_Device & device = zigbee_devices.findShortAddr(_srcaddr); const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
const char * model_c = zigbee_devices.getModelId(_srcaddr); // null if unknown const char * model_c = zigbee_devices.getModelId(shortaddr); // null if unknown
String modelId((char*) model_c); String modelId((char*) model_c);
// scan through attributes and apply specific converters // scan through attributes and apply specific converters
for (auto &attr : attr_list) { for (auto &attr : attr_list) {
@ -1324,7 +1344,7 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
case 0x02010008: // Pi Heating Demand - solve Eutotronic bug case 0x02010008: // Pi Heating Demand - solve Eutotronic bug
case 0x02014008: // Eurotronic Host Flags decoding case 0x02014008: // Eurotronic Host Flags decoding
{ {
const char * manufacturer_c = zigbee_devices.getManufacturerId(_srcaddr); // null if unknown const char * manufacturer_c = zigbee_devices.getManufacturerId(shortaddr); // null if unknown
String manufacturerId((char*) manufacturer_c); String manufacturerId((char*) manufacturer_c);
if (manufacturerId.equals(F("Eurotronic"))) { if (manufacturerId.equals(F("Eurotronic"))) {
if (ccccaaaa == 0x02010008) { if (ccccaaaa == 0x02010008) {
@ -1382,7 +1402,7 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
} }
break; break;
case 0x05000002: // ZoneStatus case 0x05000002: // ZoneStatus
const Z_Data_Alarm & alarm = (const Z_Data_Alarm&) zigbee_devices.getShortAddr(_srcaddr).data.find(Z_Data_Type::Z_Alarm, _srcendpoint); const Z_Data_Alarm & alarm = (const Z_Data_Alarm&) zigbee_devices.getShortAddr(shortaddr).data.find(Z_Data_Type::Z_Alarm, _srcendpoint);
if (&alarm != nullptr) { if (&alarm != nullptr) {
alarm.convertZoneStatus(attr_list, attr.getUInt()); alarm.convertZoneStatus(attr_list, attr.getUInt());
} }
@ -1405,15 +1425,15 @@ void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) {
uint32_t occupancy = attr.getUInt(); uint32_t occupancy = attr.getUInt();
if (occupancy) { if (occupancy) {
uint32_t pir_timer = OCCUPANCY_TIMEOUT; uint32_t pir_timer = OCCUPANCY_TIMEOUT;
const Z_Data_PIR & pir_found = (const Z_Data_PIR&) zigbee_devices.getShortAddr(_srcaddr).data.find(Z_Data_Type::Z_PIR, _srcendpoint); const Z_Data_PIR & pir_found = (const Z_Data_PIR&) zigbee_devices.getShortAddr(shortaddr).data.find(Z_Data_Type::Z_PIR, _srcendpoint);
if (&pir_found != nullptr) { if (&pir_found != nullptr) {
pir_timer = pir_found.getTimeoutSeconds() * 1000; pir_timer = pir_found.getTimeoutSeconds() * 1000;
} }
if (pir_timer > 0) { if (pir_timer > 0) {
zigbee_devices.setTimer(_srcaddr, 0 /* groupaddr */, pir_timer, _cluster_id, _srcendpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback); zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, pir_timer, cluster, _srcendpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback);
} }
} else { } else {
zigbee_devices.resetTimersForDevice(_srcaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY); zigbee_devices.resetTimersForDevice(shortaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY);
} }
break; break;
} }
@ -1460,16 +1480,16 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin
// ZCL_READ_ATTRIBUTES // ZCL_READ_ATTRIBUTES
void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) { void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
uint32_t i = 0; uint32_t i = 0;
uint32_t len = _payload.len(); uint32_t len = payload.len();
uint16_t read_attr_ids[len/2]; uint16_t read_attr_ids[len/2];
attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_CLUSTER)).setUInt(_cluster_id); attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_CLUSTER)).setUInt(cluster);
JsonGeneratorArray attr_numbers; JsonGeneratorArray attr_numbers;
Z_attribute_list attr_names; Z_attribute_list attr_names;
while (len >= 2 + i) { while (len >= 2 + i) {
uint16_t attrid = _payload.get16(i); uint16_t attrid = payload.get16(i);
attr_numbers.add(attrid); attr_numbers.add(attrid);
read_attr_ids[i/2] = attrid; read_attr_ids[i/2] = attrid;
@ -1479,7 +1499,7 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short));
uint16_t conv_attribute = pgm_read_word(&converter->attribute); uint16_t conv_attribute = pgm_read_word(&converter->attribute);
if ((conv_cluster == _cluster_id) && (conv_attribute == attrid)) { if ((conv_cluster == cluster) && (conv_attribute == attrid)) {
attr_names.addAttribute(Z_strings + pgm_read_word(&converter->name_offset), true).setBool(true); attr_names.addAttribute(Z_strings + pgm_read_word(&converter->name_offset), true).setBool(true);
break; break;
} }
@ -1490,29 +1510,29 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
attr_list.addAttributePMEM(PSTR("ReadNames")).setStrRaw(attr_names.toString(true).c_str()); attr_list.addAttributePMEM(PSTR("ReadNames")).setStrRaw(attr_names.toString(true).c_str());
// call auto-responder only if src address if different from ourselves and it was a broadcast // call auto-responder only if src address if different from ourselves and it was a broadcast
if (_srcaddr != localShortAddr || !_wasbroadcast) { if (shortaddr != localShortAddr || !_wasbroadcast) {
autoResponder(read_attr_ids, len/2); autoResponder(read_attr_ids, len/2);
} }
} }
// ZCL_CONFIGURE_REPORTING_RESPONSE // ZCL_CONFIGURE_REPORTING_RESPONSE
void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) { void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) {
uint32_t len = _payload.len(); uint32_t len = payload.len();
Z_attribute_list attr_config_list; Z_attribute_list attr_config_list;
for (uint32_t i=0; len >= i+4; i+=4) { for (uint32_t i=0; len >= i+4; i+=4) {
uint8_t status = _payload.get8(i); uint8_t status = payload.get8(i);
uint16_t attr_id = _payload.get8(i+2); uint16_t attr_id = payload.get8(i+2);
Z_attribute_list attr_config_response; Z_attribute_list attr_config_response;
attr_config_response.addAttributePMEM(PSTR("Status")).setUInt(status); attr_config_response.addAttributePMEM(PSTR("Status")).setUInt(status);
attr_config_response.addAttributePMEM(PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str()); attr_config_response.addAttributePMEM(PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str());
const __FlashStringHelper* attr_name = zigbeeFindAttributeById(_cluster_id, attr_id, nullptr, nullptr); const __FlashStringHelper* attr_name = zigbeeFindAttributeById(cluster, attr_id, nullptr, nullptr);
if (attr_name) { if (attr_name) {
attr_config_list.addAttribute(attr_name).setStrRaw(attr_config_response.toString(true).c_str()); attr_config_list.addAttribute(attr_name).setStrRaw(attr_config_response.toString(true).c_str());
} else { } else {
attr_config_list.addAttribute(_cluster_id, attr_id).setStrRaw(attr_config_response.toString(true).c_str()); attr_config_list.addAttribute(cluster, attr_id).setStrRaw(attr_config_response.toString(true).c_str());
} }
} }
@ -1522,21 +1542,21 @@ void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) {
// ZCL_WRITE_ATTRIBUTES_RESPONSE // ZCL_WRITE_ATTRIBUTES_RESPONSE
void ZCLFrame::parseWriteAttributesResponse(Z_attribute_list& attr_list) { void ZCLFrame::parseWriteAttributesResponse(Z_attribute_list& attr_list) {
parseResponse_inner(ZCL_WRITE_ATTRIBUTES_RESPONSE, false, _payload.get8(0)); parseResponse_inner(ZCL_WRITE_ATTRIBUTES_RESPONSE, false, payload.get8(0));
} }
// ZCL_READ_REPORTING_CONFIGURATION_RESPONSE // ZCL_READ_REPORTING_CONFIGURATION_RESPONSE
void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) { void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
uint32_t i = 0; uint32_t i = 0;
uint32_t len = _payload.len(); uint32_t len = payload.len();
Z_attribute &attr_root = attr_list.addAttributePMEM(PSTR("ReadConfig")); Z_attribute &attr_root = attr_list.addAttributePMEM(PSTR("ReadConfig"));
Z_attribute_list attr_1; Z_attribute_list attr_1;
while (len >= i + 4) { while (len >= i + 4) {
uint8_t status = _payload.get8(i); uint8_t status = payload.get8(i);
uint8_t direction = _payload.get8(i+1); uint8_t direction = payload.get8(i+1);
uint16_t attrid = _payload.get16(i+2); uint16_t attrid = payload.get16(i+2);
Z_attribute_list attr_2; Z_attribute_list attr_2;
if (direction) { if (direction) {
@ -1550,7 +1570,7 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short));
uint16_t conv_attribute = pgm_read_word(&converter->attribute); uint16_t conv_attribute = pgm_read_word(&converter->attribute);
if ((conv_cluster == _cluster_id) && (conv_attribute == attrid)) { if ((conv_cluster == cluster) && (conv_attribute == attrid)) {
const char * attr_name = Z_strings + pgm_read_word(&converter->name_offset); const char * attr_name = Z_strings + pgm_read_word(&converter->name_offset);
attr_2.addAttribute(attr_name, true).setBool(true); attr_2.addAttribute(attr_name, true).setBool(true);
multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx));
@ -1565,22 +1585,22 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
// no error, decode data // no error, decode data
if (direction) { if (direction) {
// only Timeout period is present // only Timeout period is present
uint16_t attr_timeout = _payload.get16(i); uint16_t attr_timeout = payload.get16(i);
i += 2; i += 2;
attr_2.addAttributePMEM(PSTR("TimeoutPeriod")).setUInt((0xFFFF == attr_timeout) ? -1 : attr_timeout); attr_2.addAttributePMEM(PSTR("TimeoutPeriod")).setUInt((0xFFFF == attr_timeout) ? -1 : attr_timeout);
} else { } else {
// direction == 0, we have a data type // direction == 0, we have a data type
uint8_t attr_type = _payload.get8(i); uint8_t attr_type = payload.get8(i);
bool attr_discrete = Z_isDiscreteDataType(attr_type); bool attr_discrete = Z_isDiscreteDataType(attr_type);
uint16_t attr_min_interval = _payload.get16(i+1); uint16_t attr_min_interval = payload.get16(i+1);
uint16_t attr_max_interval = _payload.get16(i+3); uint16_t attr_max_interval = payload.get16(i+3);
i += 5; i += 5;
attr_2.addAttributePMEM(PSTR("MinInterval")).setUInt((0xFFFF == attr_min_interval) ? -1 : attr_min_interval); attr_2.addAttributePMEM(PSTR("MinInterval")).setUInt((0xFFFF == attr_min_interval) ? -1 : attr_min_interval);
attr_2.addAttributePMEM(PSTR("MaxInterval")).setUInt((0xFFFF == attr_max_interval) ? -1 : attr_max_interval); attr_2.addAttributePMEM(PSTR("MaxInterval")).setUInt((0xFFFF == attr_max_interval) ? -1 : attr_max_interval);
if (!attr_discrete) { if (!attr_discrete) {
// decode Reportable Change // decode Reportable Change
Z_attribute &attr_change = attr_2.addAttributePMEM(PSTR("ReportableChange")); Z_attribute &attr_change = attr_2.addAttributePMEM(PSTR("ReportableChange"));
i += parseSingleAttribute(attr_change, _payload, i, attr_type); i += parseSingleAttribute(attr_change, payload, i, attr_type);
if ((1 != multiplier) && (0 != multiplier)) { if ((1 != multiplier) && (0 != multiplier)) {
float fval = attr_change.getFloat(); float fval = attr_change.getFloat();
if (multiplier > 0) { fval = fval * multiplier; } if (multiplier > 0) { fval = fval * multiplier; }
@ -1590,7 +1610,7 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
} }
} }
} }
attr_1.addAttribute(_cluster_id, attrid).setStrRaw(attr_2.toString(true).c_str()); attr_1.addAttribute(cluster, attrid).setStrRaw(attr_2.toString(true).c_str());
} }
attr_root.setStrRaw(attr_1.toString(true).c_str()); attr_root.setStrRaw(attr_1.toString(true).c_str());
} }
@ -1598,16 +1618,16 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
// ZCL_READ_ATTRIBUTES_RESPONSE // ZCL_READ_ATTRIBUTES_RESPONSE
void ZCLFrame::parseReadAttributesResponse(Z_attribute_list& attr_list) { void ZCLFrame::parseReadAttributesResponse(Z_attribute_list& attr_list) {
uint32_t i = 0; uint32_t i = 0;
uint32_t len = _payload.len(); uint32_t len = payload.len();
while (len >= i + 4) { while (len >= i + 4) {
uint16_t attrid = _payload.get16(i); uint16_t attrid = payload.get16(i);
i += 2; i += 2;
uint8_t status = _payload.get8(i++); uint8_t status = payload.get8(i++);
if (0 == status) { if (0 == status) {
Z_attribute & attr = attr_list.addAttribute(_cluster_id, attrid); Z_attribute & attr = attr_list.addAttribute(cluster, attrid);
i += parseSingleAttribute(attr, _payload, i); i += parseSingleAttribute(attr, payload, i);
} }
} }
} }
@ -1618,15 +1638,15 @@ void ZCLFrame::parseResponse_inner(uint8_t cmd, bool cluster_specific, uint8_t s
// "Device" // "Device"
char s[12]; char s[12];
snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr); snprintf_P(s, sizeof(s), PSTR("0x%04X"), shortaddr);
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_DEVICE)).setStr(s); attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_DEVICE)).setStr(s);
// "Name" // "Name"
const char * friendlyName = zigbee_devices.getFriendlyName(_srcaddr); const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
if (friendlyName) { if (friendlyName) {
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_NAME)).setStr(friendlyName); attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_NAME)).setStr(friendlyName);
} }
// "Command" // "Command"
snprintf_P(s, sizeof(s), PSTR("%04X%c%02X"), _cluster_id, cluster_specific ? '!' : '_', cmd); snprintf_P(s, sizeof(s), PSTR("%04X%c%02X"), cluster, cluster_specific ? '!' : '_', cmd);
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_CMD)).setStr(s); attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_CMD)).setStr(s);
// "Status" // "Status"
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_STATUS)).setUInt(status); attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_STATUS)).setUInt(status);
@ -1635,8 +1655,8 @@ void ZCLFrame::parseResponse_inner(uint8_t cmd, bool cluster_specific, uint8_t s
// Add Endpoint // Add Endpoint
attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_ENDPOINT)).setUInt(_srcendpoint); attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_ENDPOINT)).setUInt(_srcendpoint);
// Add Group if non-zero // Add Group if non-zero
if (_groupaddr) { // TODO what about group zero if (groupaddr) { // TODO what about group zero
attr_list.group_id = _groupaddr; attr_list.group_id = groupaddr;
} }
// Add linkquality // Add linkquality
attr_list.lqi = _linkquality; attr_list.lqi = _linkquality;
@ -1647,9 +1667,9 @@ void ZCLFrame::parseResponse_inner(uint8_t cmd, bool cluster_specific, uint8_t s
// ZCL_DEFAULT_RESPONSE // ZCL_DEFAULT_RESPONSE
void ZCLFrame::parseResponse(void) { void ZCLFrame::parseResponse(void) {
if (_payload.len() < 2) { return; } // wrong format if (payload.len() < 2) { return; } // wrong format
uint8_t cmd = _payload.get8(0); uint8_t cmd = payload.get8(0);
uint8_t status = _payload.get8(1); uint8_t status = payload.get8(1);
parseResponse_inner(cmd, true, status); parseResponse_inner(cmd, true, status);
} }
@ -1665,22 +1685,22 @@ void Z_ResetDebounce(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, u
// Parse non-normalized attributes // Parse non-normalized attributes
void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) { void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
// Check if debounce is active and if the packet is a duplicate // Check if debounce is active and if the packet is a duplicate
Z_Device & device = zigbee_devices.getShortAddr(_srcaddr); Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
if ((device.debounce_endpoint != 0) && (device.debounce_endpoint == _srcendpoint) && (device.debounce_transact == _transact_seq)) { if ((device.debounce_endpoint != 0) && (device.debounce_endpoint == _srcendpoint) && (device.debounce_transact == transactseq)) {
// this is a duplicate, drop the packet // this is a duplicate, drop the packet
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), _srcaddr, _srcendpoint); AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), shortaddr, _srcendpoint);
} else { } else {
// reset the duplicate marker, parse the packet normally, and set a timer to reset the marker later (which will discard any existing timer for the same device/endpoint) // reset the duplicate marker, parse the packet normally, and set a timer to reset the marker later (which will discard any existing timer for the same device/endpoint)
device.debounce_endpoint = _srcendpoint; device.debounce_endpoint = _srcendpoint;
device.debounce_transact = _transact_seq; device.debounce_transact = transactseq;
zigbee_devices.setTimer(_srcaddr, 0 /* groupaddr */, USE_ZIGBEE_DEBOUNCE_COMMANDS, 0 /*clusterid*/, _srcendpoint, Z_CAT_DEBOUNCE_CMD, 0, &Z_ResetDebounce); zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, USE_ZIGBEE_DEBOUNCE_COMMANDS, 0 /*clusterid*/, _srcendpoint, Z_CAT_DEBOUNCE_CMD, 0, &Z_ResetDebounce);
convertClusterSpecific(attr_list, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload); convertClusterSpecific(attr_list, cluster, cmd, _frame_control.b.direction, shortaddr, _srcendpoint, payload);
if (!Settings->flag5.zb_disable_autoquery) { if (!Settings->flag5.zb_disable_autoquery) {
// read attributes unless disabled // read attributes unless disabled
if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator) if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator)
if (_wasbroadcast) { // only update for broadcast messages since we don't see unicast from device to device and we wouldn't know the target if (_wasbroadcast) { // only update for broadcast messages since we don't see unicast from device to device and we wouldn't know the target
sendHueUpdate(BAD_SHORTADDR, _groupaddr, _cluster_id); sendHueUpdate(BAD_SHORTADDR, groupaddr, cluster);
} }
} }
} }
@ -1688,18 +1708,18 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
// Send Default Response to acknowledge the attribute reporting // Send Default Response to acknowledge the attribute reporting
if (0 == _frame_control.b.disable_def_resp) { if (0 == _frame_control.b.disable_def_resp) {
// the device expects a default response // the device expects a default response
ZCLMessage zcl(2); // message is 4 bytes ZCLFrame zcl(2); // message is 4 bytes
zcl.shortaddr = _srcaddr; zcl.shortaddr = shortaddr;
zcl.cluster = _cluster_id; zcl.cluster = cluster;
zcl.endpoint = _srcendpoint; zcl.dstendpoint = _srcendpoint;
zcl.cmd = ZCL_DEFAULT_RESPONSE; zcl.cmd = ZCL_DEFAULT_RESPONSE;
zcl.manuf = _manuf_code; zcl.manuf = manuf;
zcl.clusterSpecific = false; /* not cluster specific */ zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */ zcl.needResponse = false; /* noresponse */
zcl.direct = true; /* direct no retry */ zcl.direct = true; /* direct no retry */
zcl.setTransac(_transact_seq); zcl.setTransac(transactseq);
zcl.buf.add8(_cmd_id); zcl.payload.add8(cmd);
zcl.buf.add8(0x00); // Status = OK zcl.payload.add8(0x00); // Status = OK
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }
@ -1707,7 +1727,7 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
// ====================================================================== // ======================================================================
// Convert AnalogValue according to the device type // Convert AnalogValue according to the device type
void ZCLFrame::syntheticAnalogValue(Z_attribute_list &attr_list, class Z_attribute &attr) { void ZCLFrame::syntheticAnalogValue(Z_attribute_list &attr_list, class Z_attribute &attr) {
const char * modelId_c = zigbee_devices.getModelId(_srcaddr); // null if unknown const char * modelId_c = zigbee_devices.getModelId(shortaddr); // null if unknown
String modelId((char*) modelId_c); String modelId((char*) modelId_c);
if (modelId.startsWith(F("lumi.sensor_cube"))) { if (modelId.startsWith(F("lumi.sensor_cube"))) {
@ -1731,7 +1751,7 @@ void ZCLFrame::syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribu
uint32_t i = 0; uint32_t i = 0;
uint32_t len = buf2.len(); uint32_t len = buf2.len();
const char * modelId_c = zigbee_devices.getModelId(_srcaddr); // null if unknown const char * modelId_c = zigbee_devices.getModelId(shortaddr); // null if unknown
String modelId((char*) modelId_c); String modelId((char*) modelId_c);
while (len >= 2 + i) { while (len >= 2 + i) {
@ -1830,7 +1850,7 @@ void ZCLFrame::syntheticAqaraSensor2(class Z_attribute_list &attr_list, class Z_
// Aqara Cube and Button // Aqara Cube and Button
void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, class Z_attribute &attr) { void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, class Z_attribute &attr) {
const char * modelId_c = zigbee_devices.findShortAddr(_srcaddr).modelId; // null if unknown const char * modelId_c = zigbee_devices.findShortAddr(shortaddr).modelId; // null if unknown
String modelId((char*) modelId_c); String modelId((char*) modelId_c);
if (modelId.startsWith(F("lumi.sensor_cube"))) { // only for Aqara cube if (modelId.startsWith(F("lumi.sensor_cube"))) { // only for Aqara cube

View File

@ -152,14 +152,14 @@ void ZigbeeHueGroups(String * lights) {
} }
void ZigbeeSendHue(uint16_t shortaddr, uint16_t cluster, uint8_t cmd, const SBuffer & s) { void ZigbeeSendHue(uint16_t shortaddr, uint16_t cluster, uint8_t cmd, const SBuffer & s) {
ZCLMessage zcl(s.len()); ZCLFrame zcl(s.len());
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = cluster; zcl.cluster = cluster;
zcl.cmd = cmd; zcl.cmd = cmd;
zcl.clusterSpecific = true; zcl.clusterSpecific = true;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.replace(s); zcl.payload.replace(s);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }

View File

@ -183,16 +183,16 @@ void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster
shortaddr = BAD_SHORTADDR; // if group address, don't send to device shortaddr = BAD_SHORTADDR; // if group address, don't send to device
} }
ZCLMessage zcl(attrs_len); // message is `attrs_len` bytes ZCLFrame zcl(attrs_len); // message is `attrs_len` bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.groupaddr = groupaddr; zcl.groupaddr = groupaddr;
zcl.cluster = cluster; zcl.cluster = cluster;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = ZCL_READ_ATTRIBUTES; zcl.cmd = ZCL_READ_ATTRIBUTES;
zcl.clusterSpecific = false; zcl.clusterSpecific = false;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.addBuffer(attrs, attrs_len); zcl.payload.addBuffer(attrs, attrs_len);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }

View File

@ -1380,15 +1380,15 @@ void Z_SendSimpleDescReq(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluste
// Iterate among // Iterate among
// //
void Z_SendDeviceInfoRequest(uint16_t shortaddr) { void Z_SendDeviceInfoRequest(uint16_t shortaddr) {
ZCLMessage zcl(4); // message is 4 bytes ZCLFrame zcl(4); // message is 4 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = 0; zcl.cluster = 0;
zcl.cmd = ZCL_READ_ATTRIBUTES; zcl.cmd = ZCL_READ_ATTRIBUTES;
zcl.clusterSpecific = false; zcl.clusterSpecific = false;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.add16(0x0005); zcl.payload.add16(0x0005);
zcl.buf.add16(0x0004); zcl.payload.add16(0x0004);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
@ -1396,15 +1396,15 @@ void Z_SendDeviceInfoRequest(uint16_t shortaddr) {
// Send single attribute read request in Timer // Send single attribute read request in Timer
// //
void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
ZCLMessage zcl(2); // message is 2 bytes ZCLFrame zcl(2); // message is 2 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = cluster; zcl.cluster = cluster;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = ZCL_READ_ATTRIBUTES; zcl.cmd = ZCL_READ_ATTRIBUTES;
zcl.clusterSpecific = false; zcl.clusterSpecific = false;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.add16(value); // 04000500 zcl.payload.add16(value); // 04000500
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
@ -1413,17 +1413,17 @@ void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t
// //
void Z_WriteCIEAddress(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { void Z_WriteCIEAddress(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending CIE Address for Cluster %d in Endpoint %d of Device 0x%04X"), cluster, endpoint, shortaddr); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending CIE Address for Cluster %d in Endpoint %d of Device 0x%04X"), cluster, endpoint, shortaddr);
ZCLMessage zcl(12); // message is 12 bytes ZCLFrame zcl(12); // message is 12 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = 0x0500; zcl.cluster = 0x0500;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = ZCL_WRITE_ATTRIBUTES; zcl.cmd = ZCL_WRITE_ATTRIBUTES;
zcl.clusterSpecific = false; zcl.clusterSpecific = false;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.add16(0x0010); // attribute 0x0010 zcl.payload.add16(0x0010); // attribute 0x0010
zcl.buf.add8(ZEUI64); zcl.payload.add8(ZEUI64);
zcl.buf.add64(localIEEEAddr); zcl.payload.add64(localIEEEAddr);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
@ -1433,16 +1433,16 @@ void Z_WriteCIEAddress(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster,
// //
void Z_SendCIEZoneEnrollResponse(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { void Z_SendCIEZoneEnrollResponse(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending Enroll Zone %d for Cluster %d in Endpoint %d of Device 0x%04X"), Z_B0(value), cluster, endpoint, shortaddr); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending Enroll Zone %d for Cluster %d in Endpoint %d of Device 0x%04X"), Z_B0(value), cluster, endpoint, shortaddr);
ZCLMessage zcl(2); // message is 2 bytes ZCLFrame zcl(2); // message is 2 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = 0x0500; zcl.cluster = 0x0500;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = 0x00; // Zone Enroll Response zcl.cmd = 0x00; // Zone Enroll Response
zcl.clusterSpecific = true; zcl.clusterSpecific = true;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.add8(0x00); // success zcl.payload.add8(0x00); // success
zcl.buf.add8(Z_B0(value)); // ZoneID zcl.payload.add8(Z_B0(value)); // ZoneID
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
@ -1579,15 +1579,15 @@ void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uin
if (buf.len() > 0) { if (buf.len() > 0) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), ResponseData()); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), ResponseData());
ZCLMessage zcl(buf.len()); // message is 4 bytes ZCLFrame zcl(buf.len()); // message is 4 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = cluster; zcl.cluster = cluster;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = ZCL_CONFIGURE_REPORTING; zcl.cmd = ZCL_CONFIGURE_REPORTING;
zcl.clusterSpecific = false; /* not cluster specific */ zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */ zcl.needResponse = false; /* noresponse */
zcl.direct = false; /* discover route */ zcl.direct = false; /* discover route */
zcl.buf.addBuffer(buf); zcl.payload.addBuffer(buf);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }
@ -1639,6 +1639,11 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
// log the packet details // log the packet details
zcl_received.log(); zcl_received.log();
#ifdef USE_BERRY
// Berry pre-process messages
// callBerryZigbeeDispatcher("pre", &zcl_received);
#endif // USE_BERRY
// create the device entry if it does not exist and if it's not the local device // create the device entry if it does not exist and if it's not the local device
Z_Device & device = (srcaddr != localShortAddr) ? zigbee_devices.getShortAddr(srcaddr) : Z_Device & device = (srcaddr != localShortAddr) ? zigbee_devices.getShortAddr(srcaddr) :
device_unk; device_unk;
@ -2097,9 +2102,9 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
for (uint32_t i=0; i<attr_len; i++) { for (uint32_t i=0; i<attr_len; i++) {
uint16_t attr_id = attr_list_ids[i]; uint16_t attr_id = attr_list_ids[i];
uint32_t ccccaaaa = (_cluster_id << 16) | attr_id; uint32_t ccccaaaa = (cluster << 16) | attr_id;
Z_attribute attr; Z_attribute attr;
attr.setKeyId(_cluster_id, attr_id); attr.setKeyId(cluster, attr_id);
switch (ccccaaaa) { switch (ccccaaaa) {
case 0x00000004: attr.setStr(PSTR(USE_ZIGBEE_MANUFACTURER)); break; // Manufacturer case 0x00000004: attr.setStr(PSTR(USE_ZIGBEE_MANUFACTURER)); break; // Manufacturer
@ -2149,8 +2154,8 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
break; break;
} }
if (!attr.isNone()) { if (!attr.isNone()) {
Z_parseAttributeKey(attr, _cluster_id); Z_parseAttributeKey(attr, cluster);
attr_list.addAttribute(_cluster_id, attr_id) = attr; attr_list.addAttribute(cluster, attr_id) = attr;
} }
} }
@ -2170,21 +2175,21 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
",\"Endpoint\":%d" ",\"Endpoint\":%d"
",\"Response\":%s}" ",\"Response\":%s}"
), ),
_srcaddr, _cluster_id, _srcendpoint, shortaddr, cluster, _srcendpoint,
attr_list.toString().c_str()); attr_list.toString().c_str());
// send // send
// all good, send the packet // all good, send the packet
ZCLMessage zcl(buf.len()); // message is 4 bytes ZCLFrame zcl(buf.len()); // message is 4 bytes
zcl.shortaddr = _srcaddr; zcl.shortaddr = shortaddr;
zcl.cluster = _cluster_id; zcl.cluster = cluster;
zcl.endpoint = _srcendpoint; zcl.dstendpoint = _srcendpoint;
zcl.cmd = ZCL_READ_ATTRIBUTES_RESPONSE; zcl.cmd = ZCL_READ_ATTRIBUTES_RESPONSE;
zcl.clusterSpecific = false; /* not cluster specific */ zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */ zcl.needResponse = false; /* noresponse */
zcl.direct = true; /* direct response */ zcl.direct = true; /* direct response */
zcl.setTransac(_transact_seq); zcl.setTransac(transactseq);
zcl.buf.addBuffer(buf); zcl.payload.addBuffer(buf);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }

View File

@ -753,12 +753,12 @@ void CmndZbEZSPSend(void)
// - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr // - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr
// - len: length of the 'msg' payload // - len: length of the 'msg' payload
// - needResponse: boolean, true = we ask the target to respond, false = the target should not respond // - needResponse: boolean, true = we ask the target to respond, false = the target should not respond
// - transac: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number // - transactseq: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number
// Returns: None // Returns: None
// //
void ZigbeeZCLSend_Raw(const ZCLMessage &zcl) { void ZigbeeZCLSend_Raw(const ZCLFrame &zcl) {
SBuffer buf(32+zcl.buf.len()); SBuffer buf(32+zcl.payload.len());
#ifdef USE_ZIGBEE_ZNP #ifdef USE_ZIGBEE_ZNP
buf.add8(Z_SREQ | Z_AF); // 24 buf.add8(Z_SREQ | Z_AF); // 24
@ -770,23 +770,23 @@ void ZigbeeZCLSend_Raw(const ZCLMessage &zcl) {
} else { } else {
buf.add8(Z_Addr_ShortAddress); // 02 buf.add8(Z_Addr_ShortAddress); // 02
buf.add64(zcl.shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded buf.add64(zcl.shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded
buf.add8(zcl.endpoint); // dest endpoint buf.add8(zcl.dstendpoint); // dest endpoint
} }
buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan
buf.add8(0x01); // source endpoint buf.add8(0x01); // source endpoint
buf.add16(zcl.cluster); buf.add16(zcl.cluster);
buf.add8(zcl.transac); // transac buf.add8(zcl.transactseq); // transactseq
buf.add8(0x30); // 30 options buf.add8(0x30); // 30 options
buf.add8(0x1E); // 1E radius buf.add8(0x1E); // 1E radius
buf.add16(3 + zcl.buf.len() + (zcl.manuf ? 2 : 0)); buf.add16(3 + zcl.payload.len() + (zcl.manuf ? 2 : 0));
buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
if (zcl.manuf) { if (zcl.manuf) {
buf.add16(zcl.manuf); // add Manuf Id if not null buf.add16(zcl.manuf); // add Manuf Id if not null
} }
buf.add8(zcl.transac); // Transaction Sequence Number buf.add8(zcl.transactseq); // Transaction Sequence Number
buf.add8(zcl.cmd); buf.add8(zcl.cmd);
buf.addBuffer(zcl.buf); buf.addBuffer(zcl.payload);
ZigbeeZNPSend(buf.getBuffer(), buf.len()); ZigbeeZNPSend(buf.getBuffer(), buf.len());
#endif // USE_ZIGBEE_ZNP #endif // USE_ZIGBEE_ZNP
@ -801,25 +801,25 @@ void ZigbeeZCLSend_Raw(const ZCLMessage &zcl) {
buf.add16(Z_PROF_HA); // Home Automation profile buf.add16(Z_PROF_HA); // Home Automation profile
buf.add16(zcl.cluster); // cluster buf.add16(zcl.cluster); // cluster
buf.add8(0x01); // srcEp buf.add8(0x01); // srcEp
buf.add8(zcl.endpoint); // dstEp buf.add8(zcl.dstendpoint); // dstEp
if (zcl.direct) { if (zcl.direct) {
buf.add16(0x0000); // APS frame buf.add16(0x0000); // APS frame
} else { } else {
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
} }
buf.add16(zcl.groupaddr); // groupId buf.add16(zcl.groupaddr); // groupId
buf.add8(zcl.transac); buf.add8(zcl.transactseq);
// end of ApsFrame // end of ApsFrame
buf.add8(0x01); // tag TODO buf.add8(0x01); // tag TODO
buf.add8(3 + zcl.buf.len() + (zcl.manuf ? 2 : 0)); buf.add8(3 + zcl.payload.len() + (zcl.manuf ? 2 : 0));
buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
if (zcl.manuf) { if (zcl.manuf) {
buf.add16(zcl.manuf); // add Manuf Id if not null buf.add16(zcl.manuf); // add Manuf Id if not null
} }
buf.add8(zcl.transac); // Transaction Sequance Number buf.add8(zcl.transactseq); // Transaction Sequance Number
buf.add8(zcl.cmd); buf.add8(zcl.cmd);
buf.addBuffer(zcl.buf); buf.addBuffer(zcl.payload);
} else { } else {
// send broadcast group address, aka groupcast // send broadcast group address, aka groupcast
buf.add16(EZSP_sendMulticast); // 3800 buf.add16(EZSP_sendMulticast); // 3800
@ -827,27 +827,27 @@ void ZigbeeZCLSend_Raw(const ZCLMessage &zcl) {
buf.add16(Z_PROF_HA); // Home Automation profile buf.add16(Z_PROF_HA); // Home Automation profile
buf.add16(zcl.cluster); // cluster buf.add16(zcl.cluster); // cluster
buf.add8(0x01); // srcEp buf.add8(0x01); // srcEp
buf.add8(zcl.endpoint); // broadcast endpoint for groupcast buf.add8(zcl.dstendpoint); // broadcast endpoint for groupcast
if (zcl.direct) { if (zcl.direct) {
buf.add16(0x0000); // APS frame buf.add16(0x0000); // APS frame
} else { } else {
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
} }
buf.add16(zcl.groupaddr); // groupId buf.add16(zcl.groupaddr); // groupId
buf.add8(zcl.transac); buf.add8(zcl.transactseq);
// end of ApsFrame // end of ApsFrame
buf.add8(0); // hops, 0x00 = EMBER_MAX_HOPS buf.add8(0); // hops, 0x00 = EMBER_MAX_HOPS
buf.add8(7); // nonMemberRadius, 7 = infinite buf.add8(7); // nonMemberRadius, 7 = infinite
buf.add8(0x01); // tag TODO buf.add8(0x01); // tag TODO
buf.add8(3 + zcl.buf.len() + (zcl.manuf ? 2 : 0)); buf.add8(3 + zcl.payload.len() + (zcl.manuf ? 2 : 0));
buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
if (zcl.manuf) { if (zcl.manuf) {
buf.add16(zcl.manuf); // add Manuf Id if not null buf.add16(zcl.manuf); // add Manuf Id if not null
} }
buf.add8(zcl.transac); // Transaction Sequance Number buf.add8(zcl.transactseq); // Transaction Sequance Number
buf.add8(zcl.cmd); buf.add8(zcl.cmd);
buf.addBuffer(zcl.buf); buf.addBuffer(zcl.payload);
} }
ZigbeeEZSPSendCmd(buf.buf(), buf.len()); ZigbeeEZSPSendCmd(buf.buf(), buf.len());

View File

@ -178,18 +178,18 @@ void CmndZbReset(void) {
// - param: pointer to HEX string for payload, should not be nullptr // - param: pointer to HEX string for payload, should not be nullptr
// Returns: None // Returns: None
// //
void zigbeeZCLSendCmd(class ZCLMessage &zcl) { void zigbeeZCLSendCmd(class ZCLFrame &zcl) {
if ((0 == zcl.endpoint) && (zcl.validShortaddr())) { if ((0 == zcl.dstendpoint) && (zcl.validShortaddr())) {
// endpoint is not specified, let's try to find it from shortAddr, unless it's a group address // endpoint is not specified, let's try to find it from shortAddr, unless it's a group address
zcl.endpoint = zigbee_devices.findFirstEndpoint(zcl.shortaddr); zcl.dstendpoint = zigbee_devices.findFirstEndpoint(zcl.shortaddr);
if (0x00 == zcl.endpoint) { zcl.endpoint = 0x01; } // if we don't know the endpoint, try 0x01 if (0x00 == zcl.dstendpoint) { zcl.dstendpoint = 0x01; } // if we don't know the endpoint, try 0x01
//AddLog(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); //AddLog(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
} }
// AddLog(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %_B"), // AddLog(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %_B"),
// zcl.shortaddr, zcl.groupaddr, zcl.cluster, zcl.endpoint, zcl.cmd, &zcl.buf); // zcl.shortaddr, zcl.groupaddr, zcl.cluster, zcl.dstendpoint, zcl.cmd, &zcl.payload);
if ((0 == zcl.endpoint) && (zcl.validShortaddr())) { // endpoint null is ok for group address if ((0 == zcl.dstendpoint) && (zcl.validShortaddr())) { // endpoint null is ok for group address
AddLog(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); AddLog(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
return; return;
} }
@ -197,12 +197,12 @@ void zigbeeZCLSendCmd(class ZCLMessage &zcl) {
// everything is good, we can send the command // everything is good, we can send the command
if (!zcl.transacSet) { if (!zcl.transacSet) {
zcl.transac = zigbee_devices.getNextSeqNumber(zcl.shortaddr); zcl.transactseq = zigbee_devices.getNextSeqNumber(zcl.shortaddr);
zcl.transacSet = true; zcl.transacSet = true;
} }
AddLog(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend %s: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%_B\""), AddLog(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend %s: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%_B\""),
zcl.validShortaddr() ? "device":"group", zcl.validShortaddr() ? zcl.shortaddr : zcl.groupaddr, zcl.endpoint, zcl.cluster, zcl.cmd, &zcl.buf); zcl.validShortaddr() ? "device":"group", zcl.validShortaddr() ? zcl.shortaddr : zcl.groupaddr, zcl.dstendpoint, zcl.cluster, zcl.cmd, &zcl.payload);
ZigbeeZCLSend_Raw(zcl); ZigbeeZCLSend_Raw(zcl);
@ -210,7 +210,7 @@ void zigbeeZCLSendCmd(class ZCLMessage &zcl) {
if (zcl.clusterSpecific) { if (zcl.clusterSpecific) {
if (!Settings->flag5.zb_disable_autoquery) { if (!Settings->flag5.zb_disable_autoquery) {
// read back attribute value unless it is disabled // read back attribute value unless it is disabled
sendHueUpdate(zcl.shortaddr, zcl.groupaddr, zcl.cluster, zcl.endpoint); sendHueUpdate(zcl.shortaddr, zcl.groupaddr, zcl.cluster, zcl.dstendpoint);
} }
} }
} }
@ -321,15 +321,15 @@ bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_stat
// Parse "Report", "Write", "Response" or "Config" attribute // Parse "Report", "Write", "Response" or "Config" attribute
// Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01) // Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01)
// //
void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLMessage & zcl) { void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLFrame & zcl) {
zcl.buf.reserve(200); // buffer to store the binary output of attibutes zcl.payload.reserve(200); // buffer to store the binary output of attibutes
SBuffer & buf = zcl.buf; // synonym SBuffer & buf = zcl.payload; // synonym
if (nullptr == XdrvMailbox.command) { if (nullptr == XdrvMailbox.command) {
XdrvMailbox.command = (char*) ""; // prevent a crash when calling ReponseCmndChar and there was no previous command XdrvMailbox.command = (char*) ""; // prevent a crash when calling ReponseCmndChar and there was no previous command
} }
bool tuya_protocol = zigbee_devices.isTuyaProtocol(zcl.shortaddr, zcl.endpoint); bool tuya_protocol = zigbee_devices.isTuyaProtocol(zcl.shortaddr, zcl.dstendpoint);
// iterate on keys // iterate on keys
for (auto key : val_pubwrite.getObject()) { for (auto key : val_pubwrite.getObject()) {
@ -449,7 +449,7 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLMessage & zc
} }
// Parse the "Send" attribute and send the command // Parse the "Send" attribute and send the command
void ZbSendSend(class JsonParserToken val_cmd, ZCLMessage & zcl) { void ZbSendSend(class JsonParserToken val_cmd, ZCLFrame & zcl) {
zcl.clusterSpecific = true; zcl.clusterSpecific = true;
static const char delim[] = ", "; // delimiters for parameters static const char delim[] = ", "; // delimiters for parameters
@ -525,7 +525,7 @@ void ZbSendSend(class JsonParserToken val_cmd, ZCLMessage & zcl) {
} else { } else {
zcl.cmd = cmd_var; // or simply copy the cmd number zcl.cmd = cmd_var; // or simply copy the cmd number
} }
zigbeeCmdAddParams(zcl.buf, tasmota_cmd, x, y, z); // fill in parameters zigbeeCmdAddParams(zcl.payload, tasmota_cmd, x, y, z); // fill in parameters
} else { } else {
// we have zero command, pass through until last error for missing command // we have zero command, pass through until last error for missing command
return; return;
@ -563,7 +563,7 @@ void ZbSendSend(class JsonParserToken val_cmd, ZCLMessage & zcl) {
// delimiter is optional // delimiter is optional
if ('/' == *data) { data++; } // skip delimiter if ('/' == *data) { data++; } // skip delimiter
zcl.buf.replace(SBuffer::SBufferFromHex(data, strlen(data))); zcl.payload.replace(SBuffer::SBufferFromHex(data, strlen(data)));
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} else { } else {
// we have an unsupported command type // we have an unsupported command type
@ -574,7 +574,7 @@ void ZbSendSend(class JsonParserToken val_cmd, ZCLMessage & zcl) {
} }
// Parse the "Send" attribute and send the command // Parse the "Send" attribute and send the command
void ZbSendRead(JsonParserToken val_attr, ZCLMessage & zcl) { void ZbSendRead(JsonParserToken val_attr, ZCLFrame & zcl) {
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5} // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5}
// ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"} // ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"}
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]} // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]}
@ -670,9 +670,9 @@ void ZbSendRead(JsonParserToken val_attr, ZCLMessage & zcl) {
if (attrs_len > 0) { if (attrs_len > 0) {
// all good, send the packet // all good, send the packet
zcl.buf.reserve(attrs_len); zcl.payload.reserve(attrs_len);
zcl.buf.setLen(0); // clear any previous buffer zcl.payload.setLen(0); // clear any previous buffer
zcl.buf.addBuffer(attrs, attrs_len); zcl.payload.addBuffer(attrs, attrs_len);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
ResponseCmndDone(); ResponseCmndDone();
} else { } else {
@ -712,7 +712,7 @@ void CmndZbSend(void) {
if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// params // params
ZCLMessage zcl; // prepare the ZCL structure ZCLFrame zcl; // prepare the ZCL structure
// parse "Device" and "Group" // parse "Device" and "Group"
JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)]; JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)];
@ -733,15 +733,15 @@ void CmndZbSend(void) {
// read other parameters // read other parameters
zcl.cluster = root.getUInt(PSTR(D_CMND_ZIGBEE_CLUSTER), zcl.cluster); zcl.cluster = root.getUInt(PSTR(D_CMND_ZIGBEE_CLUSTER), zcl.cluster);
zcl.endpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), zcl.endpoint); zcl.dstendpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), zcl.dstendpoint);
zcl.manuf = root.getUInt(PSTR(D_CMND_ZIGBEE_MANUF), zcl.manuf); zcl.manuf = root.getUInt(PSTR(D_CMND_ZIGBEE_MANUF), zcl.manuf);
// infer endpoint // infer endpoint
if (!zcl.validShortaddr()) { if (!zcl.validShortaddr()) {
zcl.endpoint = 0xFF; // endpoint not used for group addresses, so use a dummy broadcast endpoint zcl.dstendpoint = 0xFF; // endpoint not used for group addresses, so use a dummy broadcast endpoint
} else if (!zcl.validEndpoint()) { // if it was not already specified, try to guess it } else if (!zcl.validEndpoint()) { // if it was not already specified, try to guess it
zcl.endpoint = zigbee_devices.findFirstEndpoint(zcl.shortaddr); zcl.dstendpoint = zigbee_devices.findFirstEndpoint(zcl.shortaddr);
AddLog(LOG_LEVEL_DEBUG, PSTR("ZIG: guessing endpoint %d"), zcl.endpoint); AddLog(LOG_LEVEL_DEBUG, PSTR("ZIG: guessing endpoint %d"), zcl.dstendpoint);
} }
if (!zcl.validEndpoint()) { // after this, if it is still zero, then it's an error if (!zcl.validEndpoint()) { // after this, if it is still zero, then it's an error
ResponseCmndChar_P(PSTR("Missing endpoint")); ResponseCmndChar_P(PSTR("Missing endpoint"));
@ -848,7 +848,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
// Information about source device: "Device", "Endpoint", "Cluster" // Information about source device: "Device", "Endpoint", "Cluster"
// - the source endpoint must have a known IEEE address // - the source endpoint must have a known IEEE address
const Z_Device & src_device = zigbee_devices.parseDeviceFromName(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE), nullptr)); const Z_Device & src_device = zigbee_devices.parseDeviceFromName(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE)));
if (!src_device.valid()) { ResponseCmndChar_P(PSTR("Unknown source device")); return; } if (!src_device.valid()) { ResponseCmndChar_P(PSTR("Unknown source device")); return; }
// check if IEEE address is known // check if IEEE address is known
uint64_t srcLongAddr = src_device.longaddr; uint64_t srcLongAddr = src_device.longaddr;
@ -961,7 +961,7 @@ void CmndZbUnbind(void) {
// //
void CmndZbLeave(void) { void CmndZbLeave(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data).shortaddr; uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload).shortaddr;
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
#ifdef USE_ZIGBEE_ZNP #ifdef USE_ZIGBEE_ZNP
@ -993,7 +993,7 @@ void CmndZbLeave(void) {
void CmndZbBindState_or_Map(bool map) { void CmndZbBindState_or_Map(bool map) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t parsed_shortaddr;; uint16_t parsed_shortaddr;;
uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, &parsed_shortaddr).shortaddr; uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, &parsed_shortaddr, XdrvMailbox.payload).shortaddr;
if (BAD_SHORTADDR == shortaddr) { if (BAD_SHORTADDR == shortaddr) {
if ((map) && (parsed_shortaddr != shortaddr)) { if ((map) && (parsed_shortaddr != shortaddr)) {
shortaddr = parsed_shortaddr; // allow a non-existent address when ZbMap shortaddr = parsed_shortaddr; // allow a non-existent address when ZbMap
@ -1069,7 +1069,7 @@ void CmndZbProbe(void) {
// //
void CmndZbProbeOrPing(boolean probe) { void CmndZbProbeOrPing(boolean probe) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data).shortaddr; uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload).shortaddr;
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// set a timer for Reachable - 2s default value // set a timer for Reachable - 2s default value
@ -1107,7 +1107,7 @@ void CmndZbName(void) {
strtok_r(XdrvMailbox.data, ",", &p); strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // it's the only case where we create a new device Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // it's the only case where we create a new device
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p == nullptr) { if (p == nullptr) {
@ -1139,7 +1139,7 @@ void CmndZbModelId(void) {
strtok_r(XdrvMailbox.data, ",", &p); strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p != nullptr) { if (p != nullptr) {
@ -1166,7 +1166,7 @@ void CmndZbLight(void) {
strtok_r(XdrvMailbox.data, ", ", &p); strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p) { if (p) {
@ -1210,7 +1210,7 @@ void CmndZbOccupancy(void) {
strtok_r(XdrvMailbox.data, ", ", &p); strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
int8_t occupancy_time = -1; int8_t occupancy_time = -1;
@ -1237,7 +1237,7 @@ void CmndZbOccupancy(void) {
// //
void CmndZbForget(void) { void CmndZbForget(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// everything is good, we can send the command // everything is good, we can send the command
@ -1267,7 +1267,7 @@ void CmndZbInfo(void) {
CmndZbInfo_inner(device); CmndZbInfo_inner(device);
} }
} else { // try JSON } else { // try JSON
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// everything is good, we can send the command // everything is good, we can send the command
@ -1337,7 +1337,7 @@ void CmndZbenroll(void) {
if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry
char argument[XdrvMailbox.data_len]; char argument[XdrvMailbox.data_len];
Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1)); Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1), nullptr, XdrvMailbox.payload);
int enrollEndpoint = atoi(ArgV(argument, 2)); int enrollEndpoint = atoi(ArgV(argument, 2));
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
@ -1355,7 +1355,7 @@ void CmndZbcie(void) {
if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry
char argument[XdrvMailbox.data_len]; char argument[XdrvMailbox.data_len];
Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1)); Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1), nullptr, XdrvMailbox.payload);
int enrollEndpoint = atoi(ArgV(argument, 2)); int enrollEndpoint = atoi(ArgV(argument, 2));
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
@ -1576,7 +1576,7 @@ void CmndZbStatus(void) {
if (0 == XdrvMailbox.index) { if (0 == XdrvMailbox.index) {
dump = zigbee_devices.dumpCoordinator(); dump = zigbee_devices.dumpCoordinator();
} else { } else {
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload);
if (XdrvMailbox.data_len > 0) { if (XdrvMailbox.data_len > 0) {
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device); dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device);
@ -1608,7 +1608,7 @@ void CmndZbData(void) {
strtok_r(XdrvMailbox.data, ",", &p); strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p) { if (p) {

View File

@ -152,7 +152,7 @@ void PWMModulePreInit(void)
#endif // USE_PWM_DIMMER_REMOTE #endif // USE_PWM_DIMMER_REMOTE
} }
// bri: -1 = set to current light bri, -2 = timeout, 0-255 = set to bri // bri: -1 = set to last local light bri, -2 = timeout, 0-255 = set to bri
void PWMDimmerSetBrightnessLeds(int32_t bri) void PWMDimmerSetBrightnessLeds(int32_t bri)
{ {
// Find out how many of the LEDs have their ledmask bit set. // Find out how many of the LEDs have their ledmask bit set.
@ -169,7 +169,7 @@ void PWMDimmerSetBrightnessLeds(int32_t bri)
if (leds) { if (leds) {
led_timeout_seconds = 5; led_timeout_seconds = 5;
if (bri < 0) { if (bri < 0) {
bri = ((bri == -2 && Settings->flag4.led_timeout) || !Light.power ? 0 : light_state.getBri()); bri = ((bri == -2 && Settings->flag4.led_timeout) || !Light.power ? 0 : TasmotaGlobal.pwm_dimmer_led_bri);
if (!bri || !Settings->flag4.led_timeout) led_timeout_seconds = 0; if (!bri || !Settings->flag4.led_timeout) led_timeout_seconds = 0;
} }
@ -438,13 +438,13 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed)
if (!active_remote_pwm_dimmer) { if (!active_remote_pwm_dimmer) {
#endif // USE_PWM_DIMMER_REMOTE #endif // USE_PWM_DIMMER_REMOTE
// Toggle the powered-off LED option. // Toggle the LED timeout.
if (down_button_tapped) { if (down_button_tapped) {
Settings->flag4.led_timeout ^= 1; Settings->flag4.led_timeout ^= 1;
if (Light.power) PWMDimmerSetBrightnessLeds(Settings->flag4.led_timeout ? 0 : -1); if (Light.power) PWMDimmerSetBrightnessLeds(Settings->flag4.led_timeout ? 0 : -1);
} }
// Toggle the LED timeout. // Toggle the powered-off LED option.
else { else {
Settings->flag4.powered_off_led ^= 1; Settings->flag4.powered_off_led ^= 1;
PWMDimmerSetPoweredOffLed(); PWMDimmerSetPoweredOffLed();

View File

@ -911,7 +911,7 @@ void HandleWebcamMjpegTask(void) {
void HandleWebcamRoot(void) { void HandleWebcamRoot(void) {
//CamServer->redirect("http://" + String(ip) + ":81/cam.mjpeg"); //CamServer->redirect("http://" + String(ip) + ":81/cam.mjpeg");
Wc.CamServer->sendHeader("Location", WiFi.localIP().toString() + ":81/cam.mjpeg"); Wc.CamServer->sendHeader("Location", "/cam.mjpeg");
Wc.CamServer->send(302, "", ""); Wc.CamServer->send(302, "", "");
AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Root called")); AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Root called"));
} }

View File

@ -137,16 +137,15 @@
#define SSPM_FUNC_SET_TIME 12 // 0x0C #define SSPM_FUNC_SET_TIME 12 // 0x0C
#define SSPM_FUNC_IAMHERE 13 // 0x0D #define SSPM_FUNC_IAMHERE 13 // 0x0D
#define SSPM_FUNC_INIT_SCAN 16 // 0x10 #define SSPM_FUNC_INIT_SCAN 16 // 0x10
#define SSPM_FUNC_UPLOAD_HEADER 20 // 0x14 - Upload header #define SSPM_FUNC_UPLOAD_HEADER 20 // 0x14 - SPI Upload header
#define SSPM_FUNC_UNITS 21 // 0x15 #define SSPM_FUNC_UNITS 21 // 0x15
#define SSPM_FUNC_GET_ENERGY_TOTAL 22 // 0x16 #define SSPM_FUNC_GET_ENERGY_TOTAL 22 // 0x16
#define SSPM_FUNC_GET_ENERGY 24 // 0x18 #define SSPM_FUNC_GET_ENERGY 24 // 0x18
#define SSPM_FUNC_GET_LOG 26 // 0x1A #define SSPM_FUNC_GET_LOG 26 // 0x1A
#define SSPM_FUNC_ENERGY_PERIOD 27 // 0x1B #define SSPM_FUNC_ENERGY_PERIOD 27 // 0x1B
#define SSPM_FUNC_RESET 28 // 0x1C - Remove device from eWelink and factory reset #define SSPM_FUNC_RESET 28 // 0x1C - Remove device from eWelink and factory reset
#define SSPM_FUNC_ARM_RESTART 30 // 0x1E - Restart ARM #define SSPM_FUNC_UPLOAD_DATA 31 // 0x1F - SPI Upload incremental data blocks of max 512 bytes to ARM
#define SSPM_FUNC_UPLOAD_DATA 31 // 0x1F - Upload incremental data blocks of max 512 bytes to ARM #define SSPM_FUNC_UPLOAD_DONE 33 // 0x21 - SPI Finish upload
#define SSPM_FUNC_UPLOAD_DONE 33 // 0x21 - Finish upload
#define SSPM_FUNC_GET_NEW1 37 // 0x25 #define SSPM_FUNC_GET_NEW1 37 // 0x25
// From ARM to ESP // From ARM to ESP
@ -155,6 +154,7 @@
#define SSPM_FUNC_SCAN_START 15 // 0x0F #define SSPM_FUNC_SCAN_START 15 // 0x0F
#define SSPM_FUNC_SCAN_RESULT 19 // 0x13 #define SSPM_FUNC_SCAN_RESULT 19 // 0x13
#define SSPM_FUNC_SCAN_DONE 25 // 0x19 #define SSPM_FUNC_SCAN_DONE 25 // 0x19
#define SSPM_FUNC_UPLOAD_DONE_ACK 30 // 0x1E - Restart ARM
// Unknown // Unknown
#define SSPM_FUNC_01 1 // 0x01 #define SSPM_FUNC_01 1 // 0x01
@ -535,6 +535,20 @@ void SSPMSendCmnd(uint32_t command) {
/*********************************************************************************************/ /*********************************************************************************************/
void SSPMSendFindAck(void) {
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 01 00 00 FC 73
Marker |Module id |Ac|Cm|Size | |Ix|Chksm|
*/
SSPMInitSend();
SspmBuffer[15] = 0x80; // Ack
// SspmBuffer[16] = SSPM_FUNC_FIND; // 0x00
SspmBuffer[18] = 1;
SspmBuffer[19] = 0;
SSPMSend(23);
}
void SSPMSendOPS(uint32_t relay) { void SSPMSendOPS(uint32_t relay) {
/* /*
Overload Protection Overload Protection
@ -799,21 +813,6 @@ void SSPMSendInitScan(void) {
AddLog(LOG_LEVEL_DEBUG, PSTR("SPM: Start relay scan...")); AddLog(LOG_LEVEL_DEBUG, PSTR("SPM: Start relay scan..."));
} }
void SSPMSendUploadHeader(void) {
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 0b 09 09 00 1b e5 a4 c7 00 02 88 74 00 6d df
Marker | | |Cm|Size | |Checksum |UploadSize |Ix|Chksm|
*/
SSPMInitSend();
SspmBuffer[16] = SSPM_FUNC_UPLOAD_HEADER; // 0x14
SspmBuffer[18] = 0x0B;
SspmBuffer[30] = 0;
SSPMSend(33);
}
void SSPMSendGetEnergyTotal(uint32_t relay) { void SSPMSendGetEnergyTotal(uint32_t relay) {
/* /*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
@ -923,49 +922,6 @@ void SSPMSendGetEnergyPeriod(uint32_t relay) {
} }
void SSPMSendArmRestart(void) {
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
aa 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 00 01 00 01 fe 05
Marker | | |Cm|Size | |Ix|Chksm|
*/
SSPMInitSend();
SspmBuffer[16] = SSPM_FUNC_ARM_RESTART; // 0x1E
SspmBuffer[18] = 1;
Sspm->command_sequence++;
SspmBuffer[20] = Sspm->command_sequence;
SSPMSend(23);
}
void SSPMSendUpload(void) {
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 539 540 541 542 543 544 545
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 02 0c 00 00 00 00 00 00 02 00 a2 99 c3 22 00 00 01 20 cd 95 01 08 ... 04 48 af f3 01 xx yy
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 02 0c 00 00 02 00 00 00 02 00 27 f7 24 87 00 80 01 23 23 70 10 bd ... 21 fa 04 f3 02 xx yy
...
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 02 0c 00 02 86 00 00 00 02 00 f8 f5 25 6d f1 61 00 08 02 01 ff 00 ... 44 xx yy
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 00 80 00 02 88 00 00 00 00 74 95 4e 01 c1 c5 e5 02 08 c5 e5 02 08 ... 45 xx yy
Marker | | |Cm|Size |Address |UploadSize |Checksum |512 data bytes |Ix |Chksm |
*/
SSPMInitSend();
SspmBuffer[16] = SSPM_FUNC_UPLOAD_DATA; // 0x1F
Sspm->command_sequence++;
SspmBuffer[543] = Sspm->command_sequence;
SSPMSend(546);
}
void SSPMSendUploadDone(void) {
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 00 46 32 da
Marker | | |Cm|Size |Ix|Chksm|
*/
SSPMSendCmnd(SSPM_FUNC_UPLOAD_DONE); // 0x21
}
void SSPMSendGetNew1(uint32_t module) { void SSPMSendGetNew1(uint32_t module) {
/* /*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
@ -1635,6 +1591,14 @@ void SSPMHandleReceivedData(void) {
SSPMSendSetTime(); SSPMSendSetTime();
break; break;
case SSPM_FUNC_UPLOAD_DONE_ACK:
/* 0x1E
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
aa 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 00 01 00 01 fe 05
Marker | | |Cm|Size | |Ix|Chksm|
*/
SSPMSendFindAck();
break;
} }
} }
} }
@ -1766,6 +1730,54 @@ bool SSPMSendSPIFind(void) {
return false; return false;
} }
void SSPMSendSPIUploadHeader(void) {
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 0b 09 09 00 1b e5 a4 c7 00 02 88 74 00 6d df
Marker | | |Cm|Size | |Checksum |UploadSize |Ix|Chksm|
*/
SSPMInitSend();
SspmBuffer[16] = SSPM_FUNC_UPLOAD_HEADER; // 0x14
SspmBuffer[18] = 0x0B;
SspmBuffer[30] = 0;
SSPMSendSPI(33);
}
void SSPMSendSPIUpload(void) {
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 539 540 541 542 543 544 545
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 02 0c 00 00 00 00 00 00 02 00 a2 99 c3 22 00 00 01 20 cd 95 01 08 ... 04 48 af f3 01 xx yy
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 02 0c 00 00 02 00 00 00 02 00 27 f7 24 87 00 80 01 23 23 70 10 bd ... 21 fa 04 f3 02 xx yy
...
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 02 0c 00 02 86 00 00 00 02 00 f8 f5 25 6d f1 61 00 08 02 01 ff 00 ... 44 xx yy
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 00 80 00 02 88 00 00 00 00 74 95 4e 01 c1 c5 e5 02 08 c5 e5 02 08 ... 45 xx yy
Marker | | |Cm|Size |Address |UploadSize |Checksum |512 data bytes |Ix |Chksm |
*/
SSPMInitSend();
SspmBuffer[16] = SSPM_FUNC_UPLOAD_DATA; // 0x1F
Sspm->command_sequence++;
SspmBuffer[543] = Sspm->command_sequence;
SSPMSendSPI(546);
}
void SSPMSendSPIUploadDone(void) {
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 00 46 32 da
Marker | | |Cm|Size |Ix|Chksm|
*/
SSPMInitSend();
SspmBuffer[16] = SSPM_FUNC_UPLOAD_DONE; // 0x21
Sspm->command_sequence++;
SspmBuffer[19] = Sspm->command_sequence;
SSPMSendSPI(22);
}
/*********************************************************************************************/ /*********************************************************************************************/
void SSPMInit(void) { void SSPMInit(void) {
@ -1848,7 +1860,7 @@ void SSPMEvery100ms(void) {
// Fix race condition if the ARM doesn't respond // Fix race condition if the ARM doesn't respond
if ((Sspm->mstate > SPM_NONE) && (Sspm->mstate < SPM_SEND_FUNC_UNITS)) { if ((Sspm->mstate > SPM_NONE) && (Sspm->mstate < SPM_SEND_FUNC_UNITS)) {
Sspm->counter++; Sspm->counter++;
if (Sspm->counter > 20) { if (Sspm->counter > 30) {
Sspm->mstate = SPM_NONE; Sspm->mstate = SPM_NONE;
Sspm->error_led_blinks = 255; Sspm->error_led_blinks = 255;
} }
@ -2146,13 +2158,13 @@ const char kSSPMCommands[] PROGMEM = "SSPM|" // Prefix
"Display|Dump|" // SetOptions "Display|Dump|" // SetOptions
"Log|Energy|History|Scan|IamHere|" "Log|Energy|History|Scan|IamHere|"
"Reset|Map|Overload|" "Reset|Map|Overload|"
D_CMND_ENERGYTOTAL "|" D_CMND_ENERGYYESTERDAY; D_CMND_ENERGYTOTAL "|" D_CMND_ENERGYYESTERDAY "|Send";
void (* const SSPMCommand[])(void) PROGMEM = { void (* const SSPMCommand[])(void) PROGMEM = {
&CmndSSPMDisplay, &CmndSSPMDump, &CmndSSPMDisplay, &CmndSSPMDump,
&CmndSSPMLog, &CmndSSPMEnergy, &CmndSSPMHistory, &CmndSSPMScan, &CmndSSPMIamHere, &CmndSSPMLog, &CmndSSPMEnergy, &CmndSSPMHistory, &CmndSSPMScan, &CmndSSPMIamHere,
&CmndSSPMReset, &CmndSSPMMap, &CmndSSPMOverload, &CmndSSPMReset, &CmndSSPMMap, &CmndSSPMOverload,
&CmndSpmEnergyTotal, &CmndSpmEnergyYesterday }; &CmndSpmEnergyTotal, &CmndSpmEnergyYesterday, &CmndSSPMSend };
void CmndSSPMDisplay(void) { void CmndSSPMDisplay(void) {
// Select either all relays or only powered on relays // Select either all relays or only powered on relays
@ -2377,6 +2389,26 @@ void CmndSSPMMap(void) {
} }
} }
void CmndSSPMSend(void) {
// Want to send AA 55 01 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 00 ix ch ks
// SspmSend 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 00
char data[TOPSZ];
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(data))) {
strlcpy(data, XdrvMailbox.data, sizeof(data));
uint32_t len = (XdrvMailbox.data_len +1) / 3;
char *p = data;
SSPMInitSend();
for (uint32_t i = 0; i < len; i++) {
SspmBuffer[i +3] = strtol(p, &p, 16);
}
Sspm->command_sequence++;
SspmBuffer[len +3] = Sspm->command_sequence;
SSPMSend(len +6);
ResponseCmndIdxChar(ToHex_P((unsigned char *)SspmBuffer, len +6, data, sizeof(data), ' '));
}
}
/*********************************************************************************************\ /*********************************************************************************************\
* Interface * Interface
\*********************************************************************************************/ \*********************************************************************************************/