mirror of
https://github.com/GrKoR/esphome_aux_ac_component.git
synced 2026-01-02 06:39:10 +03:00
v.1 beta 1 initial commit
This commit is contained in:
417
components/aux_ac/frame.cpp
Normal file
417
components/aux_ac/frame.cpp
Normal file
@@ -0,0 +1,417 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user