|
| 1 | +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +/** |
| 16 | + * @brief This example demonstrates Zigbee Window Covering. |
| 17 | + * |
| 18 | + * The example demonstrates how to use Zigbee library to create a end device window covering device. |
| 19 | + * The window covering is a Zigbee end device, which is moving the blinds (lift+tilt) and reporting |
| 20 | + * its current position to the Zigbee network. |
| 21 | + * |
| 22 | + * Use setCoveringType() to set the type of covering (blind, shade, etc.). |
| 23 | + * |
| 24 | + * The example also demonstrates how to use the button to manually control the lift position. |
| 25 | + * |
| 26 | + * Proper Zigbee mode must be selected in Tools->Zigbee mode |
| 27 | + * and also the correct partition scheme must be selected in Tools->Partition Scheme. |
| 28 | + * |
| 29 | + * Please check the README.md for instructions and more detailed description. |
| 30 | + * |
| 31 | + * Created by hennikul and Jan Procházka (https://github.com/P-R-O-C-H-Y/) |
| 32 | + */ |
| 33 | + |
| 34 | +#ifndef ZIGBEE_MODE_ED |
| 35 | +#error "Zigbee end device mode is not selected in Tools->Zigbee mode" |
| 36 | +#endif |
| 37 | + |
| 38 | +#include "ZigbeeCore.h" |
| 39 | +#include "ep/ZigbeeWindowCovering.h" |
| 40 | + |
| 41 | +#define ZIGBEE_COVERING_ENDPOINT 10 |
| 42 | +#define BUTTON_PIN 9 // ESP32-C6/H2 Boot button |
| 43 | + |
| 44 | +#define MAX_LIFT 200 // centimeters from open position (0-900) |
| 45 | +#define MIN_LIFT 0 |
| 46 | + |
| 47 | +#define MAX_TILT 40 // centimeters from open position (0-900) |
| 48 | +#define MIN_TILT 0 |
| 49 | + |
| 50 | +uint16_t currentLift = MAX_LIFT; |
| 51 | +uint8_t currentLiftPercentage = 100; |
| 52 | + |
| 53 | +uint16_t currentTilt = MAX_TILT; |
| 54 | +uint8_t currentTiltPercentage = 100; |
| 55 | + |
| 56 | +ZigbeeWindowCovering zbCovering = ZigbeeWindowCovering(ZIGBEE_COVERING_ENDPOINT); |
| 57 | + |
| 58 | +void setup() { |
| 59 | + Serial.begin(115200); |
| 60 | + |
| 61 | + // Init button for factory reset |
| 62 | + pinMode(BUTTON_PIN, INPUT_PULLUP); |
| 63 | + |
| 64 | + // Optional: set Zigbee device name and model |
| 65 | + zbCovering.setManufacturerAndModel("Espressif", "WindowBlinds"); |
| 66 | + |
| 67 | + // Set proper covering type, it defines which attributes are available |
| 68 | + zbCovering.setCoveringType(BLIND_LIFT_AND_TILT); |
| 69 | + |
| 70 | + // Set configuration: operational, online, not commands_reversed, lift / tilt closed_loop, lift / tilt encoder_controlled |
| 71 | + zbCovering.setConfigStatus(true, true, false, true, true, true, true); |
| 72 | + |
| 73 | + // Set mode: not motor_reversed, calibration_mode, not maintenance_mode, not leds_on |
| 74 | + zbCovering.setMode(false, true, false, false); |
| 75 | + |
| 76 | + // Set limits of motion |
| 77 | + zbCovering.setLimits(MIN_LIFT, MAX_LIFT, MIN_TILT, MAX_TILT); |
| 78 | + |
| 79 | + // Set callback function for open, close, filt and tilt change, stop |
| 80 | + zbCovering.onOpen(fullOpen); |
| 81 | + zbCovering.onClose(fullClose); |
| 82 | + zbCovering.onGoToLiftPercentage(goToLiftPercentage); |
| 83 | + zbCovering.onGoToTiltPercentage(goToTiltPercentage); |
| 84 | + zbCovering.onStop(stopMotor); |
| 85 | + |
| 86 | + // Add endpoint to Zigbee Core |
| 87 | + Serial.println("Adding ZigbeeWindowCovering endpoint to Zigbee Core"); |
| 88 | + Zigbee.addEndpoint(&zbCovering); |
| 89 | + |
| 90 | + // When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE |
| 91 | + Serial.println("Calling Zigbee.begin()"); |
| 92 | + if (!Zigbee.begin()) { |
| 93 | + Serial.println("Zigbee failed to start!"); |
| 94 | + Serial.println("Rebooting..."); |
| 95 | + ESP.restart(); |
| 96 | + } |
| 97 | + Serial.println("Connecting to network"); |
| 98 | + while (!Zigbee.connected()) { |
| 99 | + Serial.print("."); |
| 100 | + delay(100); |
| 101 | + } |
| 102 | + Serial.println(); |
| 103 | + |
| 104 | + // Set initial position |
| 105 | + zbCovering.setLiftPercentage(currentLiftPercentage); |
| 106 | + zbCovering.setTiltPercentage(currentTiltPercentage); |
| 107 | +} |
| 108 | + |
| 109 | +void loop() { |
| 110 | + // Checking button for factory reset |
| 111 | + if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed |
| 112 | + // Key debounce handling |
| 113 | + delay(100); |
| 114 | + int startTime = millis(); |
| 115 | + while (digitalRead(BUTTON_PIN) == LOW) { |
| 116 | + delay(50); |
| 117 | + if ((millis() - startTime) > 3000) { |
| 118 | + // If key pressed for more than 3secs, factory reset Zigbee and reboot |
| 119 | + Serial.printf("Resetting Zigbee to factory settings, reboot.\n"); |
| 120 | + Zigbee.factoryReset(); |
| 121 | + delay(30000); |
| 122 | + } |
| 123 | + } |
| 124 | + // Manual lift control simulation by pressing button |
| 125 | + manualControl(); |
| 126 | + } |
| 127 | + delay(500); |
| 128 | +} |
| 129 | + |
| 130 | +void fullOpen() { |
| 131 | + /* This is where you would trigger your motor to go to full open state, currentLift should |
| 132 | + be updated until full open has been reached in order to provide feedback to controller of actual position |
| 133 | + The stop can be always called, so the movement can be stopped at any time */ |
| 134 | + |
| 135 | + // Our cover updates instantly! |
| 136 | + currentLift = MAX_LIFT; |
| 137 | + currentLiftPercentage = 100; |
| 138 | + Serial.println("Opening cover"); |
| 139 | + // Update the current position |
| 140 | + zbCovering.setLiftPercentage(currentLiftPercentage); |
| 141 | +} |
| 142 | + |
| 143 | +void fullClose() { |
| 144 | + /* This is where you would trigger your motor to go to full close state, currentLift should |
| 145 | + be updated until full close has been reached in order to provide feedback to controller of actual position |
| 146 | + The stop can be always called, so the movement can be stopped at any time */ |
| 147 | + |
| 148 | + // Our cover updates instantly! |
| 149 | + currentLift = MIN_LIFT; |
| 150 | + currentLiftPercentage = 0; |
| 151 | + Serial.println("Closing cover"); |
| 152 | + // Update the current position |
| 153 | + zbCovering.setLiftPercentage(currentLiftPercentage); |
| 154 | +} |
| 155 | + |
| 156 | +void goToLiftPercentage(uint8_t liftPercentage) { |
| 157 | + /* This is where you would trigger your motor to go towards liftPercentage, currentLift should |
| 158 | + be updated until liftPercentage has been reached in order to provide feedback to controller */ |
| 159 | + |
| 160 | + // Our simulated cover updates instantly! |
| 161 | + currentLift = (liftPercentage * MAX_LIFT) / 100; |
| 162 | + currentLiftPercentage = liftPercentage; |
| 163 | + Serial.printf("New requested lift from Zigbee: %d (%d)\n", currentLift, liftPercentage); |
| 164 | + |
| 165 | + // Update the current position |
| 166 | + zbCovering.setLiftPercentage(currentLiftPercentage); //or setLiftPosition() |
| 167 | +} |
| 168 | + |
| 169 | +void goToTiltPercentage(uint8_t tiltPercentage) { |
| 170 | + /* This is where you would trigger your motor to go towards tiltPercentage, currentTilt should |
| 171 | + be updated until tiltPercentage has been reached in order to provide feedback to controller */ |
| 172 | + |
| 173 | + // Our simulated cover updates instantly! |
| 174 | + currentTilt = (tiltPercentage * MAX_TILT) / 100; |
| 175 | + currentTiltPercentage = tiltPercentage; |
| 176 | + Serial.printf("New requested tilt from Zigbee: %d (%d)\n", currentTilt, tiltPercentage); |
| 177 | + |
| 178 | + // Update the current position |
| 179 | + zbCovering.setTiltPercentage(currentTiltPercentage); //or setTiltPosition() |
| 180 | +} |
| 181 | + |
| 182 | +void stopMotor() { |
| 183 | + // Motor can be stopped while moving cover toward current target, when stopped the actual position should be updated |
| 184 | + Serial.println("Stopping motor"); |
| 185 | + // Update the current position of both lift and tilt |
| 186 | + zbCovering.setLiftPercentage(currentLiftPercentage); |
| 187 | + zbCovering.setTiltPercentage(currentTiltPercentage); |
| 188 | +} |
| 189 | + |
| 190 | +void manualControl() { |
| 191 | + // Simulate lift percentage move by increasing it by 20% each time |
| 192 | + currentLiftPercentage += 20; |
| 193 | + if (currentLiftPercentage > 100) { |
| 194 | + currentLiftPercentage = 0; |
| 195 | + } |
| 196 | + zbCovering.setLiftPercentage(currentLiftPercentage); |
| 197 | + // Also setLiftPosition() can be used to set the exact position instead of percentage |
| 198 | +} |
0 commit comments