mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-23 18:56:38 +00:00
Merge branch 'development' into ir_273_dev
This commit is contained in:
commit
90c84b0c42
@ -5,6 +5,8 @@
|
|||||||
- Change wifi connectivity stability (#7602)
|
- Change wifi connectivity stability (#7602)
|
||||||
- Add ``SetOption84 1`` sends AWS IoT device shadow updates (alternative to retained)
|
- Add ``SetOption84 1`` sends AWS IoT device shadow updates (alternative to retained)
|
||||||
- Change update IRRemoteESP8266 v2.7.3
|
- Change update IRRemoteESP8266 v2.7.3
|
||||||
|
- Add ``ZbBind`` (experimental) and bug fixes
|
||||||
|
- Fix PWM flickering at low levels (#7415)
|
||||||
|
|
||||||
### 8.1.0.4 20200116
|
### 8.1.0.4 20200116
|
||||||
|
|
||||||
|
317
tasmota/core_esp8266_waveform.cpp
Normal file
317
tasmota/core_esp8266_waveform.cpp
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
/*
|
||||||
|
esp8266_waveform - General purpose waveform generation and control,
|
||||||
|
supporting outputs on all pins in parallel.
|
||||||
|
|
||||||
|
Copyright (c) 2018 Earle F. Philhower, III. All rights reserved.
|
||||||
|
|
||||||
|
The core idea is to have a programmable waveform generator with a unique
|
||||||
|
high and low period (defined in microseconds). TIMER1 is set to 1-shot
|
||||||
|
mode and is always loaded with the time until the next edge of any live
|
||||||
|
waveforms.
|
||||||
|
|
||||||
|
Up to one waveform generator per pin supported.
|
||||||
|
|
||||||
|
Each waveform generator is synchronized to the ESP cycle counter, not the
|
||||||
|
timer. This allows for removing interrupt jitter and delay as the counter
|
||||||
|
always increments once per 80MHz clock. Changes to a waveform are
|
||||||
|
contiguous and only take effect on the next waveform transition,
|
||||||
|
allowing for smooth transitions.
|
||||||
|
|
||||||
|
This replaces older tone(), analogWrite(), and the Servo classes.
|
||||||
|
|
||||||
|
Everywhere in the code where "cycles" is used, it means ESP.getCycleTime()
|
||||||
|
cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz).
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <core_version.h>
|
||||||
|
#if defined(ARDUINO_ESP8266_RELEASE_2_6_1) || defined(ARDUINO_ESP8266_RELEASE_2_6_2) || defined(ARDUINO_ESP8266_RELEASE_2_6_3)
|
||||||
|
#warning **** Tasmota is using a patched PWM Arduino version as planned ****
|
||||||
|
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "ets_sys.h"
|
||||||
|
#include "core_esp8266_waveform.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
// Maximum delay between IRQs
|
||||||
|
#define MAXIRQUS (10000)
|
||||||
|
|
||||||
|
// Set/clear GPIO 0-15 by bitmask
|
||||||
|
#define SetGPIO(a) do { GPOS = a; } while (0)
|
||||||
|
#define ClearGPIO(a) do { GPOC = a; } while (0)
|
||||||
|
|
||||||
|
// Waveform generator can create tones, PWM, and servos
|
||||||
|
typedef struct {
|
||||||
|
uint32_t nextServiceCycle; // ESP cycle timer when a transition required
|
||||||
|
uint32_t expiryCycle; // For time-limited waveform, the cycle when this waveform must stop
|
||||||
|
uint32_t nextTimeHighCycles; // Copy over low->high to keep smooth waveform
|
||||||
|
uint32_t nextTimeLowCycles; // Copy over high->low to keep smooth waveform
|
||||||
|
} Waveform;
|
||||||
|
|
||||||
|
static Waveform waveform[17]; // State of all possible pins
|
||||||
|
static volatile uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code
|
||||||
|
static volatile uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
|
||||||
|
|
||||||
|
// Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine
|
||||||
|
static volatile uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin
|
||||||
|
static volatile uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation
|
||||||
|
|
||||||
|
static uint32_t (*timer1CB)() = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
// Non-speed critical bits
|
||||||
|
#pragma GCC optimize ("Os")
|
||||||
|
|
||||||
|
static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() {
|
||||||
|
uint32_t ccount;
|
||||||
|
__asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount));
|
||||||
|
return ccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupt on/off control
|
||||||
|
static ICACHE_RAM_ATTR void timer1Interrupt();
|
||||||
|
static bool timerRunning = false;
|
||||||
|
|
||||||
|
static void initTimer() {
|
||||||
|
timer1_disable();
|
||||||
|
ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
|
||||||
|
ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt);
|
||||||
|
timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
|
||||||
|
timerRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ICACHE_RAM_ATTR deinitTimer() {
|
||||||
|
ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
|
||||||
|
timer1_disable();
|
||||||
|
timer1_isr_init();
|
||||||
|
timerRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a callback. Pass in NULL to stop it
|
||||||
|
void setTimer1Callback(uint32_t (*fn)()) {
|
||||||
|
timer1CB = fn;
|
||||||
|
if (!timerRunning && fn) {
|
||||||
|
initTimer();
|
||||||
|
timer1_write(microsecondsToClockCycles(1)); // Cause an interrupt post-haste
|
||||||
|
} else if (timerRunning && !fn && !waveformEnabled) {
|
||||||
|
deinitTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start up a waveform on a pin, or change the current one. Will change to the new
|
||||||
|
// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
|
||||||
|
// first, then it will immediately begin.
|
||||||
|
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) {
|
||||||
|
if ((pin > 16) || isFlashInterfacePin(pin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Waveform *wave = &waveform[pin];
|
||||||
|
// Adjust to shave off some of the IRQ time, approximately
|
||||||
|
wave->nextTimeHighCycles = microsecondsToClockCycles(timeHighUS);
|
||||||
|
wave->nextTimeLowCycles = microsecondsToClockCycles(timeLowUS);
|
||||||
|
wave->expiryCycle = runTimeUS ? GetCycleCount() + microsecondsToClockCycles(runTimeUS) : 0;
|
||||||
|
if (runTimeUS && !wave->expiryCycle) {
|
||||||
|
wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mask = 1<<pin;
|
||||||
|
if (!(waveformEnabled & mask)) {
|
||||||
|
// Actually set the pin high or low in the IRQ service to guarantee times
|
||||||
|
wave->nextServiceCycle = GetCycleCount() + microsecondsToClockCycles(1);
|
||||||
|
waveformToEnable |= mask;
|
||||||
|
if (!timerRunning) {
|
||||||
|
initTimer();
|
||||||
|
timer1_write(microsecondsToClockCycles(10));
|
||||||
|
} else {
|
||||||
|
// Ensure timely service....
|
||||||
|
if (T1L > microsecondsToClockCycles(10)) {
|
||||||
|
timer1_write(microsecondsToClockCycles(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (waveformToEnable) {
|
||||||
|
delay(0); // Wait for waveform to update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speed critical bits
|
||||||
|
#pragma GCC optimize ("O2")
|
||||||
|
// Normally would not want two copies like this, but due to different
|
||||||
|
// optimization levels the inline attribute gets lost if we try the
|
||||||
|
// other version.
|
||||||
|
|
||||||
|
static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() {
|
||||||
|
uint32_t ccount;
|
||||||
|
__asm__ __volatile__("rsr %0,ccount":"=a"(ccount));
|
||||||
|
return ccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) {
|
||||||
|
if (a < b) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stops a waveform on a pin
|
||||||
|
int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
|
||||||
|
// Can't possibly need to stop anything if there is no timer active
|
||||||
|
if (!timerRunning) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// If user sends in a pin >16 but <32, this will always point to a 0 bit
|
||||||
|
// If they send >=32, then the shift will result in 0 and it will also return false
|
||||||
|
uint32_t mask = 1<<pin;
|
||||||
|
if (!(waveformEnabled & mask)) {
|
||||||
|
return false; // It's not running, nothing to do here
|
||||||
|
}
|
||||||
|
waveformToDisable |= mask;
|
||||||
|
// Ensure timely service....
|
||||||
|
if (T1L > microsecondsToClockCycles(10)) {
|
||||||
|
timer1_write(microsecondsToClockCycles(10));
|
||||||
|
}
|
||||||
|
while (waveformToDisable) {
|
||||||
|
/* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
|
||||||
|
}
|
||||||
|
if (!waveformEnabled && !timer1CB) {
|
||||||
|
deinitTimer();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The SDK and hardware take some time to actually get to our NMI code, so
|
||||||
|
// decrement the next IRQ's timer value by a bit so we can actually catch the
|
||||||
|
// real CPU cycle counter we want for the waveforms.
|
||||||
|
#if F_CPU == 80000000
|
||||||
|
#define DELTAIRQ (microsecondsToClockCycles(3))
|
||||||
|
#else
|
||||||
|
#define DELTAIRQ (microsecondsToClockCycles(2))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||||
|
// Optimize the NMI inner loop by keeping track of the min and max GPIO that we
|
||||||
|
// are generating. In the common case (1 PWM) these may be the same pin and
|
||||||
|
// we can avoid looking at the other pins.
|
||||||
|
static int startPin = 0;
|
||||||
|
static int endPin = 0;
|
||||||
|
|
||||||
|
uint32_t nextEventCycles = microsecondsToClockCycles(MAXIRQUS);
|
||||||
|
uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14);
|
||||||
|
|
||||||
|
if (waveformToEnable || waveformToDisable) {
|
||||||
|
// Handle enable/disable requests from main app.
|
||||||
|
waveformEnabled = (waveformEnabled & ~waveformToDisable) | waveformToEnable; // Set the requested waveforms on/off
|
||||||
|
waveformState &= ~waveformToEnable; // And clear the state of any just started
|
||||||
|
waveformToEnable = 0;
|
||||||
|
waveformToDisable = 0;
|
||||||
|
// Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
|
||||||
|
startPin = __builtin_ffs(waveformEnabled) - 1;
|
||||||
|
// Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one)
|
||||||
|
endPin = 32 - __builtin_clz(waveformEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
if (waveformEnabled) {
|
||||||
|
do {
|
||||||
|
nextEventCycles = microsecondsToClockCycles(MAXIRQUS);
|
||||||
|
for (int i = startPin; i <= endPin; i++) {
|
||||||
|
uint32_t mask = 1<<i;
|
||||||
|
|
||||||
|
// If it's not on, ignore!
|
||||||
|
if (!(waveformEnabled & mask)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Waveform *wave = &waveform[i];
|
||||||
|
uint32_t now = GetCycleCountIRQ();
|
||||||
|
|
||||||
|
// Disable any waveforms that are done
|
||||||
|
if (wave->expiryCycle) {
|
||||||
|
int32_t expiryToGo = wave->expiryCycle - now;
|
||||||
|
if (expiryToGo < 0) {
|
||||||
|
// Done, remove!
|
||||||
|
waveformEnabled &= ~mask;
|
||||||
|
if (i == 16) {
|
||||||
|
GP16O &= ~1;
|
||||||
|
} else {
|
||||||
|
ClearGPIO(mask);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for toggles
|
||||||
|
int32_t cyclesToGo = wave->nextServiceCycle - now;
|
||||||
|
if (cyclesToGo < 0) {
|
||||||
|
cyclesToGo = -((-cyclesToGo) % (wave->nextTimeHighCycles + wave->nextTimeLowCycles));
|
||||||
|
waveformState ^= mask;
|
||||||
|
if (waveformState & mask) {
|
||||||
|
if (i == 16) {
|
||||||
|
GP16O |= 1; // GPIO16 write slow as it's RMW
|
||||||
|
} else {
|
||||||
|
SetGPIO(mask);
|
||||||
|
}
|
||||||
|
wave->nextServiceCycle = now + wave->nextTimeHighCycles + cyclesToGo;
|
||||||
|
nextEventCycles = min_u32(nextEventCycles, min_u32(wave->nextTimeHighCycles + cyclesToGo, 1));
|
||||||
|
} else {
|
||||||
|
if (i == 16) {
|
||||||
|
GP16O &= ~1; // GPIO16 write slow as it's RMW
|
||||||
|
} else {
|
||||||
|
ClearGPIO(mask);
|
||||||
|
}
|
||||||
|
wave->nextServiceCycle = now + wave->nextTimeLowCycles + cyclesToGo;
|
||||||
|
nextEventCycles = min_u32(nextEventCycles, min_u32(wave->nextTimeLowCycles + cyclesToGo, 1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t deltaCycles = wave->nextServiceCycle - now;
|
||||||
|
nextEventCycles = min_u32(nextEventCycles, deltaCycles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur
|
||||||
|
uint32_t now = GetCycleCountIRQ();
|
||||||
|
int32_t cycleDeltaNextEvent = timeoutCycle - (now + nextEventCycles);
|
||||||
|
int32_t cyclesLeftTimeout = timeoutCycle - now;
|
||||||
|
done = (cycleDeltaNextEvent < 0) || (cyclesLeftTimeout < 0);
|
||||||
|
} while (!done);
|
||||||
|
} // if (waveformEnabled)
|
||||||
|
|
||||||
|
if (timer1CB) {
|
||||||
|
nextEventCycles = min_u32(nextEventCycles, timer1CB());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextEventCycles < microsecondsToClockCycles(10)) {
|
||||||
|
nextEventCycles = microsecondsToClockCycles(10);
|
||||||
|
}
|
||||||
|
nextEventCycles -= DELTAIRQ;
|
||||||
|
|
||||||
|
// Do it here instead of global function to save time and because we know it's edge-IRQ
|
||||||
|
#if F_CPU == 160000000
|
||||||
|
T1L = nextEventCycles >> 1; // Already know we're in range by MAXIRQUS
|
||||||
|
#else
|
||||||
|
T1L = nextEventCycles; // Already know we're in range by MAXIRQUS
|
||||||
|
#endif
|
||||||
|
TEIE |= TEIE1; // Edge int enable
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ARDUINO_ESP8266_RELEASE
|
@ -493,6 +493,7 @@
|
|||||||
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
|
#define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent"
|
||||||
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
|
#define D_JSON_ZIGBEE_RECEIVED "ZbReceived"
|
||||||
#define D_JSON_ZIGBEE_RECEIVED_LEGACY "ZigbeeReceived"
|
#define D_JSON_ZIGBEE_RECEIVED_LEGACY "ZigbeeReceived"
|
||||||
|
#define D_CMND_ZIGBEE_BIND "Bind"
|
||||||
|
|
||||||
// Commands xdrv_25_A4988_Stepper.ino
|
// Commands xdrv_25_A4988_Stepper.ino
|
||||||
#define D_CMND_MOTOR "MOTOR"
|
#define D_CMND_MOTOR "MOTOR"
|
||||||
|
@ -71,6 +71,8 @@ public:
|
|||||||
uint16_t isKnownIndex(uint32_t index) const;
|
uint16_t isKnownIndex(uint32_t index) const;
|
||||||
uint16_t isKnownFriendlyName(const char * name) const;
|
uint16_t isKnownFriendlyName(const char * name) const;
|
||||||
|
|
||||||
|
uint64_t getDeviceLongAddr(uint16_t shortaddr) const;
|
||||||
|
|
||||||
// Add new device, provide ShortAddr and optional longAddr
|
// Add new device, provide ShortAddr and optional longAddr
|
||||||
// If it is already registered, update information, otherwise create the entry
|
// If it is already registered, update information, otherwise create the entry
|
||||||
void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0);
|
void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0);
|
||||||
@ -142,6 +144,7 @@ private:
|
|||||||
static int32_t findClusterEndpoint(const std::vector<uint32_t> & vecOfElements, uint16_t element);
|
static int32_t findClusterEndpoint(const std::vector<uint32_t> & vecOfElements, uint16_t element);
|
||||||
|
|
||||||
Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist
|
Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist
|
||||||
|
const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; // find Device from shortAddr, creates it if does not exist
|
||||||
Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist
|
Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist
|
||||||
|
|
||||||
int32_t findShortAddr(uint16_t shortaddr) const;
|
int32_t findShortAddr(uint16_t shortaddr) const;
|
||||||
@ -160,6 +163,9 @@ private:
|
|||||||
|
|
||||||
Z_Devices zigbee_devices = Z_Devices();
|
Z_Devices zigbee_devices = Z_Devices();
|
||||||
|
|
||||||
|
// Local coordinator information
|
||||||
|
uint64_t localIEEEAddr = 0;
|
||||||
|
|
||||||
// https://thispointer.com/c-how-to-find-an-element-in-vector-and-get-its-index/
|
// https://thispointer.com/c-how-to-find-an-element-in-vector-and-get-its-index/
|
||||||
template < typename T>
|
template < typename T>
|
||||||
bool Z_Devices::findInVector(const std::vector<T> & vecOfElements, const T & element) {
|
bool Z_Devices::findInVector(const std::vector<T> & vecOfElements, const T & element) {
|
||||||
@ -326,6 +332,11 @@ uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const {
|
||||||
|
const Z_Device & device = getShortAddrConst(shortaddr);
|
||||||
|
return device.longaddr;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// We have a seen a shortaddr on the network, get the corresponding
|
// We have a seen a shortaddr on the network, get the corresponding
|
||||||
//
|
//
|
||||||
@ -335,9 +346,18 @@ Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
|
|||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
return _devices[found];
|
return _devices[found];
|
||||||
}
|
}
|
||||||
//Serial.printf("Device entry created for shortaddr = 0x%02X, found = %d\n", shortaddr, found);
|
//Serial.printf("Device entry created for shortaddr = 0x%02X, found = %d\n", shortaddr, found);
|
||||||
return createDeviceEntry(shortaddr, 0);
|
return createDeviceEntry(shortaddr, 0);
|
||||||
}
|
}
|
||||||
|
// Same version but Const
|
||||||
|
const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const {
|
||||||
|
if (!shortaddr) { return *(Z_Device*) nullptr; } // this is not legal
|
||||||
|
int32_t found = findShortAddr(shortaddr);
|
||||||
|
if (found >= 0) {
|
||||||
|
return _devices[found];
|
||||||
|
}
|
||||||
|
return *((Z_Device*)nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// find the Device object by its longaddr (unique key if not null)
|
// find the Device object by its longaddr (unique key if not null)
|
||||||
Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) {
|
Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) {
|
||||||
|
@ -39,10 +39,10 @@ class ZCLFrame {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
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 = 0, uint16_t groupid = 0,
|
const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupid,
|
||||||
uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0,
|
uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
|
||||||
uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0,
|
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber,
|
||||||
uint32_t timestamp = 0):
|
uint32_t timestamp):
|
||||||
_cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq),
|
_cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq),
|
||||||
_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), _group_id(groupid),
|
_cluster_id(clusterid), _group_id(groupid),
|
||||||
@ -74,9 +74,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid,
|
static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid,
|
||||||
uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0,
|
uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
|
||||||
uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0,
|
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber,
|
||||||
uint32_t timestamp = 0) { // parse a raw frame and build the ZCL frame object
|
uint32_t timestamp) { // parse a raw frame and build the ZCL frame object
|
||||||
uint32_t i = offset;
|
uint32_t i = offset;
|
||||||
ZCLHeaderFrameControl_t frame_control;
|
ZCLHeaderFrameControl_t frame_control;
|
||||||
uint16_t manuf_code = 0;
|
uint16_t manuf_code = 0;
|
||||||
@ -92,7 +92,10 @@ public:
|
|||||||
cmd_id = buf.get8(i++);
|
cmd_id = buf.get8(i++);
|
||||||
ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id,
|
ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id,
|
||||||
(const char *)(buf.buf() + i), len + offset - i,
|
(const char *)(buf.buf() + i), len + offset - i,
|
||||||
clusterid, groupid);
|
clusterid, groupid,
|
||||||
|
srcaddr, srcendpoint, dstendpoint, wasbroadcast,
|
||||||
|
linkquality, securityuse, seqnumber,
|
||||||
|
timestamp);
|
||||||
return zcl_frame;
|
return zcl_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
|
|||||||
uint8_t device_state = buf.get8(14);
|
uint8_t device_state = buf.get8(14);
|
||||||
uint8_t device_associated = buf.get8(15);
|
uint8_t device_associated = buf.get8(15);
|
||||||
|
|
||||||
|
// keep track of the local IEEE address
|
||||||
|
localIEEEAddr = long_adr;
|
||||||
|
|
||||||
char hex[20];
|
char hex[20];
|
||||||
Uint64toHex(long_adr, hex, 64);
|
Uint64toHex(long_adr, hex, 64);
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
||||||
|
@ -33,19 +33,19 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
|||||||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME ;
|
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ;
|
||||||
|
|
||||||
const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" // legacy prefix -- deprecated
|
const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" // legacy prefix -- deprecated
|
||||||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME ;
|
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ;
|
||||||
|
|
||||||
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
||||||
&CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
|
&CmndZbZNPSend, &CmndZbPermitJoin,
|
||||||
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend,
|
&CmndZbStatus, &CmndZbReset, &CmndZbSend,
|
||||||
&CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive,
|
&CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive,
|
||||||
&CmndZigbeeForget, &CmndZigbeeSave, &CmndZigbeeName
|
&CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind
|
||||||
};
|
};
|
||||||
|
|
||||||
int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
||||||
@ -257,7 +257,7 @@ const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM =
|
|||||||
{ Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x01 /* STARTOPT_CLEAR_CONFIG */};
|
{ Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x01 /* STARTOPT_CLEAR_CONFIG */};
|
||||||
//"2605030101"; // Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 len, 0x01 STARTOPT_CLEAR_CONFIG
|
//"2605030101"; // Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 len, 0x01 STARTOPT_CLEAR_CONFIG
|
||||||
// Do a factory reset of the CC2530
|
// Do a factory reset of the CC2530
|
||||||
void CmndZigbeeReset(void) {
|
void CmndZbReset(void) {
|
||||||
if (ZigbeeSerial) {
|
if (ZigbeeSerial) {
|
||||||
switch (XdrvMailbox.payload) {
|
switch (XdrvMailbox.payload) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -272,7 +272,7 @@ void CmndZigbeeReset(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmndZigbeeZNPSendOrReceive(bool send)
|
void CmndZbZNPSendOrReceive(bool send)
|
||||||
{
|
{
|
||||||
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
|
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
|
||||||
uint8_t code;
|
uint8_t code;
|
||||||
@ -300,14 +300,14 @@ void CmndZigbeeZNPSendOrReceive(bool send)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For debug purposes only, simulates a message received
|
// For debug purposes only, simulates a message received
|
||||||
void CmndZigbeeZNPReceive(void)
|
void CmndZbZNPReceive(void)
|
||||||
{
|
{
|
||||||
CmndZigbeeZNPSendOrReceive(false);
|
CmndZbZNPSendOrReceive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmndZigbeeZNPSend(void)
|
void CmndZbZNPSend(void)
|
||||||
{
|
{
|
||||||
CmndZigbeeZNPSendOrReceive(true);
|
CmndZbZNPSendOrReceive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
|
||||||
@ -442,7 +442,7 @@ void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) {
|
|||||||
ResponseCmndDone();
|
ResponseCmndDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmndZigbeeSend(void) {
|
void CmndZbSend(void) {
|
||||||
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
|
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":1} }
|
||||||
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"3"} }
|
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"3"} }
|
||||||
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"0xFF"} }
|
// ZigbeeSend { "device":"0x1234", "endpoint":"0x03", "send":{"Power":"0xFF"} }
|
||||||
@ -465,9 +465,14 @@ void CmndZigbeeSend(void) {
|
|||||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||||
String cmd_str = ""; // the actual low-level command, either specified or computed
|
String cmd_str = ""; // the actual low-level command, either specified or computed
|
||||||
|
|
||||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("device"));
|
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||||
if (nullptr != &val_device) { device = strToUInt(val_device); }
|
if (nullptr != &val_device) {
|
||||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("endpoint"));
|
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||||
|
if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; }
|
||||||
|
}
|
||||||
|
if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; }
|
||||||
|
|
||||||
|
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
|
||||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||||
const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send"));
|
const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send"));
|
||||||
if (nullptr != &val_cmd) {
|
if (nullptr != &val_cmd) {
|
||||||
@ -547,8 +552,64 @@ void CmndZigbeeSend(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZBM(ZBS_BIND_REQ, Z_SREQ | Z_ZDO, ZDO_BIND_REQ,
|
||||||
|
0,0, // dstAddr - 16 bits, device to send the bind to
|
||||||
|
0,0,0,0,0,0,0,0, // srcAddr - 64 bits, IEEE binding source
|
||||||
|
0x00, // source endpoint
|
||||||
|
0x00, 0x00, // cluster
|
||||||
|
0x03, // DstAddrMode - 0x03 = ADDRESS_64_BIT
|
||||||
|
0,0,0,0,0,0,0,0, // dstAddr - 64 bits, IEEE binding destination, i.e. coordinator
|
||||||
|
0x01 // dstEndpoint - 0x01 for coordinator
|
||||||
|
)
|
||||||
|
|
||||||
|
void CmndZbBind(void) {
|
||||||
|
// ZbBind { "device":"0x1234", "endpoint":1, "cluster":6 }
|
||||||
|
|
||||||
|
// local endpoint is always 1, IEEE addresses are calculated
|
||||||
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
|
DynamicJsonBuffer jsonBuf;
|
||||||
|
JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data);
|
||||||
|
if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; }
|
||||||
|
|
||||||
|
// params
|
||||||
|
// static char delim[] = ", "; // delimiters for parameters
|
||||||
|
uint16_t device = 0xFFFF; // 0xFFFF is broadcast, so considered valid
|
||||||
|
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||||
|
uint16_t cluster = 0; // 0xFFFF is invalid
|
||||||
|
uint32_t group = 0xFFFFFFFF; // 16 bits values, otherwise 0xFFFFFFFF is unspecified
|
||||||
|
|
||||||
|
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||||
|
if (nullptr != &val_device) {
|
||||||
|
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||||
|
if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; }
|
||||||
|
}
|
||||||
|
if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; }
|
||||||
|
|
||||||
|
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
|
||||||
|
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||||
|
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
|
||||||
|
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||||
|
|
||||||
|
// TODO compute endpoint from cluster
|
||||||
|
|
||||||
|
SBuffer buf(sizeof(ZBS_BIND_REQ));
|
||||||
|
buf.add8(Z_SREQ | Z_ZDO);
|
||||||
|
buf.add8(ZDO_BIND_REQ);
|
||||||
|
buf.add16(device);
|
||||||
|
buf.add64(zigbee_devices.getDeviceLongAddr(device));
|
||||||
|
buf.add8(endpoint);
|
||||||
|
buf.add16(cluster);
|
||||||
|
buf.add8(0x03); // DstAddrMode - 0x03 = ADDRESS_64_BIT
|
||||||
|
buf.add64(localIEEEAddr); // coordinatore IEEE address
|
||||||
|
buf.add8(0x01); // local endpoint = 1
|
||||||
|
|
||||||
|
ZigbeeZNPSend(buf.getBuffer(), buf.len());
|
||||||
|
|
||||||
|
ResponseCmndDone();
|
||||||
|
}
|
||||||
|
|
||||||
// Probe a specific device to get its endpoints and supported clusters
|
// Probe a specific device to get its endpoints and supported clusters
|
||||||
void CmndZigbeeProbe(void) {
|
void CmndZbProbe(void) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||||
@ -560,7 +621,7 @@ void CmndZigbeeProbe(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Specify, read or erase a Friendly Name
|
// Specify, read or erase a Friendly Name
|
||||||
void CmndZigbeeName(void) {
|
void CmndZbName(void) {
|
||||||
// Syntax is:
|
// Syntax is:
|
||||||
// ZigbeeName <device_id>,<friendlyname> - assign a friendly name
|
// ZigbeeName <device_id>,<friendlyname> - assign a friendly name
|
||||||
// ZigbeeName <device_id> - display the current friendly name
|
// ZigbeeName <device_id> - display the current friendly name
|
||||||
@ -589,7 +650,7 @@ void CmndZigbeeName(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices
|
// Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices
|
||||||
void CmndZigbeeForget(void) {
|
void CmndZbForget(void) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||||
@ -604,7 +665,7 @@ void CmndZigbeeForget(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save Zigbee information to flash
|
// Save Zigbee information to flash
|
||||||
void CmndZigbeeSave(void) {
|
void CmndZbSave(void) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
|
|
||||||
saveZigbeeDevices();
|
saveZigbeeDevices();
|
||||||
@ -613,7 +674,7 @@ void CmndZigbeeSave(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send an attribute read command to a device, specifying cluster and list of attributes
|
// Send an attribute read command to a device, specifying cluster and list of attributes
|
||||||
void CmndZigbeeRead(void) {
|
void CmndZbRead(void) {
|
||||||
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
|
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
|
||||||
// ZigbeeRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"}
|
// ZigbeeRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"}
|
||||||
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]}
|
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]}
|
||||||
@ -629,10 +690,14 @@ void CmndZigbeeRead(void) {
|
|||||||
size_t attrs_len = 0;
|
size_t attrs_len = 0;
|
||||||
uint8_t* attrs = nullptr; // empty string is valid
|
uint8_t* attrs = nullptr; // empty string is valid
|
||||||
|
|
||||||
|
|
||||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||||
if (nullptr != &val_device) { device = strToUInt(val_device); }
|
if (nullptr != &val_device) {
|
||||||
const JsonVariant val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
|
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||||
|
if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; }
|
||||||
|
}
|
||||||
|
if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; }
|
||||||
|
|
||||||
|
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
|
||||||
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
|
||||||
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
|
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
|
||||||
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
|
||||||
@ -659,14 +724,18 @@ void CmndZigbeeRead(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
|
if ((0 != endpoint) && (attrs_len > 0)) {
|
||||||
|
ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */);
|
||||||
|
ResponseCmndDone();
|
||||||
|
} else {
|
||||||
|
ResponseCmndChar("Missing parameters");
|
||||||
|
}
|
||||||
|
|
||||||
if (attrs) { delete[] attrs; }
|
if (attrs) { delete[] attrs; }
|
||||||
ResponseCmndDone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow or Deny pairing of new Zigbee devices
|
// Allow or Deny pairing of new Zigbee devices
|
||||||
void CmndZigbeePermitJoin(void)
|
void CmndZbPermitJoin(void)
|
||||||
{
|
{
|
||||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
uint32_t payload = XdrvMailbox.payload;
|
uint32_t payload = XdrvMailbox.payload;
|
||||||
@ -683,7 +752,7 @@ void CmndZigbeePermitJoin(void)
|
|||||||
ResponseCmndDone();
|
ResponseCmndDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmndZigbeeStatus(void) {
|
void CmndZbStatus(void) {
|
||||||
if (ZigbeeSerial) {
|
if (ZigbeeSerial) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user