[tcl112][zhlt01][heatpumpir][fujitsu_general] Send and read IR frame bytes with the remote_base helpers (#21168)

This commit is contained in:
J. Nick Koston
2026-10-20 08:44:13 -04:00
committed by GitHub
parent 74e1057268
commit 7719179dcd
5 changed files with 34 additions and 90 deletions
@@ -66,9 +66,8 @@ constexpr uint8_t FUJITSU_GENERAL_FAN_SILENT = 0x04;
constexpr uint16_t FUJITSU_GENERAL_HEADER_MARK = 3300;
constexpr uint16_t FUJITSU_GENERAL_HEADER_SPACE = 1600;
constexpr uint16_t FUJITSU_GENERAL_BIT_MARK = 420;
constexpr uint16_t FUJITSU_GENERAL_ONE_SPACE = 1200;
constexpr uint16_t FUJITSU_GENERAL_ZERO_SPACE = 420;
static constexpr remote_base::BitTimings FUJITSU_GENERAL_BIT_TIMINGS{
.bit_mark = 420, .one_space = 1200, .zero_space = 420};
constexpr uint16_t FUJITSU_GENERAL_TRL_MARK = 420;
constexpr uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000;
@@ -207,14 +206,7 @@ void FujitsuGeneralClimate::transmit_(uint8_t const *message, uint8_t length) {
data->space(FUJITSU_GENERAL_HEADER_SPACE);
// Data
for (uint8_t i = 0; i < length; ++i) {
const uint8_t byte = message[i];
for (uint8_t mask = 0b00000001; mask > 0; mask <<= 1) { // write from right to left
data->mark(FUJITSU_GENERAL_BIT_MARK);
bool bit = byte & mask;
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
}
}
data->item_bytes<FUJITSU_GENERAL_BIT_TIMINGS>(message, length);
// Footer
data->mark(FUJITSU_GENERAL_TRL_MARK);
@@ -284,19 +276,12 @@ bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) {
return false;
}
uint8_t recv_message[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH] = {0};
uint8_t recv_message[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH];
// Read header
for (uint8_t byte = 0; byte < FUJITSU_GENERAL_COMMON_LENGTH; ++byte) {
// Read bit
for (uint8_t bit = 0; bit < 8; ++bit) {
if (data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ONE_SPACE)) {
recv_message[byte] |= 1 << bit; // read from right to left
} else if (!data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ZERO_SPACE)) {
ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit);
return false;
}
}
if (!data.expect_bytes<FUJITSU_GENERAL_BIT_TIMINGS>(recv_message, FUJITSU_GENERAL_COMMON_LENGTH)) {
ESP_LOGV(TAG, "Header bytes fail");
return false;
}
const uint8_t recv_message_type = recv_message[FUJITSU_GENERAL_MESSAGE_TYPE_BYTE];
@@ -319,15 +304,10 @@ bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) {
}
// Read message body
for (uint8_t byte = FUJITSU_GENERAL_COMMON_LENGTH; byte < recv_message_length; ++byte) {
for (uint8_t bit = 0; bit < 8; ++bit) {
if (data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ONE_SPACE)) {
recv_message[byte] |= 1 << bit; // read from right to left
} else if (!data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ZERO_SPACE)) {
ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit);
return false;
}
}
if (!data.expect_bytes<FUJITSU_GENERAL_BIT_TIMINGS>(recv_message + FUJITSU_GENERAL_COMMON_LENGTH,
recv_message_length - FUJITSU_GENERAL_COMMON_LENGTH)) {
ESP_LOGV(TAG, "Body bytes fail");
return false;
}
for (uint8_t byte = 0; byte < recv_message_length; ++byte) {
@@ -15,24 +15,15 @@ static constexpr uint8_t MODE_MASK = 0x07;
static constexpr uint8_t TEMP_MASK = 0x0F;
static constexpr uint8_t TEMPERATURE_BASE = 17;
static constexpr uint8_t TEMPERATURE_SHIFT = 4;
static constexpr remote_base::BitTimings MITSUBISHI_HEAVY_BIT_TIMINGS{.bit_mark = MITSUBISHI_HEAVY_BIT_MARK,
.one_space = MITSUBISHI_HEAVY_ONE_SPACE,
.zero_space = MITSUBISHI_HEAVY_ZERO_SPACE};
static bool read_raw_bytes(remote_base::RemoteReceiveData &data, uint8_t frame[MITSUBISHI_HEAVY_FRAME_LENGTH]) {
if (!data.expect_item(MITSUBISHI_HEAVY_HDR_MARK, MITSUBISHI_HEAVY_HDR_SPACE))
return false;
for (uint8_t i = 0; i < MITSUBISHI_HEAVY_FRAME_LENGTH; i++) {
uint8_t byte = 0;
for (uint8_t bit = 0; bit < 8; bit++) {
if (data.expect_item(MITSUBISHI_HEAVY_BIT_MARK, MITSUBISHI_HEAVY_ONE_SPACE)) {
byte |= 1 << bit;
} else if (!data.expect_item(MITSUBISHI_HEAVY_BIT_MARK, MITSUBISHI_HEAVY_ZERO_SPACE)) {
return false;
}
}
frame[i] = byte;
}
return true;
return data.expect_bytes<MITSUBISHI_HEAVY_BIT_TIMINGS>(frame, MITSUBISHI_HEAVY_FRAME_LENGTH);
}
static bool validate_prefix(const uint8_t frame[MITSUBISHI_HEAVY_FRAME_LENGTH]) {
+7 -21
View File
@@ -27,8 +27,8 @@ const uint8_t TCL112_HALF_DEGREE = 0b00100000;
const uint16_t TCL112_HEADER_MARK = 3100;
const uint16_t TCL112_HEADER_SPACE = 1650;
const uint16_t TCL112_BIT_MARK = 500;
const uint16_t TCL112_ONE_SPACE = 1100;
const uint16_t TCL112_ZERO_SPACE = 350;
static constexpr remote_base::BitTimings TCL112_BIT_TIMINGS{
.bit_mark = TCL112_BIT_MARK, .one_space = 1100, .zero_space = 350};
const uint32_t TCL112_GAP = TCL112_HEADER_SPACE;
void Tcl112Climate::transmit_state() {
@@ -128,13 +128,7 @@ void Tcl112Climate::transmit_state() {
data->mark(TCL112_HEADER_MARK);
data->space(TCL112_HEADER_SPACE);
// Data
for (uint8_t i : remote_state) {
for (uint8_t j = 0; j < 8; j++) {
data->mark(TCL112_BIT_MARK);
bool bit = i & (1 << j);
data->space(bit ? TCL112_ONE_SPACE : TCL112_ZERO_SPACE);
}
}
data->item_bytes<TCL112_BIT_TIMINGS>(remote_state, TCL112_STATE_LENGTH);
// Footer
data->mark(TCL112_BIT_MARK);
data->space(TCL112_GAP);
@@ -149,18 +143,10 @@ bool Tcl112Climate::on_receive(remote_base::RemoteReceiveData data) {
return false;
}
uint8_t remote_state[TCL112_STATE_LENGTH] = {0};
// Read all bytes.
for (int i = 0; i < TCL112_STATE_LENGTH; i++) {
// Read bit
for (int j = 0; j < 8; j++) {
if (data.expect_item(TCL112_BIT_MARK, TCL112_ONE_SPACE)) {
remote_state[i] |= 1 << j;
} else if (!data.expect_item(TCL112_BIT_MARK, TCL112_ZERO_SPACE)) {
ESP_LOGVV(TAG, "Byte %d bit %d fail", i, j);
return false;
}
}
uint8_t remote_state[TCL112_STATE_LENGTH];
if (!data.expect_bytes<TCL112_BIT_TIMINGS>(remote_state, TCL112_STATE_LENGTH)) {
ESP_LOGVV(TAG, "Data fail");
return false;
}
// Validate footer
if (!data.expect_mark(TCL112_BIT_MARK)) {
+10 -23
View File
@@ -5,8 +5,10 @@ namespace esphome::zhlt01 {
ESPHOME_LOG_TAG(TAG, "zhlt01.climate");
static constexpr uint8_t ZHLT01_MESSAGE_LENGTH = 12;
void ZHLT01Climate::transmit_state() {
uint8_t ir_message[12] = {0};
uint8_t ir_message[ZHLT01_MESSAGE_LENGTH] = {0};
// Byte 1 : Timer
ir_message[1] = 0x00; // Timer off
@@ -102,7 +104,7 @@ void ZHLT01Climate::transmit_state() {
ir_message[11] = 0xD5;
// Set checksum bytes
for (int i = 0; i < 12; i += 2) {
for (int i = 0; i < ZHLT01_MESSAGE_LENGTH; i += 2) {
ir_message[i] = ~ir_message[i + 1];
}
@@ -117,13 +119,7 @@ void ZHLT01Climate::transmit_state() {
data->space(AC1_HDR_SPACE);
// Data
for (uint8_t i : ir_message) {
for (uint8_t j = 0; j < 8; j++) {
data->mark(AC1_BIT_MARK);
bool bit = i & (1 << j);
data->space(bit ? AC1_ONE_SPACE : AC1_ZERO_SPACE);
}
}
data->item_bytes<AC1_BIT_TIMINGS>(ir_message, sizeof(ir_message));
// Footer
data->mark(AC1_BIT_MARK);
@@ -140,19 +136,10 @@ bool ZHLT01Climate::on_receive(remote_base::RemoteReceiveData data) {
}
// Decode IR message
uint8_t ir_message[12] = {0};
// Read all bytes
for (int i = 0; i < 12; i++) {
// Read bit
for (int j = 0; j < 8; j++) {
if (data.expect_item(AC1_BIT_MARK, AC1_ONE_SPACE)) {
ir_message[i] |= 1 << j;
} else if (!data.expect_item(AC1_BIT_MARK, AC1_ZERO_SPACE)) {
ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
return false;
}
}
ESP_LOGVV(TAG, "Byte %d %02X", i, ir_message[i]);
uint8_t ir_message[ZHLT01_MESSAGE_LENGTH];
if (!data.expect_bytes<AC1_BIT_TIMINGS>(ir_message, sizeof(ir_message))) {
ESP_LOGV(TAG, "Data fail");
return false;
}
// Validate footer
@@ -162,7 +149,7 @@ bool ZHLT01Climate::on_receive(remote_base::RemoteReceiveData data) {
}
// Validate checksum
for (int i = 0; i < 12; i += 2) {
for (int i = 0; i < ZHLT01_MESSAGE_LENGTH; i += 2) {
if (ir_message[i] != (uint8_t) (~ir_message[i + 1])) {
ESP_LOGV(TAG, "Byte %d checksum incorrect (%02X != %02X)", i, ir_message[i], (uint8_t) (~ir_message[i + 1]));
return false;
+2 -2
View File
@@ -100,8 +100,8 @@ namespace esphome::zhlt01 {
static const uint32_t AC1_HDR_MARK = 6100;
static const uint32_t AC1_HDR_SPACE = 7400;
static const uint32_t AC1_BIT_MARK = 500;
static const uint32_t AC1_ZERO_SPACE = 600;
static const uint32_t AC1_ONE_SPACE = 1800;
static constexpr remote_base::BitTimings AC1_BIT_TIMINGS{
.bit_mark = AC1_BIT_MARK, .one_space = 1800, .zero_space = 600};
/********************************************************************************
*