ESPHome 2025.5.0
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 (int 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 (int 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:
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
251
252bool DaikinArcClimate::parse_state_frame_(const uint8_t frame[]) {
253 uint8_t checksum = 0;
254 for (int i = 0; i < (DAIKIN_STATE_FRAME_SIZE - 1); i++) {
255 checksum += frame[i];
256 }
257 if (frame[DAIKIN_STATE_FRAME_SIZE - 1] != checksum) {
258 ESP_LOGI(TAG, "checksum error");
259 return false;
260 }
261
262 char buf[DAIKIN_STATE_FRAME_SIZE * 3 + 1] = {0};
263 for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
264 sprintf(buf, "%s%02x ", buf, frame[i]);
265 }
266 ESP_LOGD(TAG, "FRAME %s", buf);
267
268 uint8_t mode = frame[5];
269 if (mode & DAIKIN_MODE_ON) {
270 switch (mode & 0xF0) {
271 case DAIKIN_MODE_COOL:
272 this->mode = climate::CLIMATE_MODE_COOL;
273 break;
274 case DAIKIN_MODE_DRY:
275 this->mode = climate::CLIMATE_MODE_DRY;
276 break;
277 case DAIKIN_MODE_HEAT:
278 this->mode = climate::CLIMATE_MODE_HEAT;
279 break;
280 case DAIKIN_MODE_AUTO:
282 break;
283 case DAIKIN_MODE_FAN:
285 break;
286 }
287 } else {
288 this->mode = climate::CLIMATE_MODE_OFF;
289 }
290 uint8_t temperature = frame[6];
291 if (!(temperature & 0xC0)) {
292 this->target_temperature = temperature >> 1;
293 this->target_temperature += (temperature & 0x1) ? 0.5 : 0;
294 }
295 this->target_humidity = frame[7]; // 0, 40, 45, 50, 0xff
296 uint8_t fan_mode = frame[8];
297 uint8_t swing_mode = frame[9];
298 if (fan_mode & 0xF && swing_mode & 0xF) {
299 this->swing_mode = climate::CLIMATE_SWING_BOTH;
300 } else if (fan_mode & 0xF) {
301 this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
302 } else if (swing_mode & 0xF) {
303 this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
304 } else {
305 this->swing_mode = climate::CLIMATE_SWING_OFF;
306 }
307 switch (fan_mode & 0xF0) {
308 case DAIKIN_FAN_1:
309 case DAIKIN_FAN_2:
311 this->fan_mode = climate::CLIMATE_FAN_LOW;
312 break;
313 case DAIKIN_FAN_3:
314 this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
315 break;
316 case DAIKIN_FAN_4:
317 case DAIKIN_FAN_5:
318 this->fan_mode = climate::CLIMATE_FAN_HIGH;
319 break;
320 case DAIKIN_FAN_AUTO:
321 this->fan_mode = climate::CLIMATE_FAN_AUTO;
322 break;
323 }
324 /*
325 05 0 [1:3]MODE 1 [OFF TMR] [ON TMR] Power
326 06-07 TEMP
327 08 [0:3] SPEED [4:7] Swing
328 09 00
329 10 00
330 11, 12: timer
331 13 [0:6] 0000000 [7] POWERMODE
332 14 0a
333 15 c4
334 16 [0:3] 8 00 [6:7] SENSOR WIND = 11 / NORMAL = 00
335 17 24
336 05 06 07 08 09 10 11 12 13 14 15 16 17 18
337 None FRAME 11 da 27 00 00 49 2e 00 b0 00 00 06 60 00 0a c4 80 24 11
338 1H FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 c6 30 00 2a c4 80 24 c5
339 1H30 FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 a6 32 00 2a c4 80 24 a7
340 2H FRAME 11 da 27 00 00 4d 2e 00 b0 00 00 86 34 00 2a c4 80 24 89
341
342 */
343 this->publish_state();
344 return true;
345}
346
348 uint8_t state_frame[DAIKIN_STATE_FRAME_SIZE] = {};
349
350 bool valid_daikin_frame = false;
352 valid_daikin_frame = true;
353 int bytes_count = data.size() / 2 / 8;
354 std::unique_ptr<char[]> buf(new char[bytes_count * 3 + 1]);
355 buf[0] = '\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 sprintf(buf.get(), "%s%02x ", buf.get(), 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];
372 sbuf[0] = '\0';
373 for (size_t j = 0; j < 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[0] = '\0';
379 }
380 char type_ch = ' ';
381 // debug_tolerance = 25%
382
383 if (DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_MARK))
384 type_ch = 'P';
385 if (DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_SPACE))
386 type_ch = 'a';
387 if (DAIKIN_DBG_LOWER(DAIKIN_HEADER_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_HEADER_MARK))
388 type_ch = 'H';
389 if (DAIKIN_DBG_LOWER(DAIKIN_HEADER_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_HEADER_SPACE))
390 type_ch = 'h';
391 if (DAIKIN_DBG_LOWER(DAIKIN_BIT_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_BIT_MARK))
392 type_ch = 'B';
393 if (DAIKIN_DBG_LOWER(DAIKIN_ONE_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ONE_SPACE))
394 type_ch = '1';
395 if (DAIKIN_DBG_LOWER(DAIKIN_ZERO_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ZERO_SPACE))
396 type_ch = '0';
397
398 if (abs(data[j]) > 100000) {
399 sprintf(sbuf, "%s%-5d[%c] ", sbuf, data[j] > 0 ? 99999 : -99999, type_ch);
400 } else {
401 sprintf(sbuf, "%s%-5d[%c] ", sbuf, (int) (round(data[j] / 10.) * 10), type_ch);
402 }
403 if (j == data.size() - 1) {
404 ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);
405 }
406 }
407 }
408
409 data.reset();
410
412 ESP_LOGI(TAG, "non daikin_arc expect item");
413 return false;
414 }
415
416 for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) {
417 uint8_t byte = 0;
418 for (int8_t bit = 0; bit < 8; bit++) {
420 byte |= 1 << bit;
421 } else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
422 ESP_LOGI(TAG, "non daikin_arc expect item pos: %d", pos);
423 return false;
424 }
425 }
426 state_frame[pos] = byte;
427 if (pos == 0) {
428 // frame header
429 if (byte != 0x11) {
430 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
431 return false;
432 }
433 } else if (pos == 1) {
434 // frame header
435 if (byte != 0xDA) {
436 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
437 return false;
438 }
439 } else if (pos == 2) {
440 // frame header
441 if (byte != 0x27) {
442 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
443 return false;
444 }
445 } else if (pos == 3) { // NOLINT(bugprone-branch-clone)
446 // frame header
447 if (byte != 0x00) {
448 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
449 return false;
450 }
451 } else if (pos == 4) {
452 // frame type
453 if (byte != 0x00) {
454 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
455 return false;
456 }
457 } else if (pos == 5) {
458 if (data.size() == 385) {
459 /*
460 11 da 27 00 00 1a 0c 04 2c 21 61 07 00 07 0c 00 18 00 0e 3c 00 6c 1b 61
461 Inside Temp
462 Outside Temp
463 Humdidity
464
465 */
466 this->current_temperature = state_frame[5]; // Inside temperature
467 // this->current_temperature = state_frame[6]; // Outside temperature
468 this->publish_state();
469 return true;
470 } else if ((byte & 0x40) != 0x40) {
471 ESP_LOGI(TAG, "non daikin_arc expect pos: %d header: %02x", pos, byte);
472 return false;
473 }
474 }
475 }
476 return this->parse_state_frame_(state_frame);
477}
478
480 if (call.get_target_humidity().has_value()) {
481 this->target_humidity = *call.get_target_humidity();
482 }
484}
485
486} // namespace daikin_arc
487} // 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:277
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
float current_humidity
The current humidity of the climate device, as reported from the integration.
Definition climate.h:182
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:202
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition climate.h:179
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:395
float target_humidity
The target humidity of the climate device.
Definition climate.h:196
This class contains all static data for climate devices.
void set_supports_current_humidity(bool supports_current_humidity)
void set_visual_min_humidity(float visual_min_humidity)
void set_supports_target_humidity(bool supports_target_humidity)
void set_visual_max_humidity(float visual_max_humidity)
void set_supports_current_temperature(bool supports_current_temperature)
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:89
bool expect_item(uint32_t mark, uint32_t space)
void set_carrier_frequency(uint32_t carrier_frequency)
Definition remote_base.h:34
@ 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
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition helpers.h:101
uint16_t temperature
Definition sun_gtil2.cpp:12