ESPHome 2025.5.0
Loading...
Searching...
No Matches
tx20.cpp
Go to the documentation of this file.
1#include "tx20.h"
2#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 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}
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
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:39
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:44
std::string wind_cardinal_direction_
Definition tx20.h:44
void loop() override
Definition tx20.cpp:35
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:27
float get_setup_priority() const override
Definition tx20.cpp:42
@ INTERRUPT_ANY_EDGE
Definition gpio.h:43
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:19
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint32_t IRAM_ATTR HOT micros()
Definition core.cpp:29
std::string to_string(int value)
Definition helpers.cpp:82
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:28
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:145
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