Merge PR #1: Initial version

This commit is contained in:
Jenkins 2019-09-19 12:27:40 +02:00
commit 62f0e9ac0c
26 changed files with 1761 additions and 0 deletions

2
.gitignore vendored
View File

@ -41,3 +41,5 @@ target_wrapper.*
# QtCreator CMake
CMakeLists.txt.user*
.crossbuilder

3
debian/changelog vendored Normal file
View File

@ -0,0 +1,3 @@
nymea-gpio (1.0.0) UNRELEASED; urgency=medium
-- Simon Stürz <simon.stuerz@nymea.io> Wed, 04 Sep 2019 11:50:53 +0200

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

48
debian/control vendored Normal file
View File

@ -0,0 +1,48 @@
Source: nymea-gpio
Section: utils
Priority: optional
Maintainer: Simon Stürz <simon.stuerz@nymea.io>
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: 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})
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.

19
debian/copyright vendored Normal file
View File

@ -0,0 +1,19 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Author: Simon Stürz <simon.stuerz@nymea.io>
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'.
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 <simon.stuerz@nymea.io>
License: LGPL-3
Files: nymea-gpio-tool/*
Copyright: (C) 2019 Simon Stürz <simon.stuerz@nymea.io>
License: GPL-3+

3
debian/libnymea-gpio-dev.install.in vendored Normal file
View File

@ -0,0 +1,3 @@
usr/lib/@DEB_HOST_MULTIARCH@/libnymea-gpio.so
usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/nymea-gpio.pc
usr/include/nymea-gpio/* usr/include/nymea-gpio/

3
debian/libnymea-gpio.install.in vendored Normal file
View File

@ -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

1
debian/nymea-gpio-tool.install.in vendored Normal file
View File

@ -0,0 +1 @@
nymea-gpio-tool/nymea-gpio-tool usr/bin

22
debian/rules vendored Executable file
View File

@ -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=)

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

30
docs/config.qdocconf Normal file
View File

@ -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 ../docs
headers.fileextensions = "*.h"
sources.fileextensions = "*.cpp *.qdoc"
Cpp.ignoredirectives = Q_DECLARE_METATYPE Q_DECLARE_LOGGING_CATEGORY Q_LOGGING_CATEGORY Q_ENUM

View File

@ -0,0 +1,11 @@
HTML.templatedir = .
HTML.postpostheader = \
"<div class=\"content mainContent\">\n"
HTML.prologue = \
"<div class=\"context\">\n"
HTML.footer = \
"</div>\n" \
"</div>\n"

11
docs/index.qdoc Normal file
View File

@ -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
*/

478
libnymea-gpio/gpio.cpp Normal file
View File

@ -0,0 +1,478 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\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
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
*/
/*!
\enum Gpio::Direction
This enum type specifies the dirction a Gpio.
\value DirectionInput
The Gpio is configured as \b input.
\value DirectionOutput
The Gpio is configured as \b output.
\value DirectionInvalid
The direction is not valid.
*/
/*!
\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.
*/
/*!
\enum Gpio::Edge
This enum type specifies the edge interrupt type of a Gpio.
\value EdgeFalling
The Gpio reacts on falling edge interrupt.
\value EdgeRising
The Gpio reacts on rising edge interrupt.
\value EdgeBoth
The Gpio reacts on both, rising and falling edge interrupt.
\value EdgeNone
The 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. */
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 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<number>} of this Gpio. */
QString Gpio::gpioDirectory() const
{
return m_gpioDirectory.canonicalPath();
}
/*! 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 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()) {
qCDebug(dcGpio()) << "GPIO" << m_gpio << "already exported.";
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 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();
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)
{
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() + QDir::separator() + "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 Gpio. */
Gpio::Direction Gpio::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;
}
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 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.";
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() + QDir::separator() + "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 Gpio. */
Gpio::Value Gpio::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;
}
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 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;
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;
}
QTextStream out(&activeLowFile);
if (activeLow) {
out << "0";
} else {
out << "1";
}
activeLowFile.close();
return true;
}
/*! 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");
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;
}
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;
}
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 Gpio. */
Gpio::Edge Gpio::edgeInterrupt()
{
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;
}
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;
}
/*! Prints the given \a gpio to \a debug. */
QDebug operator<<(QDebug debug, Gpio *gpio)
{
debug.nospace() << "Gpio(" << gpio->gpioNumber() << ", ";
if (gpio->direction() == Gpio::DirectionInput) {
debug.nospace() << "input, ";
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, ";
} 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";
}
debug.nospace() << ")";
return debug.space();
}

