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