diff --git a/Gpio.cpp b/Gpio.cpp index 42f85d3..51df9d9 100644 --- a/Gpio.cpp +++ b/Gpio.cpp @@ -6,23 +6,20 @@ #include "PreferencesKeys.h" Gpio* Gpio::_inst = nullptr; -NukiWrapper* Gpio::_nuki = nullptr; -unsigned long Gpio::_lockedTs = 0; +unsigned long Gpio::_debounceTs = 0; const uint Gpio::_debounceTime = 1000; -Gpio::Gpio(Preferences* preferences, NukiWrapper* nuki) +Gpio::Gpio(Preferences* preferences) : _preferences(preferences) { _inst = this; loadPinConfiguration(); - _inst->init(nuki); + _inst->init(); } -void Gpio::init(NukiWrapper* nuki) +void Gpio::init() { - _nuki = nuki; - for(const auto& entry : _inst->_pinConfiguration) { const auto it = std::find(_inst->availablePins().begin(), _inst->availablePins().end(), entry.pin); @@ -118,6 +115,19 @@ const std::vector &Gpio::pinConfiguration() const return _pinConfiguration; } +PinRole Gpio::getPinRole(uint8_t pin) +{ + for(const auto& entry : _pinConfiguration) + { + if(entry.pin == pin) + { + return entry.role; + } + } + + return PinRole::Disabled; +} + String Gpio::getRoleDescription(PinRole role) const { switch(role) @@ -147,6 +157,7 @@ void Gpio::getConfigurationText(String& text, const std::vector& pinCo { if(entry.role != PinRole::Disabled) { + text.concat("GPIO "); text.concat(entry.pin); if(entry.pin < 10) { @@ -164,23 +175,41 @@ const std::vector& Gpio::getAllRoles() const return _allRoles; } +void Gpio::notify(const GpioAction &action) +{ + for(auto& callback : _callbacks) + { + callback(action); + } +} + +void Gpio::addCallback(std::function callback) +{ + _callbacks.push_back(callback); +} + void Gpio::isrLock() { - if(millis() < _lockedTs) return; - _nuki->lock(); - _lockedTs = millis() + _debounceTime; + if(millis() < _debounceTs) return; + _inst->notify(GpioAction::Lock); + _debounceTs = millis() + _debounceTime; } void Gpio::isrUnlock() { - if(millis() < _lockedTs) return; - _nuki->unlock(); - _lockedTs = millis() + _debounceTime; + if(millis() < _debounceTs) return; + _inst->notify(GpioAction::Unlock); + _debounceTs = millis() + _debounceTime; } void Gpio::isrUnlatch() { - if(millis() < _lockedTs) return; - _nuki->unlatch(); - _lockedTs = millis() + _debounceTime; -} \ No newline at end of file + if(millis() < _debounceTs) return; + _inst->notify(GpioAction::Unlatch); + _debounceTs = millis() + _debounceTime; +} + +void Gpio::setPinOutput(const uint8_t& pin, const uint8_t& state) +{ + digitalWrite(pin, state); +} diff --git a/Gpio.h b/Gpio.h index a2b8ff6..e36f498 100644 --- a/Gpio.h +++ b/Gpio.h @@ -1,7 +1,8 @@ #pragma once - -#include "NukiWrapper.h" +#include +#include +#include enum class PinRole { @@ -13,6 +14,13 @@ enum class PinRole OutputHighUnlocked, }; +enum class GpioAction +{ + Lock, + Unlock, + Unlatch +}; + struct PinEntry { uint8_t pin = 0; @@ -22,8 +30,10 @@ struct PinEntry class Gpio { public: - Gpio(Preferences* preferences, NukiWrapper* nuki); - static void init(NukiWrapper* nuki); + Gpio(Preferences* preferences); + static void init(); + + void addCallback(std::function callback); void loadPinConfiguration(); void savePinConfiguration(const std::vector& pinConfiguration); @@ -31,12 +41,17 @@ public: const std::vector& availablePins() const; const std::vector& pinConfiguration() const; + PinRole getPinRole(uint8_t pin); String getRoleDescription(PinRole role) const; void getConfigurationText(String& text, const std::vector& pinConfiguration) const; const std::vector& getAllRoles() const; + void setPinOutput(const uint8_t& pin, const uint8_t& state); + private: + void notify(const GpioAction& action); + const std::vector _availablePins = { 2, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 32, 33 }; const std::vector _allRoles = { @@ -55,9 +70,10 @@ private: static void IRAM_ATTR isrUnlock(); static void IRAM_ATTR isrUnlatch(); + std::vector> _callbacks; + static Gpio* _inst; - static NukiWrapper* _nuki; - static unsigned long _lockedTs; + static unsigned long _debounceTs; Preferences* _preferences = nullptr; }; diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index 24ac52a..28c517b 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -8,11 +8,12 @@ NukiWrapper* nukiInst; -NukiWrapper::NukiWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, NetworkLock* network, Preferences* preferences) +NukiWrapper::NukiWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, NetworkLock* network, Gpio* gpio, Preferences* preferences) : _deviceName(deviceName), _bleScanner(scanner), _nukiLock(deviceName, id), _network(network), + _gpio(gpio), _preferences(preferences) { nukiInst = this; @@ -26,6 +27,8 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, uint32_t id, BleScanner: network->setLockActionReceivedCallback(nukiInst->onLockActionReceivedCallback); network->setConfigUpdateReceivedCallback(nukiInst->onConfigUpdateReceivedCallback); network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback); + + _gpio->addCallback(NukiWrapper::gpioActionCallback); } @@ -307,6 +310,7 @@ void NukiWrapper::updateKeyTurnerState() _retryLockstateCount = 0; _network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState); + updateGpioOutputs(); char lockStateStr[20]; lockstateToString(_keyTurnerState.lockState, lockStateStr); @@ -446,6 +450,22 @@ void NukiWrapper::onKeypadCommandReceivedCallback(const char *command, const uin nukiInst->onKeypadCommandReceived(command, id, name, code, enabled); } +void NukiWrapper::gpioActionCallback(const GpioAction &action) +{ + switch(action) + { + case GpioAction::Lock: + nukiInst->lock(); + break; + case GpioAction::Unlock: + nukiInst->unlock(); + break; + case GpioAction::Unlatch: + nukiInst->unlatch(); + break; + } +} + void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value) { if(strcmp(topic, mqtt_topic_config_button_enabled) == 0) @@ -719,3 +739,26 @@ void NukiWrapper::disableWatchdog() { _restartBeaconTimeout = -1; } + +void NukiWrapper::updateGpioOutputs() +{ + using namespace NukiLock; + + const auto& pinConfiguration = _gpio->pinConfiguration(); + + const LockState& lockState = _keyTurnerState.lockState; + + for(const auto& entry : pinConfiguration) + { + switch(entry.role) + { + case PinRole::OutputHighLocked: + _gpio->setPinOutput(entry.pin, lockState == LockState::Locked || lockState == LockState::Locking ? HIGH : LOW); + break; + case PinRole::OutputHighUnlocked: + _gpio->setPinOutput(entry.pin, lockState == LockState::Locked || lockState == LockState::Locking ? LOW : HIGH); + break; + } + } +} + diff --git a/NukiWrapper.h b/NukiWrapper.h index 081530f..e553f15 100644 --- a/NukiWrapper.h +++ b/NukiWrapper.h @@ -5,11 +5,12 @@ #include "NukiDataTypes.h" #include "BleScanner.h" #include "NukiLock.h" +#include "Gpio.h" class NukiWrapper : public Nuki::SmartlockEventHandler { public: - NukiWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, NetworkLock* network, Preferences* preferences); + NukiWrapper(const std::string& deviceName, uint32_t id, BleScanner::Scanner* scanner, NetworkLock* network, Gpio* gpio, Preferences* preferences); virtual ~NukiWrapper(); void initialize(const bool& firstStart); @@ -42,6 +43,8 @@ private: static bool onLockActionReceivedCallback(const char* value); static void onConfigUpdateReceivedCallback(const char* topic, const char* value); static void onKeypadCommandReceivedCallback(const char* command, const uint& id, const String& name, const String& code, const int& enabled); + static void gpioActionCallback(const GpioAction& action); + void onConfigUpdateReceived(const char* topic, const char* value); void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled); @@ -52,6 +55,8 @@ private: void updateKeypad(); void postponeBleWatchdog(); + void updateGpioOutputs(); + void readConfig(); void readAdvancedConfig(); @@ -63,8 +68,9 @@ private: std::string _deviceName; NukiLock::NukiLock _nukiLock; - BleScanner::Scanner* _bleScanner; - NetworkLock* _network; + BleScanner::Scanner* _bleScanner = nullptr; + NetworkLock* _network = nullptr; + Gpio* _gpio = nullptr; Preferences* _preferences; int _intervalLockstate = 0; // seconds int _intervalBattery = 0; // seconds diff --git a/main.cpp b/main.cpp index a8850d9..a5f2ab2 100644 --- a/main.cpp +++ b/main.cpp @@ -208,16 +208,18 @@ void setup() bleScanner->initialize("NukiHub"); bleScanner->setScanDuration(10); + gpio = new Gpio(preferences); + String gpioDesc; + gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration()); + Serial.print(gpioDesc.c_str()); + Log->println(lockEnabled ? F("NUKI Lock enabled") : F("NUKI Lock disabled")); if(lockEnabled) { - nuki = new NukiWrapper("NukiHub", deviceId, bleScanner, networkLock, preferences); + nuki = new NukiWrapper("NukiHub", deviceId, bleScanner, networkLock, gpio, preferences); nuki->initialize(firstStart); - gpio = new Gpio(preferences, nuki); - String gpioDesc; - gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration()); - Serial.print(gpioDesc.c_str()); + // if(preferences->getBool(preference_gpio_locking_enabled)) // {