7static const char *
const TAG =
"mitsubishi_cn105.driver";
9static constexpr uint32_t WRITE_TIMEOUT_MS = 2000;
11static constexpr size_t REQUEST_PAYLOAD_LEN = 0x10;
12static constexpr size_t HEADER_LEN = 5;
13static constexpr uint8_t PREAMBLE = 0xFC;
14static constexpr uint8_t HEADER_BYTE_1 = 0x01;
15static constexpr uint8_t HEADER_BYTE_2 = 0x30;
17static constexpr uint8_t PACKET_TYPE_CONNECT_REQUEST = 0x5A;
18static constexpr uint8_t PACKET_TYPE_CONNECT_RESPONSE = 0x7A;
19static constexpr std::array<uint8_t, 2> CONNECT_REQUEST_PAYLOAD = {0xCA, 0x01};
21static constexpr uint8_t PACKET_TYPE_STATUS_REQUEST = 0x42;
22static constexpr uint8_t PACKET_TYPE_STATUS_RESPONSE = 0x62;
23static constexpr uint8_t STATUS_MSG_SETTINGS = 0x02;
24static constexpr uint8_t STATUS_MSG_ROOM_TEMP = 0x03;
25static constexpr std::array<uint8_t, 2> STATUS_MSG_TYPES = {STATUS_MSG_SETTINGS, STATUS_MSG_ROOM_TEMP};
27static constexpr uint8_t
checksum(
const uint8_t *bytes,
size_t length) {
28 return static_cast<uint8_t
>(0xFC - std::accumulate(bytes, bytes +
length, uint8_t{0}));
31template<std::
size_t PayloadSize>
32static constexpr auto make_packet(uint8_t
type,
const std::array<uint8_t, PayloadSize> &payload) {
33 const size_t full_len = PayloadSize + HEADER_LEN + 1;
34 std::array<uint8_t, full_len> packet{PREAMBLE,
type, HEADER_BYTE_1, HEADER_BYTE_2,
static_cast<uint8_t
>(PayloadSize)};
35 std::copy_n(payload.begin(), PayloadSize, packet.begin() + HEADER_LEN);
36 packet.back() =
checksum(packet.data(), packet.size() - 1);
40static float decode_temperature(
int temp_a,
int temp_b,
int delta) {
41 return temp_b != 0 ? (temp_b - 128) / 2.0f : delta + temp_a;
44static constexpr auto CONNECT_PACKET = make_packet(PACKET_TYPE_CONNECT_REQUEST, CONNECT_REQUEST_PAYLOAD);
157 std::array<uint8_t, REQUEST_PAYLOAD_LEN> payload = {STATUS_MSG_TYPES[this->
status_msg_index_]};
158 this->
send_packet_(make_packet(PACKET_TYPE_STATUS_REQUEST, payload));
167 uint8_t watchdog = 64;
169 uint8_t &value = this->read_buffer_[this->read_pos_];
171 ESP_LOGW(TAG,
"UART read failed while data available");
175 switch (++this->read_pos_) {
177 if (value != PREAMBLE) {
186 if (value != HEADER_BYTE_1) {
192 if (value != HEADER_BYTE_2) {
198 static_assert(READ_BUFFER_SIZE > HEADER_LEN);
199 if (this->read_buffer_[HEADER_LEN - 1] >= READ_BUFFER_SIZE - HEADER_LEN) {
208 const size_t len_without_checksum = HEADER_LEN +
static_cast<size_t>(this->read_buffer_[HEADER_LEN - 1]);
209 if (this->read_pos_ <= len_without_checksum) {
213 if (
checksum(this->read_buffer_, len_without_checksum) != value) {
218 bool processed = this->
process_rx_packet_(this->read_buffer_[1], this->read_buffer_ + HEADER_LEN,
219 len_without_checksum - HEADER_LEN);
229 case PACKET_TYPE_CONNECT_RESPONSE:
233 case PACKET_TYPE_STATUS_RESPONSE:
237 ESP_LOGVV(TAG,
"RX unknown packet type 0x%02X",
type);
244 ESP_LOGVV(TAG,
"RX status packet too short");
248 const auto previous = this->
status_;
249 const auto msg_type = payload[0];
263 case STATUS_MSG_SETTINGS:
266 case STATUS_MSG_ROOM_TEMP:
270 ESP_LOGVV(TAG,
"RX unsupported status msg type 0x%02X", msg_type);
277 ESP_LOGVV(TAG,
"RX settings payload too short");
289 ESP_LOGVV(TAG,
"RX room temperature payload too short");
303#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
312 return LOG_STR(
"Not connected");
314 return LOG_STR(
"Connecting");
316 return LOG_STR(
"Connected");
318 return LOG_STR(
"UpdatingStatus");
320 return LOG_STR(
"StatusUpdated");
322 return LOG_STR(
"ScheduleNextStatusUpdate");
324 return LOG_STR(
"WaitingForScheduledStatusUpdate");
326 return LOG_STR(
"ReadTimeout");
328 return LOG_STR(
"Unknown");
void cancel_waiting_and_transition_to_(State state)
static void dump_buffer_vv(const char *prefix, const uint8_t *data, size_t len)
bool process_status_packet_(const uint8_t *payload, size_t len)
uint8_t status_msg_index_
void reset_read_position_and_dump_buffer_(const char *prefix)
static bool should_transition(State from, State to)
void set_state_(State new_state)
bool process_rx_packet_(uint8_t type, const uint8_t *payload, size_t len)
bool is_status_initialized() const
uart::UARTDevice & device_
void did_transition_(State to)
std::optional< uint32_t > status_update_start_ms_
@ WAITING_FOR_SCHEDULED_STATUS_UPDATE
@ SCHEDULE_NEXT_STATUS_UPDATE
std::optional< uint32_t > write_timeout_start_ms_
static const LogString * state_to_string(State state)
bool parse_status_settings_(const uint8_t *payload, size_t len)
uint32_t update_interval_ms_
void send_packet_(const uint8_t *packet, size_t len)
bool parse_status_payload_(uint8_t msg_type, const uint8_t *payload, size_t len)
bool read_incoming_bytes_()
bool parse_status_room_temperature_(const uint8_t *payload, size_t len)
bool read_byte(uint8_t *data)
void write_array(const uint8_t *data, size_t len)
uint32_t get_loop_time_ms()
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".