ESPHome  2022.9.2
tx20.cpp
Go to the documentation of this file.
1 #include "tx20.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 
5 namespace esphome {
6 namespace tx20 {
7 
8 static const char *const TAG = "tx20";
9 static const uint8_t MAX_BUFFER_SIZE = 41;
10 static const uint16_t TX20_MAX_TIME = MAX_BUFFER_SIZE * 1200 + 5000;
11 static const uint16_t TX20_BIT_TIME = 1200;
12 static const char *const DIRECTIONS[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
13  "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"};
14 
16  ESP_LOGCONFIG(TAG, "Setting up Tx20");
17  this->pin_->setup();
18 
19  this->store_.buffer = new uint16_t[MAX_BUFFER_SIZE];
20  this->store_.pin = this->pin_->to_isr();
21  this->store_.reset();
22 
24 }
26  ESP_LOGCONFIG(TAG, "Tx20:");
27 
28  LOG_SENSOR(" ", "Wind speed:", this->wind_speed_sensor_);
29  LOG_SENSOR(" ", "Wind direction degrees:", this->wind_direction_degrees_sensor_);
30 
31  LOG_PIN(" Pin: ", this->pin_);
32 }
34  if (this->store_.tx20_available) {
35  this->decode_and_publish_();
36  this->store_.reset();
37  }
38 }
39 
41 
43 
45  ESP_LOGVV(TAG, "Decode Tx20...");
46 
47  std::string string_buffer;
48  std::string string_buffer_2;
49  std::vector<bool> bit_buffer;
50  bool current_bit = true;
51 
52  for (int i = 1; i <= this->store_.buffer_index; i++) {
53  string_buffer_2 += to_string(this->store_.buffer[i]) + ", ";
54  uint8_t repeat = this->store_.buffer[i] / TX20_BIT_TIME;
55  // ignore segments at the end that were too short
56  string_buffer.append(repeat, current_bit ? '1' : '0');
57  bit_buffer.insert(bit_buffer.end(), repeat, current_bit);
58  current_bit = !current_bit;
59  }
60  current_bit = !current_bit;
61  if (string_buffer.length() < MAX_BUFFER_SIZE) {
62  uint8_t remain = MAX_BUFFER_SIZE - string_buffer.length();
63  string_buffer_2 += to_string(remain) + ", ";
64  string_buffer.append(remain, current_bit ? '1' : '0');
65  bit_buffer.insert(bit_buffer.end(), remain, current_bit);
66  }
67 
68  uint8_t tx20_sa = 0;
69  uint8_t tx20_sb = 0;
70  uint8_t tx20_sd = 0;
71  uint8_t tx20_se = 0;
72  uint16_t tx20_sc = 0;
73  uint16_t tx20_sf = 0;
74  uint8_t tx20_wind_direction = 0;
75  float tx20_wind_speed_kmh = 0;
76  uint8_t bit_count = 0;
77 
78  for (int i = 41; i > 0; i--) {
79  uint8_t bit = bit_buffer.at(bit_count);
80  bit_count++;
81  if (i > 41 - 5) {
82  // start, inverted
83  tx20_sa = (tx20_sa << 1) | (bit ^ 1);
84  } else if (i > 41 - 5 - 4) {
85  // wind dir, inverted
86  tx20_sb = tx20_sb >> 1 | ((bit ^ 1) << 3);
87  } else if (i > 41 - 5 - 4 - 12) {
88  // windspeed, inverted
89  tx20_sc = tx20_sc >> 1 | ((bit ^ 1) << 11);
90  } else if (i > 41 - 5 - 4 - 12 - 4) {
91  // checksum, inverted
92  tx20_sd = tx20_sd >> 1 | ((bit ^ 1) << 3);
93  } else if (i > 41 - 5 - 4 - 12 - 4 - 4) {
94  // wind dir
95  tx20_se = tx20_se >> 1 | (bit << 3);
96  } else {
97  // windspeed
98  tx20_sf = tx20_sf >> 1 | (bit << 11);
99  }
100  }
101 
102  uint8_t chk = (tx20_sb + (tx20_sc & 0xf) + ((tx20_sc >> 4) & 0xf) + ((tx20_sc >> 8) & 0xf));
103  chk &= 0xf;
104  bool value_set = false;
105  // checks:
106  // 1. Check that the start frame is 00100 (0x04)
107  // 2. Check received checksum matches calculated checksum
108  // 3. Check that Wind Direction matches Wind Direction (Inverted)
109  // 4. Check that Wind Speed matches Wind Speed (Inverted)
110  ESP_LOGVV(TAG, "BUFFER %s", string_buffer_2.c_str());
111  ESP_LOGVV(TAG, "Decoded bits %s", string_buffer.c_str());
112 
113  if (tx20_sa == 4) {
114  if (chk == tx20_sd) {
115  if (tx20_sf == tx20_sc) {
116  tx20_wind_speed_kmh = float(tx20_sc) * 0.36f;
117  ESP_LOGV(TAG, "WindSpeed %f", tx20_wind_speed_kmh);
118  if (this->wind_speed_sensor_ != nullptr)
119  this->wind_speed_sensor_->publish_state(tx20_wind_speed_kmh);
120  value_set = true;
121  }
122  if (tx20_se == tx20_sb) {
123  tx20_wind_direction = tx20_se;
124  if (tx20_wind_direction >= 0 && tx20_wind_direction < 16) {
125  wind_cardinal_direction_ = DIRECTIONS[tx20_wind_direction];
126  }
127  ESP_LOGV(TAG, "WindDirection %d", tx20_wind_direction);
128  if (this->wind_direction_degrees_sensor_ != nullptr)
129  this->wind_direction_degrees_sensor_->publish_state(float(tx20_wind_direction) * 22.5f);
130  value_set = true;
131  }
132  if (!value_set) {
133  ESP_LOGW(TAG, "No value set!");
134  }
135  } else {
136  ESP_LOGW(TAG, "Checksum wrong!");
137  }
138  } else {
139  ESP_LOGW(TAG, "Start wrong!");
140  }
141 }
142 
144  arg->pin_state = arg->pin.digital_read();
145  const uint32_t now = micros();
146  if (!arg->start_time) {
147  // only detect a start if the bit is high
148  if (!arg->pin_state) {
149  return;
150  }
151  arg->buffer[arg->buffer_index] = 1;
152  arg->start_time = now;
153  arg->buffer_index++;
154  return;
155  }
156  const uint32_t delay = now - arg->start_time;
157  const uint8_t index = arg->buffer_index;
158 
159  // first delay has to be ~2400
160  if (index == 1 && (delay > 3000 || delay < 2400)) {
161  arg->reset();
162  return;
163  }
164  // second delay has to be ~1200
165  if (index == 2 && (delay > 1500 || delay < 1200)) {
166  arg->reset();
167  return;
168  }
169  // third delay has to be ~2400
170  if (index == 3 && (delay > 3000 || delay < 2400)) {
171  arg->reset();
172  return;
173  }
174 
175  if (arg->tx20_available || ((arg->spent_time + delay > TX20_MAX_TIME) && arg->start_time)) {
176  arg->tx20_available = true;
177  return;
178  }
179  if (index <= MAX_BUFFER_SIZE) {
180  arg->buffer[index] = delay;
181  }
182  arg->spent_time += delay;
183  arg->start_time = now;
184  arg->buffer_index++;
185 }
186 void IRAM_ATTR Tx20ComponentStore::reset() {
187  tx20_available = false;
188  buffer_index = 0;
189  spent_time = 0;
190  // rearm it!
191  start_time = 0;
192 }
193 
194 } // namespace tx20
195 } // namespace esphome
volatile uint8_t buffer_index
Definition: tx20.h:14
InternalGPIOPin * pin_
Definition: tx20.h:45
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:18
volatile uint32_t spent_time
Definition: tx20.h:15
void loop() override
Definition: tx20.cpp:33
static void gpio_intr(Tx20ComponentStore *arg)
Definition: tx20.cpp:143
sensor::Sensor * wind_direction_degrees_sensor_
Definition: tx20.h:47
virtual void setup()=0
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:28
float get_setup_priority() const override
Definition: tx20.cpp:40
volatile uint32_t start_time
Definition: tx20.h:13
void dump_config() override
Definition: tx20.cpp:25
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:72
sensor::Sensor * wind_speed_sensor_
Definition: tx20.h:46
void setup() override
Definition: tx20.cpp:15
ISRInternalGPIOPin pin
Definition: tx20.h:18
virtual ISRInternalGPIOPin to_isr() const =0
Store data in a class that doesn&#39;t use multiple-inheritance (vtables in flash)
Definition: tx20.h:11
std::string to_string(int value)
Definition: helpers.cpp:36
volatile bool tx20_available
Definition: tx20.h:16
std::string wind_cardinal_direction_
Definition: tx20.h:44
volatile uint16_t * buffer
Definition: tx20.h:12
Definition: a4988.cpp:4
Tx20ComponentStore store_
Definition: tx20.h:48
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition: gpio.h:81
std::string get_wind_cardinal_direction() const
Get the textual representation of the wind direction (&#39;N&#39;, &#39;SSE&#39;, ..).
Definition: tx20.cpp:42
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27