diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cd6ac87c16..4af4a2ef8d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,6 +284,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp + libraries/Zigbee/src/ep/ZigbeeSmartButton.cpp ) set(ARDUINO_LIBRARY_BLE_SRCS diff --git a/libraries/Zigbee/examples/Zigbee_SmartButton/README.md b/libraries/Zigbee/examples/Zigbee_SmartButton/README.md new file mode 100644 index 00000000000..b70f57d6e89 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_SmartButton/README.md @@ -0,0 +1,68 @@ +# Arduino-ESP32 Zigbee On/Off Light Switch Example + +This example shows how to configure Zigbee Coordinator and use it as a Home Automation (HA) on/off light switch. + +# 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 end device (loaded with Zigbee_On_Off_Light example). +* A USB cable for power supply and programming. +* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee coordinator and upload the Zigbee_On_Off_Switch example. + +### Configure the Project + +Set the Button Switch GPIO by changing the `GPIO_INPUT_IO_TOGGLE_SWITCH` 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 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 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) diff --git a/libraries/Zigbee/examples/Zigbee_SmartButton/Zigbee_SmartButton.ino b/libraries/Zigbee/examples/Zigbee_SmartButton/Zigbee_SmartButton.ino new file mode 100644 index 00000000000..4e8f7ca69ed --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_SmartButton/Zigbee_SmartButton.ino @@ -0,0 +1,193 @@ +// Copyright 2024 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 light switch. + * + * The example demonstrates how to use Zigbee library to control a light bulb. + * The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator (Switch). + * Button switch and Zigbee runs in separate tasks. + * + * 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 Smart button () configuration */ +#define SWITCH_1_ENDPOINT_NUMBER 5 // SINGLE PRESS +#define SWITCH_2_ENDPOINT_NUMBER 6 // DOUBLE PRESS +#define SWITCH_3_ENDPOINT_NUMBER 7 // LONG PRESS + +uint8_t button = BOOT_PIN; + +// Timing thresholds (in milliseconds) +const uint16_t debounceTime = 100; // Debounce time +const uint16_t doublePressTime = 200; // Max time between double presses +const uint16_t longPressTime = 1000; // Time to detect a long press +const uint16_t rebootPressTime = 5000; // Time to detect a long press for reboot + +// Variables to track button state +int buttonState = HIGH; // Current state of the button (HIGH means not pressed) +int lastButtonState = HIGH; // Previous state of the button + +// Timing variables +unsigned long lastDebounceTime = 0; // Last time the button state changed +unsigned long firstPressTime = 0; // Time of the first press in a sequence +unsigned long buttonPressStart = 0; // Time when the button was first pressed down + +// Flags +bool singlePressDetected = false; +bool doublePressDetected = false; +bool longPressDetected = false; +bool rebootPressDetected = false; +bool longPressHandled = false; +bool doublePressHandled = false; + +ZigbeeSmartButton zbSmartButton1 = ZigbeeSmartButton(SWITCH_1_ENDPOINT_NUMBER); +ZigbeeSmartButton zbSmartButton2 = ZigbeeSmartButton(SWITCH_2_ENDPOINT_NUMBER); +ZigbeeSmartButton zbSmartButton3 = ZigbeeSmartButton(SWITCH_3_ENDPOINT_NUMBER); + +/********************* Arduino functions **************************/ +void setup() { + Serial.begin(115200); + + // Configure button switch + pinMode(button, INPUT_PULLUP); + + //Optional: set Zigbee device name and model + zbSmartButton1.setManufacturerAndModel("Espressif", "ZigbeeSmartButton"); + zbSmartButton2.setManufacturerAndModel("Espressif", "ZigbeeSmartButton"); + zbSmartButton3.setManufacturerAndModel("Espressif", "ZigbeeSmartButton"); + + //Add endpoints to Zigbee Core + Zigbee.addEndpoint(&zbSmartButton1); + Zigbee.addEndpoint(&zbSmartButton2); + Zigbee.addEndpoint(&zbSmartButton3); + + // // When all EPs are registered, start Zigbee with ZIGBEE_COORDINATOR mode + // if (!Zigbee.begin()) { + // Serial.println("Zigbee failed to start!"); + // Serial.println("Short press the button to reboot, long press to factory reset Zigbee."); + // // on button press reboot, on long press factory reset + // while(true){ + // if (digitalRead(button) == LOW) { + // delay(100); + // int startTime = millis(); + // while (digitalRead(button) == LOW) { + // delay(50); + // if ((millis() - startTime) > 3000) { + // Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + // delay(1000); + // Zigbee.factoryReset(); + // } + // } + // ESP.restart(); + // } + // delay(1); + // } + // } + // Serial.println("Connecting to network"); + // while (!Zigbee.connected()) { + // Serial.print("."); + // delay(100); + // } + // Serial.println(); +} + +void loop() { + // Read the button state + int reading = digitalRead(button); + + // Debounce handling + if (reading != lastButtonState) { + lastDebounceTime = millis(); + } + + if ((millis() - lastDebounceTime) > debounceTime) { + // If the state has stabilized, update button state + if (reading != buttonState) { + buttonState = reading; + + // Detect press events + if (buttonState == LOW) { + buttonPressStart = millis(); + + if (millis() - firstPressTime <= doublePressTime && firstPressTime != 0) { + doublePressDetected = true; + firstPressTime = 0; // Reset for the next sequence + } else { + firstPressTime = millis(); + } + } else { + // Button released + unsigned long pressDuration = millis() - buttonPressStart; + + if (!longPressHandled && pressDuration >= rebootPressTime) { + rebootPressDetected = true; + } else if (!longPressHandled && pressDuration >= longPressTime) { + longPressDetected = true; + } else if (!doublePressHandled && !doublePressDetected && pressDuration < longPressTime) { + singlePressDetected = true; + } + + longPressHandled = false; // Reset for next press + + // // If button is pressed again in a short time, it's a double press so reset the single press flag + // if (singlePressDetected){ + // // wait for the double press time window + + // } + } + } + } + + // Handle events + if (singlePressDetected && !doublePressDetected) { + Serial.println("Single press detected"); + singlePressDetected = false; + // zbSmartButton1.toggle(); + } + + if (doublePressDetected) { + Serial.println("Double press detected"); + doublePressDetected = false; + doublePressHandled = true; + // zbSmartButton2.toggle(); + } + + if (longPressDetected) { + Serial.println("Long press detected"); + longPressDetected = false; + longPressHandled = true; + // zbSmartButton3.toggle(); + } + + if (rebootPressDetected) { + Serial.println("Reboot press detected. Rebooting..."); + rebootPressDetected = false; + longPressHandled = true; + // Zigbee.factoryReset(); + } + + lastButtonState = reading; +} diff --git a/libraries/Zigbee/examples/Zigbee_SmartButton/ci.json b/libraries/Zigbee/examples/Zigbee_SmartButton/ci.json new file mode 100644 index 00000000000..e79a477da11 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_SmartButton/ci.json @@ -0,0 +1,6 @@ +{ + "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", + "requires": [ + "CONFIG_SOC_IEEE802154_SUPPORTED=y" + ] +} diff --git a/libraries/Zigbee/src/Zigbee.h b/libraries/Zigbee/src/Zigbee.h index 727761c900d..dd8290deb63 100644 --- a/libraries/Zigbee/src/Zigbee.h +++ b/libraries/Zigbee/src/Zigbee.h @@ -17,3 +17,4 @@ #include "ep/ZigbeeFlowSensor.h" #include "ep/ZigbeeOccupancySensor.h" #include "ep/ZigbeeCarbonDioxideSensor.h" +#include "ep/ZigbeeSmartButton.h" diff --git a/libraries/Zigbee/src/ep/ZigbeeSmartButton.cpp b/libraries/Zigbee/src/ep/ZigbeeSmartButton.cpp new file mode 100644 index 00000000000..18a87ac4b31 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeSmartButton.cpp @@ -0,0 +1,24 @@ +#include "ZigbeeSmartButton.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +ZigbeeSmartButton::ZigbeeSmartButton(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID; + + 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); + + _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID, .app_device_version = 0}; +} + +void ZigbeeSmartButton::toggle() { + esp_zb_zcl_on_off_cmd_t cmd_req; + cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; + cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID; + log_v("Sending 'Smart button (ep: %d) toggle' command", _endpoint); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_on_off_cmd_req(&cmd_req); + esp_zb_lock_release(); +} + +#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeSmartButton.h b/libraries/Zigbee/src/ep/ZigbeeSmartButton.h new file mode 100644 index 00000000000..9f74f851182 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeSmartButton.h @@ -0,0 +1,20 @@ +/* Class of Zigbee On/Off Switch endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +class ZigbeeSmartButton : public ZigbeeEP { +public: + ZigbeeSmartButton(uint8_t endpoint); + ~ZigbeeSmartButton(); + + void toggle(); +}; + +#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED