This guide shows you how to create a Matter ESP32 compatible device that sends temperature, humidity, and pressure readings from a BME280 sensor. The device can be commissioned into Apple Home, Google Home, Amazon Alexa, Home Assistant, or other Matter controllers. The ESP32 will be programmed with Arduini IDE.

Prerequisites
Before proceeding with this tutorial, you should have the ESP32 boards installed in your Arduino IDE:
You will also need an ESP32 development board model of your choice and a Home Automation controller like Amazon Echo Dot Max (Alexa).
Matter Protocol Introduction
Matter, formerly known as Project CHIP (Connected Home over IP), is an open-source and industry standard connectivity protocol for smart home devices. Matter protocol was developed by the Connectivity Standards Alliance (CSA) with contributions from major companies including Apple, Google, Amazon, Samsung, and others. Matter aims to solve the fragmentation problem where each company builds its own proprietary protocol to interact with its devices.

Key features of the Matter protocol:
- Interoperability: devices from different manufacturers work together seamlessly in the same ecosystem (Apple Home, Google Home, Amazon Alexa, Home Assistant, Samsung SmartThings, etc…).
- Local control: communication happens primarily on the local network (Wi-Fi, Thread, or Ethernet). The Cloud connectivity is optional.
- Security: built-in encrypted communication and secure commissioning.
- Simple setup: devices are commissioned using a QR code or a manual pairing code.
- Multi-admin support: a single device can be controlled by multiple smart home controllers at the same time.
- IP-based: runs over standard IP networks.

A few concepts worth knowing before you dive into this guide:
- Node: a single Matter device (example: in this guide the ESP32 is a node).
- Endpoint: a functional unit inside a node. A single node (Matter device) can expose multiple endpoints (example: temperature/humidity sensor).
- Cluster: a group of related attributes and commands tied to a function (example: the Temperature Measurement cluster has a MeasuredValue attribute).
- Fabric: the logical network created when a controller (Apple Home, Google Home, Alexa, Home Assistant, etc…) commissions your device. A node can belong to multiple fabrics at once.
- Commissioning: the process of assigning a Matter device (node) to a fabric, using a QR code or an 11-digit manual pairing code. It’s typically done over BLE or Wi-Fi.
- Wi-Fi: Matter runs over IP, and nodes (example: ESP32 devices) can join a Matter network over Wi-Fi.
The ESP32 Arduino Core since version 3.1.0 comes with the Matter library, so you don’t need to install any additional libraries in your Arduino IDE.
Typical Matter Device Workflow
The following diagram illustrates the complete workflow of a Matter device from when it is uncommissioned to normal operation and optional sharing across multiple smart home controller ecosystems.

1. Uncommissioned Device / Node
- First device usage (or after reset)
- Shows QR code + manual pairing code
- User scans QR code or enters manual pairing code into a smart home controller (fabric): Alexa, Apple Home, Google Home, Home Assistant, etc…
2. Commissioning
- Secure pairing session starts (PASE)
- Confirms it’s a Matter device
- Wi-Fi credentials are given (usually over BLE)
- Device joins the Matter fabric (example: Alexa – Echo Dot Max)
3. Joins the Network
- Connects to Wi-Fi
- Becomes reachable via IPv6 + mDNS
4. Normal Operation
- Controllers discover and control the Matter device
- All communication is encrypted
- Works locally even without internet
5. Multi-admin (optional)
- Original QR/pairing code cannot be reused
- Open pairing mode from the first connected app to generate / share device by creating a new temporary manual pairing code
- Use the new generated code to add the device to another ecosystem: Apple Home, Google Home, Home Assistant, etc…
- The same Matter device belongs to multiple fabrics and can be controlled by several ecosystems at the same time
Supported ESP32 Chips
The following ESP32 chips support the Matter protocol. Additionally, it also shows which ones support CHIPoBLE. For chips that don’t support it, the credentials need to be inserted in the sketch. In the other scenarios, they are transferred over BLE from the controller.
| Chip | Commissioning Path | CHIPoBLE * |
|---|---|---|
| ESP32 | Wi-Fi (enter SSID/Password in sketch) | Off |
| ESP32-S2 | Wi-Fi (enter SSID/Password in sketch) | Off |
| ESP32-S3 | Wi-Fi (fabric/controller) | On |
| ESP32-C3 | Wi-Fi (fabric/controller) | On |
| ESP32-C5 | Wi-Fi (fabric/controller) | On |
| ESP32-C6 | Wi-Fi (fabric/controller) | On |
| ESP32-H2 | Thread (fabric/controller) | On |
* CHIPoBLE stands for CHIP (Connected Home over IP), the original name of the Matter protocol, over BLE (Bluetooth Low Energy).
Parts Required
To complete this tutorial, you need the following parts:
- BME280 sensor module
- ESP32 S3 dev board or another ESP32 of your choice
- Breadboard
- Jumper wires
- Amazon Echo Dot Max (Alexa) – or any other Matter hub/controller
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
The BME280 sensor module reads barometric pressure, temperature, and humidity. There are several versions of this sensor module. We’re using the module illustrated in the figure below.

