diff --git a/components/samsung_ac/converions.cpp b/components/samsung_ac/conversions.cpp similarity index 100% rename from components/samsung_ac/converions.cpp rename to components/samsung_ac/conversions.cpp diff --git a/components/samsung_ac/protocol_non_nasa.cpp b/components/samsung_ac/protocol_non_nasa.cpp index a5cdf50e..db3abaab 100644 --- a/components/samsung_ac/protocol_non_nasa.cpp +++ b/components/samsung_ac/protocol_non_nasa.cpp @@ -28,25 +28,73 @@ namespace esphome std::string NonNasaCommand20::to_string() { std::string str; - str += "target_temp:" + std::to_string(target_temp) + ";"; - str += "room_temp:" + std::to_string(room_temp) + ";"; - str += "pipe_in:" + std::to_string(pipe_in) + ";"; - str += "pipe_out:" + std::to_string(pipe_out) + ";"; - str += "power:" + std::to_string(power ? 1 : 0) + ";"; - str += "wind_direction:" + std::to_string((uint8_t)wind_direction) + ";"; - str += "fanspeed:" + std::to_string((uint8_t)fanspeed) + ";"; - str += "mode:" + long_to_hex((uint8_t)mode) + ";"; + str += "target_temp:" + std::to_string(target_temp) + "; "; + str += "room_temp:" + std::to_string(room_temp) + "; "; + str += "pipe_in:" + std::to_string(pipe_in) + "; "; + str += "pipe_out:" + std::to_string(pipe_out) + "; "; + str += "power:" + std::to_string(power ? 1 : 0) + "; "; + str += "wind_direction:" + std::to_string((uint8_t)wind_direction) + "; "; + str += "fanspeed:" + std::to_string((uint8_t)fanspeed) + "; "; + str += "mode:" + long_to_hex((uint8_t)mode); + return str; + } + + std::string NonNasaCommandC0::to_string() + { + std::string str; + str += "ou_operation_mode:" + long_to_hex((uint8_t)outdoor_unit_operation_mode) + "; "; + str += "ou_4way_valve:" + std::to_string(outdoor_unit_4_way_valve ? 1 : 0) + "; "; + str += "ou_hot_gas_bypass:" + std::to_string(outdoor_unit_hot_gas_bypass ? 1 : 0) + "; "; + str += "ou_compressor:" + std::to_string(outdoor_unit_compressor ? 1 : 0) + "; "; + str += "ou_ac_fan:" + std::to_string(outdoor_unit_ac_fan ? 1 : 0) + "; "; + str += "ou_outdoor_temp[°C]:" + std::to_string(outdoor_unit_outdoor_temp_c) + "; "; + str += "ou_discharge_temp[°C]:" + std::to_string(outdoor_unit_discharge_temp_c) + "; "; + str += "ou_condenser_mid_temp[°C]:" + std::to_string(outdoor_unit_condenser_mid_temp_c); + return str; + } + + std::string NonNasaCommandC1::to_string() + { + std::string str; + str += "ou_sump_temp[°C]:" + std::to_string(outdoor_unit_sump_temp_c); + return str; + } + + std::string NonNasaCommandF0::to_string() + { + std::string str; + str += "ou_freeze_protection:" + std::to_string(outdoor_unit_freeze_protection ? 1 : 0) + "; "; + str += "ou_heating_overload:" + std::to_string(outdoor_unit_heating_overload ? 1 : 0) + "; "; + str += "ou_defrost_control:" + std::to_string(outdoor_unit_defrost_control ? 1 : 0) + "; "; + str += "ou_discharge_protection:" + std::to_string(outdoor_unit_discharge_protection ? 1 : 0) + "; "; + str += "ou_current_control:" + std::to_string(outdoor_unit_current_control ? 1 : 0) + "; "; + str += "inverter_order_frequency[Hz]:" + std::to_string(inverter_order_frequency_hz) + "; "; + str += "inverter_target_frequency[Hz]:" + std::to_string(inverter_target_frequency_hz) + "; "; + str += "inverter_current_frequency[Hz]:" + std::to_string(inverter_current_frequency_hz) + "; "; + str += "ou_bldc_fan:" + std::to_string(outdoor_unit_bldc_fan ? 1 : 0) + "; "; + str += "ou_error_code:" + long_to_hex((uint8_t)outdoor_unit_error_code); + return str; + } + + std::string NonNasaCommandF1::to_string() + { + std::string str; + str += "Electronic Expansion Valves: "; + str += "EEV_A:" + std::to_string(outdoor_unit_EEV_A) + "; "; + str += "EEV_B:" + std::to_string(outdoor_unit_EEV_B) + "; "; + str += "EEV_C:" + std::to_string(outdoor_unit_EEV_C) + "; "; + str += "EEV_D:" + std::to_string(outdoor_unit_EEV_D); return str; } std::string NonNasaCommandF3::to_string() { std::string str; - str += "inverter_max_frequency[Hz]:" + std::to_string(inverter_max_frequency_hz) + ";"; - str += "inverter_total_capacity_requirement[kW]:" + std::to_string(inverter_total_capacity_requirement_kw) + ";"; - str += "inverter_current[ADC]:" + std::to_string(inverter_current_a) + ";"; - str += "inverter_voltage[VDC]:" + std::to_string(inverter_voltage_v) + ";"; - str += "inverter_power[W]:" + std::to_string(inverter_power_w) + ";"; + str += "inverter_max_frequency[Hz]:" + std::to_string(inverter_max_frequency_hz) + "; "; + str += "inverter_total_capacity_requirement[kW]:" + std::to_string(inverter_total_capacity_requirement_kw) + "; "; + str += "inverter_current[ADC]:" + std::to_string(inverter_current_a) + "; "; + str += "inverter_voltage[VDC]:" + std::to_string(inverter_voltage_v) + "; "; + str += "inverter_power[W]:" + std::to_string(inverter_power_w); return str; } @@ -64,19 +112,34 @@ namespace esphome str += "command20:{" + command20.to_string() + "}"; break; } + case NonNasaCommand::CmdC0: + { + str += "commandC0:{" + commandC0.to_string() + "}"; + break; + } + case NonNasaCommand::CmdC1: + { + str += "commandC1:{" + commandC1.to_string() + "}"; + break; + } case NonNasaCommand::CmdC6: { str += "commandC6:{" + commandC6.to_string() + "}"; break; } - case NonNasaCommand::CmdF3: + case NonNasaCommand::CmdF0: { - str += "commandF3:{" + commandF3.to_string() + "}"; + str += "commandF0:{" + commandF0.to_string() + "}"; break; } - case NonNasaCommand::CmdF8: + case NonNasaCommand::CmdF1: { - str += "commandF8:{" + commandF8.to_string() + "}"; + str += "commandF1:{" + commandF1.to_string() + "}"; + break; + } + case NonNasaCommand::CmdF3: + { + str += "commandF3:{" + commandF3.to_string() + "}"; break; } default: @@ -131,11 +194,50 @@ namespace esphome return DecodeResult::Ok; } + case NonNasaCommand::CmdC0: // outdoor unit data + { + commandC0.outdoor_unit_operation_mode = data[4]; // modes need to be specified + commandC0.outdoor_unit_4_way_valve = data[6] & 0b10000000; + commandC0.outdoor_unit_hot_gas_bypass = data[6] & 0b00100000; + commandC0.outdoor_unit_compressor = data[6] & 0b00000100; + commandC0.outdoor_unit_ac_fan = data[7] & 0b00000011; + commandC0.outdoor_unit_outdoor_temp_c = data[8] - 55; + commandC0.outdoor_unit_discharge_temp_c = data[10] - 55; + commandC0.outdoor_unit_condenser_mid_temp_c = data[11] - 55; + return DecodeResult::Ok; + } + case NonNasaCommand::CmdC1: // outdoor unit data + { + commandC1.outdoor_unit_sump_temp_c = data[8] - 55; + return DecodeResult::Ok; + } case NonNasaCommand::CmdC6: { commandC6.control_status = data[4]; return DecodeResult::Ok; } + case NonNasaCommand::CmdF0: // outdoor unit data + { + commandF0.outdoor_unit_freeze_protection = data[4] & 0b10000000; + commandF0.outdoor_unit_heating_overload = data[4] & 0b01000000; + commandF0.outdoor_unit_defrost_control = data[4] & 0b00100000; + commandF0.outdoor_unit_discharge_protection = data[4] & 0b00010000; + commandF0.outdoor_unit_current_control = data[4] & 0b00001000; + commandF0.inverter_order_frequency_hz = data[5]; + commandF0.inverter_target_frequency_hz = data[6]; + commandF0.inverter_current_frequency_hz = data[7]; + commandF0.outdoor_unit_bldc_fan = data[8] & 0b00000011; // not sure if correct, i have no ou with BLDC-fan + commandF0.outdoor_unit_error_code = data[10]; + return DecodeResult::Ok; + } + case NonNasaCommand::CmdF1: // outdoor unit eev-values + { + commandF1.outdoor_unit_EEV_A = (data[4] * 256) + data[5]; + commandF1.outdoor_unit_EEV_B = (data[6] * 256) + data[7]; + commandF1.outdoor_unit_EEV_C = (data[8] * 256) + data[9]; + commandF1.outdoor_unit_EEV_D = (data[10] * 256) + data[11]; + return DecodeResult::Ok; + } case NonNasaCommand::CmdF3: // power consumption { // Maximum frequency for Inverter (compressor-motor of outdoor-unit) in Hz @@ -323,12 +425,12 @@ namespace esphome void NonNasaProtocol::publish_altmode_message(MessageTarget *target, const std::string &address, AltMode value) { - // TODO + ESP_LOGW(TAG, "change altmode is currently not implemented"); } void NonNasaProtocol::publish_swing_mode_message(MessageTarget *target, const std::string &address, SwingMode value) { - // TODO + ESP_LOGW(TAG, "change swingmode is currently not implemented"); } void NonNasaProtocol::publish_request(MessageTarget *target, const std::string &address, ProtocolRequest &request) @@ -389,6 +491,17 @@ namespace esphome return nonpacket_.decode(data); } + void send_requests(MessageTarget *target, uint8_t delay_ms) + { + while (nonnasa_requests.size() > 0) + { + delay(delay_ms); + auto data = nonnasa_requests.front().encode(); + target->publish_data(data); + nonnasa_requests.pop(); + } + } + void process_non_nasa_packet(MessageTarget *target) { if (debug_log_packets) @@ -415,19 +528,20 @@ namespace esphome else if (nonpacket_.cmd == NonNasaCommand::CmdF8) { // After cmd F8 (src:c8 dst:f0) is a lage gap in communication, time to send data - if (nonpacket_.src == "c8" && nonpacket_.dst == "f0") { - while (nonnasa_requests.size() > 0) - { - auto data = nonnasa_requests.front().encode(); - // the communication needs a delay from cmdf8 to send the data. - // series of test-delay-times: 1ms: no reaction, 7ms reactions half the time, 10ms very often a reaction (95%) -> delay on 20ms should be safe - // the gap is around ~300ms - delay(20); - target->publish_data(data); - nonnasa_requests.pop(); - } + // the communication needs a delay from cmdf8 to send the data. + // series of test-delay-times: 1ms: no reaction, 7ms reactions half the time, 10ms very often a reaction (95%) -> delay on 20ms should be safe + // the gap is around ~300ms + send_requests(target, 20); + } + } + else if (nonpacket_.cmd == NonNasaCommand::CmdC6) + { + // If the control status is set we can send also + if (nonpacket_.src == "c8" && nonpacket_.dst == "d0" && nonpacket_.commandC6.control_status == true) + { + send_requests(target, 20); } } } diff --git a/components/samsung_ac/protocol_non_nasa.h b/components/samsung_ac/protocol_non_nasa.h index 27a3e80e..b8df5033 100644 --- a/components/samsung_ac/protocol_non_nasa.h +++ b/components/samsung_ac/protocol_non_nasa.h @@ -37,7 +37,7 @@ namespace esphome Stop = 31 }; - struct NonNasaCommand20 + struct NonNasaCommand20 // from indoor units { uint8_t target_temp = 0; uint8_t room_temp = 0; @@ -53,6 +53,27 @@ namespace esphome std::string to_string(); }; + struct NonNasaCommandC0 // from outdoor unit + { + uint8_t outdoor_unit_operation_mode = 0; + bool outdoor_unit_4_way_valve = false; + bool outdoor_unit_hot_gas_bypass = false; + bool outdoor_unit_compressor = false; + bool outdoor_unit_ac_fan = false; + uint8_t outdoor_unit_outdoor_temp_c = 0; + uint8_t outdoor_unit_discharge_temp_c = 0; + uint8_t outdoor_unit_condenser_mid_temp_c = 0; + + std::string to_string(); + }; + + struct NonNasaCommandC1 // from outdoor unit + { + uint8_t outdoor_unit_sump_temp_c = 0; + + std::string to_string(); + }; + struct NonNasaCommandC6 { bool control_status = false; @@ -62,7 +83,34 @@ namespace esphome }; }; - struct NonNasaCommandF3 + struct NonNasaCommandF0 // from outdoor unit + { + bool outdoor_unit_freeze_protection = false; + bool outdoor_unit_heating_overload = false; + bool outdoor_unit_defrost_control = false; + bool outdoor_unit_discharge_protection = false; + bool outdoor_unit_current_control = false; + uint8_t inverter_order_frequency_hz = 0; + uint8_t inverter_target_frequency_hz = 0; + uint8_t inverter_current_frequency_hz = 0; + bool outdoor_unit_bldc_fan = false; + uint8_t outdoor_unit_error_code = 0; + + std::string to_string(); + }; + + struct NonNasaCommandF1 // from outdoor unit + { + uint16_t outdoor_unit_EEV_A = 0; + uint16_t outdoor_unit_EEV_B = 0; + uint16_t outdoor_unit_EEV_C = 0; + uint16_t outdoor_unit_EEV_D = 0; + + std::string to_string(); + }; + + + struct NonNasaCommandF3 // from outdoor unit { uint8_t inverter_max_frequency_hz = 0; float inverter_total_capacity_requirement_kw = 0; @@ -88,8 +136,12 @@ namespace esphome enum class NonNasaCommand : uint8_t { Cmd20 = 0x20, + CmdC0 = 0xc0, + CmdC1 = 0xc1, CmdC6 = 0xc6, - CmdF3 = 0xf3, + CmdF0 = 0xf0, + CmdF1 = 0xf1, + CmdF3 = 0xf3, CmdF8 = 0xF8, }; @@ -107,7 +159,11 @@ namespace esphome union { NonNasaCommand20 command20; + NonNasaCommandC0 commandC0; + NonNasaCommandC1 commandC1; NonNasaCommandC6 commandC6; + NonNasaCommandF0 commandF0; + NonNasaCommandF1 commandF1; NonNasaCommandF3 commandF3; NonNasaCommandRaw commandF8; // Unknown structure for now NonNasaCommandRaw commandRaw;