ESPHome 2025.5.0
Loading...
Searching...
No Matches
fujitsu_general.cpp
Go to the documentation of this file.
1#include "fujitsu_general.h"
2
3namespace esphome {
4namespace fujitsu_general {
5
6// bytes' bits are reversed for fujitsu, so nibbles are ordered 1, 0, 3, 2, 5, 4, etc...
7
8#define SET_NIBBLE(message, nibble, value) \
9 ((message)[(nibble) / 2] |= ((value) &0b00001111) << (((nibble) % 2) ? 0 : 4))
10#define GET_NIBBLE(message, nibble) (((message)[(nibble) / 2] >> (((nibble) % 2) ? 0 : 4)) & 0b00001111)
11
12static const char *const TAG = "fujitsu_general.climate";
13
14// Common header
16const uint8_t FUJITSU_GENERAL_COMMON_BYTE0 = 0x14;
17const uint8_t FUJITSU_GENERAL_COMMON_BYTE1 = 0x63;
18const uint8_t FUJITSU_GENERAL_COMMON_BYTE2 = 0x00;
19const uint8_t FUJITSU_GENERAL_COMMON_BYTE3 = 0x10;
20const uint8_t FUJITSU_GENERAL_COMMON_BYTE4 = 0x10;
22
23// State message - temp & fan etc.
26
27// Util messages - off & eco etc.
32
33// State header
36
37// State footer
39
40// Temperature
42
43// Power on
45const uint8_t FUJITSU_GENERAL_POWER_OFF = 0x00;
46const uint8_t FUJITSU_GENERAL_POWER_ON = 0x01;
47
48// Mode
49const uint8_t FUJITSU_GENERAL_MODE_NIBBLE = 19;
50const uint8_t FUJITSU_GENERAL_MODE_AUTO = 0x00;
51const uint8_t FUJITSU_GENERAL_MODE_COOL = 0x01;
52const uint8_t FUJITSU_GENERAL_MODE_DRY = 0x02;
53const uint8_t FUJITSU_GENERAL_MODE_FAN = 0x03;
54const uint8_t FUJITSU_GENERAL_MODE_HEAT = 0x04;
55// const uint8_t FUJITSU_GENERAL_MODE_10C = 0x0B;
56
57// Swing
59const uint8_t FUJITSU_GENERAL_SWING_NONE = 0x00;
60const uint8_t FUJITSU_GENERAL_SWING_VERTICAL = 0x01;
62const uint8_t FUJITSU_GENERAL_SWING_BOTH = 0x03;
63
64// Fan
65const uint8_t FUJITSU_GENERAL_FAN_NIBBLE = 21;
66const uint8_t FUJITSU_GENERAL_FAN_AUTO = 0x00;
67const uint8_t FUJITSU_GENERAL_FAN_HIGH = 0x01;
68const uint8_t FUJITSU_GENERAL_FAN_MEDIUM = 0x02;
69const uint8_t FUJITSU_GENERAL_FAN_LOW = 0x03;
70const uint8_t FUJITSU_GENERAL_FAN_SILENT = 0x04;
71
72// TODO Outdoor Unit Low Noise
73// const uint8_t FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14 = 0xA0;
74// const uint8_t FUJITSU_GENERAL_STATE_BYTE14 = 0x20;
75
76const uint16_t FUJITSU_GENERAL_HEADER_MARK = 3300;
77const uint16_t FUJITSU_GENERAL_HEADER_SPACE = 1600;
78
79const uint16_t FUJITSU_GENERAL_BIT_MARK = 420;
80const uint16_t FUJITSU_GENERAL_ONE_SPACE = 1200;
81const uint16_t FUJITSU_GENERAL_ZERO_SPACE = 420;
82
83const uint16_t FUJITSU_GENERAL_TRL_MARK = 420;
84const uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000;
85
86const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY = 38000;
87
89 if (this->mode == climate::CLIMATE_MODE_OFF) {
90 this->transmit_off_();
91 return;
92 }
93
94 ESP_LOGV(TAG, "Transmit state");
95
96 uint8_t remote_state[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH] = {0};
97
98 // Common message header
99 remote_state[0] = FUJITSU_GENERAL_COMMON_BYTE0;
100 remote_state[1] = FUJITSU_GENERAL_COMMON_BYTE1;
101 remote_state[2] = FUJITSU_GENERAL_COMMON_BYTE2;
102 remote_state[3] = FUJITSU_GENERAL_COMMON_BYTE3;
103 remote_state[4] = FUJITSU_GENERAL_COMMON_BYTE4;
104 remote_state[5] = FUJITSU_GENERAL_MESSAGE_TYPE_STATE;
105 remote_state[6] = FUJITSU_GENERAL_STATE_HEADER_BYTE0;
106 remote_state[7] = FUJITSU_GENERAL_STATE_HEADER_BYTE1;
107
108 // unknown, does not appear to change with any remote settings
109 remote_state[14] = FUJITSU_GENERAL_STATE_FOOTER_BYTE0;
110
111 // Set temperature
112 uint8_t temperature_clamped =
113 (uint8_t) roundf(clamp<float>(this->target_temperature, FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX));
114 uint8_t temperature_offset = temperature_clamped - FUJITSU_GENERAL_TEMP_MIN;
115 SET_NIBBLE(remote_state, FUJITSU_GENERAL_TEMPERATURE_NIBBLE, temperature_offset);
116
117 // Set power on
118 if (!this->power_) {
120 }
121
122 // Set mode
123 switch (this->mode) {
126 break;
129 break;
131 SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_DRY);
132 break;
134 SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_FAN);
135 break;
137 default:
139 break;
140 // TODO: CLIMATE_MODE_10C is missing from esphome
141 }
142
143 // Set fan
144 switch (this->fan_mode.value()) {
146 SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_HIGH);
147 break;
150 break;
152 SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_LOW);
153 break;
156 break;
158 default:
159 SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_AUTO);
160 break;
161 }
162
163 // Set swing
164 switch (this->swing_mode) {
167 break;
170 break;
173 break;
175 default:
177 break;
178 }
179
180 // TODO: missing support for outdoor unit low noise
181 // remote_state[14] = (byte) remote_state[14] | FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14;
182
183 remote_state[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH - 1] = this->checksum_state_(remote_state);
184
186
187 this->power_ = true;
188}
189
191 ESP_LOGV(TAG, "Transmit off");
192
193 uint8_t remote_state[FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH] = {0};
194
195 remote_state[0] = FUJITSU_GENERAL_COMMON_BYTE0;
196 remote_state[1] = FUJITSU_GENERAL_COMMON_BYTE1;
197 remote_state[2] = FUJITSU_GENERAL_COMMON_BYTE2;
198 remote_state[3] = FUJITSU_GENERAL_COMMON_BYTE3;
199 remote_state[4] = FUJITSU_GENERAL_COMMON_BYTE4;
200 remote_state[5] = FUJITSU_GENERAL_MESSAGE_TYPE_OFF;
201 remote_state[6] = this->checksum_util_(remote_state);
202
204
205 this->power_ = false;
206}
207
208void FujitsuGeneralClimate::transmit_(uint8_t const *message, uint8_t length) {
209 ESP_LOGV(TAG, "Transmit message length %d", length);
210
211 auto transmit = this->transmitter_->transmit();
212 auto *data = transmit.get_data();
213
215
216 // Header
217 data->mark(FUJITSU_GENERAL_HEADER_MARK);
218 data->space(FUJITSU_GENERAL_HEADER_SPACE);
219
220 // Data
221 for (uint8_t i = 0; i < length; ++i) {
222 const uint8_t byte = message[i];
223 for (uint8_t mask = 0b00000001; mask > 0; mask <<= 1) { // write from right to left
224 data->mark(FUJITSU_GENERAL_BIT_MARK);
225 bool bit = byte & mask;
227 }
228 }
229
230 // Footer
231 data->mark(FUJITSU_GENERAL_TRL_MARK);
232 data->space(FUJITSU_GENERAL_TRL_SPACE);
233
234 transmit.perform();
235}
236
237uint8_t FujitsuGeneralClimate::checksum_state_(uint8_t const *message) {
238 uint8_t checksum = 0;
239 for (uint8_t i = 7; i < FUJITSU_GENERAL_STATE_MESSAGE_LENGTH - 1; ++i) {
240 checksum += message[i];
241 }
242 return 256 - checksum;
243}
244
245uint8_t FujitsuGeneralClimate::checksum_util_(uint8_t const *message) { return 255 - message[5]; }
246
248 ESP_LOGV(TAG, "Received IR message");
249
250 // Validate header
252 ESP_LOGV(TAG, "Header fail");
253 return false;
254 }
255
256 uint8_t recv_message[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH] = {0};
257
258 // Read header
259 for (uint8_t byte = 0; byte < FUJITSU_GENERAL_COMMON_LENGTH; ++byte) {
260 // Read bit
261 for (uint8_t bit = 0; bit < 8; ++bit) {
263 recv_message[byte] |= 1 << bit; // read from right to left
265 ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit);
266 return false;
267 }
268 }
269 }
270
271 const uint8_t recv_message_type = recv_message[FUJITSU_GENERAL_MESSAGE_TYPE_BYTE];
272 uint8_t recv_message_length;
273
274 switch (recv_message_type) {
276 ESP_LOGV(TAG, "Received state message");
277 recv_message_length = FUJITSU_GENERAL_STATE_MESSAGE_LENGTH;
278 break;
282 ESP_LOGV(TAG, "Received util message");
283 recv_message_length = FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH;
284 break;
285 default:
286 ESP_LOGV(TAG, "Unknown message type %X", recv_message_type);
287 return false;
288 }
289
290 // Read message body
291 for (uint8_t byte = FUJITSU_GENERAL_COMMON_LENGTH; byte < recv_message_length; ++byte) {
292 for (uint8_t bit = 0; bit < 8; ++bit) {
294 recv_message[byte] |= 1 << bit; // read from right to left
296 ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit);
297 return false;
298 }
299 }
300 }
301
302 for (uint8_t byte = 0; byte < recv_message_length; ++byte) {
303 ESP_LOGVV(TAG, "%02X", recv_message[byte]);
304 }
305
306 const uint8_t recv_checksum = recv_message[recv_message_length - 1];
307 uint8_t calculated_checksum;
308 if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_STATE) {
309 calculated_checksum = this->checksum_state_(recv_message);
310 } else {
311 calculated_checksum = this->checksum_util_(recv_message);
312 }
313
314 if (recv_checksum != calculated_checksum) {
315 ESP_LOGV(TAG, "Checksum fail - expected %X - got %X", calculated_checksum, recv_checksum);
316 return false;
317 }
318
319 if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_STATE) {
320 const uint8_t recv_tempertature = GET_NIBBLE(recv_message, FUJITSU_GENERAL_TEMPERATURE_NIBBLE);
321 const uint8_t offset_temperature = recv_tempertature + FUJITSU_GENERAL_TEMP_MIN;
322 this->target_temperature = offset_temperature;
323 ESP_LOGV(TAG, "Received temperature %d", offset_temperature);
324
325 const uint8_t recv_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_MODE_NIBBLE);
326 ESP_LOGV(TAG, "Received mode %X", recv_mode);
327 switch (recv_mode) {
330 break;
333 break;
336 break;
339 break;
341 default:
342 // TODO: CLIMATE_MODE_10C is missing from esphome
344 break;
345 }
346
347 const uint8_t recv_fan_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_FAN_NIBBLE);
348 ESP_LOGV(TAG, "Received fan mode %X", recv_fan_mode);
349 switch (recv_fan_mode) {
352 break;
355 break;
358 break;
361 break;
363 default:
365 break;
366 }
367
368 const uint8_t recv_swing_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_SWING_NIBBLE);
369 ESP_LOGV(TAG, "Received swing mode %X", recv_swing_mode);
370 switch (recv_swing_mode) {
373 break;
376 break;
379 break;
381 default:
383 }
384
385 this->power_ = true;
386 }
387
388 else if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_OFF) {
389 ESP_LOGV(TAG, "Received off message");
391 this->power_ = false;
392 }
393
394 else {
395 ESP_LOGV(TAG, "Received unsupprted message type %X", recv_message_type);
396 return false;
397 }
398
399 this->publish_state();
400 return true;
401}
402
403} // namespace fujitsu_general
404} // namespace esphome
uint8_t checksum
Definition bl0906.h:3
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
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:202
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:395
uint8_t checksum_state_(uint8_t const *message)
Calculate checksum for a state message.
void transmit_state() override
Transmit via IR the state of this climate controller.
uint8_t checksum_util_(uint8_t const *message)
Calculate cecksum for a util message.
bool on_receive(remote_base::RemoteReceiveData data) override
Parse incoming message.
void transmit_off_()
Transmit via IR power off command.
void transmit_(uint8_t const *message, uint8_t length)
Transmit message as IR pulses.
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_QUIET
The fan mode is set to Quiet.
@ CLIMATE_FAN_HIGH
The fan mode is set to High.
const uint8_t FUJITSU_GENERAL_FAN_NIBBLE
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_STATE
const uint16_t FUJITSU_GENERAL_ZERO_SPACE
const uint8_t FUJITSU_GENERAL_POWER_ON
const uint8_t FUJITSU_GENERAL_FAN_AUTO
const uint8_t FUJITSU_GENERAL_SWING_NONE
const uint8_t FUJITSU_GENERAL_FAN_SILENT
const uint16_t FUJITSU_GENERAL_HEADER_MARK
const uint16_t FUJITSU_GENERAL_TRL_SPACE
const uint16_t FUJITSU_GENERAL_BIT_MARK
const uint8_t FUJITSU_GENERAL_POWER_ON_NIBBLE
const uint8_t FUJITSU_GENERAL_FAN_HIGH
const uint8_t FUJITSU_GENERAL_COMMON_BYTE4
const uint8_t FUJITSU_GENERAL_SWING_BOTH
const uint16_t FUJITSU_GENERAL_HEADER_SPACE
const uint8_t FUJITSU_GENERAL_POWER_OFF
const uint16_t FUJITSU_GENERAL_ONE_SPACE
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_ECONOMY
const uint8_t FUJITSU_GENERAL_TEMPERATURE_NIBBLE
const uint8_t FUJITSU_GENERAL_COMMON_LENGTH
const uint8_t FUJITSU_GENERAL_MODE_DRY
const uint8_t FUJITSU_GENERAL_COMMON_BYTE1
const uint8_t FUJITSU_GENERAL_FAN_MEDIUM
const uint8_t FUJITSU_GENERAL_STATE_HEADER_BYTE1
const uint8_t FUJITSU_GENERAL_MODE_FAN
const uint8_t FUJITSU_GENERAL_MODE_AUTO
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_BYTE
const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY
const uint8_t FUJITSU_GENERAL_SWING_VERTICAL
const uint8_t FUJITSU_GENERAL_STATE_MESSAGE_LENGTH
const uint16_t FUJITSU_GENERAL_TRL_MARK
const uint8_t FUJITSU_GENERAL_SWING_HORIZONTAL
const uint8_t FUJITSU_GENERAL_MODE_HEAT
const uint8_t FUJITSU_GENERAL_MODE_NIBBLE
const uint8_t FUJITSU_GENERAL_STATE_HEADER_BYTE0
const uint8_t FUJITSU_GENERAL_TEMP_MAX
const uint8_t FUJITSU_GENERAL_COMMON_BYTE2
const uint8_t FUJITSU_GENERAL_STATE_FOOTER_BYTE0
const uint8_t FUJITSU_GENERAL_TEMP_MIN
const uint8_t FUJITSU_GENERAL_MODE_COOL
const uint8_t FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH
const uint8_t FUJITSU_GENERAL_FAN_LOW
const uint8_t FUJITSU_GENERAL_COMMON_BYTE0
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_OFF
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_NUDGE
const uint8_t FUJITSU_GENERAL_SWING_NIBBLE
const uint8_t FUJITSU_GENERAL_COMMON_BYTE3
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint16_t length
Definition tt21100.cpp:0