ESPHome  2024.5.2
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 
8 namespace esphome {
9 namespace daikin_arc {
10 
11 static 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:
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 
245  traits.set_supports_current_humidity(false);
246  traits.set_supports_target_humidity(true);
247  traits.set_visual_min_humidity(38);
248  traits.set_visual_max_humidity(52);
249  return traits;
250 }
251 
252 bool 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:
281  this->mode = climate::CLIMATE_MODE_HEAT_COOL;
282  break;
283  case DAIKIN_MODE_FAN:
284  this->mode = climate::CLIMATE_MODE_FAN_ONLY;
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:
310  case DAIKIN_FAN_SILENT:
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
This class is used to encode all control actions on a climate device.
Definition: climate.h:33
The fan mode is set to Low.
Definition: climate_mode.h:54
value_type const & value() const
Definition: optional.h:89
const uint8_t DAIKIN_MODE_HEAT
Definition: daikin_arc.h:16
float current_humidity
The current humidity of the climate device, as reported from the integration.
Definition: climate.h:182
const uint32_t DAIKIN_MESSAGE_SPACE
Definition: daikin_arc.h:40
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition: climate.h:202
void set_visual_min_humidity(float visual_min_humidity)
The fan mode is set to Both.
Definition: climate_mode.h:74
float target_temperature
The target temperature of the climate device.
Definition: climate.h:186
const uint32_t DAIKIN_ONE_SPACE
Definition: daikin_arc.h:38
void set_carrier_frequency(uint32_t carrier_frequency)
Definition: remote_base.h:34
void set_visual_max_humidity(float visual_max_humidity)
void set_supports_target_humidity(bool supports_target_humidity)
void set_supports_current_humidity(bool supports_current_humidity)
This class contains all static data for climate devices.
The climate device is set to heat to reach the target temperature.
Definition: climate_mode.h:18
ClimateMode mode
The active mode of the climate device.
Definition: climate.h:173
const uint8_t DAIKIN_MODE_OFF
Definition: daikin_arc.h:19
const uint8_t DAIKIN_TEMP_MAX
Definition: daikin_arc.h:11
const uint8_t DAIKIN_MODE_COOL
Definition: daikin_arc.h:15
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition: climate.h:179
bool has_value() const
Definition: optional.h:87
The climate device is set to dry/humidity mode.
Definition: climate_mode.h:22
const uint32_t DAIKIN_BIT_MARK
Definition: daikin_arc.h:37
void control(const climate::ClimateCall &call) override
Override control to change settings of the climate device.
Definition: climate_ir.cpp:61
float target_humidity
The target humidity of the climate device.
Definition: climate.h:196
void control(const climate::ClimateCall &call) override
Definition: daikin_arc.cpp:479
const uint8_t DAIKIN_MODE_FAN
Definition: daikin_arc.h:18
const uint32_t DAIKIN_HEADER_SPACE
Definition: daikin_arc.h:36
The fan mode is set to Horizontal.
Definition: climate_mode.h:78
The climate device is set to cool to reach the target temperature.
Definition: climate_mode.h:16
const uint8_t DAIKIN_FAN_1
Definition: daikin_arc.h:25
The fan mode is set to Auto.
Definition: climate_mode.h:52
const uint8_t DAIKIN_FAN_5
Definition: daikin_arc.h:29
const uint8_t DAIKIN_TEMP_MIN
Definition: daikin_arc.h:10
const uint32_t DAIKIN_HEADER_MARK
Definition: daikin_arc.h:35
RemoteTransmitterBase * transmitter_
Definition: remote_base.h:276
uint16_t temperature
Definition: sun_gtil2.cpp:26
const uint32_t DAIKIN_ARC_PRE_SPACE
Definition: daikin_arc.h:34
The climate device is set to heat/cool to reach the target temperature.
Definition: climate_mode.h:14
The fan mode is set to Vertical.
Definition: climate_mode.h:76
uint8_t checksum
Definition: bl0939.h:35
bool on_receive(remote_base::RemoteReceiveData data) override
Definition: daikin_arc.cpp:347
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition: climate.cpp:395
The fan mode is set to High.
Definition: climate_mode.h:58
The swing mode is set to Off.
Definition: climate_mode.h:72
The climate device is off.
Definition: climate_mode.h:12
bool parse_state_frame_(const uint8_t frame[])
Definition: daikin_arc.cpp:252
const uint8_t DAIKIN_MODE_DRY
Definition: daikin_arc.h:17
const uint8_t DAIKIN_MODE_AUTO
Definition: daikin_arc.h:14
const uint8_t DAIKIN_MODE_ON
Definition: daikin_arc.h:20
climate::ClimateTraits traits() override
Return the traits of this controller.
Definition: climate_ir.cpp:9
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition: climate.h:199
const uint8_t DAIKIN_FAN_SILENT
Definition: daikin_arc.h:24
const uint32_t DAIKIN_IR_FREQUENCY
Definition: daikin_arc.h:32
const uint8_t DAIKIN_STATE_FRAME_SIZE
Definition: daikin_arc.h:47
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
climate::ClimateTraits traits() override
Definition: daikin_arc.cpp:242
const uint8_t DAIKIN_FAN_4
Definition: daikin_arc.h:28
void set_supports_current_temperature(bool supports_current_temperature)
The fan mode is set to Medium.
Definition: climate_mode.h:56
const uint32_t DAIKIN_ARC_PRE_MARK
Definition: daikin_arc.h:33
bool expect_item(uint32_t mark, uint32_t space)
Definition: remote_base.cpp:74
The climate device only has the fan enabled, no heating or cooling is taking place.
Definition: climate_mode.h:20
const uint32_t DAIKIN_ZERO_SPACE
Definition: daikin_arc.h:39
const uint8_t DAIKIN_FAN_2
Definition: daikin_arc.h:26
const optional< float > & get_target_humidity() const
Definition: climate.cpp:277
const uint8_t DAIKIN_FAN_3
Definition: daikin_arc.h:27
const uint8_t DAIKIN_FAN_AUTO
Definition: daikin_arc.h:23