ESPHome 2025.12.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
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 for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
262 sprintf(buf, "%s%02x ", buf, 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 std::unique_ptr<char[]> buf(new char[bytes_count * 3 + 1]);
353 buf[0] = '\0';
354 for (size_t i = 0; i < bytes_count; i++) {
355 uint8_t byte = 0;
356 for (int8_t bit = 0; bit < 8; bit++) {
358 byte |= 1 << bit;
359 } else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
360 valid_daikin_frame = false;
361 break;
362 }
363 }
364 sprintf(buf.get(), "%s%02x ", buf.get(), byte);
365 }
366 ESP_LOGD(TAG, "WHOLE FRAME %s size: %d", buf.get(), data.size());
367 }
368 if (!valid_daikin_frame) {
369 char sbuf[16 * 10 + 1];
370 sbuf[0] = '\0';
371 for (size_t j = 0; j < static_cast<size_t>(data.size()); j++) {
372 if ((j - 2) % 16 == 0) {
373 if (j > 0) {
374 ESP_LOGD(TAG, "DATA %04x: %s", (j - 16 > 0xffff ? 0 : j - 16), sbuf);
375 }
376 sbuf[0] = '\0';
377 }
378 char type_ch = ' ';
379 // debug_tolerance = 25%
380
381 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_MARK)) <= data[j] &&
382 data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_MARK)))
383 type_ch = 'P';
384 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_SPACE)) <= -data[j] &&
385 -data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_SPACE)))
386 type_ch = 'a';
387 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_HEADER_MARK)) <= data[j] &&
388 data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_HEADER_MARK)))
389 type_ch = 'H';
390 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_HEADER_SPACE)) <= -data[j] &&
391 -data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_HEADER_SPACE)))
392 type_ch = 'h';
393 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_BIT_MARK)) <= data[j] &&
394 data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_BIT_MARK)))
395 type_ch = 'B';
396 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ONE_SPACE)) <= -data[j] &&
397 -data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ONE_SPACE)))
398 type_ch = '1';
399 if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ZERO_SPACE)) <= -data[j] &&
400 -data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ZERO_SPACE)))
401 type_ch = '0';
402
403 if (abs(data[j]) > 100000) {
404 sprintf(sbuf, "%s%-5d[%c] ", sbuf, data[j] > 0 ? 99999 : -99999, type_ch);
405 } else {
406 sprintf(sbuf, "%s%-5d[%c] ", sbuf, (int) (round(data[j] / 10.) * 10), type_ch);
407 }
408 if (j + 1 == static_cast<size_t>(data.size())) {
409 ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);
410 }
411 }
412 }
413
414 data.reset();
415
417 ESP_LOGI(TAG, "non daikin_arc expect item");
418 return false;
419 }
420
421 for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) {
422 uint8_t byte = 0;
423 for (int8_t bit = 0; bit < 8; bit++) {
425 byte |= 1 << bit;
426 } else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
427 ESP_LOGI(TAG, "non daikin_arc expect item pos: %d", pos);
428 return false;
429 }
430 }
431 state_frame[pos] = byte;
432 if (pos == 0) {
433 // frame header
434 if (byte != 0x11) {
435 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
436 return false;
437 }
438 } else if (pos == 1) {
439 // frame header
440 if (byte != 0xDA) {
441 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
442 return false;
443 }
444 } else if (pos == 2) {
445 // frame header
446 if (byte != 0x27) {
447 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
448 return false;
449 }
450 } else if (pos == 3) { // NOLINT(bugprone-branch-clone)
451 // frame header
452 if (byte != 0x00) {
453 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
454 return false;
455 }
456 } else if (pos == 4) {
457 // frame type
458 if (byte != 0x00) {
459 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
460 return false;
461 }
462 } else if (pos == 5) {
463 if (data.size() == 385) {
464 /*
465 11 da 27 00 00 1a 0c 04 2c 21 61 07 00 07 0c 00 18 00 0e 3c 00 6c 1b 61
466 Inside Temp
467 Outside Temp
468 Humdidity
469
470 */
471 this->current_temperature = state_frame[5]; // Inside temperature
472 // this->current_temperature = state_frame[6]; // Outside temperature
473 this->publish_state();
474 return true;
475 } else if ((byte & 0x40) != 0x40) {
476 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
477 return false;
478 }
479 }
480 }
481 return this->parse_state_frame_(state_frame);
482}
483
485 if (call.get_target_humidity().has_value()) {
486 this->target_humidity = *call.get_target_humidity();
487 }
489}
490
491} // namespace daikin_arc
492} // 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:290
ClimateMode mode
The active mode of the climate device.
Definition climate.h:256
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:250
float target_temperature
The target temperature of the climate device.
Definition climate.h:237
float current_humidity
The current humidity of the climate device, as reported from the integration.
Definition climate.h:233
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:262
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition climate.h:230
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:426
float target_humidity
The target humidity of the climate device.
Definition climate.h:247
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
uint16_t temperature
Definition sun_gtil2.cpp:12