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