Skip to content

Commit a510d79

Browse files
authored
Merge branch 'master' into feat/zibee-sensor-default-value
2 parents 1002054 + 1e52aa5 commit a510d79

File tree

16 files changed

+1493
-90
lines changed

16 files changed

+1493
-90
lines changed

cores/esp32/esp32-hal-adc.c

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "esp_adc/adc_oneshot.h"
2121
#include "esp_adc/adc_continuous.h"
2222
#include "esp_adc/adc_cali_scheme.h"
23+
#include "esp_heap_caps.h"
2324

2425
#if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_REV_MIN_FULL >= 300
2526
// NOTE: These weak definitions allow successful linkage if the real efuse calibration functions are missing.
@@ -403,43 +404,40 @@ adc_continuous_result_t *adc_result = NULL;
403404
static bool adcContinuousDetachBus(void *adc_unit_number) {
404405
adc_unit_t adc_unit = (adc_unit_t)adc_unit_number - 1;
405406

407+
// Guard against double-cleanup: check if already cleaned up
406408
if (adc_handle[adc_unit].adc_continuous_handle == NULL) {
407409
return true;
408-
} else {
409-
esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle);
410+
}
411+
412+
// Clean up ADC driver
413+
esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle);
414+
if (err != ESP_OK) {
415+
return false;
416+
}
417+
adc_handle[adc_unit].adc_continuous_handle = NULL;
418+
419+
// Clean up calibration handle if exists
420+
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
421+
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
422+
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
410423
if (err != ESP_OK) {
411424
return false;
412425
}
413-
adc_handle[adc_unit].adc_continuous_handle = NULL;
414-
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
415-
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
416-
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
417-
if (err != ESP_OK) {
418-
return false;
419-
}
420426
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
421-
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
422-
if (err != ESP_OK) {
423-
return false;
424-
}
425-
#else
426-
log_e("ADC Calibration scheme is not supported!");
427+
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
428+
if (err != ESP_OK) {
427429
return false;
428-
#endif
429430
}
431+
#else
432+
log_e("ADC Calibration scheme is not supported!");
433+
return false;
434+
#endif
430435
adc_handle[adc_unit].adc_cali_handle = NULL;
431-
432-
//set all used pins to INIT state
433-
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
434-
int io_pin;
435-
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
436-
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) {
437-
if (!perimanClearPinBus(io_pin)) {
438-
return false;
439-
}
440-
}
441-
}
442436
}
437+
438+
// Don't call perimanClearPinBus() here - the peripheral manager already handles it.
439+
// This callback is only responsible for cleaning up the IDF's ADC driver and calibration handles.
440+
// It does NOT free the adc_result buffer. The caller is responsible for freeing adc_result.
443441
return true;
444442
}
445443

@@ -549,6 +547,14 @@ bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversi
549547
}
550548
#endif
551549

550+
#if CONFIG_IDF_TARGET_ESP32P4
551+
// Align conversion frame size to cache line size (required for DMA on targets with cache)
552+
uint32_t alignment_remainder = adc_handle[adc_unit].conversion_frame_size % CONFIG_CACHE_L1_CACHE_LINE_SIZE;
553+
if (alignment_remainder != 0) {
554+
adc_handle[adc_unit].conversion_frame_size += (CONFIG_CACHE_L1_CACHE_LINE_SIZE - alignment_remainder);
555+
}
556+
#endif
557+
552558
adc_handle[adc_unit].buffer_size = adc_handle[adc_unit].conversion_frame_size * 2;
553559

554560
//Conversion frame size buffer cant be bigger than 4092 bytes
@@ -626,8 +632,21 @@ bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms)
626632
uint32_t bytes_read = 0;
627633
uint32_t read_raw[used_adc_channels];
628634
uint32_t read_count[used_adc_channels];
629-
uint8_t adc_read[adc_handle[ADC_UNIT_1].conversion_frame_size];
630-
memset(adc_read, 0xcc, sizeof(adc_read));
635+
636+
// Allocate DMA buffer with cache line alignment (required for ESP32-P4 and other targets with cache)
637+
size_t buffer_size = adc_handle[ADC_UNIT_1].conversion_frame_size;
638+
#if CONFIG_IDF_TARGET_ESP32P4
639+
uint8_t *adc_read = (uint8_t *)heap_caps_aligned_alloc(CONFIG_CACHE_L1_CACHE_LINE_SIZE, buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
640+
#else
641+
uint8_t *adc_read = (uint8_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
642+
#endif
643+
if (adc_read == NULL) {
644+
log_e("Failed to allocate DMA buffer");
645+
*buffer = NULL;
646+
return false;
647+
}
648+
649+
memset(adc_read, 0xcc, buffer_size);
631650
memset(read_raw, 0, sizeof(read_raw));
632651
memset(read_count, 0, sizeof(read_count));
633652

@@ -638,6 +657,8 @@ bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms)
638657
} else {
639658
log_e("Reading data failed with error: %X", err);
640659
}
660+
free(adc_read);
661+
adc_read = NULL;
641662
*buffer = NULL;
642663
return false;
643664
}
@@ -676,6 +697,8 @@ bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms)
676697
}
677698
}
678699

