diff --git a/API.md b/API.md index 354382cfc..f10ae2c93 100644 --- a/API.md +++ b/API.md @@ -25,7 +25,7 @@ FUNC_EVERY_250_MSECOND | | 1 | 3 | 2 | | FUNC_EVERY_SECOND | | 1 | 2 | | | FUNC_SAVE_AT_MIDNIGHT | | | x | | | At midnight FUNC_SAVE_BEFORE_RESTART | | 2 | 1 | | | Just before a planned restart -FUNC_AFTER_TELEPERIOD | | x | | | | At end of teleperiod +FUNC_AFTER_TELEPERIOD | | 2 | 1 | | | At end of teleperiod FUNC_JSON_APPEND | | 2 | 1 | 3 | | Extend teleperiod JSON text FUNC_WEB_SENSOR | | 2 | 1 | 3 | | Add sensor data to web GUI FUNC_COMMAND | x | 1 | 2 | 3 | 4 | When a command is not recognized diff --git a/BUILDS.md b/BUILDS.md index d2ca20438..f09de3125 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -142,7 +142,7 @@ | USE_SR04 | - | - | - | - | x | - | - | | USE_TM1638 | - | - | - | - | x | - | - | | USE_HX711 | - | - | - | - | x | - | - | -| USE_TX20_WIND_SENSOR | - | - | - | - | x | - | - | +| USE_TX2x_WIND_SENSOR | - | - | - | - | - | - | - | | USE_RC_SWITCH | - | - | - | - | x | - | - | | USE_RF_SENSOR | - | - | - | - | x | - | - | AlectoV2 only | USE_HRE | - | - | - | - | x | - | - | diff --git a/tasmota/language/bg-BG.h b/tasmota/language/bg-BG.h index 9a6ce8abc..b2b151644 100644 --- a/tasmota/language/bg-BG.h +++ b/tasmota/language/bg-BG.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Посока на вятъра" -#define D_TX20_WIND_DEGREES "Степени на вятъра" #define D_TX20_WIND_SPEED "Скорост на вятъра" -#define D_TX20_WIND_SPEED_AVG "Средна скорост на вятъра" #define D_TX20_WIND_SPEED_MIN "Мини. скорост на вятъра" #define D_TX20_WIND_SPEED_MAX "Макс. скорост на вятъра" #define D_TX20_NORTH "С" diff --git a/tasmota/language/cs-CZ.h b/tasmota/language/cs-CZ.h index d98361640..332a45920 100644 --- a/tasmota/language/cs-CZ.h +++ b/tasmota/language/cs-CZ.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Směr větru" -#define D_TX20_WIND_DEGREES "Úhel větru" #define D_TX20_WIND_SPEED "Rychlost větru" -#define D_TX20_WIND_SPEED_AVG "Průměrná rychlost větru" #define D_TX20_WIND_SPEED_MIN "Minimální rychlost větru" #define D_TX20_WIND_SPEED_MAX "Maximální rychlost větru" #define D_TX20_NORTH "S" diff --git a/tasmota/language/de-DE.h b/tasmota/language/de-DE.h index 05835a49c..1f534f27f 100644 --- a/tasmota/language/de-DE.h +++ b/tasmota/language/de-DE.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Windrichtung" -#define D_TX20_WIND_DEGREES "Windrichtung Grad" #define D_TX20_WIND_SPEED "Windgeschwindigkeit" -#define D_TX20_WIND_SPEED_AVG "Windgeschwindigkeit Ø" #define D_TX20_WIND_SPEED_MIN "Windgeschwindigkeit Min" #define D_TX20_WIND_SPEED_MAX "Windgeschwindigkeit Max" #define D_TX20_NORTH "N" diff --git a/tasmota/language/el-GR.h b/tasmota/language/el-GR.h index 0ddd43918..fc540043e 100644 --- a/tasmota/language/el-GR.h +++ b/tasmota/language/el-GR.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Κατεύθυνση ανέμου" -#define D_TX20_WIND_DEGREES "Βαθμός ανέμου" #define D_TX20_WIND_SPEED "Ταχύτητα ανέμου" -#define D_TX20_WIND_SPEED_AVG "Μέση ταχύτητα ανέμου" #define D_TX20_WIND_SPEED_MIN "Ελάχιστη ταχύτητα ανέμου" #define D_TX20_WIND_SPEED_MAX "Μέγιστη ταχύτητα ανέμου" #define D_TX20_NORTH "Β" diff --git a/tasmota/language/en-GB.h b/tasmota/language/en-GB.h index b175cc193..e5a82f411 100644 --- a/tasmota/language/en-GB.h +++ b/tasmota/language/en-GB.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_DEGREES "Wind Degrees" #define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" #define D_TX20_WIND_SPEED_MIN "Wind Speed Min" #define D_TX20_WIND_SPEED_MAX "Wind Speed Max" #define D_TX20_NORTH "N" diff --git a/tasmota/language/es-ES.h b/tasmota/language/es-ES.h index 12e732bc2..9331a3f62 100644 --- a/tasmota/language/es-ES.h +++ b/tasmota/language/es-ES.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Dirección del Viento" -#define D_TX20_WIND_DEGREES "Ángulo del Viento" #define D_TX20_WIND_SPEED "Vel. del Viento" -#define D_TX20_WIND_SPEED_AVG "Vel. Prom. del Viento" #define D_TX20_WIND_SPEED_MIN "Vel. Min. del Viento" #define D_TX20_WIND_SPEED_MAX "Vel. Max. del Viento" #define D_TX20_NORTH "N" diff --git a/tasmota/language/fr-FR.h b/tasmota/language/fr-FR.h index e9c31da46..1ca53402e 100644 --- a/tasmota/language/fr-FR.h +++ b/tasmota/language/fr-FR.h @@ -504,9 +504,7 @@ //xsns_35_TX20.ino #define D_TX20_WIND_DIRECTION "Direction du vent" -#define D_TX20_WIND_DEGREES "Degré de vent" #define D_TX20_WIND_SPEED "Vitesse du vent" -#define D_TX20_WIND_SPEED_AVG "Vitesse Moy." #define D_TX20_WIND_SPEED_MIN "Vitesse Min" #define D_TX20_WIND_SPEED_MAX "Vitesse Max" #define D_TX20_NORTH "N" diff --git a/tasmota/language/he-HE.h b/tasmota/language/he-HE.h index f836a92b9..a8f3ba572 100644 --- a/tasmota/language/he-HE.h +++ b/tasmota/language/he-HE.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "כיוון הרוח" -#define D_TX20_WIND_DEGREES "זווית הרוח" #define D_TX20_WIND_SPEED "מהירות הרוח" -#define D_TX20_WIND_SPEED_AVG "מהירות הרוח ממוצעת" #define D_TX20_WIND_SPEED_MIN "מהירות הרוח היא מינימלית" #define D_TX20_WIND_SPEED_MAX "מהירות הרוח מקסימלית" #define D_TX20_NORTH "N" diff --git a/tasmota/language/hu-HU.h b/tasmota/language/hu-HU.h index da313caaa..2d888bd81 100644 --- a/tasmota/language/hu-HU.h +++ b/tasmota/language/hu-HU.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Szélirány" -#define D_TX20_WIND_DEGREES "Szél mértéke" #define D_TX20_WIND_SPEED "Szélsebesség" -#define D_TX20_WIND_SPEED_AVG "Átlag szélsebesség" #define D_TX20_WIND_SPEED_MIN "Min. szélsebesség" #define D_TX20_WIND_SPEED_MAX "Max. szélsebesség" #define D_TX20_NORTH "É" diff --git a/tasmota/language/it-IT.h b/tasmota/language/it-IT.h index efaecc1a5..3ec7e9773 100644 --- a/tasmota/language/it-IT.h +++ b/tasmota/language/it-IT.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Direzione Vento" -#define D_TX20_WIND_DEGREES "Angolo Vento" #define D_TX20_WIND_SPEED "Velocità Vento" -#define D_TX20_WIND_SPEED_AVG "Velocità Media Vento" #define D_TX20_WIND_SPEED_MIN "Velocità Minima Vento" #define D_TX20_WIND_SPEED_MAX "Velocità Massima Vento" #define D_TX20_NORTH "N" diff --git a/tasmota/language/ko-KO.h b/tasmota/language/ko-KO.h index 794b97e84..2377fadb1 100644 --- a/tasmota/language/ko-KO.h +++ b/tasmota/language/ko-KO.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "풍향" -#define D_TX20_WIND_DEGREES "바람 정도" #define D_TX20_WIND_SPEED "풍속" -#define D_TX20_WIND_SPEED_AVG "평균 풍속" #define D_TX20_WIND_SPEED_MIN "풍속 최소" #define D_TX20_WIND_SPEED_MAX "풍속 최대" #define D_TX20_NORTH "N" diff --git a/tasmota/language/nl-NL.h b/tasmota/language/nl-NL.h index 288da67d5..24bdc17e6 100644 --- a/tasmota/language/nl-NL.h +++ b/tasmota/language/nl-NL.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Windrichting" -#define D_TX20_WIND_DEGREES "Wind graad" #define D_TX20_WIND_SPEED "Windsnelheid" -#define D_TX20_WIND_SPEED_AVG "Windsnelheid gemiddeld" #define D_TX20_WIND_SPEED_MIN "Windsnelhied minimum" #define D_TX20_WIND_SPEED_MAX "Windsnelhied maximaal" #define D_TX20_NORTH "N" diff --git a/tasmota/language/pl-PL.h b/tasmota/language/pl-PL.h index e15ccbbc2..c96df7c95 100644 --- a/tasmota/language/pl-PL.h +++ b/tasmota/language/pl-PL.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Kierunek" -#define D_TX20_WIND_DEGREES "Grad" #define D_TX20_WIND_SPEED "Prędkość" -#define D_TX20_WIND_SPEED_AVG "Średnia prędkość" #define D_TX20_WIND_SPEED_MIN "Minimalna prędkość" #define D_TX20_WIND_SPEED_MAX "Maksymalna prędkość" #define D_TX20_NORTH "N" diff --git a/tasmota/language/pt-BR.h b/tasmota/language/pt-BR.h index 9b24d05c8..38c1e4b48 100644 --- a/tasmota/language/pt-BR.h +++ b/tasmota/language/pt-BR.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Direção do vento" -#define D_TX20_WIND_DEGREES "Ângulo do vento" #define D_TX20_WIND_SPEED "Velocidade do vento" -#define D_TX20_WIND_SPEED_AVG "Velocidade média do vento" #define D_TX20_WIND_SPEED_MIN "Velocidade do vento Mínima" #define D_TX20_WIND_SPEED_MAX "Velocidade do vento Máxima" #define D_TX20_NORTH "N" diff --git a/tasmota/language/pt-PT.h b/tasmota/language/pt-PT.h index 9565fc7a5..4ed7d874d 100644 --- a/tasmota/language/pt-PT.h +++ b/tasmota/language/pt-PT.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Direção do vento" -#define D_TX20_WIND_DEGREES "Ângulo do vento" #define D_TX20_WIND_SPEED "Velocidade do vento" -#define D_TX20_WIND_SPEED_AVG "Velocidade média do vento" #define D_TX20_WIND_SPEED_MIN "Velocidade mínima do vento" #define D_TX20_WIND_SPEED_MAX "Velocidade máxima do vento" #define D_TX20_NORTH "N" diff --git a/tasmota/language/ru-RU.h b/tasmota/language/ru-RU.h index 700958875..92307ba89 100644 --- a/tasmota/language/ru-RU.h +++ b/tasmota/language/ru-RU.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_DEGREES "Wind Degrees" #define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" #define D_TX20_WIND_SPEED_MIN "Wind Speed Min" #define D_TX20_WIND_SPEED_MAX "Wind Speed Max" #define D_TX20_NORTH "N" diff --git a/tasmota/language/sk-SK.h b/tasmota/language/sk-SK.h index dbee001e9..327b4c11f 100644 --- a/tasmota/language/sk-SK.h +++ b/tasmota/language/sk-SK.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Smer vetra" -#define D_TX20_WIND_DEGREES "Uhol vetra" #define D_TX20_WIND_SPEED "Rýchlosť vetra" -#define D_TX20_WIND_SPEED_AVG "Priemerná rýchlosť vetra" #define D_TX20_WIND_SPEED_MIN "Minimálna rýchlosť vetra" #define D_TX20_WIND_SPEED_MAX "Maximálna rýchlosť vetra" #define D_TX20_NORTH "S" diff --git a/tasmota/language/sv-SE.h b/tasmota/language/sv-SE.h index a8e5aa6a8..30434d696 100644 --- a/tasmota/language/sv-SE.h +++ b/tasmota/language/sv-SE.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Vindriktning" -#define D_TX20_WIND_DEGREES "Vindvinkel" #define D_TX20_WIND_SPEED "Vindstyrka" -#define D_TX20_WIND_SPEED_AVG "Vindstyrka medel" #define D_TX20_WIND_SPEED_MIN "Vindstyrka min" #define D_TX20_WIND_SPEED_MAX "Vindstyrka max" #define D_TX20_NORTH "N" diff --git a/tasmota/language/tr-TR.h b/tasmota/language/tr-TR.h index 430289372..637899101 100644 --- a/tasmota/language/tr-TR.h +++ b/tasmota/language/tr-TR.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_DEGREES "Wind Degrees" #define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" #define D_TX20_WIND_SPEED_MIN "Wind Speed Min" #define D_TX20_WIND_SPEED_MAX "Wind Speed Max" #define D_TX20_NORTH "N" diff --git a/tasmota/language/uk-UA.h b/tasmota/language/uk-UA.h index 2b4873e7a..0bf4736cb 100644 --- a/tasmota/language/uk-UA.h +++ b/tasmota/language/uk-UA.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Напрям вітру" -#define D_TX20_WIND_DEGREES "Кут вітру" #define D_TX20_WIND_SPEED "Швидкість вітру" -#define D_TX20_WIND_SPEED_AVG "Середня швидкість вітру" #define D_TX20_WIND_SPEED_MIN "Мінімальна швидкість вітру" #define D_TX20_WIND_SPEED_MAX "Максимальна швидкість вітру" #define D_TX20_NORTH "Пн" diff --git a/tasmota/language/zh-CN.h b/tasmota/language/zh-CN.h index 5a71e95dc..6e2621b4e 100644 --- a/tasmota/language/zh-CN.h +++ b/tasmota/language/zh-CN.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "风向" -#define D_TX20_WIND_DEGREES "风度" #define D_TX20_WIND_SPEED "风速" -#define D_TX20_WIND_SPEED_AVG "平均风速" #define D_TX20_WIND_SPEED_MIN "最低风速" #define D_TX20_WIND_SPEED_MAX "最高风速" #define D_TX20_NORTH "北" diff --git a/tasmota/language/zh-TW.h b/tasmota/language/zh-TW.h index 9883972f1..b5445a3f6 100644 --- a/tasmota/language/zh-TW.h +++ b/tasmota/language/zh-TW.h @@ -504,9 +504,7 @@ //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_DEGREES "Wind Degrees" #define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" #define D_TX20_WIND_SPEED_MIN "Wind Speed Min" #define D_TX20_WIND_SPEED_MAX "Wind Speed Max" #define D_TX20_NORTH "N" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 8fc58ac4d..06901fa12 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -627,8 +627,8 @@ // #define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) // Select none or only one of the below defines -//#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+1k3 code) -//#define USE_TX23_WIND_SENSOR // Add support for La Crosse TX23 anemometer (+1k4 code) +//#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k6/0k8 code) +//#define USE_TX23_WIND_SENSOR // Add support for La Crosse TX23 anemometer (+2k7/1k code) //#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index ad9b38165..d7396267b 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -787,6 +787,7 @@ void PerformEverySecond(void) #endif // USE_RULES } + XsnsCall(FUNC_AFTER_TELEPERIOD); XdrvCall(FUNC_AFTER_TELEPERIOD); } } diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h index 8f5038387..2e3ed1e26 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_post.h @@ -230,8 +230,8 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack #define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code) #define USE_HX711 // Add support for HX711 load cell (+1k5 code) //#define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) -//#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+1k3 code) -//#define USE_TX23_WIND_SENSOR // Add support for La Crosse TX23 anemometer (+1k4 code) +//#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k6/0k8 code) +//#define USE_TX23_WIND_SENSOR // Add support for La Crosse TX23 anemometer (+2k7/1k code) #define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) #define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) diff --git a/tasmota/xsns_35_tx20.ino b/tasmota/xsns_35_tx20.ino index 14fd5c9f9..69844fda4 100644 --- a/tasmota/xsns_35_tx20.ino +++ b/tasmota/xsns_35_tx20.ino @@ -18,10 +18,7 @@ */ #if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) -#if defined(USE_TX20_WIND_SENSOR) && defined(USE_TX23_WIND_SENSOR) -#undef USE_TX20_WIND_SENSOR -#warning **** use USE_TX20_WIND_SENSOR or USE_TX23_WIND_SENSOR but not both together, TX20 disabled **** -#endif + /*********************************************************************************************\ * La Crosse TX20/TX23 Anemometer * @@ -30,18 +27,39 @@ * https://www.john.geek.nz/2011/07/la-crosse-tx20-anemometer-communication-protocol/ * http://www.rd-1000.com/chpm78/lacrosse/Lacrosse_TX23_protocol.html * https://www.john.geek.nz/2012/08/la-crosse-tx23u-anemometer-communication-protocol/ - * + * * TX23 RJ11 connection: * 1 yellow - GND * 2 green - NC * 3 red - Vcc 3.3V * 4 black/brown - TxD Signal (GPIOxx) + * + * Reads speed and direction + * + * Calculate statistics: + * speed avg/min/max + * direction avg/min/max/range + * + * avg values are updated continuously (using exponentially weighted average) + * min/max/range values are reset after TelePeriod time or TX2X_WEIGHT_AVG_SAMPLE seconds + * (if TelePeriod is disabled) + * + * Statistic calculation can be disabled by defining USE_TX2X_WIND_SENSOR_NOSTATISTICS + * (saves 1k8) \*********************************************************************************************/ #define XSNS_35 35 +#if defined(USE_TX20_WIND_SENSOR) && defined(USE_TX23_WIND_SENSOR) +#undef USE_TX20_WIND_SENSOR +#warning **** use USE_TX20_WIND_SENSOR or USE_TX23_WIND_SENSOR but not both together, TX20 disabled **** +#endif // USE_TX20_WIND_SENSOR && USE_TX23_WIND_SENSOR + +// #define USE_TX2X_WIND_SENSOR_NOSTATISTICS // suppress statistics (speed/dir avg/min/max/range) + #define TX2X_BIT_TIME 1220 // microseconds #define TX2X_WEIGHT_AVG_SAMPLE 150 // seconds +#define TX2X_TIMEOUT 10 // seconds #define TX23_READ_INTERVAL 4 // seconds (don't use less than 3) // The Arduino standard GPIO routines are not enough, @@ -51,20 +69,36 @@ extern "C" { } #ifdef USE_TX20_WIND_SENSOR - #define D_TX2x_NAME "TX20" -#else - #define D_TX2x_NAME "TX23" -#endif +#undef D_TX2x_NAME +#define D_TX2x_NAME "TX20" +#else // USE_TX20_WIND_SENSOR +#undef D_TX2x_NAME +#define D_TX2x_NAME "TX23" +#endif // USE_TX20_WIND_SENSOR #ifdef USE_WEBSERVER +#define D_TX20_WIND_AVG "∅" +#define D_TX20_WIND_ANGLE "∠" const char HTTP_SNS_TX2X[] PROGMEM = "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_AVG "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED " " D_TX20_WIND_AVG "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" - "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION "{m}%s %s°{e}"; +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION "{m}%s %s°{e}" +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_AVG "{m}%s %s°{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_ANGLE "{m}%s° (%s°,%s°)"; +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS + ; #endif // USE_WEBSERVER +// float saves 48 byte +float const ff_pi = 3.1415926535897932384626433; // Pi +float const ff_halfpi = ff_pi / 2.0; +float const ff_pi180 = ff_pi / 180.0; + const char kTx2xDirections[] PROGMEM = D_TX20_NORTH "|" D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" D_TX20_NORTH D_TX20_EAST "|" @@ -82,26 +116,38 @@ const char kTx2xDirections[] PROGMEM = D_TX20_NORTH "|" D_TX20_NORTH D_TX20_WEST "|" D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; -uint8_t tx2x_sa = 0; -uint8_t tx2x_sb = 0; -uint8_t tx2x_sd = 0; -uint8_t tx2x_se = 0; -uint16_t tx2x_sc = 0; -uint16_t tx2x_sf = 0; - float tx2x_wind_speed_kmh = 0; +int32_t tx2x_wind_direction = 0; + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS float tx2x_wind_speed_min = 200.0; float tx2x_wind_speed_max = 0; float tx2x_wind_speed_avg = 0; -uint8_t tx2x_wind_direction = 0; -int tx2x_count = 0; -uint16_t tx2x_avg_samples; +float tx2x_wind_direction_avg_x = 0; +float tx2x_wind_direction_avg_y = 0; +float tx2x_wind_direction_avg = 0; +int32_t tx2x_wind_direction_min = 0; +int32_t tx2x_wind_direction_max = 0; -bool tx2x_available = false; +uint32_t tx2x_count = 0; +uint32_t tx2x_avg_samples; +uint32_t last_uptime = 0; +bool tx2x_valuesread = false; +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS + +#ifdef DEBUG_TASMOTA_SENSOR +uint32_t tx2x_sa = 0; +uint32_t tx2x_sb = 0; +uint32_t tx2x_sc = 0; +uint32_t tx2x_sd = 0; +uint32_t tx2x_se = 0; +uint32_t tx2x_sf = 0; +#endif // DEBUG_TASMOTA_SENSOR +uint32_t last_available = 0; #ifdef USE_TX23_WIND_SENSOR -uint8_t tx23_stage = 0; -#endif +uint32_t tx23_stage = 0; +#endif // USE_TX23_WIND_SENSOR #ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception void TX2xStartRead(void) ICACHE_RAM_ATTR; // As iram is tight and it works this way too @@ -120,7 +166,7 @@ void TX2xStartRead(void) * sd - Checksum (invert) * se - Wind direction 0 - 15 * sf - Wind speed 0 - 511 - * + * * La Crosse TX23 Anemometer datagram after setting TxD to low/high * 1-1 0 1 0-0 11011 0011 111010101111 0101 1100 000101010000 1-1 - Received pin data at 1200 uSec per bit * t s c sa sb sc sd se sf @@ -139,23 +185,30 @@ void TX2xStartRead(void) { if ((2==tx23_stage) || (3==tx23_stage)) { -#endif - tx2x_available = false; - +#endif // USE_TX23_WIND_SENSOR +#ifdef DEBUG_TASMOTA_SENSOR tx2x_sa = 0; tx2x_sb = 0; + tx2x_sc = 0; tx2x_sd = 0; tx2x_se = 0; - tx2x_sc = 0; tx2x_sf = 0; +#else // DEBUG_TASMOTA_SENSOR + uint32_t tx2x_sa = 0; + uint32_t tx2x_sb = 0; + uint32_t tx2x_sc = 0; + uint32_t tx2x_sd = 0; + uint32_t tx2x_se = 0; + uint32_t tx2x_sf = 0; +#endif // DEBUG_TASMOTA_SENSOR delayMicroseconds(TX2X_BIT_TIME / 2); for (int32_t bitcount = 41; bitcount > 0; bitcount--) { - uint8_t dpin = (digitalRead(pin[GPIO_TX2X_TXD_BLACK])); + uint32_t dpin = (digitalRead(pin[GPIO_TX2X_TXD_BLACK])); #ifdef USE_TX23_WIND_SENSOR dpin ^= 1; -#endif +#endif // USE_TX23_WIND_SENSOR if (bitcount > 41 - 5) { // start frame (invert) tx2x_sa = (tx2x_sa << 1) | (dpin ^ 1); @@ -178,27 +231,64 @@ void TX2xStartRead(void) delayMicroseconds(TX2X_BIT_TIME); } - uint8_t chk = (tx2x_sb + (tx2x_sc & 0xf) + ((tx2x_sc >> 4) & 0xf) + ((tx2x_sc >> 8) & 0xf)); + uint32_t chk = (tx2x_sb + (tx2x_sc & 0xf) + ((tx2x_sc >> 4) & 0xf) + ((tx2x_sc >> 8) & 0xf)); chk &= 0xf; - // check checksum, non-inverted with inverted values and max. speed - tx2x_available = ((chk == tx2x_sd) && (tx2x_sb==tx2x_se) && (tx2x_sc==tx2x_sf) && (tx2x_sc < 511)); + // check checksum, start frame,non-inverted==inverted values and max. speed + ; + if ((chk == tx2x_sd) && (0x1b==tx2x_sa) && (tx2x_sb==tx2x_se) && (tx2x_sc==tx2x_sf) && (tx2x_sc < 511)) { + last_available = uptime; + // Wind speed spec: 0 to 180 km/h (0 to 50 m/s) + tx2x_wind_speed_kmh = float(tx2x_sc) * 0.36; + tx2x_wind_direction = tx2x_sb; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + if (!tx2x_valuesread) { + tx2x_wind_direction_min = tx2x_wind_direction; + tx2x_wind_direction_max = tx2x_wind_direction; + tx2x_valuesread = true; + } +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS + } + #ifdef USE_TX23_WIND_SENSOR } tx23_stage++; } -#endif +#endif // USE_TX23_WIND_SENSOR // Must clear this bit in the interrupt register, // it gets set even when interrupts are disabled GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pin[GPIO_TX2X_TXD_BLACK]); } -void Tx2xResetStat(void) +bool Tx2xAvailable(void) { - tx2x_wind_speed_min = tx2x_wind_speed_kmh; - tx2x_wind_speed_max = tx2x_wind_speed_kmh; - uint16_t tx2x_prev_avg_samples = tx2x_avg_samples; + return ((uptime - last_available) < TX2X_TIMEOUT); +} + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS +float atan2f(float a, float b) +{ + float atan2val; + if (b > 0) { + atan2val = atanf(a/b); + } else if ((b < 0) && (a >= 0)) { + atan2val = atanf(a/b) + ff_pi; + } else if ((b < 0) && (a < 0)) { + atan2val = atanf(a/b) - ff_pi; + } else if ((b == 0) && (a > 0)) { + atan2val = ff_halfpi; + } else if ((b == 0) && (a < 0)) { + atan2val = 0 - (ff_halfpi); + } else if ((b == 0) && (a == 0)) { + atan2val = 1000; //represents undefined + } + return atan2val; +} + +void Tx2xCheckSampleCount(void) +{ + uint32_t tx2x_prev_avg_samples = tx2x_avg_samples; if (Settings.tele_period) { // number for avg samples = teleperiod value if set tx2x_avg_samples = Settings.tele_period; @@ -212,6 +302,23 @@ void Tx2xResetStat(void) } } +void Tx2xResetStat(void) +{ + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": reset statistics")); + last_uptime = uptime; + Tx2xResetStatData(); +} + +void Tx2xResetStatData(void) +{ + tx2x_wind_speed_min = tx2x_wind_speed_kmh; + tx2x_wind_speed_max = tx2x_wind_speed_kmh; + + tx2x_wind_direction_min = tx2x_wind_direction; + tx2x_wind_direction_max = tx2x_wind_direction; +} +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS + void Tx2xRead(void) { #ifdef USE_TX23_WIND_SENSOR @@ -220,7 +327,7 @@ void Tx2xRead(void) // |____| |___________| |_| |__XXXXXXXXXX // trigger start conv Startframe Data // - // note: TX23 speed calculation is unstable when conversion starts + // note: TX23 speed calculation is unstable when conversion starts // less than 2 seconds after last request if ((uptime % TX23_READ_INTERVAL)==0) { // TX23 start transmission by pulling down TxD line for at minimum 500ms @@ -234,22 +341,21 @@ void Tx2xRead(void) tx23_stage = 1; // first rising signal is invalid pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT_PULLUP); } -#endif - if (0!=Settings.tele_period && Settings.tele_period!=tx2x_avg_samples) { - // new teleperiod value - Tx2xResetStat(); - } - if (tx2x_available) { - // Wind speed spec: 0 to 180 km/h (0 to 50 m/s) - tx2x_wind_speed_kmh = float(tx2x_sc) * 0.36; +#endif // USE_TX23_WIND_SENSOR + if (Tx2xAvailable()) { +#ifdef DEBUG_TASMOTA_SENSOR + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": sa=0x%02lx sb=%ld (0x%02lx), sc=%ld (0x%03lx), sd=0x%02lx, se=%ld, sf=%ld"), tx2x_sa,tx2x_sb,tx2x_sb,tx2x_sc,tx2x_sc,tx2x_sd,tx2x_se,tx2x_sf); +#endif // DEBUG_TASMOTA_SENSOR +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS if (tx2x_wind_speed_kmh < tx2x_wind_speed_min) { tx2x_wind_speed_min = tx2x_wind_speed_kmh; } if (tx2x_wind_speed_kmh > tx2x_wind_speed_max) { tx2x_wind_speed_max = tx2x_wind_speed_kmh; } - // 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 + + // 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 (tx2x_count <= tx2x_avg_samples) { tx2x_count++; @@ -257,72 +363,200 @@ void Tx2xRead(void) tx2x_wind_speed_avg -= tx2x_wind_speed_avg / tx2x_count; tx2x_wind_speed_avg += tx2x_wind_speed_kmh / tx2x_count; - tx2x_wind_direction = tx2x_sb; - - if (!(uptime % tx2x_avg_samples)) { - tx2x_wind_speed_min = tx2x_wind_speed_kmh; - tx2x_wind_speed_max = tx2x_wind_speed_kmh; - + tx2x_wind_direction_avg_x -= tx2x_wind_direction_avg_x / tx2x_count; + tx2x_wind_direction_avg_x += cosf((tx2x_wind_direction*22.5) * ff_pi180) / tx2x_count; + tx2x_wind_direction_avg_y -= tx2x_wind_direction_avg_y / tx2x_count; + tx2x_wind_direction_avg_y += sinf((tx2x_wind_direction*22.5) * ff_pi180) / tx2x_count; + tx2x_wind_direction_avg = atan2f(tx2x_wind_direction_avg_y, tx2x_wind_direction_avg_x) * 180.0f / ff_pi; + if (tx2x_wind_direction_avg<0.0) { + tx2x_wind_direction_avg += 360.0; } + if (tx2x_wind_direction_avg>360.0) { + tx2x_wind_direction_avg -= 360.0; + } + + int32_t tx2x_wind_direction_avg_int = int((tx2x_wind_direction_avg/22.5)+0.5) % 16; + + // degrees min/max + if (tx2x_wind_direction > tx2x_wind_direction_avg_int) { + // clockwise or left-handed rotation + if ((tx2x_wind_direction-tx2x_wind_direction_avg_int)>8) { + // diff > 180° + if ((tx2x_wind_direction - 16) < tx2x_wind_direction_min) { + // new min (negative values < 0) + tx2x_wind_direction_min = tx2x_wind_direction - 16; + } + } else { + // diff <= 180° + if (tx2x_wind_direction > tx2x_wind_direction_max) { + // new max (origin max) + tx2x_wind_direction_max = tx2x_wind_direction; + } + } + } else { + // also clockwise or left-handed rotation but needs other tests + if ((tx2x_wind_direction_avg_int-tx2x_wind_direction)>8) { + // diff > 180° + if ((tx2x_wind_direction + 16) > tx2x_wind_direction_max) { + // new max (overflow values > 15) + tx2x_wind_direction_max = tx2x_wind_direction + 16; + } + } else { + // diff <= 180° + if (tx2x_wind_direction < tx2x_wind_direction_min) { + // new min (origin min) + tx2x_wind_direction_min = tx2x_wind_direction; + } + } + } + +#ifdef DEBUG_TASMOTA_SENSOR + char diravg[33]; + dtostrfd(tx2x_wind_direction_avg, 1, diravg); + char cosx[33]; + dtostrfd(tx2x_wind_direction_avg_x, 1, cosx); + char siny[33]; + dtostrfd(tx2x_wind_direction_avg_y, 1, siny); + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": dir stat - counter=%ld, actint=%ld, avgint=%ld, avg=%s (cosx=%s, siny=%s), min %d, max %d"), + (uptime-last_uptime), + tx2x_wind_direction, + tx2x_wind_direction_avg_int, + diravg, + cosx, + siny, + tx2x_wind_direction_min, + tx2x_wind_direction_max + ); +#endif // DEBUG_TASMOTA_SENSOR +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS + } else { + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": not available")); + tx2x_wind_speed_kmh = 0; + tx2x_wind_direction = 0; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + tx2x_wind_speed_avg = 0; + tx2x_wind_direction_avg = 0; + Tx2xResetStatData(); +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS } + +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + Tx2xCheckSampleCount(); + if (0==Settings.tele_period && (uptime-last_uptime)>=tx2x_avg_samples) { + Tx2xResetStat(); + } +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS } -void Tx2xInit(void) +void Tx2xInit(void) { +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + tx2x_valuesread = false; Tx2xResetStat(); + Tx2xCheckSampleCount(); +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS #ifdef USE_TX23_WIND_SENSOR tx23_stage = 0; pinMode(pin[GPIO_TX2X_TXD_BLACK], OUTPUT); digitalWrite(pin[GPIO_TX2X_TXD_BLACK], LOW); -#else +#else // USE_TX23_WIND_SENSOR pinMode(pin[GPIO_TX2X_TXD_BLACK], INPUT); -#endif +#endif // USE_TX23_WIND_SENSOR attachInterrupt(pin[GPIO_TX2X_TXD_BLACK], TX2xStartRead, RISING); } +int32_t Tx2xNormalize(int32_t value) +{ + while (value>15) { + value -= 16; + } + while (value<0) { + value += 16; + } + return value; +} + void Tx2xShow(bool json) { - char wind_speed_string[33]; + char wind_speed_string[17]; dtostrfd(tx2x_wind_speed_kmh, 1, wind_speed_string); - char wind_speed_min_string[33]; + char wind_direction_string[17]; + dtostrfd(tx2x_wind_direction*22.5, 1, wind_direction_string); + char wind_direction_cardinal_string[4]; + GetTextIndexed(wind_direction_cardinal_string, sizeof(wind_direction_cardinal_string), tx2x_wind_direction, kTx2xDirections); +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + char wind_speed_min_string[17]; dtostrfd(tx2x_wind_speed_min, 1, wind_speed_min_string); - char wind_speed_max_string[33]; + char wind_speed_max_string[17]; dtostrfd(tx2x_wind_speed_max, 1, wind_speed_max_string); - char wind_speed_avg_string[33]; + char wind_speed_avg_string[17]; dtostrfd(tx2x_wind_speed_avg, 1, wind_speed_avg_string); - char wind_direction_degree_string[33]; - dtostrfd(tx2x_wind_direction*22.5, 1, wind_direction_degree_string); - char wind_direction_string[4]; - GetTextIndexed(wind_direction_string, sizeof(wind_direction_string), tx2x_wind_direction, kTx2xDirections); + char wind_direction_avg_string[17]; + dtostrfd(tx2x_wind_direction_avg, 1, wind_direction_avg_string); + char wind_direction_avg_cardinal_string[4]; + GetTextIndexed(wind_direction_avg_cardinal_string, sizeof(wind_direction_avg_cardinal_string), int((tx2x_wind_direction_avg/22.5f)+0.5f) % 16, kTx2xDirections); + char wind_direction_range_string[17]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_max-tx2x_wind_direction_min)*22.5, 1, wind_direction_range_string); + char wind_direction_min_string[17]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_min)*22.5, 1, wind_direction_min_string); + char wind_direction_max_string[17]; + dtostrfd(Tx2xNormalize(tx2x_wind_direction_max)*22.5, 1, wind_direction_max_string); +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS if (json) { +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS #ifdef USE_TX2x_LEGACY_JSON - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"Speed\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), - wind_speed_string, wind_speed_avg_string, wind_speed_max_string, wind_direction_string, wind_direction_degree); -#else - // new format grouped by Speed and Dir(ection) - // Card = cardianal (N../O../S../W..) - // Deg = Degree - ResponseAppend_P( - PSTR(",\"" D_TX2x_NAME "\":{\"Speed\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Direction\":{\"Cardinal\":\"%s\",\"Degree\":%s}}"), - wind_speed_string, - wind_speed_avg_string, - wind_speed_min_string, - wind_speed_max_string, - wind_direction_string, - wind_direction_degree_string + ResponseAppend_P(Tx2xAvailable()?PSTR(",\"" D_TX2x_NAME "\":{\"Speed\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\",\"Degree\":%s}"):PSTR(""), + wind_speed_string, + wind_speed_avg_string, + wind_speed_max_string, + wind_direction_cardinal_string, + wind_direction_string ); -#endif +#else // USE_TX2x_LEGACY_JSON + ResponseAppend_P(Tx2xAvailable()?PSTR(",\"" D_TX2x_NAME "\":{\"Speed\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"):PSTR(""), + wind_speed_string, + wind_speed_avg_string, + wind_speed_min_string, + wind_speed_max_string, + wind_direction_cardinal_string, + wind_direction_string, + wind_direction_avg_string, + wind_direction_avg_cardinal_string, + wind_direction_min_string, + wind_direction_max_string, + wind_direction_range_string + ); +#endif // USE_TX2x_LEGACY_JSON +#else // USE_TX2X_WIND_SENSOR_NOSTATISTICS +#ifdef USE_TX2x_LEGACY_JSON + ResponseAppend_P(Tx2xAvailable()?PSTR(",\"" D_TX2x_NAME "\":{\"Speed\":%s,\"Direction\":\"%s\",\"Degree\":%s}"):PSTR(""), + wind_speed_string, wind_direction_cardinal_string, wind_direction_string); +#else // USE_TX2x_LEGACY_JSON + ResponseAppend_P(Tx2xAvailable()?PSTR(",\"" D_TX2x_NAME "\":{\"Speed\":{\"Act\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s}}"):PSTR(""), + wind_speed_string, wind_direction_cardinal_string, wind_direction_string); +#endif // USE_TX2x_LEGACY_JSON +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS + #ifdef USE_WEBSERVER } else { WSContentSend_PD( - HTTP_SNS_TX2X, - wind_speed_string, - wind_speed_avg_string, - wind_speed_min_string, - wind_speed_max_string, - wind_direction_string, - wind_direction_degree_string + Tx2xAvailable()?HTTP_SNS_TX2X:PSTR(""), + wind_speed_string, +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + wind_speed_avg_string, + wind_speed_min_string, + wind_speed_max_string, +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS + wind_direction_cardinal_string, + wind_direction_string +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + ,wind_direction_avg_cardinal_string, + wind_direction_avg_string, + wind_direction_range_string, + wind_direction_min_string, + wind_direction_max_string +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS ); #endif // USE_WEBSERVER } @@ -344,6 +578,11 @@ bool Xsns35(uint8_t function) case FUNC_EVERY_SECOND: Tx2xRead(); break; +#ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS + case FUNC_AFTER_TELEPERIOD: + Tx2xResetStat(); + break; +#endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS case FUNC_JSON_APPEND: Tx2xShow(true); break; @@ -352,6 +591,7 @@ bool Xsns35(uint8_t function) Tx2xShow(false); break; #endif // USE_WEBSERVER + } } return result;