From 40a3cb68fa773fc58b6b8bda0063c8e24db4e493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 4 Sep 2019 11:59:07 +0200 Subject: [PATCH 01/17] Add debian folder and basic structure of the library --- gpio.cpp | 341 ++++++++++++++++++++++++++++++++++++++++++++++ gpio.h | 69 ++++++++++ libnymea-gpio.pro | 40 ++++++ 3 files changed, 450 insertions(+) create mode 100644 gpio.cpp create mode 100644 gpio.h create mode 100644 libnymea-gpio.pro diff --git a/gpio.cpp b/gpio.cpp new file mode 100644 index 0000000..c47ba0b --- /dev/null +++ b/gpio.cpp @@ -0,0 +1,341 @@ +#include "gpio.h" + +Q_LOGGING_CATEGORY(dcGpio, "Gpio") + +/*! Constructs a \l{Gpio} object to represent a GPIO with the given \a gpio number and \a parent. */ +Gpio::Gpio(int gpio, QObject *parent) : + QObject(parent), + m_gpio(gpio), + m_direction(Gpio::DirectionInvalid), + m_gpioDirectory(QDir(QString("/sys/class/gpio/gpio%1/").arg(QString::number(gpio)))) +{ + +} + +/*! Destroys and unexports the \l{Gpio}. */ +Gpio::~Gpio() +{ + unexportGpio(); +} + +/*! Returns true if the directories \tt {/sys/class/gpio} and \tt {/sys/class/gpio/export} do exist. */ +bool Gpio::isAvailable() +{ + return QFile("/sys/class/gpio/export").exists(); +} + +/*! Returns the directory \tt {/sys/class/gpio/gpio} of this Gpio. */ +QString Gpio::gpioDirectory() const +{ + return m_gpioDirectory.canonicalPath(); +} + +/*! Returns the number of this \l{Gpio}. \note The Gpio number is mostly not equivalent with the pin number. */ +int Gpio::gpioNumber() const +{ + return m_gpio; +} + +/*! Returns true if this \l{Gpio} could be exported in the system file \tt {/sys/class/gpio/export}. If this Gpio is already exported, this function will return true. */ +bool Gpio::exportGpio() +{ + // Check if already exported + if (m_gpioDirectory.exists()) + return true; + + QFile exportFile("/sys/class/gpio/export"); + if (!exportFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO export file:" << exportFile.errorString(); + return false; + } + + QTextStream out(&exportFile); + out << m_gpio; + exportFile.close(); + return true; +} + +/*! Returns true if this \l{Gpio} could be unexported in the system file \tt {/sys/class/gpio/unexport}. */ +bool Gpio::unexportGpio() +{ + QFile unexportFile("/sys/class/gpio/unexport"); + if (!unexportFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO unexport file:" << unexportFile.errorString(); + return false; + } + + QTextStream out(&unexportFile); + out << m_gpio; + unexportFile.close(); + return true; +} + +/*! Returns true if the \a direction of this GPIO could be set. \sa Gpio::Direction, */ +bool Gpio::setDirection(Gpio::Direction direction) +{ + if (direction == Gpio::DirectionInvalid) { + qCWarning(dcGpio()) << "Setting an invalid direction is forbidden."; + return false; + } + + QFile directionFile(m_gpioDirectory.path() + "/direction"); + if (!directionFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "direction file:" << directionFile.errorString(); + return false; + } + + m_direction = direction; + + QTextStream out(&directionFile); + switch (m_direction) { + case DirectionInput: + out << "in"; + break; + case DirectionOutput: + out << "out"; + break; + default: + break; + } + + directionFile.close(); + return true; +} + +/*! Returns the direction of this \l{Gpio}. */ +Gpio::Direction Gpio::direction() +{ + QFile directionFile(m_gpioDirectory.path() + "/direction"); + if (!directionFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "direction file:" << directionFile.fileName() << directionFile.errorString(); + return Gpio::DirectionInvalid; + } + + QString direction; + QTextStream in(&directionFile); + in >> direction; + directionFile.close(); + + if (direction == "in") { + m_direction = DirectionInput; + return Gpio::DirectionInput; + } else if (direction == "out") { + m_direction = DirectionOutput; + return Gpio::DirectionOutput; + } + + return Gpio::DirectionInvalid; +} + +/*! Returns true if the digital \a value of this \l{Gpio} could be set correctly. */ +bool Gpio::setValue(Gpio::Value value) +{ + // Check given value + if (value == Gpio::ValueInvalid) { + qCWarning(dcGpio()) << "Setting an invalid value is forbidden."; + return false; + } + + // Check current direction + if (m_direction == Gpio::DirectionInput) { + qCWarning(dcGpio()) << "Setting the value of an input GPIO is forbidden."; + return false; + } + + if (m_direction == Gpio::DirectionInvalid) { + qCWarning(dcGpio()) << "The direction of GPIO" << m_gpio << "is invalid."; + return false; + } + + QFile valueFile(m_gpioDirectory.path() + "/value"); + if (!valueFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "value file:" << valueFile.errorString(); + return false; + } + + QTextStream out(&valueFile); + switch (value) { + case ValueLow: + out << "0"; + break; + case ValueHigh: + out << "1"; + break; + default: + valueFile.close(); + return false; + } + + valueFile.close(); + return true; +} + +/*! Returns the current digital value of this \l{Gpio}. */ +Gpio::Value Gpio::value() +{ + QFile valueFile(m_gpioDirectory.path() + "/value"); + if (!valueFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "value file:" << valueFile.errorString(); + return Gpio::ValueInvalid; + } + + QString value; + QTextStream in(&valueFile); + in >> value; + valueFile.close(); + + if (value == "0") { + return Gpio::ValueLow; + } else if (value == "1") { + return Gpio::ValueHigh; + } + + return Gpio::ValueInvalid; +} + +/*! This method allows to invert the logic of this \l{Gpio}. Returns true, if the GPIO could be set \a activeLow. */ +bool Gpio::setActiveLow(bool activeLow) +{ + QFile activeLowFile(m_gpioDirectory.path() + "/active_low"); + if (!activeLowFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "active_low file:" << activeLowFile.errorString(); + return false; + } + + QTextStream out(&activeLowFile); + if (activeLow) { + out << "0"; + } else { + out << "1"; + } + + activeLowFile.close(); + return true; +} + +/*! Returns true if the logic of this \l{Gpio} is inverted (1 = low, 0 = high). */ +bool Gpio::activeLow() +{ + QFile activeLowFile(m_gpioDirectory.path() + "/active_low"); + if (!activeLowFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "active_low file:" << activeLowFile.errorString(); + return false; + } + + QString value; + QTextStream in(&activeLowFile); + in >> value; + activeLowFile.close(); + + if (value == "0") + return true; + + return false; +} + +/*! Returns true if the \a edge of this GPIO could be set correctly. The \a edge parameter specifies, + * when an interrupt occurs. */ +bool Gpio::setEdgeInterrupt(Gpio::Edge edge) +{ + if (m_direction == Gpio::DirectionOutput) { + qCWarning(dcGpio()) << "Could not set edge interrupt, GPIO is configured as an output."; + return false; + } + + QFile edgeFile(m_gpioDirectory.path() + "/edge"); + if (!edgeFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "edge file:" << edgeFile.errorString(); + return false; + } + + QTextStream out(&edgeFile); + switch (edge) { + case EdgeFalling: + out << "falling"; + break; + case EdgeRising: + out << "rising"; + break; + case EdgeBoth: + out << "both"; + break; + case EdgeNone: + out << "none"; + break; + } + + edgeFile.close(); + return true; +} + +/*! Returns the edge interrupt of this \l{Gpio}. */ +Gpio::Edge Gpio::edgeInterrupt() +{ + QFile edgeFile(m_gpioDirectory.path() + "/edge"); + if (!edgeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "edge file:" << edgeFile.errorString(); + return Gpio::EdgeNone; + } + + QString edge; + QTextStream in(&edgeFile); + in >> edge; + edgeFile.close(); + + if (edge.contains("falling")) { + return Gpio::EdgeFalling; + } else if (edge.contains("rising")) { + return Gpio::EdgeRising; + } else if (edge.contains("both")) { + return Gpio::EdgeBoth; + } else if (edge.contains("none")) { + return Gpio::EdgeNone; + } + + return Gpio::EdgeNone; +} + + + +QDebug operator<<(QDebug debug, Gpio *gpio) +{ + debug.nospace() << "Gpio(" << gpio->gpioNumber() << ", "; + if (gpio->direction() == Gpio::DirectionInput) { + debug.nospace() << "Input, "; + } else if (gpio->direction() == Gpio::DirectionOutput) { + debug.nospace() << "Output, "; + } else { + debug.nospace() << "Invalid, "; + } + + switch (gpio->edgeInterrupt()) { + case Gpio::EdgeFalling: + debug.nospace() << "Ir: Falling, "; + break; + case Gpio::EdgeRising: + debug.nospace() << "Ir: Rising, "; + break; + case Gpio::EdgeBoth: + debug.nospace() << "Ir: Both, "; + break; + case Gpio::EdgeNone: + debug.nospace() << "Ir: None, "; + break; + } + + if (gpio->activeLow()) { + debug.nospace() << "Active Low: 1, "; + } else { + debug.nospace() << "Active Low: 0, "; + } + + if (gpio->value() == Gpio::ValueHigh) { + debug.nospace() << "Value: 1)"; + } else if (gpio->value() == Gpio::ValueLow) { + debug.nospace() << "Value: 0)"; + } else { + debug.nospace() << "Value: Invalid)"; + } + + return debug; +} diff --git a/gpio.h b/gpio.h new file mode 100644 index 0000000..d914619 --- /dev/null +++ b/gpio.h @@ -0,0 +1,69 @@ +#ifndef GPIO_H +#define GPIO_H + +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(dcGpio) + +class Gpio : public QObject +{ + Q_OBJECT +public: + enum Direction { + DirectionInvalid, + DirectionInput, + DirectionOutput + }; + Q_ENUM(Direction) + + enum Value { + ValueInvalid = -1, + ValueLow = 0, + ValueHigh = 1 + }; + Q_ENUM(Value) + + enum Edge { + EdgeFalling, + EdgeRising, + EdgeBoth, + EdgeNone + }; + Q_ENUM(Edge) + + explicit Gpio(int gpio, QObject *parent = nullptr); + ~Gpio(); + + static bool isAvailable(); + + QString gpioDirectory() const; + int gpioNumber() const; + + bool exportGpio(); + bool unexportGpio(); + + bool setDirection(Gpio::Direction direction); + Gpio::Direction direction(); + + bool setValue(Gpio::Value value); + Gpio::Value value(); + + bool setActiveLow(bool activeLow); + bool activeLow(); + + bool setEdgeInterrupt(Gpio::Edge edge); + Gpio::Edge edgeInterrupt(); + +private: + int m_gpio = 0; + Gpio::Direction m_direction = Gpio::DirectionOutput; + QDir m_gpioDirectory; + +}; + +QDebug operator<< (QDebug debug, Gpio *gpio); + +#endif // GPIO_H diff --git a/libnymea-gpio.pro b/libnymea-gpio.pro new file mode 100644 index 0000000..ee75995 --- /dev/null +++ b/libnymea-gpio.pro @@ -0,0 +1,40 @@ +TARGET = nymea-gpio +TEMPLATE = lib + +QT -= gui + +QMAKE_CXXFLAGS *= -Werror -std=c++11 -g +QMAKE_LFLAGS *= -std=c++11 + +VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') + + +DEFINES += LIBNYMEAGPIO_LIBRARY + +SOURCES += \ + gpio.cpp + +HEADERS += \ + gpio.h + +target.path = $$[QT_INSTALL_LIBS] +INSTALLS += target + +# install header file with relative subdirectory +for(header, HEADERS) { + path = /usr/include/libnymea-gpio/$${dirname(header)} + eval(headers_$${path}.files += $${header}) + eval(headers_$${path}.path = $${path}) + eval(INSTALLS *= headers_$${path}) +} + +# Create pkgconfig file +CONFIG += create_pc create_prl no_install_prl +QMAKE_PKGCONFIG_NAME = libnymea-gpio +QMAKE_PKGCONFIG_DESCRIPTION = nymea gpio development library +QMAKE_PKGCONFIG_PREFIX = $$[QT_INSTALL_PREFIX] +QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_PREFIX]/include/libnymea-gpio/ +QMAKE_PKGCONFIG_LIBDIR = $$target.path +QMAKE_PKGCONFIG_VERSION = $$VERSION_STRING +QMAKE_PKGCONFIG_FILE = libnymea-gpio +QMAKE_PKGCONFIG_DESTDIR = pkgconfig From 5cf1f75a168c6abddb7d80666c799384f41be5b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 4 Sep 2019 11:59:34 +0200 Subject: [PATCH 02/17] Add debian folder --- debian/changelog | 5 ++++ debian/compat | 1 + debian/control | 39 +++++++++++++++++++++++++++++ debian/copyright | 11 ++++++++ debian/libnymea-gpio-dev.install.in | 3 +++ debian/libnymea-gpio.install.in | 3 +++ debian/rules | 22 ++++++++++++++++ debian/source/format | 1 + 8 files changed, 85 insertions(+) create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/libnymea-gpio-dev.install.in create mode 100644 debian/libnymea-gpio.install.in create mode 100755 debian/rules create mode 100644 debian/source/format diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..a74681c --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +libnymea-gpio (0.1.0) bionic; urgency=medium + + * Initial release. + + -- Simon Stürz Wed, 04 Sep 2019 11:50:53 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..892c31a --- /dev/null +++ b/debian/control @@ -0,0 +1,39 @@ +Source: libnymea-gpio +Section: utils +Priority: optional +Maintainer: Simon Stürz +Build-Depends: debhelper (>= 9.0.0), + dpkg-dev (>= 1.16.1~), + qt5-default, + qt5-qmake, + qtbase5-dev, + qtbase5-dev-tools +Standards-Version: 3.9.7 + +Package: libnymea-gpio +Section: libs +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends} +Description: Qt 5 based library for GPIO interaction. + Qt 5 based library for GPIO interaction. + +Package: libnymea-gpio-dev +Section: libdevel +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + pkg-config, + libnymea-gpio (= ${binary:Version}), +Description: Qt 5 based library for GPIO interaction - development files + Development files for Qt 5 based GPIO library. + +Package: libnymea-gpio-dbg +Priority: extra +Architecture: any +Section: debug +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libnymea-gpio (= ${binary:Version}) +Description: Qt 5 based library for GPIO interaction - debug symbols + Debug Symbols for Qt 5 based GPIO library. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..a5e8d5e --- /dev/null +++ b/debian/copyright @@ -0,0 +1,11 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Author: Simon Stürz +Download: https://github.com/guh/libnymea-gpio + +License: LGPL-3 + On Debian systems, the complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/LGPL-3'. + +Files: * +Copyright: (C) 2019 Simon Stürz +License: LGPL-3 diff --git a/debian/libnymea-gpio-dev.install.in b/debian/libnymea-gpio-dev.install.in new file mode 100644 index 0000000..6be4c17 --- /dev/null +++ b/debian/libnymea-gpio-dev.install.in @@ -0,0 +1,3 @@ +usr/lib/@DEB_HOST_MULTIARCH@/libnymea-gpio.so +usr/include/libnymea-gpio/* usr/include/libnymea-gpio +usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/libnymea-gpio.pc diff --git a/debian/libnymea-gpio.install.in b/debian/libnymea-gpio.install.in new file mode 100644 index 0000000..ecbcd05 --- /dev/null +++ b/debian/libnymea-gpio.install.in @@ -0,0 +1,3 @@ +usr/lib/@DEB_HOST_MULTIARCH@/libnymea-gpio.so.1 +usr/lib/@DEB_HOST_MULTIARCH@/libnymea-gpio.so.1.0 +usr/lib/@DEB_HOST_MULTIARCH@/libnymea-gpio.so.1.0.0 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..50a3c52 --- /dev/null +++ b/debian/rules @@ -0,0 +1,22 @@ +#!/usr/bin/make -f + +export DH_VERBOSE=1 +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) + +PREPROCESS_FILES := $(wildcard debian/*.in) + +$(PREPROCESS_FILES:.in=): %: %.in + sed 's,/@DEB_HOST_MULTIARCH@,$(DEB_HOST_MULTIARCH:%=/%),g' $< > $@ + +%: + dh $@ --buildsystem=qmake --parallel + +override_dh_install: $(PREPROCESS_FILES:.in=) + dh_install + +override_dh_strip: + dh_strip --dbg-package=libnymea-gpio-dbg + +override_dh_auto_clean: + dh_auto_clean + rm -rf $(PREPROCESS_FILES:.in=) diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) From 8d1369c7ef2d3a4df8f704fb97c21da1405992bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 4 Sep 2019 12:46:11 +0200 Subject: [PATCH 03/17] Add basic structure for gpio monitor --- .gitignore | 2 + gpiomonitor.cpp | 110 ++++++++++++++++++++++++++++++++++++++++++++++ gpiomonitor.h | 51 +++++++++++++++++++++ libnymea-gpio.pro | 7 +-- 4 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 gpiomonitor.cpp create mode 100644 gpiomonitor.h diff --git a/.gitignore b/.gitignore index 5291a38..8176148 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ target_wrapper.* # QtCreator CMake CMakeLists.txt.user* + +.crossbuilder diff --git a/gpiomonitor.cpp b/gpiomonitor.cpp new file mode 100644 index 0000000..48f69c6 --- /dev/null +++ b/gpiomonitor.cpp @@ -0,0 +1,110 @@ +#include "gpiomonitor.h" + +#include + +GpioMonitor::GpioMonitor(int gpio, QObject *parent) : + QThread(parent) +{ + m_gpio = new Gpio(gpio, this); + connect(this, &GpioMonitor::started, this, &GpioMonitor::onThreadStarted); + connect(this, &GpioMonitor::finished, this, &GpioMonitor::onThreadFinished); +} + +GpioMonitor::~GpioMonitor() +{ + disable(); + wait(); +} + +Gpio::Value GpioMonitor::value() +{ + QMutexLocker valueLocker(&m_valueMutex); + return m_value; +} + +bool GpioMonitor::enabled() const +{ + return m_enabled; +} + +void GpioMonitor::setValue(Gpio::Value value) +{ + QMutexLocker valueLocker(&m_valueMutex); + if (m_value == value) + return; + + m_value = value; + emit valueChanged(m_value); +} + +void GpioMonitor::setEnabled(bool enabled) +{ + if (m_enabled == enabled) + return; + + m_enabled = enabled; + emit enabledChanged(m_enabled); +} + +void GpioMonitor::run() +{ + // Poll the GPIO value until the stop is true + while (true) { + + // Note: the setValue() method takes care about the mutex locking + setValue(m_gpio->value()); + + // Check if we should stop the thread + QMutexLocker stopLocker(&m_stopMutex); + if (m_stop) break; + msleep(50); + } +} + +void GpioMonitor::onThreadStarted() +{ + setEnabled(true); +} + +void GpioMonitor::onThreadFinished() +{ + setEnabled(false); +} + +bool GpioMonitor::enable() +{ + if (isRunning()) { + qCWarning(dcGpio()) << "This GPIO monitor is already running."; + return true; + } + + QMutexLocker locker(&m_stopMutex); + m_stop = false; + + if (!m_gpio->exportGpio()) { + qCWarning(dcGpio()) << "Could not enable GPIO monitor."; + return false; + } + + if (!m_gpio->setDirection(Gpio::DirectionInput)) { + qCWarning(dcGpio()) << "Could not enable GPIO monitor."; + return false; + } + + if (!m_gpio->setEdgeInterrupt(Gpio::EdgeBoth)) { + qCWarning(dcGpio()) << "Could not enable GPIO monitor."; + return false; + } + + // Everything went fine, lets start the poll thread and inform about the result + start(); + return true; +} + +void GpioMonitor::disable() +{ + // Stop the thread if not already disabled + QMutexLocker locker(&m_stopMutex); + if (m_stop) return; + m_stop = true; +} diff --git a/gpiomonitor.h b/gpiomonitor.h new file mode 100644 index 0000000..da234f3 --- /dev/null +++ b/gpiomonitor.h @@ -0,0 +1,51 @@ +#ifndef GPIOMONITOR_H +#define GPIOMONITOR_H + +#include +#include +#include + +#include "gpio.h" + +class GpioMonitor : public QThread +{ + Q_OBJECT +public: + explicit GpioMonitor(int gpio, QObject *parent = nullptr); + ~GpioMonitor() override; + + Gpio::Value value(); + bool enabled() const; + +private: + Gpio *m_gpio = nullptr; + bool m_enabled = false; + + // Thread stuff + QMutex m_valueMutex; + Gpio::Value m_value = Gpio::ValueInvalid; + + QMutex m_stopMutex; + bool m_stop = false; + + void setValue(Gpio::Value value); + void setEnabled(bool enabled); + +protected: + void run() override; + +signals: + void valueChanged(bool value); + void enabledChanged(bool enabled); + +private slots: + void onThreadStarted(); + void onThreadFinished(); + +public slots: + bool enable(); + void disable(); + +}; + +#endif // GPIOMONITOR_H diff --git a/libnymea-gpio.pro b/libnymea-gpio.pro index ee75995..4d36cfc 100644 --- a/libnymea-gpio.pro +++ b/libnymea-gpio.pro @@ -8,14 +8,15 @@ QMAKE_LFLAGS *= -std=c++11 VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') - DEFINES += LIBNYMEAGPIO_LIBRARY SOURCES += \ - gpio.cpp + gpio.cpp \ + gpiomonitor.cpp HEADERS += \ - gpio.h + gpio.h \ + gpiomonitor.h target.path = $$[QT_INSTALL_LIBS] INSTALLS += target From 6af34bd5314e5141a64415203a99fc0e28738453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 4 Sep 2019 13:59:24 +0200 Subject: [PATCH 04/17] Add tool basic structure and rename to nymea-gpio --- debian/changelog | 2 +- debian/control | 13 ++++++++++-- debian/copyright | 12 +++++++++-- debian/nymea-gpio.tool.install.in | 1 + gpio.cpp => libnymea-gpio/gpio.cpp | 0 gpio.h => libnymea-gpio/gpio.h | 0 .../gpiomonitor.cpp | 13 ++++++++++-- gpiomonitor.h => libnymea-gpio/gpiomonitor.h | 0 .../libnymea-gpio.pro | 11 ++-------- nymea-gpio-tool/main.cpp | 21 +++++++++++++++++++ nymea-gpio-tool/nymea-gpio-tool.pro | 15 +++++++++++++ nymea-gpio.pri | 10 +++++++++ nymea-gpio.pro | 4 ++++ 13 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 debian/nymea-gpio.tool.install.in rename gpio.cpp => libnymea-gpio/gpio.cpp (100%) rename gpio.h => libnymea-gpio/gpio.h (100%) rename gpiomonitor.cpp => libnymea-gpio/gpiomonitor.cpp (91%) rename gpiomonitor.h => libnymea-gpio/gpiomonitor.h (100%) rename libnymea-gpio.pro => libnymea-gpio/libnymea-gpio.pro (82%) create mode 100644 nymea-gpio-tool/main.cpp create mode 100644 nymea-gpio-tool/nymea-gpio-tool.pro create mode 100644 nymea-gpio.pri create mode 100644 nymea-gpio.pro diff --git a/debian/changelog b/debian/changelog index a74681c..99fdfbb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -libnymea-gpio (0.1.0) bionic; urgency=medium +nymea-gpio (1.0.0) bionic; urgency=medium * Initial release. diff --git a/debian/control b/debian/control index 892c31a..06579e7 100644 --- a/debian/control +++ b/debian/control @@ -1,4 +1,4 @@ -Source: libnymea-gpio +Source: nymea-gpio Section: utils Priority: optional Maintainer: Simon Stürz @@ -18,13 +18,22 @@ Depends: ${shlibs:Depends}, Description: Qt 5 based library for GPIO interaction. Qt 5 based library for GPIO interaction. +Package: nymea-gpio-tool +Section: tools +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libnymea-gpio (= ${binary:Version}) +Description: Qt 5 based tool for GPIO interaction. + Qt 5 based tool for GPIO interaction. + Package: libnymea-gpio-dev Section: libdevel Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, pkg-config, - libnymea-gpio (= ${binary:Version}), + libnymea-gpio (= ${binary:Version}) Description: Qt 5 based library for GPIO interaction - development files Development files for Qt 5 based GPIO library. diff --git a/debian/copyright b/debian/copyright index a5e8d5e..6eb66da 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,11 +1,19 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Author: Simon Stürz -Download: https://github.com/guh/libnymea-gpio +Download: https://github.com/guh/nymea-gpio License: LGPL-3 On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/LGPL-3'. -Files: * +License: GPL-3+ + On Debian systems, the complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL-3'. + +Files: libnymea-gpio/* Copyright: (C) 2019 Simon Stürz License: LGPL-3 + +Files: nymea-gpio-tool/* +Copyright: (C) 2019 Simon Stürz +License: GPL-3+ diff --git a/debian/nymea-gpio.tool.install.in b/debian/nymea-gpio.tool.install.in new file mode 100644 index 0000000..0e8e70b --- /dev/null +++ b/debian/nymea-gpio.tool.install.in @@ -0,0 +1 @@ +nymea-gpio-tool usr/bin diff --git a/gpio.cpp b/libnymea-gpio/gpio.cpp similarity index 100% rename from gpio.cpp rename to libnymea-gpio/gpio.cpp diff --git a/gpio.h b/libnymea-gpio/gpio.h similarity index 100% rename from gpio.h rename to libnymea-gpio/gpio.h diff --git a/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp similarity index 91% rename from gpiomonitor.cpp rename to libnymea-gpio/gpiomonitor.cpp index 48f69c6..7e66708 100644 --- a/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -48,6 +48,9 @@ void GpioMonitor::setEnabled(bool enabled) void GpioMonitor::run() { + // Initialize the current value + setValue(m_gpio->value()); + // Poll the GPIO value until the stop is true while (true) { @@ -78,8 +81,11 @@ bool GpioMonitor::enable() return true; } - QMutexLocker locker(&m_stopMutex); - m_stop = false; + // Init the GPIO + if (!m_gpio->isAvailable()) { + qCWarning(dcGpio()) << "Could not enable GPIO monitor."; + return false; + } if (!m_gpio->exportGpio()) { qCWarning(dcGpio()) << "Could not enable GPIO monitor."; @@ -96,6 +102,9 @@ bool GpioMonitor::enable() return false; } + QMutexLocker locker(&m_stopMutex); + m_stop = false; + // Everything went fine, lets start the poll thread and inform about the result start(); return true; diff --git a/gpiomonitor.h b/libnymea-gpio/gpiomonitor.h similarity index 100% rename from gpiomonitor.h rename to libnymea-gpio/gpiomonitor.h diff --git a/libnymea-gpio.pro b/libnymea-gpio/libnymea-gpio.pro similarity index 82% rename from libnymea-gpio.pro rename to libnymea-gpio/libnymea-gpio.pro index 4d36cfc..bd37280 100644 --- a/libnymea-gpio.pro +++ b/libnymea-gpio/libnymea-gpio.pro @@ -1,15 +1,8 @@ +include(../nymea-gpio.pri) + TARGET = nymea-gpio TEMPLATE = lib -QT -= gui - -QMAKE_CXXFLAGS *= -Werror -std=c++11 -g -QMAKE_LFLAGS *= -std=c++11 - -VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') - -DEFINES += LIBNYMEAGPIO_LIBRARY - SOURCES += \ gpio.cpp \ gpiomonitor.cpp diff --git a/nymea-gpio-tool/main.cpp b/nymea-gpio-tool/main.cpp new file mode 100644 index 0000000..99aff39 --- /dev/null +++ b/nymea-gpio-tool/main.cpp @@ -0,0 +1,21 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication application(argc, argv); + application.setOrganizationName("guh"); + application.setApplicationName("nymea-gpio-tool"); + application.setApplicationVersion(VERSION_STRING); + + QCommandLineParser parser; + parser.addHelpOption(); + QString applicationDescription = QString("\nnymea-gpio-tool is a command line tool which allowes to interact with GPIOs.\n" + "Version: %1\n" + "Copyright %2 2019 Simon Stürz \n" + "Released under the GNU GENERAL PUBLIC LICENSE Version 3.").arg(application.applicationVersion()).arg(QChar(0xA9)); + parser.setApplicationDescription(applicationDescription); + parser.process(application); + + return application.exec(); +} diff --git a/nymea-gpio-tool/nymea-gpio-tool.pro b/nymea-gpio-tool/nymea-gpio-tool.pro new file mode 100644 index 0000000..302ae7f --- /dev/null +++ b/nymea-gpio-tool/nymea-gpio-tool.pro @@ -0,0 +1,15 @@ +include(../nymea-gpio.pri) + +TARGET = nymea-gpio-tool + +CONFIG += console +CONFIG -= app_bundle +TEMPLATE = app + +INCLUDEPATH += $$top_srcdir/libnymea-gpio/ +LIBS += -L$$top_builddir/libnymea-gpio/ -lnymea-gpio + +SOURCES += main.cpp \ + +target.path = /usr/bin +INSTALLS += target diff --git a/nymea-gpio.pri b/nymea-gpio.pri new file mode 100644 index 0000000..9fb5920 --- /dev/null +++ b/nymea-gpio.pri @@ -0,0 +1,10 @@ +QT -= gui + +QMAKE_CXXFLAGS *= -Werror -std=c++11 -g +QMAKE_LFLAGS *= -std=c++11 + +top_srcdir=$$PWD +top_builddir=$$shadowed($$PWD) + +VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') +DEFINES += VERSION_STRING=\\\"$${VERSION_STRING}\\\" diff --git a/nymea-gpio.pro b/nymea-gpio.pro new file mode 100644 index 0000000..57b0086 --- /dev/null +++ b/nymea-gpio.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = libnymea-gpio nymea-gpio-tool +nymea-gpio-tool.depends = libnymea-gpio + From 576dd14ac633e76928765e20ae90a7da1ba6b79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 4 Sep 2019 14:02:06 +0200 Subject: [PATCH 05/17] Add packaging for nymea-gpio-tool --- debian/nymea-gpio-tool.install.in | 1 + debian/nymea-gpio.tool.install.in | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 debian/nymea-gpio-tool.install.in delete mode 100644 debian/nymea-gpio.tool.install.in diff --git a/debian/nymea-gpio-tool.install.in b/debian/nymea-gpio-tool.install.in new file mode 100644 index 0000000..e63d844 --- /dev/null +++ b/debian/nymea-gpio-tool.install.in @@ -0,0 +1 @@ +nymea-gpio-tool/nymea-gpio-tool usr/bin diff --git a/debian/nymea-gpio.tool.install.in b/debian/nymea-gpio.tool.install.in deleted file mode 100644 index 0e8e70b..0000000 --- a/debian/nymea-gpio.tool.install.in +++ /dev/null @@ -1 +0,0 @@ -nymea-gpio-tool usr/bin From f9581b386bf7167fc3de27bfc079bada9eb1e121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 4 Sep 2019 17:02:25 +0200 Subject: [PATCH 06/17] Add first version of gpio-tool --- libnymea-gpio/gpiomonitor.cpp | 9 +- libnymea-gpio/gpiomonitor.h | 3 +- nymea-gpio-tool/main.cpp | 131 +++++++++++++++++++++++++++- nymea-gpio-tool/nymea-gpio-tool.pro | 2 + 4 files changed, 138 insertions(+), 7 deletions(-) diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index 7e66708..22d4975 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -2,8 +2,9 @@ #include -GpioMonitor::GpioMonitor(int gpio, QObject *parent) : - QThread(parent) +GpioMonitor::GpioMonitor(int gpio, Gpio::Edge edge, QObject *parent) : + QThread(parent), + m_edge(edge) { m_gpio = new Gpio(gpio, this); connect(this, &GpioMonitor::started, this, &GpioMonitor::onThreadStarted); @@ -97,8 +98,8 @@ bool GpioMonitor::enable() return false; } - if (!m_gpio->setEdgeInterrupt(Gpio::EdgeBoth)) { - qCWarning(dcGpio()) << "Could not enable GPIO monitor."; + if (!m_gpio->setEdgeInterrupt(m_edge)) { + qCWarning(dcGpio()) << "Could not set interrupt for the GPIO monitor."; return false; } diff --git a/libnymea-gpio/gpiomonitor.h b/libnymea-gpio/gpiomonitor.h index da234f3..1cc4a59 100644 --- a/libnymea-gpio/gpiomonitor.h +++ b/libnymea-gpio/gpiomonitor.h @@ -11,7 +11,7 @@ class GpioMonitor : public QThread { Q_OBJECT public: - explicit GpioMonitor(int gpio, QObject *parent = nullptr); + explicit GpioMonitor(int gpio, Gpio::Edge edge = Gpio::EdgeBoth, QObject *parent = nullptr); ~GpioMonitor() override; Gpio::Value value(); @@ -19,6 +19,7 @@ public: private: Gpio *m_gpio = nullptr; + Gpio::Edge m_edge = Gpio::EdgeBoth; bool m_enabled = false; // Thread stuff diff --git a/nymea-gpio-tool/main.cpp b/nymea-gpio-tool/main.cpp index 99aff39..85f7ec0 100644 --- a/nymea-gpio-tool/main.cpp +++ b/nymea-gpio-tool/main.cpp @@ -1,6 +1,8 @@ #include #include +#include "gpiomonitor.h" + int main(int argc, char *argv[]) { QCoreApplication application(argc, argv); @@ -10,12 +12,137 @@ int main(int argc, char *argv[]) QCommandLineParser parser; parser.addHelpOption(); + parser.addVersionOption(); QString applicationDescription = QString("\nnymea-gpio-tool is a command line tool which allowes to interact with GPIOs.\n" "Version: %1\n" - "Copyright %2 2019 Simon Stürz \n" - "Released under the GNU GENERAL PUBLIC LICENSE Version 3.").arg(application.applicationVersion()).arg(QChar(0xA9)); + "Copyright %2 2019 Simon Stürz \n\n" + "Released under the GNU GENERAL PUBLIC LICENSE Version 3.\n").arg(application.applicationVersion()).arg(QChar(0xA9)); parser.setApplicationDescription(applicationDescription); + + QCommandLineOption gpioOption(QStringList() << "g" << "gpio", "The gpio number to use.", "GPIO"); + parser.addOption(gpioOption); + + QCommandLineOption interruptOption(QStringList() << "i" << "interrupt", "Configure the input GPIO to the given interrupt. This option is only allowed for monitoring. Allowerd interrupts are: [rising, falling, both, none]. Default is \"both\".", "INTERRUPT"); + parser.addOption(interruptOption); + + QCommandLineOption valueOption(QStringList() << "s" << "set-value", "Configure the GPIO to output and set the value. Allowerd values are: [0, 1].", "VALUE"); + parser.addOption(valueOption); + + QCommandLineOption monitorOption(QStringList() << "m" << "monitor", "Monitor the given gpio. The GPIO wil automatically configured as input and print any change regarding to the given interrupt behaviour."); + parser.addOption(monitorOption); + + QCommandLineOption activeLowOption(QStringList() << "a" << "active-low", "Set the GPIO to active low. Allowerd values are: [0, 1]", "VALUE"); + parser.addOption(activeLowOption); + parser.process(application); + // Make sure there is a GPIO number passed + if (!parser.isSet(gpioOption)) { + qCritical() << "No GPIO number specified. Please specify a valid GPIO number using -g, --gpio GPIO"; + parser.showHelp(1); + } + + // Verify GPIO number + bool gpioNumberOk; + int gpioNumber = parser.value(gpioOption).toInt(&gpioNumberOk); + if (!gpioNumberOk || gpioNumber < 0) { + qCritical() << "Invalid GPIO number" << parser.value(gpioOption) << "passed. The GPIO number has to be a positiv integer."; + application.exit(1); + } + + // Verify input output operations + if ((parser.isSet(interruptOption) || parser.isSet(monitorOption)) && parser.isSet(valueOption)) { + qCritical() << "Invalid parameter combination. The set value can only be used for output GPIO, the monitor and interrupt parameter can only be used for input GPIO."; + application.exit(1); + } + + Gpio::Edge edge = Gpio::EdgeBoth; + if (parser.isSet(interruptOption)) { + if (parser.value(interruptOption).toLower() == "rising") { + edge = Gpio::EdgeRising; + } else if (parser.value(interruptOption).toLower() == "falling") { + edge = Gpio::EdgeFalling; + } else if (parser.value(interruptOption).toLower() == "none") { + edge = Gpio::EdgeNone; + } else if (parser.value(interruptOption).toLower() == "both") { + edge = Gpio::EdgeBoth; + } else { + qCritical() << "Invalid interrupt parameter" << parser.value(interruptOption) << "passed. Valid options are [rising, falling, both, none]."; + application.exit(1); + } + } + + bool activeLow = false; + if (parser.isSet(activeLowOption)) { + if (parser.value(activeLowOption) == "1") { + activeLow = true; + } else if (parser.value(activeLowOption) == "0") { + activeLow = false; + } else { + qCritical() << "Invalid active low parameter" << parser.value(activeLowOption) << "passed. Valid options are [0, 1]."; + application.exit(1); + } + } + + Gpio::Value value = Gpio::ValueInvalid; + if (parser.isSet(valueOption)) { + if (parser.value(valueOption) == "1") { + value = Gpio::ValueHigh; + } else if (parser.value(valueOption) == "0") { + value = Gpio::ValueLow; + } else { + qCritical() << "Invalid set value parameter" << parser.value(valueOption) << "passed. Valid options are [0, 1]."; + application.exit(1); + } + } + + if (!Gpio::isAvailable()) { + qCritical() << "There are no GPIOs available on this platform."; + application.exit(2); + } + + // Configure the GPIO + if (parser.isSet(valueOption)) { + Gpio *gpio = new Gpio(gpioNumber); + if (!gpio->exportGpio()) { + qCritical() << "Could not export GPIO" << gpioNumber; + exit(1); + } + + if (!gpio->setDirection(Gpio::DirectionOutput)) { + qCritical() << "Could not configure GPIO" << gpioNumber << "as output."; + exit(1); + } + + if (parser.isSet(activeLowOption)) { + if (!gpio->setActiveLow(activeLow)) { + qCritical() << "Could not set GPIO" << gpioNumber << "to active low" << activeLow; + exit(1); + } + } + + // Finally set the value + if (!gpio->setValue(value)) { + qCritical() << "Could not set GPIO" << gpioNumber << "value to" << value; + exit(1); + } + + application.exit(); + } else { + GpioMonitor *monitor = new GpioMonitor(gpioNumber, edge); + QObject::connect(monitor, &GpioMonitor::enabledChanged, [gpioNumber](bool enabled) { + qDebug() << "GPIO" << gpioNumber << "monitor" << (enabled ? "enabled" : "disabled"); + }); + + QObject::connect(monitor, &GpioMonitor::valueChanged, [gpioNumber](bool value) { + qDebug() << "GPIO" << gpioNumber << "value changed" << (value ? "1" : "0"); + }); + + if (!monitor->enable()) { + qCritical() << "Could not enable GPIO" << gpioNumber << "monitor."; + exit(1); + } + } + return application.exec(); } diff --git a/nymea-gpio-tool/nymea-gpio-tool.pro b/nymea-gpio-tool/nymea-gpio-tool.pro index 302ae7f..880ba19 100644 --- a/nymea-gpio-tool/nymea-gpio-tool.pro +++ b/nymea-gpio-tool/nymea-gpio-tool.pro @@ -13,3 +13,5 @@ SOURCES += main.cpp \ target.path = /usr/bin INSTALLS += target + +HEADERS += From 237925b497506ada8c6f988aa01eccf23a6d539a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 5 Sep 2019 14:46:08 +0200 Subject: [PATCH 07/17] Make gpio monitor work properly based in interrupts --- libnymea-gpio/gpio.cpp | 84 +++++++++------ libnymea-gpio/gpiomonitor.cpp | 161 ++++++++++++++++++++++------ libnymea-gpio/gpiomonitor.h | 14 ++- libnymea-gpio/libnymea-gpio.pro | 8 +- nymea-gpio-tool/application.cpp | 46 ++++++++ nymea-gpio-tool/application.h | 15 +++ nymea-gpio-tool/main.cpp | 56 ++++++---- nymea-gpio-tool/nymea-gpio-tool.pro | 6 +- nymea-gpio.pri | 7 +- 9 files changed, 294 insertions(+), 103 deletions(-) create mode 100644 nymea-gpio-tool/application.cpp create mode 100644 nymea-gpio-tool/application.h diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index c47ba0b..3b49755 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -7,7 +7,7 @@ Gpio::Gpio(int gpio, QObject *parent) : QObject(parent), m_gpio(gpio), m_direction(Gpio::DirectionInvalid), - m_gpioDirectory(QDir(QString("/sys/class/gpio/gpio%1/").arg(QString::number(gpio)))) + m_gpioDirectory(QDir(QString("/sys/class/gpio/gpio%1").arg(QString::number(gpio)))) { } @@ -39,9 +39,12 @@ int Gpio::gpioNumber() const /*! Returns true if this \l{Gpio} could be exported in the system file \tt {/sys/class/gpio/export}. If this Gpio is already exported, this function will return true. */ bool Gpio::exportGpio() { + qCDebug(dcGpio()) << "Export GPIO" << m_gpio; // Check if already exported - if (m_gpioDirectory.exists()) + if (m_gpioDirectory.exists()) { + qCDebug(dcGpio()) << "GPIO" << m_gpio << "already exported."; return true; + } QFile exportFile("/sys/class/gpio/export"); if (!exportFile.open(QIODevice::WriteOnly | QIODevice::Text)) { @@ -58,6 +61,8 @@ bool Gpio::exportGpio() /*! Returns true if this \l{Gpio} could be unexported in the system file \tt {/sys/class/gpio/unexport}. */ bool Gpio::unexportGpio() { + qCDebug(dcGpio()) << "Unexport GPIO" << m_gpio; + QFile unexportFile("/sys/class/gpio/unexport"); if (!unexportFile.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO unexport file:" << unexportFile.errorString(); @@ -73,12 +78,13 @@ bool Gpio::unexportGpio() /*! Returns true if the \a direction of this GPIO could be set. \sa Gpio::Direction, */ bool Gpio::setDirection(Gpio::Direction direction) { + qCDebug(dcGpio()) << "Set GPIO" << m_gpio << "direction" << direction; if (direction == Gpio::DirectionInvalid) { qCWarning(dcGpio()) << "Setting an invalid direction is forbidden."; return false; } - QFile directionFile(m_gpioDirectory.path() + "/direction"); + QFile directionFile(m_gpioDirectory.path() + QDir::separator() + "direction"); if (!directionFile.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "direction file:" << directionFile.errorString(); return false; @@ -105,7 +111,7 @@ bool Gpio::setDirection(Gpio::Direction direction) /*! Returns the direction of this \l{Gpio}. */ Gpio::Direction Gpio::direction() { - QFile directionFile(m_gpioDirectory.path() + "/direction"); + QFile directionFile(m_gpioDirectory.path() + QDir::separator() + "direction"); if (!directionFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "direction file:" << directionFile.fileName() << directionFile.errorString(); return Gpio::DirectionInvalid; @@ -130,6 +136,8 @@ Gpio::Direction Gpio::direction() /*! Returns true if the digital \a value of this \l{Gpio} could be set correctly. */ bool Gpio::setValue(Gpio::Value value) { + qCDebug(dcGpio()) << "Set GPIO" << m_gpio << "value" << value; + // Check given value if (value == Gpio::ValueInvalid) { qCWarning(dcGpio()) << "Setting an invalid value is forbidden."; @@ -147,7 +155,7 @@ bool Gpio::setValue(Gpio::Value value) return false; } - QFile valueFile(m_gpioDirectory.path() + "/value"); + QFile valueFile(m_gpioDirectory.path() + QDir::separator() + "value"); if (!valueFile.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "value file:" << valueFile.errorString(); return false; @@ -173,7 +181,7 @@ bool Gpio::setValue(Gpio::Value value) /*! Returns the current digital value of this \l{Gpio}. */ Gpio::Value Gpio::value() { - QFile valueFile(m_gpioDirectory.path() + "/value"); + QFile valueFile(m_gpioDirectory.path() + QDir::separator() + "value"); if (!valueFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "value file:" << valueFile.errorString(); return Gpio::ValueInvalid; @@ -196,7 +204,9 @@ Gpio::Value Gpio::value() /*! This method allows to invert the logic of this \l{Gpio}. Returns true, if the GPIO could be set \a activeLow. */ bool Gpio::setActiveLow(bool activeLow) { - QFile activeLowFile(m_gpioDirectory.path() + "/active_low"); + qCDebug(dcGpio()) << "Set GPIO" << m_gpio << "active low" << activeLow; + + QFile activeLowFile(m_gpioDirectory.path() + QDir::separator() + "active_low"); if (!activeLowFile.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "active_low file:" << activeLowFile.errorString(); return false; @@ -216,7 +226,7 @@ bool Gpio::setActiveLow(bool activeLow) /*! Returns true if the logic of this \l{Gpio} is inverted (1 = low, 0 = high). */ bool Gpio::activeLow() { - QFile activeLowFile(m_gpioDirectory.path() + "/active_low"); + QFile activeLowFile(m_gpioDirectory.path() + QDir::separator() + "active_low"); if (!activeLowFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "active_low file:" << activeLowFile.errorString(); return false; @@ -237,12 +247,14 @@ bool Gpio::activeLow() * when an interrupt occurs. */ bool Gpio::setEdgeInterrupt(Gpio::Edge edge) { + if (m_direction == Gpio::DirectionOutput) { qCWarning(dcGpio()) << "Could not set edge interrupt, GPIO is configured as an output."; return false; } - QFile edgeFile(m_gpioDirectory.path() + "/edge"); + qCDebug(dcGpio()) << "Set GPIO" << m_gpio << "edge interrupt" << edge; + QFile edgeFile(m_gpioDirectory.path() + QDir::separator() + "edge"); if (!edgeFile.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "edge file:" << edgeFile.errorString(); return false; @@ -271,7 +283,7 @@ bool Gpio::setEdgeInterrupt(Gpio::Edge edge) /*! Returns the edge interrupt of this \l{Gpio}. */ Gpio::Edge Gpio::edgeInterrupt() { - QFile edgeFile(m_gpioDirectory.path() + "/edge"); + QFile edgeFile(m_gpioDirectory.path() + QDir::separator() + "edge"); if (!edgeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "edge file:" << edgeFile.errorString(); return Gpio::EdgeNone; @@ -301,41 +313,43 @@ QDebug operator<<(QDebug debug, Gpio *gpio) { debug.nospace() << "Gpio(" << gpio->gpioNumber() << ", "; if (gpio->direction() == Gpio::DirectionInput) { - debug.nospace() << "Input, "; - } else if (gpio->direction() == Gpio::DirectionOutput) { - debug.nospace() << "Output, "; - } else { - debug.nospace() << "Invalid, "; - } + debug.nospace() << "input, "; - switch (gpio->edgeInterrupt()) { - case Gpio::EdgeFalling: - debug.nospace() << "Ir: Falling, "; - break; - case Gpio::EdgeRising: - debug.nospace() << "Ir: Rising, "; - break; - case Gpio::EdgeBoth: - debug.nospace() << "Ir: Both, "; - break; - case Gpio::EdgeNone: - debug.nospace() << "Ir: None, "; - break; + switch (gpio->edgeInterrupt()) { + case Gpio::EdgeFalling: + debug.nospace() << "edge: falling, "; + break; + case Gpio::EdgeRising: + debug.nospace() << "edge: rising, "; + break; + case Gpio::EdgeBoth: + debug.nospace() << "edge: both, "; + break; + case Gpio::EdgeNone: + debug.nospace() << "edge: none, "; + break; + } + } else if (gpio->direction() == Gpio::DirectionOutput) { + debug.nospace() << "output, "; + } else { + debug.nospace() << "invalid, "; } if (gpio->activeLow()) { - debug.nospace() << "Active Low: 1, "; + debug.nospace() << "active low: 1, "; } else { - debug.nospace() << "Active Low: 0, "; + debug.nospace() << "active low: 0, "; } if (gpio->value() == Gpio::ValueHigh) { - debug.nospace() << "Value: 1)"; + debug.nospace() << "value: 1"; } else if (gpio->value() == Gpio::ValueLow) { - debug.nospace() << "Value: 0)"; + debug.nospace() << "value: 0"; } else { - debug.nospace() << "Value: Invalid)"; + debug.nospace() << "value: invalid"; } - return debug; + debug.nospace() << ")"; + + return debug.space(); } diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index 22d4975..b8589d7 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -1,20 +1,47 @@ #include "gpiomonitor.h" +#include #include -GpioMonitor::GpioMonitor(int gpio, Gpio::Edge edge, QObject *parent) : +GpioMonitor::GpioMonitor(int gpio, QObject *parent) : QThread(parent), - m_edge(edge) + m_gpioNumber(gpio) { - m_gpio = new Gpio(gpio, this); - connect(this, &GpioMonitor::started, this, &GpioMonitor::onThreadStarted); - connect(this, &GpioMonitor::finished, this, &GpioMonitor::onThreadFinished); + // Inform about the thread status + connect(this, &GpioMonitor::started, this, &GpioMonitor::onThreadStarted, Qt::DirectConnection); + connect(this, &GpioMonitor::finished, this, &GpioMonitor::onThreadFinished, Qt::DirectConnection); } GpioMonitor::~GpioMonitor() { disable(); - wait(); + wait(200); +} + +Gpio::Edge GpioMonitor::edge() const +{ + return m_edge; +} + +void GpioMonitor::setEdge(Gpio::Edge edge) +{ + if (m_edge == edge) + return; + + m_edge = edge; +} + +bool GpioMonitor::activeLow() const +{ + return m_activeLow; +} + +void GpioMonitor::setActiveLow(bool activeLow) +{ + if (m_activeLow == activeLow) + return; + + m_activeLow = activeLow; } Gpio::Value GpioMonitor::value() @@ -31,11 +58,18 @@ bool GpioMonitor::enabled() const void GpioMonitor::setValue(Gpio::Value value) { QMutexLocker valueLocker(&m_valueMutex); - if (m_value == value) - return; - m_value = value; - emit valueChanged(m_value); + + switch (m_value) { + case Gpio::ValueLow: + emit interruptOccured(false); + break; + case Gpio::ValueHigh: + emit interruptOccured(true); + break; + default: + break; + } } void GpioMonitor::setEnabled(bool enabled) @@ -49,70 +83,129 @@ void GpioMonitor::setEnabled(bool enabled) void GpioMonitor::run() { - // Initialize the current value - setValue(m_gpio->value()); + // Create GPIO in the thread for initialisation + Gpio *inputGpio = new Gpio(m_gpioNumber); + if (!inputGpio->exportGpio()) { + qCWarning(dcGpio()) << "Could not enable GPIO monitor."; + delete inputGpio; + return; + } - // Poll the GPIO value until the stop is true + if (!inputGpio->setDirection(Gpio::DirectionInput)) { + qCWarning(dcGpio()) << "Could not enable GPIO monitor."; + delete inputGpio; + return; + } + + if (!inputGpio->setEdgeInterrupt(m_edge)) { + qCWarning(dcGpio()) << "Could not set interrupt for the GPIO monitor."; + delete inputGpio; + return; + } + + if (!inputGpio->setActiveLow(m_activeLow)) { + qCWarning(dcGpio()) << "Could not set active low for the GPIO monitor."; + delete inputGpio; + return; + } + + + // In order to do correctly, use poll (2) according to the kernel documentation + // https://www.kernel.org/doc/Documentation/gpio/sysfs.txt + QFile valueFile(inputGpio->gpioDirectory() + QDir::separator() + "value"); + if (!valueFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << inputGpio << "value file:" << valueFile.errorString(); + delete inputGpio; + return; + } + + struct pollfd fdset[1]; + int rc = -1; + uint nfds = 1; + int timeout = 100; // ms + fdset[0].fd = valueFile.handle(); + fdset[0].events = POLLPRI; + + // Poll the GPIO value until stop is true while (true) { + // Poll the value file + rc = poll(fdset, nfds, timeout); - // Note: the setValue() method takes care about the mutex locking - setValue(m_gpio->value()); + // Poll failed... + if (rc < 0) { + qCWarning(dcGpio()) << "Failed to poll" << inputGpio; + break; + } // Check if we should stop the thread QMutexLocker stopLocker(&m_stopMutex); if (m_stop) break; - msleep(50); + + // No interrupt occured + if (rc == 0) + continue; + + // Interrupt occured + if (fdset[0].revents & POLLPRI) { + QString valueString; + QTextStream readStream(&valueFile); + if (!readStream.seek(0)) { + qCWarning(dcGpio()) << "Failed to seek value file of" << inputGpio; + continue; + } + + // Notify the main thread about the interrupt + readStream >> valueString; + if (valueString == "0") { + setValue(Gpio::ValueLow); + } else { + setValue(Gpio::ValueHigh); + } + } } + + // Clean up once done + valueFile.close(); + delete inputGpio; } void GpioMonitor::onThreadStarted() { + qCDebug(dcGpio()) << "Monitor thread started"; setEnabled(true); } void GpioMonitor::onThreadFinished() { + qCDebug(dcGpio()) << "Monitor thread finished"; setEnabled(false); } bool GpioMonitor::enable() { + qCDebug(dcGpio()) << "Enable gpio monitor"; if (isRunning()) { qCWarning(dcGpio()) << "This GPIO monitor is already running."; return true; } // Init the GPIO - if (!m_gpio->isAvailable()) { - qCWarning(dcGpio()) << "Could not enable GPIO monitor."; - return false; - } - - if (!m_gpio->exportGpio()) { - qCWarning(dcGpio()) << "Could not enable GPIO monitor."; - return false; - } - - if (!m_gpio->setDirection(Gpio::DirectionInput)) { - qCWarning(dcGpio()) << "Could not enable GPIO monitor."; - return false; - } - - if (!m_gpio->setEdgeInterrupt(m_edge)) { - qCWarning(dcGpio()) << "Could not set interrupt for the GPIO monitor."; + if (!Gpio::isAvailable()) { + qCWarning(dcGpio()) << "Could not enable GPIO monitor. There are no GPIOs available on this platform."; return false; } QMutexLocker locker(&m_stopMutex); m_stop = false; - // Everything went fine, lets start the poll thread and inform about the result + // Everything looks good, lets start the poll thread and inform about the result start(); return true; } void GpioMonitor::disable() { + qCDebug(dcGpio()) << "Disable gpio monitor"; // Stop the thread if not already disabled QMutexLocker locker(&m_stopMutex); if (m_stop) return; diff --git a/libnymea-gpio/gpiomonitor.h b/libnymea-gpio/gpiomonitor.h index 1cc4a59..bb109dd 100644 --- a/libnymea-gpio/gpiomonitor.h +++ b/libnymea-gpio/gpiomonitor.h @@ -11,15 +11,23 @@ class GpioMonitor : public QThread { Q_OBJECT public: - explicit GpioMonitor(int gpio, Gpio::Edge edge = Gpio::EdgeBoth, QObject *parent = nullptr); + explicit GpioMonitor(int gpio, QObject *parent = nullptr); ~GpioMonitor() override; + Gpio::Edge edge() const; + void setEdge(Gpio::Edge edge); + + bool activeLow() const; + void setActiveLow(bool activeLow); + Gpio::Value value(); + bool enabled() const; private: - Gpio *m_gpio = nullptr; + int m_gpioNumber = -1; Gpio::Edge m_edge = Gpio::EdgeBoth; + bool m_activeLow = true; bool m_enabled = false; // Thread stuff @@ -36,7 +44,7 @@ protected: void run() override; signals: - void valueChanged(bool value); + void interruptOccured(bool value); void enabledChanged(bool enabled); private slots: diff --git a/libnymea-gpio/libnymea-gpio.pro b/libnymea-gpio/libnymea-gpio.pro index bd37280..033393e 100644 --- a/libnymea-gpio/libnymea-gpio.pro +++ b/libnymea-gpio/libnymea-gpio.pro @@ -3,14 +3,14 @@ include(../nymea-gpio.pri) TARGET = nymea-gpio TEMPLATE = lib -SOURCES += \ - gpio.cpp \ - gpiomonitor.cpp - HEADERS += \ gpio.h \ gpiomonitor.h +SOURCES += \ + gpio.cpp \ + gpiomonitor.cpp + target.path = $$[QT_INSTALL_LIBS] INSTALLS += target diff --git a/nymea-gpio-tool/application.cpp b/nymea-gpio-tool/application.cpp new file mode 100644 index 0000000..73c1d21 --- /dev/null +++ b/nymea-gpio-tool/application.cpp @@ -0,0 +1,46 @@ +#include "application.h" + +#include +#include + +static void catchUnixSignals(const std::vector& quitSignals, const std::vector& ignoreSignals = std::vector()) +{ + auto handler = [](int sig) ->void { + switch (sig) { + case SIGQUIT: + qDebug() << "Cought SIGQUIT quit signal..."; + break; + case SIGINT: + qDebug() << "Cought SIGINT quit signal..."; + break; + case SIGTERM: + qDebug() << "Cought SIGTERM quit signal..."; + break; + case SIGHUP: + qDebug() << "Cought SIGHUP quit signal..."; + break; + case SIGSEGV: { + qCritical() << "Cought SIGSEGV signal. Segmentation fault!"; + exit(EXIT_FAILURE); + } + default: + break; + } + + Application::quit(); + }; + + // all these signals will be ignored. + for (int sig : ignoreSignals) + signal(sig, SIG_IGN); + + for (int sig : quitSignals) + signal(sig, handler); + +} + +Application::Application(int &argc, char **argv) : + QCoreApplication(argc, argv) +{ + catchUnixSignals({SIGQUIT, SIGINT, SIGTERM, SIGHUP, SIGSEGV}); +} diff --git a/nymea-gpio-tool/application.h b/nymea-gpio-tool/application.h new file mode 100644 index 0000000..64941bc --- /dev/null +++ b/nymea-gpio-tool/application.h @@ -0,0 +1,15 @@ +#ifndef APPLICATION_H +#define APPLICATION_H + +#include +#include + +class Application : public QCoreApplication +{ + Q_OBJECT +public: + explicit Application(int &argc, char **argv); + +}; + +#endif // APPLICATION_H diff --git a/nymea-gpio-tool/main.cpp b/nymea-gpio-tool/main.cpp index 85f7ec0..6aeb4d3 100644 --- a/nymea-gpio-tool/main.cpp +++ b/nymea-gpio-tool/main.cpp @@ -1,11 +1,11 @@ -#include #include +#include "application.h" #include "gpiomonitor.h" int main(int argc, char *argv[]) { - QCoreApplication application(argc, argv); + Application application(argc, argv); application.setOrganizationName("guh"); application.setApplicationName("nymea-gpio-tool"); application.setApplicationVersion(VERSION_STRING); @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) QCommandLineOption valueOption(QStringList() << "s" << "set-value", "Configure the GPIO to output and set the value. Allowerd values are: [0, 1].", "VALUE"); parser.addOption(valueOption); - QCommandLineOption monitorOption(QStringList() << "m" << "monitor", "Monitor the given gpio. The GPIO wil automatically configured as input and print any change regarding to the given interrupt behaviour."); + QCommandLineOption monitorOption(QStringList() << "m" << "monitor", "Monitor the given GPIO. The GPIO will automatically configured as input and print any change regarding to the given interrupt behaviour."); parser.addOption(monitorOption); QCommandLineOption activeLowOption(QStringList() << "a" << "active-low", "Set the GPIO to active low. Allowerd values are: [0, 1]", "VALUE"); @@ -39,7 +39,7 @@ int main(int argc, char *argv[]) // Make sure there is a GPIO number passed if (!parser.isSet(gpioOption)) { qCritical() << "No GPIO number specified. Please specify a valid GPIO number using -g, --gpio GPIO"; - parser.showHelp(1); + parser.showHelp(EXIT_FAILURE); } // Verify GPIO number @@ -47,13 +47,13 @@ int main(int argc, char *argv[]) int gpioNumber = parser.value(gpioOption).toInt(&gpioNumberOk); if (!gpioNumberOk || gpioNumber < 0) { qCritical() << "Invalid GPIO number" << parser.value(gpioOption) << "passed. The GPIO number has to be a positiv integer."; - application.exit(1); + return EXIT_FAILURE; } // Verify input output operations if ((parser.isSet(interruptOption) || parser.isSet(monitorOption)) && parser.isSet(valueOption)) { qCritical() << "Invalid parameter combination. The set value can only be used for output GPIO, the monitor and interrupt parameter can only be used for input GPIO."; - application.exit(1); + return EXIT_FAILURE; } Gpio::Edge edge = Gpio::EdgeBoth; @@ -68,19 +68,19 @@ int main(int argc, char *argv[]) edge = Gpio::EdgeBoth; } else { qCritical() << "Invalid interrupt parameter" << parser.value(interruptOption) << "passed. Valid options are [rising, falling, both, none]."; - application.exit(1); + return EXIT_FAILURE; } } - bool activeLow = false; + bool activeLow = true; if (parser.isSet(activeLowOption)) { if (parser.value(activeLowOption) == "1") { - activeLow = true; - } else if (parser.value(activeLowOption) == "0") { activeLow = false; + } else if (parser.value(activeLowOption) == "0") { + activeLow = true; } else { qCritical() << "Invalid active low parameter" << parser.value(activeLowOption) << "passed. Valid options are [0, 1]."; - application.exit(1); + return EXIT_FAILURE; } } @@ -92,13 +92,13 @@ int main(int argc, char *argv[]) value = Gpio::ValueLow; } else { qCritical() << "Invalid set value parameter" << parser.value(valueOption) << "passed. Valid options are [0, 1]."; - application.exit(1); + return EXIT_FAILURE; } } if (!Gpio::isAvailable()) { qCritical() << "There are no GPIOs available on this platform."; - application.exit(2); + return EXIT_FAILURE; } // Configure the GPIO @@ -106,42 +106,54 @@ int main(int argc, char *argv[]) Gpio *gpio = new Gpio(gpioNumber); if (!gpio->exportGpio()) { qCritical() << "Could not export GPIO" << gpioNumber; - exit(1); + return EXIT_FAILURE; } if (!gpio->setDirection(Gpio::DirectionOutput)) { qCritical() << "Could not configure GPIO" << gpioNumber << "as output."; - exit(1); + return EXIT_FAILURE; } if (parser.isSet(activeLowOption)) { if (!gpio->setActiveLow(activeLow)) { qCritical() << "Could not set GPIO" << gpioNumber << "to active low" << activeLow; - exit(1); + return EXIT_FAILURE; } } // Finally set the value if (!gpio->setValue(value)) { qCritical() << "Could not set GPIO" << gpioNumber << "value to" << value; - exit(1); + return EXIT_FAILURE; } - application.exit(); + delete gpio; + return EXIT_SUCCESS; } else { - GpioMonitor *monitor = new GpioMonitor(gpioNumber, edge); + GpioMonitor *monitor = new GpioMonitor(gpioNumber); + monitor->setEdge(edge); + monitor->setActiveLow(activeLow); + + // Inform about enabled changed QObject::connect(monitor, &GpioMonitor::enabledChanged, [gpioNumber](bool enabled) { qDebug() << "GPIO" << gpioNumber << "monitor" << (enabled ? "enabled" : "disabled"); }); - QObject::connect(monitor, &GpioMonitor::valueChanged, [gpioNumber](bool value) { - qDebug() << "GPIO" << gpioNumber << "value changed" << (value ? "1" : "0"); + // Inform about interrupt + QObject::connect(monitor, &GpioMonitor::interruptOccured, [gpioNumber](bool value) { + qDebug() << "GPIO" << gpioNumber << "interrupt occured. Current value:" << (value ? "1" : "0"); }); + // Enable the monitor if (!monitor->enable()) { qCritical() << "Could not enable GPIO" << gpioNumber << "monitor."; - exit(1); + return EXIT_FAILURE; } + + // Clean up the gpio once done + QObject::connect(&application, &Application::aboutToQuit, [monitor](){ + delete monitor; + }); } return application.exec(); diff --git a/nymea-gpio-tool/nymea-gpio-tool.pro b/nymea-gpio-tool/nymea-gpio-tool.pro index 880ba19..2e6a605 100644 --- a/nymea-gpio-tool/nymea-gpio-tool.pro +++ b/nymea-gpio-tool/nymea-gpio-tool.pro @@ -9,9 +9,11 @@ TEMPLATE = app INCLUDEPATH += $$top_srcdir/libnymea-gpio/ LIBS += -L$$top_builddir/libnymea-gpio/ -lnymea-gpio +HEADERS += \ + application.h + SOURCES += main.cpp \ + application.cpp target.path = /usr/bin INSTALLS += target - -HEADERS += diff --git a/nymea-gpio.pri b/nymea-gpio.pri index 9fb5920..f6dcac7 100644 --- a/nymea-gpio.pri +++ b/nymea-gpio.pri @@ -1,7 +1,8 @@ -QT -= gui -QMAKE_CXXFLAGS *= -Werror -std=c++11 -g -QMAKE_LFLAGS *= -std=c++11 +QMAKE_CXXFLAGS += -Werror -std=c++11 -g +QMAKE_LFLAGS += -std=c++11 + +QT -= gui top_srcdir=$$PWD top_builddir=$$shadowed($$PWD) From 60027f558024a5f97b4fb9bb61a01fd22e8aaac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 13 Sep 2019 15:32:18 +0200 Subject: [PATCH 08/17] Add documentation for GpioMonitor and Gpio --- debian/changelog | 2 +- debian/libnymea-gpio-dev.install.in | 4 +- libnymea-gpio/gpio.cpp | 84 ++++++++++++++++++ libnymea-gpio/gpiobutton.cpp | 131 ++++++++++++++++++++++++++++ libnymea-gpio/gpiobutton.h | 61 +++++++++++++ libnymea-gpio/gpiomonitor.cpp | 51 +++++++++++ libnymea-gpio/gpiomonitor.h | 2 +- libnymea-gpio/libnymea-gpio.pro | 10 ++- 8 files changed, 337 insertions(+), 8 deletions(-) create mode 100644 libnymea-gpio/gpiobutton.cpp create mode 100644 libnymea-gpio/gpiobutton.h diff --git a/debian/changelog b/debian/changelog index 99fdfbb..eb4ea68 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -nymea-gpio (1.0.0) bionic; urgency=medium +nymea-gpio (1.0.0) UNRELEASED; urgency=medium * Initial release. diff --git a/debian/libnymea-gpio-dev.install.in b/debian/libnymea-gpio-dev.install.in index 6be4c17..a7b9db4 100644 --- a/debian/libnymea-gpio-dev.install.in +++ b/debian/libnymea-gpio-dev.install.in @@ -1,3 +1,3 @@ usr/lib/@DEB_HOST_MULTIARCH@/libnymea-gpio.so -usr/include/libnymea-gpio/* usr/include/libnymea-gpio -usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/libnymea-gpio.pc +usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/nymea-gpio.pc +usr/include/nymea-gpio/* usr/include/nymea-gpio/ diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index 3b49755..199b9c9 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -1,5 +1,89 @@ #include "gpio.h" +/*! + \class Gpio + \brief Represents a system GPIO in linux systems. + + A "General Purpose Input/Output" (GPIO) is a flexible software-controlled + digital signal. They are provided from many kinds of chip, and are familiar + to Linux developers working with embedded and custom hardware. Each GPIO + represents a bit connected to a particular pin, or "ball" on Ball Grid Array + (BGA) packages. Board schematics show which external hardware connects to + which GPIOs. Drivers can be written generically, so that board setup code + passes such pin configuration data to drivers + (\l{https://www.kernel.org/doc/Documentation/gpio/gpio.txt}{source}). + General Purpose Input/Output (a.k.a. GPIO) is a generic pin on a chip whose + behavior (including whether it is an input or output pin) can be controlled + through this class. An object of of the Gpio class represents a pin. + + \code + Gpio *gpioOut = new Gpio(23, this); + // Export Gpio + if (!gpioOut->exportGpio()) { + qWarning() << "Could not export Gpio" << gpioOut->gpioNumber(); + gpioOut->deleteLater(); + return; + } + // Configure Gpio direction + if (!gpioOut->setDirection(PiGpio::DirectionOutput)) { + qWarning() << "Could not set direction of Gpio" << gpioOut->gpioNumber(); + gpioOut->deleteLater(); + return; + } + gpioOut->setValue(Gpio::ValueHigh) + \endcode + + \code + Gpio *gpioIn = new Gpio(24, this); + // Export Gpio + if (!gpioIn->exportGpio()) { + qWarning() << "Could not export Gpio" << gpioIn->gpioNumber(); + gpioIn->deleteLater(); + return; + } + // Configure Gpio direction + if (!gpioIn->setDirection(PiGpio::DirectionInput)) { + qWarning() << "Could not set direction of Gpio" << gpioIn->gpioNumber(); + gpioIn->deleteLater(); + return; + } + qDebug() << "Current value" << gpioIn->value(); + \endcode + \sa GpioMonitor, GpioButton +*/ + +/*! \enum Gpio::Direction + This enum type specifies the dirction a \l{Gpio}. + \value DirectionInput + The \l{Gpio} is configured as \b input. + \value DirectionOutput + The \l{Gpio} is configured as \b output. + \value DirectionInvalid + The direction is not valid. +*/ + +/*! \enum Gpio::Value + This enum type specifies the value a \l{Gpio}. + \value ValueLow + The \l{Gpio} is low. + \value ValueHigh + The \l{Gpio} is high. + \value ValueInvalid + The value is not valid. +*/ + +/*! \enum Gpio::Edge + This enum type specifies the edge interrupt type of a \l{Gpio}. + \value EdgeFalling + The \l{Gpio} reacts on falling edge interrupt. + \value EdgeRising + The \l{Gpio} reacts on rising edge interrupt. + \value EdgeBoth + The \l{Gpio} reacts on both, rising and falling edge interrupt. + \value EdgeNone + The \l{Gpio} does not react on interrupts. +*/ + Q_LOGGING_CATEGORY(dcGpio, "Gpio") /*! Constructs a \l{Gpio} object to represent a GPIO with the given \a gpio number and \a parent. */ diff --git a/libnymea-gpio/gpiobutton.cpp b/libnymea-gpio/gpiobutton.cpp new file mode 100644 index 0000000..b197eea --- /dev/null +++ b/libnymea-gpio/gpiobutton.cpp @@ -0,0 +1,131 @@ +#include "gpiobutton.h" + +GpioButton::GpioButton(int gpioNumber, QObject *parent) : + QObject(parent), + m_gpioNumber(gpioNumber) +{ + +} + +int GpioButton::gpioNumber() const +{ + return m_gpioNumber; +} + +bool GpioButton::activeLow() const +{ + return m_activeLow; +} + +void GpioButton::setActiveLow(bool activeLow) +{ + m_activeLow = activeLow; +} + +bool GpioButton::repeateLongPressed() const +{ + return m_repeateLongPressed; +} + +void GpioButton::setRepeateLongPressed(bool repeateLongPressed) +{ + m_repeateLongPressed = repeateLongPressed; +} + +int GpioButton::longPressedTimeout() const +{ + return m_longPressedTimeout; +} + +void GpioButton::setLongPressedTimeout(int longPressedTimeout) +{ + m_longPressedTimeout = longPressedTimeout; +} + +QString GpioButton::name() const +{ + return m_name; +} + +void GpioButton::setName(const QString &name) +{ + m_name = name; +} + +void GpioButton::onTimeout() +{ + qCDebug(dcGpio()) << this << "long pressed"; + emit longPressed(); +} + +void GpioButton::onInterruptOccured(bool value) +{ + if (value) { + // Pressed + qCDebug(dcGpio()) << this << "pressed"; + emit pressed(); + + m_timer->setSingleShot(!m_repeateLongPressed); + m_timer->start(m_longPressedTimeout); + m_time.restart(); + } else { + // Released + qCDebug(dcGpio()) << this << "released"; + emit released(); + + m_timer->stop(); + int duration = m_time.elapsed(); + + // Debounce and limit to 500 ms + if (duration >= 10 && duration <= 500) { + qCDebug(dcGpio()) << this << "clicked"; + emit clicked(); + } + } +} + +bool GpioButton::enable() +{ + // Make sure we have a clean start + disable(); + + m_monitor = new GpioMonitor(m_gpioNumber, this); + m_monitor->setEdge(Gpio::EdgeBoth); + m_monitor->setActiveLow(m_activeLow); + + if (!m_monitor->enable()) { + qCWarning(dcGpio()) << "Could not enable GPIO monitor for" << this; + delete m_monitor; + m_monitor = nullptr; + return false; + } + connect(m_monitor, &GpioMonitor::interruptOccured, this, &GpioButton::onInterruptOccured, Qt::DirectConnection); + + // Setup timer, if this timer reaches timeout, a long pressed happend + m_timer = new QTimer(this); + m_timer->setTimerType(Qt::PreciseTimer); + m_timer->setSingleShot(!m_repeateLongPressed); + m_timer->setInterval(m_longPressedTimeout); + connect(m_timer, &QTimer::timeout, this, &GpioButton::onTimeout, Qt::DirectConnection); + return true; +} + +void GpioButton::disable() +{ + if (m_monitor) { + delete m_monitor; + m_monitor = nullptr; + } + + if (m_timer) { + delete m_timer; + m_timer = nullptr; + } +} + +QDebug operator<<(QDebug debug, GpioButton *gpioButton) +{ + debug.nospace() << "GpioButton(" << gpioButton->gpioNumber() << ", "; + debug.nospace() << "name: " << gpioButton->name() << ")"; + return debug.space(); +} diff --git a/libnymea-gpio/gpiobutton.h b/libnymea-gpio/gpiobutton.h new file mode 100644 index 0000000..5c69d19 --- /dev/null +++ b/libnymea-gpio/gpiobutton.h @@ -0,0 +1,61 @@ +#ifndef GPIOBUTTON_H +#define GPIOBUTTON_H + +#include +#include +#include + +#include "gpiomonitor.h" + +class GpioButton : public QObject +{ + Q_OBJECT +public: + explicit GpioButton(int gpioNumber, QObject *parent = nullptr); + + int gpioNumber() const; + + bool activeLow() const; + void setActiveLow(bool activeLow); + + bool repeateLongPressed() const; + void setRepeateLongPressed(bool repeateLongPressed); + + int longPressedTimeout() const; + void setLongPressedTimeout(int longPressedTimeout); + + QString name() const; + void setName(const QString &name); + +private: + int m_gpioNumber; + bool m_activeLow = true; + bool m_repeateLongPressed = false; + int m_longPressedTimeout = 250; + QString m_name; + + GpioMonitor *m_monitor = nullptr; + QTimer *m_timer = nullptr; + + QTime m_time; + +signals: + void clicked(); + void pressed(); + void released(); + void longPressed(); + +private slots: + void onTimeout(); + void onInterruptOccured(bool value); + +public slots: + bool enable(); + void disable(); + +}; + +QDebug operator<< (QDebug debug, GpioButton *gpioButton); + + +#endif // GPIOBUTTON_H diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index b8589d7..bb10ac8 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -1,8 +1,42 @@ +/*! + \class GpioMonitor + \brief Monitor for GPIO interrupts. + + This class allows to monitor an input GPIO for the interrupts depending on the \l{Gpio:Edge} configuration. + + \code + GpioMonitor *monitor = new GpioMonitor(112, this); + + if (!monitor->enable()) { + qWarning() << "Could not enable GPIO monitor"; + monitor->deleteLater(); + return; + } + + connect(monitor, &GpioMonitor::interruptOccured, this, [this, monitor](bool value){ + qDebug() << "GPIO value changed" << value; + }); + + \endcode + +*/ + +/*! \fn void GpioMonitor::interruptOccured(bool value); + This signal will be emitted, if an interrupt on the monitored Gpio occured with the new \a value. This event depends on the Gpio::Edge configuration of the Gpio. + + \sa edge(), setEdge() +*/ + +/*! \fn void GpioMonitor::enabledChanged(bool enabled); + This signal will be emitted when the GpioMonitor \a enabled changed. +*/ + #include "gpiomonitor.h" #include #include +/*! Constructs a \l{GpioMonitor} object with the given \a gpio number and \a parent. */ GpioMonitor::GpioMonitor(int gpio, QObject *parent) : QThread(parent), m_gpioNumber(gpio) @@ -12,17 +46,28 @@ GpioMonitor::GpioMonitor(int gpio, QObject *parent) : connect(this, &GpioMonitor::finished, this, &GpioMonitor::onThreadFinished, Qt::DirectConnection); } +/*! Destroys and unexports the Gpio. */ GpioMonitor::~GpioMonitor() { disable(); wait(200); } +/*! Returns the edge interrupt configuration for this GpioMonitor. + + \sa Gpio::Edge + +*/ Gpio::Edge GpioMonitor::edge() const { return m_edge; } +/*! Sets the edge interrupt configuration for this GpioMonitor to the given \a edge. + + \sa Gpio::Edge + +*/ void GpioMonitor::setEdge(Gpio::Edge edge) { if (m_edge == edge) @@ -31,11 +76,13 @@ void GpioMonitor::setEdge(Gpio::Edge edge) m_edge = edge; } +/*! Returns true, if the monitor is configured as active low. If active low is true, the GPIO values and interrupt behavior will be inverted. */ bool GpioMonitor::activeLow() const { return m_activeLow; } +/*! Sets the the monitor to \a activeLow. If active low is true, the GPIO values and interrupt behavior will be inverted. */ void GpioMonitor::setActiveLow(bool activeLow) { if (m_activeLow == activeLow) @@ -44,12 +91,14 @@ void GpioMonitor::setActiveLow(bool activeLow) m_activeLow = activeLow; } +/*! Returns the current value of the Gpio. */ Gpio::Value GpioMonitor::value() { QMutexLocker valueLocker(&m_valueMutex); return m_value; } +/*! Returns true if this GpioMonitor is enabled. */ bool GpioMonitor::enabled() const { return m_enabled; @@ -181,6 +230,7 @@ void GpioMonitor::onThreadFinished() setEnabled(false); } +/*! Returns true, if this GpioMonitor was enabled successfully. */ bool GpioMonitor::enable() { qCDebug(dcGpio()) << "Enable gpio monitor"; @@ -203,6 +253,7 @@ bool GpioMonitor::enable() return true; } +/*! Disables this GpioMonitor. The \l{interruptOccured()} signal will not be emitted any more and the Gpio will be unexported. */ void GpioMonitor::disable() { qCDebug(dcGpio()) << "Disable gpio monitor"; diff --git a/libnymea-gpio/gpiomonitor.h b/libnymea-gpio/gpiomonitor.h index bb109dd..f84488d 100644 --- a/libnymea-gpio/gpiomonitor.h +++ b/libnymea-gpio/gpiomonitor.h @@ -32,7 +32,7 @@ private: // Thread stuff QMutex m_valueMutex; - Gpio::Value m_value = Gpio::ValueInvalid; + Gpio::Value m_value = Gpio::ValueInvalid; QMutex m_stopMutex; bool m_stop = false; diff --git a/libnymea-gpio/libnymea-gpio.pro b/libnymea-gpio/libnymea-gpio.pro index 033393e..ed3db59 100644 --- a/libnymea-gpio/libnymea-gpio.pro +++ b/libnymea-gpio/libnymea-gpio.pro @@ -5,10 +5,12 @@ TEMPLATE = lib HEADERS += \ gpio.h \ + gpiobutton.h \ gpiomonitor.h SOURCES += \ gpio.cpp \ + gpiobutton.cpp \ gpiomonitor.cpp target.path = $$[QT_INSTALL_LIBS] @@ -16,7 +18,7 @@ INSTALLS += target # install header file with relative subdirectory for(header, HEADERS) { - path = /usr/include/libnymea-gpio/$${dirname(header)} + path = $$[QT_INSTALL_PREFIX]/include/nymea-gpio/$${dirname(header)} eval(headers_$${path}.files += $${header}) eval(headers_$${path}.path = $${path}) eval(INSTALLS *= headers_$${path}) @@ -24,11 +26,11 @@ for(header, HEADERS) { # Create pkgconfig file CONFIG += create_pc create_prl no_install_prl -QMAKE_PKGCONFIG_NAME = libnymea-gpio +QMAKE_PKGCONFIG_NAME = nymea-gpio QMAKE_PKGCONFIG_DESCRIPTION = nymea gpio development library QMAKE_PKGCONFIG_PREFIX = $$[QT_INSTALL_PREFIX] -QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_PREFIX]/include/libnymea-gpio/ +QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_PREFIX]/include/nymea-gpio/ QMAKE_PKGCONFIG_LIBDIR = $$target.path QMAKE_PKGCONFIG_VERSION = $$VERSION_STRING -QMAKE_PKGCONFIG_FILE = libnymea-gpio +QMAKE_PKGCONFIG_FILE = nymea-gpio QMAKE_PKGCONFIG_DESTDIR = pkgconfig From bdfc868f10da536f2387a3c96b7db9f8efa7532d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 13 Sep 2019 15:47:27 +0200 Subject: [PATCH 09/17] Update documentation --- libnymea-gpio/gpio.cpp | 96 +++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index 199b9c9..fc891d8 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -1,55 +1,55 @@ #include "gpio.h" /*! - \class Gpio - \brief Represents a system GPIO in linux systems. + \class Gpio + \brief Represents a system GPIO in linux systems. - A "General Purpose Input/Output" (GPIO) is a flexible software-controlled - digital signal. They are provided from many kinds of chip, and are familiar - to Linux developers working with embedded and custom hardware. Each GPIO - represents a bit connected to a particular pin, or "ball" on Ball Grid Array - (BGA) packages. Board schematics show which external hardware connects to - which GPIOs. Drivers can be written generically, so that board setup code - passes such pin configuration data to drivers - (\l{https://www.kernel.org/doc/Documentation/gpio/gpio.txt}{source}). - General Purpose Input/Output (a.k.a. GPIO) is a generic pin on a chip whose - behavior (including whether it is an input or output pin) can be controlled - through this class. An object of of the Gpio class represents a pin. + A "General Purpose Input/Output" (GPIO) is a flexible software-controlled + digital signal. They are provided from many kinds of chip, and are familiar + to Linux developers working with embedded and custom hardware. Each GPIO + represents a bit connected to a particular pin, or "ball" on Ball Grid Array + (BGA) packages. Board schematics show which external hardware connects to + which GPIOs. Drivers can be written generically, so that board setup code + passes such pin configuration data to drivers + (\l{https://www.kernel.org/doc/Documentation/gpio/gpio.txt}{source}). + General Purpose Input/Output (a.k.a. GPIO) is a generic pin on a chip whose + behavior (including whether it is an input or output pin) can be controlled + through this class. An object of of the Gpio class represents a pin. - \code - Gpio *gpioOut = new Gpio(23, this); - // Export Gpio - if (!gpioOut->exportGpio()) { - qWarning() << "Could not export Gpio" << gpioOut->gpioNumber(); - gpioOut->deleteLater(); - return; - } - // Configure Gpio direction - if (!gpioOut->setDirection(PiGpio::DirectionOutput)) { - qWarning() << "Could not set direction of Gpio" << gpioOut->gpioNumber(); - gpioOut->deleteLater(); - return; - } - gpioOut->setValue(Gpio::ValueHigh) - \endcode + \code + Gpio *gpioOut = new Gpio(23, this); + // Export Gpio + if (!gpioOut->exportGpio()) { + qWarning() << "Could not export Gpio" << gpioOut->gpioNumber(); + gpioOut->deleteLater(); + return; + } + // Configure Gpio direction + if (!gpioOut->setDirection(PiGpio::DirectionOutput)) { + qWarning() << "Could not set direction of Gpio" << gpioOut->gpioNumber(); + gpioOut->deleteLater(); + return; + } + gpioOut->setValue(Gpio::ValueHigh) + \endcode - \code - Gpio *gpioIn = new Gpio(24, this); - // Export Gpio - if (!gpioIn->exportGpio()) { - qWarning() << "Could not export Gpio" << gpioIn->gpioNumber(); - gpioIn->deleteLater(); - return; - } - // Configure Gpio direction - if (!gpioIn->setDirection(PiGpio::DirectionInput)) { - qWarning() << "Could not set direction of Gpio" << gpioIn->gpioNumber(); - gpioIn->deleteLater(); - return; - } - qDebug() << "Current value" << gpioIn->value(); - \endcode - \sa GpioMonitor, GpioButton + \code + Gpio *gpioIn = new Gpio(24, this); + // Export Gpio + if (!gpioIn->exportGpio()) { + qWarning() << "Could not export Gpio" << gpioIn->gpioNumber(); + gpioIn->deleteLater(); + return; + } + // Configure Gpio direction + if (!gpioIn->setDirection(PiGpio::DirectionInput)) { + qWarning() << "Could not set direction of Gpio" << gpioIn->gpioNumber(); + gpioIn->deleteLater(); + return; + } + qDebug() << "Current value" << gpioIn->value(); + \endcode + \sa GpioMonitor, GpioButton */ /*! \enum Gpio::Direction @@ -86,7 +86,7 @@ Q_LOGGING_CATEGORY(dcGpio, "Gpio") -/*! Constructs a \l{Gpio} object to represent a GPIO with the given \a gpio number and \a parent. */ +/*! Constructs a Gpio object to represent a GPIO with the given \a gpio number and \a parent. */ Gpio::Gpio(int gpio, QObject *parent) : QObject(parent), m_gpio(gpio), @@ -96,7 +96,7 @@ Gpio::Gpio(int gpio, QObject *parent) : } -/*! Destroys and unexports the \l{Gpio}. */ +/*! Destroys and unexports the Gpio. */ Gpio::~Gpio() { unexportGpio(); From 6ce902b8297f455822681943ccf1d10afa34ea7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 13 Sep 2019 15:49:48 +0200 Subject: [PATCH 10/17] Update documentation --- libnymea-gpio/gpio.cpp | 5 +++-- libnymea-gpio/gpiomonitor.cpp | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index fc891d8..8b4b58e 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -1,8 +1,7 @@ -#include "gpio.h" - /*! \class Gpio \brief Represents a system GPIO in linux systems. + \inmodule nymea-gpio A "General Purpose Input/Output" (GPIO) is a flexible software-controlled digital signal. They are provided from many kinds of chip, and are familiar @@ -84,6 +83,8 @@ The \l{Gpio} does not react on interrupts. */ +#include "gpio.h" + Q_LOGGING_CATEGORY(dcGpio, "Gpio") /*! Constructs a Gpio object to represent a GPIO with the given \a gpio number and \a parent. */ diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index bb10ac8..485c274 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -1,23 +1,23 @@ /*! - \class GpioMonitor - \brief Monitor for GPIO interrupts. + \class GpioMonitor + \brief Monitor for GPIO interrupts. - This class allows to monitor an input GPIO for the interrupts depending on the \l{Gpio:Edge} configuration. + This class allows to monitor an input GPIO for the interrupts depending on the \l{Gpio:Edge} configuration. - \code - GpioMonitor *monitor = new GpioMonitor(112, this); + \code + GpioMonitor *monitor = new GpioMonitor(112, this); - if (!monitor->enable()) { - qWarning() << "Could not enable GPIO monitor"; - monitor->deleteLater(); - return; - } + if (!monitor->enable()) { + qWarning() << "Could not enable GPIO monitor"; + monitor->deleteLater(); + return; + } - connect(monitor, &GpioMonitor::interruptOccured, this, [this, monitor](bool value){ - qDebug() << "GPIO value changed" << value; - }); + connect(monitor, &GpioMonitor::interruptOccured, this, [this, monitor](bool value){ + qDebug() << "GPIO value changed" << value; + }); - \endcode + \endcode */ From 83799d9289ac560f6ed37aa991e37136e1c16cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 13 Sep 2019 16:29:07 +0200 Subject: [PATCH 11/17] Add qdoc configuration --- docs/config.qdocconf | 30 ++++++++++++++ docs/html-template.qdocconf | 11 +++++ libnymea-gpio/gpio.cpp | 73 +++++++++++++++++++++++---------- libnymea-gpio/gpio.h | 23 +++++++++++ libnymea-gpio/gpiobutton.cpp | 22 ++++++++++ libnymea-gpio/gpiobutton.h | 22 ++++++++++ libnymea-gpio/gpiomonitor.cpp | 24 +++++++++++ libnymea-gpio/gpiomonitor.h | 22 ++++++++++ nymea-gpio-tool/application.cpp | 22 ++++++++++ nymea-gpio-tool/application.h | 22 ++++++++++ nymea-gpio-tool/main.cpp | 22 ++++++++++ 11 files changed, 271 insertions(+), 22 deletions(-) create mode 100644 docs/config.qdocconf create mode 100644 docs/html-template.qdocconf diff --git a/docs/config.qdocconf b/docs/config.qdocconf new file mode 100644 index 0000000..69cc0d8 --- /dev/null +++ b/docs/config.qdocconf @@ -0,0 +1,30 @@ +include(html-template.qdocconf) + +project = nymea-gpio +description = nymea-gpio documentation + +dita.metadata.default.author = Simon Stürz +dita.metadata.default.permissions = all +dita.metadata.default.publisher = guh GmbH +dita.metadata.default.copyryear = 2019 +dita.metadata.default.copyrholder = Simon Stürz +dita.metadata.default.audience = programmer + +outputdir = html +outputformats = HTML + +language = Cpp + +naturallanguage = en_US +outputencoding = UTF-8 +sourceencoding = UTF-8 + +syntaxhighlighting = true + +headerdirs = ../libnymea-gpio +sourcedirs = ../libnymea-gpio + +headers.fileextensions = "*.h" +sources.fileextensions = "*.cpp" + +Cpp.ignoredirectives = Q_DECLARE_METATYPE Q_DECLARE_LOGGING_CATEGORY Q_LOGGING_CATEGORY diff --git a/docs/html-template.qdocconf b/docs/html-template.qdocconf new file mode 100644 index 0000000..d5b6643 --- /dev/null +++ b/docs/html-template.qdocconf @@ -0,0 +1,11 @@ +HTML.templatedir = . + +HTML.postpostheader = \ + "
\n" + +HTML.prologue = \ + "
\n" + +HTML.footer = \ + "
\n" \ + "
\n" diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index 8b4b58e..432fde2 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -1,3 +1,25 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + /*! \class Gpio \brief Represents a system GPIO in linux systems. @@ -48,39 +70,44 @@ } qDebug() << "Current value" << gpioIn->value(); \endcode - \sa GpioMonitor, GpioButton + \sa GpioMonitor */ + /*! \enum Gpio::Direction - This enum type specifies the dirction a \l{Gpio}. + This enum type specifies the dirction a Gpio. + \value DirectionInput - The \l{Gpio} is configured as \b input. + The Gpio is configured as \b input. \value DirectionOutput - The \l{Gpio} is configured as \b output. + The Gpio is configured as \b output. \value DirectionInvalid The direction is not valid. */ /*! \enum Gpio::Value - This enum type specifies the value a \l{Gpio}. + This enum type specifies the value a Gpio. + \value ValueLow - The \l{Gpio} is low. + The Gpio is low. \value ValueHigh - The \l{Gpio} is high. + The Gpio is high. \value ValueInvalid The value is not valid. */ + /*! \enum Gpio::Edge - This enum type specifies the edge interrupt type of a \l{Gpio}. + This enum type specifies the edge interrupt type of a Gpio. + \value EdgeFalling - The \l{Gpio} reacts on falling edge interrupt. + The Gpio reacts on falling edge interrupt. \value EdgeRising - The \l{Gpio} reacts on rising edge interrupt. + The Gpio reacts on rising edge interrupt. \value EdgeBoth - The \l{Gpio} reacts on both, rising and falling edge interrupt. + The Gpio reacts on both, rising and falling edge interrupt. \value EdgeNone - The \l{Gpio} does not react on interrupts. + The Gpio does not react on interrupts. */ #include "gpio.h" @@ -115,13 +142,15 @@ QString Gpio::gpioDirectory() const return m_gpioDirectory.canonicalPath(); } -/*! Returns the number of this \l{Gpio}. \note The Gpio number is mostly not equivalent with the pin number. */ +/*! Returns the number of this Gpio. + \note The Gpio number is mostly not equivalent with the pin number. +*/ int Gpio::gpioNumber() const { return m_gpio; } -/*! Returns true if this \l{Gpio} could be exported in the system file \tt {/sys/class/gpio/export}. If this Gpio is already exported, this function will return true. */ +/*! Returns true if this Gpio could be exported in the system file \tt {/sys/class/gpio/export}. If this Gpio is already exported, this function will return true. */ bool Gpio::exportGpio() { qCDebug(dcGpio()) << "Export GPIO" << m_gpio; @@ -143,7 +172,7 @@ bool Gpio::exportGpio() return true; } -/*! Returns true if this \l{Gpio} could be unexported in the system file \tt {/sys/class/gpio/unexport}. */ +/*! Returns true if this Gpio could be unexported in the system file \tt {/sys/class/gpio/unexport}. */ bool Gpio::unexportGpio() { qCDebug(dcGpio()) << "Unexport GPIO" << m_gpio; @@ -193,7 +222,7 @@ bool Gpio::setDirection(Gpio::Direction direction) return true; } -/*! Returns the direction of this \l{Gpio}. */ +/*! Returns the direction of this Gpio. */ Gpio::Direction Gpio::direction() { QFile directionFile(m_gpioDirectory.path() + QDir::separator() + "direction"); @@ -218,7 +247,7 @@ Gpio::Direction Gpio::direction() return Gpio::DirectionInvalid; } -/*! Returns true if the digital \a value of this \l{Gpio} could be set correctly. */ +/*! Returns true if the digital \a value of this Gpio could be set correctly. */ bool Gpio::setValue(Gpio::Value value) { qCDebug(dcGpio()) << "Set GPIO" << m_gpio << "value" << value; @@ -263,7 +292,7 @@ bool Gpio::setValue(Gpio::Value value) return true; } -/*! Returns the current digital value of this \l{Gpio}. */ +/*! Returns the current digital value of this Gpio. */ Gpio::Value Gpio::value() { QFile valueFile(m_gpioDirectory.path() + QDir::separator() + "value"); @@ -286,7 +315,7 @@ Gpio::Value Gpio::value() return Gpio::ValueInvalid; } -/*! This method allows to invert the logic of this \l{Gpio}. Returns true, if the GPIO could be set \a activeLow. */ +/*! This method allows to invert the logic of this Gpio. Returns true, if the GPIO could be set \a activeLow. */ bool Gpio::setActiveLow(bool activeLow) { qCDebug(dcGpio()) << "Set GPIO" << m_gpio << "active low" << activeLow; @@ -308,7 +337,7 @@ bool Gpio::setActiveLow(bool activeLow) return true; } -/*! Returns true if the logic of this \l{Gpio} is inverted (1 = low, 0 = high). */ +/*! Returns true if the logic of this Gpio is inverted (1 = low, 0 = high). */ bool Gpio::activeLow() { QFile activeLowFile(m_gpioDirectory.path() + QDir::separator() + "active_low"); @@ -365,7 +394,7 @@ bool Gpio::setEdgeInterrupt(Gpio::Edge edge) return true; } -/*! Returns the edge interrupt of this \l{Gpio}. */ +/*! Returns the edge interrupt of this Gpio. */ Gpio::Edge Gpio::edgeInterrupt() { QFile edgeFile(m_gpioDirectory.path() + QDir::separator() + "edge"); @@ -393,7 +422,7 @@ Gpio::Edge Gpio::edgeInterrupt() } - +/*! Prints the given \a gpio to \a debug. */ QDebug operator<<(QDebug debug, Gpio *gpio) { debug.nospace() << "Gpio(" << gpio->gpioNumber() << ", "; diff --git a/libnymea-gpio/gpio.h b/libnymea-gpio/gpio.h index d914619..cc32d16 100644 --- a/libnymea-gpio/gpio.h +++ b/libnymea-gpio/gpio.h @@ -1,3 +1,25 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #ifndef GPIO_H #define GPIO_H @@ -11,6 +33,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcGpio) class Gpio : public QObject { Q_OBJECT + public: enum Direction { DirectionInvalid, diff --git a/libnymea-gpio/gpiobutton.cpp b/libnymea-gpio/gpiobutton.cpp index b197eea..093e39a 100644 --- a/libnymea-gpio/gpiobutton.cpp +++ b/libnymea-gpio/gpiobutton.cpp @@ -1,3 +1,25 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #include "gpiobutton.h" GpioButton::GpioButton(int gpioNumber, QObject *parent) : diff --git a/libnymea-gpio/gpiobutton.h b/libnymea-gpio/gpiobutton.h index 5c69d19..57f8aec 100644 --- a/libnymea-gpio/gpiobutton.h +++ b/libnymea-gpio/gpiobutton.h @@ -1,3 +1,25 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #ifndef GPIOBUTTON_H #define GPIOBUTTON_H diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index 485c274..83ab7ce 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -1,6 +1,29 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + /*! \class GpioMonitor \brief Monitor for GPIO interrupts. + \inmodule nymea-gpio This class allows to monitor an input GPIO for the interrupts depending on the \l{Gpio:Edge} configuration. @@ -130,6 +153,7 @@ void GpioMonitor::setEnabled(bool enabled) emit enabledChanged(m_enabled); } +/*! Reimplementation of the QThread run() method. Within the thread the Gpio value will be polled using poll() 2. */ void GpioMonitor::run() { // Create GPIO in the thread for initialisation diff --git a/libnymea-gpio/gpiomonitor.h b/libnymea-gpio/gpiomonitor.h index f84488d..e9653c7 100644 --- a/libnymea-gpio/gpiomonitor.h +++ b/libnymea-gpio/gpiomonitor.h @@ -1,3 +1,25 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #ifndef GPIOMONITOR_H #define GPIOMONITOR_H diff --git a/nymea-gpio-tool/application.cpp b/nymea-gpio-tool/application.cpp index 73c1d21..624d34b 100644 --- a/nymea-gpio-tool/application.cpp +++ b/nymea-gpio-tool/application.cpp @@ -1,3 +1,25 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #include "application.h" #include diff --git a/nymea-gpio-tool/application.h b/nymea-gpio-tool/application.h index 64941bc..8fbfa8d 100644 --- a/nymea-gpio-tool/application.h +++ b/nymea-gpio-tool/application.h @@ -1,3 +1,25 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #ifndef APPLICATION_H #define APPLICATION_H diff --git a/nymea-gpio-tool/main.cpp b/nymea-gpio-tool/main.cpp index 6aeb4d3..3496cf5 100644 --- a/nymea-gpio-tool/main.cpp +++ b/nymea-gpio-tool/main.cpp @@ -1,3 +1,25 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Simon Stürz * + * * + * This file is part of nymea-gpio. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #include #include "application.h" From 2dea4225e4ef8bcf62c702ddf14e4c43e9bc99fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 13 Sep 2019 16:45:36 +0200 Subject: [PATCH 12/17] Fix qdoc macro issue --- docs/config.qdocconf | 2 +- libnymea-gpio/gpio.cpp | 18 ++++++++++-------- libnymea-gpio/gpiobutton.cpp | 1 + libnymea-gpio/gpiobutton.h | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/config.qdocconf b/docs/config.qdocconf index 69cc0d8..e55b75d 100644 --- a/docs/config.qdocconf +++ b/docs/config.qdocconf @@ -27,4 +27,4 @@ sourcedirs = ../libnymea-gpio headers.fileextensions = "*.h" sources.fileextensions = "*.cpp" -Cpp.ignoredirectives = Q_DECLARE_METATYPE Q_DECLARE_LOGGING_CATEGORY Q_LOGGING_CATEGORY +Cpp.ignoredirectives = Q_DECLARE_METATYPE Q_DECLARE_LOGGING_CATEGORY Q_LOGGING_CATEGORY Q_ENUM diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index 432fde2..915e3b0 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -74,7 +74,8 @@ */ -/*! \enum Gpio::Direction +/*! + \enum Gpio::Direction This enum type specifies the dirction a Gpio. \value DirectionInput @@ -85,19 +86,21 @@ The direction is not valid. */ -/*! \enum Gpio::Value +/*! + \enum Gpio::Value This enum type specifies the value a Gpio. + \value ValueInvalid + The value is not valid. \value ValueLow The Gpio is low. \value ValueHigh The Gpio is high. - \value ValueInvalid - The value is not valid. + */ - -/*! \enum Gpio::Edge +/*! + \enum Gpio::Edge This enum type specifies the edge interrupt type of a Gpio. \value EdgeFalling @@ -357,8 +360,7 @@ bool Gpio::activeLow() return false; } -/*! Returns true if the \a edge of this GPIO could be set correctly. The \a edge parameter specifies, - * when an interrupt occurs. */ +/*! Returns true if the \a edge of this GPIO could be set correctly. The \a edge parameter specifies, when an interrupt occurs. */ bool Gpio::setEdgeInterrupt(Gpio::Edge edge) { diff --git a/libnymea-gpio/gpiobutton.cpp b/libnymea-gpio/gpiobutton.cpp index 093e39a..30431e0 100644 --- a/libnymea-gpio/gpiobutton.cpp +++ b/libnymea-gpio/gpiobutton.cpp @@ -21,6 +21,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "gpiobutton.h" +#include "gpiomonitor.h" GpioButton::GpioButton(int gpioNumber, QObject *parent) : QObject(parent), diff --git a/libnymea-gpio/gpiobutton.h b/libnymea-gpio/gpiobutton.h index 57f8aec..65e55a1 100644 --- a/libnymea-gpio/gpiobutton.h +++ b/libnymea-gpio/gpiobutton.h @@ -27,7 +27,7 @@ #include #include -#include "gpiomonitor.h" +class GpioMonitor; class GpioButton : public QObject { From 4984618797fb92a9582fdc09d3e40c976c04264e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 13 Sep 2019 17:09:46 +0200 Subject: [PATCH 13/17] Complete documentations --- libnymea-gpio/gpio.cpp | 6 +++ libnymea-gpio/gpiobutton.cpp | 79 ++++++++++++++++++++++++++++++++++- libnymea-gpio/gpiomonitor.cpp | 25 ++++++----- 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index 915e3b0..8934d76 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -39,35 +39,41 @@ \code Gpio *gpioOut = new Gpio(23, this); + // Export Gpio if (!gpioOut->exportGpio()) { qWarning() << "Could not export Gpio" << gpioOut->gpioNumber(); gpioOut->deleteLater(); return; } + // Configure Gpio direction if (!gpioOut->setDirection(PiGpio::DirectionOutput)) { qWarning() << "Could not set direction of Gpio" << gpioOut->gpioNumber(); gpioOut->deleteLater(); return; } + gpioOut->setValue(Gpio::ValueHigh) \endcode \code Gpio *gpioIn = new Gpio(24, this); + // Export Gpio if (!gpioIn->exportGpio()) { qWarning() << "Could not export Gpio" << gpioIn->gpioNumber(); gpioIn->deleteLater(); return; } + // Configure Gpio direction if (!gpioIn->setDirection(PiGpio::DirectionInput)) { qWarning() << "Could not set direction of Gpio" << gpioIn->gpioNumber(); gpioIn->deleteLater(); return; } + qDebug() << "Current value" << gpioIn->value(); \endcode \sa GpioMonitor diff --git a/libnymea-gpio/gpiobutton.cpp b/libnymea-gpio/gpiobutton.cpp index 30431e0..2bfaa2f 100644 --- a/libnymea-gpio/gpiobutton.cpp +++ b/libnymea-gpio/gpiobutton.cpp @@ -20,56 +20,128 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/*! + \class GpioButton + \brief Represents a GPIO Button with some helper methods. + \inmodule nymea-gpio + + This class represents a Button based on a GPIO. The class takes care about the \l{clicked()} signal handling, debounces the GPIO signal + and offers a nice interface for \l{longPressed()} behaviour. + + In order to get the button signals, the button has to be enabled using \l{enable()}. + + \code + GpioButton *button = new GpioButton(15, this); + button->setName("User button"); + if (!button->enable()) { + qWarning() << "Could not enable the" << this; + } + + connect(button, &GpioButton::clicked, this, [this, button](){ + qDebug() << button << "clicked"; + }); + + \endcode + +*/ + +/*! + \fn void GpioButton::clicked(); + This signal will be emitted when the button gets clicked. A button will has been clicked, if it was pressed at leased for 10 ms and at most 500 ms. +*/ + +/*! + \fn void GpioButton::pressed(); + This signal will be emitted when the button gets pressed. +*/ + +/*! + \fn void GpioButton::released(); + This signal will be emitted whenever the button gets released. +*/ + +/*! + \fn void GpioButton::longPressed(); + This signal will be emitted whenever the button gets pressed for a certain time. + + \sa longPressedTimeout(), repeateLongPressed() +*/ + #include "gpiobutton.h" #include "gpiomonitor.h" -GpioButton::GpioButton(int gpioNumber, QObject *parent) : +/*! Constructs a \l{GpioButton} object with the given \a gpio number and \a parent. */ +GpioButton::GpioButton(int gpio, QObject *parent) : QObject(parent), - m_gpioNumber(gpioNumber) + m_gpioNumber(gpio) { } +/*! Returns the gpio number for this GpioButton. */ int GpioButton::gpioNumber() const { return m_gpioNumber; } +/*! Returns \c true the gpio button is configured as active low. + + \sa Gpio::activeLow() +*/ bool GpioButton::activeLow() const { return m_activeLow; } +/*! Sets the gpio button active low configuration to \a activeLow for this GpioButton. + + \sa Gpio::setActiveLow() +*/ void GpioButton::setActiveLow(bool activeLow) { m_activeLow = activeLow; } +/*! Returns \c true, if the \l{longPressed()} signal will be emited again if the button will be hold down. */ bool GpioButton::repeateLongPressed() const { return m_repeateLongPressed; } +/*! Sets repeate long pressed configuration to \a repeateLongPressed. If \a repeateLongPressed is true, the longPressed() signal will be repeated as long the button will be hold down. + + \sa longPressedTimeout() +*/ void GpioButton::setRepeateLongPressed(bool repeateLongPressed) { m_repeateLongPressed = repeateLongPressed; } +/*! Returns the long pressed timout duration in milliseconds. If the button gets hold down for this duration, the longPressed() signal will be emitted. + + \sa longPressed() +*/ int GpioButton::longPressedTimeout() const { return m_longPressedTimeout; } +/*! Sets the long pressed timout duration to \a longPressedTimeout in milliseconds. If the button gets hold down for this duration, the longPressed() signal will be emitted. + + \sa longPressed() +*/ void GpioButton::setLongPressedTimeout(int longPressedTimeout) { m_longPressedTimeout = longPressedTimeout; } +/*! Returns the \c name for this GpioButton. This is optional, but will be printed in the debug operator. */ QString GpioButton::name() const { return m_name; } +/*! Sets the \a name for this GpioButton. This is optional, but will be printed in the debug operator. */ void GpioButton::setName(const QString &name) { m_name = name; @@ -107,6 +179,7 @@ void GpioButton::onInterruptOccured(bool value) } } +/*! Returns \c true, if this GpioButton was enabled successfully. */ bool GpioButton::enable() { // Make sure we have a clean start @@ -133,6 +206,7 @@ bool GpioButton::enable() return true; } +/*! Disable this GpioButton. The Gpio will be unexported. */ void GpioButton::disable() { if (m_monitor) { @@ -146,6 +220,7 @@ void GpioButton::disable() } } +/*! Prints the given \a gpioButton to \a debug. */ QDebug operator<<(QDebug debug, GpioButton *gpioButton) { debug.nospace() << "GpioButton(" << gpioButton->gpioNumber() << ", "; diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index 83ab7ce..271007f 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -25,7 +25,12 @@ \brief Monitor for GPIO interrupts. \inmodule nymea-gpio - This class allows to monitor an input GPIO for the interrupts depending on the \l{Gpio:Edge} configuration. + This class allows to monitor an input GPIO for the interrupts depending on the edge interrupt configuration. + + This class will start a poll thread in the background. Depending on the Gpio::Edge configuration, the \l{interruptOccured()} signal + will be emitted. Default is Gpio::EdgeBoth which means the interrupt will be on rising and falling signal of the Gpio. + + The behavior of the interrupt can also be inverted using the \l{activeLow()} parameter. \code GpioMonitor *monitor = new GpioMonitor(112, this); @@ -44,13 +49,15 @@ */ -/*! \fn void GpioMonitor::interruptOccured(bool value); +/*! + \fn void GpioMonitor::interruptOccured(bool value); This signal will be emitted, if an interrupt on the monitored Gpio occured with the new \a value. This event depends on the Gpio::Edge configuration of the Gpio. \sa edge(), setEdge() */ -/*! \fn void GpioMonitor::enabledChanged(bool enabled); +/*! + \fn void GpioMonitor::enabledChanged(bool enabled); This signal will be emitted when the GpioMonitor \a enabled changed. */ @@ -76,21 +83,13 @@ GpioMonitor::~GpioMonitor() wait(200); } -/*! Returns the edge interrupt configuration for this GpioMonitor. - - \sa Gpio::Edge - -*/ +/*! Returns the edge interrupt configuration for this GpioMonitor. */ Gpio::Edge GpioMonitor::edge() const { return m_edge; } -/*! Sets the edge interrupt configuration for this GpioMonitor to the given \a edge. - - \sa Gpio::Edge - -*/ +/*! Sets the edge interrupt configuration for this GpioMonitor to the given \a edge. */ void GpioMonitor::setEdge(Gpio::Edge edge) { if (m_edge == edge) From 754074ae779473590a6a33ad33501ad1b703b041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 13 Sep 2019 17:12:12 +0200 Subject: [PATCH 14/17] Fix documentation example code --- libnymea-gpio/gpiobutton.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libnymea-gpio/gpiobutton.cpp b/libnymea-gpio/gpiobutton.cpp index 2bfaa2f..ffe6e38 100644 --- a/libnymea-gpio/gpiobutton.cpp +++ b/libnymea-gpio/gpiobutton.cpp @@ -35,6 +35,8 @@ button->setName("User button"); if (!button->enable()) { qWarning() << "Could not enable the" << this; + button->deleteLater(); + return; } connect(button, &GpioButton::clicked, this, [this, button](){ From f8b8963c54754e6b3d17b8dbd09861d1909b7677 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 14 Sep 2019 00:48:10 +0200 Subject: [PATCH 15/17] drop unneeded line from changelog --- debian/changelog | 2 -- 1 file changed, 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index eb4ea68..8008a65 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,3 @@ nymea-gpio (1.0.0) UNRELEASED; urgency=medium - * Initial release. - -- Simon Stürz Wed, 04 Sep 2019 11:50:53 +0200 From 9b87a51ce5ee8aa89889ca8102de084392ff3a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 16 Sep 2019 09:05:11 +0200 Subject: [PATCH 16/17] Add index documentation page --- docs/config.qdocconf | 4 ++-- docs/index.qdoc | 11 +++++++++++ libnymea-gpio/gpio.cpp | 1 + libnymea-gpio/gpiobutton.cpp | 1 + libnymea-gpio/gpiobutton.h | 2 +- libnymea-gpio/gpiomonitor.cpp | 1 + 6 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 docs/index.qdoc diff --git a/docs/config.qdocconf b/docs/config.qdocconf index e55b75d..ef63711 100644 --- a/docs/config.qdocconf +++ b/docs/config.qdocconf @@ -22,9 +22,9 @@ sourceencoding = UTF-8 syntaxhighlighting = true headerdirs = ../libnymea-gpio -sourcedirs = ../libnymea-gpio +sourcedirs = ../libnymea-gpio ../docs headers.fileextensions = "*.h" -sources.fileextensions = "*.cpp" +sources.fileextensions = "*.cpp *.qdoc" Cpp.ignoredirectives = Q_DECLARE_METATYPE Q_DECLARE_LOGGING_CATEGORY Q_LOGGING_CATEGORY Q_ENUM diff --git a/docs/index.qdoc b/docs/index.qdoc new file mode 100644 index 0000000..917cee1 --- /dev/null +++ b/docs/index.qdoc @@ -0,0 +1,11 @@ +/*! + \page index.html + \title nymea-gpio documentation + + The nymea-gpio library allowes to interact in an confortable way with system GPIOs. + + \chapter Classes + \annotatedlist gpio + +*/ + diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index 8934d76..0b51ed5 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -24,6 +24,7 @@ \class Gpio \brief Represents a system GPIO in linux systems. \inmodule nymea-gpio + \ingroup gpio A "General Purpose Input/Output" (GPIO) is a flexible software-controlled digital signal. They are provided from many kinds of chip, and are familiar diff --git a/libnymea-gpio/gpiobutton.cpp b/libnymea-gpio/gpiobutton.cpp index ffe6e38..f61b3f0 100644 --- a/libnymea-gpio/gpiobutton.cpp +++ b/libnymea-gpio/gpiobutton.cpp @@ -24,6 +24,7 @@ \class GpioButton \brief Represents a GPIO Button with some helper methods. \inmodule nymea-gpio + \ingroup gpio This class represents a Button based on a GPIO. The class takes care about the \l{clicked()} signal handling, debounces the GPIO signal and offers a nice interface for \l{longPressed()} behaviour. diff --git a/libnymea-gpio/gpiobutton.h b/libnymea-gpio/gpiobutton.h index 65e55a1..f80d7ac 100644 --- a/libnymea-gpio/gpiobutton.h +++ b/libnymea-gpio/gpiobutton.h @@ -33,7 +33,7 @@ class GpioButton : public QObject { Q_OBJECT public: - explicit GpioButton(int gpioNumber, QObject *parent = nullptr); + explicit GpioButton(int gpio, QObject *parent = nullptr); int gpioNumber() const; diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index 271007f..3bb77c5 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -24,6 +24,7 @@ \class GpioMonitor \brief Monitor for GPIO interrupts. \inmodule nymea-gpio + \ingroup gpio This class allows to monitor an input GPIO for the interrupts depending on the edge interrupt configuration. From 81200a0d63ba4e784d7aab7828b679736f8550f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 19 Sep 2019 09:20:18 +0200 Subject: [PATCH 17/17] Fix gpio button --- libnymea-gpio/gpiobutton.cpp | 4 ++-- libnymea-gpio/gpiomonitor.cpp | 24 +++++++++--------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/libnymea-gpio/gpiobutton.cpp b/libnymea-gpio/gpiobutton.cpp index f61b3f0..8c98980 100644 --- a/libnymea-gpio/gpiobutton.cpp +++ b/libnymea-gpio/gpiobutton.cpp @@ -198,14 +198,14 @@ bool GpioButton::enable() m_monitor = nullptr; return false; } - connect(m_monitor, &GpioMonitor::interruptOccured, this, &GpioButton::onInterruptOccured, Qt::DirectConnection); + connect(m_monitor, &GpioMonitor::interruptOccured, this, &GpioButton::onInterruptOccured); // Setup timer, if this timer reaches timeout, a long pressed happend m_timer = new QTimer(this); m_timer->setTimerType(Qt::PreciseTimer); m_timer->setSingleShot(!m_repeateLongPressed); m_timer->setInterval(m_longPressedTimeout); - connect(m_timer, &QTimer::timeout, this, &GpioButton::onTimeout, Qt::DirectConnection); + connect(m_timer, &QTimer::timeout, this, &GpioButton::onTimeout); return true; } diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index 3bb77c5..1fd9d42 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -157,38 +157,33 @@ void GpioMonitor::setEnabled(bool enabled) void GpioMonitor::run() { // Create GPIO in the thread for initialisation - Gpio *inputGpio = new Gpio(m_gpioNumber); - if (!inputGpio->exportGpio()) { + Gpio inputGpio(m_gpioNumber); + if (!inputGpio.exportGpio()) { qCWarning(dcGpio()) << "Could not enable GPIO monitor."; - delete inputGpio; return; } - if (!inputGpio->setDirection(Gpio::DirectionInput)) { + if (!inputGpio.setDirection(Gpio::DirectionInput)) { qCWarning(dcGpio()) << "Could not enable GPIO monitor."; - delete inputGpio; return; } - if (!inputGpio->setEdgeInterrupt(m_edge)) { + if (!inputGpio.setEdgeInterrupt(m_edge)) { qCWarning(dcGpio()) << "Could not set interrupt for the GPIO monitor."; - delete inputGpio; return; } - if (!inputGpio->setActiveLow(m_activeLow)) { + if (!inputGpio.setActiveLow(m_activeLow)) { qCWarning(dcGpio()) << "Could not set active low for the GPIO monitor."; - delete inputGpio; return; } // In order to do correctly, use poll (2) according to the kernel documentation // https://www.kernel.org/doc/Documentation/gpio/sysfs.txt - QFile valueFile(inputGpio->gpioDirectory() + QDir::separator() + "value"); + QFile valueFile(inputGpio.gpioDirectory() + QDir::separator() + "value"); if (!valueFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - qCWarning(dcGpio()) << "Could not open GPIO" << inputGpio << "value file:" << valueFile.errorString(); - delete inputGpio; + qCWarning(dcGpio()) << "Could not open GPIO" << &inputGpio << "value file:" << valueFile.errorString(); return; } @@ -206,7 +201,7 @@ void GpioMonitor::run() // Poll failed... if (rc < 0) { - qCWarning(dcGpio()) << "Failed to poll" << inputGpio; + qCWarning(dcGpio()) << "Failed to poll" << &inputGpio; break; } @@ -223,7 +218,7 @@ void GpioMonitor::run() QString valueString; QTextStream readStream(&valueFile); if (!readStream.seek(0)) { - qCWarning(dcGpio()) << "Failed to seek value file of" << inputGpio; + qCWarning(dcGpio()) << "Failed to seek value file of" << &inputGpio; continue; } @@ -239,7 +234,6 @@ void GpioMonitor::run() // Clean up once done valueFile.close(); - delete inputGpio; } void GpioMonitor::onThreadStarted()