MatterWindowCovering
About
The MatterWindowCovering class provides a window covering endpoint for Matter networks. This endpoint implements the Matter window covering standard for motorized blinds, shades, and other window coverings with lift and tilt control.
Features: * Lift position and percentage control (0-100%) * Tilt position and percentage control (0-100%) * Multiple window covering types support * Callback support for open, close, lift, tilt, and stop commands * Integration with Apple HomeKit, Amazon Alexa, and Google Home * Matter standard compliance
Supported Window Covering Types:
* ROLLERSHADE - Lift support
* ROLLERSHADE_2_MOTOR - Lift support
* ROLLERSHADE_EXTERIOR - Lift support
* ROLLERSHADE_EXTERIOR_2_MOTOR - Lift support
* DRAPERY - Lift support
* AWNING - Lift support
* SHUTTER - Tilt support
* BLIND_TILT_ONLY - Tilt support
* BLIND_LIFT_AND_TILT - Lift and Tilt support
* PROJECTOR_SCREEN - Lift support
Use Cases: * Motorized blinds * Automated shades * Smart window coverings * Projector screens * Awnings and drapes
API Reference
Constructor
MatterWindowCovering
Creates a new Matter window covering endpoint.
MatterWindowCovering();
Initialization
begin
Initializes the Matter window covering endpoint with optional initial positions and covering type.
bool begin(uint8_t liftPercent = 100, uint8_t tiltPercent = 0, WindowCoveringType_t coveringType = ROLLERSHADE);
liftPercent- Initial lift percentage (0-100, default: 100 = fully open)tiltPercent- Initial tilt percentage (0-100, default: 0)coveringType- Window covering type (default: ROLLERSHADE). This determines which features (lift, tilt, or both) are enabled.
This function will return true if successful, false otherwise.
Note: Lift percentage 0 means fully closed, 100 means fully open. Tilt percentage 0 means fully closed, 100 means fully open. The covering type must be specified during initialization to ensure the correct features (lift and/or tilt) are enabled.
end
Stops processing Matter window covering events.
void end();
Lift Position Control
setLiftPosition
Sets the window covering lift position.
bool setLiftPosition(uint16_t liftPosition);
liftPosition- Lift position value
This function will return true if successful, false otherwise.
getLiftPosition
Gets the current lift position.
uint16_t getLiftPosition();
This function will return the current lift position.
setLiftPercentage
Sets the window covering lift position as a percentage. This method updates the CurrentPositionLiftPercent100ths attribute, which reflects the device’s actual position. The TargetPositionLiftPercent100ths attribute is set by Matter commands/apps when a new target is requested.
bool setLiftPercentage(uint8_t liftPercent);
liftPercent- Lift percentage (0-100, where 0 is fully closed, 100 is fully open)
This function will return true if successful, false otherwise.
Note: When the device reaches the target position, call setOperationalState(LIFT, STALL) to indicate that movement is complete.
getLiftPercentage
Gets the current lift percentage.
uint8_t getLiftPercentage();
This function will return the current lift percentage (0-100).
Tilt Position Control
setTiltPosition
Sets the window covering tilt position. Note that tilt is a rotation, not a linear measurement. This method converts the absolute position to percentage using the installed limits.
bool setTiltPosition(uint16_t tiltPosition);
tiltPosition- Tilt position value (absolute value for conversion, not a physical unit)
This function will return true if successful, false otherwise.
getTiltPosition
Gets the current tilt position. Note that tilt is a rotation, not a linear measurement.
uint16_t getTiltPosition();
This function will return the current tilt position (absolute value for conversion, not a physical unit).
setTiltPercentage
Sets the window covering tilt position as a percentage. This method updates the CurrentPositionTiltPercent100ths attribute, which reflects the device’s actual position. The TargetPositionTiltPercent100ths attribute is set by Matter commands/apps when a new target is requested.
bool setTiltPercentage(uint8_t tiltPercent);
tiltPercent- Tilt percentage (0-100, where 0 is fully closed, 100 is fully open)
This function will return true if successful, false otherwise.
Note: When the device reaches the target position, call setOperationalState(TILT, STALL) to indicate that movement is complete.
getTiltPercentage
Gets the current tilt percentage.
uint8_t getTiltPercentage();
This function will return the current tilt percentage (0-100).
Window Covering Type
setCoveringType
Sets the window covering type.
bool setCoveringType(WindowCoveringType_t coveringType);
coveringType- Window covering type (see Window Covering Types enum)
This function will return true if successful, false otherwise.
getCoveringType
Gets the current window covering type.
WindowCoveringType_t getCoveringType();
This function will return the current window covering type.
Installed Limit Control
setInstalledOpenLimitLift
Sets the installed open limit for lift (centimeters). This defines the physical position when the window covering is fully open.
bool setInstalledOpenLimitLift(uint16_t openLimit);
openLimit- Open limit position (centimeters)
This function will return true if successful, false otherwise.
getInstalledOpenLimitLift
Gets the installed open limit for lift.
uint16_t getInstalledOpenLimitLift();
This function will return the installed open limit for lift (centimeters).
setInstalledClosedLimitLift
Sets the installed closed limit for lift (centimeters). This defines the physical position when the window covering is fully closed.
bool setInstalledClosedLimitLift(uint16_t closedLimit);
closedLimit- Closed limit position (centimeters)
This function will return true if successful, false otherwise.
getInstalledClosedLimitLift
Gets the installed closed limit for lift.
uint16_t getInstalledClosedLimitLift();
This function will return the installed closed limit for lift (centimeters).
setInstalledOpenLimitTilt
Sets the installed open limit for tilt (absolute value for conversion, not a physical unit). This is used for converting between absolute position and percentage.
bool setInstalledOpenLimitTilt(uint16_t openLimit);
openLimit- Open limit absolute value
This function will return true if successful, false otherwise.
Note: Tilt is a rotation, not a linear measurement. These limits are used for position conversion only.
getInstalledOpenLimitTilt
Gets the installed open limit for tilt.
uint16_t getInstalledOpenLimitTilt();
This function will return the installed open limit for tilt (absolute value).
setInstalledClosedLimitTilt
Sets the installed closed limit for tilt (absolute value for conversion, not a physical unit). This is used for converting between absolute position and percentage.
bool setInstalledClosedLimitTilt(uint16_t closedLimit);
closedLimit- Closed limit absolute value
This function will return true if successful, false otherwise.
Note: Tilt is a rotation, not a linear measurement. These limits are used for position conversion only.
getInstalledClosedLimitTilt
Gets the installed closed limit for tilt.
uint16_t getInstalledClosedLimitTilt();
This function will return the installed closed limit for tilt (absolute value).
Target Position Control
setTargetLiftPercent100ths
Sets the target lift position in percent100ths (0-10000, where 0 is fully closed, 10000 is fully open).
bool setTargetLiftPercent100ths(uint16_t liftPercent100ths);
liftPercent100ths- Target lift position in percent100ths (0-10000)
This function will return true if successful, false otherwise.
Note: This sets the target position that the device should move towards. The actual position should be updated using setLiftPercentage().
getTargetLiftPercent100ths
Gets the current target lift position in percent100ths.
uint16_t getTargetLiftPercent100ths();
This function will return the current target lift position in percent100ths (0-10000).
setTargetTiltPercent100ths
Sets the target tilt position in percent100ths (0-10000, where 0 is fully closed, 10000 is fully open).
bool setTargetTiltPercent100ths(uint16_t tiltPercent100ths);
tiltPercent100ths- Target tilt position in percent100ths (0-10000)
This function will return true if successful, false otherwise.
Note: This sets the target position that the device should move towards. The actual position should be updated using setTiltPercentage().
getTargetTiltPercent100ths
Gets the current target tilt position in percent100ths.
uint16_t getTargetTiltPercent100ths();
This function will return the current target tilt position in percent100ths (0-10000).
Operational Status Control
setOperationalStatus
Sets the full operational status bitmap.
bool setOperationalStatus(uint8_t operationalStatus);
operationalStatus- Full operational status bitmap value
This function will return true if successful, false otherwise.
Note: It is recommended to use setOperationalState() to set individual field states instead of setting the full bitmap directly.
getOperationalStatus
Gets the full operational status bitmap.
uint8_t getOperationalStatus();
This function will return the current operational status bitmap value.
setOperationalState
Sets the operational state for a specific field (LIFT or TILT). The GLOBAL field is automatically updated based on priority (LIFT > TILT).
bool setOperationalState(OperationalStatusField_t field, OperationalState_t state);
field- Field to set (LIFTorTILT).GLOBALcannot be set directly.state- Operational state (STALL,MOVING_UP_OR_OPEN, orMOVING_DOWN_OR_CLOSE)
This function will return true if successful, false otherwise.
Note: Only LIFT and TILT fields can be set directly. The GLOBAL field is automatically updated based on the active field (LIFT has priority over TILT).
getOperationalState
Gets the operational state for a specific field.
OperationalState_t getOperationalState(OperationalStatusField_t field);
field- Field to get (GLOBAL,LIFT, orTILT)
This function will return the operational state for the specified field (STALL, MOVING_UP_OR_OPEN, or MOVING_DOWN_OR_CLOSE).
Event Handling
The MatterWindowCovering class automatically detects Matter commands and calls the appropriate callbacks when registered. There are two types of callbacks:
Target Position Callbacks (triggered when TargetPosition attributes change):
* onOpen() - called when UpOrOpen command is received (sets target to 0% = fully open)
* onClose() - called when DownOrClose command is received (sets target to 100% = fully closed)
* onStop() - called when StopMotion command is received (sets target to current position)
* onGoToLiftPercentage() - called when TargetPositionLiftPercent100ths changes (from any command, setTargetLiftPercent100ths(), or direct attribute write)
* onGoToTiltPercentage() - called when TargetPositionTiltPercent100ths changes (from any command, setTargetTiltPercent100ths(), or direct attribute write)
Current Position Callback (triggered when CurrentPosition attributes change):
* onChange() - called when CurrentPositionLiftPercent100ths or CurrentPositionTiltPercent100ths change (after setLiftPercentage()/setTiltPercentage() are called or when a Matter controller updates these attributes directly)
Important: onChange() is not automatically called when Matter commands are executed. Commands modify TargetPosition, not CurrentPosition. To trigger onChange(), your onGoToLiftPercentage() or onGoToTiltPercentage() callback must call setLiftPercentage() or setTiltPercentage() when the physical device actually moves.
Note: All callbacks are optional. If a specific callback is not registered, only the generic onGoToLiftPercentage() or onGoToTiltPercentage() callbacks will be called (if registered).
onOpen
Sets a callback function to be called when the UpOrOpen command is received from a Matter controller. This command sets the target position to 0% (fully open).
void onOpen(EndPointOpenCB onChangeCB);
onChangeCB- Function to call whenUpOrOpencommand is received
The callback signature is:
bool onChangeCallback();
onClose
Sets a callback function to be called when the DownOrClose command is received from a Matter controller. This command sets the target position to 100% (fully closed).
void onClose(EndPointCloseCB onChangeCB);
onChangeCB- Function to call whenDownOrClosecommand is received
The callback signature is:
bool onChangeCallback();
onGoToLiftPercentage
Sets a callback function to be called when TargetPositionLiftPercent100ths changes. This is triggered by:
* Matter commands: UpOrOpen, DownOrClose, StopMotion, GoToLiftPercentage
* Calling setTargetLiftPercent100ths()
* Direct attribute writes to TargetPositionLiftPercent100ths
This callback is always called when the target lift position changes, regardless of which command or method was used to change it.
Note: This callback receives the target position. To update the current position (which triggers onChange()), call setLiftPercentage() when the physical device actually moves.
void onGoToLiftPercentage(EndPointLiftCB onChangeCB);
onChangeCB- Function to call when target lift percentage changes
The callback signature is:
bool onChangeCallback(uint8_t liftPercent);
liftPercent- Target lift percentage (0-100, where 0 is fully closed, 100 is fully open)
onGoToTiltPercentage
Sets a callback function to be called when TargetPositionTiltPercent100ths changes. This is triggered by:
* Matter commands: UpOrOpen, DownOrClose, StopMotion, GoToTiltPercentage
* Calling setTargetTiltPercent100ths()
* Direct attribute writes to TargetPositionTiltPercent100ths
This callback is always called when the target tilt position changes, regardless of which command or method was used to change it.
Note: This callback receives the target position. To update the current position (which triggers onChange()), call setTiltPercentage() when the physical device actually moves.
void onGoToTiltPercentage(EndPointTiltCB onChangeCB);
onChangeCB- Function to call when target tilt percentage changes
The callback signature is:
bool onChangeCallback(uint8_t tiltPercent);
tiltPercent- Target tilt percentage (0-100, where 0 is fully closed, 100 is fully open)
onStop
Sets a callback function to be called when the StopMotion command is received from a Matter controller. This command sets the target position to the current position, effectively stopping any movement.
void onStop(EndPointStopCB onChangeCB);
onChangeCB- Function to call whenStopMotioncommand is received
The callback signature is:
bool onChangeCallback();
onChange
Sets a callback function to be called when CurrentPositionLiftPercent100ths or CurrentPositionTiltPercent100ths attributes change. This is different from onGoToLiftPercentage() and onGoToTiltPercentage(), which are called when TargetPosition attributes change.
When ``onChange()`` is called:
* When CurrentPositionLiftPercent100ths changes (after setLiftPercentage() is called or when a Matter controller updates this attribute directly)
* When CurrentPositionTiltPercent100ths changes (after setTiltPercentage() is called or when a Matter controller updates this attribute directly)
Important: onChange() is not automatically called when Matter commands are executed. Commands modify TargetPosition attributes, which trigger onGoToLiftPercentage() or onGoToTiltPercentage() callbacks instead. To trigger onChange() after a command, your onGoToLiftPercentage() or onGoToTiltPercentage() callback must call setLiftPercentage() or setTiltPercentage() to update the CurrentPosition attributes when the physical device actually moves.
void onChange(EndPointCB onChangeCB);
onChangeCB- Function to call when current position attributes change
The callback signature is:
bool onChangeCallback(uint8_t liftPercent, uint8_t tiltPercent);
liftPercent- Current lift percentage (0-100)tiltPercent- Current tilt percentage (0-100)
updateAccessory
Updates the state of the window covering using the current Matter internal state.
void updateAccessory();
This function will call the registered callback with the current state.
Example
Window Covering
// 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.
// Matter Manager
#include <Matter.h>
#if !CONFIG_ENABLE_CHIPOBLE
// if the device can be commissioned using BLE, WiFi is not used - save flash space
#include <WiFi.h>
#endif
#include <Preferences.h>
// List of Matter Endpoints for this Node
// Window Covering Endpoint
MatterWindowCovering WindowBlinds;
// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network
#if !CONFIG_ENABLE_CHIPOBLE
// WiFi is manually set and started
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
const char *password = "your-password"; // Change this to your WiFi password
#endif
// it will keep last Lift & Tilt state stored, using Preferences
Preferences matterPref;
const char *liftPercentPrefKey = "LiftPercent";
const char *tiltPercentPrefKey = "TiltPercent";
// set your board USER BUTTON pin here
const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button.
// Button control
uint32_t button_time_stamp = 0; // debouncing control
bool button_state = false; // false = released | true = pressed
const uint32_t debounceTime = 250; // button debouncing time (ms)
const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission
// Window covering limits
// Lift limits in centimeters (physical position)
const uint16_t MAX_LIFT = 200; // Maximum lift position (fully open)
const uint16_t MIN_LIFT = 0; // Minimum lift position (fully closed)
// Tilt limits (absolute values for conversion, not physical units)
// Tilt is a rotation, not a linear measurement
const uint16_t MAX_TILT = 90; // Maximum tilt absolute value
const uint16_t MIN_TILT = 0; // Minimum tilt absolute value
// Current window covering state
// These will be initialized in setup() based on installed limits and saved percentages
uint16_t currentLift = 0; // Lift position in cm
uint8_t currentLiftPercent = 100;
uint8_t currentTiltPercent = 0; // Tilt rotation percentage (0-100%)
// Visualize window covering position using RGB LED
// Lift percentage controls brightness (0% = off, 100% = full brightness)
#ifdef RGB_BUILTIN
const uint8_t ledPin = RGB_BUILTIN;
#else
const uint8_t ledPin = 2; // Set your pin here if your board has not defined RGB_BUILTIN
#warning "Do not forget to set the RGB LED pin"
#endif
void visualizeWindowBlinds(uint8_t liftPercent, uint8_t tiltPercent) {
#ifdef RGB_BUILTIN
// Use RGB LED to visualize lift position (brightness) and tilt (color shift)
float brightness = (float)liftPercent / 100.0; // 0.0 to 1.0
// Tilt affects color: 0% = red, 100% = blue
uint8_t red = (uint8_t)(map(tiltPercent, 0, 100, 255, 0) * brightness);
uint8_t blue = (uint8_t)(map(tiltPercent, 0, 100, 0, 255) * brightness);
uint8_t green = 0;
rgbLedWrite(ledPin, red, green, blue);
#else
// For non-RGB boards, just use brightness
uint8_t brightnessValue = map(liftPercent, 0, 100, 0, 255);
analogWrite(ledPin, brightnessValue);
#endif
}
// Window Covering Callbacks
bool fullOpen() {
// This is where you would trigger your motor to go to full open state
// For simulation, we update instantly
uint16_t openLimit = WindowBlinds.getInstalledOpenLimitLift();
currentLift = openLimit;
currentLiftPercent = 100;
Serial.printf("Opening window covering to full open (position: %d cm)\r\n", currentLift);
// Update CurrentPosition to reflect actual position (setLiftPercentage now only updates CurrentPosition)
WindowBlinds.setLiftPercentage(currentLiftPercent);
// Set operational status to STALL when movement is complete
WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL);
// Store state
matterPref.putUChar(liftPercentPrefKey, currentLiftPercent);
return true;
}
bool fullClose() {
// This is where you would trigger your motor to go to full close state
// For simulation, we update instantly
uint16_t closedLimit = WindowBlinds.getInstalledClosedLimitLift();
currentLift = closedLimit;
currentLiftPercent = 0;
Serial.printf("Closing window covering to full close (position: %d cm)\r\n", currentLift);
// Update CurrentPosition to reflect actual position (setLiftPercentage now only updates CurrentPosition)
WindowBlinds.setLiftPercentage(currentLiftPercent);
// Set operational status to STALL when movement is complete
WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL);
// Store state
matterPref.putUChar(liftPercentPrefKey, currentLiftPercent);
return true;
}
bool goToLiftPercentage(uint8_t liftPercent) {
// update Lift operational state
if (liftPercent > currentLiftPercent) {
// Set operational status to OPEN
WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::MOVING_UP_OR_OPEN);
}
if (liftPercent < currentLiftPercent) {
// Set operational status to CLOSE
WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::MOVING_DOWN_OR_CLOSE);
}
// This is where you would trigger your motor to go towards liftPercent
// For simulation, we update instantly
// Calculate absolute position based on installed limits
uint16_t openLimit = WindowBlinds.getInstalledOpenLimitLift();
uint16_t closedLimit = WindowBlinds.getInstalledClosedLimitLift();
// Linear interpolation: 0% = openLimit, 100% = closedLimit
if (openLimit < closedLimit) {
currentLift = openLimit + ((closedLimit - openLimit) * liftPercent) / 100;
} else {
currentLift = openLimit - ((openLimit - closedLimit) * liftPercent) / 100;
}
currentLiftPercent = liftPercent;
Serial.printf("Moving lift to %d%% (position: %d cm)\r\n", currentLiftPercent, currentLift);
// Update CurrentPosition to reflect actual position (setLiftPercentage now only updates CurrentPosition)
WindowBlinds.setLiftPercentage(currentLiftPercent);
// Set operational status to STALL when movement is complete
WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL);
// Store state
matterPref.putUChar(liftPercentPrefKey, currentLiftPercent);
return true;
}
bool goToTiltPercentage(uint8_t tiltPercent) {
// update Tilt operational state
if (tiltPercent < currentTiltPercent) {
// Set operational status to OPEN
WindowBlinds.setOperationalState(MatterWindowCovering::TILT, MatterWindowCovering::MOVING_UP_OR_OPEN);
}
if (tiltPercent > currentTiltPercent) {
// Set operational status to CLOSE
WindowBlinds.setOperationalState(MatterWindowCovering::TILT, MatterWindowCovering::MOVING_DOWN_OR_CLOSE);
}
// This is where you would trigger your motor to rotate the shade to tiltPercent
// For simulation, we update instantly
currentTiltPercent = tiltPercent;
Serial.printf("Rotating tilt to %d%%\r\n", currentTiltPercent);
// Update CurrentPosition to reflect actual position
WindowBlinds.setTiltPercentage(currentTiltPercent);
// Set operational status to STALL when movement is complete
WindowBlinds.setOperationalState(MatterWindowCovering::TILT, MatterWindowCovering::STALL);
// Store state
matterPref.putUChar(tiltPercentPrefKey, currentTiltPercent);
return true;
}
bool stopMotor() {
// Motor can be stopped while moving cover toward current target
Serial.println("Stopping window covering motor");
// Update CurrentPosition to reflect actual position when stopped
// (setLiftPercentage and setTiltPercentage now only update CurrentPosition)
WindowBlinds.setLiftPercentage(currentLiftPercent);
WindowBlinds.setTiltPercentage(currentTiltPercent);
// Set operational status to STALL for both lift and tilt
WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL);
WindowBlinds.setOperationalState(MatterWindowCovering::TILT, MatterWindowCovering::STALL);
return true;
}
void setup() {
// Initialize the USER BUTTON (Boot button) GPIO
pinMode(buttonPin, INPUT_PULLUP);
// Initialize the RGB LED GPIO
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
Serial.begin(115200);
// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network
#if !CONFIG_ENABLE_CHIPOBLE
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
// Manually connect to WiFi
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\r\nWiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(500);
#endif
// Initialize Matter EndPoint
matterPref.begin("MatterPrefs", false);
// default lift percentage is 100% (fully open) if not stored before
uint8_t lastLiftPercent = matterPref.getUChar(liftPercentPrefKey, 100);
// default tilt percentage is 0% if not stored before
uint8_t lastTiltPercent = matterPref.getUChar(tiltPercentPrefKey, 0);
// Initialize window covering with BLIND_LIFT_AND_TILT type
WindowBlinds.begin(lastLiftPercent, lastTiltPercent, MatterWindowCovering::BLIND_LIFT_AND_TILT);
// Configure installed limits for lift and tilt
WindowBlinds.setInstalledOpenLimitLift(MIN_LIFT);
WindowBlinds.setInstalledClosedLimitLift(MAX_LIFT);
WindowBlinds.setInstalledOpenLimitTilt(MIN_TILT);
WindowBlinds.setInstalledClosedLimitTilt(MAX_TILT);
// Initialize current positions based on percentages and installed limits
uint16_t openLimitLift = WindowBlinds.getInstalledOpenLimitLift();
uint16_t closedLimitLift = WindowBlinds.getInstalledClosedLimitLift();
currentLiftPercent = lastLiftPercent;
if (openLimitLift < closedLimitLift) {
currentLift = openLimitLift + ((closedLimitLift - openLimitLift) * lastLiftPercent) / 100;
} else {
currentLift = openLimitLift - ((openLimitLift - closedLimitLift) * lastLiftPercent) / 100;
}
currentTiltPercent = lastTiltPercent;
Serial.printf(
"Window Covering limits configured: Lift [%d-%d cm], Tilt [%d-%d]\r\n", WindowBlinds.getInstalledOpenLimitLift(),
WindowBlinds.getInstalledClosedLimitLift(), WindowBlinds.getInstalledOpenLimitTilt(), WindowBlinds.getInstalledClosedLimitTilt()
);
Serial.printf("Initial positions: Lift=%d cm (%d%%), Tilt=%d%%\r\n", currentLift, currentLiftPercent, currentTiltPercent);
// Set callback functions
WindowBlinds.onOpen(fullOpen);
WindowBlinds.onClose(fullClose);
WindowBlinds.onGoToLiftPercentage(goToLiftPercentage);
WindowBlinds.onGoToTiltPercentage(goToTiltPercentage);
WindowBlinds.onStop(stopMotor);
// Generic callback for Lift or Tilt change
WindowBlinds.onChange([](uint8_t liftPercent, uint8_t tiltPercent) {
Serial.printf("Window Covering changed: Lift=%d%%, Tilt=%d%%\r\n", liftPercent, tiltPercent);
visualizeWindowBlinds(liftPercent, tiltPercent);
return true;
});
// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
// This may be a restart of a already commissioned Matter accessory
if (Matter.isDeviceCommissioned()) {
Serial.println("Matter Node is commissioned and connected to the network. Ready for use.");
Serial.printf("Initial state: Lift=%d%%, Tilt=%d%%\r\n", WindowBlinds.getLiftPercentage(), WindowBlinds.getTiltPercentage());
// Update visualization based on initial state
visualizeWindowBlinds(WindowBlinds.getLiftPercentage(), WindowBlinds.getTiltPercentage());
}
}
void loop() {
// Check Matter Window Covering Commissioning state, which may change during execution of loop()
if (!Matter.isDeviceCommissioned()) {
Serial.println("");
Serial.println("Matter Node is not commissioned yet.");
Serial.println("Initiate the device discovery in your Matter environment.");
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
// waits for Matter Window Covering Commissioning.
uint32_t timeCount = 0;
while (!Matter.isDeviceCommissioned()) {
delay(100);
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
}
}
Serial.printf("Initial state: Lift=%d%%, Tilt=%d%%\r\n", WindowBlinds.getLiftPercentage(), WindowBlinds.getTiltPercentage());
// Update visualization based on initial state
visualizeWindowBlinds(WindowBlinds.getLiftPercentage(), WindowBlinds.getTiltPercentage());
Serial.println("Matter Node is commissioned and connected to the network. Ready for use.");
}
// A button is also used to control the window covering
// Check if the button has been pressed
if (digitalRead(buttonPin) == LOW && !button_state) {
// deals with button debouncing
button_time_stamp = millis(); // record the time while the button is pressed.
button_state = true; // pressed.
}
// Onboard User Button is used to manually change lift percentage or to decommission
uint32_t time_diff = millis() - button_time_stamp;
if (digitalRead(buttonPin) == HIGH && button_state && time_diff > debounceTime) {
// Button is released - cycle lift percentage by 20%
button_state = false; // released
uint8_t targetLiftPercent = currentLiftPercent;
// go to the closest next 20% or move 20% more
if ((targetLiftPercent % 20) != 0) {
targetLiftPercent = ((targetLiftPercent / 20) + 1) * 20;
} else {
targetLiftPercent += 20;
}
if (targetLiftPercent > 100) {
targetLiftPercent = 0;
}
Serial.printf("User button released. Setting lift to %d%%\r\n", targetLiftPercent);
WindowBlinds.setTargetLiftPercent100ths(targetLiftPercent * 100);
}
// Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node
if (button_state && time_diff > decommissioningTimeout) {
Serial.println("Decommissioning the Window Covering Matter Accessory. It shall be commissioned again.");
WindowBlinds.setLiftPercentage(0); // close the covering
Matter.decommission();
button_time_stamp = millis(); // avoid running decommissioning again, reboot takes a second or so
}
}