Skip to content

Commit c484f14

Browse files
committed
feat(matter): add new matter endpoint for thermostat
1 parent 6bf6df2 commit c484f14

File tree

7 files changed

+862
-0
lines changed

7 files changed

+862
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS
181181
libraries/Matter/src/MatterEndpoints/MatterPressureSensor.cpp
182182
libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp
183183
libraries/Matter/src/MatterEndpoints/MatterOnOffPlugin.cpp
184+
libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp
184185
libraries/Matter/src/Matter.cpp)
185186

186187
set(ARDUINO_LIBRARY_PPP_SRCS
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// Copyright 2024 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+
This example is an example code that will create a Matter Device which can be
17+
commissioned and controlled from a Matter Environment APP.
18+
Additionally the ESP32 will send debug messages indicating the Matter activity.
19+
Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages.
20+
*/
21+
22+
// Matter Manager
23+
#include <Matter.h>
24+
#include <WiFi.h>
25+
26+
// List of Matter Endpoints for this Node
27+
// Matter Thermostat Endpoint
28+
MatterThermostat SimulatedThermostat;
29+
30+
// WiFi is manually set and started
31+
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
32+
const char *password = "your-password"; // Change this to your WiFi password
33+
34+
// set your board USER BUTTON pin here - decommissioning button
35+
const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button.
36+
37+
// Button control - decommision the Matter Node
38+
uint32_t button_time_stamp = 0; // debouncing control
39+
bool button_state = false; // false = released | true = pressed
40+
const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission
41+
42+
// Simulate a system that will activate heating/cooling in addition to a temperature sensor - add your preferred code here
43+
float getSimulatedTemperature(bool isHeating, bool isCooling) {
44+
// read sensor temperature and apply heating/cooling
45+
float simulatedTempHWSensor = SimulatedThermostat.getLocalTemperature();
46+
47+
if (isHeating) {
48+
// it will increase to simulate a heating system
49+
simulatedTempHWSensor = simulatedTempHWSensor + 0.5;
50+
}
51+
if (isCooling) {
52+
// it will decrease to simulate a colling system
53+
simulatedTempHWSensor = simulatedTempHWSensor - 0.5;
54+
}
55+
// otherwise, it will keep the temperature stable
56+
return simulatedTempHWSensor;
57+
}
58+
59+
void setup() {
60+
// Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node
61+
pinMode(buttonPin, INPUT_PULLUP);
62+
63+
Serial.begin(115200);
64+
65+
// Manually connect to WiFi
66+
WiFi.begin(ssid, password);
67+
// Wait for connection
68+
while (WiFi.status() != WL_CONNECTED) {
69+
delay(500);
70+
Serial.print(".");
71+
}
72+
Serial.println();
73+
74+
// Simulated Thermostat in COOLING and HEATING mode with Auto Mode to keep the temperature between setpoints
75+
// Auto Mode can only be used when the control sequence of operation is Cooling & Heating
76+
SimulatedThermostat.begin(MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_HEATING, true);
77+
78+
// Matter beginning - Last step, after all EndPoints are initialized
79+
Matter.begin();
80+
81+
// Check Matter Accessory Commissioning state, which may change during execution of loop()
82+
if (!Matter.isDeviceCommissioned()) {
83+
Serial.println("");
84+
Serial.println("Matter Node is not commissioned yet.");
85+
Serial.println("Initiate the device discovery in your Matter environment.");
86+
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
87+
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
88+
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
89+
// waits for Matter Thermostat Commissioning.
90+
uint32_t timeCount = 0;
91+
while (!Matter.isDeviceCommissioned()) {
92+
delay(100);
93+
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
94+
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
95+
}
96+
}
97+
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
98+
99+
// after commissioning, set initial thermostat parameters
100+
// start the thermostat in AUTO mode
101+
SimulatedThermostat.setMode(MatterThermostat::THERMOSTAT_MODE_AUTO);
102+
// cooling setpoint must be lower than heating setpoint by at least 2.5C (deadband), in auto mode
103+
SimulatedThermostat.setCoolingHeatingSetpoints(20.0, 23.00); // the target cooler and heating setpoint
104+
// set the local temperature sensor in Celsius
105+
SimulatedThermostat.setLocalTemperature(12.50);
106+
107+
Serial.println();
108+
Serial.printf("Initial Setpoints are %.01fC to %.01fC with a minimum 2.5C difference\r\n", SimulatedThermostat.getHeatingSetpoint(), SimulatedThermostat.getCoolingSetpoint());
109+
Serial.printf("Auto mode is ON. Initial Temperature of %.01fC \r\n", SimulatedThermostat.getLocalTemperature());
110+
Serial.println("Local Temperature Sensor will be simulated every 10 seconds and changed by a simulated heater and cooler to move in between setpoints.");
111+
}
112+
}
113+
114+
// This will simulate the thermostat control system (heating and cooling)
115+
// User can set a local temperature using the Serial input (type a number and press Enter)
116+
// New temperature can be an positive or negative temperature in Celsius, between -50C and 50C
117+
// Initial local temperature is 10C as defined in getSimulatedTemperature() function
118+
void readSerialForNewTemperature() {
119+
static String newTemperatureStr;
120+
121+
while (Serial.available()) {
122+
char c = Serial.read();
123+
if (c == '\n' || c == '\r') {
124+
if (newTemperatureStr.length() > 0) {
125+
// convert the string to a float value
126+
float newTemperature = newTemperatureStr.toFloat();
127+
// check if the new temperature is valid
128+
if (newTemperature >= -50.0 && newTemperature <= 50.0) {
129+
// set the new temperature
130+
SimulatedThermostat.setLocalTemperature(newTemperature);
131+
Serial.printf("New Temperature is %.01fC\r\n", newTemperature);
132+
} else {
133+
Serial.println("Invalid Temperature value. Please type a number between -50 and 50");
134+
}
135+
newTemperatureStr = "";
136+
}
137+
} else {
138+
if (c == '+' || c == '-' || (c >= '0' && c <= '9') || c == '.') {
139+
newTemperatureStr += c;
140+
} else {
141+
Serial.println("Invalid character. Please type a number between -50 and 50");
142+
newTemperatureStr = "";
143+
}
144+
}
145+
}
146+
}
147+
148+
// loop will simulate the thermostat control system
149+
// User can set a local temperature using the Serial input (type a number and press Enter)
150+
// User can change the thermostat mode using the Matter APP (smartphone)
151+
// The loop will simulate a heating and cooling system and the associated local temperature change
152+
void loop() {
153+
static uint32_t timeCounter = 0;
154+
155+
// Simulate the heating and cooling systems
156+
static bool isHeating = false;
157+
static bool isCooling = false;
158+
159+
// check if a new temperature is typed in the Serial Monitor
160+
readSerialForNewTemperature();
161+
162+
// simulate thermostat with heating/cooling system and the associated local temperature change, every 10s
163+
if (!(timeCounter++ % 20)) { // delaying for 500ms x 20 = 10s
164+
float localTemperature = getSimulatedTemperature(isHeating, isCooling);
165+
// Print the current thermostat local temperature value
166+
Serial.printf("Current Local Temperature is %.01fC\r\n", localTemperature);
167+
SimulatedThermostat.setLocalTemperature(localTemperature); // publish the new temperature value
168+
169+
// Simulate the thermostat control system - User has 4 modes: OFF, HEAT, COOL, AUTO
170+
if (SimulatedThermostat.getMode() == MatterThermostat::THERMOSTAT_MODE_OFF) {
171+
// turn off the heating and cooling systems
172+
isHeating = false;
173+
isCooling = false;
174+
}
175+
// User APP has set the thermostat to AUTO mode -- keeping the tempeature between both setpoints
176+
if (SimulatedThermostat.getMode() == MatterThermostat::THERMOSTAT_MODE_AUTO) {
177+
// check if the heating system should be turned on or off
178+
if (localTemperature < SimulatedThermostat.getHeatingSetpoint() + SimulatedThermostat.getDeadBand()) {
179+
// turn on the heating system and turn off the cooling system
180+
isHeating = true;
181+
isCooling = false;
182+
}
183+
if (localTemperature > SimulatedThermostat.getCoolingSetpoint() - SimulatedThermostat.getDeadBand()) {
184+
// turn off the heating system and turn on the cooling system
185+
isHeating = false;
186+
isCooling = true;
187+
}
188+
}
189+
// User APP has set the thermostat to AUTO mode -- keeping the tempeature between both setpoints
190+
if (SimulatedThermostat.getMode() == MatterThermostat::THERMOSTAT_MODE_AUTO) {
191+
// check if the heating system should be turned on or off
192+
if (localTemperature < SimulatedThermostat.getHeatingSetpoint() + SimulatedThermostat.getDeadBand()) {
193+
// turn on the heating system and turn off the cooling system
194+
isHeating = true;
195+
isCooling = false;
196+
}
197+
if (localTemperature > SimulatedThermostat.getCoolingSetpoint() - SimulatedThermostat.getDeadBand()) {
198+
// turn off the heating system and turn on the cooling system
199+
isHeating = false;
200+
isCooling = true;
201+
}
202+
}
203+
// Simulate the heating system - User has turned the heating system ON
204+
if (SimulatedThermostat.getMode() == MatterThermostat::THERMOSTAT_MODE_HEAT) {
205+
isHeating = true;
206+
isCooling = false; // keep the cooling system off as it is in heating mode
207+
// when the heating system is in HEATING mode, it will be turned off as soon as the local temperature is above the setpoint
208+
if (localTemperature > SimulatedThermostat.getHeatingSetpoint()) {
209+
// turn off the heating system
210+
isHeating = false;
211+
}
212+
}
213+
// Simulate the cooling system - User has turned the cooling system ON
214+
if (SimulatedThermostat.getMode() == MatterThermostat::THERMOSTAT_MODE_COOL) {
215+
isCooling = true;
216+
isHeating = false; // keep the heating system off as it is in cooling mode
217+
// when the cooling system is in COOLING mode, it will be turned off as soon as the local temperature is bellow the setpoint
218+
if (localTemperature < SimulatedThermostat.getCoolingSetpoint()) {
219+
// turn off the cooling system
220+
isCooling = false;
221+
}
222+
}
223+
224+
// Reporting Heating and Cooling status
225+
Serial.printf("\tThermostat Mode: %s >>> Heater is %s -- Cooler is %s\r\n", MatterThermostat::getThermostatModeString(SimulatedThermostat.getMode()), isHeating ? "ON" : "OFF", isCooling ? "ON" : "OFF");
226+
}
227+
228+
// Check if the button has been pressed
229+
if (digitalRead(buttonPin) == LOW && !button_state) {
230+
// deals with button debouncing
231+
button_time_stamp = millis(); // record the time while the button is pressed.
232+
button_state = true; // pressed.
233+
}
234+
235+
if (digitalRead(buttonPin) == HIGH && button_state) {
236+
button_state = false; // released
237+
}
238+
239+
// Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node
240+
uint32_t time_diff = millis() - button_time_stamp;
241+
if (button_state && time_diff > decommissioningTimeout) {
242+
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
243+
Matter.decommission();
244+
button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so
245+
}
246+
247+
delay(500);
248+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"fqbn_append": "PartitionScheme=huge_app",
3+
"requires": [
4+
"CONFIG_SOC_WIFI_SUPPORTED=y",
5+
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
6+
]
7+
}

