fixed uart connection, cmd sequence refactoring

This commit is contained in:
GrKoR
2021-10-30 00:34:43 +03:00
parent cd8bdfc63f
commit 238e6f7441
2 changed files with 119 additions and 73 deletions

View File

@@ -429,15 +429,19 @@ enum sequence_packet_type_t : uint8_t {
AC_SPT_SENT_PACKET = 0x02 // отправленный пакет
};
// элемент последовательности
/** элемент последовательности
* Поля item_type, func, timeout и cmd устанавливаются ручками и задают параметры выполнения шага последовательности.
* Поля msec, packet_type и packet заполняются движком при обработке последовательности.
**/
struct sequence_item_t {
sequence_item_type_t item_type; // тип элемента последовательности
bool (AirCon::*func)(); // указатель на функцию, отрабатывающую шаг последовательности
uint16_t timeout; // допустимый таймаут в ожидании пакета (применим только для входящих пакетов)
ac_command_t cmd; // новое состояние сплита, нужно для передачи кондиционеру команд
//******* поля ниже заполняются функциями обработки последовательности ***********
uint32_t msec; // время старта текущего шага последовательности (для входящего пакета и паузы)
sequence_packet_type_t packet_type; // тип пакета (входящий, исходящий или вовсе не пакет)
packet_t packet; // данные пакета
ac_command_t cmd; // новое состояние сплита, нужно для передачи кондиционеру команд
};
/*****************************************************************************************************************************************************/
@@ -470,7 +474,21 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
// флаг подключения к UART
bool _hw_initialized = false;
// указатель на UART, по которому общаемся с кондиционером
esphome::uart::UARTDevice *_ac_serial;
esphome::uart::UARTComponent *_ac_serial;
// UART wrappers: peek
int peek() {
uint8_t data;
if (!_ac_serial->peek_byte(&data)) return -1;
return data;
}
// UART wrappers: read
int read() {
uint8_t data;
if (!_ac_serial->read_byte(&data)) return -1;
return data;
}
// флаг обмена пакетами с кондиционером (если проходят пинги, значит есть коннект)
bool _has_connection = false;
@@ -508,7 +526,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
// возвращает индекс первого пустого шага последовательности команд
uint8_t _getNextFreeSequenceStep(){
for (size_t i = 0; i < AC_SEQUENCE_MAX_LEN; i++) {
if (_sequence[i].item_type != AC_SIT_NONE){
if (_sequence[i].item_type == AC_SIT_NONE){
return i;
}
}
@@ -521,6 +539,42 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return (AC_SEQUENCE_MAX_LEN - _getNextFreeSequenceStep());
}
// добавляет шаг в последовательность команд
// возвращает false, если не нашлось места для шага
bool _addSequenceStep(const sequence_item_type_t item_type, bool (AirCon::*func)() = nullptr, ac_command_t *cmd = nullptr, uint16_t timeout = AC_SEQUENCE_DEFAULT_TIMEOUT){
if (!_hasFreeSequenceStep()) return false; // если места нет, то уходим
if (item_type == AC_SIT_NONE) return false; // глупость какая-то, уходим
if ((item_type == AC_SIT_FUNC) && (func == nullptr)) return false; // должна быть передана функция для такого типа шага
if ((item_type != AC_SIT_DELAY) && (item_type != AC_SIT_FUNC)){
// какой-то неизвестный тип
_debugMsg(F("_addSequenceStep: unknown sequence item type = %u"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, item_type);
return false;
}
uint8_t step = _getNextFreeSequenceStep();
_sequence[step].item_type = item_type;
// если задержка нулевая, то присваиваем дефолтную задержку
if (timeout == 0) timeout = AC_SEQUENCE_DEFAULT_TIMEOUT;
_sequence[step].timeout = timeout;
_sequence[step].func = func;
if (cmd != nullptr) _sequence[step].cmd = *cmd; // так как в структуре команды только простые типы, то можно вот так присваивать
return true;
}
// додбавляет в последовательность шаг с задержкой
bool _addSequenceDelayStep(uint16_t timeout){
return this->_addSequenceStep(AC_SIT_DELAY, nullptr, nullptr, timeout);
}
// добавляет в последовательность функциональный шаг
bool _addSequenceFuncStep(bool (AirCon::*func)(), ac_command_t *cmd = nullptr, uint16_t timeout = AC_SEQUENCE_DEFAULT_TIMEOUT){
return this->_addSequenceStep(AC_SIT_FUNC, func, cmd, timeout);
}
// выполняет всю логику очередного шага последовательности команд
void _doSequence(){
if (!hasSequence()) return;
@@ -555,7 +609,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
// если время вышло, то отчитываемся в лог и очищаем последовательность
if (millis() - _sequence[_sequence_current_step].msec >= _sequence[_sequence_current_step].timeout) {
_debugMsg(F("Sequence [step %u]: step timed out (%u ms)"), ESPHOME_LOG_LEVEL_WARN, __LINE__, _sequence_current_step, millis() - _sequence[_sequence_current_step].msec);
_debugMsg(F("Sequence [step %u]: step timed out (it took %u ms instead of %u ms)"), ESPHOME_LOG_LEVEL_WARN, __LINE__, _sequence_current_step, millis() - _sequence[_sequence_current_step].msec, _sequence[_sequence_current_step].timeout);
_clearSequence();
return;
}
@@ -713,10 +767,11 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
};
if (_ac_serial->peek() == AC_PACKET_START_BYTE) {
if (this->peek() == AC_PACKET_START_BYTE) {
// если во входящий пакет что-то уже загружено, значит это какие-то ошибочные данные или неизвестные пакеты
// надо эту инфу вывалить в лог
if (_inPacket.bytesLoaded > 0){
_debugMsg(F("Start byte received but there are some unparsed bytes in the buffer:"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
_debugPrintPacket(&_inPacket, ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
}
_clearInPacket();
@@ -739,10 +794,11 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
while (_ac_serial->available() > 0)
{
// если наткнулись на старт пакета, то выходим из while
if (_ac_serial->peek() == AC_PACKET_START_BYTE) break;
// если какие-то данные были загружены в буфер, то они будут выгружены в лог при загрузке нового пакета
if (this->peek() == AC_PACKET_START_BYTE) break;
// читаем байт в буфер входящего пакета
_inPacket.data[_inPacket.bytesLoaded] = _ac_serial->read();
_inPacket.data[_inPacket.bytesLoaded] = this->read();
_inPacket.bytesLoaded++;
// если буфер уже полон, надо его вывалить в лог и очистить
@@ -767,7 +823,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return;
}
_inPacket.data[_inPacket.bytesLoaded] = _ac_serial->read();
_inPacket.data[_inPacket.bytesLoaded] = this->read();
_inPacket.bytesLoaded++;
// данных достаточно для заголовка
@@ -870,7 +926,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
case AC_PTYPE_CMD: { // команда сплиту; модуль отправляет такие команды, когда что-то хочет от сплита
// сплит такие команды отправлять не должен, поэтому жалуемся в лог
_debugMsg(F("Parser: packet type=0x06 received. This isn't expected."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
_debugMsg(F("Parser: packet type=0x06 received from HVAC. This isn't expected."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
// очищаем пакет
_clearInPacket();
_setStateMachineState(ACSM_IDLE);
@@ -1501,10 +1557,10 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
AirCon(){ initAC(); };
AirCon(esphome::uart::UARTDevice *parent) { initAC(parent); };
AirCon(esphome::uart::UARTComponent *parent) { initAC(parent); };
// инициализация объекта
void initAC(esphome::uart::UARTDevice *parent = nullptr){
void initAC(esphome::uart::UARTComponent *parent = nullptr){
_dataMillis = millis();
_clearInPacket();
_clearOutPacket();
@@ -2099,23 +2155,19 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return false;
}
uint8_t step = _getNextFreeSequenceStep();
/**************************************************************************************/
//step++; // - getSmallInfo
_sequence[step].item_type = AC_SIT_FUNC;
_sequence[step].func = &AirCon::sq_requestSmallStatus;
//_sequence[step].timeout = 0; // пусть будет таймаут по-умолчанию
/*************************************** getSmallInfo request ***********************************************/
if (!_addSequenceFuncStep(&AirCon::sq_requestSmallStatus)) {
_debugMsg(F("getStatusSmall: getSmallInfo request sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
return false;
}
/*************************************** getSmallInfo control ***********************************************/
if (!_addSequenceFuncStep(&AirCon::sq_controlSmallStatus)) {
_debugMsg(F("getStatusSmall: getSmallInfo control sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
return false;
}
/**************************************************************************************/
/**************************************************************************************/
step++; // - control getSmallInfo
_sequence[step].item_type = AC_SIT_FUNC;
_sequence[step].func = &AirCon::sq_controlSmallStatus;
//_sequence[step].timeout = 1000;
/**************************************************************************************/
_debugMsg(F("getStatusSmall: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
_debugMsg(F("getStatusSmall: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
return true;
}
@@ -2132,23 +2184,19 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return false;
}
uint8_t step = _getNextFreeSequenceStep();
/**************************************************************************************/
//step++; // - getBigInfo
_sequence[step].item_type = AC_SIT_FUNC;
_sequence[step].func = &AirCon::sq_requestBigStatus;
//_sequence[step].timeout = 0; // пусть будет таймаут по-умолчанию
/*************************************** getBigInfo request ***********************************************/
if (!_addSequenceFuncStep(&AirCon::sq_requestBigStatus)) {
_debugMsg(F("getStatusSmall: getBigInfo request sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
return false;
}
/*************************************** getBigInfo control ***********************************************/
if (!_addSequenceFuncStep(&AirCon::sq_controlBigStatus)) {
_debugMsg(F("getStatusSmall: getBigInfo control sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
return false;
}
/**************************************************************************************/
/**************************************************************************************/
step++; // - control getSmallInfo
_sequence[step].item_type = AC_SIT_FUNC;
_sequence[step].func = &AirCon::sq_controlBigStatus;
//_sequence[step].timeout = 0; // пусть будет таймаут по-умолчанию
/**************************************************************************************/
_debugMsg(F("getStatusBig: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
_debugMsg(F("getStatusBig: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
return true;
}
@@ -2170,7 +2218,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return false;
}
_debugMsg(F("getStatusBigAndSmall: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
_debugMsg(F("getStatusBigAndSmall: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
return true;
}
@@ -2194,7 +2242,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return false;
};
_debugMsg(F("startupSequence: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
_debugMsg(F("startupSequence: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
return true;
}
@@ -2222,22 +2270,16 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return false;
}
uint8_t step = _getNextFreeSequenceStep();
/**************************************************************************************/
//step++; // - set params
_sequence[step].item_type = AC_SIT_FUNC;
_sequence[step].func = &AirCon::sq_requestDoCommand;
// так как в структуре команды нет указателей, то простое присваивание возможно
_sequence[step].cmd = *cmd;
//_sequence[step].timeout = 0; // пусть будет таймаут по-умолчанию
/**************************************************************************************/
/**************************************************************************************/
step++; // - control of params setting
_sequence[step].item_type = AC_SIT_FUNC;
_sequence[step].func = &AirCon::sq_controlDoCommand;
//_sequence[step].timeout = 1000;
/*************************************** set params request ***********************************************/
if (!_addSequenceFuncStep(&AirCon::sq_requestDoCommand, cmd)) {
_debugMsg(F("getStatusSmall: getBigInfo request sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
return false;
}
/*************************************** set params control ***********************************************/
if (!_addSequenceFuncStep(&AirCon::sq_controlDoCommand)) {
_debugMsg(F("getStatusSmall: getBigInfo control sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
return false;
}
/**************************************************************************************/
// добавление финального запроса маленького статусного пакета в последовательность команд
@@ -2246,7 +2288,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return false;
}
_debugMsg(F("commandSequence: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
_debugMsg(F("commandSequence: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
return true;
}
@@ -2266,7 +2308,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
// добавляем команду в последовательность
if (!commandSequence(&cmd)) return false;
_debugMsg(F("powerSequence: loaded (power = %02X)"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, pwr);
_debugMsg(F("powerSequence: loaded (power = %02X)"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, pwr);
return true;
}
@@ -2284,14 +2326,6 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
};
void loop() override {
if ((millis()-_dataMillis) > 3000){
_dataMillis = millis();
_debugMsg(F("current status:"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
_debugMsg(F(" _hw_initialized = %02X"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_hw_initialized);
_debugMsg(F(" _has_connection = %02X"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_has_connection);
_debugMsg(F(" _ac_state = %02X"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_ac_state);
}
if (!get_hw_initialized()) return;
/// отрабатываем состояния конечного автомата
@@ -2321,14 +2355,22 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
if ((millis()-_dataMillis) > _update_period){
_dataMillis = millis();
_debugMsg(F("update period:"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
_debugMsg(F(" _hw_initialized = %02X)"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_hw_initialized);
_debugMsg(F(" _has_connection = %02X)"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_has_connection);
// обычный wifi-модуль запрашивает маленький пакет статуса
// но нам никто не мешает запрашивать и большой и маленький, чтобы чаще обновлять комнатную температуру
// делаем этот запросом только в случае, если есть коннект с кондиционером
if (get_has_connection()) getStatusBigAndSmall();
}
/*
// это экспериментальная секция для отладки функционала
static uint32_t debug_millis = millis();
if (millis()-debug_millis > 10000){
debug_millis = millis();
//_debugMsg(F("Test!"), ESPHOME_LOG_LEVEL_WARN, __LINE__);
//if (_current_ac_state.power == AC_POWER_OFF) powerSequence(AC_POWER_ON);
//else powerSequence(AC_POWER_OFF);
}
*/
};
};

View File

@@ -90,6 +90,10 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await climate.register_climate(var, config)
parent = await cg.get_variable(config[CONF_UART_ID])
cg.add(var.initAC(parent))
cg.add(var.set_period(config[CONF_PERIOD].total_milliseconds))
cg.add(var.set_show_action(config[CONF_SHOW_ACTION]))
if CONF_SUPPORTED_MODES in config: