ESPHome  2024.12.2
whynter.cpp
Go to the documentation of this file.
1 #include "whynter.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace whynter {
6 
7 static const char *const TAG = "climate.whynter";
8 
9 const uint16_t BITS = 32;
10 
11 // Static First Byte
12 const uint32_t COMMAND_MASK = 0xFF << 24;
13 const uint32_t COMMAND_CODE = 0x12 << 24;
14 
15 // Power
16 const uint32_t POWER_SHIFT = 8;
17 const uint32_t POWER_MASK = 1 << POWER_SHIFT;
18 const uint32_t POWER_OFF = 0 << POWER_SHIFT;
19 
20 // Mode
21 const uint32_t MODE_SHIFT = 16;
22 const uint32_t MODE_MASK = 0b1111 << MODE_SHIFT;
23 const uint32_t MODE_FAN = 0b0001 << MODE_SHIFT;
24 const uint32_t MODE_DRY = 0b0010 << MODE_SHIFT;
25 const uint32_t MODE_HEAT = 0b0100 << MODE_SHIFT;
26 const uint32_t MODE_COOL = 0b1000 << MODE_SHIFT;
27 
28 // Fan Speed
29 const uint32_t FAN_SHIFT = 20;
30 const uint32_t FAN_MASK = 0b111 << FAN_SHIFT;
31 const uint32_t FAN_HIGH = 0b001 << FAN_SHIFT;
32 const uint32_t FAN_MED = 0b010 << FAN_SHIFT;
33 const uint32_t FAN_LOW = 0b100 << FAN_SHIFT;
34 
35 // Temperature Unit
36 const uint32_t UNIT_SHIFT = 10;
37 const uint32_t UNIT_MASK = 1 << UNIT_SHIFT;
38 
39 // Temperature Value
40 const uint32_t TEMP_MASK = 0xFF;
41 const uint32_t TEMP_OFFSET_C = 16;
42 
44  uint32_t remote_state = COMMAND_CODE;
47  switch (this->mode) {
49  remote_state |= POWER_MASK;
50  remote_state |= MODE_FAN;
51  break;
53  remote_state |= POWER_MASK;
54  remote_state |= MODE_DRY;
55  break;
57  remote_state |= POWER_MASK;
58  remote_state |= MODE_HEAT;
59  break;
61  remote_state |= POWER_MASK;
62  remote_state |= MODE_COOL;
63  break;
65  remote_state |= POWER_OFF;
66  break;
67  default:
68  remote_state |= POWER_OFF;
69  }
70  mode_before_ = this->mode;
71 
72  switch (this->fan_mode.value()) {
74  remote_state |= FAN_LOW;
75  break;
77  remote_state |= FAN_MED;
78  break;
80  remote_state |= FAN_HIGH;
81  break;
82  default:
83  remote_state |= FAN_HIGH;
84  }
85 
86  if (fahrenheit_) {
87  remote_state |= UNIT_MASK;
88  uint8_t temp =
90  temp = esphome::reverse_bits(temp);
91  remote_state |= temp;
92  } else {
93  uint8_t temp = (uint8_t) roundf(clamp<float>(this->target_temperature, TEMP_MIN_C, TEMP_MAX_C) - TEMP_OFFSET_C);
94  temp = esphome::reverse_bits(temp);
95  remote_state |= temp;
96  }
97 
98  transmit_(remote_state);
99  this->publish_state();
100 }
101 
103  uint8_t nbits = 0;
104  uint32_t remote_state = 0;
105 
106  if (!data.expect_item(this->header_high_, this->header_low_))
107  return false;
108 
109  for (nbits = 0; nbits < 32; nbits++) {
110  if (data.expect_item(this->bit_high_, this->bit_one_low_)) {
111  remote_state = (remote_state << 1) | 1;
112  } else if (data.expect_item(this->bit_high_, this->bit_zero_low_)) {
113  remote_state = (remote_state << 1) | 0;
114  } else if (nbits == BITS) {
115  break;
116  } else {
117  return false;
118  }
119  }
120 
121  ESP_LOGD(TAG, "Decoded 0x%02" PRIX32, remote_state);
122  if ((remote_state & COMMAND_MASK) != COMMAND_CODE)
123  return false;
124  if ((remote_state & POWER_MASK) != POWER_MASK) {
126  } else {
127  if ((remote_state & MODE_MASK) == MODE_FAN) {
129  } else if ((remote_state & MODE_MASK) == MODE_DRY) {
131  } else if ((remote_state & MODE_MASK) == MODE_HEAT) {
133  } else if ((remote_state & MODE_MASK) == MODE_COOL) {
135  }
136 
137  // Temperature
138  if ((remote_state & UNIT_MASK) == UNIT_MASK) { // Fahrenheit
139  this->target_temperature = esphome::fahrenheit_to_celsius(esphome::reverse_bits(remote_state & TEMP_MASK) >> 24);
140  } else { // Celsius
141  this->target_temperature = (esphome::reverse_bits(remote_state & TEMP_MASK) >> 24) + TEMP_OFFSET_C;
142  }
143 
144  // Fan Speed
145  if ((remote_state & FAN_MASK) == FAN_LOW) {
147  } else if ((remote_state & FAN_MASK) == FAN_MED) {
149  } else if ((remote_state & FAN_MASK) == FAN_HIGH) {
151  }
152  }
153  this->publish_state();
154 
155  return true;
156 }
157 
158 void Whynter::transmit_(uint32_t value) {
159  ESP_LOGD(TAG, "Sending Whynter code: 0x%02" PRIX32, value);
160 
161  auto transmit = this->transmitter_->transmit();
162  auto *data = transmit.get_data();
163 
164  data->set_carrier_frequency(38000);
165  data->reserve(2 + BITS * 2u);
166 
167  data->item(this->header_high_, this->header_low_);
168 
169  for (uint32_t mask = 1UL << (BITS - 1); mask != 0; mask >>= 1) {
170  if (value & mask) {
171  data->item(this->bit_high_, this->bit_one_low_);
172  } else {
173  data->item(this->bit_high_, this->bit_zero_low_);
174  }
175  }
176  data->mark(this->bit_high_);
177  transmit.perform();
178 }
179 
180 } // namespace whynter
181 } // namespace esphome
The fan mode is set to Low.
Definition: climate_mode.h:54
bool on_receive(remote_base::RemoteReceiveData data) override
Handle received IR Buffer.
Definition: whynter.cpp:102
value_type const & value() const
Definition: optional.h:89
void transmit_(uint32_t value)
Definition: whynter.cpp:158
const uint16_t BITS
Definition: whynter.cpp:9
const uint8_t TEMP_MIN_C
Definition: whynter.h:11
float target_temperature
The target temperature of the climate device.
Definition: climate.h:186
void set_carrier_frequency(uint32_t carrier_frequency)
Definition: remote_base.h:34
const uint32_t COMMAND_MASK
Definition: whynter.cpp:12
const uint8_t TEMP_MAX_C
Definition: whynter.h:12
const uint32_t UNIT_SHIFT
Definition: whynter.cpp:36
The climate device is set to heat to reach the target temperature.
Definition: climate_mode.h:18
const uint32_t POWER_OFF
Definition: whynter.cpp:18
ClimateMode mode
The active mode of the climate device.
Definition: climate.h:173
const uint32_t UNIT_MASK
Definition: whynter.cpp:37
const uint32_t FAN_HIGH
Definition: whynter.cpp:31
The climate device is set to dry/humidity mode.
Definition: climate_mode.h:22
const uint32_t FAN_MED
Definition: whynter.cpp:32
const uint32_t FAN_LOW
Definition: whynter.cpp:33
climate::ClimateMode mode_before_
Definition: whynter.h:49
const uint32_t POWER_SHIFT
Definition: whynter.cpp:16
const uint32_t TEMP_OFFSET_C
Definition: whynter.cpp:41
uint8_t reverse_bits(uint8_t x)
Reverse the order of 8 bits.
Definition: helpers.h:223
const uint32_t MODE_HEAT
Definition: whynter.cpp:25
The climate device is set to cool to reach the target temperature.
Definition: climate_mode.h:16
constexpr float celsius_to_fahrenheit(float value)
Convert degrees Celsius to degrees Fahrenheit.
Definition: helpers.h:474
void transmit_state() override
Transmit via IR the state of this climate controller.
Definition: whynter.cpp:43
const uint32_t MODE_MASK
Definition: whynter.cpp:22
const uint8_t TEMP_MAX_F
Definition: whynter.h:14
RemoteTransmitterBase * transmitter_
Definition: remote_base.h:276
const uint32_t FAN_SHIFT
Definition: whynter.cpp:29
const uint32_t MODE_FAN
Definition: whynter.cpp:23
The climate device is set to heat/cool to reach the target temperature.
Definition: climate_mode.h:14
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition: climate.cpp:395
const uint8_t TEMP_MIN_F
Definition: whynter.h:13
The fan mode is set to High.
Definition: climate_mode.h:58
The climate device is off.
Definition: climate_mode.h:12
const uint32_t COMMAND_CODE
Definition: whynter.cpp:13
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition: climate.h:199
const uint32_t MODE_SHIFT
Definition: whynter.cpp:21
const uint32_t TEMP_MASK
Definition: whynter.cpp:40
const uint32_t MODE_COOL
Definition: whynter.cpp:26
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
const uint32_t MODE_DRY
Definition: whynter.cpp:24
The fan mode is set to Medium.
Definition: climate_mode.h:56
constexpr float fahrenheit_to_celsius(float value)
Convert degrees Fahrenheit to degrees Celsius.
Definition: helpers.h:476
const uint32_t FAN_MASK
Definition: whynter.cpp:30
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 POWER_MASK
Definition: whynter.cpp:17