diff --git a/components/ratgdo/ratgdo.cpp b/components/ratgdo/ratgdo.cpp index cd06ba6..3609bfe 100644 --- a/components/ratgdo/ratgdo.cpp +++ b/components/ratgdo/ratgdo.cpp @@ -16,6 +16,9 @@ #include "esphome/core/log.h" +#define ESP_LOG1 ESP_LOGV +#define ESP_LOG2 ESP_LOGV + namespace esphome { namespace ratgdo { @@ -97,20 +100,19 @@ namespace ratgdo { uint16_t cmd = ((fixed >> 24) & 0xf00) | (data & 0xff); data &= ~0xf000; // clear parity nibble - Command cmd_enum = to_Command(cmd, Command::UNKNOWN); - if ((fixed & 0xfffffff) == this->remote_id_) { // my commands - ESP_LOGV(TAG, "[%ld] received mine: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data); + ESP_LOG1(TAG, "[%ld] received mine: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data); return static_cast(Command::UNKNOWN); } else { - ESP_LOGV(TAG, "[%ld] received rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data); + ESP_LOG1(TAG, "[%ld] received rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data); } + Command cmd_enum = to_Command(cmd, Command::UNKNOWN); uint8_t nibble = (data >> 8) & 0xff; uint8_t byte1 = (data >> 16) & 0xff; uint8_t byte2 = (data >> 24) & 0xff; - ESP_LOGV(TAG, "cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, Command_to_string(cmd_enum), byte2, byte1, nibble); + ESP_LOG1(TAG, "cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, Command_to_string(cmd_enum), byte2, byte1, nibble); if (cmd == Command::STATUS) { @@ -144,26 +146,55 @@ namespace ratgdo { } } - if (door_state == DoorState::OPEN) { - this->door_position = 1.0; - } else if (door_state == DoorState::CLOSED) { - this->door_position = 0.0; - } else { - if (*this->closing_duration == 0 || *this->opening_duration == 0 || *this->door_position == DOOR_POSITION_UNKNOWN) { + if (door_state == DoorState::OPENING) { + // door started opening + if (prev_door_state == DoorState::CLOSING) { + this->door_position_update(); + this->cancel_position_sync_callbacks(); + this->door_move_delta = DOOR_DELTA_UNKNOWN; + } + this->door_start_moving = millis(); + this->door_start_position = *this->door_position; + if (this->door_move_delta == DOOR_DELTA_UNKNOWN) { + this->door_move_delta = 1.0 - this->door_start_position; + } + this->schedule_door_position_sync(); + + // this would only get called if no status message is received after door stops moving + // request a status message in that case + set_timeout("door_status_update", (*this->opening_duration + 1) * 1000, [=]() { + this->send_command(Command::GET_STATUS); + }); + } else if (door_state == DoorState::CLOSING) { + // door started closing + if (prev_door_state == DoorState::OPENING) { + this->door_position_update(); + this->cancel_position_sync_callbacks(); + this->door_move_delta = DOOR_DELTA_UNKNOWN; + } + this->door_start_moving = millis(); + this->door_start_position = *this->door_position; + if (this->door_move_delta == DOOR_DELTA_UNKNOWN) { + this->door_move_delta = 0.0 - this->door_start_position; + } + this->schedule_door_position_sync(); + + // this would only get called if no status message is received after door stops moving + // request a status message in that case + set_timeout("door_status_update", (*this->closing_duration + 1) * 1000, [=]() { + this->send_command(Command::GET_STATUS); + }); + } else if (door_state == DoorState::STOPPED) { + this->door_position_update(); + if (*this->door_position == DOOR_POSITION_UNKNOWN) { this->door_position = 0.5; // best guess } - } - - if (door_state == DoorState::OPENING && !this->moving_to_position) { - this->position_sync_while_opening(1.0 - *this->door_position); - this->moving_to_position = true; - } - if (door_state == DoorState::CLOSING && !this->moving_to_position) { - this->position_sync_while_closing(*this->door_position); - this->moving_to_position = true; - } - - if (door_state == DoorState::OPEN || door_state == DoorState::CLOSED || door_state == DoorState::STOPPED) { + this->cancel_position_sync_callbacks(); + } else if (door_state == DoorState::OPEN) { + this->door_position = 1.0; + this->cancel_position_sync_callbacks(); + } else if (door_state == DoorState::CLOSED) { + this->door_position = 0.0; this->cancel_position_sync_callbacks(); } @@ -205,7 +236,7 @@ namespace ratgdo { } else if (cmd == Command::MOTOR_ON) { this->motor_state = MotorState::ON; ESP_LOGD(TAG, "Motor: state=%s", MotorState_to_string(*this->motor_state)); - } else if (cmd == Command::OPEN) { + } else if (cmd == Command::DOOR_ACTION) { this->button_state = (byte1 & 1) == 1 ? ButtonState::PRESSED : ButtonState::RELEASED; ESP_LOGD(TAG, "Open: button=%s", ButtonState_to_string(*this->button_state)); } else if (cmd == Command::OPENINGS) { @@ -231,16 +262,39 @@ namespace ratgdo { return cmd; } + void RATGDOComponent::schedule_door_position_sync(float update_period) + { + ESP_LOG1(TAG, "Schedule position sync: delta %f, start position: %f, start moving: %d", + this->door_move_delta, this->door_start_position, this->door_start_moving); + auto duration = this->door_move_delta > 0 ? *this->opening_duration : *this->closing_duration; + auto count = int(1000 * duration / update_period); + set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) { + this->door_position_update(); + return RetryResult::RETRY; + }); + } + + void RATGDOComponent::door_position_update() + { + if (this->door_start_moving == 0 || this->door_start_position == DOOR_POSITION_UNKNOWN || this->door_move_delta == DOOR_DELTA_UNKNOWN) { + return; + } + auto now = millis(); + auto duration = this->door_move_delta > 0 ? *this->opening_duration : -*this->closing_duration; + auto position = this->door_start_position + (now - this->door_start_moving) / (1000 * duration); + ESP_LOG2(TAG, "[%d] Position update: %f", now, position); + this->door_position = clamp(position, 0.0f, 1.0f); + } + void RATGDOComponent::encode_packet(Command command, uint32_t data, bool increment, WirePacket& packet) { auto cmd = static_cast(command); uint64_t fixed = ((cmd & ~0xff) << 24) | this->remote_id_; uint32_t send_data = (data << 8) | (cmd & 0xff); - ESP_LOGV(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), *this->rolling_code_counter, fixed, send_data); + ESP_LOG2(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), *this->rolling_code_counter, fixed, send_data); encode_wireline(*this->rolling_code_counter, fixed, send_data, packet); - this->print_packet(packet); if (increment) { this->increment_rolling_code_counter(); } @@ -271,7 +325,7 @@ namespace ratgdo { void RATGDOComponent::print_packet(const WirePacket& packet) const { - ESP_LOGV(TAG, "Counter: %d Send code: [%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X]", + ESP_LOG2(TAG, "Counter: %d Send code: [%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X]", *this->rolling_code_counter, packet[0], packet[1], @@ -312,7 +366,7 @@ namespace ratgdo { const long PULSES_LOWER_LIMIT = 3; if (current_millis - last_millis > CHECK_PERIOD) { - // ESP_LOGD(TAG, "%ld: Obstruction count: %d, expected: %d, since asleep: %ld", + // ESP_LOGD(TAG, "%ld: Obstruction count: %d, expected: %d, since asleep: %ld", // current_millis, this->isr_store_.obstruction_low_count, PULSES_EXPECTED, // current_millis - last_asleep // ); @@ -348,6 +402,7 @@ namespace ratgdo { while (this->sw_serial_.available()) { uint8_t ser_byte = this->sw_serial_.read(); if (ser_byte != 0x55 && ser_byte != 0x01 && ser_byte != 0x00) { + ESP_LOG2(TAG, "Ignoring byte: %02X, baud: %d", ser_byte, this->sw_serial_.baudRate()); byte_count = 0; continue; } @@ -415,6 +470,9 @@ namespace ratgdo { delayMicroseconds(100); } + ESP_LOG2(TAG, "Sending packet"); + this->print_packet(this->tx_packet_); + // indicate the start of a frame by pulling the 12V line low for at leat 1 byte followed by // one STOP bit, which indicates to the receiving end that the start of the message follows // The output pin is controlling a transistor, so the logic is inverted @@ -447,7 +505,7 @@ namespace ratgdo { 500, MAX_ATTEMPTS, [=](uint8_t r) { auto result = sync_step(); if (result == RetryResult::RETRY) { - if (r == MAX_ATTEMPTS-2 && *this->door_state == DoorState::UNKNOWN) { // made a few attempts and no progress (door state is the first sync request) + if (r == MAX_ATTEMPTS - 2 && *this->door_state == DoorState::UNKNOWN) { // made a few attempts and no progress (door state is the first sync request) // increment rolling code counter by some amount in case we crashed without writing to flash the latest value this->increment_rolling_code_counter(MAX_CODES_WITHOUT_FLASH_WRITE); } @@ -467,7 +525,6 @@ namespace ratgdo { if (*this->door_state == DoorState::OPENING) { return; // gets ignored by opener } - this->cancel_position_sync_callbacks(); this->door_command(data::DOOR_OPEN); } @@ -477,7 +534,6 @@ namespace ratgdo { if (*this->door_state == DoorState::CLOSING || *this->door_state == DoorState::OPENING) { return; // gets ignored by opener } - this->cancel_position_sync_callbacks(); this->door_command(data::DOOR_CLOSE); } @@ -496,59 +552,10 @@ namespace ratgdo { if (*this->door_state == DoorState::OPENING) { return; // gets ignored by opener } - this->cancel_position_sync_callbacks(); this->door_command(data::DOOR_TOGGLE); } - void RATGDOComponent::position_sync_while_opening(float delta, float update_period) - { - if (*this->opening_duration == 0) { - ESP_LOGW(TAG, "I don't know opening duration, ignoring position sync"); - return; - } - auto updates = *this->opening_duration * 1000 * delta / update_period; - auto position_update = delta / updates; - auto count = int(updates); - ESP_LOGV(TAG, "[Opening] Position sync %d times: ", count); - // try to keep position in sync while door is moving - set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) { - ESP_LOGV(TAG, "[Opening] Position sync: %d: ", r); - this->door_position = *this->door_position + position_update; - return RetryResult::RETRY; - }); - - // this would only get called if no status message is received after door stops moving - // request a status message in that case, will get cancelled if a status message is received before - set_timeout("door_status_update", (*this->opening_duration + 1) * 1000, [=]() { - this->send_command(Command::GET_STATUS); - }); - } - - void RATGDOComponent::position_sync_while_closing(float delta, float update_period) - { - if (*this->closing_duration == 0) { - ESP_LOGW(TAG, "I don't know closing duration, ignoring position sync"); - return; - } - auto updates = *this->closing_duration * 1000 * delta / update_period; - auto position_update = delta / updates; - auto count = int(updates); - ESP_LOGV(TAG, "[Closing] Position sync %d times: ", count); - // try to keep position in sync while door is moving - set_retry("position_sync_while_moving", update_period, count, [=](uint8_t r) { - ESP_LOGV(TAG, "[Closing] Position sync: %d: ", r); - this->door_position = *this->door_position - position_update; - return RetryResult::RETRY; - }); - - // this would only get called if no status message is received after door stops moving - // request a status message in that case - set_timeout("door_status_update", (*this->closing_duration + 1) * 1000, [=]() { - this->send_command(Command::GET_STATUS); - }); - } - void RATGDOComponent::door_move_to_position(float position) { if (*this->door_state == DoorState::OPENING || *this->door_state == DoorState::CLOSING) { @@ -562,50 +569,44 @@ namespace ratgdo { return; } - auto duration = delta > 0 ? *this->opening_duration : *this->closing_duration; + auto duration = delta > 0 ? *this->opening_duration : -*this->closing_duration; if (duration == 0) { ESP_LOGW(TAG, "I don't know duration, ignoring move to position"); return; } - if (delta > 0) { // open - this->door_command(data::DOOR_OPEN); - this->position_sync_while_opening(delta); - } else { // close - delta = -delta; - this->door_command(data::DOOR_CLOSE); - this->position_sync_while_closing(delta); - } - - auto operation_time = duration * 1000 * delta; + auto operation_time = 1000 * duration * delta; + this->door_move_delta = delta; ESP_LOGD(TAG, "Moving to position %.2f in %.1fs", position, operation_time / 1000.0); - this->moving_to_position = true; + + this->door_command(delta > 0 ? data::DOOR_OPEN : data::DOOR_CLOSE); set_timeout("move_to_position", operation_time, [=] { this->door_command(data::DOOR_STOP); - this->moving_to_position = false; - this->door_position = position; }); } void RATGDOComponent::cancel_position_sync_callbacks() { - if (this->moving_to_position) { + if (this->door_start_moving != 0) { ESP_LOGD(TAG, "Cancelling position callbacks"); cancel_timeout("move_to_position"); cancel_retry("position_sync_while_moving"); cancel_timeout("door_status_update"); + + this->door_start_moving = 0; + this->door_start_position = DOOR_POSITION_UNKNOWN; + this->door_move_delta = DOOR_DELTA_UNKNOWN; } - moving_to_position = false; } void RATGDOComponent::door_command(uint32_t data) { data |= (1 << 16); // button 1 ? data |= (1 << 8); // button press - this->send_command(Command::OPEN, data, false); - set_timeout(100, [=] { + this->send_command(Command::DOOR_ACTION, data, false); + set_timeout(200, [=] { auto data2 = data & ~(1 << 8); // button release - this->send_command(Command::OPEN, data2); + this->send_command(Command::DOOR_ACTION, data2); }); } diff --git a/components/ratgdo/ratgdo.h b/components/ratgdo/ratgdo.h index ef9c2b6..3ae419b 100644 --- a/components/ratgdo/ratgdo.h +++ b/components/ratgdo/ratgdo.h @@ -36,6 +36,7 @@ namespace ratgdo { typedef uint8_t WirePacket[PACKET_LENGTH]; const float DOOR_POSITION_UNKNOWN = -1.0; + const float DOOR_DELTA_UNKNOWN = -2.0; namespace data { const uint32_t LIGHT_OFF = 0; @@ -64,7 +65,7 @@ namespace ratgdo { (LEARN_2, 0x181), (LOCK, 0x18c), - (OPEN, 0x280), + (DOOR_ACTION, 0x280), (LIGHT, 0x281), (MOTOR_ON, 0x284), (MOTION, 0x285), @@ -88,7 +89,7 @@ namespace ratgdo { struct RATGDOStore { int obstruction_low_count = 0; // count obstruction low pulses - static void IRAM_ATTR HOT isr_obstruction(RATGDOStore* arg) + static void IRAM_ATTR HOT isr_obstruction(RATGDOStore* arg) { arg->obstruction_low_count++; } @@ -111,7 +112,10 @@ namespace ratgdo { observable door_state { DoorState::UNKNOWN }; observable door_position { DOOR_POSITION_UNKNOWN }; - bool moving_to_position { false }; + + unsigned long door_start_moving { 0 }; + float door_start_position { DOOR_POSITION_UNKNOWN }; + float door_move_delta { DOOR_DELTA_UNKNOWN }; observable light_state { LightState::UNKNOWN }; observable lock_state { LockState::UNKNOWN }; @@ -146,12 +150,12 @@ namespace ratgdo { void close_door(); void stop_door(); void door_move_to_position(float position); - void position_sync_while_opening(float delta, float update_period = 500); - void position_sync_while_closing(float delta, float update_period = 500); - void cancel_position_sync_callbacks(); void set_door_position(float door_position) { this->door_position = door_position; } void set_opening_duration(float duration); void set_closing_duration(float duration); + void schedule_door_position_sync(float update_period = 500); + void door_position_update(); + void cancel_position_sync_callbacks(); // light void toggle_light();