Refactor command handling (#10)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Marius Muja 2023-06-24 13:38:44 -07:00 committed by GitHub
parent a7e7ff82e9
commit 37a4b88355
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 287 additions and 153 deletions

View File

@ -26,12 +26,15 @@ namespace ratgdo {
break; break;
case DoorState::DOOR_STATE_OPENING: case DoorState::DOOR_STATE_OPENING:
this->current_operation = COVER_OPERATION_OPENING; this->current_operation = COVER_OPERATION_OPENING;
this->position = 0.5;
break; break;
case DoorState::DOOR_STATE_CLOSING: case DoorState::DOOR_STATE_CLOSING:
this->current_operation = COVER_OPERATION_CLOSING; this->current_operation = COVER_OPERATION_CLOSING;
this->position = 0.5;
break; break;
case DoorState::DOOR_STATE_STOPPED: case DoorState::DOOR_STATE_STOPPED:
this->position = COVER_OPEN; this->current_operation = COVER_OPERATION_IDLE;
this->position = 0.5;
default: default:
this->current_operation = COVER_OPERATION_IDLE; this->current_operation = COVER_OPERATION_IDLE;
@ -45,6 +48,8 @@ namespace ratgdo {
{ {
auto traits = CoverTraits(); auto traits = CoverTraits();
traits.set_supports_stop(true); traits.set_supports_stop(true);
traits.set_supports_toggle(true);
traits.set_supports_position(true);
return traits; return traits;
} }
void RATGDOCover::control(const CoverCall& call) void RATGDOCover::control(const CoverCall& call)
@ -52,6 +57,9 @@ namespace ratgdo {
if (call.get_stop()) { if (call.get_stop()) {
this->parent_->stopDoor(); this->parent_->stopDoor();
} }
if (call.get_toggle()) {
this->parent_->toggleDoor();
}
if (call.get_position().has_value()) { if (call.get_position().has_value()) {
auto pos = *call.get_position(); auto pos = *call.get_position();
if (pos == COVER_OPEN) { if (pos == COVER_OPEN) {

View File

@ -23,7 +23,6 @@ namespace ratgdo {
static const char* const TAG = "ratgdo"; static const char* const TAG = "ratgdo";
static const int STARTUP_DELAY = 2000; // delay before enabling interrupts static const int STARTUP_DELAY = 2000; // delay before enabling interrupts
static const uint64_t REMOTE_ID = 0x539; static const uint64_t REMOTE_ID = 0x539;
static const uint16_t STATUS_CMD = 0x81;
static const uint8_t MAX_CODES_WITHOUT_FLASH_WRITE = 3; static const uint8_t MAX_CODES_WITHOUT_FLASH_WRITE = 3;
void IRAM_ATTR HOT RATGDOStore::isrObstruction(RATGDOStore* arg) void IRAM_ATTR HOT RATGDOStore::isrObstruction(RATGDOStore* arg)
@ -76,6 +75,56 @@ namespace ratgdo {
ESP_LOGCONFIG(TAG, " Rolling Code Counter: %d", this->rollingCodeCounter); ESP_LOGCONFIG(TAG, " Rolling Code Counter: %d", this->rollingCodeCounter);
} }
const char* cmd_name(uint16_t cmd)
{
// from: https://github.com/argilo/secplus/blob/f98c3220356c27717a25102c0b35815ebbd26ccc/secplus.py#L540
switch (cmd) {
// sent by opener (motor)
case 0x081:
return "status";
case 0x084:
return "unknown_1";
case 0x085:
return "unknown_2";
case 0x0a1:
return "pair_3_resp";
case 0x284:
return "motor_on";
case 0x393:
return "learn_3_resp";
case 0x401:
return "pair_2_resp";
case 0x48c:
return "openings";
// sent by switch
case 0x080:
return "get_status";
case 0x0a0:
return "pair_3";
case 0x181:
return "learn_2";
case 0x18c:
return "lock";
case 0x280:
return "open";
case 0x281:
return "light";
case 0x285:
return "motion";
case 0x391:
return "learn_1";
case 0x392:
return "learn_3";
case 0x400:
return "pair_2";
case 0x48b:
return "get_openings";
default:
return "unknown";
}
}
uint16_t RATGDOComponent::readRollingCode() uint16_t RATGDOComponent::readRollingCode()
{ {
uint32_t rolling = 0; uint32_t rolling = 0;
@ -90,51 +139,80 @@ namespace ratgdo {
decode_wireline(this->rxRollingCode, &rolling, &fixed, &data); decode_wireline(this->rxRollingCode, &rolling, &fixed, &data);
cmd = ((fixed >> 24) & 0xf00) | (data & 0xff); cmd = ((fixed >> 24) & 0xf00) | (data & 0xff);
data &= ~0xf000; // clear parity nibble
nibble = (data >> 8) & 0xf; if ((fixed & 0xfff) == REMOTE_ID) { // my commands
ESP_LOGD(TAG, "[%ld] received mine: rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
return 0;
} else {
ESP_LOGD(TAG, "[%ld] received rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), rolling, fixed, data);
}
nibble = (data >> 8) & 0xff;
byte1 = (data >> 16) & 0xff; byte1 = (data >> 16) & 0xff;
byte2 = (data >> 24) & 0xff; byte2 = (data >> 24) & 0xff;
if (cmd == STATUS_CMD) { ESP_LOGD(TAG, "cmd=%03x (%s) byte2=%02x byte1=%02x nibble=%01x", cmd, cmd_name(cmd), byte2, byte1, nibble);
this->doorState = nibble;
this->lightState = (byte2 >> 1) & 1; if (cmd == command::STATUS) {
this->lockState = byte2 & 1; auto doorState = static_cast<DoorState>(nibble);
if (doorState == DoorState::DOOR_STATE_CLOSED && this->doorState != doorState) {
transmit(command::GET_OPENINGS);
}
this->doorState = doorState;
this->lightState = static_cast<LightState>((byte2 >> 1) & 1);
this->lockState = static_cast<LockState>(byte2 & 1);
this->motionState = MotionState::MOTION_STATE_CLEAR; // when the status message is read, reset motion state to 0|clear this->motionState = MotionState::MOTION_STATE_CLEAR; // when the status message is read, reset motion state to 0|clear
this->motorState = MotorState::MOTOR_STATE_OFF; // when the status message is read, reset motor state to 0|off this->motorState = MotorState::MOTOR_STATE_OFF; // when the status message is read, reset motor state to 0|off
// obstruction = (byte1 >> 6) & 1; // unreliable due to the time it takes to register an obstruction // this->obstructionState = static_cast<ObstructionState>((byte1 >> 6) & 1);
ESP_LOGV(TAG, "Door: %d Light: %d Lock: %d", this->doorState, this->lightState, this->lockState); ESP_LOGD(TAG, "Status: door=%s light=%s lock=%s",
door_state_to_string(this->doorState),
} else if (cmd == 0x281) { light_state_to_string(this->lightState),
if (this->lightState == LightState::LIGHT_STATE_ON) { lock_state_to_string(this->lockState));
} else if (cmd == command::LIGHT) {
if (nibble == 0) {
this->lightState = LightState::LIGHT_STATE_OFF; this->lightState = LightState::LIGHT_STATE_OFF;
} else { } else if (nibble == 1) {
this->lightState = LightState::LIGHT_STATE_ON; this->lightState = LightState::LIGHT_STATE_ON;
} else if (nibble == 2) { // toggle
this->lightState = light_state_toggle(this->lightState);
} }
ESP_LOGV(TAG, "Light: %d (toggle)", this->lightState); ESP_LOGD(TAG, "Light: action=%s state=%s",
} else if (cmd == 0x284) { nibble == 0 ? "OFF" : nibble == 1 ? "ON"
: "TOGGLE",
light_state_to_string(this->lightState));
} else if (cmd == command::MOTOR_ON) {
this->motorState = MotorState::MOTOR_STATE_ON; this->motorState = MotorState::MOTOR_STATE_ON;
} else if (cmd == 0x280) { ESP_LOGD(TAG, "Motor: state=%s", motor_state_to_string(this->motorState));
this->buttonState = byte1 == 1 ? ButtonState::BUTTON_STATE_PRESSED : ButtonState::BUTTON_STATE_RELEASED; } else if (cmd == command::OPEN) {
ESP_LOGV(TAG, "Pressed: %d", this->buttonState); this->buttonState = (byte1 & 1) == 1 ? ButtonState::BUTTON_STATE_PRESSED : ButtonState::BUTTON_STATE_RELEASED;
} else if (cmd == 0x48c) { ESP_LOGD(TAG, "Open: button=%s", button_state_to_string(this->buttonState));
} else if (cmd == command::OPENINGS) {
this->openings = (byte1 << 8) | byte2; this->openings = (byte1 << 8) | byte2;
ESP_LOGV(TAG, "Openings: %d", this->openings); ESP_LOGD(TAG, "Openings: %d", this->openings);
} else if (cmd == 0x285) { } else if (cmd == command::MOTION) {
this->motionState = MotionState::MOTION_STATE_DETECTED; // toggle bit this->motionState = MotionState::MOTION_STATE_DETECTED;
ESP_LOGV(TAG, "Motion: %d (toggle)", this->motionState); if (this->lightState == LightState::LIGHT_STATE_OFF) {
transmit(command::GET_STATUS);
}
ESP_LOGD(TAG, "Motion: %s", motion_state_to_string(this->motionState));
} else { } else {
// 0x84 -- is it used? ESP_LOGD(TAG, "Unhandled command: cmd=%03x nibble=%02x byte1=%02x byte2=%02x fixed=%010" PRIx64 " data=%08" PRIx32, cmd, nibble, byte1, byte2, fixed, data);
ESP_LOGV(TAG, "Unknown command: cmd=%04x nibble=%02d byte1=%02d byte2=%02d", cmd, nibble, byte1, byte2);
} }
return cmd; return cmd;
} }
void RATGDOComponent::getRollingCode(cmd command) void RATGDOComponent::getRollingCode(command::cmd command, uint32_t data, bool increment)
{ {
uint64_t fixed = command.fixed | REMOTE_ID; uint64_t fixed = ((command & ~0xff) << 24) | REMOTE_ID;
encode_wireline(this->rollingCodeCounter, fixed, command.data, this->txRollingCode); uint32_t send_data = (data << 8) | (command & 0xff);
ESP_LOGD(TAG, "[%ld] Encode for transmit rolling=%07" PRIx32 " fixed=%010" PRIx64 " data=%08" PRIx32, millis(), this->rollingCodeCounter, fixed, send_data);
encode_wireline(this->rollingCodeCounter, fixed, send_data, this->txRollingCode);
printRollingCode(); printRollingCode();
if (command != Command.DOOR1) { // door2 is created with same counter and should always be called after door1 if (increment) {
incrementRollingCodeCounter(); incrementRollingCodeCounter();
} }
} }
@ -260,7 +338,7 @@ namespace ratgdo {
if (byte_count == CODE_LENGTH) { if (byte_count == CODE_LENGTH) {
reading_msg = false; reading_msg = false;
byte_count = 0; byte_count = 0;
if (readRollingCode() == STATUS_CMD && this->forceUpdate_) { if (readRollingCode() == command::STATUS && this->forceUpdate_) {
this->forceUpdate_ = false; this->forceUpdate_ = false;
this->previousDoorState = DoorState::DOOR_STATE_UNKNOWN; this->previousDoorState = DoorState::DOOR_STATE_UNKNOWN;
this->previousLightState = LightState::LIGHT_STATE_UNKNOWN; this->previousLightState = LightState::LIGHT_STATE_UNKNOWN;
@ -275,58 +353,51 @@ namespace ratgdo {
void RATGDOComponent::statusUpdateLoop() void RATGDOComponent::statusUpdateLoop()
{ {
if (this->doorState != this->previousDoorState) { if (this->doorState != this->previousDoorState) {
DoorState val = static_cast<DoorState>(this->doorState); ESP_LOGV(TAG, "Door state: %s", door_state_to_string(this->doorState));
ESP_LOGV(TAG, "Door state: %s", door_state_to_string(val));
for (auto* child : this->children_) { for (auto* child : this->children_) {
child->on_door_state(val); child->on_door_state(this->doorState);
} }
this->previousDoorState = this->doorState; this->previousDoorState = this->doorState;
} }
if (this->lightState != this->previousLightState) { if (this->lightState != this->previousLightState) {
LightState val = static_cast<LightState>(this->lightState); ESP_LOGV(TAG, "Light state %s (%d)", light_state_to_string(this->lightState), this->lightState);
ESP_LOGV(TAG, "Light state %s (%d)", light_state_to_string(val), this->lightState);
for (auto* child : this->children_) { for (auto* child : this->children_) {
child->on_light_state(val); child->on_light_state(this->lightState);
} }
this->previousLightState = this->lightState; this->previousLightState = this->lightState;
} }
if (this->lockState != this->previousLockState) { if (this->lockState != this->previousLockState) {
LockState val = static_cast<LockState>(this->lockState); ESP_LOGV(TAG, "Lock state %s", lock_state_to_string(this->lockState));
ESP_LOGV(TAG, "Lock state %s", lock_state_to_string(val));
for (auto* child : this->children_) { for (auto* child : this->children_) {
child->on_lock_state(val); child->on_lock_state(this->lockState);
} }
this->previousLockState = this->lockState; this->previousLockState = this->lockState;
} }
if (this->obstructionState != this->previousObstructionState) { if (this->obstructionState != this->previousObstructionState) {
ObstructionState val = static_cast<ObstructionState>(this->obstructionState); ESP_LOGV(TAG, "Obstruction state %s", obstruction_state_to_string(this->obstructionState));
ESP_LOGV(TAG, "Obstruction state %s", obstruction_state_to_string(val));
for (auto* child : this->children_) { for (auto* child : this->children_) {
child->on_obstruction_state(val); child->on_obstruction_state(this->obstructionState);
} }
this->previousObstructionState = this->obstructionState; this->previousObstructionState = this->obstructionState;
} }
if (this->motorState != this->previousMotorState) { if (this->motorState != this->previousMotorState) {
MotorState val = static_cast<MotorState>(this->motorState); ESP_LOGV(TAG, "Motor state %s", motor_state_to_string(this->motorState));
ESP_LOGV(TAG, "Motor state %s", motor_state_to_string(val));
for (auto* child : this->children_) { for (auto* child : this->children_) {
child->on_motor_state(val); child->on_motor_state(this->motorState);
} }
this->previousMotorState = this->motorState; this->previousMotorState = this->motorState;
} }
if (this->motionState != this->previousMotionState) { if (this->motionState != this->previousMotionState) {
MotionState val = static_cast<MotionState>(this->motionState); ESP_LOGV(TAG, "Motion state %s", motion_state_to_string(this->motionState));
ESP_LOGV(TAG, "Motion state %s", motion_state_to_string(val));
for (auto* child : this->children_) { for (auto* child : this->children_) {
child->on_motion_state(val); child->on_motion_state(this->motionState);
} }
this->previousMotionState = this->motionState; this->previousMotionState = this->motionState;
} }
if (this->buttonState != this->previousButtonState) { if (this->buttonState != this->previousButtonState) {
ButtonState val = static_cast<ButtonState>(this->buttonState); ESP_LOGV(TAG, "Button state %s", button_state_to_string(this->buttonState));
ESP_LOGV(TAG, "Button state %s", button_state_to_string(val));
for (auto* child : this->children_) { for (auto* child : this->children_) {
child->on_button_state(val); child->on_button_state(this->buttonState);
} }
this->previousButtonState = this->buttonState; this->previousButtonState = this->buttonState;
} }
@ -342,7 +413,7 @@ namespace ratgdo {
void RATGDOComponent::query() void RATGDOComponent::query()
{ {
this->forceUpdate_ = true; this->forceUpdate_ = true;
sendCommandAndSaveCounter(Command.REBOOT2); sendCommandAndSaveCounter(command::GET_STATUS);
} }
/************************* DOOR COMMUNICATION *************************/ /************************* DOOR COMMUNICATION *************************/
@ -354,9 +425,9 @@ namespace ratgdo {
* The opener requires a specific duration low/high pulse before it will accept * The opener requires a specific duration low/high pulse before it will accept
* a message * a message
*/ */
void RATGDOComponent::transmit(cmd command) void RATGDOComponent::transmit(command::cmd command, uint32_t data, bool increment)
{ {
getRollingCode(command); getRollingCode(command, data, increment);
this->output_gdo_pin_->digital_write(true); // pull the line high for 1305 micros so the this->output_gdo_pin_->digital_write(true); // pull the line high for 1305 micros so the
// door opener responds to the message // door opener responds to the message
delayMicroseconds(1305); delayMicroseconds(1305);
@ -370,38 +441,30 @@ namespace ratgdo {
{ {
this->rollingCodeUpdatesEnabled_ = false; this->rollingCodeUpdatesEnabled_ = false;
for (int i = 0; i <= MAX_CODES_WITHOUT_FLASH_WRITE; i++) { for (int i = 0; i <= MAX_CODES_WITHOUT_FLASH_WRITE; i++) {
transmit(Command.REBOOT1); // get openings transmit(command::GET_OPENINGS); // get openings
delay(65); delay(65);
} }
transmit(Command.REBOOT2); // get state transmit(command::GET_STATUS); // get state
delay(65); delay(65);
transmit(Command.REBOOT3); transmit(command::PAIR_3);
delay(65); delay(65);
transmit(Command.REBOOT4); transmit(command::GET_STATUS);
delay(65); delay(65);
transmit(Command.REBOOT5); transmit(command::LEARN_3);
delay(65); delay(65);
this->rollingCodeUpdatesEnabled_ = true; this->rollingCodeUpdatesEnabled_ = true;
sendCommandAndSaveCounter(Command.REBOOT6); sendCommandAndSaveCounter(command::LEARN_3);
delay(65); delay(65);
} }
void RATGDOComponent::openDoor() void RATGDOComponent::openDoor()
{ {
if (this->doorState == DoorState::DOOR_STATE_OPEN || this->doorState == DoorState::DOOR_STATE_OPENING) { doorCommand(data::OPEN);
ESP_LOGV(TAG, "The door is already %s", door_state_to_string(static_cast<DoorState>(this->doorState)));
return;
}
toggleDoor();
} }
void RATGDOComponent::closeDoor() void RATGDOComponent::closeDoor()
{ {
if (this->doorState == DoorState::DOOR_STATE_CLOSED || this->doorState == DoorState::DOOR_STATE_CLOSING) { doorCommand(data::CLOSE);
ESP_LOGV(TAG, "The door is already %s", door_state_to_string(static_cast<DoorState>(this->doorState)));
return;
}
toggleDoor();
} }
void RATGDOComponent::stopDoor() void RATGDOComponent::stopDoor()
@ -415,66 +478,59 @@ namespace ratgdo {
void RATGDOComponent::toggleDoor() void RATGDOComponent::toggleDoor()
{ {
transmit(Command.DOOR1); doorCommand(data::TOGGLE);
delay(40);
sendCommandAndSaveCounter(Command.DOOR2);
} }
bool RATGDOComponent::isLightOn() void RATGDOComponent::doorCommand(uint32_t data)
{ {
return this->lightState == LightState::LIGHT_STATE_ON; data |= (1 << 16); // button 1 ?
data |= (1 << 8); // button press
transmit(command::OPEN, data, false);
delay(40);
data &= ~(1 << 8); // button release
sendCommandAndSaveCounter(command::OPEN, data);
} }
void RATGDOComponent::lightOn() void RATGDOComponent::lightOn()
{ {
if (this->lightState == LightState::LIGHT_STATE_ON) { this->lightState = LightState::LIGHT_STATE_ON;
ESP_LOGV(TAG, "The light is already on"); sendCommandAndSaveCounter(command::LIGHT, data::ON);
return;
}
toggleLight();
} }
void RATGDOComponent::lightOff() void RATGDOComponent::lightOff()
{ {
if (this->lightState == LightState::LIGHT_STATE_OFF) { this->lightState = LightState::LIGHT_STATE_OFF;
ESP_LOGV(TAG, "The light is already off"); sendCommandAndSaveCounter(command::LIGHT, data::OFF);
return;
}
toggleLight();
} }
void RATGDOComponent::toggleLight() void RATGDOComponent::toggleLight()
{ {
sendCommandAndSaveCounter(Command.LIGHT); this->lightState = light_state_toggle(this->lightState);
sendCommandAndSaveCounter(command::LIGHT, data::TOGGLE);
} }
// Lock functions // Lock functions
void RATGDOComponent::lock() void RATGDOComponent::lock()
{ {
if (this->lockState == LockState::LOCK_STATE_LOCKED) { this->lockState = LockState::LOCK_STATE_LOCKED;
ESP_LOGV(TAG, "already locked"); sendCommandAndSaveCounter(command::LOCK, data::ON);
return;
}
toggleLock();
} }
void RATGDOComponent::unlock() void RATGDOComponent::unlock()
{ {
if (this->lockState == LockState::LOCK_STATE_UNLOCKED) { this->lockState = LockState::LOCK_STATE_UNLOCKED;
ESP_LOGV(TAG, "already unlocked"); sendCommandAndSaveCounter(command::LOCK, data::OFF);
return;
}
toggleLock();
} }
void RATGDOComponent::toggleLock() void RATGDOComponent::toggleLock()
{ {
sendCommandAndSaveCounter(Command.LOCK); this->lockState = lock_state_toggle(this->lockState);
sendCommandAndSaveCounter(command::LOCK, data::TOGGLE);
} }
void RATGDOComponent::sendCommandAndSaveCounter(cmd command) void RATGDOComponent::sendCommandAndSaveCounter(command::cmd command, uint32_t data, bool increment)
{ {
transmit(command); transmit(command, data, increment);
this->pref_.save(&this->rollingCodeCounter); this->pref_.save(&this->rollingCodeCounter);
if (!this->lastSyncedRollingCodeCounter || this->rollingCodeCounter - this->lastSyncedRollingCodeCounter >= MAX_CODES_WITHOUT_FLASH_WRITE) { if (!this->lastSyncedRollingCodeCounter || this->rollingCodeCounter - this->lastSyncedRollingCodeCounter >= MAX_CODES_WITHOUT_FLASH_WRITE) {
this->lastSyncedRollingCodeCounter = this->rollingCodeCounter; this->lastSyncedRollingCodeCounter = this->rollingCodeCounter;
@ -489,7 +545,7 @@ namespace ratgdo {
} }
LightState RATGDOComponent::getLightState() LightState RATGDOComponent::getLightState()
{ {
return static_cast<LightState>(this->lightState); return this->lightState;
} }
} // namespace ratgdo } // namespace ratgdo

View File

@ -33,40 +33,71 @@ namespace ratgdo {
static const uint8_t CODE_LENGTH = 19; static const uint8_t CODE_LENGTH = 19;
struct cmd { /*
uint64_t fixed; from: https://github.com/argilo/secplus/blob/f98c3220356c27717a25102c0b35815ebbd26ccc/secplus.py#L540
uint32_t data; _WIRELINE_COMMANDS = {
inline bool operator!=(cmd const& other) const # sent by opener
{ 0x081: "status",
return (fixed != other.fixed || data != other.data); 0x084: "unknown_1",
} 0x085: "unknown_2",
}; 0x0a1: "pair_3_resp",
0x284: "motor_on",
0x393: "learn_3_resp",
0x401: "pair_2_resp",
0x48c: "openings",
typedef struct { # sent by switch
cmd REBOOT1; 0x080: "get_status",
cmd REBOOT2; 0x0a0: "pair_3",
cmd REBOOT3; 0x181: "learn_2",
cmd REBOOT4; 0x18c: "lock",
cmd REBOOT5; 0x280: "open",
cmd REBOOT6; 0x281: "light",
cmd DOOR1; 0x285: "motion",
cmd DOOR2; 0x391: "learn_1",
cmd LIGHT; 0x392: "learn_3",
cmd LOCK; 0x400: "pair_2",
} cmds; 0x48b: "get_openings",
}
*/
namespace data {
const uint32_t OFF = 0;
const uint32_t CLOSE = 0;
const uint32_t ON = 1;
const uint32_t OPEN = 1;
const uint32_t TOGGLE = 2;
}
namespace command {
enum cmd : uint64_t {
GET_STATUS = 0x080,
STATUS = 0x081,
OBST_1 = 0x084, // sent when an obstruction happens?
OBST_2 = 0x085, // sent when an obstruction happens?
PAIR_3 = 0x0a0,
PAIR_3_RESP = 0x0a1,
LEARN_2 = 0x181,
LOCK = 0x18c,
OPEN = 0x280,
LIGHT = 0x281,
MOTOR_ON = 0x284,
MOTION = 0x285,
LEARN_1 = 0x391,
LEARN_3 = 0x392,
LEARN_3_RESP = 0x393,
PAIR_2 = 0x400,
PAIR_2_RESP = 0x401,
GET_OPENINGS = 0x48b,
OPENINGS = 0x48c,
};
}
const cmds Command = {
.REBOOT1 = (cmd) { 0x400000000, 0x0000618b },
.REBOOT2 = (cmd) { 0, 0x01009080 },
.REBOOT3 = (cmd) { 0, 0x0000b1a0 },
.REBOOT4 = (cmd) { 0, 0x01009080 },
.REBOOT5 = (cmd) { 0x300000000, 0x00008092 },
.REBOOT6 = (cmd) { 0x300000000, 0x00008092 },
.DOOR1 = (cmd) { 0x200000000, 0x01018280 },
.DOOR2 = (cmd) { 0x200000000, 0x01009280 },
.LIGHT = (cmd) { 0x200000000, 0x00009281 },
.LOCK = (cmd) { 0x0100000000, 0x0000728c },
};
struct RATGDOStore { struct RATGDOStore {
ISRInternalGPIOPin input_obst; ISRInternalGPIOPin input_obst;
@ -87,27 +118,32 @@ namespace ratgdo {
uint32_t rollingCodeCounter { 0 }; uint32_t rollingCodeCounter { 0 };
uint32_t lastSyncedRollingCodeCounter { 0 }; uint32_t lastSyncedRollingCodeCounter { 0 };
uint16_t previousOpenings { 0 }; // number of times the door has been opened
uint16_t openings { 0 }; // number of times the door has been opened
uint8_t txRollingCode[CODE_LENGTH]; uint8_t txRollingCode[CODE_LENGTH];
uint8_t rxRollingCode[CODE_LENGTH]; uint8_t rxRollingCode[CODE_LENGTH];
uint8_t previousDoorState { DoorState::DOOR_STATE_UNKNOWN }; uint16_t previousOpenings { 0 }; // number of times the door has been opened
uint8_t previousLightState { LightState::LIGHT_STATE_UNKNOWN }; uint16_t openings { 0 }; // number of times the door has been opened
uint8_t previousLockState { LockState::LOCK_STATE_UNKNOWN };
uint8_t previousObstructionState { ObstructionState::OBSTRUCTION_STATE_UNKNOWN };
uint8_t previousMotorState { MotorState::MOTOR_STATE_UNKNOWN };
uint8_t previousButtonState { ButtonState::BUTTON_STATE_UNKNOWN };
uint8_t previousMotionState { MotionState::MOTION_STATE_UNKNOWN };
uint8_t doorState { DoorState::DOOR_STATE_UNKNOWN }; DoorState previousDoorState { DoorState::DOOR_STATE_UNKNOWN };
uint8_t lightState { LightState::LIGHT_STATE_UNKNOWN }; DoorState doorState { DoorState::DOOR_STATE_UNKNOWN };
uint8_t lockState { LockState::LOCK_STATE_UNKNOWN };
uint8_t obstructionState { ObstructionState::OBSTRUCTION_STATE_UNKNOWN }; LightState previousLightState { LightState::LIGHT_STATE_UNKNOWN };
uint8_t motorState { MotorState::MOTOR_STATE_UNKNOWN }; LightState lightState { LightState::LIGHT_STATE_UNKNOWN };
uint8_t buttonState { ButtonState::BUTTON_STATE_UNKNOWN };
uint8_t motionState { MotionState::MOTION_STATE_UNKNOWN }; LockState previousLockState { LockState::LOCK_STATE_UNKNOWN };
LockState lockState { LockState::LOCK_STATE_UNKNOWN };
ObstructionState previousObstructionState { ObstructionState::OBSTRUCTION_STATE_UNKNOWN };
ObstructionState obstructionState { ObstructionState::OBSTRUCTION_STATE_UNKNOWN };
MotorState previousMotorState { MotorState::MOTOR_STATE_UNKNOWN };
MotorState motorState { MotorState::MOTOR_STATE_UNKNOWN };
ButtonState previousButtonState { ButtonState::BUTTON_STATE_UNKNOWN };
ButtonState buttonState { ButtonState::BUTTON_STATE_UNKNOWN };
MotionState previousMotionState { MotionState::MOTION_STATE_UNKNOWN };
MotionState motionState { MotionState::MOTION_STATE_UNKNOWN };
void set_output_gdo_pin(InternalGPIOPin* pin) { this->output_gdo_pin_ = pin; }; void set_output_gdo_pin(InternalGPIOPin* pin) { this->output_gdo_pin_ = pin; };
void set_input_gdo_pin(InternalGPIOPin* pin) { this->input_gdo_pin_ = pin; }; void set_input_gdo_pin(InternalGPIOPin* pin) { this->input_gdo_pin_ = pin; };
@ -115,29 +151,33 @@ namespace ratgdo {
/********************************** FUNCTION DECLARATION /********************************** FUNCTION DECLARATION
* *****************************************/ * *****************************************/
void transmit(cmd command); void transmit(command::cmd command, uint32_t data = 0, bool increment = true);
void sync(); void sync();
void gdoStateLoop(); void gdoStateLoop();
void obstructionLoop(); void obstructionLoop();
void statusUpdateLoop(); void statusUpdateLoop();
void sendCommandAndSaveCounter(cmd command); void sendCommandAndSaveCounter(command::cmd command, uint32_t data = 0, bool increment = true);
void doorCommand(uint32_t data);
void toggleDoor(); void toggleDoor();
void openDoor(); void openDoor();
void closeDoor(); void closeDoor();
void stopDoor(); void stopDoor();
void toggleLight(); void toggleLight();
void lightOn(); void lightOn();
void lightOff(); void lightOff();
bool isLightOn();
void toggleLock(); void toggleLock();
void lock(); void lock();
void unlock(); void unlock();
void query(); void query();
void printRollingCode(); void printRollingCode();
void getRollingCode(cmd command); void getRollingCode(command::cmd command, uint32_t data, bool increment);
uint16_t readRollingCode(); uint16_t readRollingCode();
void incrementRollingCodeCounter(); void incrementRollingCodeCounter();
void sendRollingCodeChanged(); void sendRollingCodeChanged();

View File

@ -38,6 +38,20 @@ namespace ratgdo {
} }
} }
LightState light_state_toggle(LightState state)
{
switch (state) {
case LIGHT_STATE_OFF:
return LIGHT_STATE_ON;
case LIGHT_STATE_ON:
return LIGHT_STATE_OFF;
// 2 and 3 appears sometimes
case LIGHT_STATE_UNKNOWN:
default:
return LIGHT_STATE_UNKNOWN;
}
}
const char* lock_state_to_string(LockState state) const char* lock_state_to_string(LockState state)
{ {
switch (state) { switch (state) {
@ -51,6 +65,20 @@ namespace ratgdo {
} }
} }
LockState lock_state_toggle(LockState state)
{
switch (state) {
case LOCK_STATE_UNLOCKED:
return LOCK_STATE_LOCKED;
case LOCK_STATE_LOCKED:
return LOCK_STATE_UNLOCKED;
// 2 and 3 appears sometimes
case LOCK_STATE_UNKNOWN:
default:
return LOCK_STATE_UNKNOWN;
}
}
const char* motion_state_to_string(MotionState state) const char* motion_state_to_string(MotionState state)
{ {
switch (state) { switch (state) {

View File

@ -38,6 +38,7 @@ namespace ratgdo {
LIGHT_STATE_UNKNOWN = 2, LIGHT_STATE_UNKNOWN = 2,
}; };
const char* light_state_to_string(LightState state); const char* light_state_to_string(LightState state);
LightState light_state_toggle(LightState state);
/// Enum for all states a the lock can be in. /// Enum for all states a the lock can be in.
enum LockState : uint8_t { enum LockState : uint8_t {
@ -46,6 +47,7 @@ namespace ratgdo {
LOCK_STATE_UNKNOWN = 2, LOCK_STATE_UNKNOWN = 2,
}; };
const char* lock_state_to_string(LockState state); const char* lock_state_to_string(LockState state);
LockState lock_state_toggle(LockState state);
/// Enum for all states a the motion can be in. /// Enum for all states a the motion can be in.
enum MotionState : uint8_t { enum MotionState : uint8_t {