xsns_35_tx20.ino 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. xsns_35_Tx20.ino - La Crosse Tx20 wind sensor support for Sonoff-Tasmota
  3. Copyright (C) 2018 Thomas Eckerstorfer and Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_TX20_WIND_SENSOR
  16. /*********************************************************************************************\
  17. * La Crosse Tx20 wind sensor
  18. *
  19. * based on https://github.com/bunnyhu/ESP8266_TX20_wind_sensor/
  20. * http://blog.bubux.de/windsensor-tx20-mit-esp8266/
  21. * https://www.john.geek.nz/2011/07/la-crosse-tx20-anemometer-communication-protocol/
  22. \*********************************************************************************************/
  23. #define XSNS_35 35
  24. #define TX20_BIT_TIME 1220 // microseconds
  25. #define TX20_RESET_VALUES 60 // seconds
  26. // The Arduino standard GPIO routines are not enough,
  27. // must use some from the Espressif SDK as well
  28. extern "C" {
  29. #include "gpio.h"
  30. }
  31. #ifdef USE_WEBSERVER
  32. const char HTTP_SNS_TX20[] PROGMEM = "%s"
  33. "{s}TX20 " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}"
  34. "{s}TX20 " D_TX20_WIND_SPEED_AVG "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}"
  35. "{s}TX20 " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}"
  36. "{s}TX20 " D_TX20_WIND_DIRECTION "{m}%s{e}";
  37. #endif // USE_WEBSERVER
  38. const char kTx20Directions[] PROGMEM = D_TX20_NORTH "|"
  39. D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|"
  40. D_TX20_NORTH D_TX20_EAST "|"
  41. D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|"
  42. D_TX20_EAST "|"
  43. D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|"
  44. D_TX20_SOUTH D_TX20_EAST "|"
  45. D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|"
  46. D_TX20_SOUTH "|"
  47. D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|"
  48. D_TX20_SOUTH D_TX20_WEST "|"
  49. D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|"
  50. D_TX20_WEST "|"
  51. D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|"
  52. D_TX20_NORTH D_TX20_WEST "|"
  53. D_TX20_NORTH D_TX20_NORTH D_TX20_WEST;
  54. uint8_t tx20_sa = 0;
  55. uint8_t tx20_sb = 0;
  56. uint8_t tx20_sd = 0;
  57. uint8_t tx20_se = 0;
  58. uint16_t tx20_sc = 0;
  59. uint16_t tx20_sf = 0;
  60. float tx20_wind_speed_kmh = 0;
  61. float tx20_wind_speed_max = 0;
  62. float tx20_wind_speed_avg = 0;
  63. float tx20_wind_sum = 0;
  64. int tx20_count = 0;
  65. uint8_t tx20_wind_direction = 0;
  66. boolean tx20_available = false;
  67. void Tx20StartRead(void)
  68. {
  69. /* La Crosse TX20 Anemometer datagram every 2 seconds
  70. * 0-0 11011 0011 111010101111 0101 1100 000101010000 0-0 - Received pin data at 1200 uSec per bit
  71. * sa sb sc sd se sf
  72. * 00100 1100 000101010000 1010 1100 000101010000 - sa to sd inverted user data, LSB first
  73. * sa - Start frame always 00100
  74. * sb - Wind direction 0 - 15
  75. * sc - Wind speed 0 - 511
  76. * sd - Checksum
  77. * se - Wind direction 0 - 15
  78. * sf - Wind speed 0 - 511
  79. */
  80. tx20_available = false;
  81. tx20_sa = 0;
  82. tx20_sb = 0;
  83. tx20_sd = 0;
  84. tx20_se = 0;
  85. tx20_sc = 0;
  86. tx20_sf = 0;
  87. delayMicroseconds(TX20_BIT_TIME / 2);
  88. for (int bitcount = 41; bitcount > 0; bitcount--) {
  89. uint8_t dpin = (digitalRead(pin[GPIO_TX20_TXD_BLACK]));
  90. if (bitcount > 41 - 5) {
  91. // start, inverted
  92. tx20_sa = (tx20_sa << 1) | (dpin ^ 1);
  93. } else if (bitcount > 41 - 5 - 4) {
  94. // wind dir, inverted
  95. tx20_sb = tx20_sb >> 1 | ((dpin ^ 1) << 3);
  96. } else if (bitcount > 41 - 5 - 4 - 12) {
  97. // windspeed, inverted
  98. tx20_sc = tx20_sc >> 1 | ((dpin ^ 1) << 11);
  99. } else if (bitcount > 41 - 5 - 4 - 12 - 4) {
  100. // checksum, inverted
  101. tx20_sd = tx20_sd >> 1 | ((dpin ^ 1) << 3);
  102. } else if (bitcount > 41 - 5 - 4 - 12 - 4 - 4) {
  103. // wind dir
  104. tx20_se = tx20_se >> 1 | (dpin << 3);
  105. } else {
  106. // windspeed
  107. tx20_sf = tx20_sf >> 1 | (dpin << 11);
  108. }
  109. delayMicroseconds(TX20_BIT_TIME);
  110. }
  111. uint8_t chk = (tx20_sb + (tx20_sc & 0xf) + ((tx20_sc >> 4) & 0xf) + ((tx20_sc >> 8) & 0xf));
  112. chk &= 0xf;
  113. if ((chk == tx20_sd) && (tx20_sc < 400)) { // if checksum seems to be ok and wind speed below 40 m/s
  114. tx20_available = true;
  115. }
  116. /*
  117. if ((tx20_sb == tx20_se) && (tx20_sc == tx20_sf) && (tx20_sc < 400)) {
  118. tx20_available = true;
  119. }
  120. */
  121. // Must clear this bit in the interrupt register,
  122. // it gets set even when interrupts are disabled
  123. GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pin[GPIO_TX20_TXD_BLACK]);
  124. }
  125. void Tx20Read(void)
  126. {
  127. if (!(uptime % TX20_RESET_VALUES)) {
  128. tx20_count = 0;
  129. tx20_wind_sum = 0;
  130. tx20_wind_speed_max = 0;
  131. }
  132. else if (tx20_available) {
  133. tx20_wind_speed_kmh = float(tx20_sc) * 0.36;
  134. if (tx20_wind_speed_kmh > tx20_wind_speed_max) {
  135. tx20_wind_speed_max = tx20_wind_speed_kmh;
  136. }
  137. tx20_count++;
  138. tx20_wind_sum += tx20_wind_speed_kmh;
  139. tx20_wind_speed_avg = tx20_wind_sum / tx20_count;
  140. tx20_wind_direction = tx20_sb;
  141. }
  142. }
  143. void Tx20Init(void) {
  144. pinMode(pin[GPIO_TX20_TXD_BLACK], INPUT);
  145. attachInterrupt(pin[GPIO_TX20_TXD_BLACK], Tx20StartRead, RISING);
  146. }
  147. void Tx20Show(boolean json)
  148. {
  149. char wind_speed_string[33];
  150. dtostrfd(tx20_wind_speed_kmh, 2, wind_speed_string);
  151. char wind_speed_max_string[33];
  152. dtostrfd(tx20_wind_speed_max, 2, wind_speed_max_string);
  153. char wind_speed_avg_string[33];
  154. dtostrfd(tx20_wind_speed_avg, 2, wind_speed_avg_string);
  155. char wind_direction_string[4];
  156. GetTextIndexed(wind_direction_string, sizeof(wind_direction_string), tx20_wind_direction, kTx20Directions);
  157. if (json) {
  158. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"TX20\":{\"Speed\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\"}"),
  159. mqtt_data, wind_speed_string, wind_speed_avg_string, wind_speed_max_string, wind_direction_string);
  160. #ifdef USE_WEBSERVER
  161. } else {
  162. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TX20, mqtt_data, wind_speed_string, wind_speed_avg_string, wind_speed_max_string, wind_direction_string);
  163. #endif // USE_WEBSERVER
  164. }
  165. }
  166. /*********************************************************************************************\
  167. * Interface
  168. \*********************************************************************************************/
  169. boolean Xsns35(byte function)
  170. {
  171. boolean result = false;
  172. if (pin[GPIO_TX20_TXD_BLACK] < 99) {
  173. switch (function) {
  174. case FUNC_INIT:
  175. Tx20Init();
  176. break;
  177. case FUNC_EVERY_SECOND:
  178. Tx20Read();
  179. break;
  180. case FUNC_JSON_APPEND:
  181. Tx20Show(1);
  182. break;
  183. #ifdef USE_WEBSERVER
  184. case FUNC_WEB_APPEND:
  185. Tx20Show(0);
  186. break;
  187. #endif // USE_WEBSERVER
  188. }
  189. }
  190. return result;
  191. }
  192. #endif // USE_TX20_WIND_SENSOR