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 setCoveingType() 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+
42+ #define ZIGBEE_COVERING_ENDPOINT 10
43+ #define BUTTON_PIN 9 // ESP32-C6/H2 Boot button
44+
45+ #define MAX_LIFT 200 // centimeters from open position (0-900)
46+ #define MIN_LIFT 0
47+
48+ #define MAX_TILT 40 // centimeters from open position (0-900)
49+ #define MIN_TILT 0
50+
51+ uint16_t currentLift = MAX_LIFT;
52+ uint8_t currentLiftPercentage = 100 ;
53+
54+ uint16_t currentTilt = MAX_TILT;
55+ uint8_t currentTiltPercentage = 100 ;
56+
57+ ZigbeeWindowCovering zbCovering = ZigbeeWindowCovering(ZIGBEE_COVERING_ENDPOINT);
58+
59+ void setup () {
60+ Serial.begin (115200 );
61+
62+ // Init button for factory reset
63+ pinMode (BUTTON_PIN, INPUT_PULLUP);
64+
65+ // Optional: set Zigbee device name and model
66+ zbCovering.setManufacturerAndModel (" Espressif" , " WindowBlinds" );
67+
68+ // Set proper covering type, it defines which attributes are available
69+ zbCovering.setCoveringType (BLIND_LIFT_AND_TILT);
70+
71+ // Set configuration: operational, online, not commands_reversed, lift / tilt closed_loop, lift / tilt encoder_controlled
72+ zbCovering.setConfigStatus (true , true , false , true , true , true , true );
73+
74+ // Set mode: not motor_reversed, calibration_mode, not maintenance_mode, not leds_on
75+ zbCovering.setMode (false , true , false , false );
76+
77+ // Set limits of motion
78+ zbCovering.setLimits (MIN_LIFT, MAX_LIFT, MIN_TILT, MAX_TILT);
79+
80+ // Set callback function for open, close, filt and tilt change, stop
81+ zbCovering.onOpen (fullOpen);
82+ zbCovering.onClose (fullClose);
83+ zbCovering.onGoToLiftPercentage (goToLiftPercentage);
84+ zbCovering.onGoToTiltPercentage (goToTiltPercentage);
85+ zbCovering.onStop (stopMotor);
86+
87+ // Add endpoint to Zigbee Core
88+ Serial.println (" Adding ZigbeeWindowCovering endpoint to Zigbee Core" );
89+ Zigbee.addEndpoint (&zbCovering);
90+
91+ // When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
92+ Serial.println (" Calling Zigbee.begin()" );
93+ if (!Zigbee.begin ()) {
94+ Serial.println (" Zigbee failed to start!" );
95+ Serial.println (" Rebooting..." );
96+ ESP.restart ();
97+ }
98+ Serial.println (" Connecting to network" );
99+ while (!Zigbee.connected ()) {
100+ Serial.print (" ." );
101+ delay (100 );
102+ }
103+ Serial.println ();
104+
105+ // Set initial position
106+ zbCovering.setLiftPercentage (currentLiftPercentage);
107+ zbCovering.setTiltPercentage (currentTiltPercentage);
108+ }
109+
110+ void loop () {
111+ // Checking button for factory reset
112+ if (digitalRead (BUTTON_PIN) == LOW) { // Push button pressed
113+ // Key debounce handling
114+ delay (100 );
115+ int startTime = millis ();
116+ while (digitalRead (BUTTON_PIN) == LOW) {
117+ delay (50 );
118+ if ((millis () - startTime) > 3000 ) {
119+ // If key pressed for more than 3secs, factory reset Zigbee and reboot
120+ Serial.printf (" Resetting Zigbee to factory settings, reboot.\n " );
121+ Zigbee.factoryReset ();
122+ delay (30000 );
123+ }
124+ }
125+ // Manual lift controll simulation by pressing button
126+ manualControl ();
127+ }
128+ delay (500 );
129+ }
130+
131+ void fullOpen (){
132+ /* This is where you would trigger your motor to go to full open state, currentLift should
133+ be updated until full open has been reached in order to provide feedback to controller of actual position
134+ The stop can be always called, so the movement can be stopped at any time */
135+
136+ // Our cover updates instantly!
137+ currentLift = MAX_LIFT;
138+ currentLiftPercentage = 100 ;
139+ Serial.println (" Opening cover" );
140+ // Update the current position
141+ zbCovering.setLiftPercentage (currentLiftPercentage);
142+ }
143+
144+ void fullClose (){
145+ /* This is where you would trigger your motor to go to full close state, currentLift should
146+ be updated until full close has been reached in order to provide feedback to controller of actual position
147+ The stop can be always called, so the movement can be stopped at any time */
148+
149+ // Our cover updates instantly!
150+ currentLift = MIN_LIFT;
151+ currentLiftPercentage = 0 ;
152+ Serial.println (" Closing cover" );
153+ // Update the current position
154+ zbCovering.setLiftPercentage (currentLiftPercentage);
155+ }
156+
157+ void goToLiftPercentage (uint8_t liftPercentage) {
158+ /* This is where you would trigger your motor to go towards liftPercentage, currentLift should
159+ be updated until liftPercentage has been reached in order to provide feedback to controller */
160+
161+ // Our simulated cover updates instantly!
162+ currentLift = (liftPercentage * MAX_LIFT) / 100 ;
163+ currentLiftPercentage = liftPercentage;
164+ Serial.printf (" New requsted lift from Zigbee: %d (%d)\n " , currentLift, liftPercentage);
165+
166+ // Update the current position
167+ zbCovering.setLiftPercentage (currentLiftPercentage); // or setLiftPosition()
168+ }
169+
170+ void goToTiltPercentage (uint8_t tiltPercentage) {
171+ /* This is where you would trigger your motor to go towards tiltPercentage, currentTilt should
172+ be updated until tiltPercentage has been reached in order to provide feedback to controller */
173+
174+ // Our simulated cover updates instantly!
175+ currentTilt = (tiltPercentage * MAX_TILT) / 100 ;
176+ currentTiltPercentage = tiltPercentage;
177+ Serial.printf (" New requsted tilt from Zigbee: %d (%d)\n " , currentTilt, tiltPercentage);
178+
179+ // Update the current position
180+ zbCovering.setTiltPercentage (currentTiltPercentage); // or setTiltPosition()
181+ }
182+
183+
184+ void stopMotor () {
185+ // Motor can be stopped while moving cover toward current target, when stopped the actual position should be updated
186+ Serial.println (" Stopping motor" );
187+ // Update the current position of both lift and tilt
188+ zbCovering.setLiftPercentage (currentLiftPercentage);
189+ zbCovering.setTiltPercentage (currentTiltPercentage);
190+ }
191+
192+ void manualControl () {
193+ // Simulate lift percentage move by increasing it by 20% each time
194+ currentLiftPercentage += 20 ;
195+ if (currentLiftPercentage > 100 ) {
196+ currentLiftPercentage = 0 ;
197+ }
198+ zbCovering.setLiftPercentage (currentLiftPercentage);
199+ // Also setLiftPosition() can be used to set the exact position instead of percentage
200+ }
0 commit comments