mirror of
https://github.com/GrKoR/esphome_aux_ac_component.git
synced 2026-01-01 22:29:09 +03:00
418 lines
13 KiB
C++
418 lines
13 KiB
C++
#include "frame.h"
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
namespace esphome
|
|
{
|
|
namespace aux_airconditioner
|
|
{
|
|
|
|
bool Frame::_is_header_loaded() const
|
|
{
|
|
return this->size() >= Frame::FRAME_HEADER_SIZE;
|
|
}
|
|
|
|
Frame::crc16_t Frame::_calc_crc(uint8_t data_size) const
|
|
{
|
|
Frame::crc16_t crc16;
|
|
|
|
uint8_t data_length = data_size;
|
|
uint8_t corrected_data_length = data_length + (data_length % 2); // data length should be even for crc16
|
|
|
|
uint8_t crc_buffer[corrected_data_length];
|
|
memset(crc_buffer, 0, corrected_data_length);
|
|
memcpy(crc_buffer, this->data(), data_length);
|
|
|
|
data_length = corrected_data_length;
|
|
|
|
uint32_t crc_tmp = 0;
|
|
uint16_t *p_u16 = (uint16_t *)crc_buffer;
|
|
while (data_length > 0)
|
|
{
|
|
crc_tmp += *p_u16;
|
|
p_u16++;
|
|
data_length -= 2;
|
|
}
|
|
crc_tmp = (crc_tmp >> 16) + (crc_tmp & 0xFFFF);
|
|
crc_tmp = ~crc_tmp;
|
|
|
|
crc16.crc16 = crc_tmp & 0xFFFF;
|
|
return crc16;
|
|
}
|
|
|
|
FrameState Frame::_set_frame_state(FrameState state)
|
|
{
|
|
_state = state;
|
|
return this->_state;
|
|
}
|
|
|
|
std::string Frame::_dump_data(const uint8_t *data, uint8_t data_length)
|
|
{
|
|
if (data == nullptr || data_length == 0)
|
|
return "";
|
|
|
|
uint8_t counter = 0;
|
|
std::stringstream ss;
|
|
ss << std::hex << std::uppercase;
|
|
while (counter < data_length)
|
|
{
|
|
ss << std::setfill('0') << std::setw(2) << (int)*data;
|
|
counter++;
|
|
data++;
|
|
if (counter < data_length)
|
|
ss << " ";
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
FrameType Frame::get_frame_type() const
|
|
{
|
|
return (this->_is_header_loaded()) ? (FrameType)this->get_value(Frame::OFFSET_FRAME_TYPE) : (FrameType)0;
|
|
}
|
|
|
|
Frame &Frame::set_frame_type(FrameType frame_type)
|
|
{
|
|
if (this->_is_header_loaded())
|
|
this->set_value(Frame::OFFSET_FRAME_TYPE, frame_type);
|
|
|
|
return *this;
|
|
}
|
|
|
|
uint8_t Frame::get_body_length() const
|
|
{
|
|
return (this->_is_header_loaded()) ? this->get_value(Frame::OFFSET_BODY_LENGTH) : 0;
|
|
}
|
|
|
|
Frame &Frame::set_body_length(uint8_t body_length)
|
|
{
|
|
if (this->_is_header_loaded())
|
|
this->set_value(Frame::OFFSET_BODY_LENGTH, body_length);
|
|
|
|
return *this;
|
|
}
|
|
|
|
FrameDirection Frame::get_frame_dir() const
|
|
{
|
|
return (this->_is_header_loaded()) ? (FrameDirection)this->get_value(Frame::OFFSET_FRAME_DIRECTION) : (FrameDirection)0;
|
|
}
|
|
|
|
Frame &Frame::set_frame_dir(FrameDirection frame_direction)
|
|
{
|
|
if (this->_is_header_loaded())
|
|
this->set_value(Frame::OFFSET_FRAME_DIRECTION, frame_direction);
|
|
|
|
return *this;
|
|
}
|
|
|
|
Frame &Frame::set_frame_time(uint32_t time)
|
|
{
|
|
this->_frame_time = time;
|
|
return *this;
|
|
}
|
|
|
|
Frame &Frame::clear()
|
|
{
|
|
this->_data.clear();
|
|
this->_frame_time = 0;
|
|
this->update_frame_state();
|
|
return *this;
|
|
}
|
|
|
|
bool Frame::send(UARTComponent &uart)
|
|
{
|
|
uart.write_array(this->data(), this->size());
|
|
ESP_LOGD(TAG, "%s", this->to_string(true).c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
FrameState Frame::load(UARTComponent &uart)
|
|
{
|
|
if (!this->has_frame_state(FRAME_STATE_PARTIALLY_LOADED))
|
|
this->clear();
|
|
|
|
if (uart.available() == 0)
|
|
return this->get_frame_state();
|
|
|
|
uint8_t data_byte = 0;
|
|
if (this->has_frame_state(FRAME_STATE_BLANK))
|
|
{
|
|
while (uart.available() &&
|
|
this->has_frame_state(FRAME_STATE_BLANK))
|
|
{
|
|
if (!uart.read_byte(&data_byte))
|
|
{
|
|
ESP_LOGW(TAG, "uart read error");
|
|
break;
|
|
}
|
|
|
|
if (data_byte == this->get_start_byte())
|
|
{
|
|
this->append_data(data_byte);
|
|
this->update_frame_state();
|
|
}
|
|
}
|
|
}
|
|
|
|
while (uart.available() &&
|
|
this->has_frame_state(FRAME_STATE_PARTIALLY_LOADED))
|
|
{
|
|
this->update_frame_state();
|
|
if (this->has_frame_state(FRAME_STATE_OK))
|
|
break;
|
|
|
|
if (this->has_frame_state(FRAME_STATE_ERROR))
|
|
{
|
|
ESP_LOGW(TAG, "Broken frame received: %s", this->to_string(true).c_str());
|
|
break;
|
|
}
|
|
|
|
if (!uart.read_byte(&data_byte))
|
|
{
|
|
ESP_LOGW(TAG, "UART read error");
|
|
break;
|
|
}
|
|
|
|
this->append_data(data_byte);
|
|
}
|
|
|
|
return this->update_frame_state();
|
|
}
|
|
|
|
Frame &Frame::append_data(uint8_t data, bool update_state)
|
|
{
|
|
this->_data.insert(this->_data.end(), data);
|
|
if (update_state)
|
|
this->update_frame_state();
|
|
return *this;
|
|
}
|
|
|
|
Frame &Frame::append_data(const uint8_t data, const uint8_t count, bool update_state)
|
|
{
|
|
this->_data.insert(this->_data.end(), count, data);
|
|
if (update_state)
|
|
this->update_frame_state();
|
|
return *this;
|
|
}
|
|
|
|
Frame &Frame::append_data(std::vector<uint8_t> data, bool update_state)
|
|
{
|
|
this->_data.insert(this->_data.end(), data.begin(), data.end());
|
|
if (update_state)
|
|
this->update_frame_state();
|
|
return *this;
|
|
}
|
|
|
|
Frame &Frame::append_data(const uint8_t *data, uint8_t data_length, bool update_state)
|
|
{
|
|
if (data != nullptr && data_length != 0)
|
|
std::copy(data, data + data_length, std::back_inserter(this->_data));
|
|
|
|
if (update_state)
|
|
this->update_frame_state();
|
|
return *this;
|
|
}
|
|
|
|
Frame &Frame::trim_data(uint8_t first_element_index)
|
|
{
|
|
if (first_element_index < this->size())
|
|
{
|
|
this->_data.erase(this->_data.begin() + first_element_index, this->_data.end());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Frame &Frame::update_crc(bool update_state)
|
|
{
|
|
if (!this->_is_header_loaded())
|
|
return *this;
|
|
|
|
uint8_t expected_frame_size = Frame::FRAME_HEADER_SIZE + this->get_body_length() + sizeof(crc16_t);
|
|
if (this->size() < expected_frame_size - 2 ||
|
|
this->size() > expected_frame_size)
|
|
return *this;
|
|
|
|
if (this->size() > expected_frame_size)
|
|
{
|
|
this->_data.erase(this->_data.begin() + expected_frame_size, this->_data.end());
|
|
}
|
|
else
|
|
{
|
|
this->_data.insert(this->_data.end(), expected_frame_size - this->size(), 0x00);
|
|
}
|
|
|
|
crc16_t crc = this->_calc_crc(this->size() - sizeof(crc16_t));
|
|
this->_data.erase(this->_data.end() - 2, this->_data.end());
|
|
this->_data.insert(this->_data.end(), {crc.crc[0], crc.crc[1]});
|
|
|
|
if (update_state)
|
|
this->update_frame_state();
|
|
return *this;
|
|
}
|
|
|
|
bool Frame::is_valid_crc() const
|
|
{
|
|
if (this->size() < 2)
|
|
return false;
|
|
|
|
crc16_t crc;
|
|
memcpy(&crc, &(this->_data.rbegin()[1]), 2);
|
|
return this->_calc_crc(this->size() - 2).crc16 == crc.crc16;
|
|
}
|
|
|
|
bool Frame::get_bit(uint8_t data_index, uint8_t bit_index) const
|
|
{
|
|
if (bit_index > 7)
|
|
return false;
|
|
|
|
return get_value(data_index, (1 << bit_index)) >> bit_index == 1;
|
|
}
|
|
|
|
Frame &Frame::set_bit(uint8_t data_index, uint8_t bit_index, bool value)
|
|
{
|
|
if (bit_index > 7)
|
|
return *this;
|
|
|
|
this->set_value(data_index, (value << bit_index), (1 << bit_index));
|
|
return *this;
|
|
}
|
|
|
|
uint8_t Frame::get_value(uint8_t index, uint8_t mask, uint8_t shift) const
|
|
{
|
|
if (index >= this->size())
|
|
return 0;
|
|
|
|
return (this->_data[index] & mask) >> shift;
|
|
}
|
|
|
|
Frame &Frame::set_value(uint8_t index, uint8_t value, uint8_t mask, uint8_t shift)
|
|
{
|
|
if (index >= this->size())
|
|
return *this;
|
|
|
|
this->_data[index] &= ~mask;
|
|
this->_data[index] |= (value << shift) & mask;
|
|
return *this;
|
|
}
|
|
|
|
bool Frame::get_crc(uint16_t &crc16) const
|
|
{
|
|
if (this->size() < 2)
|
|
return false;
|
|
|
|
memcpy(&crc16, &(this->_data.rbegin()[1]), 2);
|
|
return true;
|
|
}
|
|
|
|
bool Frame::get_crc(uint8_t &crc16_1, uint8_t &crc16_2) const
|
|
{
|
|
if (this->size() < 2)
|
|
return false;
|
|
|
|
crc16_1 = this->_data.rbegin()[1];
|
|
crc16_2 = this->_data.rbegin()[0];
|
|
return true;
|
|
}
|
|
|
|
FrameState Frame::update_frame_state()
|
|
{
|
|
this->_state = FRAME_STATE_ERROR;
|
|
if (this->size() == 0)
|
|
return this->_set_frame_state(FRAME_STATE_BLANK);
|
|
|
|
if (this->_data[0] != this->get_start_byte())
|
|
return this->_set_frame_state(FRAME_STATE_ERROR);
|
|
|
|
if (this->size() < Frame::FRAME_HEADER_SIZE)
|
|
return this->_set_frame_state(FRAME_STATE_PARTIALLY_LOADED);
|
|
|
|
if (this->size() >= Frame::FRAME_HEADER_SIZE)
|
|
{
|
|
if (this->size() < Frame::FRAME_HEADER_SIZE + this->get_body_length() + sizeof(crc16_t))
|
|
return this->_set_frame_state(FRAME_STATE_PARTIALLY_LOADED);
|
|
|
|
if (this->size() > Frame::FRAME_HEADER_SIZE + this->get_body_length() + sizeof(crc16_t))
|
|
return this->_set_frame_state(FRAME_STATE_ERROR);
|
|
|
|
if (this->size() == Frame::FRAME_HEADER_SIZE + this->get_body_length() + sizeof(crc16_t))
|
|
{
|
|
return this->_set_frame_state(this->is_valid_crc() ? FRAME_STATE_OK : FRAME_STATE_ERROR);
|
|
}
|
|
}
|
|
return this->_state;
|
|
}
|
|
|
|
std::string Frame::to_string(bool show_time) const
|
|
{
|
|
std::stringstream ss;
|
|
if (show_time)
|
|
ss << std::setfill('0') << std::setw(10) << _frame_time << ": ";
|
|
|
|
if (this->has_frame_state(FRAME_STATE_OK))
|
|
{
|
|
ss << this->direction_to_string()
|
|
<< "[" << _dump_data(this->data(), Frame::FRAME_HEADER_SIZE) << "] "
|
|
<< _dump_data(this->data() + Frame::FRAME_HEADER_SIZE, this->get_body_length()) << ((this->get_body_length() != 0) ? " " : "")
|
|
<< "[" << _dump_data(this->data() + Frame::FRAME_HEADER_SIZE + this->get_body_length(), sizeof(crc16_t)) << "]";
|
|
}
|
|
else
|
|
{
|
|
ss << "[--] " << _dump_data(this->data(), this->size());
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
std::string Frame::state_to_string() const
|
|
{
|
|
switch (this->get_frame_state())
|
|
{
|
|
case FRAME_STATE_BLANK:
|
|
return "blank";
|
|
|
|
case FRAME_STATE_ERROR:
|
|
return "error";
|
|
|
|
case FRAME_STATE_PARTIALLY_LOADED:
|
|
return "partially loaded";
|
|
|
|
case FRAME_STATE_OK:
|
|
return "ok";
|
|
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
std::string Frame::type_to_string() const
|
|
{
|
|
switch (this->get_frame_type())
|
|
{
|
|
case FRAME_TYPE_COMMAND:
|
|
return "command";
|
|
|
|
case FRAME_TYPE_INIT:
|
|
return "init";
|
|
|
|
case FRAME_TYPE_PING:
|
|
return "ping";
|
|
|
|
case FRAME_TYPE_RESPONSE:
|
|
return "response";
|
|
|
|
case FRAME_TYPE_STRANGE:
|
|
return "strange";
|
|
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
std::string Frame::direction_to_string() const
|
|
{
|
|
return (this->get_frame_dir() == FRAME_DIR_TO_AC) ? "[=>] " : "[<=] ";
|
|
}
|
|
|
|
} // namespace aux_ac
|
|
} // namespace esphome
|