libraries/Matter/keywords.txt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ MatterContactSensor KEYWORD1
2424
MatterPressureSensor KEYWORD1
2525
MatterOccupancySensor KEYWORD1
2626
MatterOnOffPlugin KEYWORD1
27+
MatterThermostat KEYWORD1
28+
ControlSequenceOfOperation_t KEYWORD1
29+
ThermostatMode_t KEYWORD1
30+
EndPointCB KEYWORD1
31+
EndPointHeatingSetpointCB KEYWORD1
32+
EndPointCoolingSetpointCB KEYWORD1
33+
EndPointTemperatureCB KEYWORD1
34+
EndPointModeCB KEYWORD1
35+
EndPointSpeedCB KEYWORD1
36+
EndPointOnOffCB KEYWORD1
37+
EndPointBrightnessCB KEYWORD1
38+
EndPointRGBColorCB KEYWORD1
39+
EndPointTemperatureCB KEYWORD1
2740

2841
#######################################
2942
# Methods and Functions (KEYWORD2)
@@ -78,6 +91,24 @@ setPressure KEYWORD2
7891
getPressure KEYWORD2
7992
setOccupancy KEYWORD2
8093
getOccupancy KEYWORD2
94+
getControlSequence KEYWORD2
95+
getMinHeatSetpoint KEYWORD2
96+
getMaxHeatSetpoint KEYWORD2
97+
getMinCoolSetpoint KEYWORD2
98+
getMaxCoolSetpoint KEYWORD2
99+
getDeadBand KEYWORD2
100+
setCoolingSetpoint KEYWORD2
101+
getCoolingSetpoint KEYWORD2
102+
setHeatingSetpoint KEYWORD2
103+
getHeatingSetpoint KEYWORD2
104+
setCoolingHeatingSetpoints KEYWORD2
105+
setLocalTemperature KEYWORD2
106+
getLocalTemperature KEYWORD2
107+
getThermostatModeString KEYWORD2
108+
onChangeMode KEYWORD2
109+
onChangeLocalTemperature KEYWORD2
110+
onChangeCoolingSetpoint KEYWORD2
111+
onChangeHeatingSetpoint KEYWORD2
81112

