Skip to content

Commit fe3284f

Browse files
authored
Merge pull request #32 from Lyr3x/v1.1-pololu
V1.1 pololu
2 parents 54e9492 + 8c97f71 commit fe3284f

File tree

65 files changed

+403
-51115
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+403
-51115
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
.vscode/
88
lib/Configuration/Config.h
99
people_counter/
10-
.esphome/
10+
people_counter_dev/
11+
.esphome/
12+
.DS_Store

README.md

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,94 @@
11
# RooDe
2+
23
People counter working with any smart home system which supports ESPHome and therefore Home Assistamt. All necessary entities are created automatically.
34

45

56
[![Roode community](https://img.shields.io/discord/879407995837087804.svg?label=Discord&logo=Discord&colorB=7289da&style=for-the-badge)](https://discord.gg/RK3KJeSy)
67

78

9+
![Roode](Roode.png)
10+
11+
## Algorithm
12+
The implemented Algorithm is an improved version of my own implementation which checks the direction of a movement through two defined zones. ST implemented a nice and efficient way to track the path from one to the other direction. I migrated the algorigthm with some changes into the Roode project.
13+
The concept of path tracking is the detecion of a human:
14+
* In the first zone only
15+
* In both zones
16+
* In the second zone only
17+
* In no zone
18+
19+
That way we can ensure the direction of movement.
20+
21+
The sensor creates a 16x16 grid and the final distance is computed by taking the average of the distance of the values of the grid.
22+
We are defining two different Region of Interest (ROI) inside this grid. Then the sensor will measure the two distances in the two zones and will detect any presence and tracks the path to receive the direction.
23+
24+
However, the algorithm is very sensitive to the slightest modification of the ROI, regarding both its size and its positioning inside the grid.
25+
26+
ST Microelectronics define the values for the parameters as default like this:
27+
- `ROI_width = 8 //min 4`
28+
- `ROI_height = 16 //min 4`
29+
- `center = {167,231}`
30+
31+
The center of the ROI you set is based on the table below and the optical center has to be set as the pad above and to the right of your exact center:
32+
33+
34+
35+
| 128 | 136 | 144 | 152 | 160 | 168 | 176 | 184 | 192 | 200 | 208 | 216 | 224 | 232 | 240 | 248 |
36+
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
37+
| 129 | 137 | 145 | 153 | 161 | 169 | 177 | 185 | 193 | 201 | 209 | 217 | 225 | 233 | 241 | 249 |
38+
| 130 | 138 | 146 | 154 | 162 | 170 | 178 | 186 | 194 | 202 | 210 | 218 | 226 | 234 | 242 | 250 |
39+
| 131 | 139 | 146 | 155 | 163 | 171 | 179 | 187 | 195 | 203 | 211 | 219 | 227 | 235 | 243 | 251 |
40+
| 132 | 140 | 147 | 156 | 164 | 172 | 180 | 188 | 196 | 204 | 212 | 220 | 228 | 236 | 244 | 252 |
41+
| 133 | 141 | 148 | 157 | 165 | 173 | 181 | 189 | 197 | 205 | 213 | 221 | 229 | 237 | 245 | 253 |
42+
| 134 | 142 | 149 | 158 | 166 | 174 | 182 | 190 | 198 | 206 | 214 | 222 | 230 | 238 | 246 | 254 |
43+
| 135 | 143 | 150 | 159 | 167 | 175 | 183 | 191 | 199 | 207 | 215 | 223 | 231 | 239 | 247 | 255 |
44+
| 127 | 119 | 111 | 103 | 95 | 87 | 79 | 71 | 63 | 55 | 47 | 39 | 31 | 23 | 15 | 7 |
45+
| 126 | 118 | 110 | 102 | 94 | 86 | 78 | 70 | 62 | 54 | 46 | 38 | 30 | 22 | 14 | 6 |
46+
| 125 | 117 | 109 | 101 | 93 | 85 | 77 | 69 | 61 | 53 | 45 | 37 | 29 | 21 | 13 | 5 |
47+
| 124 | 116 | 108 | 100 | 92 | 84 | 76 | 68 | 60 | 52 | 44 | 36 | 28 | 20 | 12 | 4 |
48+
| 123 | 115 | 107 | 99 | 91 | 83 | 75 | 67 | 59 | 51 | 43 | 35 | 27 | 19 | 11 | 3 |
49+
| 122 | 114 | 106 | 98 | 90 | 82 | 74 | 66 | 58 | 50 | 42 | 34 | 26 | 18 | 10 | 2 |
50+
| 121 | 113 | 105 | 97 | 89 | 81 | 73 | 65 | 57 | 49 | 41 | 33 | 25 | 17 | 9 | 1 |
51+
| 120 | 112 | 104 | 96 | 88 | 80 | 72 | 64 | 56 | 48 | 40 | 32 | 24 | 16 | 8 | 0 |
52+
53+
54+
55+
56+
57+
#### Threshold distance
58+
59+
Another crucial choice is the one corresponding to the threshold. Indeed a movement is detected whenever the distance read by the sensor is below this value. The code contains a vector as threshold, as one (as myself) might need a different threshold for each zone.
60+
61+
The SparkFun library also supports more formats for the threshold: for example one can set that a movement is detected whenever the distance is between two values. However, more information for the interested reader can be found on the corresponding page.
62+
63+
With the updated code (however only for esp32 at the moment) the threshold is automatically calculated by the sensor. To do so it is necessary to position the sensor and, after turning it on, wait for 10 seconds without passing under it. After this time, the average of the measures for each zone will be computed and the thereshold for each ROI will correspond to 80% of the average value. Also the value of 80% can be modified in the code, by editing the variable `threshold_percentage`
64+
65+
The calibration of the threshold can also be triggered by a MQTT message. An example for doing so is in the file `integration_with_home_assistant.md`.
66+
67+
68+
69+
## Useful links
70+
71+
[SparkFun library guide](https://learn.sparkfun.com/tutorials/qwiic-distance-sensor-vl53l1x-hookup-guide/all) with more information about the functions used in the code
872

973
## Hardware
1074
There will be a specific Hardware setup (recommended brands etc.) soon!
11-
* ESP8266 or ESP32
12-
* 1x VL53L1X
75+
* ESP8266 or ESP32 (Wemos D1 mini case will be available)
76+
* 1x VL53L1X (GY-53 and cheap chinese sensors)
1377
* Optional HC-SR501
1478
* Optional 128x32 OLED
1579
* Power Supply
1680
* Encolsure (see .stl files) - will be updated soon!
81+
Pins:
82+
#define SDA_PIN D2
83+
#define SCL_PIN D1
84+
85+
## Configuration
86+
### ESPHome
87+
Configue at least the secrets.yaml with your wifi SSID and password to connect. Check the peopleCounter.yaml to adapt the exposed sensors to your needs.
88+
89+
### Entry/Exit inverted:
90+
Set #define INVERT_DIRECTION or comment it out to invert the direction.
91+
1792

1893
## Configuration
1994
Be sure to configure your wifi credentials and adapt the Config.h to set everyhting to your needs.
@@ -65,4 +140,4 @@ Calibration v2 calibrates both zones individually (thanks to @andrea-fox).
65140
* CALIBRATION_VAL to 4000
66141
### Changelog v.0.9.4-release
67142
* Added standard deviation threhsold calculation
68-
* Removed constant THRESHOLD_X
143+
* Removed constant THRESHOLD_X

home_assistant.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
To make all functions of Roode work with home assistant you need to set up a few entities and automations.
2+
Roode has endpoints to set the count value, reset the counter to 0 and to recalibrate. Unfourtunately its not possible to expose buttons via ESPHome that do just that.
3+
4+
```
5+
# This automation script runs when a value is received via MQTT on retained topic: setTemperature
6+
# It sets the value slider on the GUI. This slides also had its own automation when the value is changed.
7+
- alias: "Set people slider"
8+
trigger:
9+
platform: mqtt
10+
topic: "people_counter/sensor/people_counter_people/state"
11+
action:
12+
service: input_number.set_value
13+
target:
14+
entity_id: input_number.set_people
15+
data:
16+
value: "{{ trigger.payload }}"
17+
- alias: "People slider moved"
18+
trigger:
19+
platform: state
20+
entity_id: input_number.set_people
21+
action:
22+
service: mqtt.publish
23+
data:
24+
topic: "people_counter/sensor/people/set"
25+
retain: true
26+
payload: "{{ states('input_number.set_people') | int }}"
27+
```

lib/Calibration/Calibration.h

Lines changed: 40 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -35,92 +35,20 @@ static int delay_between_measurements_short = 22;
3535
// value which defines the threshold which activates the short distance mode (the sensor supports it only up to a distance of 1300 mm)
3636
static int short_distance_threshold = 1300;
3737

38-
/*
39-
##### CALIBRATION END #####
40-
*/
4138
#endif //#ifdef CALIBRATIONV2
4239

43-
#ifdef CALIBRATION
44-
int calculateStandardDeviation(int irValues[])
45-
{
46-
auto sumOfValues = 0;
47-
auto arrLength = 0;
48-
for (int i = 0; i < 30; i++)
49-
{
50-
if (irValues[i] != 0)
51-
{
52-
sumOfValues += irValues[i];
53-
arrLength++;
54-
}
55-
}
56-
57-
auto meanValue = sumOfValues / arrLength;
58-
59-
auto standardDeviation = 0;
60-
for (int i = 0; i < arrLength; ++i)
61-
{
62-
standardDeviation += pow(irValues[i] - meanValue, 2);
63-
}
64-
standardDeviation /= arrLength;
65-
66-
standardDeviation = sqrt(standardDeviation);
67-
68-
return standardDeviation;
69-
}
70-
71-
//Calibration v1
72-
void calibration(VL53L1XSensor Sensor)
73-
{
74-
ESP_LOGI("VL53L1X custom sensor", "#### calibration started ####");
75-
int irValues[30] = {};
76-
uint16_t min = 0;
77-
auto n = 0;
78-
Sensor.readRangeContinuoisMillimeters(roiConfig1, 100);
79-
for (int m = 0; m < CALIBRATION_VAL; m++)
80-
{
81-
auto sensor_value = Sensor.readRangeContinuoisMillimeters(roiConfig1);
82-
83-
// #ifdef MY_DEBUG
84-
ESP_LOGD("VL53L1X custom sensor", "sensor_value: %d", sensor_value);
85-
// #endif
86-
//calculate the max without jumps for the room sensor
87-
if ((sensor_value < min) || ((sensor_value - min) == sensor_value))
88-
{
89-
ESP_LOGD("VL53L1X custom sensor", "sensor_value: %d", sensor_value);
90-
min = sensor_value;
91-
if (n < 30)
92-
{
93-
irValues[n] = min;
94-
n++;
95-
}
96-
}
97-
}
98-
auto sd = 0;
99-
100-
sd = calculateStandardDeviation(irValues);
101-
102-
// Serial.print("standard deviation: " + threshold);
103-
// threshold = max + THRESHOLD_X;#
104-
#undef DIST_THRESHOLD_MAX
105-
#define DIST_THRESHOLD_MAX min - sd
106-
ESP_LOGI("VL53L1X custom sensor", "standard deviation: %d", sd);
107-
ESP_LOGI("VL53L1X custom sensor", "new threshold: %d", DIST_THRESHOLD_MAX);
108-
ESP_LOGI("VL53L1X custom sensor", "#### calibration done ####");
109-
}
110-
111-
#endif //#ifdef CALIBRATION
112-
11340
#ifdef CALIBRATIONV2
114-
void calibration(VL53L1XSensor Sensor)
41+
void calibration(VL53L1X distanceSensor)
11542
{
11643
// the sensor does 100 measurements for each zone (zones are predefined)
117-
Sensor.setIntermeasurementPeriod(time_budget_in_ms_long);
118-
Sensor.setRangeMode(LONG_RANGE);
11944
time_budget_in_ms = time_budget_in_ms_long;
12045
delay_between_measurements = delay_between_measurements_long;
46+
distanceSensor.startContinuous(delay_between_measurements);
47+
distanceSensor.setDistanceMode(VL53L1X::Long);
48+
distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000);
12149
center[0] = 167;
12250
center[1] = 231;
123-
ROI_height = 8;
51+
ROI_height = 16;
12452
ROI_width = 8;
12553
delay(500);
12654

@@ -132,19 +60,23 @@ void calibration(VL53L1XSensor Sensor)
13260
for (int i = 0; i < number_attempts; i++)
13361
{
13462
// increase sum of values in Zone 1
135-
Sensor.startMeasurement();
136-
delay(delay_between_measurements);
137-
distance = Sensor.readRangeContinuoisMillimeters(roiConfig1);
138-
Sensor.stopMeasurement();
63+
distanceSensor.setROISize(ROI_width, ROI_height);
64+
distanceSensor.setROICenter(center[zone]);
65+
distanceSensor.startContinuous(delay_between_measurements);
66+
distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000);
67+
distance = distanceSensor.read();
68+
distanceSensor.stopContinuous();
13969
sum_zone_0 = sum_zone_0 + distance;
14070
zone++;
14171
zone = zone % 2;
14272

14373
// increase sum of values in Zone 2
144-
Sensor.startMeasurement();
145-
delay(delay_between_measurements);
146-
distance = Sensor.readRangeContinuoisMillimeters(roiConfig2);
147-
Sensor.stopMeasurement();
74+
distanceSensor.setROISize(ROI_width, ROI_height);
75+
distanceSensor.setROICenter(center[zone]);
76+
distanceSensor.startContinuous(delay_between_measurements);
77+
distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000);
78+
distance = distanceSensor.read();
79+
distanceSensor.stopContinuous();
14880
sum_zone_1 = sum_zone_1 + distance;
14981
zone++;
15082
zone = zone % 2;
@@ -161,10 +93,9 @@ void calibration(VL53L1XSensor Sensor)
16193
if (average_zone_0 <= short_distance_threshold || average_zone_1 <= short_distance_threshold)
16294
{
16395
// we can use the short mode, which allows more precise measurements up to 1.3 meters
164-
Sensor.setIntermeasurementPeriod(time_budget_in_ms_short);
165-
Sensor.setRangeMode(SHORT_RANGE);
16696
time_budget_in_ms = time_budget_in_ms_short;
16797
delay_between_measurements = delay_between_measurements_short;
98+
distanceSensor.setDistanceMode(VL53L1X::Short);
16899
}
169100
delay(250);
170101

@@ -230,19 +161,23 @@ void calibration(VL53L1XSensor Sensor)
230161
for (int i = 0; i < number_attempts; i++)
231162
{
232163
// increase sum of values in Zone 0
233-
Sensor.startMeasurement();
234-
delay(delay_between_measurements);
235-
distance = Sensor.readRangeContinuoisMillimeters(roiConfig1);
236-
Sensor.stopMeasurement();
164+
distanceSensor.setROISize(ROI_width, ROI_height);
165+
distanceSensor.setROICenter(center[zone]);
166+
distanceSensor.startContinuous(delay_between_measurements);
167+
distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000);
168+
distance = distanceSensor.read();
169+
distanceSensor.stopContinuous();
237170
sum_zone_0 = sum_zone_0 + distance;
238171
zone++;
239172
zone = zone % 2;
240173

241174
// increase sum of values in Zone 1
242-
Sensor.startMeasurement();
243-
delay(delay_between_measurements);
244-
distance = Sensor.readRangeContinuoisMillimeters(roiConfig2);
245-
Sensor.stopMeasurement();
175+
distanceSensor.setROISize(ROI_width, ROI_height);
176+
distanceSensor.setROICenter(center[zone]);
177+
distanceSensor.startContinuous(delay_between_measurements);
178+
distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000);
179+
distance = distanceSensor.read();
180+
distanceSensor.stopContinuous();
246181
sum_zone_1 = sum_zone_1 + distance;
247182
zone++;
248183
zone = zone % 2;
@@ -254,15 +189,15 @@ void calibration(VL53L1XSensor Sensor)
254189

255190
DIST_THRESHOLD_MAX[0] = threshold_zone_0;
256191
DIST_THRESHOLD_MAX[1] = threshold_zone_1;
257-
ESP_LOGI("VL53L1X custom sensor", "Threshold zone1: %d", threshold_zone_0);
258-
ESP_LOGI("VL53L1X custom sensor", "Threshold zone2: %d", threshold_zone_1);
259-
delay(2000);
260192

261193
// we now save the values into the EEPROM memory
262194
int hundred_threshold_zone_0 = threshold_zone_0 / 100;
263195
int hundred_threshold_zone_1 = threshold_zone_1 / 100;
264196
int unit_threshold_zone_0 = threshold_zone_0 - 100 * hundred_threshold_zone_0;
265197
int unit_threshold_zone_1 = threshold_zone_1 - 100 * hundred_threshold_zone_1;
198+
ESP_LOGI("VL53L1X custom sensor", "Threshold zone1: %d", threshold_zone_0);
199+
ESP_LOGI("VL53L1X custom sensor", "Threshold zone2: %d", threshold_zone_1);
200+
delay(2000);
266201

267202
EEPROM.write(0, 1);
268203
EEPROM.write(1, center[0]);
@@ -275,7 +210,7 @@ void calibration(VL53L1XSensor Sensor)
275210
EEPROM.commit();
276211
}
277212