92
libnymea-gpio/gpio.h Normal file
View File

@ -0,0 +1,92 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef GPIO_H
#define GPIO_H
#include <QDir>
#include <QDebug>
#include <QObject>
#include <QLoggingCategory>
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

View File

@ -0,0 +1,232 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\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.
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;
button->deleteLater();
return;
}
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"
/*! Constructs a \l{GpioButton} object with the given \a gpio number and \a parent. */
GpioButton::GpioButton(int gpio, QObject *parent) :
QObject(parent),
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;
}
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();
}
}
}
/*! Returns \c true, if this GpioButton was enabled successfully. */
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);
// 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);
return true;
}
/*! Disable this GpioButton. The Gpio will be unexported. */
void GpioButton::disable()
{
if (m_monitor) {
delete m_monitor;
m_monitor = nullptr;
}
if (m_timer) {
delete m_timer;
m_timer = nullptr;
}
}
/*! Prints the given \a gpioButton to \a debug. */
QDebug operator<<(QDebug debug, GpioButton *gpioButton)
{
debug.nospace() << "GpioButton(" << gpioButton->gpioNumber() << ", ";
debug.nospace() << "name: " << gpioButton->name() << ")";
return debug.space();
}

View File

@ -0,0 +1,83 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef GPIOBUTTON_H
#define GPIOBUTTON_H
#include <QTime>
#include <QTimer>
#include <QObject>
class GpioMonitor;
class GpioButton : public QObject
{
Q_OBJECT
public:
explicit GpioButton(int gpio, 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

View File

@ -0,0 +1,282 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\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.
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);
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 <poll.h>
#include <QMutexLocker>
/*! 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)
{
// Inform about the thread status
connect(this, &GpioMonitor::started, this, &GpioMonitor::onThreadStarted, Qt::DirectConnection);
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. */
Gpio::Edge GpioMonitor::edge() const
{
return m_edge;
}
/*! Sets the edge interrupt configuration for this GpioMonitor to the given \a edge. */
void GpioMonitor::setEdge(Gpio::Edge edge)
{
if (m_edge == edge)
return;
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)
return;
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;
}
void GpioMonitor::setValue(Gpio::Value value)
{
QMutexLocker valueLocker(&m_valueMutex);
m_value = 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)
{
if (m_enabled == enabled)
return;
m_enabled = 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
Gpio inputGpio(m_gpioNumber);
if (!inputGpio.exportGpio()) {
qCWarning(dcGpio()) << "Could not enable GPIO monitor.";
return;
}
if (!inputGpio.setDirection(Gpio::DirectionInput)) {
qCWarning(dcGpio()) << "Could not enable GPIO monitor.";
return;
}
if (!inputGpio.setEdgeInterrupt(m_edge)) {
qCWarning(dcGpio()) << "Could not set interrupt for the GPIO monitor.";
return;
}
if (!inputGpio.setActiveLow(m_activeLow)) {
qCWarning(dcGpio()) << "Could not set active low for the GPIO monitor.";
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();
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);
// 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;
// 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();
}
void GpioMonitor::onThreadStarted()
{
qCDebug(dcGpio()) << "Monitor thread started";
setEnabled(true);
}
void GpioMonitor::onThreadFinished()
{
qCDebug(dcGpio()) << "Monitor thread finished";
setEnabled(false);
}
/*! Returns true, if this GpioMonitor was enabled successfully. */
bool GpioMonitor::enable()
{
qCDebug(dcGpio()) << "Enable gpio monitor";
if (isRunning()) {
qCWarning(dcGpio()) << "This GPIO monitor is already running.";
return true;
}
// Init the GPIO
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 looks good, lets start the poll thread and inform about the result
start();
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";
// Stop the thread if not already disabled
QMutexLocker locker(&m_stopMutex);
if (m_stop) return;
m_stop = true;
}

View File

@ -0,0 +1,82 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef GPIOMONITOR_H
#define GPIOMONITOR_H
#include <QMutex>
#include <QThread>
#include <QObject>
#include "gpio.h"
class GpioMonitor : public QThread
{
Q_OBJECT
public:
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:
int m_gpioNumber = -1;
Gpio::Edge m_edge = Gpio::EdgeBoth;
bool m_activeLow = true;
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 interruptOccured(bool value);
void enabledChanged(bool enabled);
private slots:
void onThreadStarted();
void onThreadFinished();
public slots:
bool enable();
void disable();
};
#endif // GPIOMONITOR_H

View File

