diff --git a/include/Consts.h b/include/Consts.h index e7ede351..c9ef6d63 100644 --- a/include/Consts.h +++ b/include/Consts.h @@ -20,12 +20,12 @@ /* * Sensor */ -//#define level_enable -//#define analog_enable -//#define dallas_enable -//#define dht_enable -//#define bmp_enable -//#define bme_enable +#define level_enable +#define analog_enable +#define dallas_enable +#define dht_enable +#define bmp_enable +#define bme_enable /* * Logging diff --git a/include/Global.h b/include/Global.h index 4f224d5a..e992709a 100644 --- a/include/Global.h +++ b/include/Global.h @@ -114,23 +114,26 @@ extern AsyncEventSource events; extern int sensors_reading_map[15]; -enum { ROUTER_SEARCHING, - WIFI_MQTT_CONNECTION_CHECK, - SENSORS, - STEPPER1, - STEPPER2, - LOG1, - LOG2, - LOG3, - LOG4, - LOG5, - TIMER_COUNTDOWN, - TIME, - TIME_SYNC, - STATISTICS, - UDP, - UDP_DB, - TEST }; +enum +{ + ROUTER_SEARCHING, + WIFI_MQTT_CONNECTION_CHECK, + SENSORS, + STEPPER1, + STEPPER2, + LOG1, + LOG2, + LOG3, + LOG4, + LOG5, + TIMER_COUNTDOWN, + TIME, + TIME_SYNC, + STATISTICS, + UDP, + UDP_DB, + TEST +}; extern TickerScheduler ts; @@ -150,34 +153,30 @@ extern String received_udp_line; extern int udp_period; #ifdef level_enable -#include "GyverFilters.h" //настраивается в GyverHacks.h - MEDIAN_FILTER_SIZE -GMedian medianFilter; +#include "GyverFilters.h" +GMedian<10, int> medianFilter; #endif -#ifdef dallas_enable #include #include -OneWire *oneWire; -DallasTemperature sensors; -#endif +extern OneWire *oneWire; +extern DallasTemperature sensors; -#ifdef dht_enable #include DHTesp dht; -#endif #include #ifdef bmp_enable #include -Adafruit_BMP280 bmp; // use I2C interface +Adafruit_BMP280 bmp; // use I2C interface Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor(); Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor(); #endif #ifdef bme_enable #include -Adafruit_BME280 bme; // use I2C interface +Adafruit_BME280 bme; // use I2C interface Adafruit_Sensor *bme_temp = bme.getTemperatureSensor(); Adafruit_Sensor *bme_pressure = bme.getPressureSensor(); Adafruit_Sensor *bme_humidity = bme.getHumiditySensor(); @@ -256,6 +255,10 @@ extern String writeFile(String fileName, String strings); extern String readFile(String fileName, size_t len); extern String addFile(String fileName, String strings); +// Main Utils - явно +extern uint8_t hexStringToUint8(String hex); +extern uint16_t hexStringToUint16(String hex); + //STRING extern String selectToMarkerLast(String str, String found); extern String selectToMarker(String str, String found); @@ -293,8 +296,45 @@ extern void eventGen(String event_name, String number); extern String add_set(String param_name); //Sensors +// И как раз тут хорошо просто в Sensors.h это пихать - а не в один здоровенный ФАЙЛ extern void sensors_init(); +extern void level(); +extern void level_reading(); + +extern void analog(); +extern void analog_reading1(); +extern void analog_reading2(); +extern void dallas_reading(); +extern void dhtT_reading(); + +extern void dallas(); + +extern void bmp280T(); +extern void bmp280P(); +extern void bmp280T_reading(); +extern void bmp280P_reading(); + +extern void bme280T(); +extern void bme280P(); +extern void bme280H(); +extern void bme280A(); + +extern void bme280T_reading(); +extern void bme280P_reading(); +extern void bme280H_reading(); +extern void bme280A_reading(); + +extern void dhtT(); +extern void dhtH(); +extern void dhtP(); +extern void dhtC(); +extern void dhtD(); +extern void dhtH_reading(); +extern void dhtP_reading(); +extern void dhtC_reading(); +extern void dhtD_reading(); + //Timers extern void Timer_countdown_init(); extern void timerStart_(); @@ -326,7 +366,6 @@ extern void choose_widget_and_create(String widget_name, String page_name, Strin extern void createChart(String widget_name, String page_name, String page_number, String file, String topic, String maxCount); // Push - extern void Push_init(); // UDP diff --git a/lib/GyverFilters/examples/GFilterRA/GFilterRA.ino b/lib/GyverFilters/examples/GFilterRA/GFilterRA.ino new file mode 100644 index 00000000..c6db1517 --- /dev/null +++ b/lib/GyverFilters/examples/GFilterRA/GFilterRA.ino @@ -0,0 +1,16 @@ +#include "GyverFilters.h" +GFilterRA analog0; // фильтр назовём analog0 + +void setup() { + Serial.begin(9600); + + // установка коэффициента фильтрации (0.0... 1.0). Чем меньше, тем плавнее фильтр + analog0.setCoef(0.01); + + // установка шага фильтрации (мс). Чем меньше, тем резче фильтр + analog0.setStep(10); +} + +void loop() { + Serial.println(analog0.filteredTime(analogRead(0))); +} diff --git a/lib/GyverFilters/examples/GLinear_arrays/GLinear_arrays.ino b/lib/GyverFilters/examples/GLinear_arrays/GLinear_arrays.ino new file mode 100644 index 00000000..6c407ec4 --- /dev/null +++ b/lib/GyverFilters/examples/GLinear_arrays/GLinear_arrays.ino @@ -0,0 +1,32 @@ +/* + Пример линейной аппроксимации методом наименьших квадратов + Два массива: по оси Х и по оси У + Линейная аппроксимация повозоляет получить уравнение прямой, + равноудалённой от точек на плоскости ХУ. Удобно для расчёта + роста изменяющейся шумящей величины. Уравнение вида у = A*x + B + В папке с данным примером есть скриншот из excel, + иллюстрирующий работу аппроксимации с такими же исходными +*/ + +// два массива с данными (одинаковой размероности и размера) +int x_array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; +int y_array[] = {1, 5, 2, 8, 3, 9, 10, 5, 15, 12}; + +#include +GLinear test; // указываем тип данных в <> + +void setup() { + Serial.begin(9600); + + // передаём массивы и размер одного из них + test.compute((int*)x_array, (int*)y_array, sizeof(x_array)); + + // Уравнение вида у = A*x + B + Serial.println(test.getA()); // получить коэффициент А + Serial.println(test.getB()); // получить коэффициент В + Serial.println(test.getDelta()); // получить изменение (аппроксимированное) +} + +void loop() { + +} \ No newline at end of file diff --git a/lib/GyverFilters/examples/GLinear_arrays/excel.jpg b/lib/GyverFilters/examples/GLinear_arrays/excel.jpg new file mode 100644 index 00000000..9cd71117 Binary files /dev/null and b/lib/GyverFilters/examples/GLinear_arrays/excel.jpg differ diff --git a/lib/GyverFilters/examples/GLinear_running/GLinear_running.ino b/lib/GyverFilters/examples/GLinear_running/GLinear_running.ino new file mode 100644 index 00000000..575c50dc --- /dev/null +++ b/lib/GyverFilters/examples/GLinear_running/GLinear_running.ino @@ -0,0 +1,34 @@ +/* + Пример линейной аппроксимации методом наименьших квадратов + Два массива: по оси Х и по оси У + Наполнение массивов осуществляется динамически: сдвигом и записью в крайнюю ячейку, + то есть аппроксимация по последним ARRAY_SIZE изменениям!! +*/ +#define ARRAY_SIZE 10 // размер пространства для аппроксимации + +// два массива с данными (одинаковой размероности и размера) +int x_array[ARRAY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // ось x от 1 до 10, допустим СЕКУНД +int y_array[ARRAY_SIZE]; // значения по оси У будем брать с датчика + +#include +GLinear test; // указываем тип данных в <> + +void setup() { + Serial.begin(9600); +} + +void loop() { + for (byte i = 0; i < ARRAY_SIZE - 1; i++) { // счётчик от 0 до ARRAY_SIZE + y_array[i] = y_array[i + 1]; // сдвинуть массив давлений КРОМЕ ПОСЛЕДНЕЙ ЯЧЕЙКИ на шаг назад + } + // последний элемент массива теперь - новое значение (просто с аналог. датчика) + y_array[ARRAY_SIZE - 1] = analogRead(0); + + // передаём массивы и размер одного из них + test.compute((int*)x_array, (int*)y_array, sizeof(x_array)); + + // по нашим исходным данным это будет производная, т.е. "изменение единиц в секунду" + Serial.println(test.getDelta()); // получить изменение (аппроксимированное) + + delay(1000); // секундная задержка +} diff --git a/lib/GyverFilters/examples/alphabeta_example/alphabeta_example.ino b/lib/GyverFilters/examples/alphabeta_example/alphabeta_example.ino new file mode 100644 index 00000000..8f43b77a --- /dev/null +++ b/lib/GyverFilters/examples/alphabeta_example/alphabeta_example.ino @@ -0,0 +1,24 @@ +/* + Пример альфа-бета фильтра +*/ + +#include "GyverFilters.h" + +// параметры: период дискретизации (измерений), process variation, noise variation +GABfilter testFilter(0.08, 40, 1); + +void setup() { + Serial.begin(9600); +} + +void loop() { + delay(80); + int value = analogRead(0); + value += random(2) * random(-1, 2) * random(10, 70); + Serial.print("$"); + Serial.print(value); + Serial.print(" "); + value = testFilter.filtered((int)value); + Serial.print(value); + Serial.println(";"); +} diff --git a/lib/GyverFilters/examples/filters_comparsion/filters_comparsion.ino b/lib/GyverFilters/examples/filters_comparsion/filters_comparsion.ino new file mode 100644 index 00000000..b981993a --- /dev/null +++ b/lib/GyverFilters/examples/filters_comparsion/filters_comparsion.ino @@ -0,0 +1,30 @@ +/* + Сравнение калмана и бегущего среднего +*/ +#include "GyverFilters.h" + +// параметры: разброс измерения, разброс оценки, скорость изменения значений +// разброс измерения: шум измерений +// разброс оценки: подстраивается сам, можно поставить таким же как разброс измерения +// скорость изменения значений: 0.001-1, варьировать самому + +GKalman kalman(90, 90, 0.5); +GFilterRA average(0.5, 80); + +void setup() { + Serial.begin(9600); +} + +void loop() { + int value = analogRead(0); + value += random(2) * random(-1, 2) * random(50, 100); + Serial.print("$"); + Serial.print(value); + Serial.print(" "); + + Serial.print((int)kalman.filtered(value)); + Serial.print(" "); + Serial.print((int)average.filtered(value)); + Serial.println(";"); + delay(80); +} diff --git a/lib/GyverFilters/examples/kalman_example/kalman_example.ino b/lib/GyverFilters/examples/kalman_example/kalman_example.ino new file mode 100644 index 00000000..802307af --- /dev/null +++ b/lib/GyverFilters/examples/kalman_example/kalman_example.ino @@ -0,0 +1,31 @@ +/* + Пример простого одномерного фильтра +*/ + +#include "GyverFilters.h" + +// параметры: разброс измерения, разброс оценки, скорость изменения значений +// разброс измерения: шум измерений +// разброс оценки: подстраивается сам, можно поставить таким же как разброс измерения +// скорость изменения значений: 0.001-1, варьировать самому + +GKalman testFilter(40, 40, 0.5); + +// также может быть объявлен как (разброс измерения, скорость изменения значений) +// GKalman testFilter(40, 0.5); + +void setup() { + Serial.begin(9600); +} + +void loop() { + delay(80); + int value = analogRead(0); + value += random(2) * random(-1, 2) * random(10, 70); + Serial.print("$"); + Serial.print(value); + Serial.print(" "); + value = testFilter.filtered((int)value); + Serial.print(value); + Serial.println(";"); +} diff --git a/lib/GyverFilters/examples/median3_example/median3_example.ino b/lib/GyverFilters/examples/median3_example/median3_example.ino new file mode 100644 index 00000000..c99176bc --- /dev/null +++ b/lib/GyverFilters/examples/median3_example/median3_example.ino @@ -0,0 +1,21 @@ +/* + Пример использования быстрого медианного фильтра 3 порядка +*/ + +#include "GyverFilters.h" +GMedian3 testFilter; // указываем тип данных в <> + +void setup() { + Serial.begin(9600); +} + +void loop() { + int value = analogRead(0); + // добавляем шум "выбросы" + value += random(2) * random(2) * random(-1, 2) * random(50, 250); + Serial.print(value); + Serial.print(','); + value = testFilter.filtered(value); + Serial.println(value); + delay(80); +} diff --git a/lib/GyverFilters/examples/median_example/median_example.ino b/lib/GyverFilters/examples/median_example/median_example.ino new file mode 100644 index 00000000..c3fb0067 --- /dev/null +++ b/lib/GyverFilters/examples/median_example/median_example.ino @@ -0,0 +1,23 @@ +/* + Пример использования медианного фильтра. +*/ + +#include "GyverFilters.h" + +// указываем размер окна и тип данных в <> +GMedian<10, int> testFilter; + +void setup() { + Serial.begin(9600); +} + +void loop() { + delay(80); + int value = analogRead(0); + // добавляем шум "выбросы" + value += random(2) * random(2) * random(-1, 2) * random(50, 250); + Serial.print(value); + Serial.print(','); + value = testFilter.filtered(value); + Serial.println(value); +} \ No newline at end of file diff --git a/lib/GyverFilters/keywords.txt b/lib/GyverFilters/keywords.txt new file mode 100644 index 00000000..57f38dbc --- /dev/null +++ b/lib/GyverFilters/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring Map For GyverFilters +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +GyverFilters KEYWORD1 +GFilterRA KEYWORD1 +GMedian3 KEYWORD1 +GMedian KEYWORD1 +GABfilter KEYWORD1 +GKalman KEYWORD1 +GLinear KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setCoef KEYWORD2 +setStep KEYWORD2 +filteredTime KEYWORD2 +filtered KEYWORD2 +setParameters KEYWORD2 +getA KEYWORD2 +getB KEYWORD2 +getDelta KEYWORD2 \ No newline at end of file diff --git a/lib/GyverFilters/library.properties b/lib/GyverFilters/library.properties new file mode 100644 index 00000000..7c211d3d --- /dev/null +++ b/lib/GyverFilters/library.properties @@ -0,0 +1,9 @@ +name=GyverFilters +version=2.0 +author=AlexGyver +maintainer=AlexGyver +sentence=Library with few filters for data. +paragraph=Includes median, running average, AB, simplified Kalman and linear approximation filtering algorithms. +category=Data Processing +url=https://github.com/AlexGyver/GyverLibs +architectures=* \ No newline at end of file diff --git a/lib/GyverFilters/src/GyverFilters.h b/lib/GyverFilters/src/GyverFilters.h new file mode 100644 index 00000000..e03e4bf5 --- /dev/null +++ b/lib/GyverFilters/src/GyverFilters.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +/* + GyverFilters - библиотека с некоторыми удобными фильтрами: + - GFilterRA - компактная альтернатива фильтра экспоненциальное бегущее среднее (Running Average) + - GMedian3 - быстрый медианный фильтр 3-го порядка (отсекает выбросы) + - GMedian - медианный фильтр N-го порядка. Порядок настраивается в GyverFilters.h - MEDIAN_FILTER_SIZE + - GABfilter - альфа-бета фильтр (разновидность Калмана для одномерного случая) + - GKalman - упрощённый Калман для одномерного случая (на мой взгляд лучший из фильтров) + - GLinear - линейная аппроксимация методом наименьших квадратов для двух массивов + + Версии + - 1.6 от 12.11.2019 + - 1.7: исправлен GLinear + - 1.8: небольшие улучшения + - 2.0: + - Улучшен и исправлен median и median3 + - Улучшен linear + - Смотрите примеры! Использование этих фильтров чуть изменилось +*/ \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/alfaBeta.h b/lib/GyverFilters/src/filters/alfaBeta.h new file mode 100644 index 00000000..51b09726 --- /dev/null +++ b/lib/GyverFilters/src/filters/alfaBeta.h @@ -0,0 +1,37 @@ +#pragma once +#include + +// альфа-бета фильтр +class GABfilter { +public: + // период дискретизации (измерений), process variation, noise variation + GABfilter(float delta, float sigma_process, float sigma_noise) {setParameters(delta, sigma_process, sigma_noise);} + + // период дискретизации (измерений), process variation, noise variation + void setParameters(float delta, float sigma_process, float sigma_noise) { + dt = delta; + float lambda = (float)sigma_process * dt * dt / sigma_noise; + float r = (4 + lambda - (float)sqrt(8 * lambda + lambda * lambda)) / 4; + a = (float)1 - r * r; + b = (float)2 * (2 - a) - 4 * (float)sqrt(1 - a); + } + + // возвращает фильтрованное значение + float filtered(float value) { + xm = value; + xk = xk_1 + ((float) vk_1 * dt ); + vk = vk_1; + rk = xm - xk; + xk += (float)a * rk; + vk += (float)( b * rk ) / dt; + xk_1 = xk; + vk_1 = vk; + return xk_1; + } + +private: + float dt; + float xk_1, vk_1, a, b; + float xk, vk, rk; + float xm; +}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/kalman.h b/lib/GyverFilters/src/filters/kalman.h new file mode 100644 index 00000000..10458090 --- /dev/null +++ b/lib/GyverFilters/src/filters/kalman.h @@ -0,0 +1,38 @@ +#pragma once +#include + +// упрощённый Калман для одномерного случая +class GKalman { +public: + // разброс измерения, разброс оценки, скорость изменения значений + GKalman(float mea_e, float est_e, float q) { setParameters(mea_e, est_e, q); } + + // разброс измерения, скорость изменения значений (разброс измерения принимается равным разбросу оценки) + GKalman(float mea_e, float q) {GKalman::setParameters(mea_e, mea_e, q);} + + // разброс измерения, разброс оценки, скорость изменения значений + void setParameters(float mea_e, float est_e, float q) { + _err_measure = mea_e; + _err_estimate = est_e; + _q = q; + } + + // разброс измерения, скорость изменения значений (разброс измерения принимается равным разбросу оценки) + void setParameters(float mea_e, float q) {setParameters(mea_e, mea_e, q);} + + // возвращает фильтрованное значение + float filtered(float value) { + float _kalman_gain, _current_estimate; + _kalman_gain = _err_estimate / (_err_estimate + _err_measure); + _current_estimate = _last_estimate + _kalman_gain * (value - _last_estimate); + _err_estimate = (1.0 - _kalman_gain)*_err_estimate + fabs(_last_estimate-_current_estimate)*_q; + _last_estimate=_current_estimate; + return _current_estimate; + } + +private: + float _err_measure = 0.0; + float _err_estimate = 0.0; + float _q = 0.0; + float _last_estimate = 0.0; +}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/linear.h b/lib/GyverFilters/src/filters/linear.h new file mode 100644 index 00000000..65a134f3 --- /dev/null +++ b/lib/GyverFilters/src/filters/linear.h @@ -0,0 +1,30 @@ +#pragma once +#include + +// линейная аппроксимация методом наименьших квадратов +template < typename TYPE > +class GLinear { +public: + GLinear(){}; + void compute(TYPE *x_array, TYPE *y_array, int arrSize) { // аппроксимировать + int32_t sumX = 0, sumY = 0, sumX2 = 0, sumXY = 0; + arrSize /= sizeof(int); + for (int i = 0; i < arrSize; i++) { // для всех элементов массива + sumX += x_array[i]; + sumY += (long)y_array[i]; + sumX2 += x_array[i] * x_array[i]; + sumXY += (long)y_array[i] * x_array[i]; + } + a = (long)arrSize * sumXY; // расчёт коэффициента наклона приямой + a = a - (long)sumX * sumY; + a = (float)a / (arrSize * sumX2 - sumX * sumX); + b = (float)(sumY - (float)a * sumX) / arrSize; + delta = a * arrSize; // расчёт изменения + } + float getA() {return a;} // получить коэффициент А + float getB() {return b;} // получить коэффициент В + float getDelta() {return delta;} // получить аппроксимированное изменение + +private: + float a, b, delta; +}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/median.h b/lib/GyverFilters/src/filters/median.h new file mode 100644 index 00000000..f7580806 --- /dev/null +++ b/lib/GyverFilters/src/filters/median.h @@ -0,0 +1,35 @@ +#pragma once + +// медианный фильтр N-го порядка +template < int SIZE, typename TYPE > +class GMedian { +public: + TYPE filtered(TYPE newVal) { + buffer[_count] = newVal; + if ((_count < _numRead - 1) && (buffer[_count] > buffer[_count + 1])) { + for (int i = _count; i < _numRead - 1; i++) { + if (buffer[i] > buffer[i + 1]) { + float buff = buffer[i]; + buffer[i] = buffer[i + 1]; + buffer[i + 1] = buff; + } + } + } else { + if ((_count > 0) and (buffer[_count - 1] > buffer[_count])) { + for (int i = _count; i > 0; i--) { + if (buffer[i] < buffer[i - 1]) { + float buff = buffer[i]; + buffer[i] = buffer[i - 1]; + buffer[i - 1] = buff; + } + } + } + } + if (++_count >= _numRead) _count = 0; + return buffer[(int)_numRead / 2]; + } +private: + TYPE buffer[SIZE]; + byte _count = 0; + byte _numRead = SIZE; +}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/median3.h b/lib/GyverFilters/src/filters/median3.h new file mode 100644 index 00000000..8b99cc1e --- /dev/null +++ b/lib/GyverFilters/src/filters/median3.h @@ -0,0 +1,30 @@ +#pragma once + +// быстрый медианный фильтр 3-го порядка +template < typename TYPE > +class GMedian3 { +public: + TYPE filtered(TYPE value) { // возвращает фильтрованное значение + buffer[_counter] = value; + if (++_counter > 2) _counter = 0; + + TYPE middle; + + if ((buffer[0] <= buffer[1]) && (buffer[0] <= buffer[2])) { + middle = (buffer[1] <= buffer[2]) ? buffer[1] : buffer[2]; + } + else { + if ((buffer[1] <= buffer[0]) && (buffer[1] <= buffer[2])) { + middle = (buffer[0] <= buffer[2]) ? buffer[0] : buffer[2]; + } + else { + middle = (buffer[0] <= buffer[1]) ? buffer[0] : buffer[1]; + } + } + return middle; + } + +private: + TYPE buffer[3]; + uint8_t _counter = 0; +}; \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/runningAverage.cpp b/lib/GyverFilters/src/filters/runningAverage.cpp new file mode 100644 index 00000000..9a1b2b03 --- /dev/null +++ b/lib/GyverFilters/src/filters/runningAverage.cpp @@ -0,0 +1,43 @@ +#include + +GFilterRA::GFilterRA() {} + +GFilterRA::GFilterRA(float coef, uint16_t interval) { + _coef = coef; + _filterInterval = interval; +} + +GFilterRA::GFilterRA(float coef) { + _coef = coef; +} + +void GFilterRA::setCoef(float coef) { + _coef = coef; +} +void GFilterRA::setStep(uint16_t interval) { + _filterInterval = interval; +} + +float GFilterRA::filteredTime(int16_t value) { + if (millis() - _filterTimer >= _filterInterval) { + _filterTimer = millis(); + return GFilterRA::filtered(value); + } +} + +float GFilterRA::filteredTime(float value) { + if (millis() - _filterTimer >= _filterInterval) { + _filterTimer = millis(); + return GFilterRA::filtered(value); + } +} + +float GFilterRA::filtered(int16_t value) { + _lastValue += (float)(value - _lastValue) * _coef; + return _lastValue; +} + +float GFilterRA::filtered(float value) { + _lastValue += (float)(value - _lastValue) * _coef; + return _lastValue; +} \ No newline at end of file diff --git a/lib/GyverFilters/src/filters/runningAverage.h b/lib/GyverFilters/src/filters/runningAverage.h new file mode 100644 index 00000000..7910a0fe --- /dev/null +++ b/lib/GyverFilters/src/filters/runningAverage.h @@ -0,0 +1,24 @@ +#pragma once +#include + +// экспоненциальное бегущее среднее +class GFilterRA +{ +public: + GFilterRA(); // инициализация фильтра + GFilterRA(float coef); // расширенная инициализация фильтра (коэффициент) + GFilterRA(float coef, uint16_t interval); // расширенная инициализация фильтра (коэффициент, шаг фильтрации) + void setCoef(float coef); // настройка коэффициента фильтрации (0.00 - 1.00). Чем меньше, тем плавнее + void setStep(uint16_t interval); // установка шага фильтрации (мс). Чем меньше, тем резче фильтр + + float filteredTime(int16_t value); // возвращает фильтрованное значение с опорой на встроенный таймер + float filtered(int16_t value); // возвращает фильтрованное значение + + float filteredTime(float value); // возвращает фильтрованное значение с опорой на встроенный таймер + float filtered(float value); // возвращает фильтрованное значение + +private: + float _coef = 0.0, _lastValue = 0.0; + uint32_t _filterTimer = 0; + uint16_t _filterInterval = 0; +}; \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 84ab6931..16f15936 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,6 +18,12 @@ lib_deps= Bounce2 PubSubClient ESP8266-StringCommand + DallasTemperature + DHT sensor library for ESPx + Adafruit BMP280 Library + Adafruit BME280 Library + +#board_build.partitions = partitions_custom.csv [env:esp32dev] platform = espressif32 diff --git a/src/Global.cpp b/src/Global.cpp index 9a24eeda..4df4e3e7 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -98,3 +98,7 @@ int udp_period; // i2c String i2c_list; + +extern OneWire *oneWire; +extern DallasTemperature sensors; + diff --git a/src/Sensors.cpp b/src/Sensors.cpp index 890dc2a2..a5a228bb 100644 --- a/src/Sensors.cpp +++ b/src/Sensors.cpp @@ -1,45 +1,66 @@ #include "Global.h" -void sensors_init() { +String perception(byte value); +void bmp280T_reading(); +String get_comfort_status(ComfortState cf); + +void sensors_init() +{ ts.add( - SENSORS, 1000, [&](void*) { + SENSORS, 1000, [&](void *) { static int counter; counter++; #ifdef level_enable - if (sensors_reading_map[0] == 1) level_reading(); + if (sensors_reading_map[0] == 1) + level_reading(); #endif - if (counter > 10) { + if (counter > 10) + { counter = 0; #ifdef analog_enable - if (sensors_reading_map[1] == 1) analog_reading1(); - if (sensors_reading_map[2] == 1) analog_reading2(); + if (sensors_reading_map[1] == 1) + analog_reading1(); + if (sensors_reading_map[2] == 1) + analog_reading2(); #endif #ifdef dallas_enable - if (sensors_reading_map[3] == 1) dallas_reading(); + if (sensors_reading_map[3] == 1) + dallas_reading(); #endif #ifdef dht_enable - if (sensors_reading_map[4] == 1) dhtT_reading(); - if (sensors_reading_map[5] == 1) dhtH_reading(); - if (sensors_reading_map[6] == 1) dhtP_reading(); - if (sensors_reading_map[7] == 1) dhtC_reading(); - if (sensors_reading_map[8] == 1) dhtD_reading(); + if (sensors_reading_map[4] == 1) + dhtT_reading(); + if (sensors_reading_map[5] == 1) + dhtH_reading(); + if (sensors_reading_map[6] == 1) + dhtP_reading(); + if (sensors_reading_map[7] == 1) + dhtC_reading(); + if (sensors_reading_map[8] == 1) + dhtD_reading(); #endif #ifdef bmp_enable - if (sensors_reading_map[9] == 1) bmp280T_rading(); - if (sensors_reading_map[10] == 1) bmp280P_reading(); + if (sensors_reading_map[9] == 1) + bmp280T_reading(); + if (sensors_reading_map[10] == 1) + bmp280P_reading(); #endif #ifdef bme_enable - if (sensors_reading_map[11] == 1) bme280T_reading(); - if (sensors_reading_map[12] == 1) bme280P_reading(); - if (sensors_reading_map[13] == 1) bme280H_reading(); - if (sensors_reading_map[14] == 1) bme280A_reading(); + if (sensors_reading_map[11] == 1) + bme280T_reading(); + if (sensors_reading_map[12] == 1) + bme280P_reading(); + if (sensors_reading_map[13] == 1) + bme280H_reading(); + if (sensors_reading_map[14] == 1) + bme280A_reading(); #endif } }, @@ -50,7 +71,8 @@ void sensors_init() { //=========================================Модуль измерения уровня в баке================================================================== #ifdef level_enable //level L 14 12 Вода#в#баке,#% Датчики fill-gauge 125 20 1 -void level() { +void level() +{ String value_name = sCmd.next(); String trig = sCmd.next(); String echo = sCmd.next(); @@ -71,7 +93,8 @@ void level() { sensors_reading_map[0] = 1; } -void level_reading() { +void level_reading() +{ long duration_; int distance_cm; int level; @@ -83,11 +106,12 @@ void level_reading() { digitalWrite(trig, HIGH); delayMicroseconds(10); digitalWrite(trig, LOW); - duration_ = pulseIn(echo, HIGH, 30000); // 3000 µs = 50cm // 30000 µs = 5 m + duration_ = pulseIn(echo, HIGH, 30000); // 3000 µs = 50cm // 30000 µs = 5 m distance_cm = duration_ / 29 / 2; - distance_cm = medianFilter.filtered(distance_cm); //отсечение промахов медианным фильтром + distance_cm = medianFilter.filtered(distance_cm); //отсечение промахов медианным фильтром counter++; - if (counter > tank_level_times_to_send) { + if (counter > tank_level_times_to_send) + { counter = 0; level = map(distance_cm, jsonReadInt(optionJson, "e_lev"), @@ -103,7 +127,8 @@ void level_reading() { //=========================================Модуль аналогового сенсора====================================================================== #ifdef analog_enable //analog adc 0 Аналоговый#вход,#% Датчики any-data 1 1023 1 100 1 -void analog() { +void analog() +{ String value_name = sCmd.next(); String pin = sCmd.next(); String widget_name = sCmd.next(); @@ -121,15 +146,18 @@ void analog() { jsonWriteStr(optionJson, value_name + "_st_out", analog_start_out); jsonWriteStr(optionJson, value_name + "_end_out", analog_end_out); choose_widget_and_create(widget_name, page_name, page_number, type, value_name); - if (enter_to_analog_counter == 1) { + if (enter_to_analog_counter == 1) + { sensors_reading_map[1] = 1; } - if (enter_to_analog_counter == 2) { + if (enter_to_analog_counter == 2) + { sensors_reading_map[2] = 1; } } -void analog_reading1() { +void analog_reading1() +{ String value_name = selectFromMarkerToMarker(analog_value_names_list, ",", 0); #ifdef ESP32 int analog_in = analogRead(34); @@ -148,7 +176,8 @@ void analog_reading1() { Serial.println("[i] sensor '" + value_name + "' data: " + String(analog)); } -void analog_reading2() { +void analog_reading2() +{ String value_name = selectFromMarkerToMarker(analog_value_names_list, ",", 1); #ifdef ESP32 int analog_in = analogRead(35); @@ -170,7 +199,8 @@ void analog_reading2() { //========================================================================================================================================= //=========================================Модуль температурного сенсора ds18b20=========================================================== #ifdef dallas_enable -void dallas() { +void dallas() +{ String value_name = sCmd.next(); String pin = sCmd.next(); String address = sCmd.next(); @@ -186,7 +216,8 @@ void dallas() { sensors_reading_map[3] = 1; } -void dallas_reading() { +void dallas_reading() +{ float temp = 0; sensors.requestTemperatures(); temp = sensors.getTempCByIndex(0); @@ -200,7 +231,8 @@ void dallas_reading() { //=========================================Модуль сенсоров DHT============================================================================= #ifdef dht_enable //dhtT t 2 dht11 Температура#DHT,#t°C Датчики any-data 1 -void dhtT() { +void dhtT() +{ String value_name = sCmd.next(); String pin = sCmd.next(); String sensor_type = sCmd.next(); @@ -209,26 +241,33 @@ void dhtT() { String type = sCmd.next(); String page_number = sCmd.next(); dhtT_value_name = value_name; - if (sensor_type == "dht11") { + if (sensor_type == "dht11") + { dht.setup(pin.toInt(), DHTesp::DHT11); } - if (sensor_type == "dht22") { + if (sensor_type == "dht22") + { dht.setup(pin.toInt(), DHTesp::DHT22); } choose_widget_and_create(widget_name, page_name, page_number, type, value_name); sensors_reading_map[4] = 1; } -void dhtT_reading() { +void dhtT_reading() +{ float value = 0; static int counter; - if (dht.getStatus() != 0 && counter < 5) { + if (dht.getStatus() != 0 && counter < 5) + { sendSTATUS(dhtT_value_name, String(dht.getStatusString())); counter++; - } else { + } + else + { counter = 0; value = dht.getTemperature(); - if (String(value) != "nan") { + if (String(value) != "nan") + { eventGen(dhtT_value_name, ""); jsonWriteStr(configJson, dhtT_value_name, String(value)); sendSTATUS(dhtT_value_name, String(value)); @@ -238,7 +277,8 @@ void dhtT_reading() { } //dhtH h 2 dht11 Влажность#DHT,#t°C Датчики any-data 1 -void dhtH() { +void dhtH() +{ String value_name = sCmd.next(); String pin = sCmd.next(); String sensor_type = sCmd.next(); @@ -247,26 +287,33 @@ void dhtH() { String type = sCmd.next(); String page_number = sCmd.next(); dhtH_value_name = value_name; - if (sensor_type == "dht11") { + if (sensor_type == "dht11") + { dht.setup(pin.toInt(), DHTesp::DHT11); } - if (sensor_type == "dht22") { + if (sensor_type == "dht22") + { dht.setup(pin.toInt(), DHTesp::DHT22); } choose_widget_and_create(widget_name, page_name, page_number, type, value_name); sensors_reading_map[5] = 1; } -void dhtH_reading() { +void dhtH_reading() +{ float value = 0; static int counter; - if (dht.getStatus() != 0 && counter < 5) { + if (dht.getStatus() != 0 && counter < 5) + { sendSTATUS(dhtH_value_name, String(dht.getStatusString())); counter++; - } else { + } + else + { counter = 0; value = dht.getHumidity(); - if (String(value) != "nan") { + if (String(value) != "nan") + { eventGen(dhtH_value_name, ""); jsonWriteStr(configJson, dhtH_value_name, String(value)); sendSTATUS(dhtH_value_name, String(value)); @@ -276,7 +323,8 @@ void dhtH_reading() { } //dhtPerception Восприятие: Датчики 4 -void dhtP() { +void dhtP() +{ String widget_name = sCmd.next(); String page_name = sCmd.next(); String page_number = sCmd.next(); @@ -284,35 +332,65 @@ void dhtP() { sensors_reading_map[6] = 1; } -void dhtP_reading() { +void dhtP_reading() +{ byte value; - if (dht.getStatus() != 0) { + if (dht.getStatus() != 0) + { sendSTATUS("dhtPerception", String(dht.getStatusString())); - } else { + } + else + { value = dht.computePerception(jsonReadStr(configJson, dhtT_value_name).toFloat(), jsonReadStr(configJson, dhtH_value_name).toFloat(), false); String final_line = perception(value); jsonWriteStr(configJson, "dhtPerception", final_line); eventGen("dhtPerception", ""); sendSTATUS("dhtPerception", final_line); - if (client_mqtt.connected()) { + if (client_mqtt.connected()) + { Serial.println("[i] sensor 'dhtPerception' data: " + final_line); } } } -String perception(byte value) { - if (value == 0) return "Сухой воздух"; - if (value == 1) return "Комфортно"; - if (value == 2) return "Уютно"; - if (value == 3) return "Хорошо"; - if (value == 4) return "Неудобно"; - if (value == 5) return "Довольно неудобно"; - if (value == 6) return "Очень неудобно"; - if (value == 7) return "Сильно неудобно, полный звиздец"; +String perception(byte value) +{ + String res; + switch (value) + { + case 0: + res = F("Сухой воздух"); + break; + case 1: + res = F("Комфортно"); + break; + case 2: + res = F("Уютно"); + break; + case 3: + res = F("Хорошо"); + break; + case 4: + res = F("Неудобно"); + break; + case 5: + res = F("Довольно неудобно"); + break; + case 6: + res = F("Очень неудобно"); + break; + case 7: + res = F("Сильно неудобно, полный звиздец"); + default: + res = F("Unknown"); + break; + } + return res; } //dhtComfort Степень#комфорта: Датчики 3 -void dhtC() { +void dhtC() +{ String widget_name = sCmd.next(); String page_name = sCmd.next(); String page_number = sCmd.next(); @@ -320,12 +398,15 @@ void dhtC() { sensors_reading_map[7] = 1; } -void dhtC_reading() { - float value; +void dhtC_reading() +{ ComfortState cf; - if (dht.getStatus() != 0) { + if (dht.getStatus() != 0) + { sendSTATUS("dhtComfort", String(dht.getStatusString())); - } else { + } + else + { value = dht.getComfortRatio(cf, jsonReadStr(configJson, dhtT_value_name).toFloat(), jsonReadStr(configJson, dhtH_value_name).toFloat(), false); String final_line = get_comfort_status(cf); jsonWriteStr(configJson, "dhtComfort", final_line); @@ -335,45 +416,48 @@ void dhtC_reading() { } } -String get_comfort_status(ComfortState cf) { +String get_comfort_status(ComfortState cf) +{ String comfortStatus; - switch (cf) { - case Comfort_OK: - comfortStatus = "Отлично"; - break; - case Comfort_TooHot: - comfortStatus = "Очень жарко"; - break; - case Comfort_TooCold: - comfortStatus = "Очень холодно"; - break; - case Comfort_TooDry: - comfortStatus = "Очень сухо"; - break; - case Comfort_TooHumid: - comfortStatus = "Очень влажно"; - break; - case Comfort_HotAndHumid: - comfortStatus = "Жарко и влажно"; - break; - case Comfort_HotAndDry: - comfortStatus = "Жарко и сухо"; - break; - case Comfort_ColdAndHumid: - comfortStatus = "Холодно и влажно"; - break; - case Comfort_ColdAndDry: - comfortStatus = "Холодно и сухо"; - break; - default: - comfortStatus = "Неизвестно"; - break; + switch (cf) + { + case Comfort_OK: + comfortStatus = "Отлично"; + break; + case Comfort_TooHot: + comfortStatus = "Очень жарко"; + break; + case Comfort_TooCold: + comfortStatus = "Очень холодно"; + break; + case Comfort_TooDry: + comfortStatus = "Очень сухо"; + break; + case Comfort_TooHumid: + comfortStatus = "Очень влажно"; + break; + case Comfort_HotAndHumid: + comfortStatus = "Жарко и влажно"; + break; + case Comfort_HotAndDry: + comfortStatus = "Жарко и сухо"; + break; + case Comfort_ColdAndHumid: + comfortStatus = "Холодно и влажно"; + break; + case Comfort_ColdAndDry: + comfortStatus = "Холодно и сухо"; + break; + default: + comfortStatus = "Неизвестно"; + break; }; return comfortStatus; } //dhtDewpoint Точка#росы: Датчики 5 -void dhtD() { +void dhtD() +{ String widget_name = sCmd.next(); String page_name = sCmd.next(); String page_number = sCmd.next(); @@ -381,11 +465,15 @@ void dhtD() { sensors_reading_map[8] = 1; } -void dhtD_reading() { +void dhtD_reading() +{ float value; - if (dht.getStatus() != 0) { + if (dht.getStatus() != 0) + { sendSTATUS("dhtDewpoint", String(dht.getStatusString())); - } else { + } + else + { value = dht.computeDewPoint(jsonReadStr(configJson, dhtT_value_name).toFloat(), jsonReadStr(configJson, dhtH_value_name).toFloat(), false); jsonWriteInt(configJson, "dhtDewpoint", value); eventGen("dhtDewpoint", ""); @@ -397,9 +485,10 @@ void dhtD_reading() { //=========================================i2c bus esp8266 scl-4 sda-5 ==================================================================== //========================================================================================================================================= //=========================================Модуль сенсоров bmp280========================================================================== -#ifdef bmp_enable + //bmp280T temp1 0x76 Температура#bmp280 Датчики any-data 1 -void bmp280T() { +void bmp280T() +{ String value_name = sCmd.next(); String address = sCmd.next(); String widget_name = sCmd.next(); @@ -418,7 +507,8 @@ void bmp280T() { sensors_reading_map[9] = 1; } -void bmp280T_rading() { +void bmp280T_reading() +{ float value = 0; sensors_event_t temp_event, pressure_event; bmp_temp->getEvent(&temp_event); @@ -430,7 +520,8 @@ void bmp280T_rading() { } //bmp280P press1 0x76 Давление#bmp280 Датчики any-data 2 -void bmp280P() { +void bmp280P() +{ String value_name = sCmd.next(); String address = sCmd.next(); String widget_name = sCmd.next(); @@ -449,7 +540,8 @@ void bmp280P() { sensors_reading_map[10] = 1; } -void bmp280P_reading() { +void bmp280P_reading() +{ float value = 0; sensors_event_t temp_event, pressure_event; bmp_pressure->getEvent(&pressure_event); @@ -460,12 +552,12 @@ void bmp280P_reading() { sendSTATUS(bmp280P_value_name, String(value)); Serial.println("[i] sensor '" + bmp280P_value_name + "' data: " + String(value)); } -#endif + //========================================================================================================================================= //=============================================Модуль сенсоров bme280====================================================================== -#ifdef bme_enable //bme280T temp1 0x76 Температура#bmp280 Датчики any-data 1 -void bme280T() { +void bme280T() +{ String value_name = sCmd.next(); String address = sCmd.next(); String widget_name = sCmd.next(); @@ -478,7 +570,8 @@ void bme280T() { sensors_reading_map[11] = 1; } -void bme280T_reading() { +void bme280T_reading() +{ float value = 0; value = bme.readTemperature(); jsonWriteStr(configJson, bme280T_value_name, String(value)); @@ -488,7 +581,8 @@ void bme280T_reading() { } //bme280P pres1 0x76 Давление#bmp280 Датчики any-data 1 -void bme280P() { +void bme280P() +{ String value_name = sCmd.next(); String address = sCmd.next(); String widget_name = sCmd.next(); @@ -501,7 +595,8 @@ void bme280P() { sensors_reading_map[12] = 1; } -void bme280P_reading() { +void bme280P_reading() +{ float value = 0; value = bme.readPressure(); value = value / 1.333224; @@ -512,7 +607,8 @@ void bme280P_reading() { } //bme280H hum1 0x76 Влажность#bmp280 Датчики any-data 1 -void bme280H() { +void bme280H() +{ String value_name = sCmd.next(); String address = sCmd.next(); String widget_name = sCmd.next(); @@ -525,7 +621,8 @@ void bme280H() { sensors_reading_map[13] = 1; } -void bme280H_reading() { +void bme280H_reading() +{ float value = 0; value = bme.readHumidity(); jsonWriteStr(configJson, bme280H_value_name, String(value)); @@ -535,7 +632,8 @@ void bme280H_reading() { } //bme280A altit1 0x76 Высота#bmp280 Датчики any-data 1 -void bme280A() { +void bme280A() +{ String value_name = sCmd.next(); String address = sCmd.next(); String widget_name = sCmd.next(); @@ -548,7 +646,8 @@ void bme280A() { sensors_reading_map[14] = 1; } -void bme280A_reading() { +void bme280A_reading() +{ float value = 0; value = bme.readAltitude(1013.25); jsonWriteStr(configJson, bme280A_value_name, String(value)); @@ -556,4 +655,3 @@ void bme280A_reading() { sendSTATUS(bme280A_value_name, String(value)); Serial.println("[i] sensor '" + bme280A_value_name + "' data: " + String(value)); } -#endif diff --git a/src/main.cpp b/src/main.cpp index 025f81f1..8b6d24f2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,8 +39,6 @@ String jsonWriteFloat(String& json, String name, float volume) { return json; } -//============================================================================================================ -//=============================================BIT AND BYTE=================================================== uint8_t hexStringToUint8(String hex) { uint8_t tmp = strtol(hex.c_str(), NULL, 0); if (tmp >= 0x00 && tmp <= 0xFF) {