700+
free(adc_read);
701+
adc_read = NULL;
679702
*buffer = adc_result;
680703
return true;
681704

@@ -708,16 +731,29 @@ bool analogContinuousStop() {
708731
}
709732

710733
bool analogContinuousDeinit() {
711-
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
712-
esp_err_t err = adc_continuous_deinit(adc_handle[ADC_UNIT_1].adc_continuous_handle);
713-
if (err != ESP_OK) {
714-
return false;
734+
if (adc_handle[ADC_UNIT_1].adc_continuous_handle == NULL) {
735+
log_i("ADC Continuous was not initialized");
736+
return true;
737+
}
738+
739+
// Clear all used pins from peripheral manager
740+
// This will trigger adcContinuousDetachBus() callback which cleans up the ADC driver
741+
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_1); channel++) {
742+
int io_pin;
743+
adc_oneshot_channel_to_io(ADC_UNIT_1, channel, &io_pin);
744+
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) {
745+
if (!perimanClearPinBus(io_pin)) {
746+
return false;
747+
}
715748
}
749+
}
750+
751+
// Free the result buffer (callback doesn't do this)
752+
if (adc_result != NULL) {
716753
free(adc_result);
717-
adc_handle[ADC_UNIT_1].adc_continuous_handle = NULL;
718-
} else {
719-
log_i("ADC Continuous was not initialized");
754+
adc_result = NULL;
720755
}
756+
721757
return true;
722758
}
723759

docs/en/matter/ep_occupancy_sensor.rst

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ The ``MatterOccupancySensor`` class provides an occupancy sensor endpoint for Ma
1010
**Features:**
1111
* Occupancy state reporting (occupied/unoccupied)
1212
* Multiple sensor type support (PIR, Ultrasonic, Physical Contact)
13+
* HoldTime attribute for configuring how long the sensor holds the "occupied" state
14+
* HoldTimeLimits (min, max, default) for validation and controller guidance
15+
* HoldTime change callback for real-time updates from Matter controllers
1316
* Simple boolean state
1417
* Read-only sensor (no control functionality)
1518
* Automatic state updates
@@ -104,6 +107,83 @@ Gets the current occupancy state.
104107
105108
This function will return ``true`` if occupied, ``false`` if unoccupied.
106109

110+
HoldTime Control
111+
****************
112+
113+
setHoldTime
114+
^^^^^^^^^^^
115+
116+
Sets the HoldTime value (in seconds). The HoldTime determines how long the sensor maintains the "occupied" state after the last detection.
117+
118+
.. code-block:: arduino
119+
120+
bool setHoldTime(uint16_t _holdTime_seconds);
121+
122+
* ``_holdTime_seconds`` - HoldTime value in seconds
123+
124+
**Important:** This function must be called after ``Matter.begin()`` has been called, as it requires the Matter event loop to be running.
125+
126+
This function will return ``true`` if successful, ``false`` otherwise.
127+
128+
getHoldTime
129+
^^^^^^^^^^^
130+
131+
Gets the current HoldTime value (in seconds).
132+
133+
.. code-block:: arduino
134+
135+
uint16_t getHoldTime();
136+
137+
This function will return the current HoldTime value in seconds.
138+
139+
setHoldTimeLimits
140+
^^^^^^^^^^^^^^^^^
141+
142+
Sets the HoldTime limits (minimum, maximum, and default values). These limits define the valid range for HoldTime values and provide metadata for Matter controllers.
143+
144+
.. code-block:: arduino
145+
146+
bool setHoldTimeLimits(uint16_t _holdTimeMin_seconds, uint16_t _holdTimeMax_seconds, uint16_t _holdTimeDefault_seconds);
147+
148+
* ``_holdTimeMin_seconds`` - Minimum HoldTime value in seconds
149+
* ``_holdTimeMax_seconds`` - Maximum HoldTime value in seconds
150+
* ``_holdTimeDefault_seconds`` - Default/recommended HoldTime value in seconds (informational metadata for controllers)
151+
152+
**Important:**
153+
* This function must be called after ``Matter.begin()`` has been called, as it requires the Matter event loop to be running.
154+
* The ``holdTimeDefault_seconds`` parameter is informational metadata for Matter controllers (recommended default value). It does NOT automatically set the HoldTime attribute - use ``setHoldTime()`` to set the actual value.
155+
* If the current HoldTime value is outside the new limits, it will be automatically adjusted to the nearest limit (minimum or maximum).
156+
157+
This function will return ``true`` if successful, ``false`` otherwise.
158+
159+
onHoldTimeChange
160+
^^^^^^^^^^^^^^^^
161+
162+
Sets a callback function that will be called when the HoldTime value is changed by a Matter Controller.
163+
164+
.. code-block:: arduino
165+
166+
void onHoldTimeChange(HoldTimeChangeCB onHoldTimeChangeCB);
167+
168+
* ``onHoldTimeChangeCB`` - Callback function of type ``HoldTimeChangeCB``
169+
170+
The callback function signature is:
171+
172+
.. code-block:: arduino
173+
174+
using HoldTimeChangeCB = std::function<bool(uint16_t holdTime_seconds)>;
175+
176+
The callback receives the new HoldTime value and can return ``true`` to accept the change or ``false`` to reject it.
177+
178+
Example:
179+
180+
.. code-block:: arduino
181+
182+
OccupancySensor.onHoldTimeChange([](uint16_t holdTime_seconds) -> bool {
183+
Serial.printf("HoldTime changed to %u seconds\n", holdTime_seconds);
184+
return true; // Accept the change
185+
});
186+
107187
Operators
108188
*********
109189

