ESPHome 2026.2.1
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 static uint8_t last_humidity = 0x66;
95 if (remote_state[7] != last_humidity && this->mode != climate::CLIMATE_MODE_OFF) {
96 ESP_LOGD(TAG, "Set Humditiy: %d, %d\n", (int) this->target_humidity, (int) remote_state[7]);
97 remote_header[9] |= 0x10;
98 last_humidity = remote_state[7];
99 }
100 uint16_t fan_speed = this->fan_speed_();
101 remote_state[8] = fan_speed >> 8;
102 remote_state[9] = fan_speed & 0xff;
103
104 // Calculate checksum
105 for (size_t i = 0; i < sizeof(remote_header) - 1; i++) {
106 remote_header[sizeof(remote_header) - 1] += remote_header[i];
107 }
108
109 // Calculate checksum
110 for (int i = 0; i < DAIKIN_STATE_FRAME_SIZE - 1; i++) {
111 remote_state[DAIKIN_STATE_FRAME_SIZE - 1] += remote_state[i];
112 }
113
114 auto transmit = this->transmitter_->transmit();
115 auto *data = transmit.get_data();
117
118 data->mark(DAIKIN_ARC_PRE_MARK);
119 data->space(DAIKIN_ARC_PRE_SPACE);
120
121 data->mark(DAIKIN_HEADER_MARK);
122 data->space(DAIKIN_HEADER_SPACE);
123
124 for (uint8_t i : remote_header) {
125 for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
126 data->mark(DAIKIN_BIT_MARK);
127 bool bit = i & mask;
128 data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
129 }
130 }
131 data->mark(DAIKIN_BIT_MARK);
132 data->space(DAIKIN_MESSAGE_SPACE);
133
134 data->mark(DAIKIN_HEADER_MARK);
135 data->space(DAIKIN_HEADER_SPACE);
136
137 for (uint8_t i : remote_state) {
138 for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
139 data->mark(DAIKIN_BIT_MARK);
140 bool bit = i & mask;
141 data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
142 }
143 }
144 data->mark(DAIKIN_BIT_MARK);
145 data->space(0);
146
147 transmit.perform();
148}
149
151 uint8_t operating_mode = DAIKIN_MODE_ON;
152 switch (this->mode) {
154 operating_mode |= DAIKIN_MODE_COOL;
155 break;
157 operating_mode |= DAIKIN_MODE_DRY;
158 break;
160 operating_mode |= DAIKIN_MODE_HEAT;
161 break;
163 operating_mode |= DAIKIN_MODE_AUTO;
164 break;
166 operating_mode |= DAIKIN_MODE_FAN;
167 break;
169 default:
170 operating_mode = DAIKIN_MODE_OFF;
171 break;
172 }
173
174 return operating_mode;
175}
176
178 uint16_t fan_speed;
179 switch (this->fan_mode.value()) {
181 fan_speed = DAIKIN_FAN_1 << 8;
182 break;
184 fan_speed = DAIKIN_FAN_3 << 8;
185 break;
187 fan_speed = DAIKIN_FAN_5 << 8;
188 break;
190 default:
191 fan_speed = DAIKIN_FAN_AUTO << 8;
192 }
193
194 // If swing is enabled switch first 4 bits to 1111
195 switch (this->swing_mode) {
197 fan_speed |= 0x0F00;
198 break;
200 fan_speed |= 0x000F;
201 break;
203 fan_speed |= 0x0F0F;
204 break;
205 default:
206 break;
207 }
208 return fan_speed;
209}
210
212 // Force special temperatures depending on the mode
213 switch (this->mode) {
215 return 0x32;
218 return 0xc0;
219 default:
220 float new_temp = clamp<float>(this->target_temperature, DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX);
221 uint8_t temperature = (uint8_t) floor(new_temp);
222 return temperature << 1 | (new_temp - temperature > 0 ? 0x01 : 0);
223 }
224}
225
227 if (this->target_humidity == 39) {
228 return 0;
229 } else if (this->target_humidity <= 40 || this->target_humidity == 44) {
230 return 40;
231 } else if (this->target_humidity <= 45 || this->target_humidity == 49) // 41 - 45
232 {
233 return 45;
234 } else if (this->target_humidity <= 50 || this->target_humidity == 52) // 45 - 50
235 {
236 return 50;
237 } else {
238 return 0xff;
239 }
240}
241
249
250bool DaikinArcClimate::parse_state_frame_(const uint8_t frame[]) {
251 uint8_t checksum = 0;
252 for (int i = 0; i < (DAIKIN_STATE_FRAME_SIZE - 1); i++) {
253 checksum += frame[i];
254 }
255 if (frame[DAIKIN_STATE_FRAME_SIZE - 1] != checksum) {
256 ESP_LOGI(TAG, "checksum error");
257 return false;
258 }
259
260 char buf[DAIKIN_STATE_FRAME_SIZE * 3 + 1] = {0};
261 size_t pos = 0;
262 for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
263 pos = buf_append_printf(buf, sizeof(buf), pos, "%02x ", frame[i]);
264 }
265 ESP_LOGD(TAG, "FRAME %s", buf);
266
267 uint8_t mode = frame[5];
268 if (mode & DAIKIN_MODE_ON) {
269 switch (mode & 0xF0) {
270 case DAIKIN_MODE_COOL:
271 this->mode = climate::CLIMATE_MODE_COOL;
272 break;
273 case DAIKIN_MODE_DRY:
274 this->mode = climate::CLIMATE_MODE_DRY;
275 break;
276 case DAIKIN_MODE_HEAT:
277 this->mode = climate::CLIMATE_MODE_HEAT;
278 break;
279 case DAIKIN_MODE_AUTO:
281 break;
282 case DAIKIN_MODE_FAN:
284 break;
285 }
286 } else {
287 this->mode = climate::CLIMATE_MODE_OFF;
288 }
289 uint8_t temperature = frame[6];
290 if (!(temperature & 0xC0)) {
291 this->target_temperature = temperature >> 1;
292 this->target_temperature += (temperature & 0x1) ? 0.5 : 0;
293 }
294 this->target_humidity = frame[7]; // 0, 40, 45, 50, 0xff
295 uint8_t fan_mode = frame[8];
296 uint8_t swing_mode = frame[9];
297 if (fan_mode & 0xF && swing_mode & 0xF) {
298 this->swing_mode = climate::CLIMATE_SWING_BOTH;
299 } else if (fan_mode & 0xF) {
300 this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
301 } else if (swing_mode & 0xF) {
302 this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
303 } else {
304 this->swing_mode = climate::CLIMATE_SWING_OFF;
305 }
306 switch (fan_mode & 0xF0) {
307 case DAIKIN_FAN_1:
308 case DAIKIN_FAN_2:
310 this->fan_mode = climate::CLIMATE_FAN_LOW;
311 break;
312 case DAIKIN_FAN_3:
313 this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
314 break;
315 case DAIKIN_FAN_4:
316 case DAIKIN_FAN_5:
317 this->fan_mode = climate::CLIMATE_FAN_HIGH;
318 break;
319 case DAIKIN_FAN_AUTO:
320 this->fan_mode = climate::CLIMATE_FAN_AUTO;
321 break;
322 }
323 /*
324 05 0 [1:3]MODE 1 [OFF TMR] [ON TMR] Power
325 06-07 TEMP
326 08 [0:3] SPEED [4:7] Swing
327 09 00
328 10 00
329 11, 12: timer
330 13 [0:6] 0000000 [7] POWERMODE
331 14 0a
332 15 c4
333 16 [0:3] 8 00 [6:7] SENSOR WIND = 11 / NORMAL = 00
334 17 24
335 05 06 07 08 09 10 11 12 13 14 15 16 17 18
336 None FRAME 11 da 27 00 00 49 2e 00 b0 00 00 06 60 00 0a c4 80 24 11
337 1H FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 c6 30 00 2a c4 80 24 c5
338 1H30 FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 a6 32 00 2a c4 80 24 a7
339 2H FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 86 34 00 2a c4 80 24 89
340
341 */
342 this->publish_state();
343 return true;
344}
345
347 uint8_t state_frame[DAIKIN_STATE_FRAME_SIZE] = {};
348
349 bool valid_daikin_frame = false;
351 valid_daikin_frame = true;
352 size_t bytes_count = data.size() / 2 / 8;
353 size_t buf_size = bytes_count * 3 + 1;
354 std::unique_ptr<char[]> buf(new char[buf_size]()); // value-initialize (zero-fill)
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.get(), buf_size, buf_pos, "%02x ", byte);
367 }
368 ESP_LOGD(TAG, "WHOLE FRAME %s size: %d", buf.get(), 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 if (call.get_target_humidity().has_value()) {
489 this->target_humidity = *call.get_target_humidity();
490 }
492}
493
494} // namespace daikin_arc
495} // 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
const optional< float > & get_target_humidity() const
Definition climate.cpp:302
ClimateMode mode
The active mode of the climate device.
Definition climate.h:262
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:256
float target_temperature
The target temperature of the climate device.
Definition climate.h:243
float current_humidity
The current humidity of the climate device, as reported from the integration.
Definition climate.h:239
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:268
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition climate.h:236
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:437
float target_humidity
The target humidity of the climate device.
Definition climate.h:253
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
value_type const & value() const
Definition optional.h:94
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_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:729
uint16_t temperature
Definition sun_gtil2.cpp:12