Skip to content

Commit 73b4574

Browse files
committed
examples/sensors: Example to demonstrate few sensors
Added the temperature, humidity, and occupancy sensor on different endpoints. Fixes #1105
1 parent 2f4ddec commit 73b4574

15 files changed

+786
-0
lines changed

examples/.build-rules.yml

+6
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,9 @@ examples/bridge_apps/bridge_cli:
128128
- if: IDF_TARGET in ["esp32", "esp32c3"]
129129
temporary: true
130130
reason: the other targets are not tested yet
131+
132+
examples/sensors:
133+
enable:
134+
- if: IDF_TARGET in ["esp32c3"]
135+
temporary: true
136+
reason: the other targets are not tested yet

examples/sensors/CMakeLists.txt

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# The following lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.5)
4+
5+
if(NOT DEFINED ENV{ESP_MATTER_PATH})
6+
message(FATAL_ERROR "Please set ESP_MATTER_PATH to the path of esp-matter repo")
7+
endif(NOT DEFINED ENV{ESP_MATTER_PATH})
8+
9+
set(PROJECT_VER "1.0")
10+
set(PROJECT_VER_NUMBER 1)
11+
12+
set(ESP_MATTER_PATH $ENV{ESP_MATTER_PATH})
13+
set(MATTER_SDK_PATH ${ESP_MATTER_PATH}/connectedhomeip/connectedhomeip)
14+
15+
# This should be done before using the IDF_TARGET variable.
16+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
17+
18+
set(EXTRA_COMPONENT_DIRS
19+
"${ESP_MATTER_PATH}/examples/common"
20+
"${MATTER_SDK_PATH}/config/esp32/components"
21+
"${ESP_MATTER_PATH}/components"
22+
${extra_components_dirs_append})
23+
24+
project(sensors)
25+
26+
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H;-Wno-overloaded-virtual" APPEND)
27+
idf_build_set_property(C_COMPILE_OPTIONS "-Os" APPEND)
28+
# For RISCV chips, project_include.cmake sets -Wno-format, but does not clear various
29+
# flags that depend on -Wformat
30+
idf_build_set_property(COMPILE_OPTIONS "-Wno-format-nonliteral;-Wno-format-security" APPEND)

examples/sensors/README.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Matter Sensors
2+
3+
This example demonstrates the integration of temperature and humidity sensors (SHTC3)
4+
and an occupancy sensor (PIR).
5+
6+
This application creates the temperature sensor, humidity sensor, and occupancy sensor
7+
on endpoint 1, 2, and 3 respectively.
8+
9+
See the [docs](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html)
10+
for more information about building and flashing the firmware.
11+
12+
## Connecting the sensors
13+
14+
- Connecting the SHTC3, temperature and humidity sensor
15+
16+
| ESP32-C3 Pin | SHTC3 Pin |
17+
|--------------|-----------|
18+
| GND | GND |
19+
| 3V3 | VCC |
20+
| GPIO 4 | SDA |
21+
| GPIO 5 | SCL |
22+
23+
- Connecting the PIR sensor
24+
25+
| ESP32-C3 Pin | PIR Pin |
26+
|--------------|---------|
27+
| GND | GND |
28+
| 3V3 | VCC |
29+
| GPIO 7 | Output |
30+
31+
**_NOTE:_**:
32+
- Above mentioned wiring connection is configured by default in the example.
33+
- Ensure that the GPIO pins used for the sensors are correctly configured through menuconfig.
34+
- Modify the configuration parameters as needed for your specific hardware setup.
35+
36+
## Usage
37+
38+
- Commission the app using Matter controller and read the attributes.
39+
40+
Below, we are using chip-tool to commission and subscribe the sensor attributes.
41+
-
42+
```
43+
# Commission
44+
chip-tool pairing ble-wifi 1 (SSID) (PASSPHRASE) 20202021 3840
45+
46+
# Start chip-tool in interactive mode
47+
chip-tool interactive start
48+
49+
# Subscribe to attributes
50+
> temperaturemeasurement subscribe measured-value 3 10 1 1
51+
> relativehumiditymeasurement subscribe measured-value 3 10 1 2
52+
> occupancysensing subscribe occupancy 3 10 1 3
53+
```