@@ -145,8 +225,22 @@ Example:
145225
Example
146226
-------
147227

148-
Occupancy Sensor
149-
****************
228+
Basic Occupancy Sensor
229+
**********************
150230

151231
.. literalinclude:: ../../../libraries/Matter/examples/MatterOccupancySensor/MatterOccupancySensor.ino
152232
:language: arduino
233+
234+
Occupancy Sensor with HoldTime
235+
*******************************
236+
237+
For an example that demonstrates HoldTime functionality, see:
238+
239+
.. literalinclude:: ../../../libraries/Matter/examples/MatterOccupancyWithHoldTime/MatterOccupancyWithHoldTime.ino
240+
:language: arduino
241+
242+
This example shows:
243+
* How to configure HoldTimeLimits after ``Matter.begin()``
244+
* How to set and persist HoldTime values
245+
* How to use the ``onHoldTimeChange()`` callback
246+
* How to implement HoldTime expiration logic in sensor simulation

docs/en/matter/matter.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ The Matter library includes a comprehensive set of examples demonstrating variou
205205
* **Matter Pressure Sensor** - Creates a Matter-compatible pressure sensor device with automatic simulation of pressure readings. `View Matter Pressure Sensor code on GitHub <https://github.com/espressif/arduino-esp32/tree/master/libraries/Matter/examples/MatterPressureSensor>`_
206206
* **Matter Contact Sensor** - Creates a Matter-compatible contact sensor device (open/closed state). `View Matter Contact Sensor code on GitHub <https://github.com/espressif/arduino-esp32/tree/master/libraries/Matter/examples/MatterContactSensor>`_
207207
* **Matter Occupancy Sensor** - Creates a Matter-compatible occupancy sensor device with automatic simulation of occupancy state changes. `View Matter Occupancy Sensor code on GitHub <https://github.com/espressif/arduino-esp32/tree/master/libraries/Matter/examples/MatterOccupancySensor>`_
208+
* **Matter Occupancy Sensor with HoldTime** - Creates a Matter-compatible occupancy sensor device with HoldTime functionality, automatic simulation of occupancy state changes, HoldTime configuration with persistence across reboots, and HoldTime change callback for real-time updates from Matter controllers. `View Matter Occupancy Sensor with HoldTime code on GitHub <https://github.com/espressif/arduino-esp32/tree/master/libraries/Matter/examples/MatterOccupancyWithHoldTime>`_
208209
* **Matter Water Leak Detector** - Creates a Matter-compatible water leak detector device with automatic simulation of water leak detection state changes. `View Matter Water Leak Detector code on GitHub <https://github.com/espressif/arduino-esp32/tree/master/libraries/Matter/examples/MatterWaterLeakDetector>`_
209210
* **Matter Water Freeze Detector** - Creates a Matter-compatible water freeze detector device with automatic simulation of water freeze detection state changes. `View Matter Water Freeze Detector code on GitHub <https://github.com/espressif/arduino-esp32/tree/master/libraries/Matter/examples/MatterWaterFreezeDetector>`_
210211
* **Matter Rain Sensor** - Creates a Matter-compatible rain sensor device with automatic simulation of rain detection state changes. `View Matter Rain Sensor code on GitHub <https://github.com/espressif/arduino-esp32/tree/master/libraries/Matter/examples/MatterRainSensor>`_

0 commit comments

Comments
 (0)