mirror of
https://github.com/espressif/esp-matter.git
synced 2026-04-27 11:03:05 +00:00
examples/sensors: Example to demonstrate few sensors
Added the temperature, humidity, and occupancy sensor on different endpoints. Fixes https://github.com/espressif/esp-matter/issues/1105
This commit is contained in:
@@ -128,3 +128,9 @@ examples/bridge_apps/bridge_cli:
|
||||
- if: IDF_TARGET in ["esp32", "esp32c3"]
|
||||
temporary: true
|
||||
reason: the other targets are not tested yet
|
||||
|
||||
examples/sensors:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32c3"]
|
||||
temporary: true
|
||||
reason: the other targets are not tested yet
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
if(NOT DEFINED ENV{ESP_MATTER_PATH})
|
||||
message(FATAL_ERROR "Please set ESP_MATTER_PATH to the path of esp-matter repo")
|
||||
endif(NOT DEFINED ENV{ESP_MATTER_PATH})
|
||||
|
||||
set(PROJECT_VER "1.0")
|
||||
set(PROJECT_VER_NUMBER 1)
|
||||
|
||||
set(ESP_MATTER_PATH $ENV{ESP_MATTER_PATH})
|
||||
set(MATTER_SDK_PATH ${ESP_MATTER_PATH}/connectedhomeip/connectedhomeip)
|
||||
|
||||
# This should be done before using the IDF_TARGET variable.
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"${ESP_MATTER_PATH}/examples/common"
|
||||
"${MATTER_SDK_PATH}/config/esp32/components"
|
||||
"${ESP_MATTER_PATH}/components"
|
||||
${extra_components_dirs_append})
|
||||
|
||||
project(sensors)
|
||||
|
||||
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H;-Wno-overloaded-virtual" APPEND)
|
||||
idf_build_set_property(C_COMPILE_OPTIONS "-Os" APPEND)
|
||||
# For RISCV chips, project_include.cmake sets -Wno-format, but does not clear various
|
||||
# flags that depend on -Wformat
|
||||
idf_build_set_property(COMPILE_OPTIONS "-Wno-format-nonliteral;-Wno-format-security" APPEND)
|
||||
@@ -0,0 +1,53 @@
|
||||
# Matter Sensors
|
||||
|
||||
This example demonstrates the integration of temperature and humidity sensors (SHTC3)
|
||||
and an occupancy sensor (PIR).
|
||||
|
||||
This application creates the temperature sensor, humidity sensor, and occupancy sensor
|
||||
on endpoint 1, 2, and 3 respectively.
|
||||
|
||||
See the [docs](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html)
|
||||
for more information about building and flashing the firmware.
|
||||
|
||||
## Connecting the sensors
|
||||
|
||||
- Connecting the SHTC3, temperature and humidity sensor
|
||||
|
||||
| ESP32-C3 Pin | SHTC3 Pin |
|
||||
|--------------|-----------|
|
||||
| GND | GND |
|
||||
| 3V3 | VCC |
|
||||
| GPIO 4 | SDA |
|
||||
| GPIO 5 | SCL |
|
||||
|
||||
- Connecting the PIR sensor
|
||||
|
||||
| ESP32-C3 Pin | PIR Pin |
|
||||
|--------------|---------|
|
||||
| GND | GND |
|
||||
| 3V3 | VCC |
|
||||
| GPIO 7 | Output |
|
||||
|
||||
**_NOTE:_**:
|
||||
- Above mentioned wiring connection is configured by default in the example.
|
||||
- Ensure that the GPIO pins used for the sensors are correctly configured through menuconfig.
|
||||
- Modify the configuration parameters as needed for your specific hardware setup.
|
||||
|
||||
## Usage
|
||||
|
||||
- Commission the app using Matter controller and read the attributes.
|
||||
|
||||
Below, we are using chip-tool to commission and subscribe the sensor attributes.
|
||||
-
|
||||
```
|
||||
# Commission
|
||||
chip-tool pairing ble-wifi 1 (SSID) (PASSPHRASE) 20202021 3840
|
||||
|
||||
# Start chip-tool in interactive mode
|
||||
chip-tool interactive start
|
||||
|
||||
# Subscribe to attributes
|
||||
> temperaturemeasurement subscribe measured-value 3 10 1 1
|
||||
> relativehumiditymeasurement subscribe measured-value 3 10 1 2
|
||||
> occupancysensing subscribe occupancy 3 10 1 3
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(SRC_DIRS "." "drivers"
|
||||
PRIV_INCLUDE_DIRS "." "drivers" "${ESP_MATTER_PATH}/examples/common/utils")
|
||||
|
||||
set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-DCHIP_HAVE_CONFIG_H")
|
||||
@@ -0,0 +1,21 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config SHTC3_I2C_SDA_PIN
|
||||
int "I2C SDA Pin"
|
||||
default 4
|
||||
help
|
||||
GPIO number for I2C master data
|
||||
|
||||
config SHTC3_I2C_SCL_PIN
|
||||
int "I2C SCL Pin"
|
||||
default 5
|
||||
help
|
||||
GPIO number for I2C master clock
|
||||
|
||||
config PIR_DATA_PIN
|
||||
int "PIR Data Pin"
|
||||
default 7
|
||||
help
|
||||
Default PIR Data Pin
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <app/server/CommissioningWindowManager.h>
|
||||
#include <app/server/Server.h>
|
||||
#include <bsp/esp-bsp.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_matter.h>
|
||||
#include <esp_matter_ota.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#include <app_openthread_config.h>
|
||||
#include <app_reset.h>
|
||||
#include <common_macros.h>
|
||||
|
||||
// drivers implemented by this example
|
||||
#include <drivers/shtc3.h>
|
||||
#include <drivers/pir.h>
|
||||
|
||||
static const char *TAG = "app_main";
|
||||
|
||||
using namespace esp_matter;
|
||||
using namespace esp_matter::attribute;
|
||||
using namespace esp_matter::endpoint;
|
||||
using namespace chip::app::Clusters;
|
||||
|
||||
// Application cluster specification, 7.18.2.11. Temperature
|
||||
// represents a temperature on the Celsius scale with a resolution of 0.01°C.
|
||||
// temp = (temperature in °C) x 100
|
||||
static void temp_sensor_notification(uint16_t endpoint_id, float temp, void *user_data)
|
||||
{
|
||||
// schedule the attribute update so that we can report it from matter thread
|
||||
chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, temp]() {
|
||||
attribute_t * attribute = attribute::get(endpoint_id,
|
||||
TemperatureMeasurement::Id,
|
||||
TemperatureMeasurement::Attributes::MeasuredValue::Id);
|
||||
|
||||
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
|
||||
attribute::get_val(attribute, &val);
|
||||
val.val.i16 = static_cast<int16_t>(temp * 100);
|
||||
|
||||
attribute::update(endpoint_id, TemperatureMeasurement::Id, TemperatureMeasurement::Attributes::MeasuredValue::Id, &val);
|
||||
});
|
||||
}
|
||||
|
||||
// Application cluster specification, 2.6.4.1. MeasuredValue Attribute
|
||||
// represents the humidity in percent.
|
||||
// humidity = (humidity in %) x 100
|
||||
static void humidity_sensor_notification(uint16_t endpoint_id, float humidity, void *user_data)
|
||||
{
|
||||
// schedule the attribute update so that we can report it from matter thread
|
||||
chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, humidity]() {
|
||||
attribute_t * attribute = attribute::get(endpoint_id,
|
||||
RelativeHumidityMeasurement::Id,
|
||||
RelativeHumidityMeasurement::Attributes::MeasuredValue::Id);
|
||||
|
||||
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
|
||||
attribute::get_val(attribute, &val);
|
||||
val.val.u16 = static_cast<uint16_t>(humidity * 100);
|
||||
|
||||
attribute::update(endpoint_id, RelativeHumidityMeasurement::Id, RelativeHumidityMeasurement::Attributes::MeasuredValue::Id, &val);
|
||||
});
|
||||
}
|
||||
|
||||
static void occupancy_sensor_notification(uint16_t endpoint_id, bool occupancy, void *user_data)
|
||||
{
|
||||
// schedule the attribute update so that we can report it from matter thread
|
||||
chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, occupancy]() {
|
||||
attribute_t * attribute = attribute::get(endpoint_id,
|
||||
OccupancySensing::Id,
|
||||
OccupancySensing::Attributes::Occupancy::Id);
|
||||
|
||||
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
|
||||
attribute::get_val(attribute, &val);
|
||||
val.val.b = occupancy;
|
||||
|
||||
attribute::update(endpoint_id, OccupancySensing::Id, OccupancySensing::Attributes::Occupancy::Id, &val);
|
||||
});
|
||||
}
|
||||
|
||||
static esp_err_t factory_reset_button_register()
|
||||
{
|
||||
button_handle_t push_button;
|
||||
esp_err_t err = bsp_iot_button_create(&push_button, NULL, BSP_BUTTON_NUM);
|
||||
VerifyOrReturnError(err == ESP_OK, err);
|
||||
return app_reset_button_register(push_button);
|
||||
}
|
||||
|
||||
static void open_commissioning_window_if_necessary()
|
||||
{
|
||||
VerifyOrReturn(chip::Server::GetInstance().GetFabricTable().FabricCount() == 0);
|
||||
|
||||
chip::CommissioningWindowManager & commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
|
||||
VerifyOrReturn(commissionMgr.IsCommissioningWindowOpen() == false);
|
||||
|
||||
// After removing last fabric, this example does not remove the Wi-Fi credentials
|
||||
// and still has IP connectivity so, only advertising on DNS-SD.
|
||||
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(chip::System::Clock::Seconds16(300),
|
||||
chip::CommissioningWindowAdvertisement::kDnssdOnly);
|
||||
if (err != CHIP_NO_ERROR)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to open commissioning window, err:%" CHIP_ERROR_FORMAT, err.Format());
|
||||
}
|
||||
}
|
||||
|
||||
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg)
|
||||
{
|
||||
switch (event->Type) {
|
||||
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete:
|
||||
ESP_LOGI(TAG, "Commissioning complete");
|
||||
break;
|
||||
|
||||
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired:
|
||||
ESP_LOGI(TAG, "Commissioning failed, fail safe timer expired");
|
||||
break;
|
||||
|
||||
case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
|
||||
ESP_LOGI(TAG, "Fabric removed successfully");
|
||||
open_commissioning_window_if_necessary();
|
||||
break;
|
||||
|
||||
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized:
|
||||
ESP_LOGI(TAG, "BLE deinitialized and memory reclaimed");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This callback is invoked when clients interact with the Identify Cluster.
|
||||
// In the callback implementation, an endpoint can identify itself. (e.g., by flashing an LED or light).
|
||||
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id,
|
||||
uint8_t effect_variant, void *priv_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// This callback is called for every attribute update. The callback implementation shall
|
||||
// handle the desired attributes and return an appropriate error code. If the attribute
|
||||
// is not of your interest, please do not return an error code and strictly return ESP_OK.
|
||||
static esp_err_t app_attribute_update_cb(attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id,
|
||||
uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data)
|
||||
{
|
||||
// Since this is just a sensor and we don't expect any writes on our temperature sensor,
|
||||
// so, return success.
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
extern "C" void app_main()
|
||||
{
|
||||
/* Initialize the ESP NVS layer */
|
||||
nvs_flash_init();
|
||||
|
||||
/* Initialize push button on the dev-kit to reset the device */
|
||||
esp_err_t err = factory_reset_button_register();
|
||||
ABORT_APP_ON_FAILURE(ESP_OK == err, ESP_LOGE(TAG, "Failed to initialize reset button, err:%d", err));
|
||||
|
||||
/* Create a Matter node and add the mandatory Root Node device type on endpoint 0 */
|
||||
node::config_t node_config;
|
||||
node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
|
||||
ABORT_APP_ON_FAILURE(node != nullptr, ESP_LOGE(TAG, "Failed to create Matter node"));
|
||||
|
||||
// add temperature sensor device
|
||||
temperature_sensor::config_t temp_sensor_config;
|
||||
endpoint_t * temp_sensor_ep = temperature_sensor::create(node, &temp_sensor_config, ENDPOINT_FLAG_NONE, NULL);
|
||||
ABORT_APP_ON_FAILURE(temp_sensor_ep != nullptr, ESP_LOGE(TAG, "Failed to create temperature_sensor endpoint"));
|
||||
|
||||
// add the humidity sensor device
|
||||
humidity_sensor::config_t humidity_sensor_config;
|
||||
endpoint_t * humidity_sensor_ep = humidity_sensor::create(node, &humidity_sensor_config, ENDPOINT_FLAG_NONE, NULL);
|
||||
ABORT_APP_ON_FAILURE(humidity_sensor_ep != nullptr, ESP_LOGE(TAG, "Failed to create humidity_sensor endpoint"));
|
||||
|
||||
// initialize temperature and humidity sensor driver (shtc3)
|
||||
static shtc3_sensor_config_t shtc3_config = {
|
||||
.temperature = {
|
||||
.cb = temp_sensor_notification,
|
||||
.endpoint_id = endpoint::get_id(temp_sensor_ep),
|
||||
},
|
||||
.humidity = {
|
||||
.cb = humidity_sensor_notification,
|
||||
.endpoint_id = endpoint::get_id(humidity_sensor_ep),
|
||||
},
|
||||
};
|
||||
err = shtc3_sensor_init(&shtc3_config);
|
||||
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to initialize temperature sensor driver"));
|
||||
|
||||
// add the occupancy sensor
|
||||
occupancy_sensor::config_t occupancy_sensor_config;
|
||||
occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type =
|
||||
chip::to_underlying(OccupancySensing::OccupancySensorTypeEnum::kPir);
|
||||
occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type_bitmap =
|
||||
chip::to_underlying(OccupancySensing::OccupancySensorTypeBitmap::kPir);
|
||||
|
||||
endpoint_t * occupancy_sensor_ep = occupancy_sensor::create(node, &occupancy_sensor_config, ENDPOINT_FLAG_NONE, NULL);
|
||||
ABORT_APP_ON_FAILURE(occupancy_sensor_ep != nullptr, ESP_LOGE(TAG, "Failed to create occupancy_sensor endpoint"));
|
||||
|
||||
// initialize occupancy sensor driver (pir)
|
||||
static pir_sensor_config_t pir_config = {
|
||||
.cb = occupancy_sensor_notification,
|
||||
.endpoint_id = endpoint::get_id(occupancy_sensor_ep),
|
||||
};
|
||||
err = pir_sensor_init(&pir_config);
|
||||
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to initialize occupancy sensor driver"));
|
||||
|
||||
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
|
||||
/* Set OpenThread platform config */
|
||||
esp_openthread_platform_config_t config = {
|
||||
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
|
||||
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
|
||||
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
|
||||
};
|
||||
set_openthread_platform_config(&config);
|
||||
#endif
|
||||
|
||||
/* Matter start */
|
||||
err = esp_matter::start(app_event_cb);
|
||||
ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to start Matter, err:%d", err));
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
|
||||
#include <platform/ESP32/OpenthreadLauncher.h>
|
||||
#include "esp_openthread_types.h"
|
||||
|
||||
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
|
||||
{ \
|
||||
.radio_mode = RADIO_MODE_NATIVE, \
|
||||
}
|
||||
|
||||
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
|
||||
{ \
|
||||
.host_connection_mode = HOST_CONNECTION_MODE_NONE, \
|
||||
}
|
||||
|
||||
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
|
||||
{ \
|
||||
.storage_partition_name = "nvs", \
|
||||
.netif_queue_size = 10, \
|
||||
.task_queue_size = 10, \
|
||||
}
|
||||
#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <lib/support/CodeUtils.h>
|
||||
|
||||
#include <drivers/pir.h>
|
||||
|
||||
#define PIR_SENSOR_PIN (static_cast<gpio_num_t>(CONFIG_PIR_DATA_PIN))
|
||||
|
||||
typedef struct {
|
||||
pir_sensor_config_t *config;
|
||||
bool is_initialized;
|
||||
} pir_sensor_ctx_t;
|
||||
|
||||
static pir_sensor_ctx_t s_ctx;
|
||||
|
||||
static void IRAM_ATTR pir_gpio_handler(void *arg)
|
||||
{
|
||||
static bool occupancy = false;
|
||||
bool new_occupancy = gpio_get_level(PIR_SENSOR_PIN);
|
||||
|
||||
// we only need to notify application layer if occupancy changed
|
||||
if (occupancy != new_occupancy) {
|
||||
occupancy = new_occupancy;
|
||||
if (s_ctx.config->cb) {
|
||||
s_ctx.config->cb(s_ctx.config->endpoint_id, new_occupancy, s_ctx.config->user_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pir_gpio_init(gpio_num_t pin)
|
||||
{
|
||||
gpio_reset_pin(pin);
|
||||
gpio_set_intr_type(pin, GPIO_INTR_ANYEDGE);
|
||||
gpio_set_direction(pin, GPIO_MODE_INPUT);
|
||||
|
||||
gpio_set_pull_mode(pin, GPIO_PULLDOWN_ONLY);
|
||||
|
||||
gpio_install_isr_service(0);
|
||||
gpio_isr_handler_add(pin, pir_gpio_handler, NULL);
|
||||
}
|
||||
|
||||
esp_err_t pir_sensor_init(pir_sensor_config_t *config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_ctx.is_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
pir_gpio_init(PIR_SENSOR_PIN);
|
||||
|
||||
s_ctx.config = config;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
// This file implements the PIR sensor driver.
|
||||
// This is implemented keeping the Matter requirements in mind.
|
||||
#pragma once
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
using pir_sensor_cb_t = void (*)(uint16_t endpoint_id, bool occupied, void *user_data);
|
||||
|
||||
typedef struct {
|
||||
// This callback functon will be called periodically to report the temperature.
|
||||
pir_sensor_cb_t cb = NULL;
|
||||
// endpoint_id associated with temperature sensor
|
||||
uint16_t endpoint_id;
|
||||
// user data
|
||||
void *user_data = NULL;
|
||||
} pir_sensor_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize sensor driver. This function should be called only once
|
||||
*
|
||||
* @param config sensor configurations. This should last for the lifetime of the driver
|
||||
* as driver layer do not make a copy of this object.
|
||||
*
|
||||
* @return esp_err_t - ESP_OK on success,
|
||||
* ESP_ERR_INVALID_ARG if config is NULL
|
||||
* ESP_ERR_INVALID_STATE if driver is already initialized
|
||||
* appropriate error code otherwise
|
||||
*/
|
||||
esp_err_t pir_sensor_init(pir_sensor_config_t *config);
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h>
|
||||
#include <esp_random.h>
|
||||
#include <driver/i2c.h>
|
||||
|
||||
#include <lib/support/CodeUtils.h>
|
||||
|
||||
#include <drivers/shtc3.h>
|
||||
|
||||
static const char * TAG = "shtc3";
|
||||
|
||||
#define I2C_MASTER_SCL_IO CONFIG_SHTC3_I2C_SCL_PIN
|
||||
#define I2C_MASTER_SDA_IO CONFIG_SHTC3_I2C_SDA_PIN
|
||||
#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */
|
||||
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
|
||||
|
||||
#define SHTC3_SENSOR_ADDR 0x70 /*!< I2C address of SHTC3 sensor */
|
||||
|
||||
typedef struct {
|
||||
shtc3_sensor_config_t *config;
|
||||
esp_timer_handle_t timer;
|
||||
bool is_initialized = false;
|
||||
} shtc3_sensor_ctx_t;
|
||||
|
||||
static shtc3_sensor_ctx_t s_ctx;
|
||||
|
||||
static esp_err_t shtc3_init_i2c()
|
||||
{
|
||||
i2c_config_t i2c_conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master = {
|
||||
.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
},
|
||||
};
|
||||
|
||||
esp_err_t err = i2c_param_config(I2C_MASTER_NUM, &i2c_conf);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to configure I2C driver, err:%d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return i2c_driver_install(I2C_MASTER_NUM, I2C_MODE_MASTER, 0, 0, 0);
|
||||
}
|
||||
|
||||
static esp_err_t shtc3_read(uint8_t *data, size_t size)
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (SHTC3_SENSOR_ADDR << 1) | I2C_MASTER_WRITE, true /* enable_ack */);
|
||||
// Read temperature first then humidity, with clock stretching enabled
|
||||
i2c_master_write_byte(cmd, 0x7C, true /* enable_ack */);
|
||||
i2c_master_write_byte(cmd, 0xA2, true /* enable_ack */);
|
||||
i2c_master_stop(cmd);
|
||||
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(1000));
|
||||
i2c_cmd_link_delete(cmd);
|
||||
cmd = NULL;
|
||||
|
||||
// Wait for measurement to complete
|
||||
vTaskDelay(pdMS_TO_TICKS(15));
|
||||
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (SHTC3_SENSOR_ADDR << 1) | I2C_MASTER_READ, true /* enable_ack */);
|
||||
i2c_master_read(cmd, data, size, I2C_MASTER_LAST_NACK);
|
||||
i2c_master_stop(cmd);
|
||||
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(1000));
|
||||
i2c_cmd_link_delete(cmd);
|
||||
cmd = NULL;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Temperature in degree Celsius
|
||||
static float shtc3_get_temp(uint16_t raw_temp)
|
||||
{
|
||||
return 175.0f * (static_cast<float>(raw_temp) / 65535.0f) - 45.0f;
|
||||
}
|
||||
|
||||
// Humidity in percentage
|
||||
static float shtc3_get_humidity(uint16_t raw_humidity)
|
||||
{
|
||||
return 100.0f * (static_cast<float>(raw_humidity) / 65535.0f);
|
||||
}
|
||||
|
||||
static esp_err_t shtc3_get_read_temp_and_humidity(float & temp, float & humidity)
|
||||
{
|
||||
// foreach temperature and humidity: two bytes data, one byte for checksum
|
||||
uint8_t data[6] = {0};
|
||||
|
||||
esp_err_t err = shtc3_read(data, sizeof(data));
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
uint16_t raw_temp = (data[0] << 8) | data[1];
|
||||
uint16_t raw_humidity = (data[3] << 8) | data[4];
|
||||
|
||||
temp = shtc3_get_temp(raw_temp);
|
||||
humidity = shtc3_get_humidity(raw_humidity);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void timer_cb_internal(void *arg)
|
||||
{
|
||||
auto *ctx = (shtc3_sensor_ctx_t *) arg;
|
||||
if (!(ctx && ctx->config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
float temp, humidity;
|
||||
esp_err_t err = shtc3_get_read_temp_and_humidity(temp, humidity);
|
||||
if (err != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
if (ctx->config->temperature.cb) {
|
||||
ctx->config->temperature.cb(ctx->config->temperature.endpoint_id, temp, ctx->config->user_data);
|
||||
}
|
||||
if (ctx->config->humidity.cb) {
|
||||
ctx->config->humidity.cb(ctx->config->humidity.endpoint_id, humidity, ctx->config->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t shtc3_sensor_init(shtc3_sensor_config_t *config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
// we need at least one callback so that we can start notifying application layer
|
||||
if (config->temperature.cb == NULL || config->humidity.cb == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_ctx.is_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = shtc3_init_i2c();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// keep the pointer to config
|
||||
s_ctx.config = config;
|
||||
|
||||
esp_timer_create_args_t args = {
|
||||
.callback = timer_cb_internal,
|
||||
.arg = &s_ctx,
|
||||
};
|
||||
|
||||
err = esp_timer_create(&args, &s_ctx.timer);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_timer_create failed, err:%d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_timer_start_periodic(s_ctx.timer, config->interval_ms * 1000);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_timer_start_periodic failed: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
s_ctx.is_initialized = true;
|
||||
ESP_LOGI(TAG, "shtc3 initialized successfully");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
// This file implements the SHTC3 temperature and humidity sensor driver.
|
||||
// This is implemented keeping the Matter requirements in mind.
|
||||
//
|
||||
// Datasheet: https://sensirion.com/media/documents/643F9C8E/63A5A436/Datasheet_SHTC3.pdf
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
using shtc3_sensor_cb_t = void (*)(uint16_t endpoint_id, float value, void *user_data);
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
// This callback functon will be called periodically to report the temperature.
|
||||
shtc3_sensor_cb_t cb = NULL;
|
||||
// endpoint_id associated with temperature sensor
|
||||
uint16_t endpoint_id;
|
||||
} temperature;
|
||||
|
||||
struct {
|
||||
// This callback functon will be called periodically to report the humidity.
|
||||
shtc3_sensor_cb_t cb = NULL;
|
||||
// endpoint_id associated with humidity sensor
|
||||
uint16_t endpoint_id;
|
||||
} humidity;
|
||||
|
||||
// user data
|
||||
void *user_data = NULL;
|
||||
|
||||
// polling interval in milliseconds, defaults to 5000 ms
|
||||
uint32_t interval_ms = 5000;
|
||||
} shtc3_sensor_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize sensor driver. This function should be called only once
|
||||
* When initializing, at least one callback should be provided, else it
|
||||
* returns ESP_ERR_INVALID_ARG.
|
||||
*
|
||||
* @param config sensor configurations. This should last for the lifetime of the driver
|
||||
* as driver layer do not make a copy of this object.
|
||||
*
|
||||
* @return esp_err_t - ESP_OK on success,
|
||||
* ESP_ERR_INVALID_ARG if config is NULL
|
||||
* ESP_ERR_INVALID_STATE if driver is already initialized
|
||||
* appropriate error code otherwise
|
||||
*/
|
||||
esp_err_t shtc3_sensor_init(shtc3_sensor_config_t *config);
|
||||
@@ -0,0 +1,8 @@
|
||||
dependencies:
|
||||
espressif/cmake_utilities:
|
||||
version: 0.*
|
||||
rules: # will add "optional_component" only when all if clauses are True
|
||||
- if: "idf_version >=5.0"
|
||||
- if: "target in [esp32c2]"
|
||||
esp_bsp_generic:
|
||||
version: "^1.1.0"
|
||||
@@ -0,0 +1,10 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: Firmware partition offset needs to be 64K aligned, initial 36K (9 sectors) are reserved for bootloader and partition table
|
||||
esp_secure_cert, 0x3F, ,0xd000, 0x2000, encrypted
|
||||
nvs, data, nvs, 0x10000, 0xC000,
|
||||
nvs_keys, data, nvs_keys,, 0x1000, encrypted
|
||||
otadata, data, ota, , 0x2000
|
||||
phy_init, data, phy, , 0x1000,
|
||||
ota_0, app, ota_0, 0x20000, 0x1E0000,
|
||||
ota_1, app, ota_1, 0x200000, 0x1E0000,
|
||||
fctry, data, nvs, 0x3E0000, 0x6000
|
||||
|
@@ -0,0 +1,53 @@
|
||||
# Default to 921600 baud when flashing and monitoring device
|
||||
CONFIG_ESPTOOLPY_BAUD_921600B=y
|
||||
CONFIG_ESPTOOLPY_BAUD=921600
|
||||
CONFIG_ESPTOOLPY_COMPRESSED=y
|
||||
CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y
|
||||
CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
|
||||
#enable BT
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
|
||||
#disable BT connection reattempt
|
||||
CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n
|
||||
|
||||
#enable lwip ipv6 autoconfig
|
||||
CONFIG_LWIP_IPV6_AUTOCONFIG=y
|
||||
|
||||
# Use a custom partition table
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0xC000
|
||||
|
||||
#enable lwIP route hooks
|
||||
CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y
|
||||
CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y
|
||||
|
||||
# Button
|
||||
CONFIG_BUTTON_PERIOD_TIME_MS=20
|
||||
CONFIG_BUTTON_LONG_PRESS_TIME_MS=5000
|
||||
|
||||
# disable softap by default
|
||||
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
|
||||
|
||||
# Disable DS Peripheral
|
||||
CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n
|
||||
|
||||
# Enable HKDF in mbedtls
|
||||
CONFIG_MBEDTLS_HKDF_C=y
|
||||
|
||||
# Increase LwIP IPv6 address number to 6 (MAX_FABRIC + 1)
|
||||
# unique local addresses for fabrics(MAX_FABRIC), a link local address(1)
|
||||
CONFIG_LWIP_IPV6_NUM_ADDRESSES=6
|
||||
|
||||
# ESP32-DevKitC Settings
|
||||
# Buttons
|
||||
CONFIG_BSP_BUTTONS_NUM=1
|
||||
CONFIG_BSP_BUTTON_1_TYPE_GPIO=y
|
||||
CONFIG_BSP_BUTTON_1_GPIO=0
|
||||
CONFIG_BSP_BUTTON_1_LEVEL=0
|
||||
|
||||
# Enable OTA Requestor
|
||||
CONFIG_ENABLE_OTA_REQUESTOR=y
|
||||
@@ -0,0 +1,8 @@
|
||||
CONFIG_IDF_TARGET="esp32c3"
|
||||
|
||||
# ESP32-C3-DevKitC-02 Settings
|
||||
# Buttons
|
||||
CONFIG_BSP_BUTTONS_NUM=1
|
||||
CONFIG_BSP_BUTTON_1_TYPE_GPIO=y
|
||||
CONFIG_BSP_BUTTON_1_GPIO=9
|
||||
CONFIG_BSP_BUTTON_1_LEVEL=0
|
||||
Reference in New Issue
Block a user