@ -0,0 +1,36 @@
include(../nymea-gpio.pri)
TARGET = nymea-gpio
TEMPLATE = lib
HEADERS += \
gpio.h \
gpiobutton.h \
gpiomonitor.h
SOURCES += \
gpio.cpp \
gpiobutton.cpp \
gpiomonitor.cpp
target.path = $$[QT_INSTALL_LIBS]
INSTALLS += target
# install header file with relative subdirectory
for(header, HEADERS) {
path = $$[QT_INSTALL_PREFIX]/include/nymea-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 = nymea-gpio
QMAKE_PKGCONFIG_DESCRIPTION = nymea gpio development library
QMAKE_PKGCONFIG_PREFIX = $$[QT_INSTALL_PREFIX]
QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_PREFIX]/include/nymea-gpio/
QMAKE_PKGCONFIG_LIBDIR = $$target.path
QMAKE_PKGCONFIG_VERSION = $$VERSION_STRING
QMAKE_PKGCONFIG_FILE = nymea-gpio
QMAKE_PKGCONFIG_DESTDIR = pkgconfig

View File

@ -0,0 +1,68 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "application.h"
#include <QDebug>
#include <signal.h>
static void catchUnixSignals(const std::vector<int>& quitSignals, const std::vector<int>& ignoreSignals = std::vector<int>())
{
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});
}

View File

@ -0,0 +1,37 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef APPLICATION_H
#define APPLICATION_H
#include <QObject>
#include <QCoreApplication>
class Application : public QCoreApplication
{
Q_OBJECT
public:
explicit Application(int &argc, char **argv);
};
#endif // APPLICATION_H

182
nymea-gpio-tool/main.cpp Normal file
View File

@ -0,0 +1,182 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* 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 *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <QCommandLineParser>
#include "application.h"
#include "gpiomonitor.h"
int main(int argc, char *argv[])
{
Application application(argc, argv);
application.setOrganizationName("guh");
application.setApplicationName("nymea-gpio-tool");
application.setApplicationVersion(VERSION_STRING);
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 <simon.stuerz@nymea.io>\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 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");
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(EXIT_FAILURE);
}
// 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.";
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.";
return EXIT_FAILURE;
}
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].";
return EXIT_FAILURE;
}
}
bool activeLow = true;
if (parser.isSet(activeLowOption)) {
if (parser.value(activeLowOption) == "1") {
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].";
return EXIT_FAILURE;
}
}
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].";
return EXIT_FAILURE;
}
}
if (!Gpio::isAvailable()) {
qCritical() << "There are no GPIOs available on this platform.";
return EXIT_FAILURE;
}
// Configure the GPIO
if (parser.isSet(valueOption)) {
Gpio *gpio = new Gpio(gpioNumber);
if (!gpio->exportGpio()) {
qCritical() << "Could not export GPIO" << gpioNumber;
return EXIT_FAILURE;
}
if (!gpio->setDirection(Gpio::DirectionOutput)) {
qCritical() << "Could not configure GPIO" << gpioNumber << "as output.";
return EXIT_FAILURE;
}
if (parser.isSet(activeLowOption)) {
if (!gpio->setActiveLow(activeLow)) {
qCritical() << "Could not set GPIO" << gpioNumber << "to active low" << activeLow;
return EXIT_FAILURE;
}
}
// Finally set the value
if (!gpio->setValue(value)) {
qCritical() << "Could not set GPIO" << gpioNumber << "value to" << value;
return EXIT_FAILURE;
}
delete gpio;
return EXIT_SUCCESS;
} else {
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");
});
// 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.";
return EXIT_FAILURE;
}
// Clean up the gpio once done
QObject::connect(&application, &Application::aboutToQuit, [monitor](){
delete monitor;
});
}
return application.exec();
}

View File

@ -0,0 +1,19 @@
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
HEADERS += \
application.h
SOURCES += main.cpp \
application.cpp
target.path = /usr/bin
INSTALLS += target

11
nymea-gpio.pri Normal file
View File

@ -0,0 +1,11 @@
QMAKE_CXXFLAGS += -Werror -std=c++11 -g
QMAKE_LFLAGS += -std=c++11
QT -= gui
top_srcdir=$$PWD
top_builddir=$$shadowed($$PWD)
VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"')
DEFINES += VERSION_STRING=\\\"$${VERSION_STRING}\\\"

4
nymea-gpio.pro Normal file
View File

@ -0,0 +1,4 @@
TEMPLATE = subdirs
SUBDIRS = libnymea-gpio nymea-gpio-tool
nymea-gpio-tool.depends = libnymea-gpio