278-
void calibration_boot(VL53L1XSensor Sensor)
213+
void calibration_boot(VL53L1X distanceSensor)
279214
{
280215
ESP_LOGI("VL53L1X custom sensor", "#### calibration started ####");
281216
if (save_calibration_result)
@@ -296,28 +231,28 @@ void calibration_boot(VL53L1XSensor Sensor)
296231
// if the distance measured is small, then we can use the short range mode of the sensor
297232
if (min(DIST_THRESHOLD_MAX[0], DIST_THRESHOLD_MAX[1]) <= short_distance_threshold)
298233
{
299-
Sensor.setIntermeasurementPeriod(time_budget_in_ms_short);
300-
Sensor.setRangeMode(SHORT_RANGE);
301234
time_budget_in_ms = time_budget_in_ms_short;
302235
delay_between_measurements = delay_between_measurements_short;
236+
distanceSensor.setDistanceMode(VL53L1X::Short);
237+
distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000);
303238
}
304239
else
305240
{
306-
Sensor.setIntermeasurementPeriod(time_budget_in_ms_short);
307-
Sensor.setRangeMode(LONG_RANGE);
308241
time_budget_in_ms = time_budget_in_ms_long;
309242
delay_between_measurements = delay_between_measurements_long;
243+
distanceSensor.setDistanceMode(VL53L1X::Long);
244+
distanceSensor.setMeasurementTimingBudget(time_budget_in_ms * 1000);
310245
}
311246
// client.publish(mqtt_serial_publish_distance_ch, "All values updated");
312247
}
313248
else
314249
{
315250
// there are no data in the EEPROM memory
316-
calibration(Sensor);
251+
calibration(distanceSensor);
317252
}
318253
}
319254
else
320-
calibration(Sensor);
255+
calibration(distanceSensor);
321256
ESP_LOGI("VL53L1X custom sensor", "#### calibration done ####");
322257
}
323258
#endif //#ifdef CALIBRATIONV2

0 commit comments

Comments
 (0)