Merge branch 'master' into release/v3.3.x
This commit is contained in:
commit
15038e64b3
47 changed files with 4455 additions and 249 deletions
|
|
@ -301,6 +301,9 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS
|
|||
libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeBinary.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp
|
||||
)
|
||||
|
||||
set(ARDUINO_LIBRARY_BLE_SRCS
|
||||
|
|
|
|||
626
boards.txt
626
boards.txt
File diff suppressed because it is too large
Load diff
|
|
@ -192,6 +192,7 @@ static void audio_feed_task(void *arg) {
|
|||
|
||||
/* Feed samples of an audio stream to the AFE_SR */
|
||||
g_sr_data->afe_handle->feed(g_sr_data->afe_data, audio_buffer);
|
||||
vTaskDelay(2);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ uint8_t analogPin = A0;
|
|||
uint8_t button = BOOT_PIN;
|
||||
|
||||
ZigbeeAnalog zbAnalogDevice = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
|
||||
ZigbeeAnalog zbAnalogTemp = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 1);
|
||||
ZigbeeAnalog zbAnalogFan = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 2);
|
||||
ZigbeeAnalog zbAnalogPercent = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 3);
|
||||
|
||||
void onAnalogOutputChange(float analog_output) {
|
||||
Serial.printf("Received analog output change: %.1f\r\n", analog_output);
|
||||
|
|
@ -57,15 +60,43 @@ void setup() {
|
|||
// Optional: set Zigbee device name and model
|
||||
zbAnalogDevice.setManufacturerAndModel("Espressif", "ZigbeeAnalogDevice");
|
||||
|
||||
// Add analog clusters to Zigbee Analog according your needs
|
||||
// Set up analog input
|
||||
zbAnalogDevice.addAnalogInput();
|
||||
zbAnalogDevice.setAnalogInputApplication(ESP_ZB_ZCL_AI_POWER_IN_WATTS_CONSUMPTION);
|
||||
zbAnalogDevice.setAnalogInputDescription("Power Consumption (Watts)");
|
||||
zbAnalogDevice.setAnalogInputResolution(0.01);
|
||||
|
||||
// Set up analog output
|
||||
zbAnalogDevice.addAnalogOutput();
|
||||
zbAnalogDevice.setAnalogOutputApplication(ESP_ZB_ZCL_AI_RPM_OTHER);
|
||||
zbAnalogDevice.setAnalogOutputDescription("Fan Speed (RPM)");
|
||||
|
||||
// If analog output cluster is added, set callback function for analog output change
|
||||
zbAnalogDevice.onAnalogOutputChange(onAnalogOutputChange);
|
||||
|
||||
// Set up analog input
|
||||
zbAnalogTemp.addAnalogInput();
|
||||
zbAnalogTemp.setAnalogInputApplication(ESP_ZB_ZCL_AI_TEMPERATURE_OTHER);
|
||||
zbAnalogTemp.setAnalogInputDescription("Temperature");
|
||||
zbAnalogTemp.setAnalogInputResolution(0.1);
|
||||
|
||||
// Set up analog input
|
||||
zbAnalogFan.addAnalogInput();
|
||||
zbAnalogFan.setAnalogInputApplication(ESP_ZB_ZCL_AI_RPM_OTHER);
|
||||
zbAnalogFan.setAnalogInputDescription("RPM");
|
||||
zbAnalogFan.setAnalogInputResolution(1);
|
||||
|
||||
// Set up analog input
|
||||
zbAnalogPercent.addAnalogInput();
|
||||
zbAnalogPercent.setAnalogInputApplication(ESP_ZB_ZCL_AI_PERCENTAGE_OTHER);
|
||||
zbAnalogPercent.setAnalogInputDescription("Percentage");
|
||||
zbAnalogPercent.setAnalogInputResolution(0.01);
|
||||
|
||||
// Add endpoints to Zigbee Core
|
||||
Zigbee.addEndpoint(&zbAnalogDevice);
|
||||
Zigbee.addEndpoint(&zbAnalogTemp);
|
||||
Zigbee.addEndpoint(&zbAnalogFan);
|
||||
Zigbee.addEndpoint(&zbAnalogPercent);
|
||||
|
||||
Serial.println("Starting Zigbee...");
|
||||
// When all EPs are registered, start Zigbee in End Device mode
|
||||
|
|
@ -95,9 +126,15 @@ void loop() {
|
|||
float analog = (float)analogRead(analogPin);
|
||||
Serial.printf("Updating analog input to %.1f\r\n", analog);
|
||||
zbAnalogDevice.setAnalogInput(analog);
|
||||
zbAnalogTemp.setAnalogInput(analog / 100);
|
||||
zbAnalogFan.setAnalogInput(analog);
|
||||
zbAnalogPercent.setAnalogInput(analog / 10);
|
||||
|
||||
// Analog input supports reporting
|
||||
zbAnalogDevice.reportAnalogInput();
|
||||
zbAnalogTemp.reportAnalogInput();
|
||||
zbAnalogFan.reportAnalogInput();
|
||||
zbAnalogPercent.reportAnalogInput();
|
||||
}
|
||||
|
||||
// Checking button for factory reset and reporting
|
||||
|
|
|
|||
75
libraries/Zigbee/examples/Zigbee_Binary_Input/README.md
Normal file
75
libraries/Zigbee/examples/Zigbee_Binary_Input/README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Arduino-ESP32 Zigbee Binary Input Example
|
||||
|
||||
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) binary input device with two different applications: HVAC fan status and security zone armed status.
|
||||
|
||||
# Supported Targets
|
||||
|
||||
Currently, this example supports the following targets.
|
||||
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
## Binary Input Functions
|
||||
|
||||
* The example implements two binary inputs:
|
||||
- HVAC Fan Status: Reports the current state of a fan
|
||||
- Security Zone Armed: Reports the armed state of a security zone
|
||||
* By clicking the button (BOOT) on this board, it will toggle both binary inputs and immediately send a report of their states to the network.
|
||||
* Holding the button for more than 3 seconds will trigger a factory reset of the Zigbee device.
|
||||
|
||||
## Hardware Required
|
||||
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
The example uses the following default pins:
|
||||
* Button: `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2)
|
||||
|
||||
#### Using Arduino IDE
|
||||
|
||||
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
|
||||
|
||||
* Before Compile/Verify, select the correct board: `Tools -> Board`.
|
||||
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
|
||||
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
|
||||
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
|
||||
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
|
||||
You can do the following:
|
||||
|
||||
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
|
||||
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
|
||||
|
||||
By default, the coordinator network is closed after rebooting or flashing new firmware.
|
||||
To open the network you have 2 options:
|
||||
|
||||
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
|
||||
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
|
||||
|
||||
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
|
||||
|
||||
* **LED not blinking:** Check the wiring connection and the IO selection.
|
||||
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
|
||||
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
|
||||
|
||||
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
|
||||
|
||||
## Contribute
|
||||
|
||||
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
|
||||
|
||||
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
|
||||
|
||||
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
|
||||
|
||||
## Resources
|
||||
|
||||
* Official ESP32 Forum: [Link](https://esp32.com)
|
||||
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
|
||||
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
|
||||
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
|
||||
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @brief This example demonstrates Zigbee binary input device.
|
||||
*
|
||||
* The example demonstrates how to use Zigbee library to create an end device binary sensor device.
|
||||
*
|
||||
* Proper Zigbee mode must be selected in Tools->Zigbee mode
|
||||
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
|
||||
*
|
||||
* Please check the README.md for instructions and more detailed description.
|
||||
*
|
||||
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
|
||||
*/
|
||||
|
||||
#ifndef ZIGBEE_MODE_ED
|
||||
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
|
||||
#endif
|
||||
|
||||
#include "Zigbee.h"
|
||||
|
||||
/* Zigbee binary sensor device configuration */
|
||||
#define BINARY_DEVICE_ENDPOINT_NUMBER 1
|
||||
|
||||
uint8_t binaryPin = A0;
|
||||
uint8_t button = BOOT_PIN;
|
||||
|
||||
ZigbeeBinary zbBinaryFan = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER);
|
||||
ZigbeeBinary zbBinaryZone = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER + 1);
|
||||
|
||||
bool binaryStatus = false;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting...");
|
||||
|
||||
// Init button switch
|
||||
pinMode(button, INPUT_PULLUP);
|
||||
|
||||
// Set analog resolution to 10 bits
|
||||
analogReadResolution(10);
|
||||
|
||||
// Optional: set Zigbee device name and model
|
||||
zbBinaryFan.setManufacturerAndModel("Espressif", "ZigbeeBinarySensor");
|
||||
|
||||
// Set up binary fan status input (HVAC)
|
||||
zbBinaryFan.addBinaryInput();
|
||||
zbBinaryFan.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_HVAC_FAN_STATUS);
|
||||
zbBinaryFan.setBinaryInputDescription("Fan Status");
|
||||
|
||||
// Set up binary zone armed input (Security)
|
||||
zbBinaryZone.addBinaryInput();
|
||||
zbBinaryZone.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_SECURITY_ZONE_ARMED);
|
||||
zbBinaryZone.setBinaryInputDescription("Zone Armed");
|
||||
|
||||
// Add endpoints to Zigbee Core
|
||||
Zigbee.addEndpoint(&zbBinaryFan);
|
||||
Zigbee.addEndpoint(&zbBinaryZone);
|
||||
|
||||
Serial.println("Starting Zigbee...");
|
||||
// When all EPs are registered, start Zigbee in End Device mode
|
||||
if (!Zigbee.begin()) {
|
||||
Serial.println("Zigbee failed to start!");
|
||||
Serial.println("Rebooting...");
|
||||
ESP.restart();
|
||||
} else {
|
||||
Serial.println("Zigbee started successfully!");
|
||||
}
|
||||
Serial.println("Connecting to network");
|
||||
while (!Zigbee.connected()) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println("Connected");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Checking button for factory reset and reporting
|
||||
if (digitalRead(button) == LOW) { // Push button pressed
|
||||
// Key debounce handling
|
||||
delay(100);
|
||||
int startTime = millis();
|
||||
while (digitalRead(button) == LOW) {
|
||||
delay(50);
|
||||
if ((millis() - startTime) > 3000) {
|
||||
// If key pressed for more than 3secs, factory reset Zigbee and reboot
|
||||
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
|
||||
delay(1000);
|
||||
Zigbee.factoryReset();
|
||||
}
|
||||
}
|
||||
// Toggle binary input
|
||||
binaryStatus = !binaryStatus;
|
||||
zbBinaryFan.setBinaryInput(binaryStatus);
|
||||
zbBinaryZone.setBinaryInput(binaryStatus);
|
||||
zbBinaryFan.reportBinaryInput();
|
||||
zbBinaryZone.reportBinaryInput();
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
7
libraries/Zigbee/examples/Zigbee_Binary_Input/ci.json
Normal file
7
libraries/Zigbee/examples/Zigbee_Binary_Input/ci.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
|
||||
"requires": [
|
||||
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
|
||||
"CONFIG_ZB_ENABLED=y"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
# Arduino-ESP32 Zigbee AC Electrical Measurement Example
|
||||
|
||||
This example shows how to configure the Zigbee router device and use it as a Home Automation (HA) AC electrical measurement device that reports voltage, current, power, and frequency measurements.
|
||||
|
||||
# Supported Targets
|
||||
|
||||
Currently, this example supports the following targets.
|
||||
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
## AC Electrical Measurement Functions
|
||||
|
||||
* After this board first starts up, it would be configured locally to report AC electrical measurements:
|
||||
- AC voltage in volts (0-300.00 V)
|
||||
- AC current in amps (0-10.000 A)
|
||||
- AC power in watts (0-3200.0 W)
|
||||
- AC frequency in hertz (0-65.000 Hz)
|
||||
* Holding the button (BOOT) for more than 3 seconds will trigger a factory reset of the device.
|
||||
* The device reports measurements every 2 seconds with simulated values.
|
||||
|
||||
## Measurement Precision
|
||||
|
||||
The example demonstrates how to set up proper measurement precision using multiplier and divisor values:
|
||||
* Voltage: 1/100 = 0.01 V (1 unit = 10 mV)
|
||||
* Current: 1/1000 = 0.001 A (1 unit = 1 mA)
|
||||
* Power: 1/10 = 0.1 W (1 unit = 100 mW)
|
||||
* Frequency: 1/1000 = 0.001 Hz (1 unit = 1 mHz)
|
||||
|
||||
These settings ensure accurate reporting of measurements with proper decimal precision in the Zigbee network.
|
||||
|
||||
## Hardware Required
|
||||
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Set the ADC GPIO by changing the `analogPin` variable. By default, it's the pin `A0`.
|
||||
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
|
||||
|
||||
#### Using Arduino IDE
|
||||
|
||||
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
|
||||
|
||||
* Before Compile/Verify, select the correct board: `Tools -> Board`.
|
||||
* Select the Router Zigbee mode: `Tools -> Zigbee mode: Zigbee Router`
|
||||
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
|
||||
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
|
||||
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the Router device flashed with this example is not connecting to the coordinator, erase the flash of the Router device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
|
||||
You can do the following:
|
||||
|
||||
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
|
||||
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
|
||||
|
||||
By default, the coordinator network is closed after rebooting or flashing new firmware.
|
||||
To open the network you have 2 options:
|
||||
|
||||
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
|
||||
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
|
||||
|
||||
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
|
||||
|
||||
* **LED not blinking:** Check the wiring connection and the IO selection.
|
||||
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
|
||||
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
|
||||
|
||||
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
|
||||
|
||||
## Contribute
|
||||
|
||||
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
|
||||
|
||||
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
|
||||
|
||||
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
|
||||
|
||||
## Resources
|
||||
|
||||
* Official ESP32 Forum: [Link](https://esp32.com)
|
||||
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
|
||||
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
|
||||
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
|
||||
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @brief This example demonstrates Zigbee electrical AC measurement device.
|
||||
*
|
||||
* The example demonstrates how to use Zigbee library to create a router device that measures
|
||||
* AC electrical parameters like voltage, current, power, power factor and frequency.
|
||||
*
|
||||
* Proper Zigbee mode must be selected in Tools->Zigbee mode
|
||||
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
|
||||
*
|
||||
* Please check the README.md for instructions and more detailed description.
|
||||
*
|
||||
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
|
||||
*/
|
||||
|
||||
// Recommended to use Router mode, as this type of device is expected to be mains powered.
|
||||
#ifndef ZIGBEE_MODE_ZCZR
|
||||
#error "Zigbee coordinator/router mode is not selected in Tools->Zigbee mode"
|
||||
#endif
|
||||
|
||||
#include "Zigbee.h"
|
||||
|
||||
/* Zigbee AC measurement device configuration */
|
||||
#define AC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER 1
|
||||
|
||||
uint8_t analogPin = A0;
|
||||
uint8_t button = BOOT_PIN;
|
||||
|
||||
ZigbeeElectricalMeasurement zbElectricalMeasurement = ZigbeeElectricalMeasurement(AC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER);
|
||||
|
||||
void onAnalogOutputChange(float analog_output) {
|
||||
Serial.printf("Received analog output change: %.1f\r\n", analog_output);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting...");
|
||||
|
||||
// Init button switch
|
||||
pinMode(button, INPUT_PULLUP);
|
||||
|
||||
// Set analog resolution to 10 bits
|
||||
analogReadResolution(10);
|
||||
|
||||
// Optional: set Zigbee device name and model
|
||||
zbElectricalMeasurement.setManufacturerAndModel("Espressif", "ZigbeeElectricalMeasurementAC");
|
||||
|
||||
// Add analog clusters to Zigbee Analog according your needs
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.addACMeasurement(
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC
|
||||
); // frequency is not phase specific (shared)
|
||||
|
||||
// Optional: set Multiplier/Divisor for the measurements
|
||||
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, 1, 100); // 1/100 = 0.01V (1 unit of measurement = 0.01V = 10mV)
|
||||
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, 1, 1000); // 1/1000 = 0.001A (1 unit of measurement = 0.001A = 1mA)
|
||||
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, 1, 10); // 1/10 = 0.1W (1 unit of measurement = 0.1W = 100mW)
|
||||
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, 1, 1000); // 1/1000 = 0.001Hz (1 unit of measurement = 0.001Hz = 1mHz)
|
||||
|
||||
// Optional: set Min/max values for the measurements
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, 0, 30000); // 0-300.00V
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, 0, 10000); // 0-10.000A
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, 0, 32000); // 0-3200.0W
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, 0, 65000); // 0-65.000Hz
|
||||
|
||||
// Optional: set power factor for the measurements
|
||||
zbElectricalMeasurement.setACPowerFactor(ZIGBEE_AC_PHASE_TYPE_A, 98); // 100 = 1.00, 0 = 0.00, -100 = -1.00
|
||||
|
||||
// Add endpoints to Zigbee Core
|
||||
Zigbee.addEndpoint(&zbElectricalMeasurement);
|
||||
|
||||
Serial.println("Starting Zigbee...");
|
||||
// When all EPs are registered, start Zigbee in Router mode
|
||||
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
|
||||
Serial.println("Zigbee failed to start!");
|
||||
Serial.println("Rebooting...");
|
||||
ESP.restart();
|
||||
} else {
|
||||
Serial.println("Zigbee started successfully!");
|
||||
}
|
||||
Serial.println("Connecting to network");
|
||||
while (!Zigbee.connected()) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println("Connected");
|
||||
|
||||
// Optional: Add reporting for AC measurements (this is overridden by HomeAssistant ZHA if used as a Zigbee coordinator)
|
||||
zbElectricalMeasurement.setACReporting(
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, 0, 30, 10
|
||||
); // report every 30 seconds if value changes by 10 (0.1V)
|
||||
zbElectricalMeasurement.setACReporting(
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, 0, 30, 100
|
||||
); // report every 30 seconds if value changes by 10 (0.1A)
|
||||
zbElectricalMeasurement.setACReporting(
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, 0, 30, 10
|
||||
); // report every 30 seconds if value changes by 10 (1W)
|
||||
zbElectricalMeasurement.setACReporting(
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, 0, 30, 100
|
||||
); // report every 30 seconds if value changes by 100 (0.1Hz)
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static uint32_t timeCounter = 0;
|
||||
|
||||
static uint8_t randomizer = 0;
|
||||
// Read ADC value as current to demonstrate the measurements and update the electrical measurement values every 2s
|
||||
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
|
||||
uint16_t voltage = 23000 + randomizer; // 230.00 V
|
||||
uint16_t current = analogReadMilliVolts(analogPin); // demonstrates 0-3.3A
|
||||
int16_t power = ((voltage / 100) * (current / 1000) * 10); //calculate power in W
|
||||
uint16_t frequency = 50135; // 50.000 Hz
|
||||
Serial.printf("Updating AC voltage to %d (0.01V), current to %d (mA), power to %d (0.1W), frequency to %d (mHz)\r\n", voltage, current, power, frequency);
|
||||
|
||||
// Update the measurements
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, voltage);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, current);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, power);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, frequency);
|
||||
|
||||
// Report the measurements
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC);
|
||||
|
||||
randomizer += 10;
|
||||
}
|
||||
|
||||
// Checking button for factory reset and reporting
|
||||
if (digitalRead(button) == LOW) { // Push button pressed
|
||||
// Key debounce handling
|
||||
delay(100);
|
||||
int startTime = millis();
|
||||
while (digitalRead(button) == LOW) {
|
||||
delay(50);
|
||||
if ((millis() - startTime) > 3000) {
|
||||
// If key pressed for more than 3secs, factory reset Zigbee and reboot
|
||||
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
|
||||
delay(1000);
|
||||
Zigbee.factoryReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr",
|
||||
"requires": [
|
||||
"CONFIG_ZB_ENABLED=y"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
# Arduino-ESP32 Zigbee AC Electrical MultiPhase Measurement Example
|
||||
|
||||
This example shows how to configure the Zigbee router device and use it as a Home Automation (HA) AC electrical measurement device that reports voltage, current, power, and frequency measurements across three phases.
|
||||
|
||||
# Supported Targets
|
||||
|
||||
Currently, this example supports the following targets.
|
||||
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
## AC Electrical Measurement Functions
|
||||
|
||||
* After this board first starts up, it would be configured locally to report AC electrical measurements:
|
||||
- AC voltage in volts (0-300.00 V) for each phase (A, B, C)
|
||||
- AC current in amps (0-10.000 A) for each phase (A, B, C)
|
||||
- AC power in watts (0-3200.0 W) for each phase (A, B, C)
|
||||
- AC frequency in hertz (0-65.000 Hz) shared across all phases
|
||||
* Holding the button (BOOT) for more than 3 seconds will trigger a factory reset of the device.
|
||||
* The device reports measurements every 2 seconds with simulated values.
|
||||
|
||||
## Measurement Precision
|
||||
|
||||
The example demonstrates how to set up proper measurement precision using multiplier and divisor values:
|
||||
* Voltage: 1/100 = 0.01 V (1 unit = 10 mV)
|
||||
* Current: 1/1000 = 0.001 A (1 unit = 1 mA)
|
||||
* Power: 1/10 = 0.1 W (1 unit = 100 mW)
|
||||
* Frequency: 1/1000 = 0.001 Hz (1 unit = 1 mHz)
|
||||
|
||||
These settings ensure accurate reporting of measurements with proper decimal precision in the Zigbee network.
|
||||
|
||||
## Hardware Required
|
||||
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Set the ADC GPIO by changing the `analogPin` variable. By default, it's the pin `A0`.
|
||||
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
|
||||
|
||||
#### Using Arduino IDE
|
||||
|
||||
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
|
||||
|
||||
* Before Compile/Verify, select the correct board: `Tools -> Board`.
|
||||
* Select the Router Zigbee mode: `Tools -> Zigbee mode: Zigbee Router`
|
||||
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
|
||||
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
|
||||
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the Router device flashed with this example is not connecting to the coordinator, erase the flash of the Router device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
|
||||
You can do the following:
|
||||
|
||||
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
|
||||
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
|
||||
|
||||
By default, the coordinator network is closed after rebooting or flashing new firmware.
|
||||
To open the network you have 2 options:
|
||||
|
||||
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
|
||||
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
|
||||
|
||||
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
|
||||
|
||||
* **LED not blinking:** Check the wiring connection and the IO selection.
|
||||
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
|
||||
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
|
||||
|
||||
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
|
||||
|
||||
## Contribute
|
||||
|
||||
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
|
||||
|
||||
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
|
||||
|
||||
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
|
||||
|
||||
## Resources
|
||||
|
||||
* Official ESP32 Forum: [Link](https://esp32.com)
|
||||
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
|
||||
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
|
||||
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
|
||||
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @brief This example demonstrates Zigbee electrical AC measurement device with multi-phase support.
|
||||
*
|
||||
* The example demonstrates how to use Zigbee library to create a router device that measures
|
||||
* AC electrical parameters like voltage, current, power, power factor and frequency across
|
||||
* three phases (A, B, C). This allows monitoring of three-phase power systems commonly used
|
||||
* in industrial and commercial applications.
|
||||
*
|
||||
* The device measures:
|
||||
* - Per phase: voltage, current, power, power factor
|
||||
* - Shared: frequency (common across all phases)
|
||||
*
|
||||
* Proper Zigbee mode must be selected in Tools->Zigbee mode
|
||||
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
|
||||
*
|
||||
* Please check the README.md for instructions and more detailed description.
|
||||
*
|
||||
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
|
||||
*/
|
||||
|
||||
// Recommended to use Router mode, as this type of device is expected to be mains powered.
|
||||
#ifndef ZIGBEE_MODE_ZCZR
|
||||
#error "Zigbee coordinator/router mode is not selected in Tools->Zigbee mode"
|
||||
#endif
|
||||
|
||||
#include "Zigbee.h"
|
||||
|
||||
/* Zigbee AC measurement device configuration */
|
||||
#define AC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER 1
|
||||
|
||||
uint8_t analogPin = A0;
|
||||
uint8_t button = BOOT_PIN;
|
||||
|
||||
ZigbeeElectricalMeasurement zbElectricalMeasurement = ZigbeeElectricalMeasurement(AC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER);
|
||||
|
||||
void onAnalogOutputChange(float analog_output) {
|
||||
Serial.printf("Received analog output change: %.1f\r\n", analog_output);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting...");
|
||||
|
||||
// Init button switch
|
||||
pinMode(button, INPUT_PULLUP);
|
||||
|
||||
// Set analog resolution to 10 bits
|
||||
analogReadResolution(10);
|
||||
|
||||
// Optional: set Zigbee device name and model
|
||||
zbElectricalMeasurement.setManufacturerAndModel("Espressif", "ZigbeeElectricalMeasurementAC");
|
||||
|
||||
// Add analog clusters to Zigbee Analog according your needs
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_B);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_B);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_B);
|
||||
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_C);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_C);
|
||||
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_C);
|
||||
|
||||
zbElectricalMeasurement.addACMeasurement(
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC
|
||||
); // frequency is not phase specific (shared)
|
||||
|
||||
// Recommended: set Multiplier/Divisor for the measurements (common for all phases)
|
||||
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, 1, 100); // 1/100 = 0.01V (1 unit of measurement = 0.01V = 10mV)
|
||||
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, 1, 1000); // 1/1000 = 0.001A (1 unit of measurement = 0.001A = 1mA)
|
||||
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, 1, 10); // 1/10 = 0.1W (1 unit of measurement = 0.1W = 100mW)
|
||||
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, 1, 1000); // 1/1000 = 0.001Hz (1 unit of measurement = 0.001Hz = 1mHz)
|
||||
|
||||
// Optional: set Min/max values for the measurements
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, 0, 30000); // 0-300.00V
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, 0, 10000); // 0-10.000A
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, 0, 32000); // 0-3200.0W
|
||||
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_B, 0, 30000); // 0-300.00V
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_B, 0, 10000); // 0-10.000A
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_B, 0, 32000); // 0-3200.0W
|
||||
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_C, 0, 30000); // 0-300.00V
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_C, 0, 10000); // 0-10.000A
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_C, 0, 32000); // 0-3200.0W
|
||||
|
||||
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, 0, 65000); // 0-65.000Hz
|
||||
|
||||
// Add endpoints to Zigbee Core
|
||||
Zigbee.addEndpoint(&zbElectricalMeasurement);
|
||||
|
||||
Serial.println("Starting Zigbee...");
|
||||
// When all EPs are registered, start Zigbee in Router mode
|
||||
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
|
||||
Serial.println("Zigbee failed to start!");
|
||||
Serial.println("Rebooting...");
|
||||
ESP.restart();
|
||||
} else {
|
||||
Serial.println("Zigbee started successfully!");
|
||||
}
|
||||
Serial.println("Connecting to network");
|
||||
while (!Zigbee.connected()) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println("Connected");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static uint32_t timeCounter = 0;
|
||||
static uint8_t randomizer = 0;
|
||||
// Read ADC value and update the analog value every 2s
|
||||
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
|
||||
uint16_t voltage = 23000 + randomizer; // 230.00 V
|
||||
uint16_t current = analogReadMilliVolts(analogPin); // demonstrates approx 0-3.3A
|
||||
int16_t power = ((voltage / 100) * (current / 1000) * 10); //calculate power in W
|
||||
uint16_t frequency = 50135; // 50.000 Hz
|
||||
Serial.printf("Updating AC voltage to %d (0.01V), current to %d (mA), power to %d (0.1W), frequency to %d (mHz)\r\n", voltage, current, power, frequency);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, voltage);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, current);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, power);
|
||||
|
||||
// Phase B demonstrates phase shift
|
||||
current += 500;
|
||||
voltage += 500;
|
||||
power = ((voltage / 100) * (current / 1000) * 10); //calculate power in W
|
||||
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_B, voltage);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_B, current);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_B, power);
|
||||
|
||||
// Phase C demonstrates phase shift
|
||||
current += 500;
|
||||
voltage += 500;
|
||||
power = ((voltage / 100) * (current / 1000) * 10); //calculate power in W
|
||||
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_C, voltage);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_C, current);
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_C, power);
|
||||
|
||||
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, frequency);
|
||||
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A);
|
||||
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_B);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_B);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_B);
|
||||
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_C);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_C);
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_C);
|
||||
|
||||
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC);
|
||||
|
||||
randomizer += 10;
|
||||
}
|
||||
|
||||
// Checking button for factory reset and reporting
|
||||
if (digitalRead(button) == LOW) { // Push button pressed
|
||||
// Key debounce handling
|
||||
delay(100);
|
||||
int startTime = millis();
|
||||
while (digitalRead(button) == LOW) {
|
||||
delay(50);
|
||||
if ((millis() - startTime) > 3000) {
|
||||
// If key pressed for more than 3secs, factory reset Zigbee and reboot
|
||||
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
|
||||
delay(1000);
|
||||
Zigbee.factoryReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr",
|
||||
"requires": [
|
||||
"CONFIG_ZB_ENABLED=y"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
# Arduino-ESP32 Zigbee DC Electrical Measurement Example
|
||||
|
||||
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) DC electrical measurement device that reports voltage, current, and power measurements.
|
||||
|
||||
# Supported Targets
|
||||
|
||||
Currently, this example supports the following targets.
|
||||
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
## DC Electrical Measurement Functions
|
||||
|
||||
* After this board first starts up, it would be configured locally to report DC electrical measurements:
|
||||
- DC voltage in millivolts (0-5000 mV)
|
||||
- DC current in milliamps (0-1000 mA)
|
||||
- DC power in milliwatts (0-5000 mW)
|
||||
* Holding the button (BOOT) for more than 3 seconds will trigger a factory reset of the device.
|
||||
* The device reports measurements every 30 seconds if the value changes by more than the configured delta.
|
||||
|
||||
## Measurement Precision
|
||||
|
||||
The example demonstrates how to set up proper measurement precision using multiplier and divisor values:
|
||||
* Voltage: 1/1000 = 0.001 V (1 unit = 1 mV)
|
||||
* Current: 1/1000 = 0.001 A (1 unit = 1 mA)
|
||||
* Power: 1/1000 = 0.001 W (1 unit = 1 mW)
|
||||
|
||||
These settings ensure accurate reporting of measurements with proper decimal precision in the Zigbee network.
|
||||
|
||||
## Hardware Required
|
||||
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Set the ADC GPIO by changing the `analogPin` variable. By default, it's the pin `A0`.
|
||||
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
|
||||
|
||||
#### Using Arduino IDE
|
||||
|
||||
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
|
||||
|
||||
* Before Compile/Verify, select the correct board: `Tools -> Board`.
|
||||
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
|
||||
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
|
||||
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
|
||||
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
|
||||
You can do the following:
|
||||
|
||||
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
|
||||
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
|
||||
|
||||
By default, the coordinator network is closed after rebooting or flashing new firmware.
|
||||
To open the network you have 2 options:
|
||||
|
||||
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
|
||||
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
|
||||
|
||||
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
|
||||
|
||||
* **LED not blinking:** Check the wiring connection and the IO selection.
|
||||
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
|
||||
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
|
||||
|
||||
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
|
||||
|
||||
## Contribute
|
||||
|
||||
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
|
||||
|
||||
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
|
||||
|
||||
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
|
||||
|
||||
## Resources
|
||||
|
||||
* Official ESP32 Forum: [Link](https://esp32.com)
|
||||
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
|
||||
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
|
||||
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
|
||||
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @brief This example demonstrates a Zigbee DC electrical measurement sensor.
|
||||
*
|
||||
* The example shows how to use the Zigbee library to create an end device that measures
|
||||
* DC voltage, current and power using the Electrical Measurement cluster.
|
||||
*
|
||||
* The device reports:
|
||||
* - DC voltage in millivolts (0-5000mV)
|
||||
* - DC current in milliamps (0-1000mA)
|
||||
* - DC power in milliwatts (0-5000mW)
|
||||
*
|
||||
* Proper Zigbee mode must be selected in Tools->Zigbee mode
|
||||
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
|
||||
*
|
||||
* Please check the README.md for instructions and more detailed description.
|
||||
*
|
||||
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
|
||||
*/
|
||||
|
||||
#ifndef ZIGBEE_MODE_ED
|
||||
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
|
||||
#endif
|
||||
|
||||
#include "Zigbee.h"
|
||||
|
||||
/* Zigbee DC measurement device configuration */
|
||||
#define DC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER 1
|
||||
|
||||
uint8_t analogPin = A0;
|
||||
uint8_t button = BOOT_PIN;
|
||||
|
||||
ZigbeeElectricalMeasurement zbElectricalMeasurement = ZigbeeElectricalMeasurement(DC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting...");
|
||||
|
||||
// Init button switch
|
||||
pinMode(button, INPUT_PULLUP);
|
||||
|
||||
// Set analog resolution to 10 bits
|
||||
analogReadResolution(10);
|
||||
|
||||
// Optional: set Zigbee device name and model
|
||||
zbElectricalMeasurement.setManufacturerAndModel("Espressif", "ZigbeeElectricalMeasurementDC");
|
||||
|
||||
// Add analog clusters to Zigbee Analog according your needs
|
||||
zbElectricalMeasurement.addDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE);
|
||||
zbElectricalMeasurement.addDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT);
|
||||
zbElectricalMeasurement.addDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_POWER);
|
||||
|
||||
// // Optional: set Min/max values for the measurements
|
||||
zbElectricalMeasurement.setDCMinMaxValue(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE, 0, 5000); // 0-5.000V
|
||||
zbElectricalMeasurement.setDCMinMaxValue(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT, 0, 1000); // 0-1.000A
|
||||
zbElectricalMeasurement.setDCMinMaxValue(ZIGBEE_DC_MEASUREMENT_TYPE_POWER, 0, 5000); // 0-5.000W
|
||||
|
||||
// // Optional: set Multiplier/Divisor for the measurements
|
||||
zbElectricalMeasurement.setDCMultiplierDivisor(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE, 1, 1000); // 1/1000 = 0.001V (1 unit of measurement = 0.001V = 1mV)
|
||||
zbElectricalMeasurement.setDCMultiplierDivisor(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT, 1, 1000); // 1/1000 = 0.001A (1 unit of measurement = 0.001A = 1mA)
|
||||
zbElectricalMeasurement.setDCMultiplierDivisor(ZIGBEE_DC_MEASUREMENT_TYPE_POWER, 1, 1000); // 1/1000 = 0.001W (1 unit of measurement = 0.001W = 1mW)
|
||||
|
||||
// Add endpoints to Zigbee Core
|
||||
Zigbee.addEndpoint(&zbElectricalMeasurement);
|
||||
|
||||
Serial.println("Starting Zigbee...");
|
||||
// When all EPs are registered, start Zigbee in End Device mode
|
||||
if (!Zigbee.begin()) {
|
||||
Serial.println("Zigbee failed to start!");
|
||||
Serial.println("Rebooting...");
|
||||
ESP.restart();
|
||||
} else {
|
||||
Serial.println("Zigbee started successfully!");
|
||||
}
|
||||
Serial.println("Connecting to network");
|
||||
while (!Zigbee.connected()) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println("Connected");
|
||||
|
||||
// Optional: Add reporting for DC measurements (this is overridden by HomeAssistant ZHA if connected to its network)
|
||||
zbElectricalMeasurement.setDCReporting(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE, 0, 30, 10); // report every 30 seconds if value changes by 10 (0.1V)
|
||||
zbElectricalMeasurement.setDCReporting(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT, 0, 30, 10); // report every 30 seconds if value changes by 10 (0.1A)
|
||||
zbElectricalMeasurement.setDCReporting(ZIGBEE_DC_MEASUREMENT_TYPE_POWER, 0, 30, 10); // report every 30 seconds if value changes by 10 (0.1W)
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static uint32_t timeCounter = 0;
|
||||
static uint8_t randomizer = 0;
|
||||
// Read ADC value and update the analog value every 2s
|
||||
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
|
||||
int16_t voltage_mv = (int16_t)(analogReadMilliVolts(analogPin));
|
||||
int16_t current_ma = randomizer; //0-255mA
|
||||
int16_t power_mw = voltage_mv * current_ma / 1000; //calculate power in mW
|
||||
Serial.printf("Updating DC voltage to %d mV, current to %d mA, power to %d mW\r\n", voltage_mv, current_ma, power_mw);
|
||||
zbElectricalMeasurement.setDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE, voltage_mv);
|
||||
zbElectricalMeasurement.setDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT, current_ma);
|
||||
zbElectricalMeasurement.setDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_POWER, power_mw);
|
||||
// Analog input supports reporting
|
||||
zbElectricalMeasurement.reportDC(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE);
|
||||
zbElectricalMeasurement.reportDC(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT);
|
||||
zbElectricalMeasurement.reportDC(ZIGBEE_DC_MEASUREMENT_TYPE_POWER);
|
||||
|
||||
randomizer += 10;
|
||||
}
|
||||
|
||||
// Checking button for factory reset and reporting
|
||||
if (digitalRead(button) == LOW) { // Push button pressed
|
||||
// Key debounce handling
|
||||
delay(100);
|
||||
int startTime = millis();
|
||||
while (digitalRead(button) == LOW) {
|
||||
delay(50);
|
||||
if ((millis() - startTime) > 3000) {
|
||||
// If key pressed for more than 3secs, factory reset Zigbee and reboot
|
||||
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
|
||||
delay(1000);
|
||||
Zigbee.factoryReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
|
||||
"requires": [
|
||||
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
|
||||
"CONFIG_ZB_ENABLED=y"
|
||||
]
|
||||
}
|
||||
110
libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/README.md
Normal file
110
libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/README.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# Arduino-ESP32 Zigbee Multi-Switch Example
|
||||
|
||||
This example demonstrates how to configure a Zigbee device as a multi-switch controller that can control up to three different Zigbee lights independently. The switch can operate in either coordinator or router mode, making it compatible with Home Assistant integration.
|
||||
|
||||
# Supported Targets
|
||||
|
||||
Currently, this example supports the following targets.
|
||||
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
## Hardware Required
|
||||
|
||||
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee multi-switch controller
|
||||
* One or more Zigbee light devices (loaded with Zigbee_On_Off_Light example)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
The example uses the BOOT button (pin 9) on ESP32-C6 and ESP32-H2 as the physical switch input. The switch can be configured to operate in two modes:
|
||||
|
||||
1. **Coordinator Mode**: For running your own Zigbee network
|
||||
2. **Router Mode**: For Home Assistant integration
|
||||
|
||||
#### Using Arduino IDE
|
||||
|
||||
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
|
||||
|
||||
* Before Compile/Verify, select the correct board: `Tools -> Board`
|
||||
* Select the Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`
|
||||
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
|
||||
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port
|
||||
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`
|
||||
|
||||
## Features
|
||||
|
||||
The multi-switch example provides the following functionality:
|
||||
|
||||
1. **Light Configuration**
|
||||
- Configure up to 3 different lights using their endpoint and IEEE address
|
||||
- Configuration is stored in NVS (Non-Volatile Storage) and persists after power loss
|
||||
- Remove configured lights when needed
|
||||
|
||||
2. **Control Commands**
|
||||
- Control all bound lights simultaneously:
|
||||
- Turn all bound lights ON
|
||||
- Turn all bound lights OFF
|
||||
- Toggle all bound lights
|
||||
- Control individual lights (1-3):
|
||||
- Turn light ON
|
||||
- Turn light OFF
|
||||
- Toggle light
|
||||
|
||||
3. **Network Management**
|
||||
- Factory reset capability
|
||||
- Open network for device joining
|
||||
- View bound devices and current light configurations
|
||||
|
||||
## Serial Commands
|
||||
|
||||
The example accepts the following commands through the serial interface:
|
||||
|
||||
* `config` - Configure a new light (requires light number, endpoint, and IEEE address)
|
||||
* `remove` - Remove a configured light
|
||||
* `on` - Turn all bound lights ON
|
||||
* `off` - Turn all bound lights OFF
|
||||
* `toggle` - Toggle all bound lights
|
||||
* `1on`, `2on`, `3on` - Turn individual light ON
|
||||
* `1off`, `2off`, `3off` - Turn individual light OFF
|
||||
* `1toggle`, `2toggle`, `3toggle` - Toggle individual light
|
||||
* `freset` - Perform factory reset
|
||||
* `open_network` - Open network for device joining (only for coordinator role)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the End device flashed with the example `Zigbee_On_Off_Light` is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
|
||||
You can do the following:
|
||||
|
||||
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`
|
||||
* In the `Zigbee_On_Off_Light` example sketch call `Zigbee.factoryReset()`
|
||||
|
||||
By default, the coordinator network is closed after rebooting or flashing new firmware.
|
||||
To open the network you have 2 options:
|
||||
|
||||
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time)` before calling `Zigbee.begin()`
|
||||
* In application you can anytime call `Zigbee.openNetwork(time)` to open the network for devices to join
|
||||
|
||||
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
|
||||
|
||||
* **LED not blinking:** Check the wiring connection and the IO selection
|
||||
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed
|
||||
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation
|
||||
|
||||
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
|
||||
|
||||
## Contribute
|
||||
|
||||
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
|
||||
|
||||
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
|
||||
|
||||
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
|
||||
|
||||
## Resources
|
||||
|
||||
* Official ESP32 Forum: [Link](https://esp32.com)
|
||||
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
|
||||
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
|
||||
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
|
||||
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @brief This example demonstrates simple Zigbee multi-light switch.
|
||||
*
|
||||
* The example demonstrates how to use Zigbee library to control multiple light bulbs.
|
||||
* The light bulbs are Zigbee devices, which are controlled by a Zigbee coordinator/router (Multi-Switch).
|
||||
* Settings are stored in NVS to not be lost after power loss.
|
||||
* Configuring and controlling the lights is done via serial input.
|
||||
*
|
||||
* Proper Zigbee mode must be selected in Tools->Zigbee mode
|
||||
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
|
||||
*
|
||||
* Please check the README.md for instructions and more detailed description.
|
||||
*
|
||||
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
|
||||
*/
|
||||
|
||||
#ifndef ZIGBEE_MODE_ZCZR
|
||||
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
|
||||
#endif
|
||||
|
||||
#include "Zigbee.h"
|
||||
#include <Preferences.h>
|
||||
|
||||
#define ZIGBEE_ROLE ZIGBEE_ROUTER // ZIGBEE_ROUTER for HomeAssistant integration, ZIGBEE_COORDINATOR for running own network
|
||||
|
||||
/* Zigbee switch configuration */
|
||||
#define SWITCH_ENDPOINT_NUMBER 1
|
||||
|
||||
uint8_t button = BOOT_PIN;
|
||||
|
||||
ZigbeeSwitch zbSwitch = ZigbeeSwitch(SWITCH_ENDPOINT_NUMBER);
|
||||
|
||||
int buttonState;
|
||||
int lastButtonState = LOW;
|
||||
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
|
||||
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
|
||||
|
||||
// To be stored in NVS to not be lost after power loss
|
||||
Preferences prefs;
|
||||
|
||||
zb_device_params_t light_1;
|
||||
zb_device_params_t light_2;
|
||||
zb_device_params_t light_3;
|
||||
|
||||
void storeLightParams(zb_device_params_t *light, int light_number) {
|
||||
char key[10];
|
||||
snprintf(key, sizeof(key), "light_%d", light_number);
|
||||
prefs.putBytes(key, light, sizeof(zb_device_params_t));
|
||||
}
|
||||
|
||||
void loadLightParams(zb_device_params_t *light, int light_number) {
|
||||
char key[10];
|
||||
snprintf(key, sizeof(key), "light_%d", light_number);
|
||||
prefs.getBytes(key, light, sizeof(zb_device_params_t));
|
||||
}
|
||||
|
||||
/********************* Arduino functions **************************/
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize Preferences
|
||||
prefs.begin("lights", false); // false means read/write mode
|
||||
|
||||
// Load saved light parameters
|
||||
loadLightParams(&light_1, 1);
|
||||
loadLightParams(&light_2, 2);
|
||||
loadLightParams(&light_3, 3);
|
||||
|
||||
// Init button switch
|
||||
pinMode(button, INPUT_PULLUP);
|
||||
|
||||
// Set Zigbee device name and model
|
||||
zbSwitch.setManufacturerAndModel("Espressif", "ZBMultiSwitch");
|
||||
|
||||
// Set binding settings depending on the role
|
||||
if (ZIGBEE_ROLE == ZIGBEE_COORDINATOR) {
|
||||
zbSwitch.allowMultipleBinding(true); // To allow binding multiple lights to the switch
|
||||
} else {
|
||||
zbSwitch.setManualBinding(true); //Set manual binding to true, so binding is done on Home Assistant side
|
||||
}
|
||||
|
||||
// Add endpoint to Zigbee Core
|
||||
Serial.println("Adding ZigbeeSwitch endpoint to Zigbee Core");
|
||||
Zigbee.addEndpoint(&zbSwitch);
|
||||
|
||||
// When all EPs are registered, start Zigbee with given role
|
||||
if (!Zigbee.begin(ZIGBEE_ROLE)) {
|
||||
Serial.println("Zigbee failed to start!");
|
||||
Serial.println("Rebooting...");
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
Serial.println("Connecting to network");
|
||||
while (!Zigbee.connected()) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Handle button switch in loop()
|
||||
if (digitalRead(button) == LOW) { // Push button pressed
|
||||
// Key debounce handling
|
||||
while (digitalRead(button) == LOW) {
|
||||
delay(50);
|
||||
}
|
||||
// Print bound devices
|
||||
Serial.println("Bound devices:");
|
||||
zbSwitch.printBoundDevices(Serial);
|
||||
Serial.println("Lights configured:");
|
||||
Serial.printf("Light 1: %d %s\n", light_1.endpoint, Zigbee.formatIEEEAddress(light_1.ieee_addr));
|
||||
Serial.printf("Light 2: %d %s\n", light_2.endpoint, Zigbee.formatIEEEAddress(light_2.ieee_addr));
|
||||
Serial.printf("Light 3: %d %s\n", light_3.endpoint, Zigbee.formatIEEEAddress(light_3.ieee_addr));
|
||||
}
|
||||
// Handle serial input to configure and control the lights
|
||||
if (Serial.available()) {
|
||||
String command = Serial.readString();
|
||||
Serial.println("Command: " + command);
|
||||
|
||||
if (command == "config") {
|
||||
//wait for light number, endpoint and ieee address
|
||||
Serial.println("Enter light number (1-3):");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
int light_number = Serial.parseInt();
|
||||
Serial.println("Enter endpoint:");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
int endpoint = Serial.parseInt();
|
||||
Serial.println("Enter ieee address:");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
String ieee_address = Serial.readStringUntil('\n');
|
||||
ieee_address.trim();
|
||||
//convert ieee address to uint8_t array (format in string is 00:00:00:00:00:00:00:00)
|
||||
uint8_t ieee_address_array[8];
|
||||
int index = 0;
|
||||
bool valid = true;
|
||||
|
||||
// Check if the string has the correct format (8 hex pairs with colons)
|
||||
if (ieee_address.length() != 23) { // 8 pairs * 2 + 7 colons
|
||||
Serial.println("Invalid IEEE address format. Expected format: 00:00:00:00:00:00:00:00");
|
||||
valid = false;
|
||||
} else {
|
||||
for (int i = 0; i < ieee_address.length() && index < 8 && valid; i += 3) {
|
||||
// Check for colon at expected positions
|
||||
if (i > 0 && ieee_address.charAt(i - 1) != ':') {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
// Convert two hex characters to a byte
|
||||
char hex[3] = {ieee_address.charAt(i), ieee_address.charAt(i + 1), '\0'};
|
||||
char *endptr;
|
||||
long value = strtol(hex, &endptr, 16);
|
||||
if (*endptr != '\0' || value < 0 || value > 255) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
// Store bytes in reverse order to match Zigbee standard
|
||||
ieee_address_array[7 - index++] = (uint8_t)value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid || index != 8) {
|
||||
Serial.println("Invalid IEEE address. Please enter a valid address in format: 00:00:00:00:00:00:00:00");
|
||||
return;
|
||||
}
|
||||
//set the light parameters
|
||||
if (light_number == 1) {
|
||||
light_1.endpoint = endpoint;
|
||||
memcpy(light_1.ieee_addr, ieee_address_array, 8);
|
||||
storeLightParams(&light_1, 1);
|
||||
} else if (light_number == 2) {
|
||||
light_2.endpoint = endpoint;
|
||||
memcpy(light_2.ieee_addr, ieee_address_array, 8);
|
||||
storeLightParams(&light_2, 2);
|
||||
} else if (light_number == 3) {
|
||||
light_3.endpoint = endpoint;
|
||||
memcpy(light_3.ieee_addr, ieee_address_array, 8);
|
||||
storeLightParams(&light_3, 3);
|
||||
}
|
||||
Serial.printf("Light %d configured\n", light_number);
|
||||
} else if (command == "remove") {
|
||||
//wait for light number
|
||||
Serial.println("Enter light number (1-3):");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
int light_number = Serial.parseInt();
|
||||
uint8_t ieee_address_empty[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
if (light_number == 1) {
|
||||
light_1.endpoint = 0;
|
||||
memcpy(light_1.ieee_addr, ieee_address_empty, 8);
|
||||
storeLightParams(&light_1, 1);
|
||||
} else if (light_number == 2) {
|
||||
light_2.endpoint = 0;
|
||||
memcpy(light_2.ieee_addr, ieee_address_empty, 8);
|
||||
storeLightParams(&light_2, 2);
|
||||
} else if (light_number == 3) {
|
||||
light_3.endpoint = 0;
|
||||
memcpy(light_3.ieee_addr, ieee_address_empty, 8);
|
||||
storeLightParams(&light_3, 3);
|
||||
}
|
||||
Serial.printf("Light %d removed\n", light_number);
|
||||
} else if (command == "on") {
|
||||
Serial.println(" --> SIG Input : All Lights ON");
|
||||
zbSwitch.lightOn();
|
||||
} else if (command == "off") {
|
||||
Serial.println(" --> SIG Input : All Lights OFF");
|
||||
zbSwitch.lightOff();
|
||||
} else if (command == "toggle") {
|
||||
Serial.println(" --> SIG Input : All Lights Toggle");
|
||||
zbSwitch.lightToggle();
|
||||
} else if (command == "1on") {
|
||||
Serial.println(" --> SIG Input : Light 1 ON");
|
||||
zbSwitch.lightOn(light_1.endpoint, light_1.ieee_addr);
|
||||
} else if (command == "1off") {
|
||||
Serial.println(" --> SIG Input : Light 1 OFF");
|
||||
zbSwitch.lightOff(light_1.endpoint, light_1.ieee_addr);
|
||||
} else if (command == "1toggle") {
|
||||
Serial.println(" --> SIG Input : Light 1 Toggle");
|
||||
zbSwitch.lightToggle(light_1.endpoint, light_1.ieee_addr);
|
||||
} else if (command == "2on") {
|
||||
Serial.println(" --> SIG Input : Light 2 ON");
|
||||
zbSwitch.lightOn(light_2.endpoint, light_2.ieee_addr);
|
||||
} else if (command == "2off") {
|
||||
Serial.println(" --> SIG Input : Light 2 OFF");
|
||||
zbSwitch.lightOff(light_2.endpoint, light_2.ieee_addr);
|
||||
} else if (command == "2toggle") {
|
||||
Serial.println(" --> SIG Input : Light 2 Toggle");
|
||||
zbSwitch.lightToggle(light_2.endpoint, light_2.ieee_addr);
|
||||
} else if (command == "3on") {
|
||||
Serial.println(" --> SIG Input : Light 3 ON");
|
||||
zbSwitch.lightOn(light_3.endpoint, light_3.ieee_addr);
|
||||
} else if (command == "3off") {
|
||||
Serial.println(" --> SIG Input : Light 3 OFF");
|
||||
zbSwitch.lightOff(light_3.endpoint, light_3.ieee_addr);
|
||||
} else if (command == "3toggle") {
|
||||
Serial.println(" --> SIG Input : Light 3 Toggle");
|
||||
zbSwitch.lightToggle(light_3.endpoint, light_3.ieee_addr);
|
||||
} else if (command == "freset") {
|
||||
Serial.println(" --> SIG Input : Factory Reset!");
|
||||
delay(1500);
|
||||
Zigbee.factoryReset();
|
||||
} else if (command == "open_network") {
|
||||
Serial.println(" --> SIG Input : Open Network");
|
||||
if (ZIGBEE_ROLE == ZIGBEE_COORDINATOR) {
|
||||
Zigbee.openNetwork(180);
|
||||
} else {
|
||||
Serial.println("Open network is only available for coordinator role");
|
||||
}
|
||||
} else {
|
||||
Serial.println("Unknown command");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr",
|
||||
"requires": [
|
||||
"CONFIG_SOC_IEEE802154_SUPPORTED=y"
|
||||
]
|
||||
}
|
||||
61
libraries/Zigbee/examples/Zigbee_Power_Outlet/README.md
Normal file
61
libraries/Zigbee/examples/Zigbee_Power_Outlet/README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
@ -0,0 +1,68 @@
|
||||
# Arduino-ESP32 Zigbee On/Off Power Outlet Example
|
||||
|
||||
This example shows how to configure Zigbee Router device and use it as a Home Automation (HA) on/off power outlet.
|
||||
|
||||
# Supported Targets
|
||||
|
||||
Currently, this example supports the following targets.
|
||||
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
## Hardware Required
|
||||
|
||||
* A USB cable for power supply and programming.
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Set the Button GPIO by changing the `button` definition. By default, it's the pin `9` (BOOT button on ESP32-C6 and ESP32-H2).
|
||||
|
||||
#### Using Arduino IDE
|
||||
|
||||
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
|
||||
|
||||
* Before Compile/Verify, select the correct board: `Tools -> Board`.
|
||||
* Select the Coordinator Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`.
|
||||
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee ZCZR 4MB with spiffs`.
|
||||
* Select the COM port: `Tools -> Port: xxx where the `xxx` is the detected COM port.
|
||||
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device flashed with the example `Zigbee_Power_Outlet` is not connecting to the coordinator, erase the flash of the device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator or do some big changes in the application code.
|
||||
You can do the following:
|
||||
|
||||
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
|
||||
* In the `Zigbee_Power_Outlet` example sketch call `Zigbee.factoryReset();`.
|
||||
|
||||
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
|
||||
|
||||
* **LED not blinking:** Check the wiring connection and the IO selection.
|
||||
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
|
||||
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
|
||||
|
||||
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
|
||||
|
||||
## Contribute
|
||||
|
||||
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
|
||||
|
||||
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
|
||||
|
||||
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
|
||||
|
||||
## Resources
|
||||
|
||||
* Official ESP32 Forum: [Link](https://esp32.com)
|
||||
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
|
||||
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
|
||||
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
|
||||
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @brief This example demonstrates simple Zigbee power outlet.
|
||||
*
|
||||
* The example demonstrates how to use Zigbee library to create a end device power outlet.
|
||||
* The power outlet is a Zigbee end device, which is controlled by a Zigbee coordinator.
|
||||
*
|
||||
* Proper Zigbee mode must be selected in Tools->Zigbee mode
|
||||
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
|
||||
*
|
||||
* Please check the README.md for instructions and more detailed description.
|
||||
*
|
||||
* Created by Ludovic Boué (https://github.com/lboue)
|
||||
*/
|
||||
|
||||
#ifndef ZIGBEE_MODE_ZCZR
|
||||
#error "Zigbee router mode is not selected in Tools->Zigbee mode"
|
||||
#endif
|
||||
|
||||
#include "Zigbee.h"
|
||||
|
||||
/* Zigbee power outlet configuration */
|
||||
#define ZIGBEE_OUTLET_ENDPOINT 1
|
||||
uint8_t led = RGB_BUILTIN;
|
||||
uint8_t button = BOOT_PIN;
|
||||
|
||||
ZigbeePowerOutlet zbOutlet = ZigbeePowerOutlet(ZIGBEE_OUTLET_ENDPOINT);
|
||||
|
||||
/********************* RGB LED functions **************************/
|
||||
void setLED(bool value) {
|
||||
digitalWrite(led, value);
|
||||
}
|
||||
|
||||
/********************* Arduino functions **************************/
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
|
||||
pinMode(led, OUTPUT);
|
||||
digitalWrite(led, LOW);
|
||||
|
||||
// Init button for factory reset
|
||||
pinMode(button, INPUT_PULLUP);
|
||||
|
||||
//Optional: set Zigbee device name and model
|
||||
zbOutlet.setManufacturerAndModel("Espressif", "ZBPowerOutlet");
|
||||
|
||||
// Set callback function for power outlet change
|
||||
zbOutlet.onPowerOutletChange(setLED);
|
||||
|
||||
//Add endpoint to Zigbee Core
|
||||
Serial.println("Adding ZigbeePowerOutlet endpoint to Zigbee Core");
|
||||
Zigbee.addEndpoint(&zbOutlet);
|
||||
|
||||
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
|
||||
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
|
||||
Serial.println("Zigbee failed to start!");
|
||||
Serial.println("Rebooting...");
|
||||
ESP.restart();
|
||||
}
|
||||
Serial.println("Connecting to network");
|
||||
while (!Zigbee.connected()) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Checking button for factory reset
|
||||
if (digitalRead(button) == LOW) { // Push button pressed
|
||||
// Key debounce handling
|
||||
delay(100);
|
||||
int startTime = millis();
|
||||
while (digitalRead(button) == LOW) {
|
||||
delay(50);
|
||||
if ((millis() - startTime) > 3000) {
|
||||
// If key pressed for more than 3secs, factory reset Zigbee and reboot
|
||||
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
|
||||
delay(1000);
|
||||
Zigbee.factoryReset();
|
||||
}
|
||||
}
|
||||
// Toggle state by pressing the button
|
||||
zbOutlet.setState(!zbOutlet.getPowerOutletState());
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
7
libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json
Normal file
7
libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr",
|
||||
"requires": [
|
||||
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
|
||||
"CONFIG_ZB_ENABLED=y"
|
||||
]
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ ZigbeeRangeExtender KEYWORD1
|
|||
ZigbeeVibrationSensor KEYWORD1
|
||||
ZigbeeWindowCovering KEYWORD1
|
||||
ZigbeeIlluminanceSensor KEYWORD1
|
||||
ZigbeePowerOutlet KEYWORD1
|
||||
|
||||
# Other
|
||||
zigbee_role_t KEYWORD1
|
||||
|
|
|
|||
|
|
@ -16,11 +16,15 @@
|
|||
#include "ep/ZigbeeLight.h"
|
||||
//// Controllers
|
||||
#include "ep/ZigbeeThermostat.h"
|
||||
////Outlets
|
||||
#include "ep/ZigbeePowerOutlet.h"
|
||||
//// Sensors
|
||||
#include "ep/ZigbeeAnalog.h"
|
||||
#include "ep/ZigbeeBinary.h"
|
||||
#include "ep/ZigbeeCarbonDioxideSensor.h"
|
||||
#include "ep/ZigbeeContactSwitch.h"
|
||||
#include "ep/ZigbeeDoorWindowHandle.h"
|
||||
#include "ep/ZigbeeElectricalMeasurement.h"
|
||||
#include "ep/ZigbeeFlowSensor.h"
|
||||
#include "ep/ZigbeeIlluminanceSensor.h"
|
||||
#include "ep/ZigbeeOccupancySensor.h"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "ZigbeeHandlers.cpp"
|
||||
#include "Arduino.h"
|
||||
#include <set>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -30,6 +31,7 @@ ZigbeeCore::ZigbeeCore() {
|
|||
_connected = false;
|
||||
_scan_duration = 3; // default scan duration
|
||||
_rx_on_when_idle = true;
|
||||
_debug = false;
|
||||
if (!lock) {
|
||||
lock = xSemaphoreCreateBinary();
|
||||
if (lock == NULL) {
|
||||
|
|
@ -40,6 +42,7 @@ ZigbeeCore::ZigbeeCore() {
|
|||
|
||||
//forward declaration
|
||||
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message);
|
||||
bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind);
|
||||
|
||||
bool ZigbeeCore::begin(esp_zb_cfg_t *role_cfg, bool erase_nvs) {
|
||||
if (!zigbeeInit(role_cfg, erase_nvs)) {
|
||||
|
|
@ -173,6 +176,9 @@ bool ZigbeeCore::zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Register APSDATA INDICATION handler to catch bind/unbind requests
|
||||
esp_zb_aps_data_indication_handler_register(zb_apsde_data_indication_handler);
|
||||
|
||||
//Erase NVRAM before creating connection to new Coordinator
|
||||
if (erase_nvs) {
|
||||
esp_zb_nvram_erase_at_start(true);
|
||||
|
|
@ -223,6 +229,13 @@ void ZigbeeCore::openNetwork(uint8_t time) {
|
|||
}
|
||||
}
|
||||
|
||||
void ZigbeeCore::closeNetwork() {
|
||||
if (started()) {
|
||||
log_v("Closing network");
|
||||
esp_zb_bdb_close_network();
|
||||
}
|
||||
}
|
||||
|
||||
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) {
|
||||
ESP_ERROR_CHECK(esp_zb_bdb_start_top_level_commissioning(mode_mask));
|
||||
}
|
||||
|
|
@ -234,6 +247,9 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t)*p_sg_p;
|
||||
//coordinator variables
|
||||
esp_zb_zdo_signal_device_annce_params_t *dev_annce_params = NULL;
|
||||
esp_zb_zdo_signal_leave_params_t *leave_params = NULL;
|
||||
//router variables
|
||||
esp_zb_zdo_signal_device_update_params_t *dev_update_params = NULL;
|
||||
|
||||
//main switch
|
||||
switch (sig_type) {
|
||||
|
|
@ -267,7 +283,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
} else {
|
||||
// Save the channel mask to NVRAM in case of reboot which may be on a different channel after a change in the network
|
||||
Zigbee.setNVRAMChannelMask(1 << esp_zb_get_current_channel());
|
||||
Zigbee._connected = true;
|
||||
Zigbee._connected = true; // Coordinator is always connected
|
||||
}
|
||||
Zigbee.searchBindings();
|
||||
}
|
||||
|
|
@ -287,6 +303,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4], extended_pan_id[3], extended_pan_id[2], extended_pan_id[1],
|
||||
extended_pan_id[0], esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address()
|
||||
);
|
||||
Zigbee._connected = true;
|
||||
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
|
||||
} else {
|
||||
log_i("Restart network formation (status: %s)", esp_err_to_name(err_status));
|
||||
|
|
@ -340,20 +357,60 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
*/
|
||||
// for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
|
||||
// Check if the device is already bound
|
||||
bool found = false;
|
||||
// Get the list of devices bound to the EP
|
||||
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
|
||||
for (std::list<zb_device_params_t *>::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) {
|
||||
if (((*device)->short_addr == dev_annce_params->device_short_addr) || (memcmp((*device)->ieee_addr, dev_annce_params->ieee_addr, 8) == 0)) {
|
||||
found = true;
|
||||
log_d("Device already bound to endpoint %d", (*it)->getEndpoint());
|
||||
break;
|
||||
log_d("Checking endpoint %d", (*it)->getEndpoint());
|
||||
if (!(*it)->epUseManualBinding()) {
|
||||
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
|
||||
// Check if the device is already bound
|
||||
bool found = false;
|
||||
// Get the list of devices bound to the EP
|
||||
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
|
||||
for (std::list<zb_device_params_t *>::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) {
|
||||
if (((*device)->short_addr == dev_annce_params->device_short_addr) || (memcmp((*device)->ieee_addr, dev_annce_params->ieee_addr, 8) == 0)) {
|
||||
found = true;
|
||||
log_d("Device already bound to endpoint %d", (*it)->getEndpoint());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint());
|
||||
(*it)->findEndpoint(&cmd_req);
|
||||
log_d("Endpoint %d is searching for device", (*it)->getEndpoint());
|
||||
break; // Only one endpoint per device
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
(*it)->findEndpoint(&cmd_req);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_ZB_ZDO_SIGNAL_DEVICE_UPDATE: // Router
|
||||
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_ROUTER) {
|
||||
dev_update_params = (esp_zb_zdo_signal_device_update_params_t *)esp_zb_app_signal_get_params(p_sg_p);
|
||||
log_i("New device commissioned or rejoined (short: 0x%04hx)", dev_update_params->short_addr);
|
||||
esp_zb_zdo_match_desc_req_param_t cmd_req;
|
||||
cmd_req.dst_nwk_addr = dev_update_params->short_addr;
|
||||
cmd_req.addr_of_interest = dev_update_params->short_addr;
|
||||
// for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
log_d("Checking endpoint %d", (*it)->getEndpoint());
|
||||
if (!(*it)->epUseManualBinding()) {
|
||||
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
|
||||
// Check if the device is already bound
|
||||
bool found = false;
|
||||
// Get the list of devices bound to the EP
|
||||
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
|
||||
for (std::list<zb_device_params_t *>::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) {
|
||||
if (((*device)->short_addr == dev_update_params->short_addr) || (memcmp((*device)->ieee_addr, dev_update_params->long_addr, 8) == 0)) {
|
||||
found = true;
|
||||
log_d("Device already bound to endpoint %d", (*it)->getEndpoint());
|
||||
break;
|
||||
}
|
||||
}
|
||||
log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint());
|
||||
if (!found) {
|
||||
(*it)->findEndpoint(&cmd_req);
|
||||
log_d("Endpoint %d is searching for device", (*it)->getEndpoint());
|
||||
break; // Only one endpoint per device
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -371,15 +428,47 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
}
|
||||
break;
|
||||
case ESP_ZB_ZDO_SIGNAL_LEAVE: // End Device + Router
|
||||
// Device was removed from the network, factory reset the device
|
||||
// Received signal to leave the network
|
||||
if ((zigbee_role_t)Zigbee.getRole() != ZIGBEE_COORDINATOR) {
|
||||
Zigbee.factoryReset(true);
|
||||
leave_params = (esp_zb_zdo_signal_leave_params_t *)esp_zb_app_signal_get_params(p_sg_p);
|
||||
log_v("Signal to leave the network, leave type: %d", leave_params->leave_type);
|
||||
if (leave_params->leave_type == ESP_ZB_NWK_LEAVE_TYPE_RESET) { // Leave without rejoin -> Factory reset
|
||||
log_i("Leave without rejoin, factory reset the device");
|
||||
Zigbee.factoryReset(true);
|
||||
} else { // Leave with rejoin -> Rejoin the network, only reboot the device
|
||||
log_i("Leave with rejoin, only reboot the device");
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: log_v("ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type, esp_err_to_name(err_status)); break;
|
||||
}
|
||||
}
|
||||
|
||||
// APS DATA INDICATION HANDLER to catch bind/unbind requests
|
||||
bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind) {
|
||||
if (Zigbee.getDebugMode()) {
|
||||
log_d("APSDE INDICATION - Received APSDE-DATA indication, status: %d", ind.status);
|
||||
log_d(
|
||||
"APSDE INDICATION - dst_endpoint: %d, src_endpoint: %d, dst_addr_mode: %d, src_addr_mode: %d, cluster_id: 0x%04x, asdu_length: %d", ind.dst_endpoint,
|
||||
ind.src_endpoint, ind.dst_addr_mode, ind.src_addr_mode, ind.cluster_id, ind.asdu_length
|
||||
);
|
||||
log_d(
|
||||
"APSDE INDICATION - dst_short_addr: 0x%04x, src_short_addr: 0x%04x, profile_id: 0x%04x, security_status: %d, lqi: %d, rx_time: %d", ind.dst_short_addr,
|
||||
ind.src_short_addr, ind.profile_id, ind.security_status, ind.lqi, ind.rx_time
|
||||
);
|
||||
}
|
||||
if (ind.status == 0x00) {
|
||||
// Catch bind/unbind requests to update the bound devices list
|
||||
if (ind.cluster_id == 0x21 || ind.cluster_id == 0x22) {
|
||||
Zigbee.searchBindings();
|
||||
}
|
||||
} else {
|
||||
log_e("APSDE INDICATION - Invalid status of APSDE-DATA indication, error code: %d", ind.status);
|
||||
}
|
||||
return false; //False to let the stack process the message as usual
|
||||
}
|
||||
|
||||
void ZigbeeCore::factoryReset(bool restart) {
|
||||
if (restart) {
|
||||
log_v("Factory resetting Zigbee stack, device will reboot");
|
||||
|
|
@ -444,63 +533,194 @@ void ZigbeeCore::scanDelete() {
|
|||
_scan_status = ZB_SCAN_FAILED;
|
||||
}
|
||||
|
||||
// Recall bounded devices from the binding table after reboot
|
||||
// Recall bounded devices from the binding table after reboot or when requested
|
||||
void ZigbeeCore::bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx) {
|
||||
bool done = true;
|
||||
esp_zb_zdo_mgmt_bind_param_t *req = (esp_zb_zdo_mgmt_bind_param_t *)user_ctx;
|
||||
esp_zb_zdp_status_t zdo_status = (esp_zb_zdp_status_t)table_info->status;
|
||||
log_d("Binding table callback for address 0x%04x with status %d", req->dst_addr, zdo_status);
|
||||
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
// Print binding table log simple
|
||||
log_d("Binding table info: total %d, index %d, count %d", table_info->total, table_info->index, table_info->count);
|
||||
|
||||
if (table_info->total == 0) {
|
||||
log_d("No binding table entries found");
|
||||
// Clear all bound devices since there are no entries
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
log_d("Clearing bound devices for EP %d", (*it)->getEndpoint());
|
||||
(*it)->clearBoundDevices();
|
||||
}
|
||||
free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a set to track found devices using both short and IEEE addresses
|
||||
struct DeviceIdentifier {
|
||||
uint8_t endpoint;
|
||||
uint16_t short_addr;
|
||||
esp_zb_ieee_addr_t ieee_addr;
|
||||
bool is_ieee;
|
||||
|
||||
bool operator<(const DeviceIdentifier &other) const {
|
||||
if (endpoint != other.endpoint) {
|
||||
return endpoint < other.endpoint;
|
||||
}
|
||||
if (is_ieee != other.is_ieee) {
|
||||
return is_ieee < other.is_ieee;
|
||||
}
|
||||
if (is_ieee) {
|
||||
return memcmp(ieee_addr, other.ieee_addr, sizeof(esp_zb_ieee_addr_t)) < 0;
|
||||
}
|
||||
return short_addr < other.short_addr;
|
||||
}
|
||||
};
|
||||
static std::set<DeviceIdentifier> found_devices;
|
||||
static std::vector<esp_zb_zdo_binding_table_record_t> all_records;
|
||||
|
||||
// If this is the first chunk (index 0), clear the previous data
|
||||
if (table_info->index == 0) {
|
||||
found_devices.clear();
|
||||
all_records.clear();
|
||||
}
|
||||
|
||||
// Add current records to our collection
|
||||
esp_zb_zdo_binding_table_record_t *record = table_info->record;
|
||||
for (int i = 0; i < table_info->count; i++) {
|
||||
log_d(
|
||||
"Binding table record: src_endp %d, dst_endp %d, cluster_id 0x%04x, dst_addr_mode %d", record->src_endp, record->dst_endp, record->cluster_id,
|
||||
"Processing record %d: src_endp %d, dst_endp %d, cluster_id 0x%04x, dst_addr_mode %d", i, record->src_endp, record->dst_endp, record->cluster_id,
|
||||
record->dst_addr_mode
|
||||
);
|
||||
|
||||
zb_device_params_t *device = (zb_device_params_t *)calloc(1, sizeof(zb_device_params_t));
|
||||
device->endpoint = record->dst_endp;
|
||||
if (record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT || record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT) {
|
||||
device->short_addr = record->dst_address.addr_short;
|
||||
} else { //ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT
|
||||
memcpy(device->ieee_addr, record->dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
|
||||
}
|
||||
|
||||
// Add to list of bound devices of proper endpoint
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
if ((*it)->getEndpoint() == record->src_endp) {
|
||||
(*it)->addBoundDevice(device);
|
||||
log_d(
|
||||
"Device bound to EP %d -> device endpoint: %d, short addr: 0x%04x, ieee addr: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", record->src_endp,
|
||||
device->endpoint, device->short_addr, device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3],
|
||||
device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
all_records.push_back(*record);
|
||||
record = record->next;
|
||||
}
|
||||
|
||||
// Continue reading the binding table
|
||||
// If this is not the last chunk, request the next one
|
||||
if (table_info->index + table_info->count < table_info->total) {
|
||||
/* There are unreported binding table entries, request for them. */
|
||||
log_d("Requesting next chunk of binding table (current index: %d, count: %d, total: %d)", table_info->index, table_info->count, table_info->total);
|
||||
req->start_index = table_info->index + table_info->count;
|
||||
esp_zb_zdo_binding_table_req(req, bindingTableCb, req);
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is the last chunk, process all records
|
||||
log_d("Processing final chunk of binding table, total records: %d", all_records.size());
|
||||
for (const auto &record : all_records) {
|
||||
|
||||
if (done) {
|
||||
// Print bound devices
|
||||
log_d("Filling bounded devices finished");
|
||||
DeviceIdentifier dev_id;
|
||||
dev_id.endpoint = record.src_endp;
|
||||
dev_id.is_ieee = (record.dst_addr_mode == ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT);
|
||||
|
||||
if (dev_id.is_ieee) {
|
||||
memcpy(dev_id.ieee_addr, record.dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
|
||||
dev_id.short_addr = 0xFFFF; // Invalid short address
|
||||
} else {
|
||||
dev_id.short_addr = record.dst_address.addr_short;
|
||||
memset(dev_id.ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
|
||||
}
|
||||
|
||||
// Track this device as found
|
||||
found_devices.insert(dev_id);
|
||||
}
|
||||
|
||||
// Now process each endpoint and update its bound devices
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
log_d("Processing endpoint %d", (*it)->getEndpoint());
|
||||
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
|
||||
std::list<zb_device_params_t *> devices_to_remove;
|
||||
|
||||
// First, identify devices that need to be removed
|
||||
for (std::list<zb_device_params_t *>::iterator dev_it = bound_devices.begin(); dev_it != bound_devices.end(); ++dev_it) {
|
||||
DeviceIdentifier dev_id;
|
||||
dev_id.endpoint = (*it)->getEndpoint();
|
||||
|
||||
// Create both short and IEEE address identifiers for the device
|
||||
bool found = false;
|
||||
|
||||
// Check if device exists with short address
|
||||
if ((*dev_it)->short_addr != 0xFFFF) {
|
||||
dev_id.is_ieee = false;
|
||||
dev_id.short_addr = (*dev_it)->short_addr;
|
||||
memset(dev_id.ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
|
||||
if (found_devices.find(dev_id) != found_devices.end()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if device exists with IEEE address
|
||||
if (!found) {
|
||||
dev_id.is_ieee = true;
|
||||
memcpy(dev_id.ieee_addr, (*dev_it)->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
dev_id.short_addr = 0xFFFF;
|
||||
if (found_devices.find(dev_id) != found_devices.end()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
devices_to_remove.push_back(*dev_it);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove devices that are no longer in the binding table
|
||||
for (std::list<zb_device_params_t *>::iterator dev_it = devices_to_remove.begin(); dev_it != devices_to_remove.end(); ++dev_it) {
|
||||
(*it)->removeBoundDevice(*dev_it);
|
||||
free(*dev_it);
|
||||
}
|
||||
|
||||
// Now add new devices from the binding table
|
||||
for (const auto &record : all_records) {
|
||||
if (record.src_endp == (*it)->getEndpoint()) {
|
||||
log_d("Processing binding record for EP %d", record.src_endp);
|
||||
zb_device_params_t *device = (zb_device_params_t *)calloc(1, sizeof(zb_device_params_t));
|
||||
if (!device) {
|
||||
log_e("Failed to allocate memory for device params");
|
||||
continue;
|
||||
}
|
||||
device->endpoint = record.dst_endp;
|
||||
|
||||
bool is_ieee = (record.dst_addr_mode == ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT);
|
||||
if (is_ieee) {
|
||||
memcpy(device->ieee_addr, record.dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
|
||||
device->short_addr = 0xFFFF;
|
||||
} else {
|
||||
device->short_addr = record.dst_address.addr_short;
|
||||
memset(device->ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
|
||||
}
|
||||
|
||||
// Check if device already exists
|
||||
bool device_exists = false;
|
||||
for (std::list<zb_device_params_t *>::iterator dev_it = bound_devices.begin(); dev_it != bound_devices.end(); ++dev_it) {
|
||||
if (is_ieee) {
|
||||
if (memcmp((*dev_it)->ieee_addr, device->ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0) {
|
||||
device_exists = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((*dev_it)->short_addr == device->short_addr) {
|
||||
device_exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!device_exists) {
|
||||
(*it)->addBoundDevice(device);
|
||||
log_d(
|
||||
"Device bound to EP %d -> device endpoint: %d, %s: %s", record.src_endp, device->endpoint, is_ieee ? "ieee addr" : "short addr",
|
||||
is_ieee ? formatIEEEAddress(device->ieee_addr) : formatShortAddress(device->short_addr)
|
||||
);
|
||||
} else {
|
||||
log_d("Device already exists, freeing allocated memory");
|
||||
free(device); // Free the device if it already exists
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print bound devices
|
||||
log_d("Filling bounded devices finished");
|
||||
free(req);
|
||||
}
|
||||
} else {
|
||||
log_e("Binding table request failed with status: %d", zdo_status);
|
||||
free(req);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "esp_zigbee_core.h"
|
||||
#include "zdo/esp_zigbee_zdo_common.h"
|
||||
#include "aps/esp_zigbee_aps.h"
|
||||
#include <esp32-hal-log.h>
|
||||
#include <list>
|
||||
#include "ZigbeeEP.h"
|
||||
|
|
@ -100,6 +101,7 @@ private:
|
|||
uint8_t _open_network;
|
||||
zigbee_scan_result_t *_scan_result;
|
||||
SemaphoreHandle_t lock;
|
||||
bool _debug;
|
||||
|
||||
bool zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs);
|
||||
static void scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t count, esp_zb_network_descriptor_t *nwk_descriptor);
|
||||
|
|
@ -156,6 +158,7 @@ public:
|
|||
}
|
||||
void setRebootOpenNetwork(uint8_t time);
|
||||
void openNetwork(uint8_t time);
|
||||
void closeNetwork();
|
||||
|
||||
//scan_duration Time spent scanning each channel, in units of ((1 << scan_duration) + 1) * a beacon time. (15.36 microseconds)
|
||||
void scanNetworks(uint32_t channel_mask = ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK, uint8_t scan_duration = 5);
|
||||
|
|
@ -166,8 +169,29 @@ public:
|
|||
|
||||
void factoryReset(bool restart = true);
|
||||
|
||||
void setDebugMode(bool debug) {
|
||||
_debug = debug;
|
||||
}
|
||||
bool getDebugMode() {
|
||||
return _debug;
|
||||
}
|
||||
|
||||
// Friend function declaration to allow access to private members
|
||||
friend void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct);
|
||||
friend bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind);
|
||||
|
||||
// Helper functions for formatting addresses
|
||||
static inline const char *formatIEEEAddress(const esp_zb_ieee_addr_t addr) {
|
||||
static char buf[24];
|
||||
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", addr[7], addr[6], addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline const char *formatShortAddress(uint16_t addr) {
|
||||
static char buf[7];
|
||||
snprintf(buf, sizeof(buf), "0x%04X", addr);
|
||||
return buf;
|
||||
}
|
||||
};
|
||||
|
||||
extern ZigbeeCore Zigbee;
|
||||
|
|
|
|||
|
|
@ -7,11 +7,6 @@
|
|||
#include "esp_zigbee_cluster.h"
|
||||
#include "zcl/esp_zigbee_zcl_power_config.h"
|
||||
|
||||
bool ZigbeeEP::_is_bound = false;
|
||||
bool ZigbeeEP::_allow_multiple_binding = false;
|
||||
|
||||
//TODO: is_bound and allow_multiple_binding to make not static
|
||||
|
||||
/* Zigbee End Device Class */
|
||||
ZigbeeEP::ZigbeeEP(uint8_t endpoint) {
|
||||
_endpoint = endpoint;
|
||||
|
|
@ -22,6 +17,9 @@ ZigbeeEP::ZigbeeEP(uint8_t endpoint) {
|
|||
_read_model = NULL;
|
||||
_read_manufacturer = NULL;
|
||||
_time_status = 0;
|
||||
_is_bound = false;
|
||||
_use_manual_binding = false;
|
||||
_allow_multiple_binding = false;
|
||||
if (!lock) {
|
||||
lock = xSemaphoreCreateBinary();
|
||||
if (lock == NULL) {
|
||||
|
|
@ -562,6 +560,54 @@ void ZigbeeEP::requestOTAUpdate() {
|
|||
esp_zb_lock_release();
|
||||
}
|
||||
|
||||
void ZigbeeEP::removeBoundDevice(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) {
|
||||
log_d(
|
||||
"Attempting to remove device with endpoint %d and IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], ieee_addr[5],
|
||||
ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]
|
||||
);
|
||||
|
||||
for (std::list<zb_device_params_t *>::iterator it = _bound_devices.begin(); it != _bound_devices.end(); ++it) {
|
||||
if ((*it)->endpoint == endpoint && memcmp((*it)->ieee_addr, ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0) {
|
||||
log_d("Found matching device, removing it");
|
||||
_bound_devices.erase(it);
|
||||
if (_bound_devices.empty()) {
|
||||
_is_bound = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
log_w("No matching device found for removal");
|
||||
}
|
||||
|
||||
void ZigbeeEP::removeBoundDevice(zb_device_params_t *device) {
|
||||
if (!device) {
|
||||
log_e("Invalid device parameters provided");
|
||||
return;
|
||||
}
|
||||
|
||||
log_d(
|
||||
"Attempting to remove device with endpoint %d, short address 0x%04x, IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", device->endpoint,
|
||||
device->short_addr, device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2],
|
||||
device->ieee_addr[1], device->ieee_addr[0]
|
||||
);
|
||||
|
||||
for (std::list<zb_device_params_t *>::iterator it = _bound_devices.begin(); it != _bound_devices.end(); ++it) {
|
||||
bool endpoint_matches = ((*it)->endpoint == device->endpoint);
|
||||
bool short_addr_matches = (device->short_addr != 0xFFFF && (*it)->short_addr == device->short_addr);
|
||||
bool ieee_addr_matches = (memcmp((*it)->ieee_addr, device->ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0);
|
||||
|
||||
if (endpoint_matches && (short_addr_matches || ieee_addr_matches)) {
|
||||
log_d("Found matching device by %s, removing it", short_addr_matches ? "short address" : "IEEE address");
|
||||
_bound_devices.erase(it);
|
||||
if (_bound_devices.empty()) {
|
||||
_is_bound = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
log_w("No matching device found for removal");
|
||||
}
|
||||
|
||||
const char *ZigbeeEP::esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status) {
|
||||
switch (status) {
|
||||
case ESP_ZB_ZCL_STATUS_SUCCESS: return "Success";
|
||||
|
|
|
|||
|
|
@ -66,12 +66,15 @@ public:
|
|||
return _bound_devices;
|
||||
}
|
||||
|
||||
static bool bound() {
|
||||
bool bound() {
|
||||
return _is_bound;
|
||||
}
|
||||
static void allowMultipleBinding(bool bind) {
|
||||
void allowMultipleBinding(bool bind) {
|
||||
_allow_multiple_binding = bind;
|
||||
}
|
||||
void setManualBinding(bool bind) {
|
||||
_use_manual_binding = bind;
|
||||
}
|
||||
|
||||
// Set Manufacturer name and model
|
||||
bool setManufacturerAndModel(const char *name, const char *model);
|
||||
|
|
@ -98,6 +101,9 @@ public:
|
|||
bool epAllowMultipleBinding() {
|
||||
return _allow_multiple_binding;
|
||||
}
|
||||
bool epUseManualBinding() {
|
||||
return _use_manual_binding;
|
||||
}
|
||||
|
||||
// OTA methods
|
||||
/**
|
||||
|
|
@ -138,6 +144,14 @@ public:
|
|||
_is_bound = true;
|
||||
}
|
||||
|
||||
virtual void removeBoundDevice(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr);
|
||||
virtual void removeBoundDevice(zb_device_params_t *device);
|
||||
|
||||
virtual void clearBoundDevices() {
|
||||
_bound_devices.clear();
|
||||
_is_bound = false;
|
||||
}
|
||||
|
||||
void onIdentify(void (*callback)(uint16_t)) {
|
||||
_on_identify = callback;
|
||||
}
|
||||
|
|
@ -157,8 +171,9 @@ protected:
|
|||
esp_zb_ha_standard_devices_t _device_id;
|
||||
esp_zb_endpoint_config_t _ep_config;
|
||||
esp_zb_cluster_list_t *_cluster_list;
|
||||
static bool _is_bound;
|
||||
static bool _allow_multiple_binding;
|
||||
bool _is_bound;
|
||||
bool _allow_multiple_binding;
|
||||
bool _use_manual_binding;
|
||||
std::list<zb_device_params_t *> _bound_devices;
|
||||
SemaphoreHandle_t lock;
|
||||
zb_power_source_t _power_source;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,33 @@ ZigbeeAnalog::ZigbeeAnalog(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
|||
}
|
||||
|
||||
bool ZigbeeAnalog::addAnalogInput() {
|
||||
esp_err_t ret = esp_zb_cluster_list_add_analog_input_cluster(_cluster_list, esp_zb_analog_input_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
esp_zb_attribute_list_t *esp_zb_analog_input_cluster = esp_zb_analog_input_cluster_create(NULL);
|
||||
|
||||
// Create default description for Analog Input
|
||||
char default_description[] = "\x0C"
|
||||
"Analog Input";
|
||||
uint32_t application_type = 0x00000000 | (ESP_ZB_ZCL_AI_GROUP_ID << 24);
|
||||
float resolution = 0.1; // Default resolution of 0.1
|
||||
|
||||
esp_err_t ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID, (void *)default_description);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_APPLICATION_TYPE_ID, (void *)&application_type);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_RESOLUTION_ID, (void *)&resolution);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add resolution attribute: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = esp_zb_cluster_list_add_analog_input_cluster(_cluster_list, esp_zb_analog_input_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add Analog Input cluster: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
|
|
@ -22,8 +48,55 @@ bool ZigbeeAnalog::addAnalogInput() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Check esp_zigbee_zcl_analog_input.h for application type values
|
||||
bool ZigbeeAnalog::setAnalogInputApplication(uint32_t application_type) {
|
||||
if (!(_analog_clusters & ANALOG_INPUT)) {
|
||||
log_e("Analog Input cluster not added");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the Analog Input group ID (0x00) to the application type
|
||||
uint32_t application_type_value = (ESP_ZB_ZCL_AI_GROUP_ID << 24) | application_type;
|
||||
|
||||
esp_zb_attribute_list_t *analog_input_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_APPLICATION_TYPE_ID, (void *)&application_type_value);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set AI application type: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeAnalog::addAnalogOutput() {
|
||||
esp_err_t ret = esp_zb_cluster_list_add_analog_output_cluster(_cluster_list, esp_zb_analog_output_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
esp_zb_attribute_list_t *esp_zb_analog_output_cluster = esp_zb_analog_output_cluster_create(NULL);
|
||||
|
||||
// Create default description for Analog Output
|
||||
char default_description[] = "\x0D"
|
||||
"Analog Output";
|
||||
uint32_t application_type = 0x00000000 | (ESP_ZB_ZCL_AO_GROUP_ID << 24);
|
||||
float resolution = 1; // Default resolution of 1
|
||||
|
||||
esp_err_t ret =
|
||||
esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, (void *)default_description);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID, (void *)&resolution);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add resolution attribute: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = esp_zb_cluster_list_add_analog_output_cluster(_cluster_list, esp_zb_analog_output_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add Analog Output cluster: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
|
|
@ -32,6 +105,26 @@ bool ZigbeeAnalog::addAnalogOutput() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Check esp_zigbee_zcl_analog_output.h for application type values
|
||||
bool ZigbeeAnalog::setAnalogOutputApplication(uint32_t application_type) {
|
||||
if (!(_analog_clusters & ANALOG_OUTPUT)) {
|
||||
log_e("Analog Output cluster not added");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the Analog Output group ID (0x00) to the application type
|
||||
uint32_t application_type_value = (ESP_ZB_ZCL_AO_GROUP_ID << 24) | application_type;
|
||||
|
||||
esp_zb_attribute_list_t *analog_output_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type_value);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set AO application type: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//set attribute method -> method overridden in child class
|
||||
void ZigbeeAnalog::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {
|
||||
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT) {
|
||||
|
|
@ -120,4 +213,126 @@ bool ZigbeeAnalog::setAnalogInputReporting(uint16_t min_interval, uint16_t max_i
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeAnalog::setAnalogInputDescription(const char *description) {
|
||||
if (!(_analog_clusters & ANALOG_INPUT)) {
|
||||
log_e("Analog Input cluster not added");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
|
||||
char zb_description[ZB_MAX_NAME_LENGTH + 2];
|
||||
|
||||
// Convert description to ZCL string
|
||||
size_t description_length = strlen(description);
|
||||
if (description_length > ZB_MAX_NAME_LENGTH) {
|
||||
log_e("Description is too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get and check the analog input cluster
|
||||
esp_zb_attribute_list_t *analog_input_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
if (analog_input_cluster == nullptr) {
|
||||
log_e("Failed to get analog input cluster");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the length as the first element
|
||||
zb_description[0] = static_cast<char>(description_length); // Cast size_t to char
|
||||
// Use memcpy to copy the characters to the result array
|
||||
memcpy(zb_description + 1, description, description_length);
|
||||
// Null-terminate the array
|
||||
zb_description[description_length + 1] = '\0';
|
||||
|
||||
// Update the description attribute
|
||||
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID, (void *)zb_description);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeAnalog::setAnalogOutputDescription(const char *description) {
|
||||
if (!(_analog_clusters & ANALOG_OUTPUT)) {
|
||||
log_e("Analog Output cluster not added");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
|
||||
char zb_description[ZB_MAX_NAME_LENGTH + 2];
|
||||
|
||||
// Convert description to ZCL string
|
||||
size_t description_length = strlen(description);
|
||||
if (description_length > ZB_MAX_NAME_LENGTH) {
|
||||
log_e("Description is too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get and check the analog output cluster
|
||||
esp_zb_attribute_list_t *analog_output_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
if (analog_output_cluster == nullptr) {
|
||||
log_e("Failed to get analog output cluster");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the length as the first element
|
||||
zb_description[0] = static_cast<char>(description_length); // Cast size_t to char
|
||||
// Use memcpy to copy the characters to the result array
|
||||
memcpy(zb_description + 1, description, description_length);
|
||||
// Null-terminate the array
|
||||
zb_description[description_length + 1] = '\0';
|
||||
|
||||
// Update the description attribute
|
||||
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, (void *)zb_description);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeAnalog::setAnalogInputResolution(float resolution) {
|
||||
if (!(_analog_clusters & ANALOG_INPUT)) {
|
||||
log_e("Analog Input cluster not added");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_zb_attribute_list_t *analog_input_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
if (analog_input_cluster == nullptr) {
|
||||
log_e("Failed to get analog input cluster");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_RESOLUTION_ID, (void *)&resolution);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set resolution: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeAnalog::setAnalogOutputResolution(float resolution) {
|
||||
if (!(_analog_clusters & ANALOG_OUTPUT)) {
|
||||
log_e("Analog Output cluster not added");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_zb_attribute_list_t *analog_output_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
if (analog_output_cluster == nullptr) {
|
||||
log_e("Failed to get analog output cluster");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID, (void *)&resolution);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set resolution: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
|
|
|
|||
|
|
@ -31,6 +31,16 @@ public:
|
|||
bool addAnalogInput();
|
||||
bool addAnalogOutput();
|
||||
|
||||
// Set the application type and description for the analog input
|
||||
bool setAnalogInputApplication(uint32_t application_type); // Check esp_zigbee_zcl_analog_input.h for application type values
|
||||
bool setAnalogInputDescription(const char *description);
|
||||
bool setAnalogInputResolution(float resolution);
|
||||
|
||||
// Set the application type and description for the analog output
|
||||
bool setAnalogOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_analog_output.h for application type values
|
||||
bool setAnalogOutputDescription(const char *description);
|
||||
bool setAnalogOutputResolution(float resolution);
|
||||
|
||||
// Use to set a cb function to be called on analog output change
|
||||
void onAnalogOutputChange(void (*callback)(float analog)) {
|
||||
_on_analog_output_change = callback;
|
||||
|
|
|
|||
144
libraries/Zigbee/src/ep/ZigbeeBinary.cpp
Normal file
144
libraries/Zigbee/src/ep/ZigbeeBinary.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#include "ZigbeeBinary.h"
|
||||
#if CONFIG_ZB_ENABLED
|
||||
|
||||
ZigbeeBinary::ZigbeeBinary(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID;
|
||||
|
||||
//Create basic binary sensor clusters without configuration
|
||||
_cluster_list = esp_zb_zcl_cluster_list_create();
|
||||
esp_zb_cluster_list_add_basic_cluster(_cluster_list, esp_zb_basic_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
esp_zb_cluster_list_add_identify_cluster(_cluster_list, esp_zb_identify_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
_ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID, .app_device_version = 0};
|
||||
}
|
||||
|
||||
bool ZigbeeBinary::addBinaryInput() {
|
||||
esp_zb_attribute_list_t *esp_zb_binary_input_cluster = esp_zb_binary_input_cluster_create(NULL);
|
||||
|
||||
// Create default description for Binary Input
|
||||
char default_description[] = "\x0C"
|
||||
"Binary Input";
|
||||
uint32_t application_type = 0x00000000 | (0x03 << 24); // Group ID 0x03
|
||||
|
||||
esp_err_t ret = esp_zb_binary_input_cluster_add_attr(esp_zb_binary_input_cluster, ESP_ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, (void *)default_description);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = esp_zb_binary_input_cluster_add_attr(esp_zb_binary_input_cluster, ESP_ZB_ZCL_ATTR_BINARY_INPUT_APPLICATION_TYPE_ID, (void *)&application_type);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = esp_zb_cluster_list_add_binary_input_cluster(_cluster_list, esp_zb_binary_input_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add Binary Input cluster: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
_binary_clusters |= BINARY_INPUT;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check Zigbee Cluster Specification 3.14.11.19.4 Binary Inputs (BI) Types for application type values
|
||||
bool ZigbeeBinary::setBinaryInputApplication(uint32_t application_type) {
|
||||
if (!(_binary_clusters & BINARY_INPUT)) {
|
||||
log_e("Binary Input cluster not added");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the Binary Input group ID (0x03) to the application type
|
||||
uint32_t application_type_value = (0x03 << 24) | application_type;
|
||||
|
||||
esp_zb_attribute_list_t *binary_input_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
esp_err_t ret = esp_zb_cluster_update_attr(binary_input_cluster, ESP_ZB_ZCL_ATTR_BINARY_INPUT_APPLICATION_TYPE_ID, (void *)&application_type_value);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set Binary Input application type: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeBinary::setBinaryInput(bool input) {
|
||||
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
|
||||
if (!(_binary_clusters & BINARY_INPUT)) {
|
||||
log_e("Binary Input cluster not added");
|
||||
return false;
|
||||
}
|
||||
log_d("Setting binary input to %d", input);
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
ret = esp_zb_zcl_set_attribute_val(
|
||||
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &input, false
|
||||
);
|
||||
esp_zb_lock_release();
|
||||
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
|
||||
log_e("Failed to set binary input: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeBinary::reportBinaryInput() {
|
||||
/* Send report attributes command */
|
||||
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
|
||||
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
|
||||
report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID;
|
||||
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
|
||||
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT;
|
||||
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
|
||||
report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
|
||||
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
|
||||
esp_zb_lock_release();
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to send Binary Input report: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
log_v("Binary Input report sent");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeBinary::setBinaryInputDescription(const char *description) {
|
||||
if (!(_binary_clusters & BINARY_INPUT)) {
|
||||
log_e("Binary Input cluster not added");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
|
||||
char zb_description[ZB_MAX_NAME_LENGTH + 2];
|
||||
|
||||
// Convert description to ZCL string
|
||||
size_t description_length = strlen(description);
|
||||
if (description_length > ZB_MAX_NAME_LENGTH) {
|
||||
log_e("Description is too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get and check the binary input cluster
|
||||
esp_zb_attribute_list_t *binary_input_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
if (binary_input_cluster == nullptr) {
|
||||
log_e("Failed to get binary input cluster");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the length as the first element
|
||||
zb_description[0] = static_cast<char>(description_length); // Cast size_t to char
|
||||
// Use memcpy to copy the characters to the result array
|
||||
memcpy(zb_description + 1, description, description_length);
|
||||
// Null-terminate the array
|
||||
zb_description[description_length + 1] = '\0';
|
||||
|
||||
// Update the description attribute
|
||||
esp_err_t ret = esp_zb_cluster_update_attr(binary_input_cluster, ESP_ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, (void *)zb_description);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
85
libraries/Zigbee/src/ep/ZigbeeBinary.h
Normal file
85
libraries/Zigbee/src/ep/ZigbeeBinary.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* Class of Zigbee Binary sensor endpoint inherited from common EP class */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_ZB_ENABLED
|
||||
|
||||
#include "ZigbeeEP.h"
|
||||
#include "ha/esp_zigbee_ha_standard.h"
|
||||
|
||||
//enum for bits set to check what analog cluster were added
|
||||
enum zigbee_binary_clusters {
|
||||
BINARY_INPUT = 1,
|
||||
BINARY_OUTPUT = 2
|
||||
};
|
||||
|
||||
// HVAC application types for Binary Input (more can be found in Zigbee Cluster Specification 3.14.11.19.4 Binary Inputs (BI) Types)
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_BOILER_STATUS 0x00000003 // Type 0x00, Index 0x0003
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_CHILLER_STATUS 0x00000013 // Type 0x00, Index 0x0013
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_OCCUPANCY 0x00000031 // Type 0x00, Index 0x0031
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_FAN_STATUS 0x00000035 // Type 0x00, Index 0x0035
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_FILTER_STATUS 0x00000036 // Type 0x00, Index 0x0036
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_HEATING_ALARM 0x0000003E // Type 0x00, Index 0x003E
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_COOLING_ALARM 0x0000001D // Type 0x00, Index 0x001D
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_UNIT_ENABLE 0x00000090 // Type 0x00, Index 0x0090
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_OTHER 0x0000FFFF // Type 0x00, Index 0xFFFF
|
||||
|
||||
// Security application types for Binary Input
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_GLASS_BREAKAGE_DETECTION_0 0x01000000 // Type 0x01, Index 0x0000
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_INTRUSION_DETECTION 0x01000001 // Type 0x01, Index 0x0001
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_MOTION_DETECTION 0x01000002 // Type 0x01, Index 0x0002
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_GLASS_BREAKAGE_DETECTION_1 0x01000003 // Type 0x01, Index 0x0003
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_ZONE_ARMED 0x01000004 // Type 0x01, Index 0x0004
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_GLASS_BREAKAGE_DETECTION_2 0x01000005 // Type 0x01, Index 0x0005
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_SMOKE_DETECTION 0x01000006 // Type 0x01, Index 0x0006
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_CARBON_DIOXIDE_DETECTION 0x01000007 // Type 0x01, Index 0x0007
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_HEAT_DETECTION 0x01000008 // Type 0x01, Index 0x0008
|
||||
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF // Type 0x01, Index 0xFFFF
|
||||
|
||||
typedef struct zigbee_binary_cfg_s {
|
||||
esp_zb_basic_cluster_cfg_t basic_cfg;
|
||||
esp_zb_identify_cluster_cfg_t identify_cfg;
|
||||
// esp_zb_binary_output_cluster_cfg_t binary_output_cfg;
|
||||
esp_zb_binary_input_cluster_cfg_t binary_input_cfg;
|
||||
} zigbee_binary_cfg_t;
|
||||
|
||||
class ZigbeeBinary : public ZigbeeEP {
|
||||
public:
|
||||
ZigbeeBinary(uint8_t endpoint);
|
||||
~ZigbeeBinary() {}
|
||||
|
||||
// Add binary cluster
|
||||
bool addBinaryInput();
|
||||
// bool addBinaryOutput();
|
||||
|
||||
// Set the application type and description for the binary input
|
||||
bool setBinaryInputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_input.h for application type values
|
||||
bool setBinaryInputDescription(const char *description);
|
||||
|
||||
// Set the application type and description for the binary output
|
||||
// bool setBinaryOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_output.h for application type values
|
||||
// bool setBinaryOutputDescription(const char *description);
|
||||
|
||||
// Use to set a cb function to be called on binary output change
|
||||
// void onBinaryOutputChange(void (*callback)(bool binary_output)) {
|
||||
// _on_binary_output_change = callback;
|
||||
// }
|
||||
|
||||
// Set the binary input value
|
||||
bool setBinaryInput(bool input);
|
||||
|
||||
// Report Binary Input value
|
||||
bool reportBinaryInput();
|
||||
|
||||
private:
|
||||
// void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override;
|
||||
|
||||
// void (*_on_binary_output_change)(bool);
|
||||
// void binaryOutputChanged(bool binary_output);
|
||||
|
||||
uint8_t _binary_clusters;
|
||||
};
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
|
|
@ -6,7 +6,8 @@ ZigbeeColorDimmerSwitch *ZigbeeColorDimmerSwitch::_instance = nullptr;
|
|||
|
||||
ZigbeeColorDimmerSwitch::ZigbeeColorDimmerSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_COLOR_DIMMER_SWITCH_DEVICE_ID;
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_device = nullptr; // Initialize light pointer to null
|
||||
|
||||
esp_zb_color_dimmable_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_COLOR_DIMMABLE_SWITCH_CONFIG();
|
||||
_cluster_list = esp_zb_color_dimmable_switch_clusters_create(&switch_cfg);
|
||||
|
|
@ -17,20 +18,39 @@ ZigbeeColorDimmerSwitch::ZigbeeColorDimmerSwitch(uint8_t endpoint) : ZigbeeEP(en
|
|||
}
|
||||
|
||||
void ZigbeeColorDimmerSwitch::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeColorDimmerSwitch *instance = static_cast<ZigbeeColorDimmerSwitch *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_i("Bound successfully!");
|
||||
if (user_ctx) {
|
||||
zb_device_params_t *light = (zb_device_params_t *)user_ctx;
|
||||
if (instance->_device) {
|
||||
zb_device_params_t *light = (zb_device_params_t *)instance->_device;
|
||||
log_i("The light originating from address(0x%x) on endpoint(%d)", light->short_addr, light->endpoint);
|
||||
_instance->_bound_devices.push_back(light);
|
||||
log_d("Light bound to a switch on EP %d", instance->_endpoint);
|
||||
instance->_bound_devices.push_back(light);
|
||||
}
|
||||
_is_bound = true;
|
||||
instance->_is_bound = true;
|
||||
} else {
|
||||
log_e("Binding failed!");
|
||||
instance->_device = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeColorDimmerSwitch::bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeColorDimmerSwitch *instance = static_cast<ZigbeeColorDimmerSwitch *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("bindCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->bindCb(zdo_status, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeColorDimmerSwitch::findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeColorDimmerSwitch *instance = static_cast<ZigbeeColorDimmerSwitch *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("findCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->findCb(zdo_status, addr, endpoint, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeColorDimmerSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeColorDimmerSwitch *instance = static_cast<ZigbeeColorDimmerSwitch *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_d("Found light endpoint");
|
||||
esp_zb_zdo_bind_req_param_t bind_req;
|
||||
|
|
@ -39,22 +59,23 @@ void ZigbeeColorDimmerSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t ad
|
|||
light->short_addr = addr;
|
||||
esp_zb_ieee_address_by_short(light->short_addr, light->ieee_addr);
|
||||
esp_zb_get_long_address(bind_req.src_address);
|
||||
bind_req.src_endp = *((uint8_t *)user_ctx); //_endpoint;
|
||||
bind_req.src_endp = instance->_endpoint;
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF;
|
||||
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
|
||||
memcpy(bind_req.dst_address_u.addr_long, light->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
bind_req.dst_endp = endpoint;
|
||||
bind_req.req_dst_addr = esp_zb_get_short_address();
|
||||
instance->_device = light;
|
||||
log_v("Try to bind on/off control of dimmable light");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, NULL);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeColorDimmerSwitch::bindCbWrapper, NULL);
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL;
|
||||
log_v("Try to bind level control of dimmable light");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, NULL);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeColorDimmerSwitch::bindCbWrapper, NULL);
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL;
|
||||
log_v("Try to bind color control of dimmable light");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, (void *)light);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeColorDimmerSwitch::bindCbWrapper, this);
|
||||
} else {
|
||||
log_v("No color dimmable light endpoint found");
|
||||
log_d("No color dimmable light endpoint found");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +91,7 @@ void ZigbeeColorDimmerSwitch::findEndpoint(esp_zb_zdo_match_desc_req_param_t *cm
|
|||
.num_out_clusters = 3,
|
||||
.cluster_list = cluster_list,
|
||||
};
|
||||
esp_zb_zdo_match_cluster(&color_dimmable_light_req, findCb, &_endpoint);
|
||||
esp_zb_zdo_match_cluster(&color_dimmable_light_req, ZigbeeColorDimmerSwitch::findCbWrapper, this);
|
||||
}
|
||||
|
||||
// Methods to control the light
|
||||
|
|
|
|||
|
|
@ -47,10 +47,13 @@ public:
|
|||
private:
|
||||
// save instance of the class in order to use it in static functions
|
||||
static ZigbeeColorDimmerSwitch *_instance;
|
||||
zb_device_params_t *_device;
|
||||
|
||||
void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req);
|
||||
static void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
|
||||
void calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y);
|
||||
};
|
||||
|
|
|
|||
979
libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp
Normal file
979
libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp
Normal file
|
|
@ -0,0 +1,979 @@
|
|||
#include "ZigbeeElectricalMeasurement.h"
|
||||
#if CONFIG_ZB_ENABLED
|
||||
|
||||
// Workaround for wrong name in ZCL header
|
||||
#define ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_ID ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DCPOWER_ID
|
||||
|
||||
esp_zb_cluster_list_t *zigbee_electrical_measurement_clusters_create(zigbee_electrical_measurement_cfg_t *electrical_measurement) {
|
||||
esp_zb_basic_cluster_cfg_t *basic_cfg = electrical_measurement ? &(electrical_measurement->basic_cfg) : NULL;
|
||||
esp_zb_identify_cluster_cfg_t *identify_cfg = electrical_measurement ? &(electrical_measurement->identify_cfg) : NULL;
|
||||
esp_zb_electrical_meas_cluster_cfg_t *electrical_measurement_cfg = electrical_measurement ? &(electrical_measurement->electrical_measurement_cfg) : NULL;
|
||||
esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
|
||||
esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
esp_zb_cluster_list_add_electrical_meas_cluster(
|
||||
cluster_list, esp_zb_electrical_meas_cluster_create(electrical_measurement_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE
|
||||
);
|
||||
return cluster_list;
|
||||
}
|
||||
|
||||
ZigbeeElectricalMeasurement::ZigbeeElectricalMeasurement(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID;
|
||||
|
||||
//Create custom pressure sensor configuration
|
||||
zigbee_electrical_measurement_cfg_t electrical_measurement_cfg = ZIGBEE_DEFAULT_ELECTRICAL_MEASUREMENT_CONFIG();
|
||||
_cluster_list = zigbee_electrical_measurement_clusters_create(&electrical_measurement_cfg);
|
||||
|
||||
_ep_config = {
|
||||
.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_METER_INTERFACE_DEVICE_ID, .app_device_version = 0
|
||||
};
|
||||
}
|
||||
|
||||
/* DC MEASUREMENT */
|
||||
|
||||
bool ZigbeeElectricalMeasurement::addDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type) {
|
||||
esp_zb_attribute_list_t *electrical_measurement_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
// Set the DC measurement type bit in the measurement type attribute
|
||||
measure_type |= ESP_ZB_ZCL_ELECTRICAL_MEASUREMENT_DC_MEASUREMENT;
|
||||
esp_zb_cluster_update_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_MEASUREMENT_TYPE_ID, &measure_type);
|
||||
|
||||
int16_t default_min = -32767;
|
||||
int16_t default_max = 32767;
|
||||
int16_t default_measurement = 0;
|
||||
uint16_t default_multiplier = 1;
|
||||
uint16_t default_divisor = 1;
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
// Add the DC Voltage attributes
|
||||
if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE) {
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_ID, (void *)&default_measurement
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret =
|
||||
esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_MIN_ID, (void *)&default_min);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage min: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret =
|
||||
esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_MAX_ID, (void *)&default_max);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage max: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_MULTIPLIER_ID, (void *)&default_multiplier
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_DIVISOR_ID, (void *)&default_divisor
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT) {
|
||||
// Add the DC Current attributes
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_ID, (void *)&default_measurement
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC current: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret =
|
||||
esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_MIN_ID, (void *)&default_min);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC current min: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret =
|
||||
esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_MAX_ID, (void *)&default_max);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC current max: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_MULTIPLIER_ID, (void *)&default_multiplier
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC current multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_DIVISOR_ID, (void *)&default_divisor
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC current divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
} else { //(measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_POWER)
|
||||
// Add the DC Power attributes
|
||||
ret =
|
||||
esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_ID, (void *)&default_measurement);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC power: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_MIN_ID, (void *)&default_min);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC power min: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_MAX_ID, (void *)&default_max);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC power max: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_MULTIPLIER_ID, (void *)&default_multiplier
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC power multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_DIVISOR_ID, (void *)&default_divisor
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC power divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::setDCMinMaxValue(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type, int16_t min, int16_t max) {
|
||||
esp_zb_attribute_list_t *electrical_measurement_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_MIN_ID;
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_MAX_ID;
|
||||
if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT) {
|
||||
attr_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_MIN_ID;
|
||||
attr_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_MAX_ID;
|
||||
} else if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_POWER) {
|
||||
attr_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_MIN_ID;
|
||||
attr_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_MAX_ID;
|
||||
}
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_min, (void *)&min);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_max, (void *)&max);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::setDCMultiplierDivisor(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type, uint16_t multiplier, uint16_t divisor) {
|
||||
esp_zb_attribute_list_t *electrical_measurement_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_multiplier = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_MULTIPLIER_ID;
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_divisor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_DIVISOR_ID;
|
||||
|
||||
if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT) {
|
||||
attr_multiplier = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_MULTIPLIER_ID;
|
||||
attr_divisor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_DIVISOR_ID;
|
||||
} else if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_POWER) {
|
||||
attr_multiplier = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_MULTIPLIER_ID;
|
||||
attr_divisor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_DIVISOR_ID;
|
||||
}
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_multiplier, (void *)&multiplier);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_divisor, (void *)&divisor);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::setDCReporting(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type, uint16_t min_interval, uint16_t max_interval, int16_t delta) {
|
||||
uint16_t attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_ID;
|
||||
if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT) {
|
||||
attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_ID;
|
||||
} else if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_POWER) {
|
||||
attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_ID;
|
||||
}
|
||||
|
||||
esp_zb_zcl_reporting_info_t reporting_info;
|
||||
memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t));
|
||||
reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV;
|
||||
reporting_info.ep = _endpoint;
|
||||
reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT;
|
||||
reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE;
|
||||
reporting_info.attr_id = attr_id;
|
||||
reporting_info.u.send_info.min_interval = min_interval;
|
||||
reporting_info.u.send_info.max_interval = max_interval;
|
||||
reporting_info.u.send_info.def_min_interval = min_interval;
|
||||
reporting_info.u.send_info.def_max_interval = max_interval;
|
||||
reporting_info.u.send_info.delta.s16 = delta;
|
||||
reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID;
|
||||
reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
esp_err_t ret = esp_zb_zcl_update_reporting_info(&reporting_info);
|
||||
esp_zb_lock_release();
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set reporting: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::setDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type, int16_t measurement) {
|
||||
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
|
||||
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_ID;
|
||||
if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT) {
|
||||
attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_ID;
|
||||
} else if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_POWER) {
|
||||
attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_ID;
|
||||
}
|
||||
|
||||
log_v("Updating DC measurement value...");
|
||||
/* Update DC sensor measured value */
|
||||
log_d("Setting DC measurement to %d", measurement);
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
ret = esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id, &measurement, false);
|
||||
esp_zb_lock_release();
|
||||
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
|
||||
log_e("Failed to set DC measurement: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::reportDC(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type) {
|
||||
uint16_t attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_VOLTAGE_ID;
|
||||
if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT) {
|
||||
attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_CURRENT_ID;
|
||||
} else if (measurement_type == ZIGBEE_DC_MEASUREMENT_TYPE_POWER) {
|
||||
attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_DC_POWER_ID;
|
||||
}
|
||||
/* Send report attributes command */
|
||||
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
|
||||
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
|
||||
report_attr_cmd.attributeID = attr_id;
|
||||
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
|
||||
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT;
|
||||
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
|
||||
report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
|
||||
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
|
||||
esp_zb_lock_release();
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to send DC report: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
log_v("DC report sent");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* AC MEASUREMENT */
|
||||
|
||||
bool ZigbeeElectricalMeasurement::addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type) {
|
||||
esp_zb_attribute_list_t *electrical_measurement_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
// Non phase specific dont need any bit set
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: measure_type |= ESP_ZB_ZCL_ELECTRICAL_MEASUREMENT_PHASE_A_MEASUREMENT; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: measure_type |= ESP_ZB_ZCL_ELECTRICAL_MEASUREMENT_PHASE_B_MEASUREMENT; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: measure_type |= ESP_ZB_ZCL_ELECTRICAL_MEASUREMENT_PHASE_C_MEASUREMENT; break;
|
||||
default: log_e("Invalid phase type"); break;
|
||||
}
|
||||
// Set Active measurement bit for active power and power factor, otherwise no bit needed for voltage, current
|
||||
if (measurement_type == ZIGBEE_AC_MEASUREMENT_TYPE_POWER || measurement_type == ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR) {
|
||||
measure_type |= ESP_ZB_ZCL_ELECTRICAL_MEASUREMENT_ACTIVE_MEASUREMENT; // Active power is used
|
||||
}
|
||||
// Update the measurement type attribute
|
||||
esp_zb_cluster_update_attr(electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_MEASUREMENT_TYPE_ID, &measure_type);
|
||||
|
||||
// Default values for AC measurements
|
||||
[[maybe_unused]]
|
||||
int16_t default_ac_power_min = -32767;
|
||||
[[maybe_unused]]
|
||||
int16_t default_ac_power_max = 32767;
|
||||
[[maybe_unused]]
|
||||
int16_t default_ac_power_measurement = 0;
|
||||
[[maybe_unused]]
|
||||
uint16_t default_ac_min = 0x0000;
|
||||
[[maybe_unused]]
|
||||
uint16_t default_ac_max = 0xffff;
|
||||
[[maybe_unused]]
|
||||
uint16_t default_ac_measurement = 0x0000;
|
||||
[[maybe_unused]]
|
||||
uint16_t default_ac_multiplier = 1;
|
||||
[[maybe_unused]]
|
||||
uint16_t default_ac_divisor = 1;
|
||||
[[maybe_unused]]
|
||||
int8_t default_ac_power_factor = 100;
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
// AC Frequency
|
||||
if (measurement_type == ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY) { // No phase specific
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_ID, (void *)&default_ac_measurement
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_MIN_ID, (void *)&default_ac_min
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage min: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_MAX_ID, (void *)&default_ac_max
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage max: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_MULTIPLIER_ID, (void *)&default_ac_multiplier
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_DIVISOR_ID, (void *)&default_ac_divisor
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add DC voltage divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Add the AC Voltage attributes
|
||||
else if (measurement_type == ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE) {
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_voltage = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_ID;
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_voltage_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MIN_ID;
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_voltage_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MAX_ID;
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A:
|
||||
// already set
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B:
|
||||
attr_voltage = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_PHB_ID;
|
||||
attr_voltage_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MIN_PH_B_ID;
|
||||
attr_voltage_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MAX_PH_B_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C:
|
||||
attr_voltage = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_PHC_ID;
|
||||
attr_voltage_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MIN_PH_C_ID;
|
||||
attr_voltage_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MAX_PH_C_ID;
|
||||
break;
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_voltage, (void *)&default_ac_measurement);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC voltage: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_voltage_min, (void *)&default_ac_min);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC voltage min: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_voltage_max, (void *)&default_ac_max);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC voltage max: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
if (!ac_volt_mult_div_set) {
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACVOLTAGE_MULTIPLIER_ID, (void *)&default_ac_multiplier
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC voltage multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACVOLTAGE_DIVISOR_ID, (void *)&default_ac_divisor
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC voltage divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ac_volt_mult_div_set = true; // Set flag to true, so we dont add the attributes again
|
||||
}
|
||||
}
|
||||
// Add the AC Current attributes
|
||||
else if (measurement_type == ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT) {
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_current = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_ID;
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_current_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MIN_ID;
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_current_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MAX_ID;
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A:
|
||||
// already set
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B:
|
||||
attr_current = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_PHB_ID;
|
||||
attr_current_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MIN_PH_B_ID;
|
||||
attr_current_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MAX_PH_B_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C:
|
||||
attr_current = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_PHC_ID;
|
||||
attr_current_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MIN_PH_C_ID;
|
||||
attr_current_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MAX_PH_C_ID;
|
||||
break;
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_current, (void *)&default_ac_measurement);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC current: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_current_min, (void *)&default_ac_min);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC current min: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_current_max, (void *)&default_ac_max);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC current max: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
if (!ac_current_mult_div_set) {
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACCURRENT_MULTIPLIER_ID, (void *)&default_ac_multiplier
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC current multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACCURRENT_DIVISOR_ID, (void *)&default_ac_divisor
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC current divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ac_current_mult_div_set = true; // Set flag to true, so we dont add the attributes again
|
||||
}
|
||||
}
|
||||
// Add the AC Power attributes
|
||||
else if (measurement_type == ZIGBEE_AC_MEASUREMENT_TYPE_POWER) {
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_power = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_ID;
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_power_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MIN_ID;
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_power_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MAX_ID;
|
||||
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A:
|
||||
// already set
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B:
|
||||
attr_power = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_PHB_ID;
|
||||
attr_power_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MIN_PH_B_ID;
|
||||
attr_power_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MAX_PH_B_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C:
|
||||
attr_power = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_PHC_ID;
|
||||
attr_power_min = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MIN_PH_C_ID;
|
||||
attr_power_max = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MAX_PH_C_ID;
|
||||
break;
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_power, (void *)&default_ac_measurement);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC power: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_power_min, (void *)&default_ac_min);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC power min: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_power_max, (void *)&default_ac_max);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC power max: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
if (!ac_power_mult_div_set) {
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACPOWER_MULTIPLIER_ID, (void *)&default_ac_multiplier
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC power multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(
|
||||
electrical_measurement_cluster, ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACPOWER_DIVISOR_ID, (void *)&default_ac_divisor
|
||||
);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC power divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ac_power_mult_div_set = true; // Set flag to true, so we dont add the attributes again
|
||||
}
|
||||
} else { //(measurement_type == ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR)
|
||||
esp_zb_zcl_electrical_measurement_attr_t attr_power_factor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_ID;
|
||||
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A:
|
||||
// already set
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_power_factor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_PH_B_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_power_factor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_PH_C_ID; break;
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
ret = esp_zb_electrical_meas_cluster_add_attr(electrical_measurement_cluster, attr_power_factor, (void *)&default_ac_power_factor);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to add AC power factor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::setACMinMaxValue(
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type, int32_t min_value, int32_t max_value
|
||||
) {
|
||||
uint16_t attr_min_id = 0;
|
||||
uint16_t attr_max_id = 0;
|
||||
|
||||
// Check min/max values are valid for the measurement type
|
||||
switch (measurement_type) {
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE:
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT:
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY:
|
||||
if (min_value < 0 || min_value > UINT16_MAX || max_value < 0 || max_value > UINT16_MAX) {
|
||||
log_e("AC measurement min/max values must be between 0 and %u (got min=%d, max=%d)", UINT16_MAX, min_value, max_value);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER:
|
||||
if (min_value < INT16_MIN || min_value > INT16_MAX || max_value < INT16_MIN || max_value > INT16_MAX) {
|
||||
log_e("AC power min/max values must be between %d and %d (got min=%d, max=%d)", INT16_MIN, INT16_MAX, min_value, max_value);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default: log_e("Invalid measurement type"); return false;
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
int16_t int16_min_value = (int16_t)min_value;
|
||||
[[maybe_unused]]
|
||||
int16_t int16_max_value = (int16_t)max_value;
|
||||
[[maybe_unused]]
|
||||
uint16_t uint16_min_value = (uint16_t)min_value;
|
||||
[[maybe_unused]]
|
||||
uint16_t uint16_max_value = (uint16_t)max_value;
|
||||
|
||||
//TODO: Log info about min and max values for different measurement types
|
||||
switch (measurement_type) {
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MIN_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MAX_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MIN_PH_B_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MAX_PH_B_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MIN_PH_C_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_VOLTAGE_MAX_PH_C_ID;
|
||||
break;
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MIN_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MAX_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MIN_PH_B_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MAX_PH_B_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MIN_PH_C_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMS_CURRENT_MAX_PH_C_ID;
|
||||
break;
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MIN_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MAX_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MIN_PH_B_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MAX_PH_B_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C:
|
||||
attr_min_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MIN_PH_C_ID;
|
||||
attr_max_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_MAX_PH_C_ID;
|
||||
break;
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default: log_e("Invalid measurement type"); return false;
|
||||
}
|
||||
esp_zb_attribute_list_t *electrical_measurement_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_min_id, (void *)&min_value);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_max_id, (void *)&max_value);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, uint16_t multiplier, uint16_t divisor) {
|
||||
uint16_t attr_multiplier = 0;
|
||||
uint16_t attr_divisor = 0;
|
||||
|
||||
switch (measurement_type) {
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE:
|
||||
attr_multiplier = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACVOLTAGE_MULTIPLIER_ID;
|
||||
attr_divisor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACVOLTAGE_DIVISOR_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT:
|
||||
attr_multiplier = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACCURRENT_MULTIPLIER_ID;
|
||||
attr_divisor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACCURRENT_DIVISOR_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER:
|
||||
attr_multiplier = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACPOWER_MULTIPLIER_ID;
|
||||
attr_divisor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACPOWER_DIVISOR_ID;
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY:
|
||||
attr_multiplier = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_MULTIPLIER_ID;
|
||||
attr_divisor = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_DIVISOR_ID;
|
||||
break;
|
||||
default: log_e("Invalid measurement type"); return false;
|
||||
}
|
||||
esp_zb_attribute_list_t *electrical_measurement_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_multiplier, (void *)&multiplier);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set multiplier: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_divisor, (void *)&divisor);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set divisor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::setACPowerFactor(ZIGBEE_AC_PHASE_TYPE phase_type, int8_t power_factor) {
|
||||
uint16_t attr_id = 0;
|
||||
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_PH_B_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_PH_C_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
|
||||
esp_zb_attribute_list_t *electrical_measurement_cluster =
|
||||
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
ret = esp_zb_cluster_update_attr(electrical_measurement_cluster, attr_id, (void *)&power_factor);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set power factor: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ZigbeeElectricalMeasurement::setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type, int32_t value) {
|
||||
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
|
||||
uint16_t attr_id = 0;
|
||||
|
||||
// Check value is valid for the measurement type
|
||||
switch (measurement_type) {
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE:
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT:
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY:
|
||||
if (value < 0 || value > UINT16_MAX) {
|
||||
log_e("AC measurement value must be between 0 and %u (got %d)", UINT16_MAX, value);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER:
|
||||
if (value < INT16_MIN || value > INT16_MAX) {
|
||||
log_e("AC power value must be between %d and %d (got %d)", INT16_MIN, INT16_MAX, value);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR:
|
||||
if (value < -100 || value > 100) {
|
||||
log_e("AC power factor value must be between -100 and 100 (got %d)", value);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default: log_e("Invalid measurement type"); return false;
|
||||
}
|
||||
// Convert value to appropriate type based on measurement type
|
||||
uint16_t uint16_value = (uint16_t)value;
|
||||
int16_t int16_value = (int16_t)value;
|
||||
int8_t int8_value = (int8_t)value;
|
||||
|
||||
switch (measurement_type) {
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
// Use uint16_t for voltage
|
||||
log_v("Updating AC voltage measurement value...");
|
||||
log_d("Setting AC voltage to %u", uint16_value);
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
ret =
|
||||
esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id, &uint16_value, false);
|
||||
esp_zb_lock_release();
|
||||
break;
|
||||
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
// Use uint16_t for current
|
||||
log_v("Updating AC current measurement value...");
|
||||
log_d("Setting AC current to %u", uint16_value);
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
ret =
|
||||
esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id, &uint16_value, false);
|
||||
esp_zb_lock_release();
|
||||
break;
|
||||
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
// Use int16_t for power
|
||||
log_v("Updating AC power measurement value...");
|
||||
log_d("Setting AC power to %d", int16_value);
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
ret = esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id, &int16_value, false);
|
||||
esp_zb_lock_release();
|
||||
break;
|
||||
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY:
|
||||
attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_ID;
|
||||
// Use uint16_t for frequency
|
||||
log_v("Updating AC frequency measurement value...");
|
||||
log_d("Setting AC frequency to %u", uint16_value);
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
ret =
|
||||
esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id, &uint16_value, false);
|
||||
esp_zb_lock_release();
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_PH_B_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_POWER_FACTOR_PH_C_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
// Use int8_t for power factor
|
||||
log_v("Updating AC power factor measurement value...");
|
||||
log_d("Setting AC power factor to %d", int8_value);
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
ret = esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attr_id, &int8_value, false);
|
||||
esp_zb_lock_release();
|
||||
break;
|
||||
default: log_e("Invalid measurement type"); return false;
|
||||
}
|
||||
|
||||
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
|
||||
log_e("Failed to set AC measurement: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::setACReporting(
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type, uint16_t min_interval, uint16_t max_interval, int32_t delta
|
||||
) {
|
||||
uint16_t attr_id = 0;
|
||||
|
||||
// Convert value to appropriate type based on measurement type
|
||||
uint16_t uint16_delta = (uint16_t)delta;
|
||||
int16_t int16_delta = (int16_t)delta;
|
||||
|
||||
switch (measurement_type) {
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_ID; break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR: log_e("Power factor attribute reporting not supported by zigbee specification"); return false;
|
||||
default: log_e("Invalid measurement type"); return false;
|
||||
}
|
||||
|
||||
esp_zb_zcl_reporting_info_t reporting_info;
|
||||
memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t));
|
||||
reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV;
|
||||
reporting_info.ep = _endpoint;
|
||||
reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT;
|
||||
reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE;
|
||||
reporting_info.attr_id = attr_id;
|
||||
reporting_info.u.send_info.min_interval = min_interval;
|
||||
reporting_info.u.send_info.max_interval = max_interval;
|
||||
reporting_info.u.send_info.def_min_interval = min_interval;
|
||||
reporting_info.u.send_info.def_max_interval = max_interval;
|
||||
if (measurement_type == ZIGBEE_AC_MEASUREMENT_TYPE_POWER) {
|
||||
reporting_info.u.send_info.delta.s16 = int16_delta;
|
||||
} else {
|
||||
reporting_info.u.send_info.delta.u16 = uint16_delta;
|
||||
}
|
||||
reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID;
|
||||
reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
esp_err_t ret = esp_zb_zcl_update_reporting_info(&reporting_info);
|
||||
esp_zb_lock_release();
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to set reporting: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeElectricalMeasurement::reportAC(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type) {
|
||||
uint16_t attr_id = 0;
|
||||
|
||||
switch (measurement_type) {
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSVOLTAGE_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_RMSCURRENT_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER:
|
||||
switch (phase_type) {
|
||||
case ZIGBEE_AC_PHASE_TYPE_A: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_B: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_PHB_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_C: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_ACTIVE_POWER_PHC_ID; break;
|
||||
case ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC:
|
||||
default: log_e("Invalid phase type"); return false;
|
||||
}
|
||||
break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY: attr_id = ESP_ZB_ZCL_ATTR_ELECTRICAL_MEASUREMENT_AC_FREQUENCY_ID; break;
|
||||
case ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR: log_e("Power factor attribute reporting not supported by zigbee specification"); return false;
|
||||
default: log_e("Invalid measurement type"); return false;
|
||||
}
|
||||
/* Send report attributes command */
|
||||
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
|
||||
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
|
||||
report_attr_cmd.attributeID = attr_id;
|
||||
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
|
||||
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT;
|
||||
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
|
||||
report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
|
||||
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
|
||||
esp_zb_lock_release();
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to send AC report: 0x%x: %s", ret, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
log_v("AC report sent");
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
105
libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h
Normal file
105
libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* Class of Zigbee Pressure sensor endpoint inherited from common EP class */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_ZB_ENABLED
|
||||
|
||||
#include "ZigbeeEP.h"
|
||||
#include "ha/esp_zigbee_ha_standard.h"
|
||||
|
||||
// clang-format off
|
||||
#define ZIGBEE_DEFAULT_ELECTRICAL_MEASUREMENT_CONFIG() \
|
||||
{ \
|
||||
.basic_cfg = \
|
||||
{ \
|
||||
.zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \
|
||||
.power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \
|
||||
}, \
|
||||
.identify_cfg = \
|
||||
{ \
|
||||
.identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \
|
||||
}, \
|
||||
.electrical_measurement_cfg = \
|
||||
{ \
|
||||
.measured_type = 0x00, \
|
||||
}, \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
enum ZIGBEE_DC_MEASUREMENT_TYPE {
|
||||
ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE = 0x0001,
|
||||
ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT = 0x0002,
|
||||
ZIGBEE_DC_MEASUREMENT_TYPE_POWER = 0x0003,
|
||||
};
|
||||
|
||||
enum ZIGBEE_AC_MEASUREMENT_TYPE {
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE = 0x0001,
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT = 0x0002,
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_POWER = 0x0003,
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR = 0x0004,
|
||||
ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY = 0x0005,
|
||||
};
|
||||
|
||||
enum ZIGBEE_AC_PHASE_TYPE {
|
||||
ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC = 0x0000,
|
||||
ZIGBEE_AC_PHASE_TYPE_A = 0x0001,
|
||||
ZIGBEE_AC_PHASE_TYPE_B = 0x0002,
|
||||
ZIGBEE_AC_PHASE_TYPE_C = 0x0003,
|
||||
};
|
||||
|
||||
typedef struct zigbee_electrical_measurement_cfg_s {
|
||||
esp_zb_basic_cluster_cfg_t basic_cfg;
|
||||
esp_zb_identify_cluster_cfg_t identify_cfg;
|
||||
esp_zb_electrical_meas_cluster_cfg_t electrical_measurement_cfg;
|
||||
} zigbee_electrical_measurement_cfg_t;
|
||||
|
||||
class ZigbeeElectricalMeasurement : public ZigbeeEP {
|
||||
public:
|
||||
ZigbeeElectricalMeasurement(uint8_t endpoint);
|
||||
~ZigbeeElectricalMeasurement() {}
|
||||
|
||||
/**
|
||||
* @brief DC measurement methods
|
||||
*/
|
||||
// Add a DC measurement type
|
||||
bool addDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type);
|
||||
// Set the DC measurement value for the given measurement type
|
||||
bool setDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type, int16_t value);
|
||||
// Set the DC min and max value for the given measurement type
|
||||
bool setDCMinMaxValue(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type, int16_t min, int16_t max);
|
||||
// Set the DC multiplier and divisor for the given measurement type
|
||||
bool setDCMultiplierDivisor(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type, uint16_t multiplier, uint16_t divisor);
|
||||
// Set the DC reporting interval for the given measurement type in seconds and delta (measurement change)
|
||||
bool setDCReporting(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type, uint16_t min_interval, uint16_t max_interval, int16_t delta);
|
||||
// Report the DC measurement value for the given measurement type
|
||||
bool reportDC(ZIGBEE_DC_MEASUREMENT_TYPE measurement_type);
|
||||
|
||||
/**
|
||||
* @brief AC measurement methods
|
||||
*/
|
||||
// Add an AC measurement type for selected phase type
|
||||
bool addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type);
|
||||
// Set the AC measurement value for the given measurement type and phase type (uint16_t for voltage, current and frequency, int32_t for power)
|
||||
bool setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type, int32_t value);
|
||||
// Set the AC min and max value for the given measurement type and phase type (uint16_t for voltage, current and frequency, int32_t for power)
|
||||
bool setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type, int32_t min, int32_t max);
|
||||
// Set the AC multiplier and divisor for the given measurement type (common for all phases)
|
||||
bool setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, uint16_t multiplier, uint16_t divisor);
|
||||
// Set the AC power factor for the given phase type (-100 to 100 %)
|
||||
bool setACPowerFactor(ZIGBEE_AC_PHASE_TYPE phase_type, int8_t power_factor);
|
||||
// Set the AC reporting interval for the given measurement type and phase type in seconds and delta (measurement change - uint16_t for voltage, current and frequency, int32_t for power)
|
||||
bool
|
||||
setACReporting(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type, uint16_t min_interval, uint16_t max_interval, int32_t delta);
|
||||
// Report the AC measurement value for the given measurement type and phase type
|
||||
bool reportAC(ZIGBEE_AC_MEASUREMENT_TYPE measurement_type, ZIGBEE_AC_PHASE_TYPE phase_type);
|
||||
|
||||
private:
|
||||
uint32_t measure_type = 0x0000;
|
||||
bool ac_volt_mult_div_set = false;
|
||||
bool ac_current_mult_div_set = false;
|
||||
bool ac_power_mult_div_set = false;
|
||||
};
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
58
libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp
Normal file
58
libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#include "ZigbeePowerOutlet.h"
|
||||
#if CONFIG_ZB_ENABLED
|
||||
|
||||
ZigbeePowerOutlet::ZigbeePowerOutlet(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID;
|
||||
|
||||
esp_zb_mains_power_outlet_cfg_t outlet_cfg = ESP_ZB_DEFAULT_MAINS_POWER_OUTLET_CONFIG();
|
||||
_cluster_list = esp_zb_mains_power_outlet_clusters_create(&outlet_cfg);
|
||||
_ep_config = {
|
||||
.endpoint = endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID, .app_device_version = 0
|
||||
};
|
||||
log_v("Outlet endpoint created %d", _endpoint);
|
||||
}
|
||||
|
||||
//set attribute method -> method overridden in child class
|
||||
void ZigbeePowerOutlet::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {
|
||||
//check the data and call right method
|
||||
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {
|
||||
if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) {
|
||||
_current_state = *(bool *)message->attribute.data.value;
|
||||
stateChanged();
|
||||
} else {
|
||||
log_w("Received message ignored. Attribute ID: %d not supported for On/Off Outlet", message->attribute.id);
|
||||
}
|
||||
} else {
|
||||
log_w("Received message ignored. Cluster ID: %d not supported for On/Off Outlet", message->info.cluster);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeePowerOutlet::stateChanged() {
|
||||
if (_on_state_change) {
|
||||
_on_state_change(_current_state);
|
||||
} else {
|
||||
log_w("No callback function set for outlet change");
|
||||
}
|
||||
}
|
||||
|
||||
bool ZigbeePowerOutlet::setState(bool state) {
|
||||
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
|
||||
_current_state = state;
|
||||
stateChanged();
|
||||
|
||||
log_v("Updating on/off outlet state to %d", state);
|
||||
/* Update on/off outlet state */
|
||||
esp_zb_lock_acquire(portMAX_DELAY);
|
||||
ret = esp_zb_zcl_set_attribute_val(
|
||||
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ON_OFF, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID, &_current_state, false
|
||||
);
|
||||
esp_zb_lock_release();
|
||||
|
||||
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
|
||||
log_e("Failed to set outlet state: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
41
libraries/Zigbee/src/ep/ZigbeePowerOutlet.h
Normal file
41
libraries/Zigbee/src/ep/ZigbeePowerOutlet.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* Class of Zigbee On/Off Power outlet endpoint inherited from common EP class */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_ZB_ENABLED
|
||||
|
||||
#include "ZigbeeEP.h"
|
||||
#include "ha/esp_zigbee_ha_standard.h"
|
||||
|
||||
class ZigbeePowerOutlet : public ZigbeeEP {
|
||||
public:
|
||||
ZigbeePowerOutlet(uint8_t endpoint);
|
||||
~ZigbeePowerOutlet() {}
|
||||
|
||||
// Use to set a cb function to be called on outlet change
|
||||
void onPowerOutletChange(void (*callback)(bool)) {
|
||||
_on_state_change = callback;
|
||||
}
|
||||
// Use to restore outlet state
|
||||
void restoreState() {
|
||||
stateChanged();
|
||||
}
|
||||
// Use to control outlet state
|
||||
bool setState(bool state);
|
||||
// Use to get outlet state
|
||||
bool getPowerOutletState() {
|
||||
return _current_state;
|
||||
}
|
||||
|
||||
private:
|
||||
void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override;
|
||||
//callback function to be called on outlet change
|
||||
void (*_on_state_change)(bool);
|
||||
void stateChanged();
|
||||
|
||||
bool _current_state;
|
||||
};
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
|
|
@ -7,6 +7,7 @@ ZigbeeSwitch *ZigbeeSwitch::_instance = nullptr;
|
|||
ZigbeeSwitch::ZigbeeSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID;
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_device = nullptr;
|
||||
|
||||
esp_zb_on_off_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();
|
||||
_cluster_list = esp_zb_on_off_switch_clusters_create(&switch_cfg);
|
||||
|
|
@ -15,18 +16,40 @@ ZigbeeSwitch::ZigbeeSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
|||
}
|
||||
|
||||
void ZigbeeSwitch::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeSwitch *instance = static_cast<ZigbeeSwitch *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_i("Bound successfully!");
|
||||
if (user_ctx) {
|
||||
zb_device_params_t *light = (zb_device_params_t *)user_ctx;
|
||||
if (instance->_device) {
|
||||
zb_device_params_t *light = (zb_device_params_t *)instance->_device;
|
||||
log_i("The light originating from address(0x%x) on endpoint(%d)", light->short_addr, light->endpoint);
|
||||
_instance->_bound_devices.push_back(light);
|
||||
log_d("Light bound to a switch on EP %d", instance->_endpoint);
|
||||
instance->_bound_devices.push_back(light);
|
||||
}
|
||||
_is_bound = true;
|
||||
instance->_is_bound = true;
|
||||
} else {
|
||||
instance->_device = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeSwitch::bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeSwitch *instance = static_cast<ZigbeeSwitch *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("bindCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->bindCb(zdo_status, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// Static wrapper for findCb
|
||||
void ZigbeeSwitch::findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeSwitch *instance = static_cast<ZigbeeSwitch *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("findCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->findCb(zdo_status, addr, endpoint, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeSwitch *instance = static_cast<ZigbeeSwitch *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_d("Found light endpoint");
|
||||
esp_zb_zdo_bind_req_param_t bind_req;
|
||||
|
|
@ -34,15 +57,21 @@ void ZigbeeSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t
|
|||
light->endpoint = endpoint;
|
||||
light->short_addr = addr;
|
||||
esp_zb_ieee_address_by_short(light->short_addr, light->ieee_addr);
|
||||
log_d("Light found: short address(0x%x), endpoint(%d)", light->short_addr, light->endpoint);
|
||||
|
||||
esp_zb_get_long_address(bind_req.src_address);
|
||||
bind_req.src_endp = *((uint8_t *)user_ctx); //_endpoint;
|
||||
bind_req.src_endp = instance->_endpoint;
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF;
|
||||
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
|
||||
memcpy(bind_req.dst_address_u.addr_long, light->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
bind_req.dst_endp = endpoint;
|
||||
bind_req.req_dst_addr = esp_zb_get_short_address();
|
||||
log_i("Try to bind On/Off");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, (void *)light);
|
||||
log_v("Try to bind On/Off");
|
||||
//save light params in the class
|
||||
instance->_device = light;
|
||||
|
||||
log_d("Find callback on EP %d", instance->_endpoint);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeSwitch::bindCbWrapper, this);
|
||||
} else {
|
||||
log_d("No light endpoint found");
|
||||
}
|
||||
|
|
@ -59,7 +88,7 @@ void ZigbeeSwitch::findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req) {
|
|||
.num_out_clusters = 1,
|
||||
.cluster_list = cluster_list,
|
||||
};
|
||||
esp_zb_zdo_match_cluster(&on_off_req, findCb, &_endpoint);
|
||||
esp_zb_zdo_match_cluster(&on_off_req, ZigbeeSwitch::findCbWrapper, this);
|
||||
}
|
||||
|
||||
// Methods to control the light
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ public:
|
|||
private:
|
||||
// save instance of the class in order to use it in static functions
|
||||
static ZigbeeSwitch *_instance;
|
||||
|
||||
zb_device_params_t *_device;
|
||||
void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req);
|
||||
static void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
};
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ ZigbeeThermostat *ZigbeeThermostat::_instance = nullptr;
|
|||
|
||||
ZigbeeThermostat::ZigbeeThermostat(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_THERMOSTAT_DEVICE_ID;
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_device = nullptr; // Initialize sensor pointer to null
|
||||
|
||||
//use custom config to avoid narrowing error -> must be fixed in zigbee-sdk
|
||||
esp_zb_thermostat_cfg_t thermostat_cfg = ZB_DEFAULT_THERMOSTAT_CONFIG();
|
||||
|
|
@ -29,21 +30,39 @@ ZigbeeThermostat::ZigbeeThermostat(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
|||
}
|
||||
|
||||
void ZigbeeThermostat::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeThermostat *instance = static_cast<ZigbeeThermostat *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
if (user_ctx) {
|
||||
zb_device_params_t *sensor = (zb_device_params_t *)user_ctx;
|
||||
log_i("The temperature sensor originating from address(0x%x) on endpoint(%d)", sensor->short_addr, sensor->endpoint);
|
||||
_instance->_bound_devices.push_back(sensor);
|
||||
} else {
|
||||
log_v("Local binding success");
|
||||
log_i("Bound successfully!");
|
||||
if (instance->_device) {
|
||||
zb_device_params_t *sensor = (zb_device_params_t *)instance->_device;
|
||||
log_i("The sensor originating from address(0x%x) on endpoint(%d)", sensor->short_addr, sensor->endpoint);
|
||||
log_d("Sensor bound to thermostat on EP %d", instance->_endpoint);
|
||||
instance->_bound_devices.push_back(sensor);
|
||||
}
|
||||
_is_bound = true;
|
||||
instance->_is_bound = true;
|
||||
} else {
|
||||
log_e("Binding failed!");
|
||||
instance->_device = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeThermostat::bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeThermostat *instance = static_cast<ZigbeeThermostat *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("bindCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->bindCb(zdo_status, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeThermostat::findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeThermostat *instance = static_cast<ZigbeeThermostat *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("findCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->findCb(zdo_status, addr, endpoint, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeThermostat *instance = static_cast<ZigbeeThermostat *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_i("Found temperature sensor");
|
||||
esp_zb_zdo_bind_req_param_t bind_req;
|
||||
|
|
@ -56,37 +75,34 @@ void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uin
|
|||
|
||||
/* 1. Send binding request to the sensor */
|
||||
bind_req.req_dst_addr = addr;
|
||||
log_d("Request temperature sensor to bind us");
|
||||
|
||||
/* populate the src information of the binding */
|
||||
memcpy(bind_req.src_address, sensor->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
bind_req.src_endp = endpoint;
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
|
||||
log_d("Bind temperature sensor");
|
||||
|
||||
/* populate the dst information of the binding */
|
||||
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
|
||||
esp_zb_get_long_address(bind_req.dst_address_u.addr_long);
|
||||
bind_req.dst_endp = *((uint8_t *)user_ctx); //_endpoint;
|
||||
bind_req.dst_endp = instance->_endpoint;
|
||||
|
||||
log_i("Request temperature sensor to bind us");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, NULL);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeThermostat::bindCbWrapper, NULL);
|
||||
|
||||
/* 2. Send binding request to self */
|
||||
bind_req.req_dst_addr = esp_zb_get_short_address();
|
||||
|
||||
/* populate the src information of the binding */
|
||||
esp_zb_get_long_address(bind_req.src_address);
|
||||
bind_req.src_endp = *((uint8_t *)user_ctx); //_endpoint;
|
||||
bind_req.src_endp = instance->_endpoint;
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
|
||||
|
||||
/* populate the dst information of the binding */
|
||||
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
|
||||
memcpy(bind_req.dst_address_u.addr_long, sensor->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
bind_req.dst_endp = endpoint;
|
||||
log_i("Try to bind Temperature Measurement");
|
||||
//save sensor params in the class
|
||||
instance->_device = sensor;
|
||||
|
||||
log_i("Bind temperature sensor");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, (void *)sensor);
|
||||
log_d("Find callback on EP %d", instance->_endpoint);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeThermostat::bindCbWrapper, this);
|
||||
} else {
|
||||
log_d("No temperature sensor endpoint found");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +112,7 @@ void ZigbeeThermostat::findEndpoint(esp_zb_zdo_match_desc_req_param_t *param) {
|
|||
param->num_in_clusters = 1;
|
||||
param->num_out_clusters = 0;
|
||||
param->cluster_list = cluster_list;
|
||||
esp_zb_zdo_match_cluster(param, findCb, &_endpoint);
|
||||
esp_zb_zdo_match_cluster(param, ZigbeeThermostat::findCbWrapper, this);
|
||||
}
|
||||
|
||||
void ZigbeeThermostat::zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ public:
|
|||
private:
|
||||
// save instance of the class in order to use it in static functions
|
||||
static ZigbeeThermostat *_instance;
|
||||
zb_device_params_t *_device;
|
||||
|
||||
void (*_on_temp_recieve)(float);
|
||||
void (*_on_config_recieve)(float, float, float);
|
||||
|
|
@ -56,8 +57,10 @@ private:
|
|||
float _tolerance;
|
||||
|
||||
void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req);
|
||||
static void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
|
||||
void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) override;
|
||||
};
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -7,11 +7,8 @@
|
|||
# See https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/partition-tables.html
|
||||
# for explanation of partition table structure and uses.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import division, print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import binascii
|
||||
import errno
|
||||
|
|
@ -22,26 +19,34 @@ import struct
|
|||
import sys
|
||||
|
||||
MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
|
||||
MD5_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for MD5 sum
|
||||
MD5_PARTITION_BEGIN = b"\xeb\xeb" + b"\xff" * 14 # The first 2 bytes are like magic numbers for MD5 sum
|
||||
PARTITION_TABLE_SIZE = 0x1000 # Size of partition table
|
||||
|
||||
MIN_PARTITION_SUBTYPE_APP_OTA = 0x10
|
||||
NUM_PARTITION_SUBTYPE_APP_OTA = 16
|
||||
MIN_PARTITION_SUBTYPE_APP_TEE = 0x30
|
||||
NUM_PARTITION_SUBTYPE_APP_TEE = 2
|
||||
|
||||
SECURE_NONE = None
|
||||
SECURE_V1 = "v1"
|
||||
SECURE_V2 = "v2"
|
||||
|
||||
__version__ = "1.2"
|
||||
__version__ = "1.5"
|
||||
|
||||
APP_TYPE = 0x00
|
||||
DATA_TYPE = 0x01
|
||||
BOOTLOADER_TYPE = 0x02
|
||||
PARTITION_TABLE_TYPE = 0x03
|
||||
|
||||
TYPES = {
|
||||
"bootloader": BOOTLOADER_TYPE,
|
||||
"partition_table": PARTITION_TABLE_TYPE,
|
||||
"app": APP_TYPE,
|
||||
"data": DATA_TYPE,
|
||||
}
|
||||
|
||||
NVS_RW_MIN_PARTITION_SIZE = 0x3000
|
||||
|
||||
|
||||
def get_ptype_as_int(ptype):
|
||||
"""Convert a string which might be numeric or the name of a partition type to an integer"""
|
||||
|
|
@ -56,6 +61,15 @@ def get_ptype_as_int(ptype):
|
|||
|
||||
# Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h
|
||||
SUBTYPES = {
|
||||
BOOTLOADER_TYPE: {
|
||||
"primary": 0x00,
|
||||
"ota": 0x01,
|
||||
"recovery": 0x02,
|
||||
},
|
||||
PARTITION_TABLE_TYPE: {
|
||||
"primary": 0x00,
|
||||
"ota": 0x01,
|
||||
},
|
||||
APP_TYPE: {
|
||||
"factory": 0x00,
|
||||
"test": 0x20,
|
||||
|
|
@ -72,6 +86,7 @@ SUBTYPES = {
|
|||
"fat": 0x81,
|
||||
"spiffs": 0x82,
|
||||
"littlefs": 0x83,
|
||||
"tee_ota": 0x90,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -90,6 +105,8 @@ def get_subtype_as_int(ptype, subtype):
|
|||
ALIGNMENT = {
|
||||
APP_TYPE: 0x10000,
|
||||
DATA_TYPE: 0x1000,
|
||||
BOOTLOADER_TYPE: 0x1000,
|
||||
PARTITION_TABLE_TYPE: 0x1000,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -98,14 +115,18 @@ def get_alignment_offset_for_type(ptype):
|
|||
|
||||
|
||||
def get_alignment_size_for_type(ptype):
|
||||
if ptype == APP_TYPE and secure == SECURE_V1:
|
||||
# For secure boot v1 case, app partition must be 64K aligned
|
||||
# signature block (68 bytes) lies at the very end of 64K block
|
||||
return 0x10000
|
||||
if ptype == APP_TYPE and secure == SECURE_V2:
|
||||
# For secure boot v2 case, app partition must be 4K aligned
|
||||
# signature block (4K) is kept after padding the unsigned image to 64K boundary
|
||||
return 0x1000
|
||||
if ptype == APP_TYPE:
|
||||
if secure == SECURE_V1:
|
||||
# For secure boot v1 case, app partition must be 64K aligned
|
||||
# signature block (68 bytes) lies at the very end of 64K block
|
||||
return 0x10000
|
||||
elif secure == SECURE_V2:
|
||||
# For secure boot v2 case, app partition must be 4K aligned
|
||||
# signature block (4K) is kept after padding the unsigned image to 64K boundary
|
||||
return 0x1000
|
||||
else:
|
||||
# For no secure boot enabled case, app partition must be 4K aligned (min. flash erase size)
|
||||
return 0x1000
|
||||
# No specific size alignment requirement as such
|
||||
return 0x1
|
||||
|
||||
|
|
@ -115,6 +136,10 @@ def get_partition_type(ptype):
|
|||
return APP_TYPE
|
||||
if ptype == "data":
|
||||
return DATA_TYPE
|
||||
if ptype == "bootloader":
|
||||
return BOOTLOADER_TYPE
|
||||
if ptype == "partition_table":
|
||||
return PARTITION_TABLE_TYPE
|
||||
raise InputError("Invalid partition type")
|
||||
|
||||
|
||||
|
|
@ -134,6 +159,8 @@ quiet = False
|
|||
md5sum = True
|
||||
secure = SECURE_NONE
|
||||
offset_part_table = 0
|
||||
primary_bootloader_offset = None
|
||||
recovery_bootloader_offset = None
|
||||
|
||||
|
||||
def status(msg):
|
||||
|
|
@ -165,7 +192,7 @@ class PartitionTable(list):
|
|||
return cls.from_csv(data), False
|
||||
|
||||
@classmethod
|
||||
def from_csv(cls, csv_contents): # noqa: C901
|
||||
def from_csv(cls, csv_contents):
|
||||
res = PartitionTable()
|
||||
lines = csv_contents.splitlines()
|
||||
|
||||
|
|
@ -194,6 +221,11 @@ class PartitionTable(list):
|
|||
# fix up missing offsets & negative sizes
|
||||
last_end = offset_part_table + PARTITION_TABLE_SIZE # first offset after partition table
|
||||
for e in res:
|
||||
is_primary_bootloader = e.type == BOOTLOADER_TYPE and e.subtype == SUBTYPES[e.type]["primary"]
|
||||
is_primary_partition_table = e.type == PARTITION_TABLE_TYPE and e.subtype == SUBTYPES[e.type]["primary"]
|
||||
if is_primary_bootloader or is_primary_partition_table:
|
||||
# They do not participate in the restoration of missing offsets
|
||||
continue
|
||||
if e.offset is not None and e.offset < last_end:
|
||||
if e == res[0]:
|
||||
raise InputError(
|
||||
|
|
@ -203,8 +235,8 @@ class PartitionTable(list):
|
|||
)
|
||||
else:
|
||||
raise InputError(
|
||||
"CSV Error at line %d: Partitions overlap. Partition sets offset 0x%x. Previous partition ends 0x%x" # noqa: E501
|
||||
% (e.line_no, e.offset, last_end)
|
||||
"CSV Error at line %d: Partitions overlap. Partition sets offset 0x%x. "
|
||||
"Previous partition ends 0x%x" % (e.line_no, e.offset, last_end)
|
||||
)
|
||||
if e.offset is None:
|
||||
pad_to = get_alignment_offset_for_type(e.type)
|
||||
|
|
@ -246,14 +278,14 @@ class PartitionTable(list):
|
|||
return p
|
||||
return None
|
||||
|
||||
def verify(self): # noqa: C901
|
||||
def verify(self):
|
||||
# verify each partition individually
|
||||
for p in self:
|
||||
p.verify()
|
||||
|
||||
# check on duplicate name
|
||||
names = [p.name for p in self]
|
||||
duplicates = set(n for n in names if names.count(n) > 1) # noqa: C401
|
||||
duplicates = {n for n in names if names.count(n) > 1}
|
||||
|
||||
# print sorted duplicate partitions by name
|
||||
if len(duplicates) != 0:
|
||||
|
|
@ -267,9 +299,12 @@ class PartitionTable(list):
|
|||
last = None
|
||||
for p in sorted(self, key=lambda x: x.offset):
|
||||
if p.offset < offset_part_table + PARTITION_TABLE_SIZE:
|
||||
raise InputError(
|
||||
"Partition offset 0x%x is below 0x%x" % (p.offset, offset_part_table + PARTITION_TABLE_SIZE)
|
||||
)
|
||||
is_primary_bootloader = p.type == BOOTLOADER_TYPE and p.subtype == SUBTYPES[p.type]["primary"]
|
||||
is_primary_partition_table = p.type == PARTITION_TABLE_TYPE and p.subtype == SUBTYPES[p.type]["primary"]
|
||||
if not (is_primary_bootloader or is_primary_partition_table):
|
||||
raise InputError(
|
||||
"Partition offset 0x%x is below 0x%x" % (p.offset, offset_part_table + PARTITION_TABLE_SIZE)
|
||||
)
|
||||
if last is not None and p.offset < last.offset + last.size:
|
||||
raise InputError(
|
||||
"Partition at 0x%x overlaps 0x%x-0x%x" % (p.offset, last.offset, last.offset + last.size - 1)
|
||||
|
|
@ -282,7 +317,8 @@ class PartitionTable(list):
|
|||
for p in otadata_duplicates:
|
||||
critical("%s" % (p.to_csv()))
|
||||
raise InputError(
|
||||
'Found multiple otadata partitions. Only one partition can be defined with type="data"(1) and subtype="ota"(0).' # noqa: E501
|
||||
"Found multiple otadata partitions. Only one partition can be defined with "
|
||||
'type="data"(1) and subtype="ota"(0).'
|
||||
)
|
||||
|
||||
if len(otadata_duplicates) == 1 and otadata_duplicates[0].size != 0x2000:
|
||||
|
|
@ -290,6 +326,23 @@ class PartitionTable(list):
|
|||
critical("%s" % (p.to_csv()))
|
||||
raise InputError("otadata partition must have size = 0x2000")
|
||||
|
||||
# Above checks but for TEE otadata
|
||||
otadata_duplicates = [
|
||||
p for p in self if p.type == TYPES["data"] and p.subtype == SUBTYPES[DATA_TYPE]["tee_ota"]
|
||||
]
|
||||
if len(otadata_duplicates) > 1:
|
||||
for p in otadata_duplicates:
|
||||
critical("%s" % (p.to_csv()))
|
||||
raise InputError(
|
||||
"Found multiple TEE otadata partitions. Only one partition can be defined with "
|
||||
'type="data"(1) and subtype="tee_ota"(0x90).'
|
||||
)
|
||||
|
||||
if len(otadata_duplicates) == 1 and otadata_duplicates[0].size != 0x2000:
|
||||
p = otadata_duplicates[0]
|
||||
critical("%s" % (p.to_csv()))
|
||||
raise InputError("TEE otadata partition must have size = 0x2000")
|
||||
|
||||
def flash_size(self):
|
||||
"""Return the size that partitions will occupy in flash
|
||||
(ie the offset the last partition ends at)
|
||||
|
|
@ -321,7 +374,7 @@ class PartitionTable(list):
|
|||
data = b[o : o + 32]
|
||||
if len(data) != 32:
|
||||
raise InputError("Partition table length must be a multiple of 32 bytes")
|
||||
if data == b"\xFF" * 32:
|
||||
if data == b"\xff" * 32:
|
||||
return result # got end marker
|
||||
if md5sum and data[:2] == MD5_PARTITION_BEGIN[:2]: # check only the magic number part
|
||||
if data[16:] == md5.digest():
|
||||
|
|
@ -342,7 +395,7 @@ class PartitionTable(list):
|
|||
result += MD5_PARTITION_BEGIN + hashlib.md5(result).digest()
|
||||
if len(result) >= MAX_PARTITION_LENGTH:
|
||||
raise InputError("Binary partition table length (%d) longer than max" % len(result))
|
||||
result += b"\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
|
||||
result += b"\xff" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
|
||||
return result
|
||||
|
||||
def to_csv(self, simple_formatting=False):
|
||||
|
|
@ -352,16 +405,20 @@ class PartitionTable(list):
|
|||
|
||||
|
||||
class PartitionDefinition(object):
|
||||
MAGIC_BYTES = b"\xAA\x50"
|
||||
MAGIC_BYTES = b"\xaa\x50"
|
||||
|
||||
# dictionary maps flag name (as used in CSV flags list, property name)
|
||||
# to bit set in flags words in binary format
|
||||
FLAGS = {"encrypted": 0}
|
||||
FLAGS = {"encrypted": 0, "readonly": 1}
|
||||
|
||||
# add subtypes for the 16 OTA slot values ("ota_XX, etc.")
|
||||
for ota_slot in range(NUM_PARTITION_SUBTYPE_APP_OTA):
|
||||
SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = MIN_PARTITION_SUBTYPE_APP_OTA + ota_slot
|
||||
|
||||
# add subtypes for the 2 TEE OTA slot values ("tee_XX, etc.")
|
||||
for tee_slot in range(NUM_PARTITION_SUBTYPE_APP_TEE):
|
||||
SUBTYPES[TYPES["app"]]["tee_%d" % tee_slot] = MIN_PARTITION_SUBTYPE_APP_TEE + tee_slot
|
||||
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.type = None
|
||||
|
|
@ -369,6 +426,7 @@ class PartitionDefinition(object):
|
|||
self.offset = None
|
||||
self.size = None
|
||||
self.encrypted = False
|
||||
self.readonly = False
|
||||
|
||||
@classmethod
|
||||
def from_csv(cls, line, line_no):
|
||||
|
|
@ -381,8 +439,8 @@ class PartitionDefinition(object):
|
|||
res.name = fields[0]
|
||||
res.type = res.parse_type(fields[1])
|
||||
res.subtype = res.parse_subtype(fields[2])
|
||||
res.offset = res.parse_address(fields[3])
|
||||
res.size = res.parse_address(fields[4])
|
||||
res.offset = res.parse_address(fields[3], res.type, res.subtype)
|
||||
res.size = res.parse_size(fields[4], res.type)
|
||||
if res.size is None:
|
||||
raise InputError("Size field can't be empty")
|
||||
|
||||
|
|
@ -452,12 +510,36 @@ class PartitionDefinition(object):
|
|||
return SUBTYPES[DATA_TYPE]["undefined"]
|
||||
return parse_int(strval, SUBTYPES.get(self.type, {}))
|
||||
|
||||
def parse_address(self, strval):
|
||||
def parse_size(self, strval, ptype):
|
||||
if ptype == BOOTLOADER_TYPE:
|
||||
if primary_bootloader_offset is None:
|
||||
raise InputError("Primary bootloader offset is not defined. Please use --primary-bootloader-offset")
|
||||
return offset_part_table - primary_bootloader_offset
|
||||
if ptype == PARTITION_TABLE_TYPE:
|
||||
return PARTITION_TABLE_SIZE
|
||||
if strval == "":
|
||||
return None # PartitionTable will fill in default
|
||||
return parse_int(strval)
|
||||
|
||||
def verify(self): # noqa: C901
|
||||
def parse_address(self, strval, ptype, psubtype):
|
||||
if ptype == BOOTLOADER_TYPE:
|
||||
if psubtype == SUBTYPES[ptype]["primary"]:
|
||||
if primary_bootloader_offset is None:
|
||||
raise InputError("Primary bootloader offset is not defined. Please use --primary-bootloader-offset")
|
||||
return primary_bootloader_offset
|
||||
if psubtype == SUBTYPES[ptype]["recovery"]:
|
||||
if recovery_bootloader_offset is None:
|
||||
raise InputError(
|
||||
"Recovery bootloader offset is not defined. Please use --recovery-bootloader-offset"
|
||||
)
|
||||
return recovery_bootloader_offset
|
||||
if ptype == PARTITION_TABLE_TYPE and psubtype == SUBTYPES[ptype]["primary"]:
|
||||
return offset_part_table
|
||||
if strval == "":
|
||||
return None # PartitionTable will fill in default
|
||||
return parse_int(strval)
|
||||
|
||||
def verify(self):
|
||||
if self.type is None:
|
||||
raise ValidationError(self, "Type field is not set")
|
||||
if self.subtype is None:
|
||||
|
|
@ -469,7 +551,7 @@ class PartitionDefinition(object):
|
|||
offset_align = get_alignment_offset_for_type(self.type)
|
||||
if self.offset % offset_align:
|
||||
raise ValidationError(self, "Offset 0x%x is not aligned to 0x%x" % (self.offset, offset_align))
|
||||
if self.type == APP_TYPE and secure is not SECURE_NONE:
|
||||
if self.type == APP_TYPE:
|
||||
size_align = get_alignment_size_for_type(self.type)
|
||||
if self.size % size_align:
|
||||
raise ValidationError(self, "Size 0x%x is not aligned to 0x%x" % (self.size, size_align))
|
||||
|
|
@ -489,6 +571,23 @@ class PartitionDefinition(object):
|
|||
% (self.name, self.type, self.subtype)
|
||||
)
|
||||
|
||||
always_rw_data_subtypes = [SUBTYPES[DATA_TYPE]["ota"], SUBTYPES[DATA_TYPE]["coredump"]]
|
||||
if self.type == TYPES["data"] and self.subtype in always_rw_data_subtypes and self.readonly is True:
|
||||
raise ValidationError(
|
||||
self,
|
||||
"'%s' partition of type %s and subtype %s is always read-write and cannot be read-only"
|
||||
% (self.name, self.type, self.subtype),
|
||||
)
|
||||
|
||||
if self.type == TYPES["data"] and self.subtype == SUBTYPES[DATA_TYPE]["nvs"]:
|
||||
if self.size < NVS_RW_MIN_PARTITION_SIZE and self.readonly is False:
|
||||
raise ValidationError(
|
||||
self,
|
||||
"""'%s' partition of type %s and subtype %s of this size (0x%x) must be flagged as 'readonly' \
|
||||
(the size of read/write NVS has to be at least 0x%x)"""
|
||||
% (self.name, self.type, self.subtype, self.size, NVS_RW_MIN_PARTITION_SIZE),
|
||||
)
|
||||
|
||||
STRUCT_FORMAT = b"<2sBBLL16sL"
|
||||
|
||||
@classmethod
|
||||
|
|
@ -574,11 +673,13 @@ def parse_int(v, keywords={}):
|
|||
raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ", ".join(keywords)))
|
||||
|
||||
|
||||
def main(): # noqa: C901
|
||||
def main():
|
||||
global quiet
|
||||
global md5sum
|
||||
global offset_part_table
|
||||
global secure
|
||||
global primary_bootloader_offset
|
||||
global recovery_bootloader_offset
|
||||
parser = argparse.ArgumentParser(description="ESP32 partition table utility")
|
||||
|
||||
parser.add_argument(
|
||||
|
|
@ -600,6 +701,8 @@ def main(): # noqa: C901
|
|||
)
|
||||
parser.add_argument("--quiet", "-q", help="Don't print non-critical status messages to stderr", action="store_true")
|
||||
parser.add_argument("--offset", "-o", help="Set offset partition table", default="0x8000")
|
||||
parser.add_argument("--primary-bootloader-offset", help="Set primary bootloader offset", default=None)
|
||||
parser.add_argument("--recovery-bootloader-offset", help="Set recovery bootloader offset", default=None)
|
||||
parser.add_argument(
|
||||
"--secure",
|
||||
help="Require app partitions to be suitable for secure boot",
|
||||
|
|
@ -622,6 +725,15 @@ def main(): # noqa: C901
|
|||
md5sum = not args.disable_md5sum
|
||||
secure = args.secure
|
||||
offset_part_table = int(args.offset, 0)
|
||||
if args.primary_bootloader_offset is not None:
|
||||
primary_bootloader_offset = int(args.primary_bootloader_offset, 0)
|
||||
if primary_bootloader_offset >= offset_part_table:
|
||||
raise InputError(
|
||||
f"Unsupported configuration. Primary bootloader must be below partition table. "
|
||||
f"Check --primary-bootloader-offset={primary_bootloader_offset:#x} and --offset={offset_part_table:#x}"
|
||||
)
|
||||
if args.recovery_bootloader_offset is not None:
|
||||
recovery_bootloader_offset = int(args.recovery_bootloader_offset, 0)
|
||||
if args.extra_partition_subtypes:
|
||||
add_extra_subtypes(args.extra_partition_subtypes)
|
||||
|
||||
|
|
@ -647,7 +759,7 @@ def main(): # noqa: C901
|
|||
|
||||
if input_is_binary:
|
||||
output = table.to_csv()
|
||||
with sys.stdout if args.output == "-" else open(args.output, "w") as f:
|
||||
with sys.stdout if args.output == "-" else open(args.output, "w", encoding="utf-8") as f:
|
||||
f.write(output)
|
||||
else:
|
||||
output = table.to_binary()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
ota_0, app, ota_0, 0x10000, 0x3ED000,
|
||||
ota_1, app, ota_1, 0x3FD000, 0x3ED000,
|
||||
ota_0, app, ota_0, 0x10000, 0x3EA000,
|
||||
ota_1, app, ota_1, 0x400000, 0x3EA000,
|
||||
fctry, data, nvs, 0x7EA000, 0x6000,
|
||||
coredump, data, coredump,0x7F0000, 0x10000,
|
||||
|
|
|
|||
|
34
variants/dfrobot_lorawan_esp32s3/pins_arduino.h
Normal file
34
variants/dfrobot_lorawan_esp32s3/pins_arduino.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LED_BUILTIN 21
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 8;
|
||||
static const uint8_t SCL = 9;
|
||||
|
||||
#define LORA_ANTPWR 42 //RXEN
|
||||
#define LORA_RST 41 //RST
|
||||
#define LORA_BUSY 40 //BUSY
|
||||
#define LORA_DIO1 4 //DIO
|
||||
|
||||
static const uint8_t LORA_SS = 10;
|
||||
static const uint8_t LORA_MOSI = 6;
|
||||
static const uint8_t LORA_MISO = 5;
|
||||
static const uint8_t LORA_SCK = 7;
|
||||
|
||||
static const uint8_t SS = 17;
|
||||
static const uint8_t MOSI = 11;
|
||||
static const uint8_t MISO = 13;
|
||||
static const uint8_t SCK = 12;
|
||||
|
||||
#define TFT_DC 14
|
||||
#define TFT_CS 17
|
||||
#define TFT_RST 15
|
||||
#define TFT_BL 16 // Backlight pin
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
60
variants/yb_esp32s3_drv/pins_arduino.h
Normal file
60
variants/yb_esp32s3_drv/pins_arduino.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USB_VID 0x303A
|
||||
#define USB_PID 0x1001
|
||||
|
||||
static const uint8_t LED_BUILTIN = 47;
|
||||
#define BUILTIN_LED LED_BUILTIN // backward compatibility
|
||||
#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 8;
|
||||
static const uint8_t SCL = 9;
|
||||
|
||||
static const uint8_t SS = 10;
|
||||
static const uint8_t MOSI = 11;
|
||||
static const uint8_t MISO = 13;
|
||||
static const uint8_t SCK = 12;
|
||||
|
||||
// 2 connectors (X, Y) with 4 driver channels each
|
||||
static const uint8_t X1 = 35;
|
||||
static const uint8_t X2 = 36;
|
||||
static const uint8_t X3 = 37;
|
||||
static const uint8_t X4 = 38;
|
||||
static const uint8_t Y1 = 4;
|
||||
static const uint8_t Y2 = 5;
|
||||
static const uint8_t Y3 = 6;
|
||||
static const uint8_t Y4 = 7;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 8;
|
||||
static const uint8_t A4 = 9;
|
||||
static const uint8_t A5 = 10;
|
||||
static const uint8_t A6 = 11;
|
||||
static const uint8_t A7 = 12;
|
||||
static const uint8_t A8 = 13;
|
||||
static const uint8_t A9 = 14;
|
||||
static const uint8_t A10 = 15;
|
||||
static const uint8_t A11 = 16;
|
||||
static const uint8_t A12 = 17;
|
||||
static const uint8_t A13 = 18;
|
||||
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T8 = 8;
|
||||
static const uint8_t T9 = 9;
|
||||
static const uint8_t T10 = 10;
|
||||
static const uint8_t T11 = 11;
|
||||
static const uint8_t T12 = 12;
|
||||
static const uint8_t T13 = 13;
|
||||
static const uint8_t T14 = 14;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
Loading…
Reference in a new issue