ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
daikin_arc.cpp
Go to the documentation of this file.
1#include "daikin_arc.h"
2
3#include <cmath>
4
6#include "esphome/core/log.h"
7
9
10ESPHOME_LOG_TAG(TAG, "daikin.climate");
11
14
15 // Never send nan to HA
16 if (std::isnan(this->target_humidity))
17 this->target_humidity = 0;
18 if (std::isnan(this->current_temperature))
19 this->current_temperature = 0;
20 if (std::isnan(this->current_humidity))
21 this->current_humidity = 0;
22}
23
25 uint8_t remote_header[8] = {0x11, 0xDA, 0x27, 0x00, 0x84, 0x87, 0x20, 0x00};
26
27 // Calculate checksum
28 for (size_t i = 0; i < sizeof(remote_header) - 1; i++) {
29 remote_header[sizeof(remote_header) - 1] += remote_header[i];
30 }
31
32 auto transmit = this->transmitter_->transmit();
33 auto *data = transmit.get_data();
34 data->set_carrier_frequency(DAIKIN_IR_FREQUENCY);
35
36 data->mark(DAIKIN_ARC_PRE_MARK);
37 data->space(DAIKIN_ARC_PRE_SPACE);
38
39 data->mark(DAIKIN_HEADER_MARK);
40 data->space(DAIKIN_HEADER_SPACE);
41
42 data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_header, sizeof(remote_header));
43 data->mark(DAIKIN_BIT_MARK);
44 data->space(0);
45
46 transmit.perform();
47}
48
50 // 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00,
51 // 0x42, 0x49, 0x05, 0xA2,
52 uint8_t remote_header[20] = {0x11, 0xDA, 0x27, 0x00, 0x02, 0xd0, 0x02, 0x03, 0x80, 0x03, 0x82, 0x30, 0x41, 0x1f, 0x82,
53 0xf4,
54 /* とつど */
55 /* 0x13 */
56 0x00, 0x24, 0x00, 0x00};
57
58 // 05 0 [1:3]MODE 1 [OFF TMR] [ON TMR] Power
59 // 06-07 TEMP
60 // 08 [0:3] SPEED [4:7] Swing
61 // 09 00
62 // 10 00
63 // 11, 12: timer
64 // 13 [0:6] 0000000 [7] POWERMODE
65 // 14 0a
66 // 15 c4
67 // 16 [0:3] 8 00 [6:7] SENSOR WIND = 11 / NORMAL = 00
68 // 17 24
69
70 uint8_t remote_state[19] = {
71 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x60, 0x00, 0x0a, 0xC4,
72 /* MODE TEMP HUMD FANH FANL
73 パワフル音声応答 */
74 /* ON
75 0x01入 0x0a */
76 /* OF
77 0x00切 0x02 */
78 0x80, 0x24, 0x00
79 /* センサー風 */
80 /* ON 0x83 */
81 /* OF 0x80 */
82 };
83
84 remote_state[5] = this->operation_mode_() | 0x08;
85 remote_state[6] = this->temperature_();
86 remote_state[7] = this->humidity_();
87 if (remote_state[7] != this->last_humidity_ && this->mode != climate::CLIMATE_MODE_OFF) {
88 ESP_LOGD(TAG, "Set Humditiy: %d, %d\n", (int) this->target_humidity, (int) remote_state[7]);
89 remote_header[9] |= 0x10;
90 this->last_humidity_ = remote_state[7];
91 }
92 uint16_t fan_speed = this->fan_speed_();
93 remote_state[8] = fan_speed >> 8;
94 remote_state[9] = fan_speed & 0xff;
95
96 // Calculate checksum
97 for (size_t i = 0; i < sizeof(remote_header) - 1; i++) {
98 remote_header[sizeof(remote_header) - 1] += remote_header[i];
99 }
100
101 // Calculate checksum
102 for (int i = 0; i < DAIKIN_STATE_FRAME_SIZE - 1; i++) {
103 remote_state[DAIKIN_STATE_FRAME_SIZE - 1] += remote_state[i];
104 }
105
106 auto transmit = this->transmitter_->transmit();
107 auto *data = transmit.get_data();
108 data->set_carrier_frequency(DAIKIN_IR_FREQUENCY);
109
110 data->mark(DAIKIN_ARC_PRE_MARK);
111 data->space(DAIKIN_ARC_PRE_SPACE);
112
113 data->mark(DAIKIN_HEADER_MARK);
114 data->space(DAIKIN_HEADER_SPACE);
115
116 data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_header, sizeof(remote_header));
117 data->mark(DAIKIN_BIT_MARK);
118 data->space(DAIKIN_MESSAGE_SPACE);
119
120 data->mark(DAIKIN_HEADER_MARK);
121 data->space(DAIKIN_HEADER_SPACE);
122
123 data->item_bytes<DAIKIN_BIT_TIMINGS>(remote_state, sizeof(remote_state));
124 data->mark(DAIKIN_BIT_MARK);
125 data->space(0);
126
127 transmit.perform();
128}
129
131 uint8_t operating_mode = DAIKIN_MODE_ON;
132 switch (this->mode) {
134 operating_mode |= DAIKIN_MODE_COOL;
135 break;
137 operating_mode |= DAIKIN_MODE_DRY;
138 break;
140 operating_mode |= DAIKIN_MODE_HEAT;
141 break;
143 operating_mode |= DAIKIN_MODE_AUTO;
144 break;
146 operating_mode |= DAIKIN_MODE_FAN;
147 break;
149 default:
150 operating_mode = DAIKIN_MODE_OFF;
151 break;
152 }
153
154 return operating_mode;
155}
156
158 uint16_t fan_speed;
159 switch (this->fan_mode.value_or(climate::CLIMATE_FAN_ON)) {
161 fan_speed = DAIKIN_FAN_1 << 8;
162 break;
164 fan_speed = DAIKIN_FAN_3 << 8;
165 break;
167 fan_speed = DAIKIN_FAN_5 << 8;
168 break;
170 default:
171 fan_speed = DAIKIN_FAN_AUTO << 8;
172 }
173
174 // If swing is enabled switch first 4 bits to 1111
175 switch (this->swing_mode) {
177 fan_speed |= 0x0F00;
178 break;
180 fan_speed |= 0x000F;
181 break;
183 fan_speed |= 0x0F0F;
184 break;
185 default:
186 break;
187 }
188 return fan_speed;
189}
190
192 // Force special temperatures depending on the mode
193 switch (this->mode) {
195 return 0x32;
198 return 0xc0;
199 default:
200 float new_temp = clamp<float>(this->target_temperature, DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX);
201 uint8_t temperature = (uint8_t) std::floor(new_temp);
202 return temperature << 1 | (new_temp - temperature > 0 ? 0x01 : 0);
203 }
204}
205
207 if (this->target_humidity == 39) {
208 return 0;
209 } else if (this->target_humidity <= 40 || this->target_humidity == 44) {
210 return 40;
211 } else if (this->target_humidity <= 45 || this->target_humidity == 49) // 41 - 45
212 {
213 return 45;
214 } else if (this->target_humidity <= 50 || this->target_humidity == 52) // 45 - 50
215 {
216 return 50;
217 } else {
218 return 0xff;
219 }
220}
221
229
230bool DaikinArcClimate::parse_state_frame_(const uint8_t frame[]) {
231 const uint8_t checksum = sum_bytes(frame, DAIKIN_STATE_FRAME_SIZE - 1);
232 if (frame[DAIKIN_STATE_FRAME_SIZE - 1] != checksum) {
233 ESP_LOGI(TAG, "checksum error");
234 return false;
235 }
236
237 char buf[DAIKIN_STATE_FRAME_SIZE * 3 + 1] = {0};
238 size_t pos = 0;
239 for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
240 pos = buf_append_printf(buf, sizeof(buf), pos, "%02x ", frame[i]);
241 }
242 ESP_LOGD(TAG, "FRAME %s", buf);
243
244 uint8_t mode = frame[5];
245 if (mode & DAIKIN_MODE_ON) {
246 switch (mode & 0xF0) {
247 case DAIKIN_MODE_COOL:
248 this->mode = climate::CLIMATE_MODE_COOL;
249 break;
250 case DAIKIN_MODE_DRY:
251 this->mode = climate::CLIMATE_MODE_DRY;
252 break;
253 case DAIKIN_MODE_HEAT:
254 this->mode = climate::CLIMATE_MODE_HEAT;
255 break;
256 case DAIKIN_MODE_AUTO:
258 break;
259 case DAIKIN_MODE_FAN:
261 break;
262 }
263 } else {
264 this->mode = climate::CLIMATE_MODE_OFF;
265 }
266 uint8_t temperature = frame[6];
267 if (!(temperature & 0xC0)) {
268 this->target_temperature = temperature >> 1;
269 this->target_temperature += (temperature & 0x1) ? 0.5 : 0;
270 }
271 this->target_humidity = frame[7]; // 0, 40, 45, 50, 0xff
272 uint8_t fan_mode = frame[8];
273 uint8_t swing_mode = frame[9];
274 if (fan_mode & 0xF && swing_mode & 0xF) {
275 this->swing_mode = climate::CLIMATE_SWING_BOTH;
276 } else if (fan_mode & 0xF) {
277 this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
278 } else if (swing_mode & 0xF) {
279 this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
280 } else {
281 this->swing_mode = climate::CLIMATE_SWING_OFF;
282 }
283 switch (fan_mode & 0xF0) {
284 case DAIKIN_FAN_1:
285 case DAIKIN_FAN_2:
287 this->fan_mode = climate::CLIMATE_FAN_LOW;
288 break;
289 case DAIKIN_FAN_3:
290 this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
291 break;
292 case DAIKIN_FAN_4:
293 case DAIKIN_FAN_5:
294 this->fan_mode = climate::CLIMATE_FAN_HIGH;
295 break;
296 case DAIKIN_FAN_AUTO:
297 this->fan_mode = climate::CLIMATE_FAN_AUTO;
298 break;
299 }
300 /*
301 05 0 [1:3]MODE 1 [OFF TMR] [ON TMR] Power
302 06-07 TEMP
303 08 [0:3] SPEED [4:7] Swing
304 09 00
305 10 00
306 11, 12: timer
307 13 [0:6] 0000000 [7] POWERMODE
308 14 0a
309 15 c4
310 16 [0:3] 8 00 [6:7] SENSOR WIND = 11 / NORMAL = 00
311 17 24
312 05 06 07 08 09 10 11 12 13 14 15 16 17 18
313 None FRAME 11 da 27 00 00 49 2e 00 b0 00 00 06 60 00 0a c4 80 24 11
314 1H FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 c6 30 00 2a c4 80 24 c5
315 1H30 FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 a6 32 00 2a c4 80 24 a7
316 2H FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 86 34 00 2a c4 80 24 89
317
318 */
319 this->publish_state();
320 return true;
321}
322
324 uint8_t state_frame[DAIKIN_STATE_FRAME_SIZE];
325
326 bool valid_daikin_frame = false;
327 if (data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
328 valid_daikin_frame = true;
329 size_t bytes_count = data.size() / 2 / 8;
330 // Header (20) + state (19) = 39 bytes max; truncates gracefully via buf_append_printf
331 char buf[40 * 3 + 1] = {};
332 constexpr size_t buf_size = sizeof(buf);
333 size_t buf_pos = 0;
334 for (size_t i = 0; i < bytes_count; i++) {
335 uint8_t byte = 0;
336 for (int8_t bit = 0; bit < 8; bit++) {
337 if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE)) {
338 byte |= 1 << bit;
339 } else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
340 valid_daikin_frame = false;
341 break;
342 }
343 }
344 buf_pos = buf_append_printf(buf, buf_size, buf_pos, "%02x ", byte);
345 }
346 ESP_LOGD(TAG, "WHOLE FRAME %s size: %d", buf, data.size());
347 }
348 if (!valid_daikin_frame) {
349 char sbuf[16 * 10 + 1] = {0};
350 size_t sbuf_pos = 0;
351 for (size_t j = 0; j < static_cast<size_t>(data.size()); j++) {
352 if ((j - 2) % 16 == 0) {
353 if (j > 0) {
354 ESP_LOGD(TAG, "DATA %04x: %s", (j - 16 > 0xffff ? 0 : j - 16), sbuf);
355 }
356 sbuf_pos = 0;
357 }
358 char type_ch = ' ';
359 // debug_tolerance = 25%
360
361 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_MARK)) <= data[j] &&
362 data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_MARK)))
363 type_ch = 'P';
364 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_SPACE)) <= -data[j] &&
365 -data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_SPACE)))
366 type_ch = 'a';
367 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_HEADER_MARK)) <= data[j] &&
368 data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_HEADER_MARK)))
369 type_ch = 'H';
370 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_HEADER_SPACE)) <= -data[j] &&
371 -data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_HEADER_SPACE)))
372 type_ch = 'h';
373 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_BIT_MARK)) <= data[j] &&
374 data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_BIT_MARK)))
375 type_ch = 'B';
376 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ONE_SPACE)) <= -data[j] &&
377 -data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ONE_SPACE)))
378 type_ch = '1';
379 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ZERO_SPACE)) <= -data[j] &&
380 -data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ZERO_SPACE)))
381 type_ch = '0';
382
383 if (abs(data[j]) > 100000) {
384 sbuf_pos = buf_append_printf(sbuf, sizeof(sbuf), sbuf_pos, "%-5d[%c] ", data[j] > 0 ? 99999 : -99999, type_ch);
385 } else {
386 sbuf_pos =
387 buf_append_printf(sbuf, sizeof(sbuf), sbuf_pos, "%-5d[%c] ", (int) (round(data[j] / 10.) * 10), type_ch);
388 }
389 if (j + 1 == static_cast<size_t>(data.size())) {
390 ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);
391 }
392 }
393 }
394
395 data.reset();
396
397 if (!data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
398 ESP_LOGI(TAG, "non daikin_arc expect item");
399 return false;
400 }
401
402 for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) {
403 if (!data.expect_bytes<DAIKIN_BIT_TIMINGS>(&state_frame[pos], 1)) {
404 ESP_LOGI(TAG, "non daikin_arc expect item pos: %d", pos);
405 return false;
406 }
407 const uint8_t byte = state_frame[pos];
408 if (pos == 0) {
409 // frame header
410 if (byte != 0x11) {
411 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
412 return false;
413 }
414 } else if (pos == 1) {
415 // frame header
416 if (byte != 0xDA) {
417 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
418 return false;
419 }
420 } else if (pos == 2) {
421 // frame header
422 if (byte != 0x27) {
423 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
424 return false;
425 }
426 } else if (pos == 3) { // NOLINT(bugprone-branch-clone)
427 // frame header
428 if (byte != 0x00) {
429 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
430 return false;
431 }
432 } else if (pos == 4) {
433 // frame type
434 if (byte != 0x00) {
435 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
436 return false;
437 }
438 } else if (pos == 5) {
439 if (data.size() == 385) {
440 /*
441 11 da 27 00 00 1a 0c 04 2c 21 61 07 00 07 0c 00 18 00 0e 3c 00 6c 1b 61
442 Inside Temp
443 Outside Temp
444 Humdidity
445
446 */
447 this->current_temperature = state_frame[5]; // Inside temperature
448 // this->current_temperature = state_frame[6]; // Outside temperature
449 this->publish_state();
450 return true;
451 } else if ((byte & 0x40) != 0x40) {
452 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
453 return false;
454 }
455 }
456 }
457 return this->parse_state_frame_(state_frame);
458}
459
461 auto target_humidity = call.get_target_humidity();
462 if (target_humidity.has_value()) {
464 }
466}
467
468} // namespace esphome::daikin_arc
uint8_t checksum
Definition bl0906.h:3
This class is used to encode all control actions on a climate device.
Definition climate.h:31
ClimateMode mode
The active mode of the climate device.
Definition climate.h:317
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:311
float target_temperature
The target temperature of the climate device.
Definition climate.h:298
float current_humidity
The current humidity of the climate device, as reported from the integration.
Definition climate.h:294
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:323
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition climate.h:291
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:432
float target_humidity
The target humidity of the climate device.
Definition climate.h:308
void add_feature_flags(uint32_t feature_flags)
void set_visual_min_humidity(float visual_min_humidity)
void set_visual_max_humidity(float visual_max_humidity)
void control(const climate::ClimateCall &call) override
Override control to change settings of the climate device.
climate::ClimateTraits traits() override
Return the traits of this controller.
Definition climate_ir.cpp:8
void control(const climate::ClimateCall &call) override
bool parse_state_frame_(const uint8_t frame[])
climate::ClimateTraits traits() override
bool on_receive(remote_base::RemoteReceiveData data) override
@ CLIMATE_SUPPORTS_CURRENT_TEMPERATURE
@ CLIMATE_SWING_OFF
The swing mode is set to Off.
@ CLIMATE_SWING_HORIZONTAL
The fan mode is set to Horizontal.
@ CLIMATE_SWING_VERTICAL
The fan mode is set to Vertical.
@ CLIMATE_SWING_BOTH
The fan mode is set to Both.
@ 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_ON
The fan mode is set to On.
@ 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 uint32_t DAIKIN_ZERO_SPACE
Definition daikin_arc.h:38
const uint32_t DAIKIN_HEADER_SPACE
Definition daikin_arc.h:35
const uint32_t DAIKIN_ARC_PRE_SPACE
Definition daikin_arc.h:33
const uint8_t DAIKIN_MODE_AUTO
Definition daikin_arc.h:13
const uint32_t DAIKIN_ARC_PRE_MARK
Definition daikin_arc.h:32
const uint32_t DAIKIN_MESSAGE_SPACE
Definition daikin_arc.h:41
const uint8_t DAIKIN_TEMP_MIN
Definition daikin_arc.h:9
const uint32_t DAIKIN_HEADER_MARK
Definition daikin_arc.h:34
const uint8_t DAIKIN_FAN_2
Definition daikin_arc.h:25
const uint8_t DAIKIN_FAN_5
Definition daikin_arc.h:28
const uint32_t DAIKIN_ONE_SPACE
Definition daikin_arc.h:37
const uint8_t DAIKIN_MODE_COOL
Definition daikin_arc.h:14
const uint32_t DAIKIN_BIT_MARK
Definition daikin_arc.h:36
const uint8_t DAIKIN_FAN_3
Definition daikin_arc.h:26
const uint8_t DAIKIN_FAN_4
Definition daikin_arc.h:27
ESPHOME_LOG_TAG(TAG, "daikin.climate")
const uint8_t DAIKIN_FAN_1
Definition daikin_arc.h:24
const uint32_t DAIKIN_IR_FREQUENCY
Definition daikin_arc.h:31
const uint8_t DAIKIN_MODE_FAN
Definition daikin_arc.h:17
const uint8_t DAIKIN_TEMP_MAX
Definition daikin_arc.h:10
const uint8_t DAIKIN_MODE_HEAT
Definition daikin_arc.h:15
const uint8_t DAIKIN_MODE_DRY
Definition daikin_arc.h:16
const uint8_t DAIKIN_STATE_FRAME_SIZE
Definition daikin_arc.h:48
const uint8_t DAIKIN_MODE_OFF
Definition daikin_arc.h:18
const uint8_t DAIKIN_FAN_SILENT
Definition daikin_arc.h:23
const uint8_t DAIKIN_FAN_AUTO
Definition daikin_arc.h:22
const uint8_t DAIKIN_MODE_ON
Definition daikin_arc.h:19
constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum=0)
Add up the bytes of data with size len, truncated to 8 bits: the checksum many serial and IR protocol...
Definition helpers.h:894
size_t size_t pos
Definition helpers.h:1255
uint16_t temperature
Definition sun_gtil2.cpp:12