82113
#######################################
83114
# Constants (LITERAL1)
@@ -104,3 +135,13 @@ FAN_MODE_SEQ_OFF_LOW_MED_HIGH_AUTO LITERAL1
104135
FAN_MODE_SEQ_OFF_LOW_HIGH_AUTO LITERAL1
105136
FAN_MODE_SEQ_OFF_HIGH_AUTO LITERAL1
106137
FAN_MODE_SEQ_OFF_HIGH LITERAL1
138+
THERMOSTAT_SEQ_OP_COOLING LITERAL1
139+
THERMOSTAT_SEQ_OP_COOLING_REHEAT LITERAL1
140+
THERMOSTAT_SEQ_OP_HEATING LITERAL1
141+
THERMOSTAT_SEQ_OP_HEATING_REHEAT LITERAL1
142+
THERMOSTAT_SEQ_OP_COOLING_HEATING LITERAL1
143+
THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT LITERAL1
144+
THERMOSTAT_MODE_OFF LITERAL1
145+
THERMOSTAT_MODE_AUTO LITERAL1
146+
THERMOSTAT_MODE_COOL LITERAL1
147+
THERMOSTAT_MODE_HEAT LITERAL1

libraries/Matter/src/Matter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <MatterEndpoints/MatterPressureSensor.h>
3333
#include <MatterEndpoints/MatterOccupancySensor.h>
3434
#include <MatterEndpoints/MatterOnOffPlugin.h>
35+
#include <MatterEndpoints/MatterThermostat.h>
3536

3637
using namespace esp_matter;
3738

@@ -70,6 +71,7 @@ class ArduinoMatter {
7071
friend class MatterPressureSensor;
7172
friend class MatterOccupancySensor;
7273
friend class MatterOnOffPlugin;
74+
friend class MatterThermostat;
7375

7476
protected:
7577
static void _init();

0 commit comments

Comments
 (0)