set gpio output depending on lock state

This commit is contained in:
technyon
2023-04-07 14:00:05 +02:00
parent e3237a648c
commit a30b716869
5 changed files with 128 additions and 32 deletions

View File

@@ -6,23 +6,20 @@
#include "PreferencesKeys.h" #include "PreferencesKeys.h"
Gpio* Gpio::_inst = nullptr; Gpio* Gpio::_inst = nullptr;
NukiWrapper* Gpio::_nuki = nullptr; unsigned long Gpio::_debounceTs = 0;
unsigned long Gpio::_lockedTs = 0;
const uint Gpio::_debounceTime = 1000; const uint Gpio::_debounceTime = 1000;
Gpio::Gpio(Preferences* preferences, NukiWrapper* nuki) Gpio::Gpio(Preferences* preferences)
: _preferences(preferences) : _preferences(preferences)
{ {
_inst = this; _inst = this;
loadPinConfiguration(); loadPinConfiguration();
_inst->init(nuki); _inst->init();
} }
void Gpio::init(NukiWrapper* nuki) void Gpio::init()
{ {
_nuki = nuki;
for(const auto& entry : _inst->_pinConfiguration) for(const auto& entry : _inst->_pinConfiguration)
{ {
const auto it = std::find(_inst->availablePins().begin(), _inst->availablePins().end(), entry.pin); const auto it = std::find(_inst->availablePins().begin(), _inst->availablePins().end(), entry.pin);
@@ -118,6 +115,19 @@ const std::vector<PinEntry> &Gpio::pinConfiguration() const
return _pinConfiguration; 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 String Gpio::getRoleDescription(PinRole role) const
{ {
switch(role) switch(role)
@@ -147,6 +157,7 @@ void Gpio::getConfigurationText(String& text, const std::vector<PinEntry>& pinCo
{ {
if(entry.role != PinRole::Disabled) if(entry.role != PinRole::Disabled)
{ {
text.concat("GPIO ");
text.concat(entry.pin); text.concat(entry.pin);
if(entry.pin < 10) if(entry.pin < 10)
{ {
@@ -164,23 +175,41 @@ const std::vector<PinRole>& Gpio::getAllRoles() const
return _allRoles; return _allRoles;
} }
void Gpio::notify(const GpioAction &action)
{
for(auto& callback : _callbacks)
{
callback(action);
}
}
void Gpio::addCallback(std::function<void(const GpioAction&)> callback)
{
_callbacks.push_back(callback);
}
void Gpio::isrLock() void Gpio::isrLock()
{ {
if(millis() < _lockedTs) return; if(millis() < _debounceTs) return;
_nuki->lock(); _inst->notify(GpioAction::Lock);
_lockedTs = millis() + _debounceTime; _debounceTs = millis() + _debounceTime;
} }
void Gpio::isrUnlock() void Gpio::isrUnlock()
{ {
if(millis() < _lockedTs) return; if(millis() < _debounceTs) return;
_nuki->unlock(); _inst->notify(GpioAction::Unlock);
_lockedTs = millis() + _debounceTime; _debounceTs = millis() + _debounceTime;
} }
void Gpio::isrUnlatch() void Gpio::isrUnlatch()
{ {
if(millis() < _lockedTs) return; if(millis() < _debounceTs) return;
_nuki->unlatch(); _inst->notify(GpioAction::Unlatch);
_lockedTs = millis() + _debounceTime; _debounceTs = millis() + _debounceTime;
} }
void Gpio::setPinOutput(const uint8_t& pin, const uint8_t& state)
{
digitalWrite(pin, state);
}

28
Gpio.h
View File

@@ -1,7 +1,8 @@
#pragma once #pragma once
#include <functional>
#include "NukiWrapper.h" #include <Preferences.h>
#include <vector>
enum class PinRole enum class PinRole
{ {
@@ -13,6 +14,13 @@ enum class PinRole
OutputHighUnlocked, OutputHighUnlocked,
}; };
enum class GpioAction
{
Lock,
Unlock,
Unlatch
};
struct PinEntry struct PinEntry
{ {
uint8_t pin = 0; uint8_t pin = 0;
@@ -22,8 +30,10 @@ struct PinEntry
class Gpio class Gpio
{ {
public: public:
Gpio(Preferences* preferences, NukiWrapper* nuki); Gpio(Preferences* preferences);
static void init(NukiWrapper* nuki); static void init();
void addCallback(std::function<void(const GpioAction&)> callback);
void loadPinConfiguration(); void loadPinConfiguration();
void savePinConfiguration(const std::vector<PinEntry>& pinConfiguration); void savePinConfiguration(const std::vector<PinEntry>& pinConfiguration);
@@ -31,12 +41,17 @@ public:
const std::vector<uint8_t>& availablePins() const; const std::vector<uint8_t>& availablePins() const;
const std::vector<PinEntry>& pinConfiguration() const; const std::vector<PinEntry>& pinConfiguration() const;
PinRole getPinRole(uint8_t pin);
String getRoleDescription(PinRole role) const; String getRoleDescription(PinRole role) const;
void getConfigurationText(String& text, const std::vector<PinEntry>& pinConfiguration) const; void getConfigurationText(String& text, const std::vector<PinEntry>& pinConfiguration) const;
const std::vector<PinRole>& getAllRoles() const; const std::vector<PinRole>& getAllRoles() const;
void setPinOutput(const uint8_t& pin, const uint8_t& state);
private: private:
void notify(const GpioAction& action);
const std::vector<uint8_t> _availablePins = { 2, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 32, 33 }; const std::vector<uint8_t> _availablePins = { 2, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 32, 33 };
const std::vector<PinRole> _allRoles = const std::vector<PinRole> _allRoles =
{ {
@@ -55,9 +70,10 @@ private:
static void IRAM_ATTR isrUnlock(); static void IRAM_ATTR isrUnlock();
static void IRAM_ATTR isrUnlatch(); static void IRAM_ATTR isrUnlatch();
std::vector<std::function<void(const GpioAction&)>> _callbacks;
static Gpio* _inst; static Gpio* _inst;
static NukiWrapper* _nuki; static unsigned long _debounceTs;
static unsigned long _lockedTs;
Preferences* _preferences = nullptr; Preferences* _preferences = nullptr;
}; };

View File

@@ -8,11 +8,12 @@
NukiWrapper* nukiInst; 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), : _deviceName(deviceName),
_bleScanner(scanner), _bleScanner(scanner),
_nukiLock(deviceName, id), _nukiLock(deviceName, id),
_network(network), _network(network),
_gpio(gpio),
_preferences(preferences) _preferences(preferences)
{ {
nukiInst = this; nukiInst = this;
@@ -26,6 +27,8 @@ NukiWrapper::NukiWrapper(const std::string& deviceName, uint32_t id, BleScanner:
network->setLockActionReceivedCallback(nukiInst->onLockActionReceivedCallback); network->setLockActionReceivedCallback(nukiInst->onLockActionReceivedCallback);
network->setConfigUpdateReceivedCallback(nukiInst->onConfigUpdateReceivedCallback); network->setConfigUpdateReceivedCallback(nukiInst->onConfigUpdateReceivedCallback);
network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback); network->setKeypadCommandReceivedCallback(nukiInst->onKeypadCommandReceivedCallback);
_gpio->addCallback(NukiWrapper::gpioActionCallback);
} }
@@ -307,6 +310,7 @@ void NukiWrapper::updateKeyTurnerState()
_retryLockstateCount = 0; _retryLockstateCount = 0;
_network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState); _network->publishKeyTurnerState(_keyTurnerState, _lastKeyTurnerState);
updateGpioOutputs();
char lockStateStr[20]; char lockStateStr[20];
lockstateToString(_keyTurnerState.lockState, lockStateStr); lockstateToString(_keyTurnerState.lockState, lockStateStr);
@@ -446,6 +450,22 @@ void NukiWrapper::onKeypadCommandReceivedCallback(const char *command, const uin
nukiInst->onKeypadCommandReceived(command, id, name, code, enabled); 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) void NukiWrapper::onConfigUpdateReceived(const char *topic, const char *value)
{ {
if(strcmp(topic, mqtt_topic_config_button_enabled) == 0) if(strcmp(topic, mqtt_topic_config_button_enabled) == 0)
@@ -719,3 +739,26 @@ void NukiWrapper::disableWatchdog()
{ {
_restartBeaconTimeout = -1; _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;
}
}
}

View File

@@ -5,11 +5,12 @@
#include "NukiDataTypes.h" #include "NukiDataTypes.h"
#include "BleScanner.h" #include "BleScanner.h"
#include "NukiLock.h" #include "NukiLock.h"
#include "Gpio.h"
class NukiWrapper : public Nuki::SmartlockEventHandler class NukiWrapper : public Nuki::SmartlockEventHandler
{ {
public: 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(); virtual ~NukiWrapper();
void initialize(const bool& firstStart); void initialize(const bool& firstStart);
@@ -42,6 +43,8 @@ private:
static bool onLockActionReceivedCallback(const char* value); static bool onLockActionReceivedCallback(const char* value);
static void onConfigUpdateReceivedCallback(const char* topic, 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 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 onConfigUpdateReceived(const char* topic, const char* value);
void onKeypadCommandReceived(const char* command, const uint& id, const String& name, const String& code, const int& enabled); 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 updateKeypad();
void postponeBleWatchdog(); void postponeBleWatchdog();
void updateGpioOutputs();
void readConfig(); void readConfig();
void readAdvancedConfig(); void readAdvancedConfig();
@@ -63,8 +68,9 @@ private:
std::string _deviceName; std::string _deviceName;
NukiLock::NukiLock _nukiLock; NukiLock::NukiLock _nukiLock;
BleScanner::Scanner* _bleScanner; BleScanner::Scanner* _bleScanner = nullptr;
NetworkLock* _network; NetworkLock* _network = nullptr;
Gpio* _gpio = nullptr;
Preferences* _preferences; Preferences* _preferences;
int _intervalLockstate = 0; // seconds int _intervalLockstate = 0; // seconds
int _intervalBattery = 0; // seconds int _intervalBattery = 0; // seconds

View File

@@ -208,16 +208,18 @@ void setup()
bleScanner->initialize("NukiHub"); bleScanner->initialize("NukiHub");
bleScanner->setScanDuration(10); 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")); Log->println(lockEnabled ? F("NUKI Lock enabled") : F("NUKI Lock disabled"));
if(lockEnabled) if(lockEnabled)
{ {
nuki = new NukiWrapper("NukiHub", deviceId, bleScanner, networkLock, preferences); nuki = new NukiWrapper("NukiHub", deviceId, bleScanner, networkLock, gpio, preferences);
nuki->initialize(firstStart); 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)) // if(preferences->getBool(preference_gpio_locking_enabled))
// { // {