This sensor communicates via I2C communication protocol. You can use the default ESP32 I2C pins as shown in the following table:
| BME280 | ESP32 | ESP32-S3 |
| Vin | 3.3V | 3.3V |
| GND | GND | GND |
| SCL | GPIO 22 | GPIO 9 |
| SDA | GPIO 21 | GPIO 8 |
If you have a different ESP32 board model, double-check its pinout to locate the default I2C pins.
Schematic – ESP32 with BME280 using I2C
Wire the BME280 sensor to the ESP32 SDA and SCL pins, as shown in the following schematic diagram (check ESP32 Pinout Reference).

In the case of ESP32-S3 boards, the I2C pins are SDA connected to GPIO 8 and SCL connected to GPIO 9, as shown in the following schematic diagram (check ESP32-S3 GPIO Reference Guide).

Installing BME280 Library
For this tutorial, we’ll use a BME280 sensor and the Adafruit_BME280 library. Install it before proceeding.
In the Arduino IDE, go to Sketch > Include Library > Manage Libraries… and search for the library name: Adafruit BME280. You also need to install the Adafruit Unified Sensor Library (a pop-up shows up to install it if you haven’t already)

Arduino Sketch – ESP32 Matter Device: Send Temperature, Humidity, and Pressure
The following example is based on the official Espressif Arduino ESP32 Matter library. It turns your ESP32 into a fully functional Matter device (node) that sends readings from a BME280 sensor. The device can be commissioned into any major Matter ecosystem.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-matter-temperature-humidity-bme280-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <Arduino.h>
#include <Matter.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
// 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"
// Matter endpoints
MatterTemperatureSensor TemperatureSensor;
MatterHumiditySensor HumiditySensor;
MatterPressureSensor PressureSensor;
// I2C pins for the BME280
#define I2C_SDA 8
#define I2C_SCL 9
// BME280
Adafruit_BME280 bme;
// Latest BME280 readings
float temperature, humidity, pressure;
// Update interval for the BME280 sensor readings (in milliseconds)
const uint32_t SENSOR_INTERVAL_MS = 30000;
// Button pin for decommissioning the Matter accessory
const uint8_t buttonPin = BOOT_PIN;
MatterButton button;
// Read BME280 sensor values
bool readBME280() {
temperature = bme.readTemperature();
humidity = bme.readHumidity();
pressure = bme.readPressure() / 100.0F;
return !(isnan(temperature) || isnan(humidity) || isnan(pressure));
}
void setup() {
button.begin(buttonPin);
Serial.begin(115200);
// Initialize BME280 Sensor
Wire.begin(I2C_SDA, I2C_SCL);
if (!bme.begin(0x76, &Wire) && !bme.begin(0x77, &Wire)) {
Serial.println("BME280 not found!");
while(1);
}
// Connect to Wi-Fi if CHIPoBLE is not enabled
#if !CONFIG_ENABLE_CHIPOBLE
matterConnectWiFi(WIFI_SSID, WIFI_PASSWORD);
#endif
// Read initial sensor values and initialize Matter endpoints
readBME280();
TemperatureSensor.begin(temperature);
HumiditySensor.begin(humidity);
PressureSensor.begin(pressure);
// Start Matter stack and wait until ready
Matter.begin();
matterWaitUntilReady();
}
void loop() {
matterRestartIfNoFabric();
static uint32_t lastUpdate = 0;
// Read BME280 sensor values at defined intervals
if (millis() - lastUpdate >= SENSOR_INTERVAL_MS) {
lastUpdate = millis();
if (readBME280()) {
Serial.printf("Temperature: %.2f C | Humidity: %.2f %% | Pressure: %.2f hPa\r\n",
temperature, humidity, pressure);
TemperatureSensor.setTemperature(temperature);
HumiditySensor.setHumidity(humidity);
PressureSensor.setPressure(pressure);
}
}
matterButtonEvent_t ev;
// Poll the button for events and handle long hold for decommissioning
while ((ev = button.poll()) != MATTER_BUTTON_NONE) {
if (ev == MATTER_BUTTON_LONG_HOLD) {
Serial.println("Decommissioning Matter Accessory. It shall be commissioned again.");
Matter.decommission();
}
}
delay(100);
}
You must select in the Tools – Partition Scheme: “Huge APP (3MB No OT /1MB SPIFFS)” and the code will work straight away. Depending on your board you might need to insert your network credentials in the following lines in the code.
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID";
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD";
How the Code Works
Continue reading to learn how the code works, or skip to the Demonstration section.
Including Libraries
Start by including the following libraries:
#include <Arduino.h>
#include <Matter.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
- Matter.h – Main Matter library (handles the protocol stack)
- Wire.h – library for I2C (Two-Wire Interface) communication
- Adafruit_BME280.h – library for BME280 environmental sensor. It reads temperature, humidity, and barometric pressure
Network Credentials
These Wi-Fi credentials are only needed if the board can’t commission over BLE (Arduino prebuild on ESP32 or ESP32-S2), in that case the sketch joins the network itself. When Matter uses BLE commissioning (CHIPoBLE), the hub provides the SSID and password, so you can leave the default SSID/Password placeholders.
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID";
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD";
Matter Endpoints
These next lines create three Matter sensor objects on the ESP32. Each object represents a virtual Matter device that can report its sensor measurement to a Matter controller.
MatterTemperatureSensor TemperatureSensor;
MatterHumiditySensor HumiditySensor;
MatterPressureSensor PressureSensor;
BME280
Define the pins that the BME280 sensor is connected to. We’re using GPIOs 8 and 9 (ESP32-S3).
// I2C pins for the BME280
#define SDA_PIN 8
#define SCL_PIN 9
Create an instance of the Adafruit_BME280 called bme.
Adafruit_BME280 bme;
Variables that will store the sensor readings:
float temperature, humidity, pressure;
The update interval for the BME280 sensor readings (in milliseconds) by default is set to 30000 ms (30 seconds).
const uint32_t SENSOR_INTERVAL_MS = 30000;
BOOT Button
We’ll use the ESP32 on-board BOOT button (usually GPIO 0) for local GPIO control and factory reset (decommissioning).
const uint8_t buttonPin = BOOT_PIN;
MatterButton button;
Take new readings
This function takes a new BME280 measurement: reads temperature, humidity, and pressure. Then, it returns true if all three readings are valid.
bool readBME280() {
temperature = bme.readTemperature();
humidity = bme.readHumidity();
pressure = bme.readPressure() / 100.0F;
return !(isnan(temperature) || isnan(humidity) || isnan(pressure));
}
setup()
The setup() function starts by initializing the buttonPin. It also starts the serial communication.
void setup() {
button.begin(buttonPin);
Serial.begin(115200);
This code section initializes the I2C bus on the specified pins, tries to connect to a BME280 sensor using the address 0x76 or 0x77. Next, it configures the sensor for forced mode measurements with minimal oversampling and no filtering.
// Initialize BME280 Sensor
Wire.begin(I2C_SDA, I2C_SCL);
if (!bme.begin(0x76, &Wire) && !bme.begin(0x77, &Wire)) {
Serial.println("BME280 not found!");
while(1);
}
Depending on your board, it connects to Wi-Fi using the network credentials if CHIPoBLE is disabled.
// 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
Read initial sensor values and initialize Matter endpoints:
readBME280();
TemperatureSensor.begin(temperature);
HumiditySensor.begin(humidity);
PressureSensor.begin(pressure);
The first line begins the Matter stack and waits until it is fully ready.
Matter.begin();
matterWaitUntilReady();
loop()
The loop() starts by calling the matterRestartIfNoFabric() function. This is a helper function that checks whether the device is still part of a Matter fabric (it’s still commissioned to a controller). If the device has no fabric (it’s currently decommissioned), it restarts the Matter stack so it can be discovered and commissioned.
matterRestartIfNoFabric();
This next snippet reads the BME280 sensor every 30 seconds, prints the values to Serial, and updates the three Matter sensor endpoints with the new readings (keeping previous values if the read fails).
static uint32_t lastUpdate = 0;
// Read BME280 sensor values at defined intervals
if (millis() - lastUpdate >= SENSOR_INTERVAL_MS) {
lastUpdate = millis();
if (readBME280()) {
Serial.printf("Temperature: %.2f C | Humidity: %.2f %% | Pressure: %.2f hPa\r\n",
temperature, humidity, pressure);
TemperatureSensor.setTemperature(temperature);
HumiditySensor.setHumidity(humidity);
PressureSensor.setPressure(pressure);
}
}
Declares a variable ev of type matterButtonEvent_t that checks for button events (long press).
matterButtonEvent_t ev;
Finally, if the on-board BOOT button is pressed for more than 5 seconds, it removes the device from all Matter fabrics (factory reset). The device will need to be commissioned again.
while ((ev = button.poll()) != MATTER_BUTTON_NONE) {
if (ev == MATTER_BUTTON_LONG_HOLD) {
Serial.println("Decommissioning Matter Accessory. It shall be commissioned again.");
Matter.decommission();
}
}
Upload Code to Your Board
After inserting your network credentials, follow these next steps to upload the code to the ESP32 board.
First, select your board: Tools – Board – ESP32 Arduino – select your board (for example, ESP32S3 Dev Module).
Then, go to Tools and select the Partition Scheme: “Huge APP (3MB No OT /1MB SPIFFS)”

Important: before the first upload, we recommend erasing all the flash memory. Go to Tools and enable the option Erase All Flash Before Sketch Upload: Enabled. This clears previous Wi-Fi credentials and Matter fabric data. Do this at least for the first upload or when commissioning fails.

Finally, having your board plugged into your computer and with the correct serial port selected, click the Arduino IDE Upload button.

Commissioning Your ESP32 Matter Device
On the first ESP32 boot (or after erasing all the flash memory), open your Arduino IDE Serial Monitor, press the ESP32 on-board RST/EN button

It should print something like this:

You can either use the 11-digit manual pairing code or open the URL in your browser to get a QR code to scan in your smart home app.

Add Matter Device to Amazon Alexa
For this example, we’ll be using the Amazon Alexa app, but in the next section we’ll show the steps to add to other controller applications.
First, open the Amazon Alexa app and tap the + icon:

Choose the Add Device option:

Select Matter logo or Other.

When asked if the device has a logo, select the “YES” option. Then, scan QR code or enter the manual pairing code:

Having your device fairly close to the matter device, click “Connect“.

The ESP32 is not Matter-certified, but add the device by tapping Yes.

Your ESP32 Matter device will be found and connected; click Next.

After successful commissioning, the Arduino IDE serial monitor prints “Matter Node is commissioned…” and the LED reflects the current state.

Your temperature and humidity readings will be added to your Devices list. You can open it as shown in the next image.

Note: the Matter temperature attribute is defined in Celsius. The controller converts it for display, so Alexa shows Fahrenheit if your phone or app settings are set to Fahrenheit.
Add Matter Device to Apple Home, Google Home, Home Assistant, or SmartThings
These are the quick instructions on how to add a Matter device to different controller applications. You must have the controller running in your home like Apple Home, Google Home, Home Asssistant or SmartThings.
- Apple Home:
- Open the iPhone Home app
- Tap the + icon in the top right corner
- Add Accessory
- Scan QR code or tap the “More options” link to enter the manual pairing code
- Google Home:
- Open the Google Home app
- Tap the + icon
- Set up device
- New device
- Matter device
- Scan QR code or enter the manual pairing code
- Home Assistant:
- Open Home Assistant
- Go to Settings
- Devices & Services
- Add Integration
- Matter
- Enter the manual pairing code or scan QR code
- Samsung SmartThings:
- Open the SmartThings app
- Tap the + icon
- Select Partner devices: Matter
- Scan QR code or enter the manual pairing code
Important: after commissioning a Matter device to one controller, you cannot reuse the original QR code or manual pairing code to add it to another controller. Instead, you must open a new commissioning window from the first controller’s app (Share / Turn on Pairing Mode / Link to another platform) to generate a temporary new code, then use that code to share the device into the new platform.
Note: some apps may warn that this is not an official or certified device, but you need to select the option to add it.
Demonstration
From a Matter app (like Alexa) or voice hub, you can view or request the temperature and humidity values.

You can also monitor the readings being sent out every 30 seconds in the Arduino IDE Serial Monitor.

IMPORTANT: you can hold the ESP32 BOOT button for more than 5 seconds to decommission the Matter device (factory reset). The device must be re-commissioned afterwards with a new QR code or by entering the manual pairing code.
Home Assistant Integration
Some Matter controllers (Alexa, Apple Home) don’t support pressure, but Home Assistant does. You can commission this Matter device into your Home Assistant installation to view all three BME280 readings.

Arduino Sketch – ESP32 Matter Device in Deep Sleep Mode: Temperature, Humidity, and Pressure
The following example turns your ESP32 into a functional Matter device (node) that is constantly in deep sleep to save power, so it can be battery powered. Then, it wakes up at regular intervals to send readings from a BME280 sensor.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-matter-temperature-humidity-bme280-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <Arduino.h>
#include <Matter.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include "esp_sleep.h"
// Matter endpoints
MatterTemperatureSensor TemperatureSensor;
MatterHumiditySensor HumiditySensor;
MatterPressureSensor PressureSensor;
// 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"
// I2C pins for the BME280
#define I2C_SDA 8
#define I2C_SCL 9
// These 4 strings are used for Matter device identity and commissioning (They have a limit of 32 characters)
const char* MATTER_VENDOR_NAME = "MyESP32";
const char* MATTER_PRODUCT_NAME = "BME280 Sensor";
const char* MATTER_DEVICE_NAME = "Living Room BME280";
const char* MATTER_SERIAL_NUMBER = "BME-001";
// Hardware version
const uint16_t MATTER_HW_VERSION = 1;
// Hardware version string
const char* MATTER_HW_VERSION_STR = "RevA";
// Commissioning codes: custom manual pairing code / QR code
const uint32_t MATTER_SETUP_PASSCODE = 20202021; // 8-digit setup PIN
const uint16_t MATTER_SETUP_DISCRIMINATOR = 0xF00; // 0–4095
const uint32_t FIRST_AWAKE_MS = 5UL * 60UL * 1000UL; // 5 minutes after first boot or board reset
const uint32_t SENSOR_INTERVAL_MS = 30000; // 30s while fully awake
const uint32_t SLEEP_TIME_SECONDS = 60; // Deep sleep interval
const uint32_t ONLINE_TIMEOUT_MS = 20000; // Wait for CASE on timer wake
const uint32_t POST_UPDATE_AWAKE_MS = 10000; // Stay awake after publish
// Button pin for decommissioning the Matter accessory
const uint8_t buttonPin = BOOT_PIN;
MatterButton button;
Adafruit_BME280 bme;
bool isTimerWake = false;
uint32_t awakeStartMs = 0;
// Read BME280 sensor values
bool readBME280(float &temperature, float &humidity, float &pressure) {
if (!bme.takeForcedMeasurement()) {
return false;
}
temperature = bme.readTemperature();
humidity = bme.readHumidity();
pressure = bme.readPressure() / 100.0F;
return !(isnan(temperature) || isnan(humidity) || isnan(pressure));
}
// Go to deep sleep for SLEEP_TIME_SECONDS
void goToDeepSleep() {
uint64_t sleep_us = (uint64_t)SLEEP_TIME_SECONDS * 1000000ULL;
esp_sleep_enable_timer_wakeup(sleep_us);
Serial.printf("Going to deep sleep for %u seconds...\r\n", SLEEP_TIME_SECONDS);
Serial.flush();
delay(50);
esp_deep_sleep_start();
}
// Update sensor readings and publish to Matter endpoints
void updateSensorsAndMatter() {
float t, h, p;
if (readBME280(t, h, p)) {
Serial.printf("Temperature: %.2f C | Humidity: %.2f %% | Pressure: %.2f hPa\r\n", t, h, p);
TemperatureSensor.setTemperature(t);
HumiditySensor.setHumidity(h);
PressureSensor.setPressure(p);
} else {
Serial.println("BME280 read failed.");
}
}
// Handle button events for decommissioning
void handleButton() {
matterButtonEvent_t ev;
while ((ev = button.poll()) != MATTER_BUTTON_NONE) {
if (ev == MATTER_BUTTON_LONG_HOLD) {
Serial.println("Decommissioning Matter Accessory. It shall be commissioned again.");
Matter.decommission();
}
}
}
// Apply Matter device identity and commissioning codes
void applyMatterIdentity() {
// These configurations must be called BEFORE Matter.begin()
Matter.setVendorName(MATTER_VENDOR_NAME);
Matter.setProductName(MATTER_PRODUCT_NAME);
Matter.setDeviceName(MATTER_DEVICE_NAME);
Matter.setSerialNumber(MATTER_SERIAL_NUMBER);
Matter.setHardwareVersion(MATTER_HW_VERSION);
Matter.setHardwareVersionString(MATTER_HW_VERSION_STR);
Matter.setSetupPasscode(MATTER_SETUP_PASSCODE);
Matter.setSetupDiscriminator(MATTER_SETUP_DISCRIMINATOR);
Serial.println("Matter identity configured:");
Serial.printf(" Vendor: %s\r\n", MATTER_VENDOR_NAME);
Serial.printf(" Product: %s\r\n", MATTER_PRODUCT_NAME);
Serial.printf(" Device: %s\r\n", MATTER_DEVICE_NAME);
Serial.printf(" Passcode: %lu\r\n", (unsigned long)MATTER_SETUP_PASSCODE);
Serial.printf(" Discriminator: 0x%03X (%u)\r\n", MATTER_SETUP_DISCRIMINATOR, MATTER_SETUP_DISCRIMINATOR);
}
void setup() {
button.begin(buttonPin);
Serial.begin(115200);
delay(100);
// Check wakeup reason
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
isTimerWake = (cause == ESP_SLEEP_WAKEUP_TIMER);
if (isTimerWake) {
Serial.println("Wakeup caused by timer. It will go back to deep sleep after publishing readings.");
} else {
Serial.println("ESP32 first boot / reset. It will stay awake for 5 minutes to allow commissioning.");
}
// Initialize BME280 sensor
Wire.begin(I2C_SDA, I2C_SCL);
if (!bme.begin(0x76, &Wire) && !bme.begin(0x77, &Wire)) {
Serial.println("BME280 not found - check wiring and I2C pins.");
while (true) delay(1000);
}
bme.setSampling(
Adafruit_BME280::MODE_FORCED,
Adafruit_BME280::SAMPLING_X1,
Adafruit_BME280::SAMPLING_X1,
Adafruit_BME280::SAMPLING_X1,
Adafruit_BME280::FILTER_OFF
);
// Connect to Wi-Fi if CHIPoBLE is not enabled
#if !CONFIG_ENABLE_CHIPOBLE
matterConnectWiFi(WIFI_SSID, WIFI_PASSWORD);
#endif
// Read initial sensor values and initialize Matter endpoints
float t = 0, h = 0, p = 0;
if (!readBME280(t, h, p)) {
t = h = p = 0;
}
TemperatureSensor.begin(t);
HumiditySensor.begin(h);
PressureSensor.begin(p);
// Apply Matter identity and commissioning codes
applyMatterIdentity();
// Start Matter stack and wait until ready
Matter.begin();
matterWaitUntilReady();
// If not commissioned, print manual pairing code and QR code URL
if (!Matter.isDeviceCommissioned()) {
Serial.println("----------------------------------------");
Serial.println("Device not commissioned yet.");
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
Serial.println("----------------------------------------");
}
matterRestartIfNoFabric();
// If this is a timer wakeup, wait for CASE session and publish readings
if (isTimerWake) {
uint32_t t0 = millis();
while (!Matter.isOnline() && (millis() - t0) < ONLINE_TIMEOUT_MS) {
matterRestartIfNoFabric();
handleButton();
delay(100);
}
if (Matter.isOnline()) {
Serial.println("Controller online (CASE up) – publishing readings");
} else {
Serial.println("WARNING: Controller not online – publishing anyway");
}
// Publish sensor values
updateSensorsAndMatter();
// Stay awake so reports can reach Alexa; re-publish halfway
Serial.printf("Staying awake %u ms...\r\n", POST_UPDATE_AWAKE_MS);
t0 = millis();
bool resent = false;
while (millis() - t0 < POST_UPDATE_AWAKE_MS) {
matterRestartIfNoFabric();
handleButton();
if (!resent && (millis() - t0) > (POST_UPDATE_AWAKE_MS / 2)) {
Serial.println("Re-publishing readings...");
updateSensorsAndMatter();
resent = true;
}
delay(50);
}
goToDeepSleep();
}
// First boot / reset: one update, then 5 minute fully awake to allow commissioning to other controllers
updateSensorsAndMatter();
awakeStartMs = millis();
Serial.println("Entering 5 minutes awake period...");
}
void loop() {
// Check if the device is still commissioned. If not, restart to allow re-commissioning
matterRestartIfNoFabric();
handleButton();
// Update sensor readings every SENSOR_INTERVAL_MS
static uint32_t lastUpdate = 0;
if (millis() - lastUpdate >= SENSOR_INTERVAL_MS) {
lastUpdate = millis();
updateSensorsAndMatter();
}
// After 5 minutes, go to deep sleep
if (millis() - awakeStartMs >= FIRST_AWAKE_MS) {
Serial.println("5 minutes finished: entering deep sleep cycle.");
goToDeepSleep();
}
delay(50);
}
You must select in the Tools – Partition Scheme: “Huge APP (3MB No OT /1MB SPIFFS)” and the code will work straight away. Depending on your board, you might need to insert your network credentials in the following lines in the code.
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID";
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD";
When you board first powers on or resets, it stays fully awake for 5 minutes, reading the BME280 and sending temperature, humidity, and pressure to the Matter controller every 30 seconds while also watching the BOOT button for a long press decommission or re-commissioned to another controller.

After that 5-minute window, your board enters into the following deep sleep cycle:
- It deep sleeps for 60 seconds (wakes on a timer wake up).
- Reconnects Wi‑Fi/Matter.
- Publishes the latest sensor values.
- Stays awake another 20 seconds (re-publishing once halfway through so your controller has a better chance to receive the reports).
- Finally, goes back to deep sleep.
As described earlier, powering the board or pressing the on-board reset button wakes your ESP32 for 5 minutes, so you can decommission or share it with another controller.

Troubleshooting
The following table shows the most common errors when following this tutorial, and possible solutions.
| Error | Solution |
| Sketch too large / not enough space / compilation fails | In the Tools menu, you must select the option: Partition Scheme: “Huge APP (3MB No OT /1MB SPIFFS)” |
| Device never shows the pairing code | Check that you’ve entered the correct SSID/password in your code |
| Commissioning fails after previous attempt / Want to re-commission to another controller | – Press the BOOT button for 5 seconds – Or you can also go to Tools and enable the option Erase All Flash Before Sketch Upload: Enabled, then re-upload the code to your board |
| No serial output printed | Press the RST/EN button to restart the ESP32 while the Arduino IDE Serial Monitor is open with 115200 baud rate with the correct serial port selected |
Note: in the Arduino IDE Serial monitor, you might see many log messages being printed over time and right after setup. You might see messages like “sending failed”, “transient send error”, “request on unknown cluster”, or similar. They’re usually harmless, and you can ignore them unless the device stays unreachable.
Wrapping Up
In this guide, you learned how to turn your ESP32 into a Matter device that reports temperature, humidity and pressure readings from a BME280 sensor. You’ve also learned the basics about the Matter protocol and how to commission the device to different controllers, such as Alexa, Apple Home, Google Home, Home Assistant, and SmartThings.
We have a similar tutorial for an On/Off Matter device, check it out below:
If you liked this project, you may also like these other tutorials:
- Building an ESP32 Web Server: The Complete Guide for Beginners
- Getting Started with ESP-NOW (ESP32 with Arduino IDE)
- What is MQTT and How It Works
- ESP32 Save Data Permanently using Preferences Library
Thanks for reading.




