ESPHome 2025.5.0
Loading...
Searching...
No Matches
tcl112.cpp
Go to the documentation of this file.
1#include "tcl112.h"
2#include "esphome/core/log.h"
3
4namespace esphome {
5namespace tcl112 {
6
7static const char *const TAG = "tcl112.climate";
8
9const uint16_t TCL112_STATE_LENGTH = 14;
10const uint16_t TCL112_BITS = TCL112_STATE_LENGTH * 8;
11
12const uint8_t TCL112_HEAT = 1;
13const uint8_t TCL112_DRY = 2;
14const uint8_t TCL112_COOL = 3;
15const uint8_t TCL112_FAN = 7;
16const uint8_t TCL112_AUTO = 8;
17
18const uint8_t TCL112_FAN_AUTO = 0;
19const uint8_t TCL112_FAN_LOW = 2;
20const uint8_t TCL112_FAN_MED = 3;
21const uint8_t TCL112_FAN_HIGH = 5;
22
23const uint8_t TCL112_VSWING_MASK = 0x38;
24const uint8_t TCL112_POWER_MASK = 0x04;
25
26const uint8_t TCL112_HALF_DEGREE = 0b00100000;
27
28const uint16_t TCL112_HEADER_MARK = 3100;
29const uint16_t TCL112_HEADER_SPACE = 1650;
30const uint16_t TCL112_BIT_MARK = 500;
31const uint16_t TCL112_ONE_SPACE = 1100;
32const uint16_t TCL112_ZERO_SPACE = 350;
34
36 uint8_t remote_state[TCL112_STATE_LENGTH] = {0};
37
38 // A known good state. (On, Cool, 24C)
39 remote_state[0] = 0x23;
40 remote_state[1] = 0xCB;
41 remote_state[2] = 0x26;
42 remote_state[3] = 0x01;
43 remote_state[5] = 0x24;
44 remote_state[6] = 0x03;
45 remote_state[7] = 0x07;
46 remote_state[8] = 0x40;
47
48 // Set mode
49 switch (this->mode) {
51 remote_state[6] &= 0xF0;
52 remote_state[6] |= TCL112_AUTO;
53 break;
55 remote_state[6] &= 0xF0;
56 remote_state[6] |= TCL112_COOL;
57 break;
59 remote_state[6] &= 0xF0;
60 remote_state[6] |= TCL112_HEAT;
61 break;
63 remote_state[6] &= 0xF0;
64 remote_state[6] |= TCL112_DRY;
65 break;
67 remote_state[6] &= 0xF0;
68 remote_state[6] |= TCL112_FAN;
69 break;
71 default:
72 remote_state[5] &= ~TCL112_POWER_MASK;
73 break;
74 }
75
76 // Set temperature
77 // Make sure we have desired temp in the correct range.
78 float safecelsius = std::max(this->target_temperature, TCL112_TEMP_MIN);
79 safecelsius = std::min(safecelsius, TCL112_TEMP_MAX);
80 // Convert to integer nr. of half degrees.
81 auto half_degrees = static_cast<uint8_t>(safecelsius * 2);
82 if (half_degrees & 1) { // Do we have a half degree celsius?
83 remote_state[12] |= TCL112_HALF_DEGREE; // Add 0.5 degrees
84 } else {
85 remote_state[12] &= ~TCL112_HALF_DEGREE; // Clear the half degree.
86 }
87 remote_state[7] &= 0xF0; // Clear temp bits.
88 remote_state[7] |= ((uint8_t) TCL112_TEMP_MAX - half_degrees / 2);
89
90 // Set fan
91 uint8_t selected_fan;
92 switch (this->fan_mode.value()) {
94 selected_fan = TCL112_FAN_HIGH;
95 break;
97 selected_fan = TCL112_FAN_MED;
98 break;
100 selected_fan = TCL112_FAN_LOW;
101 break;
103 default:
104 selected_fan = TCL112_FAN_AUTO;
105 }
106 remote_state[8] |= selected_fan;
107
108 // Set swing
110 remote_state[8] |= TCL112_VSWING_MASK;
111 }
112
113 // Calculate & set the checksum for the current internal state of the remote.
114 // Stored the checksum value in the last byte.
115 for (uint8_t checksum_byte = 0; checksum_byte < TCL112_STATE_LENGTH - 1; checksum_byte++)
116 remote_state[TCL112_STATE_LENGTH - 1] += remote_state[checksum_byte];
117
118 ESP_LOGV(TAG, "Sending: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0],
119 remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6],
120 remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12],
121 remote_state[13]);
122
123 auto transmit = this->transmitter_->transmit();
124 auto *data = transmit.get_data();
125
126 data->set_carrier_frequency(38000);
127
128 // Header
129 data->mark(TCL112_HEADER_MARK);
130 data->space(TCL112_HEADER_SPACE);
131 // Data
132 for (uint8_t i : remote_state) {
133 for (uint8_t j = 0; j < 8; j++) {
134 data->mark(TCL112_BIT_MARK);
135 bool bit = i & (1 << j);
136 data->space(bit ? TCL112_ONE_SPACE : TCL112_ZERO_SPACE);
137 }
138 }
139 // Footer
140 data->mark(TCL112_BIT_MARK);
141 data->space(TCL112_GAP);
142
143 transmit.perform();
144}
145
147 // Validate header
149 ESP_LOGVV(TAG, "Header fail");
150 return false;
151 }
152
153 uint8_t remote_state[TCL112_STATE_LENGTH] = {0};
154 // Read all bytes.
155 for (int i = 0; i < TCL112_STATE_LENGTH; i++) {
156 // Read bit
157 for (int j = 0; j < 8; j++) {
159 remote_state[i] |= 1 << j;
160 } else if (!data.expect_item(TCL112_BIT_MARK, TCL112_ZERO_SPACE)) {
161 ESP_LOGVV(TAG, "Byte %d bit %d fail", i, j);
162 return false;
163 }
164 }
165 }
166 // Validate footer
167 if (!data.expect_mark(TCL112_BIT_MARK)) {
168 ESP_LOGVV(TAG, "Footer fail");
169 return false;
170 }
171
172 uint8_t checksum = 0;
173 // Calculate & set the checksum for the current internal state of the remote.
174 // Stored the checksum value in the last byte.
175 for (uint8_t checksum_byte = 0; checksum_byte < TCL112_STATE_LENGTH - 1; checksum_byte++)
176 checksum += remote_state[checksum_byte];
177 if (checksum != remote_state[TCL112_STATE_LENGTH - 1]) {
178 ESP_LOGVV(TAG, "Checksum fail");
179 return false;
180 }
181
182 ESP_LOGV(TAG, "Received: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
183 remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5],
184 remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11],
185 remote_state[12], remote_state[13]);
186
187 // two first bytes are constant
188 if (remote_state[0] != 0x23 || remote_state[1] != 0xCB)
189 return false;
190
191 if ((remote_state[5] & TCL112_POWER_MASK) == 0) {
193 } else {
194 auto mode = remote_state[6] & 0x0F;
195 switch (mode) {
196 case TCL112_HEAT:
198 break;
199 case TCL112_COOL:
201 break;
202 case TCL112_DRY:
204 break;
205 case TCL112_FAN:
207 break;
208 case TCL112_AUTO:
210 break;
211 }
212 }
213 auto temp = TCL112_TEMP_MAX - remote_state[7];
214 if (remote_state[12] & TCL112_HALF_DEGREE)
215 temp += .5f;
216 this->target_temperature = temp;
217 auto fan = remote_state[8] & 0x7;
218 switch (fan) {
219 case TCL112_FAN_HIGH:
221 break;
222 case TCL112_FAN_MED:
224 break;
225 case TCL112_FAN_LOW:
227 break;
228 case TCL112_FAN_AUTO:
229 default:
231 break;
232 }
233 if ((remote_state[8] & TCL112_VSWING_MASK) == TCL112_VSWING_MASK) {
235 } else {
237 }
238
239 this->publish_state();
240 return true;
241}
242
243} // namespace tcl112
244} // namespace esphome
uint8_t checksum
Definition bl0906.h:3
ClimateMode mode
The active mode of the climate device.
Definition climate.h:173
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:199
float target_temperature
The target temperature of the climate device.
Definition climate.h:186
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:202
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:395
value_type const & value() const
Definition optional.h:89
bool expect_item(uint32_t mark, uint32_t space)
void set_carrier_frequency(uint32_t carrier_frequency)
Definition remote_base.h:34
void transmit_state() override
Transmit via IR the state of this climate controller.
Definition tcl112.cpp:35
bool on_receive(remote_base::RemoteReceiveData data) override
Handle received IR Buffer.
Definition tcl112.cpp:146
@ CLIMATE_SWING_OFF
The swing mode is set to Off.
@ CLIMATE_SWING_VERTICAL
The fan mode is set to Vertical.
@ CLIMATE_MODE_DRY
The climate device is set to dry/humidity mode.
@ CLIMATE_MODE_FAN_ONLY
The climate device only has the fan enabled, no heating or cooling is taking place.
@ CLIMATE_MODE_HEAT
The climate device is set to heat to reach the target temperature.
@ CLIMATE_MODE_COOL
The climate device is set to cool to reach the target temperature.
@ CLIMATE_MODE_HEAT_COOL
The climate device is set to heat/cool to reach the target temperature.
@ CLIMATE_MODE_OFF
The climate device is off.
@ CLIMATE_FAN_MEDIUM
The fan mode is set to Medium.
@ CLIMATE_FAN_AUTO
The fan mode is set to Auto.
@ CLIMATE_FAN_LOW
The fan mode is set to Low.
@ CLIMATE_FAN_HIGH
The fan mode is set to High.
const uint8_t TCL112_DRY
Definition tcl112.cpp:13
const uint8_t TCL112_FAN
Definition tcl112.cpp:15
const uint8_t TCL112_AUTO
Definition tcl112.cpp:16
const float TCL112_TEMP_MAX
Definition tcl112.h:9
const uint16_t TCL112_BIT_MARK
Definition tcl112.cpp:30
const uint8_t TCL112_HALF_DEGREE
Definition tcl112.cpp:26
const uint8_t TCL112_VSWING_MASK
Definition tcl112.cpp:23
const uint16_t TCL112_HEADER_MARK
Definition tcl112.cpp:28
const uint16_t TCL112_ONE_SPACE
Definition tcl112.cpp:31
const uint16_t TCL112_HEADER_SPACE
Definition tcl112.cpp:29
const uint32_t TCL112_GAP
Definition tcl112.cpp:33
const uint8_t TCL112_FAN_MED
Definition tcl112.cpp:20
const uint8_t TCL112_FAN_HIGH
Definition tcl112.cpp:21
const uint16_t TCL112_ZERO_SPACE
Definition tcl112.cpp:32
const float TCL112_TEMP_MIN
Definition tcl112.h:10
const uint8_t TCL112_FAN_LOW
Definition tcl112.cpp:19
const uint8_t TCL112_FAN_AUTO
Definition tcl112.cpp:18
const uint8_t TCL112_COOL
Definition tcl112.cpp:14
const uint16_t TCL112_BITS
Definition tcl112.cpp:10
const uint8_t TCL112_POWER_MASK
Definition tcl112.cpp:24
const uint16_t TCL112_STATE_LENGTH
Definition tcl112.cpp:9
const uint8_t TCL112_HEAT
Definition tcl112.cpp:12
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7