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