From a5e334d4c1d0d46b5d503e70207938e36163f06c Mon Sep 17 00:00:00 2001 From: technyon Date: Sat, 3 Jun 2023 21:20:19 +0200 Subject: [PATCH 01/10] changes for generial input/output via mqtt --- .gitignore | 1 + CMakeLists.txt | 3 + Gpio.cpp | 44 ++++-- Gpio.h | 14 +- MqttTopics.h | 4 +- Network.cpp | 27 +++- Network.h | 7 +- NukiOpenerWrapper.cpp | 2 +- NukiOpenerWrapper.h | 2 +- NukiWrapper.cpp | 2 +- NukiWrapper.h | 2 +- lib/BleScanner/src/BleScanner.cpp | 2 + lib/gpio2go/CMakeLists.txt | 58 +++++++ lib/gpio2go/LICENSE | 21 +++ lib/gpio2go/README.md | 1 + lib/gpio2go/main.cpp | 38 +++++ lib/gpio2go/src/Gpio2Go.cpp | 242 ++++++++++++++++++++++++++++++ lib/gpio2go/src/Gpio2Go.h | 49 ++++++ lib/gpio2go/src/InterruptMode.h | 10 ++ lib/gpio2go/src/PinMode.h | 20 +++ lib/nuki_ble | 2 +- main.cpp | 12 +- networkDevices/NetworkDevice.h | 1 - 23 files changed, 534 insertions(+), 30 deletions(-) create mode 100644 lib/gpio2go/CMakeLists.txt create mode 100644 lib/gpio2go/LICENSE create mode 100644 lib/gpio2go/README.md create mode 100644 lib/gpio2go/main.cpp create mode 100644 lib/gpio2go/src/Gpio2Go.cpp create mode 100644 lib/gpio2go/src/Gpio2Go.h create mode 100644 lib/gpio2go/src/InterruptMode.h create mode 100644 lib/gpio2go/src/PinMode.h diff --git a/.gitignore b/.gitignore index 2be016c..6e63376 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build cmake-build-debug cmake-build-release +cmake-build-release-s3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 03386c4..d52a687 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,9 @@ set(SRCFILES lib/nuki_ble/src/NukiUtils.cpp lib/nuki_ble/src/NukiLockUtils.cpp lib/nuki_ble/src/NukiOpenerUtils.cpp + lib/gpio2go/src/Gpio2Go.cpp + lib/gpio2go/src/InterruptMode.h + lib/gpio2go/src/PinMode.h lib/BleScanner/src/BleInterfaces.h lib/BleScanner/src/BleScanner.cpp lib/MqttLogger/src/MqttLogger.cpp diff --git a/Gpio.cpp b/Gpio.cpp index e3680cc..0a00697 100644 --- a/Gpio.cpp +++ b/Gpio.cpp @@ -5,6 +5,7 @@ #include "Logger.h" #include "PreferencesKeys.h" #include "RestartReason.h" +#include "lib/gpio2go/src/Gpio2Go.h" Gpio* Gpio::_inst = nullptr; unsigned long Gpio::_debounceTs = 0; @@ -65,10 +66,24 @@ void Gpio::init() pinMode(entry.pin, INPUT_PULLUP); attachInterrupt(entry.pin, isrDeactivateRtoCm, FALLING); break; + case PinRole::OutputHighLocked: + case PinRole::OutputHighUnlocked: + case PinRole::OutputHighMotorBlocked: + case PinRole::OutputHighRtoActive: + case PinRole::OutputHighCmActive: + case PinRole::OutputHighRtoOrCmActive: + case PinRole::GeneralOutput: + pinMode(entry.pin, OUTPUT); + break; + case PinRole::GeneralInput: + Gpio2Go::configurePin(entry.pin, PinMode::InputPullup, InterruptMode::Change, 300); + break; default: pinMode(entry.pin, OUTPUT); break; } + + Gpio2Go::subscribe(Gpio::inputCallback); } } @@ -169,6 +184,10 @@ String Gpio::getRoleDescription(PinRole role) const return "Output: High when CM active"; case PinRole::OutputHighRtoOrCmActive: return "Output: High when RTO or CM active"; + case PinRole::GeneralOutput: + return "General output"; + case PinRole::GeneralInput: + return "General input (Pull-up)"; default: return "Unknown"; } @@ -198,15 +217,20 @@ const std::vector& Gpio::getAllRoles() const return _allRoles; } -void Gpio::notify(const GpioAction &action) +void Gpio::notify(const GpioAction &action, const int& pin) { for(auto& callback : _callbacks) { - callback(action); + callback(action, pin); } } -void Gpio::addCallback(std::function callback) +void Gpio::inputCallback(const int &pin) +{ + _inst->notify(GpioAction::GeneralInput, pin); +} + +void Gpio::addCallback(std::function callback) { _callbacks.push_back(callback); } @@ -214,49 +238,49 @@ void Gpio::addCallback(std::function callback) void Gpio::isrLock() { if(millis() < _debounceTs) return; - _inst->notify(GpioAction::Lock); + _inst->notify(GpioAction::Lock, -1); _debounceTs = millis() + _debounceTime; } void Gpio::isrUnlock() { if(millis() < _debounceTs) return; - _inst->notify(GpioAction::Unlock); + _inst->notify(GpioAction::Unlock, -1); _debounceTs = millis() + _debounceTime; } void Gpio::isrUnlatch() { if(millis() < _debounceTs) return; - _inst->notify(GpioAction::Unlatch); + _inst->notify(GpioAction::Unlatch, -1); _debounceTs = millis() + _debounceTime; } void Gpio::isrElectricStrikeActuation() { if(millis() < _debounceTs) return; - _inst->notify(GpioAction::ElectricStrikeActuation); + _inst->notify(GpioAction::ElectricStrikeActuation, -1); _debounceTs = millis() + _debounceTime; } void Gpio::isrActivateRTO() { if(millis() < _debounceTs) return; - _inst->notify(GpioAction::ActivateRTO); + _inst->notify(GpioAction::ActivateRTO, -1); _debounceTs = millis() + _debounceTime; } void Gpio::isrActivateCM() { if(millis() < _debounceTs) return; - _inst->notify(GpioAction::ActivateCM); + _inst->notify(GpioAction::ActivateCM, -1); _debounceTs = millis() + _debounceTime; } void Gpio::isrDeactivateRtoCm() { if(millis() < _debounceTs) return; - _inst->notify(GpioAction::DeactivateRtoCm); + _inst->notify(GpioAction::DeactivateRtoCm, -1); _debounceTs = millis() + _debounceTime; } diff --git a/Gpio.h b/Gpio.h index 768d717..8bdb9f3 100644 --- a/Gpio.h +++ b/Gpio.h @@ -19,7 +19,9 @@ enum class PinRole OutputHighMotorBlocked, OutputHighRtoActive, OutputHighCmActive, - OutputHighRtoOrCmActive + OutputHighRtoOrCmActive, + GeneralOutput, + GeneralInput }; enum class GpioAction @@ -30,7 +32,8 @@ enum class GpioAction ElectricStrikeActuation, ActivateRTO, ActivateCM, - DeactivateRtoCm + DeactivateRtoCm, + GeneralInput }; struct PinEntry @@ -47,7 +50,7 @@ public: void migrateObsoleteSetting(); - void addCallback(std::function callback); + void addCallback(std::function callback); void loadPinConfiguration(); void savePinConfiguration(const std::vector& pinConfiguration); @@ -63,7 +66,8 @@ public: void setPinOutput(const uint8_t& pin, const uint8_t& state); private: - void notify(const GpioAction& action); + void IRAM_ATTR notify(const GpioAction& action, const int& pin); + static void inputCallback(const int & pin); 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 = @@ -94,7 +98,7 @@ private: static void IRAM_ATTR isrActivateCM(); static void IRAM_ATTR isrDeactivateRtoCm(); - std::vector> _callbacks; + std::vector> _callbacks; static Gpio* _inst; static unsigned long _debounceTs; diff --git a/MqttTopics.h b/MqttTopics.h index b80feee..0c0c81a 100644 --- a/MqttTopics.h +++ b/MqttTopics.h @@ -60,4 +60,6 @@ #define mqtt_topic_restart_reason_fw "/maintenance/restartReasonNukiHub" #define mqtt_topic_restart_reason_esp "/maintenance/restartReasonNukiEsp" #define mqtt_topic_mqtt_connection_state "/maintenance/mqttConnectionState" -#define mqtt_topic_network_device "/maintenance/networkDevice" \ No newline at end of file +#define mqtt_topic_network_device "/maintenance/networkDevice" + +#define mqtt_topic_gpio_prefix "/gpio/" diff --git a/Network.cpp b/Network.cpp index 4156a88..9b77546 100644 --- a/Network.cpp +++ b/Network.cpp @@ -14,8 +14,9 @@ bool _versionPublished = false; RTC_NOINIT_ATTR char WiFi_fallbackDetect[14]; -Network::Network(Preferences *preferences, const String& maintenancePathPrefix, char* buffer, size_t bufferSize) +Network::Network(Preferences *preferences, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize) : _preferences(preferences), + _gpio(gpio), _buffer(buffer), _bufferSize(bufferSize) { @@ -137,6 +138,9 @@ void Network::setupDevice() void Network::initialize() { + _networkGpio->initialize(); + registerMqttReceiver(_networkGpio); + _restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; @@ -204,6 +208,20 @@ void Network::initialize() } _publishDebugInfo = _preferences->getBool(preference_publish_debug_info); + + for(const auto& pinEntry : _gpio->pinConfiguration()) + { + switch(pinEntry.role) + { + case PinRole::GeneralInput: + String inp = "input_"; + inp.concat(pinEntry.pin); + initTopic(mqtt_topic_gpio_prefix, inp.c_str(), ) + break; + case PinRole::GeneralOutput: + break; + } + } } bool Network::update() @@ -303,6 +321,8 @@ bool Network::update() _lastMaintenanceTs = ts; } + _networkGpio->update(); + return true; } @@ -1238,3 +1258,8 @@ void Network::disableMqtt() _device->disableMqtt(); _mqttEnabled = false; } + +NetworkDevice *Network::device() +{ + return _device; +} diff --git a/Network.h b/Network.h index 1ef54fc..daf8594 100644 --- a/Network.h +++ b/Network.h @@ -7,6 +7,8 @@ #include "MqttReceiver.h" #include "networkDevices/IPConfiguration.h" #include "MqttTopics.h" +#include "Gpio.h" +#include "NetworkGpio.h" enum class NetworkDeviceType { @@ -23,7 +25,7 @@ enum class NetworkDeviceType class Network { public: - explicit Network(Preferences* preferences, const String& maintenancePathPrefix, char* buffer, size_t bufferSize); + explicit Network(Preferences* preferences, Gpio* gpio, const String& maintenancePathPrefix, char* buffer, size_t bufferSize); void initialize(); bool update(); @@ -70,6 +72,8 @@ public: void setKeepAliveCallback(std::function reconnectTick); void addReconnectedCallback(std::function reconnectedCallback); + NetworkDevice* device(); + private: static void onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); void onMqttDataReceived(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); @@ -105,6 +109,7 @@ private: char _mqttConnectionStateTopic[211] = {0}; Preferences* _preferences; + Gpio* _gpio; IPConfiguration* _ipConfiguration = nullptr; String _hostname; char _hostnameArr[101] = {0}; diff --git a/NukiOpenerWrapper.cpp b/NukiOpenerWrapper.cpp index df4df28..fd0bc55 100644 --- a/NukiOpenerWrapper.cpp +++ b/NukiOpenerWrapper.cpp @@ -482,7 +482,7 @@ void NukiOpenerWrapper::onKeypadCommandReceivedCallback(const char *command, con nukiOpenerInst->onKeypadCommandReceived(command, id, name, code, enabled); } -void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action) +void NukiOpenerWrapper::gpioActionCallback(const GpioAction &action, const int& pin) { switch(action) { diff --git a/NukiOpenerWrapper.h b/NukiOpenerWrapper.h index da80621..276c3cc 100644 --- a/NukiOpenerWrapper.h +++ b/NukiOpenerWrapper.h @@ -46,7 +46,7 @@ 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); + static void gpioActionCallback(const GpioAction& action, const int& pin); 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); diff --git a/NukiWrapper.cpp b/NukiWrapper.cpp index 8b527f8..c4946d4 100644 --- a/NukiWrapper.cpp +++ b/NukiWrapper.cpp @@ -450,7 +450,7 @@ void NukiWrapper::onKeypadCommandReceivedCallback(const char *command, const uin nukiInst->onKeypadCommandReceived(command, id, name, code, enabled); } -void NukiWrapper::gpioActionCallback(const GpioAction &action) +void NukiWrapper::gpioActionCallback(const GpioAction &action, const int& pin) { switch(action) { diff --git a/NukiWrapper.h b/NukiWrapper.h index e553f15..5572354 100644 --- a/NukiWrapper.h +++ b/NukiWrapper.h @@ -43,7 +43,7 @@ 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); + static void gpioActionCallback(const GpioAction& action, const int& pin); 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); diff --git a/lib/BleScanner/src/BleScanner.cpp b/lib/BleScanner/src/BleScanner.cpp index be78ddb..4fdba82 100644 --- a/lib/BleScanner/src/BleScanner.cpp +++ b/lib/BleScanner/src/BleScanner.cpp @@ -36,6 +36,8 @@ void Scanner::update() { return; } + bleScan->clearResults(); + bool result = bleScan->start(scanDuration, nullptr, false); if (!result) { scanErrors++; diff --git a/lib/gpio2go/CMakeLists.txt b/lib/gpio2go/CMakeLists.txt new file mode 100644 index 0000000..6fbd13c --- /dev/null +++ b/lib/gpio2go/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.0.0) + +if(NOT ARDUINO_BOARD) + set(ARDUINO_BOARD "ESP32 Dev Module [esp32.esp32]") +endif() + +project(gpio2go CXX) + +# ARDUHAL_LOG_LEVEL_NONE, define ARDUHAL_LOG_LEVEL_ERROR, define ARDUHAL_LOG_LEVEL_WARN, define ARDUHAL_LOG_LEVEL_INFO, +# define ARDUHAL_LOG_LEVEL_DEBUG, define ARDUHAL_LOG_LEVEL_VERBOSE + +set(LOG_LEVEL ARDUHAL_LOG_LEVEL_NONE) + +#add_compile_definitions(DEBUG_SENSE_NUKI) +#add_compile_definitions(DEBUG_NUKI_COMMAND) +#add_compile_definitions(DEBUG_NUKI_CONNECT) +#add_compile_definitions(DEBUG_NUKI_COMMUNICATION) +#add_compile_definitions(DEBUG_NUKI_HEX_DATA) +#add_compile_definitions(DEBUG_NUKI_READABLE_DATA) + +add_compile_definitions(ESP_PLATFORM) +add_compile_definitions(ESP32) +add_compile_definitions(ARDUINO_ARCH_ESP32) + +include_directories(${PROJECT_NAME} + PRIVATE + src +) + +set(SRCFILES + src/PinMode.h + src/Gpio2Go.cpp + src/InterruptMode.h +) + +file(GLOB_RECURSE SRCFILESREC + +) + +add_executable(${PROJECT_NAME} + main.cpp + ${SRCFILES} + ${SRCFILESREC} + ) + +target_compile_definitions(${PROJECT_NAME} + PRIVATE + ARDUHAL_LOG_LEVEL=${LOG_LEVEL} + CORE_DEBUG_LEVEL=${LOG_LEVEL} + ) + +target_link_arduino_libraries(${PROJECT_NAME} + PRIVATE + core +) + +target_enable_arduino_upload(${PROJECT_NAME}) + diff --git a/lib/gpio2go/LICENSE b/lib/gpio2go/LICENSE new file mode 100644 index 0000000..cef1a2b --- /dev/null +++ b/lib/gpio2go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Jan-Ole Schümann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/gpio2go/README.md b/lib/gpio2go/README.md new file mode 100644 index 0000000..b173d34 --- /dev/null +++ b/lib/gpio2go/README.md @@ -0,0 +1 @@ +# gpio2go \ No newline at end of file diff --git a/lib/gpio2go/main.cpp b/lib/gpio2go/main.cpp new file mode 100644 index 0000000..0c6330b --- /dev/null +++ b/lib/gpio2go/main.cpp @@ -0,0 +1,38 @@ +#include "Arduino.h" +#include "Gpio2Go.h" + +#define INPUT_PIN 21 + +bool hasMessage = false; +String message; + +void inputCb(const int & pin) +{ + message = ""; + message.concat("Input, Pin "); + message.concat(pin); + message.concat(" "); + message.concat(", state "); + message.concat(digitalRead(INPUT_PIN) ? "High" : "Low"); + hasMessage = true; +} + +void setup() +{ + Serial.begin(115200); + + delay(1100); + Serial.println(F("Started")); + Gpio2Go::configurePin(INPUT_PIN, PinMode::InputPullup, InterruptMode::Change, 200); + Gpio2Go::subscribe(inputCb); +} + +void loop() +{ + delay(100); + if(hasMessage) + { + hasMessage = false; + Serial.println(message); + } +} \ No newline at end of file diff --git a/lib/gpio2go/src/Gpio2Go.cpp b/lib/gpio2go/src/Gpio2Go.cpp new file mode 100644 index 0000000..e3e4965 --- /dev/null +++ b/lib/gpio2go/src/Gpio2Go.cpp @@ -0,0 +1,242 @@ +#include "Gpio2Go.h" + + +void Gpio2Go::configurePin(int pin, PinMode pin_Mode, InterruptMode interrupt_Mode, uint16_t timeoutAfterTriggerMS) +{ + timeoutDurations[pin - GPIO2GO_NR_FIRST_PIN] = timeoutAfterTriggerMS; + + switch(pin_Mode) + { + case PinMode::InputPullup: + pinMode(pin, INPUT_PULLUP); + attachIsr(pin, interrupt_Mode); + break; + case PinMode::InputPullDown: + pinMode(pin, INPUT_PULLDOWN); + attachIsr(pin, interrupt_Mode); + break; + case PinMode::Output: + pinMode(pin, OUTPUT); + break; + } +} + +void Gpio2Go::subscribe(std::function callback) +{ + subscriptions.push_back(callback); +} + +unsigned long Gpio2Go::getLastTriggeredMillis(const int &pin) +{ + if(pin >= GPIO2GO_NR_FIRST_PIN && pin <= (GPIO2GO_NR_OF_PINS + GPIO2GO_NR_FIRST_PIN)) + { + return lastTriggeredTimestamps[pin - GPIO2GO_NR_FIRST_PIN]; + } + return -1; +} + +void Gpio2Go::attachIsr(int pin, InterruptMode interruptMode) +{ + switch(pin) + { + case 2: + attachInterrupt(2, isrGpio2, resolveInterruptMode(interruptMode)); + break; + case 4: + attachInterrupt(4, isrGpio4, resolveInterruptMode(interruptMode)); + break; + case 5: + attachInterrupt(5, isrGpio5, resolveInterruptMode(interruptMode)); + break; + case 13: + attachInterrupt(13, isrGpio13, resolveInterruptMode(interruptMode)); + break; + case 14: + attachInterrupt(14, isrGpio14, resolveInterruptMode(interruptMode)); + break; + case 15: + attachInterrupt(15, isrGpio15, resolveInterruptMode(interruptMode)); + break; + case 16: + attachInterrupt(16, isrGpio16, resolveInterruptMode(interruptMode)); + break; + case 17: + attachInterrupt(17, isrGpio17, resolveInterruptMode(interruptMode)); + break; + case 18: + attachInterrupt(18, isrGpio18, resolveInterruptMode(interruptMode)); + break; + case 19: + attachInterrupt(19, isrGpio19, resolveInterruptMode(interruptMode)); + break; + case 20: + attachInterrupt(20, isrGpio20, resolveInterruptMode(interruptMode)); + break; + case 21: + attachInterrupt(21, isrGpio21, resolveInterruptMode(interruptMode)); + break; + case 22: + attachInterrupt(22, isrGpio22, resolveInterruptMode(interruptMode)); + break; + case 23: + attachInterrupt(23, isrGpio23, resolveInterruptMode(interruptMode)); + break; + case 24: + attachInterrupt(24, isrGpio24, resolveInterruptMode(interruptMode)); + break; + case 25: + attachInterrupt(25, isrGpio25, resolveInterruptMode(interruptMode)); + break; + case 26: + attachInterrupt(26, isrGpio26, resolveInterruptMode(interruptMode)); + break; + case 27: + attachInterrupt(27, isrGpio27, resolveInterruptMode(interruptMode)); + break; + case 32: + attachInterrupt(32, isrGpio32, resolveInterruptMode(interruptMode)); + break; + case 33: + attachInterrupt(33, isrGpio33, resolveInterruptMode(interruptMode)); + break; + default: + throw std::runtime_error("Gpio2Go: Unsupported pin."); + } +} + +int Gpio2Go::resolveInterruptMode(InterruptMode interruptMode) +{ + switch(interruptMode) + { + case InterruptMode::Rising: + return RISING; + case InterruptMode::Falling: + return FALLING; + case InterruptMode::Change: + return CHANGE; + case InterruptMode::OnLow: + return ONLOW; + case InterruptMode::OnHigh: + return ONHIGH; + default: + throw std::runtime_error("Gpio2Go: Unsupported interrupt mode."); + } +} + +void Gpio2Go::isrHandler(int pin) +{ + unsigned long timeout = lastTriggeredTimestamps[pin - GPIO2GO_NR_FIRST_PIN]; + if(timeoutDurations[pin - GPIO2GO_NR_FIRST_PIN] != 0 && (millis() - timeout) < timeoutDurations[pin - GPIO2GO_NR_FIRST_PIN]) return; + lastTriggeredTimestamps[pin - GPIO2GO_NR_FIRST_PIN] = millis(); + + bool state = digitalRead(pin) == HIGH; + + for(const auto& callback : subscriptions) + { + callback(pin); + } +} + +void Gpio2Go::isrGpio2() +{ + isrHandler(2); +} + +void Gpio2Go::isrGpio4() +{ + isrHandler(4); +} + +void Gpio2Go::isrGpio5() +{ + isrHandler(5); +} + +void Gpio2Go::isrGpio13() +{ + isrHandler(13); +} + +void Gpio2Go::isrGpio14() +{ + isrHandler(14); +} + +void Gpio2Go::isrGpio15() +{ + isrHandler(15); +} + +void Gpio2Go::isrGpio16() +{ + isrHandler(16); +} + +void Gpio2Go::isrGpio17() +{ + isrHandler(17); +} + +void Gpio2Go::isrGpio18() +{ + isrHandler(18); +} + +void Gpio2Go::isrGpio19() +{ + isrHandler(19); +} + +void Gpio2Go::isrGpio20() +{ + isrHandler(20); +} + +void Gpio2Go::isrGpio21() +{ + isrHandler(21); +} + +void Gpio2Go::isrGpio22() +{ + isrHandler(22); +} + +void Gpio2Go::isrGpio23() +{ + isrHandler(23); +} + +void Gpio2Go::isrGpio24() +{ + isrHandler(24); +} + +void Gpio2Go::isrGpio25() +{ + isrHandler(25); +} + +void Gpio2Go::isrGpio26() +{ + isrHandler(26); +} + +void Gpio2Go::isrGpio27() +{ + isrHandler(27); +} + +void Gpio2Go::isrGpio32() +{ + isrHandler(32); +} + +void Gpio2Go::isrGpio33() +{ + isrHandler(33); +} + +unsigned long Gpio2Go::lastTriggeredTimestamps[] = {0}; +uint16_t Gpio2Go::timeoutDurations[] = {0}; +std::vector> Gpio2Go::subscriptions; \ No newline at end of file diff --git a/lib/gpio2go/src/Gpio2Go.h b/lib/gpio2go/src/Gpio2Go.h new file mode 100644 index 0000000..1339119 --- /dev/null +++ b/lib/gpio2go/src/Gpio2Go.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include "esp_attr.h" +#include "PinMode.h" +#include "InterruptMode.h" + +#define GPIO2GO_NR_OF_PINS 31 +#define GPIO2GO_NR_FIRST_PIN 2 + +class Gpio2Go +{ +public: + static void configurePin(int pin, PinMode pin_Mode, InterruptMode interrupt_Mode, uint16_t timeoutAfterTriggerMS); + static void subscribe(std::function callback); + + unsigned long getLastTriggeredMillis(const int& pin); + +private: + static void attachIsr(int pin, InterruptMode interruptMode); + static int resolveInterruptMode(InterruptMode interruptMode); + + static void IRAM_ATTR isrHandler(int pin); + static void IRAM_ATTR isrGpio2(); + static void IRAM_ATTR isrGpio4(); + static void IRAM_ATTR isrGpio5(); + static void IRAM_ATTR isrGpio13(); + static void IRAM_ATTR isrGpio14(); + static void IRAM_ATTR isrGpio15(); + static void IRAM_ATTR isrGpio16(); + static void IRAM_ATTR isrGpio17(); + static void IRAM_ATTR isrGpio18(); + static void IRAM_ATTR isrGpio19(); + static void IRAM_ATTR isrGpio20(); + static void IRAM_ATTR isrGpio21(); + static void IRAM_ATTR isrGpio22(); + static void IRAM_ATTR isrGpio23(); + static void IRAM_ATTR isrGpio24(); + static void IRAM_ATTR isrGpio25(); + static void IRAM_ATTR isrGpio26(); + static void IRAM_ATTR isrGpio27(); + static void IRAM_ATTR isrGpio32(); + static void IRAM_ATTR isrGpio33(); + + static unsigned long DRAM_ATTR lastTriggeredTimestamps[GPIO2GO_NR_OF_PINS]; + static uint16_t DRAM_ATTR timeoutDurations[GPIO2GO_NR_OF_PINS]; + static std::vector> DRAM_ATTR subscriptions; +}; diff --git a/lib/gpio2go/src/InterruptMode.h b/lib/gpio2go/src/InterruptMode.h new file mode 100644 index 0000000..15517c8 --- /dev/null +++ b/lib/gpio2go/src/InterruptMode.h @@ -0,0 +1,10 @@ +#pragma once + +enum class InterruptMode +{ + Rising = 0x01, + Falling = 0x02, + Change = 0x03, + OnLow = 0x04, + OnHigh = 0x05 +}; \ No newline at end of file diff --git a/lib/gpio2go/src/PinMode.h b/lib/gpio2go/src/PinMode.h new file mode 100644 index 0000000..9753e38 --- /dev/null +++ b/lib/gpio2go/src/PinMode.h @@ -0,0 +1,20 @@ +#pragma once + +enum class PinMode +{ + Output = 0x03, + InputPullup = 0x05, + InputPullDown = 0x09 +}; + +//#define INPUT 0x01 +//// Changed OUTPUT from 0x02 to behave the same as Arduino pinMode(pin,OUTPUT) +//// where you can read the state of pin even when it is set as OUTPUT +//#define OUTPUT 0x03 +//#define PULLUP 0x04 +//#define INPUT_PULLUP 0x05 +//#define PULLDOWN 0x08 +//#define INPUT_PULLDOWN 0x09 +//#define OPEN_DRAIN 0x10 +//#define OUTPUT_OPEN_DRAIN 0x12 +//#define ANALOG 0xC0 \ No newline at end of file diff --git a/lib/nuki_ble b/lib/nuki_ble index 1de6290..d23f6d7 160000 --- a/lib/nuki_ble +++ b/lib/nuki_ble @@ -1 +1 @@ -Subproject commit 1de629002a03e6fffc9acd8b746baf498b50f77b +Subproject commit d23f6d794ab0cfffeb838af3eea197197287b984 diff --git a/main.cpp b/main.cpp index 97dc63b..b9119df 100644 --- a/main.cpp +++ b/main.cpp @@ -180,11 +180,16 @@ void setup() restartTs = preferences->getInt(preference_restart_timer) * 60 * 1000; } + gpio = new Gpio(preferences); + String gpioDesc; + gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration(), "\n\r"); + Serial.print(gpioDesc.c_str()); + lockEnabled = preferences->getBool(preference_lock_enabled); openerEnabled = preferences->getBool(preference_opener_enabled); const String mqttLockPath = preferences->getString(preference_mqtt_lock_path); - network = new Network(preferences, mqttLockPath, CharBuffer::get(), CHAR_BUFFER_SIZE); + network = new Network(preferences, gpio, mqttLockPath, CharBuffer::get(), CHAR_BUFFER_SIZE); network->initialize(); networkLock = new NetworkLock(network, preferences, CharBuffer::get(), CHAR_BUFFER_SIZE); @@ -209,11 +214,6 @@ void setup() bleScanner->initialize("NukiHub"); bleScanner->setScanDuration(10); - gpio = new Gpio(preferences); - String gpioDesc; - gpio->getConfigurationText(gpioDesc, gpio->pinConfiguration(), "\n\r"); - Serial.print(gpioDesc.c_str()); - Log->println(lockEnabled ? F("NUKI Lock enabled") : F("NUKI Lock disabled")); if(lockEnabled) { diff --git a/networkDevices/NetworkDevice.h b/networkDevices/NetworkDevice.h index 6d55e3c..8c87246 100644 --- a/networkDevices/NetworkDevice.h +++ b/networkDevices/NetworkDevice.h @@ -50,7 +50,6 @@ public: virtual uint16_t mqttSubscribe(const char* topic, uint8_t qos) = 0; protected: - const uint16_t _mqttMaxBufferSize = 6144; const String _hostname; const IPConfiguration* _ipConfiguration = nullptr; }; \ No newline at end of file From 356b34b29372188b3f7c91acab8e3c650a1e90b3 Mon Sep 17 00:00:00 2001 From: technyon Date: Sat, 3 Jun 2023 22:59:03 +0200 Subject: [PATCH 02/10] publish gpio role --- Gpio.h | 2 ++ MqttTopics.h | 3 ++- Network.cpp | 63 +++++++++++++++++++++++++++------------------------- Network.h | 4 ++-- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/Gpio.h b/Gpio.h index 8bdb9f3..4481014 100644 --- a/Gpio.h +++ b/Gpio.h @@ -85,6 +85,8 @@ private: PinRole::OutputHighRtoActive, PinRole::OutputHighCmActive, PinRole::OutputHighRtoOrCmActive, + PinRole::GeneralInput, + PinRole::GeneralOutput }; std::vector _pinConfiguration; diff --git a/MqttTopics.h b/MqttTopics.h index 0c0c81a..d1a5568 100644 --- a/MqttTopics.h +++ b/MqttTopics.h @@ -62,4 +62,5 @@ #define mqtt_topic_mqtt_connection_state "/maintenance/mqttConnectionState" #define mqtt_topic_network_device "/maintenance/networkDevice" -#define mqtt_topic_gpio_prefix "/gpio/" +#define mqtt_topic_gpio_prefix "/gpio" +#define mqtt_topic_gpio_input "/input_" diff --git a/Network.cpp b/Network.cpp index e2001d6..654178d 100644 --- a/Network.cpp +++ b/Network.cpp @@ -36,7 +36,8 @@ Network::Network(Preferences *preferences, Gpio* gpio, const String& maintenance _maintenancePathPrefix[i] = maintenancePathPrefix.charAt(i); } - String connectionStateTopic = _preferences->getString(preference_mqtt_lock_path) + mqtt_topic_mqtt_connection_state; + _lockPath = _preferences->getString(preference_mqtt_lock_path); + String connectionStateTopic = _lockPath + mqtt_topic_mqtt_connection_state; memset(_mqttConnectionStateTopic, 0, sizeof(_mqttConnectionStateTopic)); len = connectionStateTopic.length(); @@ -144,9 +145,6 @@ void Network::setupDevice() void Network::initialize() { - _networkGpio->initialize(); - registerMqttReceiver(_networkGpio); - _restartOnDisconnect = _preferences->getBool(preference_restart_on_disconnect); _rssiPublishInterval = _preferences->getInt(preference_rssi_publish_interval) * 1000; @@ -215,16 +213,21 @@ void Network::initialize() _publishDebugInfo = _preferences->getBool(preference_publish_debug_info); + char gpioPath[200]; + for(const auto& pinEntry : _gpio->pinConfiguration()) { switch(pinEntry.role) { case PinRole::GeneralInput: - String inp = "input_"; - inp.concat(pinEntry.pin); - initTopic(mqtt_topic_gpio_prefix, inp.c_str(), ) + memset(gpioPath, 0, sizeof(gpioPath)); + buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_input + std::to_string(pinEntry.pin)).c_str(), "role" }); + publishString(_lockPath.c_str(), gpioPath, "input"); break; case PinRole::GeneralOutput: + memset(gpioPath, 0, sizeof(gpioPath)); + buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_input + std::to_string(pinEntry.pin)).c_str(), "role" }); + publishString(_lockPath.c_str(), gpioPath, "output"); break; } } @@ -327,8 +330,6 @@ bool Network::update() _lastMaintenanceTs = ts; } - _networkGpio->update(); - return true; } @@ -464,36 +465,38 @@ bool Network::reconnect() void Network::subscribe(const char* prefix, const char *path) { char prefixedPath[500]; - buildMqttPath(prefix, path, prefixedPath); + buildMqttPath(prefixedPath, { prefix, path }); _subscribedTopics.push_back(prefixedPath); } void Network::initTopic(const char *prefix, const char *path, const char *value) { char prefixedPath[500]; - buildMqttPath(prefix, path, prefixedPath); + buildMqttPath(prefixedPath, { prefix, path }); String pathStr = prefixedPath; String valueStr = value; _initTopics[pathStr] = valueStr; } -void Network::buildMqttPath(const char* prefix, const char* path, char* outPath) +void Network::buildMqttPath(char* outPath, std::initializer_list paths) { int offset = 0; - int i=0; - while(prefix[i] != 0x00) - { - outPath[offset] = prefix[i]; - ++offset; - ++i; - } - i=0; - while(path[i] != 0x00) + for(const char* path : paths) { - outPath[offset] = path[i]; - ++i; - ++offset; + if(path[0] != '/') + { + outPath[offset] = '/'; + ++offset; + } + + int i = 0; + while(path[i] != 0) + { + outPath[offset] = path[i]; + ++offset; + ++i; + } } outPath[offset] = 0x00; @@ -567,7 +570,7 @@ void Network::publishFloat(const char* prefix, const char* topic, const float va char str[30]; dtostrf(value, 0, precision, str); char path[200] = {0}; - buildMqttPath(prefix, topic, path); + buildMqttPath(path, { prefix, topic }); _device->mqttPublish(path, MQTT_QOS_LEVEL, true, str); } @@ -576,7 +579,7 @@ void Network::publishInt(const char* prefix, const char *topic, const int value) char str[30]; itoa(value, str, 10); char path[200] = {0}; - buildMqttPath(prefix, topic, path); + buildMqttPath(path, { prefix, topic }); _device->mqttPublish(path, MQTT_QOS_LEVEL, true, str); } @@ -585,7 +588,7 @@ void Network::publishUInt(const char* prefix, const char *topic, const unsigned char str[30]; utoa(value, str, 10); char path[200] = {0}; - buildMqttPath(prefix, topic, path); + buildMqttPath(path, { prefix, topic }); _device->mqttPublish(path, MQTT_QOS_LEVEL, true, str); } @@ -594,7 +597,7 @@ void Network::publishULong(const char* prefix, const char *topic, const unsigned char str[30]; utoa(value, str, 10); char path[200] = {0}; - buildMqttPath(prefix, topic, path); + buildMqttPath(path, { prefix, topic }); _device->mqttPublish(path, MQTT_QOS_LEVEL, true, str); } @@ -603,14 +606,14 @@ void Network::publishBool(const char* prefix, const char *topic, const bool valu char str[2] = {0}; str[0] = value ? '1' : '0'; char path[200] = {0}; - buildMqttPath(prefix, topic, path); + buildMqttPath(path, { prefix, topic }); _device->mqttPublish(path, MQTT_QOS_LEVEL, true, str); } bool Network::publishString(const char* prefix, const char *topic, const char *value) { char path[200] = {0}; - buildMqttPath(prefix, topic, path); + buildMqttPath(path, { prefix, topic }); return _device->mqttPublish(path, MQTT_QOS_LEVEL, true, value) > 0; } diff --git a/Network.h b/Network.h index daf8594..15f762c 100644 --- a/Network.h +++ b/Network.h @@ -8,7 +8,6 @@ #include "networkDevices/IPConfiguration.h" #include "MqttTopics.h" #include "Gpio.h" -#include "NetworkGpio.h" enum class NetworkDeviceType { @@ -101,12 +100,13 @@ private: void onMqttConnect(const bool& sessionPresent); void onMqttDisconnect(const espMqttClientTypes::DisconnectReason& reason); - void buildMqttPath(const char* prefix, const char* path, char* outPath); + void buildMqttPath(char* outPath, std::initializer_list paths); static Network* _inst; const char* _lastWillPayload = "offline"; char _mqttConnectionStateTopic[211] = {0}; + String _lockPath; Preferences* _preferences; Gpio* _gpio; From b7053db76741e1b0acc11a4b9b667d6bc4d4a95f Mon Sep 17 00:00:00 2001 From: technyon Date: Sun, 4 Jun 2023 12:38:31 +0200 Subject: [PATCH 03/10] implement gpio output via mqtt --- Gpio.cpp | 13 ++++++++ Gpio.h | 1 + MqttTopics.h | 4 ++- Network.cpp | 79 +++++++++++++++++++++++++++++++++++++++---------- Network.h | 3 +- RestartReason.h | 28 +++++++++++++++--- main.cpp | 4 ++- 7 files changed, 110 insertions(+), 22 deletions(-) diff --git a/Gpio.cpp b/Gpio.cpp index c9dedfa..9d396d2 100644 --- a/Gpio.cpp +++ b/Gpio.cpp @@ -151,6 +151,18 @@ const std::vector &Gpio::pinConfiguration() const return _pinConfiguration; } +const PinRole Gpio::getPinRole(const int &pin) const +{ + for(const auto& pinEntry : _pinConfiguration) + { + if(pinEntry.pin == pin) + { + return pinEntry.role; + } + } + return PinRole::Disabled; +} + String Gpio::getRoleDescription(PinRole role) const { switch(role) @@ -315,3 +327,4 @@ void Gpio::migrateObsoleteSetting() delay(200); restartEsp(RestartReason::GpioConfigurationUpdated); } + diff --git a/Gpio.h b/Gpio.h index 4481014..af9d7ae 100644 --- a/Gpio.h +++ b/Gpio.h @@ -57,6 +57,7 @@ public: const std::vector& availablePins() const; const std::vector& pinConfiguration() const; + const PinRole getPinRole(const int& pin) const; String getRoleDescription(PinRole role) const; void getConfigurationText(String& text, const std::vector& pinConfiguration, const String& linebreak = "\n") const; diff --git a/MqttTopics.h b/MqttTopics.h index d1a5568..b5ae1ff 100644 --- a/MqttTopics.h +++ b/MqttTopics.h @@ -63,4 +63,6 @@ #define mqtt_topic_network_device "/maintenance/networkDevice" #define mqtt_topic_gpio_prefix "/gpio" -#define mqtt_topic_gpio_input "/input_" +#define mqtt_topic_gpio_pin "/pin_" +#define mqtt_topic_gpio_role "/role" +#define mqtt_topic_gpio_state "/state" diff --git a/Network.cpp b/Network.cpp index 654178d..3457ad6 100644 --- a/Network.cpp +++ b/Network.cpp @@ -213,21 +213,36 @@ void Network::initialize() _publishDebugInfo = _preferences->getBool(preference_publish_debug_info); - char gpioPath[200]; + char gpioPath[250]; + bool rebGpio = rebuildGpio(); - for(const auto& pinEntry : _gpio->pinConfiguration()) + if(rebGpio) { - switch(pinEntry.role) + Log->println(F("Rebuild MQTT GPIO structure")); + } + for (const auto &pinEntry: _gpio->pinConfiguration()) + { + switch (pinEntry.role) { case PinRole::GeneralInput: - memset(gpioPath, 0, sizeof(gpioPath)); - buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_input + std::to_string(pinEntry.pin)).c_str(), "role" }); - publishString(_lockPath.c_str(), gpioPath, "input"); + if(rebGpio) + { + buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_pin + std::to_string(pinEntry.pin)).c_str(), mqtt_topic_gpio_role}); + publishString(_lockPath.c_str(), gpioPath, "input"); + buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_pin + std::to_string(pinEntry.pin)).c_str(), mqtt_topic_gpio_state}); + publishString(_lockPath.c_str(), gpioPath, std::to_string(digitalRead(pinEntry.pin)).c_str()); + } break; case PinRole::GeneralOutput: - memset(gpioPath, 0, sizeof(gpioPath)); - buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_input + std::to_string(pinEntry.pin)).c_str(), "role" }); - publishString(_lockPath.c_str(), gpioPath, "output"); + if(rebGpio) + { + buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_pin + std::to_string(pinEntry.pin)).c_str(), mqtt_topic_gpio_role}); + publishString(_lockPath.c_str(), gpioPath, "output"); + buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_pin + std::to_string(pinEntry.pin)).c_str(), mqtt_topic_gpio_state}); + publishString(_lockPath.c_str(), gpioPath, "0"); + } + buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_pin + std::to_string(pinEntry.pin)).c_str(), mqtt_topic_gpio_state}); + subscribe(_lockPath.c_str(), gpioPath); break; } } @@ -509,11 +524,6 @@ void Network::registerMqttReceiver(MqttReceiver* receiver) void Network::onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) { - if(millis() < _ignoreSubscriptionsTs) - { - return; - } - uint8_t value[50] = {0}; size_t l = min(len, sizeof(value)-1); @@ -525,14 +535,53 @@ void Network::onMqttDataReceivedCallback(const espMqttClientTypes::MessageProper _inst->onMqttDataReceived(properties, topic, value, len, index, total); } -void Network::onMqttDataReceived(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) +void Network::onMqttDataReceived(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t& len, size_t& index, size_t& total) { + parseGpioTopics(properties, topic, payload, len, index, total); + + if(millis() < _ignoreSubscriptionsTs) + { + return; + } + for(auto receiver : _mqttReceivers) { receiver->onMqttDataReceived(topic, (byte*)payload, index); } } + +void Network::parseGpioTopics(const espMqttClientTypes::MessageProperties &properties, const char *topic, const uint8_t *payload, size_t& len, size_t& index, size_t& total) +{ + char gpioPath[250]; + buildMqttPath(gpioPath, {_lockPath.c_str(), mqtt_topic_gpio_prefix, mqtt_topic_gpio_pin}); +// /nuki_t/gpio/pin_17/state + size_t gpioLen = strlen(gpioPath); + if(strncmp(gpioPath, topic, gpioLen) == 0) + { + char pinStr[3] = {0}; + pinStr[0] = topic[gpioLen]; + if(topic[gpioLen+1] != '/') + { + pinStr[1] = topic[gpioLen+1]; + } + + int pin = std::atoi(pinStr); + + if(_gpio->getPinRole(pin) == PinRole::GeneralOutput) + { + const uint8_t pinState = strcmp((const char*)payload, "1") == 0 ? HIGH : LOW; + Log->print(F("GPIO ")); + Log->print(pin); + Log->print(F(" (Output) --> ")); + Log->println(pinState); + digitalWrite(pin, pinState); + } + + } +} + + void Network::reconfigureDevice() { _device->reconfigure(); diff --git a/Network.h b/Network.h index 15f762c..d46cd18 100644 --- a/Network.h +++ b/Network.h @@ -75,7 +75,8 @@ public: private: static void onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); - void onMqttDataReceived(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); + void onMqttDataReceived(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t& len, size_t& index, size_t& total); + void parseGpioTopics(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t& len, size_t& index, size_t& total); void setupDevice(); bool reconnect(); diff --git a/RestartReason.h b/RestartReason.h index 17752a0..9871c3f 100644 --- a/RestartReason.h +++ b/RestartReason.h @@ -25,24 +25,37 @@ enum class RestartReason #define RESTART_REASON_VALID_DETECT 0xa00ab00bc00bd00d; extern int restartReason; -extern uint64_t restartReasonValid; +extern uint64_t restartReasonValidDetect; +extern bool rebuildGpioRequested; extern RestartReason currentRestartReason; +extern bool restartReason_isValid; + + inline static void restartEsp(RestartReason reason) { + if(reason == RestartReason::GpioConfigurationUpdated) + { + rebuildGpioRequested = true; + } restartReason = (int)reason; - restartReasonValid = RESTART_REASON_VALID_DETECT; + restartReasonValidDetect = RESTART_REASON_VALID_DETECT; ESP.restart(); } inline static void initializeRestartReason() { uint64_t cmp = RESTART_REASON_VALID_DETECT; - if(restartReasonValid == cmp) + restartReason_isValid = (restartReasonValidDetect == cmp); + if(restartReason_isValid) { currentRestartReason = (RestartReason)restartReason; - memset(&restartReasonValid, 0, sizeof(restartReasonValid)); + memset(&restartReasonValidDetect, 0, sizeof(restartReasonValidDetect)); + } + else + { + rebuildGpioRequested = false; } } @@ -121,4 +134,11 @@ inline static String getEspRestartReason() default: return "Unknown: " + (int)reason; } +} + +inline bool rebuildGpio() +{ + bool rebGpio = rebuildGpioRequested; + rebuildGpioRequested = false; + return restartReason_isValid && rebGpio; } \ No newline at end of file diff --git a/main.cpp b/main.cpp index 5e0166c..ac712f2 100644 --- a/main.cpp +++ b/main.cpp @@ -34,7 +34,9 @@ bool openerEnabled = false; unsigned long restartTs = (2^32) - 5 * 60000; RTC_NOINIT_ATTR int restartReason; -RTC_NOINIT_ATTR uint64_t restartReasonValid; +RTC_NOINIT_ATTR uint64_t restartReasonValidDetect; +RTC_NOINIT_ATTR bool rebuildGpioRequested; +bool restartReason_isValid; RestartReason currentRestartReason = RestartReason::NotApplicable; TaskHandle_t networkTaskHandle = nullptr; From 56bd07629f5f106bc9d83913ce8e1cd4a1e0db21 Mon Sep 17 00:00:00 2001 From: technyon Date: Sun, 4 Jun 2023 13:11:54 +0200 Subject: [PATCH 04/10] implement gpio input via mqtt --- Config.h | 2 ++ Gpio.cpp | 3 ++- Network.cpp | 28 ++++++++++++++++++++++++++++ Network.h | 2 ++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Config.h b/Config.h index f958528..d4e2f68 100644 --- a/Config.h +++ b/Config.h @@ -4,3 +4,5 @@ #define MQTT_QOS_LEVEL 1 #define MQTT_CLEAN_SESSIONS false + +#define GPIO_DEBOUNCE_TIME 300 \ No newline at end of file diff --git a/Gpio.cpp b/Gpio.cpp index 9d396d2..4d8fcba 100644 --- a/Gpio.cpp +++ b/Gpio.cpp @@ -1,5 +1,6 @@ #include #include "Gpio.h" +#include "Config.h" #include "Arduino.h" #include "Logger.h" #include "PreferencesKeys.h" @@ -8,7 +9,7 @@ Gpio* Gpio::_inst = nullptr; unsigned long Gpio::_debounceTs = 0; -const uint Gpio::_debounceTime = 1000; +const uint Gpio::_debounceTime = GPIO_DEBOUNCE_TIME; Gpio::Gpio(Preferences* preferences) : _preferences(preferences) diff --git a/Network.cpp b/Network.cpp index 3457ad6..e1901a3 100644 --- a/Network.cpp +++ b/Network.cpp @@ -246,6 +246,10 @@ void Network::initialize() break; } } + _gpio->addCallback([this](const GpioAction& action, const int& pin) + { + gpioActionCallback(action, pin); + }); } bool Network::update() @@ -345,6 +349,26 @@ bool Network::update() _lastMaintenanceTs = ts; } + for(const auto& gpioTs : _gpioTs) + { + uint8_t pin = gpioTs.first; + unsigned long ts = gpioTs.second; + if(ts != 0 && ((millis() - ts) >= GPIO_DEBOUNCE_TIME)) + { + _gpioTs[pin] = 0; + + uint8_t pinState = digitalRead(pin) == HIGH ? 1 : 0; + char gpioPath[250]; + buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_pin + std::to_string(pin)).c_str(), mqtt_topic_gpio_state}); + publishInt(_lockPath.c_str(), gpioPath, pinState); + + Log->print(F("GPIO ")); + Log->print(pin); + Log->print(F(" (Input) --> ")); + Log->println(pinState); + } + } + return true; } @@ -581,6 +605,10 @@ void Network::parseGpioTopics(const espMqttClientTypes::MessageProperties &prope } } +void Network::gpioActionCallback(const GpioAction &action, const int &pin) +{ + _gpioTs[pin] = millis(); +} void Network::reconfigureDevice() { diff --git a/Network.h b/Network.h index d46cd18..71e5faa 100644 --- a/Network.h +++ b/Network.h @@ -77,6 +77,7 @@ private: static void onMqttDataReceivedCallback(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); void onMqttDataReceived(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t& len, size_t& index, size_t& total); void parseGpioTopics(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t& len, size_t& index, size_t& total); + void gpioActionCallback(const GpioAction& action, const int& pin); void setupDevice(); bool reconnect(); @@ -139,6 +140,7 @@ private: bool _mqttEnabled = true; static unsigned long _ignoreSubscriptionsTs; long _rssiPublishInterval = 0; + std::map _gpioTs; char* _buffer; const size_t _bufferSize; From f839d42c83ab5edcdcc3ca02d16d07184a7889ef Mon Sep 17 00:00:00 2001 From: technyon Date: Sun, 4 Jun 2023 13:17:42 +0200 Subject: [PATCH 05/10] implement pull-up and -down --- Gpio.cpp | 9 +++++++-- Gpio.h | 6 ++++-- Network.cpp | 3 ++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Gpio.cpp b/Gpio.cpp index 4d8fcba..9af20a3 100644 --- a/Gpio.cpp +++ b/Gpio.cpp @@ -75,7 +75,10 @@ void Gpio::init() case PinRole::GeneralOutput: pinMode(entry.pin, OUTPUT); break; - case PinRole::GeneralInput: + case PinRole::GeneralInputPullDown: + Gpio2Go::configurePin(entry.pin, PinMode::InputPullDown, InterruptMode::Change, 300); + break; + case PinRole::GeneralInputPullUp: Gpio2Go::configurePin(entry.pin, PinMode::InputPullup, InterruptMode::Change, 300); break; default: @@ -198,7 +201,9 @@ String Gpio::getRoleDescription(PinRole role) const return "Output: High when RTO or CM active"; case PinRole::GeneralOutput: return "General output"; - case PinRole::GeneralInput: + case PinRole::GeneralInputPullDown: + return "General input (Pull-down)"; + case PinRole::GeneralInputPullUp: return "General input (Pull-up)"; default: return "Unknown"; diff --git a/Gpio.h b/Gpio.h index af9d7ae..a770a2c 100644 --- a/Gpio.h +++ b/Gpio.h @@ -21,7 +21,8 @@ enum class PinRole OutputHighCmActive, OutputHighRtoOrCmActive, GeneralOutput, - GeneralInput + GeneralInputPullDown, + GeneralInputPullUp }; enum class GpioAction @@ -86,7 +87,8 @@ private: PinRole::OutputHighRtoActive, PinRole::OutputHighCmActive, PinRole::OutputHighRtoOrCmActive, - PinRole::GeneralInput, + PinRole::GeneralInputPullDown, + PinRole::GeneralInputPullUp, PinRole::GeneralOutput }; diff --git a/Network.cpp b/Network.cpp index e1901a3..85598d1 100644 --- a/Network.cpp +++ b/Network.cpp @@ -224,7 +224,8 @@ void Network::initialize() { switch (pinEntry.role) { - case PinRole::GeneralInput: + case PinRole::GeneralInputPullDown: + case PinRole::GeneralInputPullUp: if(rebGpio) { buildMqttPath(gpioPath, {mqtt_topic_gpio_prefix, (mqtt_topic_gpio_pin + std::to_string(pinEntry.pin)).c_str(), mqtt_topic_gpio_role}); From f5680511927b1503ef37eb2d8048927ae4ac7c44 Mon Sep 17 00:00:00 2001 From: technyon Date: Sun, 4 Jun 2023 13:32:50 +0200 Subject: [PATCH 06/10] change version --- Config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Config.h b/Config.h index d4e2f68..4a26369 100644 --- a/Config.h +++ b/Config.h @@ -1,6 +1,6 @@ #pragma once -#define NUKI_HUB_VERSION "8.23" +#define NUKI_HUB_VERSION "8.24-pre-1" #define MQTT_QOS_LEVEL 1 #define MQTT_CLEAN_SESSIONS false From aac4f902f7f0858bbff9ec215c8867d098ad706d Mon Sep 17 00:00:00 2001 From: technyon Date: Wed, 7 Jun 2023 17:16:20 +0200 Subject: [PATCH 07/10] fix mqttlogger path --- networkDevices/EthLan8720Device.cpp | 3 ++- networkDevices/W5500Device.cpp | 3 ++- networkDevices/WifiDevice.cpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/networkDevices/EthLan8720Device.cpp b/networkDevices/EthLan8720Device.cpp index 2c56464..cb40ed6 100644 --- a/networkDevices/EthLan8720Device.cpp +++ b/networkDevices/EthLan8720Device.cpp @@ -54,7 +54,8 @@ EthLan8720Device::EthLan8720Device(const String& hostname, Preferences* preferen _path = new char[200]; memset(_path, 0, sizeof(_path)); - String pathStr = preferences->getString(preference_mqtt_lock_path); + String pathStr = "/"; + pathStr.concat(preferences->getString(preference_mqtt_lock_path)); pathStr.concat(mqtt_topic_log); strcpy(_path, pathStr.c_str()); Log = new MqttLogger(this, _path, MqttLoggerMode::MqttAndSerial); diff --git a/networkDevices/W5500Device.cpp b/networkDevices/W5500Device.cpp index 615a938..9591748 100644 --- a/networkDevices/W5500Device.cpp +++ b/networkDevices/W5500Device.cpp @@ -56,7 +56,8 @@ void W5500Device::initialize() if(_preferences->getBool(preference_mqtt_log_enabled)) { - String pathStr = _preferences->getString(preference_mqtt_lock_path); + String pathStr = "/"; + pathStr.concat(_preferences->getString(preference_mqtt_lock_path)); pathStr.concat(mqtt_topic_log); _path = new char[pathStr.length() + 1]; memset(_path, 0, sizeof(_path)); diff --git a/networkDevices/WifiDevice.cpp b/networkDevices/WifiDevice.cpp index bb66d42..7dea816 100644 --- a/networkDevices/WifiDevice.cpp +++ b/networkDevices/WifiDevice.cpp @@ -46,7 +46,8 @@ WifiDevice::WifiDevice(const String& hostname, Preferences* _preferences, const _path = new char[200]; memset(_path, 0, sizeof(_path)); - String pathStr = _preferences->getString(preference_mqtt_lock_path); + String pathStr = "/"; + pathStr.concat(_preferences->getString(preference_mqtt_lock_path)); pathStr.concat(mqtt_topic_log); strcpy(_path, pathStr.c_str()); Log = new MqttLogger(this, _path, MqttLoggerMode::MqttAndSerial); From cc741ae01e964c0ea22cee6712ccc7b334004c23 Mon Sep 17 00:00:00 2001 From: technyon Date: Wed, 7 Jun 2023 17:35:33 +0200 Subject: [PATCH 08/10] Revert "fix mqttlogger path" This reverts commit aac4f902f7f0858bbff9ec215c8867d098ad706d. --- networkDevices/EthLan8720Device.cpp | 3 +-- networkDevices/W5500Device.cpp | 3 +-- networkDevices/WifiDevice.cpp | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/networkDevices/EthLan8720Device.cpp b/networkDevices/EthLan8720Device.cpp index cb40ed6..2c56464 100644 --- a/networkDevices/EthLan8720Device.cpp +++ b/networkDevices/EthLan8720Device.cpp @@ -54,8 +54,7 @@ EthLan8720Device::EthLan8720Device(const String& hostname, Preferences* preferen _path = new char[200]; memset(_path, 0, sizeof(_path)); - String pathStr = "/"; - pathStr.concat(preferences->getString(preference_mqtt_lock_path)); + String pathStr = preferences->getString(preference_mqtt_lock_path); pathStr.concat(mqtt_topic_log); strcpy(_path, pathStr.c_str()); Log = new MqttLogger(this, _path, MqttLoggerMode::MqttAndSerial); diff --git a/networkDevices/W5500Device.cpp b/networkDevices/W5500Device.cpp index 9591748..615a938 100644 --- a/networkDevices/W5500Device.cpp +++ b/networkDevices/W5500Device.cpp @@ -56,8 +56,7 @@ void W5500Device::initialize() if(_preferences->getBool(preference_mqtt_log_enabled)) { - String pathStr = "/"; - pathStr.concat(_preferences->getString(preference_mqtt_lock_path)); + String pathStr = _preferences->getString(preference_mqtt_lock_path); pathStr.concat(mqtt_topic_log); _path = new char[pathStr.length() + 1]; memset(_path, 0, sizeof(_path)); diff --git a/networkDevices/WifiDevice.cpp b/networkDevices/WifiDevice.cpp index 7dea816..bb66d42 100644 --- a/networkDevices/WifiDevice.cpp +++ b/networkDevices/WifiDevice.cpp @@ -46,8 +46,7 @@ WifiDevice::WifiDevice(const String& hostname, Preferences* _preferences, const _path = new char[200]; memset(_path, 0, sizeof(_path)); - String pathStr = "/"; - pathStr.concat(_preferences->getString(preference_mqtt_lock_path)); + String pathStr = _preferences->getString(preference_mqtt_lock_path); pathStr.concat(mqtt_topic_log); strcpy(_path, pathStr.c_str()); Log = new MqttLogger(this, _path, MqttLoggerMode::MqttAndSerial); From f23290dea677daee1091f20146eb818238b70114 Mon Sep 17 00:00:00 2001 From: technyon Date: Wed, 7 Jun 2023 17:40:07 +0200 Subject: [PATCH 09/10] fix build mqtt path --- Config.h | 2 +- Network.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Config.h b/Config.h index 4a26369..d7b7e8a 100644 --- a/Config.h +++ b/Config.h @@ -1,6 +1,6 @@ #pragma once -#define NUKI_HUB_VERSION "8.24-pre-1" +#define NUKI_HUB_VERSION "8.24-pre-3" #define MQTT_QOS_LEVEL 1 #define MQTT_CLEAN_SESSIONS false diff --git a/Network.cpp b/Network.cpp index 85598d1..0e4cbbe 100644 --- a/Network.cpp +++ b/Network.cpp @@ -521,10 +521,11 @@ void Network::initTopic(const char *prefix, const char *path, const char *value) void Network::buildMqttPath(char* outPath, std::initializer_list paths) { int offset = 0; + int pathCount = 0; for(const char* path : paths) { - if(path[0] != '/') + if(pathCount > 0 && path[0] != '/') { outPath[offset] = '/'; ++offset; @@ -537,6 +538,7 @@ void Network::buildMqttPath(char* outPath, std::initializer_list pa ++offset; ++i; } + ++pathCount; } outPath[offset] = 0x00; From 16cf4ae36cf536bbe6e354772db6a5f115ed038a Mon Sep 17 00:00:00 2001 From: technyon Date: Thu, 8 Jun 2023 18:32:38 +0200 Subject: [PATCH 10/10] add general input/output information to the readme --- Config.h | 4 ++-- README.md | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Config.h b/Config.h index d7b7e8a..9926ccc 100644 --- a/Config.h +++ b/Config.h @@ -1,8 +1,8 @@ #pragma once -#define NUKI_HUB_VERSION "8.24-pre-3" +#define NUKI_HUB_VERSION "8.24" #define MQTT_QOS_LEVEL 1 #define MQTT_CLEAN_SESSIONS false -#define GPIO_DEBOUNCE_TIME 300 \ No newline at end of file +#define GPIO_DEBOUNCE_TIME 200 \ No newline at end of file diff --git a/README.md b/README.md index 3c50e76..eaa53b0 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,9 @@ can be configured for a specific role: - Output: High when RTO active: Outputs a high signal when ring-to-open is active (opener) - Output: High when CM active: Outputs a high signal when continuous mode is active (opener) - Output: High when RTO or CM active: Outputs a high signal when either ring-to-open or continuous mode is active (opener) +- General input (pull-down): The pin is configured in pull-down configuration and its state is published to the "gpio/pin_x/state" topic +- General input (pull-up): The pin is configured in pull-up configuration and its state is published to the "gpio/pin_x/state" topic +- Genral output: The pin is set to high or low depending on the "gpio/pin/x/state" topic Note: The old setting "Enable control via GPIO" is removed. If you had enabled this setting before upgrading to 8.22, the PINs are automatically configured to be compatible with the previously hard-coded PINs.