diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index 6d3d6f840..14bf6f698 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index cdcbf5c1b..6da814036 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index ece6601d6..63d38dc58 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index 51896cab2..d89a8c5ce 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 3af1d5bc7..dcc945c80 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index 6a83dd44e..19cbf5e66 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 583d3ca61..44e1d1020 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 91cb2b375..058789eb3 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 576cea8c1..cad66ebee 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index b6b8dcacc..5c6dcae10 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL - RX" #define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index 30de0b4dc..7a84823ff 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index dd4254954..d5922cb6a 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 996a530ae..f50d22de4 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index 3f0fa6b3d..dab2a3a54 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index 7c8822e4d..e056245c1 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index ea86274c9..83bb05575 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index 0c17bfb78..93cbf2c72 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 7ed2639f9..0a6415c16 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index d59ad5431..438bcd3c1 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 2e8f72178..740b92774 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 4749248d8..bf0377e7b 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index e4244a77d..49e7b8592 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 3398a9b87..7f92495a4 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -674,6 +674,7 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_GPIO_WEBCAM_PWDN "CAM_PWDN" #define D_GPIO_WEBCAM_RESET "CAM_RESET" #define D_GPIO_WEBCAM_XCLK "CAM_XCLK" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 66b64c94f..1fdfbda8f 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -666,7 +666,10 @@ #define USE_TASMOTA_SLAVE_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants #define USE_TASMOTA_SLAVE_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini +//#define USE_WINDMETER // Add support for analog anemometer + //#define USE_OPENTHERM // Use OpenTherm implementation + // -- End of general directives ------------------- /*********************************************************************************************\ diff --git a/tasmota/settings.h b/tasmota/settings.h index 2a7313fd9..b857d1899 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -533,8 +533,13 @@ struct { uint8_t zb_free_byte; // F33 uint16_t pms_wake_interval; // F34 uint8_t config_version; // F36 + uint8_t windmeter_pulses_x_rot; // F37 + uint16_t windmeter_radius; // F38 + uint16_t windmeter_pulse_debounce; // F3A + int16_t windmeter_speed_factor; // F3C + uint8_t windmeter_tele_pchange; // F3E - uint8_t free_f37[129]; // F37 - Decrement if adding new Setting variables just above and below + uint8_t free_f3f[121]; // F3F - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below uint16_t pulse_counter_debounce_low; // FB8 diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 1fc3995a5..56f8ea557 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -555,10 +555,12 @@ void GetFeatures(void) feature6 |= 0x00000080; // xdrv_38_ping.ino #endif #ifdef USE_THERMOSTAT - feature6 |= 0x00000200; // xsns_68_opentherm.ino + feature6 |= 0x00000100; // xsns_68_opentherm.ino #endif -// feature6 |= 0x00000100; -// feature6 |= 0x00000200; +#ifdef USE_WINDMETER + feature6 |= 0x00000200; // xsns_69_windmeter.ino +#endif + // feature6 |= 0x00000400; // feature6 |= 0x00000800; diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index dfb2434e3..3fc3cc0e0 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -183,6 +183,7 @@ #define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) //#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) //#define USE_ARDUINO_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +//#define USE_WINDMETER // Add support for analog anemometer #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_SENSORS diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index fe689cc57..cb40e6710 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -232,6 +232,7 @@ enum UserSelectablePins { GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface GPIO_BOILER_OT_RX, // OpenTherm Boiler RX pin GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin + GPIO_WINDMETER_SPEED, // WindMeter speed counter pin GPIO_SENSOR_END }; // Programmer selectable GPIO functionality @@ -320,7 +321,8 @@ const char kSensorNames[] PROGMEM = D_SENSOR_HRXL_RX "|" D_SENSOR_ELECTRIQ_MOODL "|" D_SENSOR_AS3935 "|" D_SENSOR_PMS5003_TX "|" - D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX + D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|" + D_SENSOR_WINDMETER_SPEED ; const char kSensorNamesFixed[] PROGMEM = @@ -594,6 +596,9 @@ const uint8_t kGpioNiceList[] PROGMEM = { #if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin #endif +#ifdef USE_WINDMETER + GPIO_WINDMETER_SPEED, +#endif #ifdef USE_MP3_PLAYER GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface #endif diff --git a/tasmota/xsns_68_windmeter.ino b/tasmota/xsns_68_windmeter.ino new file mode 100644 index 000000000..4738cfd9b --- /dev/null +++ b/tasmota/xsns_68_windmeter.ino @@ -0,0 +1,375 @@ +/* + xsns_68_windmeter.ino - Analog wind sensor support for Tasmota + + Copyright (C) 2020 Matteo Albinola + (inspired by great works of Thomas Eckerstorfer, Norbert Richter, Maarten Damen and Theo Arends) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_WINDMETER +/*********************************************************************************************\ + * WindMeter sensor (speed) +\*********************************************************************************************/ + +#define XSNS_68 68 + +#define D_WINDMETER_NAME "WindMeter" + +#define WINDMETER_DEF_RADIUS 61 // Radius in millimeters (calculated by measuring the distance from the centre to the edge of one of the cups) +#define WINDMETER_DEF_PULSES_X_ROT 1 // Number of pulses for a complete rotation +#define WINDMETER_DEF_PULSE_DEBOUNCE 10 // Pulse counter debounce time (milliseconds) +#define WINDMETER_DEF_COMP_FACTOR 1.18 // Compensation factor +#define WINDMETER_DEF_TELE_PCHANGE 255 // Minimum percentage change between current and last reported speed in order to trigger a new tele message (0...100, 255 means off) +#define WINDMETER_WEIGHT_AVG_SAMPLE 150 // No of samples to take + +#ifdef USE_WEBSERVER +#define D_WINDMETER_WIND_AVG "∅" +#define D_WINDMETER_WIND_ANGLE "∠" +#define D_WINDMETER_WIND_DEGREE "°" +const char HTTP_SNS_WINDMETER[] PROGMEM = + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED "{m}%s %s{e}" +#ifndef USE_WINDMETER_NOSTATISTICS + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED " " D_WINDMETER_WIND_AVG "{m}%s %s{e}" + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s %s{e}" + "{s}" D_WINDMETER_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s %s{e}" +#endif // USE_WINDMETER_NOSTATISTICS +// "{s}WindMeter " D_TX20_WIND_DIRECTION "{m}%s %s" D_WINDMETER_WIND_DEGREE "{e}" +//#ifndef USE_WINDMETER_NOSTATISTICS +// "{s}WindMeter " D_TX20_WIND_DIRECTION " " D_WINDMETER_WIND_AVG "{m}%s %s" D_WINDMETER_WIND_DEGREE "{e}" +// "{s}WindMeter " D_TX20_WIND_DIRECTION " " D_WINDMETER_WIND_ANGLE "{m}%s" D_WINDMETER_WIND_DEGREE " (%s,%s)" D_WINDMETER_WIND_DEGREE; +//#endif // USE_WINDMETER_NOSTATISTICS + ; +#endif // USE_WEBSERVER + +// float saves 48 byte +float const windmeter_pi = 3.1415926535897932384626433; // Pi +float const windmeter_2pi = windmeter_pi * 2; + +struct WINDMETER { + uint32_t counter_time; + unsigned long counter = 0; + //uint32_t speed_time; + float speed = 0; + float last_tele_speed = 0; +#ifndef USE_WINDMETER_NOSTATISTICS + float speed_min = 0; + float speed_max = 0; + float speed_avg = 0; + uint32_t samples_count = 0; + uint32_t avg_samples_no; +#endif // USE_WINDMETER_NOSTATISTICS +} WindMeter; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +void WindMeterUpdateSpeed(void) ICACHE_RAM_ATTR; +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + +void WindMeterUpdateSpeed(void) +{ + uint32_t time = micros(); + uint32_t time_diff = time - WindMeter.counter_time; + if (time_diff > Settings.windmeter_pulse_debounce * 1000) { + WindMeter.counter_time = time; + WindMeter.counter++; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Counter %d"), WindMeter.counter); + } +} + +/********************************************************************************************/ + +void WindMeterInit(void) +{ + if (!Settings.flag2.speed_conversion) { + Settings.flag2.speed_conversion = 2; // 0 = none, 1 = m/s, 2 = km/h, 3 = kn, 4 = mph, 5 = ft/s, 6 = yd/s + } + if (!Settings.windmeter_radius) { + Settings.windmeter_radius = WINDMETER_DEF_RADIUS; + } + if (!Settings.windmeter_pulses_x_rot) { + Settings.windmeter_pulses_x_rot = WINDMETER_DEF_PULSES_X_ROT; + } + if (!Settings.windmeter_pulse_debounce) { + Settings.windmeter_pulse_debounce = WINDMETER_DEF_PULSE_DEBOUNCE; + } + if (!Settings.windmeter_speed_factor) { + Settings.windmeter_speed_factor = (int16_t)(WINDMETER_DEF_COMP_FACTOR * 1000); + } + if (!Settings.windmeter_tele_pchange) { + Settings.windmeter_tele_pchange = WINDMETER_DEF_TELE_PCHANGE; + } + +#ifndef USE_WINDMETER_NOSTATISTICS + WindMeterResetStatData(); + WindMeterCheckSampleCount(); +#endif // USE_WINDMETER_NOSTATISTICS + + pinMode(Pin(GPIO_WINDMETER_SPEED), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_WINDMETER_SPEED), WindMeterUpdateSpeed, FALLING); +} + +void WindMeterEverySecond(void) +{ + //uint32_t time = micros(); + //uint32_t delta_time = time - WindMeter.speed_time; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("delta_time: %d"), delta_time); + + // speed = ( (pulses / pulses_per_rotation) * (2 * pi * radius) ) / delta_time + WindMeter.speed = ((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) * ((float)Settings.windmeter_speed_factor / 1000); + //WindMeter.speed = (((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) / ((float)delta_time / 1000000)) * ((float)Settings.windmeter_speed_factor / 1000); + WindMeter.counter = 0; + //WindMeter.speed_time = time; + + //char speed_string[FLOATSZ]; + //dtostrfd(WindMeter.speed, 2, speed_string); + //char uspeed_string[FLOATSZ]; + //dtostrfd(ConvertSpeed(WindMeter.speed), 2, uspeed_string); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("WMET: Speed %s [m/s] - %s [unit]"), speed_string, uspeed_string); + +#ifndef USE_WINDMETER_NOSTATISTICS + if (WindMeter.speed < WindMeter.speed_min) { + WindMeter.speed_min = WindMeter.speed; + } + if (WindMeter.speed > WindMeter.speed_max) { + WindMeter.speed_max = WindMeter.speed; + } + + // exponentially weighted average is not quite as smooth as the arithmetic average + // but close enough to the moving average and does not require the regular reset + // of the divider with the associated jump in avg values after period is over + if (WindMeter.samples_count <= WindMeter.avg_samples_no) { + WindMeter.samples_count++; + } + WindMeter.speed_avg -= WindMeter.speed_avg / WindMeter.samples_count; + WindMeter.speed_avg += float(WindMeter.speed) / WindMeter.samples_count; + + WindMeterCheckSampleCount(); + if (0==Settings.tele_period) { + WindMeterResetStatData(); + } +#endif // USE_WINDMETER_NOSTATISTICS + + if (WindMeterShouldTriggerTele()) { + WindMeterTriggerTele(); + } +} + +bool WindMeterShouldTriggerTele() +{ + if (Settings.windmeter_tele_pchange > 100) { + return false; + } else if (WindMeter.last_tele_speed == 0) { + return WindMeter.speed > 0; + } else { + float perc_change = (WindMeter.speed / WindMeter.last_tele_speed) -1; + return (perc_change * ((perc_change < 0) ? -100 : 100)) >= Settings.windmeter_tele_pchange; + } +} + +void WindMeterResetStatData(void) +{ + WindMeter.speed_min = WindMeter.speed; + WindMeter.speed_max = WindMeter.speed; + //WindMeter.direction_min = WindMeter.direction; + //WindMeter.direction_max = WindMeter.direction; +} + +void WindMeterCheckSampleCount(void) +{ + uint32_t prev_avg_samples_no = WindMeter.avg_samples_no; + if (Settings.tele_period) { + // number for avg samples = teleperiod value if set + WindMeter.avg_samples_no = Settings.tele_period; + } else { + // otherwise use default number of samples for this driver + WindMeter.avg_samples_no = WINDMETER_WEIGHT_AVG_SAMPLE; + } + if (prev_avg_samples_no != WindMeter.avg_samples_no) { + WindMeter.speed_avg = WindMeter.speed; + WindMeter.samples_count = 0; + } +} + +void WindMeterShow(bool json) +{ + char speed_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed), 2, speed_string); +#ifndef USE_WINDMETER_NOSTATISTICS + char speed_min_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_min), 2, speed_min_string); + char speed_max_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_max), 2, speed_max_string); + char speed_avg_string[FLOATSZ]; + dtostrfd(ConvertSpeed(WindMeter.speed_avg), 2, speed_avg_string); + //char direction_avg_string[FLOATSZ]; + //dtostrfd(WindMeter.direction_avg, 1, direction_avg_string); + //char direction_avg_cardinal_string[4]; + //GetTextIndexed(direction_avg_cardinal_string, sizeof(direction_avg_cardinal_string), int((WindMeter.direction_avg/22.5f)+0.5f) % 16, kWindMeterDirections); + //char direction_range_string[FLOATSZ]; + //dtostrfd(Tx2xNormalize(WindMeter.direction_max-WindMeter.direction_min)*22.5, 1, direction_range_string); + //char direction_min_string[FLOATSZ]; + //dtostrfd(Tx2xNormalize(WindMeter.direction_min)*22.5, 1, direction_min_string); + //char direction_max_string[FLOATSZ]; + //dtostrfd(Tx2xNormalize(WindMeter.direction_max)*22.5, 1, direction_max_string); +#endif // USE_WINDMETER_NOSTATISTICS + + if (json) { + WindMeter.last_tele_speed = WindMeter.speed; +#ifndef USE_WINDMETER_NOSTATISTICS + ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"), + speed_string, + speed_avg_string, + speed_min_string, + speed_max_string, + "n/a", //direction_cardinal_string, + "n/a", //direction_string, + "n/a", //direction_avg_string, + "n/a", //direction_avg_cardinal_string, + "n/a", //direction_min_string, + "n/a", //direction_max_string, + "n/a" //direction_range_string + ); +#else // USE_WINDMETER_NOSTATISTICS + ResponseAppend_P(PSTR(",\"" D_WINDMETER_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s}}"), + speed_string, + "n/a", //wind_direction_cardinal_string, + "n/a" //wind_direction_string + ); +#endif // USE_WINDMETER_NOSTATISTICS +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_WINDMETER, + speed_string, + SpeedUnit().c_str(), +#ifndef USE_WINDMETER_NOSTATISTICS + speed_avg_string, + SpeedUnit().c_str(), + speed_min_string, + SpeedUnit().c_str(), + speed_max_string, + SpeedUnit().c_str(), +#endif // USE_WINDMETER_NOSTATISTICS + "n/a", //wind_direction_cardinal_string, + "n/a" //wind_direction_string +#ifndef USE_WINDMETER_NOSTATISTICS + ,"n/a", //,wind_direction_avg_cardinal_string, + "n/a", //wind_direction_avg_string, + "n/a", //wind_direction_range_string, + "n/a", //wind_direction_min_string, + "n/a" //wind_direction_max_string +#endif // USE_WINDMETER_NOSTATISTICS + ); +#endif // USE_WEBSERVER + } +} + +void WindMeterTriggerTele(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#ifdef USE_RULES + RulesTeleperiod(); // Allow rule based HA messages +#endif // USE_RULES + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +bool Xsns68Cmnd(void) +{ + bool serviced = true; + bool show_parms = true; + char sub_string[XdrvMailbox.data_len +1]; + switch (XdrvMailbox.payload) { + case 1: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_radius = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 2: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_pulses_x_rot = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 3: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_pulse_debounce = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + case 4: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_speed_factor = (int16_t)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 1000); + } + break; + case 5: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.windmeter_tele_pchange = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + break; + } + + if (show_parms) { + char speed_factor_string[FLOATSZ]; + dtostrfd((float)Settings.windmeter_speed_factor / 1000, 3, speed_factor_string); + char tele_pchange_string[4] = "off"; + if (Settings.windmeter_tele_pchange <= 100) { + itoa(Settings.windmeter_tele_pchange, tele_pchange_string, 10); + } + Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%s,\"TeleTriggerMin%Change\":%s}}"), + Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, speed_factor_string, tele_pchange_string); + } + return serviced; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns68(uint8_t function) +{ + bool result = false; + if (PinUsed(GPIO_WINDMETER_SPEED)) { + switch (function) { + case FUNC_INIT: + WindMeterInit(); + break; + case FUNC_EVERY_SECOND: + WindMeterEverySecond(); + break; +#ifndef USE_WINDMETER_NOSTATISTICS + case FUNC_AFTER_TELEPERIOD: + WindMeterResetStatData(); + break; +#endif // USE_WINDMETER_NOSTATISTICS + case FUNC_JSON_APPEND: + WindMeterShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + WindMeterShow(false); + break; +#endif // USE_WEBSERVER + case FUNC_COMMAND_SENSOR: + if (XSNS_68 == XdrvMailbox.index) { + result = Xsns68Cmnd(); + } + } + } + return result; +} + +#endif // USE_WINDMETER