examples/sensors/main/CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
idf_component_register(SRC_DIRS "." "drivers"
2+
PRIV_INCLUDE_DIRS "." "drivers" "${ESP_MATTER_PATH}/examples/common/utils")
3+
4+
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17)
5+
target_compile_options(${COMPONENT_LIB} PRIVATE "-DCHIP_HAVE_CONFIG_H")
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
menu "Example Configuration"
2+
3+
config SHTC3_I2C_SDA_PIN
4+
int "I2C SDA Pin"
5+
default 4
6+
help
7+
GPIO number for I2C master data
8+
9+
config SHTC3_I2C_SCL_PIN
10+
int "I2C SCL Pin"
11+
default 5
12+
help
13+
GPIO number for I2C master clock
14+
15+
config PIR_DATA_PIN
16+
int "PIR Data Pin"
17+
default 7
18+
help
19+
Default PIR Data Pin
20+
21+
endmenu

examples/sensors/main/app_main.cpp

+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*
2+
This example code is in the Public Domain (or CC0 licensed, at your option.)
3+
4+
Unless required by applicable law or agreed to in writing, this
5+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
6+
CONDITIONS OF ANY KIND, either express or implied.
7+
*/
8+
9+
#include <app/server/CommissioningWindowManager.h>
10+
#include <app/server/Server.h>
11+
#include <bsp/esp-bsp.h>
12+
#include <esp_err.h>
13+
#include <esp_log.h>
14+
#include <esp_matter.h>
15+
#include <esp_matter_ota.h>
16+
#include <nvs_flash.h>
17+
18+
#include <app_openthread_config.h>
19+
#include <app_reset.h>
20+
#include <common_macros.h>
21+
22+
// drivers implemented by this example
23+
#include <drivers/shtc3.h>
24+
#include <drivers/pir.h>
25+
26+
static const char *TAG = "app_main";
27+
28+
using namespace esp_matter;
29+
using namespace esp_matter::attribute;
30+
using namespace esp_matter::endpoint;
31+
using namespace chip::app::Clusters;
32+
33+
// Application cluster specification, 7.18.2.11. Temperature
34+
// represents a temperature on the Celsius scale with a resolution of 0.01°C.
35+
// temp = (temperature in °C) x 100
36+
static void temp_sensor_notification(uint16_t endpoint_id, float temp, void *user_data)
37+
{
38+
// schedule the attribute update so that we can report it from matter thread
39+
chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, temp]() {
40+
attribute_t * attribute = attribute::get(endpoint_id,
41+
TemperatureMeasurement::Id,
42+
TemperatureMeasurement::Attributes::MeasuredValue::Id);
43+
44+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
45+
attribute::get_val(attribute, &val);
46+
val.val.i16 = static_cast<int16_t>(temp * 100);
47+
48+
attribute::update(endpoint_id, TemperatureMeasurement::Id, TemperatureMeasurement::Attributes::MeasuredValue::Id, &val);
49+
});
50+
}
51+
52+
// Application cluster specification, 2.6.4.1. MeasuredValue Attribute
53+
// represents the humidity in percent.
54+
// humidity = (humidity in %) x 100
55+
static void humidity_sensor_notification(uint16_t endpoint_id, float humidity, void *user_data)
56+
{
57+
// schedule the attribute update so that we can report it from matter thread
58+
chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, humidity]() {
59+
attribute_t * attribute = attribute::get(endpoint_id,
60+
RelativeHumidityMeasurement::Id,
61+
RelativeHumidityMeasurement::Attributes::MeasuredValue::Id);
62+
63+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
64+
attribute::get_val(attribute, &val);
65+
val.val.u16 = static_cast<uint16_t>(humidity * 100);
66+
67+
attribute::update(endpoint_id, RelativeHumidityMeasurement::Id, RelativeHumidityMeasurement::Attributes::MeasuredValue::Id, &val);
68+
});
69+
}
70+
71+
static void occupancy_sensor_notification(uint16_t endpoint_id, bool occupancy, void *user_data)
72+
{
73+
// schedule the attribute update so that we can report it from matter thread
74+
chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, occupancy]() {
75+
attribute_t * attribute = attribute::get(endpoint_id,
76+
OccupancySensing::Id,
77+
OccupancySensing::Attributes::Occupancy::Id);
78+
79+
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
80+
attribute::get_val(attribute, &val);
81+
val.val.b = occupancy;
82+
83+
attribute::update(endpoint_id, OccupancySensing::Id, OccupancySensing::Attributes::Occupancy::Id, &val);
84+
});
85+
}
86+
87+
static esp_err_t factory_reset_button_register()
88+
{
89+
button_handle_t push_button;
90+
esp_err_t err = bsp_iot_button_create(&push_button, NULL, BSP_BUTTON_NUM);
91+
VerifyOrReturnError(err == ESP_OK, err);
92+
return app_reset_button_register(push_button);
93+
}
94+
95+
static void open_commissioning_window_if_necessary()
96+
{
97+
VerifyOrReturn(chip::Server::GetInstance().GetFabricTable().FabricCount() == 0);
98+
99+
chip::CommissioningWindowManager & commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
100+
VerifyOrReturn(commissionMgr.IsCommissioningWindowOpen() == false);
101+
102+
// After removing last fabric, this example does not remove the Wi-Fi credentials
103+
// and still has IP connectivity so, only advertising on DNS-SD.
104+
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(chip::System::Clock::Seconds16(300),
105+
chip::CommissioningWindowAdvertisement::kDnssdOnly);
106+
if (err != CHIP_NO_ERROR)
107+
{
108+
ESP_LOGE(TAG, "Failed to open commissioning window, err:%" CHIP_ERROR_FORMAT, err.Format());
109+
}
110+
}
111+
112+
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg)
113+
{
114+
switch (event->Type) {
115+
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete:
116+
ESP_LOGI(TAG, "Commissioning complete");
117+
break;
118+
119+
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired:
120+
ESP_LOGI(TAG, "Commissioning failed, fail safe timer expired");
121+
break;
122+
123+
case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
124+
ESP_LOGI(TAG, "Fabric removed successfully");
125+
open_commissioning_window_if_necessary();
126+
break;
127+
128+
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized:
129+
ESP_LOGI(TAG, "BLE deinitialized and memory reclaimed");
130+
break;
131+
132+
default:
133+
break;
134+
}
135+
}
136+
137+
// This callback is invoked when clients interact with the Identify Cluster.
138+
// In the callback implementation, an endpoint can identify itself. (e.g., by flashing an LED or light).
139+
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id,
140+
uint8_t effect_variant, void *priv_data)
141+
{
142+
ESP_LOGI(TAG, "Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
143+
return ESP_OK;
144+
}
145+
146+
// This callback is called for every attribute update. The callback implementation shall
147+
// handle the desired attributes and return an appropriate error code. If the attribute
148+
// is not of your interest, please do not return an error code and strictly return ESP_OK.
149+
static esp_err_t app_attribute_update_cb(attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id,
150+
uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data)
151+
{
152+
// Since this is just a sensor and we don't expect any writes on our temperature sensor,
153+
// so, return success.
154+
return ESP_OK;
155+
}
156+
157+
extern "C" void app_main()
158+
{
159+
/* Initialize the ESP NVS layer */
160+
nvs_flash_init();
161+
162+
/* Initialize push button on the dev-kit to reset the device */
163+
esp_err_t err = factory_reset_button_register();
164+
ABORT_APP_ON_FAILURE(ESP_OK == err, ESP_LOGE(TAG, "Failed to initialize reset button, err:%d", err));
165+
166+
/* Create a Matter node and add the mandatory Root Node device type on endpoint 0 */
167+
node::config_t node_config;
168+
node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
169+
ABORT_APP_ON_FAILURE(node != nullptr, ESP_LOGE(TAG, "Failed to create Matter node"));
170+
171+
// add temperature sensor device
172+
temperature_sensor::config_t temp_sensor_config;
173+
endpoint_t * temp_sensor_ep = temperature_sensor::create(node, &temp_sensor_config, ENDPOINT_FLAG_NONE, NULL);
174+
ABORT_APP_ON_FAILURE(temp_sensor_ep != nullptr, ESP_LOGE(TAG, "Failed to create temperature_sensor endpoint"));
175+
176+
// add the humidity sensor device
177+
humidity_sensor::config_t humidity_sensor_config;
178+
endpoint_t * humidity_sensor_ep = humidity_sensor::create(node, &humidity_sensor_config, ENDPOINT_FLAG_NONE, NULL);
179+
ABORT_APP_ON_FAILURE(humidity_sensor_ep != nullptr, ESP_LOGE(TAG, "Failed to create humidity_sensor endpoint"));
180+
181+
// initialize temperature and humidity sensor driver (shtc3)
182+
static shtc3_sensor_config_t shtc3_config = {
183+
.temperature = {
184+
.cb = temp_sensor_notification,
185+
.endpoint_id = endpoint::get_id(temp_sensor_ep),
186+
},
187+
.humidity = {
188+
.cb = humidity_sensor_notification,
189+
.endpoint_id = endpoint::get_id(humidity_sensor_ep),
190+
},
191+
};
192+
err = shtc3_sensor_init(&shtc3_config);
193+
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to initialize temperature sensor driver"));
194+
195+
// add the occupancy sensor
196+
occupancy_sensor::config_t occupancy_sensor_config;
197+
occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type =
198+
chip::to_underlying(OccupancySensing::OccupancySensorTypeEnum::kPir);
199+
occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type_bitmap =
200+
chip::to_underlying(OccupancySensing::OccupancySensorTypeBitmap::kPir);
201+
202+
endpoint_t * occupancy_sensor_ep = occupancy_sensor::create(node, &occupancy_sensor_config, ENDPOINT_FLAG_NONE, NULL);
203+
ABORT_APP_ON_FAILURE(occupancy_sensor_ep != nullptr, ESP_LOGE(TAG, "Failed to create occupancy_sensor endpoint"));
204+
205+
// initialize occupancy sensor driver (pir)
206+
static pir_sensor_config_t pir_config = {
207+
.cb = occupancy_sensor_notification,
208+
.endpoint_id = endpoint::get_id(occupancy_sensor_ep),
209+
};
210+
err = pir_sensor_init(&pir_config);
211+
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to initialize occupancy sensor driver"));
212+
213+
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
214+
/* Set OpenThread platform config */
215+
esp_openthread_platform_config_t config = {
216+
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
217+
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
218+
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
219+
};
220+
set_openthread_platform_config(&config);
221+
#endif
222+
223+
/* Matter start */
224+
err = esp_matter::start(app_event_cb);
225+
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to start Matter, err:%d", err));
226+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
This example code is in the Public Domain (or CC0 licensed, at your option.)
3+
4+
Unless required by applicable law or agreed to in writing, this
5+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
6+
CONDITIONS OF ANY KIND, either express or implied.
7+
*/
8+
9+
#pragma once
10+
11+
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
12+
#include <platform/ESP32/OpenthreadLauncher.h>
13+
#include "esp_openthread_types.h"
14+
15+
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
16+
{ \
17+
.radio_mode = RADIO_MODE_NATIVE, \
18+
}
19+
20+
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
21+
{ \
22+
.host_connection_mode = HOST_CONNECTION_MODE_NONE, \
23+
}
24+
25+
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
26+
{ \
27+
.storage_partition_name = "nvs", \
28+
.netif_queue_size = 10, \
29+
.task_queue_size = 10, \
30+
}
31+
#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD

0 commit comments

Comments
 (0)