MatterFan
About
The MatterFan class provides a fan endpoint for Matter networks with speed and mode control. This endpoint implements the Matter fan control standard.
Features:
* On/off control
* Fan speed control (0-100%)
* Fan mode control (OFF, LOW, MEDIUM, HIGH, AUTO). ON and SMART are remapped, not stored
* Fan mode sequence configuration
* Callback support for state, speed, and mode changes
* Integration with Home Assistant, Apple Home, Amazon Alexa, and Google Home
* Matter standard compliance
Use Cases: * Smart ceiling fans * Exhaust fans * Ventilation fans * Fan speed controllers * HVAC fan control
API Reference
Constructor
MatterFan
Creates a new Matter fan endpoint.
MatterFan();
Initialization
begin
Initializes the Matter fan endpoint with optional initial speed, mode, and mode sequence.
bool begin(uint8_t percent = 0, FanMode_t fanMode = FAN_MODE_OFF, FanModeSequence_t fanModeSeq = FAN_MODE_SEQ_OFF_HIGH);
percent- Initial speed percentage (0-100, default: 0). Forced to0whenfanModeisFAN_MODE_OFF. In Auto, this isPercentCurrentonly;PercentSettingis null.fanMode- Initial fan mode (default:FAN_MODE_OFF)fanModeSeq- Fan mode sequence configuration (default:FAN_MODE_SEQ_OFF_HIGH)
This function will return true if successful, false otherwise.
end
Stops processing Matter fan events.
void end();
Constants
MAX_SPEED
Maximum speed value (100%).
static const uint8_t MAX_SPEED = 100;
MIN_SPEED
Minimum speed value (1%).
static const uint8_t MIN_SPEED = 1;
OFF_SPEED
Speed value when fan is off (0%).
static const uint8_t OFF_SPEED = 0;
Fan Modes
FanMode_t
Fan mode enumeration:
FAN_MODE_OFF- Fan is offFAN_MODE_LOW- Low speedFAN_MODE_MEDIUM- Medium speedFAN_MODE_HIGH- High speedFAN_MODE_ON- Alias: stored asFAN_MODE_HIGHFAN_MODE_AUTO- Auto mode (only valid in an Auto sequence)FAN_MODE_SMART- Alias: stored asFAN_MODE_AUTOif the sequence includes Auto, otherwiseFAN_MODE_HIGH
Matter FanModeSequence never includes On or Smart. CHIP remaps those writes the same way.
Fan Mode Sequences
FanModeSequence_t
Fan mode sequence enumeration:
FAN_MODE_SEQ_OFF_LOW_MED_HIGH- OFF, LOW, MEDIUM, HIGHFAN_MODE_SEQ_OFF_LOW_HIGH- OFF, LOW, HIGHFAN_MODE_SEQ_OFF_LOW_MED_HIGH_AUTO- OFF, LOW, MEDIUM, HIGH, AUTOFAN_MODE_SEQ_OFF_LOW_HIGH_AUTO- OFF, LOW, HIGH, AUTOFAN_MODE_SEQ_OFF_HIGH_AUTO- OFF, HIGH, AUTOFAN_MODE_SEQ_OFF_HIGH- OFF, HIGH
On/Off Control
setOnOff
Sets the on/off state of the fan.
bool setOnOff(bool newState, bool performUpdate = true);
newState- New state (true= on,false= off)performUpdate- Perform update after setting (default:true)
getOnOff
Gets the current on/off state.
bool getOnOff();
toggle
Toggles the on/off state.
bool toggle(bool performUpdate = true);
Speed Control
setSpeedPercent
Sets the fan speed percentage.
bool setSpeedPercent(uint8_t newPercent, bool performUpdate = true);
newPercent- Speed percentage (0-100)performUpdate- Perform update after setting (default:true)
Writes nullable PercentSetting and non-nullable PercentCurrent separately. In Auto, Matter may null PercentSetting; PercentCurrent stays 0-100.
getSpeedPercent
Gets the current speed percentage.
uint8_t getSpeedPercent();
Mode Control
setMode
Sets the fan mode.
bool setMode(FanMode_t newMode, bool performUpdate = true);
newMode- Fan mode to set.FAN_MODE_ONandFAN_MODE_SMARTare remapped as described underFanMode_tperformUpdate- Perform update after setting (default:true)
Matches CHIP after the mode write: FAN_MODE_OFF sets PercentSetting and PercentCurrent to 0; FAN_MODE_AUTO nulls PercentSetting and leaves PercentCurrent as the actual speed.
This function will return false if the remapped mode is not in the sequence passed to begin().
getMode
Gets the current fan mode.
FanMode_t getMode();
getFanModeString
Gets a friendly string for the fan mode.
static const char *getFanModeString(uint8_t mode);
Event Handling
onChange
Sets a callback for when any parameter changes.
void onChange(EndPointCB onChangeCB);
The callback signature is:
bool onChangeCallback(FanMode_t newMode, uint8_t newPercent);
onChangeMode
Sets a callback for mode changes.
void onChangeMode(EndPointModeCB onChangeCB);
onChangeSpeedPercent
Sets a callback for speed changes.
void onChangeSpeedPercent(EndPointSpeedCB onChangeCB);
updateAccessory
Updates the physical fan state using current Matter internal state.
void updateAccessory();
Operators
uint8_t operator
Returns the current speed percentage.
operator uint8_t();
Assignment operator
Sets the speed percentage.
void operator=(uint8_t speedPercent);
Example
Fan Control
// 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 <Arduino.h>
#include <Matter.h>
// List of Matter Endpoints for this Node
// Fan Endpoint - On/Off control + Speed Percent Control + Fan Modes
MatterFan Fan;
// Wi-Fi credentials for this sketch. Fill these in when the board cannot
// commission over BLE (Arduino prebuild on ESP32 / ESP32-S2): the sketch
// joins the AP itself. When Matter commissions over BLE (CHIPoBLE), the
// hub sends SSID and password — leave the placeholders; they are unused.
#define WIFI_SSID "your-ssid"
#define WIFI_PASSWORD "your-password"
// set your board USER BUTTON pin here - used for toggling On/Off and decommission the Matter Node
const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button.
MatterButton button;
// set your board Analog Pin here - used for changing the Fan speed
const uint8_t analogPin = A0; // Analog Pin depends on each board
// set your board PWM Pin here - used for controlling the Fan speed (DC motor example)
// for this example, it will use the builtin board RGB LED to simulate the Fan DC motor using its brightness
#ifdef RGB_BUILTIN
const uint8_t dcMotorPin = RGB_BUILTIN;
#else
const uint8_t dcMotorPin = 2; // Set your pin here if the board has no RGB_BUILTIN
#warning "Do not forget to set the RGB LED pin"
#endif
void fanDCMotorDrive(bool fanState, uint8_t speedPercent) {
// drive the Fan DC motor
if (fanState == false) {
// turn off the Fan
#ifndef RGB_BUILTIN
// after analogWrite(), it is necessary to set the GPIO to digital mode first
pinMode(dcMotorPin, OUTPUT);
#endif
digitalWrite(dcMotorPin, LOW);
} else {
// set the Fan speed
uint8_t fanDCMotorPWM = map(speedPercent, 0, 100, 0, 255);
#ifdef RGB_BUILTIN
rgbLedWrite(dcMotorPin, fanDCMotorPWM, fanDCMotorPWM, fanDCMotorPWM);
#else
analogWrite(dcMotorPin, fanDCMotorPWM);
#endif
}
}
void setup() {
// Initialize the USER BUTTON (Boot button) GPIO that will toggle the Fan (On/Off) and decommission the Matter Node
button.begin(buttonPin);
// Initialize the Analog Pin A0 used to read input voltage and to set the Fan speed accordingly
pinMode(analogPin, INPUT);
analogReadResolution(10); // 10 bits resolution reading 0..1023
// Initialize the PWM output pin for a Fan DC motor
pinMode(dcMotorPin, OUTPUT);
Serial.begin(115200);
// CONFIG_ENABLE_CHIPOBLE=n: sketch starts Wi-Fi here; with CHIPoBLE the hub delivers credentials.
#if !CONFIG_ENABLE_CHIPOBLE
matterConnectWiFi(WIFI_SSID, WIFI_PASSWORD);
#endif
// Boot: 0% speed, Off. Sequence is Off/High, so setOnOff(true) / FAN_MODE_ON store High.
Fan.begin(0, MatterFan::FAN_MODE_OFF, MatterFan::FAN_MODE_SEQ_OFF_HIGH);
// callback functions would control Fan motor
// the Matter Controller will send new data whenever the User APP or Automation request
// single feature callbacks take place before the generic (all features) callback
// This callback will be executed whenever the speed percent matter attribute is updated
Fan.onChangeSpeedPercent([](uint8_t speedPercent) {
// setting speed to Zero, while the Fan is ON, shall turn the Fan OFF
if (speedPercent == MatterFan::OFF_SPEED && Fan.getMode() != MatterFan::FAN_MODE_OFF) {
// ATTR_UPDATE reports FanMode so the APP confirms Off. Cache stops re-entry.
return Fan.setOnOff(false, Fan.ATTR_UPDATE);
}
// changing the speed to higher than Zero, while the Fan is OFF, shall turn the Fan ON
if (speedPercent > MatterFan::OFF_SPEED && Fan.getMode() == MatterFan::FAN_MODE_OFF) {
return Fan.setOnOff(true, Fan.ATTR_UPDATE);
}
// for other case, just return true
return true;
});
// This callback will be executed whenever the fan mode matter attribute is updated
// This will take action when user APP starts the Fan by changing the mode
Fan.onChangeMode([](MatterFan::FanMode_t fanMode) {
// when the Fan is turned ON using Mode Selection, while it is OFF, shall start it by setting the speed to 50%
if (Fan.getSpeedPercent() == MatterFan::OFF_SPEED && fanMode != MatterFan::FAN_MODE_OFF) {
Serial.printf("Fan set to %s mode -- speed percentage will go to 50%%\r\n", Fan.getFanModeString(fanMode));
return Fan.setSpeedPercent(50, Fan.ATTR_UPDATE);
}
return true;
});
// Generic callback will be executed as soon as a single feature callback is done
// In this example, it will just print status messages
Fan.onChange([](MatterFan::FanMode_t fanMode, uint8_t speedPercent) {
// just report state
Serial.printf("Fan State: Mode %s | %u%% speed.\r\n", Fan.getFanModeString(fanMode), speedPercent);
// drive the Fan DC motor
fanDCMotorDrive(fanMode != MatterFan::FAN_MODE_OFF, speedPercent);
// returns success
return true;
});
// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
matterWaitUntilReady();
}
void loop() {
matterRestartIfNoFabric();
matterButtonEvent_t ev;
while ((ev = button.poll()) != MATTER_BUTTON_NONE) {
if (ev == MATTER_BUTTON_CLICK) {
Fan.toggle();
Serial.printf("User button released. Setting the Fan %s.\r\n", Fan > 0 ? "ON" : "OFF");
} else if (ev == MATTER_BUTTON_LONG_HOLD) {
Serial.println("Decommissioning Fan Matter Accessory. It shall be commissioned again.");
Matter.decommission();
}
}
// checks Analog pin and adjust the speed only if it has changed
static int lastRead = 0;
// analog values (0..1023) / 103 => mapped into 10 steps (0..9)
int anaVal = analogRead(analogPin) / 103;
if (lastRead != anaVal) {
// speed percent moves in steps of 10. Range is 10..100
if (Fan.setSpeedPercent((anaVal + 1) * 10)) {
lastRead = anaVal;
}
}
}