diff --git a/.vscode/settings.json b/.vscode/settings.json index 869d6a53..c7e4bd93 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,5 +16,5 @@ "ostream": "cpp", "istream": "cpp" }, - "python.formatting.provider": "yapf" + "python.formatting.provider": "black" } \ No newline at end of file diff --git a/components/roode/__init__.py b/components/roode/__init__.py index 7bb571ec..0f7ab63a 100644 --- a/components/roode/__init__.py +++ b/components/roode/__init__.py @@ -4,6 +4,7 @@ from esphome.components import sensor from esphome.const import CONF_ID, STATE_CLASS_MEASUREMENT, UNIT_EMPTY, UNIT_METER + # DEPENDENCIES = ["i2c"] AUTO_LOAD = ["sensor", "binary_sensor", "text_sensor"] MULTI_CONF = True @@ -13,9 +14,9 @@ roode_ns = cg.esphome_ns.namespace("roode") Roode = roode_ns.class_("Roode", cg.PollingComponent) -CONF_ROI_HEIGHT = 'roi_height' -CONF_ROI_WIDTH = 'roi_width' -CONF_ADVISED_SENSOR_ORIENTATION = 'advised_sensor_orientation' +CONF_ROI_HEIGHT = "roi_height" +CONF_ROI_WIDTH = "roi_width" +CONF_ADVISED_SENSOR_ORIENTATION = "advised_sensor_orientation" CONF_CALIBRATION = "calibration" CONF_ROI_CALIBRATION = "roi_calibration" CONF_INVERT_DIRECTION = "invert_direction" @@ -30,66 +31,72 @@ CONF_MANUAL_ACTIVE = "manual_active" CONF_CALIBRATION_ACTIVE = "calibration_active" CONF_TIMING_BUDGET = "timing_budget" +CONF_USE_SAMPLING = "use_sampling" TYPES = [ - CONF_RESTORE_VALUES, CONF_INVERT_DIRECTION, - CONF_ADVISED_SENSOR_ORIENTATION, CONF_I2C_ADDRESS + CONF_RESTORE_VALUES, + CONF_INVERT_DIRECTION, + CONF_ADVISED_SENSOR_ORIENTATION, + CONF_I2C_ADDRESS, + CONF_USE_SAMPLING, ] -CONFIG_SCHEMA = (cv.Schema({ - cv.GenerateID(): - cv.declare_id(Roode), - cv.Optional(CONF_INVERT_DIRECTION, default='false'): - cv.boolean, - cv.Optional(CONF_RESTORE_VALUES, default='false'): - cv.boolean, - cv.Optional(CONF_ADVISED_SENSOR_ORIENTATION, default='true'): - cv.boolean, - cv.Optional(CONF_I2C_ADDRESS, default=0x29): - cv.uint8_t, - cv.Exclusive( - CONF_CALIBRATION, "mode", f"Only one mode, {CONF_MANUAL} or {CONF_CALIBRATION} is usable"): - cv.Schema({ - cv.Optional(CONF_CALIBRATION_ACTIVE, default='true'): - cv.boolean, - cv.Optional(CONF_MAX_THRESHOLD_PERCENTAGE, default=85): - cv.int_range(min=50, max=100), - cv.Optional(CONF_MIN_THRESHOLD_PERCENTAGE, default=0): - cv.int_range(min=0, max=100), - cv.Optional(CONF_ROI_CALIBRATION, default='false'): - cv.boolean, - }), - cv.Exclusive( - CONF_MANUAL, "mode", f"Only one mode, {CONF_MANUAL} or {CONF_CALIBRATION} is usable"): - cv.Schema({ - cv.Optional(CONF_MANUAL_ACTIVE, default='true'): - cv.boolean, - cv.Optional(CONF_TIMING_BUDGET, default=10): - cv.int_range(min=10, max=1000), - cv.Inclusive( - CONF_SENSOR_MODE, - "manual_mode", - f"{CONF_SENSOR_MODE}, {CONF_ROI_HEIGHT}, {CONF_ROI_WIDTH} and {CONF_MANUAL_THRESHOLD} must be used together", - ): - cv.int_range(min=-1, max=2), - cv.Inclusive( - CONF_ROI_HEIGHT, - "manual_mode", - f"{CONF_SENSOR_MODE}, {CONF_ROI_HEIGHT}, {CONF_ROI_WIDTH} and {CONF_MANUAL_THRESHOLD} must be used together", - ): - cv.int_range(min=4, max=16), - cv.Inclusive( - CONF_ROI_WIDTH, - "manual_mode", - f"{CONF_SENSOR_MODE}, {CONF_ROI_HEIGHT}, {CONF_ROI_WIDTH} and {CONF_MANUAL_THRESHOLD} must be used together", - ): - cv.int_range(min=4, max=16), - cv.Inclusive( - CONF_MANUAL_THRESHOLD, - "manual_mode", - f"{CONF_SENSOR_MODE}, {CONF_ROI_HEIGHT}, {CONF_ROI_WIDTH} and {CONF_MANUAL_THRESHOLD} must be used together", - ): - cv.int_range(min=40, max=4000), - }), -}).extend(cv.polling_component_schema("100ms"))) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(Roode), + cv.Optional(CONF_INVERT_DIRECTION, default="false"): cv.boolean, + cv.Optional(CONF_RESTORE_VALUES, default="false"): cv.boolean, + cv.Optional(CONF_ADVISED_SENSOR_ORIENTATION, default="true"): cv.boolean, + cv.Optional(CONF_USE_SAMPLING, default="false"): cv.boolean, + cv.Optional(CONF_I2C_ADDRESS, default=0x29): cv.uint8_t, + cv.Exclusive( + CONF_CALIBRATION, + "mode", + f"Only one mode, {CONF_MANUAL} or {CONF_CALIBRATION} is usable", + ): cv.Schema( + { + cv.Optional(CONF_CALIBRATION_ACTIVE, default="true"): cv.boolean, + cv.Optional(CONF_MAX_THRESHOLD_PERCENTAGE, default=85): cv.int_range( + min=50, max=100 + ), + cv.Optional(CONF_MIN_THRESHOLD_PERCENTAGE, default=0): cv.int_range( + min=0, max=100 + ), + cv.Optional(CONF_ROI_CALIBRATION, default="false"): cv.boolean, + } + ), + cv.Exclusive( + CONF_MANUAL, + "mode", + f"Only one mode, {CONF_MANUAL} or {CONF_CALIBRATION} is usable", + ): cv.Schema( + { + cv.Optional(CONF_MANUAL_ACTIVE, default="true"): cv.boolean, + cv.Optional(CONF_TIMING_BUDGET, default=10): cv.int_range( + min=10, max=1000 + ), + cv.Inclusive( + CONF_SENSOR_MODE, + "manual_mode", + f"{CONF_SENSOR_MODE}, {CONF_ROI_HEIGHT}, {CONF_ROI_WIDTH} and {CONF_MANUAL_THRESHOLD} must be used together", + ): cv.int_range(min=-1, max=2), + cv.Inclusive( + CONF_ROI_HEIGHT, + "manual_mode", + f"{CONF_SENSOR_MODE}, {CONF_ROI_HEIGHT}, {CONF_ROI_WIDTH} and {CONF_MANUAL_THRESHOLD} must be used together", + ): cv.int_range(min=4, max=16), + cv.Inclusive( + CONF_ROI_WIDTH, + "manual_mode", + f"{CONF_SENSOR_MODE}, {CONF_ROI_HEIGHT}, {CONF_ROI_WIDTH} and {CONF_MANUAL_THRESHOLD} must be used together", + ): cv.int_range(min=4, max=16), + cv.Inclusive( + CONF_MANUAL_THRESHOLD, + "manual_mode", + f"{CONF_SENSOR_MODE}, {CONF_ROI_HEIGHT}, {CONF_ROI_WIDTH} and {CONF_MANUAL_THRESHOLD} must be used together", + ): cv.int_range(min=40, max=4000), + } + ), + } +).extend(cv.polling_component_schema("100ms")) async def setup_conf(config, key, hub): diff --git a/components/roode/binary_sensor.py b/components/roode/binary_sensor.py index e67b26bc..9b5e6bd6 100644 --- a/components/roode/binary_sensor.py +++ b/components/roode/binary_sensor.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor +import esphome.final_validate as fv from esphome.const import ( CONF_ID, CONF_DEVICE_CLASS, @@ -10,18 +11,34 @@ DEPENDENCIES = ["roode"] -CONF_PRESENCE = 'presence_sensor' +CONF_PRESENCE = "presence_sensor" TYPES = [CONF_PRESENCE] -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_ROODE_ID): - cv.use_id(Roode), - cv.Optional(CONF_PRESENCE): - binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): - cv.declare_id(binary_sensor.BinarySensor), - }), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_ROODE_ID): cv.use_id(Roode), + cv.Optional(CONF_PRESENCE): binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(binary_sensor.BinarySensor), + } + ), + } +) + + +# def validate_can_use_presence(value): +# main = fv.full_config.get()["roode"][0] +# presence_sensor = main.get(CONF_USE_PRESENCE) +# print(presence_sensor) +# if presence_sensor == False: +# raise cv.Invalid("Presence sensor is not enabled") +# else: +# return presence_sensor + + +# FINAL_VALIDATE_SCHEMA = cv.Schema( +# {cv.Optional(CONF_PRESENCE): validate_can_use_presence}, extra=cv.ALLOW_EXTRA +# ) async def setup_conf(config, key, hub): @@ -35,4 +52,4 @@ async def setup_conf(config, key, hub): async def to_code(config): hub = await cg.get_variable(config[CONF_ROODE_ID]) for key in TYPES: - await setup_conf(config, key, hub) \ No newline at end of file + await setup_conf(config, key, hub) diff --git a/components/roode/roode.cpp b/components/roode/roode.cpp index 2e5a3348..0d006172 100644 --- a/components/roode/roode.cpp +++ b/components/roode/roode.cpp @@ -5,6 +5,8 @@ namespace esphome namespace roode { static const char *const TAG = "Roode"; + static const char *const SETUP = "Setup"; + static const char *const CALIBRATION = "Calibration"; void Roode::dump_config() { ESP_LOGCONFIG(TAG, "dump config:"); @@ -14,8 +16,11 @@ namespace esphome } void Roode::setup() { - ESP_LOGI("Roode setup", "Booting Roode %s", VERSION); - version_sensor->publish_state(VERSION); + ESP_LOGI(SETUP, "Booting Roode %s", VERSION); + if (version_sensor != nullptr) + { + version_sensor->publish_state(VERSION); + } EEPROM.begin(EEPROM_SIZE); Wire.begin(); Wire.setClock(400000); @@ -24,25 +29,22 @@ namespace esphome { distanceSensor.setAddress(address_); } - if (Roode::invert_direction_ == true) + if (invert_direction_) { + ESP_LOGD(TAG, "Inverting direction"); LEFT = 1; RIGHT = 0; } - else - { - LEFT = 0; - RIGHT = 1; - } distanceSensor.setTimeout(500); if (!distanceSensor.init()) { - ESP_LOGE("Setup", "Failed to detect and initialize sensor!"); + ESP_LOGE(SETUP, "Failed to detect and initialize sensor!"); } if (calibration_active_) { calibration(distanceSensor); + App.feed_wdt(); } if (manual_active_) { @@ -60,22 +62,30 @@ namespace esphome peopleCounter = EEPROM.read(100); if (peopleCounter == 255) // 255 is the default value if no value was stored peopleCounter = 0; - ESP_LOGD("Roode setup", "last value: %d", peopleCounter); + ESP_LOGD("Roode setup", "last value: %u", peopleCounter); } sendCounter(peopleCounter); + distanceSensor.startContinuous(delay_between_measurements); } void Roode::update() { - distance_sensor->publish_state(distance); + if (distance_sensor != nullptr) + { + distance_sensor->publish_state(distance); + } } void Roode::loop() { + // unsigned long start = micros(); getZoneDistance(); zone++; zone = zone % 2; - yield(); + // App.feed_wdt(); + // unsigned long end = micros(); + // unsigned long delta = end - start; + // ESP_LOGI("Roode loop", "loop took %lu microseconds", delta); } void Roode::getZoneDistance() @@ -84,47 +94,57 @@ namespace esphome static int PathTrackFillingSize = 1; // init this to 1 as we start from state where nobody is any of the zones static int LeftPreviousStatus = NOBODY; static int RightPreviousStatus = NOBODY; - static uint16_t Distances[2][DISTANCES_ARRAY_SIZE]; static uint8_t DistancesTableSize[2] = {0, 0}; int CurrentZoneStatus = NOBODY; int AllZonesCurrentStatus = 0; int AnEventHasOccured = 0; - uint16_t MinDistance; - uint8_t i; distanceSensor.setROICenter(center[zone]); distanceSensor.startContinuous(delay_between_measurements); distance = distanceSensor.read(); - yield(); - distanceSensor.stopContinuous(); - - if (DistancesTableSize[zone] < DISTANCES_ARRAY_SIZE) - { - Distances[zone][DistancesTableSize[zone]] = distance; - DistancesTableSize[zone]++; - } - else - { - for (i = 1; i < DISTANCES_ARRAY_SIZE; i++) - Distances[zone][i - 1] = Distances[zone][i]; - Distances[zone][DISTANCES_ARRAY_SIZE - 1] = distance; - } - - // pick up the min distance - MinDistance = Distances[zone][0]; - if (DistancesTableSize[zone] >= 2) - { - for (i = 1; i < DistancesTableSize[zone]; i++) + distanceSensor.writeReg(distanceSensor.SYSTEM__MODE_START, 0x80); + if (use_sampling_) + { + ESP_LOGD(SETUP, "Using sampling"); + static uint16_t Distances[2][DISTANCES_ARRAY_SIZE]; + uint16_t MinDistance; + uint8_t i; + if (DistancesTableSize[zone] < DISTANCES_ARRAY_SIZE) { - if (Distances[zone][i] < MinDistance) - MinDistance = Distances[zone][i]; + Distances[zone][DistancesTableSize[zone]] = distance; + DistancesTableSize[zone]++; + ESP_LOGD(SETUP, "Distances[%d][DistancesTableSize[zone]] = %d", zone, Distances[zone][DistancesTableSize[zone]]); } + else + { + for (i = 1; i < DISTANCES_ARRAY_SIZE; i++) + Distances[zone][i - 1] = Distances[zone][i]; + Distances[zone][DISTANCES_ARRAY_SIZE - 1] = distance; + ESP_LOGD(SETUP, "Distances[%d][DISTANCES_ARRAY_SIZE - 1] = %d", zone, Distances[zone][DISTANCES_ARRAY_SIZE - 1]); + } + ESP_LOGD(SETUP, "Distances[%d][0]] = %d", zone, Distances[zone][0]); + ESP_LOGD(SETUP, "Distances[%d][1]] = %d", zone, Distances[zone][1]); + // pick up the min distance + MinDistance = Distances[zone][0]; + if (DistancesTableSize[zone] >= 2) + { + for (i = 1; i < DistancesTableSize[zone]; i++) + { + if (Distances[zone][i] < MinDistance) + MinDistance = Distances[zone][i]; + } + } + distance = MinDistance; } - if (MinDistance < DIST_THRESHOLD_MAX[zone] && MinDistance > DIST_THRESHOLD_MIN[zone]) + // PathTrack algorithm + if (distance < DIST_THRESHOLD_MAX[zone] && distance > DIST_THRESHOLD_MIN[zone]) { // Someone is in the sensing area CurrentZoneStatus = SOMEONE; - presence_sensor->publish_state(true); + if (presence_sensor != nullptr) + { + presence_sensor->publish_state(true); + } } // left zone @@ -199,7 +219,10 @@ namespace esphome peopleCounter--; sendCounter(peopleCounter); ESP_LOGD("Roode pathTracking", "Exit detected."); - entry_exit_event_sensor->publish_state("Exit"); + if (entry_exit_event_sensor != nullptr) + { + entry_exit_event_sensor->publish_state("Exit"); + } DistancesTableSize[0] = 0; DistancesTableSize[1] = 0; } @@ -210,7 +233,10 @@ namespace esphome peopleCounter++; sendCounter(peopleCounter); ESP_LOGD("Roode pathTracking", "Entry detected."); - entry_exit_event_sensor->publish_state("Entry"); + if (entry_exit_event_sensor != nullptr) + { + entry_exit_event_sensor->publish_state("Entry"); + } DistancesTableSize[0] = 0; DistancesTableSize[1] = 0; } @@ -237,18 +263,24 @@ namespace esphome PathTrack[PathTrackFillingSize - 1] = AllZonesCurrentStatus; } } - if (CurrentZoneStatus == NOBODY && LeftPreviousStatus == NOBODY && RightPreviousStatus == NOBODY) + if (presence_sensor != nullptr) { - // nobody is in the sensing area - presence_sensor->publish_state(false); + if (CurrentZoneStatus == NOBODY && LeftPreviousStatus == NOBODY && RightPreviousStatus == NOBODY) + { + // nobody is in the sensing area + presence_sensor->publish_state(false); + } } } void Roode::sendCounter(uint16_t counter) { - ESP_LOGI("Roode", "Sending people count: %d", counter); + ESP_LOGI(SETUP, "Sending people count: %d", counter); peopleCounter = counter; - people_counter_sensor->publish_state(peopleCounter); + if (people_counter_sensor != nullptr) + { + people_counter_sensor->publish_state(peopleCounter); + } if (restore_values_) { @@ -325,37 +357,31 @@ namespace esphome zone = 0; int *values_zone_0 = new int[number_attempts]; int *values_zone_1 = new int[number_attempts]; + distanceSensor.writeReg(distanceSensor.SYSTEM__MODE_START, 0x80); + distanceSensor.setROISize(Roode::roi_width_, Roode::roi_height_); + distanceSensor.startContinuous(delay_between_measurements); for (int i = 0; i < number_attempts; i++) { // increase sum of values in Zone 0 - distanceSensor.setROISize(Roode::roi_width_, Roode::roi_height_); distanceSensor.setROICenter(center[zone]); - distanceSensor.startContinuous(delay_between_measurements); - distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000); distance = distanceSensor.read(); - distanceSensor.stopContinuous(); values_zone_0[i] = distance; zone++; zone = zone % 2; - + App.feed_wdt(); // increase sum of values in Zone 1 - distanceSensor.setROISize(Roode::roi_width_, Roode::roi_height_); distanceSensor.setROICenter(center[zone]); - distanceSensor.startContinuous(delay_between_measurements); - distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000); distance = distanceSensor.read(); - distanceSensor.stopContinuous(); values_zone_1[i] = distance; zone++; zone = zone % 2; - App.feed_wdt(); } optimized_zone_0 = getOptimizedValues(values_zone_0, getSum(values_zone_0, number_attempts), number_attempts); optimized_zone_1 = getOptimizedValues(values_zone_1, getSum(values_zone_1, number_attempts), number_attempts); - EEPROM.write(13, ROI_size); } - void Roode::setSensorMode(int sensor_mode, int timing_budget) + void Roode::setSensorMode(int sensor_mode, int new_timing_budget) { + distanceSensor.writeReg(distanceSensor.SYSTEM__MODE_START, 0x80); switch (sensor_mode) { case 0: // short mode @@ -364,39 +390,39 @@ namespace esphome status = distanceSensor.setDistanceMode(VL53L1X::Short); if (!status) { - ESP_LOGE("Setup", "Could not set distance mode. mode: %d", VL53L1X::Short); + ESP_LOGE(SETUP, "Could not set distance mode. mode: %d", VL53L1X::Short); } - ESP_LOGI("Setup", "Manually set short mode. timing_budget: %d", time_budget_in_ms); + ESP_LOGI(SETUP, "Set short mode. timing_budget: %d", time_budget_in_ms); break; - case 1: // long mode - time_budget_in_ms = time_budget_in_ms_long; - delay_between_measurements = delay_between_measurements_long; - status = distanceSensor.setDistanceMode(VL53L1X::Long); + case 1: // medium mode + time_budget_in_ms = time_budget_in_ms_medium; + delay_between_measurements = delay_between_measurements_medium; + status = distanceSensor.setDistanceMode(VL53L1X::Medium); if (!status) { - ESP_LOGE("Setup", "Could not set distance mode. mode: %d", VL53L1X::Long); + ESP_LOGE(SETUP, "Could not set distance mode. mode: %d", VL53L1X::Medium); } - ESP_LOGI("Setup", "Manually set long mode. timing_budget: %d", time_budget_in_ms); + ESP_LOGI(SETUP, "Set medium mode. timing_budget: %d", time_budget_in_ms); break; - case 2: // max mode - time_budget_in_ms = time_budget_in_ms_max_range; - delay_between_measurements = delay_between_measurements_max; + case 2: // long mode + time_budget_in_ms = time_budget_in_ms_long; + delay_between_measurements = delay_between_measurements_long; status = distanceSensor.setDistanceMode(VL53L1X::Long); if (!status) { - ESP_LOGE("Setup", "Could not set distance mode. mode: %d", VL53L1X::Long); + ESP_LOGE(SETUP, "Could not set distance mode. mode: %d", VL53L1X::Long); } - ESP_LOGI("Setup", "Manually set max range mode. timing_budget: %d", time_budget_in_ms); + ESP_LOGI(SETUP, "Set long range mode. timing_budget: %d", time_budget_in_ms); break; case 3: // custom mode - time_budget_in_ms = timing_budget; - delay_between_measurements = delay_between_measurements_max; + time_budget_in_ms = new_timing_budget; + delay_between_measurements = delay_between_measurements_long; status = distanceSensor.setDistanceMode(VL53L1X::Long); if (!status) { - ESP_LOGE("Setup", "Could not set distance mode. mode: %d", VL53L1X::Long); + ESP_LOGE(SETUP, "Could not set distance mode. mode: %d", VL53L1X::Long); } - ESP_LOGI("Setup", "Manually set custom range mode. timing_budget: %d", time_budget_in_ms); + ESP_LOGI(SETUP, "Manually set custom range mode. timing_budget: %d", time_budget_in_ms); break; default: break; @@ -404,7 +430,7 @@ namespace esphome status = distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000); if (!status) { - ESP_LOGE("Setup", "Could not set timing budget. timing_budget: %d ms", time_budget_in_ms); + ESP_LOGE(SETUP, "Could not set timing budget. timing_budget: %d ms", time_budget_in_ms); } } @@ -412,31 +438,22 @@ namespace esphome { if (average_zone_0 <= short_distance_threshold || average_zone_1 <= short_distance_threshold) { - // we can use the short mode, which allows more precise measurements up to 1.3 meters - time_budget_in_ms = time_budget_in_ms_short; - delay_between_measurements = delay_between_measurements_short; - distanceSensor.setDistanceMode(VL53L1X::Short); + setSensorMode(0, time_budget_in_ms_short); } - if ((average_zone_0 > short_distance_threshold && average_zone_0 <= long_distance_threshold) || (average_zone_1 > short_distance_threshold && average_zone_1 <= long_distance_threshold)) + if ((average_zone_0 > short_distance_threshold && average_zone_0 <= medium_distance_threshold) || (average_zone_1 > short_distance_threshold && average_zone_1 <= medium_distance_threshold)) { - // we can use the short mode, which allows more precise measurements up to 1.3 meters - time_budget_in_ms = time_budget_in_ms_long; - delay_between_measurements = delay_between_measurements_long; - distanceSensor.setDistanceMode(VL53L1X::Long); + setSensorMode(1, time_budget_in_ms_medium); } - if (average_zone_0 > long_distance_threshold || average_zone_1 > long_distance_threshold) + if (average_zone_0 > medium_distance_threshold || average_zone_1 > medium_distance_threshold) { - // we can use the short mode, which allows more precise measurements up to 1.3 meters - time_budget_in_ms = time_budget_in_ms_max_range; - delay_between_measurements = delay_between_measurements_max; - distanceSensor.setDistanceMode(VL53L1X::Long); + setSensorMode(2, time_budget_in_ms_long); } status = distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000); if (!status) { - ESP_LOGE("Calibration", "Could not set timing budget. timing_budget: %d ms", time_budget_in_ms); + ESP_LOGE(CALIBRATION, "Could not set timing budget. timing_budget: %d ms", time_budget_in_ms); } } int Roode::getSum(int *array, int size) @@ -445,7 +462,7 @@ namespace esphome for (int i = 0; i < size; i++) { sum = sum + array[i]; - yield(); + App.feed_wdt(); } return sum; } @@ -459,26 +476,27 @@ namespace esphome for (int i = 0; i < size; i++) { sum_squared = sum_squared + (values[i] * values[i]); - yield(); + App.feed_wdt(); } variance = sum_squared / size - (avg * avg); sd = sqrt(variance); - ESP_LOGD("Calibration", "Zone AVG: %d", avg); - ESP_LOGD("Calibration", "Zone 0 SD: %d", sd); + ESP_LOGD(CALIBRATION, "Zone AVG: %d", avg); + ESP_LOGD(CALIBRATION, "Zone 0 SD: %d", sd); return avg - sd; } void Roode::calibration(VL53L1X distanceSensor) { + distanceSensor.writeReg(distanceSensor.SYSTEM__MODE_START, 0x80); // the sensor does 100 measurements for each zone (zones are predefined) - time_budget_in_ms = time_budget_in_ms_max_range; - delay_between_measurements = delay_between_measurements_max; - distanceSensor.startContinuous(delay_between_measurements); - distanceSensor.setDistanceMode(VL53L1X::Long); + time_budget_in_ms = time_budget_in_ms_medium; + delay_between_measurements = delay_between_measurements_medium; + distanceSensor.setDistanceMode(VL53L1X::Medium); status = distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000); + if (!status) { - ESP_LOGE("Calibration", "Could not set timing budget. timing_budget: %d ms", time_budget_in_ms); + ESP_LOGE(CALIBRATION, "Could not set timing budget. timing_budget: %d ms", time_budget_in_ms); } if (advised_sensor_orientation_) { @@ -494,34 +512,25 @@ namespace esphome roi_width_ = roi_height_; roi_height_ = roi_width_; } - yield(); zone = 0; int *values_zone_0 = new int[number_attempts]; int *values_zone_1 = new int[number_attempts]; + distanceSensor.setROISize(Roode::roi_width_, Roode::roi_height_); + distanceSensor.startContinuous(delay_between_measurements); for (int i = 0; i < number_attempts; i++) { // increase sum of values in Zone 0 - - distanceSensor.setROISize(Roode::roi_width_, Roode::roi_height_); distanceSensor.setROICenter(center[zone]); - distanceSensor.startContinuous(delay_between_measurements); - distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000); distance = distanceSensor.read(); - yield(); - distanceSensor.stopContinuous(); values_zone_0[i] = distance; zone++; zone = zone % 2; + App.feed_wdt(); // increase sum of values in Zone 1 - distanceSensor.setROISize(roi_width_, roi_height_); distanceSensor.setROICenter(center[zone]); - distanceSensor.startContinuous(delay_between_measurements); - distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000); distance = distanceSensor.read(); - yield(); - distanceSensor.stopContinuous(); values_zone_1[i] = distance; zone++; zone = zone % 2; @@ -544,35 +553,54 @@ namespace esphome int unit_threshold_zone_0 = DIST_THRESHOLD_MAX[0] - 100 * hundred_threshold_zone_0; int unit_threshold_zone_1 = DIST_THRESHOLD_MAX[1] - 100 * hundred_threshold_zone_1; publishSensorConfiguration(DIST_THRESHOLD_MAX, true); + App.feed_wdt(); if (min_threshold_percentage_ != 0) { DIST_THRESHOLD_MIN[0] = optimized_zone_0 * min_threshold_percentage_ / 100; // they can be int values, as we are not interested in the decimal part when defining the threshold DIST_THRESHOLD_MIN[1] = optimized_zone_1 * min_threshold_percentage_ / 100; publishSensorConfiguration(DIST_THRESHOLD_MIN, false); } + distanceSensor.writeReg(distanceSensor.SYSTEM__MODE_START, 0x80); } void Roode::publishSensorConfiguration(int DIST_THRESHOLD_ARR[2], bool isMax) { if (isMax) { - ESP_LOGI("Roode", "Max threshold zone0: %dmm", DIST_THRESHOLD_ARR[0]); - ESP_LOGI("Roode", "Max threshold zone1: %dmm", DIST_THRESHOLD_ARR[1]); - max_threshold_zone0_sensor->publish_state(DIST_THRESHOLD_ARR[0]); - max_threshold_zone1_sensor->publish_state(DIST_THRESHOLD_ARR[1]); - yield(); + ESP_LOGI(SETUP, "Max threshold zone0: %dmm", DIST_THRESHOLD_ARR[0]); + ESP_LOGI(SETUP, "Max threshold zone1: %dmm", DIST_THRESHOLD_ARR[1]); + if (max_threshold_zone0_sensor != nullptr) + { + max_threshold_zone0_sensor->publish_state(DIST_THRESHOLD_ARR[0]); + } + + if (max_threshold_zone1_sensor != nullptr) + { + max_threshold_zone1_sensor->publish_state(DIST_THRESHOLD_ARR[1]); + } } else { - ESP_LOGI("Roode", "Min threshold zone0: %dmm", DIST_THRESHOLD_ARR[0]); - ESP_LOGI("Roode", "Min threshold zone1: %dmm", DIST_THRESHOLD_ARR[1]); - min_threshold_zone0_sensor->publish_state(DIST_THRESHOLD_ARR[0]); - min_threshold_zone1_sensor->publish_state(DIST_THRESHOLD_ARR[1]); - yield(); + ESP_LOGI(SETUP, "Min threshold zone0: %dmm", DIST_THRESHOLD_ARR[0]); + ESP_LOGI(SETUP, "Min threshold zone1: %dmm", DIST_THRESHOLD_ARR[1]); + if (min_threshold_zone0_sensor != nullptr) + { + min_threshold_zone0_sensor->publish_state(DIST_THRESHOLD_ARR[0]); + } + if (min_threshold_zone1_sensor != nullptr) + { + min_threshold_zone1_sensor->publish_state(DIST_THRESHOLD_ARR[1]); + } + } + + if (roi_height_sensor != nullptr) + { + roi_height_sensor->publish_state(roi_height_); + } + if (roi_width_sensor != nullptr) + { + roi_width_sensor->publish_state(roi_width_); } - roi_height_sensor->publish_state(roi_height_); - roi_width_sensor->publish_state(roi_width_); - yield(); } } } \ No newline at end of file diff --git a/components/roode/roode.h b/components/roode/roode.h index e0572b0d..d8def410 100644 --- a/components/roode/roode.h +++ b/components/roode/roode.h @@ -15,11 +15,11 @@ namespace esphome { #define NOBODY 0 #define SOMEONE 1 -#define VERSION "v1.4.0-alpha-2" +#define VERSION "v1.4.0-beta" #define EEPROM_SIZE 512 static int LEFT = 0; static int RIGHT = 1; - static const uint16_t DISTANCES_ARRAY_SIZE = 10; + static const uint16_t DISTANCES_ARRAY_SIZE = 2; /* ##### CALIBRATION ##### @@ -38,13 +38,18 @@ namespace esphome static int delay_between_measurements = 0; static int time_budget_in_ms = 0; - // parameters which define the time between two different measurements in various modes (https://www.st.com/resource/en/datasheet/vl53l1x.pdf) - static int time_budget_in_ms_short = 15; // 20ms with the full API but 15ms with the ULD API (https://www.st.com/resource/en/user_manual/um2510-a-guide-to-using-the-vl53l1x-ultra-lite-driver-stmicroelectronics.pdf) - static int time_budget_in_ms_long = 33; // Works up to 3.1m increase to minimum of 140ms for 4m - static int time_budget_in_ms_max_range = 100; // Works up to 4m in the dark on a white chart + /* + Parameters which define the time between two different measurements in various modes (https://www.st.com/resource/en/datasheet/vl53l1x.pdf) + The timing budget and inter-measurement period should not be called when the sensor is + ranging. The user has to stop the ranging, change these parameters, and restart ranging + The minimum inter-measurement period must be longer than the timing budget + 4 ms. + */ + static int time_budget_in_ms_short = 15; // 20ms with the full API but 15ms with the ULD API (https://www.st.com/resource/en/user_manual/um2510-a-guide-to-using-the-vl53l1x-ultra-lite-driver-stmicroelectronics.pdf) + static int time_budget_in_ms_medium = 33; // Works up to 3.1m increase to minimum of 140ms for 4m + static int time_budget_in_ms_long = 140; // Works up to 4m in the dark on a white chart static int delay_between_measurements_short = 25; - static int delay_between_measurements_long = 50; - static int delay_between_measurements_max = 220; + static int delay_between_measurements_medium = 40; + static int delay_between_measurements_long = 150; class Roode : public PollingComponent { @@ -67,6 +72,7 @@ namespace esphome void set_invert_direction(bool dir) { invert_direction_ = dir; } void set_restore_values(bool val) { restore_values_ = val; } void set_advised_sensor_orientation(bool val) { advised_sensor_orientation_ = val; } + void set_use_sampling(bool val) { use_sampling_ = val; } void set_distance_sensor(sensor::Sensor *distance_sensor_) { distance_sensor = distance_sensor_; } void set_people_counter_sensor(sensor::Sensor *people_counter_sensor_) { people_counter_sensor = people_counter_sensor_; } void set_max_threshold_zone0_sensor(sensor::Sensor *max_threshold_zone0_sensor_) { max_threshold_zone0_sensor = max_threshold_zone0_sensor_; } @@ -88,21 +94,21 @@ namespace esphome int DIST_THRESHOLD_MIN[2] = {0, 0}; // min treshold of the two zones int roi_width_{6}; // width of the ROI int roi_height_{16}; // height of the ROI - uint64_t peopleCounter{0}; + uint16_t peopleCounter{0}; protected: VL53L1X distanceSensor; - sensor::Sensor *distance_sensor = new sensor::Sensor(); - sensor::Sensor *people_counter_sensor = new sensor::Sensor(); - sensor::Sensor *max_threshold_zone0_sensor = new sensor::Sensor(); - sensor::Sensor *max_threshold_zone1_sensor = new sensor::Sensor(); - sensor::Sensor *min_threshold_zone0_sensor = new sensor::Sensor(); - sensor::Sensor *min_threshold_zone1_sensor = new sensor::Sensor(); - sensor::Sensor *roi_height_sensor = new sensor::Sensor(); - sensor::Sensor *roi_width_sensor = new sensor::Sensor(); - binary_sensor::BinarySensor *presence_sensor = new binary_sensor::BinarySensor(); - text_sensor::TextSensor *version_sensor = new text_sensor::TextSensor(); - text_sensor::TextSensor *entry_exit_event_sensor = new text_sensor::TextSensor(); + sensor::Sensor *distance_sensor; + sensor::Sensor *people_counter_sensor; + sensor::Sensor *max_threshold_zone0_sensor; + sensor::Sensor *max_threshold_zone1_sensor; + sensor::Sensor *min_threshold_zone0_sensor; + sensor::Sensor *min_threshold_zone1_sensor; + sensor::Sensor *roi_height_sensor; + sensor::Sensor *roi_width_sensor; + binary_sensor::BinarySensor *presence_sensor; + text_sensor::TextSensor *version_sensor; + text_sensor::TextSensor *entry_exit_event_sensor; void roi_calibration(VL53L1X distanceSensor, int optimized_zone_0, int optimized_zone_1); void calibration(VL53L1X distanceSensor); @@ -116,6 +122,7 @@ namespace esphome bool roi_calibration_{false}; int sensor_mode{-1}; bool advised_sensor_orientation_{true}; + bool use_sampling_{false}; uint8_t address_ = 0x29; bool invert_direction_{false}; bool restore_values_{false}; @@ -124,11 +131,8 @@ namespace esphome uint64_t manual_threshold_{2000}; int number_attempts = 20; int timing_budget_{-1}; - int left = 0, right = 0, oldcnt; - boolean lastTrippedState = 0; - double people, distance_avg; int short_distance_threshold = 1300; - int long_distance_threshold = 3100; + int medium_distance_threshold = 3100; bool status = false; int optimized_zone_0; int optimized_zone_1; diff --git a/components/roode/sensor.py b/components/roode/sensor.py index 49a248aa..c93bcfa8 100644 --- a/components/roode/sensor.py +++ b/components/roode/sensor.py @@ -1,87 +1,87 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import (ICON_ARROW_EXPAND_VERTICAL, ICON_COUNTER, - ICON_NEW_BOX, ICON_RULER, STATE_CLASS_MEASUREMENT, - UNIT_EMPTY, ENTITY_CATEGORY_DIAGNOSTIC) +from esphome.const import ( + ICON_ARROW_EXPAND_VERTICAL, + ICON_COUNTER, + ICON_NEW_BOX, + ICON_RULER, + STATE_CLASS_MEASUREMENT, + UNIT_EMPTY, + ENTITY_CATEGORY_DIAGNOSTIC, +) from . import Roode, CONF_ROODE_ID DEPENDENCIES = ["roode"] -CONF_DISTANCE = 'distance_sensor' -CONF_PEOPLE_COUNTER = 'people_counter_sensor' -CONF_MAX_THRESHOLD_ZONE0 = 'max_threshold_zone0' -CONF_MAX_THRESHOLD_ZONE1 = 'max_threshold_zone1' -CONF_MIN_THRESHOLD_ZONE0 = 'min_threshold_zone0' -CONF_MIN_THRESHOLD_ZONE1 = 'min_threshold_zone1' -CONF_ROI_HEIGHT = 'roi_height' -CONF_ROI_WIDTH = 'roi_width' -CONFIG_SCHEMA = sensor.sensor_schema().extend({ - cv.Optional(CONF_DISTANCE): - sensor.sensor_schema( - icon=ICON_RULER, - unit_of_measurement=UNIT_EMPTY, - accuracy_decimals=2, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_PEOPLE_COUNTER): - sensor.sensor_schema( - icon=ICON_COUNTER, - unit_of_measurement=UNIT_EMPTY, - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_MAX_THRESHOLD_ZONE0): - sensor.sensor_schema( - icon="mdi:map-marker-distance", - unit_of_measurement="mm", - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_MAX_THRESHOLD_ZONE1): - sensor.sensor_schema( - icon="mdi:map-marker-distance", - unit_of_measurement="mm", - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_MIN_THRESHOLD_ZONE0): - sensor.sensor_schema( - icon="mdi:map-marker-distance", - unit_of_measurement="mm", - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_MIN_THRESHOLD_ZONE1): - sensor.sensor_schema( - icon="mdi:map-marker-distance", - unit_of_measurement="mm", - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_ROI_HEIGHT): - sensor.sensor_schema( - icon="mdi:table-row-height", - unit_of_measurement="px", - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_ROI_WIDTH): - sensor.sensor_schema( - icon="mdi:table-column-width", - unit_of_measurement="px", - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.GenerateID(CONF_ROODE_ID): - cv.use_id(Roode), -}) +CONF_DISTANCE = "distance_sensor" +CONF_PEOPLE_COUNTER = "people_counter_sensor" +CONF_MAX_THRESHOLD_ZONE0 = "max_threshold_zone0" +CONF_MAX_THRESHOLD_ZONE1 = "max_threshold_zone1" +CONF_MIN_THRESHOLD_ZONE0 = "min_threshold_zone0" +CONF_MIN_THRESHOLD_ZONE1 = "min_threshold_zone1" +CONF_ROI_HEIGHT = "roi_height" +CONF_ROI_WIDTH = "roi_width" +CONFIG_SCHEMA = sensor.sensor_schema().extend( + { + cv.Optional(CONF_DISTANCE): sensor.sensor_schema( + icon=ICON_RULER, + unit_of_measurement="mm", + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_PEOPLE_COUNTER): sensor.sensor_schema( + icon=ICON_COUNTER, + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_MAX_THRESHOLD_ZONE0): sensor.sensor_schema( + icon="mdi:map-marker-distance", + unit_of_measurement="mm", + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_MAX_THRESHOLD_ZONE1): sensor.sensor_schema( + icon="mdi:map-marker-distance", + unit_of_measurement="mm", + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_MIN_THRESHOLD_ZONE0): sensor.sensor_schema( + icon="mdi:map-marker-distance", + unit_of_measurement="mm", + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_MIN_THRESHOLD_ZONE1): sensor.sensor_schema( + icon="mdi:map-marker-distance", + unit_of_measurement="mm", + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_ROI_HEIGHT): sensor.sensor_schema( + icon="mdi:table-row-height", + unit_of_measurement="px", + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_ROI_WIDTH): sensor.sensor_schema( + icon="mdi:table-column-width", + unit_of_measurement="px", + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.GenerateID(CONF_ROODE_ID): cv.use_id(Roode), + } +) async def to_code(config): @@ -109,4 +109,4 @@ async def to_code(config): cg.add(var.set_roi_height_sensor(count)) if CONF_ROI_WIDTH in config: count = await sensor.new_sensor(config[CONF_ROI_WIDTH]) - cg.add(var.set_roi_width_sensor(count)) \ No newline at end of file + cg.add(var.set_roi_width_sensor(count)) diff --git a/components/roode/text_sensor.py b/components/roode/text_sensor.py index 0e1b2f38..ebc750a2 100644 --- a/components/roode/text_sensor.py +++ b/components/roode/text_sensor.py @@ -1,7 +1,12 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import text_sensor -from esphome.const import CONF_ID, CONF_ICON, CONF_ENTITY_CATEGORY, ENTITY_CATEGORY_DIAGNOSTIC +from esphome.const import ( + CONF_ID, + CONF_ICON, + CONF_ENTITY_CATEGORY, + ENTITY_CATEGORY_DIAGNOSTIC, +) from . import Roode, CONF_ROODE_ID DEPENDENCIES = ["roode"] @@ -11,28 +16,29 @@ TYPES = [VERSION, ENTRY_EXIT_EVENT] -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_ROODE_ID): - cv.use_id(Roode), - cv.Optional(VERSION): - text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.Optional(CONF_ICON, default="mdi:git"): - text_sensor.icon, - cv.GenerateID(): - cv.declare_id(text_sensor.TextSensor), - cv.Optional(CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC): - cv.entity_category, - }), - cv.Optional(ENTRY_EXIT_EVENT): - text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.Optional(CONF_ICON, default="mdi:sign-direction"): - text_sensor.icon, - cv.GenerateID(): - cv.declare_id(text_sensor.TextSensor), - cv.Optional(CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC): - cv.entity_category, - }), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_ROODE_ID): cv.use_id(Roode), + cv.Optional(VERSION): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.Optional(CONF_ICON, default="mdi:git"): text_sensor.icon, + cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), + cv.Optional( + CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC + ): cv.entity_category, + } + ), + cv.Optional(ENTRY_EXIT_EVENT): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.Optional(CONF_ICON, default="mdi:sign-direction"): text_sensor.icon, + cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), + cv.Optional( + CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_DIAGNOSTIC + ): cv.entity_category, + } + ), + } +) async def setup_conf(config, key, hub): @@ -46,4 +52,4 @@ async def setup_conf(config, key, hub): async def to_code(config): hub = await cg.get_variable(config[CONF_ROODE_ID]) for key in TYPES: - await setup_conf(config, key, hub) \ No newline at end of file + await setup_conf(config, key, hub) diff --git a/peopleCounter.yaml b/peopleCounter.yaml index 3dcd76f5..d387f7fc 100644 --- a/peopleCounter.yaml +++ b/peopleCounter.yaml @@ -7,7 +7,7 @@ external_components: source: type: git url: https://github.com/Lyr3x/Roode - ref: v.1.3.4 + ref: master esphome: name: $devicename diff --git a/peopleCounter32.yaml b/peopleCounter32.yaml index 6520598b..ee94e4b9 100644 --- a/peopleCounter32.yaml +++ b/peopleCounter32.yaml @@ -7,7 +7,7 @@ external_components: source: type: git url: https://github.com/Lyr3x/Roode - ref: v.1.3.4 + ref: master esphome: name: $devicename